Thanks to visit codestin.com
Credit goes to doxygen.postgresql.org

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