diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 8052f8b2d76590715ce199eb29c32fd2830a2709..ca804dea21056a0e22d8cb37c3c41a75eb8ad6da 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 6abe0d6795a28d710fbca063c1e891201b4151cb..db66a69b3ff6ffe00e8dddf2936e268087d150e2 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 d9e42011d414133fc6ea4bdb5a4b0d94e1753fcb..394a507f2ef11949206debd9fe396919a44bf2d6 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 3a4caa81f8115f99949140dbee11505811a8f231..dea29d1d8aa4995c859037ddfbee8491851ccfb1 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 3c217c98edc24c7f88f8f77081c79386d6745e11..be23d938f80f7986da8237b6c164c5092447a9f4 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 da04bee15dea67c8c26e7f7658e04b0d3c0cd159..87cb5b0d64073b7ee65c51b600d6b4dcf82d3e74 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 921ac0d0b4be6476ae2277a58f022604d6bb05c8..cea0cd2f6a5c24ac83e036133821019c2141e87f 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 b7b105ef15781e6aba32e8543abf3726ddcd1933..9e576bdecbecefe9eac1effb1b3174cb91747498 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 275523727b45bcc0daba9a233b420bb313058126..c33fd8e414a1519b63c90ac074f38da26bccc0ff 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; <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 diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out index 9ff3959160e00c60a69fc50ad5fbc13ad30b8b8c..4534ae98cc573842c71bd71c7b19ab3565e26a9a 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 '<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 diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql index a22c825129886a9817b15433d65da163492c53d2..4492a62cdb0ce3afa4521ed7531645e224ee000f 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 '<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.%-&_");