Index: src/backend/executor/execQual.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/executor/execQual.c,v retrieving revision 1.237 diff -u -3 -p -c -r1.237 execQual.c *** src/backend/executor/execQual.c 15 Nov 2008 20:52:35 -0000 1.237 --- src/backend/executor/execQual.c 17 Nov 2008 11:17:09 -0000 *************** ExecEvalXml(XmlExprState *xmlExpr, ExprC *** 3174,3202 **** switch (xexpr->op) { - case IS_XMLCONCAT: - { - List *values = NIL; - - 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) - { - *isNull = false; - return PointerGetDatum(xmlconcat(values)); - } - else - return (Datum) 0; - } - break; - case IS_XMLFOREST: { StringInfoData buf; --- 3174,3179 ---- Index: src/backend/parser/gram.y =================================================================== RCS file: /cvsroot/pgsql/src/backend/parser/gram.y,v retrieving revision 2.637 diff -u -3 -p -c -r2.637 gram.y *** src/backend/parser/gram.y 13 Nov 2008 11:10:06 -0000 2.637 --- src/backend/parser/gram.y 17 Nov 2008 11:17:09 -0000 *************** static TypeName *TableFuncTypeName(List *** 474,480 **** WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE ! XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLFOREST XMLPARSE XMLPI XMLROOT XMLSERIALIZE YEAR_P YES_P --- 474,480 ---- WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE ! XML_P XMLATTRIBUTES XMLELEMENT XMLFOREST XMLPARSE XMLPI XMLROOT XMLSERIALIZE YEAR_P YES_P *************** func_expr: func_name '(' ')' *** 8620,8629 **** v->location = @1; $$ = (Node *)v; } - | XMLCONCAT '(' expr_list ')' - { - $$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3, @1); - } | XMLELEMENT '(' NAME_P ColLabel ')' { $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, @1); --- 8620,8625 ---- *************** col_name_keyword: *** 9672,9678 **** | VALUES | VARCHAR | XMLATTRIBUTES - | XMLCONCAT | XMLELEMENT | XMLFOREST | XMLPARSE --- 9668,9673 ---- Index: src/backend/parser/keywords.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/parser/keywords.c,v retrieving revision 1.205 diff -u -3 -p -c -r1.205 keywords.c *** src/backend/parser/keywords.c 27 Oct 2008 09:37:47 -0000 1.205 --- src/backend/parser/keywords.c 17 Nov 2008 11:17:09 -0000 *************** const ScanKeyword ScanKeywords[] = { *** 414,420 **** {"write", WRITE, UNRESERVED_KEYWORD}, {"xml", XML_P, UNRESERVED_KEYWORD}, {"xmlattributes", XMLATTRIBUTES, COL_NAME_KEYWORD}, - {"xmlconcat", XMLCONCAT, COL_NAME_KEYWORD}, {"xmlelement", XMLELEMENT, COL_NAME_KEYWORD}, {"xmlforest", XMLFOREST, COL_NAME_KEYWORD}, {"xmlparse", XMLPARSE, COL_NAME_KEYWORD}, --- 414,419 ---- Index: src/backend/parser/parse_expr.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v retrieving revision 1.237 diff -u -3 -p -c -r1.237 parse_expr.c *** src/backend/parser/parse_expr.c 26 Oct 2008 02:46:25 -0000 1.237 --- src/backend/parser/parse_expr.c 17 Nov 2008 11:17:09 -0000 *************** transformXmlExpr(ParseState *pstate, Xml *** 1706,1715 **** newe = transformExpr(pstate, e); switch (x->op) { - case IS_XMLCONCAT: - newe = coerce_to_specific_type(pstate, newe, XMLOID, - "XMLCONCAT"); - break; case IS_XMLELEMENT: /* no coercion necessary */ break; --- 1706,1711 ---- Index: src/backend/parser/parse_target.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/parser/parse_target.c,v retrieving revision 1.168 diff -u -3 -p -c -r1.168 parse_target.c *** src/backend/parser/parse_target.c 7 Oct 2008 01:47:55 -0000 1.168 --- src/backend/parser/parse_target.c 17 Nov 2008 11:17:09 -0000 *************** FigureColnameInternal(Node *node, char * *** 1399,1407 **** /* make SQL/XML functions act like a regular function */ switch (((XmlExpr *) node)->op) { - case IS_XMLCONCAT: - *name = "xmlconcat"; - return 2; case IS_XMLELEMENT: *name = "xmlelement"; return 2; --- 1399,1404 ---- Index: src/backend/utils/adt/ruleutils.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v retrieving revision 1.287 diff -u -3 -p -c -r1.287 ruleutils.c *** src/backend/utils/adt/ruleutils.c 6 Oct 2008 20:29:38 -0000 1.287 --- src/backend/utils/adt/ruleutils.c 17 Nov 2008 11:17:09 -0000 *************** get_rule_expr(Node *node, deparse_contex *** 4519,4527 **** switch (xexpr->op) { - case IS_XMLCONCAT: - appendStringInfoString(buf, "XMLCONCAT("); - break; case IS_XMLELEMENT: appendStringInfoString(buf, "XMLELEMENT("); break; --- 4519,4524 ---- *************** get_rule_expr(Node *node, deparse_contex *** 4586,4592 **** appendStringInfoString(buf, ", "); switch (xexpr->op) { - case IS_XMLCONCAT: case IS_XMLELEMENT: case IS_XMLFOREST: case IS_XMLPI: --- 4583,4588 ---- Index: src/backend/utils/adt/xml.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/utils/adt/xml.c,v retrieving revision 1.81 diff -u -3 -p -c -r1.81 xml.c *** src/backend/utils/adt/xml.c 10 Nov 2008 18:02:20 -0000 1.81 --- src/backend/utils/adt/xml.c 17 Nov 2008 11:17:10 -0000 *************** xmlcomment(PG_FUNCTION_ARGS) *** 422,448 **** * 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 int global_standalone = 1; xmlChar *global_version = NULL; bool global_version_no_value = false; StringInfoData buf; ! ListCell *v; initStringInfo(&buf); ! foreach(v, args) { - xmltype *x = DatumGetXmlP(PointerGetDatum(lfirst(v))); size_t len; xmlChar *version; int standalone; char *str; ! len = VARSIZE(x) - VARHDRSZ; ! str = text_to_cstring((text *) x); parse_xml_decl((xmlChar *) str, &len, &version, NULL, &standalone); --- 422,449 ---- * TODO: xmlconcat needs to merge the notations and unparsed entities * of the argument values. Not very important in practice, though. */ ! static xmltype * ! xmlconcat_internal(int num, xmltype *args[]) { #ifdef USE_LIBXML int global_standalone = 1; xmlChar *global_version = NULL; bool global_version_no_value = false; StringInfoData buf; ! int i; ! ! Assert(num > 0); initStringInfo(&buf); ! for (i = 0; i < num; i++) { size_t len; xmlChar *version; int standalone; char *str; ! len = VARSIZE(args[i]) - VARHDRSZ; ! str = text_to_cstring((text *) args[i]); parse_xml_decl((xmlChar *) str, &len, &version, NULL, &standalone); *************** xmlconcat(List *args) *** 485,490 **** --- 486,524 ---- } + Datum + xmlconcatv(PG_FUNCTION_ARGS) + { + ArrayType *arg = PG_GETARG_ARRAYTYPE_P(0); + xmltype **newargs; + int num; + int i; + + Assert(ARR_NDIM(arg) == 1); + Assert(ARR_ELEMTYPE(arg) == XMLOID); + + newargs = palloc(sizeof(*newargs) * ARR_DIMS(arg)[0]); + + num = 0; + for (i = ARR_LBOUND(arg)[0]; i < ARR_LBOUND(arg)[0] + ARR_DIMS(arg)[0]; i++) + { + Datum el; + bool isnull; + + el = array_ref(arg, 1, &i, -1, -1, false, 'i', &isnull); + if (isnull) + continue; + else + newargs[num++] = DatumGetXmlP(el); + } + + if (num > 0) + PG_RETURN_XML_P(xmlconcat_internal(num, newargs)); + else + PG_RETURN_NULL(); + } + + /* * XMLAGG support */ *************** xmlconcat2(PG_FUNCTION_ARGS) *** 501,508 **** 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)))); } --- 535,547 ---- else if (PG_ARGISNULL(1)) PG_RETURN_XML_P(PG_GETARG_XML_P(0)); else ! { ! xmltype *newargs[2]; ! ! newargs[0] = PG_GETARG_XML_P(0); ! newargs[1] = PG_GETARG_XML_P(1); ! PG_RETURN_XML_P(xmlconcat_internal(2, newargs)); ! } } Index: src/include/catalog/pg_proc.h =================================================================== RCS file: /cvsroot/pgsql/src/include/catalog/pg_proc.h,v retrieving revision 1.528 diff -u -3 -p -c -r1.528 pg_proc.h *** src/include/catalog/pg_proc.h 14 Nov 2008 00:51:46 -0000 1.528 --- src/include/catalog/pg_proc.h 17 Nov 2008 11:17:10 -0000 *************** DATA(insert OID = 2898 ( xml_recv P *** 4193,4198 **** --- 4193,4200 ---- DESCR("I/O"); DATA(insert OID = 2899 ( xml_send PGNSP PGUID 12 1 0 0 f f t f s 1 17 "142" _null_ _null_ _null_ xml_send _null_ _null_ _null_ )); DESCR("I/O"); + DATA(insert OID = 2328 ( xmlconcat PGNSP PGUID 12 1 0 142 f f f f i 1 142 "143" "{143}" "{v}" _null_ xmlconcatv _null_ _null_ _null_ )); + DESCR("concatenate a list of XML values"); DATA(insert OID = 2900 ( xmlconcat2 PGNSP PGUID 12 1 0 0 f f f f i 2 142 "142 142" _null_ _null_ _null_ xmlconcat2 _null_ _null_ _null_ )); DESCR("aggregate transition function"); DATA(insert OID = 2901 ( xmlagg PGNSP PGUID 12 1 0 0 t f f f i 1 142 "142" _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ )); Index: src/include/nodes/primnodes.h =================================================================== RCS file: /cvsroot/pgsql/src/include/nodes/primnodes.h,v retrieving revision 1.143 diff -u -3 -p -c -r1.143 primnodes.h *** src/include/nodes/primnodes.h 6 Oct 2008 17:39:26 -0000 1.143 --- src/include/nodes/primnodes.h 17 Nov 2008 11:17:10 -0000 *************** typedef struct MinMaxExpr *** 837,843 **** */ 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) */ --- 837,842 ---- Index: src/include/utils/xml.h =================================================================== RCS file: /cvsroot/pgsql/src/include/utils/xml.h,v retrieving revision 1.24 diff -u -3 -p -c -r1.24 xml.h *** src/include/utils/xml.h 4 Apr 2008 08:33:15 -0000 1.24 --- src/include/utils/xml.h 17 Nov 2008 11:17:10 -0000 *************** extern Datum xml_out(PG_FUNCTION_ARGS); *** 32,37 **** --- 32,38 ---- extern Datum xml_recv(PG_FUNCTION_ARGS); extern Datum xml_send(PG_FUNCTION_ARGS); extern Datum xmlcomment(PG_FUNCTION_ARGS); + extern Datum xmlconcatv(PG_FUNCTION_ARGS); extern Datum xmlconcat2(PG_FUNCTION_ARGS); extern Datum texttoxml(PG_FUNCTION_ARGS); extern Datum xmltotext(PG_FUNCTION_ARGS); *************** typedef enum *** 63,69 **** XML_STANDALONE_OMITTED } XmlStandaloneType; - extern xmltype *xmlconcat(List *args); extern xmltype *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext); extern xmltype *xmlparse(text *data, XmlOptionType xmloption, bool preserve_whitespace); extern xmltype *xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null); --- 64,69 ---- Index: src/test/regress/expected/xml.out =================================================================== RCS file: /cvsroot/pgsql/src/test/regress/expected/xml.out,v retrieving revision 1.23 diff -u -3 -p -c -r1.23 xml.out *** src/test/regress/expected/xml.out 15 Nov 2008 20:52:35 -0000 1.23 --- src/test/regress/expected/xml.out 17 Nov 2008 11:17:10 -0000 *************** SELECT xmlconcat('hello', 'you'); *** 55,63 **** (1 row) SELECT xmlconcat(1, 2); ! ERROR: argument of XMLCONCAT must be type xml, not type integer LINE 1: SELECT xmlconcat(1, 2); ! ^ SELECT xmlconcat('bad', 'x'::text STRIP WHITESPACE) AS "xmlparse"; --- 436,442 ---- table_name | view_definition ------------+---------------------------------------------------------------------------------------------------------------------------- xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; ! xmlview2 | SELECT xmlconcat(VARIADIC ARRAY['hello'::xml, 'you'::xml]) AS xmlconcat; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement" FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT 'x'::text STRIP WHITESPACE) AS "xmlparse";