OSDN Git Service

Add support for optionally escaping periods when converting SQL identifiers
[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.27 2007/02/11 22:18:15 petere 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/pg_type.h"
53 #include "executor/executor.h"
54 #include "fmgr.h"
55 #include "libpq/pqformat.h"
56 #include "mb/pg_wchar.h"
57 #include "nodes/execnodes.h"
58 #include "parser/parse_expr.h"
59 #include "utils/array.h"
60 #include "utils/builtins.h"
61 #include "utils/lsyscache.h"
62 #include "utils/memutils.h"
63 #include "utils/xml.h"
64
65
66 #ifdef USE_LIBXML
67
68 static StringInfo xml_err_buf = NULL;
69
70 static void     xml_init(void);
71 static void    *xml_palloc(size_t size);
72 static void    *xml_repalloc(void *ptr, size_t size);
73 static void     xml_pfree(void *ptr);
74 static char    *xml_pstrdup(const char *string);
75 static void     xml_ereport(int level, int sqlcode,
76                                                         const char *msg);
77 static void     xml_errorHandler(void *ctxt, const char *msg, ...);
78 static void     xml_ereport_by_code(int level, int sqlcode,
79                                                                         const char *msg, int errcode);
80 static xmlChar *xml_text2xmlChar(text *in);
81 static int              parse_xml_decl(const xmlChar *str, size_t *lenp, xmlChar **version, xmlChar **encoding, int *standalone);
82 static bool             print_xml_decl(StringInfo buf, const xmlChar *version, pg_enc encoding, int standalone);
83 static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, xmlChar *encoding);
84
85 #endif /* USE_LIBXML */
86
87 XmlBinaryType xmlbinary;
88 XmlOptionType xmloption;
89
90
91 #define NO_XML_SUPPORT() \
92         ereport(ERROR, \
93                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
94                          errmsg("no XML support in this installation")))
95
96
97 Datum
98 xml_in(PG_FUNCTION_ARGS)
99 {
100 #ifdef USE_LIBXML
101         char            *s = PG_GETARG_CSTRING(0);
102         size_t          len;
103         xmltype         *vardata;
104         xmlDocPtr        doc;
105
106         len = strlen(s);
107         vardata = palloc(len + VARHDRSZ);
108         VARATT_SIZEP(vardata) = len + VARHDRSZ;
109         memcpy(VARDATA(vardata), s, len);
110
111         /*
112          * Parse the data to check if it is well-formed XML data.  Assume
113          * that ERROR occurred if parsing failed.
114          */
115         doc = xml_parse(vardata, xmloption, true, NULL);
116         xmlFreeDoc(doc);
117
118         PG_RETURN_XML_P(vardata);
119 #else
120         NO_XML_SUPPORT();
121         return 0;
122 #endif
123 }
124
125
126 #define PG_XML_DEFAULT_VERSION "1.0"
127
128
129 static char *
130 xml_out_internal(xmltype *x, pg_enc target_encoding)
131 {
132         char            *str;
133         size_t          len;
134 #ifdef USE_LIBXML
135         xmlChar         *version;
136         xmlChar         *encoding;
137         int                     standalone;
138         int                     res_code;
139 #endif
140
141         len = VARSIZE(x) - VARHDRSZ;
142         str = palloc(len + 1);
143         memcpy(str, VARDATA(x), len);
144         str[len] = '\0';
145
146 #ifdef USE_LIBXML
147         if ((res_code = parse_xml_decl((xmlChar *) str, &len, &version, &encoding, &standalone)) == 0)
148         {
149                 StringInfoData buf;
150
151                 initStringInfo(&buf);
152
153                 if (!print_xml_decl(&buf, version, target_encoding, standalone))
154                 {
155                         /*
156                          * If we are not going to produce an XML declaration, eat
157                          * a single newline in the original string to prevent
158                          * empty first lines in the output.
159                          */
160                         if (*(str + len) == '\n')
161                                 len += 1;
162                 }
163                 appendStringInfoString(&buf, str + len);
164
165                 return buf.data;
166         }
167
168         xml_ereport_by_code(WARNING, ERRCODE_INTERNAL_ERROR,
169                                                 "could not parse XML declaration in stored value", res_code);
170 #endif
171         return str;
172 }
173
174
175 Datum
176 xml_out(PG_FUNCTION_ARGS)
177 {
178         xmltype    *x = PG_GETARG_XML_P(0);
179
180         /*
181          * xml_out removes the encoding property in all cases.  This is
182          * because we cannot control from here whether the datum will be
183          * converted to a different client encoding, so we'd do more harm
184          * than good by including it.
185          */
186         PG_RETURN_CSTRING(xml_out_internal(x, 0));
187 }
188
189
190 Datum
191 xml_recv(PG_FUNCTION_ARGS)
192 {
193 #ifdef USE_LIBXML
194         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
195         xmltype    *result;
196         char       *str;
197         char       *newstr;
198         int                     nbytes;
199         xmlDocPtr       doc;
200         xmlChar    *encoding = NULL;
201
202         str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
203
204         result = palloc(nbytes + VARHDRSZ);
205         VARATT_SIZEP(result) = nbytes + VARHDRSZ;
206         memcpy(VARDATA(result), str, nbytes);
207
208         parse_xml_decl((xmlChar *) str, NULL, NULL, &encoding, NULL);
209
210         /*
211          * Parse the data to check if it is well-formed XML data.  Assume
212          * that ERROR occurred if parsing failed.
213          */
214         doc = xml_parse(result, xmloption, true, encoding);
215         xmlFreeDoc(doc);
216
217         newstr = (char *) pg_do_encoding_conversion((unsigned char *) str,
218                                                                                                 nbytes,
219                                                                                                 encoding ? pg_char_to_encoding((char *) encoding) : PG_UTF8,
220                                                                                                 GetDatabaseEncoding());
221
222         pfree(str);
223
224         if (newstr != str)
225         {
226                 free(result);
227
228                 nbytes = strlen(newstr);
229
230                 result = palloc(nbytes + VARHDRSZ);
231                 VARATT_SIZEP(result) = nbytes + VARHDRSZ;
232                 memcpy(VARDATA(result), newstr, nbytes);
233         }
234
235         PG_RETURN_XML_P(result);
236 #else
237         NO_XML_SUPPORT();
238         return 0;
239 #endif
240 }
241
242
243 Datum
244 xml_send(PG_FUNCTION_ARGS)
245 {
246         xmltype    *x = PG_GETARG_XML_P(0);
247         char       *outval = xml_out_internal(x, pg_get_client_encoding());
248         StringInfoData buf;
249
250         pq_begintypsend(&buf);
251         pq_sendstring(&buf, outval);
252         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
253 }
254
255
256 #ifdef USE_LIBXML
257 static void
258 appendStringInfoText(StringInfo str, const text *t)
259 {
260         appendBinaryStringInfo(str, VARDATA(t), VARSIZE(t) - VARHDRSZ);
261 }
262
263
264 static xmltype *
265 stringinfo_to_xmltype(StringInfo buf)
266 {
267         int32 len;
268         xmltype *result;
269
270         len = buf->len + VARHDRSZ;
271         result = palloc(len);
272         VARATT_SIZEP(result) = len;
273         memcpy(VARDATA(result), buf->data, buf->len);
274
275         return result;
276 }
277
278
279 #ifdef NOT_USED
280 static xmltype *
281 cstring_to_xmltype(const char *string)
282 {
283         int32           len;
284         xmltype    *result;
285
286         len = strlen(string) + VARHDRSZ;
287         result = palloc(len);
288         VARATT_SIZEP(result) = len;
289         memcpy(VARDATA(result), string, len - VARHDRSZ);
290
291         return result;
292 }
293 #endif
294
295
296 static xmltype *
297 xmlBuffer_to_xmltype(xmlBufferPtr buf)
298 {
299         int32           len;
300         xmltype    *result;
301
302         len = xmlBufferLength(buf) + VARHDRSZ;
303         result = palloc(len);
304         VARATT_SIZEP(result) = len;
305         memcpy(VARDATA(result), xmlBufferContent(buf), len - VARHDRSZ);
306
307         return result;
308 }
309 #endif
310
311
312 Datum
313 xmlcomment(PG_FUNCTION_ARGS)
314 {
315 #ifdef USE_LIBXML
316         text *arg = PG_GETARG_TEXT_P(0);
317         int len =  VARSIZE(arg) - VARHDRSZ;
318         StringInfoData buf;
319         int i;
320
321         /* check for "--" in string or "-" at the end */
322         for (i = 1; i < len; i++)
323                 if ((VARDATA(arg)[i] == '-' && VARDATA(arg)[i - 1] == '-')
324                         || (VARDATA(arg)[i] == '-' && i == len - 1))
325                                         ereport(ERROR,
326                                                         (errcode(ERRCODE_INVALID_XML_COMMENT),
327                                                          errmsg("invalid XML comment")));
328
329         initStringInfo(&buf);
330         appendStringInfo(&buf, "<!--");
331         appendStringInfoText(&buf, arg);
332         appendStringInfo(&buf, "-->");
333
334         PG_RETURN_XML_P(stringinfo_to_xmltype(&buf));
335 #else
336         NO_XML_SUPPORT();
337         return 0;
338 #endif
339 }
340
341
342
343 /*
344  * TODO: xmlconcat needs to merge the notations and unparsed entities
345  * of the argument values.  Not very important in practice, though.
346  */
347 xmltype *
348 xmlconcat(List *args)
349 {
350 #ifdef USE_LIBXML
351         StringInfoData buf;
352         ListCell   *v;
353
354         int                     global_standalone = 1;
355         xmlChar    *global_version = NULL;
356         bool            global_version_no_value = false;
357
358         initStringInfo(&buf);
359         foreach(v, args)
360         {
361                 size_t          len;
362                 xmlChar    *version;
363                 int                     standalone;
364                 xmltype    *x = DatumGetXmlP(PointerGetDatum(lfirst(v)));
365                 char       *str;
366
367                 len = VARSIZE(x) - VARHDRSZ;
368                 str = palloc(len + 1);
369                 memcpy(str, VARDATA(x), len);
370                 str[len] = '\0';
371
372                 parse_xml_decl((xmlChar *) str, &len, &version, NULL, &standalone);
373
374                 if (standalone == 0 && global_standalone == 1)
375                         global_standalone = 0;
376                 if (standalone < 0)
377                         global_standalone = -1;
378
379                 if (!version)
380                         global_version_no_value = true;
381                 else if (!global_version)
382                         global_version = xmlStrdup(version);
383                 else if (xmlStrcmp(version, global_version) != 0)
384                         global_version_no_value = true;
385
386                 appendStringInfoString(&buf, str + len);
387                 pfree(str);
388         }
389
390         if (!global_version_no_value || global_standalone >= 0)
391         {
392                 StringInfoData buf2;
393
394                 initStringInfo(&buf2);
395
396                 print_xml_decl(&buf2,
397                                            (!global_version_no_value && global_version) ? global_version : NULL,
398                                            0,
399                                            global_standalone);
400
401                 appendStringInfoString(&buf2, buf.data);
402                 buf = buf2;
403         }
404
405         return stringinfo_to_xmltype(&buf);
406 #else
407         NO_XML_SUPPORT();
408         return NULL;
409 #endif
410 }
411
412
413 /*
414  * XMLAGG support
415  */
416 Datum
417 xmlconcat2(PG_FUNCTION_ARGS)
418 {
419         if (PG_ARGISNULL(0))
420         {
421                 if (PG_ARGISNULL(1))
422                         PG_RETURN_NULL();
423                 else
424                         PG_RETURN_XML_P(PG_GETARG_XML_P(1));
425         }
426         else if (PG_ARGISNULL(1))
427                 PG_RETURN_XML_P(PG_GETARG_XML_P(0));
428         else
429                 PG_RETURN_XML_P(xmlconcat(list_make2(PG_GETARG_XML_P(0), PG_GETARG_XML_P(1))));
430 }
431
432
433 Datum
434 texttoxml(PG_FUNCTION_ARGS)
435 {
436         text       *data = PG_GETARG_TEXT_P(0);
437
438         PG_RETURN_XML_P(xmlparse(data, xmloption, true));
439 }
440
441
442 Datum
443 xmltotext(PG_FUNCTION_ARGS)
444 {
445         xmltype    *data = PG_GETARG_XML_P(0);
446
447         PG_RETURN_TEXT_P(xmltotext_with_xmloption(data, xmloption));
448 }
449
450
451 text *
452 xmltotext_with_xmloption(xmltype *data, XmlOptionType xmloption_arg)
453 {
454         if (xmloption_arg == XMLOPTION_DOCUMENT && !xml_is_document(data))
455                 ereport(ERROR,
456                                 (errcode(ERRCODE_NOT_AN_XML_DOCUMENT),
457                                  errmsg("not an XML document")));
458
459         /* It's actually binary compatible, save for the above check. */
460         return (text *) data;
461 }
462
463
464 xmltype *
465 xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
466 {
467 #ifdef USE_LIBXML
468         XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
469         int                     i;
470         ListCell   *arg;
471         ListCell   *narg;
472         bool            isnull;
473         xmltype    *result;
474         Datum           value;
475         char       *str;
476
477         xmlBufferPtr buf;
478         xmlTextWriterPtr writer;
479
480         buf = xmlBufferCreate();
481         writer = xmlNewTextWriterMemory(buf, 0);
482
483         xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
484
485         i = 0;
486         forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names)
487         {
488                 ExprState       *e = (ExprState *) lfirst(arg);
489                 char    *argname = strVal(lfirst(narg));
490
491                 value = ExecEvalExpr(e, econtext, &isnull, NULL);
492                 if (!isnull)
493                 {
494                         str = OutputFunctionCall(&xmlExpr->named_outfuncs[i], value);
495                         xmlTextWriterWriteAttribute(writer, (xmlChar *) argname, (xmlChar *) str);
496                         pfree(str);
497                 }
498                 i++;
499         }
500
501         foreach(arg, xmlExpr->args)
502         {
503                 ExprState       *e = (ExprState *) lfirst(arg);
504
505                 value = ExecEvalExpr(e, econtext, &isnull, NULL);
506                 if (!isnull)
507                         xmlTextWriterWriteRaw(writer, (xmlChar *) map_sql_value_to_xml_value(value, exprType((Node *) e->expr)));
508         }
509
510         xmlTextWriterEndElement(writer);
511         xmlFreeTextWriter(writer);
512
513         result = xmlBuffer_to_xmltype(buf);
514         xmlBufferFree(buf);
515         return result;
516 #else
517         NO_XML_SUPPORT();
518         return NULL;
519 #endif
520 }
521
522
523 xmltype *
524 xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace)
525 {
526 #ifdef USE_LIBXML
527         xmlDocPtr       doc;
528
529         doc = xml_parse(data, xmloption_arg, preserve_whitespace, NULL);
530         xmlFreeDoc(doc);
531
532         return (xmltype *) data;
533 #else
534         NO_XML_SUPPORT();
535         return NULL;
536 #endif
537 }
538
539
540 xmltype *
541 xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null)
542 {
543 #ifdef USE_LIBXML
544         xmltype *result;
545         StringInfoData buf;
546
547         if (pg_strncasecmp(target, "xml", 3) == 0)
548                 ereport(ERROR,
549                                 (errcode(ERRCODE_SYNTAX_ERROR), /* really */
550                                  errmsg("invalid XML processing instruction"),
551                                  errdetail("XML processing instruction target name cannot start with \"xml\".")));
552
553         /*
554          * Following the SQL standard, the null check comes after the
555          * syntax check above.
556          */
557         *result_is_null = arg_is_null;
558         if (*result_is_null)
559                 return NULL;            
560
561         initStringInfo(&buf);
562
563         appendStringInfo(&buf, "<?%s", target);
564
565         if (arg != NULL)
566         {
567                 char *string;
568
569                 string = DatumGetCString(DirectFunctionCall1(textout,
570                                                                                                          PointerGetDatum(arg)));
571                 if (strstr(string, "?>") != NULL)
572                 ereport(ERROR,
573                                 (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
574                                  errmsg("invalid XML processing instruction"),
575                                  errdetail("XML processing instruction cannot contain \"?>\".")));
576
577                 appendStringInfoChar(&buf, ' ');
578                 appendStringInfoString(&buf, string + strspn(string, " "));
579                 pfree(string);
580         }
581         appendStringInfoString(&buf, "?>");
582
583         result = stringinfo_to_xmltype(&buf);
584         pfree(buf.data);
585         return result;
586 #else
587         NO_XML_SUPPORT();
588         return NULL;
589 #endif
590 }
591
592
593 xmltype *
594 xmlroot(xmltype *data, text *version, int standalone)
595 {
596 #ifdef USE_LIBXML
597         char       *str;
598         size_t          len;
599         xmlChar    *orig_version;
600         int                     orig_standalone;
601         StringInfoData buf;
602
603         len = VARSIZE(data) - VARHDRSZ;
604         str = palloc(len + 1);
605         memcpy(str, VARDATA(data), len);
606         str[len] = '\0';
607
608         parse_xml_decl((xmlChar *) str, &len, &orig_version, NULL, &orig_standalone);
609
610         if (version)
611                 orig_version = xml_text2xmlChar(version);
612         else
613                 orig_version = NULL;
614
615         switch (standalone)
616         {
617                 case XML_STANDALONE_YES:
618                         orig_standalone = 1;
619                         break;
620                 case XML_STANDALONE_NO:
621                         orig_standalone = 0;
622                         break;
623                 case XML_STANDALONE_NO_VALUE:
624                         orig_standalone = -1;
625                         break;
626                 case XML_STANDALONE_OMITTED:
627                         /* leave original value */
628                         break;
629         }
630
631         initStringInfo(&buf);
632         print_xml_decl(&buf, orig_version, 0, orig_standalone);
633         appendStringInfoString(&buf, str + len);
634
635         return stringinfo_to_xmltype(&buf);
636 #else
637         NO_XML_SUPPORT();
638         return NULL;
639 #endif
640 }
641
642
643 /*
644  * Validate document (given as string) against DTD (given as external link)
645  * TODO !!! use text instead of cstring for second arg
646  * TODO allow passing DTD as a string value (not only as an URI)
647  * TODO redesign (see comment with '!!!' below)
648  */
649 Datum
650 xmlvalidate(PG_FUNCTION_ARGS)
651 {
652 #ifdef USE_LIBXML
653         text                            *data = PG_GETARG_TEXT_P(0);
654         text                            *dtdOrUri = PG_GETARG_TEXT_P(1);
655         bool                            result = false;
656         xmlParserCtxtPtr        ctxt = NULL;
657         xmlDocPtr                       doc = NULL;
658         xmlDtdPtr                       dtd = NULL;
659
660         xml_init();
661
662         /* We use a PG_TRY block to ensure libxml is cleaned up on error */
663         PG_TRY();
664         {
665                 ctxt = xmlNewParserCtxt();
666                 if (ctxt == NULL)
667                         xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
668                                                 "could not allocate parser context");
669
670                 doc = xmlCtxtReadMemory(ctxt, (char *) VARDATA(data),
671                                                                 VARSIZE(data) - VARHDRSZ,
672                                                                 NULL, NULL, 0);
673                 if (doc == NULL)
674                         xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
675                                                 "could not parse XML data");
676
677 #if 0
678                 uri = xmlCreateURI();
679                 elog(NOTICE, "dtd - %s", dtdOrUri);
680                 dtd = palloc(sizeof(xmlDtdPtr));
681                 uri = xmlParseURI(dtdOrUri);
682                 if (uri == NULL)
683                         xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
684                                                 "not implemented yet... (TODO)");
685                 else
686 #endif
687                         dtd = xmlParseDTD(NULL, xml_text2xmlChar(dtdOrUri));
688
689                 if (dtd == NULL)
690                         xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
691                                                 "could not load DTD");
692
693                 if (xmlValidateDtd(xmlNewValidCtxt(), doc, dtd) == 1)
694                         result = true;
695
696                 if (!result)
697                         xml_ereport(NOTICE, ERRCODE_INVALID_XML_DOCUMENT,
698                                                 "validation against DTD failed");
699
700 #if 0
701                 if (uri)
702                         xmlFreeURI(uri);
703 #endif
704                 if (dtd)
705                         xmlFreeDtd(dtd);
706                 if (doc)
707                         xmlFreeDoc(doc);
708                 if (ctxt)
709                         xmlFreeParserCtxt(ctxt);
710                 xmlCleanupParser();
711         }
712         PG_CATCH();
713         {
714 #if 0
715                 if (uri)
716                         xmlFreeURI(uri);
717 #endif
718                 if (dtd)
719                         xmlFreeDtd(dtd);
720                 if (doc)
721                         xmlFreeDoc(doc);
722                 if (ctxt)
723                         xmlFreeParserCtxt(ctxt);
724                 xmlCleanupParser();
725
726                 PG_RE_THROW();
727         }
728         PG_END_TRY();
729
730         PG_RETURN_BOOL(result);
731 #else /* not USE_LIBXML */
732         NO_XML_SUPPORT();
733         return 0;
734 #endif /* not USE_LIBXML */
735 }
736
737
738 bool
739 xml_is_document(xmltype *arg)
740 {
741 #ifdef USE_LIBXML
742         bool            result;
743         xmlDocPtr       doc = NULL;
744         MemoryContext ccxt = CurrentMemoryContext;
745
746         PG_TRY();
747         {
748                 doc = xml_parse((text *) arg, XMLOPTION_DOCUMENT, true, NULL);
749                 result = true;
750         }
751         PG_CATCH();
752         {
753                 ErrorData *errdata;
754                 MemoryContext ecxt;
755
756                 ecxt = MemoryContextSwitchTo(ccxt);
757                 errdata = CopyErrorData();
758                 if (errdata->sqlerrcode == ERRCODE_INVALID_XML_DOCUMENT)
759                 {
760                         FlushErrorState();
761                         result = false;
762                 }
763                 else
764                 {
765                         MemoryContextSwitchTo(ecxt);
766                         PG_RE_THROW();
767                 }
768         }
769         PG_END_TRY();
770
771         if (doc)
772                 xmlFreeDoc(doc);
773
774         return result;
775 #else /* not USE_LIBXML */
776         NO_XML_SUPPORT();
777         return false;
778 #endif /* not USE_LIBXML */
779 }
780
781
782 #ifdef USE_LIBXML
783
784 /*
785  * Container for some init stuff (not good design!)
786  * TODO xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and check)
787  */
788 static void
789 xml_init(void)
790 {
791         /*
792          * Currently, we have no pure UTF-8 support for internals -- check
793          * if we can work.
794          */
795         if (sizeof (char) != sizeof (xmlChar))
796                 ereport(ERROR,
797                                 (errmsg("could not initialize XML library"),
798                                  errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.",
799                                                    (int) sizeof(char), (int) sizeof(xmlChar))));
800
801         if (xml_err_buf == NULL)
802         {
803                 /* First time through: create error buffer in permanent context */
804                 MemoryContext oldcontext;
805
806                 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
807                 xml_err_buf = makeStringInfo();
808                 MemoryContextSwitchTo(oldcontext);
809         }
810         else
811         {
812                 /* Reset pre-existing buffer to empty */
813                 xml_err_buf->data[0] = '\0';
814                 xml_err_buf->len = 0;
815         }
816         /* Now that xml_err_buf exists, safe to call xml_errorHandler */
817         xmlSetGenericErrorFunc(NULL, xml_errorHandler);
818
819         xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
820
821         xmlInitParser();
822         LIBXML_TEST_VERSION;
823 }
824
825
826 /*
827  * SQL/XML allows storing "XML documents" or "XML content".  "XML
828  * documents" are specified by the XML specification and are parsed
829  * easily by libxml.  "XML content" is specified by SQL/XML as the
830  * production "XMLDecl? content".  But libxml can only parse the
831  * "content" part, so we have to parse the XML declaration ourselves
832  * to complete this.
833  */
834
835 #define CHECK_XML_SPACE(p) if (!xmlIsBlank_ch(*(p))) return XML_ERR_SPACE_REQUIRED
836 #define SKIP_XML_SPACE(p) while (xmlIsBlank_ch(*(p))) (p)++
837
838 static int
839 parse_xml_decl(const xmlChar *str, size_t *lenp, xmlChar **version, xmlChar **encoding, int *standalone)
840 {
841         const xmlChar *p;
842         const xmlChar *save_p;
843         size_t          len;
844
845         p = str;
846
847         if (version)
848                 *version = NULL;
849         if (encoding)
850                 *encoding = NULL;
851         if (standalone)
852                 *standalone = -1;
853
854         if (xmlStrncmp(p, (xmlChar *)"<?xml", 5) != 0)
855                 goto finished;
856
857         p += 5;
858
859         /* version */
860         CHECK_XML_SPACE(p);
861         SKIP_XML_SPACE(p);
862         if (xmlStrncmp(p, (xmlChar *)"version", 7) != 0)
863                 return XML_ERR_VERSION_MISSING;
864         p += 7;
865         SKIP_XML_SPACE(p);
866         if (*p != '=')
867                 return XML_ERR_VERSION_MISSING;
868         p += 1;
869         SKIP_XML_SPACE(p);
870
871         if (*p == '\'' || *p == '"')
872         {
873                 const xmlChar *q;
874
875                 q = xmlStrchr(p + 1, *p);
876                 if (!q)
877                         return XML_ERR_VERSION_MISSING;
878
879                 if (version)
880                         *version = xmlStrndup(p + 1, q - p - 1);
881                 p = q + 1;
882         }
883         else
884                 return XML_ERR_VERSION_MISSING;
885
886         /* encoding */
887         save_p = p;
888         SKIP_XML_SPACE(p);
889         if (xmlStrncmp(p, (xmlChar *)"encoding", 8) == 0)
890         {
891                 CHECK_XML_SPACE(save_p);
892                 p += 8;
893                 SKIP_XML_SPACE(p);
894                 if (*p != '=')
895                         return XML_ERR_MISSING_ENCODING;
896                 p += 1;
897                 SKIP_XML_SPACE(p);
898
899                 if (*p == '\'' || *p == '"')
900                 {
901                         const xmlChar *q;
902
903                         q = xmlStrchr(p + 1, *p);
904                         if (!q)
905                                 return XML_ERR_MISSING_ENCODING;
906
907                         if (encoding)
908                         *encoding = xmlStrndup(p + 1, q - p - 1);
909                         p = q + 1;
910                 }
911                 else
912                         return XML_ERR_MISSING_ENCODING;
913         }
914         else
915         {
916                 p = save_p;
917         }
918
919         /* standalone */
920         save_p = p;
921         SKIP_XML_SPACE(p);
922         if (xmlStrncmp(p, (xmlChar *)"standalone", 10) == 0)
923         {
924                 CHECK_XML_SPACE(save_p);
925                 p += 10;
926                 SKIP_XML_SPACE(p);
927                 if (*p != '=')
928                         return XML_ERR_STANDALONE_VALUE;
929                 p += 1;
930                 SKIP_XML_SPACE(p);
931                 if (xmlStrncmp(p, (xmlChar *)"'yes'", 5) == 0 || xmlStrncmp(p, (xmlChar *)"\"yes\"", 5) == 0)
932                 {
933                         *standalone = 1;
934                         p += 5;
935                 }
936                 else if (xmlStrncmp(p, (xmlChar *)"'no'", 4) == 0 || xmlStrncmp(p, (xmlChar *)"\"no\"", 4) == 0)
937                 {
938                         *standalone = 0;
939                         p += 4;
940                 }
941                 else
942                         return XML_ERR_STANDALONE_VALUE;
943         }
944         else
945         {
946                 p = save_p;
947         }
948
949         SKIP_XML_SPACE(p);
950         if (xmlStrncmp(p, (xmlChar *)"?>", 2) != 0)
951                 return XML_ERR_XMLDECL_NOT_FINISHED;
952         p += 2;
953
954 finished:
955         len = p - str;
956
957         for (p = str; p < str + len; p++)
958                 if (*p > 127)
959                         return XML_ERR_INVALID_CHAR;
960
961         if (lenp)
962                 *lenp = len;
963
964         return XML_ERR_OK;
965 }
966
967
968 /*
969  * Write an XML declaration.  On output, we adjust the XML declaration
970  * as follows.  (These rules are the moral equivalent of the clause
971  * "Serialization of an XML value" in the SQL standard.)
972  *
973  * We try to avoid generating an XML declaration if possible.  This is
974  * so that you don't get trivial things like xml '<foo/>' resulting in
975  * '<?xml version="1.0"?><foo/>', which would surely be annoying.  We
976  * must provide a declaration if the standalone property is specified
977  * or if we include an encoding declaration.  If we have a
978  * declaration, we must specify a version (XML requires this).
979  * Otherwise we only make a declaration if the version is not "1.0",
980  * which is the default version specified in SQL:2003.
981  */
982 static bool
983 print_xml_decl(StringInfo buf, const xmlChar *version, pg_enc encoding, int standalone)
984 {
985         if ((version && strcmp((char *) version, PG_XML_DEFAULT_VERSION) != 0)
986                 || (encoding && encoding != PG_UTF8)
987                 || standalone != -1)
988         {
989                 appendStringInfoString(buf, "<?xml");
990
991                 if (version)
992                         appendStringInfo(buf, " version=\"%s\"", version);
993                 else
994                         appendStringInfo(buf, " version=\"%s\"", PG_XML_DEFAULT_VERSION);
995
996                 if (encoding && encoding != PG_UTF8)
997                         /* XXX might be useful to convert this to IANA names
998                          * (ISO-8859-1 instead of LATIN1 etc.); needs field
999                          * experience */
1000                         appendStringInfo(buf, " encoding=\"%s\"", pg_encoding_to_char(encoding));
1001
1002                 if (standalone == 1)
1003                         appendStringInfoString(buf, " standalone=\"yes\"");
1004                 else if (standalone == 0)
1005                         appendStringInfoString(buf, " standalone=\"no\"");
1006                 appendStringInfoString(buf, "?>");
1007
1008                 return true;
1009         }
1010         else
1011                 return false;
1012 }
1013
1014
1015 /*
1016  * Convert a C string to XML internal representation
1017  *
1018  * TODO maybe, libxml2's xmlreader is better? (do not construct DOM, yet do not use SAX - see xml_reader.c)
1019  */
1020 static xmlDocPtr
1021 xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, xmlChar *encoding)
1022 {
1023         int32                           len;
1024         xmlChar                         *string;
1025         xmlChar                         *utf8string;
1026         xmlParserCtxtPtr        ctxt = NULL;
1027         xmlDocPtr                       doc = NULL;
1028
1029         len = VARSIZE(data) - VARHDRSZ; /* will be useful later */
1030         string = xml_text2xmlChar(data);
1031
1032         utf8string = pg_do_encoding_conversion(string,
1033                                                                                    len,
1034                                                                                    encoding
1035                                                                                    ? pg_char_to_encoding((char *) encoding)
1036                                                                                    : GetDatabaseEncoding(),
1037                                                                                    PG_UTF8);
1038
1039         xml_init();
1040
1041         /* We use a PG_TRY block to ensure libxml is cleaned up on error */
1042         PG_TRY();
1043         {
1044                 ctxt = xmlNewParserCtxt();
1045                 if (ctxt == NULL)
1046                         xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
1047                                                 "could not allocate parser context");
1048
1049                 if (xmloption_arg == XMLOPTION_DOCUMENT)
1050                 {
1051                         /*
1052                          * Note, that here we try to apply DTD defaults
1053                          * (XML_PARSE_DTDATTR) according to SQL/XML:10.16.7.d:
1054                          * 'Default valies defined by internal DTD are applied'.
1055                          * As for external DTDs, we try to support them too, (see
1056                          * SQL/XML:10.16.7.e)
1057                          */
1058                         doc = xmlCtxtReadDoc(ctxt, utf8string,
1059                                                                  NULL,
1060                                                                  "UTF-8",
1061                                                                  XML_PARSE_NOENT | XML_PARSE_DTDATTR
1062                                                                  | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS));
1063                         if (doc == NULL)
1064                                 xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
1065                                                         "invalid XML document");
1066                 }
1067                 else
1068                 {
1069                         int                     res_code;
1070                         size_t count;
1071                         xmlChar    *version = NULL;
1072                         int standalone = -1;
1073
1074                         doc = xmlNewDoc(NULL);
1075
1076                         res_code = parse_xml_decl(utf8string, &count, &version, NULL, &standalone);
1077                         if (res_code != 0)
1078                                 xml_ereport_by_code(ERROR, ERRCODE_INVALID_XML_CONTENT,
1079                                                                         "invalid XML content: invalid XML declaration", res_code);
1080
1081                         res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, utf8string + count, NULL);
1082                         if (res_code != 0)
1083                                 xml_ereport(ERROR, ERRCODE_INVALID_XML_CONTENT,
1084                                                         "invalid XML content");
1085
1086                         doc->version = xmlStrdup(version);
1087                         doc->encoding = xmlStrdup((xmlChar *) "UTF-8");
1088                         doc->standalone = standalone;
1089                 }
1090
1091                 if (ctxt)
1092                         xmlFreeParserCtxt(ctxt);
1093                 xmlCleanupParser();
1094         }
1095         PG_CATCH();
1096         {
1097                 if (doc)
1098                         xmlFreeDoc(doc);
1099                 doc = NULL;
1100                 if (ctxt)
1101                         xmlFreeParserCtxt(ctxt);
1102                 xmlCleanupParser();
1103
1104                 PG_RE_THROW();
1105         }
1106         PG_END_TRY();
1107
1108         return doc;
1109 }
1110
1111
1112 /*
1113  * xmlChar<->text convertions
1114  */
1115 static xmlChar *
1116 xml_text2xmlChar(text *in)
1117 {
1118         int32           len = VARSIZE(in) - VARHDRSZ;
1119         xmlChar         *res;
1120
1121         res = palloc(len + 1);
1122         memcpy(res, VARDATA(in), len);
1123         res[len] = '\0';
1124
1125         return(res);
1126 }
1127
1128
1129 /*
1130  * Wrappers for memory management functions
1131  */
1132 static void *
1133 xml_palloc(size_t size)
1134 {
1135         return palloc(size);
1136 }
1137
1138
1139 static void *
1140 xml_repalloc(void *ptr, size_t size)
1141 {
1142         return repalloc(ptr, size);
1143 }
1144
1145
1146 static void
1147 xml_pfree(void *ptr)
1148 {
1149         pfree(ptr);
1150 }
1151
1152
1153 static char *
1154 xml_pstrdup(const char *string)
1155 {
1156         return pstrdup(string);
1157 }
1158
1159
1160 /*
1161  * Wrapper for "ereport" function for XML-related errors.  The "msg"
1162  * is the SQL-level message; some can be adopted from the SQL/XML
1163  * standard.  This function adds libxml's native error messages, if
1164  * any, as detail.
1165  */
1166 static void
1167 xml_ereport(int level, int sqlcode,
1168                         const char *msg)
1169 {
1170         char *detail;
1171
1172         if (xml_err_buf->len > 0)
1173         {
1174                 detail = pstrdup(xml_err_buf->data);
1175                 xml_err_buf->data[0] = '\0';
1176                 xml_err_buf->len = 0;
1177         }
1178         else
1179                 detail = NULL;
1180
1181         /* libxml error messages end in '\n'; get rid of it */
1182         if (detail)
1183         {
1184                 size_t len;
1185
1186                 len = strlen(detail);
1187                 if (len > 0 && detail[len-1] == '\n')
1188                         detail[len-1] = '\0';
1189
1190                 ereport(level,
1191                                 (errcode(sqlcode),
1192                                  errmsg("%s", msg),
1193                                  errdetail("%s", detail)));
1194         }
1195         else
1196         {
1197                 ereport(level,
1198                                 (errcode(sqlcode),
1199                                  errmsg("%s", msg)));
1200         }
1201 }
1202
1203
1204 /*
1205  * Error handler for libxml error messages
1206  */
1207 static void
1208 xml_errorHandler(void *ctxt, const char *msg,...)
1209 {
1210         /* Append the formatted text to xml_err_buf */
1211         for (;;)
1212         {
1213                 va_list         args;
1214                 bool            success;
1215
1216                 /* Try to format the data. */
1217                 va_start(args, msg);
1218                 success = appendStringInfoVA(xml_err_buf, msg, args);
1219                 va_end(args);
1220
1221                 if (success)
1222                         break;
1223
1224                 /* Double the buffer size and try again. */
1225                 enlargeStringInfo(xml_err_buf, xml_err_buf->maxlen);
1226         }
1227 }
1228
1229
1230 /*
1231  * Wrapper for "ereport" function for XML-related errors.  The "msg"
1232  * is the SQL-level message; some can be adopted from the SQL/XML
1233  * standard.  This function uses "code" to create a textual detail
1234  * message.  At the moment, we only need to cover those codes that we
1235  * may raise in this file.
1236  */
1237 static void
1238 xml_ereport_by_code(int level, int sqlcode,
1239                                         const char *msg, int code)
1240 {
1241     const char *det;
1242
1243     switch (code)
1244         {
1245                 case XML_ERR_INVALID_CHAR:
1246                         det = "Invalid character value";
1247                         break;
1248                 case XML_ERR_SPACE_REQUIRED:
1249                         det = "Space required";
1250                         break;
1251                 case XML_ERR_STANDALONE_VALUE:
1252                         det = "standalone accepts only 'yes' or 'no'";
1253                         break;
1254                 case XML_ERR_VERSION_MISSING:
1255                         det = "Malformed declaration expecting version";
1256                         break;
1257                 case XML_ERR_MISSING_ENCODING:
1258                         det = "Missing encoding in text declaration";
1259                         break;
1260                 case XML_ERR_XMLDECL_NOT_FINISHED:
1261                         det = "Parsing XML declaration: '?>' expected";
1262                         break;
1263         default:
1264             det = "Unrecognized libxml error code: %d";
1265                         break;
1266         }
1267
1268         ereport(level,
1269                         (errcode(sqlcode),
1270                          errmsg("%s", msg),
1271                          errdetail(det, code)));
1272 }
1273
1274
1275 /*
1276  * Convert one char in the current server encoding to a Unicode codepoint.
1277  */
1278 static pg_wchar
1279 sqlchar_to_unicode(char *s)
1280 {
1281         char *utf8string;
1282         pg_wchar ret[2];                        /* need space for trailing zero */
1283
1284         utf8string = (char *) pg_do_encoding_conversion((unsigned char *) s,
1285                                                                                                         pg_mblen(s),
1286                                                                                                         GetDatabaseEncoding(),
1287                                                                                                         PG_UTF8);
1288
1289         pg_encoding_mb2wchar_with_len(PG_UTF8, utf8string, ret, pg_mblen(s));
1290
1291         return ret[0];
1292 }
1293
1294
1295 static bool
1296 is_valid_xml_namefirst(pg_wchar c)
1297 {
1298         /* (Letter | '_' | ':') */
1299         return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
1300                         || c == '_' || c == ':');
1301 }
1302
1303
1304 static bool
1305 is_valid_xml_namechar(pg_wchar c)
1306 {
1307         /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
1308         return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
1309                         || xmlIsDigitQ(c)
1310                         || c == '.' || c == '-' || c == '_' || c == ':'
1311                         || xmlIsCombiningQ(c)
1312                         || xmlIsExtenderQ(c));
1313 }
1314 #endif /* USE_LIBXML */
1315
1316
1317 /*
1318  * Map SQL identifier to XML name; see SQL/XML:2003 section 9.1.
1319  */
1320 char *
1321 map_sql_identifier_to_xml_name(char *ident, bool fully_escaped, bool escape_period)
1322 {
1323         /*
1324          * SQL/XML doesn't make use of this case anywhere, so it's
1325          * probably a mistake.
1326          */
1327         Assert(fully_escaped || !escape_period);
1328
1329 #ifdef USE_LIBXML
1330         StringInfoData buf;
1331         char *p;
1332
1333         initStringInfo(&buf);
1334
1335         for (p = ident; *p; p += pg_mblen(p))
1336         {
1337                 if (*p == ':' && (p == ident || fully_escaped))
1338                         appendStringInfo(&buf, "_x003A_");
1339                 else if (*p == '_' && *(p+1) == 'x')
1340                         appendStringInfo(&buf, "_x005F_");
1341                 else if (fully_escaped && p == ident &&
1342                                  pg_strncasecmp(p, "xml", 3) == 0)
1343                 {
1344                         if (*p == 'x')
1345                                 appendStringInfo(&buf, "_x0078_");
1346                         else
1347                                 appendStringInfo(&buf, "_x0058_");
1348                 }
1349                 else if (escape_period && *p == '.')
1350                         appendStringInfo(&buf, "_x002E_");
1351                 else
1352                 {
1353                         pg_wchar u = sqlchar_to_unicode(p);
1354
1355                         if ((p == ident)
1356                                 ? !is_valid_xml_namefirst(u)
1357                                 : !is_valid_xml_namechar(u))
1358                                 appendStringInfo(&buf, "_x%04X_", (unsigned int) u);
1359                         else
1360                                 appendBinaryStringInfo(&buf, p, pg_mblen(p));
1361                 }
1362         }
1363
1364         return buf.data;
1365 #else /* not USE_LIBXML */
1366         NO_XML_SUPPORT();
1367         return NULL;
1368 #endif /* not USE_LIBXML */
1369 }
1370
1371
1372 /*
1373  * Map a Unicode codepoint into the current server encoding.
1374  */
1375 static char *
1376 unicode_to_sqlchar(pg_wchar c)
1377 {
1378         static unsigned char utf8string[5];     /* need trailing zero */
1379
1380         if (c <= 0x7F)
1381         {
1382                 utf8string[0] = c;
1383         }
1384         else if (c <= 0x7FF)
1385         {
1386                 utf8string[0] = 0xC0 | ((c >> 6) & 0x1F);
1387                 utf8string[1] = 0x80 | (c & 0x3F);
1388         }
1389         else if (c <= 0xFFFF)
1390         {
1391                 utf8string[0] = 0xE0 | ((c >> 12) & 0x0F);
1392                 utf8string[1] = 0x80 | ((c >> 6) & 0x3F);
1393                 utf8string[2] = 0x80 | (c & 0x3F);
1394         }
1395         else
1396         {
1397                 utf8string[0] = 0xF0 | ((c >> 18) & 0x07);
1398                 utf8string[1] = 0x80 | ((c >> 12) & 0x3F);
1399                 utf8string[2] = 0x80 | ((c >> 6) & 0x3F);
1400                 utf8string[3] = 0x80 | (c & 0x3F);
1401         }
1402
1403         return (char *) pg_do_encoding_conversion(utf8string,
1404                                                                                           pg_mblen((char *) utf8string),
1405                                                                                           PG_UTF8,
1406                                                                                           GetDatabaseEncoding());
1407 }
1408
1409
1410 /*
1411  * Map XML name to SQL identifier; see SQL/XML:2003 section 9.17.
1412  */
1413 char *
1414 map_xml_name_to_sql_identifier(char *name)
1415 {
1416         StringInfoData buf;
1417         char *p;
1418
1419         initStringInfo(&buf);
1420
1421         for (p = name; *p; p += pg_mblen(p))
1422         {
1423                 if (*p == '_' && *(p+1) == 'x'
1424                         && isxdigit((unsigned char) *(p+2))
1425                         && isxdigit((unsigned char) *(p+3))
1426                         && isxdigit((unsigned char) *(p+4))
1427                         && isxdigit((unsigned char) *(p+5))
1428                         && *(p+6) == '_')
1429                 {
1430                         unsigned int u;
1431
1432                         sscanf(p + 2, "%X", &u);
1433                         appendStringInfoString(&buf, unicode_to_sqlchar(u));
1434                         p += 6;
1435                 }
1436                 else
1437                         appendBinaryStringInfo(&buf, p, pg_mblen(p));
1438         }
1439
1440         return buf.data;
1441 }
1442
1443
1444 /*
1445  * Map SQL value to XML value; see SQL/XML:2003 section 9.16.
1446  */
1447 char *
1448 map_sql_value_to_xml_value(Datum value, Oid type)
1449 {
1450         StringInfoData buf;
1451
1452         initStringInfo(&buf);
1453
1454         if (is_array_type(type))
1455         {
1456                 int i;
1457                 ArrayType *array;
1458                 Oid elmtype;
1459                 int16 elmlen;
1460                 bool elmbyval;
1461                 char elmalign;
1462
1463                 array = DatumGetArrayTypeP(value);
1464
1465                 /* TODO: need some code-fu here to remove this limitation */
1466                 if (ARR_NDIM(array) != 1)
1467                         ereport(ERROR,
1468                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1469                                          errmsg("only supported for one-dimensional array")));
1470
1471                 elmtype = ARR_ELEMTYPE(array);
1472                 get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
1473
1474                 for (i = ARR_LBOUND(array)[0];
1475                          i < ARR_LBOUND(array)[0] + ARR_DIMS(array)[0];
1476                          i++)
1477                 {
1478                         Datum subval;
1479                         bool isnull;
1480
1481                         subval = array_ref(array, 1, &i, -1, elmlen, elmbyval, elmalign, &isnull);
1482                         appendStringInfoString(&buf, "<element>");
1483                         appendStringInfoString(&buf, map_sql_value_to_xml_value(subval, elmtype));
1484                         appendStringInfoString(&buf, "</element>");
1485                 }
1486         }
1487         else
1488         {
1489                 Oid typeOut;
1490                 bool isvarlena;
1491                 char *p, *str;
1492
1493                 if (type == BOOLOID)
1494                 {
1495                         if (DatumGetBool(value))
1496                                 return "true";
1497                         else
1498                                 return "false";
1499                 }
1500
1501                 getTypeOutputInfo(type, &typeOut, &isvarlena);
1502                 str = OidOutputFunctionCall(typeOut, value);
1503
1504                 if (type == XMLOID)
1505                         return str;
1506
1507 #ifdef USE_LIBXML
1508                 if (type == BYTEAOID)
1509                 {
1510                         xmlBufferPtr buf;
1511                         xmlTextWriterPtr writer;
1512                         char *result;
1513
1514                         buf = xmlBufferCreate();
1515                         writer = xmlNewTextWriterMemory(buf, 0);
1516
1517                         if (xmlbinary == XMLBINARY_BASE64)
1518                                 xmlTextWriterWriteBase64(writer, VARDATA(value), 0, VARSIZE(value) - VARHDRSZ);
1519                         else
1520                                 xmlTextWriterWriteBinHex(writer, VARDATA(value), 0, VARSIZE(value) - VARHDRSZ);
1521
1522                         xmlFreeTextWriter(writer);
1523                         result = pstrdup((const char *) xmlBufferContent(buf));
1524                         xmlBufferFree(buf);
1525                         return result;
1526                 }
1527 #endif /* USE_LIBXML */
1528
1529                 for (p = str; *p; p += pg_mblen(p))
1530                 {
1531                         switch (*p)
1532                         {
1533                                 case '&':
1534                                         appendStringInfo(&buf, "&amp;");
1535                                         break;
1536                                 case '<':
1537                                         appendStringInfo(&buf, "&lt;");
1538                                         break;
1539                                 case '>':
1540                                         appendStringInfo(&buf, "&gt;");
1541                                         break;
1542                                 case '\r':
1543                                         appendStringInfo(&buf, "&#x0d;");
1544                                         break;
1545                                 default:
1546                                         appendBinaryStringInfo(&buf, p, pg_mblen(p));
1547                                         break;
1548                         }
1549                 }
1550         }
1551
1552         return buf.data;
1553 }