Hi

now xpath and xpath_exists supports default namespace too

updated doc,
fixed all variants of expected result test file

Regards

Pavel
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 2f036015cc..610f709933 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -10477,7 +10477,8 @@ SELECT xml_is_well_formed_document('<pg:foo xmlns:pg="http://postgresql.org/stuf
      second the namespace URI. It is not required that aliases provided in
      this array be the same as those being used in the XML document itself (in
      other words, both in the XML document and in the <function>xpath</function>
-     function context, aliases are <emphasis>local</>).
+     function context, aliases are <emphasis>local</>). Default namespace has
+     empty name (empty string) and should be only one.
     </para>
 
     <para>
@@ -10496,8 +10497,8 @@ SELECT xpath('/my:a/text()', '<my:a xmlns:my="http://example.com";>test</my:a>',
     <para>
      To deal with default (anonymous) namespaces, do something like this:
 <screen><![CDATA[
-SELECT xpath('//mydefns:b/text()', '<a xmlns="http://example.com";><b>test</b></a>',
-             ARRAY[ARRAY['mydefns', 'http://example.com']]);
+SELECT xpath('//b/text()', '<a xmlns="http://example.com";><b>test</b></a>',
+             ARRAY[ARRAY['', 'http://example.com']]);
 
  xpath
 --------
@@ -10571,8 +10572,7 @@ SELECT xpath_exists('/my:a/text()', '<my:a xmlns:my="http://example.com";>test</m
     <para>
      The optional <literal>XMLNAMESPACES</> clause is a comma-separated
      list of namespaces.  It specifies the XML namespaces used in
-     the document and their aliases. A default namespace specification
-     is not currently supported.
+     the document and their aliases.
     </para>
 
     <para>
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 1fb018416e..b60a3cfe0d 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -29,7 +29,7 @@ OBJS = acl.o amutils.o arrayfuncs.o array_expanded.o array_selfuncs.o \
 	tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
 	tsvector.o tsvector_op.o tsvector_parser.o \
 	txid.o uuid.o varbit.o varchar.o varlena.o version.o \
-	windowfuncs.o xid.o xml.o
+	windowfuncs.o xid.o xml.o xpath_parser.o
 
 like.o: like.c like_match.c
 
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 24229c2dff..90c51ea1c6 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -90,7 +90,7 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 #include "utils/xml.h"
-
+#include "utils/xpath_parser.h"
 
 /* GUC variables */
 int			xmlbinary;
@@ -187,6 +187,7 @@ typedef struct XmlTableBuilderData
 	xmlXPathCompExprPtr xpathcomp;
 	xmlXPathObjectPtr xpathobj;
 	xmlXPathCompExprPtr *xpathscomp;
+	bool		with_default_ns;
 } XmlTableBuilderData;
 #endif
 
@@ -227,6 +228,7 @@ const TableFuncRoutine XmlTableRoutine =
 #define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance";
 #define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml";
 
+#define DEFAULT_NAMESPACE_NAME		"pgdefnamespace.pgsqlxml.internal"
 
 #ifdef USE_LIBXML
 
@@ -3849,6 +3851,7 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
 	int			ndim;
 	Datum	   *ns_names_uris;
 	bool	   *ns_names_uris_nulls;
+	bool		with_default_ns = false;
 	int			ns_count;
 
 	/*
@@ -3898,7 +3901,6 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
 				 errmsg("empty XPath expression")));
 
 	string = pg_xmlCharStrndup(datastr, len);
-	xpath_expr = pg_xmlCharStrndup(VARDATA_ANY(xpath_expr_text), xpath_len);
 
 	xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
 
@@ -3941,6 +3943,26 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
 							(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 							 errmsg("neither namespace name nor URI may be null")));
 				ns_name = TextDatumGetCString(ns_names_uris[i * 2]);
+
+				/* Don't allow same namespace as out internal default namespace name */
+				if (strcmp(ns_name, DEFAULT_NAMESPACE_NAME) == 0)
+					ereport(ERROR,
+								(errcode(ERRCODE_RESERVED_NAME),
+								 errmsg("cannot to use \"%s\" as namespace name",
+										  DEFAULT_NAMESPACE_NAME),
+								 errdetail("\"%s\" is reserved for internal purpose",
+										  DEFAULT_NAMESPACE_NAME)));
+				if (*ns_name == '\0')
+				{
+					if (with_default_ns)
+						ereport(ERROR,
+								(errcode(ERRCODE_SYNTAX_ERROR),
+								 errmsg("only one default namespace is allowed")));
+
+					with_default_ns = true;
+					ns_name = DEFAULT_NAMESPACE_NAME;
+				}
+
 				ns_uri = TextDatumGetCString(ns_names_uris[i * 2 + 1]);
 				if (xmlXPathRegisterNs(xpathctx,
 									   (xmlChar *) ns_name,
@@ -3951,6 +3973,16 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
 			}
 		}
 
+		if (with_default_ns)
+		{
+			StringInfoData		str;
+
+			transformXPath(&str, text_to_cstring(xpath_expr_text), DEFAULT_NAMESPACE_NAME);
+			xpath_expr = pg_xmlCharStrndup(str.data, str.len);
+		}
+		else
+			xpath_expr = pg_xmlCharStrndup(VARDATA_ANY(xpath_expr_text), xpath_len);
+
 		xpathcomp = xmlXPathCompile(xpath_expr);
 		if (xpathcomp == NULL || xmlerrcxt->err_occurred)
 			xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
@@ -4195,6 +4227,7 @@ XmlTableInitOpaque(TableFuncScanState *state, int natts)
 	xtCxt->magic = XMLTABLE_CONTEXT_MAGIC;
 	xtCxt->natts = natts;
 	xtCxt->xpathscomp = palloc0(sizeof(xmlXPathCompExprPtr) * natts);
+	xtCxt->with_default_ns = false;
 
 	xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
 
@@ -4287,6 +4320,7 @@ XmlTableSetDocument(TableFuncScanState *state, Datum value)
 #endif							/* not USE_LIBXML */
 }
 
+
 /*
  * XmlTableSetNamespace
  *		Add a namespace declaration
@@ -4297,12 +4331,25 @@ XmlTableSetNamespace(TableFuncScanState *state, char *name, char *uri)
 #ifdef USE_LIBXML
 	XmlTableBuilderData *xtCxt;
 
-	if (name == NULL)
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("DEFAULT namespace is not supported")));
 	xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableSetNamespace");
 
+	if (name != NULL)
+	{
+		/* Don't allow same namespace as out internal default namespace name */
+		if (strcmp(name, DEFAULT_NAMESPACE_NAME) == 0)
+			ereport(ERROR,
+						(errcode(ERRCODE_RESERVED_NAME),
+						 errmsg("cannot to use \"%s\" as namespace name",
+								  DEFAULT_NAMESPACE_NAME),
+						 errdetail("\"%s\" is reserved for internal purpose",
+								  DEFAULT_NAMESPACE_NAME)));
+	}
+	else
+	{
+		xtCxt->with_default_ns = true;
+		name = DEFAULT_NAMESPACE_NAME;
+	}
+
 	if (xmlXPathRegisterNs(xtCxt->xpathcxt,
 						   pg_xmlCharStrndup(name, strlen(name)),
 						   pg_xmlCharStrndup(uri, strlen(uri))))
@@ -4331,6 +4378,14 @@ XmlTableSetRowFilter(TableFuncScanState *state, char *path)
 				(errcode(ERRCODE_DATA_EXCEPTION),
 				 errmsg("row path filter must not be empty string")));
 
