From 52594508534297c44764985128e6a741105137c6 Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Sun, 18 Oct 2026 20:29:00 +0200
Subject: [PATCH v10] Add XMLSerialize: version and explicit XML declaration

* Explicit XML declaration (SQL/XML:2023, X078)

This patch adds the options INCLUDING XMLDECLARATION and
EXCLUDING XMLDECLARATION to XMLSERIALIZE, allowing users
to explicitly control the presence of the XML declaration
(e.g., <?xml version="1.0"?>) in the serialized output of
XML values. If neither option is specified, the output is
the same as before: a declaration can only appear if the
input XML value already contained one.

A declaration written by these options never contains an
encoding declaration, since the result is a character
string that is subject to encoding conversion; xml_out()
omits it for the same reason. Without INDENT, the value
itself is not reserialized: only its XML declaration is
added, replaced or removed.

* Version support (SQL/XML:2023, X076)

The VERSION option allows specifying the version string
to use in the XML declaration. If omitted, the version of
the input's declaration is kept, or '1.0' is used if it
has none. VERSION does not by itself add a declaration.

The version string is checked by libxml2, in both DOCUMENT
and CONTENT mode, by parsing a minimal document that
declares it: versions it rejects (e.g., '2.0') raise an
error, and versions it does not fully support (e.g., '1.1')
raise a warning.

Examples:

SELECT xmlserialize(
         DOCUMENT xmlval AS text
         VERSION '1.0'
         INCLUDING XMLDECLARATION);

SELECT xmlserialize(
         DOCUMENT xmlval AS text
         EXCLUDING XMLDECLARATION);

Author: Jim Jones <jim.jones@uni-muenster.de>
Reviewed-by: solai v <solai.cdac@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/0427cced-93df-4e50-aaa0-645438a2dbe4%40uni-muenster.de
---
 doc/src/sgml/datatype.sgml            |  67 +++-
 src/backend/catalog/sql_features.txt  |   4 +-
 src/backend/executor/execExprInterp.c |   4 +-
 src/backend/parser/gram.y             |  24 +-
 src/backend/parser/parse_expr.c       |   2 +
 src/backend/utils/adt/ruleutils.c     |  12 +
 src/backend/utils/adt/xml.c           | 201 +++++++++--
 src/include/nodes/parsenodes.h        |   3 +
 src/include/nodes/primnodes.h         |  11 +
 src/include/parser/kwlist.h           |   1 +
 src/include/utils/xml.h               |   3 +-
 src/test/regress/expected/xml.out     | 468 +++++++++++++++++++++++++-
 src/test/regress/sql/xml.sql          |  85 +++++
 src/tools/pgindent/typedefs.list      |   1 +
 14 files changed, 846 insertions(+), 40 deletions(-)

diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 83bf0ad8ed2..d06c3b94a1c 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4537,7 +4537,10 @@ xml '<foo>bar</foo>'
     <type>xml</type>, uses the function
     <function>xmlserialize</function>:<indexterm><primary>xmlserialize</primary></indexterm>
 <synopsis>
-XMLSERIALIZE ( { DOCUMENT | CONTENT } <replaceable>value</replaceable> AS <replaceable>type</replaceable> [ [ NO ] INDENT ] )
+XMLSERIALIZE ( { DOCUMENT | CONTENT } <replaceable>value</replaceable> AS <replaceable>type</replaceable>
+     [ VERSION <replaceable>xmlversion</replaceable> ]
+     [ INCLUDING XMLDECLARATION | EXCLUDING XMLDECLARATION ]
+     [ [ NO ] INDENT ] )
 </synopsis>
     <replaceable>type</replaceable> can be
     <type>character</type>, <type>character varying</type>, or
@@ -4547,6 +4550,38 @@ XMLSERIALIZE ( { DOCUMENT | CONTENT } <replaceable>value</replaceable> AS <repla
     you to simply cast the value.
    </para>
 
+   <para>
+    The <literal>VERSION</literal> option sets the version string
+    used in the XML declaration. If the <literal>VERSION</literal>
+    option is omitted, the version
+    of the input <replaceable>value</replaceable>'s XML declaration is kept,
+    or <literal>1.0</literal> is used if it has none.
+    <literal>VERSION</literal> does not by itself add an XML declaration to
+    the output.  If the version string is
+    not accepted as a valid XML version by the XML parser (e.g.,
+    <literal>'2.0'</literal>), an error is raised; versions that the parser
+    does not fully support, such as <literal>'1.1'</literal>, only raise a
+    warning.
+   </para>
+
+   <para>
+    The <literal>INCLUDING XMLDECLARATION</literal> and
+    <literal>EXCLUDING XMLDECLARATION</literal> options control
+    whether the serialized output includes an XML declaration such as
+    <literal>&lt;?xml version="1.0"?&gt;</literal>.
+    If neither option is specified, the presence of the declaration in
+    the output depends on whether the input <replaceable>value</replaceable>
+    originally contained one.  A declaration written by these options or by
+    <literal>VERSION</literal> does not have an encoding declaration (see
+    <xref linkend="datatype-xml-encoding-handling"/>).
+
+    The <literal>INCLUDING XMLDECLARATION</literal> option is allowed in both
+    <literal>DOCUMENT</literal> and <literal>CONTENT</literal> modes. In
+    <literal>CONTENT</literal> mode, no structural validation is performed,
+    and the declaration is emitted according to the specified options,
+    even if it would not normally be considered valid by the XML specification.
+   </para>
+
    <para>
     The <literal>INDENT</literal> option causes the result to be
     pretty-printed, while <literal>NO INDENT</literal> (which is the
@@ -4554,6 +4589,36 @@ XMLSERIALIZE ( { DOCUMENT | CONTENT } <replaceable>value</replaceable> AS <repla
     type likewise produces the original string.
    </para>
 
+   <para>
+Examples:
+   </para>
+<screen><![CDATA[
+SELECT
+  xmlserialize(
+    DOCUMENT xmlelement(name foo, xmlelement(name bar,42)) AS text
+    VERSION '1.0'
+    INCLUDING XMLDECLARATION
+    INDENT);
+
+     xmlserialize
+-----------------------
+ <?xml version="1.0"?>+
+ <foo>                +
+   <bar>42</bar>      +
+ </foo>
+(1 row)
+
+SELECT
+  xmlserialize(
+    DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar>42</bar></foo>' AS text
+    EXCLUDING XMLDECLARATION);
+
+       xmlserialize
+--------------------------
+ <foo><bar>42</bar></foo>
+(1 row)
+]]></screen>
+
    <para>
     When a character string value is cast to or from type
     <type>xml</type> without going through <type>XMLPARSE</type> or
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index a513b85a508..32f8f28e87f 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -665,9 +665,9 @@ X072	XMLSerialize: character string serialization			YES
 X073	XMLSerialize: binary string serialization and CONTENT option			NO	
 X074	XMLSerialize: binary string serialization and DOCUMENT option			NO	
 X075	XMLSerialize: binary string serialization			NO	
-X076	XMLSerialize: VERSION			NO	
+X076	XMLSerialize: VERSION			YES	
 X077	XMLSerialize: explicit ENCODING option			NO	
-X078	XMLSerialize: explicit XML declaration			NO	
+X078	XMLSerialize: explicit XML declaration			YES	
 X080	Namespaces in XML publishing			NO	
 X081	Query-level XML namespace declarations			NO	
 X082	XML namespace declarations in DML			NO	
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 397219f7a3a..4f420ee0532 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -4697,7 +4697,9 @@ ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
 				*op->resvalue =
 					PointerGetDatum(xmltotext_with_options(DatumGetXmlP(value),
 														   xexpr->xmloption,
-														   xexpr->indent));
+														   xexpr->indent,
+														   xexpr->xmldeclaration,
+														   xexpr->version));
 				*op->resnull = false;
 			}
 			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 0563453fe24..13e5910541b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -629,6 +629,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <defelt>	xmltable_column_option_el
 %type <list>	xml_namespace_list
 %type <target>	xml_namespace_el
