PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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
10#include "executor/spi.h"
11#include "fmgr.h"
12#include "funcapi.h"
13#include "lib/stringinfo.h"
14#include "utils/builtins.h"
15#include "utils/tuplestore.h"
16#include "utils/xml.h"
17
18/* libxml includes */
19
20#include <libxml/xpath.h>
21#include <libxml/tree.h>
22#include <libxml/xmlmemory.h>
23#include <libxml/xmlerror.h>
24#include <libxml/parserInternals.h>
25
27 .name = "xml2",
28 .version = PG_VERSION
29);
30
31/* exported for use by xslt_proc.c */
32
34
35/* workspace for pgxml_xpath() */
36
43
44/* local declarations */
45
49
52
54
57
58static void cleanup_workspace(xpath_workspace *workspace);
59
60
61/*
62 * Initialize for xml parsing.
63 *
64 * As with the underlying pg_xml_init function, calls to this MUST be followed
65 * by a PG_TRY block that guarantees that pg_xml_done is called.
66 */
69{
71
72 /* Set up error handling (we share the core's error handler) */
74
75 /* Note: we're assuming an elog cannot be thrown by the following calls */
76
77 /* Initialize libxml */
79
80 return xmlerrcxt;
81}
82
83
84/* Encodes special characters (<, >, &, " and \r) as XML entities */
85
87
90{
92 text *volatile tout = NULL;
93 xmlChar *volatile tt = NULL;
95
97
98 PG_TRY();
99 {
100 xmlChar *ts;
101
103
107 "could not allocate xmlChar");
108 pfree(ts);
109
110 tout = cstring_to_text((char *) tt);
111 }
112 PG_CATCH();
113 {
114 if (tt != NULL)
115 xmlFree(tt);
116
117 pg_xml_done(xmlerrcxt, true);
118
119 PG_RE_THROW();
120 }
121 PG_END_TRY();
122
123 if (tt != NULL)
124 xmlFree(tt);
125
126 pg_xml_done(xmlerrcxt, false);
127
129}
130
131/*
132 * Function translates a nodeset into a text representation
133 *
134 * iterates over each node in the set and calls xmlNodeDump to write it to
135 * an xmlBuffer -from which an xmlChar * string is returned.
136 *
137 * each representation is surrounded by <tagname> ... </tagname>
138 *
139 * plainsep is an ordinary (not tag) separator - if used, then nodes are
140 * cast to string as output method
141 */
142static xmlChar *
147{
148 volatile xmlBufferPtr buf = NULL;
149 xmlChar *volatile result = NULL;
150 xmlChar *volatile str = NULL;
152
153 /* spin up some error handling */
155
156 PG_TRY();
157 {
159
162 "could not allocate xmlBuffer");
163
164 if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
165 {
169 }
170 if (nodeset != NULL)
171 {
172 for (int i = 0; i < nodeset->nodeNr; i++)
173 {
174 if (plainsep != NULL)
175 {
179 "could not allocate node text");
180
182 xmlFree(str);
183 str = NULL;
184
185 /* If this isn't the last entry, write the plain sep. */
186 if (i < (nodeset->nodeNr) - 1)
188 }
189 else
190 {
191 if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
192 {
196 }
198 nodeset->nodeTab[i]->doc,
199 nodeset->nodeTab[i],
200 1, 0);
201
202 if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
203 {
204 xmlBufferWriteChar(buf, "</");
207 }
208 }
209 }
210 }
211
212 if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
213 {
214 xmlBufferWriteChar(buf, "</");
217 }
218
222 "could not allocate result");
223 }
224 PG_CATCH();
225 {
226 if (result)
228 if (str)
229 xmlFree(str);
230 if (buf)
232
233 pg_xml_done(xmlerrcxt, true);
234
235 PG_RE_THROW();
236 }
237 PG_END_TRY();
238
240 pg_xml_done(xmlerrcxt, false);
241
242 return result;
243}
244
245
246/*
247 * Translate a PostgreSQL "varlena" -i.e. a variable length parameter
248 * into the libxml2 representation
249 */
250static xmlChar *
255
256/* Publicly visible XPath functions */
257
258/*
259 * This is a "raw" xpath function. Check that it returns child elements
260 * properly
261 */
263
264Datum
266{
268 text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
271 xmlChar *xpath;
272 text *volatile xpres = NULL;
273 xpath_workspace *volatile workspace = NULL;
275
278
279 PG_TRY();
280 {
281 workspace = pgxml_xpath(document, xpath, xmlerrcxt);
283 }
284 PG_CATCH();
285 {
286 if (workspace)
287 cleanup_workspace(workspace);
288
289 pg_xml_done(xmlerrcxt, true);
290 PG_RE_THROW();
291 }
292 PG_END_TRY();
293
294 cleanup_workspace(workspace);
295 pg_xml_done(xmlerrcxt, false);
296
297 pfree(xpath);
298
299 if (xpres == NULL)
302}
303
304/*
305 * The following function is almost identical, but returns the elements in
306 * a list.
307 */
309
310Datum
312{
314 text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
316 xmlChar *xpath;
317 text *volatile xpres = NULL;
318 xpath_workspace *volatile workspace = NULL;
320
323
324 PG_TRY();
325 {
326 workspace = pgxml_xpath(document, xpath, xmlerrcxt);
328 }
329 PG_CATCH();
330 {
331 if (workspace)
332 cleanup_workspace(workspace);
333
334 pg_xml_done(xmlerrcxt, true);
335 PG_RE_THROW();
336 }
337 PG_END_TRY();
338
339 cleanup_workspace(workspace);
340 pg_xml_done(xmlerrcxt, false);
341
342 pfree(xpath);
343
344 if (xpres == NULL)
347}
348
349
351
352Datum
354{
356 text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
357 xmlChar *xpath;
359 text *volatile xpres = NULL;
360 xpath_workspace *volatile workspace = NULL;
362
364
365 /*
366 * We encapsulate the supplied path with "string()" = 8 chars + 1 for NUL
367 * at end
368 */
369 /* We could try casting to string using the libxml function? */
370
371 xpath = (xmlChar *) palloc(pathsize + 9);
372 memcpy(xpath, "string(", 7);
374 xpath[pathsize + 7] = ')';
375 xpath[pathsize + 8] = '\0';
376
378
379 PG_TRY();
380 {
381 workspace = pgxml_xpath(document, xpath, xmlerrcxt);
382 xpres = pgxml_result_to_text(workspace->res, NULL, NULL, NULL);
383 }
384 PG_CATCH();
385 {
386 if (workspace)
387 cleanup_workspace(workspace);
388
389 pg_xml_done(xmlerrcxt, true);
390 PG_RE_THROW();
391 }
392 PG_END_TRY();
393
394 cleanup_workspace(workspace);
395 pg_xml_done(xmlerrcxt, false);
396
397 pfree(xpath);
398
399 if (xpres == NULL)
402}
403
404
406
407Datum
409{
411 text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
412 xmlChar *xpath;
413 volatile float4 fRes = 0.0;
414 volatile bool isNull = false;
415 xpath_workspace *volatile workspace = NULL;
417
420
421 PG_TRY();
422 {
423 workspace = pgxml_xpath(document, xpath, xmlerrcxt);
424 pfree(xpath);
425
426 if (workspace->res == NULL)
427 isNull = true;
428 else
429 fRes = xmlXPathCastToNumber(workspace->res);
430 }
431 PG_CATCH();
432 {
433 if (workspace)
434 cleanup_workspace(workspace);
435
436 pg_xml_done(xmlerrcxt, true);
437 PG_RE_THROW();
438 }
439 PG_END_TRY();
440
441 cleanup_workspace(workspace);
442 pg_xml_done(xmlerrcxt, false);
443
444 if (isNull || xmlXPathIsNaN(fRes))
446
448}
449
450
452
453Datum
455{
457 text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
458 xmlChar *xpath;
459 volatile int bRes = 0;
460 xpath_workspace *volatile workspace = NULL;
462
465
466 PG_TRY();
467 {
468 workspace = pgxml_xpath(document, xpath, xmlerrcxt);
469 pfree(xpath);
470
471 if (workspace->res == NULL)
472 bRes = 0;
473 else
474 bRes = xmlXPathCastToBoolean(workspace->res);
475 }
476 PG_CATCH();
477 {
478 if (workspace)
479 cleanup_workspace(workspace);
480
481 pg_xml_done(xmlerrcxt, true);
482 PG_RE_THROW();
483 }
484 PG_END_TRY();
485
486 cleanup_workspace(workspace);
487 pg_xml_done(xmlerrcxt, false);
488
490}
491
492
493
494/* Core function to evaluate XPath query */
495
496static xpath_workspace *
498{
502
503 workspace->doctree = NULL;
504 workspace->ctxt = NULL;
505 workspace->res = NULL;
506
507 PG_TRY();
508 {
509 workspace->doctree = xmlReadMemory((char *) VARDATA_ANY(document),
510 docsize, NULL, NULL,
512 if (workspace->doctree != NULL)
513 {
514 workspace->ctxt = xmlXPathNewContext(workspace->doctree);
515 if (workspace->ctxt == NULL)
517 "could not allocate XPath context");
518
519 workspace->ctxt->node = xmlDocGetRootElement(workspace->doctree);
520
521 /* compile the path */
522 comppath = xmlXPathCtxtCompile(workspace->ctxt, xpath);
525 "XPath Syntax Error");
526
527 /* Now evaluate the path expression. */
528 workspace->res = xmlXPathCompiledEval(comppath, workspace->ctxt);
529
531 comppath = NULL;
532 }
533 }
534 PG_CATCH();
535 {
536 if (comppath != NULL)
538 cleanup_workspace(workspace);
539
540 PG_RE_THROW();
541 }
542 PG_END_TRY();
543
544 return workspace;
545}
546
547/* Clean up after processing the result of pgxml_xpath() */
548static void
550{
551 if (workspace->res)
552 xmlXPathFreeObject(workspace->res);
553 workspace->res = NULL;
554 if (workspace->ctxt)
555 xmlXPathFreeContext(workspace->ctxt);
556 workspace->ctxt = NULL;
557 if (workspace->doctree)
558 xmlFreeDoc(workspace->doctree);
559 workspace->doctree = NULL;
560}
561
562static text *
567{
568 xmlChar *volatile xpresstr = NULL;
569 text *volatile xpres = NULL;
571
572 if (res == NULL)
573 return NULL;
574
575 /* spin some error handling */
577
578 PG_TRY();
579 {
580 switch (res->type)
581 {
582 case XPATH_NODESET:
583 xpresstr = pgxmlNodeSetToText(res->nodesetval,
584 toptag,
586 break;
587
588 case XPATH_STRING:
589 xpresstr = xmlStrdup(res->stringval);
592 "could not allocate result");
593 break;
594
595 default:
596 elog(NOTICE, "unsupported XQuery result: %d", res->type);
597 xpresstr = xmlStrdup((const xmlChar *) "<unsupported/>");
600 "could not allocate result");
601 }
602
603 /* Now convert this result back to text */
604 xpres = cstring_to_text((char *) xpresstr);
605 }
606 PG_CATCH();
607 {
608 if (xpresstr != NULL)
610
611 pg_xml_done(xmlerrcxt, true);
612
613 PG_RE_THROW();
614 }
615 PG_END_TRY();
616
617 /* Free various storage */
619
620 pg_xml_done(xmlerrcxt, false);
621
622 return xpres;
623}
624
625/*
626 * xpath_table is a table function. It needs some tidying (as do the
627 * other functions here!
628 */
630
631Datum
633{
634 /* Function parameters */
639 char *condition = text_to_cstring(PG_GETARG_TEXT_PP(4));
640
641 /* SPI (input tuple) support */
642 SPITupleTable *tuptable;
645
646
647 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
648 AttInMetadata *attinmeta;
649
650 char **values;
651 xmlChar **xpaths;
652 char *pos;
653 const char *pathsep = "|";
654
655 int numpaths;
656 int ret;
657 uint64 proc;
658 int j;
659 int rownr; /* For issuing multiple rows from one original
660 * document */
661 bool had_values; /* To determine end of nodeset results */
664 volatile xmlDocPtr doctree = NULL;
665 xmlXPathContextPtr volatile ctxt = NULL;
666 xmlXPathObjectPtr volatile res = NULL;
668 xmlChar *volatile resstr = NULL;
669
671
672 /* must have at least one output column (for the pkey) */
673 if (rsinfo->setDesc->natts < 1)
676 errmsg("xpath_table must have at least one output column")));
677
678 /*
679 * At the moment we assume that the returned attributes make sense for the
680 * XPath specified (i.e. we trust the caller). It's not fatal if they get
681 * it wrong - the input function for the column type will raise an error
682 * if the path result can't be converted into the correct binary
683 * representation.
684 */
685
686 attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
687
688 values = (char **) palloc0(rsinfo->setDesc->natts * sizeof(char *));
689 xpaths = (xmlChar **) palloc(rsinfo->setDesc->natts * sizeof(xmlChar *));
690
691 /*
692 * Split XPaths. xpathset is a writable CString.
693 *
694 * Note that we stop splitting once we've done all needed for tupdesc
695 */
696 numpaths = 0;
697 pos = xpathset;
698 while (numpaths < (rsinfo->setDesc->natts - 1))
699 {
700 xpaths[numpaths++] = (xmlChar *) pos;
701 pos = strstr(pos, pathsep);
702 if (pos != NULL)
703 {
704 *pos = '\0';
705 pos++;
706 }
707 else
708 break;
709 }
710
711 /* Now build query */
713
714 /* Build initial sql statement */
715 appendStringInfo(&query_buf, "SELECT %s, %s FROM %s WHERE %s",
716 pkeyfield,
717 xmlfield,
718 relname,
719 condition);
720
721 SPI_connect();
722
723 if ((ret = SPI_exec(query_buf.data, 0)) != SPI_OK_SELECT)
724 elog(ERROR, "xpath_table: SPI execution failed for query %s",
725 query_buf.data);
726
727 proc = SPI_processed;
728 tuptable = SPI_tuptable;
729 spi_tupdesc = tuptable->tupdesc;
730
731 /*
732 * Check that SPI returned correct result. If you put a comma into one of
733 * the function parameters, this will catch it when the SPI query returns
734 * e.g. 3 columns.
735 */
736 if (spi_tupdesc->natts != 2)
737 {
739 errmsg("expression returning multiple columns is not valid in parameter list"),
740 errdetail("Expected two columns in SPI result, got %d.", spi_tupdesc->natts)));
741 }
742
743 /*
744 * Setup the parser. This should happen after we are done evaluating the
745 * query, in case it calls functions that set up libxml differently.
746 */
748
749 PG_TRY();
750 {
751 /* For each row i.e. document returned from SPI */
752 uint64 i;
753
754 for (i = 0; i < proc; i++)
755 {
756 char *pkey;
757 char *xmldoc;
759
760 /* Extract the row data as C Strings */
761 spi_tuple = tuptable->vals[i];
764
765 /*
766 * Clear the values array, so that not-well-formed documents
767 * return NULL in all columns. Note that this also means that
768 * spare columns will be NULL.
769 */
770 for (j = 0; j < rsinfo->setDesc->natts; j++)
771 values[j] = NULL;
772
773 /* Insert primary key */
774 values[0] = pkey;
775
776 /* Parse the document */
777 if (xmldoc)
778 doctree = xmlReadMemory(xmldoc, strlen(xmldoc),
779 NULL, NULL,
781 else /* treat NULL as not well-formed */
782 doctree = NULL;
783
784 if (doctree == NULL)
785 {
786 /* not well-formed, so output all-NULL tuple */
790 }
791 else
792 {
793 /* New loop here - we have to deal with nodeset results */
794 rownr = 0;
795
796 do
797 {
798 /* Now evaluate the set of xpaths. */
799 had_values = false;
800 for (j = 0; j < numpaths; j++)
801 {
802 ctxt = NULL;
803 res = NULL;
804 comppath = NULL;
805 resstr = NULL;
806
807 ctxt = xmlXPathNewContext(doctree);
808 if (ctxt == NULL || pg_xml_error_occurred(xmlerrcxt))
811 "could not allocate XPath context");
812
813 ctxt->node = xmlDocGetRootElement(doctree);
814
815 /* compile the path */
820 "XPath Syntax Error");
821
822 /* Now evaluate the path expression. */
823 res = xmlXPathCompiledEval(comppath, ctxt);
825 comppath = NULL;
826
827 if (res != NULL)
828 {
829 switch (res->type)
830 {
831 case XPATH_NODESET:
832 /* We see if this nodeset has enough nodes */
833 if (res->nodesetval != NULL &&
835 {
836 resstr = xmlXPathCastNodeToString(res->nodesetval->nodeTab[rownr]);
840 "could not allocate result");
841 had_values = true;
842 }
843 else
844 resstr = NULL;
845
846 break;
847
848 case XPATH_STRING:
849 resstr = xmlStrdup(res->stringval);
853 "could not allocate result");
854 break;
855
856 default:
857 elog(NOTICE, "unsupported XQuery result: %d", res->type);
858 resstr = xmlStrdup((const xmlChar *) "<unsupported/>");
862 "could not allocate result");
863 }
864
865 /*
866 * Insert this into the appropriate column in the
867 * result tuple.
868 */
869 values[j + 1] = (char *) resstr;
870 resstr = NULL;
871 }
872
873 if (res != NULL)
874 {
876 res = NULL;
877 }
879 ctxt = NULL;
880 }
881
882 /* Now add the tuple to the output, if there is one. */
883 if (had_values)
884 {
888 }
889
890 /* BuildTupleFromCStrings() has copied the values. */
891 for (j = 1; j < rsinfo->setDesc->natts; j++)
892 {
893 if (values[j] != NULL)
894 {
895 xmlFree((xmlChar *) values[j]);
896 values[j] = NULL;
897 }
898 }
899
900 rownr++;
901 } while (had_values);
902 }
903
904 if (doctree != NULL)
905 xmlFreeDoc(doctree);
906 doctree = NULL;
907
908 if (pkey)
909 pfree(pkey);
910 if (xmldoc)
911 pfree(xmldoc);
912 }
913 }
914 PG_CATCH();
915 {
916 if (resstr != NULL)
918 for (j = 1; j < rsinfo->setDesc->natts; j++)
919 {
920 if (values[j] != NULL)
921 xmlFree((xmlChar *) values[j]);
922 }
923 if (res != NULL)
925 if (comppath != NULL)
927 if (ctxt != NULL)
929 if (doctree != NULL)
930 xmlFreeDoc(doctree);
931
932 pg_xml_done(xmlerrcxt, true);
933
934 PG_RE_THROW();
935 }
936 PG_END_TRY();
937
938 if (doctree != NULL)
939 xmlFreeDoc(doctree);
940
941 pg_xml_done(xmlerrcxt, false);
942
943 SPI_finish();
944
945 /*
946 * SFRM_Materialize mode expects us to return a NULL Datum. The actual
947 * tuples are in our tuplestore and passed back through rsinfo->setResult.
948 * rsinfo->setDesc is set to the tuple description that we actually used
949 * to build our tuples with, so the caller can verify we did what it was
950 * expecting.
951 */
952 return (Datum) 0;
953}
static Datum values[MAXATTR]
Definition bootstrap.c:190
int32_t int32
Definition c.h:620
uint64_t uint64
Definition c.h:625
float float4
Definition c.h:713
uint32 result
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
int errcode(int sqlerrcode)
Definition elog.c:875
#define PG_RE_THROW()
Definition elog.h:407
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define PG_TRY(...)
Definition elog.h:374
#define PG_END_TRY(...)
Definition elog.h:399
#define ERROR
Definition elog.h:40
#define PG_CATCH(...)
Definition elog.h:384
#define elog(elevel,...)
Definition elog.h:228
#define NOTICE
Definition elog.h:36
#define ereport(elevel,...)
Definition elog.h:152
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
#define palloc0_object(type)
Definition fe_memutils.h:90
#define PG_GETARG_TEXT_PP(n)
Definition fmgr.h:310
#define PG_MODULE_MAGIC_EXT(...)
Definition fmgr.h:540
#define PG_RETURN_NULL()
Definition fmgr.h:346
#define PG_FUNCTION_INFO_V1(funcname)
Definition fmgr.h:417
#define PG_RETURN_TEXT_P(x)
Definition fmgr.h:374
#define PG_RETURN_FLOAT4(x)
Definition fmgr.h:368
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition fmgr.h:360
void InitMaterializedSRF(FunctionCallInfo fcinfo, uint32 flags)
Definition funcapi.c:76
#define MAT_SRF_USE_EXPECTED_DESC
Definition funcapi.h:296
const char * str
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
int j
Definition isn.c:78
int i
Definition isn.c:77
void pfree(void *pointer)
Definition mcxt.c:1619
void * palloc0(Size size)
Definition mcxt.c:1420
void * palloc(Size size)
Definition mcxt.c:1390
static char * errmsg
NameData relname
Definition pg_class.h:40
static char buf[DEFAULT_XLOG_SEG_SIZE]
uint64_t Datum
Definition postgres.h:70
static int fb(int x)
uint64 SPI_processed
Definition spi.c:45
SPITupleTable * SPI_tuptable
Definition spi.c:46
int SPI_connect(void)
Definition spi.c:95
int SPI_finish(void)
Definition spi.c:183
int SPI_exec(const char *src, long tcount)
Definition spi.c:631
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
Definition spi.c:1221
#define SPI_OK_SELECT
Definition spi.h:86
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
TupleDesc tupdesc
Definition spi.h:25
HeapTuple * vals
Definition spi.h:26
Definition c.h:776
xmlXPathContextPtr ctxt
Definition xpath.c:40
xmlDocPtr doctree
Definition xpath.c:39
xmlXPathObjectPtr res
Definition xpath.c:41
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
Definition tuplestore.c:765
static Size VARSIZE_ANY_EXHDR(const void *PTR)
Definition varatt.h:472
static char * VARDATA_ANY(const void *PTR)
Definition varatt.h:486
text * cstring_to_text(const char *s)
Definition varlena.c:184
char * text_to_cstring(const text *t)
Definition varlena.c:217
const char * name
Datum xpath(PG_FUNCTION_ARGS)
Definition xml.c:4570
struct PgXmlErrorContext PgXmlErrorContext
Definition xml.h:48
PgXmlErrorContext * pg_xml_init(PgXmlStrictness strictness)
void xml_ereport(PgXmlErrorContext *errcxt, int level, int sqlcode, const char *msg)
bool pg_xml_error_occurred(PgXmlErrorContext *errcxt)
void pg_xml_done(PgXmlErrorContext *errcxt, bool isError)
PgXmlStrictness
Definition xml.h:40
@ PG_XML_STRICTNESS_LEGACY
Definition xml.h:41
@ PG_XML_STRICTNESS_ALL
Definition xml.h:44
static text * pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag, xmlChar *septag, xmlChar *plainsep)
Definition xpath.c:563
Datum xpath_bool(PG_FUNCTION_ARGS)
Definition xpath.c:454
Datum xpath_number(PG_FUNCTION_ARGS)
Definition xpath.c:408
Datum xpath_table(PG_FUNCTION_ARGS)
Definition xpath.c:632
static xmlChar * pgxml_texttoxmlchar(text *textstring)
Definition xpath.c:251
PgXmlErrorContext * pgxml_parser_init(PgXmlStrictness strictness)
Definition xpath.c:68
Datum xpath_string(PG_FUNCTION_ARGS)
Definition xpath.c:353
static void cleanup_workspace(xpath_workspace *workspace)
Definition xpath.c:549
Datum xml_encode_special_chars(PG_FUNCTION_ARGS)
Definition xpath.c:89
Datum xpath_list(PG_FUNCTION_ARGS)
Definition xpath.c:311
static xmlChar * pgxmlNodeSetToText(xmlNodeSetPtr nodeset, xmlChar *toptagname, xmlChar *septagname, xmlChar *plainsep)
Definition xpath.c:143
static xpath_workspace * pgxml_xpath(text *document, xmlChar *xpath, PgXmlErrorContext *xmlerrcxt)
Definition xpath.c:497
Datum xpath_nodeset(PG_FUNCTION_ARGS)
Definition xpath.c:265