Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * xml.c
4 : * XML data type support.
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * src/backend/utils/adt/xml.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 :
15 : /*
16 : * Generally, XML type support is only available when libxml use was
17 : * configured during the build. But even if that is not done, the
18 : * type and all the functions are available, but most of them will
19 : * fail. For one thing, this avoids having to manage variant catalog
20 : * installations. But it also has nice effects such as that you can
21 : * dump a database containing XML type data even if the server is not
22 : * linked with libxml. Thus, make sure xml_out() works even if nothing
23 : * else does.
24 : */
25 :
26 : /*
27 : * Notes on memory management:
28 : *
29 : * Sometimes libxml allocates global structures in the hope that it can reuse
30 : * them later on. This makes it impractical to change the xmlMemSetup
31 : * functions on-the-fly; that is likely to lead to trying to pfree() chunks
32 : * allocated with malloc() or vice versa. Since libxml might be used by
33 : * loadable modules, eg libperl, our only safe choices are to change the
34 : * functions at postmaster/backend launch or not at all. Since we'd rather
35 : * not activate libxml in sessions that might never use it, the latter choice
36 : * is the preferred one. However, for debugging purposes it can be awfully
37 : * handy to constrain libxml's allocations to be done in a specific palloc
38 : * context, where they're easy to track. Therefore there is code here that
39 : * can be enabled in debug builds to redirect libxml's allocations into a
40 : * special context LibxmlContext. It's not recommended to turn this on in
41 : * a production build because of the possibility of bad interactions with
42 : * external modules.
43 : */
44 : /* #define USE_LIBXMLCONTEXT */
45 :
46 : #include "postgres.h"
47 :
48 : #ifdef USE_LIBXML
49 : #include <libxml/chvalid.h>
50 : #include <libxml/entities.h>
51 : #include <libxml/parser.h>
52 : #include <libxml/parserInternals.h>
53 : #include <libxml/tree.h>
54 : #include <libxml/uri.h>
55 : #include <libxml/xmlerror.h>
56 : #include <libxml/xmlsave.h>
57 : #include <libxml/xmlversion.h>
58 : #include <libxml/xmlwriter.h>
59 : #include <libxml/xpath.h>
60 : #include <libxml/xpathInternals.h>
61 :
62 : /*
63 : * We used to check for xmlStructuredErrorContext via a configure test; but
64 : * that doesn't work on Windows, so instead use this grottier method of
65 : * testing the library version number.
66 : */
67 : #if LIBXML_VERSION >= 20704
68 : #define HAVE_XMLSTRUCTUREDERRORCONTEXT 1
69 : #endif
70 : #endif /* USE_LIBXML */
71 :
72 : #include "access/htup_details.h"
73 : #include "access/table.h"
74 : #include "catalog/namespace.h"
75 : #include "catalog/pg_class.h"
76 : #include "catalog/pg_type.h"
77 : #include "commands/dbcommands.h"
78 : #include "executor/spi.h"
79 : #include "executor/tablefunc.h"
80 : #include "fmgr.h"
81 : #include "lib/stringinfo.h"
82 : #include "libpq/pqformat.h"
83 : #include "mb/pg_wchar.h"
84 : #include "miscadmin.h"
85 : #include "nodes/execnodes.h"
86 : #include "nodes/miscnodes.h"
87 : #include "nodes/nodeFuncs.h"
88 : #include "utils/array.h"
89 : #include "utils/builtins.h"
90 : #include "utils/date.h"
91 : #include "utils/datetime.h"
92 : #include "utils/lsyscache.h"
93 : #include "utils/memutils.h"
94 : #include "utils/rel.h"
95 : #include "utils/syscache.h"
96 : #include "utils/xml.h"
97 :
98 :
99 : /* GUC variables */
100 : int xmlbinary = XMLBINARY_BASE64;
101 : int xmloption = XMLOPTION_CONTENT;
102 :
103 : #ifdef USE_LIBXML
104 :
105 : /* random number to identify PgXmlErrorContext */
106 : #define ERRCXT_MAGIC 68275028
107 :
108 : struct PgXmlErrorContext
109 : {
110 : int magic;
111 : /* strictness argument passed to pg_xml_init */
112 : PgXmlStrictness strictness;
113 : /* current error status and accumulated message, if any */
114 : bool err_occurred;
115 : StringInfoData err_buf;
116 : /* previous libxml error handling state (saved by pg_xml_init) */
117 : xmlStructuredErrorFunc saved_errfunc;
118 : void *saved_errcxt;
119 : /* previous libxml entity handler (saved by pg_xml_init) */
120 : xmlExternalEntityLoader saved_entityfunc;
121 : };
122 :
123 : static xmlParserInputPtr xmlPgEntityLoader(const char *URL, const char *ID,
124 : xmlParserCtxtPtr ctxt);
125 : static void xml_errsave(Node *escontext, PgXmlErrorContext *errcxt,
126 : int sqlcode, const char *msg);
127 : static void xml_errorHandler(void *data, xmlErrorPtr error);
128 : static int errdetail_for_xml_code(int code);
129 : static void chopStringInfoNewlines(StringInfo str);
130 : static void appendStringInfoLineSeparator(StringInfo str);
131 :
132 : #ifdef USE_LIBXMLCONTEXT
133 :
134 : static MemoryContext LibxmlContext = NULL;
135 :
136 : static void xml_memory_init(void);
137 : static void *xml_palloc(size_t size);
138 : static void *xml_repalloc(void *ptr, size_t size);
139 : static void xml_pfree(void *ptr);
140 : static char *xml_pstrdup(const char *string);
141 : #endif /* USE_LIBXMLCONTEXT */
142 :
143 : static xmlChar *xml_text2xmlChar(text *in);
144 : static int parse_xml_decl(const xmlChar *str, size_t *lenp,
145 : xmlChar **version, xmlChar **encoding, int *standalone);
146 : static bool print_xml_decl(StringInfo buf, const xmlChar *version,
147 : pg_enc encoding, int standalone);
148 : static bool xml_doctype_in_content(const xmlChar *str);
149 : static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
150 : bool preserve_whitespace, int encoding,
151 : XmlOptionType *parsed_xmloptiontype,
152 : xmlNodePtr *parsed_nodes,
153 : Node *escontext);
154 : static text *xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt);
155 : static int xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
156 : ArrayBuildState *astate,
157 : PgXmlErrorContext *xmlerrcxt);
158 : static xmlChar *pg_xmlCharStrndup(const char *str, size_t len);
159 : #endif /* USE_LIBXML */
160 :
161 : static void xmldata_root_element_start(StringInfo result, const char *eltname,
162 : const char *xmlschema, const char *targetns,
163 : bool top_level);
164 : static void xmldata_root_element_end(StringInfo result, const char *eltname);
165 : static StringInfo query_to_xml_internal(const char *query, char *tablename,
166 : const char *xmlschema, bool nulls, bool tableforest,
167 : const char *targetns, bool top_level);
168 : static const char *map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid,
169 : bool nulls, bool tableforest, const char *targetns);
170 : static const char *map_sql_schema_to_xmlschema_types(Oid nspid,
171 : List *relid_list, bool nulls,
172 : bool tableforest, const char *targetns);
173 : static const char *map_sql_catalog_to_xmlschema_types(List *nspid_list,
174 : bool nulls, bool tableforest,
175 : const char *targetns);
176 : static const char *map_sql_type_to_xml_name(Oid typeoid, int typmod);
177 : static const char *map_sql_typecoll_to_xmlschema_types(List *tupdesc_list);
178 : static const char *map_sql_type_to_xmlschema_type(Oid typeoid, int typmod);
179 : static void SPI_sql_row_to_xmlelement(uint64 rownum, StringInfo result,
180 : char *tablename, bool nulls, bool tableforest,
181 : const char *targetns, bool top_level);
182 :
183 : /* XMLTABLE support */
184 : #ifdef USE_LIBXML
185 : /* random number to identify XmlTableContext */
186 : #define XMLTABLE_CONTEXT_MAGIC 46922182
187 : typedef struct XmlTableBuilderData
188 : {
189 : int magic;
190 : int natts;
191 : long int row_count;
192 : PgXmlErrorContext *xmlerrcxt;
193 : xmlParserCtxtPtr ctxt;
194 : xmlDocPtr doc;
195 : xmlXPathContextPtr xpathcxt;
196 : xmlXPathCompExprPtr xpathcomp;
197 : xmlXPathObjectPtr xpathobj;
198 : xmlXPathCompExprPtr *xpathscomp;
199 : } XmlTableBuilderData;
200 : #endif
201 :
202 : static void XmlTableInitOpaque(struct TableFuncScanState *state, int natts);
203 : static void XmlTableSetDocument(struct TableFuncScanState *state, Datum value);
204 : static void XmlTableSetNamespace(struct TableFuncScanState *state, const char *name,
205 : const char *uri);
206 : static void XmlTableSetRowFilter(struct TableFuncScanState *state, const char *path);
207 : static void XmlTableSetColumnFilter(struct TableFuncScanState *state,
208 : const char *path, int colnum);
209 : static bool XmlTableFetchRow(struct TableFuncScanState *state);
210 : static Datum XmlTableGetValue(struct TableFuncScanState *state, int colnum,
211 : Oid typid, int32 typmod, bool *isnull);
212 : static void XmlTableDestroyOpaque(struct TableFuncScanState *state);
213 :
214 : const TableFuncRoutine XmlTableRoutine =
215 : {
216 : XmlTableInitOpaque,
217 : XmlTableSetDocument,
218 : XmlTableSetNamespace,
219 : XmlTableSetRowFilter,
220 : XmlTableSetColumnFilter,
221 : XmlTableFetchRow,
222 : XmlTableGetValue,
223 : XmlTableDestroyOpaque
224 : };
225 :
226 : #define NO_XML_SUPPORT() \
227 : ereport(ERROR, \
228 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
229 : errmsg("unsupported XML feature"), \
230 : errdetail("This functionality requires the server to be built with libxml support.")))
231 :
232 :
233 : /* from SQL/XML:2008 section 4.9 */
234 : #define NAMESPACE_XSD "http://www.w3.org/2001/XMLSchema"
235 : #define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance"
236 : #define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml"
237 :
238 :
239 : #ifdef USE_LIBXML
240 :
241 : static int
242 0 : xmlChar_to_encoding(const xmlChar *encoding_name)
243 : {
244 0 : int encoding = pg_char_to_encoding((const char *) encoding_name);
245 :
246 0 : if (encoding < 0)
247 0 : ereport(ERROR,
248 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
249 : errmsg("invalid encoding name \"%s\"",
250 : (const char *) encoding_name)));
251 0 : return encoding;
252 : }
253 : #endif
254 :
255 :
256 : /*
257 : * xml_in uses a plain C string to VARDATA conversion, so for the time being
258 : * we use the conversion function for the text datatype.
259 : *
260 : * This is only acceptable so long as xmltype and text use the same
261 : * representation.
262 : */
263 : Datum
264 828 : xml_in(PG_FUNCTION_ARGS)
265 : {
266 : #ifdef USE_LIBXML
267 828 : char *s = PG_GETARG_CSTRING(0);
268 : xmltype *vardata;
269 : xmlDocPtr doc;
270 :
271 : /* Build the result object. */
272 828 : vardata = (xmltype *) cstring_to_text(s);
273 :
274 : /*
275 : * Parse the data to check if it is well-formed XML data.
276 : *
277 : * Note: we don't need to worry about whether a soft error is detected.
278 : */
279 828 : doc = xml_parse(vardata, xmloption, true, GetDatabaseEncoding(),
280 828 : NULL, NULL, fcinfo->context);
281 780 : if (doc != NULL)
282 768 : xmlFreeDoc(doc);
283 :
284 780 : PG_RETURN_XML_P(vardata);
285 : #else
286 : NO_XML_SUPPORT();
287 : return 0;
288 : #endif
289 : }
290 :
291 :
292 : #define PG_XML_DEFAULT_VERSION "1.0"
293 :
294 :
295 : /*
296 : * xml_out_internal uses a plain VARDATA to C string conversion, so for the
297 : * time being we use the conversion function for the text datatype.
298 : *
299 : * This is only acceptable so long as xmltype and text use the same
300 : * representation.
301 : */
302 : static char *
303 22862 : xml_out_internal(xmltype *x, pg_enc target_encoding)
304 : {
305 22862 : char *str = text_to_cstring((text *) x);
306 :
307 : #ifdef USE_LIBXML
308 22862 : size_t len = strlen(str);
309 : xmlChar *version;
310 : int standalone;
311 : int res_code;
312 :
313 22862 : if ((res_code = parse_xml_decl((xmlChar *) str,
314 : &len, &version, NULL, &standalone)) == 0)
315 : {
316 : StringInfoData buf;
317 :
318 22862 : initStringInfo(&buf);
319 :
320 22862 : if (!print_xml_decl(&buf, version, target_encoding, standalone))
321 : {
322 : /*
323 : * If we are not going to produce an XML declaration, eat a single
324 : * newline in the original string to prevent empty first lines in
325 : * the output.
326 : */
327 22814 : if (*(str + len) == '\n')
328 6 : len += 1;
329 : }
330 22862 : appendStringInfoString(&buf, str + len);
331 :
332 22862 : pfree(str);
333 :
334 22862 : return buf.data;
335 : }
336 :
337 0 : ereport(WARNING,
338 : errcode(ERRCODE_INTERNAL_ERROR),
339 : errmsg_internal("could not parse XML declaration in stored value"),
340 : errdetail_for_xml_code(res_code));
341 : #endif
342 0 : return str;
343 : }
344 :
345 :
346 : Datum
347 22598 : xml_out(PG_FUNCTION_ARGS)
348 : {
349 22598 : xmltype *x = PG_GETARG_XML_P(0);
350 :
351 : /*
352 : * xml_out removes the encoding property in all cases. This is because we
353 : * cannot control from here whether the datum will be converted to a
354 : * different client encoding, so we'd do more harm than good by including
355 : * it.
356 : */
357 22598 : PG_RETURN_CSTRING(xml_out_internal(x, 0));
358 : }
359 :
360 :
361 : Datum
362 0 : xml_recv(PG_FUNCTION_ARGS)
363 : {
364 : #ifdef USE_LIBXML
365 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
366 : xmltype *result;
367 : char *str;
368 : char *newstr;
369 : int nbytes;
370 : xmlDocPtr doc;
371 0 : xmlChar *encodingStr = NULL;
372 : int encoding;
373 :
374 : /*
375 : * Read the data in raw format. We don't know yet what the encoding is, as
376 : * that information is embedded in the xml declaration; so we have to
377 : * parse that before converting to server encoding.
378 : */
379 0 : nbytes = buf->len - buf->cursor;
380 0 : str = (char *) pq_getmsgbytes(buf, nbytes);
381 :
382 : /*
383 : * We need a null-terminated string to pass to parse_xml_decl(). Rather
384 : * than make a separate copy, make the temporary result one byte bigger
385 : * than it needs to be.
386 : */
387 0 : result = palloc(nbytes + 1 + VARHDRSZ);
388 0 : SET_VARSIZE(result, nbytes + VARHDRSZ);
389 0 : memcpy(VARDATA(result), str, nbytes);
390 0 : str = VARDATA(result);
391 0 : str[nbytes] = '\0';
392 :
393 0 : parse_xml_decl((const xmlChar *) str, NULL, NULL, &encodingStr, NULL);
394 :
395 : /*
396 : * If encoding wasn't explicitly specified in the XML header, treat it as
397 : * UTF-8, as that's the default in XML. This is different from xml_in(),
398 : * where the input has to go through the normal client to server encoding
399 : * conversion.
400 : */
401 0 : encoding = encodingStr ? xmlChar_to_encoding(encodingStr) : PG_UTF8;
402 :
403 : /*
404 : * Parse the data to check if it is well-formed XML data. Assume that
405 : * xml_parse will throw ERROR if not.
406 : */
407 0 : doc = xml_parse(result, xmloption, true, encoding, NULL, NULL, NULL);
408 0 : xmlFreeDoc(doc);
409 :
410 : /* Now that we know what we're dealing with, convert to server encoding */
411 0 : newstr = pg_any_to_server(str, nbytes, encoding);
412 :
413 0 : if (newstr != str)
414 : {
415 0 : pfree(result);
416 0 : result = (xmltype *) cstring_to_text(newstr);
417 0 : pfree(newstr);
418 : }
419 :
420 0 : PG_RETURN_XML_P(result);
421 : #else
422 : NO_XML_SUPPORT();
423 : return 0;
424 : #endif
425 : }
426 :
427 :
428 : Datum
429 0 : xml_send(PG_FUNCTION_ARGS)
430 : {
431 0 : xmltype *x = PG_GETARG_XML_P(0);
432 : char *outval;
433 : StringInfoData buf;
434 :
435 : /*
436 : * xml_out_internal doesn't convert the encoding, it just prints the right
437 : * declaration. pq_sendtext will do the conversion.
438 : */
439 0 : outval = xml_out_internal(x, pg_get_client_encoding());
440 :
441 0 : pq_begintypsend(&buf);
442 0 : pq_sendtext(&buf, outval, strlen(outval));
443 0 : pfree(outval);
444 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
445 : }
446 :
447 :
448 : #ifdef USE_LIBXML
449 : static void
450 132 : appendStringInfoText(StringInfo str, const text *t)
451 : {
452 132 : appendBinaryStringInfo(str, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
453 132 : }
454 : #endif
455 :
456 :
457 : static xmltype *
458 21842 : stringinfo_to_xmltype(StringInfo buf)
459 : {
460 21842 : return (xmltype *) cstring_to_text_with_len(buf->data, buf->len);
461 : }
462 :
463 :
464 : static xmltype *
465 78 : cstring_to_xmltype(const char *string)
466 : {
467 78 : return (xmltype *) cstring_to_text(string);
468 : }
469 :
470 :
471 : #ifdef USE_LIBXML
472 : static xmltype *
473 21964 : xmlBuffer_to_xmltype(xmlBufferPtr buf)
474 : {
475 21964 : return (xmltype *) cstring_to_text_with_len((const char *) xmlBufferContent(buf),
476 : xmlBufferLength(buf));
477 : }
478 : #endif
479 :
480 :
481 : Datum
482 42 : xmlcomment(PG_FUNCTION_ARGS)
483 : {
484 : #ifdef USE_LIBXML
485 42 : text *arg = PG_GETARG_TEXT_PP(0);
486 42 : char *argdata = VARDATA_ANY(arg);
487 42 : int len = VARSIZE_ANY_EXHDR(arg);
488 : StringInfoData buf;
489 : int i;
490 :
491 : /* check for "--" in string or "-" at the end */
492 180 : for (i = 1; i < len; i++)
493 : {
494 144 : if (argdata[i] == '-' && argdata[i - 1] == '-')
495 6 : ereport(ERROR,
496 : (errcode(ERRCODE_INVALID_XML_COMMENT),
497 : errmsg("invalid XML comment")));
498 : }
499 36 : if (len > 0 && argdata[len - 1] == '-')
500 6 : ereport(ERROR,
501 : (errcode(ERRCODE_INVALID_XML_COMMENT),
502 : errmsg("invalid XML comment")));
503 :
504 30 : initStringInfo(&buf);
505 30 : appendStringInfoString(&buf, "<!--");
506 30 : appendStringInfoText(&buf, arg);
507 30 : appendStringInfoString(&buf, "-->");
508 :
509 30 : PG_RETURN_XML_P(stringinfo_to_xmltype(&buf));
510 : #else
511 : NO_XML_SUPPORT();
512 : return 0;
513 : #endif
514 : }
515 :
516 :
517 : Datum
518 30 : xmltext(PG_FUNCTION_ARGS)
519 : {
520 : #ifdef USE_LIBXML
521 30 : text *arg = PG_GETARG_TEXT_PP(0);
522 : text *result;
523 30 : xmlChar *xmlbuf = NULL;
524 :
525 30 : xmlbuf = xmlEncodeSpecialChars(NULL, xml_text2xmlChar(arg));
526 :
527 : Assert(xmlbuf);
528 :
529 30 : result = cstring_to_text_with_len((const char *) xmlbuf, xmlStrlen(xmlbuf));
530 30 : xmlFree(xmlbuf);
531 30 : PG_RETURN_XML_P(result);
532 : #else
533 : NO_XML_SUPPORT();
534 : return 0;
535 : #endif /* not USE_LIBXML */
536 : }
537 :
538 :
539 : /*
540 : * TODO: xmlconcat needs to merge the notations and unparsed entities
541 : * of the argument values. Not very important in practice, though.
542 : */
543 : xmltype *
544 21592 : xmlconcat(List *args)
545 : {
546 : #ifdef USE_LIBXML
547 21592 : int global_standalone = 1;
548 21592 : xmlChar *global_version = NULL;
549 21592 : bool global_version_no_value = false;
550 : StringInfoData buf;
551 : ListCell *v;
552 :
553 21592 : initStringInfo(&buf);
554 64782 : foreach(v, args)
555 : {
556 43190 : xmltype *x = DatumGetXmlP(PointerGetDatum(lfirst(v)));
557 : size_t len;
558 : xmlChar *version;
559 : int standalone;
560 : char *str;
561 :
562 43190 : len = VARSIZE(x) - VARHDRSZ;
563 43190 : str = text_to_cstring((text *) x);
564 :
565 43190 : parse_xml_decl((xmlChar *) str, &len, &version, NULL, &standalone);
566 :
567 43190 : if (standalone == 0 && global_standalone == 1)
568 0 : global_standalone = 0;
569 43190 : if (standalone < 0)
570 43178 : global_standalone = -1;
571 :
572 43190 : if (!version)
573 43172 : global_version_no_value = true;
574 18 : else if (!global_version)
575 12 : global_version = version;
576 6 : else if (xmlStrcmp(version, global_version) != 0)
577 0 : global_version_no_value = true;
578 :
579 43190 : appendStringInfoString(&buf, str + len);
580 43190 : pfree(str);
581 : }
582 :
583 21592 : if (!global_version_no_value || global_standalone >= 0)
584 : {
585 : StringInfoData buf2;
586 :
587 6 : initStringInfo(&buf2);
588 :
589 6 : print_xml_decl(&buf2,
590 6 : (!global_version_no_value) ? global_version : NULL,
591 : 0,
592 : global_standalone);
593 :
594 6 : appendBinaryStringInfo(&buf2, buf.data, buf.len);
595 6 : buf = buf2;
596 : }
597 :
598 21592 : return stringinfo_to_xmltype(&buf);
599 : #else
600 : NO_XML_SUPPORT();
601 : return NULL;
602 : #endif
603 : }
604 :
605 :
606 : /*
607 : * XMLAGG support
608 : */
609 : Datum
610 21568 : xmlconcat2(PG_FUNCTION_ARGS)
611 : {
612 21568 : if (PG_ARGISNULL(0))
613 : {
614 18 : if (PG_ARGISNULL(1))
615 0 : PG_RETURN_NULL();
616 : else
617 18 : PG_RETURN_XML_P(PG_GETARG_XML_P(1));
618 : }
619 21550 : else if (PG_ARGISNULL(1))
620 0 : PG_RETURN_XML_P(PG_GETARG_XML_P(0));
621 : else
622 21550 : PG_RETURN_XML_P(xmlconcat(list_make2(PG_GETARG_XML_P(0),
623 : PG_GETARG_XML_P(1))));
624 : }
625 :
626 :
627 : Datum
628 2 : texttoxml(PG_FUNCTION_ARGS)
629 : {
630 2 : text *data = PG_GETARG_TEXT_PP(0);
631 :
632 2 : PG_RETURN_XML_P(xmlparse(data, xmloption, true));
633 : }
634 :
635 :
636 : Datum
637 0 : xmltotext(PG_FUNCTION_ARGS)
638 : {
639 0 : xmltype *data = PG_GETARG_XML_P(0);
640 :
641 : /* It's actually binary compatible. */
642 0 : PG_RETURN_TEXT_P((text *) data);
643 : }
644 :
645 :
646 : text *
647 168 : xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
648 : {
649 : #ifdef USE_LIBXML
650 : text *volatile result;
651 : xmlDocPtr doc;
652 : XmlOptionType parsed_xmloptiontype;
653 : xmlNodePtr content_nodes;
654 168 : volatile xmlBufferPtr buf = NULL;
655 168 : volatile xmlSaveCtxtPtr ctxt = NULL;
656 168 : ErrorSaveContext escontext = {T_ErrorSaveContext};
657 : PgXmlErrorContext *xmlerrcxt;
658 : #endif
659 :
660 168 : if (xmloption_arg != XMLOPTION_DOCUMENT && !indent)
661 : {
662 : /*
663 : * We don't actually need to do anything, so just return the
664 : * binary-compatible input. For backwards-compatibility reasons,
665 : * allow such cases to succeed even without USE_LIBXML.
666 : */
667 36 : return (text *) data;
668 : }
669 :
670 : #ifdef USE_LIBXML
671 : /* Parse the input according to the xmloption */
672 132 : doc = xml_parse(data, xmloption_arg, true, GetDatabaseEncoding(),
673 : &parsed_xmloptiontype, &content_nodes,
674 : (Node *) &escontext);
675 132 : if (doc == NULL || escontext.error_occurred)
676 : {
677 30 : if (doc)
678 0 : xmlFreeDoc(doc);
679 : /* A soft error must be failure to conform to XMLOPTION_DOCUMENT */
680 30 : ereport(ERROR,
681 : (errcode(ERRCODE_NOT_AN_XML_DOCUMENT),
682 : errmsg("not an XML document")));
683 : }
684 :
685 : /* If we weren't asked to indent, we're done. */
686 102 : if (!indent)
687 : {
688 18 : xmlFreeDoc(doc);
689 18 : return (text *) data;
690 : }
691 :
692 : /* Otherwise, we gotta spin up some error handling. */
693 84 : xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
694 :
695 84 : PG_TRY();
696 : {
697 84 : size_t decl_len = 0;
698 :
699 : /* The serialized data will go into this buffer. */
700 84 : buf = xmlBufferCreate();
701 :
702 84 : if (buf == NULL || xmlerrcxt->err_occurred)
703 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
704 : "could not allocate xmlBuffer");
705 :
706 : /* Detect whether there's an XML declaration */
707 84 : parse_xml_decl(xml_text2xmlChar(data), &decl_len, NULL, NULL, NULL);
708 :
709 : /*
710 : * Emit declaration only if the input had one. Note: some versions of
711 : * xmlSaveToBuffer leak memory if a non-null encoding argument is
712 : * passed, so don't do that. We don't want any encoding conversion
713 : * anyway.
714 : */
715 84 : if (decl_len == 0)
716 72 : ctxt = xmlSaveToBuffer(buf, NULL,
717 : XML_SAVE_NO_DECL | XML_SAVE_FORMAT);
718 : else
719 12 : ctxt = xmlSaveToBuffer(buf, NULL,
720 : XML_SAVE_FORMAT);
721 :
722 84 : if (ctxt == NULL || xmlerrcxt->err_occurred)
723 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
724 : "could not allocate xmlSaveCtxt");
725 :
726 84 : if (parsed_xmloptiontype == XMLOPTION_DOCUMENT)
727 : {
728 : /* If it's a document, saving is easy. */
729 36 : if (xmlSaveDoc(ctxt, doc) == -1 || xmlerrcxt->err_occurred)
730 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
731 : "could not save document to xmlBuffer");
732 : }
733 48 : else if (content_nodes != NULL)
734 : {
735 : /*
736 : * Deal with the case where we have non-singly-rooted XML.
737 : * libxml's dump functions don't work well for that without help.
738 : * We build a fake root node that serves as a container for the
739 : * content nodes, and then iterate over the nodes.
740 : */
741 : xmlNodePtr root;
742 : xmlNodePtr newline;
743 :
744 42 : root = xmlNewNode(NULL, (const xmlChar *) "content-root");
745 42 : if (root == NULL || xmlerrcxt->err_occurred)
746 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
747 : "could not allocate xml node");
748 :
749 : /* This attaches root to doc, so we need not free it separately. */
750 42 : xmlDocSetRootElement(doc, root);
751 42 : xmlAddChild(root, content_nodes);
752 :
753 : /*
754 : * We use this node to insert newlines in the dump. Note: in at
755 : * least some libxml versions, xmlNewDocText would not attach the
756 : * node to the document even if we passed it. Therefore, manage
757 : * freeing of this node manually, and pass NULL here to make sure
758 : * there's not a dangling link.
759 : */
760 42 : newline = xmlNewDocText(NULL, (const xmlChar *) "\n");
761 42 : if (newline == NULL || xmlerrcxt->err_occurred)
762 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
763 : "could not allocate xml node");
764 :
765 108 : for (xmlNodePtr node = root->children; node; node = node->next)
766 : {
767 : /* insert newlines between nodes */
768 66 : if (node->type != XML_TEXT_NODE && node->prev != NULL)
769 : {
770 18 : if (xmlSaveTree(ctxt, newline) == -1 || xmlerrcxt->err_occurred)
771 : {
772 0 : xmlFreeNode(newline);
773 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
774 : "could not save newline to xmlBuffer");
775 : }
776 : }
777 :
778 66 : if (xmlSaveTree(ctxt, node) == -1 || xmlerrcxt->err_occurred)
779 : {
780 0 : xmlFreeNode(newline);
781 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
782 : "could not save content to xmlBuffer");
783 : }
784 : }
785 :
786 42 : xmlFreeNode(newline);
787 : }
788 :
789 84 : if (xmlSaveClose(ctxt) == -1 || xmlerrcxt->err_occurred)
790 : {
791 0 : ctxt = NULL; /* don't try to close it again */
792 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
793 : "could not close xmlSaveCtxtPtr");
794 : }
795 :
796 84 : result = (text *) xmlBuffer_to_xmltype(buf);
797 : }
798 0 : PG_CATCH();
799 : {
800 0 : if (ctxt)
801 0 : xmlSaveClose(ctxt);
802 0 : if (buf)
803 0 : xmlBufferFree(buf);
804 0 : if (doc)
805 0 : xmlFreeDoc(doc);
806 :
807 0 : pg_xml_done(xmlerrcxt, true);
808 :
809 0 : PG_RE_THROW();
810 : }
811 84 : PG_END_TRY();
812 :
813 84 : xmlBufferFree(buf);
814 84 : xmlFreeDoc(doc);
815 :
816 84 : pg_xml_done(xmlerrcxt, false);
817 :
818 84 : return result;
819 : #else
820 : NO_XML_SUPPORT();
821 : return NULL;
822 : #endif
823 : }
824 :
825 :
826 : xmltype *
827 21724 : xmlelement(XmlExpr *xexpr,
828 : Datum *named_argvalue, bool *named_argnull,
829 : Datum *argvalue, bool *argnull)
830 : {
831 : #ifdef USE_LIBXML
832 : xmltype *result;
833 : List *named_arg_strings;
834 : List *arg_strings;
835 : int i;
836 : ListCell *arg;
837 : ListCell *narg;
838 : PgXmlErrorContext *xmlerrcxt;
839 21724 : volatile xmlBufferPtr buf = NULL;
840 21724 : volatile xmlTextWriterPtr writer = NULL;
841 :
842 : /*
843 : * All arguments are already evaluated, and their values are passed in the
844 : * named_argvalue/named_argnull or argvalue/argnull arrays. This avoids
845 : * issues if one of the arguments involves a call to some other function
846 : * or subsystem that wants to use libxml on its own terms. We examine the
847 : * original XmlExpr to identify the numbers and types of the arguments.
848 : */
849 21724 : named_arg_strings = NIL;
850 21724 : i = 0;
851 21772 : foreach(arg, xexpr->named_args)
852 : {
853 54 : Expr *e = (Expr *) lfirst(arg);
854 : char *str;
855 :
856 54 : if (named_argnull[i])
857 0 : str = NULL;
858 : else
859 54 : str = map_sql_value_to_xml_value(named_argvalue[i],
860 : exprType((Node *) e),
861 : false);
862 48 : named_arg_strings = lappend(named_arg_strings, str);
863 48 : i++;
864 : }
865 :
866 21718 : arg_strings = NIL;
867 21718 : i = 0;
868 43412 : foreach(arg, xexpr->args)
869 : {
870 21694 : Expr *e = (Expr *) lfirst(arg);
871 : char *str;
872 :
873 : /* here we can just forget NULL elements immediately */
874 21694 : if (!argnull[i])
875 : {
876 21694 : str = map_sql_value_to_xml_value(argvalue[i],
877 : exprType((Node *) e),
878 : true);
879 21694 : arg_strings = lappend(arg_strings, str);
880 : }
881 21694 : i++;
882 : }
883 :
884 21718 : xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
885 :
886 21718 : PG_TRY();
887 : {
888 21718 : buf = xmlBufferCreate();
889 21718 : if (buf == NULL || xmlerrcxt->err_occurred)
890 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
891 : "could not allocate xmlBuffer");
892 21718 : writer = xmlNewTextWriterMemory(buf, 0);
893 21718 : if (writer == NULL || xmlerrcxt->err_occurred)
894 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
895 : "could not allocate xmlTextWriter");
896 :
897 21718 : xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
898 :
899 21766 : forboth(arg, named_arg_strings, narg, xexpr->arg_names)
900 : {
901 48 : char *str = (char *) lfirst(arg);
902 48 : char *argname = strVal(lfirst(narg));
903 :
904 48 : if (str)
905 48 : xmlTextWriterWriteAttribute(writer,
906 : (xmlChar *) argname,
907 : (xmlChar *) str);
908 : }
909 :
910 43412 : foreach(arg, arg_strings)
911 : {
912 21694 : char *str = (char *) lfirst(arg);
913 :
914 21694 : xmlTextWriterWriteRaw(writer, (xmlChar *) str);
915 : }
916 :
917 21718 : xmlTextWriterEndElement(writer);
918 :
919 : /* we MUST do this now to flush data out to the buffer ... */
920 21718 : xmlFreeTextWriter(writer);
921 21718 : writer = NULL;
922 :
923 21718 : result = xmlBuffer_to_xmltype(buf);
924 : }
925 0 : PG_CATCH();
926 : {
927 0 : if (writer)
928 0 : xmlFreeTextWriter(writer);
929 0 : if (buf)
930 0 : xmlBufferFree(buf);
931 :
932 0 : pg_xml_done(xmlerrcxt, true);
933 :
934 0 : PG_RE_THROW();
935 : }
936 21718 : PG_END_TRY();
937 :
938 21718 : xmlBufferFree(buf);
939 :
940 21718 : pg_xml_done(xmlerrcxt, false);
941 :
942 21718 : return result;
943 : #else
944 : NO_XML_SUPPORT();
945 : return NULL;
946 : #endif
947 : }
948 :
949 :
950 : xmltype *
951 134 : xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace)
952 : {
953 : #ifdef USE_LIBXML
954 : xmlDocPtr doc;
955 :
956 134 : doc = xml_parse(data, xmloption_arg, preserve_whitespace,
957 : GetDatabaseEncoding(), NULL, NULL, NULL);
958 86 : xmlFreeDoc(doc);
959 :
960 86 : return (xmltype *) data;
961 : #else
962 : NO_XML_SUPPORT();
963 : return NULL;
964 : #endif
965 : }
966 :
967 :
968 : xmltype *
969 72 : xmlpi(const char *target, text *arg, bool arg_is_null, bool *result_is_null)
970 : {
971 : #ifdef USE_LIBXML
972 : xmltype *result;
973 : StringInfoData buf;
974 :
975 72 : if (pg_strcasecmp(target, "xml") == 0)
976 12 : ereport(ERROR,
977 : (errcode(ERRCODE_SYNTAX_ERROR), /* really */
978 : errmsg("invalid XML processing instruction"),
979 : errdetail("XML processing instruction target name cannot be \"%s\".", target)));
980 :
981 : /*
982 : * Following the SQL standard, the null check comes after the syntax check
983 : * above.
984 : */
985 60 : *result_is_null = arg_is_null;
986 60 : if (*result_is_null)
987 12 : return NULL;
988 :
989 48 : initStringInfo(&buf);
990 :
991 48 : appendStringInfo(&buf, "<?%s", target);
992 :
993 48 : if (arg != NULL)
994 : {
995 : char *string;
996 :
997 24 : string = text_to_cstring(arg);
998 24 : if (strstr(string, "?>") != NULL)
999 6 : ereport(ERROR,
1000 : (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
1001 : errmsg("invalid XML processing instruction"),
1002 : errdetail("XML processing instruction cannot contain \"?>\".")));
1003 :
1004 18 : appendStringInfoChar(&buf, ' ');
1005 18 : appendStringInfoString(&buf, string + strspn(string, " "));
1006 18 : pfree(string);
1007 : }
1008 42 : appendStringInfoString(&buf, "?>");
1009 :
1010 42 : result = stringinfo_to_xmltype(&buf);
1011 42 : pfree(buf.data);
1012 42 : return result;
1013 : #else
1014 : NO_XML_SUPPORT();
1015 : return NULL;
1016 : #endif
1017 : }
1018 :
1019 :
1020 : xmltype *
1021 60 : xmlroot(xmltype *data, text *version, int standalone)
1022 : {
1023 : #ifdef USE_LIBXML
1024 : char *str;
1025 : size_t len;
1026 : xmlChar *orig_version;
1027 : int orig_standalone;
1028 : StringInfoData buf;
1029 :
1030 60 : len = VARSIZE(data) - VARHDRSZ;
1031 60 : str = text_to_cstring((text *) data);
1032 :
1033 60 : parse_xml_decl((xmlChar *) str, &len, &orig_version, NULL, &orig_standalone);
1034 :
1035 60 : if (version)
1036 24 : orig_version = xml_text2xmlChar(version);
1037 : else
1038 36 : orig_version = NULL;
1039 :
1040 60 : switch (standalone)
1041 : {
1042 18 : case XML_STANDALONE_YES:
1043 18 : orig_standalone = 1;
1044 18 : break;
1045 12 : case XML_STANDALONE_NO:
1046 12 : orig_standalone = 0;
1047 12 : break;
1048 12 : case XML_STANDALONE_NO_VALUE:
1049 12 : orig_standalone = -1;
1050 12 : break;
1051 18 : case XML_STANDALONE_OMITTED:
1052 : /* leave original value */
1053 18 : break;
1054 : }
1055 :
1056 60 : initStringInfo(&buf);
1057 60 : print_xml_decl(&buf, orig_version, 0, orig_standalone);
1058 60 : appendStringInfoString(&buf, str + len);
1059 :
1060 60 : return stringinfo_to_xmltype(&buf);
1061 : #else
1062 : NO_XML_SUPPORT();
1063 : return NULL;
1064 : #endif
1065 : }
1066 :
1067 :
1068 : /*
1069 : * Validate document (given as string) against DTD (given as external link)
1070 : *
1071 : * This has been removed because it is a security hole: unprivileged users
1072 : * should not be able to use Postgres to fetch arbitrary external files,
1073 : * which unfortunately is exactly what libxml is willing to do with the DTD
1074 : * parameter.
1075 : */
1076 : Datum
1077 0 : xmlvalidate(PG_FUNCTION_ARGS)
1078 : {
1079 0 : ereport(ERROR,
1080 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1081 : errmsg("xmlvalidate is not implemented")));
1082 : return 0;
1083 : }
1084 :
1085 :
1086 : bool
1087 24 : xml_is_document(xmltype *arg)
1088 : {
1089 : #ifdef USE_LIBXML
1090 : xmlDocPtr doc;
1091 24 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1092 :
1093 : /*
1094 : * We'll report "true" if no soft error is reported by xml_parse().
1095 : */
1096 24 : doc = xml_parse((text *) arg, XMLOPTION_DOCUMENT, true,
1097 : GetDatabaseEncoding(), NULL, NULL, (Node *) &escontext);
1098 24 : if (doc)
1099 12 : xmlFreeDoc(doc);
1100 :
1101 24 : return !escontext.error_occurred;
1102 : #else /* not USE_LIBXML */
1103 : NO_XML_SUPPORT();
1104 : return false;
1105 : #endif /* not USE_LIBXML */
1106 : }
1107 :
1108 :
1109 : #ifdef USE_LIBXML
1110 :
1111 : /*
1112 : * pg_xml_init_library --- set up for use of libxml
1113 : *
1114 : * This should be called by each function that is about to use libxml
1115 : * facilities but doesn't require error handling. It initializes libxml
1116 : * and verifies compatibility with the loaded libxml version. These are
1117 : * once-per-session activities.
1118 : *
1119 : * TODO: xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and
1120 : * check)
1121 : */
1122 : void
1123 91218 : pg_xml_init_library(void)
1124 : {
1125 : static bool first_time = true;
1126 :
1127 91218 : if (first_time)
1128 : {
1129 : /* Stuff we need do only once per session */
1130 :
1131 : /*
1132 : * Currently, we have no pure UTF-8 support for internals -- check if
1133 : * we can work.
1134 : */
1135 : if (sizeof(char) != sizeof(xmlChar))
1136 : ereport(ERROR,
1137 : (errmsg("could not initialize XML library"),
1138 : errdetail("libxml2 has incompatible char type: sizeof(char)=%zu, sizeof(xmlChar)=%zu.",
1139 : sizeof(char), sizeof(xmlChar))));
1140 :
1141 : #ifdef USE_LIBXMLCONTEXT
1142 : /* Set up libxml's memory allocation our way */
1143 : xml_memory_init();
1144 : #endif
1145 :
1146 : /* Check library compatibility */
1147 26 : LIBXML_TEST_VERSION;
1148 :
1149 26 : first_time = false;
1150 : }
1151 91218 : }
1152 :
1153 : /*
1154 : * pg_xml_init --- set up for use of libxml and register an error handler
1155 : *
1156 : * This should be called by each function that is about to use libxml
1157 : * facilities and requires error handling. It initializes libxml with
1158 : * pg_xml_init_library() and establishes our libxml error handler.
1159 : *
1160 : * strictness determines which errors are reported and which are ignored.
1161 : *
1162 : * Calls to this function MUST be followed by a PG_TRY block that guarantees
1163 : * that pg_xml_done() is called during either normal or error exit.
1164 : *
1165 : * This is exported for use by contrib/xml2, as well as other code that might
1166 : * wish to share use of this module's libxml error handler.
1167 : */
1168 : PgXmlErrorContext *
1169 23894 : pg_xml_init(PgXmlStrictness strictness)
1170 : {
1171 : PgXmlErrorContext *errcxt;
1172 : void *new_errcxt;
1173 :
1174 : /* Do one-time setup if needed */
1175 23894 : pg_xml_init_library();
1176 :
1177 : /* Create error handling context structure */
1178 23894 : errcxt = (PgXmlErrorContext *) palloc(sizeof(PgXmlErrorContext));
1179 23894 : errcxt->magic = ERRCXT_MAGIC;
1180 23894 : errcxt->strictness = strictness;
1181 23894 : errcxt->err_occurred = false;
1182 23894 : initStringInfo(&errcxt->err_buf);
1183 :
1184 : /*
1185 : * Save original error handler and install ours. libxml originally didn't
1186 : * distinguish between the contexts for generic and for structured error
1187 : * handlers. If we're using an old libxml version, we must thus save the
1188 : * generic error context, even though we're using a structured error
1189 : * handler.
1190 : */
1191 23894 : errcxt->saved_errfunc = xmlStructuredError;
1192 :
1193 : #ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
1194 23894 : errcxt->saved_errcxt = xmlStructuredErrorContext;
1195 : #else
1196 : errcxt->saved_errcxt = xmlGenericErrorContext;
1197 : #endif
1198 :
1199 23894 : xmlSetStructuredErrorFunc((void *) errcxt, xml_errorHandler);
1200 :
1201 : /*
1202 : * Verify that xmlSetStructuredErrorFunc set the context variable we
1203 : * expected it to. If not, the error context pointer we just saved is not
1204 : * the correct thing to restore, and since that leaves us without a way to
1205 : * restore the context in pg_xml_done, we must fail.
1206 : *
1207 : * The only known situation in which this test fails is if we compile with
1208 : * headers from a libxml2 that doesn't track the structured error context
1209 : * separately (< 2.7.4), but at runtime use a version that does, or vice
1210 : * versa. The libxml2 authors did not treat that change as constituting
1211 : * an ABI break, so the LIBXML_TEST_VERSION test in pg_xml_init_library
1212 : * fails to protect us from this.
1213 : */
1214 :
1215 : #ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
1216 23894 : new_errcxt = xmlStructuredErrorContext;
1217 : #else
1218 : new_errcxt = xmlGenericErrorContext;
1219 : #endif
1220 :
1221 23894 : if (new_errcxt != (void *) errcxt)
1222 0 : ereport(ERROR,
1223 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1224 : errmsg("could not set up XML error handler"),
1225 : errhint("This probably indicates that the version of libxml2"
1226 : " being used is not compatible with the libxml2"
1227 : " header files that PostgreSQL was built with.")));
1228 :
1229 : /*
1230 : * Also, install an entity loader to prevent unwanted fetches of external
1231 : * files and URLs.
1232 : */
1233 23894 : errcxt->saved_entityfunc = xmlGetExternalEntityLoader();
1234 23894 : xmlSetExternalEntityLoader(xmlPgEntityLoader);
1235 :
1236 23894 : return errcxt;
1237 : }
1238 :
1239 :
1240 : /*
1241 : * pg_xml_done --- restore previous libxml error handling
1242 : *
1243 : * Resets libxml's global error-handling state to what it was before
1244 : * pg_xml_init() was called.
1245 : *
1246 : * This routine verifies that all pending errors have been dealt with
1247 : * (in assert-enabled builds, anyway).
1248 : */
1249 : void
1250 23894 : pg_xml_done(PgXmlErrorContext *errcxt, bool isError)
1251 : {
1252 : void *cur_errcxt;
1253 :
1254 : /* An assert seems like enough protection here */
1255 : Assert(errcxt->magic == ERRCXT_MAGIC);
1256 :
1257 : /*
1258 : * In a normal exit, there should be no un-handled libxml errors. But we
1259 : * shouldn't try to enforce this during error recovery, since the longjmp
1260 : * could have been thrown before xml_ereport had a chance to run.
1261 : */
1262 : Assert(!errcxt->err_occurred || isError);
1263 :
1264 : /*
1265 : * Check that libxml's global state is correct, warn if not. This is a
1266 : * real test and not an Assert because it has a higher probability of
1267 : * happening.
1268 : */
1269 : #ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
1270 23894 : cur_errcxt = xmlStructuredErrorContext;
1271 : #else
1272 : cur_errcxt = xmlGenericErrorContext;
1273 : #endif
1274 :
1275 23894 : if (cur_errcxt != (void *) errcxt)
1276 0 : elog(WARNING, "libxml error handling state is out of sync with xml.c");
1277 :
1278 : /* Restore the saved handlers */
1279 23894 : xmlSetStructuredErrorFunc(errcxt->saved_errcxt, errcxt->saved_errfunc);
1280 23894 : xmlSetExternalEntityLoader(errcxt->saved_entityfunc);
1281 :
1282 : /*
1283 : * Mark the struct as invalid, just in case somebody somehow manages to
1284 : * call xml_errorHandler or xml_ereport with it.
1285 : */
1286 23894 : errcxt->magic = 0;
1287 :
1288 : /* Release memory */
1289 23894 : pfree(errcxt->err_buf.data);
1290 23894 : pfree(errcxt);
1291 23894 : }
1292 :
1293 :
1294 : /*
1295 : * pg_xml_error_occurred() --- test the error flag
1296 : */
1297 : bool
1298 0 : pg_xml_error_occurred(PgXmlErrorContext *errcxt)
1299 : {
1300 0 : return errcxt->err_occurred;
1301 : }
1302 :
1303 :
1304 : /*
1305 : * SQL/XML allows storing "XML documents" or "XML content". "XML
1306 : * documents" are specified by the XML specification and are parsed
1307 : * easily by libxml. "XML content" is specified by SQL/XML as the
1308 : * production "XMLDecl? content". But libxml can only parse the
1309 : * "content" part, so we have to parse the XML declaration ourselves
1310 : * to complete this.
1311 : */
1312 :
1313 : #define CHECK_XML_SPACE(p) \
1314 : do { \
1315 : if (!xmlIsBlank_ch(*(p))) \
1316 : return XML_ERR_SPACE_REQUIRED; \
1317 : } while (0)
1318 :
1319 : #define SKIP_XML_SPACE(p) \
1320 : while (xmlIsBlank_ch(*(p))) (p)++
1321 :
1322 : /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
1323 : /* Beware of multiple evaluations of argument! */
1324 : #define PG_XMLISNAMECHAR(c) \
1325 : (xmlIsBaseChar_ch(c) || xmlIsIdeographicQ(c) \
1326 : || xmlIsDigit_ch(c) \
1327 : || c == '.' || c == '-' || c == '_' || c == ':' \
1328 : || xmlIsCombiningQ(c) \
1329 : || xmlIsExtender_ch(c))
1330 :
1331 : /* pnstrdup, but deal with xmlChar not char; len is measured in xmlChars */
1332 : static xmlChar *
1333 188 : xml_pnstrdup(const xmlChar *str, size_t len)
1334 : {
1335 : xmlChar *result;
1336 :
1337 188 : result = (xmlChar *) palloc((len + 1) * sizeof(xmlChar));
1338 188 : memcpy(result, str, len * sizeof(xmlChar));
1339 188 : result[len] = 0;
1340 188 : return result;
1341 : }
1342 :
1343 : /* Ditto, except input is char* */
1344 : static xmlChar *
1345 2416 : pg_xmlCharStrndup(const char *str, size_t len)
1346 : {
1347 : xmlChar *result;
1348 :
1349 2416 : result = (xmlChar *) palloc((len + 1) * sizeof(xmlChar));
1350 2416 : memcpy(result, str, len);
1351 2416 : result[len] = '\0';
1352 :
1353 2416 : return result;
1354 : }
1355 :
1356 : /*
1357 : * Copy xmlChar string to PostgreSQL-owned memory, freeing the input.
1358 : *
1359 : * The input xmlChar is freed regardless of success of the copy.
1360 : */
1361 : static char *
1362 109020 : xml_pstrdup_and_free(xmlChar *str)
1363 : {
1364 : char *result;
1365 :
1366 109020 : if (str)
1367 : {
1368 109020 : PG_TRY();
1369 : {
1370 109020 : result = pstrdup((char *) str);
1371 : }
1372 0 : PG_FINALLY();
1373 : {
1374 109020 : xmlFree(str);
1375 : }
1376 109020 : PG_END_TRY();
1377 : }
1378 : else
1379 0 : result = NULL;
1380 :
1381 109020 : return result;
1382 : }
1383 :
1384 : /*
1385 : * str is the null-terminated input string. Remaining arguments are
1386 : * output arguments; each can be NULL if value is not wanted.
1387 : * version and encoding are returned as locally-palloc'd strings.
1388 : * Result is 0 if OK, an error code if not.
1389 : */
1390 : static int
1391 67324 : parse_xml_decl(const xmlChar *str, size_t *lenp,
1392 : xmlChar **version, xmlChar **encoding, int *standalone)
1393 : {
1394 : const xmlChar *p;
1395 : const xmlChar *save_p;
1396 : size_t len;
1397 : int utf8char;
1398 : int utf8len;
1399 :
1400 : /*
1401 : * Only initialize libxml. We don't need error handling here, but we do
1402 : * need to make sure libxml is initialized before calling any of its
1403 : * functions. Note that this is safe (and a no-op) if caller has already
1404 : * done pg_xml_init().
1405 : */
1406 67324 : pg_xml_init_library();
1407 :
1408 : /* Initialize output arguments to "not present" */
1409 67324 : if (version)
1410 67056 : *version = NULL;
1411 67324 : if (encoding)
1412 0 : *encoding = NULL;
1413 67324 : if (standalone)
1414 67056 : *standalone = -1;
1415 :
1416 67324 : p = str;
1417 :
1418 67324 : if (xmlStrncmp(p, (xmlChar *) "<?xml", 5) != 0)
1419 67110 : goto finished;
1420 :
1421 : /*
1422 : * If next char is a name char, it's a PI like <?xml-stylesheet ...?>
1423 : * rather than an XMLDecl, so we have done what we came to do and found no
1424 : * XMLDecl.
1425 : *
1426 : * We need an input length value for xmlGetUTF8Char, but there's no need
1427 : * to count the whole document size, so use strnlen not strlen.
1428 : */
1429 214 : utf8len = strnlen((const char *) (p + 5), MAX_MULTIBYTE_CHAR_LEN);
1430 214 : utf8char = xmlGetUTF8Char(p + 5, &utf8len);
1431 214 : if (PG_XMLISNAMECHAR(utf8char))
1432 12 : goto finished;
1433 :
1434 202 : p += 5;
1435 :
1436 : /* version */
1437 202 : CHECK_XML_SPACE(p);
1438 404 : SKIP_XML_SPACE(p);
1439 202 : if (xmlStrncmp(p, (xmlChar *) "version", 7) != 0)
1440 0 : return XML_ERR_VERSION_MISSING;
1441 202 : p += 7;
1442 202 : SKIP_XML_SPACE(p);
1443 202 : if (*p != '=')
1444 0 : return XML_ERR_VERSION_MISSING;
1445 202 : p += 1;
1446 202 : SKIP_XML_SPACE(p);
1447 :
1448 202 : if (*p == '\'' || *p == '"')
1449 202 : {
1450 : const xmlChar *q;
1451 :
1452 202 : q = xmlStrchr(p + 1, *p);
1453 202 : if (!q)
1454 0 : return XML_ERR_VERSION_MISSING;
1455 :
1456 202 : if (version)
1457 188 : *version = xml_pnstrdup(p + 1, q - p - 1);
1458 202 : p = q + 1;
1459 : }
1460 : else
1461 0 : return XML_ERR_VERSION_MISSING;
1462 :
1463 : /* encoding */
1464 202 : save_p = p;
1465 356 : SKIP_XML_SPACE(p);
1466 202 : if (xmlStrncmp(p, (xmlChar *) "encoding", 8) == 0)
1467 : {
1468 46 : CHECK_XML_SPACE(save_p);
1469 46 : p += 8;
1470 46 : SKIP_XML_SPACE(p);
1471 46 : if (*p != '=')
1472 0 : return XML_ERR_MISSING_ENCODING;
1473 46 : p += 1;
1474 46 : SKIP_XML_SPACE(p);
1475 :
1476 46 : if (*p == '\'' || *p == '"')
1477 46 : {
1478 : const xmlChar *q;
1479 :
1480 46 : q = xmlStrchr(p + 1, *p);
1481 46 : if (!q)
1482 0 : return XML_ERR_MISSING_ENCODING;
1483 :
1484 46 : if (encoding)
1485 0 : *encoding = xml_pnstrdup(p + 1, q - p - 1);
1486 46 : p = q + 1;
1487 : }
1488 : else
1489 0 : return XML_ERR_MISSING_ENCODING;
1490 : }
1491 : else
1492 : {
1493 156 : p = save_p;
1494 : }
1495 :
1496 : /* standalone */
1497 202 : save_p = p;
1498 310 : SKIP_XML_SPACE(p);
1499 202 : if (xmlStrncmp(p, (xmlChar *) "standalone", 10) == 0)
1500 : {
1501 108 : CHECK_XML_SPACE(save_p);
1502 108 : p += 10;
1503 108 : SKIP_XML_SPACE(p);
1504 108 : if (*p != '=')
1505 0 : return XML_ERR_STANDALONE_VALUE;
1506 108 : p += 1;
1507 108 : SKIP_XML_SPACE(p);
1508 216 : if (xmlStrncmp(p, (xmlChar *) "'yes'", 5) == 0 ||
1509 108 : xmlStrncmp(p, (xmlChar *) "\"yes\"", 5) == 0)
1510 : {
1511 60 : if (standalone)
1512 60 : *standalone = 1;
1513 60 : p += 5;
1514 : }
1515 96 : else if (xmlStrncmp(p, (xmlChar *) "'no'", 4) == 0 ||
1516 48 : xmlStrncmp(p, (xmlChar *) "\"no\"", 4) == 0)
1517 : {
1518 36 : if (standalone)
1519 36 : *standalone = 0;
1520 36 : p += 4;
1521 : }
1522 : else
1523 12 : return XML_ERR_STANDALONE_VALUE;
1524 : }
1525 : else
1526 : {
1527 94 : p = save_p;
1528 : }
1529 :
1530 190 : SKIP_XML_SPACE(p);
1531 190 : if (xmlStrncmp(p, (xmlChar *) "?>", 2) != 0)
1532 0 : return XML_ERR_XMLDECL_NOT_FINISHED;
1533 190 : p += 2;
1534 :
1535 67312 : finished:
1536 67312 : len = p - str;
1537 :
1538 73700 : for (p = str; p < str + len; p++)
1539 6388 : if (*p > 127)
1540 0 : return XML_ERR_INVALID_CHAR;
1541 :
1542 67312 : if (lenp)
1543 67312 : *lenp = len;
1544 :
1545 67312 : return XML_ERR_OK;
1546 : }
1547 :
1548 :
1549 : /*
1550 : * Write an XML declaration. On output, we adjust the XML declaration
1551 : * as follows. (These rules are the moral equivalent of the clause
1552 : * "Serialization of an XML value" in the SQL standard.)
1553 : *
1554 : * We try to avoid generating an XML declaration if possible. This is
1555 : * so that you don't get trivial things like xml '<foo/>' resulting in
1556 : * '<?xml version="1.0"?><foo/>', which would surely be annoying. We
1557 : * must provide a declaration if the standalone property is specified
1558 : * or if we include an encoding declaration. If we have a
1559 : * declaration, we must specify a version (XML requires this).
1560 : * Otherwise we only make a declaration if the version is not "1.0",
1561 : * which is the default version specified in SQL:2003.
1562 : */
1563 : static bool
1564 22928 : print_xml_decl(StringInfo buf, const xmlChar *version,
1565 : pg_enc encoding, int standalone)
1566 : {
1567 22928 : if ((version && strcmp((const char *) version, PG_XML_DEFAULT_VERSION) != 0)
1568 22892 : || (encoding && encoding != PG_UTF8)
1569 22892 : || standalone != -1)
1570 : {
1571 96 : appendStringInfoString(buf, "<?xml");
1572 :
1573 96 : if (version)
1574 72 : appendStringInfo(buf, " version=\"%s\"", version);
1575 : else
1576 24 : appendStringInfo(buf, " version=\"%s\"", PG_XML_DEFAULT_VERSION);
1577 :
1578 96 : if (encoding && encoding != PG_UTF8)
1579 : {
1580 : /*
1581 : * XXX might be useful to convert this to IANA names (ISO-8859-1
1582 : * instead of LATIN1 etc.); needs field experience
1583 : */
1584 0 : appendStringInfo(buf, " encoding=\"%s\"",
1585 : pg_encoding_to_char(encoding));
1586 : }
1587 :
1588 96 : if (standalone == 1)
1589 48 : appendStringInfoString(buf, " standalone=\"yes\"");
1590 48 : else if (standalone == 0)
1591 24 : appendStringInfoString(buf, " standalone=\"no\"");
1592 96 : appendStringInfoString(buf, "?>");
1593 :
1594 96 : return true;
1595 : }
1596 : else
1597 22832 : return false;
1598 : }
1599 :
1600 : /*
1601 : * Test whether an input that is to be parsed as CONTENT contains a DTD.
1602 : *
1603 : * The SQL/XML:2003 definition of CONTENT ("XMLDecl? content") is not
1604 : * satisfied by a document with a DTD, which is a bit of a wart, as it means
1605 : * the CONTENT type is not a proper superset of DOCUMENT. SQL/XML:2006 and
1606 : * later fix that, by redefining content with reference to the "more
1607 : * permissive" Document Node of the XQuery/XPath Data Model, such that any
1608 : * DOCUMENT value is indeed also a CONTENT value. That definition is more
1609 : * useful, as CONTENT becomes usable for parsing input of unknown form (think
1610 : * pg_restore).
1611 : *
1612 : * As used below in parse_xml when parsing for CONTENT, libxml does not give
1613 : * us the 2006+ behavior, but only the 2003; it will choke if the input has
1614 : * a DTD. But we can provide the 2006+ definition of CONTENT easily enough,
1615 : * by detecting this case first and simply doing the parse as DOCUMENT.
1616 : *
1617 : * A DTD can be found arbitrarily far in, but that would be a contrived case;
1618 : * it will ordinarily start within a few dozen characters. The only things
1619 : * that can precede it are an XMLDecl (here, the caller will have called
1620 : * parse_xml_decl already), whitespace, comments, and processing instructions.
1621 : * This function need only return true if it sees a valid sequence of such
1622 : * things leading to <!DOCTYPE. It can simply return false in any other
1623 : * cases, including malformed input; that will mean the input gets parsed as
1624 : * CONTENT as originally planned, with libxml reporting any errors.
1625 : *
1626 : * This is only to be called from xml_parse, when pg_xml_init has already
1627 : * been called. The input is already in UTF8 encoding.
1628 : */
1629 : static bool
1630 932 : xml_doctype_in_content(const xmlChar *str)
1631 : {
1632 932 : const xmlChar *p = str;
1633 :
1634 : for (;;)
1635 36 : {
1636 : const xmlChar *e;
1637 :
1638 1058 : SKIP_XML_SPACE(p);
1639 968 : if (*p != '<')
1640 194 : return false;
1641 774 : p++;
1642 :
1643 774 : if (*p == '!')
1644 : {
1645 72 : p++;
1646 :
1647 : /* if we see <!DOCTYPE, we can return true */
1648 72 : if (xmlStrncmp(p, (xmlChar *) "DOCTYPE", 7) == 0)
1649 42 : return true;
1650 :
1651 : /* otherwise, if it's not a comment, fail */
1652 30 : if (xmlStrncmp(p, (xmlChar *) "--", 2) != 0)
1653 0 : return false;
1654 : /* find end of comment: find -- and a > must follow */
1655 30 : p = xmlStrstr(p + 2, (xmlChar *) "--");
1656 30 : if (!p || p[2] != '>')
1657 0 : return false;
1658 : /* advance over comment, and keep scanning */
1659 30 : p += 3;
1660 30 : continue;
1661 : }
1662 :
1663 : /* otherwise, if it's not a PI <?target something?>, fail */
1664 702 : if (*p != '?')
1665 696 : return false;
1666 6 : p++;
1667 :
1668 : /* find end of PI (the string ?> is forbidden within a PI) */
1669 6 : e = xmlStrstr(p, (xmlChar *) "?>");
1670 6 : if (!e)
1671 0 : return false;
1672 :
1673 : /* advance over PI, keep scanning */
1674 6 : p = e + 2;
1675 : }
1676 : }
1677 :
1678 :
1679 : /*
1680 : * Convert a text object to XML internal representation
1681 : *
1682 : * data is the source data (must not be toasted!), encoding is its encoding,
1683 : * and xmloption_arg and preserve_whitespace are options for the
1684 : * transformation.
1685 : *
1686 : * If parsed_xmloptiontype isn't NULL, *parsed_xmloptiontype is set to the
1687 : * XmlOptionType actually used to parse the input (typically the same as
1688 : * xmloption_arg, but a DOCTYPE node in the input can force DOCUMENT mode).
1689 : *
1690 : * If parsed_nodes isn't NULL and the input is not an XML document, the list
1691 : * of parsed nodes from the xmlParseBalancedChunkMemory call will be returned
1692 : * to *parsed_nodes.
1693 : *
1694 : * Errors normally result in ereport(ERROR), but if escontext is an
1695 : * ErrorSaveContext, then "safe" errors are reported there instead, and the
1696 : * caller must check SOFT_ERROR_OCCURRED() to see whether that happened.
1697 : *
1698 : * Note: it is caller's responsibility to xmlFreeDoc() the result,
1699 : * else a permanent memory leak will ensue! But note the result could
1700 : * be NULL after a soft error.
1701 : *
1702 : * TODO maybe libxml2's xmlreader is better? (do not construct DOM,
1703 : * yet do not use SAX - see xmlreader.c)
1704 : */
1705 : static xmlDocPtr
1706 1232 : xml_parse(text *data, XmlOptionType xmloption_arg,
1707 : bool preserve_whitespace, int encoding,
1708 : XmlOptionType *parsed_xmloptiontype, xmlNodePtr *parsed_nodes,
1709 : Node *escontext)
1710 : {
1711 : int32 len;
1712 : xmlChar *string;
1713 : xmlChar *utf8string;
1714 : PgXmlErrorContext *xmlerrcxt;
1715 1232 : volatile xmlParserCtxtPtr ctxt = NULL;
1716 1232 : volatile xmlDocPtr doc = NULL;
1717 :
1718 : /*
1719 : * This step looks annoyingly redundant, but we must do it to have a
1720 : * null-terminated string in case encoding conversion isn't required.
1721 : */
1722 1232 : len = VARSIZE_ANY_EXHDR(data); /* will be useful later */
1723 1232 : string = xml_text2xmlChar(data);
1724 :
1725 : /*
1726 : * If the data isn't UTF8, we must translate before giving it to libxml.
1727 : *
1728 : * XXX ideally, we'd catch any encoding conversion failure and return a
1729 : * soft error. However, failure to convert to UTF8 should be pretty darn
1730 : * rare, so for now this is left undone.
1731 : */
1732 1232 : utf8string = pg_do_encoding_conversion(string,
1733 : len,
1734 : encoding,
1735 : PG_UTF8);
1736 :
1737 : /* Start up libxml and its parser */
1738 1232 : xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_WELLFORMED);
1739 :
1740 : /* Use a TRY block to ensure we clean up correctly */
1741 1232 : PG_TRY();
1742 : {
1743 1232 : bool parse_as_document = false;
1744 : int res_code;
1745 1232 : size_t count = 0;
1746 1232 : xmlChar *version = NULL;
1747 1232 : int standalone = 0;
1748 :
1749 : /* Any errors here are reported as hard ereport's */
1750 1232 : xmlInitParser();
1751 :
1752 1232 : ctxt = xmlNewParserCtxt();
1753 1232 : if (ctxt == NULL || xmlerrcxt->err_occurred)
1754 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
1755 : "could not allocate parser context");
1756 :
1757 : /* Decide whether to parse as document or content */
1758 1232 : if (xmloption_arg == XMLOPTION_DOCUMENT)
1759 288 : parse_as_document = true;
1760 : else
1761 : {
1762 : /* Parse and skip over the XML declaration, if any */
1763 944 : res_code = parse_xml_decl(utf8string,
1764 : &count, &version, NULL, &standalone);
1765 944 : if (res_code != 0)
1766 : {
1767 12 : errsave(escontext,
1768 : errcode(ERRCODE_INVALID_XML_CONTENT),
1769 : errmsg_internal("invalid XML content: invalid XML declaration"),
1770 : errdetail_for_xml_code(res_code));
1771 12 : goto fail;
1772 : }
1773 :
1774 : /* Is there a DOCTYPE element? */
1775 932 : if (xml_doctype_in_content(utf8string + count))
1776 42 : parse_as_document = true;
1777 : }
1778 :
1779 : /* initialize output parameters */
1780 1220 : if (parsed_xmloptiontype != NULL)
1781 132 : *parsed_xmloptiontype = parse_as_document ? XMLOPTION_DOCUMENT :
1782 : XMLOPTION_CONTENT;
1783 1220 : if (parsed_nodes != NULL)
1784 132 : *parsed_nodes = NULL;
1785 :
1786 1220 : if (parse_as_document)
1787 : {
1788 : /*
1789 : * Note, that here we try to apply DTD defaults
1790 : * (XML_PARSE_DTDATTR) according to SQL/XML:2008 GR 10.16.7.d:
1791 : * 'Default values defined by internal DTD are applied'. As for
1792 : * external DTDs, we try to support them too, (see SQL/XML:2008 GR
1793 : * 10.16.7.e)
1794 : */
1795 330 : doc = xmlCtxtReadDoc(ctxt, utf8string,
1796 : NULL,
1797 : "UTF-8",
1798 : XML_PARSE_NOENT | XML_PARSE_DTDATTR
1799 : | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS));
1800 330 : if (doc == NULL || xmlerrcxt->err_occurred)
1801 : {
1802 : /* Use original option to decide which error code to report */
1803 144 : if (xmloption_arg == XMLOPTION_DOCUMENT)
1804 138 : xml_errsave(escontext, xmlerrcxt,
1805 : ERRCODE_INVALID_XML_DOCUMENT,
1806 : "invalid XML document");
1807 : else
1808 6 : xml_errsave(escontext, xmlerrcxt,
1809 : ERRCODE_INVALID_XML_CONTENT,
1810 : "invalid XML content");
1811 96 : goto fail;
1812 : }
1813 : }
1814 : else
1815 : {
1816 890 : doc = xmlNewDoc(version);
1817 890 : if (doc == NULL || xmlerrcxt->err_occurred)
1818 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
1819 : "could not allocate XML document");
1820 :
1821 : Assert(doc->encoding == NULL);
1822 890 : doc->encoding = xmlStrdup((const xmlChar *) "UTF-8");
1823 890 : if (doc->encoding == NULL || xmlerrcxt->err_occurred)
1824 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
1825 : "could not allocate XML document");
1826 890 : doc->standalone = standalone;
1827 :
1828 : /* allow empty content */
1829 890 : if (*(utf8string + count))
1830 : {
1831 1732 : res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0,
1832 866 : utf8string + count,
1833 : parsed_nodes);
1834 866 : if (res_code != 0 || xmlerrcxt->err_occurred)
1835 : {
1836 60 : xml_errsave(escontext, xmlerrcxt,
1837 : ERRCODE_INVALID_XML_CONTENT,
1838 : "invalid XML content");
1839 12 : goto fail;
1840 : }
1841 : }
1842 : }
1843 :
1844 1136 : fail:
1845 : ;
1846 : }
1847 96 : PG_CATCH();
1848 : {
1849 96 : if (doc != NULL)
1850 48 : xmlFreeDoc(doc);
1851 96 : if (ctxt != NULL)
1852 96 : xmlFreeParserCtxt(ctxt);
1853 :
1854 96 : pg_xml_done(xmlerrcxt, true);
1855 :
1856 96 : PG_RE_THROW();
1857 : }
1858 1136 : PG_END_TRY();
1859 :
1860 1136 : xmlFreeParserCtxt(ctxt);
1861 :
1862 1136 : pg_xml_done(xmlerrcxt, false);
1863 :
1864 1136 : return doc;
1865 : }
1866 :
1867 :
1868 : /*
1869 : * xmlChar<->text conversions
1870 : */
1871 : static xmlChar *
1872 1370 : xml_text2xmlChar(text *in)
1873 : {
1874 1370 : return (xmlChar *) text_to_cstring(in);
1875 : }
1876 :
1877 :
1878 : #ifdef USE_LIBXMLCONTEXT
1879 :
1880 : /*
1881 : * Manage the special context used for all libxml allocations (but only
1882 : * in special debug builds; see notes at top of file)
1883 : */
1884 : static void
1885 : xml_memory_init(void)
1886 : {
1887 : /* Create memory context if not there already */
1888 : if (LibxmlContext == NULL)
1889 : LibxmlContext = AllocSetContextCreate(TopMemoryContext,
1890 : "Libxml context",
1891 : ALLOCSET_DEFAULT_SIZES);
1892 :
1893 : /* Re-establish the callbacks even if already set */
1894 : xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
1895 : }
1896 :
1897 : /*
1898 : * Wrappers for memory management functions
1899 : */
1900 : static void *
1901 : xml_palloc(size_t size)
1902 : {
1903 : return MemoryContextAlloc(LibxmlContext, size);
1904 : }
1905 :
1906 :
1907 : static void *
1908 : xml_repalloc(void *ptr, size_t size)
1909 : {
1910 : return repalloc(ptr, size);
1911 : }
1912 :
1913 :
1914 : static void
1915 : xml_pfree(void *ptr)
1916 : {
1917 : /* At least some parts of libxml assume xmlFree(NULL) is allowed */
1918 : if (ptr)
1919 : pfree(ptr);
1920 : }
1921 :
1922 :
1923 : static char *
1924 : xml_pstrdup(const char *string)
1925 : {
1926 : return MemoryContextStrdup(LibxmlContext, string);
1927 : }
1928 : #endif /* USE_LIBXMLCONTEXT */
1929 :
1930 :
1931 : /*
1932 : * xmlPgEntityLoader --- entity loader callback function
1933 : *
1934 : * Silently prevent any external entity URL from being loaded. We don't want
1935 : * to throw an error, so instead make the entity appear to expand to an empty
1936 : * string.
1937 : *
1938 : * We would prefer to allow loading entities that exist in the system's
1939 : * global XML catalog; but the available libxml2 APIs make that a complex
1940 : * and fragile task. For now, just shut down all external access.
1941 : */
1942 : static xmlParserInputPtr
1943 30 : xmlPgEntityLoader(const char *URL, const char *ID,
1944 : xmlParserCtxtPtr ctxt)
1945 : {
1946 30 : return xmlNewStringInputStream(ctxt, (const xmlChar *) "");
1947 : }
1948 :
1949 :
1950 : /*
1951 : * xml_ereport --- report an XML-related error
1952 : *
1953 : * The "msg" is the SQL-level message; some can be adopted from the SQL/XML
1954 : * standard. This function adds libxml's native error message, if any, as
1955 : * detail.
1956 : *
1957 : * This is exported for modules that want to share the core libxml error
1958 : * handler. Note that pg_xml_init() *must* have been called previously.
1959 : */
1960 : void
1961 12 : xml_ereport(PgXmlErrorContext *errcxt, int level, int sqlcode, const char *msg)
1962 : {
1963 : char *detail;
1964 :
1965 : /* Defend against someone passing us a bogus context struct */
1966 12 : if (errcxt->magic != ERRCXT_MAGIC)
1967 0 : elog(ERROR, "xml_ereport called with invalid PgXmlErrorContext");
1968 :
1969 : /* Flag that the current libxml error has been reported */
1970 12 : errcxt->err_occurred = false;
1971 :
1972 : /* Include detail only if we have some text from libxml */
1973 12 : if (errcxt->err_buf.len > 0)
1974 12 : detail = errcxt->err_buf.data;
1975 : else
1976 0 : detail = NULL;
1977 :
1978 12 : ereport(level,
1979 : (errcode(sqlcode),
1980 : errmsg_internal("%s", msg),
1981 : detail ? errdetail_internal("%s", detail) : 0));
1982 0 : }
1983 :
1984 :
1985 : /*
1986 : * xml_errsave --- save an XML-related error
1987 : *
1988 : * If escontext is an ErrorSaveContext, error details are saved into it,
1989 : * and control returns normally.
1990 : *
1991 : * Otherwise, the error is thrown, so that this is equivalent to
1992 : * xml_ereport() with level == ERROR.
1993 : *
1994 : * This should be used only for errors that we're sure we do not need
1995 : * a transaction abort to clean up after.
1996 : */
1997 : static void
1998 204 : xml_errsave(Node *escontext, PgXmlErrorContext *errcxt,
1999 : int sqlcode, const char *msg)
2000 : {
2001 : char *detail;
2002 :
2003 : /* Defend against someone passing us a bogus context struct */
2004 204 : if (errcxt->magic != ERRCXT_MAGIC)
2005 0 : elog(ERROR, "xml_errsave called with invalid PgXmlErrorContext");
2006 :
2007 : /* Flag that the current libxml error has been reported */
2008 204 : errcxt->err_occurred = false;
2009 :
2010 : /* Include detail only if we have some text from libxml */
2011 204 : if (errcxt->err_buf.len > 0)
2012 204 : detail = errcxt->err_buf.data;
2013 : else
2014 0 : detail = NULL;
2015 :
2016 204 : errsave(escontext,
2017 : (errcode(sqlcode),
2018 : errmsg_internal("%s", msg),
2019 : detail ? errdetail_internal("%s", detail) : 0));
2020 108 : }
2021 :
2022 :
2023 : /*
2024 : * Error handler for libxml errors and warnings
2025 : */
2026 : static void
2027 398 : xml_errorHandler(void *data, xmlErrorPtr error)
2028 : {
2029 398 : PgXmlErrorContext *xmlerrcxt = (PgXmlErrorContext *) data;
2030 398 : xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) error->ctxt;
2031 398 : xmlParserInputPtr input = (ctxt != NULL) ? ctxt->input : NULL;
2032 398 : xmlNodePtr node = error->node;
2033 398 : const xmlChar *name = (node != NULL &&
2034 398 : node->type == XML_ELEMENT_NODE) ? node->name : NULL;
2035 398 : int domain = error->domain;
2036 398 : int level = error->level;
2037 : StringInfo errorBuf;
2038 :
2039 : /*
2040 : * Defend against someone passing us a bogus context struct.
2041 : *
2042 : * We force a backend exit if this check fails because longjmp'ing out of
2043 : * libxml would likely render it unsafe to use further.
2044 : */
2045 398 : if (xmlerrcxt->magic != ERRCXT_MAGIC)
2046 0 : elog(FATAL, "xml_errorHandler called with invalid PgXmlErrorContext");
2047 :
2048 : /*----------
2049 : * Older libxml versions report some errors differently.
2050 : * First, some errors were previously reported as coming from the parser
2051 : * domain but are now reported as coming from the namespace domain.
2052 : * Second, some warnings were upgraded to errors.
2053 : * We attempt to compensate for that here.
2054 : *----------
2055 : */
2056 398 : switch (error->code)
2057 : {
2058 30 : case XML_WAR_NS_URI:
2059 30 : level = XML_ERR_ERROR;
2060 30 : domain = XML_FROM_NAMESPACE;
2061 30 : break;
2062 :
2063 54 : case XML_ERR_NS_DECL_ERROR:
2064 : case XML_WAR_NS_URI_RELATIVE:
2065 : case XML_WAR_NS_COLUMN:
2066 : case XML_NS_ERR_XML_NAMESPACE:
2067 : case XML_NS_ERR_UNDEFINED_NAMESPACE:
2068 : case XML_NS_ERR_QNAME:
2069 : case XML_NS_ERR_ATTRIBUTE_REDEFINED:
2070 : case XML_NS_ERR_EMPTY:
2071 54 : domain = XML_FROM_NAMESPACE;
2072 54 : break;
2073 : }
2074 :
2075 : /* Decide whether to act on the error or not */
2076 398 : switch (domain)
2077 : {
2078 314 : case XML_FROM_PARSER:
2079 : case XML_FROM_NONE:
2080 : case XML_FROM_MEMORY:
2081 : case XML_FROM_IO:
2082 :
2083 : /*
2084 : * Suppress warnings about undeclared entities. We need to do
2085 : * this to avoid problems due to not loading DTD definitions.
2086 : */
2087 314 : if (error->code == XML_WAR_UNDECLARED_ENTITY)
2088 6 : return;
2089 :
2090 : /* Otherwise, accept error regardless of the parsing purpose */
2091 308 : break;
2092 :
2093 84 : default:
2094 : /* Ignore error if only doing well-formedness check */
2095 84 : if (xmlerrcxt->strictness == PG_XML_STRICTNESS_WELLFORMED)
2096 66 : return;
2097 18 : break;
2098 : }
2099 :
2100 : /* Prepare error message in errorBuf */
2101 326 : errorBuf = makeStringInfo();
2102 :
2103 326 : if (error->line > 0)
2104 326 : appendStringInfo(errorBuf, "line %d: ", error->line);
2105 326 : if (name != NULL)
2106 0 : appendStringInfo(errorBuf, "element %s: ", name);
2107 326 : if (error->message != NULL)
2108 326 : appendStringInfoString(errorBuf, error->message);
2109 : else
2110 0 : appendStringInfoString(errorBuf, "(no message provided)");
2111 :
2112 : /*
2113 : * Append context information to errorBuf.
2114 : *
2115 : * xmlParserPrintFileContext() uses libxml's "generic" error handler to
2116 : * write the context. Since we don't want to duplicate libxml
2117 : * functionality here, we set up a generic error handler temporarily.
2118 : *
2119 : * We use appendStringInfo() directly as libxml's generic error handler.
2120 : * This should work because it has essentially the same signature as
2121 : * libxml expects, namely (void *ptr, const char *msg, ...).
2122 : */
2123 326 : if (input != NULL)
2124 : {
2125 326 : xmlGenericErrorFunc errFuncSaved = xmlGenericError;
2126 326 : void *errCtxSaved = xmlGenericErrorContext;
2127 :
2128 326 : xmlSetGenericErrorFunc((void *) errorBuf,
2129 : (xmlGenericErrorFunc) appendStringInfo);
2130 :
2131 : /* Add context information to errorBuf */
2132 326 : appendStringInfoLineSeparator(errorBuf);
2133 :
2134 326 : xmlParserPrintFileContext(input);
2135 :
2136 : /* Restore generic error func */
2137 326 : xmlSetGenericErrorFunc(errCtxSaved, errFuncSaved);
2138 : }
2139 :
2140 : /* Get rid of any trailing newlines in errorBuf */
2141 326 : chopStringInfoNewlines(errorBuf);
2142 :
2143 : /*
2144 : * Legacy error handling mode. err_occurred is never set, we just add the
2145 : * message to err_buf. This mode exists because the xml2 contrib module
2146 : * uses our error-handling infrastructure, but we don't want to change its
2147 : * behaviour since it's deprecated anyway. This is also why we don't
2148 : * distinguish between notices, warnings and errors here --- the old-style
2149 : * generic error handler wouldn't have done that either.
2150 : */
2151 326 : if (xmlerrcxt->strictness == PG_XML_STRICTNESS_LEGACY)
2152 : {
2153 2 : appendStringInfoLineSeparator(&xmlerrcxt->err_buf);
2154 2 : appendBinaryStringInfo(&xmlerrcxt->err_buf, errorBuf->data,
2155 : errorBuf->len);
2156 :
2157 2 : pfree(errorBuf->data);
2158 2 : pfree(errorBuf);
2159 2 : return;
2160 : }
2161 :
2162 : /*
2163 : * We don't want to ereport() here because that'd probably leave libxml in
2164 : * an inconsistent state. Instead, we remember the error and ereport()
2165 : * from xml_ereport().
2166 : *
2167 : * Warnings and notices can be reported immediately since they won't cause
2168 : * a longjmp() out of libxml.
2169 : */
2170 324 : if (level >= XML_ERR_ERROR)
2171 : {
2172 318 : appendStringInfoLineSeparator(&xmlerrcxt->err_buf);
2173 318 : appendBinaryStringInfo(&xmlerrcxt->err_buf, errorBuf->data,
2174 : errorBuf->len);
2175 :
2176 318 : xmlerrcxt->err_occurred = true;
2177 : }
2178 6 : else if (level >= XML_ERR_WARNING)
2179 : {
2180 6 : ereport(WARNING,
2181 : (errmsg_internal("%s", errorBuf->data)));
2182 : }
2183 : else
2184 : {
2185 0 : ereport(NOTICE,
2186 : (errmsg_internal("%s", errorBuf->data)));
2187 : }
2188 :
2189 324 : pfree(errorBuf->data);
2190 324 : pfree(errorBuf);
2191 : }
2192 :
2193 :
2194 : /*
2195 : * Convert libxml error codes into textual errdetail messages.
2196 : *
2197 : * This should be called within an ereport or errsave invocation,
2198 : * just as errdetail would be.
2199 : *
2200 : * At the moment, we only need to cover those codes that we
2201 : * may raise in this file.
2202 : */
2203 : static int
2204 6 : errdetail_for_xml_code(int code)
2205 : {
2206 : const char *det;
2207 :
2208 6 : switch (code)
2209 : {
2210 0 : case XML_ERR_INVALID_CHAR:
2211 0 : det = gettext_noop("Invalid character value.");
2212 0 : break;
2213 0 : case XML_ERR_SPACE_REQUIRED:
2214 0 : det = gettext_noop("Space required.");
2215 0 : break;
2216 6 : case XML_ERR_STANDALONE_VALUE:
2217 6 : det = gettext_noop("standalone accepts only 'yes' or 'no'.");
2218 6 : break;
2219 0 : case XML_ERR_VERSION_MISSING:
2220 0 : det = gettext_noop("Malformed declaration: missing version.");
2221 0 : break;
2222 0 : case XML_ERR_MISSING_ENCODING:
2223 0 : det = gettext_noop("Missing encoding in text declaration.");
2224 0 : break;
2225 0 : case XML_ERR_XMLDECL_NOT_FINISHED:
2226 0 : det = gettext_noop("Parsing XML declaration: '?>' expected.");
2227 0 : break;
2228 0 : default:
2229 0 : det = gettext_noop("Unrecognized libxml error code: %d.");
2230 0 : break;
2231 : }
2232 :
2233 6 : return errdetail(det, code);
2234 : }
2235 :
2236 :
2237 : /*
2238 : * Remove all trailing newlines from a StringInfo string
2239 : */
2240 : static void
2241 972 : chopStringInfoNewlines(StringInfo str)
2242 : {
2243 1624 : while (str->len > 0 && str->data[str->len - 1] == '\n')
2244 652 : str->data[--str->len] = '\0';
2245 972 : }
2246 :
2247 :
2248 : /*
2249 : * Append a newline after removing any existing trailing newlines
2250 : */
2251 : static void
2252 646 : appendStringInfoLineSeparator(StringInfo str)
2253 : {
2254 646 : chopStringInfoNewlines(str);
2255 646 : if (str->len > 0)
2256 428 : appendStringInfoChar(str, '\n');
2257 646 : }
2258 :
2259 :
2260 : /*
2261 : * Convert one char in the current server encoding to a Unicode codepoint.
2262 : */
2263 : static pg_wchar
2264 18280 : sqlchar_to_unicode(const char *s)
2265 : {
2266 : char *utf8string;
2267 : pg_wchar ret[2]; /* need space for trailing zero */
2268 :
2269 : /* note we're not assuming s is null-terminated */
2270 18280 : utf8string = pg_server_to_any(s, pg_mblen(s), PG_UTF8);
2271 :
2272 18280 : pg_encoding_mb2wchar_with_len(PG_UTF8, utf8string, ret,
2273 : pg_encoding_mblen(PG_UTF8, utf8string));
2274 :
2275 18280 : if (utf8string != s)
2276 0 : pfree(utf8string);
2277 :
2278 18280 : return ret[0];
2279 : }
2280 :
2281 :
2282 : static bool
2283 3638 : is_valid_xml_namefirst(pg_wchar c)
2284 : {
2285 : /* (Letter | '_' | ':') */
2286 3644 : return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
2287 7282 : || c == '_' || c == ':');
2288 : }
2289 :
2290 :
2291 : static bool
2292 14642 : is_valid_xml_namechar(pg_wchar c)
2293 : {
2294 : /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
2295 15532 : return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
2296 890 : || xmlIsDigitQ(c)
2297 254 : || c == '.' || c == '-' || c == '_' || c == ':'
2298 12 : || xmlIsCombiningQ(c)
2299 31064 : || xmlIsExtenderQ(c));
2300 : }
2301 : #endif /* USE_LIBXML */
2302 :
2303 :
2304 : /*
2305 : * Map SQL identifier to XML name; see SQL/XML:2008 section 9.1.
2306 : */
2307 : char *
2308 3652 : map_sql_identifier_to_xml_name(const char *ident, bool fully_escaped,
2309 : bool escape_period)
2310 : {
2311 : #ifdef USE_LIBXML
2312 : StringInfoData buf;
2313 : const char *p;
2314 :
2315 : /*
2316 : * SQL/XML doesn't make use of this case anywhere, so it's probably a
2317 : * mistake.
2318 : */
2319 : Assert(fully_escaped || !escape_period);
2320 :
2321 3652 : initStringInfo(&buf);
2322 :
2323 21952 : for (p = ident; *p; p += pg_mblen(p))
2324 : {
2325 18300 : if (*p == ':' && (p == ident || fully_escaped))
2326 14 : appendStringInfoString(&buf, "_x003A_");
2327 18286 : else if (*p == '_' && *(p + 1) == 'x')
2328 6 : appendStringInfoString(&buf, "_x005F_");
2329 21568 : else if (fully_escaped && p == ident &&
2330 3288 : pg_strncasecmp(p, "xml", 3) == 0)
2331 : {
2332 0 : if (*p == 'x')
2333 0 : appendStringInfoString(&buf, "_x0078_");
2334 : else
2335 0 : appendStringInfoString(&buf, "_x0058_");
2336 : }
2337 18280 : else if (escape_period && *p == '.')
2338 0 : appendStringInfoString(&buf, "_x002E_");
2339 : else
2340 : {
2341 18280 : pg_wchar u = sqlchar_to_unicode(p);
2342 :
2343 36560 : if ((p == ident)
2344 3638 : ? !is_valid_xml_namefirst(u)
2345 14642 : : !is_valid_xml_namechar(u))
2346 18 : appendStringInfo(&buf, "_x%04X_", (unsigned int) u);
2347 : else
2348 18262 : appendBinaryStringInfo(&buf, p, pg_mblen(p));
2349 : }
2350 : }
2351 :
2352 3652 : return buf.data;
2353 : #else /* not USE_LIBXML */
2354 : NO_XML_SUPPORT();
2355 : return NULL;
2356 : #endif /* not USE_LIBXML */
2357 : }
2358 :
2359 :
2360 : /*
2361 : * Map XML name to SQL identifier; see SQL/XML:2008 section 9.3.
2362 : */
2363 : char *
2364 128 : map_xml_name_to_sql_identifier(const char *name)
2365 : {
2366 : StringInfoData buf;
2367 : const char *p;
2368 :
2369 128 : initStringInfo(&buf);
2370 :
2371 704 : for (p = name; *p; p += pg_mblen(p))
2372 : {
2373 576 : if (*p == '_' && *(p + 1) == 'x'
2374 16 : && isxdigit((unsigned char) *(p + 2))
2375 16 : && isxdigit((unsigned char) *(p + 3))
2376 16 : && isxdigit((unsigned char) *(p + 4))
2377 16 : && isxdigit((unsigned char) *(p + 5))
2378 16 : && *(p + 6) == '_')
2379 16 : {
2380 : char cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1];
2381 : unsigned int u;
2382 :
2383 16 : sscanf(p + 2, "%X", &u);
2384 16 : pg_unicode_to_server(u, (unsigned char *) cbuf);
2385 16 : appendStringInfoString(&buf, cbuf);
2386 16 : p += 6;
2387 : }
2388 : else
2389 560 : appendBinaryStringInfo(&buf, p, pg_mblen(p));
2390 : }
2391 :
2392 128 : return buf.data;
2393 : }
2394 :
2395 : /*
2396 : * Map SQL value to XML value; see SQL/XML:2008 section 9.8.
2397 : *
2398 : * When xml_escape_strings is true, then certain characters in string
2399 : * values are replaced by entity references (< etc.), as specified
2400 : * in SQL/XML:2008 section 9.8 GR 9) a) iii). This is normally what is
2401 : * wanted. The false case is mainly useful when the resulting value
2402 : * is used with xmlTextWriterWriteAttribute() to write out an
2403 : * attribute, because that function does the escaping itself.
2404 : */
2405 : char *
2406 130384 : map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings)
2407 : {
2408 130384 : if (type_is_array_domain(type))
2409 : {
2410 : ArrayType *array;
2411 : Oid elmtype;
2412 : int16 elmlen;
2413 : bool elmbyval;
2414 : char elmalign;
2415 : int num_elems;
2416 : Datum *elem_values;
2417 : bool *elem_nulls;
2418 : StringInfoData buf;
2419 : int i;
2420 :
2421 6 : array = DatumGetArrayTypeP(value);
2422 6 : elmtype = ARR_ELEMTYPE(array);
2423 6 : get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
2424 :
2425 6 : deconstruct_array(array, elmtype,
2426 : elmlen, elmbyval, elmalign,
2427 : &elem_values, &elem_nulls,
2428 : &num_elems);
2429 :
2430 6 : initStringInfo(&buf);
2431 :
2432 24 : for (i = 0; i < num_elems; i++)
2433 : {
2434 18 : if (elem_nulls[i])
2435 0 : continue;
2436 18 : appendStringInfoString(&buf, "<element>");
2437 18 : appendStringInfoString(&buf,
2438 18 : map_sql_value_to_xml_value(elem_values[i],
2439 : elmtype, true));
2440 18 : appendStringInfoString(&buf, "</element>");
2441 : }
2442 :
2443 6 : pfree(elem_values);
2444 6 : pfree(elem_nulls);
2445 :
2446 6 : return buf.data;
2447 : }
2448 : else
2449 : {
2450 : Oid typeOut;
2451 : bool isvarlena;
2452 : char *str;
2453 :
2454 : /*
2455 : * Flatten domains; the special-case treatments below should apply to,
2456 : * eg, domains over boolean not just boolean.
2457 : */
2458 130378 : type = getBaseType(type);
2459 :
2460 : /*
2461 : * Special XSD formatting for some data types
2462 : */
2463 130378 : switch (type)
2464 : {
2465 66 : case BOOLOID:
2466 66 : if (DatumGetBool(value))
2467 60 : return "true";
2468 : else
2469 6 : return "false";
2470 :
2471 48 : case DATEOID:
2472 : {
2473 : DateADT date;
2474 : struct pg_tm tm;
2475 : char buf[MAXDATELEN + 1];
2476 :
2477 48 : date = DatumGetDateADT(value);
2478 : /* XSD doesn't support infinite values */
2479 48 : if (DATE_NOT_FINITE(date))
2480 0 : ereport(ERROR,
2481 : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2482 : errmsg("date out of range"),
2483 : errdetail("XML does not support infinite date values.")));
2484 48 : j2date(date + POSTGRES_EPOCH_JDATE,
2485 : &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
2486 48 : EncodeDateOnly(&tm, USE_XSD_DATES, buf);
2487 :
2488 48 : return pstrdup(buf);
2489 : }
2490 :
2491 36 : case TIMESTAMPOID:
2492 : {
2493 : Timestamp timestamp;
2494 : struct pg_tm tm;
2495 : fsec_t fsec;
2496 : char buf[MAXDATELEN + 1];
2497 :
2498 36 : timestamp = DatumGetTimestamp(value);
2499 :
2500 : /* XSD doesn't support infinite values */
2501 36 : if (TIMESTAMP_NOT_FINITE(timestamp))
2502 6 : ereport(ERROR,
2503 : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2504 : errmsg("timestamp out of range"),
2505 : errdetail("XML does not support infinite timestamp values.")));
2506 30 : else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
2507 30 : EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
2508 : else
2509 0 : ereport(ERROR,
2510 : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2511 : errmsg("timestamp out of range")));
2512 :
2513 30 : return pstrdup(buf);
2514 : }
2515 :
2516 24 : case TIMESTAMPTZOID:
2517 : {
2518 : TimestampTz timestamp;
2519 : struct pg_tm tm;
2520 : int tz;
2521 : fsec_t fsec;
2522 24 : const char *tzn = NULL;
2523 : char buf[MAXDATELEN + 1];
2524 :
2525 24 : timestamp = DatumGetTimestamp(value);
2526 :
2527 : /* XSD doesn't support infinite values */
2528 24 : if (TIMESTAMP_NOT_FINITE(timestamp))
2529 0 : ereport(ERROR,
2530 : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2531 : errmsg("timestamp out of range"),
2532 : errdetail("XML does not support infinite timestamp values.")));
2533 24 : else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
2534 24 : EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
2535 : else
2536 0 : ereport(ERROR,
2537 : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2538 : errmsg("timestamp out of range")));
2539 :
2540 24 : return pstrdup(buf);
2541 : }
2542 :
2543 : #ifdef USE_LIBXML
2544 36 : case BYTEAOID:
2545 : {
2546 36 : bytea *bstr = DatumGetByteaPP(value);
2547 : PgXmlErrorContext *xmlerrcxt;
2548 36 : volatile xmlBufferPtr buf = NULL;
2549 36 : volatile xmlTextWriterPtr writer = NULL;
2550 : char *result;
2551 :
2552 36 : xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
2553 :
2554 36 : PG_TRY();
2555 : {
2556 36 : buf = xmlBufferCreate();
2557 36 : if (buf == NULL || xmlerrcxt->err_occurred)
2558 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
2559 : "could not allocate xmlBuffer");
2560 36 : writer = xmlNewTextWriterMemory(buf, 0);
2561 36 : if (writer == NULL || xmlerrcxt->err_occurred)
2562 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
2563 : "could not allocate xmlTextWriter");
2564 :
2565 36 : if (xmlbinary == XMLBINARY_BASE64)
2566 30 : xmlTextWriterWriteBase64(writer, VARDATA_ANY(bstr),
2567 30 : 0, VARSIZE_ANY_EXHDR(bstr));
2568 : else
2569 6 : xmlTextWriterWriteBinHex(writer, VARDATA_ANY(bstr),
2570 6 : 0, VARSIZE_ANY_EXHDR(bstr));
2571 :
2572 : /* we MUST do this now to flush data out to the buffer */
2573 36 : xmlFreeTextWriter(writer);
2574 36 : writer = NULL;
2575 :
2576 36 : result = pstrdup((const char *) xmlBufferContent(buf));
2577 : }
2578 0 : PG_CATCH();
2579 : {
2580 0 : if (writer)
2581 0 : xmlFreeTextWriter(writer);
2582 0 : if (buf)
2583 0 : xmlBufferFree(buf);
2584 :
2585 0 : pg_xml_done(xmlerrcxt, true);
2586 :
2587 0 : PG_RE_THROW();
2588 : }
2589 36 : PG_END_TRY();
2590 :
2591 36 : xmlBufferFree(buf);
2592 :
2593 36 : pg_xml_done(xmlerrcxt, false);
2594 :
2595 36 : return result;
2596 : }
2597 : #endif /* USE_LIBXML */
2598 :
2599 : }
2600 :
2601 : /*
2602 : * otherwise, just use the type's native text representation
2603 : */
2604 130168 : getTypeOutputInfo(type, &typeOut, &isvarlena);
2605 130168 : str = OidOutputFunctionCall(typeOut, value);
2606 :
2607 : /* ... exactly as-is for XML, and when escaping is not wanted */
2608 130168 : if (type == XMLOID || !xml_escape_strings)
2609 21634 : return str;
2610 :
2611 : /* otherwise, translate special characters as needed */
2612 108534 : return escape_xml(str);
2613 : }
2614 : }
2615 :
2616 :
2617 : /*
2618 : * Escape characters in text that have special meanings in XML.
2619 : *
2620 : * Returns a palloc'd string.
2621 : *
2622 : * NB: this is intentionally not dependent on libxml.
2623 : */
2624 : char *
2625 108780 : escape_xml(const char *str)
2626 : {
2627 : StringInfoData buf;
2628 : const char *p;
2629 :
2630 108780 : initStringInfo(&buf);
2631 672500 : for (p = str; *p; p++)
2632 : {
2633 563720 : switch (*p)
2634 : {
2635 0 : case '&':
2636 0 : appendStringInfoString(&buf, "&");
2637 0 : break;
2638 36 : case '<':
2639 36 : appendStringInfoString(&buf, "<");
2640 36 : break;
2641 24 : case '>':
2642 24 : appendStringInfoString(&buf, ">");
2643 24 : break;
2644 0 : case '\r':
2645 0 : appendStringInfoString(&buf, "
");
2646 0 : break;
2647 563660 : default:
2648 563660 : appendStringInfoCharMacro(&buf, *p);
2649 563660 : break;
2650 : }
2651 : }
2652 108780 : return buf.data;
2653 : }
2654 :
2655 :
2656 : static char *
2657 24 : _SPI_strdup(const char *s)
2658 : {
2659 24 : size_t len = strlen(s) + 1;
2660 24 : char *ret = SPI_palloc(len);
2661 :
2662 24 : memcpy(ret, s, len);
2663 24 : return ret;
2664 : }
2665 :
2666 :
2667 : /*
2668 : * SQL to XML mapping functions
2669 : *
2670 : * What follows below was at one point intentionally organized so that
2671 : * you can read along in the SQL/XML standard. The functions are
2672 : * mostly split up the way the clauses lay out in the standards
2673 : * document, and the identifiers are also aligned with the standard
2674 : * text. Unfortunately, SQL/XML:2006 reordered the clauses
2675 : * differently than SQL/XML:2003, so the order below doesn't make much
2676 : * sense anymore.
2677 : *
2678 : * There are many things going on there:
2679 : *
2680 : * There are two kinds of mappings: Mapping SQL data (table contents)
2681 : * to XML documents, and mapping SQL structure (the "schema") to XML
2682 : * Schema. And there are functions that do both at the same time.
2683 : *
2684 : * Then you can map a database, a schema, or a table, each in both
2685 : * ways. This breaks down recursively: Mapping a database invokes
2686 : * mapping schemas, which invokes mapping tables, which invokes
2687 : * mapping rows, which invokes mapping columns, although you can't
2688 : * call the last two from the outside. Because of this, there are a
2689 : * number of xyz_internal() functions which are to be called both from
2690 : * the function manager wrapper and from some upper layer in a
2691 : * recursive call.
2692 : *
2693 : * See the documentation about what the common function arguments
2694 : * nulls, tableforest, and targetns mean.
2695 : *
2696 : * Some style guidelines for XML output: Use double quotes for quoting
2697 : * XML attributes. Indent XML elements by two spaces, but remember
2698 : * that a lot of code is called recursively at different levels, so
2699 : * it's better not to indent rather than create output that indents
2700 : * and outdents weirdly. Add newlines to make the output look nice.
2701 : */
2702 :
2703 :
2704 : /*
2705 : * Visibility of objects for XML mappings; see SQL/XML:2008 section
2706 : * 4.10.8.
2707 : */
2708 :
2709 : /*
2710 : * Given a query, which must return type oid as first column, produce
2711 : * a list of Oids with the query results.
2712 : */
2713 : static List *
2714 36 : query_to_oid_list(const char *query)
2715 : {
2716 : uint64 i;
2717 36 : List *list = NIL;
2718 : int spi_result;
2719 :
2720 36 : spi_result = SPI_execute(query, true, 0);
2721 36 : if (spi_result != SPI_OK_SELECT)
2722 0 : elog(ERROR, "SPI_execute returned %s for %s",
2723 : SPI_result_code_string(spi_result), query);
2724 :
2725 108 : for (i = 0; i < SPI_processed; i++)
2726 : {
2727 : Datum oid;
2728 : bool isnull;
2729 :
2730 72 : oid = SPI_getbinval(SPI_tuptable->vals[i],
2731 72 : SPI_tuptable->tupdesc,
2732 : 1,
2733 : &isnull);
2734 72 : if (!isnull)
2735 72 : list = lappend_oid(list, DatumGetObjectId(oid));
2736 : }
2737 :
2738 36 : return list;
2739 : }
2740 :
2741 :
2742 : static List *
2743 36 : schema_get_xml_visible_tables(Oid nspid)
2744 : {
2745 : StringInfoData query;
2746 :
2747 36 : initStringInfo(&query);
2748 36 : appendStringInfo(&query, "SELECT oid FROM pg_catalog.pg_class"
2749 : " WHERE relnamespace = %u AND relkind IN ("
2750 : CppAsString2(RELKIND_RELATION) ","
2751 : CppAsString2(RELKIND_MATVIEW) ","
2752 : CppAsString2(RELKIND_VIEW) ")"
2753 : " AND pg_catalog.has_table_privilege (oid, 'SELECT')"
2754 : " ORDER BY relname;", nspid);
2755 :
2756 36 : return query_to_oid_list(query.data);
2757 : }
2758 :
2759 :
2760 : /*
2761 : * Including the system schemas is probably not useful for a database
2762 : * mapping.
2763 : */
2764 : #define XML_VISIBLE_SCHEMAS_EXCLUDE "(nspname ~ '^pg_' OR nspname = 'information_schema')"
2765 :
2766 : #define XML_VISIBLE_SCHEMAS "SELECT oid FROM pg_catalog.pg_namespace WHERE pg_catalog.has_schema_privilege (oid, 'USAGE') AND NOT " XML_VISIBLE_SCHEMAS_EXCLUDE
2767 :
2768 :
2769 : static List *
2770 0 : database_get_xml_visible_schemas(void)
2771 : {
2772 0 : return query_to_oid_list(XML_VISIBLE_SCHEMAS " ORDER BY nspname;");
2773 : }
2774 :
2775 :
2776 : static List *
2777 0 : database_get_xml_visible_tables(void)
2778 : {
2779 : /* At the moment there is no order required here. */
2780 0 : return query_to_oid_list("SELECT oid FROM pg_catalog.pg_class"
2781 : " WHERE relkind IN ("
2782 : CppAsString2(RELKIND_RELATION) ","
2783 : CppAsString2(RELKIND_MATVIEW) ","
2784 : CppAsString2(RELKIND_VIEW) ")"
2785 : " AND pg_catalog.has_table_privilege(pg_class.oid, 'SELECT')"
2786 : " AND relnamespace IN (" XML_VISIBLE_SCHEMAS ");");
2787 : }
2788 :
2789 :
2790 : /*
2791 : * Map SQL table to XML and/or XML Schema document; see SQL/XML:2008
2792 : * section 9.11.
2793 : */
2794 :
2795 : static StringInfo
2796 96 : table_to_xml_internal(Oid relid,
2797 : const char *xmlschema, bool nulls, bool tableforest,
2798 : const char *targetns, bool top_level)
2799 : {
2800 : StringInfoData query;
2801 :
2802 96 : initStringInfo(&query);
2803 96 : appendStringInfo(&query, "SELECT * FROM %s",
2804 : DatumGetCString(DirectFunctionCall1(regclassout,
2805 : ObjectIdGetDatum(relid))));
2806 96 : return query_to_xml_internal(query.data, get_rel_name(relid),
2807 : xmlschema, nulls, tableforest,
2808 : targetns, top_level);
2809 : }
2810 :
2811 :
2812 : Datum
2813 36 : table_to_xml(PG_FUNCTION_ARGS)
2814 : {
2815 36 : Oid relid = PG_GETARG_OID(0);
2816 36 : bool nulls = PG_GETARG_BOOL(1);
2817 36 : bool tableforest = PG_GETARG_BOOL(2);
2818 36 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2819 :
2820 36 : PG_RETURN_XML_P(stringinfo_to_xmltype(table_to_xml_internal(relid, NULL,
2821 : nulls, tableforest,
2822 : targetns, true)));
2823 : }
2824 :
2825 :
2826 : Datum
2827 10 : query_to_xml(PG_FUNCTION_ARGS)
2828 : {
2829 10 : char *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
2830 10 : bool nulls = PG_GETARG_BOOL(1);
2831 10 : bool tableforest = PG_GETARG_BOOL(2);
2832 10 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2833 :
2834 10 : PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL,
2835 : NULL, nulls, tableforest,
2836 : targetns, true)));
2837 : }
2838 :
2839 :
2840 : Datum
2841 12 : cursor_to_xml(PG_FUNCTION_ARGS)
2842 : {
2843 12 : char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
2844 12 : int32 count = PG_GETARG_INT32(1);
2845 12 : bool nulls = PG_GETARG_BOOL(2);
2846 12 : bool tableforest = PG_GETARG_BOOL(3);
2847 12 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(4));
2848 :
2849 : StringInfoData result;
2850 : Portal portal;
2851 : uint64 i;
2852 :
2853 12 : initStringInfo(&result);
2854 :
2855 12 : if (!tableforest)
2856 : {
2857 6 : xmldata_root_element_start(&result, "table", NULL, targetns, true);
2858 6 : appendStringInfoChar(&result, '\n');
2859 : }
2860 :
2861 12 : SPI_connect();
2862 12 : portal = SPI_cursor_find(name);
2863 12 : if (portal == NULL)
2864 0 : ereport(ERROR,
2865 : (errcode(ERRCODE_UNDEFINED_CURSOR),
2866 : errmsg("cursor \"%s\" does not exist", name)));
2867 :
2868 12 : SPI_cursor_fetch(portal, true, count);
2869 48 : for (i = 0; i < SPI_processed; i++)
2870 36 : SPI_sql_row_to_xmlelement(i, &result, NULL, nulls,
2871 : tableforest, targetns, true);
2872 :
2873 12 : SPI_finish();
2874 :
2875 12 : if (!tableforest)
2876 6 : xmldata_root_element_end(&result, "table");
2877 :
2878 12 : PG_RETURN_XML_P(stringinfo_to_xmltype(&result));
2879 : }
2880 :
2881 :
2882 : /*
2883 : * Write the start tag of the root element of a data mapping.
2884 : *
2885 : * top_level means that this is the very top level of the eventual
2886 : * output. For example, when the user calls table_to_xml, then a call
2887 : * with a table name to this function is the top level. When the user
2888 : * calls database_to_xml, then a call with a schema name to this
2889 : * function is not the top level. If top_level is false, then the XML
2890 : * namespace declarations are omitted, because they supposedly already
2891 : * appeared earlier in the output. Repeating them is not wrong, but
2892 : * it looks ugly.
2893 : */
2894 : static void
2895 238 : xmldata_root_element_start(StringInfo result, const char *eltname,
2896 : const char *xmlschema, const char *targetns,
2897 : bool top_level)
2898 : {
2899 : /* This isn't really wrong but currently makes no sense. */
2900 : Assert(top_level || !xmlschema);
2901 :
2902 238 : appendStringInfo(result, "<%s", eltname);
2903 238 : if (top_level)
2904 : {
2905 178 : appendStringInfoString(result, " xmlns:xsi=\"" NAMESPACE_XSI "\"");
2906 178 : if (strlen(targetns) > 0)
2907 30 : appendStringInfo(result, " xmlns=\"%s\"", targetns);
2908 : }
2909 238 : if (xmlschema)
2910 : {
2911 : /* FIXME: better targets */
2912 18 : if (strlen(targetns) > 0)
2913 6 : appendStringInfo(result, " xsi:schemaLocation=\"%s #\"", targetns);
2914 : else
2915 12 : appendStringInfoString(result, " xsi:noNamespaceSchemaLocation=\"#\"");
2916 : }
2917 238 : appendStringInfoString(result, ">\n");
2918 238 : }
2919 :
2920 :
2921 : static void
2922 238 : xmldata_root_element_end(StringInfo result, const char *eltname)
2923 : {
2924 238 : appendStringInfo(result, "</%s>\n", eltname);
2925 238 : }
2926 :
2927 :
2928 : static StringInfo
2929 112 : query_to_xml_internal(const char *query, char *tablename,
2930 : const char *xmlschema, bool nulls, bool tableforest,
2931 : const char *targetns, bool top_level)
2932 : {
2933 : StringInfo result;
2934 : char *xmltn;
2935 : uint64 i;
2936 :
2937 112 : if (tablename)
2938 96 : xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
2939 : else
2940 16 : xmltn = "table";
2941 :
2942 112 : result = makeStringInfo();
2943 :
2944 112 : SPI_connect();
2945 112 : if (SPI_execute(query, true, 0) != SPI_OK_SELECT)
2946 0 : ereport(ERROR,
2947 : (errcode(ERRCODE_DATA_EXCEPTION),
2948 : errmsg("invalid query")));
2949 :
2950 112 : if (!tableforest)
2951 : {
2952 52 : xmldata_root_element_start(result, xmltn, xmlschema,
2953 : targetns, top_level);
2954 52 : appendStringInfoChar(result, '\n');
2955 : }
2956 :
2957 112 : if (xmlschema)
2958 30 : appendStringInfo(result, "%s\n\n", xmlschema);
2959 :
2960 388 : for (i = 0; i < SPI_processed; i++)
2961 276 : SPI_sql_row_to_xmlelement(i, result, tablename, nulls,
2962 : tableforest, targetns, top_level);
2963 :
2964 112 : if (!tableforest)
2965 52 : xmldata_root_element_end(result, xmltn);
2966 :
2967 112 : SPI_finish();
2968 :
2969 112 : return result;
2970 : }
2971 :
2972 :
2973 : Datum
2974 30 : table_to_xmlschema(PG_FUNCTION_ARGS)
2975 : {
2976 30 : Oid relid = PG_GETARG_OID(0);
2977 30 : bool nulls = PG_GETARG_BOOL(1);
2978 30 : bool tableforest = PG_GETARG_BOOL(2);
2979 30 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2980 : const char *result;
2981 : Relation rel;
2982 :
2983 30 : rel = table_open(relid, AccessShareLock);
2984 30 : result = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls,
2985 : tableforest, targetns);
2986 30 : table_close(rel, NoLock);
2987 :
2988 30 : PG_RETURN_XML_P(cstring_to_xmltype(result));
2989 : }
2990 :
2991 :
2992 : Datum
2993 6 : query_to_xmlschema(PG_FUNCTION_ARGS)
2994 : {
2995 6 : char *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
2996 6 : bool nulls = PG_GETARG_BOOL(1);
2997 6 : bool tableforest = PG_GETARG_BOOL(2);
2998 6 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2999 : const char *result;
3000 : SPIPlanPtr plan;
3001 : Portal portal;
3002 :
3003 6 : SPI_connect();
3004 :
3005 6 : if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
3006 0 : elog(ERROR, "SPI_prepare(\"%s\") failed", query);
3007 :
3008 6 : if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
3009 0 : elog(ERROR, "SPI_cursor_open(\"%s\") failed", query);
3010 :
3011 6 : result = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
3012 : InvalidOid, nulls,
3013 : tableforest, targetns));
3014 6 : SPI_cursor_close(portal);
3015 6 : SPI_finish();
3016 :
3017 6 : PG_RETURN_XML_P(cstring_to_xmltype(result));
3018 : }
3019 :
3020 :
3021 : Datum
3022 12 : cursor_to_xmlschema(PG_FUNCTION_ARGS)
3023 : {
3024 12 : char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
3025 12 : bool nulls = PG_GETARG_BOOL(1);
3026 12 : bool tableforest = PG_GETARG_BOOL(2);
3027 12 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
3028 : const char *xmlschema;
3029 : Portal portal;
3030 :
3031 12 : SPI_connect();
3032 12 : portal = SPI_cursor_find(name);
3033 12 : if (portal == NULL)
3034 0 : ereport(ERROR,
3035 : (errcode(ERRCODE_UNDEFINED_CURSOR),
3036 : errmsg("cursor \"%s\" does not exist", name)));
3037 12 : if (portal->tupDesc == NULL)
3038 0 : ereport(ERROR,
3039 : (errcode(ERRCODE_INVALID_CURSOR_STATE),
3040 : errmsg("portal \"%s\" does not return tuples", name)));
3041 :
3042 12 : xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
3043 : InvalidOid, nulls,
3044 : tableforest, targetns));
3045 12 : SPI_finish();
3046 :
3047 12 : PG_RETURN_XML_P(cstring_to_xmltype(xmlschema));
3048 : }
3049 :
3050 :
3051 : Datum
3052 24 : table_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
3053 : {
3054 24 : Oid relid = PG_GETARG_OID(0);
3055 24 : bool nulls = PG_GETARG_BOOL(1);
3056 24 : bool tableforest = PG_GETARG_BOOL(2);
3057 24 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
3058 : Relation rel;
3059 : const char *xmlschema;
3060 :
3061 24 : rel = table_open(relid, AccessShareLock);
3062 24 : xmlschema = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls,
3063 : tableforest, targetns);
3064 24 : table_close(rel, NoLock);
3065 :
3066 24 : PG_RETURN_XML_P(stringinfo_to_xmltype(table_to_xml_internal(relid,
3067 : xmlschema, nulls, tableforest,
3068 : targetns, true)));
3069 : }
3070 :
3071 :
3072 : Datum
3073 6 : query_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
3074 : {
3075 6 : char *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
3076 6 : bool nulls = PG_GETARG_BOOL(1);
3077 6 : bool tableforest = PG_GETARG_BOOL(2);
3078 6 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
3079 :
3080 : const char *xmlschema;
3081 : SPIPlanPtr plan;
3082 : Portal portal;
3083 :
3084 6 : SPI_connect();
3085 :
3086 6 : if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
3087 0 : elog(ERROR, "SPI_prepare(\"%s\") failed", query);
3088 :
3089 6 : if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
3090 0 : elog(ERROR, "SPI_cursor_open(\"%s\") failed", query);
3091 :
3092 6 : xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
3093 : InvalidOid, nulls, tableforest, targetns));
3094 6 : SPI_cursor_close(portal);
3095 6 : SPI_finish();
3096 :
3097 6 : PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL,
3098 : xmlschema, nulls, tableforest,
3099 : targetns, true)));
3100 : }
3101 :
3102 :
3103 : /*
3104 : * Map SQL schema to XML and/or XML Schema document; see SQL/XML:2008
3105 : * sections 9.13, 9.14.
3106 : */
3107 :
3108 : static StringInfo
3109 18 : schema_to_xml_internal(Oid nspid, const char *xmlschema, bool nulls,
3110 : bool tableforest, const char *targetns, bool top_level)
3111 : {
3112 : StringInfo result;
3113 : char *xmlsn;
3114 : List *relid_list;
3115 : ListCell *cell;
3116 :
3117 18 : xmlsn = map_sql_identifier_to_xml_name(get_namespace_name(nspid),
3118 : true, false);
3119 18 : result = makeStringInfo();
3120 :
3121 18 : xmldata_root_element_start(result, xmlsn, xmlschema, targetns, top_level);
3122 18 : appendStringInfoChar(result, '\n');
3123 :
3124 18 : if (xmlschema)
3125 6 : appendStringInfo(result, "%s\n\n", xmlschema);
3126 :
3127 18 : SPI_connect();
3128 :
3129 18 : relid_list = schema_get_xml_visible_tables(nspid);
3130 :
3131 54 : foreach(cell, relid_list)
3132 : {
3133 36 : Oid relid = lfirst_oid(cell);
3134 : StringInfo subres;
3135 :
3136 36 : subres = table_to_xml_internal(relid, NULL, nulls, tableforest,
3137 : targetns, false);
3138 :
3139 36 : appendBinaryStringInfo(result, subres->data, subres->len);
3140 36 : appendStringInfoChar(result, '\n');
3141 : }
3142 :
3143 18 : SPI_finish();
3144 :
3145 18 : xmldata_root_element_end(result, xmlsn);
3146 :
3147 18 : return result;
3148 : }
3149 :
3150 :
3151 : Datum
3152 12 : schema_to_xml(PG_FUNCTION_ARGS)
3153 : {
3154 12 : Name name = PG_GETARG_NAME(0);
3155 12 : bool nulls = PG_GETARG_BOOL(1);
3156 12 : bool tableforest = PG_GETARG_BOOL(2);
3157 12 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
3158 :
3159 : char *schemaname;
3160 : Oid nspid;
3161 :
3162 12 : schemaname = NameStr(*name);
3163 12 : nspid = LookupExplicitNamespace(schemaname, false);
3164 :
3165 12 : PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xml_internal(nspid, NULL,
3166 : nulls, tableforest, targetns, true)));
3167 : }
3168 :
3169 :
3170 : /*
3171 : * Write the start element of the root element of an XML Schema mapping.
3172 : */
3173 : static void
3174 96 : xsd_schema_element_start(StringInfo result, const char *targetns)
3175 : {
3176 96 : appendStringInfoString(result,
3177 : "<xsd:schema\n"
3178 : " xmlns:xsd=\"" NAMESPACE_XSD "\"");
3179 96 : if (strlen(targetns) > 0)
3180 18 : appendStringInfo(result,
3181 : "\n"
3182 : " targetNamespace=\"%s\"\n"
3183 : " elementFormDefault=\"qualified\"",
3184 : targetns);
3185 96 : appendStringInfoString(result,
3186 : ">\n\n");
3187 96 : }
3188 :
3189 :
3190 : static void
3191 96 : xsd_schema_element_end(StringInfo result)
3192 : {
3193 96 : appendStringInfoString(result, "</xsd:schema>");
3194 96 : }
3195 :
3196 :
3197 : static StringInfo
3198 18 : schema_to_xmlschema_internal(const char *schemaname, bool nulls,
3199 : bool tableforest, const char *targetns)
3200 : {
3201 : Oid nspid;
3202 : List *relid_list;
3203 : List *tupdesc_list;
3204 : ListCell *cell;
3205 : StringInfo result;
3206 :
3207 18 : result = makeStringInfo();
3208 :
3209 18 : nspid = LookupExplicitNamespace(schemaname, false);
3210 :
3211 18 : xsd_schema_element_start(result, targetns);
3212 :
3213 18 : SPI_connect();
3214 :
3215 18 : relid_list = schema_get_xml_visible_tables(nspid);
3216 :
3217 18 : tupdesc_list = NIL;
3218 54 : foreach(cell, relid_list)
3219 : {
3220 : Relation rel;
3221 :
3222 36 : rel = table_open(lfirst_oid(cell), AccessShareLock);
3223 36 : tupdesc_list = lappend(tupdesc_list, CreateTupleDescCopy(rel->rd_att));
3224 36 : table_close(rel, NoLock);
3225 : }
3226 :
3227 18 : appendStringInfoString(result,
3228 : map_sql_typecoll_to_xmlschema_types(tupdesc_list));
3229 :
3230 18 : appendStringInfoString(result,
3231 : map_sql_schema_to_xmlschema_types(nspid, relid_list,
3232 : nulls, tableforest, targetns));
3233 :
3234 18 : xsd_schema_element_end(result);
3235 :
3236 18 : SPI_finish();
3237 :
3238 18 : return result;
3239 : }
3240 :
3241 :
3242 : Datum
3243 12 : schema_to_xmlschema(PG_FUNCTION_ARGS)
3244 : {
3245 12 : Name name = PG_GETARG_NAME(0);
3246 12 : bool nulls = PG_GETARG_BOOL(1);
3247 12 : bool tableforest = PG_GETARG_BOOL(2);
3248 12 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
3249 :
3250 12 : PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xmlschema_internal(NameStr(*name),
3251 : nulls, tableforest, targetns)));
3252 : }
3253 :
3254 :
3255 : Datum
3256 6 : schema_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
3257 : {
3258 6 : Name name = PG_GETARG_NAME(0);
3259 6 : bool nulls = PG_GETARG_BOOL(1);
3260 6 : bool tableforest = PG_GETARG_BOOL(2);
3261 6 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
3262 : char *schemaname;
3263 : Oid nspid;
3264 : StringInfo xmlschema;
3265 :
3266 6 : schemaname = NameStr(*name);
3267 6 : nspid = LookupExplicitNamespace(schemaname, false);
3268 :
3269 6 : xmlschema = schema_to_xmlschema_internal(schemaname, nulls,
3270 : tableforest, targetns);
3271 :
3272 6 : PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xml_internal(nspid,
3273 : xmlschema->data, nulls,
3274 : tableforest, targetns, true)));
3275 : }
3276 :
3277 :
3278 : /*
3279 : * Map SQL database to XML and/or XML Schema document; see SQL/XML:2008
3280 : * sections 9.16, 9.17.
3281 : */
3282 :
3283 : static StringInfo
3284 0 : database_to_xml_internal(const char *xmlschema, bool nulls,
3285 : bool tableforest, const char *targetns)
3286 : {
3287 : StringInfo result;
3288 : List *nspid_list;
3289 : ListCell *cell;
3290 : char *xmlcn;
3291 :
3292 0 : xmlcn = map_sql_identifier_to_xml_name(get_database_name(MyDatabaseId),
3293 : true, false);
3294 0 : result = makeStringInfo();
3295 :
3296 0 : xmldata_root_element_start(result, xmlcn, xmlschema, targetns, true);
3297 0 : appendStringInfoChar(result, '\n');
3298 :
3299 0 : if (xmlschema)
3300 0 : appendStringInfo(result, "%s\n\n", xmlschema);
3301 :
3302 0 : SPI_connect();
3303 :
3304 0 : nspid_list = database_get_xml_visible_schemas();
3305 :
3306 0 : foreach(cell, nspid_list)
3307 : {
3308 0 : Oid nspid = lfirst_oid(cell);
3309 : StringInfo subres;
3310 :
3311 0 : subres = schema_to_xml_internal(nspid, NULL, nulls,
3312 : tableforest, targetns, false);
3313 :
3314 0 : appendBinaryStringInfo(result, subres->data, subres->len);
3315 0 : appendStringInfoChar(result, '\n');
3316 : }
3317 :
3318 0 : SPI_finish();
3319 :
3320 0 : xmldata_root_element_end(result, xmlcn);
3321 :
3322 0 : return result;
3323 : }
3324 :
3325 :
3326 : Datum
3327 0 : database_to_xml(PG_FUNCTION_ARGS)
3328 : {
3329 0 : bool nulls = PG_GETARG_BOOL(0);
3330 0 : bool tableforest = PG_GETARG_BOOL(1);
3331 0 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
3332 :
3333 0 : PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xml_internal(NULL, nulls,
3334 : tableforest, targetns)));
3335 : }
3336 :
3337 :
3338 : static StringInfo
3339 0 : database_to_xmlschema_internal(bool nulls, bool tableforest,
3340 : const char *targetns)
3341 : {
3342 : List *relid_list;
3343 : List *nspid_list;
3344 : List *tupdesc_list;
3345 : ListCell *cell;
3346 : StringInfo result;
3347 :
3348 0 : result = makeStringInfo();
3349 :
3350 0 : xsd_schema_element_start(result, targetns);
3351 :
3352 0 : SPI_connect();
3353 :
3354 0 : relid_list = database_get_xml_visible_tables();
3355 0 : nspid_list = database_get_xml_visible_schemas();
3356 :
3357 0 : tupdesc_list = NIL;
3358 0 : foreach(cell, relid_list)
3359 : {
3360 : Relation rel;
3361 :
3362 0 : rel = table_open(lfirst_oid(cell), AccessShareLock);
3363 0 : tupdesc_list = lappend(tupdesc_list, CreateTupleDescCopy(rel->rd_att));
3364 0 : table_close(rel, NoLock);
3365 : }
3366 :
3367 0 : appendStringInfoString(result,
3368 : map_sql_typecoll_to_xmlschema_types(tupdesc_list));
3369 :
3370 0 : appendStringInfoString(result,
3371 : map_sql_catalog_to_xmlschema_types(nspid_list, nulls, tableforest, targetns));
3372 :
3373 0 : xsd_schema_element_end(result);
3374 :
3375 0 : SPI_finish();
3376 :
3377 0 : return result;
3378 : }
3379 :
3380 :
3381 : Datum
3382 0 : database_to_xmlschema(PG_FUNCTION_ARGS)
3383 : {
3384 0 : bool nulls = PG_GETARG_BOOL(0);
3385 0 : bool tableforest = PG_GETARG_BOOL(1);
3386 0 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
3387 :
3388 0 : PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xmlschema_internal(nulls,
3389 : tableforest, targetns)));
3390 : }
3391 :
3392 :
3393 : Datum
3394 0 : database_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
3395 : {
3396 0 : bool nulls = PG_GETARG_BOOL(0);
3397 0 : bool tableforest = PG_GETARG_BOOL(1);
3398 0 : const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
3399 : StringInfo xmlschema;
3400 :
3401 0 : xmlschema = database_to_xmlschema_internal(nulls, tableforest, targetns);
3402 :
3403 0 : PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xml_internal(xmlschema->data,
3404 : nulls, tableforest, targetns)));
3405 : }
3406 :
3407 :
3408 : /*
3409 : * Map a multi-part SQL name to an XML name; see SQL/XML:2008 section
3410 : * 9.2.
3411 : */
3412 : static char *
3413 384 : map_multipart_sql_identifier_to_xml_name(const char *a, const char *b, const char *c, const char *d)
3414 : {
3415 : StringInfoData result;
3416 :
3417 384 : initStringInfo(&result);
3418 :
3419 384 : if (a)
3420 384 : appendStringInfoString(&result,
3421 384 : map_sql_identifier_to_xml_name(a, true, true));
3422 384 : if (b)
3423 384 : appendStringInfo(&result, ".%s",
3424 : map_sql_identifier_to_xml_name(b, true, true));
3425 384 : if (c)
3426 384 : appendStringInfo(&result, ".%s",
3427 : map_sql_identifier_to_xml_name(c, true, true));
3428 384 : if (d)
3429 366 : appendStringInfo(&result, ".%s",
3430 : map_sql_identifier_to_xml_name(d, true, true));
3431 :
3432 384 : return result.data;
3433 : }
3434 :
3435 :
3436 : /*
3437 : * Map an SQL table to an XML Schema document; see SQL/XML:2008
3438 : * section 9.11.
3439 : *
3440 : * Map an SQL table to XML Schema data types; see SQL/XML:2008 section
3441 : * 9.9.
3442 : */
3443 : static const char *
3444 78 : map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls,
3445 : bool tableforest, const char *targetns)
3446 : {
3447 : int i;
3448 : char *xmltn;
3449 : char *tabletypename;
3450 : char *rowtypename;
3451 : StringInfoData result;
3452 :
3453 78 : initStringInfo(&result);
3454 :
3455 78 : if (OidIsValid(relid))
3456 : {
3457 : HeapTuple tuple;
3458 : Form_pg_class reltuple;
3459 :
3460 54 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3461 54 : if (!HeapTupleIsValid(tuple))
3462 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
3463 54 : reltuple = (Form_pg_class) GETSTRUCT(tuple);
3464 :
3465 54 : xmltn = map_sql_identifier_to_xml_name(NameStr(reltuple->relname),
3466 : true, false);
3467 :
3468 54 : tabletypename = map_multipart_sql_identifier_to_xml_name("TableType",
3469 54 : get_database_name(MyDatabaseId),
3470 54 : get_namespace_name(reltuple->relnamespace),
3471 54 : NameStr(reltuple->relname));
3472 :
3473 54 : rowtypename = map_multipart_sql_identifier_to_xml_name("RowType",
3474 54 : get_database_name(MyDatabaseId),
3475 54 : get_namespace_name(reltuple->relnamespace),
3476 54 : NameStr(reltuple->relname));
3477 :
3478 54 : ReleaseSysCache(tuple);
3479 : }
3480 : else
3481 : {
3482 24 : if (tableforest)
3483 12 : xmltn = "row";
3484 : else
3485 12 : xmltn = "table";
3486 :
3487 24 : tabletypename = "TableType";
3488 24 : rowtypename = "RowType";
3489 : }
3490 :
3491 78 : xsd_schema_element_start(&result, targetns);
3492 :
3493 78 : appendStringInfoString(&result,
3494 78 : map_sql_typecoll_to_xmlschema_types(list_make1(tupdesc)));
3495 :
3496 78 : appendStringInfo(&result,
3497 : "<xsd:complexType name=\"%s\">\n"
3498 : " <xsd:sequence>\n",
3499 : rowtypename);
3500 :
3501 324 : for (i = 0; i < tupdesc->natts; i++)
3502 : {
3503 246 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
3504 :
3505 246 : if (att->attisdropped)
3506 6 : continue;
3507 480 : appendStringInfo(&result,
3508 : " <xsd:element name=\"%s\" type=\"%s\"%s></xsd:element>\n",
3509 240 : map_sql_identifier_to_xml_name(NameStr(att->attname),
3510 : true, false),
3511 : map_sql_type_to_xml_name(att->atttypid, -1),
3512 : nulls ? " nillable=\"true\"" : " minOccurs=\"0\"");
3513 : }
3514 :
3515 78 : appendStringInfoString(&result,
3516 : " </xsd:sequence>\n"
3517 : "</xsd:complexType>\n\n");
3518 :
3519 78 : if (!tableforest)
3520 : {
3521 42 : appendStringInfo(&result,
3522 : "<xsd:complexType name=\"%s\">\n"
3523 : " <xsd:sequence>\n"
3524 : " <xsd:element name=\"row\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n"
3525 : " </xsd:sequence>\n"
3526 : "</xsd:complexType>\n\n",
3527 : tabletypename, rowtypename);
3528 :
3529 42 : appendStringInfo(&result,
3530 : "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
3531 : xmltn, tabletypename);
3532 : }
3533 : else
3534 36 : appendStringInfo(&result,
3535 : "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
3536 : xmltn, rowtypename);
3537 :
3538 78 : xsd_schema_element_end(&result);
3539 :
3540 78 : return result.data;
3541 : }
3542 :
3543 :
3544 : /*
3545 : * Map an SQL schema to XML Schema data types; see SQL/XML:2008
3546 : * section 9.12.
3547 : */
3548 : static const char *
3549 18 : map_sql_schema_to_xmlschema_types(Oid nspid, List *relid_list, bool nulls,
3550 : bool tableforest, const char *targetns)
3551 : {
3552 : char *dbname;
3553 : char *nspname;
3554 : char *xmlsn;
3555 : char *schematypename;
3556 : StringInfoData result;
3557 : ListCell *cell;
3558 :
3559 18 : dbname = get_database_name(MyDatabaseId);
3560 18 : nspname = get_namespace_name(nspid);
3561 :
3562 18 : initStringInfo(&result);
3563 :
3564 18 : xmlsn = map_sql_identifier_to_xml_name(nspname, true, false);
3565 :
3566 18 : schematypename = map_multipart_sql_identifier_to_xml_name("SchemaType",
3567 : dbname,
3568 : nspname,
3569 : NULL);
3570 :
3571 18 : appendStringInfo(&result,
3572 : "<xsd:complexType name=\"%s\">\n", schematypename);
3573 18 : if (!tableforest)
3574 6 : appendStringInfoString(&result,
3575 : " <xsd:all>\n");
3576 : else
3577 12 : appendStringInfoString(&result,
3578 : " <xsd:sequence>\n");
3579 :
3580 54 : foreach(cell, relid_list)
3581 : {
3582 36 : Oid relid = lfirst_oid(cell);
3583 36 : char *relname = get_rel_name(relid);
3584 36 : char *xmltn = map_sql_identifier_to_xml_name(relname, true, false);
3585 36 : char *tabletypename = map_multipart_sql_identifier_to_xml_name(tableforest ? "RowType" : "TableType",
3586 : dbname,
3587 : nspname,
3588 : relname);
3589 :
3590 36 : if (!tableforest)
3591 12 : appendStringInfo(&result,
3592 : " <xsd:element name=\"%s\" type=\"%s\"/>\n",
3593 : xmltn, tabletypename);
3594 : else
3595 24 : appendStringInfo(&result,
3596 : " <xsd:element name=\"%s\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n",
3597 : xmltn, tabletypename);
3598 : }
3599 :
3600 18 : if (!tableforest)
3601 6 : appendStringInfoString(&result,
3602 : " </xsd:all>\n");
3603 : else
3604 12 : appendStringInfoString(&result,
3605 : " </xsd:sequence>\n");
3606 18 : appendStringInfoString(&result,
3607 : "</xsd:complexType>\n\n");
3608 :
3609 18 : appendStringInfo(&result,
3610 : "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
3611 : xmlsn, schematypename);
3612 :
3613 18 : return result.data;
3614 : }
3615 :
3616 :
3617 : /*
3618 : * Map an SQL catalog to XML Schema data types; see SQL/XML:2008
3619 : * section 9.15.
3620 : */
3621 : static const char *
3622 0 : map_sql_catalog_to_xmlschema_types(List *nspid_list, bool nulls,
3623 : bool tableforest, const char *targetns)
3624 : {
3625 : char *dbname;
3626 : char *xmlcn;
3627 : char *catalogtypename;
3628 : StringInfoData result;
3629 : ListCell *cell;
3630 :
3631 0 : dbname = get_database_name(MyDatabaseId);
3632 :
3633 0 : initStringInfo(&result);
3634 :
3635 0 : xmlcn = map_sql_identifier_to_xml_name(dbname, true, false);
3636 :
3637 0 : catalogtypename = map_multipart_sql_identifier_to_xml_name("CatalogType",
3638 : dbname,
3639 : NULL,
3640 : NULL);
3641 :
3642 0 : appendStringInfo(&result,
3643 : "<xsd:complexType name=\"%s\">\n", catalogtypename);
3644 0 : appendStringInfoString(&result,
3645 : " <xsd:all>\n");
3646 :
3647 0 : foreach(cell, nspid_list)
3648 : {
3649 0 : Oid nspid = lfirst_oid(cell);
3650 0 : char *nspname = get_namespace_name(nspid);
3651 0 : char *xmlsn = map_sql_identifier_to_xml_name(nspname, true, false);
3652 0 : char *schematypename = map_multipart_sql_identifier_to_xml_name("SchemaType",
3653 : dbname,
3654 : nspname,
3655 : NULL);
3656 :
3657 0 : appendStringInfo(&result,
3658 : " <xsd:element name=\"%s\" type=\"%s\"/>\n",
3659 : xmlsn, schematypename);
3660 : }
3661 :
3662 0 : appendStringInfoString(&result,
3663 : " </xsd:all>\n");
3664 0 : appendStringInfoString(&result,
3665 : "</xsd:complexType>\n\n");
3666 :
3667 0 : appendStringInfo(&result,
3668 : "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
3669 : xmlcn, catalogtypename);
3670 :
3671 0 : return result.data;
3672 : }
3673 :
3674 :
3675 : /*
3676 : * Map an SQL data type to an XML name; see SQL/XML:2008 section 9.4.
3677 : */
3678 : static const char *
3679 810 : map_sql_type_to_xml_name(Oid typeoid, int typmod)
3680 : {
3681 : StringInfoData result;
3682 :
3683 810 : initStringInfo(&result);
3684 :
3685 810 : switch (typeoid)
3686 : {
3687 30 : case BPCHAROID:
3688 30 : if (typmod == -1)
3689 30 : appendStringInfoString(&result, "CHAR");
3690 : else
3691 0 : appendStringInfo(&result, "CHAR_%d", typmod - VARHDRSZ);
3692 30 : break;
3693 54 : case VARCHAROID:
3694 54 : if (typmod == -1)
3695 54 : appendStringInfoString(&result, "VARCHAR");
3696 : else
3697 0 : appendStringInfo(&result, "VARCHAR_%d", typmod - VARHDRSZ);
3698 54 : break;
3699 30 : case NUMERICOID:
3700 30 : if (typmod == -1)
3701 30 : appendStringInfoString(&result, "NUMERIC");
3702 : else
3703 0 : appendStringInfo(&result, "NUMERIC_%d_%d",
3704 0 : ((typmod - VARHDRSZ) >> 16) & 0xffff,
3705 0 : (typmod - VARHDRSZ) & 0xffff);
3706 30 : break;
3707 174 : case INT4OID:
3708 174 : appendStringInfoString(&result, "INTEGER");
3709 174 : break;
3710 30 : case INT2OID:
3711 30 : appendStringInfoString(&result, "SMALLINT");
3712 30 : break;
3713 30 : case INT8OID:
3714 30 : appendStringInfoString(&result, "BIGINT");
3715 30 : break;
3716 30 : case FLOAT4OID:
3717 30 : appendStringInfoString(&result, "REAL");
3718 30 : break;
3719 0 : case FLOAT8OID:
3720 0 : appendStringInfoString(&result, "DOUBLE");
3721 0 : break;
3722 30 : case BOOLOID:
3723 30 : appendStringInfoString(&result, "BOOLEAN");
3724 30 : break;
3725 30 : case TIMEOID:
3726 30 : if (typmod == -1)
3727 30 : appendStringInfoString(&result, "TIME");
3728 : else
3729 0 : appendStringInfo(&result, "TIME_%d", typmod);
3730 30 : break;
3731 30 : case TIMETZOID:
3732 30 : if (typmod == -1)
3733 30 : appendStringInfoString(&result, "TIME_WTZ");
3734 : else
3735 0 : appendStringInfo(&result, "TIME_WTZ_%d", typmod);
3736 30 : break;
3737 30 : case TIMESTAMPOID:
3738 30 : if (typmod == -1)
3739 30 : appendStringInfoString(&result, "TIMESTAMP");
3740 : else
3741 0 : appendStringInfo(&result, "TIMESTAMP_%d", typmod);
3742 30 : break;
3743 30 : case TIMESTAMPTZOID:
3744 30 : if (typmod == -1)
3745 30 : appendStringInfoString(&result, "TIMESTAMP_WTZ");
3746 : else
3747 0 : appendStringInfo(&result, "TIMESTAMP_WTZ_%d", typmod);
3748 30 : break;
3749 30 : case DATEOID:
3750 30 : appendStringInfoString(&result, "DATE");
3751 30 : break;
3752 30 : case XMLOID:
3753 30 : appendStringInfoString(&result, "XML");
3754 30 : break;
3755 222 : default:
3756 : {
3757 : HeapTuple tuple;
3758 : Form_pg_type typtuple;
3759 :
3760 222 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeoid));
3761 222 : if (!HeapTupleIsValid(tuple))
3762 0 : elog(ERROR, "cache lookup failed for type %u", typeoid);
3763 222 : typtuple = (Form_pg_type) GETSTRUCT(tuple);
3764 :
3765 222 : appendStringInfoString(&result,
3766 222 : map_multipart_sql_identifier_to_xml_name((typtuple->typtype == TYPTYPE_DOMAIN) ? "Domain" : "UDT",
3767 222 : get_database_name(MyDatabaseId),
3768 222 : get_namespace_name(typtuple->typnamespace),
3769 222 : NameStr(typtuple->typname)));
3770 :
3771 222 : ReleaseSysCache(tuple);
3772 : }
3773 : }
3774 :
3775 810 : return result.data;
3776 : }
3777 :
3778 :
3779 : /*
3780 : * Map a collection of SQL data types to XML Schema data types; see
3781 : * SQL/XML:2008 section 9.7.
3782 : */
3783 : static const char *
3784 96 : map_sql_typecoll_to_xmlschema_types(List *tupdesc_list)
3785 : {
3786 96 : List *uniquetypes = NIL;
3787 : int i;
3788 : StringInfoData result;
3789 : ListCell *cell0;
3790 :
3791 : /* extract all column types used in the set of TupleDescs */
3792 210 : foreach(cell0, tupdesc_list)
3793 : {
3794 114 : TupleDesc tupdesc = (TupleDesc) lfirst(cell0);
3795 :
3796 702 : for (i = 0; i < tupdesc->natts; i++)
3797 : {
3798 588 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
3799 :
3800 588 : if (att->attisdropped)
3801 24 : continue;
3802 564 : uniquetypes = list_append_unique_oid(uniquetypes, att->atttypid);
3803 : }
3804 : }
3805 :
3806 : /* add base types of domains */
3807 642 : foreach(cell0, uniquetypes)
3808 : {
3809 546 : Oid typid = lfirst_oid(cell0);
3810 546 : Oid basetypid = getBaseType(typid);
3811 :
3812 546 : if (basetypid != typid)
3813 24 : uniquetypes = list_append_unique_oid(uniquetypes, basetypid);
3814 : }
3815 :
3816 : /* Convert to textual form */
3817 96 : initStringInfo(&result);
3818 :
3819 642 : foreach(cell0, uniquetypes)
3820 : {
3821 546 : appendStringInfo(&result, "%s\n",
3822 : map_sql_type_to_xmlschema_type(lfirst_oid(cell0),
3823 : -1));
3824 : }
3825 :
3826 96 : return result.data;
3827 : }
3828 :
3829 :
3830 : /*
3831 : * Map an SQL data type to a named XML Schema data type; see
3832 : * SQL/XML:2008 sections 9.5 and 9.6.
3833 : *
3834 : * (The distinction between 9.5 and 9.6 is basically that 9.6 adds
3835 : * a name attribute, which this function does. The name-less version
3836 : * 9.5 doesn't appear to be required anywhere.)
3837 : */
3838 : static const char *
3839 546 : map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
3840 : {
3841 : StringInfoData result;
3842 546 : const char *typename = map_sql_type_to_xml_name(typeoid, typmod);
3843 :
3844 546 : initStringInfo(&result);
3845 :
3846 546 : if (typeoid == XMLOID)
3847 : {
3848 24 : appendStringInfoString(&result,
3849 : "<xsd:complexType mixed=\"true\">\n"
3850 : " <xsd:sequence>\n"
3851 : " <xsd:any name=\"element\" minOccurs=\"0\" maxOccurs=\"unbounded\" processContents=\"skip\"/>\n"
3852 : " </xsd:sequence>\n"
3853 : "</xsd:complexType>\n");
3854 : }
3855 : else
3856 : {
3857 522 : appendStringInfo(&result,
3858 : "<xsd:simpleType name=\"%s\">\n", typename);
3859 :
3860 522 : switch (typeoid)
3861 : {
3862 138 : case BPCHAROID:
3863 : case VARCHAROID:
3864 : case TEXTOID:
3865 138 : appendStringInfoString(&result,
3866 : " <xsd:restriction base=\"xsd:string\">\n");
3867 138 : if (typmod != -1)
3868 0 : appendStringInfo(&result,
3869 : " <xsd:maxLength value=\"%d\"/>\n",
3870 : typmod - VARHDRSZ);
3871 138 : appendStringInfoString(&result, " </xsd:restriction>\n");
3872 138 : break;
3873 :
3874 24 : case BYTEAOID:
3875 24 : appendStringInfo(&result,
3876 : " <xsd:restriction base=\"xsd:%s\">\n"
3877 : " </xsd:restriction>\n",
3878 24 : xmlbinary == XMLBINARY_BASE64 ? "base64Binary" : "hexBinary");
3879 24 : break;
3880 :
3881 24 : case NUMERICOID:
3882 24 : if (typmod != -1)
3883 0 : appendStringInfo(&result,
3884 : " <xsd:restriction base=\"xsd:decimal\">\n"
3885 : " <xsd:totalDigits value=\"%d\"/>\n"
3886 : " <xsd:fractionDigits value=\"%d\"/>\n"
3887 : " </xsd:restriction>\n",
3888 0 : ((typmod - VARHDRSZ) >> 16) & 0xffff,
3889 0 : (typmod - VARHDRSZ) & 0xffff);
3890 24 : break;
3891 :
3892 24 : case INT2OID:
3893 24 : appendStringInfo(&result,
3894 : " <xsd:restriction base=\"xsd:short\">\n"
3895 : " <xsd:maxInclusive value=\"%d\"/>\n"
3896 : " <xsd:minInclusive value=\"%d\"/>\n"
3897 : " </xsd:restriction>\n",
3898 : SHRT_MAX, SHRT_MIN);
3899 24 : break;
3900 :
3901 96 : case INT4OID:
3902 96 : appendStringInfo(&result,
3903 : " <xsd:restriction base=\"xsd:int\">\n"
3904 : " <xsd:maxInclusive value=\"%d\"/>\n"
3905 : " <xsd:minInclusive value=\"%d\"/>\n"
3906 : " </xsd:restriction>\n",
3907 : INT_MAX, INT_MIN);
3908 96 : break;
3909 :
3910 24 : case INT8OID:
3911 24 : appendStringInfo(&result,
3912 : " <xsd:restriction base=\"xsd:long\">\n"
3913 : " <xsd:maxInclusive value=\"" INT64_FORMAT "\"/>\n"
3914 : " <xsd:minInclusive value=\"" INT64_FORMAT "\"/>\n"
3915 : " </xsd:restriction>\n",
3916 : PG_INT64_MAX,
3917 : PG_INT64_MIN);
3918 24 : break;
3919 :
3920 24 : case FLOAT4OID:
3921 24 : appendStringInfoString(&result,
3922 : " <xsd:restriction base=\"xsd:float\"></xsd:restriction>\n");
3923 24 : break;
3924 :
3925 0 : case FLOAT8OID:
3926 0 : appendStringInfoString(&result,
3927 : " <xsd:restriction base=\"xsd:double\"></xsd:restriction>\n");
3928 0 : break;
3929 :
3930 24 : case BOOLOID:
3931 24 : appendStringInfoString(&result,
3932 : " <xsd:restriction base=\"xsd:boolean\"></xsd:restriction>\n");
3933 24 : break;
3934 :
3935 48 : case TIMEOID:
3936 : case TIMETZOID:
3937 : {
3938 48 : const char *tz = (typeoid == TIMETZOID ? "(\\+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
3939 :
3940 48 : if (typmod == -1)
3941 48 : appendStringInfo(&result,
3942 : " <xsd:restriction base=\"xsd:time\">\n"
3943 : " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}(.\\p{Nd}+)?%s\"/>\n"
3944 : " </xsd:restriction>\n", tz);
3945 0 : else if (typmod == 0)
3946 0 : appendStringInfo(&result,
3947 : " <xsd:restriction base=\"xsd:time\">\n"
3948 : " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
3949 : " </xsd:restriction>\n", tz);
3950 : else
3951 0 : appendStringInfo(&result,
3952 : " <xsd:restriction base=\"xsd:time\">\n"
3953 : " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}.\\p{Nd}{%d}%s\"/>\n"
3954 : " </xsd:restriction>\n", typmod - VARHDRSZ, tz);
3955 48 : break;
3956 : }
3957 :
3958 48 : case TIMESTAMPOID:
3959 : case TIMESTAMPTZOID:
3960 : {
3961 48 : const char *tz = (typeoid == TIMESTAMPTZOID ? "(\\+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
3962 :
3963 48 : if (typmod == -1)
3964 48 : appendStringInfo(&result,
3965 : " <xsd:restriction base=\"xsd:dateTime\">\n"
3966 : " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}T\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}(.\\p{Nd}+)?%s\"/>\n"
3967 : " </xsd:restriction>\n", tz);
3968 0 : else if (typmod == 0)
3969 0 : appendStringInfo(&result,
3970 : " <xsd:restriction base=\"xsd:dateTime\">\n"
3971 : " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}T\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
3972 : " </xsd:restriction>\n", tz);
3973 : else
3974 0 : appendStringInfo(&result,
3975 : " <xsd:restriction base=\"xsd:dateTime\">\n"
3976 : " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}T\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}.\\p{Nd}{%d}%s\"/>\n"
3977 : " </xsd:restriction>\n", typmod - VARHDRSZ, tz);
3978 48 : break;
3979 : }
3980 :
3981 24 : case DATEOID:
3982 24 : appendStringInfoString(&result,
3983 : " <xsd:restriction base=\"xsd:date\">\n"
3984 : " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}\"/>\n"
3985 : " </xsd:restriction>\n");
3986 24 : break;
3987 :
3988 24 : default:
3989 24 : if (get_typtype(typeoid) == TYPTYPE_DOMAIN)
3990 : {
3991 : Oid base_typeoid;
3992 24 : int32 base_typmod = -1;
3993 :
3994 24 : base_typeoid = getBaseTypeAndTypmod(typeoid, &base_typmod);
3995 :
3996 24 : appendStringInfo(&result,
3997 : " <xsd:restriction base=\"%s\"/>\n",
3998 : map_sql_type_to_xml_name(base_typeoid, base_typmod));
3999 : }
4000 24 : break;
4001 : }
4002 522 : appendStringInfoString(&result, "</xsd:simpleType>\n");
4003 : }
4004 :
4005 546 : return result.data;
4006 : }
4007 :
4008 :
4009 : /*
4010 : * Map an SQL row to an XML element, taking the row from the active
4011 : * SPI cursor. See also SQL/XML:2008 section 9.10.
4012 : */
4013 : static void
4014 312 : SPI_sql_row_to_xmlelement(uint64 rownum, StringInfo result, char *tablename,
4015 : bool nulls, bool tableforest,
4016 : const char *targetns, bool top_level)
4017 : {
4018 : int i;
4019 : char *xmltn;
4020 :
4021 312 : if (tablename)
4022 228 : xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
4023 : else
4024 : {
4025 84 : if (tableforest)
4026 36 : xmltn = "row";
4027 : else
4028 48 : xmltn = "table";
4029 : }
4030 :
4031 312 : if (tableforest)
4032 162 : xmldata_root_element_start(result, xmltn, NULL, targetns, top_level);
4033 : else
4034 150 : appendStringInfoString(result, "<row>\n");
4035 :
4036 1272 : for (i = 1; i <= SPI_tuptable->tupdesc->natts; i++)
4037 : {
4038 : char *colname;
4039 : Datum colval;
4040 : bool isnull;
4041 :
4042 960 : colname = map_sql_identifier_to_xml_name(SPI_fname(SPI_tuptable->tupdesc, i),
4043 : true, false);
4044 960 : colval = SPI_getbinval(SPI_tuptable->vals[rownum],
4045 960 : SPI_tuptable->tupdesc,
4046 : i,
4047 : &isnull);
4048 960 : if (isnull)
4049 : {
4050 114 : if (nulls)
4051 60 : appendStringInfo(result, " <%s xsi:nil=\"true\"/>\n", colname);
4052 : }
4053 : else
4054 846 : appendStringInfo(result, " <%s>%s</%s>\n",
4055 : colname,
4056 : map_sql_value_to_xml_value(colval,
4057 846 : SPI_gettypeid(SPI_tuptable->tupdesc, i), true),
4058 : colname);
4059 : }
4060 :
4061 312 : if (tableforest)
4062 : {
4063 162 : xmldata_root_element_end(result, xmltn);
4064 162 : appendStringInfoChar(result, '\n');
4065 : }
4066 : else
4067 150 : appendStringInfoString(result, "</row>\n\n");
4068 312 : }
4069 :
4070 :
4071 : /*
4072 : * XPath related functions
4073 : */
4074 :
4075 : #ifdef USE_LIBXML
4076 :
4077 : /*
4078 : * Convert XML node to text.
4079 : *
4080 : * For attribute and text nodes, return the escaped text. For anything else,
4081 : * dump the whole subtree.
4082 : */
4083 : static text *
4084 192 : xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
4085 : {
4086 192 : xmltype *result = NULL;
4087 :
4088 192 : if (cur->type != XML_ATTRIBUTE_NODE && cur->type != XML_TEXT_NODE)
4089 162 : {
4090 162 : void (*volatile nodefree) (xmlNodePtr) = NULL;
4091 162 : volatile xmlBufferPtr buf = NULL;
4092 162 : volatile xmlNodePtr cur_copy = NULL;
4093 :
4094 162 : PG_TRY();
4095 : {
4096 : int bytes;
4097 :
4098 162 : buf = xmlBufferCreate();
4099 162 : if (buf == NULL || xmlerrcxt->err_occurred)
4100 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
4101 : "could not allocate xmlBuffer");
4102 :
4103 : /*
4104 : * Produce a dump of the node that we can serialize. xmlNodeDump
4105 : * does that, but the result of that function won't contain
4106 : * namespace definitions from ancestor nodes, so we first do a
4107 : * xmlCopyNode() which duplicates the node along with its required
4108 : * namespace definitions.
4109 : *
4110 : * Some old libxml2 versions such as 2.7.6 produce partially
4111 : * broken XML_DOCUMENT_NODE nodes (unset content field) when
4112 : * copying them. xmlNodeDump of such a node works fine, but
4113 : * xmlFreeNode crashes; set us up to call xmlFreeDoc instead.
4114 : */
4115 162 : cur_copy = xmlCopyNode(cur, 1);
4116 162 : if (cur_copy == NULL || xmlerrcxt->err_occurred)
4117 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
4118 : "could not copy node");
4119 324 : nodefree = (cur_copy->type == XML_DOCUMENT_NODE) ?
4120 162 : (void (*) (xmlNodePtr)) xmlFreeDoc : xmlFreeNode;
4121 :
4122 162 : bytes = xmlNodeDump(buf, NULL, cur_copy, 0, 0);
4123 162 : if (bytes == -1 || xmlerrcxt->err_occurred)
4124 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
4125 : "could not dump node");
4126 :
4127 162 : result = xmlBuffer_to_xmltype(buf);
4128 : }
4129 0 : PG_FINALLY();
4130 : {
4131 162 : if (nodefree)
4132 162 : nodefree(cur_copy);
4133 162 : if (buf)
4134 162 : xmlBufferFree(buf);
4135 : }
4136 162 : PG_END_TRY();
4137 : }
4138 : else
4139 : {
4140 : xmlChar *str;
4141 :
4142 30 : str = xmlXPathCastNodeToString(cur);
4143 30 : PG_TRY();
4144 : {
4145 : /* Here we rely on XML having the same representation as TEXT */
4146 30 : char *escaped = escape_xml((char *) str);
4147 :
4148 30 : result = (xmltype *) cstring_to_text(escaped);
4149 30 : pfree(escaped);
4150 : }
4151 0 : PG_FINALLY();
4152 : {
4153 30 : xmlFree(str);
4154 : }
4155 30 : PG_END_TRY();
4156 : }
4157 :
4158 192 : return result;
4159 : }
4160 :
4161 : /*
4162 : * Convert an XML XPath object (the result of evaluating an XPath expression)
4163 : * to an array of xml values, which are appended to astate. The function
4164 : * result value is the number of elements in the array.
4165 : *
4166 : * If "astate" is NULL then we don't generate the array value, but we still
4167 : * return the number of elements it would have had.
4168 : *
4169 : * Nodesets are converted to an array containing the nodes' textual
4170 : * representations. Primitive values (float, double, string) are converted
4171 : * to a single-element array containing the value's string representation.
4172 : */
4173 : static int
4174 536 : xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
4175 : ArrayBuildState *astate,
4176 : PgXmlErrorContext *xmlerrcxt)
4177 : {
4178 536 : int result = 0;
4179 : Datum datum;
4180 : Oid datumtype;
4181 : char *result_str;
4182 :
4183 536 : switch (xpathobj->type)
4184 : {
4185 494 : case XPATH_NODESET:
4186 494 : if (xpathobj->nodesetval != NULL)
4187 : {
4188 350 : result = xpathobj->nodesetval->nodeNr;
4189 350 : if (astate != NULL)
4190 : {
4191 : int i;
4192 :
4193 164 : for (i = 0; i < result; i++)
4194 : {
4195 90 : datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
4196 : xmlerrcxt));
4197 90 : (void) accumArrayResult(astate, datum, false,
4198 : XMLOID, CurrentMemoryContext);
4199 : }
4200 : }
4201 : }
4202 494 : return result;
4203 :
4204 12 : case XPATH_BOOLEAN:
4205 12 : if (astate == NULL)
4206 0 : return 1;
4207 12 : datum = BoolGetDatum(xpathobj->boolval);
4208 12 : datumtype = BOOLOID;
4209 12 : break;
4210 :
4211 18 : case XPATH_NUMBER:
4212 18 : if (astate == NULL)
4213 12 : return 1;
4214 6 : datum = Float8GetDatum(xpathobj->floatval);
4215 6 : datumtype = FLOAT8OID;
4216 6 : break;
4217 :
4218 12 : case XPATH_STRING:
4219 12 : if (astate == NULL)
4220 0 : return 1;
4221 12 : datum = CStringGetDatum((char *) xpathobj->stringval);
4222 12 : datumtype = CSTRINGOID;
4223 12 : break;
4224 :
4225 0 : default:
4226 0 : elog(ERROR, "xpath expression result type %d is unsupported",
4227 : xpathobj->type);
4228 : return 0; /* keep compiler quiet */
4229 : }
4230 :
4231 : /* Common code for scalar-value cases */
4232 30 : result_str = map_sql_value_to_xml_value(datum, datumtype, true);
4233 30 : datum = PointerGetDatum(cstring_to_xmltype(result_str));
4234 30 : (void) accumArrayResult(astate, datum, false,
4235 : XMLOID, CurrentMemoryContext);
4236 30 : return 1;
4237 : }
4238 :
4239 :
4240 : /*
4241 : * Common code for xpath() and xmlexists()
4242 : *
4243 : * Evaluate XPath expression and return number of nodes in res_nitems
4244 : * and array of XML values in astate. Either of those pointers can be
4245 : * NULL if the corresponding result isn't wanted.
4246 : *
4247 : * It is up to the user to ensure that the XML passed is in fact
4248 : * an XML document - XPath doesn't work easily on fragments without
4249 : * a context node being known.
4250 : */
4251 : static void
4252 554 : xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
4253 : int *res_nitems, ArrayBuildState *astate)
4254 : {
4255 : PgXmlErrorContext *xmlerrcxt;
4256 554 : volatile xmlParserCtxtPtr ctxt = NULL;
4257 554 : volatile xmlDocPtr doc = NULL;
4258 554 : volatile xmlXPathContextPtr xpathctx = NULL;
4259 554 : volatile xmlXPathCompExprPtr xpathcomp = NULL;
4260 554 : volatile xmlXPathObjectPtr xpathobj = NULL;
4261 : char *datastr;
4262 : int32 len;
4263 : int32 xpath_len;
4264 : xmlChar *string;
4265 : xmlChar *xpath_expr;
4266 554 : size_t xmldecl_len = 0;
4267 : int i;
4268 : int ndim;
4269 : Datum *ns_names_uris;
4270 : bool *ns_names_uris_nulls;
4271 : int ns_count;
4272 :
4273 : /*
4274 : * Namespace mappings are passed as text[]. If an empty array is passed
4275 : * (ndim = 0, "0-dimensional"), then there are no namespace mappings.
4276 : * Else, a 2-dimensional array with length of the second axis being equal
4277 : * to 2 should be passed, i.e., every subarray contains 2 elements, the
4278 : * first element defining the name, the second one the URI. Example:
4279 : * ARRAY[ARRAY['myns', 'http://example.com'], ARRAY['myns2',
4280 : * 'http://example2.com']].
4281 : */
4282 554 : ndim = namespaces ? ARR_NDIM(namespaces) : 0;
4283 554 : if (ndim != 0)
4284 : {
4285 : int *dims;
4286 :
4287 126 : dims = ARR_DIMS(namespaces);
4288 :
4289 126 : if (ndim != 2 || dims[1] != 2)
4290 0 : ereport(ERROR,
4291 : (errcode(ERRCODE_DATA_EXCEPTION),
4292 : errmsg("invalid array for XML namespace mapping"),
4293 : errdetail("The array must be two-dimensional with length of the second axis equal to 2.")));
4294 :
4295 : Assert(ARR_ELEMTYPE(namespaces) == TEXTOID);
4296 :
4297 126 : deconstruct_array_builtin(namespaces, TEXTOID,
4298 : &ns_names_uris, &ns_names_uris_nulls,
4299 : &ns_count);
4300 :
4301 : Assert((ns_count % 2) == 0); /* checked above */
4302 126 : ns_count /= 2; /* count pairs only */
4303 : }
4304 : else
4305 : {
4306 428 : ns_names_uris = NULL;
4307 428 : ns_names_uris_nulls = NULL;
4308 428 : ns_count = 0;
4309 : }
4310 :
4311 554 : datastr = VARDATA(data);
4312 554 : len = VARSIZE(data) - VARHDRSZ;
4313 554 : xpath_len = VARSIZE_ANY_EXHDR(xpath_expr_text);
4314 554 : if (xpath_len == 0)
4315 6 : ereport(ERROR,
4316 : (errcode(ERRCODE_DATA_EXCEPTION),
4317 : errmsg("empty XPath expression")));
4318 :
4319 548 : string = pg_xmlCharStrndup(datastr, len);
4320 548 : xpath_expr = pg_xmlCharStrndup(VARDATA_ANY(xpath_expr_text), xpath_len);
4321 :
4322 : /*
4323 : * In a UTF8 database, skip any xml declaration, which might assert
4324 : * another encoding. Ignore parse_xml_decl() failure, letting
4325 : * xmlCtxtReadMemory() report parse errors. Documentation disclaims
4326 : * xpath() support for non-ASCII data in non-UTF8 databases, so leave
4327 : * those scenarios bug-compatible with historical behavior.
4328 : */
4329 548 : if (GetDatabaseEncoding() == PG_UTF8)
4330 184 : parse_xml_decl(string, &xmldecl_len, NULL, NULL, NULL);
4331 :
4332 548 : xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
4333 :
4334 548 : PG_TRY();
4335 : {
4336 548 : xmlInitParser();
4337 :
4338 : /*
4339 : * redundant XML parsing (two parsings for the same value during one
4340 : * command execution are possible)
4341 : */
4342 548 : ctxt = xmlNewParserCtxt();
4343 548 : if (ctxt == NULL || xmlerrcxt->err_occurred)
4344 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
4345 : "could not allocate parser context");
4346 1096 : doc = xmlCtxtReadMemory(ctxt, (char *) string + xmldecl_len,
4347 548 : len - xmldecl_len, NULL, NULL, 0);
4348 548 : if (doc == NULL || xmlerrcxt->err_occurred)
4349 12 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
4350 : "could not parse XML document");
4351 536 : xpathctx = xmlXPathNewContext(doc);
4352 536 : if (xpathctx == NULL || xmlerrcxt->err_occurred)
4353 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
4354 : "could not allocate XPath context");
4355 536 : xpathctx->node = (xmlNodePtr) doc;
4356 :
4357 : /* register namespaces, if any */
4358 536 : if (ns_count > 0)
4359 : {
4360 252 : for (i = 0; i < ns_count; i++)
4361 : {
4362 : char *ns_name;
4363 : char *ns_uri;
4364 :
4365 126 : if (ns_names_uris_nulls[i * 2] ||
4366 126 : ns_names_uris_nulls[i * 2 + 1])
4367 0 : ereport(ERROR,
4368 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4369 : errmsg("neither namespace name nor URI may be null")));
4370 126 : ns_name = TextDatumGetCString(ns_names_uris[i * 2]);
4371 126 : ns_uri = TextDatumGetCString(ns_names_uris[i * 2 + 1]);
4372 126 : if (xmlXPathRegisterNs(xpathctx,
4373 : (xmlChar *) ns_name,
4374 : (xmlChar *) ns_uri) != 0)
4375 0 : ereport(ERROR, /* is this an internal error??? */
4376 : (errmsg("could not register XML namespace with name \"%s\" and URI \"%s\"",
4377 : ns_name, ns_uri)));
4378 : }
4379 : }
4380 :
4381 536 : xpathcomp = xmlXPathCompile(xpath_expr);
4382 536 : if (xpathcomp == NULL || xmlerrcxt->err_occurred)
4383 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
4384 : "invalid XPath expression");
4385 :
4386 : /*
4387 : * Version 2.6.27 introduces a function named
4388 : * xmlXPathCompiledEvalToBoolean, which would be enough for xmlexists,
4389 : * but we can derive the existence by whether any nodes are returned,
4390 : * thereby preventing a library version upgrade and keeping the code
4391 : * the same.
4392 : */
4393 536 : xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
4394 536 : if (xpathobj == NULL || xmlerrcxt->err_occurred)
4395 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
4396 : "could not create XPath object");
4397 :
4398 : /*
4399 : * Extract the results as requested.
4400 : */
4401 536 : if (res_nitems != NULL)
4402 432 : *res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
4403 : else
4404 104 : (void) xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
4405 : }
4406 12 : PG_CATCH();
4407 : {
4408 12 : if (xpathobj)
4409 0 : xmlXPathFreeObject(xpathobj);
4410 12 : if (xpathcomp)
4411 0 : xmlXPathFreeCompExpr(xpathcomp);
4412 12 : if (xpathctx)
4413 0 : xmlXPathFreeContext(xpathctx);
4414 12 : if (doc)
4415 12 : xmlFreeDoc(doc);
4416 12 : if (ctxt)
4417 12 : xmlFreeParserCtxt(ctxt);
4418 :
4419 12 : pg_xml_done(xmlerrcxt, true);
4420 :
4421 12 : PG_RE_THROW();
4422 : }
4423 536 : PG_END_TRY();
4424 :
4425 536 : xmlXPathFreeObject(xpathobj);
4426 536 : xmlXPathFreeCompExpr(xpathcomp);
4427 536 : xmlXPathFreeContext(xpathctx);
4428 536 : xmlFreeDoc(doc);
4429 536 : xmlFreeParserCtxt(ctxt);
4430 :
4431 536 : pg_xml_done(xmlerrcxt, false);
4432 536 : }
4433 : #endif /* USE_LIBXML */
4434 :
4435 : /*
4436 : * Evaluate XPath expression and return array of XML values.
4437 : *
4438 : * As we have no support of XQuery sequences yet, this function seems
4439 : * to be the most useful one (array of XML functions plays a role of
4440 : * some kind of substitution for XQuery sequences).
4441 : */
4442 : Datum
4443 122 : xpath(PG_FUNCTION_ARGS)
4444 : {
4445 : #ifdef USE_LIBXML
4446 122 : text *xpath_expr_text = PG_GETARG_TEXT_PP(0);
4447 122 : xmltype *data = PG_GETARG_XML_P(1);
4448 122 : ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
4449 : ArrayBuildState *astate;
4450 :
4451 122 : astate = initArrayResult(XMLOID, CurrentMemoryContext, true);
4452 122 : xpath_internal(xpath_expr_text, data, namespaces,
4453 : NULL, astate);
4454 104 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
4455 : #else
4456 : NO_XML_SUPPORT();
4457 : return 0;
4458 : #endif
4459 : }
4460 :
4461 : /*
4462 : * Determines if the node specified by the supplied XPath exists
4463 : * in a given XML document, returning a boolean.
4464 : */
4465 : Datum
4466 198 : xmlexists(PG_FUNCTION_ARGS)
4467 : {
4468 : #ifdef USE_LIBXML
4469 198 : text *xpath_expr_text = PG_GETARG_TEXT_PP(0);
4470 198 : xmltype *data = PG_GETARG_XML_P(1);
4471 : int res_nitems;
4472 :
4473 198 : xpath_internal(xpath_expr_text, data, NULL,
4474 : &res_nitems, NULL);
4475 :
4476 198 : PG_RETURN_BOOL(res_nitems > 0);
4477 : #else
4478 : NO_XML_SUPPORT();
4479 : return 0;
4480 : #endif
4481 : }
4482 :
4483 : /*
4484 : * Determines if the node specified by the supplied XPath exists
4485 : * in a given XML document, returning a boolean. Differs from
4486 : * xmlexists as it supports namespaces and is not defined in SQL/XML.
4487 : */
4488 : Datum
4489 234 : xpath_exists(PG_FUNCTION_ARGS)
4490 : {
4491 : #ifdef USE_LIBXML
4492 234 : text *xpath_expr_text = PG_GETARG_TEXT_PP(0);
4493 234 : xmltype *data = PG_GETARG_XML_P(1);
4494 234 : ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
4495 : int res_nitems;
4496 :
4497 234 : xpath_internal(xpath_expr_text, data, namespaces,
4498 : &res_nitems, NULL);
4499 :
4500 234 : PG_RETURN_BOOL(res_nitems > 0);
4501 : #else
4502 : NO_XML_SUPPORT();
4503 : return 0;
4504 : #endif
4505 : }
4506 :
4507 : /*
4508 : * Functions for checking well-formed-ness
4509 : */
4510 :
4511 : #ifdef USE_LIBXML
4512 : static bool
4513 114 : wellformed_xml(text *data, XmlOptionType xmloption_arg)
4514 : {
4515 : xmlDocPtr doc;
4516 114 : ErrorSaveContext escontext = {T_ErrorSaveContext};
4517 :
4518 : /*
4519 : * We'll report "true" if no soft error is reported by xml_parse().
4520 : */
4521 114 : doc = xml_parse(data, xmloption_arg, true,
4522 : GetDatabaseEncoding(), NULL, NULL, (Node *) &escontext);
4523 114 : if (doc)
4524 60 : xmlFreeDoc(doc);
4525 :
4526 114 : return !escontext.error_occurred;
4527 : }
4528 : #endif
4529 :
4530 : Datum
4531 90 : xml_is_well_formed(PG_FUNCTION_ARGS)
4532 : {
4533 : #ifdef USE_LIBXML
4534 90 : text *data = PG_GETARG_TEXT_PP(0);
4535 :
4536 90 : PG_RETURN_BOOL(wellformed_xml(data, xmloption));
4537 : #else
4538 : NO_XML_SUPPORT();
4539 : return 0;
4540 : #endif /* not USE_LIBXML */
4541 : }
4542 :
4543 : Datum
4544 12 : xml_is_well_formed_document(PG_FUNCTION_ARGS)
4545 : {
4546 : #ifdef USE_LIBXML
4547 12 : text *data = PG_GETARG_TEXT_PP(0);
4548 :
4549 12 : PG_RETURN_BOOL(wellformed_xml(data, XMLOPTION_DOCUMENT));
4550 : #else
4551 : NO_XML_SUPPORT();
4552 : return 0;
4553 : #endif /* not USE_LIBXML */
4554 : }
4555 :
4556 : Datum
4557 12 : xml_is_well_formed_content(PG_FUNCTION_ARGS)
4558 : {
4559 : #ifdef USE_LIBXML
4560 12 : text *data = PG_GETARG_TEXT_PP(0);
4561 :
4562 12 : PG_RETURN_BOOL(wellformed_xml(data, XMLOPTION_CONTENT));
4563 : #else
4564 : NO_XML_SUPPORT();
4565 : return 0;
4566 : #endif /* not USE_LIBXML */
4567 : }
4568 :
4569 : /*
4570 : * support functions for XMLTABLE
4571 : *
4572 : */
4573 : #ifdef USE_LIBXML
4574 :
4575 : /*
4576 : * Returns private data from executor state. Ensure validity by check with
4577 : * MAGIC number.
4578 : */
4579 : static inline XmlTableBuilderData *
4580 155188 : GetXmlTableBuilderPrivateData(TableFuncScanState *state, const char *fname)
4581 : {
4582 : XmlTableBuilderData *result;
4583 :
4584 155188 : if (!IsA(state, TableFuncScanState))
4585 0 : elog(ERROR, "%s called with invalid TableFuncScanState", fname);
4586 155188 : result = (XmlTableBuilderData *) state->opaque;
4587 155188 : if (result->magic != XMLTABLE_CONTEXT_MAGIC)
4588 0 : elog(ERROR, "%s called with invalid TableFuncScanState", fname);
4589 :
4590 155188 : return result;
4591 : }
4592 : #endif
4593 :
4594 : /*
4595 : * XmlTableInitOpaque
4596 : * Fill in TableFuncScanState->opaque for XmlTable processor; initialize
4597 : * the XML parser.
4598 : *
4599 : * Note: Because we call pg_xml_init() here and pg_xml_done() in
4600 : * XmlTableDestroyOpaque, it is critical for robustness that no other
4601 : * executor nodes run until this node is processed to completion. Caller
4602 : * must execute this to completion (probably filling a tuplestore to exhaust
4603 : * this node in a single pass) instead of using row-per-call mode.
4604 : */
4605 : static void
4606 264 : XmlTableInitOpaque(TableFuncScanState *state, int natts)
4607 : {
4608 : #ifdef USE_LIBXML
4609 264 : volatile xmlParserCtxtPtr ctxt = NULL;
4610 : XmlTableBuilderData *xtCxt;
4611 : PgXmlErrorContext *xmlerrcxt;
4612 :
4613 264 : xtCxt = palloc0(sizeof(XmlTableBuilderData));
4614 264 : xtCxt->magic = XMLTABLE_CONTEXT_MAGIC;
4615 264 : xtCxt->natts = natts;
4616 264 : xtCxt->xpathscomp = palloc0(sizeof(xmlXPathCompExprPtr) * natts);
4617 :
4618 264 : xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
4619 :
4620 264 : PG_TRY();
4621 : {
4622 264 : xmlInitParser();
4623 :
4624 264 : ctxt = xmlNewParserCtxt();
4625 264 : if (ctxt == NULL || xmlerrcxt->err_occurred)
4626 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
4627 : "could not allocate parser context");
4628 : }
4629 0 : PG_CATCH();
4630 : {
4631 0 : if (ctxt != NULL)
4632 0 : xmlFreeParserCtxt(ctxt);
4633 :
4634 0 : pg_xml_done(xmlerrcxt, true);
4635 :
4636 0 : PG_RE_THROW();
4637 : }
4638 264 : PG_END_TRY();
4639 :
4640 264 : xtCxt->xmlerrcxt = xmlerrcxt;
4641 264 : xtCxt->ctxt = ctxt;
4642 :
4643 264 : state->opaque = xtCxt;
4644 : #else
4645 : NO_XML_SUPPORT();
4646 : #endif /* not USE_LIBXML */
4647 264 : }
4648 :
4649 : /*
4650 : * XmlTableSetDocument
4651 : * Install the input document
4652 : */
4653 : static void
4654 264 : XmlTableSetDocument(TableFuncScanState *state, Datum value)
4655 : {
4656 : #ifdef USE_LIBXML
4657 : XmlTableBuilderData *xtCxt;
4658 264 : xmltype *xmlval = DatumGetXmlP(value);
4659 : char *str;
4660 : xmlChar *xstr;
4661 : int length;
4662 264 : volatile xmlDocPtr doc = NULL;
4663 264 : volatile xmlXPathContextPtr xpathcxt = NULL;
4664 :
4665 264 : xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableSetDocument");
4666 :
4667 : /*
4668 : * Use out function for casting to string (remove encoding property). See
4669 : * comment in xml_out.
4670 : */
4671 264 : str = xml_out_internal(xmlval, 0);
4672 :
4673 264 : length = strlen(str);
4674 264 : xstr = pg_xmlCharStrndup(str, length);
4675 :
4676 264 : PG_TRY();
4677 : {
4678 264 : doc = xmlCtxtReadMemory(xtCxt->ctxt, (char *) xstr, length, NULL, NULL, 0);
4679 264 : if (doc == NULL || xtCxt->xmlerrcxt->err_occurred)
4680 0 : xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
4681 : "could not parse XML document");
4682 264 : xpathcxt = xmlXPathNewContext(doc);
4683 264 : if (xpathcxt == NULL || xtCxt->xmlerrcxt->err_occurred)
4684 0 : xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
4685 : "could not allocate XPath context");
4686 264 : xpathcxt->node = (xmlNodePtr) doc;
4687 : }
4688 0 : PG_CATCH();
4689 : {
4690 0 : if (xpathcxt != NULL)
4691 0 : xmlXPathFreeContext(xpathcxt);
4692 0 : if (doc != NULL)
4693 0 : xmlFreeDoc(doc);
4694 :
4695 0 : PG_RE_THROW();
4696 : }
4697 264 : PG_END_TRY();
4698 :
4699 264 : xtCxt->doc = doc;
4700 264 : xtCxt->xpathcxt = xpathcxt;
4701 : #else
4702 : NO_XML_SUPPORT();
4703 : #endif /* not USE_LIBXML */
4704 264 : }
4705 :
4706 : /*
4707 : * XmlTableSetNamespace
4708 : * Add a namespace declaration
4709 : */
4710 : static void
4711 18 : XmlTableSetNamespace(TableFuncScanState *state, const char *name, const char *uri)
4712 : {
4713 : #ifdef USE_LIBXML
4714 : XmlTableBuilderData *xtCxt;
4715 :
4716 18 : if (name == NULL)
4717 6 : ereport(ERROR,
4718 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4719 : errmsg("DEFAULT namespace is not supported")));
4720 12 : xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableSetNamespace");
4721 :
4722 12 : if (xmlXPathRegisterNs(xtCxt->xpathcxt,
4723 12 : pg_xmlCharStrndup(name, strlen(name)),
4724 12 : pg_xmlCharStrndup(uri, strlen(uri))))
4725 0 : xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_DATA_EXCEPTION,
4726 : "could not set XML namespace");
4727 : #else
4728 : NO_XML_SUPPORT();
4729 : #endif /* not USE_LIBXML */
4730 12 : }
4731 :
4732 : /*
4733 : * XmlTableSetRowFilter
4734 : * Install the row-filter Xpath expression.
4735 : */
4736 : static void
4737 258 : XmlTableSetRowFilter(TableFuncScanState *state, const char *path)
4738 : {
4739 : #ifdef USE_LIBXML
4740 : XmlTableBuilderData *xtCxt;
4741 : xmlChar *xstr;
4742 :
4743 258 : xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableSetRowFilter");
4744 :
4745 258 : if (*path == '\0')
4746 0 : ereport(ERROR,
4747 : (errcode(ERRCODE_DATA_EXCEPTION),
4748 : errmsg("row path filter must not be empty string")));
4749 :
4750 258 : xstr = pg_xmlCharStrndup(path, strlen(path));
4751 :
4752 258 : xtCxt->xpathcomp = xmlXPathCompile(xstr);
4753 258 : if (xtCxt->xpathcomp == NULL || xtCxt->xmlerrcxt->err_occurred)
4754 0 : xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_SYNTAX_ERROR,
4755 : "invalid XPath expression");
4756 : #else
4757 : NO_XML_SUPPORT();
4758 : #endif /* not USE_LIBXML */
4759 258 : }
4760 :
4761 : /*
4762 : * XmlTableSetColumnFilter
4763 : * Install the column-filter Xpath expression, for the given column.
4764 : */
4765 : static void
4766 774 : XmlTableSetColumnFilter(TableFuncScanState *state, const char *path, int colnum)
4767 : {
4768 : #ifdef USE_LIBXML
4769 : XmlTableBuilderData *xtCxt;
4770 : xmlChar *xstr;
4771 :
4772 : Assert(PointerIsValid(path));
4773 :
4774 774 : xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableSetColumnFilter");
4775 :
4776 774 : if (*path == '\0')
4777 0 : ereport(ERROR,
4778 : (errcode(ERRCODE_DATA_EXCEPTION),
4779 : errmsg("column path filter must not be empty string")));
4780 :
4781 774 : xstr = pg_xmlCharStrndup(path, strlen(path));
4782 :
4783 774 : xtCxt->xpathscomp[colnum] = xmlXPathCompile(xstr);
4784 774 : if (xtCxt->xpathscomp[colnum] == NULL || xtCxt->xmlerrcxt->err_occurred)
4785 0 : xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_DATA_EXCEPTION,
4786 : "invalid XPath expression");
4787 : #else
4788 : NO_XML_SUPPORT();
4789 : #endif /* not USE_LIBXML */
4790 774 : }
4791 :
4792 : /*
4793 : * XmlTableFetchRow
4794 : * Prepare the next "current" tuple for upcoming GetValue calls.
4795 : * Returns false if the row-filter expression returned no more rows.
4796 : */
4797 : static bool
4798 22306 : XmlTableFetchRow(TableFuncScanState *state)
4799 : {
4800 : #ifdef USE_LIBXML
4801 : XmlTableBuilderData *xtCxt;
4802 :
4803 22306 : xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableFetchRow");
4804 :
4805 : /* Propagate our own error context to libxml2 */
4806 22306 : xmlSetStructuredErrorFunc((void *) xtCxt->xmlerrcxt, xml_errorHandler);
4807 :
4808 22306 : if (xtCxt->xpathobj == NULL)
4809 : {
4810 258 : xtCxt->xpathobj = xmlXPathCompiledEval(xtCxt->xpathcomp, xtCxt->xpathcxt);
4811 258 : if (xtCxt->xpathobj == NULL || xtCxt->xmlerrcxt->err_occurred)
4812 0 : xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
4813 : "could not create XPath object");
4814 :
4815 258 : xtCxt->row_count = 0;
4816 : }
4817 :
4818 22306 : if (xtCxt->xpathobj->type == XPATH_NODESET)
4819 : {
4820 22306 : if (xtCxt->xpathobj->nodesetval != NULL)
4821 : {
4822 22306 : if (xtCxt->row_count++ < xtCxt->xpathobj->nodesetval->nodeNr)
4823 22060 : return true;
4824 : }
4825 : }
4826 :
4827 246 : return false;
4828 : #else
4829 : NO_XML_SUPPORT();
4830 : return false;
4831 : #endif /* not USE_LIBXML */
4832 : }
4833 :
4834 : /*
4835 : * XmlTableGetValue
4836 : * Return the value for column number 'colnum' for the current row. If
4837 : * column -1 is requested, return representation of the whole row.
4838 : *
4839 : * This leaks memory, so be sure to reset often the context in which it's
4840 : * called.
4841 : */
4842 : static Datum
4843 131310 : XmlTableGetValue(TableFuncScanState *state, int colnum,
4844 : Oid typid, int32 typmod, bool *isnull)
4845 : {
4846 : #ifdef USE_LIBXML
4847 : XmlTableBuilderData *xtCxt;
4848 131310 : Datum result = (Datum) 0;
4849 : xmlNodePtr cur;
4850 131310 : char *cstr = NULL;
4851 131310 : volatile xmlXPathObjectPtr xpathobj = NULL;
4852 :
4853 131310 : xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableGetValue");
4854 :
4855 : Assert(xtCxt->xpathobj &&
4856 : xtCxt->xpathobj->type == XPATH_NODESET &&
4857 : xtCxt->xpathobj->nodesetval != NULL);
4858 :
4859 : /* Propagate our own error context to libxml2 */
4860 131310 : xmlSetStructuredErrorFunc((void *) xtCxt->xmlerrcxt, xml_errorHandler);
4861 :
4862 131310 : *isnull = false;
4863 :
4864 131310 : cur = xtCxt->xpathobj->nodesetval->nodeTab[xtCxt->row_count - 1];
4865 :
4866 : Assert(xtCxt->xpathscomp[colnum] != NULL);
4867 :
4868 131310 : PG_TRY();
4869 : {
4870 : /* Set current node as entry point for XPath evaluation */
4871 131310 : xtCxt->xpathcxt->node = cur;
4872 :
4873 : /* Evaluate column path */
4874 131310 : xpathobj = xmlXPathCompiledEval(xtCxt->xpathscomp[colnum], xtCxt->xpathcxt);
4875 131310 : if (xpathobj == NULL || xtCxt->xmlerrcxt->err_occurred)
4876 0 : xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
4877 : "could not create XPath object");
4878 :
4879 : /*
4880 : * There are four possible cases, depending on the number of nodes
4881 : * returned by the XPath expression and the type of the target column:
4882 : * a) XPath returns no nodes. b) The target type is XML (return all
4883 : * as XML). For non-XML return types: c) One node (return content).
4884 : * d) Multiple nodes (error).
4885 : */
4886 131310 : if (xpathobj->type == XPATH_NODESET)
4887 : {
4888 131280 : int count = 0;
4889 :
4890 131280 : if (xpathobj->nodesetval != NULL)
4891 131070 : count = xpathobj->nodesetval->nodeNr;
4892 :
4893 131280 : if (xpathobj->nodesetval == NULL || count == 0)
4894 : {
4895 22194 : *isnull = true;
4896 : }
4897 : else
4898 : {
4899 109086 : if (typid == XMLOID)
4900 : {
4901 : text *textstr;
4902 : StringInfoData str;
4903 :
4904 : /* Concatenate serialized values */
4905 72 : initStringInfo(&str);
4906 174 : for (int i = 0; i < count; i++)
4907 : {
4908 : textstr =
4909 102 : xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
4910 : xtCxt->xmlerrcxt);
4911 :
4912 102 : appendStringInfoText(&str, textstr);
4913 : }
4914 72 : cstr = str.data;
4915 : }
4916 : else
4917 : {
4918 : xmlChar *str;
4919 :
4920 109014 : if (count > 1)
4921 6 : ereport(ERROR,
4922 : (errcode(ERRCODE_CARDINALITY_VIOLATION),
4923 : errmsg("more than one value returned by column XPath expression")));
4924 :
4925 109008 : str = xmlXPathCastNodeSetToString(xpathobj->nodesetval);
4926 109008 : cstr = str ? xml_pstrdup_and_free(str) : "";
4927 : }
4928 : }
4929 : }
4930 30 : else if (xpathobj->type == XPATH_STRING)
4931 : {
4932 : /* Content should be escaped when target will be XML */
4933 18 : if (typid == XMLOID)
4934 6 : cstr = escape_xml((char *) xpathobj->stringval);
4935 : else
4936 12 : cstr = (char *) xpathobj->stringval;
4937 : }
4938 12 : else if (xpathobj->type == XPATH_BOOLEAN)
4939 : {
4940 : char typcategory;
4941 : bool typispreferred;
4942 : xmlChar *str;
4943 :
4944 : /* Allow implicit casting from boolean to numbers */
4945 6 : get_type_category_preferred(typid, &typcategory, &typispreferred);
4946 :
4947 6 : if (typcategory != TYPCATEGORY_NUMERIC)
4948 6 : str = xmlXPathCastBooleanToString(xpathobj->boolval);
4949 : else
4950 0 : str = xmlXPathCastNumberToString(xmlXPathCastBooleanToNumber(xpathobj->boolval));
4951 :
4952 6 : cstr = xml_pstrdup_and_free(str);
4953 : }
4954 6 : else if (xpathobj->type == XPATH_NUMBER)
4955 : {
4956 : xmlChar *str;
4957 :
4958 6 : str = xmlXPathCastNumberToString(xpathobj->floatval);
4959 6 : cstr = xml_pstrdup_and_free(str);
4960 : }
4961 : else
4962 0 : elog(ERROR, "unexpected XPath object type %u", xpathobj->type);
4963 :
4964 : /*
4965 : * By here, either cstr contains the result value, or the isnull flag
4966 : * has been set.
4967 : */
4968 : Assert(cstr || *isnull);
4969 :
4970 131304 : if (!*isnull)
4971 109110 : result = InputFunctionCall(&state->in_functions[colnum],
4972 : cstr,
4973 109110 : state->typioparams[colnum],
4974 : typmod);
4975 : }
4976 6 : PG_FINALLY();
4977 : {
4978 131310 : if (xpathobj != NULL)
4979 131310 : xmlXPathFreeObject(xpathobj);
4980 : }
4981 131310 : PG_END_TRY();
4982 :
4983 131304 : return result;
4984 : #else
4985 : NO_XML_SUPPORT();
4986 : return 0;
4987 : #endif /* not USE_LIBXML */
4988 : }
4989 :
4990 : /*
4991 : * XmlTableDestroyOpaque
4992 : * Release all libxml2 resources
4993 : */
4994 : static void
4995 264 : XmlTableDestroyOpaque(TableFuncScanState *state)
4996 : {
4997 : #ifdef USE_LIBXML
4998 : XmlTableBuilderData *xtCxt;
4999 :
5000 264 : xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableDestroyOpaque");
5001 :
5002 : /* Propagate our own error context to libxml2 */
5003 264 : xmlSetStructuredErrorFunc((void *) xtCxt->xmlerrcxt, xml_errorHandler);
5004 :
5005 264 : if (xtCxt->xpathscomp != NULL)
5006 : {
5007 : int i;
5008 :
5009 1116 : for (i = 0; i < xtCxt->natts; i++)
5010 852 : if (xtCxt->xpathscomp[i] != NULL)
5011 774 : xmlXPathFreeCompExpr(xtCxt->xpathscomp[i]);
5012 : }
5013 :
5014 264 : if (xtCxt->xpathobj != NULL)
5015 258 : xmlXPathFreeObject(xtCxt->xpathobj);
5016 264 : if (xtCxt->xpathcomp != NULL)
5017 258 : xmlXPathFreeCompExpr(xtCxt->xpathcomp);
5018 264 : if (xtCxt->xpathcxt != NULL)
5019 264 : xmlXPathFreeContext(xtCxt->xpathcxt);
5020 264 : if (xtCxt->doc != NULL)
5021 264 : xmlFreeDoc(xtCxt->doc);
5022 264 : if (xtCxt->ctxt != NULL)
5023 264 : xmlFreeParserCtxt(xtCxt->ctxt);
5024 :
5025 264 : pg_xml_done(xtCxt->xmlerrcxt, true);
5026 :
5027 : /* not valid anymore */
5028 264 : xtCxt->magic = 0;
5029 264 : state->opaque = NULL;
5030 :
5031 : #else
5032 : NO_XML_SUPPORT();
5033 : #endif /* not USE_LIBXML */
5034 264 : }
|