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-2016, 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, NULL);
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, NULL);
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  "LibxmlContext",
1462 
1463  /* Re-establish the callbacks even if already set */
1464  xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
1465 }
1466 
1467 /*
1468  * Wrappers for memory management functions
1469  */
1470 static void *
1471 xml_palloc(size_t size)
1472 {
1473  return MemoryContextAlloc(LibxmlContext, size);
1474 }
1475 
1476 
1477 static void *
1478 xml_repalloc(void *ptr, size_t size)
1479 {
1480  return repalloc(ptr, size);
1481 }
1482 
1483 
1484 static void
1485 xml_pfree(void *ptr)
1486 {
1487  /* At least some parts of libxml assume xmlFree(NULL) is allowed */
1488  if (ptr)
1489  pfree(ptr);
1490 }
1491 
1492 
1493 static char *
1494 xml_pstrdup(const char *string)
1495 {
1496  return MemoryContextStrdup(LibxmlContext, string);
1497 }
1498 #endif /* USE_LIBXMLCONTEXT */
1499 
1500 
1501 /*
1502  * xmlPgEntityLoader --- entity loader callback function
1503  *
1504  * Silently prevent any external entity URL from being loaded. We don't want
1505  * to throw an error, so instead make the entity appear to expand to an empty
1506  * string.
1507  *
1508  * We would prefer to allow loading entities that exist in the system's
1509  * global XML catalog; but the available libxml2 APIs make that a complex
1510  * and fragile task. For now, just shut down all external access.
1511  */
1512 static xmlParserInputPtr
1513 xmlPgEntityLoader(const char *URL, const char *ID,
1514  xmlParserCtxtPtr ctxt)
1515 {
1516  return xmlNewStringInputStream(ctxt, (const xmlChar *) "");
1517 }
1518 
1519 
1520 /*
1521  * xml_ereport --- report an XML-related error
1522  *
1523  * The "msg" is the SQL-level message; some can be adopted from the SQL/XML
1524  * standard. This function adds libxml's native error message, if any, as
1525  * detail.
1526  *
1527  * This is exported for modules that want to share the core libxml error
1528  * handler. Note that pg_xml_init() *must* have been called previously.
1529  */
1530 void
1531 xml_ereport(PgXmlErrorContext *errcxt, int level, int sqlcode, const char *msg)
1532 {
1533  char *detail;
1534 
1535  /* Defend against someone passing us a bogus context struct */
1536  if (errcxt->magic != ERRCXT_MAGIC)
1537  elog(ERROR, "xml_ereport called with invalid PgXmlErrorContext");
1538 
1539  /* Flag that the current libxml error has been reported */
1540  errcxt->err_occurred = false;
1541 
1542  /* Include detail only if we have some text from libxml */
1543  if (errcxt->err_buf.len > 0)
1544  detail = errcxt->err_buf.data;
1545  else
1546  detail = NULL;
1547 
1548  ereport(level,
1549  (errcode(sqlcode),
1550  errmsg_internal("%s", msg),
1551  detail ? errdetail_internal("%s", detail) : 0));
1552 }
1553 
1554 
1555 /*
1556  * Error handler for libxml errors and warnings
1557  */
1558 static void
1559 xml_errorHandler(void *data, xmlErrorPtr error)
1560 {
1561  PgXmlErrorContext *xmlerrcxt = (PgXmlErrorContext *) data;
1562  xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) error->ctxt;
1563  xmlParserInputPtr input = (ctxt != NULL) ? ctxt->input : NULL;
1564  xmlNodePtr node = error->node;
1565  const xmlChar *name = (node != NULL &&
1566  node->type == XML_ELEMENT_NODE) ? node->name : NULL;
1567  int domain = error->domain;
1568  int level = error->level;
1569  StringInfo errorBuf;
1570 
1571  /*
1572  * Defend against someone passing us a bogus context struct.
1573  *
1574  * We force a backend exit if this check fails because longjmp'ing out of
1575  * libxml would likely render it unsafe to use further.
1576  */
1577  if (xmlerrcxt->magic != ERRCXT_MAGIC)
1578  elog(FATAL, "xml_errorHandler called with invalid PgXmlErrorContext");
1579 
1580  /*----------
1581  * Older libxml versions report some errors differently.
1582  * First, some errors were previously reported as coming from the parser
1583  * domain but are now reported as coming from the namespace domain.
1584  * Second, some warnings were upgraded to errors.
1585  * We attempt to compensate for that here.
1586  *----------
1587  */
1588  switch (error->code)
1589  {
1590  case XML_WAR_NS_URI:
1591  level = XML_ERR_ERROR;
1592  domain = XML_FROM_NAMESPACE;
1593  break;
1594 
1595  case XML_ERR_NS_DECL_ERROR:
1596  case XML_WAR_NS_URI_RELATIVE:
1597  case XML_WAR_NS_COLUMN:
1598  case XML_NS_ERR_XML_NAMESPACE:
1599  case XML_NS_ERR_UNDEFINED_NAMESPACE:
1600  case XML_NS_ERR_QNAME:
1601  case XML_NS_ERR_ATTRIBUTE_REDEFINED:
1602  case XML_NS_ERR_EMPTY:
1603  domain = XML_FROM_NAMESPACE;
1604  break;
1605  }
1606 
1607  /* Decide whether to act on the error or not */
1608  switch (domain)
1609  {
1610  case XML_FROM_PARSER:
1611  case XML_FROM_NONE:
1612  case XML_FROM_MEMORY:
1613  case XML_FROM_IO:
1614 
1615  /*
1616  * Suppress warnings about undeclared entities. We need to do
1617  * this to avoid problems due to not loading DTD definitions.
1618  */
1619  if (error->code == XML_WAR_UNDECLARED_ENTITY)
1620  return;
1621 
1622  /* Otherwise, accept error regardless of the parsing purpose */
1623  break;
1624 
1625  default:
1626  /* Ignore error if only doing well-formedness check */
1627  if (xmlerrcxt->strictness == PG_XML_STRICTNESS_WELLFORMED)
1628  return;
1629  break;
1630  }
1631 
1632  /* Prepare error message in errorBuf */
1633  errorBuf = makeStringInfo();
1634 
1635  if (error->line > 0)
1636  appendStringInfo(errorBuf, "line %d: ", error->line);
1637  if (name != NULL)
1638  appendStringInfo(errorBuf, "element %s: ", name);
1639  appendStringInfoString(errorBuf, error->message);
1640 
1641  /*
1642  * Append context information to errorBuf.
1643  *
1644  * xmlParserPrintFileContext() uses libxml's "generic" error handler to
1645  * write the context. Since we don't want to duplicate libxml
1646  * functionality here, we set up a generic error handler temporarily.
1647  *
1648  * We use appendStringInfo() directly as libxml's generic error handler.
1649  * This should work because it has essentially the same signature as
1650  * libxml expects, namely (void *ptr, const char *msg, ...).
1651  */
1652  if (input != NULL)
1653  {
1654  xmlGenericErrorFunc errFuncSaved = xmlGenericError;
1655  void *errCtxSaved = xmlGenericErrorContext;
1656 
1657  xmlSetGenericErrorFunc((void *) errorBuf,
1658  (xmlGenericErrorFunc) appendStringInfo);
1659 
1660  /* Add context information to errorBuf */
1661  appendStringInfoLineSeparator(errorBuf);
1662 
1663  xmlParserPrintFileContext(input);
1664 
1665  /* Restore generic error func */
1666  xmlSetGenericErrorFunc(errCtxSaved, errFuncSaved);
1667  }
1668 
1669  /* Get rid of any trailing newlines in errorBuf */
1670  chopStringInfoNewlines(errorBuf);
1671 
1672  /*
1673  * Legacy error handling mode. err_occurred is never set, we just add the
1674  * message to err_buf. This mode exists because the xml2 contrib module
1675  * uses our error-handling infrastructure, but we don't want to change its
1676  * behaviour since it's deprecated anyway. This is also why we don't
1677  * distinguish between notices, warnings and errors here --- the old-style
1678  * generic error handler wouldn't have done that either.
1679  */
1680  if (xmlerrcxt->strictness == PG_XML_STRICTNESS_LEGACY)
1681  {
1682  appendStringInfoLineSeparator(&xmlerrcxt->err_buf);
1683  appendStringInfoString(&xmlerrcxt->err_buf, errorBuf->data);
1684 
1685  pfree(errorBuf->data);
1686  pfree(errorBuf);
1687  return;
1688  }
1689 
1690  /*
1691  * We don't want to ereport() here because that'd probably leave libxml in
1692  * an inconsistent state. Instead, we remember the error and ereport()
1693  * from xml_ereport().
1694  *
1695  * Warnings and notices can be reported immediately since they won't cause
1696  * a longjmp() out of libxml.
1697  */
1698  if (level >= XML_ERR_ERROR)
1699  {
1700  appendStringInfoLineSeparator(&xmlerrcxt->err_buf);
1701  appendStringInfoString(&xmlerrcxt->err_buf, errorBuf->data);
1702 
1703  xmlerrcxt->err_occurred = true;
1704  }
1705  else if (level >= XML_ERR_WARNING)
1706  {
1707  ereport(WARNING,
1708  (errmsg_internal("%s", errorBuf->data)));
1709  }
1710  else
1711  {
1712  ereport(NOTICE,
1713  (errmsg_internal("%s", errorBuf->data)));
1714  }
1715 
1716  pfree(errorBuf->data);
1717  pfree(errorBuf);
1718 }
1719 
1720 
1721 /*
1722  * Wrapper for "ereport" function for XML-related errors. The "msg"
1723  * is the SQL-level message; some can be adopted from the SQL/XML
1724  * standard. This function uses "code" to create a textual detail
1725  * message. At the moment, we only need to cover those codes that we
1726  * may raise in this file.
1727  */
1728 static void
1729 xml_ereport_by_code(int level, int sqlcode,
1730  const char *msg, int code)
1731 {
1732  const char *det;
1733 
1734  switch (code)
1735  {
1736  case XML_ERR_INVALID_CHAR:
1737  det = gettext_noop("Invalid character value.");
1738  break;
1739  case XML_ERR_SPACE_REQUIRED:
1740  det = gettext_noop("Space required.");
1741  break;
1742  case XML_ERR_STANDALONE_VALUE:
1743  det = gettext_noop("standalone accepts only 'yes' or 'no'.");
1744  break;
1745  case XML_ERR_VERSION_MISSING:
1746  det = gettext_noop("Malformed declaration: missing version.");
1747  break;
1748  case XML_ERR_MISSING_ENCODING:
1749  det = gettext_noop("Missing encoding in text declaration.");
1750  break;
1751  case XML_ERR_XMLDECL_NOT_FINISHED:
1752  det = gettext_noop("Parsing XML declaration: '?>' expected.");
1753  break;
1754  default:
1755  det = gettext_noop("Unrecognized libxml error code: %d.");
1756  break;
1757  }
1758 
1759  ereport(level,
1760  (errcode(sqlcode),
1761  errmsg_internal("%s", msg),
1762  errdetail(det, code)));
1763 }
1764 
1765 
1766 /*
1767  * Remove all trailing newlines from a StringInfo string
1768  */
1769 static void
1770 chopStringInfoNewlines(StringInfo str)
1771 {
1772  while (str->len > 0 && str->data[str->len - 1] == '\n')
1773  str->data[--str->len] = '\0';
1774 }
1775 
1776 
1777 /*
1778  * Append a newline after removing any existing trailing newlines
1779  */
1780 static void
1781 appendStringInfoLineSeparator(StringInfo str)
1782 {
1783  chopStringInfoNewlines(str);
1784  if (str->len > 0)
1785  appendStringInfoChar(str, '\n');
1786 }
1787 
1788 
1789 /*
1790  * Convert one char in the current server encoding to a Unicode codepoint.
1791  */
1792 static pg_wchar
1793 sqlchar_to_unicode(char *s)
1794 {
1795  char *utf8string;
1796  pg_wchar ret[2]; /* need space for trailing zero */
1797 
1798  /* note we're not assuming s is null-terminated */
1799  utf8string = pg_server_to_any(s, pg_mblen(s), PG_UTF8);
1800 
1801  pg_encoding_mb2wchar_with_len(PG_UTF8, utf8string, ret,
1802  pg_encoding_mblen(PG_UTF8, utf8string));
1803 
1804  if (utf8string != s)
1805  pfree(utf8string);
1806 
1807  return ret[0];
1808 }
1809 
1810 
1811 static bool
1812 is_valid_xml_namefirst(pg_wchar c)
1813 {
1814  /* (Letter | '_' | ':') */
1815  return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
1816  || c == '_' || c == ':');
1817 }
1818 
1819 
1820 static bool
1821 is_valid_xml_namechar(pg_wchar c)
1822 {
1823  /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
1824  return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
1825  || xmlIsDigitQ(c)
1826  || c == '.' || c == '-' || c == '_' || c == ':'
1827  || xmlIsCombiningQ(c)
1828  || xmlIsExtenderQ(c));
1829 }
1830 #endif /* USE_LIBXML */
1831 
1832 
1833 /*
1834  * Map SQL identifier to XML name; see SQL/XML:2008 section 9.1.
1835  */
1836 char *
1837 map_sql_identifier_to_xml_name(char *ident, bool fully_escaped,
1838  bool escape_period)
1839 {
1840 #ifdef USE_LIBXML
1842  char *p;
1843 
1844  /*
1845  * SQL/XML doesn't make use of this case anywhere, so it's probably a
1846  * mistake.
1847  */
1848  Assert(fully_escaped || !escape_period);
1849 
1850  initStringInfo(&buf);
1851 
1852  for (p = ident; *p; p += pg_mblen(p))
1853  {
1854  if (*p == ':' && (p == ident || fully_escaped))
1855  appendStringInfoString(&buf, "_x003A_");
1856  else if (*p == '_' && *(p + 1) == 'x')
1857  appendStringInfoString(&buf, "_x005F_");
1858  else if (fully_escaped && p == ident &&
1859  pg_strncasecmp(p, "xml", 3) == 0)
1860  {
1861  if (*p == 'x')
1862  appendStringInfoString(&buf, "_x0078_");
1863  else
1864  appendStringInfoString(&buf, "_x0058_");
1865  }
1866  else if (escape_period && *p == '.')
1867  appendStringInfoString(&buf, "_x002E_");
1868  else
1869  {
1870  pg_wchar u = sqlchar_to_unicode(p);
1871 
1872  if ((p == ident)
1873  ? !is_valid_xml_namefirst(u)
1874  : !is_valid_xml_namechar(u))
1875  appendStringInfo(&buf, "_x%04X_", (unsigned int) u);
1876  else
1877  appendBinaryStringInfo(&buf, p, pg_mblen(p));
1878  }
1879  }
1880 
1881  return buf.data;
1882 #else /* not USE_LIBXML */
1883  NO_XML_SUPPORT();
1884  return NULL;
1885 #endif /* not USE_LIBXML */
1886 }
1887 
1888 
1889 /*
1890  * Map a Unicode codepoint into the current server encoding.
1891  */
1892 static char *
1894 {
1895  char utf8string[8]; /* need room for trailing zero */
1896  char *result;
1897 
1898  memset(utf8string, 0, sizeof(utf8string));
1899  unicode_to_utf8(c, (unsigned char *) utf8string);
1900 
1901  result = pg_any_to_server(utf8string, strlen(utf8string), PG_UTF8);
1902  /* if pg_any_to_server didn't strdup, we must */
1903  if (result == utf8string)
1904  result = pstrdup(result);
1905  return result;
1906 }
1907 
1908 
1909 /*
1910  * Map XML name to SQL identifier; see SQL/XML:2008 section 9.3.
1911  */
1912 char *
1914 {
1916  char *p;
1917 
1918  initStringInfo(&buf);
1919 
1920  for (p = name; *p; p += pg_mblen(p))
1921  {
1922  if (*p == '_' && *(p + 1) == 'x'
1923  && isxdigit((unsigned char) *(p + 2))
1924  && isxdigit((unsigned char) *(p + 3))
1925  && isxdigit((unsigned char) *(p + 4))
1926  && isxdigit((unsigned char) *(p + 5))
1927  && *(p + 6) == '_')
1928  {
1929  unsigned int u;
1930 
1931  sscanf(p + 2, "%X", &u);
1933  p += 6;
1934  }
1935  else
1936  appendBinaryStringInfo(&buf, p, pg_mblen(p));
1937  }
1938 
1939  return buf.data;
1940 }
1941 
1942 /*
1943  * Map SQL value to XML value; see SQL/XML:2008 section 9.8.
1944  *
1945  * When xml_escape_strings is true, then certain characters in string
1946  * values are replaced by entity references (&lt; etc.), as specified
1947  * in SQL/XML:2008 section 9.8 GR 9) a) iii). This is normally what is
1948  * wanted. The false case is mainly useful when the resulting value
1949  * is used with xmlTextWriterWriteAttribute() to write out an
1950  * attribute, because that function does the escaping itself.
1951  */
1952 char *
1953 map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings)
1954 {
1955  if (type_is_array_domain(type))
1956  {
1957  ArrayType *array;
1958  Oid elmtype;
1959  int16 elmlen;
1960  bool elmbyval;
1961  char elmalign;
1962  int num_elems;
1963  Datum *elem_values;
1964  bool *elem_nulls;
1966  int i;
1967 
1968  array = DatumGetArrayTypeP(value);
1969  elmtype = ARR_ELEMTYPE(array);
1970  get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
1971 
1972  deconstruct_array(array, elmtype,
1973  elmlen, elmbyval, elmalign,
1974  &elem_values, &elem_nulls,
1975  &num_elems);
1976 
1977  initStringInfo(&buf);
1978 
1979  for (i = 0; i < num_elems; i++)
1980  {
1981  if (elem_nulls[i])
1982  continue;
1983  appendStringInfoString(&buf, "<element>");
1985  map_sql_value_to_xml_value(elem_values[i],
1986  elmtype, true));
1987  appendStringInfoString(&buf, "</element>");
1988  }
1989 
1990  pfree(elem_values);
1991  pfree(elem_nulls);
1992 
1993  return buf.data;
1994  }
1995  else
1996  {
1997  Oid typeOut;
1998  bool isvarlena;
1999  char *str;
2000 
2001  /*
2002  * Flatten domains; the special-case treatments below should apply to,
2003  * eg, domains over boolean not just boolean.
2004  */
2005  type = getBaseType(type);
2006 
2007  /*
2008  * Special XSD formatting for some data types
2009  */
2010  switch (type)
2011  {
2012  case BOOLOID:
2013  if (DatumGetBool(value))
2014  return "true";
2015  else
2016  return "false";
2017 
2018  case DATEOID:
2019  {
2020  DateADT date;
2021  struct pg_tm tm;
2022  char buf[MAXDATELEN + 1];
2023 
2024  date = DatumGetDateADT(value);
2025  /* XSD doesn't support infinite values */
2026  if (DATE_NOT_FINITE(date))
2027  ereport(ERROR,
2028  (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2029  errmsg("date out of range"),
2030  errdetail("XML does not support infinite date values.")));
2032  &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
2033  EncodeDateOnly(&tm, USE_XSD_DATES, buf);
2034 
2035  return pstrdup(buf);
2036  }
2037 
2038  case TIMESTAMPOID:
2039  {
2041  struct pg_tm tm;
2042  fsec_t fsec;
2043  char buf[MAXDATELEN + 1];
2044 
2045  timestamp = DatumGetTimestamp(value);
2046 
2047  /* XSD doesn't support infinite values */
2048  if (TIMESTAMP_NOT_FINITE(timestamp))
2049  ereport(ERROR,
2050  (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2051  errmsg("timestamp out of range"),
2052  errdetail("XML does not support infinite timestamp values.")));
2053  else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
2054  EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
2055  else
2056  ereport(ERROR,
2057  (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2058  errmsg("timestamp out of range")));
2059 
2060  return pstrdup(buf);
2061  }
2062 
2063  case TIMESTAMPTZOID:
2064  {
2066  struct pg_tm tm;
2067  int tz;
2068  fsec_t fsec;
2069  const char *tzn = NULL;
2070  char buf[MAXDATELEN + 1];
2071 
2072  timestamp = DatumGetTimestamp(value);
2073 
2074  /* XSD doesn't support infinite values */
2075  if (TIMESTAMP_NOT_FINITE(timestamp))
2076  ereport(ERROR,
2077  (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2078  errmsg("timestamp out of range"),
2079  errdetail("XML does not support infinite timestamp values.")));
2080  else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
2081  EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
2082  else
2083  ereport(ERROR,
2084  (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2085  errmsg("timestamp out of range")));
2086 
2087  return pstrdup(buf);
2088  }
2089 
2090 #ifdef USE_LIBXML
2091  case BYTEAOID:
2092  {
2093  bytea *bstr = DatumGetByteaPP(value);
2094  PgXmlErrorContext *xmlerrcxt;
2095  volatile xmlBufferPtr buf = NULL;
2096  volatile xmlTextWriterPtr writer = NULL;
2097  char *result;
2098 
2099  xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
2100 
2101  PG_TRY();
2102  {
2103  buf = xmlBufferCreate();
2104  if (buf == NULL || xmlerrcxt->err_occurred)
2105  xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
2106  "could not allocate xmlBuffer");
2107  writer = xmlNewTextWriterMemory(buf, 0);
2108  if (writer == NULL || xmlerrcxt->err_occurred)
2109  xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
2110  "could not allocate xmlTextWriter");
2111 
2112  if (xmlbinary == XMLBINARY_BASE64)
2113  xmlTextWriterWriteBase64(writer, VARDATA_ANY(bstr),
2114  0, VARSIZE_ANY_EXHDR(bstr));
2115  else
2116  xmlTextWriterWriteBinHex(writer, VARDATA_ANY(bstr),
2117  0, VARSIZE_ANY_EXHDR(bstr));
2118 
2119  /* we MUST do this now to flush data out to the buffer */
2120  xmlFreeTextWriter(writer);
2121  writer = NULL;
2122 
2123  result = pstrdup((const char *) xmlBufferContent(buf));
2124  }
2125  PG_CATCH();
2126  {
2127  if (writer)
2128  xmlFreeTextWriter(writer);
2129  if (buf)
2130  xmlBufferFree(buf);
2131 
2132  pg_xml_done(xmlerrcxt, true);
2133 
2134  PG_RE_THROW();
2135  }
2136  PG_END_TRY();
2137 
2138  xmlBufferFree(buf);
2139 
2140  pg_xml_done(xmlerrcxt, false);
2141 
2142  return result;
2143  }
2144 #endif /* USE_LIBXML */
2145 
2146  }
2147 
2148  /*
2149  * otherwise, just use the type's native text representation
2150  */
2151  getTypeOutputInfo(type, &typeOut, &isvarlena);
2152  str = OidOutputFunctionCall(typeOut, value);
2153 
2154  /* ... exactly as-is for XML, and when escaping is not wanted */
2155  if (type == XMLOID || !xml_escape_strings)
2156  return str;
2157 
2158  /* otherwise, translate special characters as needed */
2159  return escape_xml(str);
2160  }
2161 }
2162 
2163 
2164 /*
2165  * Escape characters in text that have special meanings in XML.
2166  *
2167  * Returns a palloc'd string.
2168  *
2169  * NB: this is intentionally not dependent on libxml.
2170  */
2171 char *
2172 escape_xml(const char *str)
2173 {
2175  const char *p;
2176 
2177  initStringInfo(&buf);
2178  for (p = str; *p; p++)
2179  {
2180  switch (*p)
2181  {
2182  case '&':
2183  appendStringInfoString(&buf, "&amp;");
2184  break;
2185  case '<':
2186  appendStringInfoString(&buf, "&lt;");
2187  break;
2188  case '>':
2189  appendStringInfoString(&buf, "&gt;");
2190  break;
2191  case '\r':
2192  appendStringInfoString(&buf, "&#x0d;");
2193  break;
2194  default:
2195  appendStringInfoCharMacro(&buf, *p);
2196  break;
2197  }
2198  }
2199  return buf.data;
2200 }
2201 
2202 
2203 static char *
2204 _SPI_strdup(const char *s)
2205 {
2206  size_t len = strlen(s) + 1;
2207  char *ret = SPI_palloc(len);
2208 
2209  memcpy(ret, s, len);
2210  return ret;
2211 }
2212 
2213 
2214 /*
2215  * SQL to XML mapping functions
2216  *
2217  * What follows below was at one point intentionally organized so that
2218  * you can read along in the SQL/XML standard. The functions are
2219  * mostly split up the way the clauses lay out in the standards
2220  * document, and the identifiers are also aligned with the standard
2221  * text. Unfortunately, SQL/XML:2006 reordered the clauses
2222  * differently than SQL/XML:2003, so the order below doesn't make much
2223  * sense anymore.
2224  *
2225  * There are many things going on there:
2226  *
2227  * There are two kinds of mappings: Mapping SQL data (table contents)
2228  * to XML documents, and mapping SQL structure (the "schema") to XML
2229  * Schema. And there are functions that do both at the same time.
2230  *
2231  * Then you can map a database, a schema, or a table, each in both
2232  * ways. This breaks down recursively: Mapping a database invokes
2233  * mapping schemas, which invokes mapping tables, which invokes
2234  * mapping rows, which invokes mapping columns, although you can't
2235  * call the last two from the outside. Because of this, there are a
2236  * number of xyz_internal() functions which are to be called both from
2237  * the function manager wrapper and from some upper layer in a
2238  * recursive call.
2239  *
2240  * See the documentation about what the common function arguments
2241  * nulls, tableforest, and targetns mean.
2242  *
2243  * Some style guidelines for XML output: Use double quotes for quoting
2244  * XML attributes. Indent XML elements by two spaces, but remember
2245  * that a lot of code is called recursively at different levels, so
2246  * it's better not to indent rather than create output that indents
2247  * and outdents weirdly. Add newlines to make the output look nice.
2248  */
2249 
2250 
2251 /*
2252  * Visibility of objects for XML mappings; see SQL/XML:2008 section
2253  * 4.10.8.
2254  */
2255 
2256 /*
2257  * Given a query, which must return type oid as first column, produce
2258  * a list of Oids with the query results.
2259  */
2260 static List *
2261 query_to_oid_list(const char *query)
2262 {
2263  uint64 i;
2264  List *list = NIL;
2265 
2266  SPI_execute(query, true, 0);
2267 
2268  for (i = 0; i < SPI_processed; i++)
2269  {
2270  Datum oid;
2271  bool isnull;
2272 
2273  oid = SPI_getbinval(SPI_tuptable->vals[i],
2275  1,
2276  &isnull);
2277  if (!isnull)
2278  list = lappend_oid(list, DatumGetObjectId(oid));
2279  }
2280 
2281  return list;
2282 }
2283 
2284 
2285 static List *
2287 {
2288  StringInfoData query;
2289 
2290  initStringInfo(&query);
2291  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);
2292 
2293  return query_to_oid_list(query.data);
2294 }
2295 
2296 
2297 /*
2298  * Including the system schemas is probably not useful for a database
2299  * mapping.
2300  */
2301 #define XML_VISIBLE_SCHEMAS_EXCLUDE "(nspname ~ '^pg_' OR nspname = 'information_schema')"
2302 
2303 #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
2304 
2305 
2306 static List *
2308 {
2309  return query_to_oid_list(XML_VISIBLE_SCHEMAS " ORDER BY nspname;");
2310 }
2311 
2312 
2313 static List *
2315 {
2316  /* At the moment there is no order required here. */
2317  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 ");");
2318 }
2319 
2320 
2321 /*
2322  * Map SQL table to XML and/or XML Schema document; see SQL/XML:2008
2323  * section 9.11.
2324  */
2325 
2326 static StringInfo
2328  const char *xmlschema, bool nulls, bool tableforest,
2329  const char *targetns, bool top_level)
2330 {
2331  StringInfoData query;
2332 
2333  initStringInfo(&query);
2334  appendStringInfo(&query, "SELECT * FROM %s",
2336  ObjectIdGetDatum(relid))));
2337  return query_to_xml_internal(query.data, get_rel_name(relid),
2338  xmlschema, nulls, tableforest,
2339  targetns, top_level);
2340 }
2341 
2342 
2343 Datum
2345 {
2346  Oid relid = PG_GETARG_OID(0);
2347  bool nulls = PG_GETARG_BOOL(1);
2348  bool tableforest = PG_GETARG_BOOL(2);
2349  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2350 
2352  nulls, tableforest,
2353  targetns, true)));
2354 }
2355 
2356 
2357 Datum
2359 {
2360  char *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
2361  bool nulls = PG_GETARG_BOOL(1);
2362  bool tableforest = PG_GETARG_BOOL(2);
2363  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2364 
2366  NULL, nulls, tableforest,
2367  targetns, true)));
2368 }
2369 
2370 
2371 Datum
2373 {
2374  char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
2375  int32 count = PG_GETARG_INT32(1);
2376  bool nulls = PG_GETARG_BOOL(2);
2377  bool tableforest = PG_GETARG_BOOL(3);
2378  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(4));
2379 
2380  StringInfoData result;
2381  Portal portal;
2382  uint64 i;
2383 
2384  initStringInfo(&result);
2385 
2386  SPI_connect();
2387  portal = SPI_cursor_find(name);
2388  if (portal == NULL)
2389  ereport(ERROR,
2390  (errcode(ERRCODE_UNDEFINED_CURSOR),
2391  errmsg("cursor \"%s\" does not exist", name)));
2392 
2393  SPI_cursor_fetch(portal, true, count);
2394  for (i = 0; i < SPI_processed; i++)
2395  SPI_sql_row_to_xmlelement(i, &result, NULL, nulls,
2396  tableforest, targetns, true);
2397 
2398  SPI_finish();
2399 
2401 }
2402 
2403 
2404 /*
2405  * Write the start tag of the root element of a data mapping.
2406  *
2407  * top_level means that this is the very top level of the eventual
2408  * output. For example, when the user calls table_to_xml, then a call
2409  * with a table name to this function is the top level. When the user
2410  * calls database_to_xml, then a call with a schema name to this
2411  * function is not the top level. If top_level is false, then the XML
2412  * namespace declarations are omitted, because they supposedly already
2413  * appeared earlier in the output. Repeating them is not wrong, but
2414  * it looks ugly.
2415  */
2416 static void
2417 xmldata_root_element_start(StringInfo result, const char *eltname,
2418  const char *xmlschema, const char *targetns,
2419  bool top_level)
2420 {
2421  /* This isn't really wrong but currently makes no sense. */
2422  Assert(top_level || !xmlschema);
2423 
2424  appendStringInfo(result, "<%s", eltname);
2425  if (top_level)
2426  {
2427  appendStringInfoString(result, " xmlns:xsi=\"" NAMESPACE_XSI "\"");
2428  if (strlen(targetns) > 0)
2429  appendStringInfo(result, " xmlns=\"%s\"", targetns);
2430  }
2431  if (xmlschema)
2432  {
2433  /* FIXME: better targets */
2434  if (strlen(targetns) > 0)
2435  appendStringInfo(result, " xsi:schemaLocation=\"%s #\"", targetns);
2436  else
2437  appendStringInfoString(result, " xsi:noNamespaceSchemaLocation=\"#\"");
2438  }
2439  appendStringInfoString(result, ">\n");
2440 }
2441 
2442 
2443 static void
2444 xmldata_root_element_end(StringInfo result, const char *eltname)
2445 {
2446  appendStringInfo(result, "</%s>\n", eltname);
2447 }
2448 
2449 
2450 static StringInfo
2451 query_to_xml_internal(const char *query, char *tablename,
2452  const char *xmlschema, bool nulls, bool tableforest,
2453  const char *targetns, bool top_level)
2454 {
2455  StringInfo result;
2456  char *xmltn;
2457  uint64 i;
2458 
2459  if (tablename)
2460  xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
2461  else
2462  xmltn = "table";
2463 
2464  result = makeStringInfo();
2465 
2466  SPI_connect();
2467  if (SPI_execute(query, true, 0) != SPI_OK_SELECT)
2468  ereport(ERROR,
2469  (errcode(ERRCODE_DATA_EXCEPTION),
2470  errmsg("invalid query")));
2471 
2472  if (!tableforest)
2473  {
2474  xmldata_root_element_start(result, xmltn, xmlschema,
2475  targetns, top_level);
2476  appendStringInfoChar(result, '\n');
2477  }
2478 
2479  if (xmlschema)
2480  appendStringInfo(result, "%s\n\n", xmlschema);
2481 
2482  for (i = 0; i < SPI_processed; i++)
2483  SPI_sql_row_to_xmlelement(i, result, tablename, nulls,
2484  tableforest, targetns, top_level);
2485 
2486  if (!tableforest)
2487  xmldata_root_element_end(result, xmltn);
2488 
2489  SPI_finish();
2490 
2491  return result;
2492 }
2493 
2494 
2495 Datum
2497 {
2498  Oid relid = PG_GETARG_OID(0);
2499  bool nulls = PG_GETARG_BOOL(1);
2500  bool tableforest = PG_GETARG_BOOL(2);
2501  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2502  const char *result;
2503  Relation rel;
2504 
2505  rel = heap_open(relid, AccessShareLock);
2506  result = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls,
2507  tableforest, targetns);
2508  heap_close(rel, NoLock);
2509 
2511 }
2512 
2513 
2514 Datum
2516 {
2517  char *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
2518  bool nulls = PG_GETARG_BOOL(1);
2519  bool tableforest = PG_GETARG_BOOL(2);
2520  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2521  const char *result;
2522  SPIPlanPtr plan;
2523  Portal portal;
2524 
2525  SPI_connect();
2526 
2527  if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
2528  elog(ERROR, "SPI_prepare(\"%s\") failed", query);
2529 
2530  if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
2531  elog(ERROR, "SPI_cursor_open(\"%s\") failed", query);
2532 
2534  InvalidOid, nulls,
2535  tableforest, targetns));
2536  SPI_cursor_close(portal);
2537  SPI_finish();
2538 
2540 }
2541 
2542 
2543 Datum
2545 {
2546  char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
2547  bool nulls = PG_GETARG_BOOL(1);
2548  bool tableforest = PG_GETARG_BOOL(2);
2549  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2550  const char *xmlschema;
2551  Portal portal;
2552 
2553  SPI_connect();
2554  portal = SPI_cursor_find(name);
2555  if (portal == NULL)
2556  ereport(ERROR,
2557  (errcode(ERRCODE_UNDEFINED_CURSOR),
2558  errmsg("cursor \"%s\" does not exist", name)));
2559 
2560  xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
2561  InvalidOid, nulls,
2562  tableforest, targetns));
2563  SPI_finish();
2564 
2565  PG_RETURN_XML_P(cstring_to_xmltype(xmlschema));
2566 }
2567 
2568 
2569 Datum
2571 {
2572  Oid relid = PG_GETARG_OID(0);
2573  bool nulls = PG_GETARG_BOOL(1);
2574  bool tableforest = PG_GETARG_BOOL(2);
2575  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2576  Relation rel;
2577  const char *xmlschema;
2578 
2579  rel = heap_open(relid, AccessShareLock);
2580  xmlschema = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls,
2581  tableforest, targetns);
2582  heap_close(rel, NoLock);
2583 
2585  xmlschema, nulls, tableforest,
2586  targetns, true)));
2587 }
2588 
2589 
2590 Datum
2592 {
2593  char *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
2594  bool nulls = PG_GETARG_BOOL(1);
2595  bool tableforest = PG_GETARG_BOOL(2);
2596  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2597 
2598  const char *xmlschema;
2599  SPIPlanPtr plan;
2600  Portal portal;
2601 
2602  SPI_connect();
2603 
2604  if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
2605  elog(ERROR, "SPI_prepare(\"%s\") failed", query);
2606 
2607  if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
2608  elog(ERROR, "SPI_cursor_open(\"%s\") failed", query);
2609 
2610  xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
2611  InvalidOid, nulls, tableforest, targetns));
2612  SPI_cursor_close(portal);
2613  SPI_finish();
2614 
2616  xmlschema, nulls, tableforest,
2617  targetns, true)));
2618 }
2619 
2620 
2621 /*
2622  * Map SQL schema to XML and/or XML Schema document; see SQL/XML:2008
2623  * sections 9.13, 9.14.
2624  */
2625 
2626 static StringInfo
2627 schema_to_xml_internal(Oid nspid, const char *xmlschema, bool nulls,
2628  bool tableforest, const char *targetns, bool top_level)
2629 {
2630  StringInfo result;
2631  char *xmlsn;
2632  List *relid_list;
2633  ListCell *cell;
2634 
2636  true, false);
2637  result = makeStringInfo();
2638 
2639  xmldata_root_element_start(result, xmlsn, xmlschema, targetns, top_level);
2640  appendStringInfoChar(result, '\n');
2641 
2642  if (xmlschema)
2643  appendStringInfo(result, "%s\n\n", xmlschema);
2644 
2645  SPI_connect();
2646 
2647  relid_list = schema_get_xml_visible_tables(nspid);
2648 
2649  SPI_push();
2650 
2651  foreach(cell, relid_list)
2652  {
2653  Oid relid = lfirst_oid(cell);
2654  StringInfo subres;
2655 
2656  subres = table_to_xml_internal(relid, NULL, nulls, tableforest,
2657  targetns, false);
2658 
2659  appendStringInfoString(result, subres->data);
2660  appendStringInfoChar(result, '\n');
2661  }
2662 
2663  SPI_pop();
2664  SPI_finish();
2665 
2666  xmldata_root_element_end(result, xmlsn);
2667 
2668  return result;
2669 }
2670 
2671 
2672 Datum
2674 {
2675  Name name = PG_GETARG_NAME(0);
2676  bool nulls = PG_GETARG_BOOL(1);
2677  bool tableforest = PG_GETARG_BOOL(2);
2678  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2679 
2680  char *schemaname;
2681  Oid nspid;
2682 
2683  schemaname = NameStr(*name);
2684  nspid = LookupExplicitNamespace(schemaname, false);
2685 
2687  nulls, tableforest, targetns, true)));
2688 }
2689 
2690 
2691 /*
2692  * Write the start element of the root element of an XML Schema mapping.
2693  */
2694 static void
2695 xsd_schema_element_start(StringInfo result, const char *targetns)
2696 {
2697  appendStringInfoString(result,
2698  "<xsd:schema\n"
2699  " xmlns:xsd=\"" NAMESPACE_XSD "\"");
2700  if (strlen(targetns) > 0)
2701  appendStringInfo(result,
2702  "\n"
2703  " targetNamespace=\"%s\"\n"
2704  " elementFormDefault=\"qualified\"",
2705  targetns);
2706  appendStringInfoString(result,
2707  ">\n\n");
2708 }
2709 
2710 
2711 static void
2713 {
2714  appendStringInfoString(result, "</xsd:schema>");
2715 }
2716 
2717 
2718 static StringInfo
2719 schema_to_xmlschema_internal(const char *schemaname, bool nulls,
2720  bool tableforest, const char *targetns)
2721 {
2722  Oid nspid;
2723  List *relid_list;
2724  List *tupdesc_list;
2725  ListCell *cell;
2726  StringInfo result;
2727 
2728  result = makeStringInfo();
2729 
2730  nspid = LookupExplicitNamespace(schemaname, false);
2731 
2732  xsd_schema_element_start(result, targetns);
2733 
2734  SPI_connect();
2735 
2736  relid_list = schema_get_xml_visible_tables(nspid);
2737 
2738  tupdesc_list = NIL;
2739  foreach(cell, relid_list)
2740  {
2741  Relation rel;
2742 
2743  rel = heap_open(lfirst_oid(cell), AccessShareLock);
2744  tupdesc_list = lappend(tupdesc_list, CreateTupleDescCopy(rel->rd_att));
2745  heap_close(rel, NoLock);
2746  }
2747 
2748  appendStringInfoString(result,
2749  map_sql_typecoll_to_xmlschema_types(tupdesc_list));
2750 
2751  appendStringInfoString(result,
2752  map_sql_schema_to_xmlschema_types(nspid, relid_list,
2753  nulls, tableforest, targetns));
2754 
2755  xsd_schema_element_end(result);
2756 
2757  SPI_finish();
2758 
2759  return result;
2760 }
2761 
2762 
2763 Datum
2765 {
2766  Name name = PG_GETARG_NAME(0);
2767  bool nulls = PG_GETARG_BOOL(1);
2768  bool tableforest = PG_GETARG_BOOL(2);
2769  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2770 
2772  nulls, tableforest, targetns)));
2773 }
2774 
2775 
2776 Datum
2778 {
2779  Name name = PG_GETARG_NAME(0);
2780  bool nulls = PG_GETARG_BOOL(1);
2781  bool tableforest = PG_GETARG_BOOL(2);
2782  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2783  char *schemaname;
2784  Oid nspid;
2785  StringInfo xmlschema;
2786 
2787  schemaname = NameStr(*name);
2788  nspid = LookupExplicitNamespace(schemaname, false);
2789 
2790  xmlschema = schema_to_xmlschema_internal(schemaname, nulls,
2791  tableforest, targetns);
2792 
2794  xmlschema->data, nulls,
2795  tableforest, targetns, true)));
2796 }
2797 
2798 
2799 /*
2800  * Map SQL database to XML and/or XML Schema document; see SQL/XML:2008
2801  * sections 9.16, 9.17.
2802  */
2803 
2804 static StringInfo
2805 database_to_xml_internal(const char *xmlschema, bool nulls,
2806  bool tableforest, const char *targetns)
2807 {
2808  StringInfo result;
2809  List *nspid_list;
2810  ListCell *cell;
2811  char *xmlcn;
2812 
2814  true, false);
2815  result = makeStringInfo();
2816 
2817  xmldata_root_element_start(result, xmlcn, xmlschema, targetns, true);
2818  appendStringInfoChar(result, '\n');
2819 
2820  if (xmlschema)
2821  appendStringInfo(result, "%s\n\n", xmlschema);
2822 
2823  SPI_connect();
2824 
2825  nspid_list = database_get_xml_visible_schemas();
2826 
2827  SPI_push();
2828 
2829  foreach(cell, nspid_list)
2830  {
2831  Oid nspid = lfirst_oid(cell);
2832  StringInfo subres;
2833 
2834  subres = schema_to_xml_internal(nspid, NULL, nulls,
2835  tableforest, targetns, false);
2836 
2837  appendStringInfoString(result, subres->data);
2838  appendStringInfoChar(result, '\n');
2839  }
2840 
2841  SPI_pop();
2842  SPI_finish();
2843 
2844  xmldata_root_element_end(result, xmlcn);
2845 
2846  return result;
2847 }
2848 
2849 
2850 Datum
2852 {
2853  bool nulls = PG_GETARG_BOOL(0);
2854  bool tableforest = PG_GETARG_BOOL(1);
2855  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
2856 
2858  tableforest, targetns)));
2859 }
2860 
2861 
2862 static StringInfo
2863 database_to_xmlschema_internal(bool nulls, bool tableforest,
2864  const char *targetns)
2865 {
2866  List *relid_list;
2867  List *nspid_list;
2868  List *tupdesc_list;
2869  ListCell *cell;
2870  StringInfo result;
2871 
2872  result = makeStringInfo();
2873 
2874  xsd_schema_element_start(result, targetns);
2875 
2876  SPI_connect();
2877 
2878  relid_list = database_get_xml_visible_tables();
2879  nspid_list = database_get_xml_visible_schemas();
2880 
2881  tupdesc_list = NIL;
2882  foreach(cell, relid_list)
2883  {
2884  Relation rel;
2885 
2886  rel = heap_open(lfirst_oid(cell), AccessShareLock);
2887  tupdesc_list = lappend(tupdesc_list, CreateTupleDescCopy(rel->rd_att));
2888  heap_close(rel, NoLock);
2889  }
2890 
2891  appendStringInfoString(result,
2892  map_sql_typecoll_to_xmlschema_types(tupdesc_list));
2893 
2894  appendStringInfoString(result,
2895  map_sql_catalog_to_xmlschema_types(nspid_list, nulls, tableforest, targetns));
2896 
2897  xsd_schema_element_end(result);
2898 
2899  SPI_finish();
2900 
2901  return result;
2902 }
2903 
2904 
2905 Datum
2907 {
2908  bool nulls = PG_GETARG_BOOL(0);
2909  bool tableforest = PG_GETARG_BOOL(1);
2910  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
2911 
2913  tableforest, targetns)));
2914 }
2915 
2916 
2917 Datum
2919 {
2920  bool nulls = PG_GETARG_BOOL(0);
2921  bool tableforest = PG_GETARG_BOOL(1);
2922  const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
2923  StringInfo xmlschema;
2924 
2925  xmlschema = database_to_xmlschema_internal(nulls, tableforest, targetns);
2926 
2928  nulls, tableforest, targetns)));
2929 }
2930 
2931 
2932 /*
2933  * Map a multi-part SQL name to an XML name; see SQL/XML:2008 section
2934  * 9.2.
2935  */
2936 static char *
2937 map_multipart_sql_identifier_to_xml_name(char *a, char *b, char *c, char *d)
2938 {
2939  StringInfoData result;
2940 
2941  initStringInfo(&result);
2942 
2943  if (a)
2944  appendStringInfoString(&result,
2945  map_sql_identifier_to_xml_name(a, true, true));
2946  if (b)
2947  appendStringInfo(&result, ".%s",
2948  map_sql_identifier_to_xml_name(b, true, true));
2949  if (c)
2950  appendStringInfo(&result, ".%s",
2951  map_sql_identifier_to_xml_name(c, true, true));
2952  if (d)
2953  appendStringInfo(&result, ".%s",
2954  map_sql_identifier_to_xml_name(d, true, true));
2955 
2956  return result.data;
2957 }
2958 
2959 
2960 /*
2961  * Map an SQL table to an XML Schema document; see SQL/XML:2008
2962  * section 9.11.
2963  *
2964  * Map an SQL table to XML Schema data types; see SQL/XML:2008 section
2965  * 9.9.
2966  */
2967 static const char *
2968 map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls,
2969  bool tableforest, const char *targetns)
2970 {
2971  int i;
2972  char *xmltn;
2973  char *tabletypename;
2974  char *rowtypename;
2975  StringInfoData result;
2976 
2977  initStringInfo(&result);
2978 
2979  if (OidIsValid(relid))
2980  {
2981  HeapTuple tuple;
2982  Form_pg_class reltuple;
2983 
2984  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
2985  if (!HeapTupleIsValid(tuple))
2986  elog(ERROR, "cache lookup failed for relation %u", relid);
2987  reltuple = (Form_pg_class) GETSTRUCT(tuple);
2988 
2989  xmltn = map_sql_identifier_to_xml_name(NameStr(reltuple->relname),
2990  true, false);
2991 
2992  tabletypename = map_multipart_sql_identifier_to_xml_name("TableType",
2994  get_namespace_name(reltuple->relnamespace),
2995  NameStr(reltuple->relname));
2996 
2997  rowtypename = map_multipart_sql_identifier_to_xml_name("RowType",
2999  get_namespace_name(reltuple->relnamespace),
3000  NameStr(reltuple->relname));
3001 
3002  ReleaseSysCache(tuple);
3003  }
3004  else
3005  {
3006  if (tableforest)
3007  xmltn = "row";
3008  else
3009  xmltn = "table";
3010 
3011  tabletypename = "TableType";
3012  rowtypename = "RowType";
3013  }
3014 
3015  xsd_schema_element_start(&result, targetns);
3016 
3017  appendStringInfoString(&result,
3019 
3020  appendStringInfo(&result,
3021  "<xsd:complexType name=\"%s\">\n"
3022  " <xsd:sequence>\n",
3023  rowtypename);
3024 
3025  for (i = 0; i < tupdesc->natts; i++)
3026  {
3027  if (tupdesc->attrs[i]->attisdropped)
3028  continue;
3029  appendStringInfo(&result,
3030  " <xsd:element name=\"%s\" type=\"%s\"%s></xsd:element>\n",
3031  map_sql_identifier_to_xml_name(NameStr(tupdesc->attrs[i]->attname),
3032  true, false),
3033  map_sql_type_to_xml_name(tupdesc->attrs[i]->atttypid, -1),
3034  nulls ? " nillable=\"true\"" : " minOccurs=\"0\"");
3035  }
3036 
3037  appendStringInfoString(&result,
3038  " </xsd:sequence>\n"
3039  "</xsd:complexType>\n\n");
3040 
3041  if (!tableforest)
3042  {
3043  appendStringInfo(&result,
3044  "<xsd:complexType name=\"%s\">\n"
3045  " <xsd:sequence>\n"
3046  " <xsd:element name=\"row\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n"
3047  " </xsd:sequence>\n"
3048  "</xsd:complexType>\n\n",
3049  tabletypename, rowtypename);
3050 
3051  appendStringInfo(&result,
3052  "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
3053  xmltn, tabletypename);
3054  }
3055  else
3056  appendStringInfo(&result,
3057  "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
3058  xmltn, rowtypename);
3059 
3060  xsd_schema_element_end(&result);
3061 
3062  return result.data;
3063 }
3064 
3065 
3066 /*
3067  * Map an SQL schema to XML Schema data types; see SQL/XML:2008
3068  * section 9.12.
3069  */
3070 static const char *
3071 map_sql_schema_to_xmlschema_types(Oid nspid, List *relid_list, bool nulls,
3072  bool tableforest, const char *targetns)
3073 {
3074  char *dbname;
3075  char *nspname;
3076  char *xmlsn;
3077  char *schematypename;
3078  StringInfoData result;
3079  ListCell *cell;
3080 
3081  dbname = get_database_name(MyDatabaseId);
3082  nspname = get_namespace_name(nspid);
3083 
3084  initStringInfo(&result);
3085 
3086  xmlsn = map_sql_identifier_to_xml_name(nspname, true, false);
3087 
3088  schematypename = map_multipart_sql_identifier_to_xml_name("SchemaType",
3089  dbname,
3090  nspname,
3091  NULL);
3092 
3093  appendStringInfo(&result,
3094  "<xsd:complexType name=\"%s\">\n", schematypename);
3095  if (!tableforest)
3096  appendStringInfoString(&result,
3097  " <xsd:all>\n");
3098  else
3099  appendStringInfoString(&result,
3100  " <xsd:sequence>\n");
3101 
3102  foreach(cell, relid_list)
3103  {
3104  Oid relid = lfirst_oid(cell);
3105  char *relname = get_rel_name(relid);
3106  char *xmltn = map_sql_identifier_to_xml_name(relname, true, false);
3107  char *tabletypename = map_multipart_sql_identifier_to_xml_name(tableforest ? "RowType" : "TableType",
3108  dbname,
3109  nspname,
3110  relname);
3111 
3112  if (!tableforest)
3113  appendStringInfo(&result,
3114  " <xsd:element name=\"%s\" type=\"%s\"/>\n",
3115  xmltn, tabletypename);
3116  else
3117  appendStringInfo(&result,
3118  " <xsd:element name=\"%s\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n",
3119  xmltn, tabletypename);
3120  }
3121 
3122  if (!tableforest)
3123  appendStringInfoString(&result,
3124  " </xsd:all>\n");
3125  else
3126  appendStringInfoString(&result,
3127  " </xsd:sequence>\n");
3128  appendStringInfoString(&result,
3129  "</xsd:complexType>\n\n");
3130 
3131  appendStringInfo(&result,
3132  "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
3133  xmlsn, schematypename);
3134 
3135  return result.data;
3136 }
3137 
3138 
3139 /*
3140  * Map an SQL catalog to XML Schema data types; see SQL/XML:2008
3141  * section 9.15.
3142  */
3143 static const char *
3145  bool tableforest, const char *targetns)
3146 {
3147  char *dbname;
3148  char *xmlcn;
3149  char *catalogtypename;
3150  StringInfoData result;
3151  ListCell *cell;
3152 
3153  dbname = get_database_name(MyDatabaseId);
3154 
3155  initStringInfo(&result);
3156 
3157  xmlcn = map_sql_identifier_to_xml_name(dbname, true, false);
3158 
3159  catalogtypename = map_multipart_sql_identifier_to_xml_name("CatalogType",
3160  dbname,
3161  NULL,
3162  NULL);
3163 
3164  appendStringInfo(&result,
3165  "<xsd:complexType name=\"%s\">\n", catalogtypename);
3166  appendStringInfoString(&result,
3167  " <xsd:all>\n");
3168 
3169  foreach(cell, nspid_list)
3170  {
3171  Oid nspid = lfirst_oid(cell);
3172  char *nspname = get_namespace_name(nspid);
3173  char *xmlsn = map_sql_identifier_to_xml_name(nspname, true, false);
3174  char *schematypename = map_multipart_sql_identifier_to_xml_name("SchemaType",
3175  dbname,
3176  nspname,
3177  NULL);
3178 
3179  appendStringInfo(&result,
3180  " <xsd:element name=\"%s\" type=\"%s\"/>\n",
3181  xmlsn, schematypename);
3182  }
3183 
3184  appendStringInfoString(&result,
3185  " </xsd:all>\n");
3186  appendStringInfoString(&result,
3187  "</xsd:complexType>\n\n");
3188 
3189  appendStringInfo(&result,
3190  "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
3191  xmlcn, catalogtypename);
3192 
3193  return result.data;
3194 }
3195 
3196 
3197 /*
3198  * Map an SQL data type to an XML name; see SQL/XML:2008 section 9.4.
3199  */
3200 static const char *
3201 map_sql_type_to_xml_name(Oid typeoid, int typmod)
3202 {
3203  StringInfoData result;
3204 
3205  initStringInfo(&result);
3206 
3207  switch (typeoid)
3208  {
3209  case BPCHAROID:
3210  if (typmod == -1)
3211  appendStringInfoString(&result, "CHAR");
3212  else
3213  appendStringInfo(&result, "CHAR_%d", typmod - VARHDRSZ);
3214  break;
3215  case VARCHAROID:
3216  if (typmod == -1)
3217  appendStringInfoString(&result, "VARCHAR");
3218  else
3219  appendStringInfo(&result, "VARCHAR_%d", typmod - VARHDRSZ);
3220  break;
3221  case NUMERICOID:
3222  if (typmod == -1)
3223  appendStringInfoString(&result, "NUMERIC");
3224  else
3225  appendStringInfo(&result, "NUMERIC_%d_%d",
3226  ((typmod - VARHDRSZ) >> 16) & 0xffff,
3227  (typmod - VARHDRSZ) & 0xffff);
3228  break;
3229  case INT4OID:
3230  appendStringInfoString(&result, "INTEGER");
3231  break;
3232  case INT2OID:
3233  appendStringInfoString(&result, "SMALLINT");
3234  break;
3235  case INT8OID:
3236  appendStringInfoString(&result, "BIGINT");
3237  break;
3238  case FLOAT4OID:
3239  appendStringInfoString(&result, "REAL");
3240  break;
3241  case FLOAT8OID:
3242  appendStringInfoString(&result, "DOUBLE");
3243  break;
3244  case BOOLOID:
3245  appendStringInfoString(&result, "BOOLEAN");
3246  break;
3247  case TIMEOID:
3248  if (typmod == -1)
3249  appendStringInfoString(&result, "TIME");
3250  else
3251  appendStringInfo(&result, "TIME_%d", typmod);
3252  break;
3253  case TIMETZOID:
3254  if (typmod == -1)
3255  appendStringInfoString(&result, "TIME_WTZ");
3256  else
3257  appendStringInfo(&result, "TIME_WTZ_%d", typmod);
3258  break;
3259  case TIMESTAMPOID:
3260  if (typmod == -1)
3261  appendStringInfoString(&result, "TIMESTAMP");
3262  else
3263  appendStringInfo(&result, "TIMESTAMP_%d", typmod);
3264  break;
3265  case TIMESTAMPTZOID:
3266  if (typmod == -1)
3267  appendStringInfoString(&result, "TIMESTAMP_WTZ");
3268  else
3269  appendStringInfo(&result, "TIMESTAMP_WTZ_%d", typmod);
3270  break;
3271  case DATEOID:
3272  appendStringInfoString(&result, "DATE");
3273  break;
3274  case XMLOID:
3275  appendStringInfoString(&result, "XML");
3276  break;
3277  default:
3278  {
3279  HeapTuple tuple;
3280  Form_pg_type typtuple;
3281 
3282  tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeoid));
3283  if (!HeapTupleIsValid(tuple))
3284  elog(ERROR, "cache lookup failed for type %u", typeoid);
3285  typtuple = (Form_pg_type) GETSTRUCT(tuple);
3286 
3287  appendStringInfoString(&result,
3288  map_multipart_sql_identifier_to_xml_name((typtuple->typtype == TYPTYPE_DOMAIN) ? "Domain" : "UDT",
3290  get_namespace_name(typtuple->typnamespace),
3291  NameStr(typtuple->typname)));
3292 
3293  ReleaseSysCache(tuple);
3294  }
3295  }
3296 
3297  return result.data;
3298 }
3299 
3300 
3301 /*
3302  * Map a collection of SQL data types to XML Schema data types; see
3303  * SQL/XML:2008 section 9.7.
3304  */
3305 static const char *
3307 {
3308  List *uniquetypes = NIL;
3309  int i;
3310  StringInfoData result;
3311  ListCell *cell0;
3312 
3313  /* extract all column types used in the set of TupleDescs */
3314  foreach(cell0, tupdesc_list)
3315  {
3316  TupleDesc tupdesc = (TupleDesc) lfirst(cell0);
3317 
3318  for (i = 0; i < tupdesc->natts; i++)
3319  {
3320  if (tupdesc->attrs[i]->attisdropped)
3321  continue;
3322  uniquetypes = list_append_unique_oid(uniquetypes,
3323  tupdesc->attrs[i]->atttypid);
3324  }
3325  }
3326 
3327  /* add base types of domains */
3328  foreach(cell0, uniquetypes)
3329  {
3330  Oid typid = lfirst_oid(cell0);
3331  Oid basetypid = getBaseType(typid);
3332 
3333  if (basetypid != typid)
3334  uniquetypes = list_append_unique_oid(uniquetypes, basetypid);
3335  }
3336 
3337  /* Convert to textual form */
3338  initStringInfo(&result);
3339 
3340  foreach(cell0, uniquetypes)
3341  {
3342  appendStringInfo(&result, "%s\n",
3344  -1));
3345  }
3346 
3347  return result.data;
3348 }
3349 
3350 
3351 /*
3352  * Map an SQL data type to a named XML Schema data type; see
3353  * SQL/XML:2008 sections 9.5 and 9.6.
3354  *
3355  * (The distinction between 9.5 and 9.6 is basically that 9.6 adds
3356  * a name attribute, which this function does. The name-less version
3357  * 9.5 doesn't appear to be required anywhere.)
3358  */
3359 static const char *
3361 {
3362  StringInfoData result;
3363  const char *typename = map_sql_type_to_xml_name(typeoid, typmod);
3364 
3365  initStringInfo(&result);
3366 
3367  if (typeoid == XMLOID)
3368  {
3369  appendStringInfoString(&result,
3370  "<xsd:complexType mixed=\"true\">\n"
3371  " <xsd:sequence>\n"
3372  " <xsd:any name=\"element\" minOccurs=\"0\" maxOccurs=\"unbounded\" processContents=\"skip\"/>\n"
3373  " </xsd:sequence>\n"
3374  "</xsd:complexType>\n");
3375  }
3376  else
3377  {
3378  appendStringInfo(&result,
3379  "<xsd:simpleType name=\"%s\">\n", typename);
3380 
3381  switch (typeoid)
3382  {
3383  case BPCHAROID:
3384  case VARCHAROID:
3385  case TEXTOID:
3386  appendStringInfo(&result,
3387  " <xsd:restriction base=\"xsd:string\">\n");
3388  if (typmod != -1)
3389  appendStringInfo(&result,
3390  " <xsd:maxLength value=\"%d\"/>\n",
3391  typmod - VARHDRSZ);
3392  appendStringInfoString(&result, " </xsd:restriction>\n");
3393  break;
3394 
3395  case BYTEAOID:
3396  appendStringInfo(&result,
3397  " <xsd:restriction base=\"xsd:%s\">\n"
3398  " </xsd:restriction>\n",
3399  xmlbinary == XMLBINARY_BASE64 ? "base64Binary" : "hexBinary");
3400  break;
3401 
3402  case NUMERICOID:
3403  if (typmod != -1)
3404  appendStringInfo(&result,
3405  " <xsd:restriction base=\"xsd:decimal\">\n"
3406  " <xsd:totalDigits value=\"%d\"/>\n"
3407  " <xsd:fractionDigits value=\"%d\"/>\n"
3408  " </xsd:restriction>\n",
3409  ((typmod - VARHDRSZ) >> 16) & 0xffff,
3410  (typmod - VARHDRSZ) & 0xffff);
3411  break;
3412 
3413  case INT2OID:
3414  appendStringInfo(&result,
3415  " <xsd:restriction base=\"xsd:short\">\n"
3416  " <xsd:maxInclusive value=\"%d\"/>\n"
3417  " <xsd:minInclusive value=\"%d\"/>\n"
3418  " </xsd:restriction>\n",
3419  SHRT_MAX, SHRT_MIN);
3420  break;
3421 
3422  case INT4OID:
3423  appendStringInfo(&result,
3424  " <xsd:restriction base=\"xsd:int\">\n"
3425  " <xsd:maxInclusive value=\"%d\"/>\n"
3426  " <xsd:minInclusive value=\"%d\"/>\n"
3427  " </xsd:restriction>\n",
3428  INT_MAX, INT_MIN);
3429  break;
3430 
3431  case INT8OID:
3432  appendStringInfo(&result,
3433  " <xsd:restriction base=\"xsd:long\">\n"
3434  " <xsd:maxInclusive value=\"" INT64_FORMAT "\"/>\n"
3435  " <xsd:minInclusive value=\"" INT64_FORMAT "\"/>\n"
3436  " </xsd:restriction>\n",
3437  (((uint64) 1) << (sizeof(int64) * 8 - 1)) - 1,
3438  (((uint64) 1) << (sizeof(int64) * 8 - 1)));
3439  break;
3440 
3441  case FLOAT4OID:
3442  appendStringInfoString(&result,
3443  " <xsd:restriction base=\"xsd:float\"></xsd:restriction>\n");
3444  break;
3445 
3446  case FLOAT8OID:
3447  appendStringInfoString(&result,
3448  " <xsd:restriction base=\"xsd:double\"></xsd:restriction>\n");
3449  break;
3450 
3451  case BOOLOID:
3452  appendStringInfoString(&result,
3453  " <xsd:restriction base=\"xsd:boolean\"></xsd:restriction>\n");
3454  break;
3455 
3456  case TIMEOID:
3457  case TIMETZOID:
3458  {
3459  const char *tz = (typeoid == TIMETZOID ? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
3460 
3461  if (typmod == -1)
3462  appendStringInfo(&result,
3463  " <xsd:restriction base=\"xsd:time\">\n"
3464  " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}(.\\p{Nd}+)?%s\"/>\n"
3465  " </xsd:restriction>\n", tz);
3466  else if (typmod == 0)
3467  appendStringInfo(&result,
3468  " <xsd:restriction base=\"xsd:time\">\n"
3469  " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
3470  " </xsd:restriction>\n", tz);
3471  else
3472  appendStringInfo(&result,
3473  " <xsd:restriction base=\"xsd:time\">\n"
3474  " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}.\\p{Nd}{%d}%s\"/>\n"
3475  " </xsd:restriction>\n", typmod - VARHDRSZ, tz);
3476  break;
3477  }
3478 
3479  case TIMESTAMPOID:
3480  case TIMESTAMPTZOID:
3481  {
3482  const char *tz = (typeoid == TIMESTAMPTZOID ? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
3483 
3484  if (typmod == -1)
3485  appendStringInfo(&result,
3486  " <xsd:restriction base=\"xsd:dateTime\">\n"
3487  " <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"
3488  " </xsd:restriction>\n", tz);
3489  else if (typmod == 0)
3490  appendStringInfo(&result,
3491  " <xsd:restriction base=\"xsd:dateTime\">\n"
3492  " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}T\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
3493  " </xsd:restriction>\n", tz);
3494  else
3495  appendStringInfo(&result,
3496  " <xsd:restriction base=\"xsd:dateTime\">\n"
3497  " <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"
3498  " </xsd:restriction>\n", typmod - VARHDRSZ, tz);
3499  break;
3500  }
3501 
3502  case DATEOID:
3503  appendStringInfoString(&result,
3504  " <xsd:restriction base=\"xsd:date\">\n"
3505  " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}\"/>\n"
3506  " </xsd:restriction>\n");
3507  break;
3508 
3509  default:
3510  if (get_typtype(typeoid) == TYPTYPE_DOMAIN)
3511  {
3512  Oid base_typeoid;
3513  int32 base_typmod = -1;
3514 
3515  base_typeoid = getBaseTypeAndTypmod(typeoid, &base_typmod);
3516 
3517  appendStringInfo(&result,
3518  " <xsd:restriction base=\"%s\"/>\n",
3519  map_sql_type_to_xml_name(base_typeoid, base_typmod));
3520  }
3521  break;
3522  }
3523  appendStringInfoString(&result, "</xsd:simpleType>\n");
3524  }
3525 
3526  return result.data;
3527 }
3528 
3529 
3530 /*
3531  * Map an SQL row to an XML element, taking the row from the active
3532  * SPI cursor. See also SQL/XML:2008 section 9.10.
3533  */
3534 static void
3535 SPI_sql_row_to_xmlelement(uint64 rownum, StringInfo result, char *tablename,
3536  bool nulls, bool tableforest,
3537  const char *targetns, bool top_level)
3538 {
3539  int i;
3540  char *xmltn;
3541 
3542  if (tablename)
3543  xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
3544  else
3545  {
3546  if (tableforest)
3547  xmltn = "row";
3548  else
3549  xmltn = "table";
3550  }
3551 
3552  if (tableforest)
3553  xmldata_root_element_start(result, xmltn, NULL, targetns, top_level);
3554  else
3555  appendStringInfoString(result, "<row>\n");
3556 
3557  for (i = 1; i <= SPI_tuptable->tupdesc->natts; i++)
3558  {
3559  char *colname;
3560  Datum colval;
3561  bool isnull;
3562 
3564  true, false);
3565  colval = SPI_getbinval(SPI_tuptable->vals[rownum],
3567  i,
3568  &isnull);
3569  if (isnull)
3570  {
3571  if (nulls)
3572  appendStringInfo(result, " <%s xsi:nil=\"true\"/>\n", colname);
3573  }
3574  else
3575  appendStringInfo(result, " <%s>%s</%s>\n",
3576  colname,
3578  SPI_gettypeid(SPI_tuptable->tupdesc, i), true),
3579  colname);
3580  }
3581 
3582  if (tableforest)
3583  {
3584  xmldata_root_element_end(result, xmltn);
3585  appendStringInfoChar(result, '\n');
3586  }
3587  else
3588  appendStringInfoString(result, "</row>\n\n");
3589 }
3590 
3591 
3592 /*
3593  * XPath related functions
3594  */
3595 
3596 #ifdef USE_LIBXML
3597 
3598 /*
3599  * Convert XML node to text (dump subtree in case of element,
3600  * return value otherwise)
3601  */
3602 static text *
3603 xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
3604 {
3605  xmltype *result;
3606 
3607  if (cur->type == XML_ELEMENT_NODE)
3608  {
3609  xmlBufferPtr buf;
3610  xmlNodePtr cur_copy;
3611 
3612  buf = xmlBufferCreate();
3613 
3614  /*
3615  * The result of xmlNodeDump() won't contain namespace definitions
3616  * from parent nodes, but xmlCopyNode() duplicates a node along with
3617  * its required namespace definitions.
3618  */
3619  cur_copy = xmlCopyNode(cur, 1);
3620 
3621  if (cur_copy == NULL)
3622  xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
3623  "could not copy node");
3624 
3625  PG_TRY();
3626  {
3627  xmlNodeDump(buf, NULL, cur_copy, 0, 1);
3628  result = xmlBuffer_to_xmltype(buf);
3629  }
3630  PG_CATCH();
3631  {
3632  xmlFreeNode(cur_copy);
3633  xmlBufferFree(buf);
3634  PG_RE_THROW();
3635  }
3636  PG_END_TRY();
3637  xmlFreeNode(cur_copy);
3638  xmlBufferFree(buf);
3639  }
3640  else
3641  {
3642  xmlChar *str;
3643 
3644  str = xmlXPathCastNodeToString(cur);
3645  PG_TRY();
3646  {
3647  /* Here we rely on XML having the same representation as TEXT */
3648  char *escaped = escape_xml((char *) str);
3649 
3650  result = (xmltype *) cstring_to_text(escaped);
3651  pfree(escaped);
3652  }
3653  PG_CATCH();
3654  {
3655  xmlFree(str);
3656  PG_RE_THROW();
3657  }
3658  PG_END_TRY();
3659  xmlFree(str);
3660  }
3661 
3662  return result;
3663 }
3664 
3665 /*
3666  * Convert an XML XPath object (the result of evaluating an XPath expression)
3667  * to an array of xml values, which are appended to astate. The function
3668  * result value is the number of elements in the array.
3669  *
3670  * If "astate" is NULL then we don't generate the array value, but we still
3671  * return the number of elements it would have had.
3672  *
3673  * Nodesets are converted to an array containing the nodes' textual
3674  * representations. Primitive values (float, double, string) are converted
3675  * to a single-element array containing the value's string representation.
3676  */
3677 static int
3678 xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
3679  ArrayBuildState *astate,
3680  PgXmlErrorContext *xmlerrcxt)
3681 {
3682  int result = 0;
3683  Datum datum;
3684  Oid datumtype;
3685  char *result_str;
3686 
3687  switch (xpathobj->type)
3688  {
3689  case XPATH_NODESET:
3690  if (xpathobj->nodesetval != NULL)
3691  {
3692  result = xpathobj->nodesetval->nodeNr;
3693  if (astate != NULL)
3694  {
3695  int i;
3696 
3697  for (i = 0; i < result; i++)
3698  {
3699  datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
3700  xmlerrcxt));
3701  (void) accumArrayResult(astate, datum, false,
3703  }
3704  }
3705  }
3706  return result;
3707 
3708  case XPATH_BOOLEAN:
3709  if (astate == NULL)
3710  return 1;
3711  datum = BoolGetDatum(xpathobj->boolval);
3712  datumtype = BOOLOID;
3713  break;
3714 
3715  case XPATH_NUMBER:
3716  if (astate == NULL)
3717  return 1;
3718  datum = Float8GetDatum(xpathobj->floatval);
3719  datumtype = FLOAT8OID;
3720  break;
3721 
3722  case XPATH_STRING:
3723  if (astate == NULL)
3724  return 1;
3725  datum = CStringGetDatum((char *) xpathobj->stringval);
3726  datumtype = CSTRINGOID;
3727  break;
3728 
3729  default:
3730  elog(ERROR, "xpath expression result type %d is unsupported",
3731  xpathobj->type);
3732  return 0; /* keep compiler quiet */
3733  }
3734 
3735  /* Common code for scalar-value cases */
3736  result_str = map_sql_value_to_xml_value(datum, datumtype, true);
3737  datum = PointerGetDatum(cstring_to_xmltype(result_str));
3738  (void) accumArrayResult(astate, datum, false,
3740  return 1;
3741 }
3742 
3743 
3744 /*
3745  * Common code for xpath() and xmlexists()
3746  *
3747  * Evaluate XPath expression and return number of nodes in res_items
3748  * and array of XML values in astate. Either of those pointers can be
3749  * NULL if the corresponding result isn't wanted.
3750  *
3751  * It is up to the user to ensure that the XML passed is in fact
3752  * an XML document - XPath doesn't work easily on fragments without
3753  * a context node being known.
3754  */
3755 static void
3756 xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
3757  int *res_nitems, ArrayBuildState *astate)
3758 {
3759  PgXmlErrorContext *xmlerrcxt;
3760  volatile xmlParserCtxtPtr ctxt = NULL;
3761  volatile xmlDocPtr doc = NULL;
3762  volatile xmlXPathContextPtr xpathctx = NULL;
3763  volatile xmlXPathCompExprPtr xpathcomp = NULL;
3764  volatile xmlXPathObjectPtr xpathobj = NULL;
3765  char *datastr;
3766  int32 len;
3767  int32 xpath_len;
3768  xmlChar *string;
3769  xmlChar *xpath_expr;
3770  int i;
3771  int ndim;
3772  Datum *ns_names_uris;
3773  bool *ns_names_uris_nulls;
3774  int ns_count;
3775 
3776  /*
3777  * Namespace mappings are passed as text[]. If an empty array is passed
3778  * (ndim = 0, "0-dimensional"), then there are no namespace mappings.
3779  * Else, a 2-dimensional array with length of the second axis being equal
3780  * to 2 should be passed, i.e., every subarray contains 2 elements, the
3781  * first element defining the name, the second one the URI. Example:
3782  * ARRAY[ARRAY['myns', 'http://example.com'], ARRAY['myns2',
3783  * 'http://example2.com']].
3784  */
3785  ndim = namespaces ? ARR_NDIM(namespaces) : 0;
3786  if (ndim != 0)
3787  {
3788  int *dims;
3789 
3790  dims = ARR_DIMS(namespaces);
3791 
3792  if (ndim != 2 || dims[1] != 2)
3793  ereport(ERROR,
3794  (errcode(ERRCODE_DATA_EXCEPTION),
3795  errmsg("invalid array for XML namespace mapping"),
3796  errdetail("The array must be two-dimensional with length of the second axis equal to 2.")));
3797 
3798  Assert(ARR_ELEMTYPE(namespaces) == TEXTOID);
3799 
3800  deconstruct_array(namespaces, TEXTOID, -1, false, 'i',
3801  &ns_names_uris, &ns_names_uris_nulls,
3802  &ns_count);
3803 
3804  Assert((ns_count % 2) == 0); /* checked above */
3805  ns_count /= 2; /* count pairs only */
3806  }
3807  else
3808  {
3809  ns_names_uris = NULL;
3810  ns_names_uris_nulls = NULL;
3811  ns_count = 0;
3812  }
3813 
3814  datastr = VARDATA(data);
3815  len = VARSIZE(data) - VARHDRSZ;
3816  xpath_len = VARSIZE(xpath_expr_text) - VARHDRSZ;
3817  if (xpath_len == 0)
3818  ereport(ERROR,
3819  (errcode(ERRCODE_DATA_EXCEPTION),
3820  errmsg("empty XPath expression")));
3821 
3822  string = (xmlChar *) palloc((len + 1) * sizeof(xmlChar));
3823  memcpy(string, datastr, len);
3824  string[len] = '\0';
3825 
3826  xpath_expr = (xmlChar *) palloc((xpath_len + 1) * sizeof(xmlChar));
3827  memcpy(xpath_expr, VARDATA(xpath_expr_text), xpath_len);
3828  xpath_expr[xpath_len] = '\0';
3829 
3830  xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
3831 
3832  PG_TRY();
3833  {
3834  xmlInitParser();
3835 
3836  /*
3837  * redundant XML parsing (two parsings for the same value during one
3838  * command execution are possible)
3839  */
3840  ctxt = xmlNewParserCtxt();
3841  if (ctxt == NULL || xmlerrcxt->err_occurred)
3842  xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
3843  "could not allocate parser context");
3844  doc = xmlCtxtReadMemory(ctxt, (char *) string, len, NULL, NULL, 0);
3845  if (doc == NULL || xmlerrcxt->err_occurred)
3846  xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
3847  "could not parse XML document");
3848  xpathctx = xmlXPathNewContext(doc);
3849  if (xpathctx == NULL || xmlerrcxt->err_occurred)
3850  xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
3851  "could not allocate XPath context");
3852  xpathctx->node = xmlDocGetRootElement(doc);
3853  if (xpathctx->node == NULL || xmlerrcxt->err_occurred)
3854  xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
3855  "could not find root XML element");
3856 
3857  /* register namespaces, if any */
3858  if (ns_count > 0)
3859  {
3860  for (i = 0; i < ns_count; i++)
3861  {
3862  char *ns_name;
3863  char *ns_uri;
3864 
3865  if (ns_names_uris_nulls[i * 2] ||
3866  ns_names_uris_nulls[i * 2 + 1])
3867  ereport(ERROR,
3868  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3869  errmsg("neither namespace name nor URI may be null")));
3870  ns_name = TextDatumGetCString(ns_names_uris[i * 2]);
3871  ns_uri = TextDatumGetCString(ns_names_uris[i * 2 + 1]);
3872  if (xmlXPathRegisterNs(xpathctx,
3873  (xmlChar *) ns_name,
3874  (xmlChar *) ns_uri) != 0)
3875  ereport(ERROR, /* is this an internal error??? */
3876  (errmsg("could not register XML namespace with name \"%s\" and URI \"%s\"",
3877  ns_name, ns_uri)));
3878  }
3879  }
3880 
3881  xpathcomp = xmlXPathCompile(xpath_expr);
3882  if (xpathcomp == NULL || xmlerrcxt->err_occurred)
3883  xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
3884  "invalid XPath expression");
3885 
3886  /*
3887  * Version 2.6.27 introduces a function named
3888  * xmlXPathCompiledEvalToBoolean, which would be enough for xmlexists,
3889  * but we can derive the existence by whether any nodes are returned,
3890  * thereby preventing a library version upgrade and keeping the code
3891  * the same.
3892  */
3893  xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
3894  if (xpathobj == NULL || xmlerrcxt->err_occurred)
3895  xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
3896  "could not create XPath object");
3897 
3898  /*
3899  * Extract the results as requested.
3900  */
3901  if (res_nitems != NULL)
3902  *res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
3903  else
3904  (void) xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
3905  }
3906  PG_CATCH();
3907  {
3908  if (xpathobj)
3909  xmlXPathFreeObject(xpathobj);
3910  if (xpathcomp)
3911  xmlXPathFreeCompExpr(xpathcomp);
3912  if (xpathctx)
3913  xmlXPathFreeContext(xpathctx);
3914  if (doc)
3915  xmlFreeDoc(doc);
3916  if (ctxt)
3917  xmlFreeParserCtxt(ctxt);
3918 
3919  pg_xml_done(xmlerrcxt, true);
3920 
3921  PG_RE_THROW();
3922  }
3923  PG_END_TRY();
3924 
3925  xmlXPathFreeObject(xpathobj);
3926  xmlXPathFreeCompExpr(xpathcomp);
3927  xmlXPathFreeContext(xpathctx);
3928  xmlFreeDoc(doc);
3929  xmlFreeParserCtxt(ctxt);
3930 
3931  pg_xml_done(xmlerrcxt, false);
3932 }
3933 #endif /* USE_LIBXML */
3934 
3935 /*
3936  * Evaluate XPath expression and return array of XML values.
3937  *
3938  * As we have no support of XQuery sequences yet, this function seems
3939  * to be the most useful one (array of XML functions plays a role of
3940  * some kind of substitution for XQuery sequences).
3941  */
3942 Datum
3944 {
3945 #ifdef USE_LIBXML
3946  text *xpath_expr_text = PG_GETARG_TEXT_P(0);
3947  xmltype *data = PG_GETARG_XML_P(1);
3948  ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
3949  ArrayBuildState *astate;
3950 
3951  astate = initArrayResult(XMLOID, CurrentMemoryContext, true);
3952  xpath_internal(xpath_expr_text, data, namespaces,
3953  NULL, astate);
3955 #else
3956  NO_XML_SUPPORT();
3957  return 0;
3958 #endif
3959 }
3960 
3961 /*
3962  * Determines if the node specified by the supplied XPath exists
3963  * in a given XML document, returning a boolean.
3964  */
3965 Datum
3967 {
3968 #ifdef USE_LIBXML
3969  text *xpath_expr_text = PG_GETARG_TEXT_P(0);
3970  xmltype *data = PG_GETARG_XML_P(1);
3971  int res_nitems;
3972 
3973  xpath_internal(xpath_expr_text, data, NULL,
3974  &res_nitems, NULL);
3975 
3976  PG_RETURN_BOOL(res_nitems > 0);
3977 #else
3978  NO_XML_SUPPORT();
3979  return 0;
3980 #endif
3981 }
3982 
3983 /*
3984  * Determines if the node specified by the supplied XPath exists
3985  * in a given XML document, returning a boolean. Differs from
3986  * xmlexists as it supports namespaces and is not defined in SQL/XML.
3987  */
3988 Datum
3990 {
3991 #ifdef USE_LIBXML
3992  text *xpath_expr_text = PG_GETARG_TEXT_P(0);
3993  xmltype *data = PG_GETARG_XML_P(1);
3994  ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
3995  int res_nitems;
3996 
3997  xpath_internal(xpath_expr_text, data, namespaces,
3998  &res_nitems, NULL);
3999 
4000  PG_RETURN_BOOL(res_nitems > 0);
4001 #else
4002  NO_XML_SUPPORT();
4003  return 0;
4004 #endif
4005 }
4006 
4007 /*
4008  * Functions for checking well-formed-ness
4009  */
4010 
4011 #ifdef USE_LIBXML
4012 static bool
4013 wellformed_xml(text *data, XmlOptionType xmloption_arg)
4014 {
4015  bool result;
4016  volatile xmlDocPtr doc = NULL;
4017 
4018  /* We want to catch any exceptions and return false */
4019  PG_TRY();
4020  {
4021  doc = xml_parse(data, xmloption_arg, true, GetDatabaseEncoding());
4022  result = true;
4023  }
4024  PG_CATCH();
4025  {
4026  FlushErrorState();
4027  result = false;
4028  }
4029  PG_END_TRY();
4030 
4031  if (doc)
4032  xmlFreeDoc(doc);
4033 
4034  return result;
4035 }
4036 #endif
4037 
4038 Datum
4040 {
4041 #ifdef USE_LIBXML
4042  text *data = PG_GETARG_TEXT_P(0);
4043 
4044  PG_RETURN_BOOL(wellformed_xml(data, xmloption));
4045 #else
4046  NO_XML_SUPPORT();
4047  return 0;
4048 #endif /* not USE_LIBXML */
4049 }
4050 
4051 Datum
4053 {
4054 #ifdef USE_LIBXML
4055  text *data = PG_GETARG_TEXT_P(0);
4056 
4057  PG_RETURN_BOOL(wellformed_xml(data, XMLOPTION_DOCUMENT));
4058 #else
4059  NO_XML_SUPPORT();
4060  return 0;
4061 #endif /* not USE_LIBXML */
4062 }
4063 
4064 Datum
4066 {
4067 #ifdef USE_LIBXML
4068  text *data = PG_GETARG_TEXT_P(0);
4069 
4070  PG_RETURN_BOOL(wellformed_xml(data, XMLOPTION_CONTENT));
4071 #else
4072  NO_XML_SUPPORT();
4073  return 0;
4074 #endif /* not USE_LIBXML */
4075 }
#define MAXDATELEN
Definition: datetime.h:203
void EncodeDateOnly(struct pg_tm *tm, int style, char *str)
Definition: datetime.c:3947
#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:2719
#define TIMESTAMPTZOID
Definition: pg_type.h:513
ExprState xprstate
Definition: execnodes.h:956
signed short int16
Definition: c.h:252
static void xsd_schema_element_start(StringInfo result, const char *targetns)
Definition: xml.c:2695
#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:954
static void xmldata_root_element_start(StringInfo result, const char *eltname, const char *xmlschema, const char *targetns, bool top_level)
Definition: xml.c:2417
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition: lsyscache.c:2256
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:140
#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:2627
#define DatumGetDateADT(X)
Definition: date.h:73
#define type_is_array_domain(typid)
Definition: lsyscache.h:165
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:2702
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:631
static List * query_to_oid_list(const char *query)
Definition: xml.c:2261
unsigned char * unicode_to_utf8(pg_wchar c, unsigned char *utf8string)
Definition: wchar.c:475
char * name
Definition: primnodes.h:1077
static List * schema_get_xml_visible_tables(Oid nspid)
Definition: xml.c:2286
static void error(void)
Definition: sql-dyntest.c:147
double fsec_t
Definition: timestamp.h:53
ArrayBuildState * initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
Definition: arrayfuncs.c:4951
List * args
Definition: execnodes.h:958
int sqlerrcode
Definition: elog.h:341
Datum xmlvalidate(PG_FUNCTION_ARGS)
Definition: xml.c:827
int SPI_connect(void)
Definition: spi.c:85
int32 DateADT
Definition: date.h:22
void SPI_push(void)
Definition: spi.c:322
#define TEXTOID
Definition: pg_type.h:324
#define VARSIZE(PTR)
Definition: postgres.h:306
ErrorData * CopyErrorData(void)
Definition: elog.c:1497
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:358
#define VARHDRSZ
Definition: c.h:440
#define DatumGetObjectId(X)
Definition: postgres.h:508
char * pstrdup(const char *in)
Definition: mcxt.c:1168
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition: spi.c:547
Datum database_to_xmlschema(PG_FUNCTION_ARGS)
Definition: xml.c:2906
int timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, const char **tzn, pg_tz *attimezone)
Definition: timestamp.c:1861
int SPI_finish(void)
Definition: spi.c:160
StringInfo makeStringInfo(void)
Definition: stringinfo.c:28
#define NAMESPACE_XSI
Definition: xml.c:178
StringInfoData * StringInfo
Definition: stringinfo.h:43
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:4065
Datum query_to_xmlschema(PG_FUNCTION_ARGS)
Definition: xml.c:2515
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:491
#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:3144
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:162
#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:1111
#define heap_close(r, l)
Definition: heapam.h:97
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:548
#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:4052
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:2937
HeapTuple * vals
Definition: spi.h:27
List * arg_names
Definition: primnodes.h:1079
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:530
static StringInfo database_to_xml_internal(const char *xmlschema, bool nulls, bool tableforest, const char *targetns)
Definition: xml.c:2805
bytea * pq_endtypsend(StringInfo buf)
Definition: pqformat.c:378
int natts
Definition: tupdesc.h:73
void FlushErrorState(void)
Definition: elog.c:1587
uint64 SPI_processed
Definition: spi.c:39
#define ALLOCSET_DEFAULT_MINSIZE
Definition: memutils.h:142
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:141
#define PG_XML_DEFAULT_VERSION
Definition: xml.c:231
const char * pq_getmsgbytes(StringInfo msg, int datalen)
Definition: pqformat.c:549
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:2193
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:844
Portal SPI_cursor_find(const char *name)
Definition: spi.c:1418
#define XML_VISIBLE_SCHEMAS
Definition: xml.c:2303
static char * relname(char const *dir, char const *base)
Definition: zic.c:755
#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:127
#define TIMESTAMP_NOT_FINITE(j)
Definition: timestamp.h:144
double TimestampTz
Definition: timestamp.h:51
Datum xpath(PG_FUNCTION_ARGS)
Definition: xml.c:3943
char * map_sql_identifier_to_xml_name(char *ident, bool fully_escaped, bool escape_period)
Definition: xml.c:1837
void pfree(void *pointer)
Definition: mcxt.c:995
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
#define TIMESTAMPOID
Definition: pg_type.h:507
#define ObjectIdGetDatum(X)
Definition: postgres.h:515
#define ERROR
Definition: elog.h:43
Datum schema_to_xmlschema(PG_FUNCTION_ARGS)
Definition: xml.c:2764
#define DatumGetCString(X)
Definition: postgres.h:574
Expr * expr
Definition: execnodes.h:578
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:46
static char * _SPI_strdup(const char *s)
Definition: xml.c:2204
Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
Definition: spi.c:898
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2024
Definition: c.h:488
static void appendStringInfoText(StringInfo str, const text *t)
Definition: varlena.c:3539
#define INT2OID
Definition: pg_type.h:308
#define XMLOID
Definition: pg_type.h:359
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:157
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:2344
#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:162
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:2777
#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:4062
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
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:2968
#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:5057
unsigned int pg_wchar
Definition: mbprint.c:31
void j2date(int jd, int *year, int *month, int *day)
Definition: datetime.c:321
MemoryContext TopMemoryContext
Definition: mcxt.c:43
List * lappend(List *list, void *datum)
Definition: list.c:128
static const char * map_sql_typecoll_to_xmlschema_types(List *tupdesc_list)
Definition: xml.c:3306
void * SPI_palloc(Size size)
Definition: spi.c:984
bool xml_is_document(xmltype *arg)
Definition: xml.c:837
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:169
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
#define WARNING
Definition: elog.h:40
static const char * map_sql_type_to_xml_name(Oid typeoid, int typmod)
Definition: xml.c:3201
#define VARCHAROID
Definition: pg_type.h:495
#define PG_GETARG_XML_P(n)
Definition: xml.h:52
#define TextDatumGetCString(d)
Definition: builtins.h:807
static List * database_get_xml_visible_schemas(void)
Definition: xml.c:2307
#define FLOAT4OID
Definition: pg_type.h:408
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:436
double Timestamp
Definition: timestamp.h:50
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:303
uintptr_t Datum
Definition: postgres.h:374
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:990
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:2712
Oid MyDatabaseId
Definition: globals.c:74
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1298
Datum xml_is_well_formed(PG_FUNCTION_ARGS)
Definition: xml.c:4039
TupleDesc tupdesc
Definition: spi.h:26
TupleDesc rd_att
Definition: rel.h:84
#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:2451
static StringInfo database_to_xmlschema_internal(bool nulls, bool tableforest, const char *targetns)
Definition: xml.c:2863
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:125
#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:292
Datum xmlconcat2(PG_FUNCTION_ARGS)
Definition: xml.c:527
text * cstring_to_text(const char *s)
Definition: varlena.c:150
Datum database_to_xml(PG_FUNCTION_ARGS)
Definition: xml.c:2851
#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:2327
#define CSTRINGOID
Definition: pg_type.h:672
#define Assert(condition)
Definition: c.h:667
#define lfirst(lc)
Definition: pg_list.h:106
Datum schema_to_xml(PG_FUNCTION_ARGS)
Definition: xml.c:2673
Datum regclassout(PG_FUNCTION_ARGS)
Definition: regproc.c:1087
char * map_xml_name_to_sql_identifier(char *name)
Definition: xml.c:1913
static const char * map_sql_schema_to_xmlschema_types(Oid nspid, List *relid_list, bool nulls, bool tableforest, const char *targetns)
Definition: xml.c:3071
XmlOptionType
Definition: primnodes.h:1067
Datum xmlexists(PG_FUNCTION_ARGS)
Definition: xml.c:3966
#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:42
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
List * named_args
Definition: execnodes.h:957
#define FLOAT8OID
Definition: pg_type.h:411
static void xmldata_root_element_end(StringInfo result, const char *eltname)
Definition: xml.c:2444
#define PG_RE_THROW()
Definition: elog.h:313
int pg_mblen(const char *mbstr)
Definition: mbutils.c:771
#define BOOLOID
Definition: pg_type.h:288
double timestamp
#define ARR_NDIM(a)
Definition: array.h:271
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1024
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:1486
FormData_pg_class * Form_pg_class
Definition: pg_class.h:92
char * text_to_cstring(const text *t)
Definition: varlena.c:183
ArrayBuildState * accumArrayResult(ArrayBuildState *astate, Datum dvalue, bool disnull, Oid element_type, MemoryContext rcontext)
Definition: arrayfuncs.c:4993
Datum table_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
Definition: xml.c:2570
Datum xpath_exists(PG_FUNCTION_ARGS)
Definition: xml.c:3989
e
Definition: preproc-init.c:82
tuple list
Definition: sort-test.py:11
#define USE_XSD_DATES
Definition: miscadmin.h:212
int tm_year
Definition: pgtime.h:32
Datum table_to_xmlschema(PG_FUNCTION_ARGS)
Definition: xml.c:2496
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:2048
#define VARSIZE_ANY_EXHDR(PTR)
Definition: postgres.h:342
void * palloc(Size size)
Definition: mcxt.c:894
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:1155
static xmltype * stringinfo_to_xmltype(StringInfo buf)
Definition: xml.c:396
int xmloption
Definition: xml.c:96
void SPI_pop(void)
Definition: spi.c:329
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:752
Datum texttoxml(PG_FUNCTION_ARGS)
Definition: xml.c:545
Datum query_to_xml(PG_FUNCTION_ARGS)
Definition: xml.c:2358
#define ALLOCSET_DEFAULT_INITSIZE
Definition: memutils.h:143
int i
xmltype * xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
Definition: xml.c:577
#define NameStr(name)
Definition: c.h:494
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:1430
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:233
Definition: c.h:434
#define PG_FUNCTION_ARGS
Definition: fmgr.h:150
#define ALLOCSET_DEFAULT_MAXSIZE
Definition: memutils.h:144
#define POSTGRES_EPOCH_JDATE
Definition: timestamp.h:185
#define SET_VARSIZE(PTR, len)
Definition: postgres.h:330
Datum cursor_to_xml(PG_FUNCTION_ARGS)
Definition: xml.c:2372
#define elog
Definition: elog.h:218
Datum query_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
Definition: xml.c:2591
char * escape_xml(const char *str)
Definition: xml.c:2172
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:572
#define PG_TRY()
Definition: elog.h:283
char * map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings)
Definition: xml.c:1953
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2239
static const char * map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
Definition: xml.c:3360
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:208
PgXmlErrorContext * pg_xml_init(PgXmlStrictness strictness)
#define PG_RETURN_NULL()
Definition: fmgr.h:289
#define PG_END_TRY()
Definition: elog.h:299
Datum database_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
Definition: xml.c:2918
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:3535
#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:2544
static char * unicode_to_sqlchar(pg_wchar c)
Definition: xml.c:1893
int SPI_execute(const char *src, bool read_only, long tcount)
Definition: spi.c:369
#define DatumGetTimestamp(X)
Definition: timestamp.h:47
static List * database_get_xml_visible_tables(void)
Definition: xml.c:2314
#define ExecEvalExpr(expr, econtext, isNull, isDone)
Definition: executor.h:72
#define DatumGetArrayTypeP(X)
Definition: array.h:242