+%type <ival>	opt_xml_declaration_option
+%type <str>		opt_xml_declaration_version
 
 %type <node>	func_application func_expr_common_subexpr
 %type <node>	func_expr func_expr_windowless
@@ -807,8 +809,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 	WAIT WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
 
-	XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLNAMESPACES
-	XMLPARSE XMLPI XMLROOT XMLSERIALIZE XMLTABLE
+	XML_P XMLATTRIBUTES XMLCONCAT XMLDECLARATION XMLELEMENT XMLEXISTS XMLFOREST
+	XMLNAMESPACES XMLPARSE XMLPI XMLROOT XMLSERIALIZE XMLTABLE
 
 	YEAR_P YES_P
 
@@ -16453,14 +16455,16 @@ func_expr_common_subexpr:
 					$$ = makeXmlExpr(IS_XMLROOT, NULL, NIL,
 									 list_make3($3, $5, $6), @1);
 				}
-			| XMLSERIALIZE '(' document_or_content a_expr AS SimpleTypename xml_indent_option ')'
+			| XMLSERIALIZE '(' document_or_content a_expr AS SimpleTypename opt_xml_declaration_version opt_xml_declaration_option xml_indent_option ')'
 				{
 					XmlSerialize *n = makeNode(XmlSerialize);
 
 					n->xmloption = $3;
 					n->expr = $4;
 					n->typeName = $6;
-					n->indent = $7;
+					n->version = $7;
+					n->xmldeclaration = $8;
+					n->indent = $9;
 					n->location = @1;
 					$$ = (Node *) n;
 				}
@@ -16685,6 +16689,16 @@ xml_indent_option: INDENT							{ $$ = true; }
 			| /*EMPTY*/								{ $$ = false; }
 		;
 
+opt_xml_declaration_version:
+			VERSION_P Sconst		{ $$ = $2; }
+			| /*EMPTY*/				{ $$ = NULL; }
+		;
+
+opt_xml_declaration_option: INCLUDING XMLDECLARATION	{ $$ = XMLSERIALIZE_INCLUDING_XMLDECLARATION; }
+			| EXCLUDING XMLDECLARATION					{ $$ = XMLSERIALIZE_EXCLUDING_XMLDECLARATION; }
+			| /*EMPTY*/									{ $$ = XMLSERIALIZE_NO_XMLDECLARATION_OPTION; }
+		;
+
 xml_whitespace_option: PRESERVE WHITESPACE_P		{ $$ = true; }
 			| STRIP_P WHITESPACE_P					{ $$ = false; }
 			| /*EMPTY*/								{ $$ = false; }
@@ -18402,6 +18416,7 @@ unreserved_keyword:
 			| WRAPPER
 			| WRITE
 			| XML_P
+			| XMLDECLARATION
 			| YEAR_P
 			| YES_P
 			| ZONE
@@ -19063,6 +19078,7 @@ bare_label_keyword:
 			| XML_P
 			| XMLATTRIBUTES
 			| XMLCONCAT
+			| XMLDECLARATION
 			| XMLELEMENT
 			| XMLEXISTS
 			| XMLFOREST
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 05a6b72d4c9..1660d6590f4 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -2512,6 +2512,8 @@ transformXmlSerialize(ParseState *pstate, XmlSerialize *xs)
 
 	xexpr->xmloption = xs->xmloption;
 	xexpr->indent = xs->indent;
+	xexpr->version = xs->version;
+	xexpr->xmldeclaration = xs->xmldeclaration;
 	xexpr->location = xs->location;
 	/* We actually only need these to be able to parse back the expression. */
 	xexpr->type = targetType;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 5e8e1683db0..903163f360b 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -10257,6 +10257,18 @@ get_rule_expr(Node *node, deparse_context *context,
 					appendStringInfo(buf, " AS %s",
 									 format_type_with_typemod(xexpr->type,
 															  xexpr->typmod));
+
+					if (xexpr->version)
+					{
+						appendStringInfoString(buf, " VERSION ");
+						simple_quote_literal(buf, xexpr->version);
+					}
+
+					if (xexpr->xmldeclaration == XMLSERIALIZE_INCLUDING_XMLDECLARATION)
+						appendStringInfoString(buf, " INCLUDING XMLDECLARATION");
+					else if (xexpr->xmldeclaration == XMLSERIALIZE_EXCLUDING_XMLDECLARATION)
+						appendStringInfoString(buf, " EXCLUDING XMLDECLARATION");
+
 					if (xexpr->indent)
 						appendStringInfoString(buf, " INDENT");
 					else
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index dab00954c6f..ec0925ccf62 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -152,8 +152,10 @@ static xmlChar *xml_text2xmlChar(text *in);
 static int	parse_xml_decl(const xmlChar *str, size_t *lenp,
 						   xmlChar **version, xmlChar **encoding, int *standalone);
 static bool print_xml_decl(StringInfo buf, const xmlChar *version,
-						   pg_enc encoding, int standalone);
+						   pg_enc encoding, int standalone,
+						   bool force_xmldeclaration);
 static bool xml_doctype_in_content(const xmlChar *str);
