From: Peter Eisentraut Date: Fri, 12 Jan 2007 16:29:24 +0000 (+0000) Subject: Allow for arbitrary data types as content in XMLELEMENT. The original X-Git-Tag: REL8_3_BETA1~1527 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=fc568b9d8f6b30d0a5573b73c719e23fa0a6a979;p=postgresql Allow for arbitrary data types as content in XMLELEMENT. The original coercion to type xml was a mistake. Escape values so they are valid XML character data. --- diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 033dd6c75c..e3c79c0cf5 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.205 2007/01/08 23:41:56 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.206 2007/01/12 16:29:24 petere Exp $ * *------------------------------------------------------------------------- */ @@ -1455,10 +1455,6 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) newe = coerce_to_specific_type(pstate, newe, XMLOID, "XMLCONCAT"); break; - case IS_XMLELEMENT: - newe = coerce_to_specific_type(pstate, newe, XMLOID, - "XMLELEMENT"); - break; case IS_XMLFOREST: newe = coerce_to_specific_type(pstate, newe, XMLOID, "XMLFOREST"); @@ -1488,7 +1484,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) newx->args = lappend(newx->args, newe); i++; } - + return (Node *) newx; } diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 5616259b29..3689c9203e 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.14 2007/01/10 20:33:54 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.15 2007/01/12 16:29:24 petere Exp $ * *------------------------------------------------------------------------- */ @@ -35,12 +35,16 @@ #include #endif /* USE_LIBXML */ +#include "catalog/pg_type.h" #include "executor/executor.h" #include "fmgr.h" #include "libpq/pqformat.h" #include "mb/pg_wchar.h" #include "nodes/execnodes.h" +#include "parser/parse_expr.h" +#include "utils/array.h" #include "utils/builtins.h" +#include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/xml.h" @@ -66,6 +70,8 @@ static void xml_ereport_by_code(int level, int sqlcode, static xmlChar *xml_text2xmlChar(text *in); static xmlDocPtr xml_parse(text *data, bool is_document, bool preserve_whitespace); +static char *map_sql_value_to_xml_value(Datum value, Oid type); + #endif /* USE_LIBXML */ #define NO_XML_SUPPORT() \ @@ -284,13 +290,7 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext) value = ExecEvalExpr(e, econtext, &isnull, NULL); if (!isnull) - { - /* we know the value is XML type */ - str = DatumGetCString(DirectFunctionCall1(xml_out, - value)); - xmlTextWriterWriteRaw(writer, (xmlChar *) str); - pfree(str); - } + xmlTextWriterWriteRaw(writer, (xmlChar *) map_sql_value_to_xml_value(value, exprType((Node *) e->expr))); } xmlTextWriterEndElement(writer); @@ -1258,3 +1258,87 @@ map_xml_name_to_sql_identifier(char *name) return buf.data; } + + +#ifdef USE_LIBXML +/* + * Map SQL value to XML value; see SQL/XML:2003 section 9.16. + */ +static char * +map_sql_value_to_xml_value(Datum value, Oid type) +{ + StringInfoData buf; + + initStringInfo(&buf); + + if (is_array_type(type)) + { + int i; + ArrayType *array; + Oid elmtype; + int16 elmlen; + bool elmbyval; + char elmalign; + + array = DatumGetArrayTypeP(value); + + /* TODO: need some code-fu here to remove this limitation */ + if (ARR_NDIM(array) != 1) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("only supported for one-dimensional array"))); + + elmtype = ARR_ELEMTYPE(array); + get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign); + + for (i = ARR_LBOUND(array)[0]; + i < ARR_LBOUND(array)[0] + ARR_DIMS(array)[0]; + i++) + { + Datum subval; + bool isnull; + + subval = array_ref(array, 1, &i, -1, elmlen, elmbyval, elmalign, &isnull); + appendStringInfoString(&buf, ""); + appendStringInfoString(&buf, map_sql_value_to_xml_value(subval, elmtype)); + appendStringInfoString(&buf, ""); + } + } + else + { + Oid typeOut; + bool isvarlena; + char *p, *str; + + getTypeOutputInfo(type, &typeOut, &isvarlena); + str = OidOutputFunctionCall(typeOut, value); + + if (type == XMLOID) + return str; + + for (p = str; *p; p += pg_mblen(p)) + { + switch (*p) + { + case '&': + appendStringInfo(&buf, "&"); + break; + case '<': + appendStringInfo(&buf, "<"); + break; + case '>': + appendStringInfo(&buf, ">"); + break; + case '\r': + appendStringInfo(&buf, " "); + break; + default: + appendBinaryStringInfo(&buf, p, pg_mblen(p)); + break; + } + } + } + + return buf.data; +} +#endif /* USE_LIBXML */ diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 4179233e99..275523727b 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -83,10 +83,44 @@ SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp; linda19100 (6 rows) -SELECT xmlelement(name wrong, 37); -ERROR: argument of XMLELEMENT must be type xml, not type integer SELECT xmlelement(name duplicate, xmlattributes(1 as a, 2 as b, 3 as a)); ERROR: XML attribute name "a" appears more than once +SELECT xmlelement(name num, 37); + xmlelement +--------------- + 37 +(1 row) + +SELECT xmlelement(name foo, text 'bar'); + xmlelement +---------------- + bar +(1 row) + +SELECT xmlelement(name foo, xml 'bar'); + xmlelement +---------------- + bar +(1 row) + +SELECT xmlelement(name foo, text 'br'); + xmlelement +------------------------- + b<a/>r +(1 row) + +SELECT xmlelement(name foo, xml 'br'); + xmlelement +------------------- + br +(1 row) + +SELECT xmlelement(name foo, array[1, 2, 3]); + xmlelement +------------------------------------------------------------------------- + 123 +(1 row) + SELECT xmlparse(content 'abc'); xmlparse ---------- diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out index 8ef963b8e9..9ff3959160 100644 --- a/src/test/regress/expected/xml_1.out +++ b/src/test/regress/expected/xml_1.out @@ -44,10 +44,20 @@ SELECT xmlelement(name element, xmlelement(name nested, 'stuff')); ERROR: no XML support in this installation SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp; ERROR: no XML support in this installation -SELECT xmlelement(name wrong, 37); -ERROR: no XML support in this installation SELECT xmlelement(name duplicate, xmlattributes(1 as a, 2 as b, 3 as a)); ERROR: no XML support in this installation +SELECT xmlelement(name num, 37); +ERROR: no XML support in this installation +SELECT xmlelement(name foo, text 'bar'); +ERROR: no XML support in this installation +SELECT xmlelement(name foo, xml 'bar'); +ERROR: no XML support in this installation +SELECT xmlelement(name foo, text 'br'); +ERROR: no XML support in this installation +SELECT xmlelement(name foo, xml 'br'); +ERROR: no XML support in this installation +SELECT xmlelement(name foo, array[1, 2, 3]); +ERROR: no XML support in this installation SELECT xmlparse(content 'abc'); ERROR: no XML support in this installation SELECT xmlparse(content 'x'); diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql index d3a1e6104b..a22c825129 100644 --- a/src/test/regress/sql/xml.sql +++ b/src/test/regress/sql/xml.sql @@ -37,9 +37,15 @@ SELECT xmlelement(name element, xmlelement(name nested, 'stuff')); SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp; -SELECT xmlelement(name wrong, 37); SELECT xmlelement(name duplicate, xmlattributes(1 as a, 2 as b, 3 as a)); +SELECT xmlelement(name num, 37); +SELECT xmlelement(name foo, text 'bar'); +SELECT xmlelement(name foo, xml 'bar'); +SELECT xmlelement(name foo, text 'br'); +SELECT xmlelement(name foo, xml 'br'); +SELECT xmlelement(name foo, array[1, 2, 3]); + SELECT xmlparse(content 'abc'); SELECT xmlparse(content 'x');