4 * Parser interface for DOM-based parser (libxml) rather than
5 * stream-based SAX-type parser
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>
30/* exported for use by xslt_proc.c */
34/* workspace for pgxml_xpath() */
40 xmlXPathObjectPtr
res;
43/* local declarations */
46 xmlChar *toptagname, xmlChar *septagname,
50 xmlChar *septag, xmlChar *plainsep);
61 * Initialize for xml parsing.
63 * As with the underlying pg_xml_init function, calls to this MUST be followed
64 * by a PG_TRY block that guarantees that pg_xml_done is called.
71 /* Set up error handling (we share the core's error handler) */
74 /* Note: we're assuming an elog cannot be thrown by the following calls */
76 /* Initialize libxml */
83/* Encodes special characters (<, >, &, " and \r) as XML entities */
91 text *
volatile tout = NULL;
92 xmlChar *
volatile tt = NULL;
103 tt = xmlEncodeSpecialChars(NULL, ts);
106 "could not allocate xmlChar");
131 * Function translates a nodeset into a text representation
133 * iterates over each node in the set and calls xmlNodeDump to write it to
134 * an xmlBuffer -from which an xmlChar * string is returned.
136 * each representation is surrounded by <tagname> ... </tagname>
138 * plainsep is an ordinary (not tag) separator - if used, then nodes are
139 * cast to string as output method
147 volatile xmlBufferPtr
buf = NULL;
148 xmlChar *
volatile result = NULL;
151 /* spin up some error handling */
156 buf = xmlBufferCreate();
160 "could not allocate xmlBuffer");
162 if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
164 xmlBufferWriteChar(
buf,
"<");
165 xmlBufferWriteCHAR(
buf, toptagname);
166 xmlBufferWriteChar(
buf,
">");
170 for (
int i = 0;
i < nodeset->nodeNr;
i++)
172 if (plainsep != NULL)
174 xmlBufferWriteCHAR(
buf,
175 xmlXPathCastNodeToString(nodeset->nodeTab[
i]));
177 /* If this isn't the last entry, write the plain sep. */
178 if (
i < (nodeset->nodeNr) - 1)
179 xmlBufferWriteChar(
buf, (
char *) plainsep);
183 if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
185 xmlBufferWriteChar(
buf,
"<");
186 xmlBufferWriteCHAR(
buf, septagname);
187 xmlBufferWriteChar(
buf,
">");
190 nodeset->nodeTab[
i]->doc,
194 if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
196 xmlBufferWriteChar(
buf,
"</");
197 xmlBufferWriteCHAR(
buf, septagname);
198 xmlBufferWriteChar(
buf,
">");
204 if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
206 xmlBufferWriteChar(
buf,
"</");
207 xmlBufferWriteCHAR(
buf, toptagname);
208 xmlBufferWriteChar(
buf,
">");
211 result = xmlStrdup(xmlBufferContent(
buf));
214 "could not allocate result");
234/* Translate a PostgreSQL "varlena" -i.e. a variable length parameter
235 * into the libxml2 representation
243/* Publicly visible XPath functions */
246 * This is a "raw" xpath function. Check that it returns child elements
259 text *
volatile xpres = NULL;
292 * The following function is almost identical, but returns the elements in
304 text *
volatile xpres = NULL;
346 text *
volatile xpres = NULL;
353 * We encapsulate the supplied path with "string()" = 8 chars + 1 for NUL
356 /* We could try casting to string using the libxml function? */
359 memcpy(
xpath,
"string(", 7);
361 xpath[pathsize + 7] =
')';
362 xpath[pathsize + 8] =
'0円';
400 volatile float4 fRes = 0.0;
401 volatile bool isNull =
false;
413 if (workspace->
res == NULL)
416 fRes = xmlXPathCastToNumber(workspace->
res);
431 if (isNull || xmlXPathIsNaN(fRes))
446 volatile int bRes = 0;
458 if (workspace->
res == NULL)
461 bRes = xmlXPathCastToBoolean(workspace->
res);
481/* Core function to evaluate XPath query */
487 xmlXPathCompExprPtr comppath;
492 workspace->
ctxt = NULL;
493 workspace->
res = NULL;
498 if (workspace->
doctree != NULL)
500 workspace->
ctxt = xmlXPathNewContext(workspace->
doctree);
501 workspace->
ctxt->node = xmlDocGetRootElement(workspace->
doctree);
503 /* compile the path */
504 comppath = xmlXPathCtxtCompile(workspace->
ctxt,
xpath);
507 "XPath Syntax Error");
509 /* Now evaluate the path expression. */
510 workspace->
res = xmlXPathCompiledEval(comppath, workspace->
ctxt);
512 xmlXPathFreeCompExpr(comppath);
518/* Clean up after processing the result of pgxml_xpath() */
523 xmlXPathFreeObject(workspace->
res);
524 workspace->
res = NULL;
526 xmlXPathFreeContext(workspace->
ctxt);
527 workspace->
ctxt = NULL;
529 xmlFreeDoc(workspace->
doctree);
539 xmlChar *
volatile xpresstr = NULL;
540 text *
volatile xpres = NULL;
546 /* spin some error handling */
560 xpresstr = xmlStrdup(res->stringval);
563 "could not allocate result");
567 elog(
NOTICE,
"unsupported XQuery result: %d", res->type);
568 xpresstr = xmlStrdup((
const xmlChar *)
"<unsupported/>");
571 "could not allocate result");
574 /* Now convert this result back to text */
579 if (xpresstr != NULL)
588 /* Free various storage */
597 * xpath_table is a table function. It needs some tidying (as do the
598 * other functions here!
605 /* Function parameters */
612 /* SPI (input tuple) support */
624 const char *pathsep =
"|";
630 int rownr;
/* For issuing multiple rows from one original
632 bool had_values;
/* To determine end of nodeset results */
635 volatile xmlDocPtr doctree = NULL;
639 /* must have at least one output column (for the pkey) */
642 (
errcode(ERRCODE_SYNTAX_ERROR),
643 errmsg(
"xpath_table must have at least one output column")));
646 * At the moment we assume that the returned attributes make sense for the
647 * XPath specified (i.e. we trust the caller). It's not fatal if they get
648 * it wrong - the input function for the column type will raise an error
649 * if the path result can't be converted into the correct binary
659 * Split XPaths. xpathset is a writable CString.
661 * Note that we stop splitting once we've done all needed for tupdesc
667 xpaths[numpaths++] = (xmlChar *) pos;
668 pos = strstr(pos, pathsep);
678 /* Now build query */
681 /* Build initial sql statement */
691 elog(
ERROR,
"xpath_table: SPI execution failed for query %s",
696 spi_tupdesc = tuptable->
tupdesc;
699 * Check that SPI returned correct result. If you put a comma into one of
700 * the function parameters, this will catch it when the SPI query returns
703 if (spi_tupdesc->
natts != 2)
706 errmsg(
"expression returning multiple columns is not valid in parameter list"),
707 errdetail(
"Expected two columns in SPI result, got %d.", spi_tupdesc->
natts)));
711 * Setup the parser. This should happen after we are done evaluating the
712 * query, in case it calls functions that set up libxml differently.
718 /* For each row i.e. document returned from SPI */
721 for (
i = 0;
i < proc;
i++)
725 xmlXPathContextPtr ctxt;
726 xmlXPathObjectPtr res;
728 xmlXPathCompExprPtr comppath;
731 /* Extract the row data as C Strings */
732 spi_tuple = tuptable->
vals[
i];
737 * Clear the values array, so that not-well-formed documents
738 * return NULL in all columns. Note that this also means that
739 * spare columns will be NULL.
744 /* Insert primary key */
747 /* Parse the document */
749 doctree = xmlReadMemory(xmldoc, strlen(xmldoc),
752 else /* treat NULL as not well-formed */
757 /* not well-formed, so output all-NULL tuple */
764 /* New loop here - we have to deal with nodeset results */
769 /* Now evaluate the set of xpaths. */
771 for (
j = 0;
j < numpaths;
j++)
773 ctxt = xmlXPathNewContext(doctree);
776 ERROR, ERRCODE_OUT_OF_MEMORY,
777 "could not allocate XPath context");
779 ctxt->node = xmlDocGetRootElement(doctree);
781 /* compile the path */
782 comppath = xmlXPathCtxtCompile(ctxt, xpaths[
j]);
785 ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
786 "XPath Syntax Error");
788 /* Now evaluate the path expression. */
789 res = xmlXPathCompiledEval(comppath, ctxt);
790 xmlXPathFreeCompExpr(comppath);
797 /* We see if this nodeset has enough nodes */
798 if (res->nodesetval != NULL &&
799 rownr < res->nodesetval->nodeNr)
801 resstr = xmlXPathCastNodeToString(res->nodesetval->nodeTab[rownr]);
804 ERROR, ERRCODE_OUT_OF_MEMORY,
805 "could not allocate result");
814 resstr = xmlStrdup(res->stringval);
817 ERROR, ERRCODE_OUT_OF_MEMORY,
818 "could not allocate result");
822 elog(
NOTICE,
"unsupported XQuery result: %d", res->type);
823 resstr = xmlStrdup((
const xmlChar *)
"<unsupported/>");
826 ERROR, ERRCODE_OUT_OF_MEMORY,
827 "could not allocate result");
831 * Insert this into the appropriate column in the
834 values[
j + 1] = (
char *) resstr;
836 xmlXPathFreeContext(ctxt);
839 /* Now add the tuple to the output, if there is one. */
848 }
while (had_values);
880 * SFRM_Materialize mode expects us to return a NULL Datum. The actual
881 * tuples are in our tuplestore and passed back through rsinfo->setResult.
882 * rsinfo->setDesc is set to the tuple description that we actually used
883 * to build our tuples with, so the caller can verify we did what it was
static Datum values[MAXATTR]
int errdetail(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
#define PG_GETARG_TEXT_PP(n)
#define PG_RETURN_TEXT_P(x)
#define PG_RETURN_FLOAT4(x)
#define PG_RETURN_BOOL(x)
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
#define MAT_SRF_USE_EXPECTED_DESC
void heap_freetuple(HeapTuple htup)
void pfree(void *pointer)
void * palloc0(Size size)
SPITupleTable * SPI_tuptable
int SPI_exec(const char *src, long tcount)
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
void appendStringInfo(StringInfo str, const char *fmt,...)
void initStringInfo(StringInfo str)
Tuplestorestate * setResult
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
static Size VARSIZE_ANY_EXHDR(const void *PTR)
static char * VARDATA_ANY(const void *PTR)
text * cstring_to_text(const char *s)
char * text_to_cstring(const text *t)
Datum xpath(PG_FUNCTION_ARGS)
struct PgXmlErrorContext PgXmlErrorContext
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)
@ PG_XML_STRICTNESS_LEGACY
static text * pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag, xmlChar *septag, xmlChar *plainsep)
Datum xpath_bool(PG_FUNCTION_ARGS)
Datum xpath_number(PG_FUNCTION_ARGS)
Datum xpath_table(PG_FUNCTION_ARGS)
static xmlChar * pgxml_texttoxmlchar(text *textstring)
PgXmlErrorContext * pgxml_parser_init(PgXmlStrictness strictness)
Datum xpath_string(PG_FUNCTION_ARGS)
static void cleanup_workspace(xpath_workspace *workspace)
Datum xml_encode_special_chars(PG_FUNCTION_ARGS)
Datum xpath_list(PG_FUNCTION_ARGS)
static xmlChar * pgxmlNodeSetToText(xmlNodeSetPtr nodeset, xmlChar *toptagname, xmlChar *septagname, xmlChar *plainsep)
static xpath_workspace * pgxml_xpath(text *document, xmlChar *xpath, PgXmlErrorContext *xmlerrcxt)
PG_MODULE_MAGIC_EXT(.name="xml2",.version=PG_VERSION)
Datum xpath_nodeset(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(xml_encode_special_chars)