pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Chapman Flack <chap@anastigmatix.net>
To: Pavel Stehule <pavel.stehule@gmail.com>
Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Subject: Re: PostgreSQL vs SQL/XML Standards
Date: Thu, 24 Jan 2019 23:46:29 -0500
Message-ID: <5C4A94A5.8010402@anastigmatix.net> (raw)
In-Reply-To: <CAFj8pRCLXAU5wsdLksh4z8_2Sz5baVM9HOZntG1UqQ9tGrjkPQ@mail.gmail.com>
References: <CAFj8pRDFQJMoKf-X+3p1ASwU02nnXfAXrg83Z3fHUBd-Gwuuxw@mail.gmail.com>
	<CAFj8pRDRrxyr8=2PEFxP_eZCRqJMU4Jpj0whJM9pX0V4Q4EQvw@mail.gmail.com>
	<5C291741.9060506@anastigmatix.net>
	<CAFj8pRAmSUO1g1qnVgXmE=7bPULYjwDfyO5KDohsHMFesDenmA@mail.gmail.com>
	<5C297BB7.9070509@anastigmatix.net>
	<CAFj8pRCN=0k507nTfvNQuruHvomA4SiC6uGUXY5Gz0crxf-JAQ@mail.gmail.com>
	<5C3A7A90.3070207@anastigmatix.net>
	<CAFj8pRCSa0ekwc8AOUnEsLeJhWbq7_rOyFzruopfgBTaMpUWMA@mail.gmail.com>
	<5C43FAEB.2070000@anastigmatix.net>
	<CAFj8pRDLywawDY-dRsYdjF036iR_Y4em77X1DF7VodUqau_0Vg@mail.gmail.com>
	<5C4401BE.4080606@anastigmatix.net>
	<CAFj8pRCKwDGqP55RTykDq87DXuXStE+3Qy2Hf9Wxh_A-cUnwTw@mail.gmail.com>
	<5C449C71.7060903@anastigmatix.net>
	<CAFj8pRAabQWW13FS58fj1h6YxKsbhEaegRctf28TFeheWGn_3A@mail.gmail.com>
	<5C44B332.6070609@anastigmatix.net>
	<CAFj8pRAfjUJZX3so2TooQvag2C+5b1+sm4rzZuJkuO5UtDtkhg@mail.gmail.com>
	<5C44F26D.5010703@anastigmatix.net>
	<CAFj8pRCLXAU5wsdLksh4z8_2Sz5baVM9HOZntG1UqQ9tGrjkPQ@mail.gmail.com>

Hi,

On 01/21/19 01:33, Pavel Stehule wrote:
> ne 20. 1. 2019 23:13 odesílatel Chapman Flack <chap@anastigmatix.net>
> napsal:

>> form (whether or not the word LATERAL is used), and re-executes xmltable
>> whenever the referenced column value changes. In that case, whether the
>> default argument is evaluated at function entry or later doesn't seem
>> to matter: the function is re-executed, so evaluating the new default
>> at the time of entry is sufficient.
> 
> it has sense only for volatile functions. it was not often. On second hand
> deferred evaluation shoul not be a problem, and more, it is evaluated only
> when it is necessary, what is more sensible for me.

That makes sense. I trimmed the language about input rows and referring to
earlier columns, and put more emphasis on the usefulness of evaluating
volatile functions only when needed.

I am:

- re-attaching xmltable-xpath-result-processing-bugfix-5.patch unchanged
  (just so CF app does not lose track)

- re-attaching xmltable-xmlexists-passing-mechanisms-1.patch unchanged

- attaching for the first time xml-functions-type-docfix-1.patch

The doc patch is made to go on top of the passing-mechanisms patch
(there were some doc changes in passing-mechanisms, changed again in
the new patch to be links to the added Limits and Compatibility section).

I hope the patched docs describe accurately what we have at this point.

Regards,
-Chap

Attachments:

  [text/x-patch] xmltable-xpath-result-processing-bugfix-5.patch (14.2K, ../5C4A94A5.8010402@anastigmatix.net/2-xmltable-xpath-result-processing-bugfix-5.patch)
  download | inline diff:
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 90d67f1acf..06f3f69073 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -10999,9 +10999,9 @@ $$ AS data;
 
 SELECT xmltable.*
   FROM xmlelements, XMLTABLE('/root' PASSING data COLUMNS element text);
