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