OSDN Git Service

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 94fc8fd..2a95d8f 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 47c2e5c..f0ba6c3 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>
@@ -3475,6 +3475,24 @@ XMLSERIALIZE ( { DOCUMENT | CONTENT } <replaceable>value</replaceable> AS <repla
    </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.
     When using the text mode to pass queries to the server and query
index 60f9d35..75a9e42 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 217b1a0..c256d73 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 b8607c7..368f3e0 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 394a507..a807ef1 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 3b283d2..d9a9ce5 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;
@@ -972,6 +938,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
  *
  * TODO maybe, libxml2's xmlreader is better? (do not construct DOM, yet do not use SAX - see xml_reader.c)
index 9f2cdc4..dad2898 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;
@@ -2293,6 +2295,16 @@ static struct config_string ConfigureNamesString[] =
        },
 
        {
+               {"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."),
                        NULL,
@@ -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 3b9730c..b3c24fb 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 b580fff..f5b3351 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 d91d765..0c08667 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 dd35f1b..89124eb 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 8e32183..b3117c2 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');