diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 8052f8b2d76..ca804dea210 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.206 2007/01/12 21:47:26 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.207 2007/01/14 13:11:53 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2808,6 +2808,25 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
standalone));
}
break;
+
+ case IS_DOCUMENT:
+ {
+ ExprState *e;
+
+ /* optional argument is known to be xml */
+ Assert(list_length(xmlExpr->args) == 1);
+
+ e = (ExprState *) linitial(xmlExpr->args);
+ value = ExecEvalExpr(e, econtext, &isnull, NULL);
+ if (isnull)
+ return (Datum) 0;
+ else
+ {
+ *isNull = false;
+ return BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
+ }
+ }
+ break;
}
if (*isNull)
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6abe0d6795a..db66a69b3ff 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.573 2007/01/09 02:14:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.574 2007/01/14 13:11:53 petere Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -7147,6 +7147,16 @@ a_expr: c_expr { $$ = $1; }
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("UNIQUE predicate is not yet implemented")));
}
+ | a_expr IS DOCUMENT_P %prec IS
+ {
+ $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, list_make1($1));
+ }
+ | a_expr IS NOT DOCUMENT_P %prec IS
+ {
+ $$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL,
+ makeXmlExpr(IS_DOCUMENT, NULL, NIL, list_make1($1)),
+ @2);
+ }
;
/*
@@ -7207,6 +7217,16 @@ b_expr: c_expr
{
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "<>", $1, (Node *) $6, @2);
}
+ | b_expr IS DOCUMENT_P %prec IS
+ {
+ $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, list_make1($1));
+ }
+ | b_expr IS NOT DOCUMENT_P %prec IS
+ {
+ $$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL,
+ makeXmlExpr(IS_DOCUMENT, NULL, NIL, list_make1($1)),
+ @2);
+ }
;
/*
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index d9e42011d41..394a507f2ef 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.207 2007/01/12 22:09:49 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.208 2007/01/14 13:11:53 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1483,6 +1483,10 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
else
newe = coerce_to_boolean(pstate, newe, "XMLROOT");
break;
+ case IS_DOCUMENT:
+ newe = coerce_to_specific_type(pstate, newe, XMLOID,
+ "IS DOCUMENT");
+ break;
}
newx->args = lappend(newx->args, newe);
i++;
@@ -1782,7 +1786,10 @@ exprType(Node *expr)
type = ((MinMaxExpr *) expr)->minmaxtype;
break;
case T_XmlExpr:
- type = XMLOID;
+ if (((XmlExpr *) expr)->op == IS_DOCUMENT)
+ type = BOOLOID;
+ else
+ type = XMLOID;
break;
case T_NullIfExpr:
type = exprType((Node *) linitial(((NullIfExpr *) expr)->args));
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 3a4caa81f81..dea29d1d8aa 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.152 2007/01/05 22:19:34 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.153 2007/01/14 13:11:54 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1337,6 +1337,9 @@ FigureColnameInternal(Node *node, char **name)
case IS_XMLROOT:
*name = "xmlroot";
return 2;
+ case IS_DOCUMENT:
+ /* nothing */
+ break;
}
break;
default:
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3c217c98edc..be23d938f80 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -2,7 +2,7 @@
* ruleutils.c - Functions to convert stored expressions/querytrees
* back to source text
*
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.241 2007/01/09 02:14:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.242 2007/01/14 13:11:54 petere Exp $
**********************************************************************/
#include "postgres.h"
@@ -3847,6 +3847,8 @@ get_rule_expr(Node *node, deparse_context *context,
case IS_XMLROOT:
appendStringInfoString(buf, "XMLROOT(");
break;
+ case IS_DOCUMENT:
+ break;
}
if (xexpr->name)
{
@@ -3888,6 +3890,7 @@ get_rule_expr(Node *node, deparse_context *context,
case IS_XMLELEMENT:
case IS_XMLFOREST:
case IS_XMLPI:
+ case IS_DOCUMENT:
/* no extra decoration needed */
get_rule_expr((Node *) xexpr->args, context, true);
break;
@@ -3943,7 +3946,10 @@ get_rule_expr(Node *node, deparse_context *context,
}
}
- appendStringInfoChar(buf, ')');
+ if (xexpr->op == IS_DOCUMENT)
+ appendStringInfoString(buf, " IS DOCUMENT");
+ else
+ appendStringInfoChar(buf, ')');
}
break;
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index da04bee15de..87cb5b0d640 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -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.16 2007/01/12 21:47:26 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.17 2007/01/14 13:11:54 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -515,6 +515,50 @@ xmlvalidate(PG_FUNCTION_ARGS)
}
+bool
+xml_is_document(xmltype *arg)
+{
+#ifdef USE_LIBXML
+ bool result;
+ xmlDocPtr doc = NULL;
+ MemoryContext ccxt = CurrentMemoryContext;
+
+ PG_TRY();
+ {
+ doc = xml_parse((text *) arg, true, true);
+ result = true;
+ }
+ PG_CATCH();
+ {
+ ErrorData *errdata;
+ MemoryContext ecxt;
+
+ ecxt = MemoryContextSwitchTo(ccxt);
+ errdata = CopyErrorData();
+ if (errdata->sqlerrcode == ERRCODE_INVALID_XML_DOCUMENT)
+ {
+ FlushErrorState();
+ result = false;
+ }
+ else
+ {
+ MemoryContextSwitchTo(ecxt);
+ PG_RE_THROW();
+ }
+ }
+ PG_END_TRY();
+
+ if (doc)
+ xmlFreeDoc(doc);
+
+ return result;
+#else /* not USE_LIBXML */
+ NO_XML_SUPPORT();
+ return false;
+#endif /* not USE_LIBXML */
+}
+
+
#ifdef USE_LIBXML
/*
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 921ac0d0b4b..cea0cd2f6a5 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.122 2007/01/05 22:19:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.123 2007/01/14 13:11:54 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -725,7 +725,8 @@ typedef enum XmlExprOp
IS_XMLFOREST, /* XMLFOREST(xml_attributes) */
IS_XMLPARSE, /* XMLPARSE(text, is_doc, preserve_ws) */
IS_XMLPI, /* XMLPI(name [, args]) */
- IS_XMLROOT /* XMLROOT(xml, version, standalone) */
+ IS_XMLROOT, /* XMLROOT(xml, version, standalone) */
+ IS_DOCUMENT /* xmlval IS DOCUMENT */
} XmlExprOp;
typedef struct XmlExpr
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index b7b105ef157..9e576bdecbe 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -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.9 2007/01/12 21:47:27 petere Exp $
+ * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.10 2007/01/14 13:11:54 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -37,6 +37,7 @@ extern xmltype *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext);
extern xmltype *xmlparse(text *data, bool is_doc, bool preserve_whitespace);
extern xmltype *xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null);
extern xmltype *xmlroot(xmltype *data, text *version, int standalone);
+extern bool xml_is_document(xmltype *arg);
extern char *map_sql_identifier_to_xml_name(char *ident, bool fully_escaped);
extern char *map_xml_name_to_sql_identifier(char *name);
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index 275523727b4..c33fd8e414a 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -228,6 +228,33 @@ SELECT xmlserialize(content data as character varying) FROM xmltest;
two
(2 rows)
+SELECT xml 'bar' IS DOCUMENT;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT xml 'barfoo' IS DOCUMENT;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT xml '' IS NOT DOCUMENT;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT xml 'abc' IS NOT DOCUMENT;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '<>' IS NOT DOCUMENT;
+ERROR: invalid XML content
+DETAIL: Element name not found
-- Check mapping SQL identifier to XML name
SELECT xmlpi(name ":::_xml_abc135.%-&_");
xmlpi
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index 9ff3959160e..4534ae98cc5 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -107,6 +107,16 @@ SELECT xmlserialize(content data as character varying) FROM xmltest;
------
(0 rows)
+SELECT xml 'bar' IS DOCUMENT;
+ERROR: no XML support in this installation
+SELECT xml 'barfoo' IS DOCUMENT;
+ERROR: no XML support in this installation
+SELECT xml '' IS NOT DOCUMENT;
+ERROR: no XML support in this installation
+SELECT xml 'abc' IS NOT DOCUMENT;
+ERROR: no XML support in this installation
+SELECT '<>' IS NOT DOCUMENT;
+ERROR: no XML support in this installation
-- Check mapping SQL identifier to XML name
SELECT xmlpi(name ":::_xml_abc135.%-&_");
ERROR: no XML support in this installation
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index a22c8251298..4492a62cdb0 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -86,6 +86,13 @@ SELECT xmlroot (
SELECT xmlserialize(content data as character varying) FROM xmltest;
+SELECT xml 'bar' IS DOCUMENT;
+SELECT xml 'barfoo' IS DOCUMENT;
+SELECT xml '' IS NOT DOCUMENT;
+SELECT xml 'abc' IS NOT DOCUMENT;
+SELECT '<>' IS NOT DOCUMENT;
+
+
-- Check mapping SQL identifier to XML name
SELECT xmlpi(name ":::_xml_abc135.%-&_");