X-Original-To: pgsql-sql-postgresql.org@localhost.postgresql.org Received: from localhost (unknown [200.46.204.144]) by svr1.postgresql.org (Postfix) with ESMTP id D600C8B9F77 for ; Mon, 21 Feb 2005 14:55:56 +0000 (GMT) Received: from svr1.postgresql.org ([200.46.204.71]) by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) with ESMTP id 92384-08 for ; Mon, 21 Feb 2005 14:55:47 +0000 (GMT) Received: from pd7mo4no.prod.shaw.ca (idcmail-mo2no.cg.shawcable.net [64.59.134.9]) by svr1.postgresql.org (Postfix) with ESMTP id 332318BAFE7 for ; Mon, 21 Feb 2005 14:55:49 +0000 (GMT) Received: from pd6mr8no.prod.shaw.ca (pd6mr8no-qfe2.prod.shaw.ca [10.0.144.221]) by l-daemon (Sun ONE Messaging Server 6.0 HotFix 1.01 (built Mar 15 2004)) with ESMTP id <0IC900ILPO4Z5UC0@l-daemon> for pgsql-sql@postgresql.org; Mon, 21 Feb 2005 07:55:47 -0700 (MST) Received: from pn7ml4no.prod.shaw.ca ([10.0.149.113]) by pd6mr8no.prod.shaw.ca (Sun ONE Messaging Server 6.0 HotFix 1.01 (built Mar 15 2004)) with ESMTP id <0IC900B9RO4ZS3N0@pd6mr8no.prod.shaw.ca> for pgsql-sql@postgresql.org; Mon, 21 Feb 2005 07:55:47 -0700 (MST) Received: from Dell4500 (S01060004236fc1f7.wp.shawcable.net [24.77.13.13]) by l-daemon (iPlanet Messaging Server 5.2 HotFix 1.18 (built Jul 28 2003)) with SMTP id <0IC90013GO4RX3@l-daemon> for pgsql-sql@postgresql.org; Mon, 21 Feb 2005 07:55:47 -0700 (MST) Date: Mon, 21 Feb 2005 08:55:39 -0600 From: George Weaver Subject: Re: Working with XML. To: Theo Galanakis , pgsql-sql@postgresql.org Message-id: <005601c51825$6b430ca0$6400a8c0@Dell4500> MIME-version: 1.0 X-MIMEOLE: Produced By Microsoft MimeOLE V6.00.2900.2180 X-Mailer: Microsoft Outlook Express 6.00.2900.2180 Content-type: multipart/mixed; boundary="----=_NextPart_000_0052_01C517F3.1DA8F130" X-Priority: 3 X-MSMail-priority: Normal References: X-Virus-Scanned: by amavisd-new at hub.org X-Spam-Status: No, hits=1.548 tagged_above=0 required=5 tests=AWL, DNS_FROM_RFC_ABUSE, DNS_FROM_RFC_POST, FORGED_RCVD_HELO, HTML_30_40, HTML_MESSAGE X-Spam-Level: * X-Archive-Number: 200502/223 X-Sequence-Number: 20334 This is a multi-part message in MIME format. ------=_NextPart_000_0052_01C517F3.1DA8F130 Content-Type: multipart/alternative; boundary="----=_NextPart_001_0053_01C517F3.1DAB3B20" ------=_NextPart_001_0053_01C517F3.1DAB3B20 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable MessageHi Theo, There have been 2 major changes between the 7 and 8 versions that affect = the coding in xml2. You need to edit the source code in order for it to = compile properly on 7. First, work_mem has to be changed to SortMem (line 666). I.e. tupstore =3D tuplestore_begin_heap(true, false, work_mem); should be changed to: tupstore =3D tuplestore_begin_heap(true, SortMem); Second, the error reporting framework has changed.=20 To fix this, you need to change all the ereport coding to use elog. = I.e. ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("xpath_table must be called as a table = function"))); should be changed to: elog(ERROR,"xpath_table must be called as a table function"); I have attached an edited copy of xpath.c with these changes, if you = would like to work with it. Regards, George ----- Original Message -----=20 From: Theo Galanakis=20 To: 'pgsql-sql@postgresql.org'=20 Sent: Sunday, February 20, 2005 11:28 PM Subject: FW: [SQL] Working with XML. Hi,=20 I have copied all the files manually from = http://developer.postgresql.org/docs/pgsql/contrib/ for the xml2 = contribution. However I have the following issue when I attempt to = compile with gmake: gcc -I/usr/include/libxml2 -fpic -I. -I../../src/include -D_GNU_SOURCE = -c -o xpath.o xpath.c xpath.c: In function `xpath_table': xpath.c:689: `work_mem' undeclared (first use in this function) xpath.c:689: (Each undeclared identifier is reported only once xpath.c:689: for each function it appears in.) gmake: *** [xpath.o] Error 1 I have installed : libxml2-devel-2.5.10-1.rpm What am I doing wrong, or can someone point me to the direction of a = binary for XML2 on RedHat ES3, Postgres 7.4.5. Cheers, =20 Theo ------=_NextPart_001_0053_01C517F3.1DAB3B20 Content-Type: text/html; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Message
Hi Theo,

