mirror of
https://github.com/postgres/postgres.git
synced 2025-06-14 18:42:34 +03:00
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.
This commit is contained in:
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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,
|
newe = coerce_to_specific_type(pstate, newe, XMLOID,
|
||||||
"XMLCONCAT");
|
"XMLCONCAT");
|
||||||
break;
|
break;
|
||||||
case IS_XMLELEMENT:
|
|
||||||
newe = coerce_to_specific_type(pstate, newe, XMLOID,
|
|
||||||
"XMLELEMENT");
|
|
||||||
break;
|
|
||||||
case IS_XMLFOREST:
|
case IS_XMLFOREST:
|
||||||
newe = coerce_to_specific_type(pstate, newe, XMLOID,
|
newe = coerce_to_specific_type(pstate, newe, XMLOID,
|
||||||
"XMLFOREST");
|
"XMLFOREST");
|
||||||
@ -1488,7 +1484,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
|
|||||||
newx->args = lappend(newx->args, newe);
|
newx->args = lappend(newx->args, newe);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (Node *) newx;
|
return (Node *) newx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 <libxml/xmlwriter.h>
|
#include <libxml/xmlwriter.h>
|
||||||
#endif /* USE_LIBXML */
|
#endif /* USE_LIBXML */
|
||||||
|
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
#include "mb/pg_wchar.h"
|
#include "mb/pg_wchar.h"
|
||||||
#include "nodes/execnodes.h"
|
#include "nodes/execnodes.h"
|
||||||
|
#include "parser/parse_expr.h"
|
||||||
|
#include "utils/array.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/xml.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 xmlChar *xml_text2xmlChar(text *in);
|
||||||
static xmlDocPtr xml_parse(text *data, bool is_document, bool preserve_whitespace);
|
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 */
|
#endif /* USE_LIBXML */
|
||||||
|
|
||||||
#define NO_XML_SUPPORT() \
|
#define NO_XML_SUPPORT() \
|
||||||
@ -284,13 +290,7 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
|
|||||||
|
|
||||||
value = ExecEvalExpr(e, econtext, &isnull, NULL);
|
value = ExecEvalExpr(e, econtext, &isnull, NULL);
|
||||||
if (!isnull)
|
if (!isnull)
|
||||||
{
|
xmlTextWriterWriteRaw(writer, (xmlChar *) map_sql_value_to_xml_value(value, exprType((Node *) e->expr)));
|
||||||
/* we know the value is XML type */
|
|
||||||
str = DatumGetCString(DirectFunctionCall1(xml_out,
|
|
||||||
value));
|
|
||||||
xmlTextWriterWriteRaw(writer, (xmlChar *) str);
|
|
||||||
pfree(str);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xmlTextWriterEndElement(writer);
|
xmlTextWriterEndElement(writer);
|
||||||
@ -1258,3 +1258,87 @@ map_xml_name_to_sql_identifier(char *name)
|
|||||||
|
|
||||||
return buf.data;
|
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, "<element>");
|
||||||
|
appendStringInfoString(&buf, map_sql_value_to_xml_value(subval, elmtype));
|
||||||
|
appendStringInfoString(&buf, "</element>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 */
|
||||||
|
@ -83,10 +83,44 @@ SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp;
|
|||||||
<employee><name>linda</name><age>19</age><pay>100</pay></employee>
|
<employee><name>linda</name><age>19</age><pay>100</pay></employee>
|
||||||
(6 rows)
|
(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));
|
SELECT xmlelement(name duplicate, xmlattributes(1 as a, 2 as b, 3 as a));
|
||||||
ERROR: XML attribute name "a" appears more than once
|
ERROR: XML attribute name "a" appears more than once
|
||||||
|
SELECT xmlelement(name num, 37);
|
||||||
|
xmlelement
|
||||||
|
---------------
|
||||||
|
<num>37</num>
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT xmlelement(name foo, text 'bar');
|
||||||
|
xmlelement
|
||||||
|
----------------
|
||||||
|
<foo>bar</foo>
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT xmlelement(name foo, xml 'bar');
|
||||||
|
xmlelement
|
||||||
|
----------------
|
||||||
|
<foo>bar</foo>
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT xmlelement(name foo, text 'b<a/>r');
|
||||||
|
xmlelement
|
||||||
|
-------------------------
|
||||||
|
<foo>b<a/>r</foo>
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT xmlelement(name foo, xml 'b<a/>r');
|
||||||
|
xmlelement
|
||||||
|
-------------------
|
||||||
|
<foo>b<a/>r</foo>
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT xmlelement(name foo, array[1, 2, 3]);
|
||||||
|
xmlelement
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
<foo><element>1</element><element>2</element><element>3</element></foo>
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT xmlparse(content 'abc');
|
SELECT xmlparse(content 'abc');
|
||||||
xmlparse
|
xmlparse
|
||||||
----------
|
----------
|
||||||
|
@ -44,10 +44,20 @@ SELECT xmlelement(name element, xmlelement(name nested, 'stuff'));
|
|||||||
ERROR: no XML support in this installation
|
ERROR: no XML support in this installation
|
||||||
SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp;
|
SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp;
|
||||||
ERROR: no XML support in this installation
|
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));
|
SELECT xmlelement(name duplicate, xmlattributes(1 as a, 2 as b, 3 as a));
|
||||||
ERROR: no XML support in this installation
|
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 'b<a/>r');
|
||||||
|
ERROR: no XML support in this installation
|
||||||
|
SELECT xmlelement(name foo, xml 'b<a/>r');
|
||||||
|
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');
|
SELECT xmlparse(content 'abc');
|
||||||
ERROR: no XML support in this installation
|
ERROR: no XML support in this installation
|
||||||
SELECT xmlparse(content '<abc>x</abc>');
|
SELECT xmlparse(content '<abc>x</abc>');
|
||||||
|
@ -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 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 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 'b<a/>r');
|
||||||
|
SELECT xmlelement(name foo, xml 'b<a/>r');
|
||||||
|
SELECT xmlelement(name foo, array[1, 2, 3]);
|
||||||
|
|
||||||
|
|
||||||
SELECT xmlparse(content 'abc');
|
SELECT xmlparse(content 'abc');
|
||||||
SELECT xmlparse(content '<abc>x</abc>');
|
SELECT xmlparse(content '<abc>x</abc>');
|
||||||
|
Reference in New Issue
Block a user