+static void xmlserialize_check_version(const char *version);
 static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
 						   bool preserve_whitespace, int encoding,
 						   XmlOptionType *parsed_xmloptiontype,
@@ -324,7 +326,7 @@ xml_out_internal(xmltype *x, pg_enc target_encoding)
 
 		initStringInfo(&buf);
 
-		if (!print_xml_decl(&buf, version, target_encoding, standalone))
+		if (!print_xml_decl(&buf, version, target_encoding, standalone, false))
 		{
 			/*
 			 * If we are not going to produce an XML declaration, eat a single
@@ -619,7 +621,8 @@ xmlconcat(List *args)
 		print_xml_decl(&buf2,
 					   (!global_version_no_value) ? global_version : NULL,
 					   0,
-					   global_standalone);
+					   global_standalone,
+					   false);
 
 		appendBinaryStringInfo(&buf2, buf.data, buf.len);
 		buf = buf2;
@@ -674,7 +677,8 @@ xmltotext(PG_FUNCTION_ARGS)
 
 
 text *
-xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
+xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent,
+						XmlSerializeDeclarationOption xmldeclaration, const char *xmlserialize_version)
 {
 #ifdef USE_LIBXML
 	text	   *volatile result;
@@ -684,9 +688,12 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
 	volatile xmlSaveCtxtPtr ctxt = NULL;
 	ErrorSaveContext escontext = {T_ErrorSaveContext};
 	PgXmlErrorContext *volatile xmlerrcxt = NULL;
+	StringInfoData xmldecl;
 #endif
 
-	if (xmloption_arg != XMLOPTION_DOCUMENT && !indent)
+	if (xmloption_arg != XMLOPTION_DOCUMENT &&
+		xmldeclaration == XMLSERIALIZE_NO_XMLDECLARATION_OPTION &&
+		!indent && !xmlserialize_version)
 	{
 		/*
 		 * We don't actually need to do anything, so just return the
@@ -698,6 +705,10 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
 
 #ifdef USE_LIBXML
 
+	/* Make sure that libxml2 accepts the version given with VERSION. */
+	if (xmlserialize_version != NULL)
+		xmlserialize_check_version(xmlserialize_version);
+
 	/*
 	 * Parse the input according to the xmloption.
 	 *
@@ -717,13 +728,54 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
 				 errmsg("not an XML document")));
 	}
 
-	/* If we weren't asked to indent, we're done. */
-	if (!indent)
+	/*
+	 * If we weren't asked to indent or to explicitly hide / show the
+	 * XML declaration, we're done.
+	 */
+	if (!indent && !xmlserialize_version &&
+		xmldeclaration == XMLSERIALIZE_NO_XMLDECLARATION_OPTION)
 	{
 		xmlFreeDoc(doc);
 		return (text *) data;
 	}
 
+	/*
+	 * Without INDENT there is no need to reserialize the value, so just
+	 * replace the input's XML declaration, if any, with the requested one.
+	 */
+	if (!indent)
+	{
+		char	   *str;
+		size_t		decl_len = 0;
+		xmlChar    *version;
+		int			standalone;
+
+		xmlFreeDoc(doc);
+
+		str = text_to_cstring((text *) data);
+		parse_xml_decl((xmlChar *) str, &decl_len, &version, NULL, &standalone);
+
+		initStringInfo(&xmldecl);
+
+		if (xmldeclaration == XMLSERIALIZE_INCLUDING_XMLDECLARATION ||
+			(decl_len != 0 && xmldeclaration == XMLSERIALIZE_NO_XMLDECLARATION_OPTION &&
+			 xmlserialize_version))
+			print_xml_decl(&xmldecl,
+						   xmlserialize_version != NULL ?
+						   (const xmlChar *) xmlserialize_version : version,
+						   0, standalone, true);
+		else if (decl_len != 0 && str[decl_len] == '\n')
+		{
+			/* Don't leave an empty first line where the declaration was. */
+			decl_len++;
+		}
+
+		appendStringInfoString(&xmldecl, str + decl_len);
+		result = cstring_to_text_with_len(xmldecl.data, xmldecl.len);
+
+		return result;
+	}
+
 	/*
 	 * Otherwise, we gotta spin up some error handling.  Unlike most other
 	 * routines in this module, we already have a libxml "doc" structure to
@@ -747,17 +799,20 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
 		parse_xml_decl(xml_text2xmlChar(data), &decl_len, NULL, NULL, NULL);
 
 		/*
-		 * Emit declaration only if the input had one.  Note: some versions of
-		 * xmlSaveToBuffer leak memory if a non-null encoding argument is
-		 * passed, so don't do that.  We don't want any encoding conversion
-		 * anyway.
+		 * Indent the buffer content if the flag INDENT was used.
+		 * Note: some versions of xmlSaveToBuffer leak memory if a
+		 * non-null encoding argument is passed, so don't do that.
+		 * We don't want any encoding conversion anyway. Without any
+		 * XML declaration option, libxml2 emits the declaration only
+		 * if the input had one.  Otherwise the flag XML_SAVE_NO_DECL
+		 * is used, as we manually add the XML declaration later on
+		 * with xmlBufferAddHead(), if applicable.
 		 */
-		if (decl_len == 0)
-			ctxt = xmlSaveToBuffer(buf, NULL,
-								   XML_SAVE_NO_DECL | XML_SAVE_FORMAT);
+		if (xmldeclaration == XMLSERIALIZE_NO_XMLDECLARATION_OPTION &&
+			!xmlserialize_version && decl_len != 0)
+			ctxt = xmlSaveToBuffer(buf, NULL, XML_SAVE_FORMAT);
 		else
-			ctxt = xmlSaveToBuffer(buf, NULL,
-								   XML_SAVE_FORMAT);
+			ctxt = xmlSaveToBuffer(buf, NULL, XML_SAVE_NO_DECL | (indent ? XML_SAVE_FORMAT : 0));
 
 		if (ctxt == NULL || xmlerrcxt->err_occurred)
 			xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
@@ -777,7 +832,7 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
 			 * between the content nodes, else the output is ugly.
 			 */
 			xmlNodePtr	root = xmlDocGetRootElement(doc);
-			xmlNodePtr	newline;
+			xmlNodePtr	newline = NULL;
 
 			Assert(root != NULL);
 
@@ -788,19 +843,24 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
 			 * freeing of this node manually, and pass NULL here to make sure
 			 * there's not a dangling link.
 			 */
-			newline = xmlNewDocText(NULL, (const xmlChar *) "\n");
-			if (newline == NULL || xmlerrcxt->err_occurred)
-				xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
-							"could not allocate xml node");
+			if (indent)
+			{
+				newline = xmlNewDocText(NULL, (const xmlChar *)"\n");
+
+				if (newline == NULL || xmlerrcxt->err_occurred)
+					xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+								"could not allocate xml node");
+			}
 
 			for (xmlNodePtr node = root->children; node; node = node->next)
 			{
 				/* insert newlines between nodes */
-				if (node->type != XML_TEXT_NODE && node->prev != NULL)
+				if (node->type != XML_TEXT_NODE && node->prev != NULL && newline != NULL)
 				{
 					if (xmlSaveTree(ctxt, newline) == -1 || xmlerrcxt->err_occurred)
 					{
-						xmlFreeNode(newline);
+						if (newline != NULL)
+							xmlFreeNode(newline);
 						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
 									"could not save newline to xmlBuffer");
 					}
@@ -814,7 +874,54 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
 				}
 			}
 
