PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
xpath.c
Go to the documentation of this file.
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 
27 
28 /* exported for use by xslt_proc.c */
29 
31 
32 /* workspace for pgxml_xpath() */
33 
34 typedef struct
35 {
36  xmlDocPtr doctree;
37  xmlXPathContextPtr ctxt;
38  xmlXPathObjectPtr res;
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  */
66 {
67  PgXmlErrorContext *xmlerrcxt;
68 
69  /* Set up error handling (we share the core's error handler) */
70  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  xmlInitParser();
76 
77  xmlSubstituteEntitiesDefault(1);
78  xmlLoadExtDtdDefaultValue = 1;
79 
80  return xmlerrcxt;
81 }
82 
83 
84 /*
85  * Returns true if document is well-formed
86  *
87  * Note: this has been superseded by a core function. We still have to
88  * have it in the contrib module so that existing SQL-level references
89  * to the function won't fail; but in normal usage with up-to-date SQL
90  * definitions for the contrib module, this won't be called.
91  */
92 
94 
95 Datum
97 {
98  text *t = PG_GETARG_TEXT_PP(0); /* document buffer */
99  bool result = false;
100  int32 docsize = VARSIZE_ANY_EXHDR(t);
101  xmlDocPtr doctree;
102  PgXmlErrorContext *xmlerrcxt;
103 
105 
106  PG_TRY();
107  {
108  doctree = xmlParseMemory((char *) VARDATA_ANY(t), docsize);
109 
110  result = (doctree != NULL);
111 
112  if (doctree != NULL)
113  xmlFreeDoc(doctree);
114  }
115  PG_CATCH();
116  {
117  pg_xml_done(xmlerrcxt, true);
118 
119  PG_RE_THROW();
120  }
121  PG_END_TRY();
122 
123  pg_xml_done(xmlerrcxt, false);
124 
125  PG_RETURN_BOOL(result);
126 }
127 
128 
129 /* Encodes special characters (<, >, &, " and \r) as XML entities */
130 
132 
133 Datum
135 {
136  text *tin = PG_GETARG_TEXT_PP(0);
137  text *tout;
138  xmlChar *ts,
139  *tt;
140 
141  ts = pgxml_texttoxmlchar(tin);
142 
143  tt = xmlEncodeSpecialChars(NULL, ts);
144 
145  pfree(ts);
146 
147  tout = cstring_to_text((char *) tt);
148 
149  xmlFree(tt);
150 
151  PG_RETURN_TEXT_P(tout);
152 }
153 
154 /*
155  * Function translates a nodeset into a text representation
156  *
157  * iterates over each node in the set and calls xmlNodeDump to write it to
158  * an xmlBuffer -from which an xmlChar * string is returned.
159  *
160  * each representation is surrounded by <tagname> ... </tagname>
161  *
162  * plainsep is an ordinary (not tag) separator - if used, then nodes are
163  * cast to string as output method
164  */
165 static xmlChar *
166 pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
167  xmlChar *toptagname,
168  xmlChar *septagname,
169  xmlChar *plainsep)
170 {
171  xmlBufferPtr buf;
172  xmlChar *result;
173  int i;
174 
175  buf = xmlBufferCreate();
176 
177  if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
178  {
179  xmlBufferWriteChar(buf, "<");
180  xmlBufferWriteCHAR(buf, toptagname);
181  xmlBufferWriteChar(buf, ">");
182  }
183  if (nodeset != NULL)
184  {
185  for (i = 0; i < nodeset->nodeNr; i++)
186  {
187  if (plainsep != NULL)
188  {
189  xmlBufferWriteCHAR(buf,
190  xmlXPathCastNodeToString(nodeset->nodeTab[i]));
191 
192  /* If this isn't the last entry, write the plain sep. */
193  if (i < (nodeset->nodeNr) - 1)
194  xmlBufferWriteChar(buf, (char *) plainsep);
195  }
196  else
197  {
198  if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
199  {
200  xmlBufferWriteChar(buf, "<");
201  xmlBufferWriteCHAR(buf, septagname);
202  xmlBufferWriteChar(buf, ">");
203  }
204  xmlNodeDump(buf,
205  nodeset->nodeTab[i]->doc,
206  nodeset->nodeTab[i],
207  1, 0);
208 
209  if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
210  {
211  xmlBufferWriteChar(buf, "</");
212  xmlBufferWriteCHAR(buf, septagname);
213  xmlBufferWriteChar(buf, ">");
214  }
215  }
216  }
217  }
218 
219  if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
220  {
221  xmlBufferWriteChar(buf, "</");
222  xmlBufferWriteCHAR(buf, toptagname);
223  xmlBufferWriteChar(buf, ">");
224  }
225  result = xmlStrdup(buf->content);
226  xmlBufferFree(buf);
227  return result;
228 }
229 
230 
231 /* Translate a PostgreSQL "varlena" -i.e. a variable length parameter
232  * into the libxml2 representation
233  */
234 static xmlChar *
236 {
237  return (xmlChar *) text_to_cstring(textstring);
238 }
239 
240 /* Publicly visible XPath functions */
241 
242 /*
243  * This is a "raw" xpath function. Check that it returns child elements
244  * properly
245  */
247 
248 Datum
250 {
251  text *document = PG_GETARG_TEXT_PP(0);
252  text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
253  xmlChar *toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(2));
254  xmlChar *septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(3));
255  xmlChar *xpath;
256  text *xpres;
257  xmlXPathObjectPtr res;
258  xpath_workspace workspace;
259 
260  xpath = pgxml_texttoxmlchar(xpathsupp);
261 
262  res = pgxml_xpath(document, xpath, &workspace);
263 
264  xpres = pgxml_result_to_text(res, toptag, septag, NULL);
265 
266  cleanup_workspace(&workspace);
267 
268  pfree(xpath);
269 
270  if (xpres == NULL)
271  PG_RETURN_NULL();
272  PG_RETURN_TEXT_P(xpres);
273 }
274 
275 /*
276  * The following function is almost identical, but returns the elements in
277  * a list.
278  */
280 
281 Datum
283 {
284  text *document = PG_GETARG_TEXT_PP(0);
285  text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
286  xmlChar *plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(2));
287  xmlChar *xpath;
288  text *xpres;
289  xmlXPathObjectPtr res;
290  xpath_workspace workspace;
291 
292  xpath = pgxml_texttoxmlchar(xpathsupp);
293 
294  res = pgxml_xpath(document, xpath, &workspace);
295 
296  xpres = pgxml_result_to_text(res, NULL, NULL, plainsep);
297 
298  cleanup_workspace(&workspace);
299 
300  pfree(xpath);
301 
302  if (xpres == NULL)
303  PG_RETURN_NULL();
304  PG_RETURN_TEXT_P(xpres);
305 }
306 
307 
309 
310 Datum
312 {
313  text *document = PG_GETARG_TEXT_PP(0);
314  text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
315  xmlChar *xpath;
316  int32 pathsize;
317  text *xpres;
318  xmlXPathObjectPtr res;
319  xpath_workspace workspace;
320 
321  pathsize = VARSIZE_ANY_EXHDR(xpathsupp);
322 
323  /*
324  * We encapsulate the supplied path with "string()" = 8 chars + 1 for NUL
325  * at end
326  */
327  /* We could try casting to string using the libxml function? */
328 
329  xpath = (xmlChar *) palloc(pathsize + 9);
330  memcpy((char *) xpath, "string(", 7);
331  memcpy((char *) (xpath + 7), VARDATA_ANY(xpathsupp), pathsize);
332  xpath[pathsize + 7] = ')';
333  xpath[pathsize + 8] = '\0';
334 
335  res = pgxml_xpath(document, xpath, &workspace);
336 
337  xpres = pgxml_result_to_text(res, NULL, NULL, NULL);
338 
339  cleanup_workspace(&workspace);
340 
341  pfree(xpath);
342 
343  if (xpres == NULL)
344  PG_RETURN_NULL();
345  PG_RETURN_TEXT_P(xpres);
346 }
347 
348 
350 
351 Datum
353 {
354  text *document = PG_GETARG_TEXT_PP(0);
355  text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
356  xmlChar *xpath;
357  float4 fRes;
358  xmlXPathObjectPtr res;
359  xpath_workspace workspace;
360 
361  xpath = pgxml_texttoxmlchar(xpathsupp);
362 
363  res = pgxml_xpath(document, xpath, &workspace);
364 
365  pfree(xpath);
366 
367  if (res == NULL)
368  PG_RETURN_NULL();
369 
370  fRes = xmlXPathCastToNumber(res);
371 
372  cleanup_workspace(&workspace);
373 
374  if (xmlXPathIsNaN(fRes))
375  PG_RETURN_NULL();
376 
377  PG_RETURN_FLOAT4(fRes);
378 }
379 
380 
382 
383 Datum
385 {
386  text *document = PG_GETARG_TEXT_PP(0);
387  text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
388  xmlChar *xpath;
389  int bRes;
390  xmlXPathObjectPtr res;
391  xpath_workspace workspace;
392 
393  xpath = pgxml_texttoxmlchar(xpathsupp);
394 
395  res = pgxml_xpath(document, xpath, &workspace);
396 
397  pfree(xpath);
398 
399  if (res == NULL)
400  PG_RETURN_BOOL(false);
401 
402  bRes = xmlXPathCastToBoolean(res);
403 
404  cleanup_workspace(&workspace);
405 
406  PG_RETURN_BOOL(bRes);
407 }
408 
409 
410 
411 /* Core function to evaluate XPath query */
412 
413 static xmlXPathObjectPtr
414 pgxml_xpath(text *document, xmlChar *xpath, xpath_workspace *workspace)
415 {
416  int32 docsize = VARSIZE_ANY_EXHDR(document);
417  PgXmlErrorContext *xmlerrcxt;
418  xmlXPathCompExprPtr comppath;
419 
420  workspace->doctree = NULL;
421  workspace->ctxt = NULL;
422  workspace->res = NULL;
423 
425 
426  PG_TRY();
427  {
428  workspace->doctree = xmlParseMemory((char *) VARDATA_ANY(document),
429  docsize);
430  if (workspace->doctree != NULL)
431  {
432  workspace->ctxt = xmlXPathNewContext(workspace->doctree);
433  workspace->ctxt->node = xmlDocGetRootElement(workspace->doctree);
434 
435  /* compile the path */
436  comppath = xmlXPathCompile(xpath);
437  if (comppath == NULL)
438  xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
439  "XPath Syntax Error");
440 
441  /* Now evaluate the path expression. */
442  workspace->res = xmlXPathCompiledEval(comppath, workspace->ctxt);
443 
444  xmlXPathFreeCompExpr(comppath);
445  }
446  }
447  PG_CATCH();
448  {
449  cleanup_workspace(workspace);
450 
451  pg_xml_done(xmlerrcxt, true);
452 
453  PG_RE_THROW();
454  }
455  PG_END_TRY();
456 
457  if (workspace->res == NULL)
458  cleanup_workspace(workspace);
459 
460  pg_xml_done(xmlerrcxt, false);
461 
462  return workspace->res;
463 }
464 
465 /* Clean up after processing the result of pgxml_xpath() */
466 static void
468 {
469  if (workspace->res)
470  xmlXPathFreeObject(workspace->res);
471  workspace->res = NULL;
472  if (workspace->ctxt)
473  xmlXPathFreeContext(workspace->ctxt);
474  workspace->ctxt = NULL;
475  if (workspace->doctree)
476  xmlFreeDoc(workspace->doctree);
477  workspace->doctree = NULL;
478 }
479 
480 static text *
481 pgxml_result_to_text(xmlXPathObjectPtr res,
482  xmlChar *toptag,
483  xmlChar *septag,
484  xmlChar *plainsep)
485 {
486  xmlChar *xpresstr;
487  text *xpres;
488 
489  if (res == NULL)
490  return NULL;
491 
492  switch (res->type)
493  {
494  case XPATH_NODESET:
495  xpresstr = pgxmlNodeSetToText(res->nodesetval,
496  toptag,
497  septag, plainsep);
498  break;
499 
500  case XPATH_STRING:
501  xpresstr = xmlStrdup(res->stringval);
502  break;
503 
504  default:
505  elog(NOTICE, "unsupported XQuery result: %d", res->type);
506  xpresstr = xmlStrdup((const xmlChar *) "<unsupported/>");
507  }
508 
509  /* Now convert this result back to text */
510  xpres = cstring_to_text((char *) xpresstr);
511 
512  /* Free various storage */
513  xmlFree(xpresstr);
514 
515  return xpres;
516 }
517 
518 /*
519  * xpath_table is a table function. It needs some tidying (as do the
520  * other functions here!
521  */
523 
524 Datum
526 {
527  /* Function parameters */
528  char *pkeyfield = text_to_cstring(PG_GETARG_TEXT_PP(0));
529  char *xmlfield = text_to_cstring(PG_GETARG_TEXT_PP(1));
530  char *relname = text_to_cstring(PG_GETARG_TEXT_PP(2));
531  char *xpathset = text_to_cstring(PG_GETARG_TEXT_PP(3));
532  char *condition = text_to_cstring(PG_GETARG_TEXT_PP(4));
533 
534  /* SPI (input tuple) support */
535  SPITupleTable *tuptable;
536  HeapTuple spi_tuple;
537  TupleDesc spi_tupdesc;
538 
539  /* Output tuple (tuplestore) support */
540  Tuplestorestate *tupstore = NULL;
541  TupleDesc ret_tupdesc;
542  HeapTuple ret_tuple;
543 
544  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
545  AttInMetadata *attinmeta;
546  MemoryContext per_query_ctx;
547  MemoryContext oldcontext;
548 
549  char **values;
550  xmlChar **xpaths;
551  char *pos;
552  const char *pathsep = "|";
553 
554  int numpaths;
555  int ret;
556  uint64 proc;
557  int j;
558  int rownr; /* For issuing multiple rows from one original
559  * document */
560  bool had_values; /* To determine end of nodeset results */
561  StringInfoData query_buf;
562  PgXmlErrorContext *xmlerrcxt;
563  volatile xmlDocPtr doctree = NULL;
564 
565  /* We only have a valid tuple description in table function mode */
566  if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
567  ereport(ERROR,
568  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
569  errmsg("set-valued function called in context that cannot accept a set")));
570  if (rsinfo->expectedDesc == NULL)
571  ereport(ERROR,
572  (errcode(ERRCODE_SYNTAX_ERROR),
573  errmsg("xpath_table must be called as a table function")));
574 
575  /*
576  * We want to materialise because it means that we don't have to carry
577  * libxml2 parser state between invocations of this function
578  */
579  if (!(rsinfo->allowedModes & SFRM_Materialize))
580  ereport(ERROR,
581  (errcode(ERRCODE_SYNTAX_ERROR),
582  errmsg("xpath_table requires Materialize mode, but it is not "
583  "allowed in this context")));
584 
585  /*
586  * The tuplestore must exist in a higher context than this function call
587  * (per_query_ctx is used)
588  */
589  per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
590  oldcontext = MemoryContextSwitchTo(per_query_ctx);
591 
592  /*
593  * Create the tuplestore - work_mem is the max in-memory size before a
594  * file is created on disk to hold it.
595  */
596  tupstore =
598  false, work_mem);
599 
600  MemoryContextSwitchTo(oldcontext);
601 
602  /* get the requested return tuple description */
603  ret_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
604 
605  /* must have at least one output column (for the pkey) */
606  if (ret_tupdesc->natts < 1)
607  ereport(ERROR,
608  (errcode(ERRCODE_SYNTAX_ERROR),
609  errmsg("xpath_table must have at least one output column")));
610 
611  /*
612  * At the moment we assume that the returned attributes make sense for the
613  * XPath specified (i.e. we trust the caller). It's not fatal if they get
614  * it wrong - the input function for the column type will raise an error
615  * if the path result can't be converted into the correct binary
616  * representation.
617  */
618 
619  attinmeta = TupleDescGetAttInMetadata(ret_tupdesc);
620 
621  /* Set return mode and allocate value space. */
622  rsinfo->returnMode = SFRM_Materialize;
623  rsinfo->setDesc = ret_tupdesc;
624 
625  values = (char **) palloc(ret_tupdesc->natts * sizeof(char *));
626  xpaths = (xmlChar **) palloc(ret_tupdesc->natts * sizeof(xmlChar *));
627 
628  /*
629  * Split XPaths. xpathset is a writable CString.
630  *
631  * Note that we stop splitting once we've done all needed for tupdesc
632  */
633  numpaths = 0;
634  pos = xpathset;
635  while (numpaths < (ret_tupdesc->natts - 1))
636  {
637  xpaths[numpaths++] = (xmlChar *) pos;
638  pos = strstr(pos, pathsep);
639  if (pos != NULL)
640  {
641  *pos = '\0';
642  pos++;
643  }
644  else
645  break;
646  }
647 
648  /* Now build query */
649  initStringInfo(&query_buf);
650 
651  /* Build initial sql statement */
652  appendStringInfo(&query_buf, "SELECT %s, %s FROM %s WHERE %s",
653  pkeyfield,
654  xmlfield,
655  relname,
656  condition);
657 
658  if ((ret = SPI_connect()) < 0)
659  elog(ERROR, "xpath_table: SPI_connect returned %d", ret);
660 
661  if ((ret = SPI_exec(query_buf.data, 0)) != SPI_OK_SELECT)
662  elog(ERROR, "xpath_table: SPI execution failed for query %s",
663  query_buf.data);
664 
665  proc = SPI_processed;
666  tuptable = SPI_tuptable;
667  spi_tupdesc = tuptable->tupdesc;
668 
669  /* Switch out of SPI context */
670  MemoryContextSwitchTo(oldcontext);
671 
672  /*
673  * Check that SPI returned correct result. If you put a comma into one of
674  * the function parameters, this will catch it when the SPI query returns
675  * e.g. 3 columns.
676  */
677  if (spi_tupdesc->natts != 2)
678  {
679  ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
680  errmsg("expression returning multiple columns is not valid in parameter list"),
681  errdetail("Expected two columns in SPI result, got %d.", spi_tupdesc->natts)));
682  }
683 
684  /*
685  * Setup the parser. This should happen after we are done evaluating the
686  * query, in case it calls functions that set up libxml differently.
687  */
689 
690  PG_TRY();
691  {
692  /* For each row i.e. document returned from SPI */
693  uint64 i;
694 
695  for (i = 0; i < proc; i++)
696  {
697  char *pkey;
698  char *xmldoc;
699  xmlXPathContextPtr ctxt;
700  xmlXPathObjectPtr res;
701  xmlChar *resstr;
702  xmlXPathCompExprPtr comppath;
703 
704  /* Extract the row data as C Strings */
705  spi_tuple = tuptable->vals[i];
706  pkey = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
707  xmldoc = SPI_getvalue(spi_tuple, spi_tupdesc, 2);
708 
709  /*
710  * Clear the values array, so that not-well-formed documents
711  * return NULL in all columns. Note that this also means that
712  * spare columns will be NULL.
713  */
714  for (j = 0; j < ret_tupdesc->natts; j++)
715  values[j] = NULL;
716 
717  /* Insert primary key */
718  values[0] = pkey;
719 
720  /* Parse the document */
721  if (xmldoc)
722  doctree = xmlParseMemory(xmldoc, strlen(xmldoc));
723  else /* treat NULL as not well-formed */
724  doctree = NULL;
725 
726  if (doctree == NULL)
727  {
728  /* not well-formed, so output all-NULL tuple */
729  ret_tuple = BuildTupleFromCStrings(attinmeta, values);
730  tuplestore_puttuple(tupstore, ret_tuple);
731  heap_freetuple(ret_tuple);
732  }
733  else
734  {
735  /* New loop here - we have to deal with nodeset results */
736  rownr = 0;
737 
738  do
739  {
740  /* Now evaluate the set of xpaths. */
741  had_values = false;
742  for (j = 0; j < numpaths; j++)
743  {
744  ctxt = xmlXPathNewContext(doctree);
745  ctxt->node = xmlDocGetRootElement(doctree);
746 
747  /* compile the path */
748  comppath = xmlXPathCompile(xpaths[j]);
749  if (comppath == NULL)
750  xml_ereport(xmlerrcxt, ERROR,
751  ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
752  "XPath Syntax Error");
753 
754  /* Now evaluate the path expression. */
755  res = xmlXPathCompiledEval(comppath, ctxt);
756  xmlXPathFreeCompExpr(comppath);
757 
758  if (res != NULL)
759  {
760  switch (res->type)
761  {
762  case XPATH_NODESET:
763  /* We see if this nodeset has enough nodes */
764  if (res->nodesetval != NULL &&
765  rownr < res->nodesetval->nodeNr)
766  {
767  resstr = xmlXPathCastNodeToString(res->nodesetval->nodeTab[rownr]);
768  had_values = true;
769  }
770  else
771  resstr = NULL;
772 
773  break;
774 
775  case XPATH_STRING:
776  resstr = xmlStrdup(res->stringval);
777  break;
778 
779  default:
780  elog(NOTICE, "unsupported XQuery result: %d", res->type);
781  resstr = xmlStrdup((const xmlChar *) "<unsupported/>");
782  }
783 
784  /*
785  * Insert this into the appropriate column in the
786  * result tuple.
787  */
788  values[j + 1] = (char *) resstr;
789  }
790  xmlXPathFreeContext(ctxt);
791  }
792 
793  /* Now add the tuple to the output, if there is one. */
794  if (had_values)
795  {
796  ret_tuple = BuildTupleFromCStrings(attinmeta, values);
797  tuplestore_puttuple(tupstore, ret_tuple);
798  heap_freetuple(ret_tuple);
799  }
800 
801  rownr++;
802  } while (had_values);
803  }
804 
805  if (doctree != NULL)
806  xmlFreeDoc(doctree);
807  doctree = NULL;
808 
809  if (pkey)
810  pfree(pkey);
811  if (xmldoc)
812  pfree(xmldoc);
813  }
814  }
815  PG_CATCH();
816  {
817  if (doctree != NULL)
818  xmlFreeDoc(doctree);
819 
820  pg_xml_done(xmlerrcxt, true);
821 
822  PG_RE_THROW();
823  }
824  PG_END_TRY();
825 
826  if (doctree != NULL)
827  xmlFreeDoc(doctree);
828 
829  pg_xml_done(xmlerrcxt, false);
830 
831  tuplestore_donestoring(tupstore);
832 
833  SPI_finish();
834 
835  rsinfo->setResult = tupstore;
836 
837  /*
838  * SFRM_Materialize mode expects us to return a NULL Datum. The actual
839  * tuples are in our tuplestore and passed back through rsinfo->setResult.
840  * rsinfo->setDesc is set to the tuple description that we actually used
841  * to build our tuples with, so the caller can verify we did what it was
842  * expecting.
843  */
844  return (Datum) 0;
845 }
static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath, xpath_workspace *workspace)
Definition: xpath.c:414
xmlDocPtr doctree
Definition: xpath.c:36
static xmlChar * pgxml_texttoxmlchar(text *textstring)
Definition: xpath.c:235
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:141
#define IsA(nodeptr, _type_)
Definition: nodes.h:573
Datum xpath_string(PG_FUNCTION_ARGS)
Definition: xpath.c:311
#define PG_RETURN_FLOAT4(x)
Definition: fmgr.h:317
#define VARDATA_ANY(PTR)
Definition: postgres.h:347
static xmlChar * pgxmlNodeSetToText(xmlNodeSetPtr nodeset, xmlChar *toptagname, xmlChar *septagname, xmlChar *plainsep)
Definition: xpath.c:166
PG_MODULE_MAGIC
Definition: xpath.c:26
static text * pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag, xmlChar *septag, xmlChar *plainsep)
Definition: xpath.c:481
int SPI_connect(void)
Definition: spi.c:84
int SPI_finish(void)
Definition: spi.c:147
Datum xpath_number(PG_FUNCTION_ARGS)
Definition: xpath.c:352
#define tuplestore_donestoring(state)
Definition: tuplestore.h:60
struct PgXmlErrorContext PgXmlErrorContext
Definition: xml.h:48
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
SPITupleTable * SPI_tuptable
Definition: spi.c:41
int errcode(int sqlerrcode)
Definition: elog.c:575
PgXmlErrorContext * pgxml_parser_init(PgXmlStrictness strictness)
Definition: xpath.c:65
return result
Definition: formatting.c:1618
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1374
HeapTuple * vals
Definition: spi.h:27
Datum xpath_bool(PG_FUNCTION_ARGS)
Definition: xpath.c:384
int natts
Definition: tupdesc.h:73
uint64 SPI_processed
Definition: spi.c:39
signed int int32
Definition: c.h:256
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Definition: execTuples.c:1115
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:265
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
Definition: spi.c:803
TupleDesc expectedDesc
Definition: execnodes.h:200
Datum xpath(PG_FUNCTION_ARGS)
Definition: xml.c:4001
void pfree(void *pointer)
Definition: mcxt.c:950
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:110
#define ERROR
Definition: elog.h:43
int SPI_exec(const char *src, long tcount)
Definition: spi.c:331
Datum xml_encode_special_chars(PG_FUNCTION_ARGS)
Definition: xpath.c:134
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
Definition: tuplestore.c:715
static char * buf
Definition: pg_test_fsync.c:65
int errdetail(const char *fmt,...)
Definition: elog.c:873
void pg_xml_done(PgXmlErrorContext *errcxt, bool isError)
#define ereport(elevel, rest)
Definition: elog.h:122
void initStringInfo(StringInfo str)
Definition: stringinfo.c:65
float float4
Definition: c.h:380
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:316
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:311
uintptr_t Datum
Definition: postgres.h:372
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:1068
int work_mem
Definition: globals.c:112
TupleDesc tupdesc
Definition: spi.h:26
Datum xpath_table(PG_FUNCTION_ARGS)
Definition: xpath.c:525
#define SPI_OK_SELECT
Definition: spi.h:51
xmlXPathObjectPtr res
Definition: xpath.c:38
int allowedModes
Definition: execnodes.h:201
#define NOTICE
Definition: elog.h:37
#define PG_RETURN_TEXT_P(x)
Definition: fmgr.h:322
SetFunctionReturnMode returnMode
Definition: execnodes.h:203
#define PG_CATCH()
Definition: elog.h:293
text * cstring_to_text(const char *s)
Definition: varlena.c:149
Datum xml_is_well_formed(PG_FUNCTION_ARGS)
Definition: xpath.c:96
#define NULL
Definition: c.h:229
Datum xpath_list(PG_FUNCTION_ARGS)
Definition: xpath.c:282
#define PG_RE_THROW()
Definition: elog.h:314
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:135
Tuplestorestate * setResult
Definition: execnodes.h:206
static Datum values[MAXATTR]
Definition: bootstrap.c:162
char * text_to_cstring(const text *t)
Definition: varlena.c:182
ExprContext * econtext
Definition: execnodes.h:199
static void cleanup_workspace(xpath_workspace *workspace)
Definition: xpath.c:467
TupleDesc setDesc
Definition: execnodes.h:207
#define VARSIZE_ANY_EXHDR(PTR)
Definition: postgres.h:340
void * palloc(Size size)
Definition: mcxt.c:849
int errmsg(const char *fmt,...)
Definition: elog.c:797
PG_FUNCTION_INFO_V1(xml_is_well_formed)
int i
xmlXPathContextPtr ctxt
Definition: xpath.c:37
Definition: c.h:439
#define PG_FUNCTION_ARGS
Definition: fmgr.h:150
#define elog
Definition: elog.h:219
#define PG_TRY()
Definition: elog.h:284
PgXmlStrictness
Definition: xml.h:39
PgXmlErrorContext * pg_xml_init(PgXmlStrictness strictness)
#define PG_RETURN_NULL()
Definition: fmgr.h:297
#define PG_END_TRY()
Definition: elog.h:300
void xml_ereport(PgXmlErrorContext *errcxt, int level, int sqlcode, const char *msg)
Datum xpath_nodeset(PG_FUNCTION_ARGS)
Definition: xpath.c:249