LCOV - differential code coverage report
Current view: top level - contrib/xml2 - xpath.c (source / functions) Coverage Total Hit UNC UBC GNC CBC EUB ECB DUB DCB
Current: d36b728949bf4e37ada1cd23e0f2aaa94f609a70 vs 52e118fe2f7e3381bdaa479816a7f72eda2ae517 Lines: 62.6 % 425 266 118 41 112 154 25 52
Current Date: 2026-06-29 16:15:13 +0200 Functions: 85.7 % 21 18 3 7 11 1
Baseline: lcov-20260630-baseline Branches: 41.7 % 242 101 111 30 56 45 16 8
Baseline Date: 2026-06-29 13:01:57 +0200 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 65.7 % 70 46 20 4 37 9
(30,360] days: 58.3 % 24 14 10 12 2
(360..) days: 62.2 % 331 206 88 37 63 143
Function coverage date bins:
(30,360] days: 100.0 % 1 1 1
(360..) days: 85.0 % 20 17 3 7 10
Branch coverage date bins:
(7,30] days: 37.5 % 48 18 26 2 14 4 2
(30,360] days: 100.0 % 2 2 2
(360..) days: 37.5 % 216 81 85 28 40 41 16 6

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*
                                  2                 :                :  * contrib/xml2/xpath.c
                                  3                 :                :  *
                                  4                 :                :  * Parser interface for DOM-based parser (libxml) rather than
                                  5                 :                :  * stream-based SAX-type parser
                                  6                 :                :  */
                                  7                 :                : #include "postgres.h"
                                  8                 :                : 
                                  9                 :                : #include "access/htup_details.h"
                                 10                 :                : #include "executor/spi.h"
                                 11                 :                : #include "fmgr.h"
                                 12                 :                : #include "funcapi.h"
                                 13                 :                : #include "lib/stringinfo.h"
                                 14                 :                : #include "utils/builtins.h"
                                 15                 :                : #include "utils/tuplestore.h"
                                 16                 :                : #include "utils/xml.h"
                                 17                 :                : 
                                 18                 :                : /* libxml includes */
                                 19                 :                : 
                                 20                 :                : #include <libxml/xpath.h>
                                 21                 :                : #include <libxml/tree.h>
                                 22                 :                : #include <libxml/xmlmemory.h>
                                 23                 :                : #include <libxml/xmlerror.h>
                                 24                 :                : #include <libxml/parserInternals.h>
                                 25                 :                : 
  461 tgl@sss.pgh.pa.us          26                 :CBC           1 : PG_MODULE_MAGIC_EXT(
                                 27                 :                :                     .name = "xml2",
                                 28                 :                :                     .version = PG_VERSION
                                 29                 :                : );
                                 30                 :                : 
                                 31                 :                : /* exported for use by xslt_proc.c */
                                 32                 :                : 
                                 33                 :                : PgXmlErrorContext *pgxml_parser_init(PgXmlStrictness strictness);
                                 34                 :                : 
                                 35                 :                : /* workspace for pgxml_xpath() */
                                 36                 :                : 
                                 37                 :                : typedef struct
                                 38                 :                : {
                                 39                 :                :     xmlDocPtr   doctree;
                                 40                 :                :     xmlXPathContextPtr ctxt;
                                 41                 :                :     xmlXPathObjectPtr res;
                                 42                 :                : } xpath_workspace;
                                 43                 :                : 
                                 44                 :                : /* local declarations */
                                 45                 :                : 
                                 46                 :                : static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
                                 47                 :                :                                    xmlChar *toptagname, xmlChar *septagname,
                                 48                 :                :                                    xmlChar *plainsep);
                                 49                 :                : 
                                 50                 :                : static text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag,
                                 51                 :                :                                   xmlChar *septag, xmlChar *plainsep);
                                 52                 :                : 
                                 53                 :                : static xmlChar *pgxml_texttoxmlchar(text *textstring);
                                 54                 :                : 
                                 55                 :                : static xpath_workspace *pgxml_xpath(text *document, xmlChar *xpath,
                                 56                 :                :                                     PgXmlErrorContext *xmlerrcxt);
                                 57                 :                : 
                                 58                 :                : static void cleanup_workspace(xpath_workspace *workspace);
                                 59                 :                : 
                                 60                 :                : 
                                 61                 :                : /*
                                 62                 :                :  * Initialize for xml parsing.
                                 63                 :                :  *
                                 64                 :                :  * As with the underlying pg_xml_init function, calls to this MUST be followed
                                 65                 :                :  * by a PG_TRY block that guarantees that pg_xml_done is called.
                                 66                 :                :  */
                                 67                 :                : PgXmlErrorContext *
 5459                            68                 :             12 : pgxml_parser_init(PgXmlStrictness strictness)
                                 69                 :                : {
                                 70                 :                :     PgXmlErrorContext *xmlerrcxt;
                                 71                 :                : 
                                 72                 :                :     /* Set up error handling (we share the core's error handler) */
                                 73                 :             12 :     xmlerrcxt = pg_xml_init(strictness);
                                 74                 :                : 
                                 75                 :                :     /* Note: we're assuming an elog cannot be thrown by the following calls */
                                 76                 :                : 
                                 77                 :                :     /* Initialize libxml */
 5966                            78                 :             12 :     xmlInitParser();
                                 79                 :                : 
 5459                            80                 :             12 :     return xmlerrcxt;
                                 81                 :                : }
                                 82                 :                : 
                                 83                 :                : 
                                 84                 :                : /* Encodes special characters (<, >, &, " and \r) as XML entities */
                                 85                 :                : 
 7880 bruce@momjian.us           86                 :              1 : PG_FUNCTION_INFO_V1(xml_encode_special_chars);
                                 87                 :                : 
                                 88                 :                : Datum
 7880 bruce@momjian.us           89                 :UBC           0 : xml_encode_special_chars(PG_FUNCTION_ARGS)
                                 90                 :                : {
 3397 noah@leadboat.com          91                 :              0 :     text       *tin = PG_GETARG_TEXT_PP(0);
  357 tgl@sss.pgh.pa.us          92                 :UNC           0 :     text       *volatile tout = NULL;
                                 93                 :              0 :     xmlChar    *volatile tt = NULL;
                                 94                 :                :     PgXmlErrorContext *xmlerrcxt;
                                 95                 :                : 
  364 michael@paquier.xyz        96                 :              0 :     xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
                                 97                 :                : 
                                 98         [ #  # ]:              0 :     PG_TRY();
                                 99                 :                :     {
                                100                 :                :         xmlChar    *ts;
                                101                 :                : 
                                102                 :              0 :         ts = pgxml_texttoxmlchar(tin);
                                103                 :                : 
                                104                 :              0 :         tt = xmlEncodeSpecialChars(NULL, ts);
                                105   [ #  #  #  # ]:              0 :         if (tt == NULL || pg_xml_error_occurred(xmlerrcxt))
                                106                 :              0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
                                107                 :                :                         "could not allocate xmlChar");
                                108                 :              0 :         pfree(ts);
                                109                 :                : 
                                110                 :              0 :         tout = cstring_to_text((char *) tt);
                                111                 :                :     }
                                112                 :              0 :     PG_CATCH();
                                113                 :                :     {
                                114         [ #  # ]:              0 :         if (tt != NULL)
  357 tgl@sss.pgh.pa.us         115                 :              0 :             xmlFree(tt);
                                116                 :                : 
  364 michael@paquier.xyz       117                 :              0 :         pg_xml_done(xmlerrcxt, true);
                                118                 :                : 
                                119                 :              0 :         PG_RE_THROW();
                                120                 :                :     }
                                121         [ #  # ]:              0 :     PG_END_TRY();
                                122                 :                : 
                                123         [ #  # ]:              0 :     if (tt != NULL)
  357 tgl@sss.pgh.pa.us         124                 :              0 :         xmlFree(tt);
                                125                 :                : 
  364 michael@paquier.xyz       126                 :              0 :     pg_xml_done(xmlerrcxt, false);
                                127                 :                : 
 7880 bruce@momjian.us          128                 :UBC           0 :     PG_RETURN_TEXT_P(tout);
                                129                 :                : }
                                130                 :                : 
                                131                 :                : /*
                                132                 :                :  * Function translates a nodeset into a text representation
                                133                 :                :  *
                                134                 :                :  * iterates over each node in the set and calls xmlNodeDump to write it to
                                135                 :                :  * an xmlBuffer -from which an xmlChar * string is returned.
                                136                 :                :  *
                                137                 :                :  * each representation is surrounded by <tagname> ... </tagname>
                                138                 :                :  *
                                139                 :                :  * plainsep is an ordinary (not tag) separator - if used, then nodes are
                                140                 :                :  * cast to string as output method
                                141                 :                :  */
                                142                 :                : static xmlChar *
 8152 bruce@momjian.us          143                 :CBC           6 : pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
                                144                 :                :                    xmlChar *toptagname,
                                145                 :                :                    xmlChar *septagname,
                                146                 :                :                    xmlChar *plainsep)
                                147                 :                : {
  364 michael@paquier.xyz       148                 :GNC           6 :     volatile xmlBufferPtr buf = NULL;
  357 tgl@sss.pgh.pa.us         149                 :              6 :     xmlChar    *volatile result = NULL;
   27 michael@paquier.xyz       150                 :              6 :     xmlChar    *volatile str = NULL;
                                151                 :                :     PgXmlErrorContext *xmlerrcxt;
                                152                 :                : 
                                153                 :                :     /* spin up some error handling */
  364                           154                 :              6 :     xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
                                155                 :                : 
                                156         [ +  - ]:              6 :     PG_TRY();
                                157                 :                :     {
                                158                 :              6 :         buf = xmlBufferCreate();
                                159                 :                : 
                                160   [ +  -  -  + ]:              6 :         if (buf == NULL || pg_xml_error_occurred(xmlerrcxt))
  364 michael@paquier.xyz       161                 :UNC           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
                                162                 :                :                         "could not allocate xmlBuffer");
                                163                 :                : 
  364 michael@paquier.xyz       164   [ +  +  +  + ]:GNC           6 :         if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
                                165                 :                :         {
                                166                 :              1 :             xmlBufferWriteChar(buf, "<");
                                167                 :              1 :             xmlBufferWriteCHAR(buf, toptagname);
                                168                 :              1 :             xmlBufferWriteChar(buf, ">");
                                169                 :                :         }
                                170         [ +  - ]:              6 :         if (nodeset != NULL)
                                171                 :                :         {
  357 tgl@sss.pgh.pa.us         172         [ +  + ]:             17 :             for (int i = 0; i < nodeset->nodeNr; i++)
                                173                 :                :             {
  364 michael@paquier.xyz       174         [ +  + ]:             11 :                 if (plainsep != NULL)
                                175                 :                :                 {
   27                           176                 :              4 :                     str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
                                177   [ +  -  -  + ]:              4 :                     if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
   27 michael@paquier.xyz       178                 :UNC           0 :                         xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
                                179                 :                :                                     "could not allocate node text");
                                180                 :                : 
   27 michael@paquier.xyz       181                 :GNC           4 :                     xmlBufferWriteCHAR(buf, str);
                                182                 :              4 :                     xmlFree(str);
                                183                 :              4 :                     str = NULL;
                                184                 :                : 
                                185                 :                :                     /* If this isn't the last entry, write the plain sep. */
  364                           186         [ +  + ]:              4 :                     if (i < (nodeset->nodeNr) - 1)
                                187                 :              2 :                         xmlBufferWriteChar(buf, (char *) plainsep);
                                188                 :                :                 }
                                189                 :                :                 else
                                190                 :                :                 {
   19                           191                 :              7 :                     xmlNodePtr  node = nodeset->nodeTab[i];
                                192                 :                : 
  364                           193   [ +  -  +  + ]:              7 :                     if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
                                194                 :                :                     {
                                195                 :              4 :                         xmlBufferWriteChar(buf, "<");
                                196                 :              4 :                         xmlBufferWriteCHAR(buf, septagname);
                                197                 :              4 :                         xmlBufferWriteChar(buf, ">");
                                198                 :                :                     }
                                199                 :                : 
                                200                 :                :                     /*
                                201                 :                :                      * XML_NAMESPACE_DECL nodes are xmlNs structs, that cannot
                                202                 :                :                      * be processed by xmlNodeDump().
                                203                 :                :                      */
   19                           204         [ +  + ]:              7 :                     if (node->type == XML_NAMESPACE_DECL)
                                205                 :                :                     {
                                206                 :              1 :                         str = xmlXPathCastNodeToString(node);
                                207   [ +  -  -  + ]:              1 :                         if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
   19 michael@paquier.xyz       208                 :UNC           0 :                             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
                                209                 :                :                                         "could not allocate node text");
   19 michael@paquier.xyz       210                 :GNC           1 :                         xmlBufferWriteCHAR(buf, str);
                                211                 :              1 :                         xmlFree(str);
                                212                 :              1 :                         str = NULL;
                                213                 :                :                     }
                                214                 :                :                     else
                                215                 :              6 :                         xmlNodeDump(buf, node->doc, node, 1, 0);
                                216                 :                : 
  364                           217   [ +  -  +  + ]:              7 :                     if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
                                218                 :                :                     {
                                219                 :              4 :                         xmlBufferWriteChar(buf, "</");
                                220                 :              4 :                         xmlBufferWriteCHAR(buf, septagname);
                                221                 :              4 :                         xmlBufferWriteChar(buf, ">");
                                222                 :                :                     }
                                223                 :                :                 }
                                224                 :                :             }
                                225                 :                :         }
                                226                 :                : 
                                227   [ +  +  +  + ]:              6 :         if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
                                228                 :                :         {
                                229                 :              1 :             xmlBufferWriteChar(buf, "</");
                                230                 :              1 :             xmlBufferWriteCHAR(buf, toptagname);
                                231                 :              1 :             xmlBufferWriteChar(buf, ">");
                                232                 :                :         }
                                233                 :                : 
  358                           234                 :              6 :         result = xmlStrdup(xmlBufferContent(buf));
  364                           235   [ +  -  -  + ]:              6 :         if (result == NULL || pg_xml_error_occurred(xmlerrcxt))
  364 michael@paquier.xyz       236                 :UNC           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
                                237                 :                :                         "could not allocate result");
                                238                 :                :     }
                                239                 :              0 :     PG_CATCH();
                                240                 :                :     {
   27                           241         [ #  # ]:              0 :         if (result)
                                242                 :              0 :             xmlFree(result);
                                243         [ #  # ]:              0 :         if (str)
                                244                 :              0 :             xmlFree(str);
  364                           245         [ #  # ]:              0 :         if (buf)
                                246                 :              0 :             xmlBufferFree(buf);
                                247                 :                : 
                                248                 :              0 :         pg_xml_done(xmlerrcxt, true);
                                249                 :                : 
                                250                 :              0 :         PG_RE_THROW();
                                251                 :                :     }
  364 michael@paquier.xyz       252         [ -  + ]:GNC           6 :     PG_END_TRY();
                                253                 :                : 
 8152 bruce@momjian.us          254                 :CBC           6 :     xmlBufferFree(buf);
  364 michael@paquier.xyz       255                 :GNC           6 :     pg_xml_done(xmlerrcxt, false);
                                256                 :                : 
 8152 bruce@momjian.us          257                 :CBC           6 :     return result;
                                258                 :                : }
                                259                 :                : 
                                260                 :                : 
                                261                 :                : /*
                                262                 :                :  * Translate a PostgreSQL "varlena" -i.e. a variable length parameter
                                263                 :                :  * into the libxml2 representation
                                264                 :                :  */
                                265                 :                : static xmlChar *
                                266                 :             16 : pgxml_texttoxmlchar(text *textstring)
                                267                 :                : {
 6631 tgl@sss.pgh.pa.us         268                 :             16 :     return (xmlChar *) text_to_cstring(textstring);
                                269                 :                : }
                                270                 :                : 
                                271                 :                : /* Publicly visible XPath functions */
                                272                 :                : 
                                273                 :                : /*
                                274                 :                :  * This is a "raw" xpath function. Check that it returns child elements
                                275                 :                :  * properly
                                276                 :                :  */
 8152 bruce@momjian.us          277                 :              2 : PG_FUNCTION_INFO_V1(xpath_nodeset);
                                278                 :                : 
                                279                 :                : Datum
                                280                 :              4 : xpath_nodeset(PG_FUNCTION_ARGS)
                                281                 :                : {
 3397 noah@leadboat.com         282                 :              4 :     text       *document = PG_GETARG_TEXT_PP(0);
 3296 tgl@sss.pgh.pa.us         283                 :              4 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
 3397 noah@leadboat.com         284                 :              4 :     xmlChar    *toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(2));
                                285                 :              4 :     xmlChar    *septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(3));
                                286                 :                :     xmlChar    *xpath;
  357 tgl@sss.pgh.pa.us         287                 :GNC           4 :     text       *volatile xpres = NULL;
                                288                 :              4 :     xpath_workspace *volatile workspace = NULL;
                                289                 :                :     PgXmlErrorContext *xmlerrcxt;
                                290                 :                : 
 5695 tgl@sss.pgh.pa.us         291                 :CBC           4 :     xpath = pgxml_texttoxmlchar(xpathsupp);
  364 michael@paquier.xyz       292                 :GNC           4 :     xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
                                293                 :                : 
                                294         [ +  - ]:              4 :     PG_TRY();
                                295                 :                :     {
                                296                 :              4 :         workspace = pgxml_xpath(document, xpath, xmlerrcxt);
                                297                 :              4 :         xpres = pgxml_result_to_text(workspace->res, toptag, septag, NULL);
                                298                 :                :     }
  364 michael@paquier.xyz       299                 :UNC           0 :     PG_CATCH();
                                300                 :                :     {
                                301         [ #  # ]:              0 :         if (workspace)
                                302                 :              0 :             cleanup_workspace(workspace);
                                303                 :                : 
                                304                 :              0 :         pg_xml_done(xmlerrcxt, true);
                                305                 :              0 :         PG_RE_THROW();
                                306                 :                :     }
  364 michael@paquier.xyz       307         [ -  + ]:GNC           4 :     PG_END_TRY();
                                308                 :                : 
                                309                 :              4 :     cleanup_workspace(workspace);
                                310                 :              4 :     pg_xml_done(xmlerrcxt, false);
                                311                 :                : 
 7930 neilc@samurai.com         312                 :CBC           4 :     pfree(xpath);
                                313                 :                : 
 7975 bruce@momjian.us          314         [ -  + ]:              4 :     if (xpres == NULL)
 7975 bruce@momjian.us          315                 :UBC           0 :         PG_RETURN_NULL();
 8152 bruce@momjian.us          316                 :CBC           4 :     PG_RETURN_TEXT_P(xpres);
                                317                 :                : }
                                318                 :                : 
                                319                 :                : /*
                                320                 :                :  * The following function is almost identical, but returns the elements in
                                321                 :                :  * a list.
                                322                 :                :  */
                                323                 :              2 : PG_FUNCTION_INFO_V1(xpath_list);
                                324                 :                : 
                                325                 :                : Datum
                                326                 :              2 : xpath_list(PG_FUNCTION_ARGS)
                                327                 :                : {
 3397 noah@leadboat.com         328                 :              2 :     text       *document = PG_GETARG_TEXT_PP(0);
 3296 tgl@sss.pgh.pa.us         329                 :              2 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
 3397 noah@leadboat.com         330                 :              2 :     xmlChar    *plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(2));
                                331                 :                :     xmlChar    *xpath;
  357 tgl@sss.pgh.pa.us         332                 :GNC           2 :     text       *volatile xpres = NULL;
                                333                 :              2 :     xpath_workspace *volatile workspace = NULL;
                                334                 :                :     PgXmlErrorContext *xmlerrcxt;
                                335                 :                : 
 5695 tgl@sss.pgh.pa.us         336                 :CBC           2 :     xpath = pgxml_texttoxmlchar(xpathsupp);
  364 michael@paquier.xyz       337                 :GNC           2 :     xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
                                338                 :                : 
                                339         [ +  - ]:              2 :     PG_TRY();
                                340                 :                :     {
                                341                 :              2 :         workspace = pgxml_xpath(document, xpath, xmlerrcxt);
                                342                 :              2 :         xpres = pgxml_result_to_text(workspace->res, NULL, NULL, plainsep);
                                343                 :                :     }
  364 michael@paquier.xyz       344                 :UNC           0 :     PG_CATCH();
                                345                 :                :     {
                                346         [ #  # ]:              0 :         if (workspace)
                                347                 :              0 :             cleanup_workspace(workspace);
                                348                 :                : 
                                349                 :              0 :         pg_xml_done(xmlerrcxt, true);
                                350                 :              0 :         PG_RE_THROW();
                                351                 :                :     }
  364 michael@paquier.xyz       352         [ -  + ]:GNC           2 :     PG_END_TRY();
                                353                 :                : 
                                354                 :              2 :     cleanup_workspace(workspace);
                                355                 :              2 :     pg_xml_done(xmlerrcxt, false);
                                356                 :                : 
 7930 neilc@samurai.com         357                 :CBC           2 :     pfree(xpath);
                                358                 :                : 
 7975 bruce@momjian.us          359         [ -  + ]:              2 :     if (xpres == NULL)
 7975 bruce@momjian.us          360                 :UBC           0 :         PG_RETURN_NULL();
 8152 bruce@momjian.us          361                 :CBC           2 :     PG_RETURN_TEXT_P(xpres);
                                362                 :                : }
                                363                 :                : 
                                364                 :                : 
                                365                 :              2 : PG_FUNCTION_INFO_V1(xpath_string);
                                366                 :                : 
                                367                 :                : Datum
                                368                 :              1 : xpath_string(PG_FUNCTION_ARGS)
                                369                 :                : {
 3397 noah@leadboat.com         370                 :              1 :     text       *document = PG_GETARG_TEXT_PP(0);
 3296 tgl@sss.pgh.pa.us         371                 :              1 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
                                372                 :                :     xmlChar    *xpath;
                                373                 :                :     int32       pathsize;
  357 tgl@sss.pgh.pa.us         374                 :GNC           1 :     text       *volatile xpres = NULL;
                                375                 :              1 :     xpath_workspace *volatile workspace = NULL;
                                376                 :                :     PgXmlErrorContext *xmlerrcxt;
                                377                 :                : 
 3397 noah@leadboat.com         378   [ -  +  -  -  :CBC           1 :     pathsize = VARSIZE_ANY_EXHDR(xpathsupp);
                                     -  -  -  -  +  
                                                 - ]
                                379                 :                : 
                                380                 :                :     /*
                                381                 :                :      * We encapsulate the supplied path with "string()" = 8 chars + 1 for NUL
                                382                 :                :      * at end
                                383                 :                :      */
                                384                 :                :     /* We could try casting to string using the libxml function? */
                                385                 :                : 
 7975 bruce@momjian.us          386                 :              1 :     xpath = (xmlChar *) palloc(pathsize + 9);
  503 peter@eisentraut.org      387                 :              1 :     memcpy(xpath, "string(", 7);
                                388         [ +  - ]:              1 :     memcpy(xpath + 7, VARDATA_ANY(xpathsupp), pathsize);
 7975 bruce@momjian.us          389                 :              1 :     xpath[pathsize + 7] = ')';
                                390                 :              1 :     xpath[pathsize + 8] = '\0';
                                391                 :                : 
  364 michael@paquier.xyz       392                 :GNC           1 :     xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
                                393                 :                : 
                                394         [ +  - ]:              1 :     PG_TRY();
                                395                 :                :     {
                                396                 :              1 :         workspace = pgxml_xpath(document, xpath, xmlerrcxt);
                                397                 :              1 :         xpres = pgxml_result_to_text(workspace->res, NULL, NULL, NULL);
                                398                 :                :     }
  364 michael@paquier.xyz       399                 :UNC           0 :     PG_CATCH();
                                400                 :                :     {
                                401         [ #  # ]:              0 :         if (workspace)
                                402                 :              0 :             cleanup_workspace(workspace);
                                403                 :                : 
                                404                 :              0 :         pg_xml_done(xmlerrcxt, true);
                                405                 :              0 :         PG_RE_THROW();
                                406                 :                :     }
  364 michael@paquier.xyz       407         [ -  + ]:GNC           1 :     PG_END_TRY();
                                408                 :                : 
                                409                 :              1 :     cleanup_workspace(workspace);
                                410                 :              1 :     pg_xml_done(xmlerrcxt, false);
                                411                 :                : 
 7930 neilc@samurai.com         412                 :CBC           1 :     pfree(xpath);
                                413                 :                : 
 7975 bruce@momjian.us          414         [ +  - ]:              1 :     if (xpres == NULL)
                                415                 :              1 :         PG_RETURN_NULL();
 8152 bruce@momjian.us          416                 :UBC           0 :     PG_RETURN_TEXT_P(xpres);
                                417                 :                : }
                                418                 :                : 
                                419                 :                : 
 8152 bruce@momjian.us          420                 :CBC           1 : PG_FUNCTION_INFO_V1(xpath_number);
                                421                 :                : 
                                422                 :                : Datum
 8152 bruce@momjian.us          423                 :UBC           0 : xpath_number(PG_FUNCTION_ARGS)
                                424                 :                : {
 3397 noah@leadboat.com         425                 :              0 :     text       *document = PG_GETARG_TEXT_PP(0);
 3296 tgl@sss.pgh.pa.us         426                 :              0 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
                                427                 :                :     xmlChar    *xpath;
  357 tgl@sss.pgh.pa.us         428                 :UNC           0 :     volatile float4 fRes = 0.0;
                                429                 :              0 :     volatile bool isNull = false;
                                430                 :              0 :     xpath_workspace *volatile workspace = NULL;
                                431                 :                :     PgXmlErrorContext *xmlerrcxt;
                                432                 :                : 
 8152 bruce@momjian.us          433                 :UBC           0 :     xpath = pgxml_texttoxmlchar(xpathsupp);
  364 michael@paquier.xyz       434                 :UNC           0 :     xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
                                435                 :                : 
                                436         [ #  # ]:              0 :     PG_TRY();
                                437                 :                :     {
                                438                 :              0 :         workspace = pgxml_xpath(document, xpath, xmlerrcxt);
                                439                 :              0 :         pfree(xpath);
                                440                 :                : 
                                441         [ #  # ]:              0 :         if (workspace->res == NULL)
                                442                 :              0 :             isNull = true;
                                443                 :                :         else
                                444                 :              0 :             fRes = xmlXPathCastToNumber(workspace->res);
                                445                 :                :     }
                                446                 :              0 :     PG_CATCH();
                                447                 :                :     {
                                448         [ #  # ]:              0 :         if (workspace)
                                449                 :              0 :             cleanup_workspace(workspace);
                                450                 :                : 
                                451                 :              0 :         pg_xml_done(xmlerrcxt, true);
                                452                 :              0 :         PG_RE_THROW();
                                453                 :                :     }
                                454         [ #  # ]:              0 :     PG_END_TRY();
                                455                 :                : 
                                456                 :              0 :     cleanup_workspace(workspace);
                                457                 :              0 :     pg_xml_done(xmlerrcxt, false);
                                458                 :                : 
                                459   [ #  #  #  # ]:              0 :     if (isNull || xmlXPathIsNaN(fRes))
 7975 bruce@momjian.us          460                 :UBC           0 :         PG_RETURN_NULL();
                                461                 :                : 
 8152                           462                 :              0 :     PG_RETURN_FLOAT4(fRes);
                                463                 :                : }
                                464                 :                : 
                                465                 :                : 
 8152 bruce@momjian.us          466                 :CBC           1 : PG_FUNCTION_INFO_V1(xpath_bool);
                                467                 :                : 
                                468                 :                : Datum
 8152 bruce@momjian.us          469                 :UBC           0 : xpath_bool(PG_FUNCTION_ARGS)
                                470                 :                : {
 3397 noah@leadboat.com         471                 :              0 :     text       *document = PG_GETARG_TEXT_PP(0);
 3296 tgl@sss.pgh.pa.us         472                 :              0 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
                                473                 :                :     xmlChar    *xpath;
  357 tgl@sss.pgh.pa.us         474                 :UNC           0 :     volatile int bRes = 0;
                                475                 :              0 :     xpath_workspace *volatile workspace = NULL;
                                476                 :                :     PgXmlErrorContext *xmlerrcxt;
                                477                 :                : 
 8152 bruce@momjian.us          478                 :UBC           0 :     xpath = pgxml_texttoxmlchar(xpathsupp);
  364 michael@paquier.xyz       479                 :UNC           0 :     xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
                                480                 :                : 
                                481         [ #  # ]:              0 :     PG_TRY();
                                482                 :                :     {
                                483                 :              0 :         workspace = pgxml_xpath(document, xpath, xmlerrcxt);
                                484                 :              0 :         pfree(xpath);
                                485                 :                : 
                                486         [ #  # ]:              0 :         if (workspace->res == NULL)
                                487                 :              0 :             bRes = 0;
                                488                 :                :         else
                                489                 :              0 :             bRes = xmlXPathCastToBoolean(workspace->res);
                                490                 :                :     }
                                491                 :              0 :     PG_CATCH();
                                492                 :                :     {
                                493         [ #  # ]:              0 :         if (workspace)
                                494                 :              0 :             cleanup_workspace(workspace);
                                495                 :                : 
                                496                 :              0 :         pg_xml_done(xmlerrcxt, true);
                                497                 :              0 :         PG_RE_THROW();
                                498                 :                :     }
                                499         [ #  # ]:              0 :     PG_END_TRY();
                                500                 :                : 
                                501                 :              0 :     cleanup_workspace(workspace);
                                502                 :              0 :     pg_xml_done(xmlerrcxt, false);
                                503                 :                : 
 8152 bruce@momjian.us          504                 :UBC           0 :     PG_RETURN_BOOL(bRes);
                                505                 :                : }
                                506                 :                : 
                                507                 :                : 
                                508                 :                : 
                                509                 :                : /* Core function to evaluate XPath query */
                                510                 :                : 
                                511                 :                : static xpath_workspace *
  364 michael@paquier.xyz       512                 :GNC           7 : pgxml_xpath(text *document, xmlChar *xpath, PgXmlErrorContext *xmlerrcxt)
                                513                 :                : {
 3397 noah@leadboat.com         514   [ -  +  -  -  :CBC           7 :     int32       docsize = VARSIZE_ANY_EXHDR(document);
                                     -  -  -  -  +  
                                                 + ]
   27 michael@paquier.xyz       515                 :GNC           7 :     xmlXPathCompExprPtr volatile comppath = NULL;
  207                           516                 :              7 :     xpath_workspace *workspace = palloc0_object(xpath_workspace);
                                517                 :                : 
 5695 tgl@sss.pgh.pa.us         518                 :CBC           7 :     workspace->doctree = NULL;
                                519                 :              7 :     workspace->ctxt = NULL;
                                520                 :              7 :     workspace->res = NULL;
                                521                 :                : 
   27 michael@paquier.xyz       522         [ +  - ]:              7 :     PG_TRY();
                                523                 :                :     {
                                524         [ +  + ]:              7 :         workspace->doctree = xmlReadMemory((char *) VARDATA_ANY(document),
                                525                 :                :                                            docsize, NULL, NULL,
                                526                 :                :                                            XML_PARSE_NOENT);
                                527         [ +  + ]:              7 :         if (workspace->doctree != NULL)
                                528                 :                :         {
                                529                 :              6 :             workspace->ctxt = xmlXPathNewContext(workspace->doctree);
   27 michael@paquier.xyz       530         [ -  + ]:GNC           6 :             if (workspace->ctxt == NULL)
   27 michael@paquier.xyz       531                 :UNC           0 :                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
                                532                 :                :                             "could not allocate XPath context");
                                533                 :                : 
   27 michael@paquier.xyz       534                 :CBC           6 :             workspace->ctxt->node = xmlDocGetRootElement(workspace->doctree);
                                535                 :                : 
                                536                 :                :             /* compile the path */
                                537                 :              6 :             comppath = xmlXPathCtxtCompile(workspace->ctxt, xpath);
   27 michael@paquier.xyz       538   [ +  -  -  + ]:GNC           6 :             if (comppath == NULL || pg_xml_error_occurred(xmlerrcxt))
   27 michael@paquier.xyz       539                 :UBC           0 :                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
                                540                 :                :                             "XPath Syntax Error");
                                541                 :                : 
                                542                 :                :             /* Now evaluate the path expression. */
   27 michael@paquier.xyz       543                 :CBC           6 :             workspace->res = xmlXPathCompiledEval(comppath, workspace->ctxt);
                                544                 :                : 
                                545                 :              6 :             xmlXPathFreeCompExpr(comppath);
   27 michael@paquier.xyz       546                 :GNC           6 :             comppath = NULL;
                                547                 :                :         }
                                548                 :                :     }
   27 michael@paquier.xyz       549                 :UBC           0 :     PG_CATCH();
                                550                 :                :     {
   27 michael@paquier.xyz       551         [ #  # ]:UNC           0 :         if (comppath != NULL)
                                552                 :              0 :             xmlXPathFreeCompExpr(comppath);
   27 michael@paquier.xyz       553                 :UBC           0 :         cleanup_workspace(workspace);
                                554                 :                : 
                                555                 :              0 :         PG_RE_THROW();
                                556                 :                :     }
   27 michael@paquier.xyz       557         [ -  + ]:CBC           7 :     PG_END_TRY();
                                558                 :                : 
  364 michael@paquier.xyz       559                 :GNC           7 :     return workspace;
                                560                 :                : }
                                561                 :                : 
                                562                 :                : /* Clean up after processing the result of pgxml_xpath() */
                                563                 :                : static void
  357 tgl@sss.pgh.pa.us         564                 :CBC           7 : cleanup_workspace(xpath_workspace *workspace)
                                565                 :                : {
 5695                           566         [ +  + ]:              7 :     if (workspace->res)
                                567                 :              6 :         xmlXPathFreeObject(workspace->res);
                                568                 :              7 :     workspace->res = NULL;
                                569         [ +  + ]:              7 :     if (workspace->ctxt)
                                570                 :              6 :         xmlXPathFreeContext(workspace->ctxt);
                                571                 :              7 :     workspace->ctxt = NULL;
                                572         [ +  + ]:              7 :     if (workspace->doctree)
                                573                 :              6 :         xmlFreeDoc(workspace->doctree);
                                574                 :              7 :     workspace->doctree = NULL;
                                575                 :              7 : }
                                576                 :                : 
                                577                 :                : static text *
 7975 bruce@momjian.us          578                 :              7 : pgxml_result_to_text(xmlXPathObjectPtr res,
                                579                 :                :                      xmlChar *toptag,
                                580                 :                :                      xmlChar *septag,
                                581                 :                :                      xmlChar *plainsep)
                                582                 :                : {
  357 tgl@sss.pgh.pa.us         583                 :GNC           7 :     xmlChar    *volatile xpresstr = NULL;
                                584                 :              7 :     text       *volatile xpres = NULL;
                                585                 :                :     PgXmlErrorContext *xmlerrcxt;
                                586                 :                : 
 7975 bruce@momjian.us          587         [ +  + ]:CBC           7 :     if (res == NULL)
                                588                 :              1 :         return NULL;
                                589                 :                : 
                                590                 :                :     /* spin some error handling */
  364 michael@paquier.xyz       591                 :GNC           6 :     xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
                                592                 :                : 
                                593         [ +  - ]:              6 :     PG_TRY();
                                594                 :                :     {
                                595      [ +  -  - ]:              6 :         switch (res->type)
                                596                 :                :         {
                                597                 :              6 :             case XPATH_NODESET:
                                598                 :              6 :                 xpresstr = pgxmlNodeSetToText(res->nodesetval,
                                599                 :                :                                               toptag,
                                600                 :                :                                               septag, plainsep);
                                601                 :              6 :                 break;
                                602                 :                : 
  364 michael@paquier.xyz       603                 :UNC           0 :             case XPATH_STRING:
                                604                 :              0 :                 xpresstr = xmlStrdup(res->stringval);
                                605   [ #  #  #  # ]:              0 :                 if (xpresstr == NULL || pg_xml_error_occurred(xmlerrcxt))
                                606                 :              0 :                     xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
                                607                 :                :                                 "could not allocate result");
                                608                 :              0 :                 break;
                                609                 :                : 
                                610                 :              0 :             default:
                                611         [ #  # ]:              0 :                 elog(NOTICE, "unsupported XQuery result: %d", res->type);
                                612                 :              0 :                 xpresstr = xmlStrdup((const xmlChar *) "<unsupported/>");
                                613   [ #  #  #  # ]:              0 :                 if (xpresstr == NULL || pg_xml_error_occurred(xmlerrcxt))
                                614                 :              0 :                     xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
                                615                 :                :                                 "could not allocate result");
                                616                 :                :         }
                                617                 :                : 
                                618                 :                :         /* Now convert this result back to text */
  364 michael@paquier.xyz       619                 :GNC           6 :         xpres = cstring_to_text((char *) xpresstr);
                                620                 :                :     }
  364 michael@paquier.xyz       621                 :UNC           0 :     PG_CATCH();
                                622                 :                :     {
                                623         [ #  # ]:              0 :         if (xpresstr != NULL)
  357 tgl@sss.pgh.pa.us         624                 :              0 :             xmlFree(xpresstr);
                                625                 :                : 
  364 michael@paquier.xyz       626                 :              0 :         pg_xml_done(xmlerrcxt, true);
                                627                 :                : 
                                628                 :              0 :         PG_RE_THROW();
                                629                 :                :     }
  364 michael@paquier.xyz       630         [ -  + ]:GNC           6 :     PG_END_TRY();
                                631                 :                : 
                                632                 :                :     /* Free various storage */
  357 tgl@sss.pgh.pa.us         633                 :CBC           6 :     xmlFree(xpresstr);
                                634                 :                : 
  364 michael@paquier.xyz       635                 :GNC           6 :     pg_xml_done(xmlerrcxt, false);
                                636                 :                : 
 8152 bruce@momjian.us          637                 :CBC           6 :     return xpres;
                                638                 :                : }
                                639                 :                : 
                                640                 :                : /*
                                641                 :                :  * xpath_table is a table function. It needs some tidying (as do the
                                642                 :                :  * other functions here!
                                643                 :                :  */
                                644                 :              2 : PG_FUNCTION_INFO_V1(xpath_table);
                                645                 :                : 
                                646                 :                : Datum
 7975                           647                 :              5 : xpath_table(PG_FUNCTION_ARGS)
                                648                 :                : {
                                649                 :                :     /* Function parameters */
 5966 tgl@sss.pgh.pa.us         650                 :              5 :     char       *pkeyfield = text_to_cstring(PG_GETARG_TEXT_PP(0));
                                651                 :              5 :     char       *xmlfield = text_to_cstring(PG_GETARG_TEXT_PP(1));
                                652                 :              5 :     char       *relname = text_to_cstring(PG_GETARG_TEXT_PP(2));
                                653                 :              5 :     char       *xpathset = text_to_cstring(PG_GETARG_TEXT_PP(3));
                                654                 :              5 :     char       *condition = text_to_cstring(PG_GETARG_TEXT_PP(4));
                                655                 :                : 
                                656                 :                :     /* SPI (input tuple) support */
                                657                 :                :     SPITupleTable *tuptable;
                                658                 :                :     HeapTuple   spi_tuple;
                                659                 :                :     TupleDesc   spi_tupdesc;
                                660                 :                : 
                                661                 :                : 
 7975 bruce@momjian.us          662                 :              5 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
                                663                 :                :     AttInMetadata *attinmeta;
                                664                 :                : 
                                665                 :                :     char      **values;
                                666                 :                :     xmlChar   **xpaths;
                                667                 :                :     char       *pos;
 6925 tgl@sss.pgh.pa.us         668                 :              5 :     const char *pathsep = "|";
                                669                 :                : 
                                670                 :                :     int         numpaths;
                                671                 :                :     int         ret;
                                672                 :                :     uint64      proc;
                                673                 :                :     int         j;
                                674                 :                :     int         rownr;          /* For issuing multiple rows from one original
                                675                 :                :                                  * document */
                                676                 :                :     bool        had_values;     /* To determine end of nodeset results */
                                677                 :                :     StringInfoData query_buf;
                                678                 :                :     PgXmlErrorContext *xmlerrcxt;
 5459                           679                 :              5 :     volatile xmlDocPtr doctree = NULL;
   27 michael@paquier.xyz       680                 :GNC           5 :     xmlXPathContextPtr volatile ctxt = NULL;
                                681                 :              5 :     xmlXPathObjectPtr volatile res = NULL;
                                682                 :              5 :     xmlXPathCompExprPtr volatile comppath = NULL;
                                683                 :              5 :     xmlChar    *volatile resstr = NULL;
                                684                 :                : 
 1351 michael@paquier.xyz       685                 :CBC           5 :     InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
                                686                 :                : 
                                687                 :                :     /* must have at least one output column (for the pkey) */
 1575                           688         [ -  + ]:              5 :     if (rsinfo->setDesc->natts < 1)
 5966 tgl@sss.pgh.pa.us         689         [ #  # ]:UBC           0 :         ereport(ERROR,
                                690                 :                :                 (errcode(ERRCODE_SYNTAX_ERROR),
                                691                 :                :                  errmsg("xpath_table must have at least one output column")));
                                692                 :                : 
                                693                 :                :     /*
                                694                 :                :      * At the moment we assume that the returned attributes make sense for the
                                695                 :                :      * XPath specified (i.e. we trust the caller). It's not fatal if they get
                                696                 :                :      * it wrong - the input function for the column type will raise an error
                                697                 :                :      * if the path result can't be converted into the correct binary
                                698                 :                :      * representation.
                                699                 :                :      */
                                700                 :                : 
 1575 michael@paquier.xyz       701                 :CBC           5 :     attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
                                702                 :                : 
   27 michael@paquier.xyz       703                 :GNC           5 :     values = (char **) palloc0(rsinfo->setDesc->natts * sizeof(char *));
 1575 michael@paquier.xyz       704                 :CBC           5 :     xpaths = (xmlChar **) palloc(rsinfo->setDesc->natts * sizeof(xmlChar *));
                                705                 :                : 
                                706                 :                :     /*
                                707                 :                :      * Split XPaths. xpathset is a writable CString.
                                708                 :                :      *
                                709                 :                :      * Note that we stop splitting once we've done all needed for tupdesc
                                710                 :                :      */
 7975 bruce@momjian.us          711                 :              5 :     numpaths = 0;
                                712                 :              5 :     pos = xpathset;
 1575 michael@paquier.xyz       713         [ +  + ]:              7 :     while (numpaths < (rsinfo->setDesc->natts - 1))
                                714                 :                :     {
 5966 tgl@sss.pgh.pa.us         715                 :              5 :         xpaths[numpaths++] = (xmlChar *) pos;
 7975 bruce@momjian.us          716                 :              5 :         pos = strstr(pos, pathsep);
                                717         [ +  + ]:              5 :         if (pos != NULL)
                                718                 :                :         {
                                719                 :              2 :             *pos = '\0';
                                720                 :              2 :             pos++;
                                721                 :                :         }
                                722                 :                :         else
 5966 tgl@sss.pgh.pa.us         723                 :              3 :             break;
                                724                 :                :     }
                                725                 :                : 
                                726                 :                :     /* Now build query */
 7426 neilc@samurai.com         727                 :              5 :     initStringInfo(&query_buf);
                                728                 :                : 
                                729                 :                :     /* Build initial sql statement */
                                730                 :              5 :     appendStringInfo(&query_buf, "SELECT %s, %s FROM %s WHERE %s",
                                731                 :                :                      pkeyfield,
                                732                 :                :                      xmlfield,
                                733                 :                :                      relname,
                                734                 :                :                      condition);
                                735                 :                : 
  659 tgl@sss.pgh.pa.us         736                 :              5 :     SPI_connect();
                                737                 :                : 
 7426 neilc@samurai.com         738         [ -  + ]:              5 :     if ((ret = SPI_exec(query_buf.data, 0)) != SPI_OK_SELECT)
 5966 tgl@sss.pgh.pa.us         739         [ #  # ]:UBC           0 :         elog(ERROR, "xpath_table: SPI execution failed for query %s",
                                740                 :                :              query_buf.data);
                                741                 :                : 
 7975 bruce@momjian.us          742                 :CBC           5 :     proc = SPI_processed;
                                743                 :              5 :     tuptable = SPI_tuptable;
                                744                 :              5 :     spi_tupdesc = tuptable->tupdesc;
                                745                 :                : 
                                746                 :                :     /*
                                747                 :                :      * Check that SPI returned correct result. If you put a comma into one of
                                748                 :                :      * the function parameters, this will catch it when the SPI query returns
                                749                 :                :      * e.g. 3 columns.
                                750                 :                :      */
                                751         [ -  + ]:              5 :     if (spi_tupdesc->natts != 2)
                                752                 :                :     {
 7975 bruce@momjian.us          753         [ #  # ]:UBC           0 :         ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                754                 :                :                         errmsg("expression returning multiple columns is not valid in parameter list"),
                                755                 :                :                         errdetail("Expected two columns in SPI result, got %d.", spi_tupdesc->natts)));
                                756                 :                :     }
                                757                 :                : 
                                758                 :                :     /*
                                759                 :                :      * Setup the parser.  This should happen after we are done evaluating the
                                760                 :                :      * query, in case it calls functions that set up libxml differently.
                                761                 :                :      */
 5459 tgl@sss.pgh.pa.us         762                 :CBC           5 :     xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
                                763                 :                : 
                                764         [ +  - ]:              5 :     PG_TRY();
                                765                 :                :     {
                                766                 :                :         /* For each row i.e. document returned from SPI */
                                767                 :                :         uint64      i;
                                768                 :                : 
 5133 bruce@momjian.us          769         [ +  + ]:             10 :         for (i = 0; i < proc; i++)
                                770                 :                :         {
                                771                 :                :             char       *pkey;
                                772                 :                :             char       *xmldoc;
                                773                 :                :             HeapTuple   ret_tuple;
                                774                 :                : 
                                775                 :                :             /* Extract the row data as C Strings */
                                776                 :              5 :             spi_tuple = tuptable->vals[i];
                                777                 :              5 :             pkey = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
                                778                 :              5 :             xmldoc = SPI_getvalue(spi_tuple, spi_tupdesc, 2);
                                779                 :                : 
                                780                 :                :             /*
                                781                 :                :              * Clear the values array, so that not-well-formed documents
                                782                 :                :              * return NULL in all columns.  Note that this also means that
                                783                 :                :              * spare columns will be NULL.
                                784                 :                :              */
 1575 michael@paquier.xyz       785         [ +  + ]:             15 :             for (j = 0; j < rsinfo->setDesc->natts; j++)
 5133 bruce@momjian.us          786                 :             10 :                 values[j] = NULL;
                                787                 :                : 
                                788                 :                :             /* Insert primary key */
                                789                 :              5 :             values[0] = pkey;
                                790                 :                : 
                                791                 :                :             /* Parse the document */
                                792         [ +  - ]:              5 :             if (xmldoc)
  895 michael@paquier.xyz       793                 :              5 :                 doctree = xmlReadMemory(xmldoc, strlen(xmldoc),
                                794                 :                :                                         NULL, NULL,
                                795                 :                :                                         XML_PARSE_NOENT);
                                796                 :                :             else                /* treat NULL as not well-formed */
 5133 bruce@momjian.us          797                 :UBC           0 :                 doctree = NULL;
                                798                 :                : 
 5133 bruce@momjian.us          799         [ -  + ]:CBC           5 :             if (doctree == NULL)
                                800                 :                :             {
                                801                 :                :                 /* not well-formed, so output all-NULL tuple */
 5133 bruce@momjian.us          802                 :UBC           0 :                 ret_tuple = BuildTupleFromCStrings(attinmeta, values);
 1575 michael@paquier.xyz       803                 :              0 :                 tuplestore_puttuple(rsinfo->setResult, ret_tuple);
 5133 bruce@momjian.us          804                 :              0 :                 heap_freetuple(ret_tuple);
                                805                 :                :             }
                                806                 :                :             else
                                807                 :                :             {
                                808                 :                :                 /* New loop here - we have to deal with nodeset results */
 5133 bruce@momjian.us          809                 :CBC           5 :                 rownr = 0;
                                810                 :                : 
                                811                 :                :                 do
                                812                 :                :                 {
                                813                 :                :                     /* Now evaluate the set of xpaths. */
                                814                 :              8 :                     had_values = false;
                                815         [ +  + ]:             18 :                     for (j = 0; j < numpaths; j++)
                                816                 :                :                     {
   27 michael@paquier.xyz       817                 :GNC          10 :                         ctxt = NULL;
                                818                 :             10 :                         res = NULL;
                                819                 :             10 :                         comppath = NULL;
                                820                 :             10 :                         resstr = NULL;
                                821                 :                : 
 5133 bruce@momjian.us          822                 :CBC          10 :                         ctxt = xmlXPathNewContext(doctree);
  364 michael@paquier.xyz       823   [ +  -  -  + ]:GNC          10 :                         if (ctxt == NULL || pg_xml_error_occurred(xmlerrcxt))
  364 michael@paquier.xyz       824                 :UNC           0 :                             xml_ereport(xmlerrcxt,
                                825                 :                :                                         ERROR, ERRCODE_OUT_OF_MEMORY,
                                826                 :                :                                         "could not allocate XPath context");
                                827                 :                : 
 5133 bruce@momjian.us          828                 :CBC          10 :                         ctxt->node = xmlDocGetRootElement(doctree);
                                829                 :                : 
                                830                 :                :                         /* compile the path */
  653 tgl@sss.pgh.pa.us         831                 :             10 :                         comppath = xmlXPathCtxtCompile(ctxt, xpaths[j]);
  364 michael@paquier.xyz       832   [ +  -  -  + ]:GNC          10 :                         if (comppath == NULL || pg_xml_error_occurred(xmlerrcxt))
 5133 bruce@momjian.us          833                 :UBC           0 :                             xml_ereport(xmlerrcxt, ERROR,
                                834                 :                :                                         ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
                                835                 :                :                                         "XPath Syntax Error");
                                836                 :                : 
                                837                 :                :                         /* Now evaluate the path expression. */
 5133 bruce@momjian.us          838                 :CBC          10 :                         res = xmlXPathCompiledEval(comppath, ctxt);
                                839                 :             10 :                         xmlXPathFreeCompExpr(comppath);
   27 michael@paquier.xyz       840                 :GNC          10 :                         comppath = NULL;
                                841                 :                : 
 5133 bruce@momjian.us          842         [ +  - ]:CBC          10 :                         if (res != NULL)
                                843                 :                :                         {
                                844      [ +  -  - ]:             10 :                             switch (res->type)
                                845                 :                :                             {
                                846                 :             10 :                                 case XPATH_NODESET:
                                847                 :                :                                     /* We see if this nodeset has enough nodes */
                                848         [ +  - ]:             10 :                                     if (res->nodesetval != NULL &&
                                849         [ +  + ]:             10 :                                         rownr < res->nodesetval->nodeNr)
                                850                 :                :                                     {
                                851                 :              4 :                                         resstr = xmlXPathCastNodeToString(res->nodesetval->nodeTab[rownr]);
  364 michael@paquier.xyz       852   [ +  -  -  + ]:GNC           4 :                                         if (resstr == NULL || pg_xml_error_occurred(xmlerrcxt))
  364 michael@paquier.xyz       853                 :UNC           0 :                                             xml_ereport(xmlerrcxt,
                                854                 :                :                                                         ERROR, ERRCODE_OUT_OF_MEMORY,
                                855                 :                :                                                         "could not allocate result");
 5133 bruce@momjian.us          856                 :CBC           4 :                                         had_values = true;
                                857                 :                :                                     }
                                858                 :                :                                     else
                                859                 :              6 :                                         resstr = NULL;
                                860                 :                : 
                                861                 :             10 :                                     break;
                                862                 :                : 
 5133 bruce@momjian.us          863                 :UBC           0 :                                 case XPATH_STRING:
                                864                 :              0 :                                     resstr = xmlStrdup(res->stringval);
  364 michael@paquier.xyz       865   [ #  #  #  # ]:UNC           0 :                                     if (resstr == NULL || pg_xml_error_occurred(xmlerrcxt))
                                866                 :              0 :                                         xml_ereport(xmlerrcxt,
                                867                 :                :                                                     ERROR, ERRCODE_OUT_OF_MEMORY,
                                868                 :                :                                                     "could not allocate result");
 5133 bruce@momjian.us          869                 :UBC           0 :                                     break;
                                870                 :                : 
                                871                 :              0 :                                 default:
                                872         [ #  # ]:              0 :                                     elog(NOTICE, "unsupported XQuery result: %d", res->type);
                                873                 :              0 :                                     resstr = xmlStrdup((const xmlChar *) "<unsupported/>");
  364 michael@paquier.xyz       874   [ #  #  #  # ]:UNC           0 :                                     if (resstr == NULL || pg_xml_error_occurred(xmlerrcxt))
                                875                 :              0 :                                         xml_ereport(xmlerrcxt,
                                876                 :                :                                                     ERROR, ERRCODE_OUT_OF_MEMORY,
                                877                 :                :                                                     "could not allocate result");
                                878                 :                :                             }
                                879                 :                : 
                                880                 :                :                             /*
                                881                 :                :                              * Insert this into the appropriate column in the
                                882                 :                :                              * result tuple.
                                883                 :                :                              */
 5133 bruce@momjian.us          884                 :CBC          10 :                             values[j + 1] = (char *) resstr;
   27 michael@paquier.xyz       885                 :GNC          10 :                             resstr = NULL;
                                886                 :                :                         }
                                887                 :                : 
                                888         [ +  - ]:             10 :                         if (res != NULL)
                                889                 :                :                         {
                                890                 :             10 :                             xmlXPathFreeObject(res);
                                891                 :             10 :                             res = NULL;
                                892                 :                :                         }
 5133 bruce@momjian.us          893                 :CBC          10 :                         xmlXPathFreeContext(ctxt);
   27 michael@paquier.xyz       894                 :GNC          10 :                         ctxt = NULL;
                                895                 :                :                     }
                                896                 :                : 
                                897                 :                :                     /* Now add the tuple to the output, if there is one. */
 5133 bruce@momjian.us          898         [ +  + ]:CBC           8 :                     if (had_values)
                                899                 :                :                     {
                                900                 :              3 :                         ret_tuple = BuildTupleFromCStrings(attinmeta, values);
 1575 michael@paquier.xyz       901                 :              3 :                         tuplestore_puttuple(rsinfo->setResult, ret_tuple);
 5133 bruce@momjian.us          902                 :              3 :                         heap_freetuple(ret_tuple);
                                903                 :                :                     }
                                904                 :                : 
                                905                 :                :                     /* BuildTupleFromCStrings() has copied the values. */
   27 michael@paquier.xyz       906         [ +  + ]:GNC          18 :                     for (j = 1; j < rsinfo->setDesc->natts; j++)
                                907                 :                :                     {
                                908         [ +  + ]:             10 :                         if (values[j] != NULL)
                                909                 :                :                         {
                                910                 :              4 :                             xmlFree((xmlChar *) values[j]);
                                911                 :              4 :                             values[j] = NULL;
                                912                 :                :                         }
                                913                 :                :                     }
                                914                 :                : 
 5133 bruce@momjian.us          915                 :CBC           8 :                     rownr++;
                                916         [ +  + ]:              8 :                 } while (had_values);
                                917                 :                :             }
                                918                 :                : 
                                919         [ +  - ]:              5 :             if (doctree != NULL)
                                920                 :              5 :                 xmlFreeDoc(doctree);
                                921                 :              5 :             doctree = NULL;
                                922                 :                : 
                                923         [ +  - ]:              5 :             if (pkey)
                                924                 :              5 :                 pfree(pkey);
                                925         [ +  - ]:              5 :             if (xmldoc)
                                926                 :              5 :                 pfree(xmldoc);
                                927                 :                :         }
                                928                 :                :     }
 5459 tgl@sss.pgh.pa.us         929                 :UBC           0 :     PG_CATCH();
                                930                 :                :     {
   27 michael@paquier.xyz       931         [ #  # ]:UNC           0 :         if (resstr != NULL)
                                932                 :              0 :             xmlFree(resstr);
                                933         [ #  # ]:              0 :         for (j = 1; j < rsinfo->setDesc->natts; j++)
                                934                 :                :         {
                                935         [ #  # ]:              0 :             if (values[j] != NULL)
                                936                 :              0 :                 xmlFree((xmlChar *) values[j]);
                                937                 :                :         }
                                938         [ #  # ]:              0 :         if (res != NULL)
                                939                 :              0 :             xmlXPathFreeObject(res);
                                940         [ #  # ]:              0 :         if (comppath != NULL)
                                941                 :              0 :             xmlXPathFreeCompExpr(comppath);
                                942         [ #  # ]:              0 :         if (ctxt != NULL)
                                943                 :              0 :             xmlXPathFreeContext(ctxt);
 5459 tgl@sss.pgh.pa.us         944         [ #  # ]:UBC           0 :         if (doctree != NULL)
                                945                 :              0 :             xmlFreeDoc(doctree);
                                946                 :                : 
                                947                 :              0 :         pg_xml_done(xmlerrcxt, true);
                                948                 :                : 
                                949                 :              0 :         PG_RE_THROW();
                                950                 :                :     }
 5459 tgl@sss.pgh.pa.us         951         [ -  + ]:CBC           5 :     PG_END_TRY();
                                952                 :                : 
                                953         [ -  + ]:              5 :     if (doctree != NULL)
 5459 tgl@sss.pgh.pa.us         954                 :UBC           0 :         xmlFreeDoc(doctree);
                                955                 :                : 
 5459 tgl@sss.pgh.pa.us         956                 :CBC           5 :     pg_xml_done(xmlerrcxt, false);
                                957                 :                : 
 7975 bruce@momjian.us          958                 :              5 :     SPI_finish();
                                959                 :                : 
                                960                 :                :     /*
                                961                 :                :      * SFRM_Materialize mode expects us to return a NULL Datum. The actual
                                962                 :                :      * tuples are in our tuplestore and passed back through rsinfo->setResult.
                                963                 :                :      * rsinfo->setDesc is set to the tuple description that we actually used
                                964                 :                :      * to build our tuples with, so the caller can verify we did what it was
                                965                 :                :      * expecting.
                                966                 :                :      */
                                967                 :              5 :     return (Datum) 0;
                                968                 :                : }
        

Generated by: LCOV version 2.0-1