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