-			xmlFreeNode(newline);
+			if (newline != NULL)
+				xmlFreeNode(newline);
+		}
+
+		/*
+		 * Emit declaration if it was explicitly requested via INCLUDING
+		 * XMLDECLARATION (X078, SQL/XML:2023), or if the input had one and
+		 * VERSION was used.  Without any of these options, libxml2 has
+		 * already emitted the input's declaration, if any.
+		 */
+		if (xmldeclaration == XMLSERIALIZE_INCLUDING_XMLDECLARATION ||
+			(decl_len != 0 && xmldeclaration == XMLSERIALIZE_NO_XMLDECLARATION_OPTION &&
+			 xmlserialize_version))
+		{
+			initStringInfo(&xmldecl);
+
+			/*
+			 * No need to check the return value of print_xml_decl
+			 * because we are forcing the XML declaration to be included
+			 * by setting 'force_xmldeclaration' to true.
+			 * Therefore, print_xml_decl will always succeed and produce output.
+			 */
+			print_xml_decl(
+				&xmldecl,
+				/*
+				 * Use version from VERSION option (X076, SQL/XML:2023)
+				 * if specified; otherwise use the document's version.
+				 */
+				xmlserialize_version != NULL ? (const xmlChar *)xmlserialize_version : doc->version,
+				/*
+				 * No encoding declaration, since the result is subject to
+				 * encoding conversion.
+				 */
+				0,
+				doc->standalone,
+				true);
+			/*
+			 * We add a trailing newline if the flag INDENT was used,
+			 * otherwise XML declaration and root element will be
+			 * serialized in the same line.
+			 */
+			if (indent)
+				appendStringInfoString(&xmldecl, "\n");
+
+			if (xmlBufferAddHead(buf, (const xmlChar *)xmldecl.data, xmldecl.len) < 0 ||
+				xmlerrcxt->err_occurred)
+				xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
+							"could not add XML declaration");
 		}
 
 		if (xmlSaveClose(ctxt) == -1 || xmlerrcxt->err_occurred)
@@ -868,6 +975,43 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
 #endif
 }
 
+#ifdef USE_LIBXML
+/*
+ * Check the version given with XMLSERIALIZE's VERSION option by letting
+ * libxml2 parse a minimal document that declares it.  libxml2 decides whether
+ * an unexpected version deserves an error or just a warning.  It must also
+ * read back exactly the given string, so that the version cannot carry other
+ * pseudo-attributes into the XML declaration.
+ */
+static void
+xmlserialize_check_version(const char *version)
+{
+	StringInfoData buf;
+	ErrorSaveContext escontext = {T_ErrorSaveContext};
+	xmlDocPtr	doc;
+	bool		valid;
+
+	initStringInfo(&buf);
+	appendStringInfo(&buf, "<?xml version=\"%s\"?><x/>", version);
+
+	doc = xml_parse(cstring_to_text_with_len(buf.data, buf.len),
+					XMLOPTION_DOCUMENT, true, GetDatabaseEncoding(),
+					NULL, (Node *) &escontext);
+
+	valid = (doc != NULL && !escontext.error_occurred &&
+			 doc->version != NULL &&
+			 strcmp((const char *) doc->version, version) == 0);
+
+	if (doc)
+		xmlFreeDoc(doc);
+
+	if (!valid)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid XML version \"%s\"", version)));
+}
+#endif
+
 
 xmltype *
 xmlelement(XmlExpr *xexpr,
@@ -1118,7 +1262,7 @@ xmlroot(xmltype *data, text *version, int standalone)
 	}
 
 	initStringInfo(&buf);
-	print_xml_decl(&buf, orig_version, 0, orig_standalone);
+	print_xml_decl(&buf, orig_version, 0, orig_standalone, false);
 	appendStringInfoString(&buf, str + len);
 
 	return stringinfo_to_xmltype(&buf);
@@ -1623,14 +1767,17 @@ finished:
  * declaration, we must specify a version (XML requires this).
  * Otherwise we only make a declaration if the version is not "1.0",
  * which is the default version specified in SQL:2003.
