From b4c8d49036b971d892b2dfe1c71d3f0cee116e7f Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Sat, 20 Jan 2007 09:27:20 +0000 Subject: [PATCH] Fix xmlconcat by properly merging the XML declarations. Add aggregate function xmlagg. --- src/backend/executor/execQual.c | 25 ++++---- src/backend/utils/adt/xml.c | 98 ++++++++++++++++++++++++++++- src/include/catalog/catversion.h | 4 +- src/include/catalog/pg_aggregate.h | 5 +- src/include/catalog/pg_proc.h | 6 +- src/include/utils/xml.h | 4 +- src/test/regress/expected/xml.out | 26 +++++++- src/test/regress/expected/xml_1.out | 16 +++++ src/test/regress/sql/xml.sql | 6 ++ 9 files changed, 171 insertions(+), 19 deletions(-) diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index ca804dea21..60f9d35f1f 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.207 2007/01/14 13:11:53 petere Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.208 2007/01/20 09:27:19 petere Exp $ * *------------------------------------------------------------------------- */ @@ -2651,7 +2651,6 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, StringInfoData buf; Datum value; bool isnull; - char *str; ListCell *arg; ListCell *narg; int i; @@ -2663,20 +2662,22 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, switch (xexpr->op) { case IS_XMLCONCAT: - initStringInfo(&buf); - foreach(arg, xmlExpr->args) { - ExprState *e = (ExprState *) lfirst(arg); + List *values = NIL; - value = ExecEvalExpr(e, econtext, &isnull, NULL); - if (!isnull) + foreach(arg, xmlExpr->args) + { + ExprState *e = (ExprState *) lfirst(arg); + + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (!isnull) + values = lappend(values, DatumGetPointer(value)); + } + + if (list_length(values) > 0) { - /* we know the value is XML type */ - str = DatumGetCString(DirectFunctionCall1(xml_out, - value)); - appendStringInfoString(&buf, str); - pfree(str); *isNull = false; + return PointerGetDatum(xmlconcat(values)); } } break; diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 5f3fafe1e7..fdf7f8e0a9 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.19 2007/01/19 16:58:46 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.20 2007/01/20 09:27:19 petere Exp $ * *------------------------------------------------------------------------- */ @@ -359,6 +359,102 @@ xmlcomment(PG_FUNCTION_ARGS) } + +/* + * TODO: xmlconcat needs to merge the notations and unparsed entities + * of the argument values. Not very important in practice, though. + */ +xmltype * +xmlconcat(List *args) +{ +#ifdef USE_LIBXML + StringInfoData buf; + ListCell *v; + + int global_standalone = 1; + xmlChar *global_version = NULL; + bool global_version_no_value = false; + + initStringInfo(&buf); + foreach(v, args) + { + size_t len; + xmlChar *version; + int standalone; + xmltype *x = DatumGetXmlP(PointerGetDatum(lfirst(v))); + char *str; + + len = VARSIZE(x) - VARHDRSZ; + str = palloc(len + 1); + memcpy(str, VARDATA(x), len); + str[len] = '\0'; + + parse_xml_decl((xmlChar *) str, &len, &version, NULL, &standalone); + + if (standalone == 0 && global_standalone == 1) + global_standalone = 0; + if (standalone < 0) + global_standalone = -1; + + if (!global_version) + global_version = xmlStrdup(version); + else if (version && xmlStrcmp(version, global_version) != 0) + global_version_no_value = true; + + appendStringInfoString(&buf, str + len); + pfree(str); + } + + if (!global_version_no_value || global_standalone >= 0) + { + StringInfoData buf2; + + initStringInfo(&buf2); + + if (!global_version_no_value && global_version) + appendStringInfo(&buf2, ""); + + appendStringInfoString(&buf2, buf.data); + buf = buf2; + } + + return stringinfo_to_xmltype(&buf); +#else + NO_XML_SUPPORT(); + return NULL; +#endif +} + + +/* + * XMLAGG support + */ +Datum +xmlconcat2(PG_FUNCTION_ARGS) +{ + if (PG_ARGISNULL(0)) + { + if (PG_ARGISNULL(1)) + PG_RETURN_NULL(); + else + PG_RETURN_XML_P(PG_GETARG_XML_P(1)); + } + else if (PG_ARGISNULL(1)) + PG_RETURN_XML_P(PG_GETARG_XML_P(0)); + else + PG_RETURN_XML_P(xmlconcat(list_make2(PG_GETARG_XML_P(0), PG_GETARG_XML_P(1)))); +} + + Datum texttoxml(PG_FUNCTION_ARGS) { diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index c8892d24f2..896f1513fb 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.372 2007/01/16 21:41:13 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.373 2007/01/20 09:27:19 petere Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200701161 +#define CATALOG_VERSION_NO 200701201 #endif diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h index fcad73fdd3..1f2ba57147 100644 --- a/src/include/catalog/pg_aggregate.h +++ b/src/include/catalog/pg_aggregate.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_aggregate.h,v 1.59 2007/01/05 22:19:52 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_aggregate.h,v 1.60 2007/01/20 09:27:19 petere Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -221,6 +221,9 @@ DATA(insert ( 2241 int8or - 0 20 _null_ )); DATA(insert ( 2242 bitand - 0 1560 _null_ )); DATA(insert ( 2243 bitor - 0 1560 _null_ )); +/* xml */ +DATA(insert ( 2901 xmlconcat2 - 0 142 _null_ )); + /* * prototypes for functions in pg_aggregate.c */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 24a287e3d6..f22a0d15b8 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.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/catalog/pg_proc.h,v 1.437 2007/01/16 21:41:13 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.438 2007/01/20 09:27:19 petere Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -4037,6 +4037,10 @@ DATA(insert OID = 2898 ( xml_recv PGNSP PGUID 12 f f t f s 1 142 "2281" _nu DESCR("I/O"); DATA(insert OID = 2899 ( xml_send PGNSP PGUID 12 f f t f s 1 17 "142" _null_ _null_ _null_ xml_send - _null_ )); DESCR("I/O"); +DATA(insert OID = 2900 ( xmlconcat2 PGNSP PGUID 12 f f f f i 2 142 "142 142" _null_ _null_ _null_ xmlconcat2 - _null_ )); +DESCR("aggregate transition function"); +DATA(insert OID = 2901 ( xmlagg PGNSP PGUID 12 t f f f i 1 142 "142" _null_ _null_ _null_ aggregate_dummy - _null_ )); +DESCR("concatenate XML values"); /* diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h index ec39368891..b580fffd82 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.11 2007/01/19 16:58:46 petere Exp $ + * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.12 2007/01/20 09:27:20 petere Exp $ * *------------------------------------------------------------------------- */ @@ -30,9 +30,11 @@ extern Datum xml_out(PG_FUNCTION_ARGS); extern Datum xml_recv(PG_FUNCTION_ARGS); extern Datum xml_send(PG_FUNCTION_ARGS); extern Datum xmlcomment(PG_FUNCTION_ARGS); +extern Datum xmlconcat2(PG_FUNCTION_ARGS); extern Datum texttoxml(PG_FUNCTION_ARGS); extern Datum xmlvalidate(PG_FUNCTION_ARGS); +extern xmltype *xmlconcat(List *args); 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); diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index cfac9105d9..df625c90bb 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -55,6 +55,12 @@ ERROR: argument of XMLCONCAT must be type xml, not type integer SELECT xmlconcat('bad', '' +SELECT xmlconcat('', NULL, ''); + xmlconcat +--------------------------------------------------- + +(1 row) + SELECT xmlelement(name element, xmlattributes (1 as one, 'deuce' as two), 'content'); @@ -190,7 +196,7 @@ SELECT xmlpi(name foo, ' bar'); (1 row) SELECT xmlroot(xml '', version no value, standalone no value); - xmlroot + xmlroot --------- @@ -268,6 +274,24 @@ SELECT xml 'abc' IS NOT DOCUMENT; SELECT '<>' IS NOT DOCUMENT; ERROR: invalid XML content DETAIL: Element name not found +SELECT xmlagg(data) FROM xmltest; + xmlagg +-------------------------------------- + onetwo +(1 row) + +SELECT xmlagg(data) FROM xmltest WHERE id > 10; + xmlagg +-------- + +(1 row) + +SELECT xmlelement(name employees, xmlagg(xmlelement(name name, name))) FROM emp; + xmlelement +-------------------------------------------------------------------------------------------------------------------------------- + sharonsambilljeffcimlinda +(1 row) + -- 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 b25df3d24b..dd35f1bf4e 100644 --- a/src/test/regress/expected/xml_1.out +++ b/src/test/regress/expected/xml_1.out @@ -33,6 +33,8 @@ SELECT xmlconcat(1, 2); ERROR: argument of XMLCONCAT must be type xml, not type integer SELECT xmlconcat('bad', '', NULL, ''); +ERROR: no XML support in this installation SELECT xmlelement(name element, xmlattributes (1 as one, 'deuce' as two), 'content'); @@ -123,6 +125,20 @@ SELECT xml 'abc' IS NOT DOCUMENT; ERROR: no XML support in this installation SELECT '<>' IS NOT DOCUMENT; ERROR: no XML support in this installation +SELECT xmlagg(data) FROM xmltest; + xmlagg +-------- + +(1 row) + +SELECT xmlagg(data) FROM xmltest WHERE id > 10; + xmlagg +-------- + +(1 row) + +SELECT xmlelement(name employees, xmlagg(xmlelement(name name, name))) FROM emp; +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 804cd2c2d6..8e32183159 100644 --- a/src/test/regress/sql/xml.sql +++ b/src/test/regress/sql/xml.sql @@ -24,6 +24,7 @@ SELECT xmlconcat(xmlcomment('hello'), SELECT xmlconcat('hello', 'you'); SELECT xmlconcat(1, 2); SELECT xmlconcat('bad', '', NULL, ''); SELECT xmlelement(name element, @@ -97,6 +98,11 @@ SELECT xml 'abc' IS NOT DOCUMENT; SELECT '<>' IS NOT DOCUMENT; +SELECT xmlagg(data) FROM xmltest; +SELECT xmlagg(data) FROM xmltest WHERE id > 10; +SELECT xmlelement(name employees, xmlagg(xmlelement(name name, name))) FROM emp; + + -- Check mapping SQL identifier to XML name SELECT xmlpi(name ":::_xml_abc135.%-&_"); -- 2.40.0