-       element        
-----------------------
-   Hello2a2   bbbCC  
+         element         
+-------------------------
+   Hello2a2   bbbxxxCC  
 ]]></screen>
     </para>
 
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 1cec168b2a..6d4ba50752 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -3674,15 +3674,15 @@ SPI_sql_row_to_xmlelement(uint64 rownum, StringInfo result, char *tablename,
 #ifdef USE_LIBXML
 
 /*
- * Convert XML node to text (dump subtree in case of element,
- * return value otherwise)
+ * Convert XML node to text (dump subtree), for attribute and text
+ * returns escaped text.
  */
 static text *
 xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
 {
 	xmltype    *result;
 
-	if (cur->type == XML_ELEMENT_NODE)
+	if (cur->type != XML_ATTRIBUTE_NODE && cur->type != XML_TEXT_NODE)
 	{
 		xmlBufferPtr buf;
 		xmlNodePtr	cur_copy;
@@ -4427,6 +4427,37 @@ XmlTableFetchRow(TableFuncScanState *state)
 #endif							/* not USE_LIBXML */
 }
 
+#ifdef USE_LIBXML
+/*
+ * Copy XmlChar string to PostgreSQL memory. Ensure releasing of
+ * source xmllib string.
+ */
+static char *
+copy_and_safe_free_xmlchar(xmlChar *str)
+{
+	char *result;
+
+	if (str)
+	{
+		PG_TRY();
+		{
+			result = pstrdup((char *) str);
+		}
+		PG_CATCH();
+		{
+			xmlFree(str);
+			PG_RE_THROW();
+		}
+		PG_END_TRY();
+		xmlFree(str);
+	}
+	else
+		result = NULL;
+
+	return result;
+}
+#endif
+
 /*
  * XmlTableGetValue
  *		Return the value for column number 'colnum' for the current row.  If
@@ -4475,9 +4506,9 @@ XmlTableGetValue(TableFuncScanState *state, int colnum,
 		/*
 		 * There are four possible cases, depending on the number of nodes
 		 * returned by the XPath expression and the type of the target column:
-		 * a) XPath returns no nodes.  b) One node is returned, and column is
-		 * of type XML.  c) One node, column type other than XML.  d) Multiple
-		 * nodes are returned.
+		 * a) XPath returns no nodes.  b) The target type is XML (return all
+		 * as XML).  For non-XML types:  c) One node (return content).
+		 * d) Multiple nodes (error).
 		 */
 		if (xpathobj->type == XPATH_NODESET)
 		{
@@ -4490,84 +4521,76 @@ XmlTableGetValue(TableFuncScanState *state, int colnum,
 			{
 				*isnull = true;
 			}
-			else if (count == 1 && typid == XMLOID)
-			{
-				text	   *textstr;
-
-				/* simple case, result is one value */
-				textstr = xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[0],
-											   xtCxt->xmlerrcxt);
-				cstr = text_to_cstring(textstr);
-			}
-			else if (count == 1)
+			else
 			{
-				xmlChar    *str;
-				xmlNodePtr	node;
-
-				/*
-				 * Most nodes (elements and even attributes) store their data
-				 * in children nodes. If they don't have children nodes, it
-				 * means that they are empty (e.g. <element/>). Text nodes and
-				 * CDATA sections are an exception: they don't have children
-				 * but have content in the Text/CDATA node itself.
-				 */
-				node = xpathobj->nodesetval->nodeTab[0];
-				if (node->type != XML_CDATA_SECTION_NODE &&
-					node->type != XML_TEXT_NODE)
-					node = node->xmlChildrenNode;
-
-				str = xmlNodeListGetString(xtCxt->doc, node, 1);
-				if (str != NULL)
+				if (typid == XMLOID)
 				{
-					PG_TRY();
-					{
-						cstr = pstrdup((char *) str);
-					}
-					PG_CATCH();
+					text	   *textstr;
+					StringInfoData str;
+					int			i;
+
+					/* Concatenate serialized values */
+					initStringInfo(&str);
+					for (i = 0; i < count; i++)
 					{
-						xmlFree(str);
-						PG_RE_THROW();
+						textstr =
+							xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
+												 xtCxt->xmlerrcxt);
+
+						appendStringInfoText(&str, textstr);
 					}
-					PG_END_TRY();
-					xmlFree(str);
+					cstr = str.data;
 				}
 				else
 				{
-					/* Ensure mapping of empty tags to PostgreSQL values. */
-					cstr = "";
+					xmlChar	   *str;
+
+					if (count > 1)
+						ereport(ERROR,
+								(errcode(ERRCODE_CARDINALITY_VIOLATION),
+								 errmsg("more than one value returned by column XPath expression")));
+
+					str = xmlXPathCastNodeSetToString(xpathobj->nodesetval);
+					if (str)
+						cstr = copy_and_safe_free_xmlchar(str);
+					else
+						/* empty element */
+						cstr = "";
 				}
 			}
+		}
+		else if (xpathobj->type == XPATH_STRING)
+		{
+			/* Content should be escaped when target will be XML */
+			if (typid == XMLOID)
+				cstr = escape_xml((char *) xpathobj->stringval);
 			else
-			{
-				StringInfoData str;
-				int			i;
+				cstr = (char *) xpathobj->stringval;
 
-				Assert(count > 1);
+		}
+		else if (xpathobj->type == XPATH_BOOLEAN)
+		{
+			char		typcategory;
+			bool		typispreferred;
+			xmlChar	   *str;
 
-				/*
-				 * When evaluating the XPath expression returns multiple
-				 * nodes, the result is the concatenation of them all. The
-				 * target type must be XML.
-				 */
-				if (typid != XMLOID)
-					ereport(ERROR,
-							(errcode(ERRCODE_CARDINALITY_VIOLATION),
-							 errmsg("more than one value returned by column XPath expression")));
+			/* Allow implicit casting from boolean to numbers */
+			get_type_category_preferred(typid, &typcategory, &typispreferred);
 
-				/* Concatenate serialized values */
-				initStringInfo(&str);
-				for (i = 0; i < count; i++)
-				{
-					appendStringInfoText(&str,
-										 xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
-															  xtCxt->xmlerrcxt));
-				}
-				cstr = str.data;
-			}
+			if (typcategory != TYPCATEGORY_NUMERIC)
+				str = xmlXPathCastBooleanToString(xpathobj->boolval);
+			else
+				str = xmlXPathCastNumberToString(
+							xmlXPathCastBooleanToNumber(xpathobj->boolval));
+
+			cstr = copy_and_safe_free_xmlchar(str);
 		}
-		else if (xpathobj->type == XPATH_STRING)
+		else if (xpathobj->type == XPATH_NUMBER)
 		{
-			cstr = (char *) xpathobj->stringval;
+			xmlChar	   *str;
+
+			str = xmlXPathCastNumberToString(xpathobj->floatval);
+			cstr = copy_and_safe_free_xmlchar(str);
 		}
 		else
 			elog(ERROR, "unexpected XPath object type %u", xpathobj->type);
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index 6e1f885112..7b8da9283e 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -1210,9 +1210,9 @@ SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan"
 (2 rows)
 
 SELECT * FROM xmltable('/root' passing '<root><element>a1a<!-- aaaa -->a2a<?aaaaa?> <!--z-->  bbbb<x>xxx</x>cccc</element></root>' COLUMNS element text);
-      element      
--------------------
- a1aa2a   bbbbcccc
+       element        
+----------------------
+ a1aa2a   bbbbxxxcccc
 (1 row)
 
 SELECT * FROM xmltable('/root' passing '<root><element>a1a<!-- aaaa -->a2a<?aaaaa?> <!--z-->  bbbb<x>xxx</x>cccc</element></root>' COLUMNS element text PATH 'element/text()'); -- should fail
@@ -1493,3 +1493,35 @@ SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c
  14
 (4 rows)
 
+-- XPath result can be boolean or number too
+SELECT * FROM XMLTABLE('*' PASSING '<a>a</a>' COLUMNS a xml PATH '.', b text PATH '.', c text PATH '"hi"',  d boolean PATH '. = "a"');
+    a     | b | c  | d 
+----------+---+----+---
+ <a>a</a> | a | hi | t
+(1 row)
+
+SELECT * FROM XMLTABLE('*' PASSING '<a>a</a>' COLUMNS a xml PATH '.', b text PATH '.', c text PATH '"hi"',  d integer PATH 'string-length(.)');
+    a     | b | c  | d 
+----------+---+----+---
+ <a>a</a> | a | hi | 1
+(1 row)
+
+SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>' COLUMNS x xml PATH 'node()');
+                               x                               
+---------------------------------------------------------------
+ pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post
+(1 row)
+
+SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>' COLUMNS x xml PATH '/');
+                                  x                                   
+----------------------------------------------------------------------
+ <e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>+
+ 
+(1 row)
+
+SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH '"<foo/>"', b xml PATH '"<foo/>"');
+   a    |      b       
+--------+--------------
+ <foo/> | &lt;foo/&gt;
+(1 row)
+
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index 0eba424346..62b83fb123 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -1343,3 +1343,28 @@ SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c
 ---
 (0 rows)
 