+ *
+ * If force_xmldeclaration is true, we always make a declaration.
  */
 static bool
 print_xml_decl(StringInfo buf, const xmlChar *version,
-			   pg_enc encoding, int standalone)
+			   pg_enc encoding, int standalone,
+			   bool force_xmldeclaration)
 {
 	if ((version && strcmp((const char *) version, PG_XML_DEFAULT_VERSION) != 0)
 		|| (encoding && encoding != PG_UTF8)
-		|| standalone != -1)
+		|| standalone != -1 || force_xmldeclaration)
 	{
 		appendStringInfoString(buf, "<?xml");
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0debcd193ab..3f146979c74 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -877,6 +877,9 @@ typedef struct XmlSerialize
 	Node	   *expr;
 	TypeName   *typeName;
 	bool		indent;			/* [NO] INDENT */
+	const char *version;		/* VERSION 'xmlversion' */
+	XmlSerializeDeclarationOption xmldeclaration;	/* INCLUDING or EXCLUDING
+													 * XMLDECLARATION */
 	ParseLoc	location;		/* token location, or -1 if unknown */
 } XmlSerialize;
 
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 726c186c7d2..3d39ca1cf42 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1605,6 +1605,13 @@ typedef enum XmlOptionType
 	XMLOPTION_CONTENT,
 } XmlOptionType;
 
