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