+-- XPath result can be boolean or number too
+SELECT * FROM XMLTABLE('*' PASSING '<a>a</a>' COLUMNS a xml PATH '.', b text PATH '.', c text PATH '"hi"',  d boolean PATH '. = "a"');
+ERROR:  unsupported XML feature
+LINE 1: SELECT * FROM XMLTABLE('*' PASSING '<a>a</a>' COLUMNS a xml ...
+                                           ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT * FROM XMLTABLE('*' PASSING '<a>a</a>' COLUMNS a xml PATH '.', b text PATH '.', c text PATH '"hi"',  d integer PATH 'string-length(.)');
+ERROR:  unsupported XML feature
+LINE 1: SELECT * FROM XMLTABLE('*' PASSING '<a>a</a>' COLUMNS a xml ...
+                                           ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>' COLUMNS x xml PATH 'node()');
+ERROR:  unsupported XML feature
+LINE 1: SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?...
+                                           ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>' COLUMNS x xml PATH '/');
+ERROR:  unsupported XML feature
+LINE 1: SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?...
+                                           ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out
index 3ec56e4c54..ee6b5a6696 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -1190,9 +1190,9 @@ SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan"
 (2 rows)
 
 SELECT * FROM xmltable('/root' passing '<root><element>a1a<!-- aaaa -->a2a<?aaaaa?> <!--z-->  bbbb<x>xxx</x>cccc</element></root>' COLUMNS element text);
-      element      
--------------------
- a1aa2a   bbbbcccc
+       element        
+----------------------
+ a1aa2a   bbbbxxxcccc
 (1 row)
 
 SELECT * FROM xmltable('/root' passing '<root><element>a1a<!-- aaaa -->a2a<?aaaaa?> <!--z-->  bbbb<x>xxx</x>cccc</element></root>' COLUMNS element text PATH 'element/text()'); -- should fail
@@ -1473,3 +1473,35 @@ SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c
  14
 (4 rows)
 
+-- XPath result can be boolean or number too
+SELECT * FROM XMLTABLE('*' PASSING '<a>a</a>' COLUMNS a xml PATH '.', b text PATH '.', c text PATH '"hi"',  d boolean PATH '. = "a"');
+    a     | b | c  | d 
+----------+---+----+---
+ <a>a</a> | a | hi | t
+(1 row)
+
+SELECT * FROM XMLTABLE('*' PASSING '<a>a</a>' COLUMNS a xml PATH '.', b text PATH '.', c text PATH '"hi"',  d integer PATH 'string-length(.)');
+    a     | b | c  | d 
+----------+---+----+---
+ <a>a</a> | a | hi | 1
+(1 row)
+
+SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>' COLUMNS x xml PATH 'node()');
+                               x                               
+---------------------------------------------------------------
+ pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post
+(1 row)
+
+SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>' COLUMNS x xml PATH '/');
+                                  x                                   
+----------------------------------------------------------------------
+ <e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>+
+ 
+(1 row)
+
+SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH '"<foo/>"', b xml PATH '"<foo/>"');
+   a    |      b       
+--------+--------------
+ <foo/> | &lt;foo/&gt;
+(1 row)
+
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index 3b91b56d5a..8c3a34af81 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -595,3 +595,13 @@ INSERT INTO xmltest2 VALUES('<d><r><dc>2</dc></r></d>', 'D');
 SELECT xmltable.* FROM xmltest2, LATERAL xmltable('/d/r' PASSING x COLUMNS a int PATH '' || lower(_path) || 'c');
 SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c') PASSING x COLUMNS a int PATH '.');
 SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c') PASSING x COLUMNS a int PATH 'x' DEFAULT ascii(_path) - 54);
+
+-- XPath result can be boolean or number too
+SELECT * FROM XMLTABLE('*' PASSING '<a>a</a>' COLUMNS a xml PATH '.', b text PATH '.', c text PATH '"hi"',  d boolean PATH '. = "a"');
+SELECT * FROM XMLTABLE('*' PASSING '<a>a</a>' COLUMNS a xml PATH '.', b text PATH '.', c text PATH '"hi"',  d integer PATH 'string-length(.)');
+
+SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>' COLUMNS x xml PATH 'node()');
+SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n2>&amp;deep</n2>post</e>' COLUMNS x xml PATH '/');
+
+SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH '"<foo/>"', b xml PATH '"<foo/>"');
+

  [text/x-patch] xmltable-xmlexists-passing-mechanisms-1.patch (5.8K, ../5C4A94A5.8010402@anastigmatix.net/3-xmltable-xmlexists-passing-mechanisms-1.patch)
  download | inline diff:
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 06f3f69..df0fe4d 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -10583,7 +10583,7 @@ SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
     </indexterm>
 
 <synopsis>
-<function>XMLEXISTS</function>(<replaceable>text</replaceable> PASSING <optional>BY REF</optional> <replaceable>xml</replaceable> <optional>BY REF</optional>)
+<function>XMLEXISTS</function>(<replaceable>text</replaceable> PASSING <optional>BY { REF | VALUE }</optional> <replaceable>xml</replaceable> <optional>BY { REF | VALUE }</optional>)
 </synopsis>
 
     <para>
@@ -10596,7 +10596,7 @@ SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
     <para>
      Example:
      <screen><![CDATA[
-SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF '<towns><town>Toronto</town><town>Ottawa</town></towns>');
+SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY VALUE '<towns><town>Toronto</town><town>Ottawa</town></towns>');
 
  xmlexists
 ------------
@@ -10606,14 +10606,22 @@ SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF '<towns><town>Tor
     </para>
 
     <para>
