LCOV - differential code coverage report
Current view: top level - contrib/xml2 - xpath.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 49.3 % 274 135 139 135
Current Date: 2023-04-08 17:13:01 Functions: 66.7 % 21 14 7 14
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (120,180] days: 100.0 % 1 1 1
Legend: Lines: hit not hit (240..) days: 49.1 % 273 134 139 134
Function coverage date bins:
(240..) days: 66.7 % 21 14 7 14

 Age         Owner                  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 "miscadmin.h"
                                 15                 : #include "utils/builtins.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                 : 
 6054 tgl                        26 CBC           1 : PG_MODULE_MAGIC;
                                 27                 : 
                                 28                 : /* exported for use by xslt_proc.c */
                                 29                 : 
                                 30                 : PgXmlErrorContext *pgxml_parser_init(PgXmlStrictness strictness);
                                 31                 : 
                                 32                 : /* workspace for pgxml_xpath() */
                                 33                 : 
                                 34                 : typedef struct
                                 35                 : {
                                 36                 :     xmlDocPtr   doctree;
                                 37                 :     xmlXPathContextPtr ctxt;
                                 38                 :     xmlXPathObjectPtr res;
                                 39                 : } xpath_workspace;
                                 40                 : 
                                 41                 : /* local declarations */
                                 42                 : 
                                 43                 : static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
                                 44                 :                                    xmlChar *toptagname, xmlChar *septagname,
                                 45                 :                                    xmlChar *plainsep);
                                 46                 : 
                                 47                 : static text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag,
                                 48                 :                                   xmlChar *septag, xmlChar *plainsep);
                                 49                 : 
                                 50                 : static xmlChar *pgxml_texttoxmlchar(text *textstring);
                                 51                 : 
                                 52                 : static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath,
                                 53                 :                                      xpath_workspace *workspace);
                                 54                 : 
                                 55                 : static void cleanup_workspace(xpath_workspace *workspace);
                                 56                 : 
                                 57                 : 
                                 58                 : /*
                                 59                 :  * Initialize for xml parsing.
                                 60                 :  *
                                 61                 :  * As with the underlying pg_xml_init function, calls to this MUST be followed
                                 62                 :  * by a PG_TRY block that guarantees that pg_xml_done is called.
                                 63                 :  */
                                 64                 : PgXmlErrorContext *
 4281                            65              11 : pgxml_parser_init(PgXmlStrictness strictness)
                                 66                 : {
                                 67                 :     PgXmlErrorContext *xmlerrcxt;
                                 68                 : 
                                 69                 :     /* Set up error handling (we share the core's error handler) */
                                 70              11 :     xmlerrcxt = pg_xml_init(strictness);
                                 71                 : 
                                 72                 :     /* Note: we're assuming an elog cannot be thrown by the following calls */
                                 73                 : 
                                 74                 :     /* Initialize libxml */
 4788                            75              11 :     xmlInitParser();
                                 76                 : 
 6974 bruce                      77              11 :     xmlSubstituteEntitiesDefault(1);
                                 78              11 :     xmlLoadExtDtdDefaultValue = 1;
                                 79                 : 
 4281 tgl                        80              11 :     return xmlerrcxt;
                                 81                 : }
                                 82                 : 
                                 83                 : 
                                 84                 : /* Encodes special characters (<, >, &, " and \r) as XML entities */
                                 85                 : 
 6702 bruce                      86               1 : PG_FUNCTION_INFO_V1(xml_encode_special_chars);
                                 87                 : 
                                 88                 : Datum
 6702 bruce                      89 UBC           0 : xml_encode_special_chars(PG_FUNCTION_ARGS)
                                 90                 : {
 2219 noah                       91               0 :     text       *tin = PG_GETARG_TEXT_PP(0);
                                 92                 :     text       *tout;
                                 93                 :     xmlChar    *ts,
                                 94                 :                *tt;
                                 95                 : 
 6702 bruce                      96               0 :     ts = pgxml_texttoxmlchar(tin);
                                 97                 : 
                                 98               0 :     tt = xmlEncodeSpecialChars(NULL, ts);
                                 99                 : 
                                100               0 :     pfree(ts);
                                101                 : 
 5453 tgl                       102               0 :     tout = cstring_to_text((char *) tt);
                                103                 : 
 6702 bruce                     104               0 :     xmlFree(tt);
                                105                 : 
                                106               0 :     PG_RETURN_TEXT_P(tout);
                                107                 : }
                                108                 : 
                                109                 : /*
                                110                 :  * Function translates a nodeset into a text representation
                                111                 :  *
                                112                 :  * iterates over each node in the set and calls xmlNodeDump to write it to
                                113                 :  * an xmlBuffer -from which an xmlChar * string is returned.
                                114                 :  *
                                115                 :  * each representation is surrounded by <tagname> ... </tagname>
                                116                 :  *
                                117                 :  * plainsep is an ordinary (not tag) separator - if used, then nodes are
                                118                 :  * cast to string as output method
                                119                 :  */
                                120                 : static xmlChar *
 6974                           121               0 : pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
                                122                 :                    xmlChar *toptagname,
                                123                 :                    xmlChar *septagname,
                                124                 :                    xmlChar *plainsep)
                                125                 : {
                                126                 :     xmlBufferPtr buf;
                                127                 :     xmlChar    *result;
                                128                 :     int         i;
                                129                 : 
                                130               0 :     buf = xmlBufferCreate();
                                131                 : 
                                132               0 :     if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
                                133                 :     {
                                134               0 :         xmlBufferWriteChar(buf, "<");
                                135               0 :         xmlBufferWriteCHAR(buf, toptagname);
                                136               0 :         xmlBufferWriteChar(buf, ">");
                                137                 :     }
                                138               0 :     if (nodeset != NULL)
                                139                 :     {
                                140               0 :         for (i = 0; i < nodeset->nodeNr; i++)
                                141                 :         {
 6797                           142               0 :             if (plainsep != NULL)
                                143                 :             {
                                144               0 :                 xmlBufferWriteCHAR(buf,
 2118 tgl                       145               0 :                                    xmlXPathCastNodeToString(nodeset->nodeTab[i]));
                                146                 : 
                                147                 :                 /* If this isn't the last entry, write the plain sep. */
 6797 bruce                     148               0 :                 if (i < (nodeset->nodeNr) - 1)
 5747 tgl                       149               0 :                     xmlBufferWriteChar(buf, (char *) plainsep);
                                150                 :             }
                                151                 :             else
                                152                 :             {
 6797 bruce                     153               0 :                 if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
                                154                 :                 {
                                155               0 :                     xmlBufferWriteChar(buf, "<");
                                156               0 :                     xmlBufferWriteCHAR(buf, septagname);
                                157               0 :                     xmlBufferWriteChar(buf, ">");
                                158                 :                 }
                                159               0 :                 xmlNodeDump(buf,
                                160               0 :                             nodeset->nodeTab[i]->doc,
                                161               0 :                             nodeset->nodeTab[i],
                                162                 :                             1, 0);
                                163                 : 
                                164               0 :                 if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
                                165                 :                 {
                                166               0 :                     xmlBufferWriteChar(buf, "</");
                                167               0 :                     xmlBufferWriteCHAR(buf, septagname);
                                168               0 :                     xmlBufferWriteChar(buf, ">");
                                169                 :                 }
                                170                 :             }
                                171                 :         }
                                172                 :     }
                                173                 : 
 6974                           174               0 :     if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
                                175                 :     {
                                176               0 :         xmlBufferWriteChar(buf, "</");
                                177               0 :         xmlBufferWriteCHAR(buf, toptagname);
                                178               0 :         xmlBufferWriteChar(buf, ">");
                                179                 :     }
                                180               0 :     result = xmlStrdup(buf->content);
                                181               0 :     xmlBufferFree(buf);
                                182               0 :     return result;
                                183                 : }
                                184                 : 
                                185                 : 
                                186                 : /* Translate a PostgreSQL "varlena" -i.e. a variable length parameter
                                187                 :  * into the libxml2 representation
                                188                 :  */
                                189                 : static xmlChar *
                                190               0 : pgxml_texttoxmlchar(text *textstring)
                                191                 : {
 5453 tgl                       192               0 :     return (xmlChar *) text_to_cstring(textstring);
                                193                 : }
                                194                 : 
                                195                 : /* Publicly visible XPath functions */
                                196                 : 
                                197                 : /*
                                198                 :  * This is a "raw" xpath function. Check that it returns child elements
                                199                 :  * properly
                                200                 :  */
 6974 bruce                     201 CBC           1 : PG_FUNCTION_INFO_V1(xpath_nodeset);
                                202                 : 
                                203                 : Datum
 6974 bruce                     204 UBC           0 : xpath_nodeset(PG_FUNCTION_ARGS)
                                205                 : {
 2219 noah                      206               0 :     text       *document = PG_GETARG_TEXT_PP(0);
 2118 tgl                       207               0 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
 2219 noah                      208               0 :     xmlChar    *toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(2));
                                209               0 :     xmlChar    *septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(3));
                                210                 :     xmlChar    *xpath;
                                211                 :     text       *xpres;
                                212                 :     xmlXPathObjectPtr res;
                                213                 :     xpath_workspace workspace;
                                214                 : 
 4517 tgl                       215               0 :     xpath = pgxml_texttoxmlchar(xpathsupp);
                                216                 : 
                                217               0 :     res = pgxml_xpath(document, xpath, &workspace);
                                218                 : 
                                219               0 :     xpres = pgxml_result_to_text(res, toptag, septag, NULL);
                                220                 : 
                                221               0 :     cleanup_workspace(&workspace);
                                222                 : 
 6752 neilc                     223               0 :     pfree(xpath);
                                224                 : 
 6797 bruce                     225               0 :     if (xpres == NULL)
                                226               0 :         PG_RETURN_NULL();
 6974                           227               0 :     PG_RETURN_TEXT_P(xpres);
                                228                 : }
                                229                 : 
                                230                 : /*
                                231                 :  * The following function is almost identical, but returns the elements in
                                232                 :  * a list.
                                233                 :  */
 6974 bruce                     234 CBC           1 : PG_FUNCTION_INFO_V1(xpath_list);
                                235                 : 
                                236                 : Datum
 6974 bruce                     237 UBC           0 : xpath_list(PG_FUNCTION_ARGS)
                                238                 : {
 2219 noah                      239               0 :     text       *document = PG_GETARG_TEXT_PP(0);
 2118 tgl                       240               0 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
 2219 noah                      241               0 :     xmlChar    *plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(2));
                                242                 :     xmlChar    *xpath;
                                243                 :     text       *xpres;
                                244                 :     xmlXPathObjectPtr res;
                                245                 :     xpath_workspace workspace;
                                246                 : 
 4517 tgl                       247               0 :     xpath = pgxml_texttoxmlchar(xpathsupp);
                                248                 : 
                                249               0 :     res = pgxml_xpath(document, xpath, &workspace);
                                250                 : 
                                251               0 :     xpres = pgxml_result_to_text(res, NULL, NULL, plainsep);
                                252                 : 
                                253               0 :     cleanup_workspace(&workspace);
                                254                 : 
 6752 neilc                     255               0 :     pfree(xpath);
                                256                 : 
 6797 bruce                     257               0 :     if (xpres == NULL)
                                258               0 :         PG_RETURN_NULL();
 6974                           259               0 :     PG_RETURN_TEXT_P(xpres);
                                260                 : }
                                261                 : 
                                262                 : 
 6974 bruce                     263 CBC           2 : PG_FUNCTION_INFO_V1(xpath_string);
                                264                 : 
                                265                 : Datum
                                266               1 : xpath_string(PG_FUNCTION_ARGS)
                                267                 : {
 2219 noah                      268               1 :     text       *document = PG_GETARG_TEXT_PP(0);
 2118 tgl                       269               1 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
                                270                 :     xmlChar    *xpath;
                                271                 :     int32       pathsize;
                                272                 :     text       *xpres;
                                273                 :     xmlXPathObjectPtr res;
                                274                 :     xpath_workspace workspace;
                                275                 : 
 2219 noah                      276               1 :     pathsize = VARSIZE_ANY_EXHDR(xpathsupp);
                                277                 : 
                                278                 :     /*
                                279                 :      * We encapsulate the supplied path with "string()" = 8 chars + 1 for NUL
                                280                 :      * at end
                                281                 :      */
                                282                 :     /* We could try casting to string using the libxml function? */
                                283                 : 
 6797 bruce                     284               1 :     xpath = (xmlChar *) palloc(pathsize + 9);
 2997 tgl                       285               1 :     memcpy((char *) xpath, "string(", 7);
 2219 noah                      286               1 :     memcpy((char *) (xpath + 7), VARDATA_ANY(xpathsupp), pathsize);
 6797 bruce                     287               1 :     xpath[pathsize + 7] = ')';
                                288               1 :     xpath[pathsize + 8] = '\0';
                                289                 : 
 4517 tgl                       290               1 :     res = pgxml_xpath(document, xpath, &workspace);
                                291                 : 
                                292               1 :     xpres = pgxml_result_to_text(res, NULL, NULL, NULL);
                                293                 : 
                                294               1 :     cleanup_workspace(&workspace);
                                295                 : 
 6752 neilc                     296               1 :     pfree(xpath);
                                297                 : 
 6797 bruce                     298               1 :     if (xpres == NULL)
                                299               1 :         PG_RETURN_NULL();
 6974 bruce                     300 UBC           0 :     PG_RETURN_TEXT_P(xpres);
                                301                 : }
                                302                 : 
                                303                 : 
 6974 bruce                     304 CBC           1 : PG_FUNCTION_INFO_V1(xpath_number);
                                305                 : 
                                306                 : Datum
 6974 bruce                     307 UBC           0 : xpath_number(PG_FUNCTION_ARGS)
                                308                 : {
 2219 noah                      309               0 :     text       *document = PG_GETARG_TEXT_PP(0);
 2118 tgl                       310               0 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
                                311                 :     xmlChar    *xpath;
                                312                 :     float4      fRes;
                                313                 :     xmlXPathObjectPtr res;
                                314                 :     xpath_workspace workspace;
                                315                 : 
 6974 bruce                     316               0 :     xpath = pgxml_texttoxmlchar(xpathsupp);
                                317                 : 
 4517 tgl                       318               0 :     res = pgxml_xpath(document, xpath, &workspace);
                                319                 : 
 6752 neilc                     320               0 :     pfree(xpath);
                                321                 : 
 6974 bruce                     322               0 :     if (res == NULL)
 6797                           323               0 :         PG_RETURN_NULL();
                                324                 : 
 6974                           325               0 :     fRes = xmlXPathCastToNumber(res);
                                326                 : 
 4517 tgl                       327               0 :     cleanup_workspace(&workspace);
                                328                 : 
 6974 bruce                     329               0 :     if (xmlXPathIsNaN(fRes))
 6797                           330               0 :         PG_RETURN_NULL();
                                331                 : 
 6974                           332               0 :     PG_RETURN_FLOAT4(fRes);
                                333                 : }
                                334                 : 
                                335                 : 
 6974 bruce                     336 CBC           1 : PG_FUNCTION_INFO_V1(xpath_bool);
                                337                 : 
                                338                 : Datum
 6974 bruce                     339 UBC           0 : xpath_bool(PG_FUNCTION_ARGS)
                                340                 : {
 2219 noah                      341               0 :     text       *document = PG_GETARG_TEXT_PP(0);
 2118 tgl                       342               0 :     text       *xpathsupp = PG_GETARG_TEXT_PP(1);   /* XPath expression */
                                343                 :     xmlChar    *xpath;
                                344                 :     int         bRes;
                                345                 :     xmlXPathObjectPtr res;
                                346                 :     xpath_workspace workspace;
                                347                 : 
 6974 bruce                     348               0 :     xpath = pgxml_texttoxmlchar(xpathsupp);
                                349                 : 
 4517 tgl                       350               0 :     res = pgxml_xpath(document, xpath, &workspace);
                                351                 : 
 6752 neilc                     352               0 :     pfree(xpath);
                                353                 : 
 6974 bruce                     354               0 :     if (res == NULL)
 6797                           355               0 :         PG_RETURN_BOOL(false);
                                356                 : 
 6974                           357               0 :     bRes = xmlXPathCastToBoolean(res);
                                358                 : 
 4517 tgl                       359               0 :     cleanup_workspace(&workspace);
                                360                 : 
 6974 bruce                     361               0 :     PG_RETURN_BOOL(bRes);
                                362                 : }
                                363                 : 
                                364                 : 
                                365                 : 
                                366                 : /* Core function to evaluate XPath query */
                                367                 : 
                                368                 : static xmlXPathObjectPtr
 4517 tgl                       369 CBC           1 : pgxml_xpath(text *document, xmlChar *xpath, xpath_workspace *workspace)
                                370                 : {
 2219 noah                      371               1 :     int32       docsize = VARSIZE_ANY_EXHDR(document);
                                372                 :     PgXmlErrorContext *xmlerrcxt;
                                373                 :     xmlXPathCompExprPtr comppath;
                                374                 : 
 4517 tgl                       375               1 :     workspace->doctree = NULL;
                                376               1 :     workspace->ctxt = NULL;
                                377               1 :     workspace->res = NULL;
                                378                 : 
 4281                           379               1 :     xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
                                380                 : 
                                381               1 :     PG_TRY();
                                382                 :     {
 2219 noah                      383               1 :         workspace->doctree = xmlParseMemory((char *) VARDATA_ANY(document),
                                384                 :                                             docsize);
 4281 tgl                       385               1 :         if (workspace->doctree != NULL)
                                386                 :         {
 4281 tgl                       387 UBC           0 :             workspace->ctxt = xmlXPathNewContext(workspace->doctree);
                                388               0 :             workspace->ctxt->node = xmlDocGetRootElement(workspace->doctree);
                                389                 : 
                                390                 :             /* compile the path */
                                391               0 :             comppath = xmlXPathCompile(xpath);
                                392               0 :             if (comppath == NULL)
                                393               0 :                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
                                394                 :                             "XPath Syntax Error");
                                395                 : 
                                396                 :             /* Now evaluate the path expression. */
                                397               0 :             workspace->res = xmlXPathCompiledEval(comppath, workspace->ctxt);
                                398                 : 
                                399               0 :             xmlXPathFreeCompExpr(comppath);
                                400                 :         }
                                401                 :     }
                                402               0 :     PG_CATCH();
                                403                 :     {
 4517                           404               0 :         cleanup_workspace(workspace);
                                405                 : 
 4281                           406               0 :         pg_xml_done(xmlerrcxt, true);
                                407                 : 
                                408               0 :         PG_RE_THROW();
                                409                 :     }
 4281 tgl                       410 CBC           1 :     PG_END_TRY();
                                411                 : 
                                412               1 :     if (workspace->res == NULL)
 4517                           413               1 :         cleanup_workspace(workspace);
                                414                 : 
 4281                           415               1 :     pg_xml_done(xmlerrcxt, false);
                                416                 : 
                                417               1 :     return workspace->res;
                                418                 : }
                                419                 : 
                                420                 : /* Clean up after processing the result of pgxml_xpath() */
                                421                 : static void
 4517                           422               2 : cleanup_workspace(xpath_workspace *workspace)
                                423                 : {
                                424               2 :     if (workspace->res)
 4517 tgl                       425 UBC           0 :         xmlXPathFreeObject(workspace->res);
 4517 tgl                       426 CBC           2 :     workspace->res = NULL;
                                427               2 :     if (workspace->ctxt)
 4517 tgl                       428 UBC           0 :         xmlXPathFreeContext(workspace->ctxt);
 4517 tgl                       429 CBC           2 :     workspace->ctxt = NULL;
                                430               2 :     if (workspace->doctree)
 4517 tgl                       431 UBC           0 :         xmlFreeDoc(workspace->doctree);
 4517 tgl                       432 CBC           2 :     workspace->doctree = NULL;
                                433               2 : }
                                434                 : 
                                435                 : static text *
 6797 bruce                     436               1 : pgxml_result_to_text(xmlXPathObjectPtr res,
                                437                 :                      xmlChar *toptag,
                                438                 :                      xmlChar *septag,
                                439                 :                      xmlChar *plainsep)
                                440                 : {
                                441                 :     xmlChar    *xpresstr;
                                442                 :     text       *xpres;
                                443                 : 
                                444               1 :     if (res == NULL)
                                445               1 :         return NULL;
                                446                 : 
 6974 bruce                     447 UBC           0 :     switch (res->type)
                                448                 :     {
                                449               0 :         case XPATH_NODESET:
                                450               0 :             xpresstr = pgxmlNodeSetToText(res->nodesetval,
                                451                 :                                           toptag,
                                452                 :                                           septag, plainsep);
                                453               0 :             break;
                                454                 : 
                                455               0 :         case XPATH_STRING:
                                456               0 :             xpresstr = xmlStrdup(res->stringval);
                                457               0 :             break;
                                458                 : 
                                459               0 :         default:
 6248 neilc                     460               0 :             elog(NOTICE, "unsupported XQuery result: %d", res->type);
 5747 tgl                       461               0 :             xpresstr = xmlStrdup((const xmlChar *) "<unsupported/>");
                                462                 :     }
                                463                 : 
                                464                 :     /* Now convert this result back to text */
 5453                           465               0 :     xpres = cstring_to_text((char *) xpresstr);
                                466                 : 
                                467                 :     /* Free various storage */
 6974 bruce                     468               0 :     xmlFree(xpresstr);
                                469                 : 
                                470               0 :     return xpres;
                                471                 : }
                                472                 : 
                                473                 : /*
                                474                 :  * xpath_table is a table function. It needs some tidying (as do the
                                475                 :  * other functions here!
                                476                 :  */
 6974 bruce                     477 CBC           2 : PG_FUNCTION_INFO_V1(xpath_table);
                                478                 : 
                                479                 : Datum
 6797                           480               5 : xpath_table(PG_FUNCTION_ARGS)
                                481                 : {
                                482                 :     /* Function parameters */
 4788 tgl                       483               5 :     char       *pkeyfield = text_to_cstring(PG_GETARG_TEXT_PP(0));
                                484               5 :     char       *xmlfield = text_to_cstring(PG_GETARG_TEXT_PP(1));
                                485               5 :     char       *relname = text_to_cstring(PG_GETARG_TEXT_PP(2));
                                486               5 :     char       *xpathset = text_to_cstring(PG_GETARG_TEXT_PP(3));
                                487               5 :     char       *condition = text_to_cstring(PG_GETARG_TEXT_PP(4));
                                488                 : 
                                489                 :     /* SPI (input tuple) support */
                                490                 :     SPITupleTable *tuptable;
                                491                 :     HeapTuple   spi_tuple;
                                492                 :     TupleDesc   spi_tupdesc;
                                493                 : 
                                494                 : 
 6797 bruce                     495               5 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
                                496                 :     AttInMetadata *attinmeta;
                                497                 : 
                                498                 :     char      **values;
                                499                 :     xmlChar   **xpaths;
                                500                 :     char       *pos;
 5747 tgl                       501               5 :     const char *pathsep = "|";
                                502                 : 
                                503                 :     int         numpaths;
                                504                 :     int         ret;
                                505                 :     uint64      proc;
                                506                 :     int         j;
                                507                 :     int         rownr;          /* For issuing multiple rows from one original
                                508                 :                                  * document */
                                509                 :     bool        had_values;     /* To determine end of nodeset results */
                                510                 :     StringInfoData query_buf;
                                511                 :     PgXmlErrorContext *xmlerrcxt;
 4281                           512               5 :     volatile xmlDocPtr doctree = NULL;
                                513                 : 
  173 michael                   514               5 :     InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
                                515                 : 
                                516                 :     /* must have at least one output column (for the pkey) */
  397                           517               5 :     if (rsinfo->setDesc->natts < 1)
 4788 tgl                       518 UBC           0 :         ereport(ERROR,
                                519                 :                 (errcode(ERRCODE_SYNTAX_ERROR),
                                520                 :                  errmsg("xpath_table must have at least one output column")));
                                521                 : 
                                522                 :     /*
                                523                 :      * At the moment we assume that the returned attributes make sense for the
                                524                 :      * XPath specified (i.e. we trust the caller). It's not fatal if they get
                                525                 :      * it wrong - the input function for the column type will raise an error
                                526                 :      * if the path result can't be converted into the correct binary
                                527                 :      * representation.
                                528                 :      */
                                529                 : 
  397 michael                   530 CBC           5 :     attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
                                531                 : 
                                532               5 :     values = (char **) palloc(rsinfo->setDesc->natts * sizeof(char *));
                                533               5 :     xpaths = (xmlChar **) palloc(rsinfo->setDesc->natts * sizeof(xmlChar *));
                                534                 : 
                                535                 :     /*
                                536                 :      * Split XPaths. xpathset is a writable CString.
                                537                 :      *
                                538                 :      * Note that we stop splitting once we've done all needed for tupdesc
                                539                 :      */
 6797 bruce                     540               5 :     numpaths = 0;
                                541               5 :     pos = xpathset;
  397 michael                   542               7 :     while (numpaths < (rsinfo->setDesc->natts - 1))
                                543                 :     {
 4788 tgl                       544               5 :         xpaths[numpaths++] = (xmlChar *) pos;
 6797 bruce                     545               5 :         pos = strstr(pos, pathsep);
                                546               5 :         if (pos != NULL)
                                547                 :         {
                                548               2 :             *pos = '\0';
                                549               2 :             pos++;
                                550                 :         }
                                551                 :         else
 4788 tgl                       552               3 :             break;
                                553                 :     }
                                554                 : 
                                555                 :     /* Now build query */
 6248 neilc                     556               5 :     initStringInfo(&query_buf);
                                557                 : 
                                558                 :     /* Build initial sql statement */
                                559               5 :     appendStringInfo(&query_buf, "SELECT %s, %s FROM %s WHERE %s",
                                560                 :                      pkeyfield,
                                561                 :                      xmlfield,
                                562                 :                      relname,
                                563                 :                      condition);
                                564                 : 
 6797 bruce                     565               5 :     if ((ret = SPI_connect()) < 0)
 6797 bruce                     566 UBC           0 :         elog(ERROR, "xpath_table: SPI_connect returned %d", ret);
                                567                 : 
 6248 neilc                     568 CBC           5 :     if ((ret = SPI_exec(query_buf.data, 0)) != SPI_OK_SELECT)
 4788 tgl                       569 UBC           0 :         elog(ERROR, "xpath_table: SPI execution failed for query %s",
                                570                 :              query_buf.data);
                                571                 : 
 6797 bruce                     572 CBC           5 :     proc = SPI_processed;
                                573               5 :     tuptable = SPI_tuptable;
                                574               5 :     spi_tupdesc = tuptable->tupdesc;
                                575                 : 
                                576                 :     /*
                                577                 :      * Check that SPI returned correct result. If you put a comma into one of
                                578                 :      * the function parameters, this will catch it when the SPI query returns
                                579                 :      * e.g. 3 columns.
                                580                 :      */
                                581               5 :     if (spi_tupdesc->natts != 2)
                                582                 :     {
 6797 bruce                     583 UBC           0 :         ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                584                 :                         errmsg("expression returning multiple columns is not valid in parameter list"),
                                585                 :                         errdetail("Expected two columns in SPI result, got %d.", spi_tupdesc->natts)));
                                586                 :     }
                                587                 : 
                                588                 :     /*
                                589                 :      * Setup the parser.  This should happen after we are done evaluating the
                                590                 :      * query, in case it calls functions that set up libxml differently.
                                591                 :      */
 4281 tgl                       592 CBC           5 :     xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
                                593                 : 
                                594               5 :     PG_TRY();
                                595                 :     {
                                596                 :         /* For each row i.e. document returned from SPI */
                                597                 :         uint64      i;
                                598                 : 
 3955 bruce                     599              10 :         for (i = 0; i < proc; i++)
                                600                 :         {
                                601                 :             char       *pkey;
                                602                 :             char       *xmldoc;
                                603                 :             xmlXPathContextPtr ctxt;
                                604                 :             xmlXPathObjectPtr res;
                                605                 :             xmlChar    *resstr;
                                606                 :             xmlXPathCompExprPtr comppath;
                                607                 :             HeapTuple   ret_tuple;
                                608                 : 
                                609                 :             /* Extract the row data as C Strings */
                                610               5 :             spi_tuple = tuptable->vals[i];
                                611               5 :             pkey = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
                                612               5 :             xmldoc = SPI_getvalue(spi_tuple, spi_tupdesc, 2);
                                613                 : 
                                614                 :             /*
                                615                 :              * Clear the values array, so that not-well-formed documents
                                616                 :              * return NULL in all columns.  Note that this also means that
                                617                 :              * spare columns will be NULL.
                                618                 :              */
  397 michael                   619              15 :             for (j = 0; j < rsinfo->setDesc->natts; j++)
 3955 bruce                     620              10 :                 values[j] = NULL;
                                621                 : 
                                622                 :             /* Insert primary key */
                                623               5 :             values[0] = pkey;
                                624                 : 
                                625                 :             /* Parse the document */
                                626               5 :             if (xmldoc)
                                627               5 :                 doctree = xmlParseMemory(xmldoc, strlen(xmldoc));
                                628                 :             else                /* treat NULL as not well-formed */
 3955 bruce                     629 UBC           0 :                 doctree = NULL;
                                630                 : 
 3955 bruce                     631 CBC           5 :             if (doctree == NULL)
                                632                 :             {
                                633                 :                 /* not well-formed, so output all-NULL tuple */
 3955 bruce                     634 UBC           0 :                 ret_tuple = BuildTupleFromCStrings(attinmeta, values);
  397 michael                   635               0 :                 tuplestore_puttuple(rsinfo->setResult, ret_tuple);
 3955 bruce                     636               0 :                 heap_freetuple(ret_tuple);
                                637                 :             }
                                638                 :             else
                                639                 :             {
                                640                 :                 /* New loop here - we have to deal with nodeset results */
 3955 bruce                     641 CBC           5 :                 rownr = 0;
                                642                 : 
                                643                 :                 do
                                644                 :                 {
                                645                 :                     /* Now evaluate the set of xpaths. */
                                646               8 :                     had_values = false;
                                647              18 :                     for (j = 0; j < numpaths; j++)
                                648                 :                     {
                                649              10 :                         ctxt = xmlXPathNewContext(doctree);
                                650              10 :                         ctxt->node = xmlDocGetRootElement(doctree);
                                651                 : 
                                652                 :                         /* compile the path */
                                653              10 :                         comppath = xmlXPathCompile(xpaths[j]);
                                654              10 :                         if (comppath == NULL)
 3955 bruce                     655 UBC           0 :                             xml_ereport(xmlerrcxt, ERROR,
                                656                 :                                         ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
                                657                 :                                         "XPath Syntax Error");
                                658                 : 
                                659                 :                         /* Now evaluate the path expression. */
 3955 bruce                     660 CBC          10 :                         res = xmlXPathCompiledEval(comppath, ctxt);
                                661              10 :                         xmlXPathFreeCompExpr(comppath);
                                662                 : 
                                663              10 :                         if (res != NULL)
                                664                 :                         {
                                665              10 :                             switch (res->type)
                                666                 :                             {
                                667              10 :                                 case XPATH_NODESET:
                                668                 :                                     /* We see if this nodeset has enough nodes */
                                669              10 :                                     if (res->nodesetval != NULL &&
                                670              10 :                                         rownr < res->nodesetval->nodeNr)
                                671                 :                                     {
                                672               4 :                                         resstr = xmlXPathCastNodeToString(res->nodesetval->nodeTab[rownr]);
                                673               4 :                                         had_values = true;
                                674                 :                                     }
                                675                 :                                     else
                                676               6 :                                         resstr = NULL;
                                677                 : 
                                678              10 :                                     break;
                                679                 : 
 3955 bruce                     680 UBC           0 :                                 case XPATH_STRING:
                                681               0 :                                     resstr = xmlStrdup(res->stringval);
                                682               0 :                                     break;
                                683                 : 
                                684               0 :                                 default:
                                685               0 :                                     elog(NOTICE, "unsupported XQuery result: %d", res->type);
                                686               0 :                                     resstr = xmlStrdup((const xmlChar *) "<unsupported/>");
                                687                 :                             }
                                688                 : 
                                689                 :                             /*
                                690                 :                              * Insert this into the appropriate column in the
                                691                 :                              * result tuple.
                                692                 :                              */
 3955 bruce                     693 CBC          10 :                             values[j + 1] = (char *) resstr;
                                694                 :                         }
                                695              10 :                         xmlXPathFreeContext(ctxt);
                                696                 :                     }
                                697                 : 
                                698                 :                     /* Now add the tuple to the output, if there is one. */
                                699               8 :                     if (had_values)
                                700                 :                     {
                                701               3 :                         ret_tuple = BuildTupleFromCStrings(attinmeta, values);
  397 michael                   702               3 :                         tuplestore_puttuple(rsinfo->setResult, ret_tuple);
 3955 bruce                     703               3 :                         heap_freetuple(ret_tuple);
                                704                 :                     }
                                705                 : 
                                706               8 :                     rownr++;
                                707               8 :                 } while (had_values);
                                708                 :             }
                                709                 : 
                                710               5 :             if (doctree != NULL)
                                711               5 :                 xmlFreeDoc(doctree);
                                712               5 :             doctree = NULL;
                                713                 : 
                                714               5 :             if (pkey)
                                715               5 :                 pfree(pkey);
                                716               5 :             if (xmldoc)
                                717               5 :                 pfree(xmldoc);
                                718                 :         }
                                719                 :     }
 4281 tgl                       720 UBC           0 :     PG_CATCH();
                                721                 :     {
                                722               0 :         if (doctree != NULL)
                                723               0 :             xmlFreeDoc(doctree);
                                724                 : 
                                725               0 :         pg_xml_done(xmlerrcxt, true);
                                726                 : 
                                727               0 :         PG_RE_THROW();
                                728                 :     }
 4281 tgl                       729 CBC           5 :     PG_END_TRY();
                                730                 : 
                                731               5 :     if (doctree != NULL)
 4281 tgl                       732 UBC           0 :         xmlFreeDoc(doctree);
                                733                 : 
 4281 tgl                       734 CBC           5 :     pg_xml_done(xmlerrcxt, false);
                                735                 : 
 6797 bruce                     736               5 :     SPI_finish();
                                737                 : 
                                738                 :     /*
                                739                 :      * SFRM_Materialize mode expects us to return a NULL Datum. The actual
                                740                 :      * tuples are in our tuplestore and passed back through rsinfo->setResult.
                                741                 :      * rsinfo->setDesc is set to the tuple description that we actually used
                                742                 :      * to build our tuples with, so the caller can verify we did what it was
                                743                 :      * expecting.
                                744                 :      */
                                745               5 :     return (Datum) 0;
                                746                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a