+	if (xtCxt->with_default_ns)
+	{
+		StringInfoData		str;
+
+		transformXPath(&str, path, DEFAULT_NAMESPACE_NAME);
+		path = str.data;
+	}
+
 	xstr = pg_xmlCharStrndup(path, strlen(path));
 
 	xtCxt->xpathcomp = xmlXPathCompile(xstr);
@@ -4362,6 +4417,14 @@ XmlTableSetColumnFilter(TableFuncScanState *state, char *path, int colnum)
 				(errcode(ERRCODE_DATA_EXCEPTION),
 				 errmsg("column path filter must not be empty string")));
 
+	if (xtCxt->with_default_ns)
+	{
+		StringInfoData		str;
+
+		transformXPath(&str, path, DEFAULT_NAMESPACE_NAME);
+		path = str.data;
+	}
+
 	xstr = pg_xmlCharStrndup(path, strlen(path));
 
 	xtCxt->xpathscomp[colnum] = xmlXPathCompile(xstr);
diff --git a/src/backend/utils/adt/xpath_parser.c b/src/backend/utils/adt/xpath_parser.c
new file mode 100644
index 0000000000..ed5a071a0a
--- /dev/null
+++ b/src/backend/utils/adt/xpath_parser.c
@@ -0,0 +1,328 @@
+/*-------------------------------------------------------------------------
+ *
+ * xpath_parser.c
+ *	  XML XPath parser.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/utils/adt/xpath_parser.c
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "utils/xpath_parser.h"
+
+/*
+ * All PostgreSQL XML related functionality is based on libxml2 library, and
+ * XPath support is not an exception.  However, libxml2 doesn't support
+ * default namespace for XPath expressions. Because there are not any API
+ * how to transform or access to parsed XPath expression we have to parse
+ * XPath here.
+ *
+ * Those functionalities are implemented with a simple XPath parser/
+ * preprocessor.  This XPath parser transforms a XPath expression to another
+ * XPath expression that can be used by libxml2 XPath evaluation. It doesn't
+ * replace libxml2 XPath parser or libxml2 XPath expression evaluation.
+ */
+
+#ifdef USE_LIBXML
+
+/*
+ * We need to work with XPath expression tokens.  When expression starting with
+ * nodename, then we can use prefix.  When default namespace is defined, then we
+ * should to enhance any nodename and attribute without namespace by default
+ * namespace.
+ */
+
+typedef enum
+{
+	XPATH_TOKEN_NONE,
+	XPATH_TOKEN_NAME,
+	XPATH_TOKEN_STRING,
+	XPATH_TOKEN_NUMBER,
+	XPATH_TOKEN_OTHER
+}	XPathTokenType;
+
+typedef struct XPathTokenInfo
+{
+	XPathTokenType ttype;
+	char	   *start;
+	int			length;
+}	XPathTokenInfo;
+
+#define TOKEN_STACK_SIZE		10
+
+typedef struct ParserData
+{
+	char	   *str;
+	char	   *cur;
+	XPathTokenInfo stack[TOKEN_STACK_SIZE];
+	int			stack_length;
+}	XPathParserData;
+
+/* Any high-bit-set character is OK (might be part of a multibyte char) */
+#define NODENAME_FIRSTCHAR(c)	 ((c) == '_' || (c) == '-' || \
+								 ((c) >= 'A' && (c) <= 'Z') || \
+								 ((c) >= 'a' && (c) <= 'z') || \
+								 (IS_HIGHBIT_SET(c)))
+
+#define IS_NODENAME_CHAR(c)		(NODENAME_FIRSTCHAR(c) || (c) == '.' || \
+								 ((c) >= '0' && (c) <= '9'))
+
+
+/*
+ * Returns next char after last char of token - XPath lexer
+ */
+static char *
+getXPathToken(char *str, XPathTokenInfo * ti)
+{
+	/* skip initial spaces */
+	while (*str == ' ')
+		str++;
+
+	if (*str != '\0')
+	{
+		char		c = *str;
+
+		ti->start = str++;
+
+		if (c >= '0' && c <= '9')
+		{
+			while (*str >= '0' && *str <= '9')
+				str++;
+			if (*str == '.')
+			{
+				str++;
+				while (*str >= '0' && *str <= '9')
+					str++;
+			}
+			ti->ttype = XPATH_TOKEN_NUMBER;
+		}
+		else if (NODENAME_FIRSTCHAR(c))
+		{
+			while (IS_NODENAME_CHAR(*str))
+				str++;
+
+			ti->ttype = XPATH_TOKEN_NAME;
+		}
+		else if (c == '"')
+		{
+			while (*str != '\0')
+				if (*str++ == '"')
+					break;
+
+			ti->ttype = XPATH_TOKEN_STRING;
+		}
+		else
+			ti->ttype = XPATH_TOKEN_OTHER;
+
+		ti->length = str - ti->start;
+	}
+	else
+	{
+		ti->start = NULL;
+		ti->length = 0;
+
+		ti->ttype = XPATH_TOKEN_NONE;
+	}
+
+	return str;
+}
+
+/*
+ * reset XPath parser stack
+ */
+static void
+initXPathParser(XPathParserData * parser, char *str)
+{
+	parser->str = str;
+	parser->cur = str;
+	parser->stack_length = 0;
+}
+
+/*
+ * Returns token from stack or read token
+ */
+static void
+nextXPathToken(XPathParserData * parser, XPathTokenInfo * ti)
+{
+	if (parser->stack_length > 0)
+		memcpy(ti, &parser->stack[--parser->stack_length],
+			   sizeof(XPathTokenInfo));
+	else
+		parser->cur = getXPathToken(parser->cur, ti);
+}
+
+/*
+ * Push token to stack
+ */
+static void
+pushXPathToken(XPathParserData * parser, XPathTokenInfo * ti)
+{
+	if (parser->stack_length == TOKEN_STACK_SIZE)
+		elog(ERROR, "internal error");
+	memcpy(&parser->stack[parser->stack_length++], ti,
+		   sizeof(XPathTokenInfo));
+}
+
+/*
+ * Write token to output string
+ */
+static void
+writeXPathToken(StringInfo str, XPathTokenInfo * ti)
+{
+	Assert(ti->ttype != XPATH_TOKEN_NONE);
+
+	if (ti->ttype != XPATH_TOKEN_OTHER)
+		appendBinaryStringInfo(str, ti->start, ti->length);
+	else
+		appendStringInfoChar(str, *ti->start);
+}
+
+/*
+ * This is main part of XPath transformation. It can be called recursivly,
+ * when XPath expression contains predicates.
+ */
+static void
+_transformXPath(StringInfo str, XPathParserData * parser,
+				bool inside_predicate,
+				char *def_namespace_name)
+{
+	XPathTokenInfo t1,
+				t2;
+	bool		last_token_is_name = false;
+
+	nextXPathToken(parser, &t1);
+
+	while (t1.ttype != XPATH_TOKEN_NONE)
+	{
+		switch (t1.ttype)
+		{
+			case XPATH_TOKEN_NUMBER:
+			case XPATH_TOKEN_STRING:
+				last_token_is_name = false;
+				writeXPathToken(str, &t1);
+				nextXPathToken(parser, &t1);
+				break;
+
+			case XPATH_TOKEN_NAME:
+				{
+					bool		is_qual_name = false;
+
+					/* inside predicate ignore keywords "and" "or" */
+					if (inside_predicate)
+					{
+						if ((strncmp(t1.start, "and", 3) == 0 && t1.length == 3) ||
+						 (strncmp(t1.start, "or", 2) == 0 && t1.length == 2))
+						{
+							writeXPathToken(str, &t1);
+							nextXPathToken(parser, &t1);
+							break;
+						}
+					}
+
+					last_token_is_name = true;
+					nextXPathToken(parser, &t2);
+					if (t2.ttype == XPATH_TOKEN_OTHER)
+					{
+						if (*t2.start == '(')
+							last_token_is_name = false;
+						else if (*t2.start == ':')
+							is_qual_name = true;
+					}
+
+					if (last_token_is_name && !is_qual_name && def_namespace_name != NULL)
+						appendStringInfo(str, "%s:", def_namespace_name);
+
+					writeXPathToken(str, &t1);
+
+					if (is_qual_name)
+					{
+						writeXPathToken(str, &t2);
+						nextXPathToken(parser, &t1);
+						if (t1.ttype == XPATH_TOKEN_NAME)
+							writeXPathToken(str, &t1);
+						else
+							pushXPathToken(parser, &t1);
+					}
+					else
+						pushXPathToken(parser, &t2);
+
+					nextXPathToken(parser, &t1);
+				}
+				break;
+
+			case XPATH_TOKEN_OTHER:
+				{
+					char		c = *t1.start;
+
+					writeXPathToken(str, &t1);
+
+					if (c == '[')
+						_transformXPath(str, parser, true, def_namespace_name);
+					else
+					{
+						last_token_is_name = false;
+
+						if (c == ']' && inside_predicate)
+							return;
+
+						else if (c == '@')
+						{
+							nextXPathToken(parser, &t1);
+							if (t1.ttype == XPATH_TOKEN_NAME)
+							{
+								bool		is_qual_name = false;
+
+								/*
+								 * A default namespace declaration applies to all
+								 * unprefixed element names within its scope. Default
+								 * namespace declarations do not apply directly to
+								 * attribute names; the interpretation of unprefixed
+								 * attributes is determined by the element on which
+								 * they appear.
+								 */
+								nextXPathToken(parser, &t2);
+								if (t2.ttype == XPATH_TOKEN_OTHER && *t2.start == ':')
+									is_qual_name = true;
+
+								writeXPathToken(str, &t1);
+								if (is_qual_name)
+								{
+									writeXPathToken(str, &t2);
+									nextXPathToken(parser, &t1);
+									if (t1.ttype == XPATH_TOKEN_NAME)
+										writeXPathToken(str, &t1);
+									else
+										pushXPathToken(parser, &t1);
+								}
+								else
+									pushXPathToken(parser, &t2);
+							}
+							else
+								pushXPathToken(parser, &t1);
+						}
+					}
+					nextXPathToken(parser, &t1);
+				}
+				break;
+
+			case XPATH_TOKEN_NONE:
+				elog(ERROR, "should not be here");
+		}
+	}
+}
+
+void
+transformXPath(StringInfo str, char *xpath,
+			   char *def_namespace_name)
+{
+	XPathParserData parser;
+
+	initStringInfo(str);
+	initXPathParser(&parser, xpath);
+	_transformXPath(str, &parser, false, def_namespace_name);
+}
+
+#endif
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index bcc585d427..828d9b8352 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -1085,7 +1085,11 @@ SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y'),
                       '/rows/row'
                       PASSING '<rows xmlns="http://x.y";><row><a>10</a></row></rows>'
                       COLUMNS a int PATH 'a');