-     The <literal>BY REF</literal> clauses have no effect in
-     PostgreSQL, but are allowed for SQL conformance and compatibility
-     with other implementations.  Per SQL standard, the
-     first <literal>BY REF</literal> is required, the second is
-     optional.  Also note that the SQL standard specifies
-     the <function>xmlexists</function> construct to take an XQuery
-     expression as first argument, but PostgreSQL currently only
-     supports XPath, which is a subset of XQuery.
+     The <literal>BY REF</literal> or <literal>BY VALUE</literal> clauses
+     have no effect in PostgreSQL, but are allowed for compatibility
+     with other implementations.  Per SQL standard, the one that precedes
+     any argument is required, and indicates the default for arguments that
+     follow, and one may follow any argument to override the default.
+     PostgreSQL ignores <literal>BY REF</literal> and passes by value always.
+    </para>
+
+    <para>
+     In the SQL standard, an <function>xmlexists</function> construct first
+     appears in SQL:2006, evaluates an expression in the XML Query language,
+     allows passing values for named parameters in the expression as well
+     as for the context item, and does not require the passed values to be
+     documents, or even of XML type. In PostgreSQL, this construct currently
+     only evaluates an XPath 1.0 expression, and allows passing only one value,
+     which must be an XML document, to be the context item.  
     </para>
    </sect3>
 