+typedef enum XmlSerializeDeclarationOption
+{
+	XMLSERIALIZE_NO_XMLDECLARATION_OPTION,	/* neither option given */
+	XMLSERIALIZE_INCLUDING_XMLDECLARATION,	/* INCLUDING XMLDECLARATION */
+	XMLSERIALIZE_EXCLUDING_XMLDECLARATION,	/* EXCLUDING XMLDECLARATION */
+} XmlSerializeDeclarationOption;
+
 typedef struct XmlExpr
 {
 	Expr		xpr;
@@ -1622,6 +1629,10 @@ typedef struct XmlExpr
 	XmlOptionType xmloption pg_node_attr(query_jumble_ignore);
 	/* INDENT option for XMLSERIALIZE */
 	bool		indent;
+	/* INCLUDING or EXCLUDING XMLDECLARATION option for XMLSERIALIZE */
+	XmlSerializeDeclarationOption xmldeclaration;
+	/* VERSION option for XMLSERIALIZE */
+	const char *version;
 	/* target type/typmod for XMLSERIALIZE */
 	Oid			type pg_node_attr(query_jumble_ignore);
 	int32		typmod pg_node_attr(query_jumble_ignore);
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 53ae96c0399..4d86d131526 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -512,6 +512,7 @@ PG_KEYWORD("write", WRITE, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("xml", XML_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("xmlattributes", XMLATTRIBUTES, COL_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("xmlconcat", XMLCONCAT, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("xmldeclaration", XMLDECLARATION, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("xmlelement", XMLELEMENT, COL_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("xmlexists", XMLEXISTS, COL_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("xmlforest", XMLFOREST, COL_NAME_KEYWORD, BARE_LABEL)
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index ca266f448d6..da572e32dd2 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -78,7 +78,8 @@ extern xmltype *xmlpi(const char *target, text *arg, bool arg_is_null, bool *res
 extern xmltype *xmlroot(xmltype *data, text *version, int standalone);
 extern bool xml_is_document(xmltype *arg);
 extern text *xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg,
-									bool indent);
+									bool indent, XmlSerializeDeclarationOption xmldeclaration,
+									const char *version);
 extern char *escape_xml(const char *str);
 
 extern char *map_sql_identifier_to_xml_name(const char *ident, bool fully_escaped, bool escape_period);
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index 6998745ee03..7080317a2c6 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -682,6 +682,448 @@ SELECT xmlserialize(CONTENT  'text node<foo>    <bar></bar>   </foo>' AS text IN
  </foo>
 (1 row)
 
+-- 'including xmldeclaration' and 'excluding xmldeclaration'(DOCUMENT)
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+                 xmlserialize                  
+-----------------------------------------------
+ <?xml version="1.0"?><foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION);
+       xmlserialize       
+--------------------------
+ <foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+                 xmlserialize                  
+-----------------------------------------------
+ <?xml version="1.0"?><foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION);
+       xmlserialize       
+--------------------------
+ <foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?><foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+                         xmlserialize                          
+---------------------------------------------------------------
+ <?xml version="1.0" standalone="no"?><foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+                          xmlserialize                          
+----------------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?><foo><bar>42</bar></foo>
+(1 row)
+
+-- 'including xmldeclaration' and 'excluding xmldeclaration'(CONTENT)
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+                   xmlserialize                   
+--------------------------------------------------
+ <?xml version="1.0"?>txt<foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION);
+        xmlserialize         
+-----------------------------
+ txt<foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?>txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+                   xmlserialize                   
+--------------------------------------------------
+ <?xml version="1.0"?>txt<foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?>txt<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION);
+        xmlserialize         
+-----------------------------
+ txt<foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?>txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+                           xmlserialize                           
+------------------------------------------------------------------
+ <?xml version="1.0" standalone="no"?>txt<foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+                           xmlserialize                            
+-------------------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?>txt<foo><bar>42</bar></foo>
+(1 row)
+
+-- 'indent' + 'including xmldeclaration' and 'excluding xmldeclaration' (DOCUMENT)
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+     xmlserialize      
+-----------------------
+ <?xml version="1.0"?>+
+ <foo>                +
+   <bar>42</bar>      +
+ </foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION INDENT);
+  xmlserialize   
+-----------------
+ <foo>          +
+   <bar>42</bar>+
+ </foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+     xmlserialize      
+-----------------------
+ <?xml version="1.0"?>+
+ <foo>                +
+   <bar>42</bar>      +
+ </foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION INDENT);
+  xmlserialize   
+-----------------
+ <foo>          +
+   <bar>42</bar>+
+ </foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?><foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+             xmlserialize              
+---------------------------------------
+ <?xml version="1.0" standalone="no"?>+
+ <foo>                                +
+   <bar>42</bar>                      +
+ </foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+              xmlserialize              
+----------------------------------------
+ <?xml version="1.0" standalone="yes"?>+
+ <foo>                                 +
+   <bar>42</bar>                       +
+ </foo>
+(1 row)
+
+-- 'indent' + 'including xmldeclaration' and 'excluding xmldeclaration'(CONTENT)
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+     xmlserialize      
+-----------------------
+ <?xml version="1.0"?>+
+ txt                  +
+ <foo>                +
+   <bar>42</bar>      +
+ </foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION INDENT);
+  xmlserialize   
+-----------------
+ txt            +
+ <foo>          +
+   <bar>42</bar>+
+ </foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?>txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+     xmlserialize      
+-----------------------
+ <?xml version="1.0"?>+
+ txt                  +
+ <foo>                +
+   <bar>42</bar>      +
+ </foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?>txt<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION INDENT);
+  xmlserialize   
+-----------------
+ txt            +
+ <foo>          +
+   <bar>42</bar>+
+ </foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?>txt<foo><bar>42</bar></foo>'::xml AS text  INCLUDING XMLDECLARATION INDENT);
+             xmlserialize              
+---------------------------------------
+ <?xml version="1.0" standalone="no"?>+
+ txt                                  +
+ <foo>                                +
+   <bar>42</bar>                      +
+ </foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+              xmlserialize              
+----------------------------------------
+ <?xml version="1.0" standalone="yes"?>+
+ txt                                   +
+ <foo>                                 +
+   <bar>42</bar>                       +
+ </foo>
+(1 row)
+
+-- 'version' + 'including xmldeclaration' and 'excluding xmldeclaration'(DOCUMENT)
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.0');
+       xmlserialize       
+--------------------------
+ <foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INCLUDING XMLDECLARATION);
+                 xmlserialize                  
+-----------------------------------------------
+ <?xml version="1.0"?><foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?><foo><bar>42</bar></foo>'::xml AS text VERSION '1.0');
+                         xmlserialize                          
+---------------------------------------------------------------
+ <?xml version="1.0" standalone="no"?><foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INCLUDING XMLDECLARATION);
+                          xmlserialize                          
+----------------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?><foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' EXCLUDING XMLDECLARATION);
+WARNING:  line 1: Unsupported version '1.1'
+<?xml version="1.1"?><x/>
+                   ^
+       xmlserialize       
+--------------------------
+ <foo><bar>42</bar></foo>
+(1 row)
+
+-- 'version' + 'including xmldeclaration' and 'excluding xmldeclaration'(CONTENT)
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1');
+WARNING:  line 1: Unsupported version '1.1'
+<?xml version="1.1"?><x/>
+                   ^
+        xmlserialize         
+-----------------------------
+ txt<foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INCLUDING XMLDECLARATION);
+WARNING:  line 1: Unsupported version '1.1'
+<?xml version="1.1"?><x/>
+                   ^
+                   xmlserialize                   
+--------------------------------------------------
+ <?xml version="1.1"?>txt<foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1');
+WARNING:  line 1: Unsupported version '1.1'
+<?xml version="1.1"?><x/>
+                   ^
+                           xmlserialize                            
+-------------------------------------------------------------------
+ <?xml version="1.1" standalone="yes"?>txt<foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?>txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INCLUDING XMLDECLARATION);
+WARNING:  line 1: Unsupported version '1.1'
+<?xml version="1.1"?><x/>
+                   ^
+                           xmlserialize                           
+------------------------------------------------------------------
+ <?xml version="1.1" standalone="no"?>txt<foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?>txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' EXCLUDING XMLDECLARATION);
+WARNING:  line 1: Unsupported version '1.1'
+<?xml version="1.1"?><x/>
+                   ^
+        xmlserialize         
+-----------------------------
+ txt<foo><bar>42</bar></foo>
+(1 row)
+
+-- 'version' + 'indent' (DOCUMENT)
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INDENT);
+  xmlserialize   
+-----------------
+ <foo>          +
+   <bar>42</bar>+
+ </foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?><foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INDENT);
+             xmlserialize              
+---------------------------------------
+ <?xml version="1.0" standalone="no"?>+
+ <foo>                                +
+   <bar>42</bar>                      +
+ </foo>
+(1 row)
+
+-- 'indent' + 'version' (CONTENT)
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INDENT);
+WARNING:  line 1: Unsupported version '1.1'
+<?xml version="1.1"?><x/>
+                   ^
+  xmlserialize   
+-----------------
+ txt            +
+ <foo>          +
+   <bar>42</bar>+
+ </foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INDENT);
+WARNING:  line 1: Unsupported version '1.1'
+<?xml version="1.1"?><x/>
+                   ^
+              xmlserialize              
+----------------------------------------
+ <?xml version="1.1" standalone="yes"?>+
+ txt                                   +
+ <foo>                                 +
+   <bar>42</bar>                       +
+ </foo>
+(1 row)
+
+-- 'indent' + 'version' + 'including xmldeclaration' and 'excluding xmldeclaration'(DOCUMENT)
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INCLUDING XMLDECLARATION INDENT);
+     xmlserialize      
+-----------------------
+ <?xml version="1.0"?>+
+ <foo>                +
+   <bar>42</bar>      +
+ </foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INCLUDING XMLDECLARATION INDENT);
+              xmlserialize              
+----------------------------------------
+ <?xml version="1.0" standalone="yes"?>+
+ <foo>                                 +
+   <bar>42</bar>                       +
+ </foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' EXCLUDING XMLDECLARATION INDENT);
+  xmlserialize   
+-----------------
+ <foo>          +
+   <bar>42</bar>+
+ </foo>
+(1 row)
+
+-- 'indent' + 'version' + 'including xmldeclaration' and 'excluding xmldeclaration'(CONTENT)
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INCLUDING XMLDECLARATION INDENT);
+WARNING:  line 1: Unsupported version '1.1'
+<?xml version="1.1"?><x/>
+                   ^
+     xmlserialize      
+-----------------------
+ <?xml version="1.1"?>+
+ txt                  +
+ <foo>                +
+   <bar>42</bar>      +
+ </foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INCLUDING XMLDECLARATION INDENT);
+WARNING:  line 1: Unsupported version '1.1'
+<?xml version="1.1"?><x/>
+                   ^
+              xmlserialize              
+----------------------------------------
+ <?xml version="1.1" standalone="yes"?>+
+ txt                                   +
+ <foo>                                 +
+   <bar>42</bar>                       +
+ </foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?>txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' EXCLUDING XMLDECLARATION INDENT);
+WARNING:  line 1: Unsupported version '1.1'
+<?xml version="1.1"?><x/>
+                   ^
+  xmlserialize   
+-----------------
+ txt            +
+ <foo>          +
+   <bar>42</bar>+
+ </foo>
+(1 row)
+
+-- without INDENT, the value itself is not reserialized
+SELECT xmlserialize(DOCUMENT '<foo a=''1''><b></b>&#65;</foo>' AS text INCLUDING XMLDECLARATION);
+                    xmlserialize                    
+----------------------------------------------------
+ <?xml version="1.0"?><foo a='1'><b></b>&#65;</foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<?xml version="1.0"?><foo a=''1''><b></b></foo>' AS text EXCLUDING XMLDECLARATION);
+       xmlserialize       
+--------------------------
+ <foo a='1'><b></b></foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<!DOCTYPE foo><foo/>' AS text EXCLUDING XMLDECLARATION);
+     xmlserialize     
+----------------------
+ <!DOCTYPE foo><foo/>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT E'<?xml version="1.0"?>\n<foo/>' AS text EXCLUDING XMLDECLARATION);
+ xmlserialize 
+--------------
+ <foo/>
+(1 row)
+
+-- 'version' + 'including xmldeclaration' warning and error messages
+\set VERBOSITY terse
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INCLUDING XMLDECLARATION);
+WARNING:  line 1: Unsupported version '1.1'
+<?xml version="1.1"?><x/>
+                   ^
+                 xmlserialize                  
+-----------------------------------------------
+ <?xml version="1.1"?><foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '2.0' INCLUDING XMLDECLARATION);
+ERROR:  invalid XML version "2.0"
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION ''    INCLUDING XMLDECLARATION);
+ERROR:  invalid XML version ""
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '   ' INCLUDING XMLDECLARATION);
+ERROR:  invalid XML version "   "
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION 'foo' INCLUDING XMLDECLARATION);
+ERROR:  invalid XML version "foo"
+SELECT xmlserialize(CONTENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INCLUDING XMLDECLARATION);
+WARNING:  line 1: Unsupported version '1.1'
+<?xml version="1.1"?><x/>
+                   ^
+                 xmlserialize                  
+-----------------------------------------------
+ <?xml version="1.1"?><foo><bar>42</bar></foo>
+(1 row)
+
+SELECT xmlserialize(CONTENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '2.0' INCLUDING XMLDECLARATION);
+ERROR:  invalid XML version "2.0"
+SELECT xmlserialize(CONTENT '<foo><bar>42</bar></foo>'::xml AS text VERSION ''    INCLUDING XMLDECLARATION);
+ERROR:  invalid XML version ""
+SELECT xmlserialize(CONTENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '   ' INCLUDING XMLDECLARATION);
+ERROR:  invalid XML version "   "
+SELECT xmlserialize(CONTENT '<foo><bar>42</bar></foo>'::xml AS text VERSION 'foo' INCLUDING XMLDECLARATION);
+ERROR:  invalid XML version "foo"
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '2.0' EXCLUDING XMLDECLARATION);
+ERROR:  invalid XML version "2.0"
+SELECT xmlserialize(CONTENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.0" standalone="yes' INCLUDING XMLDECLARATION);
+ERROR:  invalid XML version "1.0" standalone="yes"
+\set VERBOSITY default
 -- check some encoding-dependent behaviors
 -- (this is mostly checking for libxml2 bugs, not our bugs...)
 DO $$
@@ -872,23 +1314,41 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10));
 CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text);
 CREATE VIEW xmlview10 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text indent);
 CREATE VIEW xmlview11 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS character varying no indent);
