10
10
#include "fmgr.h"
11
11
#include "utils/builtins.h"
12
12
#include "utils/xml.h"
13
+ #include "utils/array.h"
14
+ #include "utils/memutils.h"
15
+ #include "mb/pg_wchar.h"
13
16
14
17
#ifdef USE_LIBXSLT
15
18
35
38
extern PgXmlErrorContext * pgxml_parser_init (PgXmlStrictness strictness );
36
39
37
40
/* local defs */
41
+ static xmltype * xslt_process_internal (xmltype * doct , xmltype * ssheet , const char * * params );
38
42
static const char * * parse_params (text * paramstr );
39
43
#endif /* USE_LIBXSLT */
40
44
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 );
41
53
42
54
PG_FUNCTION_INFO_V1 (xslt_process );
43
55
@@ -48,9 +60,103 @@ xslt_process(PG_FUNCTION_ARGS)
48
60
49
61
text * doct = PG_GETARG_TEXT_PP (0 );
50
62
text * ssheet = PG_GETARG_TEXT_PP (1 );
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
+ {
51
159
text * result ;
52
- text * paramstr ;
53
- const char * * params ;
54
160
PgXmlErrorContext * xmlerrcxt ;
55
161
volatile xsltStylesheetPtr stylesheet = NULL ;
56
162
volatile xmlDocPtr doctree = NULL ;
@@ -61,48 +167,48 @@ xslt_process(PG_FUNCTION_ARGS)
61
167
xmlChar * resstr = NULL ;
62
168
int reslen = 0 ;
63
169
64
- if (fcinfo -> nargs == 3 )
65
- {
66
- paramstr = PG_GETARG_TEXT_PP (2 );
67
- params = parse_params (paramstr );
68
- }
69
- else
70
- {
71
- /* No parameters */
72
- params = (const char * * ) palloc (sizeof (char * ));
73
- params [0 ] = NULL ;
74
- }
170
+ /* the previous libxslt error context */
171
+ xmlGenericErrorFunc saved_errfunc ;
172
+ void * saved_errcxt ;
75
173
76
174
/* Setup parser */
77
- xmlerrcxt = pgxml_parser_init (PG_XML_STRICTNESS_LEGACY );
175
+ xmlerrcxt = pgxml_parser_init (PG_XML_STRICTNESS_ALL );
176
+
177
+ /*
178
+ * Save the previous libxslt error context.
179
+ */
180
+ saved_errfunc = xsltGenericError ;
181
+ saved_errcxt = xsltGenericErrorContext ;
182
+ xsltSetGenericErrorFunc (xmlerrcxt , xml_generic_error_handler );
78
183
79
184
PG_TRY ();
80
185
{
81
186
xmlDocPtr ssdoc ;
82
187
bool xslt_sec_prefs_error ;
83
188
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. It's important to set an "URL", so libxslt includes
191
+ * line numbers in error messages (cf. xsltPrintErrorContext()).
192
+ */
193
+ doctree = xml_parse (doct , XMLOPTION_DOCUMENT , true,
194
+ GetDatabaseEncoding (), NULL , NULL , NULL );
88
195
89
- if (doctree == NULL )
196
+ if (doctree == NULL || pg_xml_error_occurred ( xmlerrcxt ) )
90
197
xml_ereport (xmlerrcxt , ERROR , ERRCODE_INVALID_XML_DOCUMENT ,
91
198
"error parsing XML document" );
92
199
93
200
/* Same for stylesheet */
94
- ssdoc = xmlReadMemory ((char * ) VARDATA_ANY (ssheet ),
95
- VARSIZE_ANY_EXHDR (ssheet ), NULL , NULL ,
96
- XML_PARSE_NOENT );
201
+ ssdoc = xml_parse (ssheet , XMLOPTION_DOCUMENT , true,
202
+ GetDatabaseEncoding (), NULL , NULL , NULL );
97
203
98
- if (ssdoc == NULL )
204
+ if (ssdoc == NULL || pg_xml_error_occurred ( xmlerrcxt ) )
99
205
xml_ereport (xmlerrcxt , ERROR , ERRCODE_INVALID_XML_DOCUMENT ,
100
206
"error parsing stylesheet as XML document" );
101
207
102
208
/* After this call we need not free ssdoc separately */
103
209
stylesheet = xsltParseStylesheetDoc (ssdoc );
104
210
105
- if (stylesheet == NULL )
211
+ if (stylesheet == NULL || pg_xml_error_occurred ( xmlerrcxt ) )
106
212
xml_ereport (xmlerrcxt , ERROR , ERRCODE_INVALID_ARGUMENT_FOR_XQUERY ,
107
213
"failed to parse stylesheet" );
108
214
@@ -137,11 +243,18 @@ xslt_process(PG_FUNCTION_ARGS)
137
243
restree = xsltApplyStylesheetUser (stylesheet , doctree , params ,
138
244
NULL , NULL , xslt_ctxt );
139
245
140
- if (restree == NULL )
246
+ if (restree == NULL || pg_xml_error_occurred ( xmlerrcxt ) )
141
247
xml_ereport (xmlerrcxt , ERROR , ERRCODE_INVALID_ARGUMENT_FOR_XQUERY ,
142
248
"failed to apply stylesheet" );
143
249
250
+ /*
251
+ * FIXME: There is probably a function to convert the string to the
252
+ * correct database encoding.
253
+ */
144
254
resstat = xsltSaveResultToString (& resstr , & reslen , restree , stylesheet );
255
+ if (resstat < 0 || pg_xml_error_occurred (xmlerrcxt ))
256
+ xml_ereport (xmlerrcxt , ERROR , ERRCODE_INVALID_ARGUMENT_FOR_XQUERY ,
257
+ "failed to save result to string" );
145
258
}
146
259
PG_CATCH ();
147
260
{
@@ -157,6 +270,7 @@ xslt_process(PG_FUNCTION_ARGS)
157
270
xmlFreeDoc (doctree );
158
271
xsltCleanupGlobals ();
159
272
273
+ xsltSetGenericErrorFunc (saved_errcxt , saved_errfunc );
160
274
pg_xml_done (xmlerrcxt , true);
161
275
162
276
PG_RE_THROW ();
@@ -170,29 +284,17 @@ xslt_process(PG_FUNCTION_ARGS)
170
284
xmlFreeDoc (doctree );
171
285
xsltCleanupGlobals ();
172
286
287
+ xsltSetGenericErrorFunc (saved_errcxt , saved_errfunc );
173
288
pg_xml_done (xmlerrcxt , false);
174
289
175
- /* XXX this is pretty dubious, really ought to throw error instead */
176
- if (resstat < 0 )
177
- PG_RETURN_NULL ();
178
-
179
290
result = cstring_to_text_with_len ((char * ) resstr , reslen );
180
291
181
292
if (resstr )
182
293
xmlFree (resstr );
183
294
184
- PG_RETURN_TEXT_P (result );
185
- #else /* !USE_LIBXSLT */
186
-
187
- ereport (ERROR ,
188
- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
189
- errmsg ("xslt_process() is not available without libxslt" )));
190
- PG_RETURN_NULL ();
191
- #endif /* USE_LIBXSLT */
295
+ return result ;
192
296
}
193
297
194
- #ifdef USE_LIBXSLT
195
-
196
298
static const char * *
197
299
parse_params (text * paramstr )
198
300
{
0 commit comments