xmlconcat as variadic function

Поиск
Список
Период
Сортировка
От Peter Eisentraut
Тема xmlconcat as variadic function
Дата
Msg-id 492153FB.8000502@gmx.net
обсуждение исходный текст
Ответы Re: xmlconcat as variadic function  (Heikki Linnakangas <heikki.linnakangas@enterprisedb.com>)
Re: xmlconcat as variadic function  ("Merlin Moncure" <mmoncure@gmail.com>)
Re: xmlconcat as variadic function  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-hackers
Here is a patch to reimplement the xmlconcat functionality as a variadic
function instead of a hardcoded special expression type.  I haven't
found any variadic function in the set of built-in functions so far, so
I figured I would ask for feedback before we go down this route.

Btw., the corresponding ecpg changes appear to have fallen into place
automatically.  Nice.
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', '<syntax');
  ERROR:  invalid XML content
  LINE 1: SELECT xmlconcat('bad', '<syntax');
--- 55,64 ----
  (1 row)

  SELECT xmlconcat(1, 2);
! ERROR:  function xmlconcat(integer, integer) does not exist
  LINE 1: SELECT xmlconcat(1, 2);
!                ^
! HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
  SELECT xmlconcat('bad', '<syntax');
  ERROR:  invalid XML content
  LINE 1: SELECT xmlconcat('bad', '<syntax');
*************** SELECT table_name, view_definition FROM
*** 435,441 ****
   table_name |                                                      view_definition
                   

------------+----------------------------------------------------------------------------------------------------------------------------
   xmlview1   | SELECT xmlcomment('test'::text) AS xmlcomment;
!  xmlview2   | SELECT XMLCONCAT('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 '<abc>x</abc>'::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 '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse";

В списке pgsql-hackers по дате отправления:

Предыдущее
От: ITAGAKI Takahiro
Дата:
Сообщение: Re: auto_explain contrib moudle
Следующее
От: Heikki Linnakangas
Дата:
Сообщение: Re: xmlconcat as variadic function