+CREATE VIEW xmlview12 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text including xmldeclaration);
+CREATE VIEW xmlview13 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text excluding xmldeclaration);
+CREATE VIEW xmlview14 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text including xmldeclaration indent);
+CREATE VIEW xmlview15 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text excluding xmldeclaration indent);
+CREATE VIEW xmlview16 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text including xmldeclaration no indent);
+CREATE VIEW xmlview17 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text excluding xmldeclaration no indent);
+CREATE VIEW xmlview18 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text version '1.0' including xmldeclaration indent);
+CREATE VIEW xmlview19 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text version '1.0' including xmldeclaration no indent);
+CREATE VIEW xmlview20 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text version '1.0');
 SELECT table_name, view_definition FROM information_schema.views
   WHERE table_name LIKE 'xmlview%' ORDER BY 1;
- table_name |                                                            view_definition                                                            
-------------+---------------------------------------------------------------------------------------------------------------------------------------
+ table_name |                                                              view_definition                                                               
+------------+--------------------------------------------------------------------------------------------------------------------------------------------
  xmlview1   |  SELECT xmlcomment('test'::text) AS xmlcomment;
  xmlview10  |  SELECT XMLSERIALIZE(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text INDENT) AS "xmlserialize";
  xmlview11  |  SELECT (XMLSERIALIZE(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS character varying NO INDENT))::character varying AS "xmlserialize";
+ xmlview12  |  SELECT XMLSERIALIZE(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION NO INDENT) AS "xmlserialize";
+ xmlview13  |  SELECT XMLSERIALIZE(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION NO INDENT) AS "xmlserialize";
+ xmlview14  |  SELECT XMLSERIALIZE(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT) AS "xmlserialize";
+ xmlview15  |  SELECT XMLSERIALIZE(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION INDENT) AS "xmlserialize";
+ xmlview16  |  SELECT XMLSERIALIZE(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION NO INDENT) AS "xmlserialize";
+ xmlview17  |  SELECT XMLSERIALIZE(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION NO INDENT) AS "xmlserialize";
+ xmlview18  |  SELECT XMLSERIALIZE(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INCLUDING XMLDECLARATION INDENT) AS "xmlserialize";
+ xmlview19  |  SELECT XMLSERIALIZE(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INCLUDING XMLDECLARATION NO INDENT) AS "xmlserialize";
  xmlview2   |  SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat";
