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