1010#include "fmgr.h"
1111#include "utils/builtins.h"
1212#include "utils/xml.h"
13+ #include "utils/array.h"
14+ #include "utils/memutils.h"
15+ #include "mb/pg_wchar.h"
1316
1417#ifdef USE_LIBXSLT
1518
3538extern PgXmlErrorContext * pgxml_parser_init (PgXmlStrictness strictness );
3639
3740/* local defs */
41+ static xmltype * xslt_process_internal (xmltype * doct , xmltype * ssheet , const char * * params );
3842static const char * * parse_params (text * paramstr );
3943#endif /* USE_LIBXSLT */
4044
45+ /*
46+ * FIXME: This cannot easily be exposed in xml.h.
47+ * Perhaps there should be an xml-internal.h?
48+ */
49+ xmlDocPtr xml_parse (text * data , XmlOptionType xmloption_arg ,
50+ bool preserve_whitespace , int encoding ,
51+ XmlOptionType * parsed_xmloptiontype , xmlNodePtr * parsed_nodes ,
52+ Node * escontext );
4153
4254PG_FUNCTION_INFO_V1 (xslt_process );
4355
@@ -48,9 +60,103 @@ xslt_process(PG_FUNCTION_ARGS)
4860
4961 text * doct = PG_GETARG_TEXT_PP (0 );
5062 text * ssheet = PG_GETARG_TEXT_PP (1 );
51- text * volatile result = NULL ;
52- text * paramstr ;
53- const char * * params ;
63+ const char * * params = NULL ;
64+ text * result ;
65+
66+ if (fcinfo -> nargs == 3 )
67+ {
68+ text * paramstr = PG_GETARG_TEXT_PP (2 );
69+
70+ params = parse_params (paramstr );
71+ }
72+
73+ result = xslt_process_internal (doct , ssheet , params );
74+
75+ PG_RETURN_TEXT_P (result );
76+
77+ #else /* !USE_LIBXSLT */
78+
79+ ereport (ERROR ,
80+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
81+ errmsg ("xslt_process() is not available without libxslt" )));
82+ PG_RETURN_NULL ();
83+
84+ #endif /* USE_LIBXSLT */
85+ }
86+
87+ PG_FUNCTION_INFO_V1 (xslt_process_xmltype );
88+
89+ Datum
90+ xslt_process_xmltype (PG_FUNCTION_ARGS )
91+ {
92+ #ifdef USE_LIBXSLT
93+
94+ xmltype * doct = PG_GETARG_XML_P (0 );
95+ xmltype * ssheet = PG_GETARG_XML_P (1 );
96+ const char * * params = NULL ;
97+ xmltype * result ;
98+
99+ /*
100+ * Parameters are key-value pairs. The values are XPath expressions, so
101+ * strings will have to be escaped with single or double quotes. Even
102+ * `xsltproc --stringparam` does nothing else than adding single or double
103+ * quotes and fails if the value contains both.
104+ */
105+ if (fcinfo -> nargs == 3 )
106+ {
107+ ArrayType * paramarray = PG_GETARG_ARRAYTYPE_P (2 );
108+ Datum * arr_datums ;
109+ bool * arr_nulls ;
110+ int arr_count ;
111+ int i ,
112+ j ;
113+
114+ deconstruct_array_builtin (paramarray , TEXTOID , & arr_datums , & arr_nulls , & arr_count );
115+
116+ if ((arr_count % 2 ) != 0 )
117+ ereport (ERROR ,
118+ (errcode (ERRCODE_ARRAY_ELEMENT_ERROR ),
119+ errmsg ("number of stylesheet parameters (%d) must be a multiple of 2" ,
120+ arr_count )));
121+
122+ params = palloc_array (const char * , arr_count + 1 );
123+
124+ for (i = 0 , j = 0 ; i < arr_count ; i ++ )
125+ {
126+ char * cstr ;
127+
128+ if (arr_nulls [i ])
129+ continue ;
130+
131+ cstr = TextDatumGetCString (arr_datums [i ]);
132+ params [j ++ ] = (char * ) pg_do_encoding_conversion ((unsigned char * ) cstr ,
133+ strlen (cstr ),
134+ GetDatabaseEncoding (),
135+ PG_UTF8 );
136+ }
137+ params [j ] = NULL ;
138+ }
139+
140+ result = xslt_process_internal (doct , ssheet , params );
141+
142+ PG_RETURN_XML_P (result );
143+
144+ #else /* !USE_LIBXSLT */
145+
146+ ereport (ERROR ,
147+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
148+ errmsg ("xslt_process() is not available without libxslt" )));
149+ PG_RETURN_NULL ();
150+
151+ #endif /* USE_LIBXSLT */
152+ }
153+
154+ #ifdef USE_LIBXSLT
155+
156+ static xmltype *
157+ xslt_process_internal (xmltype * doct , xmltype * ssheet , const char * * params )
158+ {
159+ text * volatile result ;
54160 PgXmlErrorContext * xmlerrcxt ;
55161 volatile xsltStylesheetPtr stylesheet = NULL ;
56162 volatile xmlDocPtr doctree = NULL ;
@@ -60,40 +166,39 @@ xslt_process(PG_FUNCTION_ARGS)
60166 volatile int resstat = -1 ;
61167 xmlChar * volatile resstr = NULL ;
62168
63- if (fcinfo -> nargs == 3 )
64- {
65- paramstr = PG_GETARG_TEXT_PP (2 );
66- params = parse_params (paramstr );
67- }
68- else
69- {
70- /* No parameters */
71- params = (const char * * ) palloc (sizeof (char * ));
72- params [0 ] = NULL ;
73- }
169+ /* the previous libxslt error context */
170+ xmlGenericErrorFunc saved_errfunc ;
171+ void * saved_errcxt ;
74172
75173 /* Setup parser */
76- xmlerrcxt = pgxml_parser_init (PG_XML_STRICTNESS_LEGACY );
174+ xmlerrcxt = pgxml_parser_init (PG_XML_STRICTNESS_ALL );
175+
176+ /*
177+ * Save the previous libxslt error context.
178+ */
179+ saved_errfunc = xsltGenericError ;
180+ saved_errcxt = xsltGenericErrorContext ;
181+ xsltSetGenericErrorFunc (xmlerrcxt , xml_generic_error_handler );
77182
78183 PG_TRY ();
79184 {
80185 xmlDocPtr ssdoc ;
81186 bool xslt_sec_prefs_error ;
82187 int reslen = 0 ;
83188
84- /* Parse document */
85- doctree = xmlReadMemory ((char * ) VARDATA_ANY (doct ),
86- VARSIZE_ANY_EXHDR (doct ), NULL , NULL ,
87- XML_PARSE_NOENT );
189+ /*
190+ * Parse document.
191+ */
192+ doctree = xml_parse (doct , XMLOPTION_DOCUMENT , true,
193+ GetDatabaseEncoding (), NULL , NULL , NULL );
88194
89195 if (doctree == NULL || pg_xml_error_occurred (xmlerrcxt ))
90196 xml_ereport (xmlerrcxt , ERROR , ERRCODE_INVALID_XML_DOCUMENT ,
91197 "error parsing XML document" );
92198
93199 /* Same for stylesheet */
94- ssdoc = xmlReadMemory ((char * ) VARDATA_ANY (ssheet ),
95- VARSIZE_ANY_EXHDR (ssheet ), NULL , NULL ,
96- XML_PARSE_NOENT );
200+ ssdoc = xml_parse (ssheet , XMLOPTION_DOCUMENT , true,
201+ GetDatabaseEncoding (), NULL , NULL , NULL );
97202
98203 if (ssdoc == NULL || pg_xml_error_occurred (xmlerrcxt ))
99204 xml_ereport (xmlerrcxt , ERROR , ERRCODE_INVALID_XML_DOCUMENT ,
@@ -143,9 +248,10 @@ xslt_process(PG_FUNCTION_ARGS)
143248
144249 resstat = xsltSaveResultToString ((xmlChar * * ) & resstr , & reslen ,
145250 restree , stylesheet );
146-
147- if (resstat >= 0 )
148- result = cstring_to_text_with_len ((char * ) resstr , reslen );
251+ if (resstat < 0 || pg_xml_error_occurred (xmlerrcxt ))
252+ xml_ereport (xmlerrcxt , ERROR , ERRCODE_INVALID_ARGUMENT_FOR_XQUERY ,
253+ "failed to save result to string" );
254+ result = cstring_to_text_with_len ((char * ) resstr , reslen );
149255 }
150256 PG_CATCH ();
151257 {
@@ -163,6 +269,7 @@ xslt_process(PG_FUNCTION_ARGS)
163269 xmlFree (resstr );
164270 xsltCleanupGlobals ();
165271
272+ xsltSetGenericErrorFunc (saved_errcxt , saved_errfunc );
166273 pg_xml_done (xmlerrcxt , true);
167274
168275 PG_RE_THROW ();
@@ -179,24 +286,12 @@ xslt_process(PG_FUNCTION_ARGS)
179286 if (resstr )
180287 xmlFree (resstr );
181288
289+ xsltSetGenericErrorFunc (saved_errcxt , saved_errfunc );
182290 pg_xml_done (xmlerrcxt , false);
183291
184- /* XXX this is pretty dubious, really ought to throw error instead */
185- if (resstat < 0 )
186- PG_RETURN_NULL ();
187-
188- PG_RETURN_TEXT_P (result );
189- #else /* !USE_LIBXSLT */
190-
191- ereport (ERROR ,
192- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
193- errmsg ("xslt_process() is not available without libxslt" )));
194- PG_RETURN_NULL ();
195- #endif /* USE_LIBXSLT */
292+ return result ;
196293}
197294
198- #ifdef USE_LIBXSLT
199-
200295static const char * *
201296parse_params (text * paramstr )
202297{
0 commit comments