Commit 22bd156f authored by Peter Eisentraut's avatar Peter Eisentraut

Various fixes in the logic of XML functions:

- Add new SQL command SET XML OPTION (also available via regular GUC) to
  control the DOCUMENT vs. CONTENT option in implicit parsing and
  serialization operations.

- Subtle corrections in the handling of the standalone property in
  xmlroot().

- Allow xmlroot() to work on content fragments.

- Subtle corrections in the handling of the version property in
  xmlconcat().

- Code refactoring for producing XML declarations.
parent 9597446d
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.105 2007/01/25 04:35:10 momjian Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.106 2007/01/25 11:53:50 petere Exp $ -->
<chapter Id="runtime-config"> <chapter Id="runtime-config">
<title>Server Configuration</title> <title>Server Configuration</title>
...@@ -3558,6 +3558,38 @@ SELECT * FROM parent WHERE key = 2400; ...@@ -3558,6 +3558,38 @@ SELECT * FROM parent WHERE key = 2400;
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="guc-xmloption" xreflabel="xmloption">
<term><varname>xmloption</varname> (<type>string</type>)</term>
<indexterm>
<primary><varname>xmloption</> configuration parameter</primary>
</indexterm>
<indexterm>
<primary><varname>SET XML OPTION</></primary>
</indexterm>
<indexterm>
<primary><varname>XML option</></primary>
</indexterm>
<listitem>
<para>
Sets whether <literal>DOCUMENT</literal> or
<literal>CONTENT</literal> is implicit when converting between
XML and character string values. See <xref
linkend="datatype-xml"> for a description of this. Valid
values are <literal>DOCUMENT</literal> and
<literal>CONTENT</literal>. The default is
<literal>CONTENT</literal>.
</para>
<para>
According to the SQL standard, the command to set this option is
<synopsis>
SET XML OPTION { DOCUMENT | CONTENT };
</synopsis>
This syntax is also available in PostgreSQL.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</sect2> </sect2>
<sect2 id="runtime-config-client-format"> <sect2 id="runtime-config-client-format">
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.185 2007/01/18 13:59:11 petere Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.186 2007/01/25 11:53:50 petere Exp $ -->
<chapter id="datatype"> <chapter id="datatype">
<title id="datatype-title">Data Types</title> <title id="datatype-title">Data Types</title>
...@@ -3474,6 +3474,24 @@ XMLSERIALIZE ( { DOCUMENT | CONTENT } <replaceable>value</replaceable> AS <repla ...@@ -3474,6 +3474,24 @@ XMLSERIALIZE ( { DOCUMENT | CONTENT } <replaceable>value</replaceable> AS <repla
you to simply cast the value. you to simply cast the value.
</para> </para>
<para>
When character string values are cast to or from type
<type>xml</type> without going through <type>XMLPARSE</type> or
<type>XMLSERIALIZE</type>, respectively, the choice of
<literal>DOCUMENT</literal> versus <literal>CONTENT</literal> is
determined by the <quote>XML option</quote> session configuration
parameter, which can be set using the standard command
<synopsis>
SET XML OPTION { DOCUMENT | CONTENT };
</synopsis>
or the more PostgreSQL-like syntax
<synopsis>
SET xmloption TO { DOCUMENT | CONTENT };
</synopsis>
The default is <literal>CONTENT</literal>, so all forms of XML
data are allowed.
</para>
<para> <para>
Care must be taken when dealing with multiple character encodings Care must be taken when dealing with multiple character encodings
on the client, server, and in the XML data passed through them. on the client, server, and in the XML data passed through them.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.208 2007/01/20 09:27:19 petere Exp $ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.209 2007/01/25 11:53:50 petere Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2797,10 +2797,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, ...@@ -2797,10 +2797,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
e = (ExprState *) lthird(xmlExpr->args); e = (ExprState *) lthird(xmlExpr->args);
value = ExecEvalExpr(e, econtext, &isnull, NULL); value = ExecEvalExpr(e, econtext, &isnull, NULL);
if (isnull) standalone = DatumGetInt32(value);
standalone = 0;
else
standalone = (DatumGetBool(value) ? 1 : -1);
*isNull = false; *isNull = false;
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.576 2007/01/23 05:07:17 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.577 2007/01/25 11:53:51 petere Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
#include "utils/date.h" #include "utils/date.h"
#include "utils/datetime.h" #include "utils/datetime.h"
#include "utils/numeric.h" #include "utils/numeric.h"
#include "utils/xml.h"
/* Location tracking support --- simpler than bison's default */ /* Location tracking support --- simpler than bison's default */
...@@ -439,7 +440,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) ...@@ -439,7 +440,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE
XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLFOREST XMLPARSE XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLFOREST XMLPARSE
XMLPI XMLROOT XMLSERIALIZE XMLPI XMLROOT XMLSERIALIZE
YEAR_P YES_P YEAR_P YES_P
...@@ -1112,6 +1113,13 @@ set_rest: var_name TO var_list_or_default ...@@ -1112,6 +1113,13 @@ set_rest: var_name TO var_list_or_default
n->args = NIL; n->args = NIL;
$$ = n; $$ = n;
} }
| XML_P OPTION document_or_content
{
VariableSetStmt *n = makeNode(VariableSetStmt);
n->name = "xmloption";
n->args = list_make1(makeStringConst($3 ? "DOCUMENT" : "CONTENT", NULL));
$$ = n;
}
; ;
var_name: var_name:
...@@ -7938,21 +7946,13 @@ xml_root_version: VERSION_P a_expr ...@@ -7938,21 +7946,13 @@ xml_root_version: VERSION_P a_expr
; ;
opt_xml_root_standalone: ',' STANDALONE_P YES_P opt_xml_root_standalone: ',' STANDALONE_P YES_P
{ $$ = (Node *) makeBoolAConst(true); } { $$ = (Node *) makeIntConst(XML_STANDALONE_YES); }
| ',' STANDALONE_P NO | ',' STANDALONE_P NO
{ $$ = (Node *) makeBoolAConst(false); } { $$ = (Node *) makeIntConst(XML_STANDALONE_NO); }
| ',' STANDALONE_P NO VALUE_P | ',' STANDALONE_P NO VALUE_P
{ { $$ = (Node *) makeIntConst(XML_STANDALONE_NO_VALUE); }
A_Const *val = makeNode(A_Const);
val->val.type = T_Null;
$$ = (Node *) val;
}
| /*EMPTY*/ | /*EMPTY*/
{ { $$ = (Node *) makeIntConst(XML_STANDALONE_OMITTED); }
A_Const *val = makeNode(A_Const);
val->val.type = T_Null;
$$ = (Node *) val;
}
; ;
xml_attributes: XMLATTRIBUTES '(' xml_attribute_list ')' { $$ = $3; } xml_attributes: XMLATTRIBUTES '(' xml_attribute_list ')' { $$ = $3; }
...@@ -8864,6 +8864,7 @@ unreserved_keyword: ...@@ -8864,6 +8864,7 @@ unreserved_keyword:
| WITHOUT | WITHOUT
| WORK | WORK
| WRITE | WRITE
| XML_P
| YEAR_P | YEAR_P
| YES_P | YES_P
| ZONE | ZONE
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.183 2007/01/23 05:07:18 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.184 2007/01/25 11:53:51 petere Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -380,6 +380,7 @@ static const ScanKeyword ScanKeywords[] = { ...@@ -380,6 +380,7 @@ static const ScanKeyword ScanKeywords[] = {
{"without", WITHOUT}, {"without", WITHOUT},
{"work", WORK}, {"work", WORK},
{"write", WRITE}, {"write", WRITE},
{"xml", XML_P},
{"xmlattributes", XMLATTRIBUTES}, {"xmlattributes", XMLATTRIBUTES},
{"xmlconcat", XMLCONCAT}, {"xmlconcat", XMLCONCAT},
{"xmlelement", XMLELEMENT}, {"xmlelement", XMLELEMENT},
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.208 2007/01/14 13:11:53 petere Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.209 2007/01/25 11:53:51 petere Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1481,7 +1481,8 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) ...@@ -1481,7 +1481,8 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
newe = coerce_to_specific_type(pstate, newe, TEXTOID, newe = coerce_to_specific_type(pstate, newe, TEXTOID,
"XMLROOT"); "XMLROOT");
else else
newe = coerce_to_boolean(pstate, newe, "XMLROOT"); newe = coerce_to_specific_type(pstate, newe, INT4OID,
"XMLROOT");
break; break;
case IS_DOCUMENT: case IS_DOCUMENT:
newe = coerce_to_specific_type(pstate, newe, XMLOID, newe = coerce_to_specific_type(pstate, newe, XMLOID,
......
...@@ -7,7 +7,7 @@ ...@@ -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.21 2007/01/23 23:39:16 petere Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.22 2007/01/25 11:53:51 petere Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -67,11 +67,14 @@ static void xml_ereport_by_code(int level, int sqlcode, ...@@ -67,11 +67,14 @@ static void xml_ereport_by_code(int level, int sqlcode,
const char *msg, int errcode); const char *msg, int errcode);
static xmlChar *xml_text2xmlChar(text *in); static xmlChar *xml_text2xmlChar(text *in);
static int parse_xml_decl(const xmlChar *str, size_t *lenp, xmlChar **version, xmlChar **encoding, int *standalone); 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, bool is_document, bool preserve_whitespace, xmlChar *encoding);
#endif /* USE_LIBXML */ #endif /* USE_LIBXML */
XmlBinaryType xmlbinary; XmlBinaryType xmlbinary;
XmlOptionType xmloption;
#define NO_XML_SUPPORT() \ #define NO_XML_SUPPORT() \
ereport(ERROR, \ ereport(ERROR, \
...@@ -97,7 +100,7 @@ xml_in(PG_FUNCTION_ARGS) ...@@ -97,7 +100,7 @@ xml_in(PG_FUNCTION_ARGS)
* Parse the data to check if it is well-formed XML data. Assume * Parse the data to check if it is well-formed XML data. Assume
* that ERROR occurred if parsing failed. * that ERROR occurred if parsing failed.
*/ */
doc = xml_parse(vardata, false, true, NULL); doc = xml_parse(vardata, (xmloption == XMLOPTION_DOCUMENT), true, NULL);
xmlFreeDoc(doc); xmlFreeDoc(doc);
PG_RETURN_XML_P(vardata); PG_RETURN_XML_P(vardata);
...@@ -129,48 +132,13 @@ xml_out_internal(xmltype *x, pg_enc target_encoding) ...@@ -129,48 +132,13 @@ xml_out_internal(xmltype *x, pg_enc target_encoding)
str[len] = '\0'; str[len] = '\0';
#ifdef USE_LIBXML #ifdef USE_LIBXML
/*
* On output, we adjust the XML declaration as follows. (These
* rules are the moral equivalent of the clause "Serialization of
* an XML value" in the SQL standard.)
*
* We try to avoid generating an XML declaration if possible.
* This is so that you don't get trivial things like xml '<foo/>'
* resulting in '<?xml version="1.0"?><foo/>', which would surely
* be annoying. We must provide a declaration if the standalone
* property is specified or if we include an encoding
* specification. If we have a 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 ((res_code = parse_xml_decl((xmlChar *) str, &len, &version, &encoding, &standalone)) == 0) if ((res_code = parse_xml_decl((xmlChar *) str, &len, &version, &encoding, &standalone)) == 0)
{ {
StringInfoData buf; StringInfoData buf;
initStringInfo(&buf); initStringInfo(&buf);
if ((version && strcmp((char *) version, PG_XML_DEFAULT_VERSION) != 0) if (!print_xml_decl(&buf, version, target_encoding, standalone))
|| (target_encoding && target_encoding != PG_UTF8)
|| standalone != -1)
{
appendStringInfoString(&buf, "<?xml");
if (version)
appendStringInfo(&buf, " version=\"%s\"", version);
else
appendStringInfo(&buf, " version=\"%s\"", PG_XML_DEFAULT_VERSION);
if (target_encoding && target_encoding != PG_UTF8)
/* XXX might be useful to convert this to IANA names
* (ISO-8859-1 instead of LATIN1 etc.); needs field
* experience */
appendStringInfo(&buf, " encoding=\"%s\"", pg_encoding_to_char(target_encoding));
if (standalone == 1)
appendStringInfoString(&buf, " standalone=\"yes\"");
else if (standalone == 0)
appendStringInfoString(&buf, " standalone=\"no\"");
appendStringInfoString(&buf, "?>");
}
else
{ {
/* /*
* If we are not going to produce an XML declaration, eat * If we are not going to produce an XML declaration, eat
...@@ -231,7 +199,7 @@ xml_recv(PG_FUNCTION_ARGS) ...@@ -231,7 +199,7 @@ xml_recv(PG_FUNCTION_ARGS)
* Parse the data to check if it is well-formed XML data. Assume * Parse the data to check if it is well-formed XML data. Assume
* that ERROR occurred if parsing failed. * that ERROR occurred if parsing failed.
*/ */
doc = xml_parse(result, false, true, encoding); doc = xml_parse(result, (xmloption == XMLOPTION_DOCUMENT), true, encoding);
xmlFreeDoc(doc); xmlFreeDoc(doc);
newstr = (char *) pg_do_encoding_conversion((unsigned char *) str, newstr = (char *) pg_do_encoding_conversion((unsigned char *) str,
...@@ -296,6 +264,7 @@ stringinfo_to_xmltype(StringInfo buf) ...@@ -296,6 +264,7 @@ stringinfo_to_xmltype(StringInfo buf)
} }
#ifdef NOT_USED
static xmltype * static xmltype *
cstring_to_xmltype(const char *string) cstring_to_xmltype(const char *string)
{ {
...@@ -309,6 +278,7 @@ cstring_to_xmltype(const char *string) ...@@ -309,6 +278,7 @@ cstring_to_xmltype(const char *string)
return result; return result;
} }
#endif
static xmltype * static xmltype *
...@@ -394,9 +364,11 @@ xmlconcat(List *args) ...@@ -394,9 +364,11 @@ xmlconcat(List *args)
if (standalone < 0) if (standalone < 0)
global_standalone = -1; global_standalone = -1;
if (!global_version) if (!version)
global_version_no_value = true;
else if (!global_version)
global_version = xmlStrdup(version); global_version = xmlStrdup(version);
else if (version && xmlStrcmp(version, global_version) != 0) else if (xmlStrcmp(version, global_version) != 0)
global_version_no_value = true; global_version_no_value = true;
appendStringInfoString(&buf, str + len); appendStringInfoString(&buf, str + len);
...@@ -409,17 +381,10 @@ xmlconcat(List *args) ...@@ -409,17 +381,10 @@ xmlconcat(List *args)
initStringInfo(&buf2); initStringInfo(&buf2);
if (!global_version_no_value && global_version) print_xml_decl(&buf2,
appendStringInfo(&buf2, "<?xml version=\"%s\"", global_version); (!global_version_no_value && global_version) ? global_version : NULL,
else 0,
appendStringInfo(&buf2, "<?xml version=\"%s\"", PG_XML_DEFAULT_VERSION); global_standalone);
if (global_standalone == 1)
appendStringInfoString(&buf2, " standalone=\"yes\"");
else if (global_standalone == 0)
appendStringInfoString(&buf2, " standalone=\"no\"");
appendStringInfoString(&buf2, "?>");
appendStringInfoString(&buf2, buf.data); appendStringInfoString(&buf2, buf.data);
buf = buf2; buf = buf2;
...@@ -458,7 +423,7 @@ texttoxml(PG_FUNCTION_ARGS) ...@@ -458,7 +423,7 @@ texttoxml(PG_FUNCTION_ARGS)
{ {
text *data = PG_GETARG_TEXT_P(0); text *data = PG_GETARG_TEXT_P(0);
PG_RETURN_XML_P(xmlparse(data, false, true)); PG_RETURN_XML_P(xmlparse(data, (xmloption == XMLOPTION_DOCUMENT), true));
} }
...@@ -595,44 +560,45 @@ xmltype * ...@@ -595,44 +560,45 @@ xmltype *
xmlroot(xmltype *data, text *version, int standalone) xmlroot(xmltype *data, text *version, int standalone)
{ {
#ifdef USE_LIBXML #ifdef USE_LIBXML
xmltype *result; char *str;
xmlDocPtr doc; size_t len;
xmlBufferPtr buffer; xmlChar *orig_version;
xmlSaveCtxtPtr save; int orig_standalone;
StringInfoData buf;
doc = xml_parse((text *) data, true, true, NULL); len = VARSIZE(data) - VARHDRSZ;
str = palloc(len + 1);
memcpy(str, VARDATA(data), len);
str[len] = '\0';
parse_xml_decl((xmlChar *) str, &len, &orig_version, NULL, &orig_standalone);
if (version) if (version)
doc->version = xmlStrdup(xml_text2xmlChar(version)); orig_version = xml_text2xmlChar(version);
else else
doc->version = NULL; orig_version = NULL;
switch (standalone) switch (standalone)
{ {
case 1: case XML_STANDALONE_YES:
doc->standalone = 1; orig_standalone = 1;
break;
case XML_STANDALONE_NO:
orig_standalone = 0;
break; break;
case -1: case XML_STANDALONE_NO_VALUE:
doc->standalone = 0; orig_standalone = -1;
break; break;
default: case XML_STANDALONE_OMITTED:
doc->standalone = -1; /* leave original value */
break; break;
} }
buffer = xmlBufferCreate(); initStringInfo(&buf);
save = xmlSaveToBuffer(buffer, "UTF-8", 0); print_xml_decl(&buf, orig_version, 0, orig_standalone);
xmlSaveDoc(save, doc); appendStringInfoString(&buf, str + len);
xmlSaveClose(save);
xmlFreeDoc(doc);
result = cstring_to_xmltype((char *) pg_do_encoding_conversion((unsigned char *) xmlBufferContent(buffer), return stringinfo_to_xmltype(&buf);
xmlBufferLength(buffer),
PG_UTF8,
GetDatabaseEncoding()));
xmlBufferFree(buffer);
return result;
#else #else
NO_XML_SUPPORT(); NO_XML_SUPPORT();
return NULL; return NULL;
...@@ -971,6 +937,53 @@ finished: ...@@ -971,6 +937,53 @@ finished:
} }
/*
* Write an XML declaration. On output, we adjust the XML declaration
* as follows. (These rules are the moral equivalent of the clause
* "Serialization of an XML value" in the SQL standard.)
*
* We try to avoid generating an XML declaration if possible. This is
* so that you don't get trivial things like xml '<foo/>' resulting in
* '<?xml version="1.0"?><foo/>', which would surely be annoying. We
* must provide a declaration if the standalone property is specified
* or if we include an encoding declaration. If we have a
* 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.
*/
static bool
print_xml_decl(StringInfo buf, const xmlChar *version, pg_enc encoding, int standalone)
{
if ((version && strcmp((char *) version, PG_XML_DEFAULT_VERSION) != 0)
|| (encoding && encoding != PG_UTF8)
|| standalone != -1)
{
appendStringInfoString(buf, "<?xml");
if (version)
appendStringInfo(buf, " version=\"%s\"", version);
else
appendStringInfo(buf, " version=\"%s\"", PG_XML_DEFAULT_VERSION);
if (encoding && encoding != PG_UTF8)
/* XXX might be useful to convert this to IANA names
* (ISO-8859-1 instead of LATIN1 etc.); needs field
* experience */
appendStringInfo(buf, " encoding=\"%s\"", pg_encoding_to_char(encoding));
if (standalone == 1)
appendStringInfoString(buf, " standalone=\"yes\"");
else if (standalone == 0)
appendStringInfoString(buf, " standalone=\"no\"");
appendStringInfoString(buf, "?>");
return true;
}
else
return false;
}
/* /*
* Convert a C string to XML internal representation * Convert a C string to XML internal representation
* *
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>. * Written by Peter Eisentraut <peter_e@gmx.net>.
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.370 2007/01/25 04:35:11 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.371 2007/01/25 11:53:51 petere Exp $
* *
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
...@@ -145,6 +145,7 @@ static const char *assign_canonical_path(const char *newval, bool doit, GucSourc ...@@ -145,6 +145,7 @@ static const char *assign_canonical_path(const char *newval, bool doit, GucSourc
static const char *assign_backslash_quote(const char *newval, bool doit, GucSource source); static const char *assign_backslash_quote(const char *newval, bool doit, GucSource source);
static const char *assign_timezone_abbreviations(const char *newval, bool doit, GucSource source); static const char *assign_timezone_abbreviations(const char *newval, bool doit, GucSource source);
static const char *assign_xmlbinary(const char *newval, bool doit, GucSource source); static const char *assign_xmlbinary(const char *newval, bool doit, GucSource source);
static const char *assign_xmloption(const char *newval, bool doit, GucSource source);
static bool assign_tcp_keepalives_idle(int newval, bool doit, GucSource source); static bool assign_tcp_keepalives_idle(int newval, bool doit, GucSource source);
static bool assign_tcp_keepalives_interval(int newval, bool doit, GucSource source); static bool assign_tcp_keepalives_interval(int newval, bool doit, GucSource source);
...@@ -233,6 +234,7 @@ static char *XactIsoLevel_string; ...@@ -233,6 +234,7 @@ static char *XactIsoLevel_string;
static char *data_directory; static char *data_directory;
static char *custom_variable_classes; static char *custom_variable_classes;
static char *xmlbinary_string; static char *xmlbinary_string;
static char *xmloption_string;
static int max_function_args; static int max_function_args;
static int max_index_keys; static int max_index_keys;
static int max_identifier_length; static int max_identifier_length;
...@@ -2292,6 +2294,16 @@ static struct config_string ConfigureNamesString[] = ...@@ -2292,6 +2294,16 @@ static struct config_string ConfigureNamesString[] =
"base64", assign_xmlbinary, NULL "base64", assign_xmlbinary, NULL
}, },
{
{"xmloption", PGC_USERSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets whether XML data in implicit parsing and serialization "
"operations is to be considered as documents or content fragments."),
gettext_noop("Valid values are DOCUMENT and CONTENT.")
},
&xmloption_string,
"content", assign_xmloption, NULL
},
{ {
{"temp_tablespaces", PGC_USERSET, PGC_S_FILE, {"temp_tablespaces", PGC_USERSET, PGC_S_FILE,
gettext_noop("Sets the tablespaces suitable for creating new objects and sort files."), gettext_noop("Sets the tablespaces suitable for creating new objects and sort files."),
...@@ -6516,6 +6528,24 @@ assign_xmlbinary(const char *newval, bool doit, GucSource source) ...@@ -6516,6 +6528,24 @@ assign_xmlbinary(const char *newval, bool doit, GucSource source)
return newval; return newval;
} }
static const char *
assign_xmloption(const char *newval, bool doit, GucSource source)
{
XmlOptionType xo;
if (pg_strcasecmp(newval, "document") == 0)
xo = XMLOPTION_DOCUMENT;
else if (pg_strcasecmp(newval, "content") == 0)
xo = XMLOPTION_CONTENT;
else
return NULL; /* reject */
if (doit)
xmloption = xo;
return newval;
}
static bool static bool
assign_tcp_keepalives_idle(int newval, bool doit, GucSource source) assign_tcp_keepalives_idle(int newval, bool doit, GucSource source)
{ {
......
...@@ -406,6 +406,8 @@ ...@@ -406,6 +406,8 @@
#default_transaction_read_only = off #default_transaction_read_only = off
#statement_timeout = 0 # 0 is disabled #statement_timeout = 0 # 0 is disabled
#vacuum_freeze_min_age = 100000000 #vacuum_freeze_min_age = 100000000
#xmlbinary = 'base64'
#xmloption = 'content'
# - Locale and Formatting - # - Locale and Formatting -
......
...@@ -7,7 +7,7 @@ ...@@ -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/include/utils/xml.h,v 1.12 2007/01/20 09:27:20 petere Exp $ * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.13 2007/01/25 11:53:51 petere Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -34,6 +34,14 @@ extern Datum xmlconcat2(PG_FUNCTION_ARGS); ...@@ -34,6 +34,14 @@ extern Datum xmlconcat2(PG_FUNCTION_ARGS);
extern Datum texttoxml(PG_FUNCTION_ARGS); extern Datum texttoxml(PG_FUNCTION_ARGS);
extern Datum xmlvalidate(PG_FUNCTION_ARGS); extern Datum xmlvalidate(PG_FUNCTION_ARGS);
typedef enum
{
XML_STANDALONE_YES,
XML_STANDALONE_NO,
XML_STANDALONE_NO_VALUE,
XML_STANDALONE_OMITTED
} XmlStandaloneType;
extern xmltype *xmlconcat(List *args); extern xmltype *xmlconcat(List *args);
extern xmltype *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext); extern xmltype *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext);
extern xmltype *xmlparse(text *data, bool is_doc, bool preserve_whitespace); extern xmltype *xmlparse(text *data, bool is_doc, bool preserve_whitespace);
...@@ -53,4 +61,12 @@ typedef enum ...@@ -53,4 +61,12 @@ typedef enum
extern XmlBinaryType xmlbinary; extern XmlBinaryType xmlbinary;
typedef enum
{
XMLOPTION_DOCUMENT,
XMLOPTION_CONTENT
} XmlOptionType;
extern XmlOptionType xmloption;
#endif /* XML_H */ #endif /* XML_H */
...@@ -53,13 +53,19 @@ SELECT xmlconcat('hello', 'you'); ...@@ -53,13 +53,19 @@ SELECT xmlconcat('hello', 'you');
(1 row) (1 row)
SELECT xmlconcat(1, 2); SELECT xmlconcat(1, 2);
ERROR: argument of XMLCONCAT must be type xml, not type integer ERROR: argument of XMLCONCAT must be type "xml", not type integer
SELECT xmlconcat('bad', '<syntax'); SELECT xmlconcat('bad', '<syntax');
ERROR: invalid XML content ERROR: invalid XML content
DETAIL: Entity: line 1: parser error : Couldn't find end of Start Tag syntax line 1 DETAIL: Entity: line 1: parser error : Couldn't find end of Start Tag syntax line 1
<syntax <syntax
^ ^
SELECT xmlconcat('<foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>'); SELECT xmlconcat('<foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>');
xmlconcat
--------------
<foo/><bar/>
(1 row)
SELECT xmlconcat('<?xml version="1.1"?><foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>');
xmlconcat xmlconcat
----------------------------------- -----------------------------------
<?xml version="1.1"?><foo/><bar/> <?xml version="1.1"?><foo/><bar/>
...@@ -205,23 +211,48 @@ SELECT xmlroot(xml '<foo/>', version no value, standalone no value); ...@@ -205,23 +211,48 @@ SELECT xmlroot(xml '<foo/>', version no value, standalone no value);
xmlroot xmlroot
--------- ---------
<foo/> <foo/>
(1 row) (1 row)
SELECT xmlroot(xml '<foo/>', version '2.0'); SELECT xmlroot(xml '<foo/>', version '2.0');
xmlroot xmlroot
----------------------- -----------------------------
<?xml version="2.0"?> <?xml version="2.0"?><foo/>
<foo/> (1 row)
SELECT xmlroot(xml '<foo/>', version no value, standalone yes);
xmlroot
----------------------------------------------
<?xml version="1.0" standalone="yes"?><foo/>
(1 row)
SELECT xmlroot(xml '<?xml version="1.1"?><foo/>', version no value, standalone yes);
xmlroot
----------------------------------------------
<?xml version="1.0" standalone="yes"?><foo/>
(1 row) (1 row)
SELECT xmlroot(xmlroot(xml '<foo/>', version '1.0'), version '1.1', standalone no); SELECT xmlroot(xmlroot(xml '<foo/>', version '1.0'), version '1.1', standalone no);
xmlroot xmlroot
--------------------------------------- ---------------------------------------------
<?xml version="1.1" standalone="no"?> <?xml version="1.1" standalone="no"?><foo/>
(1 row)
SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value, standalone no);
xmlroot
---------------------------------------------
<?xml version="1.0" standalone="no"?><foo/>
(1 row)
SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value, standalone no value);
xmlroot
---------
<foo/> <foo/>
(1 row)
SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value);
xmlroot
----------------------------------------------
<?xml version="1.0" standalone="yes"?><foo/>
(1 row) (1 row)
SELECT xmlroot ( SELECT xmlroot (
...@@ -239,11 +270,9 @@ SELECT xmlroot ( ...@@ -239,11 +270,9 @@ SELECT xmlroot (
version '1.0', version '1.0',
standalone yes standalone yes
); );
xmlroot xmlroot
---------------------------------------------------- ------------------------------------------------------------------------------------------
<?xml version="1.0" standalone="yes"?> <?xml version="1.0" standalone="yes"?><gazonk name="val" num="2"><qux>foo</qux></gazonk>
<gazonk name="val" num="2"><qux>foo</qux></gazonk>
(1 row) (1 row)
SELECT xmlserialize(content data as character varying) FROM xmltest; SELECT xmlserialize(content data as character varying) FROM xmltest;
...@@ -313,3 +342,29 @@ SELECT xmlpi(name "123"); ...@@ -313,3 +342,29 @@ SELECT xmlpi(name "123");
<?_x0031_23?> <?_x0031_23?>
(1 row) (1 row)
PREPARE foo (xml) AS SELECT xmlconcat('<foo/>', $1);
SET XML OPTION DOCUMENT;
EXECUTE foo ('<bar/>');
xmlconcat
--------------
<foo/><bar/>
(1 row)
EXECUTE foo ('bad');
ERROR: invalid XML document
DETAIL: Entity: line 1: parser error : Start tag expected, '<' not found
bad
^
SET XML OPTION CONTENT;
EXECUTE foo ('<bar/>');
xmlconcat
--------------
<foo/><bar/>
(1 row)
EXECUTE foo ('good');
xmlconcat
------------
<foo/>good
(1 row)
...@@ -30,11 +30,13 @@ ERROR: no XML support in this installation ...@@ -30,11 +30,13 @@ ERROR: no XML support in this installation
SELECT xmlconcat('hello', 'you'); SELECT xmlconcat('hello', 'you');
ERROR: no XML support in this installation ERROR: no XML support in this installation
SELECT xmlconcat(1, 2); SELECT xmlconcat(1, 2);
ERROR: argument of XMLCONCAT must be type xml, not type integer ERROR: argument of XMLCONCAT must be type "xml", not type integer
SELECT xmlconcat('bad', '<syntax'); SELECT xmlconcat('bad', '<syntax');
ERROR: no XML support in this installation ERROR: no XML support in this installation
SELECT xmlconcat('<foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>'); SELECT xmlconcat('<foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>');
ERROR: no XML support in this installation ERROR: no XML support in this installation
SELECT xmlconcat('<?xml version="1.1"?><foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>');
ERROR: no XML support in this installation
SELECT xmlelement(name element, SELECT xmlelement(name element,
xmlattributes (1 as one, 'deuce' as two), xmlattributes (1 as one, 'deuce' as two),
'content'); 'content');
...@@ -92,8 +94,18 @@ SELECT xmlroot(xml '<foo/>', version no value, standalone no value); ...@@ -92,8 +94,18 @@ SELECT xmlroot(xml '<foo/>', version no value, standalone no value);
ERROR: no XML support in this installation ERROR: no XML support in this installation
SELECT xmlroot(xml '<foo/>', version '2.0'); SELECT xmlroot(xml '<foo/>', version '2.0');
ERROR: no XML support in this installation ERROR: no XML support in this installation
SELECT xmlroot(xml '<foo/>', version no value, standalone yes);
ERROR: no XML support in this installation
SELECT xmlroot(xml '<?xml version="1.1"?><foo/>', version no value, standalone yes);
ERROR: no XML support in this installation
SELECT xmlroot(xmlroot(xml '<foo/>', version '1.0'), version '1.1', standalone no); SELECT xmlroot(xmlroot(xml '<foo/>', version '1.0'), version '1.1', standalone no);
ERROR: no XML support in this installation ERROR: no XML support in this installation
SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value, standalone no);
ERROR: no XML support in this installation
SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value, standalone no value);
ERROR: no XML support in this installation
SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value);
ERROR: no XML support in this installation
SELECT xmlroot ( SELECT xmlroot (
xmlelement ( xmlelement (
name gazonk, name gazonk,
...@@ -144,3 +156,15 @@ SELECT xmlpi(name ":::_xml_abc135.%-&_"); ...@@ -144,3 +156,15 @@ SELECT xmlpi(name ":::_xml_abc135.%-&_");
ERROR: no XML support in this installation ERROR: no XML support in this installation
SELECT xmlpi(name "123"); SELECT xmlpi(name "123");
ERROR: no XML support in this installation ERROR: no XML support in this installation
PREPARE foo (xml) AS SELECT xmlconcat('<foo/>', $1);
ERROR: no XML support in this installation
SET XML OPTION DOCUMENT;
EXECUTE foo ('<bar/>');
ERROR: prepared statement "foo" does not exist
EXECUTE foo ('bad');
ERROR: prepared statement "foo" does not exist
SET XML OPTION CONTENT;
EXECUTE foo ('<bar/>');
ERROR: prepared statement "foo" does not exist
EXECUTE foo ('good');
ERROR: prepared statement "foo" does not exist
...@@ -25,6 +25,7 @@ SELECT xmlconcat('hello', 'you'); ...@@ -25,6 +25,7 @@ SELECT xmlconcat('hello', 'you');
SELECT xmlconcat(1, 2); SELECT xmlconcat(1, 2);
SELECT xmlconcat('bad', '<syntax'); SELECT xmlconcat('bad', '<syntax');
SELECT xmlconcat('<foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>'); SELECT xmlconcat('<foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>');
SELECT xmlconcat('<?xml version="1.1"?><foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>');
SELECT xmlelement(name element, SELECT xmlelement(name element,
...@@ -69,7 +70,13 @@ SELECT xmlpi(name foo, ' bar'); ...@@ -69,7 +70,13 @@ SELECT xmlpi(name foo, ' bar');
SELECT xmlroot(xml '<foo/>', version no value, standalone no value); SELECT xmlroot(xml '<foo/>', version no value, standalone no value);
SELECT xmlroot(xml '<foo/>', version '2.0'); SELECT xmlroot(xml '<foo/>', version '2.0');
SELECT xmlroot(xml '<foo/>', version no value, standalone yes);
SELECT xmlroot(xml '<?xml version="1.1"?><foo/>', version no value, standalone yes);
SELECT xmlroot(xmlroot(xml '<foo/>', version '1.0'), version '1.1', standalone no); SELECT xmlroot(xmlroot(xml '<foo/>', version '1.0'), version '1.1', standalone no);
SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value, standalone no);
SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value, standalone no value);
SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value);
SELECT xmlroot ( SELECT xmlroot (
xmlelement ( xmlelement (
...@@ -107,3 +114,14 @@ SELECT xmlelement(name employees, xmlagg(xmlelement(name name, name))) FROM emp; ...@@ -107,3 +114,14 @@ SELECT xmlelement(name employees, xmlagg(xmlelement(name name, name))) FROM emp;
SELECT xmlpi(name ":::_xml_abc135.%-&_"); SELECT xmlpi(name ":::_xml_abc135.%-&_");
SELECT xmlpi(name "123"); SELECT xmlpi(name "123");
PREPARE foo (xml) AS SELECT xmlconcat('<foo/>', $1);
SET XML OPTION DOCUMENT;
EXECUTE foo ('<bar/>');
EXECUTE foo ('bad');
SET XML OPTION CONTENT;
EXECUTE foo ('<bar/>');
EXECUTE foo ('good');
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment