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