OSDN Git Service

Replace direct assignments to VARATT_SIZEP(x) with SET_VARSIZE(x, len).
[pg-rex/syncrep.git] / src / backend / utils / adt / xml.c
1 /*-------------------------------------------------------------------------
2  *
3  * xml.c
4  *        XML data type support.
5  *
6  *
7  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.32 2007/02/27 23:48:09 tgl Exp $
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  * Note on memory management: Via callbacks, libxml is told to use
28  * palloc and friends for memory management.  Sometimes, libxml
29  * allocates global structures in the hope that it can reuse them
30  * later on, but if "later" is much later, the memory context
31  * management of PostgreSQL will have blown those structures away
32  * without telling libxml about it.  Therefore, it is important to
33  * call xmlCleanupParser() or perhaps some other cleanup function
34  * after using such functions, for example something from
35  * libxml/parser.h or libxml/xmlsave.h.  Unfortunately, you cannot
36  * readily tell from the API documentation when that happens, so
37  * careful evaluation is necessary when introducing new libxml APIs
38  * here.
39  */
40
41 #include "postgres.h"
42
43 #ifdef USE_LIBXML
44 #include <libxml/chvalid.h>
45 #include <libxml/parser.h>
46 #include <libxml/tree.h>
47 #include <libxml/uri.h>
48 #include <libxml/xmlerror.h>
49 #include <libxml/xmlwriter.h>
50 #endif /* USE_LIBXML */
51
52 #include "catalog/namespace.h"
53 #include "catalog/pg_type.h"
54 #include "commands/dbcommands.h"
55 #include "executor/executor.h"
56 #include "executor/spi.h"
57 #include "fmgr.h"
58 #include "lib/stringinfo.h"
59 #include "libpq/pqformat.h"
60 #include "mb/pg_wchar.h"
61 #include "miscadmin.h"
62 #include "nodes/execnodes.h"
63 #include "parser/parse_expr.h"
64 #include "utils/array.h"
65 #include "utils/builtins.h"
66 #include "utils/lsyscache.h"
67 #include "utils/memutils.h"
68 #include "utils/xml.h"
69
70
71 #ifdef USE_LIBXML
72
73 static StringInfo xml_err_buf = NULL;
74
75 static void     xml_init(void);
76 static void    *xml_palloc(size_t size);
77 static void    *xml_repalloc(void *ptr, size_t size);
78 static void     xml_pfree(void *ptr);
79 static char    *xml_pstrdup(const char *string);
80 static void     xml_ereport(int level, int sqlcode,
81                                                         const char *msg);
82 static void     xml_errorHandler(void *ctxt, const char *msg, ...);
83 static void     xml_ereport_by_code(int level, int sqlcode,
84                                                                         const char *msg, int errcode);
85 static xmlChar *xml_text2xmlChar(text *in);
86 static int              parse_xml_decl(const xmlChar *str, size_t *lenp, xmlChar **version, xmlChar **encoding, int *standalone);
87 static bool             print_xml_decl(StringInfo buf, const xmlChar *version, pg_enc encoding, int standalone);
88 static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, xmlChar *encoding);
89
90 #endif /* USE_LIBXML */
91
92 static StringInfo query_to_xml_internal(const char *query, char *tablename, const char *xmlschema, bool nulls, bool tableforest, const char *targetns);
93 static const char * map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls, bool tableforest, const char *targetns);
94 static const char * map_sql_type_to_xml_name(Oid typeoid, int typmod);
95 static const char * map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc);
96 static const char * map_sql_type_to_xmlschema_type(Oid typeoid, int typmod);
97 static void SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns);
98
99
100 XmlBinaryType xmlbinary;
101 XmlOptionType xmloption;
102
103
104 #define NO_XML_SUPPORT() \
105         ereport(ERROR, \
106                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
107                          errmsg("no XML support in this installation")))
108
109
110 #define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))
111 #define _textout(x) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(x)))
112
113
114 /* from SQL/XML:2003 section 4.7 */
115 #define NAMESPACE_XSD "http://www.w3.org/2001/XMLSchema"
116 #define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance"
117 #define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml"
118
119
120 Datum
121 xml_in(PG_FUNCTION_ARGS)
122 {
123 #ifdef USE_LIBXML
124         char            *s = PG_GETARG_CSTRING(0);
125         size_t          len;
126         xmltype         *vardata;
127         xmlDocPtr        doc;
128
129         len = strlen(s);
130         vardata = palloc(len + VARHDRSZ);
131         SET_VARSIZE(vardata, len + VARHDRSZ);
132         memcpy(VARDATA(vardata), s, len);
133
134         /*
135          * Parse the data to check if it is well-formed XML data.  Assume
136          * that ERROR occurred if parsing failed.
137          */
138         doc = xml_parse(vardata, xmloption, true, NULL);
139         xmlFreeDoc(doc);
140
141         PG_RETURN_XML_P(vardata);
142 #else
143         NO_XML_SUPPORT();
144         return 0;
145 #endif
146 }
147
148
149 #define PG_XML_DEFAULT_VERSION "1.0"
150
151
152 static char *
153 xml_out_internal(xmltype *x, pg_enc target_encoding)
154 {
155         char            *str;
156         size_t          len;
157 #ifdef USE_LIBXML
158         xmlChar         *version;
159         xmlChar         *encoding;
160         int                     standalone;
161         int                     res_code;
162 #endif
163
164         len = VARSIZE(x) - VARHDRSZ;
165         str = palloc(len + 1);
166         memcpy(str, VARDATA(x), len);
167         str[len] = '\0';
168
169 #ifdef USE_LIBXML
170         if ((res_code = parse_xml_decl((xmlChar *) str, &len, &version, &encoding, &standalone)) == 0)
171         {
172                 StringInfoData buf;
173
174                 initStringInfo(&buf);
175
176                 if (!print_xml_decl(&buf, version, target_encoding, standalone))
177                 {
178                         /*
179                          * If we are not going to produce an XML declaration, eat
180                          * a single newline in the original string to prevent
181                          * empty first lines in the output.
182                          */
183                         if (*(str + len) == '\n')
184                                 len += 1;
185                 }
186                 appendStringInfoString(&buf, str + len);
187
188                 return buf.data;
189         }
190
191         xml_ereport_by_code(WARNING, ERRCODE_INTERNAL_ERROR,
192                                                 "could not parse XML declaration in stored value", res_code);
193 #endif
194         return str;
195 }
196
197
198 Datum
199 xml_out(PG_FUNCTION_ARGS)
200 {
201         xmltype    *x = PG_GETARG_XML_P(0);
202
203         /*
204          * xml_out removes the encoding property in all cases.  This is
205          * because we cannot control from here whether the datum will be
206          * converted to a different client encoding, so we'd do more harm
207          * than good by including it.
208          */
209         PG_RETURN_CSTRING(xml_out_internal(x, 0));
210 }
211
212
213 Datum
214 xml_recv(PG_FUNCTION_ARGS)
215 {
216 #ifdef USE_LIBXML
217         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
218         xmltype    *result;
219         char       *str;
220         char       *newstr;
221         int                     nbytes;
222         xmlDocPtr       doc;
223         xmlChar    *encoding = NULL;
224
225         str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
226
227         result = palloc(nbytes + VARHDRSZ);
228         SET_VARSIZE(result, nbytes + VARHDRSZ);
229         memcpy(VARDATA(result), str, nbytes);
230
231         parse_xml_decl((xmlChar *) str, NULL, NULL, &encoding, NULL);
232
233         /*
234          * Parse the data to check if it is well-formed XML data.  Assume
235          * that ERROR occurred if parsing failed.
236          */
237         doc = xml_parse(result, xmloption, true, encoding);
238         xmlFreeDoc(doc);
239
240         newstr = (char *) pg_do_encoding_conversion((unsigned char *) str,
241                                                                                                 nbytes,
242                                                                                                 encoding ? pg_char_to_encoding((char *) encoding) : PG_UTF8,
243                                                                                                 GetDatabaseEncoding());
244
245         pfree(str);
246
247         if (newstr != str)
248         {
249                 free(result);
250
251                 nbytes = strlen(newstr);
252
253                 result = palloc(nbytes + VARHDRSZ);
254                 SET_VARSIZE(result, nbytes + VARHDRSZ);
255                 memcpy(VARDATA(result), newstr, nbytes);
256         }
257
258         PG_RETURN_XML_P(result);
259 #else
260         NO_XML_SUPPORT();
261         return 0;
262 #endif
263 }
264
265
266 Datum
267 xml_send(PG_FUNCTION_ARGS)
268 {
269         xmltype    *x = PG_GETARG_XML_P(0);
270         char       *outval = xml_out_internal(x, pg_get_client_encoding());
271         StringInfoData buf;
272
273         pq_begintypsend(&buf);
274         pq_sendstring(&buf, outval);
275         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
276 }
277
278
279 #ifdef USE_LIBXML
280 static void
281 appendStringInfoText(StringInfo str, const text *t)
282 {
283         appendBinaryStringInfo(str, VARDATA(t), VARSIZE(t) - VARHDRSZ);
284 }
285 #endif
286
287
288 static xmltype *
289 stringinfo_to_xmltype(StringInfo buf)
290 {
291         int32 len;
292         xmltype *result;
293
294         len = buf->len + VARHDRSZ;
295         result = palloc(len);
296         SET_VARSIZE(result, len);
297         memcpy(VARDATA(result), buf->data, buf->len);
298
299         return result;
300 }
301
302
303 static xmltype *
304 cstring_to_xmltype(const char *string)
305 {
306         int32           len;
307         xmltype    *result;
308
309         len = strlen(string) + VARHDRSZ;
310         result = palloc(len);
311         SET_VARSIZE(result, len);
312         memcpy(VARDATA(result), string, len - VARHDRSZ);
313
314         return result;
315 }
316
317
318 #ifdef USE_LIBXML
319 static xmltype *
320 xmlBuffer_to_xmltype(xmlBufferPtr buf)
321 {
322         int32           len;
323         xmltype    *result;
324
325         len = xmlBufferLength(buf) + VARHDRSZ;
326         result = palloc(len);
327         SET_VARSIZE(result, len);
328         memcpy(VARDATA(result), xmlBufferContent(buf), len - VARHDRSZ);
329
330         return result;
331 }
332 #endif
333
334
335 Datum
336 xmlcomment(PG_FUNCTION_ARGS)
337 {
338 #ifdef USE_LIBXML
339         text *arg = PG_GETARG_TEXT_P(0);
340         int len =  VARSIZE(arg) - VARHDRSZ;
341         StringInfoData buf;
342         int i;
343
344         /* check for "--" in string or "-" at the end */
345         for (i = 1; i < len; i++)
346                 if ((VARDATA(arg)[i] == '-' && VARDATA(arg)[i - 1] == '-')
347                         || (VARDATA(arg)[i] == '-' && i == len - 1))
348                                         ereport(ERROR,
349                                                         (errcode(ERRCODE_INVALID_XML_COMMENT),
350                                                          errmsg("invalid XML comment")));
351
352         initStringInfo(&buf);
353         appendStringInfo(&buf, "<!--");
354         appendStringInfoText(&buf, arg);
355         appendStringInfo(&buf, "-->");
356
357         PG_RETURN_XML_P(stringinfo_to_xmltype(&buf));
358 #else
359         NO_XML_SUPPORT();
360         return 0;
361 #endif
362 }
363
364
365
366 /*
367  * TODO: xmlconcat needs to merge the notations and unparsed entities
368  * of the argument values.  Not very important in practice, though.
369  */
370 xmltype *
371 xmlconcat(List *args)
372 {
373 #ifdef USE_LIBXML
374         StringInfoData buf;
375         ListCell   *v;
376
377         int                     global_standalone = 1;
378         xmlChar    *global_version = NULL;
379         bool            global_version_no_value = false;
380
381         initStringInfo(&buf);
382         foreach(v, args)
383         {
384                 size_t          len;
385                 xmlChar    *version;
386                 int                     standalone;
387                 xmltype    *x = DatumGetXmlP(PointerGetDatum(lfirst(v)));
388                 char       *str;
389
390                 len = VARSIZE(x) - VARHDRSZ;
391                 str = palloc(len + 1);
392                 memcpy(str, VARDATA(x), len);
393                 str[len] = '\0';
394
395                 parse_xml_decl((xmlChar *) str, &len, &version, NULL, &standalone);
396
397                 if (standalone == 0 && global_standalone == 1)
398                         global_standalone = 0;
399                 if (standalone < 0)
400                         global_standalone = -1;
401
402                 if (!version)
403                         global_version_no_value = true;
404                 else if (!global_version)
405                         global_version = xmlStrdup(version);
406                 else if (xmlStrcmp(version, global_version) != 0)
407                         global_version_no_value = true;
408
409                 appendStringInfoString(&buf, str + len);
410                 pfree(str);
411         }
412
413         if (!global_version_no_value || global_standalone >= 0)
414         {
415                 StringInfoData buf2;
416
417                 initStringInfo(&buf2);
418
419                 print_xml_decl(&buf2,
420                                            (!global_version_no_value && global_version) ? global_version : NULL,
421                                            0,
422                                            global_standalone);
423
424                 appendStringInfoString(&buf2, buf.data);
425                 buf = buf2;
426         }
427
428         return stringinfo_to_xmltype(&buf);
429 #else
430         NO_XML_SUPPORT();
431         return NULL;
432 #endif
433 }
434
435
436 /*
437  * XMLAGG support
438  */
439 Datum
440 xmlconcat2(PG_FUNCTION_ARGS)
441 {
442         if (PG_ARGISNULL(0))
443         {
444                 if (PG_ARGISNULL(1))
445                         PG_RETURN_NULL();
446                 else
447                         PG_RETURN_XML_P(PG_GETARG_XML_P(1));
448         }
449         else if (PG_ARGISNULL(1))
450                 PG_RETURN_XML_P(PG_GETARG_XML_P(0));
451         else
452                 PG_RETURN_XML_P(xmlconcat(list_make2(PG_GETARG_XML_P(0), PG_GETARG_XML_P(1))));
453 }
454
455
456 Datum
457 texttoxml(PG_FUNCTION_ARGS)
458 {
459         text       *data = PG_GETARG_TEXT_P(0);
460
461         PG_RETURN_XML_P(xmlparse(data, xmloption, true));
462 }
463
464
465 Datum
466 xmltotext(PG_FUNCTION_ARGS)
467 {
468         xmltype    *data = PG_GETARG_XML_P(0);
469
470         PG_RETURN_TEXT_P(xmltotext_with_xmloption(data, xmloption));
471 }
472
473
474 text *
475 xmltotext_with_xmloption(xmltype *data, XmlOptionType xmloption_arg)
476 {
477         if (xmloption_arg == XMLOPTION_DOCUMENT && !xml_is_document(data))
478                 ereport(ERROR,
479                                 (errcode(ERRCODE_NOT_AN_XML_DOCUMENT),
480                                  errmsg("not an XML document")));
481
482         /* It's actually binary compatible, save for the above check. */
483         return (text *) data;
484 }
485
486
487 xmltype *
488 xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
489 {
490 #ifdef USE_LIBXML
491         XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
492         int                     i;
493         ListCell   *arg;
494         ListCell   *narg;
495         bool            isnull;
496         xmltype    *result;
497         Datum           value;
498         char       *str;
499
500         xmlBufferPtr buf;
501         xmlTextWriterPtr writer;
502
503         buf = xmlBufferCreate();
504         writer = xmlNewTextWriterMemory(buf, 0);
505
506         xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
507
508         i = 0;
509         forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names)
510         {
511                 ExprState       *e = (ExprState *) lfirst(arg);
512                 char    *argname = strVal(lfirst(narg));
513
514                 value = ExecEvalExpr(e, econtext, &isnull, NULL);
515                 if (!isnull)
516                 {
517                         str = OutputFunctionCall(&xmlExpr->named_outfuncs[i], value);
518                         xmlTextWriterWriteAttribute(writer, (xmlChar *) argname, (xmlChar *) str);
519                         pfree(str);
520                 }
521                 i++;
522         }
523
524         foreach(arg, xmlExpr->args)
525         {
526                 ExprState       *e = (ExprState *) lfirst(arg);
527
528                 value = ExecEvalExpr(e, econtext, &isnull, NULL);
529                 if (!isnull)
530                         xmlTextWriterWriteRaw(writer, (xmlChar *) map_sql_value_to_xml_value(value, exprType((Node *) e->expr)));
531         }
532
533         xmlTextWriterEndElement(writer);
534         xmlFreeTextWriter(writer);
535
536         result = xmlBuffer_to_xmltype(buf);
537         xmlBufferFree(buf);
538         return result;
539 #else
540         NO_XML_SUPPORT();
541         return NULL;
542 #endif
543 }
544
545
546 xmltype *
547 xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace)
548 {
549 #ifdef USE_LIBXML
550         xmlDocPtr       doc;
551
552         doc = xml_parse(data, xmloption_arg, preserve_whitespace, NULL);
553         xmlFreeDoc(doc);
554
555         return (xmltype *) data;
556 #else
557         NO_XML_SUPPORT();
558         return NULL;
559 #endif
560 }
561
562
563 xmltype *
564 xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null)
565 {
566 #ifdef USE_LIBXML
567         xmltype *result;
568         StringInfoData buf;
569
570         if (pg_strncasecmp(target, "xml", 3) == 0)
571                 ereport(ERROR,
572                                 (errcode(ERRCODE_SYNTAX_ERROR), /* really */
573                                  errmsg("invalid XML processing instruction"),
574                                  errdetail("XML processing instruction target name cannot start with \"xml\".")));
575
576         /*
577          * Following the SQL standard, the null check comes after the
578          * syntax check above.
579          */
580         *result_is_null = arg_is_null;
581         if (*result_is_null)
582                 return NULL;            
583
584         initStringInfo(&buf);
585
586         appendStringInfo(&buf, "<?%s", target);
587
588         if (arg != NULL)
589         {
590                 char *string;
591
592                 string = DatumGetCString(DirectFunctionCall1(textout,
593                                                                                                          PointerGetDatum(arg)));
594                 if (strstr(string, "?>") != NULL)
595                 ereport(ERROR,
596                                 (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
597                                  errmsg("invalid XML processing instruction"),
598                                  errdetail("XML processing instruction cannot contain \"?>\".")));
599
600                 appendStringInfoChar(&buf, ' ');
601                 appendStringInfoString(&buf, string + strspn(string, " "));
602                 pfree(string);
603         }
604         appendStringInfoString(&buf, "?>");
605
606         result = stringinfo_to_xmltype(&buf);
607         pfree(buf.data);
608         return result;
609 #else
610         NO_XML_SUPPORT();
611         return NULL;
612 #endif
613 }
614
615
616 xmltype *
617 xmlroot(xmltype *data, text *version, int standalone)
618 {
619 #ifdef USE_LIBXML
620         char       *str;
621         size_t          len;
622         xmlChar    *orig_version;
623         int                     orig_standalone;
624         StringInfoData buf;
625
626         len = VARSIZE(data) - VARHDRSZ;
627         str = palloc(len + 1);
628         memcpy(str, VARDATA(data), len);
629         str[len] = '\0';
630
631         parse_xml_decl((xmlChar *) str, &len, &orig_version, NULL, &orig_standalone);
632
633         if (version)
634                 orig_version = xml_text2xmlChar(version);
635         else
636                 orig_version = NULL;
637
638         switch (standalone)
639         {
640                 case XML_STANDALONE_YES:
641                         orig_standalone = 1;
642                         break;
643                 case XML_STANDALONE_NO:
644                         orig_standalone = 0;
645                         break;
646                 case XML_STANDALONE_NO_VALUE:
647                         orig_standalone = -1;
648                         break;
649                 case XML_STANDALONE_OMITTED:
650                         /* leave original value */
651                         break;
652         }
653
654         initStringInfo(&buf);
655         print_xml_decl(&buf, orig_version, 0, orig_standalone);
656         appendStringInfoString(&buf, str + len);
657
658         return stringinfo_to_xmltype(&buf);
659 #else
660         NO_XML_SUPPORT();
661         return NULL;
662 #endif
663 }
664
665
666 /*
667  * Validate document (given as string) against DTD (given as external link)
668  * TODO !!! use text instead of cstring for second arg
669  * TODO allow passing DTD as a string value (not only as an URI)
670  * TODO redesign (see comment with '!!!' below)
671  */
672 Datum
673 xmlvalidate(PG_FUNCTION_ARGS)
674 {
675 #ifdef USE_LIBXML
676         text                            *data = PG_GETARG_TEXT_P(0);
677         text                            *dtdOrUri = PG_GETARG_TEXT_P(1);
678         bool                            result = false;
679         xmlParserCtxtPtr        ctxt = NULL;
680         xmlDocPtr                       doc = NULL;
681         xmlDtdPtr                       dtd = NULL;
682
683         xml_init();
684
685         /* We use a PG_TRY block to ensure libxml is cleaned up on error */
686         PG_TRY();
687         {
688                 ctxt = xmlNewParserCtxt();
689                 if (ctxt == NULL)
690                         xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
691                                                 "could not allocate parser context");
692
693                 doc = xmlCtxtReadMemory(ctxt, (char *) VARDATA(data),
694                                                                 VARSIZE(data) - VARHDRSZ,
695                                                                 NULL, NULL, 0);
696                 if (doc == NULL)
697                         xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
698                                                 "could not parse XML data");
699
700 #if 0
701                 uri = xmlCreateURI();
702                 elog(NOTICE, "dtd - %s", dtdOrUri);
703                 dtd = palloc(sizeof(xmlDtdPtr));
704                 uri = xmlParseURI(dtdOrUri);
705                 if (uri == NULL)
706                         xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
707                                                 "not implemented yet... (TODO)");
708                 else
709 #endif
710                         dtd = xmlParseDTD(NULL, xml_text2xmlChar(dtdOrUri));
711
712                 if (dtd == NULL)
713                         xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
714                                                 "could not load DTD");
715
716                 if (xmlValidateDtd(xmlNewValidCtxt(), doc, dtd) == 1)
717                         result = true;
718
719                 if (!result)
720                         xml_ereport(NOTICE, ERRCODE_INVALID_XML_DOCUMENT,
721                                                 "validation against DTD failed");
722
723 #if 0
724                 if (uri)
725                         xmlFreeURI(uri);
726 #endif
727                 if (dtd)
728                         xmlFreeDtd(dtd);
729                 if (doc)
730                         xmlFreeDoc(doc);
731                 if (ctxt)
732                         xmlFreeParserCtxt(ctxt);
733                 xmlCleanupParser();
734         }
735         PG_CATCH();
736         {
737 #if 0
738                 if (uri)
739                         xmlFreeURI(uri);
740 #endif
741                 if (dtd)
742                         xmlFreeDtd(dtd);
743                 if (doc)
744                         xmlFreeDoc(doc);
745                 if (ctxt)
746                         xmlFreeParserCtxt(ctxt);
747                 xmlCleanupParser();
748
749                 PG_RE_THROW();
750         }
751         PG_END_TRY();
752
753         PG_RETURN_BOOL(result);
754 #else /* not USE_LIBXML */
755         NO_XML_SUPPORT();
756         return 0;
757 #endif /* not USE_LIBXML */
758 }
759
760
761 bool
762 xml_is_document(xmltype *arg)
763 {
764 #ifdef USE_LIBXML
765         bool            result;
766         xmlDocPtr       doc = NULL;
767         MemoryContext ccxt = CurrentMemoryContext;
768
769         PG_TRY();
770         {
771                 doc = xml_parse((text *) arg, XMLOPTION_DOCUMENT, true, NULL);
772                 result = true;
773         }
774         PG_CATCH();
775         {
776                 ErrorData *errdata;
777                 MemoryContext ecxt;
778
779                 ecxt = MemoryContextSwitchTo(ccxt);
780                 errdata = CopyErrorData();
781                 if (errdata->sqlerrcode == ERRCODE_INVALID_XML_DOCUMENT)
782                 {
783                         FlushErrorState();
784                         result = false;
785                 }
786                 else
787                 {
788                         MemoryContextSwitchTo(ecxt);
789                         PG_RE_THROW();
790                 }
791         }
792         PG_END_TRY();
793
794         if (doc)
795                 xmlFreeDoc(doc);
796
797         return result;
798 #else /* not USE_LIBXML */
799         NO_XML_SUPPORT();
800         return false;
801 #endif /* not USE_LIBXML */
802 }
803
804
805 #ifdef USE_LIBXML
806
807 /*
808  * Container for some init stuff (not good design!)
809  * TODO xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and check)
810  */
811 static void
812 xml_init(void)
813 {
814         /*
815          * Currently, we have no pure UTF-8 support for internals -- check
816          * if we can work.
817          */
818         if (sizeof (char) != sizeof (xmlChar))
819                 ereport(ERROR,
820                                 (errmsg("could not initialize XML library"),
821                                  errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.",
822                                                    (int) sizeof(char), (int) sizeof(xmlChar))));
823
824         if (xml_err_buf == NULL)
825         {
826                 /* First time through: create error buffer in permanent context */
827                 MemoryContext oldcontext;
828
829                 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
830                 xml_err_buf = makeStringInfo();
831                 MemoryContextSwitchTo(oldcontext);
832         }
833         else
834         {
835                 /* Reset pre-existing buffer to empty */
836                 xml_err_buf->data[0] = '\0';
837                 xml_err_buf->len = 0;
838         }
839         /* Now that xml_err_buf exists, safe to call xml_errorHandler */
840         xmlSetGenericErrorFunc(NULL, xml_errorHandler);
841
842         xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
843
844         xmlInitParser();
845         LIBXML_TEST_VERSION;
846 }
847
848
849 /*
850  * SQL/XML allows storing "XML documents" or "XML content".  "XML
851  * documents" are specified by the XML specification and are parsed
852  * easily by libxml.  "XML content" is specified by SQL/XML as the
853  * production "XMLDecl? content".  But libxml can only parse the
854  * "content" part, so we have to parse the XML declaration ourselves
855  * to complete this.
856  */
857
858 #define CHECK_XML_SPACE(p) if (!xmlIsBlank_ch(*(p))) return XML_ERR_SPACE_REQUIRED
859 #define SKIP_XML_SPACE(p) while (xmlIsBlank_ch(*(p))) (p)++
860
861 static int
862 parse_xml_decl(const xmlChar *str, size_t *lenp, xmlChar **version, xmlChar **encoding, int *standalone)
863 {
864         const xmlChar *p;
865         const xmlChar *save_p;
866         size_t          len;
867
868         p = str;
869
870         if (version)
871                 *version = NULL;
872         if (encoding)
873                 *encoding = NULL;
874         if (standalone)
875                 *standalone = -1;
876
877         if (xmlStrncmp(p, (xmlChar *)"<?xml", 5) != 0)
878                 goto finished;
879
880         p += 5;
881
882         /* version */
883         CHECK_XML_SPACE(p);
884         SKIP_XML_SPACE(p);
885         if (xmlStrncmp(p, (xmlChar *)"version", 7) != 0)
886                 return XML_ERR_VERSION_MISSING;
887         p += 7;
888         SKIP_XML_SPACE(p);
889         if (*p != '=')
890                 return XML_ERR_VERSION_MISSING;
891         p += 1;
892         SKIP_XML_SPACE(p);
893
894         if (*p == '\'' || *p == '"')
895         {
896                 const xmlChar *q;
897
898                 q = xmlStrchr(p + 1, *p);
899                 if (!q)
900                         return XML_ERR_VERSION_MISSING;
901
902                 if (version)
903                         *version = xmlStrndup(p + 1, q - p - 1);
904                 p = q + 1;
905         }
906         else
907                 return XML_ERR_VERSION_MISSING;
908
909         /* encoding */
910         save_p = p;
911         SKIP_XML_SPACE(p);
912         if (xmlStrncmp(p, (xmlChar *)"encoding", 8) == 0)
913         {
914                 CHECK_XML_SPACE(save_p);
915                 p += 8;
916                 SKIP_XML_SPACE(p);
917                 if (*p != '=')
918                         return XML_ERR_MISSING_ENCODING;
919                 p += 1;
920                 SKIP_XML_SPACE(p);
921
922                 if (*p == '\'' || *p == '"')
923                 {
924                         const xmlChar *q;
925
926                         q = xmlStrchr(p + 1, *p);
927                         if (!q)
928                                 return XML_ERR_MISSING_ENCODING;
929
930                         if (encoding)
931                         *encoding = xmlStrndup(p + 1, q - p - 1);
932                         p = q + 1;
933                 }
934                 else
935                         return XML_ERR_MISSING_ENCODING;
936         }
937         else
938         {
939                 p = save_p;
940         }
941
942         /* standalone */
943         save_p = p;
944         SKIP_XML_SPACE(p);
945         if (xmlStrncmp(p, (xmlChar *)"standalone", 10) == 0)
946         {
947                 CHECK_XML_SPACE(save_p);
948                 p += 10;
949                 SKIP_XML_SPACE(p);
950                 if (*p != '=')
951                         return XML_ERR_STANDALONE_VALUE;
952                 p += 1;
953                 SKIP_XML_SPACE(p);
954                 if (xmlStrncmp(p, (xmlChar *)"'yes'", 5) == 0 || xmlStrncmp(p, (xmlChar *)"\"yes\"", 5) == 0)
955                 {
956                         *standalone = 1;
957                         p += 5;
958                 }
959                 else if (xmlStrncmp(p, (xmlChar *)"'no'", 4) == 0 || xmlStrncmp(p, (xmlChar *)"\"no\"", 4) == 0)
960                 {
961                         *standalone = 0;
962                         p += 4;
963                 }
964                 else
965                         return XML_ERR_STANDALONE_VALUE;
966         }
967         else
968         {
969                 p = save_p;
970         }
971
972         SKIP_XML_SPACE(p);
973         if (xmlStrncmp(p, (xmlChar *)"?>", 2) != 0)
974                 return XML_ERR_XMLDECL_NOT_FINISHED;
975         p += 2;
976
977 finished:
978         len = p - str;
979
980         for (p = str; p < str + len; p++)
981                 if (*p > 127)
982                         return XML_ERR_INVALID_CHAR;
983
984         if (lenp)
985                 *lenp = len;
986
987         return XML_ERR_OK;
988 }
989
990
991 /*
992  * Write an XML declaration.  On output, we adjust the XML declaration
993  * as follows.  (These rules are the moral equivalent of the clause
994  * "Serialization of an XML value" in the SQL standard.)
995  *
996  * We try to avoid generating an XML declaration if possible.  This is
997  * so that you don't get trivial things like xml '<foo/>' resulting in
998  * '<?xml version="1.0"?><foo/>', which would surely be annoying.  We
999  * must provide a declaration if the standalone property is specified
1000  * or if we include an encoding declaration.  If we have a
1001  * declaration, we must specify a version (XML requires this).
1002  * Otherwise we only make a declaration if the version is not "1.0",
1003  * which is the default version specified in SQL:2003.
1004  */
1005 static bool
1006 print_xml_decl(StringInfo buf, const xmlChar *version, pg_enc encoding, int standalone)
1007 {
1008         if ((version && strcmp((char *) version, PG_XML_DEFAULT_VERSION) != 0)
1009                 || (encoding && encoding != PG_UTF8)
1010                 || standalone != -1)
1011         {
1012                 appendStringInfoString(buf, "<?xml");
1013
1014                 if (version)
1015                         appendStringInfo(buf, " version=\"%s\"", version);
1016                 else
1017                         appendStringInfo(buf, " version=\"%s\"", PG_XML_DEFAULT_VERSION);
1018
1019                 if (encoding && encoding != PG_UTF8)
1020                         /* XXX might be useful to convert this to IANA names
1021                          * (ISO-8859-1 instead of LATIN1 etc.); needs field
1022                          * experience */
1023                         appendStringInfo(buf, " encoding=\"%s\"", pg_encoding_to_char(encoding));
1024
1025                 if (standalone == 1)
1026                         appendStringInfoString(buf, " standalone=\"yes\"");
1027                 else if (standalone == 0)
1028                         appendStringInfoString(buf, " standalone=\"no\"");
1029                 appendStringInfoString(buf, "?>");
1030
1031                 return true;
1032         }
1033         else
1034                 return false;
1035 }
1036
1037
1038 /*
1039  * Convert a C string to XML internal representation
1040  *
1041  * TODO maybe, libxml2's xmlreader is better? (do not construct DOM, yet do not use SAX - see xml_reader.c)
1042  */
1043 static xmlDocPtr
1044 xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, xmlChar *encoding)
1045 {
1046         int32                           len;
1047         xmlChar                         *string;
1048         xmlChar                         *utf8string;
1049         xmlParserCtxtPtr        ctxt = NULL;
1050         xmlDocPtr                       doc = NULL;
1051
1052         len = VARSIZE(data) - VARHDRSZ; /* will be useful later */
1053         string = xml_text2xmlChar(data);
1054
1055         utf8string = pg_do_encoding_conversion(string,
1056                                                                                    len,
1057                                                                                    encoding
1058                                                                                    ? pg_char_to_encoding((char *) encoding)
1059                                                                                    : GetDatabaseEncoding(),
1060                                                                                    PG_UTF8);
1061
1062         xml_init();
1063
1064         /* We use a PG_TRY block to ensure libxml is cleaned up on error */
1065         PG_TRY();
1066         {
1067                 ctxt = xmlNewParserCtxt();
1068                 if (ctxt == NULL)
1069                         xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
1070                                                 "could not allocate parser context");
1071
1072                 if (xmloption_arg == XMLOPTION_DOCUMENT)
1073                 {
1074                         /*
1075                          * Note, that here we try to apply DTD defaults
1076                          * (XML_PARSE_DTDATTR) according to SQL/XML:10.16.7.d:
1077                          * 'Default valies defined by internal DTD are applied'.
1078                          * As for external DTDs, we try to support them too, (see
1079                          * SQL/XML:10.16.7.e)
1080                          */
1081                         doc = xmlCtxtReadDoc(ctxt, utf8string,
1082                                                                  NULL,
1083                                                                  "UTF-8",
1084                                                                  XML_PARSE_NOENT | XML_PARSE_DTDATTR
1085                                                                  | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS));
1086                         if (doc == NULL)
1087                                 xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
1088                                                         "invalid XML document");
1089                 }
1090                 else
1091                 {
1092                         int                     res_code;
1093                         size_t count;
1094                         xmlChar    *version = NULL;
1095                         int standalone = -1;
1096
1097                         doc = xmlNewDoc(NULL);
1098
1099                         res_code = parse_xml_decl(utf8string, &count, &version, NULL, &standalone);
1100                         if (res_code != 0)
1101                                 xml_ereport_by_code(ERROR, ERRCODE_INVALID_XML_CONTENT,
1102                                                                         "invalid XML content: invalid XML declaration", res_code);
1103
1104                         res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, utf8string + count, NULL);
1105                         if (res_code != 0)
1106                                 xml_ereport(ERROR, ERRCODE_INVALID_XML_CONTENT,
1107                                                         "invalid XML content");
1108
1109                         doc->version = xmlStrdup(version);
1110                         doc->encoding = xmlStrdup((xmlChar *) "UTF-8");
1111                         doc->standalone = standalone;
1112                 }
1113
1114                 if (ctxt)
1115                         xmlFreeParserCtxt(ctxt);
1116                 xmlCleanupParser();
1117         }
1118         PG_CATCH();
1119         {
1120                 if (doc)
1121                         xmlFreeDoc(doc);
1122                 doc = NULL;
1123                 if (ctxt)
1124                         xmlFreeParserCtxt(ctxt);
1125                 xmlCleanupParser();
1126
1127                 PG_RE_THROW();
1128         }
1129         PG_END_TRY();
1130
1131         return doc;
1132 }
1133
1134
1135 /*
1136  * xmlChar<->text convertions
1137  */
1138 static xmlChar *
1139 xml_text2xmlChar(text *in)
1140 {
1141         int32           len = VARSIZE(in) - VARHDRSZ;
1142         xmlChar         *res;
1143
1144         res = palloc(len + 1);
1145         memcpy(res, VARDATA(in), len);
1146         res[len] = '\0';
1147
1148         return(res);
1149 }
1150
1151
1152 /*
1153  * Wrappers for memory management functions
1154  */
1155 static void *
1156 xml_palloc(size_t size)
1157 {
1158         return palloc(size);
1159 }
1160
1161
1162 static void *
1163 xml_repalloc(void *ptr, size_t size)
1164 {
1165         return repalloc(ptr, size);
1166 }
1167
1168
1169 static void
1170 xml_pfree(void *ptr)
1171 {
1172         pfree(ptr);
1173 }
1174
1175
1176 static char *
1177 xml_pstrdup(const char *string)
1178 {
1179         return pstrdup(string);
1180 }
1181
1182
1183 /*
1184  * Wrapper for "ereport" function for XML-related errors.  The "msg"
1185  * is the SQL-level message; some can be adopted from the SQL/XML
1186  * standard.  This function adds libxml's native error messages, if
1187  * any, as detail.
1188  */
1189 static void
1190 xml_ereport(int level, int sqlcode,
1191                         const char *msg)
1192 {
1193         char *detail;
1194
1195         if (xml_err_buf->len > 0)
1196         {
1197                 detail = pstrdup(xml_err_buf->data);
1198                 xml_err_buf->data[0] = '\0';
1199                 xml_err_buf->len = 0;
1200         }
1201         else
1202                 detail = NULL;
1203
1204         /* libxml error messages end in '\n'; get rid of it */
1205         if (detail)
1206         {
1207                 size_t len;
1208
1209                 len = strlen(detail);
1210                 if (len > 0 && detail[len-1] == '\n')
1211                         detail[len-1] = '\0';
1212
1213                 ereport(level,
1214                                 (errcode(sqlcode),
1215                                  errmsg("%s", msg),
1216                                  errdetail("%s", detail)));
1217         }
1218         else
1219         {
1220                 ereport(level,
1221                                 (errcode(sqlcode),
1222                                  errmsg("%s", msg)));
1223         }
1224 }
1225
1226
1227 /*
1228  * Error handler for libxml error messages
1229  */
1230 static void
1231 xml_errorHandler(void *ctxt, const char *msg,...)
1232 {
1233         /* Append the formatted text to xml_err_buf */
1234         for (;;)
1235         {
1236                 va_list         args;
1237                 bool            success;
1238
1239                 /* Try to format the data. */
1240                 va_start(args, msg);
1241                 success = appendStringInfoVA(xml_err_buf, msg, args);
1242                 va_end(args);
1243
1244                 if (success)
1245                         break;
1246
1247                 /* Double the buffer size and try again. */
1248                 enlargeStringInfo(xml_err_buf, xml_err_buf->maxlen);
1249         }
1250 }
1251
1252
1253 /*
1254  * Wrapper for "ereport" function for XML-related errors.  The "msg"
1255  * is the SQL-level message; some can be adopted from the SQL/XML
1256  * standard.  This function uses "code" to create a textual detail
1257  * message.  At the moment, we only need to cover those codes that we
1258  * may raise in this file.
1259  */
1260 static void
1261 xml_ereport_by_code(int level, int sqlcode,
1262                                         const char *msg, int code)
1263 {
1264     const char *det;
1265
1266     switch (code)
1267         {
1268                 case XML_ERR_INVALID_CHAR:
1269                         det = "Invalid character value";
1270                         break;
1271                 case XML_ERR_SPACE_REQUIRED:
1272                         det = "Space required";
1273                         break;
1274                 case XML_ERR_STANDALONE_VALUE:
1275                         det = "standalone accepts only 'yes' or 'no'";
1276                         break;
1277                 case XML_ERR_VERSION_MISSING:
1278                         det = "Malformed declaration expecting version";
1279                         break;
1280                 case XML_ERR_MISSING_ENCODING:
1281                         det = "Missing encoding in text declaration";
1282                         break;
1283                 case XML_ERR_XMLDECL_NOT_FINISHED:
1284                         det = "Parsing XML declaration: '?>' expected";
1285                         break;
1286         default:
1287             det = "Unrecognized libxml error code: %d";
1288                         break;
1289         }
1290
1291         ereport(level,
1292                         (errcode(sqlcode),
1293                          errmsg("%s", msg),
1294                          errdetail(det, code)));
1295 }
1296
1297
1298 /*
1299  * Convert one char in the current server encoding to a Unicode codepoint.
1300  */
1301 static pg_wchar
1302 sqlchar_to_unicode(char *s)
1303 {
1304         char *utf8string;
1305         pg_wchar ret[2];                        /* need space for trailing zero */
1306
1307         utf8string = (char *) pg_do_encoding_conversion((unsigned char *) s,
1308                                                                                                         pg_mblen(s),
1309                                                                                                         GetDatabaseEncoding(),
1310                                                                                                         PG_UTF8);
1311
1312         pg_encoding_mb2wchar_with_len(PG_UTF8, utf8string, ret, pg_mblen(s));
1313
1314         return ret[0];
1315 }
1316
1317
1318 static bool
1319 is_valid_xml_namefirst(pg_wchar c)
1320 {
1321         /* (Letter | '_' | ':') */
1322         return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
1323                         || c == '_' || c == ':');
1324 }
1325
1326
1327 static bool
1328 is_valid_xml_namechar(pg_wchar c)
1329 {
1330         /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
1331         return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
1332                         || xmlIsDigitQ(c)
1333                         || c == '.' || c == '-' || c == '_' || c == ':'
1334                         || xmlIsCombiningQ(c)
1335                         || xmlIsExtenderQ(c));
1336 }
1337 #endif /* USE_LIBXML */
1338
1339
1340 /*
1341  * Map SQL identifier to XML name; see SQL/XML:2003 section 9.1.
1342  */
1343 char *
1344 map_sql_identifier_to_xml_name(char *ident, bool fully_escaped, bool escape_period)
1345 {
1346 #ifdef USE_LIBXML
1347         StringInfoData buf;
1348         char *p;
1349
1350         /*
1351          * SQL/XML doesn't make use of this case anywhere, so it's
1352          * probably a mistake.
1353          */
1354         Assert(fully_escaped || !escape_period);
1355
1356         initStringInfo(&buf);
1357
1358         for (p = ident; *p; p += pg_mblen(p))
1359         {
1360                 if (*p == ':' && (p == ident || fully_escaped))
1361                         appendStringInfo(&buf, "_x003A_");
1362                 else if (*p == '_' && *(p+1) == 'x')
1363                         appendStringInfo(&buf, "_x005F_");
1364                 else if (fully_escaped && p == ident &&
1365                                  pg_strncasecmp(p, "xml", 3) == 0)
1366                 {
1367                         if (*p == 'x')
1368                                 appendStringInfo(&buf, "_x0078_");
1369                         else
1370                                 appendStringInfo(&buf, "_x0058_");
1371                 }
1372                 else if (escape_period && *p == '.')
1373                         appendStringInfo(&buf, "_x002E_");
1374                 else
1375                 {
1376                         pg_wchar u = sqlchar_to_unicode(p);
1377
1378                         if ((p == ident)
1379                                 ? !is_valid_xml_namefirst(u)
1380                                 : !is_valid_xml_namechar(u))
1381                                 appendStringInfo(&buf, "_x%04X_", (unsigned int) u);
1382                         else
1383                                 appendBinaryStringInfo(&buf, p, pg_mblen(p));
1384                 }
1385         }
1386
1387         return buf.data;
1388 #else /* not USE_LIBXML */
1389         NO_XML_SUPPORT();
1390         return NULL;
1391 #endif /* not USE_LIBXML */
1392 }
1393
1394
1395 /*
1396  * Map a Unicode codepoint into the current server encoding.
1397  */
1398 static char *
1399 unicode_to_sqlchar(pg_wchar c)
1400 {
1401         static unsigned char utf8string[5];     /* need trailing zero */
1402
1403         if (c <= 0x7F)
1404         {
1405                 utf8string[0] = c;
1406         }
1407         else if (c <= 0x7FF)
1408         {
1409                 utf8string[0] = 0xC0 | ((c >> 6) & 0x1F);
1410                 utf8string[1] = 0x80 | (c & 0x3F);
1411         }
1412         else if (c <= 0xFFFF)
1413         {
1414                 utf8string[0] = 0xE0 | ((c >> 12) & 0x0F);
1415                 utf8string[1] = 0x80 | ((c >> 6) & 0x3F);
1416                 utf8string[2] = 0x80 | (c & 0x3F);
1417         }
1418         else
1419         {
1420                 utf8string[0] = 0xF0 | ((c >> 18) & 0x07);
1421                 utf8string[1] = 0x80 | ((c >> 12) & 0x3F);
1422                 utf8string[2] = 0x80 | ((c >> 6) & 0x3F);
1423                 utf8string[3] = 0x80 | (c & 0x3F);
1424         }
1425
1426         return (char *) pg_do_encoding_conversion(utf8string,
1427                                                                                           pg_mblen((char *) utf8string),
1428                                                                                           PG_UTF8,
1429                                                                                           GetDatabaseEncoding());
1430 }
1431
1432
1433 /*
1434  * Map XML name to SQL identifier; see SQL/XML:2003 section 9.17.
1435  */
1436 char *
1437 map_xml_name_to_sql_identifier(char *name)
1438 {
1439         StringInfoData buf;
1440         char *p;
1441
1442         initStringInfo(&buf);
1443
1444         for (p = name; *p; p += pg_mblen(p))
1445         {
1446                 if (*p == '_' && *(p+1) == 'x'
1447                         && isxdigit((unsigned char) *(p+2))
1448                         && isxdigit((unsigned char) *(p+3))
1449                         && isxdigit((unsigned char) *(p+4))
1450                         && isxdigit((unsigned char) *(p+5))
1451                         && *(p+6) == '_')
1452                 {
1453                         unsigned int u;
1454
1455                         sscanf(p + 2, "%X", &u);
1456                         appendStringInfoString(&buf, unicode_to_sqlchar(u));
1457                         p += 6;
1458                 }
1459                 else
1460                         appendBinaryStringInfo(&buf, p, pg_mblen(p));
1461         }
1462
1463         return buf.data;
1464 }
1465
1466
1467 /*
1468  * Map SQL value to XML value; see SQL/XML:2003 section 9.16.
1469  */
1470 char *
1471 map_sql_value_to_xml_value(Datum value, Oid type)
1472 {
1473         StringInfoData buf;
1474
1475         initStringInfo(&buf);
1476
1477         if (is_array_type(type))
1478         {
1479                 int i;
1480                 ArrayType *array;
1481                 Oid elmtype;
1482                 int16 elmlen;
1483                 bool elmbyval;
1484                 char elmalign;
1485
1486                 array = DatumGetArrayTypeP(value);
1487
1488                 /* TODO: need some code-fu here to remove this limitation */
1489                 if (ARR_NDIM(array) != 1)
1490                         ereport(ERROR,
1491                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1492                                          errmsg("only supported for one-dimensional array")));
1493
1494                 elmtype = ARR_ELEMTYPE(array);
1495                 get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
1496
1497                 for (i = ARR_LBOUND(array)[0];
1498                          i < ARR_LBOUND(array)[0] + ARR_DIMS(array)[0];
1499                          i++)
1500                 {
1501                         Datum subval;
1502                         bool isnull;
1503
1504                         subval = array_ref(array, 1, &i, -1, elmlen, elmbyval, elmalign, &isnull);
1505                         appendStringInfoString(&buf, "<element>");
1506                         appendStringInfoString(&buf, map_sql_value_to_xml_value(subval, elmtype));
1507                         appendStringInfoString(&buf, "</element>");
1508                 }
1509         }
1510         else
1511         {
1512                 Oid typeOut;
1513                 bool isvarlena;
1514                 char *p, *str;
1515
1516                 if (type == BOOLOID)
1517                 {
1518                         if (DatumGetBool(value))
1519                                 return "true";
1520                         else
1521                                 return "false";
1522                 }
1523
1524                 getTypeOutputInfo(type, &typeOut, &isvarlena);
1525                 str = OidOutputFunctionCall(typeOut, value);
1526
1527                 if (type == XMLOID)
1528                         return str;
1529
1530 #ifdef USE_LIBXML
1531                 if (type == BYTEAOID)
1532                 {
1533                         xmlBufferPtr buf;
1534                         xmlTextWriterPtr writer;
1535                         char *result;
1536
1537                         buf = xmlBufferCreate();
1538                         writer = xmlNewTextWriterMemory(buf, 0);
1539
1540                         if (xmlbinary == XMLBINARY_BASE64)
1541                                 xmlTextWriterWriteBase64(writer, VARDATA(value), 0, VARSIZE(value) - VARHDRSZ);
1542                         else
1543                                 xmlTextWriterWriteBinHex(writer, VARDATA(value), 0, VARSIZE(value) - VARHDRSZ);
1544
1545                         xmlFreeTextWriter(writer);
1546                         result = pstrdup((const char *) xmlBufferContent(buf));
1547                         xmlBufferFree(buf);
1548                         return result;
1549                 }
1550 #endif /* USE_LIBXML */
1551
1552                 for (p = str; *p; p += pg_mblen(p))
1553                 {
1554                         switch (*p)
1555                         {
1556                                 case '&':
1557                                         appendStringInfo(&buf, "&amp;");
1558                                         break;
1559                                 case '<':
1560                                         appendStringInfo(&buf, "&lt;");
1561                                         break;
1562                                 case '>':
1563                                         appendStringInfo(&buf, "&gt;");
1564                                         break;
1565                                 case '\r':
1566                                         appendStringInfo(&buf, "&#x0d;");
1567                                         break;
1568                                 default:
1569                                         appendBinaryStringInfo(&buf, p, pg_mblen(p));
1570                                         break;
1571                         }
1572                 }
1573         }
1574
1575         return buf.data;
1576 }
1577
1578
1579 static char *
1580 _SPI_strdup(const char *s)
1581 {
1582         char *ret = SPI_palloc(strlen(s) + 1);
1583         strcpy(ret, s);
1584         return ret;
1585 }
1586
1587
1588 /*
1589  * Map SQL table to XML and/or XML Schema document; see SQL/XML:2003
1590  * section 9.3.
1591  */
1592
1593 Datum
1594 table_to_xml(PG_FUNCTION_ARGS)
1595 {
1596         Oid                     relid = PG_GETARG_OID(0);
1597         bool            nulls = PG_GETARG_BOOL(1);
1598         bool            tableforest = PG_GETARG_BOOL(2);
1599         const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1600
1601         StringInfoData query;
1602
1603         initStringInfo(&query);
1604         appendStringInfo(&query, "SELECT * FROM %s", DatumGetCString(DirectFunctionCall1(regclassout, ObjectIdGetDatum(relid))));
1605
1606         PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query.data, get_rel_name(relid), NULL, nulls, tableforest, targetns)));
1607 }
1608
1609
1610 Datum
1611 query_to_xml(PG_FUNCTION_ARGS)
1612 {
1613         char       *query = _textout(PG_GETARG_TEXT_P(0));
1614         bool            nulls = PG_GETARG_BOOL(1);
1615         bool            tableforest = PG_GETARG_BOOL(2);
1616         const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1617
1618         PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL, NULL, nulls, tableforest, targetns)));
1619 }
1620
1621
1622 Datum
1623 cursor_to_xml(PG_FUNCTION_ARGS)
1624 {
1625         char       *name = _textout(PG_GETARG_TEXT_P(0));
1626         int32           count = PG_GETARG_INT32(1);
1627         bool            nulls = PG_GETARG_BOOL(2);
1628         bool            tableforest = PG_GETARG_BOOL(3);
1629         const char *targetns = _textout(PG_GETARG_TEXT_P(4));
1630
1631         StringInfoData result;
1632         Portal          portal;
1633         int                     i;
1634
1635         initStringInfo(&result);
1636
1637         SPI_connect();
1638         portal = SPI_cursor_find(name);
1639         if (portal == NULL)
1640                 ereport(ERROR,
1641                                 (errcode(ERRCODE_UNDEFINED_CURSOR),
1642                                  errmsg("cursor \"%s\" does not exist", name)));
1643
1644         SPI_cursor_fetch(portal, true, count);
1645         for (i = 0; i < SPI_processed; i++)
1646                 SPI_sql_row_to_xmlelement(i, &result, NULL, nulls, tableforest, targetns);
1647
1648         SPI_finish();
1649
1650         PG_RETURN_XML_P(stringinfo_to_xmltype(&result));
1651 }
1652
1653
1654 static StringInfo
1655 query_to_xml_internal(const char *query, char *tablename, const char *xmlschema, bool nulls, bool tableforest, const char *targetns)
1656 {
1657         StringInfo      result;
1658         char       *xmltn;
1659         int                     i;
1660
1661         if (tablename)
1662                 xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
1663         else
1664                 xmltn = "table";
1665
1666         result = makeStringInfo();
1667
1668         SPI_connect();
1669         if (SPI_execute(query, true, 0) != SPI_OK_SELECT)
1670                 ereport(ERROR,
1671                                 (errcode(ERRCODE_DATA_EXCEPTION),
1672                                  errmsg("invalid query")));
1673
1674         if (!tableforest)
1675         {
1676                 appendStringInfo(result, "<%s xmlns:xsi=\"" NAMESPACE_XSI "\"", xmltn);
1677                 if (strlen(targetns) > 0)
1678                         appendStringInfo(result, " xmlns=\"%s\"", targetns);
1679                 if (strlen(targetns) > 0)
1680                         appendStringInfo(result, " xmlns:xsd=\"%s\"", targetns);
1681                 if (xmlschema)
1682                 {
1683                         if (strlen(targetns) > 0)
1684                                 appendStringInfo(result, " xsi:schemaLocation=\"%s #\"", targetns);
1685                         else
1686                                 appendStringInfo(result, " xsi:noNamespaceSchemaLocation=\"#\"");
1687                 }
1688                 appendStringInfo(result, ">\n\n");
1689         }
1690
1691         if (xmlschema)
1692                 appendStringInfo(result, "%s\n\n", xmlschema);
1693
1694         for(i = 0; i < SPI_processed; i++)
1695                 SPI_sql_row_to_xmlelement(i, result, tablename, nulls, tableforest, targetns);
1696
1697         if (!tableforest)
1698                 appendStringInfo(result, "</%s>\n", xmltn);
1699
1700         SPI_finish();
1701
1702         return result;
1703 }
1704
1705
1706 Datum
1707 table_to_xmlschema(PG_FUNCTION_ARGS)
1708 {
1709         Oid                     relid = PG_GETARG_OID(0);
1710         bool            nulls = PG_GETARG_BOOL(1);
1711         bool            tableforest = PG_GETARG_BOOL(2);
1712         const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1713
1714         const char *result;
1715         Relation rel;
1716
1717         rel = heap_open(relid, AccessShareLock);
1718         result = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls, tableforest, targetns);
1719         heap_close(rel, NoLock);
1720
1721         PG_RETURN_XML_P(cstring_to_xmltype(result));
1722 }
1723
1724
1725 Datum
1726 query_to_xmlschema(PG_FUNCTION_ARGS)
1727 {
1728         char       *query = _textout(PG_GETARG_TEXT_P(0));
1729         bool            nulls = PG_GETARG_BOOL(1);
1730         bool            tableforest = PG_GETARG_BOOL(2);
1731         const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1732
1733         const char *result;
1734         void       *plan;
1735         Portal          portal;
1736
1737         SPI_connect();
1738         plan = SPI_prepare(query, 0, NULL);
1739         portal = SPI_cursor_open(NULL, plan, NULL, NULL, true);
1740         result = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc, InvalidOid, nulls, tableforest, targetns));
1741         SPI_cursor_close(portal);
1742         SPI_finish();
1743
1744         PG_RETURN_XML_P(cstring_to_xmltype(result));
1745 }
1746
1747
1748 Datum
1749 cursor_to_xmlschema(PG_FUNCTION_ARGS)
1750 {
1751         char       *name = _textout(PG_GETARG_TEXT_P(0));
1752         bool            nulls = PG_GETARG_BOOL(1);
1753         bool            tableforest = PG_GETARG_BOOL(2);
1754         const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1755
1756         const char *xmlschema;
1757         Portal          portal;
1758
1759         SPI_connect();
1760         portal = SPI_cursor_find(name);
1761         if (portal == NULL)
1762                 ereport(ERROR,
1763                                 (errcode(ERRCODE_UNDEFINED_CURSOR),
1764                                  errmsg("cursor \"%s\" does not exist", name)));
1765
1766         xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc, InvalidOid, nulls, tableforest, targetns));
1767         SPI_finish();
1768
1769         PG_RETURN_XML_P(cstring_to_xmltype(xmlschema));
1770 }
1771
1772
1773 Datum
1774 table_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
1775 {
1776         Oid                     relid = PG_GETARG_OID(0);
1777         bool            nulls = PG_GETARG_BOOL(1);
1778         bool            tableforest = PG_GETARG_BOOL(2);
1779         const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1780
1781         StringInfoData query;
1782         Relation        rel;
1783         const char *xmlschema;
1784
1785         rel = heap_open(relid, AccessShareLock);
1786         xmlschema = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls, tableforest, targetns);
1787         heap_close(rel, NoLock);
1788
1789         initStringInfo(&query);
1790         appendStringInfo(&query, "SELECT * FROM %s", DatumGetCString(DirectFunctionCall1(regclassout, ObjectIdGetDatum(relid))));
1791
1792         PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query.data, get_rel_name(relid), xmlschema, nulls, tableforest, targetns)));
1793 }
1794
1795
1796 Datum
1797 query_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
1798 {
1799         char       *query = _textout(PG_GETARG_TEXT_P(0));
1800         bool            nulls = PG_GETARG_BOOL(1);
1801         bool            tableforest = PG_GETARG_BOOL(2);
1802         const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1803
1804         const char *xmlschema;
1805         void       *plan;
1806         Portal          portal;
1807
1808         SPI_connect();
1809         plan = SPI_prepare(query, 0, NULL);
1810         portal = SPI_cursor_open(NULL, plan, NULL, NULL, true);
1811         xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc, InvalidOid, nulls, tableforest, targetns));
1812         SPI_cursor_close(portal);
1813         SPI_finish();
1814
1815         PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL, xmlschema, nulls, tableforest, targetns)));
1816 }
1817
1818
1819 /*
1820  * Map a multi-part SQL name to an XML name; see SQL/XML:2003 section
1821  * 9.2.
1822  */
1823 static char *
1824 map_multipart_sql_identifier_to_xml_name(char *a, char *b, char *c, char *d)
1825 {
1826         StringInfoData result;
1827
1828         initStringInfo(&result);
1829
1830         if (a)
1831                 appendStringInfo(&result, "%s", map_sql_identifier_to_xml_name(a, true, true));
1832         if (b)
1833                 appendStringInfo(&result, ".%s", map_sql_identifier_to_xml_name(b, true, true));
1834         if (c)
1835                 appendStringInfo(&result, ".%s", map_sql_identifier_to_xml_name(c, true, true));
1836         if (d)
1837                 appendStringInfo(&result, ".%s", map_sql_identifier_to_xml_name(d, true, true));
1838
1839         return result.data;
1840 }
1841
1842
1843 /*
1844  * Map an SQL table to an XML Schema document; see SQL/XML:2003
1845  * section 9.3.
1846  *
1847  * Map an SQL table to XML Schema data types; see SQL/XML:2003 section
1848  * 9.6.
1849  */
1850 static const char *
1851 map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls, bool tableforest, const char *targetns)
1852 {
1853         int                     i;
1854         char       *xmltn;
1855         char       *tabletypename;
1856         char       *rowtypename;
1857         StringInfoData result;
1858
1859         initStringInfo(&result);
1860
1861         if (relid)
1862         {
1863                 HeapTuple tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0);
1864                 Form_pg_class reltuple = (Form_pg_class) GETSTRUCT(tuple);
1865
1866                 xmltn = map_sql_identifier_to_xml_name(NameStr(reltuple->relname), true, false);
1867
1868                 tabletypename = map_multipart_sql_identifier_to_xml_name("TableType",
1869                                                                                                                                  get_database_name(MyDatabaseId),
1870                                                                                                                                  get_namespace_name(reltuple->relnamespace),
1871                                                                                                                                  NameStr(reltuple->relname));
1872
1873                 rowtypename = map_multipart_sql_identifier_to_xml_name("RowType",
1874                                                                                                                            get_database_name(MyDatabaseId),
1875                                                                                                                            get_namespace_name(reltuple->relnamespace),
1876                                                                                                                            NameStr(reltuple->relname));
1877
1878                 ReleaseSysCache(tuple);
1879         }
1880         else
1881         {
1882                 if (tableforest)
1883                         xmltn = "row";
1884                 else
1885                         xmltn = "table";
1886
1887                 tabletypename = "TableType";
1888                 rowtypename = "RowType";
1889         }
1890
1891         appendStringInfoString(&result,
1892                                                    "<xsd:schema\n"
1893                                                    "    xmlns:xsd=\"" NAMESPACE_XSD "\"");
1894         if (strlen(targetns) > 0)
1895                 appendStringInfo(&result,
1896                                                  "\n"
1897                                                  "    targetNamespace=\"%s\"\n"
1898                                                  "    elementFormDefault=\"qualified\"",
1899                                                  targetns);
1900         appendStringInfoString(&result,
1901                                                    ">\n\n");
1902
1903         appendStringInfoString(&result,
1904                                                    map_sql_typecoll_to_xmlschema_types(tupdesc));
1905
1906         appendStringInfo(&result,
1907                                          "<xsd:complexType name=\"%s\">\n"
1908                                          "  <xsd:sequence>\n",
1909                                          rowtypename);
1910
1911         for (i = 0; i < tupdesc->natts; i++)
1912                 appendStringInfo(&result,
1913                                                  "    <xsd:element name=\"%s\" type=\"%s\"%s></xsd:element>\n",
1914                                                  map_sql_identifier_to_xml_name(NameStr(tupdesc->attrs[i]->attname), true, false),
1915                                                  map_sql_type_to_xml_name(tupdesc->attrs[i]->atttypid, -1),
1916                                                  nulls ? " nillable=\"true\"" : " minOccurs=\"0\"");
1917
1918         appendStringInfoString(&result,
1919                                                    "  </xsd:sequence>\n"
1920                                                    "</xsd:complexType>\n\n");
1921
1922         if (!tableforest)
1923         {
1924                 appendStringInfo(&result,
1925                                                  "<xsd:complexType name=\"%s\">\n"
1926                                                  "  <xsd:sequence>\n"
1927                                                  "    <xsd:element name=\"row\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n"
1928                                                  "  </xsd:sequence>\n"
1929                                                  "</xsd:complexType>\n\n",
1930                                                  tabletypename, rowtypename);
1931
1932                 appendStringInfo(&result,
1933                                                  "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
1934                                                  xmltn, tabletypename);
1935         }
1936         else
1937                 appendStringInfo(&result,
1938                                                  "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
1939                                                  xmltn, rowtypename);
1940
1941         appendStringInfoString(&result,
1942                                                    "</xsd:schema>");
1943
1944         return result.data;
1945 }
1946
1947
1948 /*
1949  * Map an SQL data type to an XML name; see SQL/XML:2003 section 9.9.
1950  */
1951 static const char *
1952 map_sql_type_to_xml_name(Oid typeoid, int typmod)
1953 {
1954         StringInfoData result;
1955
1956         initStringInfo(&result);
1957
1958         switch(typeoid)
1959         {
1960                 case BPCHAROID:
1961                         if (typmod == -1)
1962                                 appendStringInfo(&result, "CHAR");
1963                         else
1964                                 appendStringInfo(&result, "CHAR_%d", typmod - VARHDRSZ);
1965                         break;
1966                 case VARCHAROID:
1967                         if (typmod == -1)
1968                                 appendStringInfo(&result, "VARCHAR");
1969                         else
1970                                 appendStringInfo(&result, "VARCHAR_%d", typmod - VARHDRSZ);
1971                         break;
1972                 case NUMERICOID:
1973                         if (typmod == -1)
1974                                 appendStringInfo(&result, "NUMERIC");
1975                         else
1976                                 appendStringInfo(&result, "NUMERIC_%d_%d",
1977                                                                  ((typmod - VARHDRSZ) >> 16) & 0xffff,
1978                                                                  (typmod - VARHDRSZ) & 0xffff);
1979                         break;
1980                 case INT4OID:
1981                         appendStringInfo(&result, "INTEGER");
1982                         break;
1983                 case INT2OID:
1984                         appendStringInfo(&result, "SMALLINT");
1985                         break;
1986                 case INT8OID:
1987                         appendStringInfo(&result, "BIGINT");
1988                         break;
1989                 case FLOAT4OID:
1990                         appendStringInfo(&result, "REAL");
1991                         break;
1992                 case FLOAT8OID:
1993                         appendStringInfo(&result, "DOUBLE");
1994                         break;
1995                 case BOOLOID:
1996                         appendStringInfo(&result, "BOOLEAN");
1997                         break;
1998                 case TIMEOID:
1999                         if (typmod == -1)
2000                                 appendStringInfo(&result, "TIME");
2001                         else
2002                                 appendStringInfo(&result, "TIME_%d", typmod);
2003                         break;
2004                 case TIMETZOID:
2005                         if (typmod == -1)
2006                                 appendStringInfo(&result, "TIME_WTZ");
2007                         else
2008                                 appendStringInfo(&result, "TIME_WTZ_%d", typmod);
2009                         break;
2010                 case TIMESTAMPOID:
2011                         if (typmod == -1)
2012                                 appendStringInfo(&result, "TIMESTAMP");
2013                         else
2014                                 appendStringInfo(&result, "TIMESTAMP_%d", typmod);
2015                         break;
2016                 case TIMESTAMPTZOID:
2017                         if (typmod == -1)
2018                                 appendStringInfo(&result, "TIMESTAMP_WTZ");
2019                         else
2020                                 appendStringInfo(&result, "TIMESTAMP_WTZ_%d", typmod);
2021                         break;
2022                 case DATEOID:
2023                         appendStringInfo(&result, "DATE");
2024                         break;
2025                 case XMLOID:
2026                         appendStringInfo(&result, "XML");
2027                         break;
2028                 default:
2029                 {
2030                         HeapTuple tuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(typeoid), 0, 0, 0);
2031                         Form_pg_type typtuple = (Form_pg_type) GETSTRUCT(tuple);
2032
2033                         appendStringInfoString(&result,
2034                                                                    map_multipart_sql_identifier_to_xml_name((typtuple->typtype == 'd') ? "Domain" : "UDT",
2035                                                                                                                                                         get_database_name(MyDatabaseId),
2036                                                                                                                                                         get_namespace_name(typtuple->typnamespace),
2037                                                                                                                                                         NameStr(typtuple->typname)));
2038
2039                         ReleaseSysCache(tuple);
2040                 }
2041         }
2042
2043         return result.data;
2044 }
2045
2046
2047 /*
2048  * Map a collection of SQL data types to XML Schema data types; see
2049  * SQL/XML:2002 section 9.10.
2050  */
2051 static const char *
2052 map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc)
2053 {
2054         Oid                *uniquetypes;
2055         int                     i, j;
2056         int                     len;
2057         StringInfoData result;
2058
2059         initStringInfo(&result);
2060
2061         uniquetypes = palloc(2 * sizeof(*uniquetypes) * tupdesc->natts);
2062         len = 0;
2063
2064         for (i = 1; i <= tupdesc->natts; i++)
2065         {
2066                 bool already_done = false;
2067                 Oid type = SPI_gettypeid(tupdesc, i);
2068                 for (j = 0; j < len; j++)
2069                         if (type == uniquetypes[j])
2070                         {
2071                                 already_done = true;
2072                                 break;
2073                         }
2074                 if (already_done)
2075                         continue;
2076
2077                 uniquetypes[len++] = type;
2078         }
2079
2080         /* add base types of domains */
2081         for (i = 0; i < len; i++)
2082         {
2083                 bool already_done = false;
2084                 Oid type = getBaseType(uniquetypes[i]);
2085                 for (j = 0; j < len; j++)
2086                         if (type == uniquetypes[j])
2087                         {
2088                                 already_done = true;
2089                                 break;
2090                         }
2091                 if (already_done)
2092                         continue;
2093
2094                 uniquetypes[len++] = type;
2095         }
2096
2097         for (i = 0; i < len; i++)
2098                 appendStringInfo(&result, "%s\n", map_sql_type_to_xmlschema_type(uniquetypes[i], -1));
2099
2100         return result.data;
2101 }
2102
2103
2104 /*
2105  * Map an SQL data type to a named XML Schema data type; see SQL/XML
2106  * sections 9.11 and 9.15.
2107  *
2108  * (The distinction between 9.11 and 9.15 is basically that 9.15 adds
2109  * a name attribute, which thsi function does.  The name-less version
2110  * 9.11 doesn't appear to be required anywhere.)
2111  */
2112 static const char *
2113 map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
2114 {
2115         StringInfoData result;
2116         const char *typename = map_sql_type_to_xml_name(typeoid, typmod);
2117
2118         initStringInfo(&result);
2119
2120         if (typeoid == XMLOID)
2121         {
2122                 appendStringInfo(&result,
2123                                                  "<xsd:complexType mixed=\"true\">\n"
2124                                                  "  <xsd:sequence>\n"
2125                                                  "    <xsd:any name=\"element\" minOccurs=\"0\" maxOccurs=\"unbounded\" processContents=\"skip\"/>\n"
2126                                                  "  </xsd:sequence>\n"
2127                                                  "</xsd:complexType>\n");
2128         }
2129         else
2130         {
2131                 appendStringInfo(&result,
2132                                                  "<xsd:simpleType name=\"%s\">\n", typename);
2133
2134                 switch(typeoid)
2135                 {
2136                         case BPCHAROID:
2137                         case VARCHAROID:
2138                         case TEXTOID:
2139                                 if (typmod != -1)
2140                                         appendStringInfo(&result,
2141                                                                          "  <xsd:restriction base=\"xsd:string\">\n"
2142                                                                          "    <xsd:maxLength value=\"%d\"/>\n"
2143                                                                          "  </xsd:restriction>\n",
2144                                                                          typmod - VARHDRSZ);
2145                                 break;
2146
2147                         case BYTEAOID:
2148                                 appendStringInfo(&result,
2149                                                                  "  <xsd:restriction base=\"xsd:%s\">\n"
2150                                                                  "  </xsd:restriction>\n",
2151                                                                  xmlbinary == XMLBINARY_BASE64 ? "base64Binary" : "hexBinary");
2152
2153                         case NUMERICOID:
2154                                 if (typmod != -1)
2155                                         appendStringInfo(&result,
2156                                                                          "  <xsd:restriction base=\"xsd:decimal\">\n"
2157                                                                          "    <xsd:totalDigits value=\"%d\"/>\n"
2158                                                                          "    <xsd:fractionDigits value=\"%d\"/>\n"
2159                                                                          "  </xsd:restriction>\n",
2160                                                                          ((typmod - VARHDRSZ) >> 16) & 0xffff,
2161                                                                          (typmod - VARHDRSZ) & 0xffff);
2162                                 break;
2163
2164                         case INT2OID:
2165                                 appendStringInfo(&result,
2166                                                                  "  <xsd:restriction base=\"xsd:short\">\n"
2167                                                                  "    <xsd:maxInclusive value=\"%d\"/>\n"
2168                                                                  "    <xsd:minInclusive value=\"%d\"/>\n"
2169                                                                  "  </xsd:restriction>\n",
2170                                                                  SHRT_MAX, SHRT_MIN);
2171                                 break;
2172
2173                         case INT4OID:
2174                                 appendStringInfo(&result,
2175                                                                  "  <xsd:restriction base='xsd:int'>\n"
2176                                                                  "    <xsd:maxInclusive value=\"%d\"/>\n"
2177                                                                  "    <xsd:minInclusive value=\"%d\"/>\n"
2178                                                                  "  </xsd:restriction>\n",
2179                                                                  INT_MAX, INT_MIN);
2180                                 break;
2181
2182                         case INT8OID:
2183                                 appendStringInfo(&result,
2184                                                                  "  <xsd:restriction base=\"xsd:long\">\n"
2185                                                                  "    <xsd:maxInclusive value=\"" INT64_FORMAT "\"/>\n"
2186                                                                  "    <xsd:minInclusive value=\"" INT64_FORMAT "\"/>\n"
2187                                                                  "  </xsd:restriction>\n",
2188                                                                  -((INT64CONST(1) << (sizeof(int64) * 8 - 1)) + 1),
2189                                                                  (INT64CONST(1) << (sizeof(int64) * 8 - 1)));
2190                                 break;
2191
2192                         case FLOAT4OID:
2193                                 appendStringInfo(&result,
2194                                                                  "  <xsd:restriction base=\"xsd:float\"></xsd:restriction>\n");
2195                                 break;
2196
2197                         case FLOAT8OID:
2198                                 appendStringInfo(&result,
2199                                                                  "  <xsd:restriction base=\"xsd:double\"></xsd:restriction>\n");
2200                                 break;
2201
2202                         case BOOLOID:
2203                                 appendStringInfo(&result,
2204                                                                  "  <xsd:restriction base=\"xsd:boolean\"></xsd:restriction>\n");
2205                                 break;
2206
2207                         case TIMEOID:
2208                         case TIMETZOID:
2209                         {
2210                                 const char *tz = (typeoid == TIMETZOID ? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
2211
2212                                 if (typmod == -1)
2213                                         appendStringInfo(&result,
2214                                                                          "  <xsd:restriction base=\"xsd:time\">\n"
2215                                                                          "    <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}(.\\p{Nd}+)?%s\"/>\n"
2216                                                                          "  </xsd:restriction>\n", tz);
2217                                 else if (typmod == 0)
2218                                         appendStringInfo(&result,
2219                                                                          "  <xsd:restriction base=\"xsd:time\">\n"
2220                                                                          "    <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
2221                                                                          "  </xsd:restriction>\n", tz);
2222                                 else
2223                                         appendStringInfo(&result,
2224                                                                          "  <xsd:restriction base=\"xsd:time\">\n"
2225                                                                          "    <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}.\\p{Nd}{%d}%s\"/>\n"
2226                                                                          "  </xsd:restriction>\n", typmod - VARHDRSZ, tz);
2227                                 break;
2228                         }
2229
2230                         case TIMESTAMPOID:
2231                         case TIMESTAMPTZOID:
2232                         {
2233                                 const char *tz = (typeoid == TIMESTAMPTZOID ? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
2234
2235                                 if (typmod == -1)
2236                                         appendStringInfo(&result,
2237                                                                          "  <xsd:restriction base=\"xsd:time\">\n"
2238                                                                          "    <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"
2239                                                                          "  </xsd:restriction>\n", tz);
2240                                 else if (typmod == 0)
2241                                         appendStringInfo(&result,
2242                                                                          "  <xsd:restriction base=\"xsd:time\">\n"
2243                                                                          "    <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}T\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
2244                                                                          "  </xsd:restriction>\n", tz);
2245                                 else
2246                                         appendStringInfo(&result,
2247                                                                          "  <xsd:restriction base=\"xsd:time\">\n"
2248                                                                          "    <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"
2249                                                                          "  </xsd:restriction>\n", typmod - VARHDRSZ, tz);
2250                                 break;
2251                         }
2252
2253                         case DATEOID:
2254                                 appendStringInfo(&result,
2255                                                                  "  <xsd:restriction base=\"xsd:date\">\n"
2256                                                                  "    <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}\"/>\n"
2257                                                                  "  </xsd:restriction>\n");
2258                                                                  break;
2259
2260                         default:
2261                                 if (get_typtype(typeoid) == 'd')
2262                                 {
2263                                         Oid base_typeoid;
2264                                         int32 base_typmod = -1;
2265
2266                                         base_typeoid = getBaseTypeAndTypmod(typeoid, &base_typmod);
2267
2268                                         appendStringInfo(&result,
2269                                                                          "  <xsd:restriction base=\"%s\">\n",
2270                                                                          map_sql_type_to_xml_name(base_typeoid, base_typmod));
2271                                 }
2272                 }
2273                 appendStringInfo(&result,
2274                                                  "</xsd:simpleType>\n");
2275         }
2276
2277         return result.data;
2278 }
2279
2280
2281 /*
2282  * Map an SQL row to an XML element, taking the row from the active
2283  * SPI cursor.  See also SQL/XML:2003 section 9.12.
2284  */
2285 static void
2286 SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns)
2287 {
2288         int                     i;
2289         char       *xmltn;
2290
2291         if (tablename)
2292                 xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
2293         else
2294         {
2295                 if (tableforest)
2296                         xmltn = "row";
2297                 else
2298                         xmltn = "table";
2299         }
2300
2301         if (tableforest)
2302         {
2303                 appendStringInfo(result, "<%s xmlns:xsi=\"" NAMESPACE_XSI "\"", xmltn);
2304                 if (strlen(targetns) > 0)
2305                         appendStringInfo(result, " xmlns=\"%s\"", targetns);
2306                 appendStringInfo(result, ">\n");
2307         }
2308         else
2309                 appendStringInfoString(result, "<row>\n");
2310
2311         for(i = 1; i <= SPI_tuptable->tupdesc->natts; i++)
2312         {
2313                 char *colname;
2314                 Datum colval;
2315                 bool isnull;
2316
2317                 colname = map_sql_identifier_to_xml_name(SPI_fname(SPI_tuptable->tupdesc, i), true, false);
2318                 colval = SPI_getbinval(SPI_tuptable->vals[rownum], SPI_tuptable->tupdesc, i, &isnull);
2319
2320                 if (isnull)
2321                 {
2322                         if (nulls)
2323                                 appendStringInfo(result, "  <%s xsi:nil='true'/>\n", colname);
2324
2325                 }
2326                 else
2327                         appendStringInfo(result, "  <%s>%s</%s>\n",
2328                                                          colname, map_sql_value_to_xml_value(colval, SPI_gettypeid(SPI_tuptable->tupdesc, i)),
2329                                                          colname);
2330         }
2331
2332         if (tableforest)
2333                 appendStringInfo(result, "</%s>\n\n", xmltn);
2334         else
2335                 appendStringInfoString(result, "</row>\n\n");
2336 }