There have been 2 major = changes=20 between the 7 and 8 versions that affect the coding in xml2.  You = need to=20 edit the source code in order for it to compile properly on = 7.

First,=20 work_mem has to be changed to SortMem (line 666). =20 I.e.

    tupstore =3D tuplestore_begin_heap(true, = false,=20 work_mem);

should be changed to:

    tupstore =3D=20 tuplestore_begin_heap(true, SortMem);

Second, the error reporting framework has changed.

To fix this, you need to change all the ereport coding to use=20 elog.  I.e.

    ereport(ERROR,=20 (errcode(ERRCODE_SYNTAX_ERROR),
      &n= bsp;           &nb= sp;      =20 errmsg("xpath_table must be called as a table = function")));

should be=20 changed to:

    elog(ERROR,"xpath_table must be called as a = table=20 function");
I have attached an edited copy of xpath.c with these changes, if = you would=20 like to work with it.
 
Regards,
George


----- Original Message -----
From: Theo = Galanakis=20
To: 'pgsql-sql@postgresql.org'
Sent: Sunday, February 20, 2005 = 11:28=20 PM
Subject: FW: [SQL] Working with XML.


Hi, =
   =20 I have copied all the files manually from=20 http://developer.postgresql.org/docs/pgsql/contrib/ for the xml2 = contribution.=20 However I have the following issue when I attempt to compile with=20 gmake:

gcc -I/usr/include/libxml2 -fpic -I. -I../../src/include=20 -D_GNU_SOURCE   -c -o xpath.o xpath.c
xpath.c: In function=20 `xpath_table':
xpath.c:689: `work_mem' undeclared (first use in this=20 function)
xpath.c:689: (Each undeclared identifier is reported only=20 once
xpath.c:689: for each function it appears in.)
gmake: *** = [xpath.o]=20 Error 1

I have installed :

   =20 libxml2-devel-2.5.10-1.rpm

What am I doing wrong, or can someone = point me=20 to the direction of a binary for XML2 on RedHat ES3, Postgres=20 7.4.5.

Cheers,   
   =20 Theo
   
