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