Various fixes in the logic of XML functions:
authorPeter Eisentraut <peter_e@gmx.net>
Thu, 25 Jan 2007 11:53:52 +0000 (11:53 +0000)
committerPeter Eisentraut <peter_e@gmx.net>
Thu, 25 Jan 2007 11:53:52 +0000 (11:53 +0000)
- 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.

13 files changed:
doc/src/sgml/config.sgml
doc/src/sgml/datatype.sgml
src/backend/executor/execQual.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/parser/parse_expr.c
src/backend/utils/adt/xml.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/include/utils/xml.h
src/test/regress/expected/xml.out
src/test/regress/expected/xml_1.out
src/test/regress/sql/xml.sql

index 94fc8fd79aa45388f73215f3466dd0f4e894d1f3..2a95d8fae73e06379d592679cdc5f6e512f7caca 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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">
   <title>Server Configuration</title>
@@ -3558,6 +3558,38 @@ SELECT * FROM parent WHERE key = 2400;
       </listitem>
      </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>
     </sect2>
      <sect2 id="runtime-config-client-format">
index 47c2e5c74af35c043b12982222d5dd03ba18e24f..f0ba6c32c715ee3351265b8310f7fbc9d040dadd 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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">
   <title id="datatype-title">Data Types</title>
@@ -3474,6 +3474,24 @@ XMLSERIALIZE ( { DOCUMENT | CONTENT } <replaceable>value</replaceable> AS <repla
     you to simply cast the value.
    </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>
     Care must be taken when dealing with multiple character encodings
     on the client, server, and in the XML data passed through them.
index 60f9d35f1f762b908c72320b7e33a46e6a1cf15b..75a9e42a24df3ac1fb97a640990197c6173f73e9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,
 
                e = (ExprState *) lthird(xmlExpr->args);
                value = ExecEvalExpr(e, econtext, &isnull, NULL);
-               if (isnull)
-                   standalone = 0;
-               else
-                   standalone = (DatumGetBool(value) ? 1 : -1);
+               standalone = DatumGetInt32(value);
 
                *isNull = false;
 
index 217b1a0465961e14ea434e5d0d7eeb6c47f893b3..c256d73eadb084c5b1623ae842645b24a4158a2f 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * 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
  *   AUTHOR            DATE            MAJOR EVENT
@@ -60,6 +60,7 @@
 #include "utils/date.h"
 #include "utils/datetime.h"
 #include "utils/numeric.h"
+#include "utils/xml.h"
 
 
 /* Location tracking support --- simpler than bison's default */
@@ -439,7 +440,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 
    WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE
 
-   XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLFOREST XMLPARSE
+   XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLFOREST XMLPARSE
    XMLPI XMLROOT XMLSERIALIZE
 
    YEAR_P YES_P
@@ -1112,6 +1113,13 @@ set_rest:  var_name TO var_list_or_default
                    n->args = NIL;
                    $$ = 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:
@@ -7938,21 +7946,13 @@ xml_root_version: VERSION_P a_expr
        ;
 
 opt_xml_root_standalone: ',' STANDALONE_P YES_P
-               { $$ = (Node *) makeBoolAConst(true); }
+               { $$ = (Node *) makeIntConst(XML_STANDALONE_YES); }
            | ',' STANDALONE_P NO
-               { $$ = (Node *) makeBoolAConst(false); }
+               { $$ = (Node *) makeIntConst(XML_STANDALONE_NO); }
            | ',' STANDALONE_P NO VALUE_P
-               {
-                   A_Const *val = makeNode(A_Const);
-                   val->val.type = T_Null;
-                   $$ = (Node *) val;
-               }
+               { $$ = (Node *) makeIntConst(XML_STANDALONE_NO_VALUE); }
            | /*EMPTY*/
-               {
-                   A_Const *val = makeNode(A_Const);
-                   val->val.type = T_Null;
-                   $$ = (Node *) val;
-               }
+               { $$ = (Node *) makeIntConst(XML_STANDALONE_OMITTED); }
        ;
 
 xml_attributes: XMLATTRIBUTES '(' xml_attribute_list ')'   { $$ = $3; }
@@ -8864,6 +8864,7 @@ unreserved_keyword:
            | WITHOUT
            | WORK
            | WRITE
+           | XML_P
            | YEAR_P
            | YES_P
            | ZONE
index b8607c7c002a3dab01c139212df2a4b808d3ba18..368f3e06947972e98cc00c34508431f6b6b40b3f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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[] = {
    {"without", WITHOUT},
    {"work", WORK},
    {"write", WRITE},
+   {"xml", XML_P},
    {"xmlattributes", XMLATTRIBUTES},
    {"xmlconcat", XMLCONCAT},
    {"xmlelement", XMLELEMENT},
index 394a507f2ef11949206debd9fe396919a44bf2d6..a807ef12de4d36fb29c64c2c01d0de133470a207 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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)
                    newe = coerce_to_specific_type(pstate, newe, TEXTOID,
                                                   "XMLROOT");
                else
-                   newe = coerce_to_boolean(pstate, newe, "XMLROOT");
+                   newe = coerce_to_specific_type(pstate, newe, INT4OID,
+                                                  "XMLROOT");
                break;
            case IS_DOCUMENT:
                newe = coerce_to_specific_type(pstate, newe, XMLOID,
index 3b283d247f0f1688e9fcdeafba15139a85ac3b21..d9a9ce5bc369cf0702eac945c0715d2db0647110 100644 (file)
@@ -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.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,
                                    const char *msg, int errcode);
 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);
 
 #endif /* USE_LIBXML */
 
 XmlBinaryType xmlbinary;