+ xmlview20  |  SELECT XMLSERIALIZE(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' NO INDENT) AS "xmlserialize";
  xmlview3   |  SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement";
- xmlview4   |  SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement"                                +
+ xmlview4   |  SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement"                                     +
             |    FROM emp;
  xmlview5   |  SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse";
  xmlview6   |  SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi";
  xmlview7   |  SELECT XMLROOT('<foo/>'::xml, VERSION NO VALUE, STANDALONE YES) AS "xmlroot";
  xmlview8   |  SELECT (XMLSERIALIZE(CONTENT 'good'::xml AS character(10) NO INDENT))::character(10) AS "xmlserialize";
  xmlview9   |  SELECT XMLSERIALIZE(CONTENT 'good'::xml AS text NO INDENT) AS "xmlserialize";
-(11 rows)
+(20 rows)
 
 -- Text XPath expressions evaluation
 SELECT xpath('/value', data) FROM xmltest;
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index 0581d21b03f..cc60ea5d72a 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -179,6 +179,82 @@ SELECT xmlserialize(CONTENT  '<foo><bar><val x="y">42</val></bar></foo>' AS text
 SELECT xmlserialize(DOCUMENT '<foo>   <bar></bar>    </foo>' AS text INDENT);
 SELECT xmlserialize(CONTENT  'text node<foo>    <bar></bar>   </foo>' AS text INDENT);
 
+-- 'including xmldeclaration' and 'excluding xmldeclaration'(DOCUMENT)
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION);
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION);
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?><foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+-- 'including xmldeclaration' and 'excluding xmldeclaration'(CONTENT)
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?>txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?>txt<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?>txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION);
+-- 'indent' + 'including xmldeclaration' and 'excluding xmldeclaration' (DOCUMENT)
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?><foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+-- 'indent' + 'including xmldeclaration' and 'excluding xmldeclaration'(CONTENT)
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?>txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?>txt<foo><bar>42</bar></foo>'::xml AS text EXCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?>txt<foo><bar>42</bar></foo>'::xml AS text  INCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>txt<foo><bar>42</bar></foo>'::xml AS text INCLUDING XMLDECLARATION INDENT);
+-- 'version' + 'including xmldeclaration' and 'excluding xmldeclaration'(DOCUMENT)
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.0');
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INCLUDING XMLDECLARATION);
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?><foo><bar>42</bar></foo>'::xml AS text VERSION '1.0');
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INCLUDING XMLDECLARATION);
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' EXCLUDING XMLDECLARATION);
+-- 'version' + 'including xmldeclaration' and 'excluding xmldeclaration'(CONTENT)
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1');
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1');
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?>txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?>txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' EXCLUDING XMLDECLARATION);
+-- 'version' + 'indent' (DOCUMENT)
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INDENT);
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="no"?><foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INDENT);
+-- 'indent' + 'version' (CONTENT)
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INDENT);
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INDENT);
+-- 'indent' + 'version' + 'including xmldeclaration' and 'excluding xmldeclaration'(DOCUMENT)
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' INCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar>42</bar></foo>'::xml AS text VERSION '1.0' EXCLUDING XMLDECLARATION INDENT);
+-- 'indent' + 'version' + 'including xmldeclaration' and 'excluding xmldeclaration'(CONTENT)
+SELECT xmlserialize(CONTENT 'txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INCLUDING XMLDECLARATION INDENT);
+SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?>txt<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' EXCLUDING XMLDECLARATION INDENT);
+-- without INDENT, the value itself is not reserialized
+SELECT xmlserialize(DOCUMENT '<foo a=''1''><b></b>&#65;</foo>' AS text INCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<?xml version="1.0"?><foo a=''1''><b></b></foo>' AS text EXCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<!DOCTYPE foo><foo/>' AS text EXCLUDING XMLDECLARATION);
+SELECT xmlserialize(DOCUMENT E'<?xml version="1.0"?>\n<foo/>' AS text EXCLUDING XMLDECLARATION);
+
+-- 'version' + 'including xmldeclaration' warning and error messages
+\set VERBOSITY terse
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INCLUDING XMLDECLARATION);
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '2.0' INCLUDING XMLDECLARATION);
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION ''    INCLUDING XMLDECLARATION);
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '   ' INCLUDING XMLDECLARATION);
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION 'foo' INCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.1' INCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '2.0' INCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<foo><bar>42</bar></foo>'::xml AS text VERSION ''    INCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '   ' INCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<foo><bar>42</bar></foo>'::xml AS text VERSION 'foo' INCLUDING XMLDECLARATION);
+SELECT xmlserialize(DOCUMENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '2.0' EXCLUDING XMLDECLARATION);
+SELECT xmlserialize(CONTENT '<foo><bar>42</bar></foo>'::xml AS text VERSION '1.0" standalone="yes' INCLUDING XMLDECLARATION);
+\set VERBOSITY default
+
 -- check some encoding-dependent behaviors
 -- (this is mostly checking for libxml2 bugs, not our bugs...)
 DO $$
@@ -267,6 +343,15 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10));
 CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text);
 CREATE VIEW xmlview10 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text indent);
 CREATE VIEW xmlview11 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS character varying no indent);
+CREATE VIEW xmlview12 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text including xmldeclaration);
+CREATE VIEW xmlview13 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text excluding xmldeclaration);
+CREATE VIEW xmlview14 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text including xmldeclaration indent);
+CREATE VIEW xmlview15 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text excluding xmldeclaration indent);
+CREATE VIEW xmlview16 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text including xmldeclaration no indent);
+CREATE VIEW xmlview17 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text excluding xmldeclaration no indent);
+CREATE VIEW xmlview18 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text version '1.0' including xmldeclaration indent);
+CREATE VIEW xmlview19 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text version '1.0' including xmldeclaration no indent);
+CREATE VIEW xmlview20 AS SELECT xmlserialize(document '<foo><bar>42</bar></foo>' AS text version '1.0');
 
 SELECT table_name, view_definition FROM information_schema.views
   WHERE table_name LIKE 'xmlview%' ORDER BY 1;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index a7a8dbdd23f..8f234b8fbcf 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3532,6 +3532,7 @@ XmlExpr
 XmlExprOp
 XmlOptionType
 XmlSerialize
+XmlSerializeDeclarationOption
 XmlTableBuilderData
 YYLTYPE
 YYSTYPE
-- 
2.56.0

