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.%-&_");