+XmlOptionType xmloption;
+
 
 #define NO_XML_SUPPORT() \
    ereport(ERROR, \
@@ -97,7 +100,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, false, true, NULL);
+   doc = xml_parse(vardata, (xmloption == XMLOPTION_DOCUMENT), true, NULL);
    xmlFreeDoc(doc);
 
    PG_RETURN_XML_P(vardata);
@@ -129,48 +132,13 @@ xml_out_internal(xmltype *x, pg_enc target_encoding)
    str[len] = '\0';
 
 #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)
    {
        StringInfoData buf;
 
        initStringInfo(&buf);
 
-       if ((version && strcmp((char *) version, PG_XML_DEFAULT_VERSION) != 0)
-           || (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 (!print_xml_decl(&buf, version, target_encoding, standalone))
        {
            /*
             * If we are not going to produce an XML declaration, eat
@@ -231,7 +199,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, false, true, encoding);
+   doc = xml_parse(result, (xmloption == XMLOPTION_DOCUMENT), true, encoding);
    xmlFreeDoc(doc);
 
    newstr = (char *) pg_do_encoding_conversion((unsigned char *) str,
@@ -296,6 +264,7 @@ stringinfo_to_xmltype(StringInfo buf)
 }
 
 
+#ifdef NOT_USED
 static xmltype *
 cstring_to_xmltype(const char *string)
 {
@@ -309,6 +278,7 @@ cstring_to_xmltype(const char *string)
 
    return result;
 }
+#endif
 
 
 static xmltype *
@@ -394,9 +364,11 @@ xmlconcat(List *args)
        if (standalone < 0)
            global_standalone = -1;
 
-       if (!global_version)
+       if (!version)
+           global_version_no_value = true;
+       else if (!global_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;
 
        appendStringInfoString(&buf, str + len);
@@ -409,17 +381,10 @@ xmlconcat(List *args)
 
        initStringInfo(&buf2);
 
-       if (!global_version_no_value && global_version)
-           appendStringInfo(&buf2, "<?xml version=\"%s\"", global_version);
-       else
-           appendStringInfo(&buf2, "<?xml version=\"%s\"", PG_XML_DEFAULT_VERSION);
-
-       if (global_standalone == 1)
-           appendStringInfoString(&buf2, " standalone=\"yes\"");
-       else if (global_standalone == 0)
-           appendStringInfoString(&buf2, " standalone=\"no\"");
-
-       appendStringInfoString(&buf2, "?>");
+       print_xml_decl(&buf2,
+                      (!global_version_no_value && global_version) ? global_version : NULL,
+                      0,
+                      global_standalone);
 
        appendStringInfoString(&buf2, buf.data);
        buf = buf2;
@@ -458,7 +423,7 @@ texttoxml(PG_FUNCTION_ARGS)
 {
    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 *
 xmlroot(xmltype *data, text *version, int standalone)
 {
 #ifdef USE_LIBXML
-   xmltype    *result;
-   xmlDocPtr   doc;
-   xmlBufferPtr buffer;
-   xmlSaveCtxtPtr save;
+   char       *str;
+   size_t      len;
+   xmlChar    *orig_version;
+   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)
-       doc->version = xmlStrdup(xml_text2xmlChar(version));
+       orig_version = xml_text2xmlChar(version);
    else
-       doc->version = NULL;
+       orig_version = NULL;
 
    switch (standalone)
    {
-       case 1:
-           doc->standalone = 1;
+       case XML_STANDALONE_YES:
+           orig_standalone = 1;
+           break;
+       case XML_STANDALONE_NO:
+           orig_standalone = 0;
            break;
-       case -1:
-           doc->standalone = 0;
+       case XML_STANDALONE_NO_VALUE:
+           orig_standalone = -1;
            break;
-       default:
-           doc->standalone = -1;
+       case XML_STANDALONE_OMITTED:
+           /* leave original value */
            break;
    }
 
-   buffer = xmlBufferCreate();
-   save = xmlSaveToBuffer(buffer, "UTF-8", 0);
-   xmlSaveDoc(save, doc);
-   xmlSaveClose(save);
-
-   xmlFreeDoc(doc);
+   initStringInfo(&buf);
+   print_xml_decl(&buf, orig_version, 0, orig_standalone);
+   appendStringInfoString(&buf, str + len);
 
-   result = cstring_to_xmltype((char *) pg_do_encoding_conversion((unsigned char *) xmlBufferContent(buffer),
-                                                                  xmlBufferLength(buffer),
-                                                                  PG_UTF8,
-                                                                  GetDatabaseEncoding()));
-   xmlBufferFree(buffer);
-   return result;
+   return stringinfo_to_xmltype(&buf);
 #else
    NO_XML_SUPPORT();
    return NULL;
@@ -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
  *
index 9f2cdc43f77130bd28be296cd6105e6f1b7b5c3d..dad28983336307855599440d7f944ec38737b0cd 100644 (file)
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * 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
 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_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_interval(int newval, bool doit, GucSource source);
@@ -233,6 +234,7 @@ static char *XactIsoLevel_string;
 static char *data_directory;
 static char *custom_variable_classes;
 static char *xmlbinary_string;
+static char *xmloption_string;
 static int max_function_args;
 static int max_index_keys;
 static int max_identifier_length;
@@ -2292,6 +2294,16 @@ static struct config_string ConfigureNamesString[] =
        "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,
            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)
    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
 assign_tcp_keepalives_idle(int newval, bool doit, GucSource source)
 {
index 3b9730c75bbf07ab3b0fad7ce330fbf713624c87..b3c24fb2c5f538c412280b0cc75a1eabb97bfbfb 100644 (file)
 #default_transaction_read_only = off
 #statement_timeout = 0         # 0 is disabled
 #vacuum_freeze_min_age = 100000000
+#xmlbinary = 'base64'
+#xmloption = 'content'
 
 # - Locale and Formatting -
 
index b580fffd82c04494b8162ae754dff5d57112c154..f5b33512cfdfc0f1b720099a71050e5d4880509b 100644 (file)
@@ -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.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);
 extern Datum texttoxml(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 *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext);
 extern xmltype *xmlparse(text *data, bool is_doc, bool preserve_whitespace);
@@ -53,4 +61,12 @@ typedef enum
 
 extern XmlBinaryType xmlbinary;
 
+typedef enum
+{
+   XMLOPTION_DOCUMENT,
+   XMLOPTION_CONTENT
+} XmlOptionType;
+
+extern XmlOptionType xmloption;
+
 #endif /* XML_H */
index d91d76530361db24f63442645ba977731f15b22d..0c08667706884fc334e90276c307b40fc9ca2ad6 100644 (file)
@@ -53,13 +53,19 @@ SELECT xmlconcat('hello', 'you');
 (1 row)
 
 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');
 ERROR:  invalid XML content
 DETAIL:  Entity: line 1: parser error : Couldn't find end of Start Tag syntax line 1
 <syntax
        ^
 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             
 -----------------------------------
  <?xml version="1.1"?><foo/><bar/>
@@ -205,23 +211,48 @@ SELECT xmlroot(xml '<foo/>', version no value, standalone no value);
  xmlroot 
 ---------
  <foo/>
 (1 row)
 
 SELECT xmlroot(xml '<foo/>', version '2.0');
-        xmlroot        
------------------------
- <?xml version="2.0"?>
- <foo/>
+           xmlroot           
+-----------------------------
+ <?xml version="2.0"?><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)
 
 SELECT xmlroot(xmlroot(xml '<foo/>', version '1.0'), version '1.1', standalone no);
-                xmlroot                
----------------------------------------
- <?xml version="1.1" standalone="no"?>
+                   xmlroot                   
+---------------------------------------------
+ <?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/>
+(1 row)
+
+SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value);
+                   xmlroot                    
+----------------------------------------------
+ <?xml version="1.0" standalone="yes"?><foo/>
 (1 row)
 
 SELECT xmlroot (
@@ -239,11 +270,9 @@ SELECT xmlroot (
   version '1.0',
   standalone yes
 );
-                      xmlroot                       
-----------------------------------------------------
- <?xml version="1.0" standalone="yes"?>
- <gazonk name="val" num="2"><qux>foo</qux></gazonk>
+                                         xmlroot                                          
+------------------------------------------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?><gazonk name="val" num="2"><qux>foo</qux></gazonk>
 (1 row)
 
 SELECT xmlserialize(content data as character varying) FROM xmltest;
@@ -313,3 +342,29 @@ SELECT xmlpi(name "123");
  <?_x0031_23?>
 (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)
+
index dd35f1bf4e71a5405d3181a61e3f1c199be4ea86..89124ebb98e2874c738b2d2108e9f74896e33799 100644 (file)
@@ -30,11 +30,13 @@ ERROR:  no XML support in this installation
 SELECT xmlconcat('hello', 'you');
 ERROR:  no XML support in this installation
 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');
 ERROR:  no XML support in this installation
 SELECT xmlconcat('<foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>');
 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,
                   xmlattributes (1 as one, 'deuce' as two),
                   'content');
@@ -92,8 +94,18 @@ SELECT xmlroot(xml '<foo/>', version no value, standalone no value);
 ERROR:  no XML support in this installation
 SELECT xmlroot(xml '<foo/>', version '2.0');
 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);
 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 (
   xmlelement (
     name gazonk,
@@ -144,3 +156,15 @@ SELECT xmlpi(name ":::_xml_abc135.%-&_");
 ERROR:  no XML support in this installation
 SELECT xmlpi(name "123");
 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
index 8e321831596b76c4369281afd690e0f9e605cbfb..b3117c2424f2f073451a6b0b9b3a72637b04c06f 100644 (file)
@@ -25,6 +25,7 @@ SELECT xmlconcat('hello', 'you');
 SELECT xmlconcat(1, 2);
 SELECT xmlconcat('bad', '<syntax');
 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,
@@ -69,7 +70,13 @@ SELECT xmlpi(name foo, '   bar');
 
 SELECT xmlroot(xml '<foo/>', version no value, standalone no value);
 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('<?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 (
   xmlelement (
@@ -107,3 +114,14 @@ SELECT xmlelement(name employees, xmlagg(xmlelement(name name, name))) FROM emp;
 
 SELECT xmlpi(name ":::_xml_abc135.%-&_");
 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');