diff --git a/doc/src/sgml/errcodes.sgml b/doc/src/sgml/errcodes.sgml index bf06cfaf5e5..ced6197d978 100644 --- a/doc/src/sgml/errcodes.sgml +++ b/doc/src/sgml/errcodes.sgml @@ -1,4 +1,4 @@ - + <productname>PostgreSQL</productname> Error Codes @@ -541,6 +541,29 @@ untranslatable_character + +2200M +INVALID XML DOCUMENT +invalid_xml_document + + + +2200N +INVALID XML CONTENT +invalid_xml_content + + + +2200S +INVALID XML COMMENT +invalid_xml_comment + + + +2200T +INVALID XML PROCESSING INSTRUCTION +invalid_xml_processing_instruction + Class 23 — Integrity Constraint Violation diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml index a56ba9ad9b8..3119671fc4b 100644 --- a/doc/src/sgml/installation.sgml +++ b/doc/src/sgml/installation.sgml @@ -1,4 +1,4 @@ - + <![%standalone-include[<productname>PostgreSQL</>]]> @@ -909,7 +909,7 @@ su - postgres <term><option>--with-libxml</option></term> <listitem> <para> - Build with libxml, required for SQL/XML support. + Build with libxml (enables SQL/XML support). </para> </listitem> </varlistentry> diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 1e9865c3007..145da9b4636 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.201 2006/12/23 00:43:09 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.202 2006/12/24 00:29:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -117,11 +117,11 @@ static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr, static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalNullTest(NullTestState *nstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); @@ -2638,6 +2638,237 @@ ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext, return result; } +/* ---------------------------------------------------------------- + * ExecEvalXml + * ---------------------------------------------------------------- + */ +static Datum +ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone) +{ + XmlExpr *xexpr = (XmlExpr *) xmlExpr->xprstate.expr; + text *result; + StringInfoData buf; + Datum value; + bool isnull; + char *str; + ListCell *arg; + ListCell *narg; + bool found_arg; + int i; + + if (isDone) + *isDone = ExprSingleResult; + *isNull = true; /* until we get a result */ + + switch (xexpr->op) + { + case IS_XMLCONCAT: + initStringInfo(&buf); + foreach(arg, xmlExpr->args) + { + ExprState *e = (ExprState *) lfirst(arg); + + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (!isnull) + { + /* we know the value is XML type */ + str = DatumGetCString(DirectFunctionCall1(xml_out, + value)); + appendStringInfoString(&buf, str); + pfree(str); + *isNull = false; + } + } + break; + + case IS_XMLELEMENT: + initStringInfo(&buf); + *isNull = false; + appendStringInfo(&buf, "<%s", xexpr->name); + i = 0; + forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names) + { + ExprState *e = (ExprState *) lfirst(arg); + char *argname = strVal(lfirst(narg)); + + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (!isnull) + { + str = OutputFunctionCall(&xmlExpr->named_outfuncs[i], + value); + appendStringInfo(&buf, " %s=\"%s\"", argname, str); + pfree(str); + } + i++; + } + + found_arg = false; + foreach(arg, xmlExpr->args) + { + ExprState *e = (ExprState *) lfirst(arg); + + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (!isnull) + { + if (!found_arg) + { + appendStringInfoChar(&buf, '>'); + found_arg = true; + } + + /* we know the value is XML type */ + str = DatumGetCString(DirectFunctionCall1(xml_out, + value)); + appendStringInfoString(&buf, str); + pfree(str); + } + } + + if (!found_arg) + appendStringInfo(&buf, "/>"); + else + appendStringInfo(&buf, "</%s>", xexpr->name); + break; + + case IS_XMLFOREST: + initStringInfo(&buf); + i = 0; + forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names) + { + ExprState *e = (ExprState *) lfirst(arg); + char *argname = strVal(lfirst(narg)); + + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (!isnull) + { + str = OutputFunctionCall(&xmlExpr->named_outfuncs[i], + value); + appendStringInfo(&buf, "<%s>%s</%s>", + argname, str, argname); + pfree(str); + *isNull = false; + } + i++; + } + break; + + /* The remaining cases don't need to set up buf */ + case IS_XMLPARSE: + { + ExprState *e; + text *data; + bool is_document; + bool preserve_whitespace; + + /* arguments are known to be text, bool, bool */ + Assert(list_length(xmlExpr->args) == 3); + + e = (ExprState *) linitial(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) + return (Datum) 0; + data = DatumGetTextP(value); + + e = (ExprState *) lsecond(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) /* probably can't happen */ + return (Datum) 0; + is_document = DatumGetBool(value); + + e = (ExprState *) lthird(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) /* probably can't happen */ + return (Datum) 0; + preserve_whitespace = DatumGetBool(value); + + *isNull = false; + + return PointerGetDatum(xmlparse(data, + is_document, + preserve_whitespace)); + } + break; + + case IS_XMLPI: + { + ExprState *e; + text *arg; + + /* optional argument is known to be text */ + Assert(list_length(xmlExpr->args) <= 1); + + if (xmlExpr->args) + { + e = (ExprState *) linitial(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) + return (Datum) 0; + arg = DatumGetTextP(value); + } + else + arg = NULL; + + *isNull = false; + + return PointerGetDatum(xmlpi(xexpr->name, arg)); + } + break; + + case IS_XMLROOT: + { + ExprState *e; + xmltype *data; + text *version; + int standalone; + + /* arguments are known to be xml, text, bool */ + Assert(list_length(xmlExpr->args) == 3); + + e = (ExprState *) linitial(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) + return (Datum) 0; + data = DatumGetXmlP(value); + + e = (ExprState *) lsecond(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) + version = NULL; + else + version = DatumGetTextP(value); + + e = (ExprState *) lthird(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) + standalone = 0; + else + standalone = (DatumGetBool(value) ? 1 : -1); + + *isNull = false; + + return PointerGetDatum(xmlroot(data, + version, + standalone)); + } + break; + } + + if (*isNull) + result = NULL; + else + { + int len = buf.len + VARHDRSZ; + + result = palloc(len); + VARATT_SIZEP(result) = len; + memcpy(VARDATA(result), buf.data, buf.len); + } + + pfree(buf.data); + return PointerGetDatum(result); +} + /* ---------------------------------------------------------------- * ExecEvalNullIf * @@ -2881,120 +3112,6 @@ ExecEvalBooleanTest(GenericExprState *bstate, } } -/* ---------------------------------------------------------------- - * ExecEvalXml - * ---------------------------------------------------------------- - */ - -static Datum -ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) -{ - StringInfoData buf; - bool isnull; - ListCell *arg; - text *result = NULL; - int len; - - initStringInfo(&buf); - - *isNull = false; - - if (isDone) - *isDone = ExprSingleResult; - - switch (xmlExpr->op) - { - case IS_XMLCONCAT: - *isNull = true; - - foreach(arg, xmlExpr->args) - { - ExprState *e = (ExprState *) lfirst(arg); - Datum value = ExecEvalExpr(e, econtext, &isnull, NULL); - - if (!isnull) - { - appendStringInfoString(&buf, DatumGetCString(OidFunctionCall1(xmlExpr->arg_typeout, value))); - *isNull = false; - } - } - break; - - case IS_XMLELEMENT: - { - int state = 0, i = 0; - appendStringInfo(&buf, "<%s", xmlExpr->name); - foreach(arg, xmlExpr->named_args) - { - GenericExprState *gstate = (GenericExprState *) lfirst(arg); - Datum value = ExecEvalExpr(gstate->arg, econtext, &isnull, NULL); - if (!isnull) - { - char *outstr = DatumGetCString(OidFunctionCall1(xmlExpr->named_args_tcache[i], value)); - appendStringInfo(&buf, " %s=\"%s\"", xmlExpr->named_args_ncache[i], outstr); - pfree(outstr); - } - i++; - } - if (xmlExpr->args) - { - ExprState *expr = linitial(xmlExpr->args); - Datum value = ExecEvalExpr(expr, econtext, &isnull, NULL); - - if (!isnull) - { - char *outstr = DatumGetCString(OidFunctionCall1(xmlExpr->arg_typeout, value)); - if (state == 0) - { - appendStringInfoChar(&buf, '>'); - state = 1; - } - appendStringInfo(&buf, "%s", outstr); - pfree(outstr); - } - } - - if (state == 0) - appendStringInfo(&buf, "/>"); - else if (state == 1) - appendStringInfo(&buf, "</%s>", xmlExpr->name); - - } - break; - - case IS_XMLFOREST: - { - /* only if all argumets are null returns null */ - int i = 0; - *isNull = true; - foreach(arg, xmlExpr->named_args) - { - GenericExprState *gstate = (GenericExprState *) lfirst(arg); - Datum value = ExecEvalExpr(gstate->arg, econtext, &isnull, NULL); - if (!isnull) - { - char *outstr = DatumGetCString(OidFunctionCall1(xmlExpr->named_args_tcache[i], value)); - appendStringInfo(&buf, "<%s>%s</%s>", xmlExpr->named_args_ncache[i], outstr, xmlExpr->named_args_ncache[i]); - pfree(outstr); - *isNull = false; - } - i += 1; - } - } - break; - default: - break; - } - - len = buf.len + VARHDRSZ; - result = palloc(len); - VARATT_SIZEP(result) = len; - memcpy(VARDATA(result), buf.data, buf.len); - pfree(buf.data); - PG_RETURN_TEXT_P(result); -} - /* * ExecEvalCoerceToDomain * @@ -3794,59 +3911,45 @@ ExecInitExpr(Expr *node, PlanState *parent) break; case T_XmlExpr: { - List *outlist; - ListCell *arg; XmlExpr *xexpr = (XmlExpr *) node; XmlExprState *xstate = makeNode(XmlExprState); - int i = 0; - Oid typeout; - - xstate->name = xexpr->name; - + List *outlist; + ListCell *arg; + int i; + xstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalXml; - xstate->op = xexpr->op; - + xstate->named_outfuncs = (FmgrInfo *) + palloc0(list_length(xexpr->named_args) * sizeof(FmgrInfo)); outlist = NIL; - if (xexpr->named_args) + i = 0; + foreach(arg, xexpr->named_args) { - xstate->named_args_tcache = (Oid *) palloc(list_length(xexpr->named_args) * sizeof(int)); - xstate->named_args_ncache = (char **) palloc(list_length(xexpr->named_args) * sizeof(char *)); - - i = 0; - foreach(arg, xexpr->named_args) - { - bool tpisvarlena; - Expr *e = (Expr *) lfirst(arg); - ExprState *estate = ExecInitExpr(e, parent); - TargetEntry *tle; - outlist = lappend(outlist, estate); - tle = (TargetEntry *) ((GenericExprState *) estate)->xprstate.expr; - getTypeOutputInfo(exprType((Node *)tle->expr), &typeout, &tpisvarlena); - xstate->named_args_ncache[i] = tle->resname; - xstate->named_args_tcache[i] = typeout; - i++; - } - } - else - { - xstate->named_args_tcache = NULL; - xstate->named_args_ncache = NULL; + Expr *e = (Expr *) lfirst(arg); + ExprState *estate; + Oid typOutFunc; + bool typIsVarlena; + + estate = ExecInitExpr(e, parent); + outlist = lappend(outlist, estate); + + getTypeOutputInfo(exprType((Node *) e), + &typOutFunc, &typIsVarlena); + fmgr_info(typOutFunc, &xstate->named_outfuncs[i]); + i++; } xstate->named_args = outlist; - outlist = NIL; + outlist = NIL; foreach(arg, xexpr->args) { - bool tpisvarlena; - ExprState *estate; - Expr *e = (Expr *) lfirst(arg); - getTypeOutputInfo(exprType((Node *)e), &typeout, &tpisvarlena); + Expr *e = (Expr *) lfirst(arg); + ExprState *estate; + estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); } - xstate->arg_typeout = typeout; xstate->args = outlist; - + state = (ExprState *) xstate; } break; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index ca841cb181a..d46ed57d830 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.356 2006/12/23 00:43:09 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.357 2006/12/24 00:29:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1093,6 +1093,23 @@ _copyMinMaxExpr(MinMaxExpr *from) return newnode; } +/* + * _copyXmlExpr + */ +static XmlExpr * +_copyXmlExpr(XmlExpr *from) +{ + XmlExpr *newnode = makeNode(XmlExpr); + + COPY_SCALAR_FIELD(op); + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(named_args); + COPY_NODE_FIELD(arg_names); + COPY_NODE_FIELD(args); + + return newnode; +} + /* * _copyNullIfExpr (same as OpExpr) */ @@ -1138,22 +1155,6 @@ _copyBooleanTest(BooleanTest *from) return newnode; } -/* - * _copyXmlExpr - */ -static XmlExpr * -_copyXmlExpr(XmlExpr *from) -{ - XmlExpr *newnode = makeNode(XmlExpr); - - COPY_SCALAR_FIELD(op); - COPY_STRING_FIELD(name); - COPY_NODE_FIELD(named_args); - COPY_NODE_FIELD(args); - - return newnode; -} - /* * _copyCoerceToDomain */ @@ -2977,6 +2978,9 @@ copyObject(void *from) case T_MinMaxExpr: retval = _copyMinMaxExpr(from); break; + case T_XmlExpr: + retval = _copyXmlExpr(from); + break; case T_NullIfExpr: retval = _copyNullIfExpr(from); break; @@ -2986,9 +2990,6 @@ copyObject(void *from) case T_BooleanTest: retval = _copyBooleanTest(from); break; - case T_XmlExpr: - retval = _copyXmlExpr(from); - break; case T_CoerceToDomain: retval = _copyCoerceToDomain(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index a7c4ef4e2a8..29bff448c7f 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.290 2006/12/23 00:43:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.291 2006/12/24 00:29:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -454,6 +454,18 @@ _equalMinMaxExpr(MinMaxExpr *a, MinMaxExpr *b) return true; } +static bool +_equalXmlExpr(XmlExpr *a, XmlExpr *b) +{ + COMPARE_SCALAR_FIELD(op); + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(named_args); + COMPARE_NODE_FIELD(arg_names); + COMPARE_NODE_FIELD(args); + + return true; +} + static bool _equalNullIfExpr(NullIfExpr *a, NullIfExpr *b) { @@ -495,17 +507,6 @@ _equalBooleanTest(BooleanTest *a, BooleanTest *b) return true; } -static bool -_equalXmlExpr(XmlExpr *a, XmlExpr *b) -{ - COMPARE_SCALAR_FIELD(op); - COMPARE_STRING_FIELD(name); - COMPARE_NODE_FIELD(named_args); - COMPARE_NODE_FIELD(args); - - return true; -} - static bool _equalCoerceToDomain(CoerceToDomain *a, CoerceToDomain *b) { @@ -1971,6 +1972,9 @@ equal(void *a, void *b) case T_MinMaxExpr: retval = _equalMinMaxExpr(a, b); break; + case T_XmlExpr: + retval = _equalXmlExpr(a, b); + break; case T_NullIfExpr: retval = _equalNullIfExpr(a, b); break; @@ -1980,9 +1984,6 @@ equal(void *a, void *b) case T_BooleanTest: retval = _equalBooleanTest(a, b); break; - case T_XmlExpr: - retval = _equalXmlExpr(a, b); - break; case T_CoerceToDomain: retval = _equalCoerceToDomain(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b18b6988cfa..4911d6ed404 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.288 2006/12/23 00:43:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.289 2006/12/24 00:29:18 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -892,6 +892,18 @@ _outMinMaxExpr(StringInfo str, MinMaxExpr *node) WRITE_NODE_FIELD(args); } +static void +_outXmlExpr(StringInfo str, XmlExpr *node) +{ + WRITE_NODE_TYPE("XMLEXPR"); + + WRITE_ENUM_FIELD(op, XmlExprOp); + WRITE_STRING_FIELD(name); + WRITE_NODE_FIELD(named_args); + WRITE_NODE_FIELD(arg_names); + WRITE_NODE_FIELD(args); +} + static void _outNullIfExpr(StringInfo str, NullIfExpr *node) { @@ -922,17 +934,6 @@ _outBooleanTest(StringInfo str, BooleanTest *node) WRITE_ENUM_FIELD(booltesttype, BoolTestType); } -static void -_outXmlExpr(StringInfo str, XmlExpr *node) -{ - WRITE_NODE_TYPE("XMLEXPR"); - - WRITE_ENUM_FIELD(op, XmlExprOp); - WRITE_STRING_FIELD(name); - WRITE_NODE_FIELD(named_args); - WRITE_NODE_FIELD(args); -} - static void _outCoerceToDomain(StringInfo str, CoerceToDomain *node) { @@ -2026,6 +2027,9 @@ _outNode(StringInfo str, void *obj) case T_MinMaxExpr: _outMinMaxExpr(str, obj); break; + case T_XmlExpr: + _outXmlExpr(str, obj); + break; case T_NullIfExpr: _outNullIfExpr(str, obj); break; @@ -2035,9 +2039,6 @@ _outNode(StringInfo str, void *obj) case T_BooleanTest: _outBooleanTest(str, obj); break; - case T_XmlExpr: - _outXmlExpr(str, obj); - break; case T_CoerceToDomain: _outCoerceToDomain(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 37b39439eb0..781eeee14f1 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.198 2006/12/23 00:43:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.199 2006/12/24 00:29:18 tgl Exp $ * * NOTES * Path and Plan nodes do not have any readfuncs support, because we @@ -708,6 +708,23 @@ _readMinMaxExpr(void) READ_DONE(); } +/* + * _readXmlExpr + */ +static XmlExpr * +_readXmlExpr(void) +{ + READ_LOCALS(XmlExpr); + + READ_ENUM_FIELD(op, XmlExprOp); + READ_STRING_FIELD(name); + READ_NODE_FIELD(named_args); + READ_NODE_FIELD(arg_names); + READ_NODE_FIELD(args); + + READ_DONE(); +} + /* * _readNullIfExpr */ @@ -764,22 +781,6 @@ _readBooleanTest(void) READ_DONE(); } -/* - * _readXmlExpr - */ -static XmlExpr * -_readXmlExpr(void) -{ - READ_LOCALS(XmlExpr); - - READ_ENUM_FIELD(op, XmlExprOp); - READ_STRING_FIELD(name); - READ_NODE_FIELD(named_args); - READ_NODE_FIELD(args); - - READ_DONE(); -} - /* * _readCoerceToDomain */ @@ -1024,14 +1025,14 @@ parseNodeString(void) return_value = _readCoalesceExpr(); else if (MATCH("MINMAX", 6)) return_value = _readMinMaxExpr(); + else if (MATCH("XMLEXPR", 7)) + return_value = _readXmlExpr(); else if (MATCH("NULLIFEXPR", 10)) return_value = _readNullIfExpr(); else if (MATCH("NULLTEST", 8)) return_value = _readNullTest(); else if (MATCH("BOOLEANTEST", 11)) return_value = _readBooleanTest(); - else if (MATCH("XMLEXPR", 7)) - return_value = _readXmlExpr(); else if (MATCH("COERCETODOMAIN", 14)) return_value = _readCoerceToDomain(); else if (MATCH("COERCETODOMAINVALUE", 19)) diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 0f720c40e93..5ad97aabc34 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.225 2006/12/23 00:43:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.226 2006/12/24 00:29:18 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -557,10 +557,10 @@ expression_returns_set_walker(Node *node, void *context) return false; if (IsA(node, MinMaxExpr)) return false; - if (IsA(node, NullIfExpr)) - return false; if (IsA(node, XmlExpr)) return false; + if (IsA(node, NullIfExpr)) + return false; return expression_tree_walker(node, expression_returns_set_walker, context); @@ -872,14 +872,14 @@ contain_nonstrict_functions_walker(Node *node, void *context) return true; if (IsA(node, MinMaxExpr)) return true; + if (IsA(node, XmlExpr)) + return true; if (IsA(node, NullIfExpr)) return true; if (IsA(node, NullTest)) return true; if (IsA(node, BooleanTest)) return true; - if (IsA(node, XmlExpr)) - return true; return expression_tree_walker(node, contain_nonstrict_functions_walker, context); } @@ -3328,22 +3328,23 @@ expression_tree_walker(Node *node, return walker(((CoalesceExpr *) node)->args, context); case T_MinMaxExpr: return walker(((MinMaxExpr *) node)->args, context); - case T_NullIfExpr: - return walker(((NullIfExpr *) node)->args, context); - case T_NullTest: - return walker(((NullTest *) node)->arg, context); - case T_BooleanTest: - return walker(((BooleanTest *) node)->arg, context); case T_XmlExpr: { XmlExpr *xexpr = (XmlExpr *) node; if (walker(xexpr->named_args, context)) return true; + /* we assume walker doesn't care about arg_names */ if (walker(xexpr->args, context)) return true; } break; + case T_NullIfExpr: + return walker(((NullIfExpr *) node)->args, context); + case T_NullTest: + return walker(((NullTest *) node)->arg, context); + case T_BooleanTest: + return walker(((BooleanTest *) node)->arg, context); case T_CoerceToDomain: return walker(((CoerceToDomain *) node)->arg, context); case T_TargetEntry: @@ -3874,6 +3875,7 @@ expression_tree_mutator(Node *node, FLATCOPY(newnode, xexpr, XmlExpr); MUTATE(newnode->named_args, xexpr->named_args, List *); + /* assume mutator does not care about arg_names */ MUTATE(newnode->args, xexpr->args, List *); return (Node *) newnode; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index cc400407363..a1511870f28 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.569 2006/12/21 16:05:14 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.570 2006/12/24 00:29:18 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -348,8 +348,8 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) %type <target> xml_attribute_el %type <list> xml_attribute_list xml_attributes -%type <node> xml_root_version -%type <ival> opt_xml_root_standalone document_or_content xml_whitespace_option +%type <node> xml_root_version opt_xml_root_standalone +%type <boolean> document_or_content xml_whitespace_option /* @@ -371,13 +371,13 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT COMMITTED CONCURRENTLY CONNECTION CONSTRAINT CONSTRAINTS - CONTENT CONVERSION_P CONVERT COPY CREATE CREATEDB + CONTENT_P CONVERSION_P CONVERT COPY CREATE CREATEDB CREATEROLE CREATEUSER CROSS CSV CURRENT_DATE CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS - DESC DISABLE_P DISTINCT DO DOCUMENT DOMAIN_P DOUBLE_P DROP + DESC DISABLE_P DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT @@ -404,7 +404,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE - NAME NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NOCREATEDB + NAME_P NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NOCREATEDB NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NUMERIC @@ -423,9 +423,9 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE - SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE START STATEMENT - STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP SUBSTRING SUPERUSER_P SYMMETRIC - SYSID SYSTEM_P + SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE_P START STATEMENT + STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P + SYMMETRIC SYSID SYSTEM_P TABLE TABLESPACE TEMP TEMPLATE TEMPORARY THEN TIME TIMESTAMP TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P @@ -434,15 +434,15 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL UPDATE USER USING - VACUUM VALID VALIDATOR VALUE VALUES VARCHAR VARYING - VERBOSE VERSION VIEW VOLATILE + VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARYING + VERBOSE VERSION_P VIEW VOLATILE - WHEN WHERE WHITESPACE WITH WITHOUT WORK WRITE + WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLFOREST XMLPARSE XMLPI XMLROOT XMLSERIALIZE - YEAR_P YES + YEAR_P YES_P ZONE @@ -493,7 +493,8 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) * left-associativity among the JOIN rules themselves. */ %left JOIN CROSS LEFT FULL RIGHT INNER_P NATURAL -%right PRESERVE STRIP +/* kluge to keep xml_whitespace_option from causing shift/reduce conflicts */ +%right PRESERVE STRIP_P %% /* @@ -7880,95 +7881,54 @@ func_expr: func_name '(' ')' } | XMLCONCAT '(' expr_list ')' { - $$ = makeXmlExpr(IS_XMLCONCAT, NULL, NULL, $3); + $$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3); } - | XMLELEMENT '(' NAME ColLabel ')' + | XMLELEMENT '(' NAME_P ColLabel ')' { - $$ = makeXmlExpr(IS_XMLELEMENT, $4, NULL, NULL); + $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL); } - | XMLELEMENT '(' NAME ColLabel ',' xml_attributes ')' + | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ')' { - $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NULL); + $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL); } - | XMLELEMENT '(' NAME ColLabel ',' expr_list ')' + | XMLELEMENT '(' NAME_P ColLabel ',' expr_list ')' { - $$ = makeXmlExpr(IS_XMLELEMENT, $4, NULL, $6); + $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6); } - | XMLELEMENT '(' NAME ColLabel ',' xml_attributes ',' expr_list ')' + | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' expr_list ')' { $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8); } | XMLFOREST '(' xml_attribute_list ')' { - $$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NULL); + $$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL); } | XMLPARSE '(' document_or_content a_expr xml_whitespace_option ')' { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("xmlparse"); - n->args = list_make3(makeBoolAConst($3 == DOCUMENT), $4, makeBoolAConst($5 == PRESERVE)); - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->location = @1; - $$ = (Node *)n; + $$ = makeXmlExpr(IS_XMLPARSE, NULL, NIL, + list_make3($4, + makeBoolAConst($3), + makeBoolAConst($5))); } - | XMLPI '(' NAME ColLabel ')' + | XMLPI '(' NAME_P ColLabel ')' { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("xmlpi"); - n->args = list_make1(makeStringConst($4, NULL)); - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->location = @1; - $$ = (Node *)n; + $$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL); } - | XMLPI '(' NAME ColLabel ',' a_expr ')' + | XMLPI '(' NAME_P ColLabel ',' a_expr ')' { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("xmlpi"); - n->args = list_make2(makeStringConst($4, NULL), $6); - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->location = @1; - $$ = (Node *)n; + $$ = makeXmlExpr(IS_XMLPI, $4, NULL, list_make1($6)); } | XMLROOT '(' a_expr ',' xml_root_version opt_xml_root_standalone ')' { - FuncCall *n = makeNode(FuncCall); - Node *ver; - A_Const *sa; - - if ($5) - ver = $5; - else - { - A_Const *val; - - val = makeNode(A_Const); - val->val.type = T_Null; - ver = (Node *) val; - } - - if ($6) - sa = makeBoolAConst($6 == 1); - else - { - sa = makeNode(A_Const); - sa->val.type = T_Null; - } - - n->funcname = SystemFuncName("xmlroot"); - n->args = list_make3($3, ver, sa); - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->location = @1; - $$ = (Node *)n; + $$ = makeXmlExpr(IS_XMLROOT, NULL, NIL, + list_make3($3, $5, $6)); } | XMLSERIALIZE '(' document_or_content a_expr AS Typename ')' { /* * FIXME: This should be made distinguishable from - * CAST (for reverse compilation at least). + * CAST (for reverse compilation at least). Also, + * what about the document/content option?? */ $$ = makeTypeCast($4, $6); } @@ -7977,17 +7937,35 @@ func_expr: func_name '(' ')' /* * SQL/XML support */ -xml_root_version: VERSION a_expr { $$ = $2; } - | VERSION NO VALUE { $$ = NULL; } +xml_root_version: VERSION_P a_expr + { $$ = $2; } + | VERSION_P NO VALUE_P + { + A_Const *val = makeNode(A_Const); + val->val.type = T_Null; + $$ = (Node *) val; + } ; -opt_xml_root_standalone: ',' STANDALONE YES { $$ = 1; } - | ',' STANDALONE NO { $$ = -1; } - | ',' STANDALONE NO VALUE { $$ = 0; } - | /*EMPTY*/ { $$ = 0; } +opt_xml_root_standalone: ',' STANDALONE_P YES_P + { $$ = (Node *) makeBoolAConst(true); } + | ',' STANDALONE_P NO + { $$ = (Node *) makeBoolAConst(false); } + | ',' STANDALONE_P NO VALUE_P + { + A_Const *val = makeNode(A_Const); + val->val.type = T_Null; + $$ = (Node *) val; + } + | /*EMPTY*/ + { + A_Const *val = makeNode(A_Const); + val->val.type = T_Null; + $$ = (Node *) val; + } ; -xml_attributes: XMLATTRIBUTES '(' xml_attribute_list ')' { $$ = $3; } +xml_attributes: XMLATTRIBUTES '(' xml_attribute_list ')' { $$ = $3; } ; xml_attribute_list: xml_attribute_el { $$ = list_make1($1); } @@ -8000,7 +7978,7 @@ xml_attribute_el: a_expr AS ColLabel $$->name = $3; $$->indirection = NULL; $$->val = (Node *) $1; - + $$->location = @1; } | a_expr { @@ -8008,16 +7986,21 @@ xml_attribute_el: a_expr AS ColLabel $$->name = NULL; $$->indirection = NULL; $$->val = (Node *) $1; + $$->location = @1; } ; -document_or_content: DOCUMENT { $$ = DOCUMENT; } - | CONTENT { $$ = CONTENT; } +document_or_content: DOCUMENT_P { $$ = TRUE; } + | CONTENT_P { $$ = FALSE; } ; -xml_whitespace_option: PRESERVE WHITESPACE { $$ = PRESERVE; } - | STRIP WHITESPACE { $$ = STRIP; } - | /*EMPTY*/ { $$ = STRIP; } +/* + * XXX per SQL spec, the default should be STRIP WHITESPACE, but since we + * haven't implemented that yet, temporarily default to PRESERVE. + */ +xml_whitespace_option: PRESERVE WHITESPACE_P { $$ = TRUE; } + | STRIP_P WHITESPACE_P { $$ = FALSE; } + | /*EMPTY*/ { $$ = TRUE; } ; /* @@ -8712,7 +8695,7 @@ unreserved_keyword: | CONCURRENTLY | CONNECTION | CONSTRAINTS - | CONTENT + | CONTENT_P | CONVERSION_P | COPY | CREATEDB @@ -8732,7 +8715,7 @@ unreserved_keyword: | DELIMITER | DELIMITERS | DISABLE_P - | DOCUMENT + | DOCUMENT_P | DOMAIN_P | DOUBLE_P | DROP @@ -8792,7 +8775,7 @@ unreserved_keyword: | MODE | MONTH_P | MOVE - | NAME + | NAME_P | NAMES | NEXT | NO @@ -8853,18 +8836,18 @@ unreserved_keyword: | SHOW | SIMPLE | STABLE - | STANDALONE + | STANDALONE_P | START | STATEMENT | STATISTICS | STDIN | STDOUT | STORAGE - | STRIP + | STRICT_P + | STRIP_P | SUPERUSER_P | SYSID | SYSTEM_P - | STRICT_P | TABLESPACE | TEMP | TEMPLATE @@ -8883,18 +8866,18 @@ unreserved_keyword: | VACUUM | VALID | VALIDATOR + | VALUE_P | VARYING - | VERSION + | VERSION_P | VIEW - | VALUE | VOLATILE - | WHITESPACE + | WHITESPACE_P | WITH | WITHOUT | WORK | WRITE | YEAR_P - | YES + | YES_P | ZONE ; @@ -8948,8 +8931,8 @@ col_name_keyword: | VALUES | VARCHAR | XMLATTRIBUTES - | XMLELEMENT | XMLCONCAT + | XMLELEMENT | XMLFOREST | XMLPARSE | XMLPI @@ -9492,10 +9475,16 @@ doNegateFloat(Value *v) static Node * makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) { - XmlExpr *x = makeNode(XmlExpr); + XmlExpr *x = makeNode(XmlExpr); + x->op = op; x->name = name; + /* + * named_args is a list of ResTarget; it'll be split apart into separate + * expression and name lists in transformXmlExpr(). + */ x->named_args = named_args; + x->arg_names = NIL; x->args = args; return (Node *) x; } diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index b5e49e955fc..4865d360b3e 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.178 2006/12/21 16:05:14 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.179 2006/12/24 00:29:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -89,7 +89,7 @@ static const ScanKeyword ScanKeywords[] = { {"connection", CONNECTION}, {"constraint", CONSTRAINT}, {"constraints", CONSTRAINTS}, - {"content", CONTENT}, + {"content", CONTENT_P}, {"conversion", CONVERSION_P}, {"convert", CONVERT}, {"copy", COPY}, @@ -124,7 +124,7 @@ static const ScanKeyword ScanKeywords[] = { {"disable", DISABLE_P}, {"distinct", DISTINCT}, {"do", DO}, - {"document", DOCUMENT}, + {"document", DOCUMENT_P}, {"domain", DOMAIN_P}, {"double", DOUBLE_P}, {"drop", DROP}, @@ -220,7 +220,7 @@ static const ScanKeyword ScanKeywords[] = { {"mode", MODE}, {"month", MONTH_P}, {"move", MOVE}, - {"name", NAME}, + {"name", NAME_P}, {"names", NAMES}, {"national", NATIONAL}, {"natural", NATURAL}, @@ -317,7 +317,7 @@ static const ScanKeyword ScanKeywords[] = { {"smallint", SMALLINT}, {"some", SOME}, {"stable", STABLE}, - {"standalone", STANDALONE}, + {"standalone", STANDALONE_P}, {"start", START}, {"statement", STATEMENT}, {"statistics", STATISTICS}, @@ -325,7 +325,7 @@ static const ScanKeyword ScanKeywords[] = { {"stdout", STDOUT}, {"storage", STORAGE}, {"strict", STRICT_P}, - {"strip", STRIP}, + {"strip", STRIP_P}, {"substring", SUBSTRING}, {"superuser", SUPERUSER_P}, {"symmetric", SYMMETRIC}, @@ -362,17 +362,17 @@ static const ScanKeyword ScanKeywords[] = { {"vacuum", VACUUM}, {"valid", VALID}, {"validator", VALIDATOR}, - {"value", VALUE}, + {"value", VALUE_P}, {"values", VALUES}, {"varchar", VARCHAR}, {"varying", VARYING}, {"verbose", VERBOSE}, - {"version", VERSION}, + {"version", VERSION_P}, {"view", VIEW}, {"volatile", VOLATILE}, {"when", WHEN}, {"where", WHERE}, - {"whitespace", WHITESPACE}, + {"whitespace", WHITESPACE_P}, {"with", WITH}, {"without", WITHOUT}, {"work", WORK}, @@ -386,7 +386,7 @@ static const ScanKeyword ScanKeywords[] = { {"xmlroot", XMLROOT}, {"xmlserialize", XMLSERIALIZE}, {"year", YEAR_P}, - {"yes", YES}, + {"yes", YES_P}, {"zone", ZONE}, }; diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 996c724d1ce..145caab101a 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.159 2006/11/28 12:54:41 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.160 2006/12/24 00:29:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1094,7 +1094,7 @@ transformLimitClause(ParseState *pstate, Node *clause, qual = transformExpr(pstate, clause); - qual = coerce_to_bigint(pstate, qual, constructName); + qual = coerce_to_specific_type(pstate, qual, INT8OID, constructName); /* * LIMIT can't refer to any vars or aggregates of the current query; we diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 5670ed4fe74..45c8e97be1d 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.148 2006/12/21 16:05:14 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.149 2006/12/24 00:29:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -840,8 +840,8 @@ coerce_to_boolean(ParseState *pstate, Node *node, } /* - * coerce_to_integer() - * Coerce an argument of a construct that requires integer input. + * coerce_to_specific_type() + * Coerce an argument of a construct that requires a specific data type. * Also check that input is not a set. * * Returns the possibly-transformed node tree. @@ -850,103 +850,26 @@ coerce_to_boolean(ParseState *pstate, Node *node, * processing is wanted. */ Node * -coerce_to_integer(ParseState *pstate, Node *node, - const char *constructName) +coerce_to_specific_type(ParseState *pstate, Node *node, + Oid targetTypeId, + const char *constructName) { Oid inputTypeId = exprType(node); - if (inputTypeId != INT4OID) + if (inputTypeId != targetTypeId) { node = coerce_to_target_type(pstate, node, inputTypeId, - INT4OID, -1, + targetTypeId, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); if (node == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), /* translator: first %s is name of a SQL construct, eg LIMIT */ - errmsg("argument of %s must be type integer, not type %s", - constructName, format_type_be(inputTypeId)))); - } - - if (expression_returns_set(node)) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - /* translator: %s is name of a SQL construct, eg LIMIT */ - errmsg("argument of %s must not return a set", - constructName))); - - return node; -} - -/* - * coerce_to_bigint() - * Coerce an argument of a construct that requires int8 input. - * Also check that input is not a set. - * - * Returns the possibly-transformed node tree. - * - * As with coerce_type, pstate may be NULL if no special unknown-Param - * processing is wanted. - */ -Node * -coerce_to_bigint(ParseState *pstate, Node *node, - const char *constructName) -{ - Oid inputTypeId = exprType(node); - - if (inputTypeId != INT8OID) - { - node = coerce_to_target_type(pstate, node, inputTypeId, - INT8OID, -1, - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST); - if (node == NULL) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - /* translator: first %s is name of a SQL construct, eg LIMIT */ - errmsg("argument of %s must be type bigint, not type %s", - constructName, format_type_be(inputTypeId)))); - } - - if (expression_returns_set(node)) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - /* translator: %s is name of a SQL construct, eg LIMIT */ - errmsg("argument of %s must not return a set", - constructName))); - - return node; -} - -/* - * coerce_to_xml() - * Coerce an argument of a construct that requires xml input. - * Also check that input is not a set. - * - * Returns the possibly-transformed node tree. - * - * As with coerce_type, pstate may be NULL if no special unknown-Param - * processing is wanted. - */ -Node * -coerce_to_xml(ParseState *pstate, Node *node, - const char *constructName) -{ - Oid inputTypeId = exprType(node); - - if (inputTypeId != XMLOID) - { - node = coerce_to_target_type(pstate, node, inputTypeId, - XMLOID, -1, - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST); - if (node == NULL) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - /* translator: first %s is name of a SQL construct, eg LIMIT */ - errmsg("argument of %s must be type xml, not type %s", - constructName, format_type_be(inputTypeId)))); + errmsg("argument of %s must be type %s, not type %s", + constructName, + format_type_be(targetTypeId), + format_type_be(inputTypeId)))); } if (expression_returns_set(node)) diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 2a48741b3a2..7dbbb9a33a8 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.201 2006/12/23 00:43:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.202 2006/12/24 00:29:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -220,6 +220,10 @@ transformExpr(ParseState *pstate, Node *expr) result = transformMinMaxExpr(pstate, (MinMaxExpr *) expr); break; + case T_XmlExpr: + result = transformXmlExpr(pstate, (XmlExpr *) expr); + break; + case T_NullTest: { NullTest *n = (NullTest *) expr; @@ -234,10 +238,6 @@ transformExpr(ParseState *pstate, Node *expr) result = transformBooleanTest(pstate, (BooleanTest *) expr); break; - case T_XmlExpr: - result = transformXmlExpr(pstate, (XmlExpr *) expr); - break; - /********************************************* * Quietly accept node types that may be presented when we are * called on an already-transformed tree. @@ -1375,6 +1375,107 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m) return (Node *) newm; } +static Node * +transformXmlExpr(ParseState *pstate, XmlExpr *x) +{ + XmlExpr *newx = makeNode(XmlExpr); + ListCell *lc; + int i; + + newx->op = x->op; + if (x->name) + newx->name = map_sql_identifier_to_xml_name(x->name, false); + else + newx->name = NULL; + + /* + * gram.y built the named args as a list of ResTarget. Transform each, + * and break the names out as a separate list. + */ + newx->named_args = NIL; + newx->arg_names = NIL; + + foreach(lc, x->named_args) + { + ResTarget *r = (ResTarget *) lfirst(lc); + Node *expr; + char *argname; + + Assert(IsA(r, ResTarget)); + + expr = transformExpr(pstate, r->val); + + if (r->name) + argname = map_sql_identifier_to_xml_name(r->name, false); + else if (IsA(r->val, ColumnRef)) + argname = map_sql_identifier_to_xml_name(FigureColname(r->val), + true); + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + x->op == IS_XMLELEMENT + ? errmsg("unnamed attribute value must be a column reference") + : errmsg("unnamed element value must be a column reference"))); + argname = NULL; /* keep compiler quiet */ + } + + newx->named_args = lappend(newx->named_args, expr); + newx->arg_names = lappend(newx->arg_names, makeString(argname)); + } + + /* The other arguments are of varying types depending on the function */ + newx->args = NIL; + i = 0; + foreach(lc, x->args) + { + Node *e = (Node *) lfirst(lc); + Node *newe; + + newe = transformExpr(pstate, e); + switch (x->op) + { + case IS_XMLCONCAT: + newe = coerce_to_specific_type(pstate, newe, XMLOID, + "XMLCONCAT"); + break; + case IS_XMLELEMENT: + newe = coerce_to_specific_type(pstate, newe, XMLOID, + "XMLELEMENT"); + break; + case IS_XMLFOREST: + newe = coerce_to_specific_type(pstate, newe, XMLOID, + "XMLFOREST"); + break; + case IS_XMLPARSE: + if (i == 0) + newe = coerce_to_specific_type(pstate, newe, TEXTOID, + "XMLPARSE"); + else + newe = coerce_to_boolean(pstate, newe, "XMLPARSE"); + break; + case IS_XMLPI: + newe = coerce_to_specific_type(pstate, newe, TEXTOID, + "XMLPI"); + break; + case IS_XMLROOT: + if (i == 0) + newe = coerce_to_specific_type(pstate, newe, XMLOID, + "XMLROOT"); + else if (i == 1) + newe = coerce_to_specific_type(pstate, newe, TEXTOID, + "XMLROOT"); + else + newe = coerce_to_boolean(pstate, newe, "XMLROOT"); + break; + } + newx->args = lappend(newx->args, newe); + i++; + } + + return (Node *) newx; +} + static Node * transformBooleanTest(ParseState *pstate, BooleanTest *b) { @@ -1415,56 +1516,6 @@ transformBooleanTest(ParseState *pstate, BooleanTest *b) return (Node *) b; } -static Node * -transformXmlExpr(ParseState *pstate, XmlExpr *x) -{ - ListCell *lc; - XmlExpr *newx = makeNode(XmlExpr); - - newx->op = x->op; - if (x->name) - newx->name = map_sql_identifier_to_xml_name(x->name, false); - else - newx->name = NULL; - - foreach(lc, x->named_args) - { - ResTarget *r = (ResTarget *) lfirst(lc); - Node *expr = transformExpr(pstate, r->val); - char *argname = NULL; - - if (r->name) - argname = map_sql_identifier_to_xml_name(r->name, false); - else if (IsA(r->val, ColumnRef)) - argname = map_sql_identifier_to_xml_name(FigureColname(r->val), true); - else - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - x->op == IS_XMLELEMENT - ? errmsg("unnamed attribute value must be a column reference") - : errmsg("unnamed element value must be a column reference"))); - - newx->named_args = lappend(newx->named_args, - makeTargetEntry((Expr *) expr, 0, argname, false)); - } - - foreach(lc, x->args) - { - Node *e = (Node *) lfirst(lc); - Node *newe; - - newe = coerce_to_xml(pstate, transformExpr(pstate, e), - (x->op == IS_XMLCONCAT - ? "XMLCONCAT" - : (x->op == IS_XMLELEMENT - ? "XMLELEMENT" - : "XMLFOREST"))); - newx->args = lappend(newx->args, newe); - } - - return (Node *) newx; -} - /* * Construct a whole-row reference to represent the notation "relation.*". * @@ -1715,6 +1766,9 @@ exprType(Node *expr) case T_MinMaxExpr: type = ((MinMaxExpr *) expr)->minmaxtype; break; + case T_XmlExpr: + type = XMLOID; + break; case T_NullIfExpr: type = exprType((Node *) linitial(((NullIfExpr *) expr)->args)); break; @@ -1724,9 +1778,6 @@ exprType(Node *expr) case T_BooleanTest: type = BOOLOID; break; - case T_XmlExpr: - type = XMLOID; - break; case T_CoerceToDomain: type = ((CoerceToDomain *) expr)->resulttype; break; diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 906d96e45c6..ef4b9083ac6 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.150 2006/12/21 16:05:14 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.151 2006/12/24 00:29:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1328,6 +1328,15 @@ FigureColnameInternal(Node *node, char **name) case IS_XMLFOREST: *name = "xmlforest"; return 2; + case IS_XMLPARSE: + *name = "xmlparse"; + return 2; + case IS_XMLPI: + *name = "xmlpi"; + return 2; + case IS_XMLROOT: + *name = "xmlroot"; + return 2; } break; default: diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index b2ff95f457b..699b4a93704 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.237 2006/12/23 00:43:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.238 2006/12/24 00:29:19 tgl Exp $ **********************************************************************/ #include "postgres.h" @@ -2988,8 +2988,8 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_RowExpr: case T_CoalesceExpr: case T_MinMaxExpr: - case T_NullIfExpr: case T_XmlExpr: + case T_NullIfExpr: case T_Aggref: case T_FuncExpr: /* function-like: name(..) or name[..] */ @@ -3097,8 +3097,8 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_RowExpr: /* other separators */ case T_CoalesceExpr: /* own parentheses */ case T_MinMaxExpr: /* own parentheses */ - case T_NullIfExpr: /* other separators */ case T_XmlExpr: /* own parentheses */ + case T_NullIfExpr: /* other separators */ case T_Aggref: /* own parentheses */ case T_CaseExpr: /* other separators */ return true; @@ -3146,8 +3146,8 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_RowExpr: /* other separators */ case T_CoalesceExpr: /* own parentheses */ case T_MinMaxExpr: /* own parentheses */ - case T_NullIfExpr: /* other separators */ case T_XmlExpr: /* own parentheses */ + case T_NullIfExpr: /* other separators */ case T_Aggref: /* own parentheses */ case T_CaseExpr: /* other separators */ return true; @@ -3779,6 +3779,140 @@ get_rule_expr(Node *node, deparse_context *context, } break; + case T_XmlExpr: + { + XmlExpr *xexpr = (XmlExpr *) node; + bool needcomma = false; + ListCell *arg; + ListCell *narg; + Const *con; + + switch (xexpr->op) + { + case IS_XMLCONCAT: + appendStringInfoString(buf, "XMLCONCAT("); + break; + case IS_XMLELEMENT: + appendStringInfoString(buf, "XMLELEMENT("); + break; + case IS_XMLFOREST: + appendStringInfoString(buf, "XMLFOREST("); + break; + case IS_XMLPARSE: + appendStringInfoString(buf, "XMLPARSE("); + break; + case IS_XMLPI: + appendStringInfoString(buf, "XMLPI("); + break; + case IS_XMLROOT: + appendStringInfoString(buf, "XMLROOT("); + break; + } + if (xexpr->name) + { + /* + * XXX need to de-escape the name + */ + appendStringInfo(buf, "NAME %s", + quote_identifier(xexpr->name)); + needcomma = true; + } + if (xexpr->named_args) + { + if (xexpr->op != IS_XMLFOREST) + { + if (needcomma) + appendStringInfoString(buf, ", "); + appendStringInfoString(buf, "XMLATTRIBUTES("); + needcomma = false; + } + forboth(arg, xexpr->named_args, narg, xexpr->arg_names) + { + Node *e = (Node *) lfirst(arg); + char *argname = strVal(lfirst(narg)); + + if (needcomma) + appendStringInfoString(buf, ", "); + get_rule_expr((Node *) e, context, true); + /* + * XXX need to de-escape the name + */ + appendStringInfo(buf, " AS %s", + quote_identifier(argname)); + needcomma = true; + } + if (xexpr->op != IS_XMLFOREST) + appendStringInfoChar(buf, ')'); + } + if (xexpr->args) + { + if (needcomma) + appendStringInfoString(buf, ", "); + switch (xexpr->op) + { + case IS_XMLCONCAT: + case IS_XMLELEMENT: + case IS_XMLFOREST: + case IS_XMLPI: + /* no extra decoration needed */ + get_rule_expr((Node *) xexpr->args, context, true); + break; + case IS_XMLPARSE: + Assert(list_length(xexpr->args) == 3); + + con = (Const *) lsecond(xexpr->args); + Assert(IsA(con, Const)); + Assert(!con->constisnull); + if (DatumGetBool(con->constvalue)) + appendStringInfoString(buf, "DOCUMENT "); + else + appendStringInfoString(buf, "CONTENT "); + + get_rule_expr((Node *) linitial(xexpr->args), + context, true); + + con = (Const *) lthird(xexpr->args); + Assert(IsA(con, Const)); + Assert(!con->constisnull); + if (DatumGetBool(con->constvalue)) + appendStringInfoString(buf, + " PRESERVE WHITESPACE"); + else + appendStringInfoString(buf, + " STRIP WHITESPACE"); + break; + case IS_XMLROOT: + Assert(list_length(xexpr->args) == 3); + + get_rule_expr((Node *) linitial(xexpr->args), + context, true); + + appendStringInfoString(buf, ", VERSION "); + con = (Const *) lsecond(xexpr->args); + if (IsA(con, Const) && + con->constisnull) + appendStringInfoString(buf, "NO VALUE"); + else + get_rule_expr((Node *) con, context, false); + + con = (Const *) lthird(xexpr->args); + Assert(IsA(con, Const)); + if (con->constisnull) + /* suppress STANDALONE NO VALUE */ ; + else if (DatumGetBool(con->constvalue)) + appendStringInfoString(buf, + ", STANDALONE YES"); + else + appendStringInfoString(buf, + ", STANDALONE NO"); + break; + } + + } + appendStringInfoChar(buf, ')'); + } + break; + case T_NullIfExpr: { NullIfExpr *nullifexpr = (NullIfExpr *) node; @@ -3849,28 +3983,6 @@ get_rule_expr(Node *node, deparse_context *context, } break; - case T_XmlExpr: - { - XmlExpr *xexpr = (XmlExpr *) node; - - switch (xexpr->op) - { - case IS_XMLCONCAT: - appendStringInfo(buf, "XMLCONCAT("); - break; - case IS_XMLELEMENT: - appendStringInfo(buf, "XMLELEMENT("); - break; - case IS_XMLFOREST: - appendStringInfo(buf, "XMLFOREST("); - break; - } - get_rule_expr((Node *) xexpr->named_args, context, true); - get_rule_expr((Node *) xexpr->args, context, true); - appendStringInfoChar(buf, ')'); - } - break; - case T_CoerceToDomain: { CoerceToDomain *ctest = (CoerceToDomain *) node; diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index d765c8657fb..a6dc1a6e5f0 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.2 2006/12/23 04:56:50 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.3 2006/12/24 00:29:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -41,15 +41,6 @@ #ifdef USE_LIBXML -/* - * A couple of useful macros (similar to ones from libxml/parse.c) - */ -#define CMP4( s, c1, c2, c3, c4 ) \ - ( ((unsigned char *) s)[ 0 ] == c1 && ((unsigned char *) s)[ 1 ] == c2 && \ - ((unsigned char *) s)[ 2 ] == c3 && ((unsigned char *) s)[ 3 ] == c4 ) -#define CMP5( s, c1, c2, c3, c4, c5 ) \ - ( CMP4( s, c1, c2, c3, c4 ) && ((unsigned char *) s)[ 4 ] == c5 ) - #define PG_XML_DEFAULT_URI "dummy.xml" #define XML_ERRBUF_SIZE 200 @@ -177,31 +168,18 @@ xmlcomment(PG_FUNCTION_ARGS) Datum -xmlparse(PG_FUNCTION_ARGS) +texttoxml(PG_FUNCTION_ARGS) +{ + text *data = PG_GETARG_TEXT_P(0); + + PG_RETURN_XML_P(xmlparse(data, false, true)); +} + + +xmltype * +xmlparse(text *data, bool is_document, bool preserve_whitespace) { #ifdef USE_LIBXML - text *data; - bool is_document; - bool preserve_whitespace; - - data = PG_GETARG_TEXT_P(0); - - if (PG_NARGS() >= 2) - is_document = PG_GETARG_BOOL(1); - else - is_document = false; - - if (PG_NARGS() >= 3) - preserve_whitespace = PG_GETARG_BOOL(2); - else - /* - * Since the XMLPARSE grammar makes STRIP WHITESPACE the - * default, this argument should really default to false. But - * until we have actually implemented whitespace stripping, - * this would be annoying. - */ - preserve_whitespace = true; - if (!preserve_whitespace) ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -213,111 +191,93 @@ xmlparse(PG_FUNCTION_ARGS) * valies defined by internal DTD are applied'. As for external * DTDs, we try to support them too, (see SQL/XML:10.16.7.e) */ - xml_parse(data, XML_PARSE_DTDATTR, is_document); /* assume that ERROR occurred if parsing failed */ + xml_parse(data, XML_PARSE_DTDATTR, is_document); - PG_RETURN_XML_P(data); + return (xmltype *) data; #else NO_XML_SUPPORT(); - return 0; + return NULL; #endif } -Datum -xmlpi(PG_FUNCTION_ARGS) +xmltype * +xmlpi(char *target, text *arg) { #ifdef USE_LIBXML - char *target = NameStr(*PG_GETARG_NAME(0)); + xmltype *result; StringInfoData buf; - if (strlen(target) >= 3 - && (target[0] == 'x' || target[0] == 'X') - && (target[1] == 'm' || target[1] == 'M') - && (target[2] == 'l' || target[2] == 'L')) - { + if (pg_strncasecmp(target, "xml", 3) == 0) ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), + (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION), errmsg("invalid XML processing instruction"), errdetail("XML processing instruction target name cannot start with \"xml\"."))); - } initStringInfo(&buf); - appendStringInfo(&buf, "<?"); - appendStringInfoString(&buf, map_sql_identifier_to_xml_name(target, false)); - if (PG_NARGS() > 1) + appendStringInfo(&buf, "<?%s", target); + + if (arg != NULL) { - text *arg = PG_GETARG_TEXT_P(1); char *string; - string = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(arg))); - if (strstr(string, "?>")) + string = DatumGetCString(DirectFunctionCall1(textout, + PointerGetDatum(arg))); + if (strstr(string, "?>") != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION), errmsg("invalid XML processing instruction"), errdetail("XML processing instruction cannot contain \"?>\"."))); - appendStringInfoString(&buf, " "); + appendStringInfoChar(&buf, ' '); appendStringInfoString(&buf, string); + pfree(string); } appendStringInfoString(&buf, "?>"); - PG_RETURN_XML_P(stringinfo_to_xmltype(&buf)); + result = stringinfo_to_xmltype(&buf); + pfree(buf.data); + return result; #else NO_XML_SUPPORT(); - return 0; + return NULL; #endif } -Datum -xmlroot(PG_FUNCTION_ARGS) +xmltype * +xmlroot(xmltype *data, text *version, int standalone) { #ifdef USE_LIBXML - xmltype *data; - text *version; - int standalone; + xmltype *result; StringInfoData buf; - if (PG_ARGISNULL(0)) - PG_RETURN_NULL(); - else - data = PG_GETARG_XML_P(0); - - if (PG_ARGISNULL(1)) - version = NULL; - else - version = PG_GETARG_TEXT_P(1); - - if (PG_ARGISNULL(2)) - standalone = 0; - else - { - bool tmp = PG_GETARG_BOOL(2); - standalone = (tmp ? 1 : -1); - } + initStringInfo(&buf); /* * FIXME: This is probably supposed to be cleverer if there * already is an XML preamble. */ - initStringInfo(&buf); - appendStringInfo(&buf,"<?xml"); - if (version) { + if (version) + { appendStringInfo(&buf, " version=\""); appendStringInfoText(&buf, version); appendStringInfo(&buf, "\""); } if (standalone) - appendStringInfo(&buf, " standalone=\"%s\"", (standalone == 1 ? "yes" : "no")); + appendStringInfo(&buf, " standalone=\"%s\"", + (standalone == 1 ? "yes" : "no")); appendStringInfo(&buf, "?>"); appendStringInfoText(&buf, (text *) data); - PG_RETURN_XML_P(stringinfo_to_xmltype(&buf)); + result = stringinfo_to_xmltype(&buf); + pfree(buf.data); + return result; #else NO_XML_SUPPORT(); - return 0; + return NULL; #endif } @@ -456,7 +416,7 @@ xml_parse(text *data, int opts, bool is_document) /* first, we try to parse the string as XML doc, then, as XML chunk */ ereport(DEBUG3, (errmsg("string to parse: %s", string))); - if (len > 4 && CMP5(string, '<', '?', 'x', 'm', 'l')) + if (len >= 5 && strncmp((char *) string, "<?xml", 5) == 0) { /* consider it as DOCUMENT */ doc = xmlCtxtReadMemory(ctxt, (char *) string, len, @@ -918,10 +878,8 @@ map_sql_identifier_to_xml_name(char *ident, bool fully_escaped) appendStringInfo(&buf, "_x003A_"); else if (*p == '_' && *(p+1) == 'x') appendStringInfo(&buf, "_x005F_"); - else if (fully_escaped && p == ident - && ( *p == 'x' || *p == 'X') - && ( *(p+1) == 'm' || *(p+1) == 'M') - && ( *(p+2) == 'l' || *(p+2) == 'L')) + else if (fully_escaped && p == ident && + pg_strncasecmp(p, "xml", 3) == 0) { if (*p == 'x') appendStringInfo(&buf, "_x0078_"); @@ -932,9 +890,10 @@ map_sql_identifier_to_xml_name(char *ident, bool fully_escaped) { pg_wchar u = sqlchar_to_unicode(p); - if (!is_valid_xml_namechar(u) - || (p == ident && !is_valid_xml_namefirst(u))) - appendStringInfo(&buf, "_x%04X_", (unsigned int) u); + if ((p == ident) + ? !is_valid_xml_namefirst(u) + : !is_valid_xml_namechar(u)) + appendStringInfo(&buf, "_x%04X_", (unsigned int) u); else appendBinaryStringInfo(&buf, p, pg_mblen(p)); } diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index f0a840a3dde..68f5a639347 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.365 2006/12/23 00:43:12 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.366 2006/12/24 00:29:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200612221 +#define CATALOG_VERSION_NO 200612231 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 974a2db360c..0a3ccc07f6c 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.431 2006/12/21 16:05:15 petere Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.432 2006/12/24 00:29:19 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -3983,17 +3983,9 @@ DATA(insert OID = 2894 ( xml_out PGNSP PGUID 12 f f t f i 1 2275 "142" _nul DESCR("I/O"); DATA(insert OID = 2895 ( xmlcomment PGNSP PGUID 12 f f t f i 1 142 "25" _null_ _null_ _null_ xmlcomment - _null_ )); DESCR("generate an XML comment"); -DATA(insert OID = 2896 ( xmlparse PGNSP PGUID 12 f f t f i 1 142 "25" _null_ _null_ _null_ xmlparse - _null_ )); +DATA(insert OID = 2896 ( xml PGNSP PGUID 12 f f t f i 1 142 "25" _null_ _null_ _null_ texttoxml - _null_ )); DESCR("perform a non-validating parse of a character string to produce an XML value"); -DATA(insert OID = 2897 ( xmlparse PGNSP PGUID 12 f f t f i 3 142 "25 16 16" _null_ _null_ _null_ xmlparse - _null_ )); -DESCR("perform a non-validating parse of a character string to produce an XML value"); -DATA(insert OID = 2898 ( xmlpi PGNSP PGUID 12 f f t f i 1 142 "19" _null_ _null_ _null_ xmlpi - _null_ )); -DESCR("generate an XML processing instruction"); -DATA(insert OID = 2899 ( xmlpi PGNSP PGUID 12 f f t f i 2 142 "19 25" _null_ _null_ _null_ xmlpi - _null_ )); -DESCR("generate an XML processing instruction"); -DATA(insert OID = 2900 ( xmlroot PGNSP PGUID 12 f f f f i 3 142 "142 25 16" _null_ _null_ _null_ xmlroot - _null_ )); -DESCR("create an XML value by modifying the properties of the XML root information item of another XML value"); -DATA(insert OID = 2901 ( xmlvalidate PGNSP PGUID 12 f f t f i 2 16 "142 25" _null_ _null_ _null_ xmlvalidate - _null_ )); +DATA(insert OID = 2897 ( xmlvalidate PGNSP PGUID 12 f f t f i 2 16 "142 25" _null_ _null_ _null_ xmlvalidate - _null_ )); DESCR("validate an XML value"); /* diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 1db920aa0c1..f8e8f15a5db 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.163 2006/12/21 16:05:16 petere Exp $ + * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.164 2006/12/24 00:29:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -710,6 +710,18 @@ typedef struct MinMaxExprState FmgrInfo cfunc; /* lookup info for comparison func */ } MinMaxExprState; +/* ---------------- + * XmlExprState node + * ---------------- + */ +typedef struct XmlExprState +{ + ExprState xprstate; + List *named_args; /* ExprStates for named arguments */ + FmgrInfo *named_outfuncs; /* array of output fns for named arguments */ + List *args; /* ExprStates for other arguments */ +} XmlExprState; + /* ---------------- * NullTestState node * ---------------- @@ -723,22 +735,6 @@ typedef struct NullTestState TupleDesc argdesc; /* tupdesc for most recent input */ } NullTestState; -/* ---------------- - * XmlExprState node - * ---------------- - */ -typedef struct XmlExprState -{ - ExprState xprstate; - XmlExprOp op; - char *name; - List *named_args; - List *args; - Oid *named_args_tcache; - char **named_args_ncache; - Oid arg_typeout; -} XmlExprState; - /* ---------------- * CoerceToDomainState node * ---------------- diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index eb6ba18adab..fa9dff9d961 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.189 2006/12/21 16:05:16 petere Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.190 2006/12/24 00:29:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -130,6 +130,7 @@ typedef enum NodeTag T_RowCompareExpr, T_CoalesceExpr, T_MinMaxExpr, + T_XmlExpr, T_NullIfExpr, T_NullTest, T_BooleanTest, @@ -140,7 +141,6 @@ typedef enum NodeTag T_RangeTblRef, T_JoinExpr, T_FromExpr, - T_XmlExpr, /* * TAGS FOR EXPRESSION STATE NODES (execnodes.h) @@ -166,10 +166,10 @@ typedef enum NodeTag T_RowCompareExprState, T_CoalesceExprState, T_MinMaxExprState, + T_XmlExprState, T_NullTestState, T_CoerceToDomainState, T_DomainConstraintState, - T_XmlExprState, /* * TAGS FOR PLANNER NODES (relation.h) diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 5946300c975..bd2c39040fc 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.120 2006/12/23 00:43:13 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.121 2006/12/24 00:29:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -711,6 +711,33 @@ typedef struct MinMaxExpr List *args; /* the arguments */ } MinMaxExpr; +/* + * XmlExpr - various SQL/XML functions requiring special grammar productions + * + * 'name' carries the "NAME foo" argument (already XML-escaped). + * 'named_args' and 'arg_names' represent an xml_attribute list. + * 'args' carries all other arguments. + */ +typedef enum XmlExprOp +{ + IS_XMLCONCAT, /* XMLCONCAT(args) */ + IS_XMLELEMENT, /* XMLELEMENT(name, xml_attributes, args) */ + IS_XMLFOREST, /* XMLFOREST(xml_attributes) */ + IS_XMLPARSE, /* XMLPARSE(text, is_doc, preserve_ws) */ + IS_XMLPI, /* XMLPI(name [, args]) */ + IS_XMLROOT /* XMLROOT(xml, version, standalone) */ +} XmlExprOp; + +typedef struct XmlExpr +{ + Expr xpr; + XmlExprOp op; /* xml function ID */ + char *name; /* name in xml(NAME foo ...) syntaxes */ + List *named_args; /* non-XML expressions for xml_attributes */ + List *arg_names; /* parallel list of Value strings */ + List *args; /* list of expressions */ +} XmlExpr; + /* * NullIfExpr - a NULLIF expression * @@ -765,26 +792,6 @@ typedef struct BooleanTest BoolTestType booltesttype; /* test type */ } BooleanTest; -/* - * XmlExpr - holder for SQL/XML functions XMLCONCAT, - * XMLELEMENT, XMLFOREST - */ -typedef enum XmlExprOp -{ - IS_XMLCONCAT, - IS_XMLELEMENT, - IS_XMLFOREST, -} XmlExprOp; - -typedef struct XmlExpr -{ - Expr xpr; - XmlExprOp op; /* xml expression type */ - char *name; /* element name */ - List *named_args; - List *args; -} XmlExpr; - /* * CoerceToDomain * diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index 9c077095e9e..08573abd1ef 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.67 2006/12/21 16:05:16 petere Exp $ + * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.68 2006/12/24 00:29:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -55,12 +55,9 @@ extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, extern Node *coerce_to_boolean(ParseState *pstate, Node *node, const char *constructName); -extern Node *coerce_to_integer(ParseState *pstate, Node *node, - const char *constructName); -extern Node *coerce_to_bigint(ParseState *pstate, Node *node, - const char *constructName); -extern Node *coerce_to_xml(ParseState *pstate, Node *node, - const char *constructName); +extern Node *coerce_to_specific_type(ParseState *pstate, Node *node, + Oid targetTypeId, + const char *constructName); extern Oid select_common_type(List *typeids, const char *context); extern Node *coerce_to_common_type(ParseState *pstate, Node *node, diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h index a353d8837c9..713bdaeb115 100644 --- a/src/include/utils/xml.h +++ b/src/include/utils/xml.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.2 2006/12/23 04:56:50 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.3 2006/12/24 00:29:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,11 +27,13 @@ typedef struct varlena xmltype; extern Datum xml_in(PG_FUNCTION_ARGS); extern Datum xml_out(PG_FUNCTION_ARGS); extern Datum xmlcomment(PG_FUNCTION_ARGS); -extern Datum xmlparse(PG_FUNCTION_ARGS); -extern Datum xmlpi(PG_FUNCTION_ARGS); -extern Datum xmlroot(PG_FUNCTION_ARGS); +extern Datum texttoxml(PG_FUNCTION_ARGS); extern Datum xmlvalidate(PG_FUNCTION_ARGS); +extern xmltype *xmlparse(text *data, bool is_doc, bool preserve_whitespace); +extern xmltype *xmlpi(char *target, text *arg); +extern xmltype *xmlroot(xmltype *data, text *version, int standalone); + extern char *map_sql_identifier_to_xml_name(char *ident, bool fully_escaped); #endif /* XML_H */ diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index c28c2c6c8f3..bff81520616 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.180 2006/10/04 00:30:13 momjian Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.181 2006/12/24 00:29:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -4493,6 +4493,18 @@ exec_simple_check_node(Node *node) return TRUE; } + case T_XmlExpr: + { + XmlExpr *expr = (XmlExpr *) node; + + if (!exec_simple_check_node((Node *) expr->named_args)) + return FALSE; + if (!exec_simple_check_node((Node *) expr->args)) + return FALSE; + + return TRUE; + } + case T_NullIfExpr: { NullIfExpr *expr = (NullIfExpr *) node; diff --git a/src/pl/plpgsql/src/plerrcodes.h b/src/pl/plpgsql/src/plerrcodes.h index 478bf3b0806..ba0c229ccd1 100644 --- a/src/pl/plpgsql/src/plerrcodes.h +++ b/src/pl/plpgsql/src/plerrcodes.h @@ -9,7 +9,7 @@ * * Copyright (c) 2003-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/pl/plpgsql/src/plerrcodes.h,v 1.9 2006/06/16 23:29:27 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/plerrcodes.h,v 1.10 2006/12/24 00:29:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -243,6 +243,22 @@ "untranslatable_character", ERRCODE_UNTRANSLATABLE_CHARACTER }, +{ + "invalid_xml_document", ERRCODE_INVALID_XML_DOCUMENT +}, + +{ + "invalid_xml_content", ERRCODE_INVALID_XML_CONTENT +}, + +{ + "invalid_xml_comment", ERRCODE_INVALID_XML_COMMENT +}, + +{ + "invalid_xml_processing_instruction", ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION +}, + { "integrity_constraint_violation", ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION }, diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index a0e9483ebd0..7bfa35a46f8 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -74,7 +74,7 @@ WHERE p1.oid != p2.oid AND SELECT p1.oid, p1.proname, p2.oid, p2.proname FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid < p2.oid AND - p1.prosrc = p2.prosrc AND p1.prosrc NOT IN ('xmlparse', 'xmlpi') AND + p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND (p1.proisagg = false OR p2.proisagg = false) AND (p1.prolang != p2.prolang OR diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 4dd39eb164e..b8c184f784b 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -77,7 +77,7 @@ WHERE p1.oid != p2.oid AND SELECT p1.oid, p1.proname, p2.oid, p2.proname FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid < p2.oid AND - p1.prosrc = p2.prosrc AND p1.prosrc NOT IN ('xmlparse', 'xmlpi') AND + p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND (p1.proisagg = false OR p2.proisagg = false) AND (p1.prolang != p2.prolang OR