@ -16,28 +16,7 @@
# include <libxml/xmlerror.h>
# include <libxml/xmlerror.h>
# include <libxml/parserInternals.h>
# include <libxml/parserInternals.h>
/* declarations */
/* externally accessible functions */
static void * pgxml_palloc ( size_t size ) ;
static void * pgxml_repalloc ( void * ptr , size_t size ) ;
static void pgxml_pfree ( void * ptr ) ;
static char * pgxml_pstrdup ( const char * string ) ;
static void pgxml_errorHandler ( void * ctxt , const char * msg , . . . ) ;
void elog_error ( int level , char * explain , int force ) ;
void pgxml_parser_init ( void ) ;
static xmlChar * pgxmlNodeSetToText ( xmlNodeSetPtr nodeset ,
xmlChar * toptagname , xmlChar * septagname ,
xmlChar * plainsep ) ;
text * pgxml_result_to_text ( xmlXPathObjectPtr res , xmlChar * toptag ,
xmlChar * septag , xmlChar * plainsep ) ;
xmlChar * pgxml_texttoxmlchar ( text * textstring ) ;
static xmlXPathObjectPtr pgxml_xpath ( text * document , xmlChar * xpath ) ;
Datum xml_valid ( PG_FUNCTION_ARGS ) ;
Datum xml_valid ( PG_FUNCTION_ARGS ) ;
Datum xml_encode_special_chars ( PG_FUNCTION_ARGS ) ;
Datum xml_encode_special_chars ( PG_FUNCTION_ARGS ) ;
@ -48,116 +27,100 @@ Datum xpath_bool(PG_FUNCTION_ARGS);
Datum xpath_list ( PG_FUNCTION_ARGS ) ;
Datum xpath_list ( PG_FUNCTION_ARGS ) ;
Datum xpath_table ( PG_FUNCTION_ARGS ) ;
Datum xpath_table ( PG_FUNCTION_ARGS ) ;
/* Global variables */
/* these are exported for use by xslt_proc.c */
char * errbuf ; /* per line error buffer */
char * pgxml_errorMsg = NULL ; /* overall error message */
/* Convenience macros */
void elog_error ( const char * explain , bool force ) ;
void pgxml_parser_init ( void ) ;
# define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))
/* local declarations */
# define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
# define ERRBUF_SIZE 200
static void pgxml_errorHandler ( void * ctxt , const char * msg , . . . ) ;
/* memory handling passthrough functions (e.g. palloc, pstrdup are
static xmlChar * pgxmlNodeSetToText ( xmlNodeSetPtr nodeset ,
currently macros , and the others might become so . . . ) */
xmlChar * toptagname , xmlChar * septagname ,
xmlChar * plainsep ) ;
static void *
static text * pgxml_result_to_text ( xmlXPathObjectPtr res , xmlChar * toptag ,
pgxml_palloc ( size_t size )
xmlChar * septag , xmlChar * plainsep ) ;
{
/* elog(DEBUG1,"Alloc %d in CMC %x",size,CurrentMemoryContext); */
return palloc ( size ) ;
}
static void *
static xmlChar * pgxml_texttoxmlchar ( text * textstring ) ;
pgxml_repalloc ( void * ptr , size_t size )
{
/* elog(DEBUG1,"ReAlloc in CMC %x",CurrentMemoryContext);*/
return repalloc ( ptr , size ) ;
}
static void
static xmlXPathObjectPtr pgxml_xpath ( text * document , xmlChar * xpath ) ;
pgxml_pfree ( void * ptr )
{
/* elog(DEBUG1,"Free in CMC %x",CurrentMemoryContext); */
pfree ( ptr ) ;
}
static char *
/* Global variables */
pgxml_pstrdup ( const char * string )
static char * pgxml_errorMsg = NULL ; /* overall error message */
{
return pstrdup ( string ) ;
}
/* The error handling function. This formats an error message and sets
# define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
/*
* The error handling function . This formats an error message and sets
* a flag - an ereport will be issued prior to return
* a flag - an ereport will be issued prior to return
*/
*/
static void
static void
pgxml_errorHandler ( void * ctxt , const char * msg , . . . )
pgxml_errorHandler ( void * ctxt , const char * msg , . . . )
{
{
char errbuf [ 1024 ] ; /* per line error buffer */
va_list args ;
va_list args ;
/* Format the message */
va_start ( args , msg ) ;
va_start ( args , msg ) ;
vsnprintf ( errbuf , ERRBUF_SIZE , msg , args ) ;
vsnprintf ( errbuf , sizeof ( errbuf ) , msg , args ) ;
va_end ( args ) ;
va_end ( args ) ;
/* Now copy the argument across */
/* Store in, or append to, pgxml_errorMsg */
if ( pgxml_errorMsg = = NULL )
if ( pgxml_errorMsg = = NULL )
pgxml_errorMsg = pstrdup ( errbuf ) ;
pgxml_errorMsg = pstrdup ( errbuf ) ;
else
else
{
{
int32 xsize = strlen ( pgxml_errorMsg ) ;
size_t oldsize = strlen ( pgxml_errorMsg ) ;
size_t newsize = strlen ( errbuf ) ;
pgxml_errorMsg = repalloc ( pgxml_errorMsg ,
( size_t ) ( xsize + strlen ( errbuf ) + 1 ) ) ;
strncpy ( & pgxml_errorMsg [ xsize - 1 ] , errbuf , strlen ( errbuf ) ) ;
pgxml_errorMsg [ xsize + strlen ( errbuf ) - 1 ] = ' \0 ' ;
/*
* We intentionally discard the last char of the existing message ,
* which should be a carriage return . ( XXX wouldn ' t it be saner
* to keep it ? )
*/
pgxml_errorMsg = repalloc ( pgxml_errorMsg , oldsize + newsize ) ;
memcpy ( & pgxml_errorMsg [ oldsize - 1 ] , errbuf , newsize ) ;
pgxml_errorMsg [ oldsize + newsize - 1 ] = ' \0 ' ;
}
}
memset ( errbuf , 0 , ERRBUF_SIZE ) ;
}
}
/* This function reports the current message at the level specified */
/*
* This function ereports the current message if any . If force is true
* then an error is thrown even if pgxml_errorMsg hasn ' t been set .
*/
void
void
elog_error ( int level , char * explain , int force )
elog_error ( const char * explain , bool force )
{
{
if ( force | | ( pgxml_errorMsg ! = NULL ) )
if ( force | | pgxml_errorMsg ! = NULL )
{
{
if ( pgxml_errorMsg = = NULL )
if ( pgxml_errorMsg = = NULL )
{
ereport ( ERROR ,
ereport ( level , ( errcode ( ERRCODE_EXTERNAL_ROUTINE_EXCEPTION ) ,
( errcode ( ERRCODE_EXTERNAL_ROUTINE_EXCEPTION ) ,
errmsg ( explain ) ) ) ;
errmsg ( " %s " , explain ) ) ) ;
}
else
else
{
ereport ( ERROR ,
ereport ( level , ( errcode ( ERRCODE_EXTERNAL_ROUTINE_EXCEPTION ) ,
( errcode ( ERRCODE_EXTERNAL_ROUTINE_EXCEPTION ) ,
errmsg ( " %s:%s " , explain , pgxml_errorMsg ) ) ) ;
errmsg ( " %s: %s " , explain , pgxml_errorMsg ) ) ) ;
pfree ( pgxml_errorMsg ) ;
}
}
}
}
}
/*
* Initialize for xml parsing .
*/
void
void
pgxml_parser_init ( )
pgxml_parser_init ( void )
{
{
/*
/* Set up error handling */
* This code could also set parser settings from user - supplied info .
pgxml_errorMsg = NULL ;
* Quite how these settings are made is another matter : )
xmlSetGenericErrorFunc ( NULL , pgxml_errorHandler ) ;
*/
xmlMemSetup ( pgxml_pfree , pgxml_palloc , pgxml_repalloc , pgxml_pstrdup ) ;
/* Initialize libxml */
xmlInitParser ( ) ;
xmlInitParser ( ) ;
xmlSetGenericErrorFunc ( NULL , pgxml_errorHandler ) ;
xmlSubstituteEntitiesDefault ( 1 ) ;
xmlSubstituteEntitiesDefault ( 1 ) ;
xmlLoadExtDtdDefaultValue = 1 ;
xmlLoadExtDtdDefaultValue = 1 ;
pgxml_errorMsg = NULL ;
errbuf = palloc ( 200 ) ;
memset ( errbuf , 0 , 200 ) ;
}
}
@ -168,10 +131,9 @@ PG_FUNCTION_INFO_V1(xml_valid);
Datum
Datum
xml_valid ( PG_FUNCTION_ARGS )
xml_valid ( PG_FUNCTION_ARGS )
{
{
/* called as xml_valid(document) */
xmlDocPtr doctree ;
text * t = PG_GETARG_TEXT_P ( 0 ) ; /* document buffer */
text * t = PG_GETARG_TEXT_P ( 0 ) ; /* document buffer */
int32 docsize = VARSIZE ( t ) - VARHDRSZ ;
int32 docsize = VARSIZE ( t ) - VARHDRSZ ;
xmlDocPtr doctree ;
pgxml_parser_init ( ) ;
pgxml_parser_init ( ) ;
@ -181,8 +143,8 @@ xml_valid(PG_FUNCTION_ARGS)
xmlCleanupParser ( ) ;
xmlCleanupParser ( ) ;
PG_RETURN_BOOL ( false ) ; /* i.e. not well-formed */
PG_RETURN_BOOL ( false ) ; /* i.e. not well-formed */
}
}
xmlCleanupParser ( ) ;
xmlFreeDoc ( doctree ) ;
xmlFreeDoc ( doctree ) ;
xmlCleanupParser ( ) ;
PG_RETURN_BOOL ( true ) ;
PG_RETURN_BOOL ( true ) ;
}
}
@ -216,28 +178,23 @@ xml_encode_special_chars(PG_FUNCTION_ARGS)
PG_RETURN_TEXT_P ( tout ) ;
PG_RETURN_TEXT_P ( tout ) ;
}
}
static xmlChar
/*
*
* Function translates a nodeset into a text representation
*
* iterates over each node in the set and calls xmlNodeDump to write it to
* an xmlBuffer - from which an xmlChar * string is returned .
*
* each representation is surrounded by < tagname > . . . < / tagname >
*
* plainsep is an ordinary ( not tag ) separator - if used , then nodes are
* cast to string as output method
*/
static xmlChar *
pgxmlNodeSetToText ( xmlNodeSetPtr nodeset ,
pgxmlNodeSetToText ( xmlNodeSetPtr nodeset ,
xmlChar * toptagname ,
xmlChar * toptagname ,
xmlChar * septagname ,
xmlChar * septagname ,
xmlChar * plainsep )
xmlChar * plainsep )
{
{
/* Function translates a nodeset into a text representation */
/*
* iterates over each node in the set and calls xmlNodeDump to write it to
* an xmlBuffer - from which an xmlChar * string is returned .
*/
/* each representation is surrounded by <tagname> ... </tagname> */
/*
* plainsep is an ordinary ( not tag ) seperator - if used , then nodes are
* cast to string as output method
*/
xmlBufferPtr buf ;
xmlBufferPtr buf ;
xmlChar * result ;
xmlChar * result ;
int i ;
int i ;
@ -254,7 +211,6 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
{
{
for ( i = 0 ; i < nodeset - > nodeNr ; i + + )
for ( i = 0 ; i < nodeset - > nodeNr ; i + + )
{
{
if ( plainsep ! = NULL )
if ( plainsep ! = NULL )
{
{
xmlBufferWriteCHAR ( buf ,
xmlBufferWriteCHAR ( buf ,
@ -266,8 +222,6 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
}
}
else
else
{
{
if ( ( septagname ! = NULL ) & & ( xmlStrlen ( septagname ) > 0 ) )
if ( ( septagname ! = NULL ) & & ( xmlStrlen ( septagname ) > 0 ) )
{
{
xmlBufferWriteChar ( buf , " < " ) ;
xmlBufferWriteChar ( buf , " < " ) ;
@ -304,8 +258,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
/* Translate a PostgreSQL "varlena" -i.e. a variable length parameter
/* Translate a PostgreSQL "varlena" -i.e. a variable length parameter
* into the libxml2 representation
* into the libxml2 representation
*/
*/
static xmlChar *
xmlChar *
pgxml_texttoxmlchar ( text * textstring )
pgxml_texttoxmlchar ( text * textstring )
{
{
xmlChar * res ;
xmlChar * res ;
@ -318,12 +271,12 @@ pgxml_texttoxmlchar(text *textstring)
return res ;
return res ;
}
}
/* Public visible XPath functions */
/* Publicly visible XPath functions */
/* This is a "raw" xpath function. Check that it returns child elements
/*
* This is a " raw " xpath function . Check that it returns child elements
* properly
* properly
*/
*/
PG_FUNCTION_INFO_V1 ( xpath_nodeset ) ;
PG_FUNCTION_INFO_V1 ( xpath_nodeset ) ;
Datum
Datum
@ -333,8 +286,7 @@ xpath_nodeset(PG_FUNCTION_ARGS)
* toptag ,
* toptag ,
* septag ;
* septag ;
int32 pathsize ;
int32 pathsize ;
text
text * xpathsupp ,
* xpathsupp ,
* xpres ;
* xpres ;
/* PG_GETARG_TEXT_P(0) is document buffer */
/* PG_GETARG_TEXT_P(0) is document buffer */
@ -347,8 +299,7 @@ xpath_nodeset(PG_FUNCTION_ARGS)
xpath = pgxml_texttoxmlchar ( xpathsupp ) ;
xpath = pgxml_texttoxmlchar ( xpathsupp ) ;
xpres = pgxml_result_to_text (
xpres = pgxml_result_to_text ( pgxml_xpath ( PG_GETARG_TEXT_P ( 0 ) , xpath ) ,
pgxml_xpath ( PG_GETARG_TEXT_P ( 0 ) , xpath ) ,
toptag , septag , NULL ) ;
toptag , septag , NULL ) ;
/* xmlCleanupParser(); done by result_to_text routine */
/* xmlCleanupParser(); done by result_to_text routine */
@ -359,9 +310,10 @@ xpath_nodeset(PG_FUNCTION_ARGS)
PG_RETURN_TEXT_P ( xpres ) ;
PG_RETURN_TEXT_P ( xpres ) ;
}
}
/* The following function is almost identical, but returns the elements in */
/*
/* a list. */
* The following function is almost identical , but returns the elements in
* a list .
*/
PG_FUNCTION_INFO_V1 ( xpath_list ) ;
PG_FUNCTION_INFO_V1 ( xpath_list ) ;
Datum
Datum
@ -370,8 +322,7 @@ xpath_list(PG_FUNCTION_ARGS)
xmlChar * xpath ,
xmlChar * xpath ,
* plainsep ;
* plainsep ;
int32 pathsize ;
int32 pathsize ;
text
text * xpathsupp ,
* xpathsupp ,
* xpres ;
* xpres ;
/* PG_GETARG_TEXT_P(0) is document buffer */
/* PG_GETARG_TEXT_P(0) is document buffer */
@ -383,8 +334,7 @@ xpath_list(PG_FUNCTION_ARGS)
xpath = pgxml_texttoxmlchar ( xpathsupp ) ;
xpath = pgxml_texttoxmlchar ( xpathsupp ) ;
xpres = pgxml_result_to_text (
xpres = pgxml_result_to_text ( pgxml_xpath ( PG_GETARG_TEXT_P ( 0 ) , xpath ) ,
pgxml_xpath ( PG_GETARG_TEXT_P ( 0 ) , xpath ) ,
NULL , NULL , plainsep ) ;
NULL , NULL , plainsep ) ;
/* xmlCleanupParser(); done by result_to_text routine */
/* xmlCleanupParser(); done by result_to_text routine */
@ -403,8 +353,7 @@ xpath_string(PG_FUNCTION_ARGS)
{
{
xmlChar * xpath ;
xmlChar * xpath ;
int32 pathsize ;
int32 pathsize ;
text
text * xpathsupp ,
* xpathsupp ,
* xpres ;
* xpres ;
/* PG_GETARG_TEXT_P(0) is document buffer */
/* PG_GETARG_TEXT_P(0) is document buffer */
@ -424,8 +373,7 @@ xpath_string(PG_FUNCTION_ARGS)
xpath [ pathsize + 7 ] = ' ) ' ;
xpath [ pathsize + 7 ] = ' ) ' ;
xpath [ pathsize + 8 ] = ' \0 ' ;
xpath [ pathsize + 8 ] = ' \0 ' ;
xpres = pgxml_result_to_text (
xpres = pgxml_result_to_text ( pgxml_xpath ( PG_GETARG_TEXT_P ( 0 ) , xpath ) ,
pgxml_xpath ( PG_GETARG_TEXT_P ( 0 ) , xpath ) ,
NULL , NULL , NULL ) ;
NULL , NULL , NULL ) ;
xmlCleanupParser ( ) ;
xmlCleanupParser ( ) ;
@ -444,9 +392,7 @@ xpath_number(PG_FUNCTION_ARGS)
{
{
xmlChar * xpath ;
xmlChar * xpath ;
int32 pathsize ;
int32 pathsize ;
text
text * xpathsupp ;
* xpathsupp ;
float4 fRes ;
float4 fRes ;
xmlXPathObjectPtr res ;
xmlXPathObjectPtr res ;
@ -473,7 +419,6 @@ xpath_number(PG_FUNCTION_ARGS)
PG_RETURN_NULL ( ) ;
PG_RETURN_NULL ( ) ;
PG_RETURN_FLOAT4 ( fRes ) ;
PG_RETURN_FLOAT4 ( fRes ) ;
}
}
@ -484,9 +429,7 @@ xpath_bool(PG_FUNCTION_ARGS)
{
{
xmlChar * xpath ;
xmlChar * xpath ;
int32 pathsize ;
int32 pathsize ;
text
text * xpathsupp ;
* xpathsupp ;
int bRes ;
int bRes ;
xmlXPathObjectPtr res ;
xmlXPathObjectPtr res ;
@ -510,26 +453,21 @@ xpath_bool(PG_FUNCTION_ARGS)
bRes = xmlXPathCastToBoolean ( res ) ;
bRes = xmlXPathCastToBoolean ( res ) ;
xmlCleanupParser ( ) ;
xmlCleanupParser ( ) ;
PG_RETURN_BOOL ( bRes ) ;
PG_RETURN_BOOL ( bRes ) ;
}
}
/* Core function to evaluate XPath query */
/* Core function to evaluate XPath query */
xmlXPathObjectPtr
static xmlXPathObjectPtr
pgxml_xpath ( text * document , xmlChar * xpath )
pgxml_xpath ( text * document , xmlChar * xpath )
{
{
xmlDocPtr doctree ;
xmlDocPtr doctree ;
xmlXPathContextPtr ctxt ;
xmlXPathContextPtr ctxt ;
xmlXPathObjectPtr res ;
xmlXPathObjectPtr res ;
xmlXPathCompExprPtr comppath ;
xmlXPathCompExprPtr comppath ;
int32 docsize ;
int32 docsize ;
docsize = VARSIZE ( document ) - VARHDRSZ ;
docsize = VARSIZE ( document ) - VARHDRSZ ;
pgxml_parser_init ( ) ;
pgxml_parser_init ( ) ;
@ -543,16 +481,13 @@ pgxml_xpath(text *document, xmlChar * xpath)
ctxt = xmlXPathNewContext ( doctree ) ;
ctxt = xmlXPathNewContext ( doctree ) ;
ctxt - > node = xmlDocGetRootElement ( doctree ) ;
ctxt - > node = xmlDocGetRootElement ( doctree ) ;
/* compile the path */
/* compile the path */
comppath = xmlXPathCompile ( xpath ) ;
comppath = xmlXPathCompile ( xpath ) ;
if ( comppath = = NULL )
if ( comppath = = NULL )
{
{
xmlCleanupParser ( ) ;
xmlCleanupParser ( ) ;
xmlFreeDoc ( doctree ) ;
xmlFreeDoc ( doctree ) ;
elog_error ( ERROR , " XPath Syntax Error " , 1 ) ;
elog_error ( " XPath Syntax Error " , true ) ;
return NULL ;
}
}
/* Now evaluate the path expression. */
/* Now evaluate the path expression. */
@ -571,12 +506,11 @@ pgxml_xpath(text *document, xmlChar * xpath)
return res ;
return res ;
}
}
text
static text *
*
pgxml_result_to_text ( xmlXPathObjectPtr res ,
pgxml_result_to_text ( xmlXPathObjectPtr res ,
xmlChar * toptag ,
xmlChar * toptag ,
xmlChar * septag ,
xmlChar * septag ,
xmlChar * plainsep )
xmlChar * plainsep )
{
{
xmlChar * xpresstr ;
xmlChar * xpresstr ;
int32 ressize ;
int32 ressize ;
@ -604,7 +538,6 @@ pgxml_result_to_text(xmlXPathObjectPtr res,
xpresstr = xmlStrdup ( " <unsupported/> " ) ;
xpresstr = xmlStrdup ( " <unsupported/> " ) ;
}
}
/* Now convert this result back to text */
/* Now convert this result back to text */
ressize = strlen ( xpresstr ) ;
ressize = strlen ( xpresstr ) ;
xpres = ( text * ) palloc ( ressize + VARHDRSZ ) ;
xpres = ( text * ) palloc ( ressize + VARHDRSZ ) ;
@ -617,27 +550,33 @@ pgxml_result_to_text(xmlXPathObjectPtr res,
xmlFree ( xpresstr ) ;
xmlFree ( xpresstr ) ;
elog_error ( ERROR , " XPath error " , 0 ) ;
elog_error ( " XPath error " , false ) ;
return xpres ;
return xpres ;
}
}
/* xpath_table is a table function. It needs some tidying (as do the
/*
* xpath_table is a table function . It needs some tidying ( as do the
* other functions here !
* other functions here !
*/
*/
PG_FUNCTION_INFO_V1 ( xpath_table ) ;
PG_FUNCTION_INFO_V1 ( xpath_table ) ;
Datum
Datum
xpath_table ( PG_FUNCTION_ARGS )
xpath_table ( PG_FUNCTION_ARGS )
{
{
/* SPI (input tuple) support */
/* Function parameters */
char * pkeyfield = GET_STR ( PG_GETARG_TEXT_P ( 0 ) ) ;
char * xmlfield = GET_STR ( PG_GETARG_TEXT_P ( 1 ) ) ;
char * relname = GET_STR ( PG_GETARG_TEXT_P ( 2 ) ) ;
char * xpathset = GET_STR ( PG_GETARG_TEXT_P ( 3 ) ) ;
char * condition = GET_STR ( PG_GETARG_TEXT_P ( 4 ) ) ;
/* SPI (input tuple) support */
SPITupleTable * tuptable ;
SPITupleTable * tuptable ;
HeapTuple spi_tuple ;
HeapTuple spi_tuple ;
TupleDesc spi_tupdesc ;
TupleDesc spi_tupdesc ;
/* Output tuple (tuplestore) support */
/* Output tuple (tuplestore) support */
Tuplestorestate * tupstore = NULL ;
Tuplestorestate * tupstore = NULL ;
TupleDesc ret_tupdesc ;
TupleDesc ret_tupdesc ;
HeapTuple ret_tuple ;
HeapTuple ret_tuple ;
@ -647,13 +586,6 @@ xpath_table(PG_FUNCTION_ARGS)
MemoryContext per_query_ctx ;
MemoryContext per_query_ctx ;
MemoryContext oldcontext ;
MemoryContext oldcontext ;
/* Function parameters */
char * pkeyfield = GET_STR ( PG_GETARG_TEXT_P ( 0 ) ) ;
char * xmlfield = GET_STR ( PG_GETARG_TEXT_P ( 1 ) ) ;
char * relname = GET_STR ( PG_GETARG_TEXT_P ( 2 ) ) ;
char * xpathset = GET_STR ( PG_GETARG_TEXT_P ( 3 ) ) ;
char * condition = GET_STR ( PG_GETARG_TEXT_P ( 4 ) ) ;
char * * values ;
char * * values ;
xmlChar * * xpaths ;
xmlChar * * xpaths ;
xmlChar * pos ;
xmlChar * pos ;
@ -666,9 +598,8 @@ xpath_table(PG_FUNCTION_ARGS)
int j ;
int j ;
int rownr ; /* For issuing multiple rows from one original
int rownr ; /* For issuing multiple rows from one original
* document */
* document */
int had_values ; /* To determine end of nodeset results */
bool had_values ; /* To determine end of nodeset results */
StringInfoData query_buf ;
StringInfo querysql ;
/* We only have a valid tuple description in table function mode */
/* We only have a valid tuple description in table function mode */
if ( rsinfo = = NULL | | ! IsA ( rsinfo , ReturnSetInfo ) )
if ( rsinfo = = NULL | | ! IsA ( rsinfo , ReturnSetInfo ) )
@ -694,7 +625,6 @@ xpath_table(PG_FUNCTION_ARGS)
* The tuplestore must exist in a higher context than this function call
* The tuplestore must exist in a higher context than this function call
* ( per_query_ctx is used )
* ( per_query_ctx is used )
*/
*/
per_query_ctx = rsinfo - > econtext - > ecxt_per_query_memory ;
per_query_ctx = rsinfo - > econtext - > ecxt_per_query_memory ;
oldcontext = MemoryContextSwitchTo ( per_query_ctx ) ;
oldcontext = MemoryContextSwitchTo ( per_query_ctx ) ;
@ -709,6 +639,12 @@ xpath_table(PG_FUNCTION_ARGS)
/* get the requested return tuple description */
/* get the requested return tuple description */
ret_tupdesc = CreateTupleDescCopy ( rsinfo - > expectedDesc ) ;
ret_tupdesc = CreateTupleDescCopy ( rsinfo - > expectedDesc ) ;
/* must have at least one output column (for the pkey) */
if ( ret_tupdesc - > natts < 1 )
ereport ( ERROR ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
errmsg ( " xpath_table must have at least one output column " ) ) ) ;
/*
/*
* At the moment we assume that the returned attributes make sense for the
* At the moment we assume that the returned attributes make sense for the
* XPath specififed ( i . e . we trust the caller ) . It ' s not fatal if they get
* XPath specififed ( i . e . we trust the caller ) . It ' s not fatal if they get
@ -724,60 +660,58 @@ xpath_table(PG_FUNCTION_ARGS)
rsinfo - > setDesc = ret_tupdesc ;
rsinfo - > setDesc = ret_tupdesc ;
values = ( char * * ) palloc ( ret_tupdesc - > natts * sizeof ( char * ) ) ;
values = ( char * * ) palloc ( ret_tupdesc - > natts * sizeof ( char * ) ) ;
xpaths = ( xmlChar * * ) palloc ( ret_tupdesc - > natts * sizeof ( xmlChar * ) ) ;
xpaths = ( xmlChar * * ) palloc ( ret_tupdesc - > natts * sizeof ( xmlChar * ) ) ;
/* Split XPaths. xpathset is a writable CString. */
/*
* Split XPaths . xpathset is a writable CString .
/* Note that we stop splitting once we've done all needed for tupdesc */
*
* Note that we stop splitting once we ' ve done all needed for tupdesc
*/
numpaths = 0 ;
numpaths = 0 ;
pos = xpathset ;
pos = xpathset ;
do
while ( numpaths < ( ret_tup desc - > natts - 1 ) )
{
{
xpaths [ numpaths ] = pos ;
xpaths [ numpaths + + ] = ( xmlChar * ) pos ;
pos = strstr ( pos , pathsep ) ;
pos = strstr ( pos , pathsep ) ;
if ( pos ! = NULL )
if ( pos ! = NULL )
{
{
* pos = ' \0 ' ;
* pos = ' \0 ' ;
pos + + ;
pos + + ;
}
}
numpaths + + ;
else
} while ( ( pos ! = NULL ) & & ( numpaths < ( ret_tupdesc - > natts - 1 ) ) ) ;
break ;
}
/* Now build query */
/* Now build query */
initStringInfo ( & query_buf ) ;
querysql = makeStringInfo ( ) ;
/* Build initial sql statement */
/* Build initial sql statement */
appendStringInfo ( querysql , " SELECT %s, %s FROM %s WHERE %s " ,
appendStringInfo ( & query_buf , " SELECT %s, %s FROM %s WHERE %s " ,
pkeyfield ,
pkeyfield ,
xmlfield ,
xmlfield ,
relname ,
relname ,
condition
condition ) ;
) ;
if ( ( ret = SPI_connect ( ) ) < 0 )
if ( ( ret = SPI_connect ( ) ) < 0 )
elog ( ERROR , " xpath_table: SPI_connect returned %d " , ret ) ;
elog ( ERROR , " xpath_table: SPI_connect returned %d " , ret ) ;
if ( ( ret = SPI_exec ( querysql - > data , 0 ) ) ! = SPI_OK_SELECT )
if ( ( ret = SPI_exec ( query_buf . data , 0 ) ) ! = SPI_OK_SELECT )
elog ( ERROR , " xpath_table: SPI execution failed for query %s " , querysql - > data ) ;
elog ( ERROR , " xpath_table: SPI execution failed for query %s " ,
query_buf . data ) ;
proc = SPI_processed ;
proc = SPI_processed ;
/* elog(DEBUG1,"xpath_table: SPI returned %d rows",proc); */
/* elog(DEBUG1,"xpath_table: SPI returned %d rows",proc); */
tuptable = SPI_tuptable ;
tuptable = SPI_tuptable ;
spi_tupdesc = tuptable - > tupdesc ;
spi_tupdesc = tuptable - > tupdesc ;
/* Switch out of SPI context */
/* Switch out of SPI context */
MemoryContextSwitchTo ( oldcontext ) ;
MemoryContextSwitchTo ( oldcontext ) ;
/*
/* Check that SPI returned correct result. If you put a comma into one of
* Check that SPI returned correct result . If you put a comma into one of
* the function parameters , this will catch it when the SPI query returns
* the function parameters , this will catch it when the SPI query returns
* e . g . 3 columns .
* e . g . 3 columns .
*/
*/
if ( spi_tupdesc - > natts ! = 2 )
if ( spi_tupdesc - > natts ! = 2 )
{
{
ereport ( ERROR , ( errcode ( ERRCODE_INVALID_PARAMETER_VALUE ) ,
ereport ( ERROR , ( errcode ( ERRCODE_INVALID_PARAMETER_VALUE ) ,
@ -785,10 +719,12 @@ xpath_table(PG_FUNCTION_ARGS)
errdetail ( " Expected two columns in SPI result, got %d " , spi_tupdesc - > natts ) ) ) ;
errdetail ( " Expected two columns in SPI result, got %d " , spi_tupdesc - > natts ) ) ) ;
}
}
/* Setup the parser. Beware that this must happen in the same context as the
/*
* cleanup - which means that any error from here on must do cleanup to
* Setup the parser . Beware that this must happen in the same context as
* ensure that the entity table doesn ' t get freed by being out of context .
* the cleanup - which means that any error from here on must do cleanup
*/
* to ensure that the entity table doesn ' t get freed by being out of
* context .
*/
pgxml_parser_init ( ) ;
pgxml_parser_init ( ) ;
/* For each row i.e. document returned from SPI */
/* For each row i.e. document returned from SPI */
@ -796,13 +732,10 @@ xpath_table(PG_FUNCTION_ARGS)
{
{
char * pkey ;
char * pkey ;
char * xmldoc ;
char * xmldoc ;
xmlDocPtr doctree ;
xmlDocPtr doctree ;
xmlXPathContextPtr ctxt ;
xmlXPathContextPtr ctxt ;
xmlXPathObjectPtr res ;
xmlXPathObjectPtr res ;
xmlChar * resstr ;
xmlChar * resstr ;
xmlXPathCompExprPtr comppath ;
xmlXPathCompExprPtr comppath ;
/* Extract the row data as C Strings */
/* Extract the row data as C Strings */
@ -812,10 +745,9 @@ xpath_table(PG_FUNCTION_ARGS)
/*
/*
* Clear the values array , so that not - well - formed documents return
* Clear the values array , so that not - well - formed documents return
* NULL in all columns .
* NULL in all columns . Note that this also means that spare columns
* will be NULL .
*/
*/
/* Note that this also means that spare columns will be NULL. */
for ( j = 0 ; j < ret_tupdesc - > natts ; j + + )
for ( j = 0 ; j < ret_tupdesc - > natts ; j + + )
values [ j ] = NULL ;
values [ j ] = NULL ;
@ -843,10 +775,9 @@ xpath_table(PG_FUNCTION_ARGS)
do
do
{
{
/* Now evaluate the set of xpaths. */
/* Now evaluate the set of xpaths. */
had_values = 0 ;
had_values = false ;
for ( j = 0 ; j < numpaths ; j + + )
for ( j = 0 ; j < numpaths ; j + + )
{
{
ctxt = xmlXPathNewContext ( doctree ) ;
ctxt = xmlXPathNewContext ( doctree ) ;
ctxt - > node = xmlDocGetRootElement ( doctree ) ;
ctxt - > node = xmlDocGetRootElement ( doctree ) ;
xmlSetGenericErrorFunc ( ctxt , pgxml_errorHandler ) ;
xmlSetGenericErrorFunc ( ctxt , pgxml_errorHandler ) ;
@ -857,10 +788,7 @@ xpath_table(PG_FUNCTION_ARGS)
{
{
xmlCleanupParser ( ) ;
xmlCleanupParser ( ) ;
xmlFreeDoc ( doctree ) ;
xmlFreeDoc ( doctree ) ;
elog_error ( " XPath Syntax Error " , true ) ;
elog_error ( ERROR , " XPath Syntax Error " , 1 ) ;
PG_RETURN_NULL ( ) ; /* Keep compiler happy */
}
}
/* Now evaluate the path expression. */
/* Now evaluate the path expression. */
@ -873,11 +801,12 @@ xpath_table(PG_FUNCTION_ARGS)
{
{
case XPATH_NODESET :
case XPATH_NODESET :
/* We see if this nodeset has enough nodes */
/* We see if this nodeset has enough nodes */
if ( ( res - > nodesetval ! = NULL ) & & ( rownr < res - > nodesetval - > nodeNr ) )
if ( res - > nodesetval ! = NULL & &
rownr < res - > nodesetval - > nodeNr )
{
{
resstr =
resstr =
xmlXPathCastNodeToString ( res - > nodesetval - > nodeTab [ rownr ] ) ;
xmlXPathCastNodeToString ( res - > nodesetval - > nodeTab [ rownr ] ) ;
had_values = 1 ;
had_values = true ;
}
}
else
else
resstr = NULL ;
resstr = NULL ;
@ -893,7 +822,6 @@ xpath_table(PG_FUNCTION_ARGS)
resstr = xmlStrdup ( " <unsupported/> " ) ;
resstr = xmlStrdup ( " <unsupported/> " ) ;
}
}
/*
/*
* Insert this into the appropriate column in the
* Insert this into the appropriate column in the
* result tuple .
* result tuple .
@ -902,6 +830,7 @@ xpath_table(PG_FUNCTION_ARGS)
}
}
xmlXPathFreeContext ( ctxt ) ;
xmlXPathFreeContext ( ctxt ) ;
}
}
/* Now add the tuple to the output, if there is one. */
/* Now add the tuple to the output, if there is one. */
if ( had_values )
if ( had_values )
{
{
@ -911,9 +840,7 @@ xpath_table(PG_FUNCTION_ARGS)
}
}
rownr + + ;
rownr + + ;
} while ( had_values ) ;
} while ( had_values ) ;
}
}
xmlFreeDoc ( doctree ) ;
xmlFreeDoc ( doctree ) ;
@ -925,7 +852,7 @@ xpath_table(PG_FUNCTION_ARGS)
}
}
xmlCleanupParser ( ) ;
xmlCleanupParser ( ) ;
/* Needed to flag completeness in 7.3.1. 7.4 defines it as a no-op. */
tuplestore_donestoring ( tupstore ) ;
tuplestore_donestoring ( tupstore ) ;
SPI_finish ( ) ;
SPI_finish ( ) ;
@ -940,5 +867,4 @@ xpath_table(PG_FUNCTION_ARGS)
* expecting .
* expecting .
*/
*/
return ( Datum ) 0 ;
return ( Datum ) 0 ;
}
}