@@ -10820,7 +10828,7 @@ SELECT xpath_exists('/my:a/text()', '<my:a xmlns:my="http://example.com">test</m
 
 <synopsis>
 <function>xmltable</function>( <optional>XMLNAMESPACES(<replaceable>namespace uri</replaceable> AS <replaceable>namespace name</replaceable><optional>, ...</optional>), </optional>
-          <replaceable>row_expression</replaceable> PASSING <optional>BY REF</optional> <replaceable>document_expression</replaceable> <optional>BY REF</optional>
+          <replaceable>row_expression</replaceable> PASSING <optional>BY { REF | VALUE }</optional> <replaceable>document_expression</replaceable> <optional>BY { REF | VALUE }</optional>
           COLUMNS <replaceable>name</replaceable> { <replaceable>type</replaceable> <optional>PATH <replaceable>column_expression</replaceable></optional> <optional>DEFAULT <replaceable>default_expression</replaceable></optional> <optional>NOT NULL | NULL</optional>
                         | FOR ORDINALITY }
                    <optional>, ...</optional>
@@ -10850,11 +10858,11 @@ SELECT xpath_exists('/my:a/text()', '<my:a xmlns:my="http://example.com">test</m
     <para>
      <replaceable>document_expression</replaceable> provides the XML document to
      operate on.
-     The <literal>BY REF</literal> clauses have no effect in PostgreSQL,
-     but are allowed for SQL conformance and compatibility with other
-     implementations.
      The argument must be a well-formed XML document; fragments/forests
      are not accepted.
+     The <literal>BY REF</literal> or <literal>BY VALUE</literal> clauses are
+     accepted, as described for the <function>xmlexists</function> predicate,
+     but ignored; PostgreSQL currently passes XML by value always.
     </para>
 
     <para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index aeb262a..bade0fe 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -635,8 +635,8 @@ X204	XMLQuery: initializing an XQuery variable			NO
 X205	XMLQuery: EMPTY ON EMPTY option			NO	
 X206	XMLQuery: NULL ON EMPTY option			NO	
 X211	XML 1.1 support			NO	
-X221	XML passing mechanism BY VALUE			NO	
-X222	XML passing mechanism BY REF			YES	
+X221	XML passing mechanism BY VALUE			YES	
+X222	XML passing mechanism BY REF			NO	parser accepts BY REF but ignores it; passing is always BY VALUE
 X231	XML(CONTENT(UNTYPED)) type			NO	
 X232	XML(CONTENT(ANY)) type			NO	
 X241	RETURNING CONTENT in XML publishing			NO	
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c086235..1b303d7 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -13942,20 +13942,25 @@ xmlexists_argument:
 				{
 					$$ = $2;
 				}
-			| PASSING c_expr BY REF
+			| PASSING c_expr xml_passing_mech
 				{
 					$$ = $2;
 				}
-			| PASSING BY REF c_expr
+			| PASSING xml_passing_mech c_expr
 				{
-					$$ = $4;
+					$$ = $3;
 				}
-			| PASSING BY REF c_expr BY REF
+			| PASSING xml_passing_mech c_expr xml_passing_mech
 				{
-					$$ = $4;
+					$$ = $3;
 				}
 		;
 
+xml_passing_mech:
+			BY REF
+			| BY VALUE_P
+		;
+
 
 /*
  * Aggregate decoration clauses

  [text/x-patch] xml-functions-type-docfix-1.patch (30.1K, ../5C4A94A5.8010402@anastigmatix.net/4-xml-functions-type-docfix-1.patch)
  download | inline diff:
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index ed0ee58..f753105 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4269,16 +4269,21 @@ SET XML OPTION { DOCUMENT | CONTENT };
 SET xmloption TO { DOCUMENT | CONTENT };
 </synopsis>
     The default is <literal>CONTENT</literal>, so all forms of XML
-    data are allowed.
+    data are allowed except as noted below.
    </para>
 
    <note>
     <para>
-     With the default XML option setting, you cannot directly cast
-     character strings to type <type>xml</type> if they contain a
-     document type declaration, because the definition of XML content
-     fragment does not accept them.  If you need to do that, either
-     use <literal>XMLPARSE</literal> or change the XML option.
+     In the SQL:2006 and later standard, the <literal>CONTENT</literal> form
+     is a proper superset of the <literal>DOCUMENT</literal> form, and so the
+     default XML option setting would allow casting to XML from character
+     strings in either form. <productname>PostgreSQL</productname>, however,
+     uses the SQL:2003 definition in which <literal>CONTENT</literal> form
+     cannot contain a document type declaration. Therefore, there is no one
+     setting of the XML option that will allow casting to XML from every valid
+     character string. The default will work almost always, but for a document
+     with a DTD, it will be necessary to change the XML option or
+     use <literal>XMLPARSE</literal> specifying <literal>DOCUMENT</literal>.
     </para>
    </note>
 
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index df0fe4d..74a0818 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -10140,8 +10140,13 @@ CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple
 
 
  <sect1 id="functions-xml">
+
   <title>XML Functions</title>
 
+  <indexterm>
+   <primary>XML Functions</primary>
+  </indexterm>
+
   <para>
    The functions and function-like expressions described in this
    section operate on values of type <type>xml</type>.  Check <xref
@@ -10344,8 +10349,8 @@ SELECT xmlelement(name foo, xmlattributes('xyz' as bar),
      encoding, depending on the setting of the configuration parameter
      <xref linkend="guc-xmlbinary"/>.  The particular behavior for
      individual data types is expected to evolve in order to align the
-     SQL and PostgreSQL data types with the XML Schema specification,
-     at which point a more precise description will appear.
+     PostgreSQL mappings with those specified in SQL:2006 and later,
+     as discussed in <xref linkend="functions-xml-limits-casts"/>.
     </para>
    </sect3>
 
@@ -10587,10 +10592,12 @@ SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
 </synopsis>
 
     <para>
-     The function <function>xmlexists</function> returns true if the
-     XPath expression in the first argument returns any nodes, and
-     false otherwise.  (If either argument is null, the result is
-     null.)
+     The function <function>xmlexists</function> evaluates an XPath 1.0
+     expression (the first argument), with the passed value as its context item.
+     The function returns false if the result of that evaluation yields an
+     empty nodeset, true if it yields any other value.  (The function returns
+     null if an argument is null.) A nonnull value passed as the context item
+     must be an XML document, not a content fragment or any non-XML value.
     </para>
 
     <para>
@@ -10607,21 +10614,12 @@ SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY VALUE '<towns><town>T
 
     <para>
      The <literal>BY REF</literal> or <literal>BY VALUE</literal> clauses
-     have no effect in PostgreSQL, but are allowed for compatibility
-     with other implementations.  Per SQL standard, the one that precedes
-     any argument is required, and indicates the default for arguments that
-     follow, and one may follow any argument to override the default.
-     PostgreSQL ignores <literal>BY REF</literal> and passes by value always.
-    </para>
-
-    <para>
+     are accepted in <productname>PostgreSQL</productname>, but ignored, as
+     discussed in <xref linkend="functions-xml-limits-postgresql"/>.
      In the SQL standard, an <function>xmlexists</function> construct first
-     appears in SQL:2006, evaluates an expression in the XML Query language,
-     allows passing values for named parameters in the expression as well
-     as for the context item, and does not require the passed values to be
-     documents, or even of XML type. In PostgreSQL, this construct currently
-     only evaluates an XPath 1.0 expression, and allows passing only one value,
-     which must be an XML document, to be the context item.  
+     appears in SQL:2006 and evaluates an expression in the XML Query language,
+     but this implementation allows only an XPath 1.0 expression, as discussed
+     in <xref linkend="functions-xml-limits-xpath1"/>.
     </para>
    </sect3>
 
@@ -10727,12 +10725,12 @@ SELECT xml_is_well_formed_document('<pg:foo xmlns:pg="http://postgresql.org/stuf
 </synopsis>
 
     <para>
-     The function <function>xpath</function> evaluates the XPath
+     The function <function>xpath</function> evaluates the XPath 1.0
      expression <replaceable>xpath</replaceable> (a <type>text</type> value)
      against the XML value
      <replaceable>xml</replaceable>.  It returns an array of XML values
-     corresponding to the node set produced by the XPath expression.
-     If the XPath expression returns a scalar value rather than a node set,
+     corresponding to the nodeset produced by the XPath expression.
+     If the XPath expression returns a scalar value rather than a nodeset,
      a single-element array is returned.
     </para>
 
@@ -10794,9 +10792,10 @@ SELECT xpath('//mydefns:b/text()', '<a xmlns="http://example.com"><b>test</b></a
     <para>
      The function <function>xpath_exists</function> is a specialized form
      of the <function>xpath</function> function.  Instead of returning the
-     individual XML values that satisfy the XPath, this function returns a
-     Boolean indicating whether the query was satisfied or not.  This
-     function is equivalent to the standard <literal>XMLEXISTS</literal> predicate,
+     individual XML values that satisfy the XPath 1.0 expression, this function
+     returns a Boolean indicating whether the query was satisfied or not
+     (specifically, whether it produced any value other than an empty nodeset).
+     This function is equivalent to the <literal>XMLEXISTS</literal> predicate,
      except that it also offers support for a namespace mapping argument.
     </para>
 
@@ -10837,8 +10836,8 @@ SELECT xpath_exists('/my:a/text()', '<my:a xmlns:my="http://example.com">test</m
 
     <para>
      The <function>xmltable</function> function produces a table based
-     on the given XML value, an XPath filter to extract rows, and an
-     optional set of column definitions.
+     on the given XML value, an XPath filter to extract rows, and a
+     set of column definitions.
     </para>
 
     <para>
@@ -10849,30 +10848,33 @@ SELECT xpath_exists('/my:a/text()', '<my:a xmlns:my="http://example.com">test</m
     </para>
 
     <para>
-     The required <replaceable>row_expression</replaceable> argument is an XPath
-     expression that is evaluated against the supplied XML document to
-     obtain an ordered sequence of XML nodes. This sequence is what
-     <function>xmltable</function> transforms into output rows.
+     The required <replaceable>row_expression</replaceable> argument is
+     an XPath 1.0 expression that is evaluated, passing the
+     <replaceable>document_expression</replaceable> as its context item, to
+     obtain a set of XML nodes. These nodes are what
+     <function>xmltable</function> transforms into output rows. (No rows
+     will be produced if the <replaceable>document_expression</replaceable>
+     is null, or the <replaceable>row_expression</replaceable> produces an
+     empty nodeset, or any value other than a nodeset.)
     </para>
 
     <para>
-     <replaceable>document_expression</replaceable> provides the XML document to
-     operate on.
-     The argument must be a well-formed XML document; fragments/forests
-     are not accepted.
+     <replaceable>document_expression</replaceable> provides the context item
+     for the <replaceable>row_expression</replaceable>. It must be a well-formed
+     XML document; fragments/forests are not accepted.
      The <literal>BY REF</literal> or <literal>BY VALUE</literal> clauses are
-     accepted, as described for the <function>xmlexists</function> predicate,
-     but ignored; PostgreSQL currently passes XML by value always.
+     accepted but ignored, as discussed in
+     <xref linkend="functions-xml-limits-postgresql"/>.
+     In the SQL standard, an <function>xmltable</function> construct first
+     appears in SQL:2006 and evaluates expressions in the XML Query language,
+     but this implementation allows only XPath 1.0 expressions, as discussed
+     in <xref linkend="functions-xml-limits-xpath1"/>.
     </para>
 
     <para>
      The mandatory <literal>COLUMNS</literal> clause specifies the list
      of columns in the output table.
-     If the <literal>COLUMNS</literal> clause is omitted, the rows in the result
-     set contain a single column of type <literal>xml</literal> containing the
-     data matched by <replaceable>row_expression</replaceable>.
-     If <literal>COLUMNS</literal> is specified, each entry describes a
-     single column.
+     Each entry describes a single column.
      See the syntax summary above for the format.
      The column name and type are required; the path, default and
      nullability clauses are optional.
@@ -10886,38 +10888,78 @@ SELECT xpath_exists('/my:a/text()', '<my:a xmlns:my="http://example.com">test</m
     </para>
 
     <para>
-     The <literal>column_expression</literal> for a column is an XPath expression
-     that is evaluated for each row, relative to the result of the
-     <replaceable>row_expression</replaceable>, to find the value of the column.
+     The <literal>column_expression</literal> for a column is an XPath 1.0
+     expression
+     that is evaluated for each row, with the current node from the
+     <replaceable>row_expression</replaceable> result as its context item,
+     to find the value of the column.
      If no <literal>column_expression</literal> is given, then the column name
      is used as an implicit path.
     </para>
 
     <para>
-     If a column's XPath expression returns multiple elements, an error
-     is raised.
-     If the expression matches an empty tag, the result is an
-     empty string (not <literal>NULL</literal>).
-     Any <literal>xsi:nil</literal> attributes are ignored.
+     If a column's XPath expression returns a non-XML value (limited to
+     string, boolean, or double in XPath 1.0) and the column has a
+     PostgreSQL type other than <type>xml</type>, the column will be set
+     as if by assigning the value's string representation to the PostgreSQL
+     type (adjusting the "string representation" of a boolean to
+     <literal>1</literal> or <literal>0</literal> if the target column type
+     category is numeric, otherwise <literal>true</literal> or
+     <literal>false</literal>).
     </para>
 
     <para>
-     The text body of the XML matched by the <replaceable>column_expression</replaceable>
-     is used as the column value. Multiple <literal>text()</literal> nodes
-     within an element are concatenated in order. Any child elements,
-     processing instructions, and comments are ignored, but the text contents
-     of child elements are concatenated to the result.
+     If the column's expression returns a non-empty set of XML nodes
+     and the target column's type is <type>xml</type>, the column will
+     be assigned the expression result exactly, if it is of document or
+     content form.
+     <footnote>
+      <para>
+       A result containing more than one element node at the top level, or
+       non-whitespace text outside of an element, is an example of content form.
+       An XPath result can be of neither form, for example if it returns an
+       attribute node selected from the element that contains it. Such a result
+       will be put into content form with each such disallowed node replaced by
+       its string value, as defined for the XPath 1.0
+       <function>string</function> function.
+      </para>
+     </footnote>
+    </para>
+
+    <para>
+     A non-XML result assigned to an <type>xml</type> output column produces
+     content, a single text node with the string value of the result.
+     An XML result assigned to a column of any other type may not have more than
+     one node, or an error is raised. If there is exactly one node, the column
+     will be set as if by assigning the node's string
+     value (as defined for the XPath 1.0 <function>string</function> function)
+     to the PostgreSQL type.
+    </para>
+
+    <para>
+     The string value of an XML element is the concatenation, in document order,
+     of all text nodes contained in that element and its descendants. The string
+     value of an element with no descendant text nodes is an
+     empty string (not <literal>NULL</literal>).
+     Any <literal>xsi:nil</literal> attributes are ignored.
      Note that the whitespace-only <literal>text()</literal> node between two non-text
      elements is preserved, and that leading whitespace on a <literal>text()</literal>
      node is not flattened.
+     The XPath 1.0 <function>string</function> function may be consulted for the
+     rules defining the string value of other XML node types and non-XML values.
     </para>
 
     <para>
-     If the path expression does not match for a given row but
-     <replaceable>default_expression</replaceable> is specified, the value resulting
-     from evaluating that expression is used.
-     If no <literal>DEFAULT</literal> clause is given for the column,
-     the field will be set to <literal>NULL</literal>.
+     The conversion rules presented here are not exactly those of the SQL
+     standard, as discussed in <xref linkend="functions-xml-limits-casts"/>.
+    </para>
+
+    <para>
+     If the path expression returns an empty nodeset
+     (typically, when it does not match)
+     for a given row, the column will be set to <literal>NULL</literal>, unless
+     a <replaceable>default_expression</replaceable> is specified; then the
+     value resulting from evaluating that expression is used.
      It is possible for a <replaceable>default_expression</replaceable> to reference
      the value of output columns that appear prior to it in the column list,
      so the default of one column may be based on the value of another
@@ -10933,20 +10975,14 @@ SELECT xpath_exists('/my:a/text()', '<my:a xmlns:my="http://example.com">test</m
     </para>
 
     <para>
-     Unlike regular PostgreSQL functions, <replaceable>column_expression</replaceable>
-     and <replaceable>default_expression</replaceable> are not evaluated to a simple
-     value before calling the function.
-     <replaceable>column_expression</replaceable> is normally evaluated
-     exactly once per input row, and <replaceable>default_expression</replaceable>
-     is evaluated each time a default is needed for a field.
-     If the expression qualifies as stable or immutable the repeat
+     A <replaceable>default_expression</replaceable>, rather than being
+     evaluated immediately when <function>xmltable</function> is called,
+     is evaluated each time a default is needed for the column.
+     If the expression qualifies as stable or immutable, the repeat
      evaluation may be skipped.
-     Effectively <function>xmltable</function> behaves more like a subquery than a
-     function call.
      This means that you can usefully use volatile functions like
-     <function>nextval</function> in <replaceable>default_expression</replaceable>, and
-     <replaceable>column_expression</replaceable> may depend on other parts of the
-     XML document.
+     <function>nextval</function> in
+     <replaceable>default_expression</replaceable>.
     </para>
 
     <para>
@@ -11294,6 +11330,301 @@ table2-mapping
 ]]></programlisting>
    </figure>
   </sect2>
+
+  <sect2 id="functions-xml-limits-compatibility">
+   <title>Limits and Compatibility</title>
+
+   <indexterm>
+    <primary>XML Functions</primary>
+    <secondary>limits and compatibility</secondary>
+   </indexterm>
+
+   <sect3 id="functions-xml-limits-xpath1">
+    <title>Queries restricted to XPath 1.0</title>
+    
+    <para>
+     The <productname>PostgreSQL</productname>-specific functions
+     <function>xpath</function> and <function>xpath_exists</function> query
+     XML documents using the XPath language, and
+     <productname>PostgreSQL</productname> also provides XPath-only variants of
+     the standard functions <function>XMLEXISTS</function> and
+     <function>XMLTABLE</function>, which are defined in the SQL standard to use
+     the XQuery language. For all of these functions,
+     <productname>PostgreSQL</productname> relies on the
+     <productname>libxml</productname> library, which provides only XPath 1.0.
+    </para>
+
+    <para>
+     There is a strong connection between the XQuery language and XPath versions
+     2.0 and later: any expression that is syntactically valid and executes
+     successfully in both produces the same result (with a minor exception for
+     expressions containing numeric character references or predefined entity
+     references, which XQuery replaces with the corresponding character while
+     XPath leaves them alone). But there is no such connection between XPath 1.0
+     and XQuery or the later XPath versions; it was an earlier language and
+     differs in many respects.
+    </para>
+
+    <para>
+     There are two categories of limitation to keep in mind: the restriction
+     from XQuery to XPath for the functions specified in the SQL standard, and
+     the restriction of XPath to version 1.0 for both the standard and the
+     <productname>PostgreSQL</productname>-specific functions.
+    </para>
+    
+    <sect4>
+     <title>Restriction of XQuery to XPath</title>
+
+     <para>
+      Features of XQuery beyond those of XPath include:
+
+      <itemizedlist>
+       <listitem>
+        <para>
+         XQuery expressions can construct and return new XML nodes, in addition
+         to all possible XPath values. XPath can introduce and return values of
+         the atomic types (numbers, strings, and so on) but can only return XML
+         nodes already present in documents supplied as input to the expression.
+        </para>
+       </listitem>
+
+       <listitem>
+        <para>
+         XQuery has control constructs for iteration, sorting, and grouping.
+        </para>
+       </listitem>
+
+       <listitem>
+        <para>
+         XQuery allows the declaration and use of local functions.
+        </para>
+       </listitem>
+      </itemizedlist>
+     </para>
+
+     <para>
+      Recent XPath versions begin to offer overlapping capabilities
+      (such as the functional-style <function>for-each</function> and
+      <function>sort</function>, anonymous functions, and
+      <function>parse-xml</function> to create a node from a string),
+      but these were not available before XPath 3.0.
+     </para>
+    </sect4>
+
+    <sect4>
+     <title>Restriction of XPath to 1.0</title>
+
+     <para>
+      For developers familiar with XQuery and XPath 2.0 or later, or porting
+      queries from other systems, XPath 1.0 presents a number of differences to
+      contend with:
+
+      <itemizedlist>
+       <listitem>
+        <para>
+         The fundamental type of an XQuery/XPath expression, the
+         <type>sequence</type>, which can contain XML nodes, atomic values,
+         or both, does not exist in XPath 1.0. A 1.0 expression can only produce
+         a nodeset (possibly empty, or with one XML node or more), or a single
+         atomic value.
+        </para>
+       </listitem>
+
+       <listitem>
+        <para>
+         Unlike an XQuery/XPath sequence, which can contain any desired items
+         in any desired order, an XPath 1.0 nodeset has no guaranteed order and,
+         like any set, can have no member appear more than once. (The
+         <productname>libxml</productname> library does seem to always return
+         nodesets to <productname>PostgreSQL</productname> with their members
+         in the same relative order they had in the input document; it does not
+         commit to this behavior, and an XPath 1.0 expression cannot control
+         it.)
+        </para>
+       </listitem>
+
+       <listitem>
+        <para>
+         While XQuery/XPath provides all of the types defined in XML Schema
+         and many operators and functions over those types, XPath 1.0 has only
+         nodesets and three atomic types, <type>boolean</type>,
+         <type>double</type>, and <type>string</type>.
+        </para>
+       </listitem>
+
+       <listitem>
+        <para>
+         XPath 1.0 has no conditional operator. An XQuery/XPath expression
+         such as <userinput>if ( hat ) then hat/@size else "no hat"</userinput>
+         has no XPath 1.0 equivalent.
+        </para>
+       </listitem>
+
+       <listitem>
+        <para>
+         XPath 1.0 has no ordering comparison operator for strings. Both
+         <userinput>"cat" &lt; "dog"</userinput> and
+         <userinput>"cat" &gt; "dog"</userinput> are false, because each is a
+         numeric comparison of two <literal>NaN</literal>s. In contrast,
+         <literal>=</literal> and <literal>!=</literal> do compare the strings
+         as strings.
+        </para>
+       </listitem>
+
+       <listitem>
+        <para>
+         XPath 1.0 blurs the distinction between
+         <firstterm>value comparisons</firstterm> and
+         <firstterm>general comparisons</firstterm> as XQuery/XPath define them.
+         Both <userinput>sale/@hatsize = 7</userinput> and
+         <userinput>sale/@customer = "alice"</userinput> are existentially
+         quantified comparisons, true if there is any sale with the given value
+         for the attribute, but <userinput>sale/@taxable = false()</userinput>
+         is a value comparison to the
+         <firstterm>effective boolean value</firstterm> of a whole nodeset,
+         and true only if no sale has a <literal>taxable</literal> attribute
+         at all.
+        </para>
+       </listitem>
+
+       <listitem>
+        <para>
+         In the XQuery/XPath data model, a <firstterm>document node</firstterm>
+         can have either document form (exactly one top-level element, only
+         comments and processing instructions outside of it) or content form
+         (with those constraints relaxed). Its equivalent in XPath 1.0, the
+         <firstterm>root node</firstterm>, can only be in document form.
+         This is part of the reason an <type>xml</type> value passed as the
+         context item to any <productname>PostgreSQL</productname> XPath-based
+         function must be in document form.
+        </para>
+       </listitem>
+      </itemizedlist>
+     </para>
+
+     <para>
+      The differences highlighted here are not all of them. In XQuery and
+      the 2.0 and later versions of XPath, there is an XPath 1.0 compatibility
+      mode, and the W3C lists of function library
+      <footnote>
+       <para>
+        <ulink url='https://www.w3.org/TR/2010/REC-xpath-functions-20101214/#xpath1-compatibility'></ulink>
+       </para>
+      </footnote>
+      and language
+      <footnote>
+       <para>
+        <ulink url='https://www.w3.org/TR/xpath20/#id-backwards-compatibility'></ulink>
+       </para>
+      </footnote>
+      changes applied in that mode offer a more complete account of the
+      differences, if still not exhaustive; the compatibility mode cannot make
+      the later languages exactly equivalent to XPath 1.0.
+     </para>
+    </sect4>
+
+    <sect4 id="functions-xml-limits-casts">
+     <title>Mappings between SQL and XML data types and values</title>
+
+     <para>
+      In SQL:2006 and later, both directions of conversion between standard SQL
+      data types and the XML Schema types are specified precisely. However, the
+      rules are expressed using the types and semantics of XQuery/XPath, and
+      have no direct application to the different data model of XPath 1.0.
+     </para>
+
+     <para>
+      When <productname>PostgreSQL</productname> maps SQL data values to XML
+      (as in <function>xmlelement</function>), or XML to SQL (as in the output
+      columns of <function>xmltable</function>), except for the few cases
+      treated specially, <productname>PostgreSQL</productname> simply assumes
+      that the XML data type's XPath 1.0 string form will be valid as the
+      text-input form of the SQL datatype, and conversely. This rule has the
+      virtue of simplicity while producing, for many data types, results similar
+      to the mappings specified in the standard.
+     </para>
+
+     <para>
+      Where interoperability with other systems is a concern, for some data
+      types, it may be necessary to use available data type formatting functions
+      (such as those in <xref linkend="functions-formatting"/>) explicitly in
+      queries to produce the standard mappings.
+     </para>
+    </sect4>
+   </sect3>
+
+   <sect3 id="functions-xml-limits-postgresql">
+    <title>
+     Incidental limits of the implementation
+    </title>
+
+    <para>
+     This section concerns limits that are not inherent in the
+     <productname>libxml</productname> library, but apply to the current
+     implementation in <productname>PostgreSQL</productname>.
+    </para>
+
+    <sect4>
+     <title>Only <literal>BY VALUE</literal> passing mechanism supported</title>
+
+     <para>
+      The SQL standard defines two <firstterm>passing mechanisms</firstterm>
+      that apply when passing an XML argument from SQL to an XML function or
+      receiving a result: <literal>BY REF</literal>, in which a particular XML
+      value retains its node identity, and <literal>BY VALUE</literal>, in which
+      the content of the XML is passed but node identity is not preserved. A
+      mechanism can be specified before a list of parameters, as the default
+      mechanism for all of them, or after any parameter, to override the
+      default.
+     </para>
+
+     <para>
+      To illustrate the difference, if
+      <replaceable>x</replaceable> is an XML value, these two queries in
+      an SQL:2006 environment would produce true and false, respectively:
+
+      <screen><![CDATA[
+SELECT XMLQUERY('$a is $b' PASSING BY REF x AS a, x AS b NULL ON EMPTY);
+SELECT XMLQUERY('$a is $b' PASSING BY VALUE x AS a, x AS b NULL ON EMPTY);
+]]></screen>
+     </para>
+
+     <para>
+      <productname>PostgreSQL</productname> will accept either
+      <literal>BY VALUE</literal> or <literal>BY REF</literal> in an
+      <function>XMLEXISTS</function> or <function>XMLTABLE</function> construct,
+      but ignores them; the <type>xml</type> data type holds a character-string
+      serialized representation, so there is no node identity to preserve,
+      and passing is always <literal>BY VALUE</literal>.
+     </para>
+    </sect4>
+
+    <sect4>
+     <title>Cannot pass named parameters to queries</title>
+
+     <para>
+      The XPath-based functions support passing one parameter to serve as the
+      XPath expression's context item, but do not support passing additional
+      values to be available to the expression as named parameters.
+     </para>
+    </sect4>
+
+    <sect4>
+     <title>No <type>XML(SEQUENCE)</type> type</title>
+
+     <para>
+      The <productname>PostgreSQL</productname> <type>xml</type> can only hold
+      a value in <literal>DOCUMENT</literal> or <literal>CONTENT</literal> form.
+      An XQuery/XPath expression context item must be a single XML node
+      or atomic value, while XPath 1.0 further restricts it to only an XML node,
+      and has no node type allowing <literal>CONTENT</literal>. The upshot is
+      that a well-formed <literal>DOCUMENT</literal> is the only form of XML
+      value that <productname>PostgreSQL</productname> can supply as an XPath
+      context item.
+     </para>
+    </sect4>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="functions-json">
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index bade0fe..e947613 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -569,7 +569,7 @@ X056	Advanced table mapping: data mapping			YES
 X057	Advanced table mapping: metadata mapping			YES	
 X058	Advanced table mapping: base64 encoding of binary strings			YES	
 X059	Advanced table mapping: hex encoding of binary strings			YES	
-X060	XMLParse: character string input and CONTENT option			YES	
+X060	XMLParse: character string input and CONTENT option			YES	uses SQL:2003 definition, so a document with a DTD cannot be parsed as CONTENT
 X061	XMLParse: character string input and DOCUMENT option			YES	
 X065	XMLParse: BLOB input and CONTENT option			NO	
 X066	XMLParse: BLOB input and DOCUMENT option			NO	
@@ -593,7 +593,7 @@ X085	Predefined namespace prefixes			NO
 X086	XML namespace declarations in XMLTable			NO	
 X090	XML document predicate			YES	
 X091	XML content predicate			NO	
-X096	XMLExists			NO	XPath only
+X096	XMLExists			NO	XPath 1.0 only
 X100	Host language support for XML: CONTENT option			NO	
 X101	Host language support for XML: DOCUMENT option			NO	
 X110	Host language support for XML: VARCHAR mapping			NO	
@@ -661,11 +661,11 @@ X282	XMLValidate with CONTENT option			NO
 X283	XMLValidate with SEQUENCE option			NO	
 X284	XMLValidate: NAMESPACE without ELEMENT clause			NO	
 X286	XMLValidate: NO NAMESPACE with ELEMENT clause			NO	
-X300	XMLTable			NO	XPath only
+X300	XMLTable			NO	XPath 1.0 only
 X301	XMLTable: derived column list option			YES	
 X302	XMLTable: ordinality column option			YES	
 X303	XMLTable: column default option			YES	
-X304	XMLTable: passing a context item			YES	
+X304	XMLTable: passing a context item			YES	must be XML DOCUMENT
 X305	XMLTable: initializing an XQuery variable			NO	
 X400	Name and identifier mapping			YES	
 X410	Alter column data type: XML type			YES	

view thread (88+ messages)  latest in thread

Message-ID: <5C4A94A5.8010402@anastigmatix.net>
Permalink:  ../5C4A94A5.8010402@anastigmatix.net/
Also on:    postgresql.org/message-id/5C4A94A5.8010402@anastigmatix.net

 ·  · 

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-hackers@postgresql.org
  Cc: chap@anastigmatix.net, pavel.stehule@gmail.com, pgsql-hackers@lists.postgresql.org
  Subject: Re: PostgreSQL vs SQL/XML Standards
  In-Reply-To: <5C4A94A5.8010402@anastigmatix.net>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox