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