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