Add support for xmlval IS DOCUMENT expression.
authorPeter Eisentraut <peter_e@gmx.net>
Sun, 14 Jan 2007 13:11:54 +0000 (13:11 +0000)
committerPeter Eisentraut <peter_e@gmx.net>
Sun, 14 Jan 2007 13:11:54 +0000 (13:11 +0000)
src/backend/executor/execQual.c
src/backend/parser/gram.y
src/backend/parser/parse_expr.c
src/backend/parser/parse_target.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/adt/xml.c
src/include/nodes/primnodes.h
src/include/utils/xml.h
src/test/regress/expected/xml.out
src/test/regress/expected/xml_1.out
src/test/regress/sql/xml.sql

index 8052f8b2d76590715ce199eb29c32fd2830a2709..ca804dea21056a0e22d8cb37c3c41a75eb8ad6da 100644 (file)
@@ -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)
index 6abe0d6795a28d710fbca063c1e891201b4151cb..db66a69b3ff6ffe00e8dddf2936e268087d150e2 100644 (file)
@@ -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);
+                               }
                ;
 
 /*
index d9e42011d414133fc6ea4bdb5a4b0d94e1753fcb..394a507f2ef11949206debd9fe396919a44bf2d6 100644 (file)
@@ -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));
index 3a4caa81f8115f99949140dbee11505811a8f231..dea29d1d8aa4995c859037ddfbee8491851ccfb1 100644 (file)
@@ -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:
index 3c217c98edc24c7f88f8f77081c79386d6745e11..be23d938f80f7986da8237b6c164c5092447a9f4 100644 (file)
@@ -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;
 
index da04bee15dea67c8c26e7f7658e04b0d3c0cd159..87cb5b0d64073b7ee65c51b600d6b4dcf82d3e74 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.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
 
 /*
index 921ac0d0b4be6476ae2277a58f022604d6bb05c8..cea0cd2f6a5c24ac83e036133821019c2141e87f 100644 (file)
@@ -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
index b7b105ef15781e6aba32e8543abf3726ddcd1933..9e576bdecbecefe9eac1effb1b3174cb91747498 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.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);
index 275523727b45bcc0daba9a233b420bb313058126..c33fd8e414a1519b63c90ac074f38da26bccc0ff 100644 (file)
@@ -228,6 +228,33 @@ SELECT xmlserialize(content data as character varying) FROM xmltest;
  <value>two</value>
 (2 rows)
 
+SELECT xml '<foo>bar</foo>' IS DOCUMENT;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT xml '<foo>bar</foo><bar>foo</bar>' IS DOCUMENT;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT xml '<abc/>' 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                      
index 9ff3959160e00c60a69fc50ad5fbc13ad30b8b8c..4534ae98cc573842c71bd71c7b19ab3565e26a9a 100644 (file)
@@ -107,6 +107,16 @@ SELECT xmlserialize(content data as character varying) FROM xmltest;
 ------
 (0 rows)
 
+SELECT xml '<foo>bar</foo>' IS DOCUMENT;
+ERROR:  no XML support in this installation
+SELECT xml '<foo>bar</foo><bar>foo</bar>' IS DOCUMENT;
+ERROR:  no XML support in this installation
+SELECT xml '<abc/>' 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
index a22c825129886a9817b15433d65da163492c53d2..4492a62cdb0ce3afa4521ed7531645e224ee000f 100644 (file)
@@ -86,6 +86,13 @@ SELECT xmlroot (
 SELECT xmlserialize(content data as character varying) FROM xmltest;
 
 
+SELECT xml '<foo>bar</foo>' IS DOCUMENT;
+SELECT xml '<foo>bar</foo><bar>foo</bar>' IS DOCUMENT;
+SELECT xml '<abc/>' 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.%-&_");