]> granicus.if.org Git - postgresql/commitdiff
Fix xmlconcat by properly merging the XML declarations. Add aggregate
authorPeter Eisentraut <peter_e@gmx.net>
Sat, 20 Jan 2007 09:27:20 +0000 (09:27 +0000)
committerPeter Eisentraut <peter_e@gmx.net>
Sat, 20 Jan 2007 09:27:20 +0000 (09:27 +0000)
function xmlagg.

src/backend/executor/execQual.c
src/backend/utils/adt/xml.c
src/include/catalog/catversion.h
src/include/catalog/pg_aggregate.h
src/include/catalog/pg_proc.h
src/include/utils/xml.h
src/test/regress/expected/xml.out
src/test/regress/expected/xml_1.out
src/test/regress/sql/xml.sql

index ca804dea21056a0e22d8cb37c3c41a75eb8ad6da..60f9d35f1f762b908c72320b7e33a46e6a1cf15b 100644 (file)
@@ -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;
index 5f3fafe1e702c44131a89b71f5db1e5bfeb12d8e..fdf7f8e0a916c5a748ec62d6b87cb8c1d1091d62 100644 (file)
@@ -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, "<?xml version=\"%s\"", global_version);
+               else
+                       appendStringInfo(&buf2, "<?xml version=\"%s\"", PG_XML_DEFAULT_VERSION);
+
+               if (global_standalone == 1)
+                       appendStringInfoString(&buf2, " standalone=\"yes\"");
+               else if (global_standalone == 0)
+                       appendStringInfoString(&buf2, " standalone=\"no\"");
+
+               appendStringInfoString(&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)
 {
index c8892d24f230cb8f214d9cba6936a2ed107e9fe6..896f1513fb811719bbf67e3b7098631163315ffb 100644 (file)
@@ -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
index fcad73fdd357e24cc193346d07e307759905d62f..1f2ba57147a53fdcd4eab9290caab6ddb780d1e9 100644 (file)
@@ -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
  */
index 24a287e3d66818bedcefbfc1426fb0c6ba25f6d0..f22a0d15b893bfb70acf5c256b1b9e7e496c5c25 100644 (file)
@@ -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");
 
 
 /*
index ec39368891a548037e87d26b9c75668e839fa9ba..b580fffd82c04494b8162ae754dff5d57112c154 100644 (file)
@@ -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);
index cfac9105d96caa150968cbbb820145a6b7678294..df625c90bb4421e874c34fea2c0e60a581627b88 100644 (file)
@@ -55,6 +55,12 @@ ERROR:  argument of XMLCONCAT must be type xml, not type integer
 SELECT xmlconcat('bad', '<syntax');
 ERROR:  invalid XML content
 DETAIL:  Expected '>'
+SELECT xmlconcat('<foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>');
+                     xmlconcat                     
+---------------------------------------------------
+ <?xml version="1.1" standalone="no"?><foo/><bar/>
+(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 '<foo/>', version no value, standalone no value);
-        xmlroot        
+ xmlroot 
 ---------
  <foo/>
  
@@ -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                
+--------------------------------------
+ <value>one</value><value>two</value>
+(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                                                           
+--------------------------------------------------------------------------------------------------------------------------------
+ <employees><name>sharon</name><name>sam</name><name>bill</name><name>jeff</name><name>cim</name><name>linda</name></employees>
+(1 row)
+
 -- Check mapping SQL identifier to XML name
 SELECT xmlpi(name ":::_xml_abc135.%-&_");
                       xmlpi                      
index b25df3d24b90830842200286f84daede7343f7f1..dd35f1bf4e71a5405d3181a61e3f1c199be4ea86 100644 (file)
@@ -33,6 +33,8 @@ SELECT xmlconcat(1, 2);
 ERROR:  argument of XMLCONCAT must be type xml, not type integer
 SELECT xmlconcat('bad', '<syntax');
 ERROR:  no XML support in this installation
+SELECT xmlconcat('<foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>');
+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
index 804cd2c2d676e4ed2f4f196bf6f705b8e503730b..8e321831596b76c4369281afd690e0f9e605cbfb 100644 (file)
@@ -24,6 +24,7 @@ SELECT xmlconcat(xmlcomment('hello'),
 SELECT xmlconcat('hello', 'you');
 SELECT xmlconcat(1, 2);
 SELECT xmlconcat('bad', '<syntax');
+SELECT xmlconcat('<foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>');
 
 
 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.%-&_");