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