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