OSDN Git Service

Code review for XML patch. Instill a bit of sanity in the location of
[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-2006, 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.3 2006/12/24 00:29:19 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.
23  */
24
25 #include "postgres.h"
26
27 #ifdef USE_LIBXML
28 #include <libxml/chvalid.h>
29 #include <libxml/parser.h>
30 #include <libxml/tree.h>
31 #include <libxml/uri.h>
32 #include <libxml/xmlerror.h>
33 #endif /* USE_LIBXML */
34
35 #include "fmgr.h"
36 #include "mb/pg_wchar.h"
37 #include "nodes/execnodes.h"
38 #include "utils/builtins.h"
39 #include "utils/xml.h"
40
41
42 #ifdef USE_LIBXML
43
44 #define PG_XML_DEFAULT_URI "dummy.xml"
45 #define XML_ERRBUF_SIZE 200
46
47
48 static void     xml_init(void);
49 static void    *xml_palloc(size_t size);
50 static void    *xml_repalloc(void *ptr, size_t size);
51 static void     xml_pfree(void *ptr);
52 static char    *xml_pstrdup(const char *string);
53 static void     xml_ereport(int level, char *msg, void *ctxt);
54 static void     xml_errorHandler(void *ctxt, const char *msg, ...);
55 static void     xml_ereport_by_code(int level, char *msg, int errcode);
56 static xmlChar *xml_text2xmlChar(text *in);
57 static xmlDocPtr xml_parse(text *data, int opts, bool is_document);
58
59
60 /* Global variables */
61 /* taken from contrib/xml2 */
62 /* FIXME: DO NOT USE global vars !!! */
63 char       *xml_errbuf;                         /* per line error buffer */
64 char       *xml_errmsg = NULL;          /* overall error message */
65
66 #endif /* USE_LIBXML */
67
68
69 #define NO_XML_SUPPORT() ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("no XML support in this installation")))
70
71
72 Datum
73 xml_in(PG_FUNCTION_ARGS)
74 {
75 #ifdef USE_LIBXML
76         char            *s = PG_GETARG_CSTRING(0);
77         size_t          len;
78         xmltype         *vardata;
79
80         len = strlen(s);
81         vardata = palloc(len + VARHDRSZ);
82         VARATT_SIZEP(vardata) = len + VARHDRSZ;
83         memcpy(VARDATA(vardata), s, len);
84
85         /*
86          * Parse the data to check if it is well-formed XML data.  Assume
87          * that ERROR occurred if parsing failed.  Do we need DTD
88          * validation (if DTD exists)?
89          */
90         xml_parse(vardata, XML_PARSE_DTDATTR | XML_PARSE_DTDVALID, false);
91
92         PG_RETURN_XML_P(vardata);
93 #else
94         NO_XML_SUPPORT();
95         return 0;
96 #endif
97 }
98
99
100 Datum
101 xml_out(PG_FUNCTION_ARGS)
102 {
103         xmltype         *s = PG_GETARG_XML_P(0);
104         char            *result;
105         int32           len;
106
107         len = VARSIZE(s) - VARHDRSZ;
108         result = palloc(len + 1);
109         memcpy(result, VARDATA(s), len);
110         result[len] = '\0';
111
112         PG_RETURN_CSTRING(result);
113 }
114
115
116 #ifdef USE_LIBXML
117 static void
118 appendStringInfoText(StringInfo str, const text *t)
119 {
120         appendBinaryStringInfo(str, VARDATA(t), VARSIZE(t) - VARHDRSZ);
121 }
122
123
124 static xmltype *
125 stringinfo_to_xmltype(StringInfo buf)
126 {
127         int32 len;
128         xmltype *result;
129
130         len = buf->len + VARHDRSZ;
131         result = palloc(len);
132         VARATT_SIZEP(result) = len;
133         memcpy(VARDATA(result), buf->data, buf->len);
134
135         return result;
136 }
137 #endif
138
139
140 Datum
141 xmlcomment(PG_FUNCTION_ARGS)
142 {
143 #ifdef USE_LIBXML
144         text *arg = PG_GETARG_TEXT_P(0);
145         int len =  VARATT_SIZEP(arg) - VARHDRSZ;
146         StringInfoData buf;
147         int i;
148
149         /* check for "--" in string or "-" at the end */
150         for (i = 1; i < len; i++)
151                 if ((VARDATA(arg)[i] == '-' && VARDATA(arg)[i - 1] == '-')
152                         || (VARDATA(arg)[i] == '-' && i == len - 1))
153                                         ereport(ERROR,
154                                                         (errcode(ERRCODE_INVALID_XML_COMMENT),
155                                                          errmsg("invalid XML comment")));
156
157         initStringInfo(&buf);
158         appendStringInfo(&buf, "<!--");
159         appendStringInfoText(&buf, arg);
160         appendStringInfo(&buf, "-->");
161
162         PG_RETURN_XML_P(stringinfo_to_xmltype(&buf));
163 #else
164         NO_XML_SUPPORT();
165         return 0;
166 #endif
167 }
168
169
170 Datum
171 texttoxml(PG_FUNCTION_ARGS)
172 {
173         text       *data = PG_GETARG_TEXT_P(0);
174
175         PG_RETURN_XML_P(xmlparse(data, false, true));
176 }
177
178
179 xmltype *
180 xmlparse(text *data, bool is_document, bool preserve_whitespace)
181 {
182 #ifdef USE_LIBXML
183         if (!preserve_whitespace)
184                 ereport(WARNING,
185                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
186                                  errmsg("XMLPARSE with STRIP WHITESPACE is not implemented")));
187
188         /*
189          * Note, that here we try to apply DTD defaults
190          * (XML_PARSE_DTDATTR) according to SQL/XML:10.16.7.d: 'Default
191          * valies defined by internal DTD are applied'.  As for external
192          * DTDs, we try to support them too, (see SQL/XML:10.16.7.e)
193          */
194         xml_parse(data, XML_PARSE_DTDATTR, is_document);
195
196         return (xmltype *) data;
197 #else
198         NO_XML_SUPPORT();
199         return NULL;
200 #endif
201 }
202
203
204 xmltype *
205 xmlpi(char *target, text *arg)
206 {
207 #ifdef USE_LIBXML
208         xmltype *result;
209         StringInfoData buf;
210
211         if (pg_strncasecmp(target, "xml", 3) == 0)
212                 ereport(ERROR,
213                                 (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
214                                  errmsg("invalid XML processing instruction"),
215                                  errdetail("XML processing instruction target name cannot start with \"xml\".")));
216
217         initStringInfo(&buf);
218
219         appendStringInfo(&buf, "<?%s", target);
220
221         if (arg != NULL)
222         {
223                 char *string;
224
225                 string = DatumGetCString(DirectFunctionCall1(textout,
226                                                                                                          PointerGetDatum(arg)));
227                 if (strstr(string, "?>") != NULL)
228                 ereport(ERROR,
229                                 (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
230                                  errmsg("invalid XML processing instruction"),
231                                  errdetail("XML processing instruction cannot contain \"?>\".")));
232
233                 appendStringInfoChar(&buf, ' ');
234                 appendStringInfoString(&buf, string);
235                 pfree(string);
236         }
237         appendStringInfoString(&buf, "?>");
238
239         result = stringinfo_to_xmltype(&buf);
240         pfree(buf.data);
241         return result;
242 #else
243         NO_XML_SUPPORT();
244         return NULL;
245 #endif
246 }
247
248
249 xmltype *
250 xmlroot(xmltype *data, text *version, int standalone)
251 {
252 #ifdef USE_LIBXML
253         xmltype *result;
254         StringInfoData buf;
255
256         initStringInfo(&buf);
257
258         /*
259          * FIXME: This is probably supposed to be cleverer if there
260          * already is an XML preamble.
261          */
262         appendStringInfo(&buf,"<?xml");
263         if (version)
264         {
265                 appendStringInfo(&buf, " version=\"");
266                 appendStringInfoText(&buf, version);
267                 appendStringInfo(&buf, "\"");
268         }
269         if (standalone)
270                 appendStringInfo(&buf, " standalone=\"%s\"",
271                                                  (standalone == 1 ? "yes" : "no"));
272         appendStringInfo(&buf, "?>");
273         appendStringInfoText(&buf, (text *) data);
274
275         result = stringinfo_to_xmltype(&buf);
276         pfree(buf.data);
277         return result;
278 #else
279         NO_XML_SUPPORT();
280         return NULL;
281 #endif
282 }
283
284
285 /*
286  * Validate document (given as string) against DTD (given as external link)
287  * TODO !!! use text instead of cstring for second arg
288  * TODO allow passing DTD as a string value (not only as an URI)
289  * TODO redesign (see comment with '!!!' below)
290  */
291 Datum
292 xmlvalidate(PG_FUNCTION_ARGS)
293 {
294 #ifdef USE_LIBXML
295         text                            *data = PG_GETARG_TEXT_P(0);
296         text                            *dtdOrUri = PG_GETARG_TEXT_P(1);
297         bool                            result = FALSE;
298         xmlParserCtxtPtr        ctxt; /* the parser context */
299         xmlDocPtr                       doc; /* the resulting document tree */
300         xmlDtdPtr                       dtd;
301
302         xml_init();
303
304         ctxt = xmlNewParserCtxt();
305         if (ctxt == NULL)
306                 xml_ereport(ERROR, "could not allocate parser context", ctxt);
307         doc = xmlCtxtReadMemory(ctxt, (char *) VARDATA(data),
308                                                         VARSIZE(data) - VARHDRSZ, PG_XML_DEFAULT_URI, NULL, 0);
309         if (doc == NULL)
310                 xml_ereport(ERROR, "could not parse XML data", ctxt);
311
312 #if 0
313         uri = xmlCreateURI();
314         ereport(NOTICE, (errcode(0),errmsg(" dtd - %s", dtdOrUri)));
315         dtd = palloc(sizeof(xmlDtdPtr));
316         uri = xmlParseURI(dtdOrUri);
317         if (uri == NULL)
318                 xml_ereport(ERROR, "not implemented yet... (TODO)", ctxt);
319         else
320 #endif
321                 dtd = xmlParseDTD(NULL, xml_text2xmlChar(dtdOrUri));
322
323         if (dtd == NULL)
324         {
325 #if 0
326                 xmlFreeDoc(doc);
327                 xmlFreeParserCtxt(ctxt);
328 #endif
329                 xml_ereport(ERROR, "could not load DTD", ctxt);
330         }
331
332         if (xmlValidateDtd(xmlNewValidCtxt(), doc, dtd) == 1)
333                 result = TRUE;
334
335 #if 0
336         xmlFreeURI(uri);
337         xmlFreeDtd(dtd);
338         xmlFreeDoc(doc);
339         xmlFreeParserCtxt(ctxt);
340         xmlCleanupParser();
341 #endif
342
343         if (!result)
344                 xml_ereport(NOTICE, "validation against DTD failed", ctxt);
345
346         PG_RETURN_BOOL(result);
347 #else /* not USE_LIBXML */
348         NO_XML_SUPPORT();
349         return 0;
350 #endif /* not USE_LIBXML */
351 }
352
353
354 #ifdef USE_LIBXML
355
356 /*
357  * Container for some init stuff (not good design!)
358  * TODO xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and check)
359  */
360 static void
361 xml_init(void)
362 {
363         /*
364          * Currently, we have no pure UTF-8 support for internals -- check
365          * if we can work.
366          */
367         if (sizeof (char) != sizeof (xmlChar))
368                 ereport(ERROR,
369                                 (errmsg("could not initialize XML library"),
370                                  errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.",
371                                                    (int) sizeof(char), (int) sizeof(xmlChar))));
372
373         xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
374         xmlInitParser();
375         LIBXML_TEST_VERSION;
376         /* do not flood PG's logfile with libxml error messages - reset error handler*/
377         xmlSetGenericErrorFunc(NULL, xml_errorHandler);
378         xml_errmsg = NULL;
379         xml_errbuf = palloc(XML_ERRBUF_SIZE);
380         memset(xml_errbuf, 0, XML_ERRBUF_SIZE);
381 }
382
383
384 /*
385  * Convert a C string to XML internal representation
386  * (same things as for TEXT, but with checking the data for well-formedness
387  * and, moreover, validation against DTD, if needed).
388  * NOTICE: We use TEXT type as internal storage type. In the future,
389  * we plan to create own storage type (maybe several types/strategies)
390  * TODO predefined DTDs / XSDs and validation
391  * TODO validation against XML Schema
392  * TODO maybe, libxml2's xmlreader is better? (do not construct DOM, yet do not use SAX - see xml_reader.c)
393  * TODO what about internal URI for docs? (see PG_XML_DEFAULT_URI below)
394  */
395 static xmlDocPtr
396 xml_parse(text *data, int opts, bool is_document)
397 {
398         bool                            validationFailed = FALSE;
399         xmlParserCtxtPtr        ctxt; /* the parser context */
400         xmlDocPtr                       doc; /* the resulting document tree */
401         int                                     res_code;
402         int32                           len;
403         xmlChar                         *string;
404 #ifdef XML_DEBUG_DTD_CONST
405         xmlDtdPtr                       dtd; /* pointer to DTD */
406 #endif
407
408         xml_init();
409
410         len = VARSIZE(data) - VARHDRSZ; /* will be useful later */
411         string = xml_text2xmlChar(data);
412
413         ctxt = xmlNewParserCtxt();
414         if (ctxt == NULL)
415                 xml_ereport(ERROR, "could not allocate parser context", ctxt);
416
417         /* first, we try to parse the string as XML doc, then, as XML chunk */
418         ereport(DEBUG3, (errmsg("string to parse: %s", string)));
419         if (len >= 5 && strncmp((char *) string, "<?xml", 5) == 0)
420         {
421                 /* consider it as DOCUMENT */
422                 doc = xmlCtxtReadMemory(ctxt, (char *) string, len,
423                                                                 PG_XML_DEFAULT_URI, NULL, opts);
424                 if (doc == NULL)
425                 {
426                         xml_ereport(ERROR, "could not parse XML data", ctxt);
427 #if 0
428                         xmlFreeParserCtxt(ctxt);
429                         xmlCleanupParser();
430                         ereport(ERROR, (errmsg("could not parse XML data")));
431 #endif
432                 }
433         }
434         else
435         {
436                 /* attempt to parse the string as if it is an XML fragment */
437                 ereport(DEBUG3, (errmsg("the string is not an XML doc, trying to parse as a CHUNK")));
438                 doc = xmlNewDoc(NULL);
439                 /* TODO resolve: xmlParseBalancedChunkMemory assumes that string is UTF8 encoded! */
440                 res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, string, NULL);
441                 if (res_code != 0)
442                 {
443                         xmlFreeParserCtxt(ctxt);
444                         xmlCleanupParser();
445                         xml_ereport_by_code(ERROR, "could not parse XML data", res_code);
446                 }
447         }
448
449 #ifdef XML_DEBUG_DTD_CONST
450         dtd = xmlParseDTD(NULL, (xmlChar *) XML_DEBUG_DTD_CONST);
451         xml_ereport(DEBUG3, "solid path to DTD was defined for debugging purposes", ctxt);
452         if (dtd == NULL)
453         {
454                 xml_ereport(ERROR, "could not parse DTD data", ctxt);
455         }
456         else
457 #else
458         /* if dtd for our xml data is detected... */
459         if ((doc->intSubset != NULL) || (doc->extSubset != NULL))
460 #endif
461         {
462                 /* assume that inline DTD exists - validation should be performed */
463 #ifdef XML_DEBUG_DTD_CONST
464                 if (xmlValidateDtd(xmlNewValidCtxt(), doc, dtd) != 1)
465 #else
466                 if (ctxt->valid == 0)
467 #endif
468                 {
469                         /* DTD exists, but validator reported 'validation failed' */
470                         validationFailed = TRUE;
471                 }
472         }
473
474         if (validationFailed)
475                 xml_ereport(WARNING, "validation against DTD failed", ctxt);
476
477         /* TODO encoding issues
478          * (thoughts:
479          *              CASE:
480          *              - XML data has explicit encoding attribute in its prolog
481          *              - if not, assume that enc. of XML data is the same as client's one
482          *
483          *              The common rule is to accept the XML data only if its encoding
484          *              is the same as encoding of the storage (server's). The other possible
485          *              option is to accept all the docs, but DO TRANSFORMATION and, if needed,
486          *              change the prolog.
487          *
488          *              I think I'd stick the first way (for the 1st version),
489          *              it's much simplier (less errors...)
490          * ) */
491         /* ... */
492
493         xmlFreeParserCtxt(ctxt);
494         xmlCleanupParser();
495
496         ereport(DEBUG3, (errmsg("XML data successfully parsed, encoding: %s",
497                 (char *) doc->encoding)));
498
499         return doc;
500 }
501
502
503 /*
504  * xmlChar<->text convertions
505  */
506 static xmlChar *
507 xml_text2xmlChar(text *in)
508 {
509         int32           len = VARSIZE(in) - VARHDRSZ;
510         xmlChar         *res;
511
512         res = palloc(len + 1);
513         memcpy(res, VARDATA(in), len);
514         res[len] = '\0';
515
516         return(res);
517 }
518
519
520 /*
521  * Wrappers for memory management functions
522  */
523 static void *
524 xml_palloc(size_t size)
525 {
526         return palloc(size);
527 }
528
529
530 static void *
531 xml_repalloc(void *ptr, size_t size)
532 {
533         return repalloc(ptr, size);
534 }
535
536
537 static void
538 xml_pfree(void *ptr)
539 {
540         pfree(ptr);
541 }
542
543
544 static char *
545 xml_pstrdup(const char *string)
546 {
547         return pstrdup(string);
548 }
549
550
551 /*
552  * Wrapper for "ereport" function.
553  * Adds detail - libxml's native error message, if any.
554  */
555 static void
556 xml_ereport(int level, char *msg, void *ctxt)
557 {
558         char *xmlErrDetail;
559         int xmlErrLen, i;
560         xmlErrorPtr libxmlErr = NULL;
561
562         if (xml_errmsg != NULL)
563         {
564                 ereport(DEBUG1, (errmsg("%s", xml_errmsg)));
565                 pfree(xml_errmsg);
566         }
567
568         if (ctxt != NULL)
569                 libxmlErr = xmlCtxtGetLastError(ctxt);
570
571         if (libxmlErr == NULL)
572         {
573                 if (level == ERROR)
574                 {
575                         xmlFreeParserCtxt(ctxt);
576                         xmlCleanupParser();
577                 }
578                 ereport(level, (errmsg(msg)));
579         }
580         else
581         {
582                 /* as usual, libxml error message contains '\n'; get rid of it */
583                 xmlErrLen = strlen(libxmlErr->message); /* - 1; */
584                 xmlErrDetail = (char *) palloc(xmlErrLen);
585                 for (i = 0; i < xmlErrLen; i++)
586                 {
587                         if (libxmlErr->message[i] == '\n')
588                                 xmlErrDetail[i] = '.';
589                         else
590                                 xmlErrDetail[i] = libxmlErr->message[i];
591                 }
592                 if (level == ERROR)
593                 {
594                         xmlFreeParserCtxt(ctxt);
595                         xmlCleanupParser();
596                 }
597                 ereport(level, (errmsg(msg), errdetail("%s", xmlErrDetail)));
598         }
599 }
600
601
602 /*
603  * Error handler for libxml error messages
604  */
605 static void
606 xml_errorHandler(void *ctxt, const char *msg,...)
607 {
608         va_list         args;
609
610         va_start(args, msg);
611         vsnprintf(xml_errbuf, XML_ERRBUF_SIZE, msg, args);
612         va_end(args);
613         /* Now copy the argument across */
614         if (xml_errmsg == NULL)
615                 xml_errmsg = pstrdup(xml_errbuf);
616         else
617         {
618                 int32           xsize = strlen(xml_errmsg);
619
620                 xml_errmsg = repalloc(xml_errmsg, (size_t) (xsize + strlen(xml_errbuf) + 1));
621                 strncpy(&xml_errmsg[xsize - 1], xml_errbuf, strlen(xml_errbuf));
622                 xml_errmsg[xsize + strlen(xml_errbuf) - 1] = '\0';
623         }
624         memset(xml_errbuf, 0, XML_ERRBUF_SIZE);
625 }
626
627
628 /*
629  * Return error message by libxml error code
630  * TODO make them closer to recommendations from Postgres manual
631  */
632 static void
633 xml_ereport_by_code(int level, char *msg, int code)
634 {
635     const char *det;
636
637         if (code < 0)
638         {
639                 ereport(level, (errmsg(msg)));
640                 return;
641         }
642
643     switch (code) {
644         case XML_ERR_INTERNAL_ERROR:
645             det = "libxml internal error";
646             break;
647         case XML_ERR_ENTITY_LOOP:
648             det = "Detected an entity reference loop";
649             break;
650         case XML_ERR_ENTITY_NOT_STARTED:
651             det = "EntityValue: \" or ' expected";
652             break;
653         case XML_ERR_ENTITY_NOT_FINISHED:
654             det = "EntityValue: \" or ' expected";
655             break;
656         case XML_ERR_ATTRIBUTE_NOT_STARTED:
657             det = "AttValue: \" or ' expected";
658             break;
659         case XML_ERR_LT_IN_ATTRIBUTE:
660             det = "Unescaped '<' not allowed in attributes values";
661             break;
662         case XML_ERR_LITERAL_NOT_STARTED:
663             det = "SystemLiteral \" or ' expected";
664             break;
665         case XML_ERR_LITERAL_NOT_FINISHED:
666             det = "Unfinished System or Public ID \" or ' expected";
667             break;
668         case XML_ERR_MISPLACED_CDATA_END:
669             det = "Sequence ']]>' not allowed in content";
670             break;
671         case XML_ERR_URI_REQUIRED:
672             det = "SYSTEM or PUBLIC, the URI is missing";
673             break;
674         case XML_ERR_PUBID_REQUIRED:
675             det = "PUBLIC, the Public Identifier is missing";
676             break;
677         case XML_ERR_HYPHEN_IN_COMMENT:
678             det = "Comment must not contain '--' (double-hyphen)";
679             break;
680         case XML_ERR_PI_NOT_STARTED:
681             det = "xmlParsePI : no target name";
682             break;
683         case XML_ERR_RESERVED_XML_NAME:
684             det = "Invalid PI name";
685             break;
686         case XML_ERR_NOTATION_NOT_STARTED:
687             det = "NOTATION: Name expected here";
688             break;
689         case XML_ERR_NOTATION_NOT_FINISHED:
690             det = "'>' required to close NOTATION declaration";
691             break;
692         case XML_ERR_VALUE_REQUIRED:
693             det = "Entity value required";
694             break;
695         case XML_ERR_URI_FRAGMENT:
696             det = "Fragment not allowed";
697             break;
698         case XML_ERR_ATTLIST_NOT_STARTED:
699             det = "'(' required to start ATTLIST enumeration";
700             break;
701         case XML_ERR_NMTOKEN_REQUIRED:
702             det = "NmToken expected in ATTLIST enumeration";
703             break;
704         case XML_ERR_ATTLIST_NOT_FINISHED:
705             det = "')' required to finish ATTLIST enumeration";
706             break;
707         case XML_ERR_MIXED_NOT_STARTED:
708             det = "MixedContentDecl : '|' or ')*' expected";
709             break;
710         case XML_ERR_PCDATA_REQUIRED:
711             det = "MixedContentDecl : '#PCDATA' expected";
712             break;
713         case XML_ERR_ELEMCONTENT_NOT_STARTED:
714             det = "ContentDecl : Name or '(' expected";
715             break;
716         case XML_ERR_ELEMCONTENT_NOT_FINISHED:
717             det = "ContentDecl : ',' '|' or ')' expected";
718             break;
719         case XML_ERR_PEREF_IN_INT_SUBSET:
720             det = "PEReference: forbidden within markup decl in internal subset";
721             break;
722         case XML_ERR_GT_REQUIRED:
723             det = "Expected '>'";
724             break;
725         case XML_ERR_CONDSEC_INVALID:
726             det = "XML conditional section '[' expected";
727             break;
728         case XML_ERR_EXT_SUBSET_NOT_FINISHED:
729             det = "Content error in the external subset";
730             break;
731         case XML_ERR_CONDSEC_INVALID_KEYWORD:
732             det = "conditional section INCLUDE or IGNORE keyword expected";
733             break;
734         case XML_ERR_CONDSEC_NOT_FINISHED:
735             det = "XML conditional section not closed";
736             break;
737         case XML_ERR_XMLDECL_NOT_STARTED:
738             det = "Text declaration '<?xml' required";
739             break;
740         case XML_ERR_XMLDECL_NOT_FINISHED:
741             det = "parsing XML declaration: '?>' expected";
742             break;
743         case XML_ERR_EXT_ENTITY_STANDALONE:
744             det = "external parsed entities cannot be standalone";
745             break;
746         case XML_ERR_ENTITYREF_SEMICOL_MISSING:
747             det = "EntityRef: expecting ';'";
748             break;
749         case XML_ERR_DOCTYPE_NOT_FINISHED:
750             det = "DOCTYPE improperly terminated";
751             break;
752         case XML_ERR_LTSLASH_REQUIRED:
753             det = "EndTag: '</' not found";
754             break;
755         case XML_ERR_EQUAL_REQUIRED:
756             det = "Expected '='";
757             break;
758         case XML_ERR_STRING_NOT_CLOSED:
759             det = "String not closed expecting \" or '";
760             break;
761         case XML_ERR_STRING_NOT_STARTED:
762             det = "String not started expecting ' or \"";
763             break;
764         case XML_ERR_ENCODING_NAME:
765             det = "Invalid XML encoding name";
766             break;
767         case XML_ERR_STANDALONE_VALUE:
768             det = "Standalone accepts only 'yes' or 'no'";
769             break;
770         case XML_ERR_DOCUMENT_EMPTY:
771             det = "Document is empty";
772             break;
773         case XML_ERR_DOCUMENT_END:
774             det = "Extra content at the end of the document";
775             break;
776         case XML_ERR_NOT_WELL_BALANCED:
777             det = "Chunk is not well balanced";
778             break;
779         case XML_ERR_EXTRA_CONTENT:
780             det = "Extra content at the end of well balanced chunk";
781             break;
782         case XML_ERR_VERSION_MISSING:
783             det = "Malformed declaration expecting version";
784             break;
785         /* more err codes... Please, keep the order! */
786         case XML_ERR_ATTRIBUTE_WITHOUT_VALUE: /* 41 */
787                 det ="Attribute without value";
788                 break;
789         case XML_ERR_ATTRIBUTE_REDEFINED:
790                 det ="Attribute defined more than once in the same element";
791                 break;
792         case XML_ERR_COMMENT_NOT_FINISHED: /* 45 */
793             det = "Comment is not finished";
794             break;
795         case XML_ERR_NAME_REQUIRED: /* 68 */
796             det = "Element name not found";
797             break;
798         case XML_ERR_TAG_NOT_FINISHED: /* 77 */
799             det = "Closing tag not found";
800             break;
801         default:
802             det = "Unregistered error (libxml error code: %d)";
803             ereport(DEBUG1, (errmsg("Check out \"libxml/xmlerror.h\" and bring errcode \"%d\" processing to \"xml.c\".", code)));
804     }
805
806         if (xml_errmsg != NULL)
807         {
808                 ereport(DEBUG1, (errmsg("%s", xml_errmsg)));
809                 pfree(xml_errmsg);
810         }
811
812         ereport(level, (errmsg(msg), errdetail(det, code)));
813 }
814
815
816 /*
817  * Convert one char in the current server encoding to a Unicode
818  * codepoint.
819  */
820 static pg_wchar
821 sqlchar_to_unicode(char *s)
822 {
823         int save_enc;
824         pg_wchar ret;
825         char *utf8string;
826
827         utf8string = (char *) pg_do_encoding_conversion((unsigned char *) s,
828                                                                                                         pg_mblen(s),
829                                                                                                         GetDatabaseEncoding(),
830                                                                                                         PG_UTF8);
831
832         save_enc = GetDatabaseEncoding();
833         SetDatabaseEncoding(PG_UTF8);
834         pg_mb2wchar_with_len(utf8string, &ret, pg_mblen(s));
835         SetDatabaseEncoding(save_enc);
836
837         return ret;
838 }
839
840
841 static bool
842 is_valid_xml_namefirst(pg_wchar c)
843 {
844         /* (Letter | '_' | ':') */
845         return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
846                         || c == '_' || c == ':');
847 }
848
849
850 static bool
851 is_valid_xml_namechar(pg_wchar c)
852 {
853         /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
854         return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
855                         || xmlIsDigitQ(c)
856                         || c == '.' || c == '-' || c == '_' || c == ':'
857                         || xmlIsCombiningQ(c)
858                         || xmlIsExtenderQ(c));
859 }
860 #endif /* USE_LIBXML */
861
862
863 /*
864  * Map SQL identifier to XML name; see SQL/XML:2003 section 9.1.
865  */
866 char *
867 map_sql_identifier_to_xml_name(char *ident, bool fully_escaped)
868 {
869 #ifdef USE_LIBXML
870         StringInfoData buf;
871         char *p;
872
873         initStringInfo(&buf);
874
875         for (p = ident; *p; p += pg_mblen(p))
876         {
877                 if (*p == ':' && (p == ident || fully_escaped))
878                         appendStringInfo(&buf, "_x003A_");
879                 else if (*p == '_' && *(p+1) == 'x')
880                         appendStringInfo(&buf, "_x005F_");
881                 else if (fully_escaped && p == ident &&
882                                  pg_strncasecmp(p, "xml", 3) == 0)
883                 {
884                         if (*p == 'x')
885                                 appendStringInfo(&buf, "_x0078_");
886                         else
887                                 appendStringInfo(&buf, "_x0058_");
888                 }
889                 else
890                 {
891                         pg_wchar u = sqlchar_to_unicode(p);
892
893                         if ((p == ident)
894                                 ? !is_valid_xml_namefirst(u)
895                                 : !is_valid_xml_namechar(u))
896                                 appendStringInfo(&buf, "_x%04X_", (unsigned int) u);
897                         else
898                                 appendBinaryStringInfo(&buf, p, pg_mblen(p));
899                 }
900         }
901
902         return buf.data;
903 #else /* not USE_LIBXML */
904         NO_XML_SUPPORT();
905         return NULL;
906 #endif /* not USE_LIBXML */
907 }