-ERROR:  DEFAULT namespace is not supported
+ a  
+----
+ 10
+(1 row)
+
 -- used in prepare statements
 PREPARE pp AS
 SELECT  xmltable.*
@@ -1452,3 +1456,50 @@ SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c
  14
 (4 rows)
 
+-- default namespaces
+CREATE TABLE t1 (id int, doc xml);
+INSERT INTO t1 VALUES (5, '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>');
+SELECT x.* FROM t1, xmltable(XMLNAMESPACES('http://x.y' AS x), '/x:rows/x:row' PASSING t1.doc COLUMNS data int PATH 'x:a[1][@hoge]') AS x;
+ data 
+------
+   50
+(1 row)
+
+SELECT x.* FROM t1, xmltable(XMLNAMESPACES(DEFAULT 'http://x.y'), '/rows/row' PASSING t1.doc COLUMNS data int PATH 'a[1][@hoge]') AS x;
+ data 
+------
+   50
+(1 row)
+
+-- should fail
+SELECT x.* FROM t1, xmltable(XMLNAMESPACES('http://x.y' AS "pgdefnamespace.pgsqlxml.internal"), '/x:rows/x:row' PASSING t1.doc COLUMNS data int PATH 'x:a[1][@hoge]') AS x;
+ERROR:  cannot to use "pgdefnamespace.pgsqlxml.internal" as namespace name
+DETAIL:  "pgdefnamespace.pgsqlxml.internal" is reserved for internal purpose
+-- xpath and xpath_exists supports namespaces too
+SELECT xpath('/x:rows/x:row/x:a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['x', 'http://x.y']]);
+                      xpath                       
+--------------------------------------------------
+ {"<a xmlns=\"http://x.y\"; hoge=\"haha\">50</a>"}
+(1 row)
+
+SELECT xpath('/rows/row/a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['', 'http://x.y']]);
+                      xpath                       
+--------------------------------------------------
+ {"<a xmlns=\"http://x.y\"; hoge=\"haha\">50</a>"}
+(1 row)
+
+SELECT xpath_exists('/x:rows/x:row/x:a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['x', 'http://x.y']]);
+ xpath_exists 
+--------------
+ t
+(1 row)
+
+SELECT xpath_exists('/rows/row/a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['', 'http://x.y']]);
+ xpath_exists 
+--------------
+ t
+(1 row)
+
+-- should fail
+SELECT xpath_exists('/rows/row/a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['', 'http://x.y'], ARRAY['', 'http://x.z']]);
+ERROR:  only one default namespace is allowed
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index d3bd8c91d7..58f9151788 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -1302,3 +1302,59 @@ SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c
 ---
 (0 rows)
 
+-- default namespaces
+CREATE TABLE t1 (id int, doc xml);
+INSERT INTO t1 VALUES (5, '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>');
+ERROR:  unsupported XML feature
+LINE 1: INSERT INTO t1 VALUES (5, '<rows xmlns="http://x.y";><row><a ...
+                                  ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT x.* FROM t1, xmltable(XMLNAMESPACES('http://x.y' AS x), '/x:rows/x:row' PASSING t1.doc COLUMNS data int PATH 'x:a[1][@hoge]') AS x;
+ data 
+------
+(0 rows)
+
+SELECT x.* FROM t1, xmltable(XMLNAMESPACES(DEFAULT 'http://x.y'), '/rows/row' PASSING t1.doc COLUMNS data int PATH 'a[1][@hoge]') AS x;
+ data 
+------
+(0 rows)
+
+-- should fail
+SELECT x.* FROM t1, xmltable(XMLNAMESPACES('http://x.y' AS "pgdefnamespace.pgsqlxml.internal"), '/x:rows/x:row' PASSING t1.doc COLUMNS data int PATH 'x:a[1][@hoge]') AS x;
+ data 
+------
+(0 rows)
+
+-- xpath and xpath_exists supports namespaces too
+SELECT xpath('/x:rows/x:row/x:a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['x', 'http://x.y']]);
+ERROR:  unsupported XML feature
+LINE 1: SELECT xpath('/x:rows/x:row/x:a[1][@hoge]', '<rows xmlns="ht...
+                                                    ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT xpath('/rows/row/a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['', 'http://x.y']]);
+ERROR:  unsupported XML feature
+LINE 1: SELECT xpath('/rows/row/a[1][@hoge]', '<rows xmlns="http://x...
+                                              ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT xpath_exists('/x:rows/x:row/x:a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['x', 'http://x.y']]);
+ERROR:  unsupported XML feature
+LINE 1: ...ELECT xpath_exists('/x:rows/x:row/x:a[1][@hoge]', '<rows xml...
+                                                             ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT xpath_exists('/rows/row/a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['', 'http://x.y']]);
+ERROR:  unsupported XML feature
+LINE 1: SELECT xpath_exists('/rows/row/a[1][@hoge]', '<rows xmlns="h...
+                                                     ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+-- should fail
+SELECT xpath_exists('/rows/row/a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['', 'http://x.y'], ARRAY['', 'http://x.z']]);
+ERROR:  unsupported XML feature
+LINE 1: SELECT xpath_exists('/rows/row/a[1][@hoge]', '<rows xmlns="h...
+                                                     ^
+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 ff77132803..0727a9df0a 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -1065,7 +1065,11 @@ SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y'),
                       '/rows/row'
                       PASSING '<rows xmlns="http://x.y";><row><a>10</a></row></rows>'
                       COLUMNS a int PATH 'a');
-ERROR:  DEFAULT namespace is not supported
+ a  
+----
+ 10
+(1 row)
+
 -- used in prepare statements
 PREPARE pp AS
 SELECT  xmltable.*
@@ -1432,3 +1436,50 @@ SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c
  14
 (4 rows)
 
+-- default namespaces
+CREATE TABLE t1 (id int, doc xml);
+INSERT INTO t1 VALUES (5, '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>');
+SELECT x.* FROM t1, xmltable(XMLNAMESPACES('http://x.y' AS x), '/x:rows/x:row' PASSING t1.doc COLUMNS data int PATH 'x:a[1][@hoge]') AS x;
+ data 
+------
+   50
+(1 row)
+
+SELECT x.* FROM t1, xmltable(XMLNAMESPACES(DEFAULT 'http://x.y'), '/rows/row' PASSING t1.doc COLUMNS data int PATH 'a[1][@hoge]') AS x;
+ data 
+------
+   50
+(1 row)
+
+-- should fail
+SELECT x.* FROM t1, xmltable(XMLNAMESPACES('http://x.y' AS "pgdefnamespace.pgsqlxml.internal"), '/x:rows/x:row' PASSING t1.doc COLUMNS data int PATH 'x:a[1][@hoge]') AS x;
+ERROR:  cannot to use "pgdefnamespace.pgsqlxml.internal" as namespace name
+DETAIL:  "pgdefnamespace.pgsqlxml.internal" is reserved for internal purpose
+-- xpath and xpath_exists supports namespaces too
+SELECT xpath('/x:rows/x:row/x:a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['x', 'http://x.y']]);
+                      xpath                       
+--------------------------------------------------
+ {"<a xmlns=\"http://x.y\"; hoge=\"haha\">50</a>"}
+(1 row)
+
+SELECT xpath('/rows/row/a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['', 'http://x.y']]);
+                      xpath                       
+--------------------------------------------------
+ {"<a xmlns=\"http://x.y\"; hoge=\"haha\">50</a>"}
+(1 row)
+
+SELECT xpath_exists('/x:rows/x:row/x:a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['x', 'http://x.y']]);
+ xpath_exists 
+--------------
+ t
+(1 row)
+
+SELECT xpath_exists('/rows/row/a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['', 'http://x.y']]);
+ xpath_exists 
+--------------
+ t
+(1 row)
+
+-- should fail
+SELECT xpath_exists('/rows/row/a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['', 'http://x.y'], ARRAY['', 'http://x.z']]);
+ERROR:  only one default namespace is allowed
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index eb4687fb09..8381be34b1 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -558,3 +558,22 @@ 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);
+
+-- default namespaces
+CREATE TABLE t1 (id int, doc xml);
+INSERT INTO t1 VALUES (5, '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>');
+
+SELECT x.* FROM t1, xmltable(XMLNAMESPACES('http://x.y' AS x), '/x:rows/x:row' PASSING t1.doc COLUMNS data int PATH 'x:a[1][@hoge]') AS x;
+SELECT x.* FROM t1, xmltable(XMLNAMESPACES(DEFAULT 'http://x.y'), '/rows/row' PASSING t1.doc COLUMNS data int PATH 'a[1][@hoge]') AS x;
+
+-- should fail
+SELECT x.* FROM t1, xmltable(XMLNAMESPACES('http://x.y' AS "pgdefnamespace.pgsqlxml.internal"), '/x:rows/x:row' PASSING t1.doc COLUMNS data int PATH 'x:a[1][@hoge]') AS x;
+
+-- xpath and xpath_exists supports namespaces too
+SELECT xpath('/x:rows/x:row/x:a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['x', 'http://x.y']]);
+SELECT xpath('/rows/row/a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['', 'http://x.y']]);
+SELECT xpath_exists('/x:rows/x:row/x:a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['x', 'http://x.y']]);
+SELECT xpath_exists('/rows/row/a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['', 'http://x.y']]);
+
+-- should fail
+SELECT xpath_exists('/rows/row/a[1][@hoge]', '<rows xmlns="http://x.y";><row><a hoge="haha">50</a></row></rows>', ARRAY[ARRAY['', 'http://x.y'], ARRAY['', 'http://x.z']]);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to