------=_NextPart_001_0053_01C517F3.1DAB3B20-- ------=_NextPart_000_0052_01C517F3.1DA8F130 Content-Type: text/plain; name="xpath.c" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="xpath.c" /* Parser interface for DOM-based parser (libxml) rather than=0A= stream-based SAX-type parser */=0A= =0A= /*#include "errcodes.h"*/=0A= #include "postgres.h"=0A= #include "fmgr.h"=0A= #include "executor/spi.h"=0A= #include "funcapi.h"=0A= #include "miscadmin.h"=0A= #include "lib/stringinfo.h"=0A= =0A= /* libxml includes */=0A= =0A= #include =0A= #include =0A= #include =0A= #include =0A= #include =0A= =0A= /* declarations */=0A= =0A= static void *pgxml_palloc(size_t size);=0A= static void *pgxml_repalloc(void *ptr, size_t size);=0A= static void pgxml_pfree(void *ptr);=0A= static char *pgxml_pstrdup(const char *string);=0A= static void pgxml_errorHandler(void *ctxt, const char *msg,...);=0A= =0A= void elog_error(int level, char *explain, int force);=0A= void pgxml_parser_init(void);=0A= =0A= static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset,=0A= xmlChar * toptagname, xmlChar * septagname,=0A= xmlChar * plainsep);=0A= =0A= text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar * toptag,=0A= xmlChar * septag, xmlChar * plainsep);=0A= =0A= xmlChar *pgxml_texttoxmlchar(text *textstring);=0A= =0A= static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar * xpath);=0A= =0A= =0A= Datum xml_valid(PG_FUNCTION_ARGS);=0A= Datum xpath_nodeset(PG_FUNCTION_ARGS);=0A= Datum xpath_string(PG_FUNCTION_ARGS);=0A= Datum xpath_number(PG_FUNCTION_ARGS);=0A= Datum xpath_bool(PG_FUNCTION_ARGS);=0A= Datum xpath_list(PG_FUNCTION_ARGS);=0A= Datum xpath_table(PG_FUNCTION_ARGS);=0A= =0A= /* Global variables */=0A= char *errbuf; /* per line error buffer */=0A= char *pgxml_errorMsg =3D NULL; /* overall error message */=0A= =0A= /* Convenience macros */=0A= =0A= #define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, = CStringGetDatum(cstrp)))=0A= #define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, = PointerGetDatum(textp)))=0A= =0A= #define ERRBUF_SIZE 200=0A= =0A= /* memory handling passthrough functions (e.g. palloc, pstrdup are=0A= currently macros, and the others might become so...) */=0A= =0A= static void *=0A= pgxml_palloc(size_t size)=0A= {=0A= /* elog(DEBUG1,"Alloc %d in CMC %x",size,CurrentMemoryContext); */=0A= return palloc(size);=0A= }=0A= =0A= static void *=0A= pgxml_repalloc(void *ptr, size_t size)=0A= {=0A= /* elog(DEBUG1,"ReAlloc in CMC %x",CurrentMemoryContext);*/=0A= return repalloc(ptr, size);=0A= }=0A= =0A= static void=0A= pgxml_pfree(void *ptr)=0A= {=0A= /* elog(DEBUG1,"Free in CMC %x",CurrentMemoryContext); */=0A= return pfree(ptr);=0A= }=0A= =0A= static char *=0A= pgxml_pstrdup(const char *string)=0A= {=0A= return pstrdup(string);=0A= }=0A= =0A= /* The error handling function. This formats an error message and sets=0A= * a flag - an ereport will be issued prior to return=0A= */=0A= =0A= static void=0A= pgxml_errorHandler(void *ctxt, const char *msg,...)=0A= {=0A= va_list args;=0A= =0A= va_start(args, msg);=0A= vsnprintf(errbuf, ERRBUF_SIZE, msg, args);=0A= va_end(args);=0A= /* Now copy the argument across */=0A= if (pgxml_errorMsg =3D=3D NULL)=0A= pgxml_errorMsg =3D pstrdup(errbuf);=0A= else=0A= {=0A= int32 xsize =3D strlen(pgxml_errorMsg);=0A= =0A= pgxml_errorMsg =3D repalloc(pgxml_errorMsg,=0A= (size_t) (xsize + strlen(errbuf) + 1));=0A= strncpy(&pgxml_errorMsg[xsize - 1], errbuf, strlen(errbuf));=0A= pgxml_errorMsg[xsize + strlen(errbuf) - 1] =3D '\0';=0A= =0A= }=0A= memset(errbuf, 0, ERRBUF_SIZE);=0A= }=0A= =0A= /* This function reports the current message at the level specified */=0A= void=0A= elog_error(int level, char *explain, int force)=0A= {=0A= if (force || (pgxml_errorMsg !=3D NULL))=0A= {=0A= if (pgxml_errorMsg =3D=3D NULL)=0A= {=0A= /*=0A= ereport(level, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),=0A= errmsg(explain)));=0A= */=0A= elog(ERROR,explain); }=0A= else=0A= {=0A= /*=0A= ereport(level, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),=0A= errmsg("%s:%s", explain, pgxml_errorMsg)));=0A= pfree(pgxml_errorMsg);=0A= */=0A= elog(ERROR,"%s:%s", explain, pgxml_errorMsg);=0A= }=0A= }=0A= }=0A= =0A= void=0A= pgxml_parser_init()=0A= {=0A= /*=0A= * This code could also set parser settings from user-supplied info.=0A= * Quite how these settings are made is another matter :)=0A= */=0A= =0A= xmlMemSetup(pgxml_pfree, pgxml_palloc, pgxml_repalloc, pgxml_pstrdup);=0A= xmlInitParser();=0A= =0A= xmlSetGenericErrorFunc(NULL, pgxml_errorHandler);=0A= =0A= xmlSubstituteEntitiesDefault(1);=0A= xmlLoadExtDtdDefaultValue =3D 1;=0A= =0A= pgxml_errorMsg =3D NULL;=0A= =0A= errbuf =3D palloc(200);=0A= memset(errbuf, 0, 200);=0A= =0A= }=0A= =0A= =0A= /* Returns true if document is well-formed */=0A= =0A= PG_FUNCTION_INFO_V1(xml_valid);=0A= =0A= Datum=0A= xml_valid(PG_FUNCTION_ARGS)=0A= {=0A= /* called as xml_valid(document) */=0A= xmlDocPtr doctree;=0A= text *t =3D PG_GETARG_TEXT_P(0); /* document buffer */=0A= int32 docsize =3D VARSIZE(t) - VARHDRSZ;=0A= =0A= pgxml_parser_init();=0A= =0A= doctree =3D xmlParseMemory((char *) VARDATA(t), docsize);=0A= if (doctree =3D=3D NULL)=0A= {=0A= xmlCleanupParser();=0A= PG_RETURN_BOOL(false); /* i.e. not well-formed */=0A= }=0A= xmlCleanupParser();=0A= xmlFreeDoc(doctree);=0A= PG_RETURN_BOOL(true);=0A= }=0A= =0A= =0A= static xmlChar=0A= *=0A= pgxmlNodeSetToText(xmlNodeSetPtr nodeset,=0A= xmlChar * toptagname,=0A= xmlChar * septagname,=0A= xmlChar * plainsep)=0A= {=0A= /* Function translates a nodeset into a text representation */=0A= =0A= /*=0A= * iterates over each node in the set and calls xmlNodeDump to write=0A= * it to an xmlBuffer -from which an xmlChar * string is returned.=0A= */=0A= =0A= /* each representation is surrounded by ... */=0A= =0A= /*=0A= * plainsep is an ordinary (not tag) seperator - if used, then nodes=0A= * are cast to string as output method=0A= */=0A= =0A= =0A= xmlBufferPtr buf;=0A= xmlChar *result;=0A= int i;=0A= =0A= buf =3D xmlBufferCreate();=0A= =0A= if ((toptagname !=3D NULL) && (xmlStrlen(toptagname) > 0))=0A= {=0A= xmlBufferWriteChar(buf, "<");=0A= xmlBufferWriteCHAR(buf, toptagname);=0A= xmlBufferWriteChar(buf, ">");=0A= }=0A= if (nodeset !=3D NULL)=0A= {=0A= for (i =3D 0; i < nodeset->nodeNr; i++)=0A= {=0A= =0A= if (plainsep !=3D NULL)=0A= {=0A= xmlBufferWriteCHAR(buf,=0A= xmlXPathCastNodeToString(nodeset->nodeTab[i]));=0A= =0A= /* If this isn't the last entry, write the plain sep. */=0A= if (i < (nodeset->nodeNr) - 1)=0A= xmlBufferWriteChar(buf, plainsep);=0A= }=0A= else=0A= {=0A= =0A= =0A= if ((septagname !=3D NULL) && (xmlStrlen(septagname) > 0))=0A= {=0A= xmlBufferWriteChar(buf, "<");=0A= xmlBufferWriteCHAR(buf, septagname);=0A= xmlBufferWriteChar(buf, ">");=0A= }=0A= xmlNodeDump(buf,=0A= nodeset->nodeTab[i]->doc,=0A= nodeset->nodeTab[i],=0A= 1, 0);=0A= =0A= if ((septagname !=3D NULL) && (xmlStrlen(septagname) > 0))=0A= {=0A= xmlBufferWriteChar(buf, "");=0A= }=0A= }=0A= }=0A= }=0A= =0A= if ((toptagname !=3D NULL) && (xmlStrlen(toptagname) > 0))=0A= {=0A= xmlBufferWriteChar(buf, "");=0A= }=0A= result =3D xmlStrdup(buf->content);=0A= xmlBufferFree(buf);=0A= return result;=0A= }=0A= =0A= =0A= /* Translate a PostgreSQL "varlena" -i.e. a variable length parameter=0A= * into the libxml2 representation=0A= */=0A= =0A= xmlChar *=0A= pgxml_texttoxmlchar(text *textstring)=0A= {=0A= xmlChar *res;=0A= int32 txsize;=0A= =0A= txsize =3D VARSIZE(textstring) - VARHDRSZ;=0A= res =3D (xmlChar *) palloc(txsize + 1);=0A= memcpy((char *) res, VARDATA(textstring), txsize);=0A= res[txsize] =3D '\0';=0A= return res;=0A= }=0A= =0A= /* Public visible XPath functions */=0A= =0A= /* This is a "raw" xpath function. Check that it returns child elements=0A= * properly=0A= */=0A= =0A= PG_FUNCTION_INFO_V1(xpath_nodeset);=0A= =0A= Datum=0A= xpath_nodeset(PG_FUNCTION_ARGS)=0A= {=0A= xmlChar *xpath,=0A= *toptag,=0A= *septag;=0A= int32 pathsize;=0A= text=0A= *xpathsupp,=0A= *xpres;=0A= =0A= /* PG_GETARG_TEXT_P(0) is document buffer */=0A= xpathsupp =3D PG_GETARG_TEXT_P(1); /* XPath expression */=0A= =0A= toptag =3D pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));=0A= septag =3D pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3));=0A= =0A= pathsize =3D VARSIZE(xpathsupp) - VARHDRSZ;=0A= =0A= xpath =3D pgxml_texttoxmlchar(xpathsupp);=0A= =0A= xpres =3D pgxml_result_to_text(=0A= pgxml_xpath(PG_GETARG_TEXT_P(0), xpath),=0A= toptag, septag, NULL);=0A= =0A= /* xmlCleanupParser(); done by result_to_text routine */=0A= pfree((void *) xpath);=0A= =0A= if (xpres =3D=3D NULL)=0A= PG_RETURN_NULL();=0A= PG_RETURN_TEXT_P(xpres);=0A= }=0A= =0A= /* The following function is almost identical, but returns the elements = in */=0A= /* a list. */=0A= =0A= PG_FUNCTION_INFO_V1(xpath_list);=0A= =0A= Datum=0A= xpath_list(PG_FUNCTION_ARGS)=0A= {=0A= xmlChar *xpath,=0A= *plainsep;=0A= int32 pathsize;=0A= text=0A= *xpathsupp,=0A= *xpres;=0A= =0A= /* PG_GETARG_TEXT_P(0) is document buffer */=0A= xpathsupp =3D PG_GETARG_TEXT_P(1); /* XPath expression */=0A= =0A= plainsep =3D pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));=0A= =0A= pathsize =3D VARSIZE(xpathsupp) - VARHDRSZ;=0A= =0A= xpath =3D pgxml_texttoxmlchar(xpathsupp);=0A= =0A= xpres =3D pgxml_result_to_text(=0A= pgxml_xpath(PG_GETARG_TEXT_P(0), xpath),=0A= NULL, NULL, plainsep);=0A= =0A= /* xmlCleanupParser(); done by result_to_text routine */=0A= pfree((void *) xpath);=0A= =0A= if (xpres =3D=3D NULL)=0A= PG_RETURN_NULL();=0A= PG_RETURN_TEXT_P(xpres);=0A= }=0A= =0A= =0A= PG_FUNCTION_INFO_V1(xpath_string);=0A= =0A= Datum=0A= xpath_string(PG_FUNCTION_ARGS)=0A= {=0A= xmlChar *xpath;=0A= int32 pathsize;=0A= text=0A= *xpathsupp,=0A= *xpres;=0A= =0A= /* PG_GETARG_TEXT_P(0) is document buffer */=0A= xpathsupp =3D PG_GETARG_TEXT_P(1); /* XPath expression */=0A= =0A= pathsize =3D VARSIZE(xpathsupp) - VARHDRSZ;=0A= =0A= /*=0A= * We encapsulate the supplied path with "string()" =3D 8 chars + 1 for=0A= * NUL at end=0A= */=0A= /* We could try casting to string using the libxml function? */=0A= =0A= xpath =3D (xmlChar *) palloc(pathsize + 9);=0A= memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize);=0A= strncpy((char *) xpath, "string(", 7);=0A= xpath[pathsize + 7] =3D ')';=0A= xpath[pathsize + 8] =3D '\0';=0A= =0A= xpres =3D pgxml_result_to_text(=0A= pgxml_xpath(PG_GETARG_TEXT_P(0), xpath),=0A= NULL, NULL, NULL);=0A= =0A= xmlCleanupParser();=0A= pfree((void *) xpath);=0A= =0A= if (xpres =3D=3D NULL)=0A= PG_RETURN_NULL();=0A= PG_RETURN_TEXT_P(xpres);=0A= }=0A= =0A= =0A= PG_FUNCTION_INFO_V1(xpath_number);=0A= =0A= Datum=0A= xpath_number(PG_FUNCTION_ARGS)=0A= {=0A= xmlChar *xpath;=0A= int32 pathsize;=0A= text=0A= *xpathsupp;=0A= =0A= float4 fRes;=0A= =0A= xmlXPathObjectPtr res;=0A= =0A= /* PG_GETARG_TEXT_P(0) is document buffer */=0A= xpathsupp =3D PG_GETARG_TEXT_P(1); /* XPath expression */=0A= =0A= pathsize =3D VARSIZE(xpathsupp) - VARHDRSZ;=0A= =0A= xpath =3D pgxml_texttoxmlchar(xpathsupp);=0A= =0A= res =3D pgxml_xpath(PG_GETARG_TEXT_P(0), xpath);=0A= pfree((void *) xpath);=0A= =0A= if (res =3D=3D NULL)=0A= {=0A= xmlCleanupParser();=0A= PG_RETURN_NULL();=0A= }=0A= =0A= fRes =3D xmlXPathCastToNumber(res);=0A= xmlCleanupParser();=0A= if (xmlXPathIsNaN(fRes))=0A= PG_RETURN_NULL();=0A= =0A= PG_RETURN_FLOAT4(fRes);=0A= =0A= }=0A= =0A= =0A= PG_FUNCTION_INFO_V1(xpath_bool);=0A= =0A= Datum=0A= xpath_bool(PG_FUNCTION_ARGS)=0A= {=0A= xmlChar *xpath;=0A= int32 pathsize;=0A= text=0A= *xpathsupp;=0A= =0A= int bRes;=0A= =0A= xmlXPathObjectPtr res;=0A= =0A= /* PG_GETARG_TEXT_P(0) is document buffer */=0A= xpathsupp =3D PG_GETARG_TEXT_P(1); /* XPath expression */=0A= =0A= pathsize =3D VARSIZE(xpathsupp) - VARHDRSZ;=0A= =0A= xpath =3D pgxml_texttoxmlchar(xpathsupp);=0A= =0A= res =3D pgxml_xpath(PG_GETARG_TEXT_P(0), xpath);=0A= pfree((void *) xpath);=0A= =0A= if (res =3D=3D NULL)=0A= {=0A= xmlCleanupParser();=0A= PG_RETURN_BOOL(false);=0A= }=0A= =0A= bRes =3D xmlXPathCastToBoolean(res);=0A= xmlCleanupParser();=0A= PG_RETURN_BOOL(bRes);=0A= =0A= }=0A= =0A= =0A= =0A= /* Core function to evaluate XPath query */=0A= =0A= xmlXPathObjectPtr=0A= pgxml_xpath(text *document, xmlChar * xpath)=0A= {=0A= =0A= xmlDocPtr doctree;=0A= xmlXPathContextPtr ctxt;=0A= xmlXPathObjectPtr res;=0A= =0A= xmlXPathCompExprPtr comppath;=0A= =0A= int32 docsize;=0A= =0A= =0A= docsize =3D VARSIZE(document) - VARHDRSZ;=0A= =0A= pgxml_parser_init();=0A= =0A= doctree =3D xmlParseMemory((char *) VARDATA(document), docsize);=0A= if (doctree =3D=3D NULL)=0A= { /* not well-formed */=0A= return NULL;=0A= }=0A= =0A= ctxt =3D xmlXPathNewContext(doctree);=0A= ctxt->node =3D xmlDocGetRootElement(doctree);=0A= =0A= =0A= /* compile the path */=0A= comppath =3D xmlXPathCompile(xpath);=0A= if (comppath =3D=3D NULL)=0A= {=0A= xmlCleanupParser();=0A= xmlFreeDoc(doctree);=0A= elog_error(ERROR, "XPath Syntax Error", 1);=0A= =0A= return NULL;=0A= }=0A= =0A= /* Now evaluate the path expression. */=0A= res =3D xmlXPathCompiledEval(comppath, ctxt);=0A= xmlXPathFreeCompExpr(comppath);=0A= =0A= if (res =3D=3D NULL)=0A= {=0A= xmlXPathFreeContext(ctxt);=0A= /* xmlCleanupParser(); */=0A= xmlFreeDoc(doctree);=0A= =0A= return NULL;=0A= }=0A= /* xmlFreeDoc(doctree); */=0A= return res;=0A= }=0A= =0A= text=0A= *=0A= pgxml_result_to_text(xmlXPathObjectPtr res,=0A= xmlChar * toptag,=0A= xmlChar * septag,=0A= xmlChar * plainsep)=0A= {=0A= xmlChar *xpresstr;=0A= int32 ressize;=0A= text *xpres;=0A= =0A= if (res =3D=3D NULL)=0A= return NULL;=0A= switch (res->type)=0A= {=0A= case XPATH_NODESET:=0A= xpresstr =3D pgxmlNodeSetToText(res->nodesetval,=0A= toptag,=0A= septag, plainsep);=0A= break;=0A= =0A= case XPATH_STRING:=0A= xpresstr =3D xmlStrdup(res->stringval);=0A= break;=0A= =0A= default:=0A= elog(NOTICE, "Unsupported XQuery result: %d", res->type);=0A= xpresstr =3D xmlStrdup("");=0A= }=0A= =0A= =0A= /* Now convert this result back to text */=0A= ressize =3D strlen(xpresstr);=0A= xpres =3D (text *) palloc(ressize + VARHDRSZ);=0A= memcpy(VARDATA(xpres), xpresstr, ressize);=0A= VARATT_SIZEP(xpres) =3D ressize + VARHDRSZ;=0A= =0A= /* Free various storage */=0A= xmlCleanupParser();=0A= /* xmlFreeDoc(doctree); -- will die at end of tuple anyway */=0A= =0A= xmlFree(xpresstr);=0A= =0A= elog_error(ERROR, "XPath error", 0);=0A= =0A= =0A= return xpres;=0A= }=0A= =0A= /* xpath_table is a table function. It needs some tidying (as do the=0A= * other functions here!=0A= */=0A= =0A= PG_FUNCTION_INFO_V1(xpath_table);=0A= =0A= Datum=0A= xpath_table(PG_FUNCTION_ARGS)=0A= {=0A= /* SPI (input tuple) support */=0A= SPITupleTable *tuptable;=0A= HeapTuple spi_tuple;=0A= TupleDesc spi_tupdesc;=0A= =0A= /* Output tuple (tuplestore) support */=0A= Tuplestorestate *tupstore =3D NULL;=0A= TupleDesc ret_tupdesc;=0A= HeapTuple ret_tuple;=0A= =0A= ReturnSetInfo *rsinfo =3D (ReturnSetInfo *) fcinfo->resultinfo;=0A= AttInMetadata *attinmeta;=0A= MemoryContext per_query_ctx;=0A= MemoryContext oldcontext;=0A= =0A= /* Function parameters */=0A= char *pkeyfield =3D GET_STR(PG_GETARG_TEXT_P(0));=0A= char *xmlfield =3D GET_STR(PG_GETARG_TEXT_P(1));=0A= char *relname =3D GET_STR(PG_GETARG_TEXT_P(2));=0A= char *xpathset =3D GET_STR(PG_GETARG_TEXT_P(3));=0A= char *condition =3D GET_STR(PG_GETARG_TEXT_P(4));=0A= =0A= char **values;=0A= xmlChar **xpaths;=0A= xmlChar *pos;=0A= xmlChar *pathsep =3D "|";=0A= =0A= int numpaths;=0A= int ret;=0A= int proc;=0A= int i;=0A= int j;=0A= int rownr; /* For issuing multiple rows from one=0A= * original document */=0A= int had_values; /* To determine end of nodeset results */=0A= =0A= StringInfo querysql;=0A= =0A= /* We only have a valid tuple description in table function mode */=0A= if (rsinfo->expectedDesc =3D=3D NULL)=0A= {=0A= /*ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),=0A= errmsg("xpath_table must be called as a table function")));=0A= */=0A= elog(ERROR,"xpath_table must be called as a table function"); }=0A= =0A= /* The tuplestore must exist in a higher context than=0A= * this function call (per_query_ctx is used) */=0A= =0A= per_query_ctx =3D rsinfo->econtext->ecxt_per_query_memory;=0A= oldcontext =3D MemoryContextSwitchTo(per_query_ctx);=0A= =0A= /* Create the tuplestore - work_mem is the max in-memory size before a=0A= * file is created on disk to hold it.=0A= * NOTE - Changed work_mem to sortmem - Nov 22 - 04 - GW=0A= */=0A= =0A= /*tupstore =3D tuplestore_begin_heap(true, false, work_mem);*/=0A= =0A= tupstore =3D tuplestore_begin_heap(true, SortMem);=0A= =0A= MemoryContextSwitchTo(oldcontext);=0A= =0A= /* get the requested return tuple description */=0A= ret_tupdesc =3D CreateTupleDescCopy(rsinfo->expectedDesc);=0A= =0A= /*=0A= * At the moment we assume that the returned attributes make sense for=0A= * the XPath specififed (i.e. we trust the caller). It's not fatal if=0A= * they get it wrong - the input function for the column type will=0A= * raise an error if the path result can't be converted into the=0A= * correct binary representation.=0A= */=0A= =0A= attinmeta =3D TupleDescGetAttInMetadata(ret_tupdesc);=0A= =0A= /*=0A= * We want to materialise because it means that we don't have to carry=0A= * libxml2 parser state between invocations of this function=0A= */=0A= =0A= /* check to see if caller supports us returning a tuplestore */=0A= if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))=0A= /*ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),=0A= errmsg("xpath_table requires Materialize mode, but it is not "=0A= "allowed in this context")));=0A= */=0A= elog(ERROR,"xpath_table requires Materialize mode, but it is not "=0A= "allowed in this context"); =0A= /* Set return mode and allocate value space. */=0A= rsinfo->returnMode =3D SFRM_Materialize;=0A= rsinfo->setDesc =3D ret_tupdesc;=0A= =0A= values =3D (char **) palloc(ret_tupdesc->natts * sizeof(char *));=0A= =0A= xpaths =3D (xmlChar **) palloc(ret_tupdesc->natts * sizeof(xmlChar *));=0A= =0A= /* Split XPaths. xpathset is a writable CString. */=0A= =0A= /* Note that we stop splitting once we've done all needed for tupdesc */=0A= =0A= numpaths =3D 0;=0A= pos =3D xpathset;=0A= do=0A= {=0A= xpaths[numpaths] =3D pos;=0A= pos =3D strstr(pos, pathsep);=0A= if (pos !=3D NULL)=0A= {=0A= *pos =3D '\0';=0A= pos++;=0A= }=0A= numpaths++;=0A= } while ((pos !=3D NULL) && (numpaths < (ret_tupdesc->natts - 1)));=0A= =0A= /* Now build query */=0A= =0A= querysql =3D makeStringInfo();=0A= =0A= /* Build initial sql statement */=0A= appendStringInfo(querysql, "SELECT %s, %s FROM %s WHERE %s",=0A= pkeyfield,=0A= xmlfield,=0A= relname,=0A= condition=0A= );=0A= =0A= =0A= if ((ret =3D SPI_connect()) < 0)=0A= elog(ERROR, "xpath_table: SPI_connect returned %d", ret);=0A= =0A= if ((ret =3D SPI_exec(querysql->data, 0)) !=3D SPI_OK_SELECT)=0A= elog(ERROR, "xpath_table: SPI execution failed for query %s", = querysql->data);=0A= =0A= proc =3D SPI_processed;=0A= /* elog(DEBUG1,"xpath_table: SPI returned %d rows",proc); */=0A= tuptable =3D SPI_tuptable;=0A= spi_tupdesc =3D tuptable->tupdesc;=0A= =0A= /* Switch out of SPI context */=0A= MemoryContextSwitchTo(oldcontext);=0A= =0A= =0A= /* Check that SPI returned correct result. If you put a comma into one of=0A= * the function parameters, this will catch it when the SPI query returns=0A= * e.g. 3 columns.=0A= */=0A= =0A= if (spi_tupdesc->natts !=3D 2)=0A= {=0A= /*ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),=0A= errmsg("Expression returning multiple columns is not valid in = parameter list"),=0A= errdetail("Expected two columns in SPI result, got %d", = spi_tupdesc->natts)));=0A= */=0A= elog(ERROR,"Expression returning multiple columns is not valid in = parameter list"); }=0A= =0A= /* Setup the parser. Beware that this must happen in the same context as = the=0A= * cleanup - which means that any error from here on must do cleanup to=0A= * ensure that the entity table doesn't get freed by being out of = context.=0A= */=0A= pgxml_parser_init();=0A= =0A= /* For each row i.e. document returned from SPI */=0A= for (i =3D 0; i < proc; i++)=0A= {=0A= char *pkey;=0A= char *xmldoc;=0A= =0A= xmlDocPtr doctree;=0A= xmlXPathContextPtr ctxt;=0A= xmlXPathObjectPtr res;=0A= xmlChar *resstr;=0A= =0A= =0A= xmlXPathCompExprPtr comppath;=0A= =0A= /* Extract the row data as C Strings */=0A= =0A= spi_tuple =3D tuptable->vals[i];=0A= pkey =3D SPI_getvalue(spi_tuple, spi_tupdesc, 1);=0A= xmldoc =3D SPI_getvalue(spi_tuple, spi_tupdesc, 2);=0A= =0A= =0A= /*=0A= * Clear the values array, so that not-well-formed documents=0A= * return NULL in all columns.=0A= */=0A= =0A= /* Note that this also means that spare columns will be NULL. */=0A= for (j =3D 0; j < ret_tupdesc->natts; j++)=0A= values[j] =3D NULL;=0A= =0A= /* Insert primary key */=0A= values[0] =3D pkey;=0A= =0A= /* Parse the document */=0A= doctree =3D xmlParseMemory(xmldoc, strlen(xmldoc));=0A= =0A= if (doctree =3D=3D NULL)=0A= { /* not well-formed, so output all-NULL=0A= * tuple */=0A= =0A= ret_tuple =3D BuildTupleFromCStrings(attinmeta, values);=0A= oldcontext =3D MemoryContextSwitchTo(per_query_ctx);=0A= tuplestore_puttuple(tupstore, ret_tuple);=0A= MemoryContextSwitchTo(oldcontext);=0A= heap_freetuple(ret_tuple);=0A= }=0A= else=0A= {=0A= /* New loop here - we have to deal with nodeset results */=0A= rownr =3D 0;=0A= =0A= do=0A= {=0A= /* Now evaluate the set of xpaths. */=0A= had_values =3D 0;=0A= for (j =3D 0; j < numpaths; j++)=0A= {=0A= =0A= ctxt =3D xmlXPathNewContext(doctree);=0A= ctxt->node =3D xmlDocGetRootElement(doctree);=0A= xmlSetGenericErrorFunc(ctxt, pgxml_errorHandler);=0A= =0A= /* compile the path */=0A= comppath =3D xmlXPathCompile(xpaths[j]);=0A= if (comppath =3D=3D NULL)=0A= {=0A= xmlCleanupParser();=0A= xmlFreeDoc(doctree);=0A= =0A= elog_error(ERROR, "XPath Syntax Error", 1);=0A= =0A= PG_RETURN_NULL(); /* Keep compiler happy */=0A= }=0A= =0A= /* Now evaluate the path expression. */=0A= res =3D xmlXPathCompiledEval(comppath, ctxt);=0A= xmlXPathFreeCompExpr(comppath);=0A= =0A= if (res !=3D NULL)=0A= {=0A= switch (res->type)=0A= {=0A= case XPATH_NODESET:=0A= /* We see if this nodeset has enough nodes */=0A= if ((res->nodesetval !=3D NULL) && (rownr < = res->nodesetval->nodeNr))=0A= {=0A= resstr =3D=0A= xmlXPathCastNodeToString(res->nodesetval->nodeTab[rownr]);=0A= had_values =3D 1;=0A= }=0A= else=0A= resstr =3D NULL;=0A= =0A= break;=0A= =0A= case XPATH_STRING:=0A= resstr =3D xmlStrdup(res->stringval);=0A= break;=0A= =0A= default:=0A= elog(NOTICE, "Unsupported XQuery result: %d", res->type);=0A= resstr =3D xmlStrdup("");=0A= }=0A= =0A= =0A= /*=0A= * Insert this into the appropriate column in the=0A= * result tuple.=0A= */=0A= values[j + 1] =3D resstr;=0A= }=0A= xmlXPathFreeContext(ctxt);=0A= }=0A= /* Now add the tuple to the output, if there is one. */=0A= if (had_values)=0A= {=0A= ret_tuple =3D BuildTupleFromCStrings(attinmeta, values);=0A= oldcontext =3D MemoryContextSwitchTo(per_query_ctx);=0A= tuplestore_puttuple(tupstore, ret_tuple);=0A= MemoryContextSwitchTo(oldcontext);=0A= heap_freetuple(ret_tuple);=0A= }=0A= =0A= rownr++;=0A= =0A= } while (had_values);=0A= =0A= }=0A= =0A= xmlFreeDoc(doctree);=0A= =0A= pfree(pkey);=0A= pfree(xmldoc);=0A= }=0A= =0A= xmlCleanupParser();=0A= /* Needed to flag completeness in 7.3.1. 7.4 defines it as a no-op. */=0A= tuplestore_donestoring(tupstore);=0A= =0A= SPI_finish();=0A= =0A= rsinfo->setResult =3D tupstore;=0A= =0A= /*=0A= * SFRM_Materialize mode expects us to return a NULL Datum. The actual=0A= * tuples are in our tuplestore and passed back through=0A= * rsinfo->setResult. rsinfo->setDesc is set to the tuple description=0A= * that we actually used to build our tuples with, so the caller can=0A= * verify we did what it was expecting.=0A= */=0A= return (Datum) 0;=0A= =0A= }=0A= ------=_NextPart_000_0052_01C517F3.1DA8F130--