LCOV - code coverage report
Current view: top level - contrib/xml2 - xpath.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 62.6 % 425 266
Test Date: 2026-07-23 14:15:53 Functions: 85.7 % 21 18
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 41.7 % 242 101

             Branch data     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                 :             : 
      26                 :           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 *
      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 */
      78                 :          12 :     xmlInitParser();
      79                 :             : 
      80                 :          12 :     return xmlerrcxt;
      81                 :             : }
      82                 :             : 
      83                 :             : 
      84                 :             : /* Encodes special characters (<, >, &, " and \r) as XML entities */
      85                 :             : 
      86                 :           1 : PG_FUNCTION_INFO_V1(xml_encode_special_chars);
      87                 :             : 
      88                 :             : Datum
      89                 :           0 : xml_encode_special_chars(PG_FUNCTION_ARGS)
      90                 :             : {
      91                 :           0 :     text       *tin = PG_GETARG_TEXT_PP(0);
      92                 :           0 :     text       *volatile tout = NULL;
      93                 :           0 :     xmlChar    *volatile tt = NULL;
      94                 :             :     PgXmlErrorContext *xmlerrcxt;
      95                 :             : 
      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)
     115                 :           0 :             xmlFree(tt);
     116                 :             : 
     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)
     124                 :           0 :         xmlFree(tt);
     125                 :             : 
     126                 :           0 :     pg_xml_done(xmlerrcxt, false);
     127                 :             : 
     128                 :           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 *
     143                 :           6 : pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
     144                 :             :                    xmlChar *toptagname,
     145                 :             :                    xmlChar *septagname,
     146                 :             :                    xmlChar *plainsep)
     147                 :             : {
     148                 :           6 :     volatile xmlBufferPtr buf = NULL;
     149                 :           6 :     xmlChar    *volatile result = NULL;
     150                 :           6 :     xmlChar    *volatile str = NULL;
     151                 :             :     PgXmlErrorContext *xmlerrcxt;
     152                 :             : 
     153                 :             :     /* spin up some error handling */
     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))
     161                 :           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
     162                 :             :                         "could not allocate xmlBuffer");
     163                 :             : 
     164   [ +  +  +  + ]:           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                 :             :         {
     172         [ +  + ]:          17 :             for (int i = 0; i < nodeset->nodeNr; i++)
     173                 :             :             {
     174         [ +  + ]:          11 :                 if (plainsep != NULL)
     175                 :             :                 {
     176                 :           4 :                     str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
     177   [ +  -  -  + ]:           4 :                     if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
     178                 :           0 :                         xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
     179                 :             :                                     "could not allocate node text");
     180                 :             : 
     181                 :           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. */
     186         [ +  + ]:           4 :                     if (i < (nodeset->nodeNr) - 1)
     187                 :           2 :                         xmlBufferWriteChar(buf, (char *) plainsep);
     188                 :             :                 }
     189                 :             :                 else
     190                 :             :                 {
     191                 :           7 :                     xmlNodePtr  node = nodeset->nodeTab[i];
     192                 :             : 
     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                 :             :                      */
     204         [ +  + ]:           7 :                     if (node->type == XML_NAMESPACE_DECL)
     205                 :             :                     {
     206                 :           1 :                         str = xmlXPathCastNodeToString(node);
     207   [ +  -  -  + ]:           1 :                         if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
     208                 :           0 :                             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
     209                 :             :                                         "could not allocate node text");
     210                 :           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                 :             : 
     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                 :             : 
     234                 :           6 :         result = xmlStrdup(xmlBufferContent(buf));
     235   [ +  -  -  + ]:           6 :         if (result == NULL || pg_xml_error_occurred(xmlerrcxt))
     236                 :           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
     237                 :             :                         "could not allocate result");
     238                 :             :     }
     239                 :           0 :     PG_CATCH();
     240                 :             :     {
     241         [ #  # ]:           0 :         if (result)
     242                 :           0 :             xmlFree(result);
     243         [ #  # ]:           0 :         if (str)
     244                 :           0 :             xmlFree(str);
     245         [ #  # ]:           0 :         if (buf)
     246                 :           0 :             xmlBufferFree(buf);
     247                 :             : 
     248                 :           0 :         pg_xml_done(xmlerrcxt, true);
     249                 :             : 
     250                 :           0 :         PG_RE_THROW();
     251                 :             :     }
     252         [ -  + ]:           6 :     PG_END_TRY();
     253                 :             : 
     254                 :           6 :     xmlBufferFree(buf);
     255                 :           6 :     pg_xml_done(xmlerrcxt, false);
     256                 :             : 
     257                 :           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                 :             : {
     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                 :             :  */
     277                 :           2 : PG_FUNCTION_INFO_V1(xpath_nodeset);
     278                 :             : 
     279                 :             : Datum
     280                 :           4 : xpath_nodeset(PG_FUNCTION_ARGS)
     281                 :             : {
     282                 :           4 :     text       *document = PG_GETARG_TEXT_PP(0);
     283                 :           4 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
     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;
     287                 :           4 :     text       *volatile xpres = NULL;
     288                 :           4 :     xpath_workspace *volatile workspace = NULL;
     289                 :             :     PgXmlErrorContext *xmlerrcxt;
     290                 :             : 
     291                 :           4 :     xpath = pgxml_texttoxmlchar(xpathsupp);
     292                 :           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                 :             :     }
     299                 :           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                 :             :     }
     307         [ -  + ]:           4 :     PG_END_TRY();
     308                 :             : 
     309                 :           4 :     cleanup_workspace(workspace);
     310                 :           4 :     pg_xml_done(xmlerrcxt, false);
     311                 :             : 
     312                 :           4 :     pfree(xpath);
     313                 :             : 
     314         [ -  + ]:           4 :     if (xpres == NULL)
     315                 :           0 :         PG_RETURN_NULL();
     316                 :           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                 :             : {
     328                 :           2 :     text       *document = PG_GETARG_TEXT_PP(0);
     329                 :           2 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
     330                 :           2 :     xmlChar    *plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(2));
     331                 :             :     xmlChar    *xpath;
     332                 :           2 :     text       *volatile xpres = NULL;
     333                 :           2 :     xpath_workspace *volatile workspace = NULL;
     334                 :             :     PgXmlErrorContext *xmlerrcxt;
     335                 :             : 
     336                 :           2 :     xpath = pgxml_texttoxmlchar(xpathsupp);
     337                 :           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                 :             :     }
     344                 :           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                 :             :     }
     352         [ -  + ]:           2 :     PG_END_TRY();
     353                 :             : 
     354                 :           2 :     cleanup_workspace(workspace);
     355                 :           2 :     pg_xml_done(xmlerrcxt, false);
     356                 :             : 
     357                 :           2 :     pfree(xpath);
     358                 :             : 
     359         [ -  + ]:           2 :     if (xpres == NULL)
     360                 :           0 :         PG_RETURN_NULL();
     361                 :           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                 :             : {
     370                 :           1 :     text       *document = PG_GETARG_TEXT_PP(0);
     371                 :           1 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
     372                 :             :     xmlChar    *xpath;
     373                 :             :     int32       pathsize;
     374                 :           1 :     text       *volatile xpres = NULL;
     375                 :           1 :     xpath_workspace *volatile workspace = NULL;
     376                 :             :     PgXmlErrorContext *xmlerrcxt;
     377                 :             : 
     378                 :           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                 :             : 
     386                 :           1 :     xpath = (xmlChar *) palloc(pathsize + 9);
     387                 :           1 :     memcpy(xpath, "string(", 7);
     388                 :           1 :     memcpy(xpath + 7, VARDATA_ANY(xpathsupp), pathsize);
     389                 :           1 :     xpath[pathsize + 7] = ')';
     390                 :           1 :     xpath[pathsize + 8] = '\0';
     391                 :             : 
     392                 :           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                 :             :     }
     399                 :           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                 :             :     }
     407         [ -  + ]:           1 :     PG_END_TRY();
     408                 :             : 
     409                 :           1 :     cleanup_workspace(workspace);
     410                 :           1 :     pg_xml_done(xmlerrcxt, false);
     411                 :             : 
     412                 :           1 :     pfree(xpath);
     413                 :             : 
     414         [ +  - ]:           1 :     if (xpres == NULL)
     415                 :           1 :         PG_RETURN_NULL();
     416                 :           0 :     PG_RETURN_TEXT_P(xpres);
     417                 :             : }
     418                 :             : 
     419                 :             : 
     420                 :           1 : PG_FUNCTION_INFO_V1(xpath_number);
     421                 :             : 
     422                 :             : Datum
     423                 :           0 : xpath_number(PG_FUNCTION_ARGS)
     424                 :             : {
     425                 :           0 :     text       *document = PG_GETARG_TEXT_PP(0);
     426                 :           0 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
     427                 :             :     xmlChar    *xpath;
     428                 :           0 :     volatile float4 fRes = 0.0;
     429                 :           0 :     volatile bool isNull = false;
     430                 :           0 :     xpath_workspace *volatile workspace = NULL;
     431                 :             :     PgXmlErrorContext *xmlerrcxt;
     432                 :             : 
     433                 :           0 :     xpath = pgxml_texttoxmlchar(xpathsupp);
     434                 :           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))
     460                 :           0 :         PG_RETURN_NULL();
     461                 :             : 
     462                 :           0 :     PG_RETURN_FLOAT4(fRes);
     463                 :             : }
     464                 :             : 
     465                 :             : 
     466                 :           1 : PG_FUNCTION_INFO_V1(xpath_bool);
     467                 :             : 
     468                 :             : Datum
     469                 :           0 : xpath_bool(PG_FUNCTION_ARGS)
     470                 :             : {
     471                 :           0 :     text       *document = PG_GETARG_TEXT_PP(0);
     472                 :           0 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
     473                 :             :     xmlChar    *xpath;
     474                 :           0 :     volatile int bRes = 0;
     475                 :           0 :     xpath_workspace *volatile workspace = NULL;
     476                 :             :     PgXmlErrorContext *xmlerrcxt;
     477                 :             : 
     478                 :           0 :     xpath = pgxml_texttoxmlchar(xpathsupp);
     479                 :           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                 :             : 
     504                 :           0 :     PG_RETURN_BOOL(bRes);
     505                 :             : }
     506                 :             : 
     507                 :             : 
     508                 :             : 
     509                 :             : /* Core function to evaluate XPath query */
     510                 :             : 
     511                 :             : static xpath_workspace *
     512                 :           7 : pgxml_xpath(text *document, xmlChar *xpath, PgXmlErrorContext *xmlerrcxt)
     513                 :             : {
     514                 :           7 :     int32       docsize = VARSIZE_ANY_EXHDR(document);
     515                 :           7 :     xmlXPathCompExprPtr volatile comppath = NULL;
     516                 :           7 :     xpath_workspace *workspace = palloc0_object(xpath_workspace);
     517                 :             : 
     518                 :           7 :     workspace->doctree = NULL;
     519                 :           7 :     workspace->ctxt = NULL;
     520                 :           7 :     workspace->res = NULL;
     521                 :             : 
     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);
     530         [ -  + ]:           6 :             if (workspace->ctxt == NULL)
     531                 :           0 :                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
     532                 :             :                             "could not allocate XPath context");
     533                 :             : 
     534                 :           6 :             workspace->ctxt->node = xmlDocGetRootElement(workspace->doctree);
     535                 :             : 
     536                 :             :             /* compile the path */
     537                 :           6 :             comppath = xmlXPathCtxtCompile(workspace->ctxt, xpath);
     538   [ +  -  -  + ]:           6 :             if (comppath == NULL || pg_xml_error_occurred(xmlerrcxt))
     539                 :           0 :                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
     540                 :             :                             "XPath Syntax Error");
     541                 :             : 
     542                 :             :             /* Now evaluate the path expression. */
     543                 :           6 :             workspace->res = xmlXPathCompiledEval(comppath, workspace->ctxt);
     544                 :             : 
     545                 :           6 :             xmlXPathFreeCompExpr(comppath);
     546                 :           6 :             comppath = NULL;
     547                 :             :         }
     548                 :             :     }
     549                 :           0 :     PG_CATCH();
     550                 :             :     {
     551         [ #  # ]:           0 :         if (comppath != NULL)
     552                 :           0 :             xmlXPathFreeCompExpr(comppath);
     553                 :           0 :         cleanup_workspace(workspace);
     554                 :             : 
     555                 :           0 :         PG_RE_THROW();
     556                 :             :     }
     557         [ -  + ]:           7 :     PG_END_TRY();
     558                 :             : 
     559                 :           7 :     return workspace;
     560                 :             : }
     561                 :             : 
     562                 :             : /* Clean up after processing the result of pgxml_xpath() */
     563                 :             : static void
     564                 :           7 : cleanup_workspace(xpath_workspace *workspace)
     565                 :             : {
     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 *
     578                 :           7 : pgxml_result_to_text(xmlXPathObjectPtr res,
     579                 :             :                      xmlChar *toptag,
     580                 :             :                      xmlChar *septag,
     581                 :             :                      xmlChar *plainsep)
     582                 :             : {
     583                 :           7 :     xmlChar    *volatile xpresstr = NULL;
     584                 :           7 :     text       *volatile xpres = NULL;
     585                 :             :     PgXmlErrorContext *xmlerrcxt;
     586                 :             : 
     587         [ +  + ]:           7 :     if (res == NULL)
     588                 :           1 :         return NULL;
     589                 :             : 
     590                 :             :     /* spin some error handling */
     591                 :           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                 :             : 
     603                 :           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 */
     619                 :           6 :         xpres = cstring_to_text((char *) xpresstr);
     620                 :             :     }
     621                 :           0 :     PG_CATCH();
     622                 :             :     {
     623         [ #  # ]:           0 :         if (xpresstr != NULL)
     624                 :           0 :             xmlFree(xpresstr);
     625                 :             : 
     626                 :           0 :         pg_xml_done(xmlerrcxt, true);
     627                 :             : 
     628                 :           0 :         PG_RE_THROW();
     629                 :             :     }
     630         [ -  + ]:           6 :     PG_END_TRY();
     631                 :             : 
     632                 :             :     /* Free various storage */
     633                 :           6 :     xmlFree(xpresstr);
     634                 :             : 
     635                 :           6 :     pg_xml_done(xmlerrcxt, false);
     636                 :             : 
     637                 :           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
     647                 :           5 : xpath_table(PG_FUNCTION_ARGS)
     648                 :             : {
     649                 :             :     /* Function parameters */
     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                 :             : 
     662                 :           5 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     663                 :             :     AttInMetadata *attinmeta;
     664                 :             : 
     665                 :             :     char      **values;
     666                 :             :     xmlChar   **xpaths;
     667                 :             :     char       *pos;
     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;
     679                 :           5 :     volatile xmlDocPtr doctree = NULL;
     680                 :           5 :     xmlXPathContextPtr volatile ctxt = NULL;
     681                 :           5 :     xmlXPathObjectPtr volatile res = NULL;
     682                 :           5 :     xmlXPathCompExprPtr volatile comppath = NULL;
     683                 :           5 :     xmlChar    *volatile resstr = NULL;
     684                 :             : 
     685                 :           5 :     InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
     686                 :             : 
     687                 :             :     /* must have at least one output column (for the pkey) */
     688         [ -  + ]:           5 :     if (rsinfo->setDesc->natts < 1)
     689         [ #  # ]:           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                 :             : 
     701                 :           5 :     attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
     702                 :             : 
     703                 :           5 :     values = (char **) palloc0(rsinfo->setDesc->natts * sizeof(char *));
     704                 :           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                 :             :      */
     711                 :           5 :     numpaths = 0;
     712                 :           5 :     pos = xpathset;
     713         [ +  + ]:           7 :     while (numpaths < (rsinfo->setDesc->natts - 1))
     714                 :             :     {
     715                 :           5 :         xpaths[numpaths++] = (xmlChar *) pos;
     716                 :           5 :         pos = strstr(pos, pathsep);
     717         [ +  + ]:           5 :         if (pos != NULL)
     718                 :             :         {
     719                 :           2 :             *pos = '\0';
     720                 :           2 :             pos++;
     721                 :             :         }
     722                 :             :         else
     723                 :           3 :             break;
     724                 :             :     }
     725                 :             : 
     726                 :             :     /* Now build query */
     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                 :             : 
     736                 :           5 :     SPI_connect();
     737                 :             : 
     738         [ -  + ]:           5 :     if ((ret = SPI_exec(query_buf.data, 0)) != SPI_OK_SELECT)
     739         [ #  # ]:           0 :         elog(ERROR, "xpath_table: SPI execution failed for query %s",
     740                 :             :              query_buf.data);
     741                 :             : 
     742                 :           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                 :             :     {
     753         [ #  # ]:           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                 :             :      */
     762                 :           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                 :             : 
     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                 :             :              */
     785         [ +  + ]:          15 :             for (j = 0; j < rsinfo->setDesc->natts; j++)
     786                 :          10 :                 values[j] = NULL;
     787                 :             : 
     788                 :             :             /* Insert primary key */
     789                 :           5 :             values[0] = pkey;
     790                 :             : 
     791                 :             :             /* Parse the document */
     792         [ +  - ]:           5 :             if (xmldoc)
     793                 :           5 :                 doctree = xmlReadMemory(xmldoc, strlen(xmldoc),
     794                 :             :                                         NULL, NULL,
     795                 :             :                                         XML_PARSE_NOENT);
     796                 :             :             else                /* treat NULL as not well-formed */
     797                 :           0 :                 doctree = NULL;
     798                 :             : 
     799         [ -  + ]:           5 :             if (doctree == NULL)
     800                 :             :             {
     801                 :             :                 /* not well-formed, so output all-NULL tuple */
     802                 :           0 :                 ret_tuple = BuildTupleFromCStrings(attinmeta, values);
     803                 :           0 :                 tuplestore_puttuple(rsinfo->setResult, ret_tuple);
     804                 :           0 :                 heap_freetuple(ret_tuple);
     805                 :             :             }
     806                 :             :             else
     807                 :             :             {
     808                 :             :                 /* New loop here - we have to deal with nodeset results */
     809                 :           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                 :             :                     {
     817                 :          10 :                         ctxt = NULL;
     818                 :          10 :                         res = NULL;
     819                 :          10 :                         comppath = NULL;
     820                 :          10 :                         resstr = NULL;
     821                 :             : 
     822                 :          10 :                         ctxt = xmlXPathNewContext(doctree);
     823   [ +  -  -  + ]:          10 :                         if (ctxt == NULL || pg_xml_error_occurred(xmlerrcxt))
     824                 :           0 :                             xml_ereport(xmlerrcxt,
     825                 :             :                                         ERROR, ERRCODE_OUT_OF_MEMORY,
     826                 :             :                                         "could not allocate XPath context");
     827                 :             : 
     828                 :          10 :                         ctxt->node = xmlDocGetRootElement(doctree);
     829                 :             : 
     830                 :             :                         /* compile the path */
     831                 :          10 :                         comppath = xmlXPathCtxtCompile(ctxt, xpaths[j]);
     832   [ +  -  -  + ]:          10 :                         if (comppath == NULL || pg_xml_error_occurred(xmlerrcxt))
     833                 :           0 :                             xml_ereport(xmlerrcxt, ERROR,
     834                 :             :                                         ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
     835                 :             :                                         "XPath Syntax Error");
     836                 :             : 
     837                 :             :                         /* Now evaluate the path expression. */
     838                 :          10 :                         res = xmlXPathCompiledEval(comppath, ctxt);
     839                 :          10 :                         xmlXPathFreeCompExpr(comppath);
     840                 :          10 :                         comppath = NULL;
     841                 :             : 
     842         [ +  - ]:          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]);
     852   [ +  -  -  + ]:           4 :                                         if (resstr == NULL || pg_xml_error_occurred(xmlerrcxt))
     853                 :           0 :                                             xml_ereport(xmlerrcxt,
     854                 :             :                                                         ERROR, ERRCODE_OUT_OF_MEMORY,
     855                 :             :                                                         "could not allocate result");
     856                 :           4 :                                         had_values = true;
     857                 :             :                                     }
     858                 :             :                                     else
     859                 :           6 :                                         resstr = NULL;
     860                 :             : 
     861                 :          10 :                                     break;
     862                 :             : 
     863                 :           0 :                                 case XPATH_STRING:
     864                 :           0 :                                     resstr = xmlStrdup(res->stringval);
     865   [ #  #  #  # ]:           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");
     869                 :           0 :                                     break;
     870                 :             : 
     871                 :           0 :                                 default:
     872         [ #  # ]:           0 :                                     elog(NOTICE, "unsupported XQuery result: %d", res->type);
     873                 :           0 :                                     resstr = xmlStrdup((const xmlChar *) "<unsupported/>");
     874   [ #  #  #  # ]:           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                 :             :                              */
     884                 :          10 :                             values[j + 1] = (char *) resstr;
     885                 :          10 :                             resstr = NULL;
     886                 :             :                         }
     887                 :             : 
     888         [ +  - ]:          10 :                         if (res != NULL)
     889                 :             :                         {
     890                 :          10 :                             xmlXPathFreeObject(res);
     891                 :          10 :                             res = NULL;
     892                 :             :                         }
     893                 :          10 :                         xmlXPathFreeContext(ctxt);
     894                 :          10 :                         ctxt = NULL;
     895                 :             :                     }
     896                 :             : 
     897                 :             :                     /* Now add the tuple to the output, if there is one. */
     898         [ +  + ]:           8 :                     if (had_values)
     899                 :             :                     {
     900                 :           3 :                         ret_tuple = BuildTupleFromCStrings(attinmeta, values);
     901                 :           3 :                         tuplestore_puttuple(rsinfo->setResult, ret_tuple);
     902                 :           3 :                         heap_freetuple(ret_tuple);
     903                 :             :                     }
     904                 :             : 
     905                 :             :                     /* BuildTupleFromCStrings() has copied the values. */
     906         [ +  + ]:          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                 :             : 
     915                 :           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                 :             :     }
     929                 :           0 :     PG_CATCH();
     930                 :             :     {
     931         [ #  # ]:           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);
     944         [ #  # ]:           0 :         if (doctree != NULL)
     945                 :           0 :             xmlFreeDoc(doctree);
     946                 :             : 
     947                 :           0 :         pg_xml_done(xmlerrcxt, true);
     948                 :             : 
     949                 :           0 :         PG_RE_THROW();
     950                 :             :     }
     951         [ -  + ]:           5 :     PG_END_TRY();
     952                 :             : 
     953         [ -  + ]:           5 :     if (doctree != NULL)
     954                 :           0 :         xmlFreeDoc(doctree);
     955                 :             : 
     956                 :           5 :     pg_xml_done(xmlerrcxt, false);
     957                 :             : 
     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