diff options
Diffstat (limited to 'contrib/xml2')
-rw-r--r-- | contrib/xml2/.gitignore | 4 | ||||
-rw-r--r-- | contrib/xml2/Makefile | 10 | ||||
-rw-r--r-- | contrib/xml2/expected/xml2.out | 78 | ||||
-rw-r--r-- | contrib/xml2/expected/xml2_1.out | 60 | ||||
-rw-r--r-- | contrib/xml2/pgxml.sql.in | 77 | ||||
-rw-r--r-- | contrib/xml2/sql/xml2.sql | 62 | ||||
-rw-r--r-- | contrib/xml2/uninstall_pgxml.sql | 33 | ||||
-rw-r--r-- | contrib/xml2/xml2--1.0.sql | 70 | ||||
-rw-r--r-- | contrib/xml2/xml2--unpackaged--1.0.sql | 26 | ||||
-rw-r--r-- | contrib/xml2/xml2.control | 6 | ||||
-rw-r--r-- | contrib/xml2/xpath.c | 173 | ||||
-rw-r--r-- | contrib/xml2/xslt_proc.c | 52 |
12 files changed, 416 insertions, 235 deletions
diff --git a/contrib/xml2/.gitignore b/contrib/xml2/.gitignore new file mode 100644 index 0000000000..5dcb3ff972 --- /dev/null +++ b/contrib/xml2/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/contrib/xml2/Makefile b/contrib/xml2/Makefile index dd45a914c9..ad325723c9 100644 --- a/contrib/xml2/Makefile +++ b/contrib/xml2/Makefile @@ -1,15 +1,15 @@ -# $PostgreSQL: pgsql/contrib/xml2/Makefile,v 1.14 2010/03/01 18:07:59 tgl Exp $ +# contrib/xml2/Makefile MODULE_big = pgxml - OBJS = xpath.o xslt_proc.o -SHLIB_LINK += $(filter -lxslt, $(LIBS)) $(filter -lxml2, $(LIBS)) +EXTENSION = xml2 +DATA = xml2--1.0.sql xml2--unpackaged--1.0.sql -DATA_built = pgxml.sql -DATA = uninstall_pgxml.sql REGRESS = xml2 +SHLIB_LINK += $(filter -lxslt, $(LIBS)) $(filter -lxml2, $(LIBS)) + ifdef USE_PGXS PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) diff --git a/contrib/xml2/expected/xml2.out b/contrib/xml2/expected/xml2.out index 74896b0802..3bf676fb40 100644 --- a/contrib/xml2/expected/xml2.out +++ b/contrib/xml2/expected/xml2.out @@ -1,10 +1,4 @@ --- --- first, define the functions. Turn off echoing so that expected file --- does not depend on contents of pgxml.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION xml2; select query_to_xml('select 1 as x',true,false,''); query_to_xml --------------------------------------------------------------- @@ -18,7 +12,7 @@ select query_to_xml('select 1 as x',true,false,''); (1 row) -select xslt_process( query_to_xml('select x from generate_series(1,5) as +select xslt_process( query_to_xml('select x from generate_series(1,5) as x',true,false,'')::text, $$<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> @@ -145,3 +139,71 @@ values Value</attribute></attributes>'); create index idx_xpath on t1 ( xpath_string ('/attributes/attribute[@name="attr_1"]/text()', xml_data::text)); +SELECT xslt_process('<employee><name>cim</name><age>30</age><pay>400</pay></employee>'::text, $$<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> + <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/> + <xsl:strip-space elements="*"/> + <xsl:param name="n1"/> + <xsl:param name="n2"/> + <xsl:param name="n3"/> + <xsl:param name="n4"/> + <xsl:param name="n5" select="'me'"/> + <xsl:template match="*"> + <xsl:element name="samples"> + <xsl:element name="sample"> + <xsl:value-of select="$n1"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n2"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n3"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n4"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n5"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n6"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n7"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n8"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n9"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n10"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n11"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n12"/> + </xsl:element> + </xsl:element> + </xsl:template> +</xsl:stylesheet>$$::text, 'n1="v1",n2="v2",n3="v3",n4="v4",n5="v5",n6="v6",n7="v7",n8="v8",n9="v9",n10="v10",n11="v11",n12="v12"'::text); + xslt_process +------------------------ + <samples> + + <sample>v1</sample> + + <sample>v2</sample> + + <sample>v3</sample> + + <sample>v4</sample> + + <sample>v5</sample> + + <sample>v6</sample> + + <sample>v7</sample> + + <sample>v8</sample> + + <sample>v9</sample> + + <sample>v10</sample>+ + <sample>v11</sample>+ + <sample>v12</sample>+ + </samples> + + +(1 row) + diff --git a/contrib/xml2/expected/xml2_1.out b/contrib/xml2/expected/xml2_1.out index 083fc3b2ca..fda626e08c 100644 --- a/contrib/xml2/expected/xml2_1.out +++ b/contrib/xml2/expected/xml2_1.out @@ -1,10 +1,4 @@ --- --- first, define the functions. Turn off echoing so that expected file --- does not depend on contents of pgxml.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION xml2; select query_to_xml('select 1 as x',true,false,''); query_to_xml --------------------------------------------------------------- @@ -18,7 +12,7 @@ select query_to_xml('select 1 as x',true,false,''); (1 row) -select xslt_process( query_to_xml('select x from generate_series(1,5) as +select xslt_process( query_to_xml('select x from generate_series(1,5) as x',true,false,'')::text, $$<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> @@ -107,3 +101,53 @@ values Value</attribute></attributes>'); create index idx_xpath on t1 ( xpath_string ('/attributes/attribute[@name="attr_1"]/text()', xml_data::text)); +SELECT xslt_process('<employee><name>cim</name><age>30</age><pay>400</pay></employee>'::text, $$<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> + <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/> + <xsl:strip-space elements="*"/> + <xsl:param name="n1"/> + <xsl:param name="n2"/> + <xsl:param name="n3"/> + <xsl:param name="n4"/> + <xsl:param name="n5" select="'me'"/> + <xsl:template match="*"> + <xsl:element name="samples"> + <xsl:element name="sample"> + <xsl:value-of select="$n1"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n2"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n3"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n4"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n5"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n6"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n7"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n8"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n9"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n10"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n11"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n12"/> + </xsl:element> + </xsl:element> + </xsl:template> +</xsl:stylesheet>$$::text, 'n1="v1",n2="v2",n3="v3",n4="v4",n5="v5",n6="v6",n7="v7",n8="v8",n9="v9",n10="v10",n11="v11",n12="v12"'::text); +ERROR: xslt_process() is not available without libxslt diff --git a/contrib/xml2/pgxml.sql.in b/contrib/xml2/pgxml.sql.in deleted file mode 100644 index 98d8f81b57..0000000000 --- a/contrib/xml2/pgxml.sql.in +++ /dev/null @@ -1,77 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/xml2/pgxml.sql.in,v 1.12 2010/03/01 18:07:59 tgl Exp $ */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; - ---SQL for XML parser - -CREATE OR REPLACE FUNCTION xml_is_well_formed(text) RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C STRICT IMMUTABLE; - --- deprecated old name for xml_is_well_formed -CREATE OR REPLACE FUNCTION xml_valid(text) RETURNS bool -AS 'MODULE_PATHNAME', 'xml_is_well_formed' -LANGUAGE C STRICT IMMUTABLE; - -CREATE OR REPLACE FUNCTION xml_encode_special_chars(text) RETURNS text -AS 'MODULE_PATHNAME' -LANGUAGE C STRICT IMMUTABLE; - -CREATE OR REPLACE FUNCTION xpath_string(text,text) RETURNS text -AS 'MODULE_PATHNAME' -LANGUAGE C STRICT IMMUTABLE; - -CREATE OR REPLACE FUNCTION xpath_nodeset(text,text,text,text) RETURNS text -AS 'MODULE_PATHNAME' -LANGUAGE C STRICT IMMUTABLE; - -CREATE OR REPLACE FUNCTION xpath_number(text,text) RETURNS float4 -AS 'MODULE_PATHNAME' -LANGUAGE C STRICT IMMUTABLE; - -CREATE OR REPLACE FUNCTION xpath_bool(text,text) RETURNS boolean -AS 'MODULE_PATHNAME' -LANGUAGE C STRICT IMMUTABLE; - --- List function - -CREATE OR REPLACE FUNCTION xpath_list(text,text,text) RETURNS text -AS 'MODULE_PATHNAME' -LANGUAGE C STRICT IMMUTABLE; - -CREATE OR REPLACE FUNCTION xpath_list(text,text) RETURNS text -AS 'SELECT xpath_list($1,$2,'','')' -LANGUAGE SQL STRICT IMMUTABLE; - --- Wrapper functions for nodeset where no tags needed - -CREATE OR REPLACE FUNCTION xpath_nodeset(text,text) -RETURNS text -AS 'SELECT xpath_nodeset($1,$2,'''','''')' -LANGUAGE SQL STRICT IMMUTABLE; - -CREATE OR REPLACE FUNCTION xpath_nodeset(text,text,text) -RETURNS text -AS 'SELECT xpath_nodeset($1,$2,'''',$3)' -LANGUAGE SQL STRICT IMMUTABLE; - --- Table function - -CREATE OR REPLACE FUNCTION xpath_table(text,text,text,text,text) -RETURNS setof record -AS 'MODULE_PATHNAME' -LANGUAGE C STRICT STABLE; - --- XSLT functions - -CREATE OR REPLACE FUNCTION xslt_process(text,text,text) -RETURNS text -AS 'MODULE_PATHNAME' -LANGUAGE C STRICT VOLATILE; - --- the function checks for the correct argument count -CREATE OR REPLACE FUNCTION xslt_process(text,text) -RETURNS text -AS 'MODULE_PATHNAME' -LANGUAGE C STRICT IMMUTABLE; diff --git a/contrib/xml2/sql/xml2.sql b/contrib/xml2/sql/xml2.sql index 73723b6be1..4a996af716 100644 --- a/contrib/xml2/sql/xml2.sql +++ b/contrib/xml2/sql/xml2.sql @@ -1,16 +1,8 @@ --- --- first, define the functions. Turn off echoing so that expected file --- does not depend on contents of pgxml.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i pgxml.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION xml2; select query_to_xml('select 1 as x',true,false,''); -select xslt_process( query_to_xml('select x from generate_series(1,5) as +select xslt_process( query_to_xml('select x from generate_series(1,5) as x',true,false,'')::text, $$<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> @@ -80,3 +72,53 @@ Value</attribute></attributes>'); create index idx_xpath on t1 ( xpath_string ('/attributes/attribute[@name="attr_1"]/text()', xml_data::text)); + +SELECT xslt_process('<employee><name>cim</name><age>30</age><pay>400</pay></employee>'::text, $$<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> + <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/> + <xsl:strip-space elements="*"/> + <xsl:param name="n1"/> + <xsl:param name="n2"/> + <xsl:param name="n3"/> + <xsl:param name="n4"/> + <xsl:param name="n5" select="'me'"/> + <xsl:template match="*"> + <xsl:element name="samples"> + <xsl:element name="sample"> + <xsl:value-of select="$n1"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n2"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n3"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n4"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n5"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n6"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n7"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n8"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n9"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n10"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n11"/> + </xsl:element> + <xsl:element name="sample"> + <xsl:value-of select="$n12"/> + </xsl:element> + </xsl:element> + </xsl:template> +</xsl:stylesheet>$$::text, 'n1="v1",n2="v2",n3="v3",n4="v4",n5="v5",n6="v6",n7="v7",n8="v8",n9="v9",n10="v10",n11="v11",n12="v12"'::text); diff --git a/contrib/xml2/uninstall_pgxml.sql b/contrib/xml2/uninstall_pgxml.sql deleted file mode 100644 index 09441ef01f..0000000000 --- a/contrib/xml2/uninstall_pgxml.sql +++ /dev/null @@ -1,33 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/xml2/uninstall_pgxml.sql,v 1.4 2007/11/13 04:24:29 momjian Exp $ */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION xslt_process(text,text); - -DROP FUNCTION xslt_process(text,text,text); - -DROP FUNCTION xpath_table(text,text,text,text,text); - -DROP FUNCTION xpath_nodeset(text,text,text); - -DROP FUNCTION xpath_nodeset(text,text); - -DROP FUNCTION xpath_list(text,text); - -DROP FUNCTION xpath_list(text,text,text); - -DROP FUNCTION xpath_bool(text,text); - -DROP FUNCTION xpath_number(text,text); - -DROP FUNCTION xpath_nodeset(text,text,text,text); - -DROP FUNCTION xpath_string(text,text); - -DROP FUNCTION xml_encode_special_chars(text); - --- deprecated old name for xml_is_well_formed -DROP FUNCTION xml_valid(text); - -DROP FUNCTION xml_is_well_formed(text); diff --git a/contrib/xml2/xml2--1.0.sql b/contrib/xml2/xml2--1.0.sql new file mode 100644 index 0000000000..bc27dc89ca --- /dev/null +++ b/contrib/xml2/xml2--1.0.sql @@ -0,0 +1,70 @@ +/* contrib/xml2/xml2--1.0.sql */ + +--SQL for XML parser + +-- deprecated old name for xml_is_well_formed +CREATE FUNCTION xml_valid(text) RETURNS bool +AS 'xml_is_well_formed' +LANGUAGE INTERNAL STRICT STABLE; + +CREATE FUNCTION xml_encode_special_chars(text) RETURNS text +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION xpath_string(text,text) RETURNS text +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION xpath_nodeset(text,text,text,text) RETURNS text +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION xpath_number(text,text) RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION xpath_bool(text,text) RETURNS boolean +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +-- List function + +CREATE FUNCTION xpath_list(text,text,text) RETURNS text +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION xpath_list(text,text) RETURNS text +AS 'SELECT xpath_list($1,$2,'','')' +LANGUAGE SQL STRICT IMMUTABLE; + +-- Wrapper functions for nodeset where no tags needed + +CREATE FUNCTION xpath_nodeset(text,text) +RETURNS text +AS 'SELECT xpath_nodeset($1,$2,'''','''')' +LANGUAGE SQL STRICT IMMUTABLE; + +CREATE FUNCTION xpath_nodeset(text,text,text) +RETURNS text +AS 'SELECT xpath_nodeset($1,$2,'''',$3)' +LANGUAGE SQL STRICT IMMUTABLE; + +-- Table function + +CREATE FUNCTION xpath_table(text,text,text,text,text) +RETURNS setof record +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT STABLE; + +-- XSLT functions + +CREATE FUNCTION xslt_process(text,text,text) +RETURNS text +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT VOLATILE; + +-- the function checks for the correct argument count +CREATE FUNCTION xslt_process(text,text) +RETURNS text +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; diff --git a/contrib/xml2/xml2--unpackaged--1.0.sql b/contrib/xml2/xml2--unpackaged--1.0.sql new file mode 100644 index 0000000000..1aa894a619 --- /dev/null +++ b/contrib/xml2/xml2--unpackaged--1.0.sql @@ -0,0 +1,26 @@ +/* contrib/xml2/xml2--unpackaged--1.0.sql */ + +ALTER EXTENSION xml2 ADD function xslt_process(text,text); +ALTER EXTENSION xml2 ADD function xslt_process(text,text,text); +ALTER EXTENSION xml2 ADD function xpath_table(text,text,text,text,text); +ALTER EXTENSION xml2 ADD function xpath_nodeset(text,text,text); +ALTER EXTENSION xml2 ADD function xpath_nodeset(text,text); +ALTER EXTENSION xml2 ADD function xpath_list(text,text); +ALTER EXTENSION xml2 ADD function xpath_list(text,text,text); +ALTER EXTENSION xml2 ADD function xpath_bool(text,text); +ALTER EXTENSION xml2 ADD function xpath_number(text,text); +ALTER EXTENSION xml2 ADD function xpath_nodeset(text,text,text,text); +ALTER EXTENSION xml2 ADD function xpath_string(text,text); +ALTER EXTENSION xml2 ADD function xml_encode_special_chars(text); +ALTER EXTENSION xml2 ADD function xml_valid(text); + +-- xml_valid is now an alias for core xml_is_well_formed() + +CREATE OR REPLACE FUNCTION xml_valid(text) RETURNS bool +AS 'xml_is_well_formed' +LANGUAGE INTERNAL STRICT STABLE; + +-- xml_is_well_formed is now in core, not needed in extension. +-- be careful to drop extension's copy not core's. + +DROP FUNCTION @extschema@.xml_is_well_formed(text); diff --git a/contrib/xml2/xml2.control b/contrib/xml2/xml2.control new file mode 100644 index 0000000000..51de678d5f --- /dev/null +++ b/contrib/xml2/xml2.control @@ -0,0 +1,6 @@ +# xml2 extension +comment = 'XPath querying and XSLT' +default_version = '1.0' +module_pathname = '$libdir/pgxml' +# non-relocatable because xml2--unpackaged--1.0.sql needs to use @extschema@ +relocatable = false diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c index dbf0b76f92..44c600e134 100644 --- a/contrib/xml2/xpath.c +++ b/contrib/xml2/xpath.c @@ -1,5 +1,5 @@ /* - * $PostgreSQL: pgsql/contrib/xml2/xpath.c,v 1.30 2010/07/06 19:18:55 momjian Exp $ + * contrib/xml2/xpath.c * * Parser interface for DOM-based parser (libxml) rather than * stream-based SAX-type parser @@ -40,6 +40,15 @@ Datum xpath_table(PG_FUNCTION_ARGS); void pgxml_parser_init(void); +/* workspace for pgxml_xpath() */ + +typedef struct +{ + xmlDocPtr doctree; + xmlXPathContextPtr ctxt; + xmlXPathObjectPtr res; +} xpath_workspace; + /* local declarations */ static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset, @@ -51,7 +60,10 @@ static text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag, static xmlChar *pgxml_texttoxmlchar(text *textstring); -static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath); +static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath, + xpath_workspace *workspace); + +static void cleanup_workspace(xpath_workspace *workspace); /* @@ -71,7 +83,14 @@ pgxml_parser_init(void) } -/* Returns true if document is well-formed */ +/* + * Returns true if document is well-formed + * + * Note: this has been superseded by a core function. We still have to + * have it in the contrib module so that existing SQL-level references + * to the function won't fail; but in normal usage with up-to-date SQL + * definitions for the contrib module, this won't be called. + */ PG_FUNCTION_INFO_V1(xml_is_well_formed); @@ -214,25 +233,22 @@ PG_FUNCTION_INFO_V1(xpath_nodeset); Datum xpath_nodeset(PG_FUNCTION_ARGS) { - xmlChar *xpath, - *toptag, - *septag; - int32 pathsize; - text *xpathsupp, - *xpres; - - /* PG_GETARG_TEXT_P(0) is document buffer */ - xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ + text *document = PG_GETARG_TEXT_P(0); + text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ + xmlChar *toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2)); + xmlChar *septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3)); + xmlChar *xpath; + text *xpres; + xmlXPathObjectPtr res; + xpath_workspace workspace; - toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2)); - septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3)); + xpath = pgxml_texttoxmlchar(xpathsupp); - pathsize = VARSIZE(xpathsupp) - VARHDRSZ; + res = pgxml_xpath(document, xpath, &workspace); - xpath = pgxml_texttoxmlchar(xpathsupp); + xpres = pgxml_result_to_text(res, toptag, septag, NULL); - xpres = pgxml_result_to_text(pgxml_xpath(PG_GETARG_TEXT_P(0), xpath), - toptag, septag, NULL); + cleanup_workspace(&workspace); pfree(xpath); @@ -250,23 +266,21 @@ PG_FUNCTION_INFO_V1(xpath_list); Datum xpath_list(PG_FUNCTION_ARGS) { - xmlChar *xpath, - *plainsep; - int32 pathsize; - text *xpathsupp, - *xpres; - - /* PG_GETARG_TEXT_P(0) is document buffer */ - xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ + text *document = PG_GETARG_TEXT_P(0); + text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ + xmlChar *plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2)); + xmlChar *xpath; + text *xpres; + xmlXPathObjectPtr res; + xpath_workspace workspace; - plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2)); + xpath = pgxml_texttoxmlchar(xpathsupp); - pathsize = VARSIZE(xpathsupp) - VARHDRSZ; + res = pgxml_xpath(document, xpath, &workspace); - xpath = pgxml_texttoxmlchar(xpathsupp); + xpres = pgxml_result_to_text(res, NULL, NULL, plainsep); - xpres = pgxml_result_to_text(pgxml_xpath(PG_GETARG_TEXT_P(0), xpath), - NULL, NULL, plainsep); + cleanup_workspace(&workspace); pfree(xpath); @@ -281,13 +295,13 @@ PG_FUNCTION_INFO_V1(xpath_string); Datum xpath_string(PG_FUNCTION_ARGS) { + text *document = PG_GETARG_TEXT_P(0); + text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ xmlChar *xpath; int32 pathsize; - text *xpathsupp, - *xpres; - - /* PG_GETARG_TEXT_P(0) is document buffer */ - xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ + text *xpres; + xmlXPathObjectPtr res; + xpath_workspace workspace; pathsize = VARSIZE(xpathsupp) - VARHDRSZ; @@ -298,13 +312,16 @@ xpath_string(PG_FUNCTION_ARGS) /* We could try casting to string using the libxml function? */ xpath = (xmlChar *) palloc(pathsize + 9); - memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize); strncpy((char *) xpath, "string(", 7); + memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize); xpath[pathsize + 7] = ')'; xpath[pathsize + 8] = '\0'; - xpres = pgxml_result_to_text(pgxml_xpath(PG_GETARG_TEXT_P(0), xpath), - NULL, NULL, NULL); + res = pgxml_xpath(document, xpath, &workspace); + + xpres = pgxml_result_to_text(res, NULL, NULL, NULL); + + cleanup_workspace(&workspace); pfree(xpath); @@ -319,21 +336,17 @@ PG_FUNCTION_INFO_V1(xpath_number); Datum xpath_number(PG_FUNCTION_ARGS) { + text *document = PG_GETARG_TEXT_P(0); + text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ xmlChar *xpath; - int32 pathsize; - text *xpathsupp; float4 fRes; - xmlXPathObjectPtr res; - - /* PG_GETARG_TEXT_P(0) is document buffer */ - xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ - - pathsize = VARSIZE(xpathsupp) - VARHDRSZ; + xpath_workspace workspace; xpath = pgxml_texttoxmlchar(xpathsupp); - res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath); + res = pgxml_xpath(document, xpath, &workspace); + pfree(xpath); if (res == NULL) @@ -341,6 +354,8 @@ xpath_number(PG_FUNCTION_ARGS) fRes = xmlXPathCastToNumber(res); + cleanup_workspace(&workspace); + if (xmlXPathIsNaN(fRes)) PG_RETURN_NULL(); @@ -353,21 +368,17 @@ PG_FUNCTION_INFO_V1(xpath_bool); Datum xpath_bool(PG_FUNCTION_ARGS) { + text *document = PG_GETARG_TEXT_P(0); + text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ xmlChar *xpath; - int32 pathsize; - text *xpathsupp; int bRes; - xmlXPathObjectPtr res; - - /* PG_GETARG_TEXT_P(0) is document buffer */ - xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ - - pathsize = VARSIZE(xpathsupp) - VARHDRSZ; + xpath_workspace workspace; xpath = pgxml_texttoxmlchar(xpathsupp); - res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath); + res = pgxml_xpath(document, xpath, &workspace); + pfree(xpath); if (res == NULL) @@ -375,6 +386,8 @@ xpath_bool(PG_FUNCTION_ARGS) bRes = xmlXPathCastToBoolean(res); + cleanup_workspace(&workspace); + PG_RETURN_BOOL(bRes); } @@ -383,49 +396,61 @@ xpath_bool(PG_FUNCTION_ARGS) /* Core function to evaluate XPath query */ static xmlXPathObjectPtr -pgxml_xpath(text *document, xmlChar *xpath) +pgxml_xpath(text *document, xmlChar *xpath, xpath_workspace *workspace) { - xmlDocPtr doctree; - xmlXPathContextPtr ctxt; + int32 docsize = VARSIZE(document) - VARHDRSZ; xmlXPathObjectPtr res; xmlXPathCompExprPtr comppath; - int32 docsize; - docsize = VARSIZE(document) - VARHDRSZ; + workspace->doctree = NULL; + workspace->ctxt = NULL; + workspace->res = NULL; pgxml_parser_init(); - doctree = xmlParseMemory((char *) VARDATA(document), docsize); - if (doctree == NULL) + workspace->doctree = xmlParseMemory((char *) VARDATA(document), docsize); + if (workspace->doctree == NULL) return NULL; /* not well-formed */ - ctxt = xmlXPathNewContext(doctree); - ctxt->node = xmlDocGetRootElement(doctree); + workspace->ctxt = xmlXPathNewContext(workspace->doctree); + workspace->ctxt->node = xmlDocGetRootElement(workspace->doctree); /* compile the path */ comppath = xmlXPathCompile(xpath); if (comppath == NULL) { - xmlFreeDoc(doctree); + cleanup_workspace(workspace); xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, "XPath Syntax Error"); } /* Now evaluate the path expression. */ - res = xmlXPathCompiledEval(comppath, ctxt); + res = xmlXPathCompiledEval(comppath, workspace->ctxt); + workspace->res = res; + xmlXPathFreeCompExpr(comppath); if (res == NULL) - { - xmlXPathFreeContext(ctxt); - xmlFreeDoc(doctree); + cleanup_workspace(workspace); - return NULL; - } - /* xmlFreeDoc(doctree); */ return res; } +/* Clean up after processing the result of pgxml_xpath() */ +static void +cleanup_workspace(xpath_workspace *workspace) +{ + if (workspace->res) + xmlXPathFreeObject(workspace->res); + workspace->res = NULL; + if (workspace->ctxt) + xmlXPathFreeContext(workspace->ctxt); + workspace->ctxt = NULL; + if (workspace->doctree) + xmlFreeDoc(workspace->doctree); + workspace->doctree = NULL; +} + static text * pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag, diff --git a/contrib/xml2/xslt_proc.c b/contrib/xml2/xslt_proc.c index 4c80732bb8..f8f7d7263f 100644 --- a/contrib/xml2/xslt_proc.c +++ b/contrib/xml2/xslt_proc.c @@ -1,5 +1,5 @@ /* - * $PostgreSQL: pgsql/contrib/xml2/xslt_proc.c,v 1.21 2010/07/06 19:18:55 momjian Exp $ + * contrib/xml2/xslt_proc.c * * XSLT processing functions (requiring libxslt) * @@ -41,9 +41,7 @@ Datum xslt_process(PG_FUNCTION_ARGS); extern void pgxml_parser_init(void); /* local defs */ -static void parse_params(const char **params, text *paramstr); - -#define MAXPARAMS 20 /* must be even, see parse_params() */ +static const char **parse_params(text *paramstr); #endif /* USE_LIBXSLT */ @@ -57,7 +55,7 @@ xslt_process(PG_FUNCTION_ARGS) text *doct = PG_GETARG_TEXT_P(0); text *ssheet = PG_GETARG_TEXT_P(1); text *paramstr; - const char *params[MAXPARAMS + 1]; /* +1 for the terminator */ + const char **params; xsltStylesheetPtr stylesheet = NULL; xmlDocPtr doctree; xmlDocPtr restree; @@ -69,11 +67,14 @@ xslt_process(PG_FUNCTION_ARGS) if (fcinfo->nargs == 3) { paramstr = PG_GETARG_TEXT_P(2); - parse_params(params, paramstr); + params = parse_params(paramstr); } else + { /* No parameters */ + params = (const char **) palloc(sizeof(char *)); params[0] = NULL; + } /* Setup parser */ pgxml_parser_init(); @@ -139,22 +140,34 @@ xslt_process(PG_FUNCTION_ARGS) #ifdef USE_LIBXSLT -static void -parse_params(const char **params, text *paramstr) +static const char ** +parse_params(text *paramstr) { char *pos; char *pstr; - int i; char *nvsep = "="; char *itsep = ","; + const char **params; + int max_params; + int nparams; pstr = text_to_cstring(paramstr); + max_params = 20; /* must be even! */ + params = (const char **) palloc((max_params + 1) * sizeof(char *)); + nparams = 0; + pos = pstr; - for (i = 0; i < MAXPARAMS; i++) + while (*pos != '\0') { - params[i] = pos; + if (nparams >= max_params) + { + max_params *= 2; + params = (const char **) repalloc(params, + (max_params + 1) * sizeof(char *)); + } + params[nparams++] = pos; pos = strstr(pos, nvsep); if (pos != NULL) { @@ -164,13 +177,12 @@ parse_params(const char **params, text *paramstr) else { /* No equal sign, so ignore this "parameter" */ - /* We'll reset params[i] to NULL below the loop */ + nparams--; break; } - /* Value */ - i++; - /* since MAXPARAMS is even, we still have i < MAXPARAMS */ - params[i] = pos; + + /* since max_params is even, we still have nparams < max_params */ + params[nparams++] = pos; pos = strstr(pos, itsep); if (pos != NULL) { @@ -178,13 +190,13 @@ parse_params(const char **params, text *paramstr) pos++; } else - { - i++; break; - } } - params[i] = NULL; + /* Add the terminator marker; we left room for it in the palloc's */ + params[nparams] = NULL; + + return params; } #endif /* USE_LIBXSLT */ |