From ec020e1ceb94d0ceb3c0eee8c39cd197be7bb3cb Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Sat, 3 Feb 2007 14:06:56 +0000 Subject: [PATCH] Implement XMLSERIALIZE for real. Analogously, make the xml to text cast observe the xmloption. Reorganize the representation of the XML option in the parse tree and the API to make it easier to manage and understand. Add regression tests for parsing back XML expressions. --- src/backend/executor/execQual.c | 35 ++++++++++------ src/backend/nodes/copyfuncs.c | 20 ++++++++- src/backend/nodes/equalfuncs.c | 17 +++++++- src/backend/nodes/outfuncs.c | 18 +++++++- src/backend/nodes/readfuncs.c | 5 ++- src/backend/parser/gram.y | 39 ++++++++---------- src/backend/parser/parse_expr.c | 46 ++++++++++++++++++++- src/backend/parser/parse_target.c | 8 +++- src/backend/utils/adt/ruleutils.c | 52 ++++++++++++++++-------- src/backend/utils/adt/xml.c | 42 ++++++++++++++----- src/include/catalog/catversion.h | 4 +- src/include/catalog/pg_cast.h | 8 ++-- src/include/catalog/pg_proc.h | 4 +- src/include/nodes/nodes.h | 3 +- src/include/nodes/parsenodes.h | 13 +++++- src/include/nodes/primnodes.h | 12 +++++- src/include/utils/errcodes.h | 3 +- src/include/utils/xml.h | 14 +++---- src/test/regress/expected/opr_sanity.out | 7 +--- src/test/regress/expected/xml.out | 36 +++++++++++++++- src/test/regress/expected/xml_1.out | 34 ++++++++++++++-- src/test/regress/sql/opr_sanity.sql | 3 -- src/test/regress/sql/xml.sql | 20 ++++++++- 23 files changed, 344 insertions(+), 99 deletions(-) diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 6b72c02781..fd95672dee 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.211 2007/02/02 00:07:03 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.212 2007/02/03 14:06:53 petere Exp $ * *------------------------------------------------------------------------- */ @@ -2834,11 +2834,10 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, { ExprState *e; text *data; - bool is_document; bool preserve_whitespace; - /* arguments are known to be text, bool, bool */ - Assert(list_length(xmlExpr->args) == 3); + /* arguments are known to be text, bool */ + Assert(list_length(xmlExpr->args) == 2); e = (ExprState *) linitial(xmlExpr->args); value = ExecEvalExpr(e, econtext, &isnull, NULL); @@ -2848,12 +2847,6 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, e = (ExprState *) lsecond(xmlExpr->args); value = ExecEvalExpr(e, econtext, &isnull, NULL); - if (isnull) /* probably can't happen */ - return (Datum) 0; - is_document = DatumGetBool(value); - - e = (ExprState *) lthird(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); if (isnull) /* probably can't happen */ return (Datum) 0; preserve_whitespace = DatumGetBool(value); @@ -2861,7 +2854,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, *isNull = false; return PointerGetDatum(xmlparse(data, - is_document, + xexpr->xmloption, preserve_whitespace)); } break; @@ -2900,7 +2893,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, text *version; int standalone; - /* arguments are known to be xml, text, bool */ + /* arguments are known to be xml, text, int */ Assert(list_length(xmlExpr->args) == 3); e = (ExprState *) linitial(xmlExpr->args); @@ -2928,6 +2921,24 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, } break; + case IS_XMLSERIALIZE: + { + ExprState *e; + + /* argument type is known to be xml */ + Assert(list_length(xmlExpr->args) == 1); + + e = (ExprState *) linitial(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) + return (Datum) 0; + + *isNull = false; + + return PointerGetDatum(xmltotext_with_xmloption(DatumGetXmlP(value), xexpr->xmloption)); + } + break; + case IS_DOCUMENT: { ExprState *e; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index f213f216de..2d38d7fd60 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.364 2007/01/23 05:07:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.365 2007/02/03 14:06:54 petere Exp $ * *------------------------------------------------------------------------- */ @@ -1116,6 +1116,9 @@ _copyXmlExpr(XmlExpr *from) COPY_NODE_FIELD(named_args); COPY_NODE_FIELD(arg_names); COPY_NODE_FIELD(args); + COPY_SCALAR_FIELD(xmloption); + COPY_SCALAR_FIELD(type); + COPY_SCALAR_FIELD(typmod); return newnode; } @@ -1723,6 +1726,18 @@ _copyLockingClause(LockingClause *from) return newnode; } +static XmlSerialize * +_copyXmlSerialize(XmlSerialize *from) +{ + XmlSerialize *newnode = makeNode(XmlSerialize); + + COPY_SCALAR_FIELD(xmloption); + COPY_NODE_FIELD(expr); + COPY_NODE_FIELD(typename); + + return newnode; +} + static Query * _copyQuery(Query *from) { @@ -3430,6 +3445,9 @@ copyObject(void *from) case T_FuncWithArgs: retval = _copyFuncWithArgs(from); break; + case T_XmlSerialize: + retval = _copyXmlSerialize(from); + break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from)); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 31754a7bc0..c17a40bbbb 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.297 2007/01/23 05:07:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.298 2007/02/03 14:06:54 petere Exp $ * *------------------------------------------------------------------------- */ @@ -462,6 +462,9 @@ _equalXmlExpr(XmlExpr *a, XmlExpr *b) COMPARE_NODE_FIELD(named_args); COMPARE_NODE_FIELD(arg_names); COMPARE_NODE_FIELD(args); + COMPARE_SCALAR_FIELD(xmloption); + COMPARE_SCALAR_FIELD(type); + COMPARE_SCALAR_FIELD(typmod); return true; } @@ -1830,6 +1833,15 @@ _equalFkConstraint(FkConstraint *a, FkConstraint *b) return true; } +static bool +_equalXmlSerialize(XmlSerialize *a, XmlSerialize *b) +{ + COMPARE_SCALAR_FIELD(xmloption); + COMPARE_NODE_FIELD(expr); + COMPARE_NODE_FIELD(typename); + + return true; +} /* * Stuff from pg_list.h @@ -2411,6 +2423,9 @@ equal(void *a, void *b) case T_FuncWithArgs: retval = _equalFuncWithArgs(a, b); break; + case T_XmlSerialize: + retval = _equalXmlSerialize(a, b); + break; default: elog(ERROR, "unrecognized node type: %d", diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b79b7d1a2d..939c21f45a 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.295 2007/01/22 20:00:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.296 2007/02/03 14:06:54 petere Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -933,6 +933,9 @@ _outXmlExpr(StringInfo str, XmlExpr *node) WRITE_NODE_FIELD(named_args); WRITE_NODE_FIELD(arg_names); WRITE_NODE_FIELD(args); + WRITE_ENUM_FIELD(xmloption, XmlOptionType); + WRITE_OID_FIELD(type); + WRITE_INT_FIELD(typmod); } static void @@ -1521,6 +1524,16 @@ _outLockingClause(StringInfo str, LockingClause *node) WRITE_BOOL_FIELD(noWait); } +static void +_outXmlSerialize(StringInfo str, XmlSerialize *node) +{ + WRITE_NODE_TYPE("XMLSERIALIZE"); + + WRITE_ENUM_FIELD(xmloption, XmlOptionType); + WRITE_NODE_FIELD(expr); + WRITE_NODE_FIELD(typename); +} + static void _outColumnDef(StringInfo str, ColumnDef *node) { @@ -2290,6 +2303,9 @@ _outNode(StringInfo str, void *obj) case T_LockingClause: _outLockingClause(str, obj); break; + case T_XmlSerialize: + _outXmlSerialize(str, obj); + break; default: diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index b90c8dd713..17d36b4efe 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.201 2007/01/09 02:14:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.202 2007/02/03 14:06:54 petere Exp $ * * NOTES * Path and Plan nodes do not have any readfuncs support, because we @@ -723,6 +723,9 @@ _readXmlExpr(void) READ_NODE_FIELD(named_args); READ_NODE_FIELD(arg_names); READ_NODE_FIELD(args); + READ_ENUM_FIELD(xmloption, XmlOptionType); + READ_OID_FIELD(type); + READ_INT_FIELD(typmod); READ_DONE(); } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1f1dfdb761..cf25c5607a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.578 2007/02/01 19:10:27 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.579 2007/02/03 14:06:54 petere Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -350,7 +350,8 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) %type xml_attribute_el %type xml_attribute_list xml_attributes %type xml_root_version opt_xml_root_standalone -%type document_or_content xml_whitespace_option +%type document_or_content +%type xml_whitespace_option /* @@ -1117,7 +1118,7 @@ set_rest: var_name TO var_list_or_default { VariableSetStmt *n = makeNode(VariableSetStmt); n->name = "xmloption"; - n->args = list_make1(makeStringConst($3 ? "DOCUMENT" : "CONTENT", NULL)); + n->args = list_make1(makeStringConst($3 == XMLOPTION_DOCUMENT ? "DOCUMENT" : "CONTENT", NULL)); $$ = n; } ; @@ -7903,10 +7904,11 @@ func_expr: func_name '(' ')' } | XMLPARSE '(' document_or_content a_expr xml_whitespace_option ')' { - $$ = makeXmlExpr(IS_XMLPARSE, NULL, NIL, - list_make3($4, - makeBoolAConst($3), - makeBoolAConst($5))); + XmlExpr *x = (XmlExpr *) makeXmlExpr(IS_XMLPARSE, NULL, NIL, + list_make2($4, + makeBoolAConst($5))); + x->xmloption = $3; + $$ = (Node *)x; } | XMLPI '(' NAME_P ColLabel ')' { @@ -7921,14 +7923,13 @@ func_expr: func_name '(' ')' $$ = makeXmlExpr(IS_XMLROOT, NULL, NIL, list_make3($3, $5, $6)); } - | XMLSERIALIZE '(' document_or_content a_expr AS Typename ')' + | XMLSERIALIZE '(' document_or_content a_expr AS SimpleTypename ')' { - /* - * FIXME: This should be made distinguishable from - * CAST (for reverse compilation at least). Also, - * what about the document/content option?? - */ - $$ = makeTypeCast($4, $6); + XmlSerialize *n = makeNode(XmlSerialize); + n->xmloption = $3; + n->expr = $4; + n->typename = $6; + $$ = (Node *)n; } ; @@ -7980,17 +7981,13 @@ xml_attribute_el: a_expr AS ColLabel } ; -document_or_content: DOCUMENT_P { $$ = TRUE; } - | CONTENT_P { $$ = FALSE; } +document_or_content: DOCUMENT_P { $$ = XMLOPTION_DOCUMENT; } + | CONTENT_P { $$ = XMLOPTION_CONTENT; } ; -/* - * XXX per SQL spec, the default should be STRIP WHITESPACE, but since we - * haven't implemented that yet, temporarily default to PRESERVE. - */ xml_whitespace_option: PRESERVE WHITESPACE_P { $$ = TRUE; } | STRIP_P WHITESPACE_P { $$ = FALSE; } - | /*EMPTY*/ { $$ = TRUE; } + | /*EMPTY*/ { $$ = FALSE; } ; /* diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index a807ef12de..e7a37a2cfa 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.209 2007/01/25 11:53:51 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.210 2007/02/03 14:06:54 petere Exp $ * *------------------------------------------------------------------------- */ @@ -57,6 +57,7 @@ static Node *transformRowExpr(ParseState *pstate, RowExpr *r); static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c); static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m); static Node *transformXmlExpr(ParseState *pstate, XmlExpr *x); +static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs); static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); static Node *transformWholeRowRef(ParseState *pstate, char *schemaname, @@ -224,6 +225,10 @@ transformExpr(ParseState *pstate, Node *expr) result = transformXmlExpr(pstate, (XmlExpr *) expr); break; + case T_XmlSerialize: + result = transformXmlSerialize(pstate, (XmlSerialize *) expr); + break; + case T_NullTest: { NullTest *n = (NullTest *) expr; @@ -1424,6 +1429,8 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) newx->arg_names = lappend(newx->arg_names, makeString(argname)); } + newx->xmloption = x->xmloption; + if (x->op == IS_XMLELEMENT) { foreach(lc, newx->arg_names) @@ -1484,6 +1491,9 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) newe = coerce_to_specific_type(pstate, newe, INT4OID, "XMLROOT"); break; + case IS_XMLSERIALIZE: + /* not handled here */ + break; case IS_DOCUMENT: newe = coerce_to_specific_type(pstate, newe, XMLOID, "IS DOCUMENT"); @@ -1496,6 +1506,38 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) return (Node *) newx; } +static Node * +transformXmlSerialize(ParseState *pstate, XmlSerialize *xs) +{ + Oid targetType; + int32 targetTypmod; + XmlExpr *xexpr; + + xexpr = makeNode(XmlExpr); + xexpr->op = IS_XMLSERIALIZE; + xexpr->args = list_make1(coerce_to_specific_type(pstate, + transformExpr(pstate, xs->expr), + XMLOID, + "XMLSERIALIZE")); + + targetType = typenameTypeId(pstate, xs->typename); + targetTypmod = typenameTypeMod(pstate, xs->typename, targetType); + + xexpr->xmloption = xs->xmloption; + /* We actually only need these to be able to parse back the expression. */ + xexpr->type = targetType; + xexpr->typmod = targetTypmod; + + /* + * The actual target type is determined this way. SQL allows char + * and varchar as target types. We allow anything that can be + * cast implicitly from text. This way, user-defined text-like + * data types automatically fit in. + */ + return (Node *) coerce_to_target_type(pstate, (Node *) xexpr, TEXTOID, targetType, targetTypmod, + COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); +} + static Node * transformBooleanTest(ParseState *pstate, BooleanTest *b) { @@ -1789,6 +1831,8 @@ exprType(Node *expr) case T_XmlExpr: if (((XmlExpr *) expr)->op == IS_DOCUMENT) type = BOOLOID; + else if (((XmlExpr *) expr)->op == IS_XMLSERIALIZE) + type = TEXTOID; else type = XMLOID; break; diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index dea29d1d8a..cc4bc091d2 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.153 2007/01/14 13:11:54 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.154 2007/02/03 14:06:54 petere Exp $ * *------------------------------------------------------------------------- */ @@ -1337,11 +1337,17 @@ FigureColnameInternal(Node *node, char **name) case IS_XMLROOT: *name = "xmlroot"; return 2; + case IS_XMLSERIALIZE: + *name = "xmlserialize"; + return 2; case IS_DOCUMENT: /* nothing */ break; } break; + case T_XmlSerialize: + *name = "xmlserialize"; + return 2; default: break; } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 668266d1c4..3cd317361f 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.247 2007/01/30 02:39:27 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.248 2007/02/03 14:06:54 petere Exp $ * *------------------------------------------------------------------------- */ @@ -3856,9 +3856,19 @@ get_rule_expr(Node *node, deparse_context *context, case IS_XMLROOT: appendStringInfoString(buf, "XMLROOT("); break; + case IS_XMLSERIALIZE: + appendStringInfoString(buf, "XMLSERIALIZE("); + break; case IS_DOCUMENT: break; } + if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE) + { + if (xexpr->xmloption == XMLOPTION_DOCUMENT) + appendStringInfoString(buf, "DOCUMENT "); + else + appendStringInfoString(buf, "CONTENT "); + } if (xexpr->name) { appendStringInfo(buf, "NAME %s", @@ -3899,24 +3909,17 @@ get_rule_expr(Node *node, deparse_context *context, case IS_XMLELEMENT: case IS_XMLFOREST: case IS_XMLPI: + case IS_XMLSERIALIZE: /* no extra decoration needed */ get_rule_expr((Node *) xexpr->args, context, true); break; case IS_XMLPARSE: - Assert(list_length(xexpr->args) == 3); - - con = (Const *) lsecond(xexpr->args); - Assert(IsA(con, Const)); - Assert(!con->constisnull); - if (DatumGetBool(con->constvalue)) - appendStringInfoString(buf, "DOCUMENT "); - else - appendStringInfoString(buf, "CONTENT "); + Assert(list_length(xexpr->args) == 2); get_rule_expr((Node *) linitial(xexpr->args), context, true); - con = (Const *) lthird(xexpr->args); + con = (Const *) lsecond(xexpr->args); Assert(IsA(con, Const)); Assert(!con->constisnull); if (DatumGetBool(con->constvalue)) @@ -3944,12 +3947,26 @@ get_rule_expr(Node *node, deparse_context *context, Assert(IsA(con, Const)); if (con->constisnull) /* suppress STANDALONE NO VALUE */ ; - else if (DatumGetBool(con->constvalue)) - appendStringInfoString(buf, - ", STANDALONE YES"); else - appendStringInfoString(buf, - ", STANDALONE NO"); + { + switch (DatumGetInt32(con->constvalue)) + { + case XML_STANDALONE_YES: + appendStringInfoString(buf, + ", STANDALONE YES"); + break; + case XML_STANDALONE_NO: + appendStringInfoString(buf, + ", STANDALONE NO"); + break; + case XML_STANDALONE_NO_VALUE: + appendStringInfoString(buf, + ", STANDALONE NO VALUE"); + break; + default: + break; + } + } break; case IS_DOCUMENT: get_rule_expr_paren((Node *) xexpr->args, context, false, node); @@ -3957,6 +3974,9 @@ get_rule_expr(Node *node, deparse_context *context, } } + if (xexpr->op == IS_XMLSERIALIZE) + appendStringInfo(buf, " AS %s", format_type_with_typemod(xexpr->type, + xexpr->typmod)); if (xexpr->op == IS_DOCUMENT) appendStringInfoString(buf, " IS DOCUMENT"); else diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 7d963148d0..70e566327d 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.24 2007/01/27 14:50:51 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.25 2007/02/03 14:06:55 petere Exp $ * *------------------------------------------------------------------------- */ @@ -80,7 +80,7 @@ static void xml_ereport_by_code(int level, int sqlcode, 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); -static xmlDocPtr xml_parse(text *data, bool is_document, bool preserve_whitespace, xmlChar *encoding); +static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, xmlChar *encoding); #endif /* USE_LIBXML */ @@ -112,7 +112,7 @@ xml_in(PG_FUNCTION_ARGS) * Parse the data to check if it is well-formed XML data. Assume * that ERROR occurred if parsing failed. */ - doc = xml_parse(vardata, (xmloption == XMLOPTION_DOCUMENT), true, NULL); + doc = xml_parse(vardata, xmloption, true, NULL); xmlFreeDoc(doc); PG_RETURN_XML_P(vardata); @@ -211,7 +211,7 @@ xml_recv(PG_FUNCTION_ARGS) * Parse the data to check if it is well-formed XML data. Assume * that ERROR occurred if parsing failed. */ - doc = xml_parse(result, (xmloption == XMLOPTION_DOCUMENT), true, encoding); + doc = xml_parse(result, xmloption, true, encoding); xmlFreeDoc(doc); newstr = (char *) pg_do_encoding_conversion((unsigned char *) str, @@ -435,7 +435,29 @@ texttoxml(PG_FUNCTION_ARGS) { text *data = PG_GETARG_TEXT_P(0); - PG_RETURN_XML_P(xmlparse(data, (xmloption == XMLOPTION_DOCUMENT), true)); + PG_RETURN_XML_P(xmlparse(data, xmloption, true)); +} + + +Datum +xmltotext(PG_FUNCTION_ARGS) +{ + xmltype *data = PG_GETARG_XML_P(0); + + PG_RETURN_TEXT_P(xmltotext_with_xmloption(data, xmloption)); +} + + +text * +xmltotext_with_xmloption(xmltype *data, XmlOptionType xmloption_arg) +{ + if (xmloption_arg == XMLOPTION_DOCUMENT && !xml_is_document(data)) + ereport(ERROR, + (errcode(ERRCODE_NOT_AN_XML_DOCUMENT), + errmsg("not an XML document"))); + + /* It's actually binary compatible, save for the above check. */ + return (text *) data; } @@ -499,12 +521,12 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext) xmltype * -xmlparse(text *data, bool is_document, bool preserve_whitespace) +xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace) { #ifdef USE_LIBXML xmlDocPtr doc; - doc = xml_parse(data, is_document, preserve_whitespace, NULL); + doc = xml_parse(data, xmloption_arg, preserve_whitespace, NULL); xmlFreeDoc(doc); return (xmltype *) data; @@ -723,7 +745,7 @@ xml_is_document(xmltype *arg) PG_TRY(); { - doc = xml_parse((text *) arg, true, true, NULL); + doc = xml_parse((text *) arg, XMLOPTION_DOCUMENT, true, NULL); result = true; } PG_CATCH(); @@ -996,7 +1018,7 @@ print_xml_decl(StringInfo buf, const xmlChar *version, pg_enc encoding, int stan * TODO maybe, libxml2's xmlreader is better? (do not construct DOM, yet do not use SAX - see xml_reader.c) */ static xmlDocPtr -xml_parse(text *data, bool is_document, bool preserve_whitespace, xmlChar *encoding) +xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, xmlChar *encoding) { int32 len; xmlChar *string; @@ -1024,7 +1046,7 @@ xml_parse(text *data, bool is_document, bool preserve_whitespace, xmlChar *encod xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, "could not allocate parser context"); - if (is_document) + if (xmloption_arg == XMLOPTION_DOCUMENT) { /* * Note, that here we try to apply DTD defaults diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 8157a3f6a5..2f1bb8ae1a 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.378 2007/01/31 19:33:54 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.379 2007/02/03 14:06:55 petere Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200701311 +#define CATALOG_VERSION_NO 200702031 #endif diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h index 4fbf237dad..0047f865af 100644 --- a/src/include/catalog/pg_cast.h +++ b/src/include/catalog/pg_cast.h @@ -10,7 +10,7 @@ * * Copyright (c) 2002-2007, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.30 2007/01/31 19:33:54 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.31 2007/02/03 14:06:55 petere Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -300,7 +300,7 @@ DATA(insert ( 1266 25 939 i )); DATA(insert ( 25 1266 938 e )); DATA(insert ( 1700 25 1688 i )); DATA(insert ( 25 1700 1686 e )); -DATA(insert ( 142 25 0 e )); +DATA(insert ( 142 25 2922 e )); DATA(insert ( 25 142 2896 e )); /* @@ -340,7 +340,7 @@ DATA(insert ( 1266 1043 939 a )); DATA(insert ( 1043 1266 938 e )); DATA(insert ( 1700 1043 1688 a )); DATA(insert ( 1043 1700 1686 e )); -DATA(insert ( 142 1043 0 e )); +DATA(insert ( 142 1043 2922 e )); DATA(insert ( 1043 142 2896 e )); /* @@ -381,7 +381,7 @@ DATA(insert ( 1266 1042 939 a )); DATA(insert ( 1042 1266 938 e )); DATA(insert ( 1700 1042 1688 a )); DATA(insert ( 1042 1700 1686 e )); -DATA(insert ( 142 1042 0 e )); +DATA(insert ( 142 1042 2922 e )); /* * Length-coercion functions diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index eaee341096..500d239542 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.441 2007/01/28 16:16:52 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.442 2007/02/03 14:06:55 petere Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -4045,6 +4045,8 @@ DATA(insert OID = 2900 ( xmlconcat2 PGNSP PGUID 12 1 0 f f f f i 2 142 "1 DESCR("aggregate transition function"); DATA(insert OID = 2901 ( xmlagg PGNSP PGUID 12 1 0 t f f f i 1 142 "142" _null_ _null_ _null_ aggregate_dummy - _null_ )); DESCR("concatenate XML values"); +DATA(insert OID = 2922 ( text PGNSP PGUID 12 1 0 f f t f s 1 25 "142" _null_ _null_ _null_ xmltotext - _null_ )); +DESCR("serialize an XML value to a character string"); /* uuid */ DATA(insert OID = 2952 ( uuid_in PGNSP PGUID 12 1 0 f f t f i 1 2950 "2275" _null_ _null_ _null_ uuid_in - _null_ )); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index f3762facdd..2452f79294 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.193 2007/01/23 05:07:18 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.194 2007/02/03 14:06:55 petere Exp $ * *------------------------------------------------------------------------- */ @@ -331,6 +331,7 @@ typedef enum NodeTag T_FunctionParameter, T_LockingClause, T_RowMarkClause, + T_XmlSerialize, /* * TAGS FOR RANDOM OTHER STUFF diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index a252308bdb..0db7276302 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.339 2007/01/23 05:07:18 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.340 2007/02/03 14:06:55 petere Exp $ * *------------------------------------------------------------------------- */ @@ -485,6 +485,17 @@ typedef struct LockingClause bool noWait; /* NOWAIT option */ } LockingClause; +/* + * XMLSERIALIZE + */ +typedef struct XmlSerialize +{ + NodeTag type; + XmlOptionType xmloption; + Node *expr; + TypeName *typename; +} XmlSerialize; + /**************************************************************************** * Nodes for a Query tree diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index cea0cd2f6a..298ac0d95d 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.123 2007/01/14 13:11:54 petere Exp $ + * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.124 2007/02/03 14:06:56 petere Exp $ * *------------------------------------------------------------------------- */ @@ -726,9 +726,16 @@ 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_DOCUMENT /* xmlval IS DOCUMENT */ } XmlExprOp; +typedef enum +{ + XMLOPTION_DOCUMENT, + XMLOPTION_CONTENT +} XmlOptionType; + typedef struct XmlExpr { Expr xpr; @@ -737,6 +744,9 @@ typedef struct XmlExpr List *named_args; /* non-XML expressions for xml_attributes */ List *arg_names; /* parallel list of Value strings */ List *args; /* list of expressions */ + XmlOptionType xmloption; /* DOCUMENT or CONTENT */ + Oid type; /* target type for XMLSERIALIZE */ + int32 typmod; } XmlExpr; /* diff --git a/src/include/utils/errcodes.h b/src/include/utils/errcodes.h index 0e4d83a802..010918ab41 100644 --- a/src/include/utils/errcodes.h +++ b/src/include/utils/errcodes.h @@ -11,7 +11,7 @@ * * Copyright (c) 2003-2007, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/include/utils/errcodes.h,v 1.22 2007/01/05 22:19:59 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/errcodes.h,v 1.23 2007/02/03 14:06:56 petere Exp $ * *------------------------------------------------------------------------- */ @@ -148,6 +148,7 @@ #define ERRCODE_INVALID_BINARY_REPRESENTATION MAKE_SQLSTATE('2','2', 'P','0','3') #define ERRCODE_BAD_COPY_FILE_FORMAT MAKE_SQLSTATE('2','2', 'P','0','4') #define ERRCODE_UNTRANSLATABLE_CHARACTER MAKE_SQLSTATE('2','2', 'P','0','5') +#define ERRCODE_NOT_AN_XML_DOCUMENT MAKE_SQLSTATE('2', '2', '0', '0', 'L') #define ERRCODE_INVALID_XML_DOCUMENT MAKE_SQLSTATE('2', '2', '0', '0', 'M') #define ERRCODE_INVALID_XML_CONTENT MAKE_SQLSTATE('2', '2', '0', '0', 'N') #define ERRCODE_INVALID_XML_COMMENT MAKE_SQLSTATE('2', '2', '0', '0', 'S') diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h index f5b33512cf..f207917ea8 100644 --- a/src/include/utils/xml.h +++ b/src/include/utils/xml.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.13 2007/01/25 11:53:51 petere Exp $ + * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.14 2007/02/03 14:06:56 petere Exp $ * *------------------------------------------------------------------------- */ @@ -17,10 +17,12 @@ #include "fmgr.h" #include "nodes/execnodes.h" +#include "nodes/primnodes.h" typedef struct varlena xmltype; #define DatumGetXmlP(X) ((xmltype *) PG_DETOAST_DATUM(X)) +#define XmlPGetDatum(X) PointerGetDatum(X) #define PG_GETARG_XML_P(n) DatumGetXmlP(PG_GETARG_DATUM(n)) #define PG_RETURN_XML_P(x) PG_RETURN_POINTER(x) @@ -32,6 +34,7 @@ extern Datum xml_send(PG_FUNCTION_ARGS); extern Datum xmlcomment(PG_FUNCTION_ARGS); extern Datum xmlconcat2(PG_FUNCTION_ARGS); extern Datum texttoxml(PG_FUNCTION_ARGS); +extern Datum xmltotext(PG_FUNCTION_ARGS); extern Datum xmlvalidate(PG_FUNCTION_ARGS); typedef enum @@ -44,10 +47,11 @@ typedef enum extern xmltype *xmlconcat(List *args); extern xmltype *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext); -extern xmltype *xmlparse(text *data, bool is_doc, bool preserve_whitespace); +extern xmltype *xmlparse(text *data, XmlOptionType xmloption, bool preserve_whitespace); extern xmltype *xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null); 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 char *map_sql_identifier_to_xml_name(char *ident, bool fully_escaped); extern char *map_xml_name_to_sql_identifier(char *name); @@ -61,12 +65,6 @@ typedef enum extern XmlBinaryType xmlbinary; -typedef enum -{ - XMLOPTION_DOCUMENT, - XMLOPTION_CONTENT -} XmlOptionType; - extern XmlOptionType xmloption; #endif /* XML_H */ diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 33bebcfa66..fcbfe3cad5 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -288,8 +288,6 @@ WHERE c.castfunc = p.oid AND -- those are binary-compatible while the reverse way goes through rtrim(). -- As of 8.2, this finds the cast from cidr to inet, because that is a -- trivial binary coercion while the other way goes through inet_to_cidr(). --- As of 8.3, this finds casts from xml to text, varchar, and bpchar, --- because the other direction has to go through xmlparse(). SELECT * FROM pg_cast c WHERE c.castfunc = 0 AND @@ -302,10 +300,7 @@ WHERE c.castfunc = 0 AND 25 | 1042 | 0 | i 1043 | 1042 | 0 | i 650 | 869 | 0 | i - 142 | 25 | 0 | e - 142 | 1043 | 0 | e - 142 | 1042 | 0 | e -(6 rows) +(3 rows) -- **************** pg_operator **************** -- Look for illegal values in pg_operator fields. diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 0c08667706..6d79a4166e 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -275,13 +275,21 @@ SELECT xmlroot ( foo (1 row) -SELECT xmlserialize(content data as character varying) FROM xmltest; - data +SELECT xmlserialize(content data as character varying(20)) FROM xmltest; + xmlserialize -------------------- one two (2 rows) +SELECT xmlserialize(content 'good' as char(10)); + xmlserialize +-------------- + good +(1 row) + +SELECT xmlserialize(document 'bad' as text); +ERROR: not an XML document SELECT xml 'bar' IS DOCUMENT; ?column? ---------- @@ -368,3 +376,27 @@ EXECUTE foo ('good'); good (1 row) +-- Test backwards parsing +CREATE VIEW xmlview1 AS SELECT xmlcomment('test'); +CREATE VIEW xmlview2 AS SELECT xmlconcat('hello', 'you'); +CREATE VIEW xmlview3 AS SELECT xmlelement(name element, xmlattributes (1 as ":one:", 'deuce' as two), 'content&'); +CREATE VIEW xmlview4 AS SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp; +CREATE VIEW xmlview5 AS SELECT xmlparse(content 'x'); +CREATE VIEW xmlview6 AS SELECT xmlpi(name foo, 'bar'); +CREATE VIEW xmlview7 AS SELECT xmlroot(xml '', version no value, standalone yes); +CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); +CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); +SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%'; + table_name | view_definition +------------+-------------------------------------------------------------------------------------------------------------------------------- + xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; + xmlview2 | SELECT XMLCONCAT('hello'::"xml", 'you'::"xml") AS "xmlconcat"; + xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp."name" AS "name", emp.age AS age, emp.salary AS pay)) AS "xmlelement" FROM emp; + xmlview5 | SELECT XMLPARSE(CONTENT 'x'::text STRIP WHITESPACE) AS "xmlparse"; + xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; + xmlview7 | SELECT XMLROOT(''::"xml", VERSION NO VALUE, STANDALONE YES) AS "xmlroot"; + xmlview8 | SELECT (XMLSERIALIZE(CONTENT 'good'::"xml" AS character(10)))::character(10) AS "xmlserialize"; + xmlview9 | SELECT XMLSERIALIZE(CONTENT 'good'::"xml" AS text) AS "xmlserialize"; +(9 rows) + diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out index 89124ebb98..899ed4cd26 100644 --- a/src/test/regress/expected/xml_1.out +++ b/src/test/regress/expected/xml_1.out @@ -122,11 +122,15 @@ SELECT xmlroot ( standalone yes ); ERROR: no XML support in this installation -SELECT xmlserialize(content data as character varying) FROM xmltest; - data ------- +SELECT xmlserialize(content data as character varying(20)) FROM xmltest; + xmlserialize +-------------- (0 rows) +SELECT xmlserialize(content 'good' as char(10)); +ERROR: no XML support in this installation +SELECT xmlserialize(document 'bad' as text); +ERROR: no XML support in this installation SELECT xml 'bar' IS DOCUMENT; ERROR: no XML support in this installation SELECT xml 'barfoo' IS DOCUMENT; @@ -168,3 +172,27 @@ EXECUTE foo (''); ERROR: prepared statement "foo" does not exist EXECUTE foo ('good'); ERROR: prepared statement "foo" does not exist +-- Test backwards parsing +CREATE VIEW xmlview1 AS SELECT xmlcomment('test'); +CREATE VIEW xmlview2 AS SELECT xmlconcat('hello', 'you'); +ERROR: no XML support in this installation +CREATE VIEW xmlview3 AS SELECT xmlelement(name element, xmlattributes (1 as ":one:", 'deuce' as two), 'content&'); +ERROR: no XML support in this installation +CREATE VIEW xmlview4 AS SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp; +ERROR: no XML support in this installation +CREATE VIEW xmlview5 AS SELECT xmlparse(content 'x'); +CREATE VIEW xmlview6 AS SELECT xmlpi(name foo, 'bar'); +ERROR: no XML support in this installation +CREATE VIEW xmlview7 AS SELECT xmlroot(xml '', version no value, standalone yes); +ERROR: no XML support in this installation +CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); +ERROR: no XML support in this installation +CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); +ERROR: no XML support in this installation +SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%'; + table_name | view_definition +------------+------------------------------------------------------------------------------- + xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; + xmlview5 | SELECT XMLPARSE(CONTENT 'x'::text STRIP WHITESPACE) AS "xmlparse"; +(2 rows) + diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 7eff2195df..cbf9baf672 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -238,9 +238,6 @@ WHERE c.castfunc = p.oid AND -- As of 8.2, this finds the cast from cidr to inet, because that is a -- trivial binary coercion while the other way goes through inet_to_cidr(). --- As of 8.3, this finds casts from xml to text, varchar, and bpchar, --- because the other direction has to go through xmlparse(). - SELECT * FROM pg_cast c WHERE c.castfunc = 0 AND diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql index b3117c2424..2f919fb42a 100644 --- a/src/test/regress/sql/xml.sql +++ b/src/test/regress/sql/xml.sql @@ -68,6 +68,7 @@ SELECT xmlpi(name foo, null); SELECT xmlpi(name xmlstuff, null); SELECT xmlpi(name foo, ' bar'); + SELECT xmlroot(xml '', version no value, standalone no value); SELECT xmlroot(xml '', version '2.0'); SELECT xmlroot(xml '', version no value, standalone yes); @@ -95,7 +96,9 @@ SELECT xmlroot ( ); -SELECT xmlserialize(content data as character varying) FROM xmltest; +SELECT xmlserialize(content data as character varying(20)) FROM xmltest; +SELECT xmlserialize(content 'good' as char(10)); +SELECT xmlserialize(document 'bad' as text); SELECT xml 'bar' IS DOCUMENT; @@ -125,3 +128,18 @@ EXECUTE foo ('bad'); SET XML OPTION CONTENT; EXECUTE foo (''); EXECUTE foo ('good'); + + +-- Test backwards parsing + +CREATE VIEW xmlview1 AS SELECT xmlcomment('test'); +CREATE VIEW xmlview2 AS SELECT xmlconcat('hello', 'you'); +CREATE VIEW xmlview3 AS SELECT xmlelement(name element, xmlattributes (1 as ":one:", 'deuce' as two), 'content&'); +CREATE VIEW xmlview4 AS SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp; +CREATE VIEW xmlview5 AS SELECT xmlparse(content 'x'); +CREATE VIEW xmlview6 AS SELECT xmlpi(name foo, 'bar'); +CREATE VIEW xmlview7 AS SELECT xmlroot(xml '', version no value, standalone yes); +CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); +CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); + +SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%'; -- 2.40.0