aboutsummaryrefslogtreecommitdiff
path: root/contrib/xml/pgxml.c
diff options
context:
space:
mode:
authorBruce Momjian <bruce@momjian.us>2004-03-05 03:57:58 +0000
committerBruce Momjian <bruce@momjian.us>2004-03-05 03:57:58 +0000
commit31f4b59a464808ab0fec0ffb2eaa723321ea1af7 (patch)
tree004f71d1eb77899fa9e16ac8047189dcde6576e5 /contrib/xml/pgxml.c
parentadca025c9ec4b3050411eb74a5b4f9c20a4ce2b5 (diff)
downloadpostgresql-31f4b59a464808ab0fec0ffb2eaa723321ea1af7.tar.gz
postgresql-31f4b59a464808ab0fec0ffb2eaa723321ea1af7.zip
Move new version of contrib/ xml into xml2, keep old version in /xml.
Diffstat (limited to 'contrib/xml/pgxml.c')
-rw-r--r--contrib/xml/pgxml.c352
1 files changed, 352 insertions, 0 deletions
diff --git a/contrib/xml/pgxml.c b/contrib/xml/pgxml.c
new file mode 100644
index 00000000000..4d8c3b96bcf
--- /dev/null
+++ b/contrib/xml/pgxml.c
@@ -0,0 +1,352 @@
+/********************************************************
+ * Interface code to parse an XML document using expat
+ ********************************************************/
+
+#include "postgres.h"
+#include "fmgr.h"
+
+#include "expat.h"
+#include "pgxml.h"
+
+/* Memory management - we make expat use standard pg MM */
+
+XML_Memory_Handling_Suite mhs;
+
+/* passthrough functions (palloc is a macro) */
+
+static void *
+pgxml_palloc(size_t size)
+{
+ return palloc(size);
+}
+
+static void *
+pgxml_repalloc(void *ptr, size_t size)
+{
+ return repalloc(ptr, size);
+}
+
+static void
+pgxml_pfree(void *ptr)
+{
+ return pfree(ptr);
+}
+
+static void
+pgxml_mhs_init()
+{
+ mhs.malloc_fcn = pgxml_palloc;
+ mhs.realloc_fcn = pgxml_repalloc;
+ mhs.free_fcn = pgxml_pfree;
+}
+
+static void
+pgxml_handler_init()
+{
+ /*
+ * This code should set up the relevant handlers from user-supplied
+ * settings. Quite how these settings are made is another matter :)
+ */
+}
+
+/* Returns true if document is well-formed */
+
+PG_FUNCTION_INFO_V1(pgxml_parse);
+
+Datum
+pgxml_parse(PG_FUNCTION_ARGS)
+{
+ /* called as pgxml_parse(document) */
+ XML_Parser p;
+ text *t = PG_GETARG_TEXT_P(0); /* document buffer */
+ int32 docsize = VARSIZE(t) - VARHDRSZ;
+
+ pgxml_mhs_init();
+
+ pgxml_handler_init();
+
+ p = XML_ParserCreate_MM(NULL, &mhs, NULL);
+ if (!p)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+ errmsg("could not create expat parser")));
+ PG_RETURN_NULL(); /* seems appropriate if we couldn't parse */
+ }
+
+ if (!XML_Parse(p, (char *) VARDATA(t), docsize, 1))
+ {
+ /*
+ * elog(WARNING, "Parse error at line %d:%s",
+ * XML_GetCurrentLineNumber(p),
+ * XML_ErrorString(XML_GetErrorCode(p)));
+ */
+ XML_ParserFree(p);
+ PG_RETURN_BOOL(false);
+ }
+
+ XML_ParserFree(p);
+ PG_RETURN_BOOL(true);
+}
+
+/* XPath handling functions */
+
+/* XPath support here is for a very skeletal kind of XPath!
+ It was easy to program though... */
+
+/* This first is the core function that builds a result set. The
+ actual functions called by the user manipulate that result set
+ in various ways.
+*/
+
+static XPath_Results *
+build_xpath_results(text *doc, text *pathstr)
+{
+ XPath_Results *xpr;
+ char *res;
+ pgxml_udata *udata;
+ XML_Parser p;
+ int32 docsize;
+
+ xpr = (XPath_Results *) palloc((sizeof(XPath_Results)));
+ memset((void *) xpr, 0, sizeof(XPath_Results));
+ xpr->rescount = 0;
+
+ docsize = VARSIZE(doc) - VARHDRSZ;
+
+ /* res isn't going to be the real return type, it is just a buffer */
+
+ res = (char *) palloc(docsize);
+ memset((void *) res, 0, docsize);
+
+ xpr->resbuf = res;
+
+ udata = (pgxml_udata *) palloc((sizeof(pgxml_udata)));
+ memset((void *) udata, 0, sizeof(pgxml_udata));
+
+ udata->currentpath[0] = '\0';
+ udata->textgrab = 0;
+
+ udata->path = (char *) palloc(VARSIZE(pathstr));
+ memcpy(udata->path, VARDATA(pathstr), VARSIZE(pathstr) - VARHDRSZ);
+
+ udata->path[VARSIZE(pathstr) - VARHDRSZ] = '\0';
+
+ udata->resptr = res;
+ udata->reslen = 0;
+
+ udata->xpres = xpr;
+
+ /* Now fire up the parser */
+ pgxml_mhs_init();
+
+ p = XML_ParserCreate_MM(NULL, &mhs, NULL);
+ if (!p)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+ errmsg("could not create expat parser")));
+ pfree(xpr);
+ pfree(udata->path);
+ pfree(udata);
+ pfree(res);
+ return NULL;
+ }
+ XML_SetUserData(p, (void *) udata);
+
+ /* Set the handlers */
+
+ XML_SetElementHandler(p, pgxml_starthandler, pgxml_endhandler);
+ XML_SetCharacterDataHandler(p, pgxml_charhandler);
+
+ if (!XML_Parse(p, (char *) VARDATA(doc), docsize, 1))
+ {
+ /*
+ * elog(WARNING, "Parse error at line %d:%s",
+ * XML_GetCurrentLineNumber(p),
+ * XML_ErrorString(XML_GetErrorCode(p)));
+ */
+ XML_ParserFree(p);
+ pfree(xpr);
+ pfree(udata->path);
+ pfree(udata);
+
+ return NULL;
+ }
+
+ pfree(udata->path);
+ pfree(udata);
+ XML_ParserFree(p);
+ return xpr;
+}
+
+
+PG_FUNCTION_INFO_V1(pgxml_xpath);
+
+Datum
+pgxml_xpath(PG_FUNCTION_ARGS)
+{
+ /* called as pgxml_xpath(document,pathstr, index) for the moment */
+
+ XPath_Results *xpresults;
+ text *restext;
+
+ text *t = PG_GETARG_TEXT_P(0); /* document buffer */
+ text *t2 = PG_GETARG_TEXT_P(1);
+ int32 ind = PG_GETARG_INT32(2) - 1;
+
+ xpresults = build_xpath_results(t, t2);
+
+ /*
+ * This needs to be changed depending on the mechanism for returning
+ * our set of results.
+ */
+
+ if (xpresults == NULL) /* parse error (not WF or parser failure) */
+ PG_RETURN_NULL();
+
+ if (ind >= (xpresults->rescount))
+ PG_RETURN_NULL();
+
+ restext = (text *) palloc(xpresults->reslens[ind] + VARHDRSZ);
+ memcpy(VARDATA(restext), xpresults->results[ind], xpresults->reslens[ind]);
+
+ VARATT_SIZEP(restext) = xpresults->reslens[ind] + VARHDRSZ;
+
+ pfree(xpresults->resbuf);
+ pfree(xpresults);
+
+ PG_RETURN_TEXT_P(restext);
+}
+
+
+static void
+pgxml_pathcompare(void *userData)
+{
+ char *matchpos;
+
+ matchpos = strstr(UD->currentpath, UD->path);
+
+ if (matchpos == NULL)
+ { /* Should we have more logic here ? */
+ if (UD->textgrab)
+ {
+ UD->textgrab = 0;
+ pgxml_finalisegrabbedtext(userData);
+ }
+ return;
+ }
+
+ /*
+ * OK, we have a match of some sort. Now we need to check that our
+ * match is anchored to the *end* of the string AND that it is
+ * immediately preceded by a '/'
+ */
+
+ /*
+ * This test wouldn't work if strlen (UD->path) overran the length of
+ * the currentpath, but that's not possible because we got a match!
+ */
+
+ if ((matchpos + strlen(UD->path))[0] == '\0')
+ {
+ if ((UD->path)[0] == '/')
+ {
+ if (matchpos == UD->currentpath)
+ UD->textgrab = 1;
+ }
+ else
+ {
+ if ((matchpos - 1)[0] == '/')
+ UD->textgrab = 1;
+ }
+ }
+}
+
+static void
+pgxml_starthandler(void *userData, const XML_Char * name,
+ const XML_Char ** atts)
+{
+
+ char sepstr[] = "/";
+
+ if ((strlen(name) + strlen(UD->currentpath)) > MAXPATHLENGTH - 2)
+ elog(WARNING, "path too long");
+ else
+ {
+ strncat(UD->currentpath, sepstr, 1);
+ strcat(UD->currentpath, name);
+ }
+ if (UD->textgrab)
+ {
+ /*
+ * Depending on user preference, should we "reconstitute" the
+ * element into the result text?
+ */
+ }
+ else
+ pgxml_pathcompare(userData);
+}
+
+static void
+pgxml_endhandler(void *userData, const XML_Char * name)
+{
+ /*
+ * Start by removing the current element off the end of the
+ * currentpath
+ */
+
+ char *sepptr;
+
+ sepptr = strrchr(UD->currentpath, '/');
+ if (sepptr == NULL)
+ {
+ /* internal error */
+ elog(ERROR, "did not find '/'");
+ sepptr = UD->currentpath;
+ }
+ if (strcmp(name, sepptr + 1) != 0)
+ {
+ elog(WARNING, "wanted [%s], got [%s]", sepptr, name);
+ /* unmatched entry, so do nothing */
+ }
+ else
+ {
+ sepptr[0] = '\0'; /* Chop that element off the end */
+ }
+
+ if (UD->textgrab)
+ pgxml_pathcompare(userData);
+
+}
+
+static void
+pgxml_charhandler(void *userData, const XML_Char * s, int len)
+{
+ if (UD->textgrab)
+ {
+ if (len > 0)
+ {
+ memcpy(UD->resptr, s, len);
+ UD->resptr += len;
+ UD->reslen += len;
+ }
+ }
+}
+
+/* Should I be using PG list types here? */
+
+static void
+pgxml_finalisegrabbedtext(void *userData)
+{
+ /* In res/reslen, we have a single result. */
+ UD->xpres->results[UD->xpres->rescount] = UD->resptr - UD->reslen;
+ UD->xpres->reslens[UD->xpres->rescount] = UD->reslen;
+ UD->reslen = 0;
+ UD->xpres->rescount++;
+
+ /*
+ * This effectively concatenates all the results together but we do
+ * know where one ends and the next begins
+ */
+}