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