From 5d522d8ec1bd01731d0f75a4163f9a8ad435bee6 Mon Sep 17 00:00:00 2001 From: Jim Jones Date: Fri, 10 Mar 2023 13:47:16 +0100 Subject: [PATCH v21] Add pretty-printed XML output option This patch implements the XML/SQL:2011 feature 'X069, XMLSERIALIZE: INDENT.' It adds the options INDENT and NO INDENT (default) to the existing xmlserialize function. It uses the indentation feature of xmlSaveToBuffer from libxml2 to indent XML strings - XML_SAVE_FORMAT. Although the INDENT feature is designed to work with xml strings of type DOCUMENT, this implementation also allows the usage of CONTENT type strings as long as it contains a well-formed singly-rooted xml - note the XMLOPTION_DOCUMENT in the xml_parse call. This patch also includes documentation, regression tests and their three possible output files xml.out, xml_1.out and xml_2.out. --- doc/src/sgml/datatype.sgml | 8 +- src/backend/catalog/sql_features.txt | 2 +- src/backend/executor/execExprInterp.c | 9 +- src/backend/parser/gram.y | 14 ++- src/backend/parser/parse_expr.c | 1 + src/backend/utils/adt/xml.c | 74 +++++++++++++++ src/include/nodes/parsenodes.h | 1 + src/include/nodes/primnodes.h | 4 +- src/include/parser/kwlist.h | 1 + src/include/utils/xml.h | 1 + src/test/regress/expected/xml.out | 129 ++++++++++++++++++++++++++ src/test/regress/expected/xml_1.out | 85 +++++++++++++++++ src/test/regress/expected/xml_2.out | 129 ++++++++++++++++++++++++++ src/test/regress/sql/xml.sql | 29 ++++++ 14 files changed, 478 insertions(+), 9 deletions(-) diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index 467b49b199..14cbbdd71f 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -4460,14 +4460,18 @@ xml 'bar' xml, uses the function xmlserialize:xmlserialize -XMLSERIALIZE ( { DOCUMENT | CONTENT } value AS type ) +XMLSERIALIZE ( { DOCUMENT | CONTENT } value AS type [ [NO] INDENT ] ) type can be character, character varying, or text (or an alias for one of those). Again, according to the SQL standard, this is the only way to convert between type xml and character types, but PostgreSQL also allows - you to simply cast the value. + you to simply cast the value. The option INDENT allows to + indent the serialized xml output - the default is NO INDENT. + It is designed to indent XML strings of type DOCUMENT, but it can also + be used with CONTENT as long as value + contains a well-formed singly-rooted XML. diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt index 0fb9ab7533..bb4c135a7f 100644 --- a/src/backend/catalog/sql_features.txt +++ b/src/backend/catalog/sql_features.txt @@ -621,7 +621,7 @@ X061 XMLParse: character string input and DOCUMENT option YES X065 XMLParse: binary string input and CONTENT option NO X066 XMLParse: binary string input and DOCUMENT option NO X068 XMLSerialize: BOM NO -X069 XMLSerialize: INDENT NO +X069 XMLSerialize: INDENT YES X070 XMLSerialize: character string serialization and CONTENT option YES X071 XMLSerialize: character string serialization and DOCUMENT option YES X072 XMLSerialize: character string serialization YES diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 19351fe34b..3dcd15d5f0 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -3829,6 +3829,7 @@ ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op) { Datum *argvalue = op->d.xmlexpr.argvalue; bool *argnull = op->d.xmlexpr.argnull; + text *result; /* argument type is known to be xml */ Assert(list_length(xexpr->args) == 1); @@ -3837,8 +3838,12 @@ ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op) return; value = argvalue[0]; - *op->resvalue = PointerGetDatum(xmltotext_with_xmloption(DatumGetXmlP(value), - xexpr->xmloption)); + result = xmltotext_with_xmloption(DatumGetXmlP(value), + xexpr->xmloption); + if (xexpr->indent) + result = xmlserialize_indent(result); + + *op->resvalue = PointerGetDatum(result); *op->resnull = false; } break; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a0138382a1..efe88ccf9d 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -613,7 +613,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type xml_root_version opt_xml_root_standalone %type xmlexists_argument %type document_or_content -%type xml_whitespace_option +%type xml_indent_option xml_whitespace_option %type xmltable_column_list xmltable_column_option_list %type xmltable_column_el %type xmltable_column_option_el @@ -702,7 +702,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); HANDLER HAVING HEADER_P HOLD HOUR_P IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P INCLUDE - INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P + INCLUDING INCREMENT INDENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION @@ -15532,13 +15532,14 @@ func_expr_common_subexpr: $$ = makeXmlExpr(IS_XMLROOT, NULL, NIL, list_make3($3, $5, $6), @1); } - | XMLSERIALIZE '(' document_or_content a_expr AS SimpleTypename ')' + | XMLSERIALIZE '(' document_or_content a_expr AS SimpleTypename xml_indent_option ')' { XmlSerialize *n = makeNode(XmlSerialize); n->xmloption = $3; n->expr = $4; n->typeName = $6; + n->indent = $7; n->location = @1; $$ = (Node *) n; } @@ -15592,6 +15593,11 @@ document_or_content: DOCUMENT_P { $$ = XMLOPTION_DOCUMENT; } | CONTENT_P { $$ = XMLOPTION_CONTENT; } ; +xml_indent_option: INDENT { $$ = true; } + | NO INDENT { $$ = false; } + | /*EMPTY*/ { $$ = false; } + ; + xml_whitespace_option: PRESERVE WHITESPACE_P { $$ = true; } | STRIP_P WHITESPACE_P { $$ = false; } | /*EMPTY*/ { $$ = false; } @@ -16828,6 +16834,7 @@ unreserved_keyword: | INCLUDE | INCLUDING | INCREMENT + | INDENT | INDEX | INDEXES | INHERIT @@ -17384,6 +17391,7 @@ bare_label_keyword: | INCLUDE | INCLUDING | INCREMENT + | INDENT | INDEX | INDEXES | INHERIT diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 78221d2e0f..2331417552 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -2331,6 +2331,7 @@ transformXmlSerialize(ParseState *pstate, XmlSerialize *xs) typenameTypeIdAndMod(pstate, xs->typeName, &targetType, &targetTypmod); xexpr->xmloption = xs->xmloption; + xexpr->indent = xs->indent; 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/xml.c b/src/backend/utils/adt/xml.c index 079bcb1208..174831e6b4 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -631,6 +632,79 @@ xmltotext_with_xmloption(xmltype *data, XmlOptionType xmloption_arg) } +text * +xmlserialize_indent(text *data) +{ +#ifdef USE_LIBXML + text *result; + xmlDocPtr doc; + xmlSaveCtxtPtr ctxt = NULL; + xmlBufferPtr buf = NULL; + xmlChar *encodingStr = NULL; + xmlChar *version; + PgXmlErrorContext *xmlerrcxt; + int encoding; + + parse_xml_decl(xml_text2xmlChar(data), NULL, &version, &encodingStr, NULL); + encoding = encodingStr ? xmlChar_to_encoding(encodingStr) : PG_UTF8; + + doc = xml_parse(data, XMLOPTION_DOCUMENT, false, + encoding, NULL); + Assert(doc); + + xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL); + + PG_TRY(); + { + buf = xmlBufferCreate(); + if (buf == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, + "could not allocate xmlBuffer"); + + if(!version) + /* Reformat with indenting requested without XML declaration */ + ctxt = xmlSaveToBuffer(buf, (const char *) encodingStr, + XML_SAVE_NO_DECL|XML_SAVE_FORMAT); + else + ctxt = xmlSaveToBuffer(buf, (const char *) encodingStr, + XML_SAVE_FORMAT); + + if (ctxt == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, + "could not allocate parser context"); + + xmlSaveDoc(ctxt, doc); + xmlSaveClose(ctxt); + } + PG_CATCH(); + { + if (buf) + xmlBufferFree(buf); + if(doc) + xmlFreeDoc(doc); + if(ctxt) + xmlSaveClose(ctxt); + + pg_xml_done(xmlerrcxt, true); + + PG_RE_THROW(); + } + PG_END_TRY(); + + pg_xml_done(xmlerrcxt, false); + xmlFreeDoc(doc); + + result = (text *) xmlBuffer_to_xmltype(buf); + xmlBufferFree(buf); + + return result; +#else + NO_XML_SUPPORT(); + return NULL; +#endif +} + + xmltype * xmlelement(XmlExpr *xexpr, Datum *named_argvalue, bool *named_argnull, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index f7d7f10f7d..fc5b89a698 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -841,6 +841,7 @@ typedef struct XmlSerialize XmlOptionType xmloption; /* DOCUMENT or CONTENT */ Node *expr; TypeName *typeName; + bool indent; /* [NO] INDENT */ int location; /* token location, or -1 if unknown */ } XmlSerialize; diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index b4292253cc..2263dab8a1 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1461,7 +1461,7 @@ typedef enum XmlExprOp IS_XMLPARSE, /* XMLPARSE(text, is_doc, preserve_ws) */ IS_XMLPI, /* XMLPI(name [, args]) */ IS_XMLROOT, /* XMLROOT(xml, version, standalone) */ - IS_XMLSERIALIZE, /* XMLSERIALIZE(is_document, xmlval) */ + IS_XMLSERIALIZE, /* XMLSERIALIZE(is_document, xmlval, indent) */ IS_DOCUMENT /* xmlval IS DOCUMENT */ } XmlExprOp; @@ -1486,6 +1486,8 @@ typedef struct XmlExpr List *args; /* DOCUMENT or CONTENT */ XmlOptionType xmloption pg_node_attr(query_jumble_ignore); + /* INDENT option for XMLSERIALIZE */ + bool indent; /* 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 bb36213e6f..753e9ee174 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -205,6 +205,7 @@ PG_KEYWORD("in", IN_P, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("include", INCLUDE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("including", INCLUDING, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("increment", INCREMENT, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("indent", INDENT, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("index", INDEX, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("indexes", INDEXES, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("inherit", INHERIT, UNRESERVED_KEYWORD, BARE_LABEL) diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h index 311da06cd6..a1dfe4c631 100644 --- a/src/include/utils/xml.h +++ b/src/include/utils/xml.h @@ -78,6 +78,7 @@ 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_xmloption(xmltype *data, XmlOptionType xmloption_arg); +extern text *xmlserialize_indent(text *data); 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 ad852dc2f7..5f7b8f4827 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -486,6 +486,135 @@ SELECT xmlserialize(content 'good' as char(10)); SELECT xmlserialize(document 'bad' as text); ERROR: not an XML document +-- indent +SELECT xmlserialize(DOCUMENT '42' AS text INDENT); + xmlserialize +------------------------- + + + + + 42+ + + + + + +(1 row) + +SELECT xmlserialize(CONTENT '42' AS text INDENT); + xmlserialize +------------------------- + + + + + 42+ + + + + + +(1 row) + +-- no indent +SELECT xmlserialize(DOCUMENT '42' AS text NO INDENT); + xmlserialize +------------------------------------------- + 42 +(1 row) + +SELECT xmlserialize(CONTENT '42' AS text NO INDENT); + xmlserialize +------------------------------------------- + 42 +(1 row) + +\set VERBOSITY terse +-- indent malformed xml +SELECT xmlserialize(DOCUMENT '42' AS text INDENT); +ERROR: not an XML document +SELECT xmlserialize(CONTENT '42' AS text INDENT); +ERROR: invalid XML document +-- indent empty string +SELECT xmlserialize(DOCUMENT '' AS text INDENT); +ERROR: not an XML document +SELECT xmlserialize(CONTENT '' AS text INDENT); +ERROR: invalid XML document +-- whitespaces +SELECT xmlserialize(DOCUMENT ' ' AS text INDENT); +ERROR: not an XML document +SELECT xmlserialize(CONTENT ' ' AS text INDENT); +ERROR: invalid XML document +\set VERBOSITY default +-- indent null +SELECT xmlserialize(DOCUMENT NULL AS text INDENT); + xmlserialize +-------------- + +(1 row) + +SELECT xmlserialize(CONTENT NULL AS text INDENT); + xmlserialize +-------------- + +(1 row) + +-- indent encoding="ISO-8859-1" +SELECT xmlserialize(DOCUMENT '42' AS text INDENT); + xmlserialize +--------------------------------------------- + + + + + + + 42 + + + + + + +(1 row) + +SELECT xmlserialize(CONTENT '42' AS text INDENT); + xmlserialize +--------------------------------------------- + + + + + + + 42 + + + + + + +(1 row) + +-- indent encoding="UTF-8" +SELECT xmlserialize(DOCUMENT '73' AS text INDENT); + xmlserialize +---------------------------------------- + + + + + + + 73 + + + + + + +(1 row) + +SELECT xmlserialize(CONTENT '73' AS text INDENT); + xmlserialize +---------------------------------------- + + + + + + + 73 + + + + + + +(1 row) + +-- 'no indent' = not using 'no indent' +SELECT xmlserialize(DOCUMENT '42' AS text) = xmlserialize(DOCUMENT '42' AS text NO INDENT); + ?column? +---------- + t +(1 row) + +SELECT xmlserialize(CONTENT '42' AS text) = xmlserialize(CONTENT '42' AS text NO INDENT); + ?column? +---------- + t +(1 row) + SELECT xml 'bar' IS DOCUMENT; ?column? ---------- diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out index 70fe34a04f..154f1e5297 100644 --- a/src/test/regress/expected/xml_1.out +++ b/src/test/regress/expected/xml_1.out @@ -309,6 +309,91 @@ ERROR: unsupported XML feature LINE 1: SELECT xmlserialize(document 'bad' as text); ^ DETAIL: This functionality requires the server to be built with libxml support. +-- indent +SELECT xmlserialize(DOCUMENT '42' AS text INDENT); +ERROR: unsupported XML feature +LINE 1: SELECT xmlserialize(DOCUMENT '42<... + ^ +DETAIL: This functionality requires the server to be built with libxml support. +SELECT xmlserialize(CONTENT '42' AS text INDENT); +ERROR: unsupported XML feature +LINE 1: SELECT xmlserialize(CONTENT '42<... + ^ +DETAIL: This functionality requires the server to be built with libxml support. +-- no indent +SELECT xmlserialize(DOCUMENT '42' AS text NO INDENT); +ERROR: unsupported XML feature +LINE 1: SELECT xmlserialize(DOCUMENT '42<... + ^ +DETAIL: This functionality requires the server to be built with libxml support. +SELECT xmlserialize(CONTENT '42' AS text NO INDENT); +ERROR: unsupported XML feature +LINE 1: SELECT xmlserialize(CONTENT '42<... + ^ +DETAIL: This functionality requires the server to be built with libxml support. +\set VERBOSITY terse +-- indent malformed xml +SELECT xmlserialize(DOCUMENT '42' AS text INDENT); +ERROR: unsupported XML feature at character 30 +SELECT xmlserialize(CONTENT '42' AS text INDENT); +ERROR: unsupported XML feature at character 30 +-- indent empty string +SELECT xmlserialize(DOCUMENT '' AS text INDENT); +ERROR: unsupported XML feature at character 30 +SELECT xmlserialize(CONTENT '' AS text INDENT); +ERROR: unsupported XML feature at character 30 +-- whitespaces +SELECT xmlserialize(DOCUMENT ' ' AS text INDENT); +ERROR: unsupported XML feature at character 30 +SELECT xmlserialize(CONTENT ' ' AS text INDENT); +ERROR: unsupported XML feature at character 30 +\set VERBOSITY default +-- indent null +SELECT xmlserialize(DOCUMENT NULL AS text INDENT); + xmlserialize +-------------- + +(1 row) + +SELECT xmlserialize(CONTENT NULL AS text INDENT); + xmlserialize +-------------- + +(1 row) + +-- indent encoding="ISO-8859-1" +SELECT xmlserialize(DOCUMENT '42' AS text INDENT); +ERROR: unsupported XML feature +LINE 1: SELECT xmlserialize(DOCUMENT '42' AS text INDENT); +ERROR: unsupported XML feature +LINE 1: SELECT xmlserialize(CONTENT '73' AS text INDENT); +ERROR: unsupported XML feature +LINE 1: SELECT xmlserialize(DOCUMENT '73' AS text INDENT); +ERROR: unsupported XML feature +LINE 1: SELECT xmlserialize(CONTENT '42' AS text) = xmlserialize(DOCUMENT '42' AS text NO INDENT); +ERROR: unsupported XML feature +LINE 1: SELECT xmlserialize(DOCUMENT '42<... + ^ +DETAIL: This functionality requires the server to be built with libxml support. +SELECT xmlserialize(CONTENT '42' AS text) = xmlserialize(CONTENT '42' AS text NO INDENT); +ERROR: unsupported XML feature +LINE 1: SELECT xmlserialize(CONTENT '42bar' IS DOCUMENT; ERROR: unsupported XML feature LINE 1: SELECT xml 'bar' IS DOCUMENT; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 4f029d0072..42a6e8910a 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -466,6 +466,135 @@ SELECT xmlserialize(content 'good' as char(10)); SELECT xmlserialize(document 'bad' as text); ERROR: not an XML document +-- indent +SELECT xmlserialize(DOCUMENT '42' AS text INDENT); + xmlserialize +------------------------- + + + + + 42+ + + + + + +(1 row) + +SELECT xmlserialize(CONTENT '42' AS text INDENT); + xmlserialize +------------------------- + + + + + 42+ + + + + + +(1 row) + +-- no indent +SELECT xmlserialize(DOCUMENT '42' AS text NO INDENT); + xmlserialize +------------------------------------------- + 42 +(1 row) + +SELECT xmlserialize(CONTENT '42' AS text NO INDENT); + xmlserialize +------------------------------------------- + 42 +(1 row) + +\set VERBOSITY terse +-- indent malformed xml +SELECT xmlserialize(DOCUMENT '42' AS text INDENT); +ERROR: not an XML document +SELECT xmlserialize(CONTENT '42' AS text INDENT); +ERROR: invalid XML document +-- indent empty string +SELECT xmlserialize(DOCUMENT '' AS text INDENT); +ERROR: not an XML document +SELECT xmlserialize(CONTENT '' AS text INDENT); +ERROR: invalid XML document +-- whitespaces +SELECT xmlserialize(DOCUMENT ' ' AS text INDENT); +ERROR: not an XML document +SELECT xmlserialize(CONTENT ' ' AS text INDENT); +ERROR: invalid XML document +\set VERBOSITY default +-- indent null +SELECT xmlserialize(DOCUMENT NULL AS text INDENT); + xmlserialize +-------------- + +(1 row) + +SELECT xmlserialize(CONTENT NULL AS text INDENT); + xmlserialize +-------------- + +(1 row) + +-- indent encoding="ISO-8859-1" +SELECT xmlserialize(DOCUMENT '42' AS text INDENT); + xmlserialize +--------------------------------------------- + + + + + + + 42 + + + + + + +(1 row) + +SELECT xmlserialize(CONTENT '42' AS text INDENT); + xmlserialize +--------------------------------------------- + + + + + + + 42 + + + + + + +(1 row) + +-- indent encoding="UTF-8" +SELECT xmlserialize(DOCUMENT '73' AS text INDENT); + xmlserialize +---------------------------------------- + + + + + + + 73 + + + + + + +(1 row) + +SELECT xmlserialize(CONTENT '73' AS text INDENT); + xmlserialize +---------------------------------------- + + + + + + + 73 + + + + + + +(1 row) + +-- 'no indent' = not using 'no indent' +SELECT xmlserialize(DOCUMENT '42' AS text) = xmlserialize(DOCUMENT '42' AS text NO INDENT); + ?column? +---------- + t +(1 row) + +SELECT xmlserialize(CONTENT '42' AS text) = xmlserialize(CONTENT '42' AS text NO INDENT); + ?column? +---------- + t +(1 row) + SELECT xml 'bar' IS DOCUMENT; ?column? ---------- diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql index 24e40d2653..0349b624bc 100644 --- a/src/test/regress/sql/xml.sql +++ b/src/test/regress/sql/xml.sql @@ -132,6 +132,35 @@ SELECT xmlserialize(content data as character varying(20)) FROM xmltest; SELECT xmlserialize(content 'good' as char(10)); SELECT xmlserialize(document 'bad' as text); +-- indent +SELECT xmlserialize(DOCUMENT '42' AS text INDENT); +SELECT xmlserialize(CONTENT '42' AS text INDENT); +-- no indent +SELECT xmlserialize(DOCUMENT '42' AS text NO INDENT); +SELECT xmlserialize(CONTENT '42' AS text NO INDENT); +\set VERBOSITY terse +-- indent malformed xml +SELECT xmlserialize(DOCUMENT '42' AS text INDENT); +SELECT xmlserialize(CONTENT '42' AS text INDENT); +-- indent empty string +SELECT xmlserialize(DOCUMENT '' AS text INDENT); +SELECT xmlserialize(CONTENT '' AS text INDENT); +-- whitespaces +SELECT xmlserialize(DOCUMENT ' ' AS text INDENT); +SELECT xmlserialize(CONTENT ' ' AS text INDENT); +\set VERBOSITY default +-- indent null +SELECT xmlserialize(DOCUMENT NULL AS text INDENT); +SELECT xmlserialize(CONTENT NULL AS text INDENT); +-- indent encoding="ISO-8859-1" +SELECT xmlserialize(DOCUMENT '42' AS text INDENT); +SELECT xmlserialize(CONTENT '42' AS text INDENT); +-- indent encoding="UTF-8" +SELECT xmlserialize(DOCUMENT '73' AS text INDENT); +SELECT xmlserialize(CONTENT '73' AS text INDENT); +-- 'no indent' = not using 'no indent' +SELECT xmlserialize(DOCUMENT '42' AS text) = xmlserialize(DOCUMENT '42' AS text NO INDENT); +SELECT xmlserialize(CONTENT '42' AS text) = xmlserialize(CONTENT '42' AS text NO INDENT); SELECT xml 'bar' IS DOCUMENT; SELECT xml 'barfoo' IS DOCUMENT; -- 2.25.1