Header And Logo

PostgreSQL
| The world's most advanced open source database.

xml.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * xml.c
00004  *    XML data type support.
00005  *
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  * Portions Copyright (c) 1994, Regents of the University of California
00009  *
00010  * src/backend/utils/adt/xml.c
00011  *
00012  *-------------------------------------------------------------------------
00013  */
00014 
00015 /*
00016  * Generally, XML type support is only available when libxml use was
00017  * configured during the build.  But even if that is not done, the
00018  * type and all the functions are available, but most of them will
00019  * fail.  For one thing, this avoids having to manage variant catalog
00020  * installations.  But it also has nice effects such as that you can
00021  * dump a database containing XML type data even if the server is not
00022  * linked with libxml.  Thus, make sure xml_out() works even if nothing
00023  * else does.
00024  */
00025 
00026 /*
00027  * Notes on memory management:
00028  *
00029  * Sometimes libxml allocates global structures in the hope that it can reuse
00030  * them later on.  This makes it impractical to change the xmlMemSetup
00031  * functions on-the-fly; that is likely to lead to trying to pfree() chunks
00032  * allocated with malloc() or vice versa.  Since libxml might be used by
00033  * loadable modules, eg libperl, our only safe choices are to change the
00034  * functions at postmaster/backend launch or not at all.  Since we'd rather
00035  * not activate libxml in sessions that might never use it, the latter choice
00036  * is the preferred one.  However, for debugging purposes it can be awfully
00037  * handy to constrain libxml's allocations to be done in a specific palloc
00038  * context, where they're easy to track.  Therefore there is code here that
00039  * can be enabled in debug builds to redirect libxml's allocations into a
00040  * special context LibxmlContext.  It's not recommended to turn this on in
00041  * a production build because of the possibility of bad interactions with
00042  * external modules.
00043  */
00044 /* #define USE_LIBXMLCONTEXT */
00045 
00046 #include "postgres.h"
00047 
00048 #ifdef USE_LIBXML
00049 #include <libxml/chvalid.h>
00050 #include <libxml/parser.h>
00051 #include <libxml/parserInternals.h>
00052 #include <libxml/tree.h>
00053 #include <libxml/uri.h>
00054 #include <libxml/xmlerror.h>
00055 #include <libxml/xmlversion.h>
00056 #include <libxml/xmlwriter.h>
00057 #include <libxml/xpath.h>
00058 #include <libxml/xpathInternals.h>
00059 
00060 /*
00061  * We used to check for xmlStructuredErrorContext via a configure test; but
00062  * that doesn't work on Windows, so instead use this grottier method of
00063  * testing the library version number.
00064  */
00065 #if LIBXML_VERSION >= 20704
00066 #define HAVE_XMLSTRUCTUREDERRORCONTEXT 1
00067 #endif
00068 #endif   /* USE_LIBXML */
00069 
00070 #include "access/htup_details.h"
00071 #include "catalog/namespace.h"
00072 #include "catalog/pg_type.h"
00073 #include "commands/dbcommands.h"
00074 #include "executor/executor.h"
00075 #include "executor/spi.h"
00076 #include "fmgr.h"
00077 #include "lib/stringinfo.h"
00078 #include "libpq/pqformat.h"
00079 #include "mb/pg_wchar.h"
00080 #include "miscadmin.h"
00081 #include "nodes/execnodes.h"
00082 #include "nodes/nodeFuncs.h"
00083 #include "utils/array.h"
00084 #include "utils/builtins.h"
00085 #include "utils/date.h"
00086 #include "utils/datetime.h"
00087 #include "utils/lsyscache.h"
00088 #include "utils/memutils.h"
00089 #include "utils/rel.h"
00090 #include "utils/syscache.h"
00091 #include "utils/xml.h"
00092 
00093 
00094 /* GUC variables */
00095 int         xmlbinary;
00096 int         xmloption;
00097 
00098 #ifdef USE_LIBXML
00099 
00100 /* random number to identify PgXmlErrorContext */
00101 #define ERRCXT_MAGIC    68275028
00102 
00103 struct PgXmlErrorContext
00104 {
00105     int         magic;
00106     /* strictness argument passed to pg_xml_init */
00107     PgXmlStrictness strictness;
00108     /* current error status and accumulated message, if any */
00109     bool        err_occurred;
00110     StringInfoData err_buf;
00111     /* previous libxml error handling state (saved by pg_xml_init) */
00112     xmlStructuredErrorFunc saved_errfunc;
00113     void       *saved_errcxt;
00114     /* previous libxml entity handler (saved by pg_xml_init) */
00115     xmlExternalEntityLoader saved_entityfunc;
00116 };
00117 
00118 static xmlParserInputPtr xmlPgEntityLoader(const char *URL, const char *ID,
00119                   xmlParserCtxtPtr ctxt);
00120 static void xml_errorHandler(void *data, xmlErrorPtr error);
00121 static void xml_ereport_by_code(int level, int sqlcode,
00122                     const char *msg, int errcode);
00123 static void chopStringInfoNewlines(StringInfo str);
00124 static void appendStringInfoLineSeparator(StringInfo str);
00125 
00126 #ifdef USE_LIBXMLCONTEXT
00127 
00128 static MemoryContext LibxmlContext = NULL;
00129 
00130 static void xml_memory_init(void);
00131 static void *xml_palloc(size_t size);
00132 static void *xml_repalloc(void *ptr, size_t size);
00133 static void xml_pfree(void *ptr);
00134 static char *xml_pstrdup(const char *string);
00135 #endif   /* USE_LIBXMLCONTEXT */
00136 
00137 static xmlChar *xml_text2xmlChar(text *in);
00138 static int parse_xml_decl(const xmlChar *str, size_t *lenp,
00139                xmlChar **version, xmlChar **encoding, int *standalone);
00140 static bool print_xml_decl(StringInfo buf, const xmlChar *version,
00141                pg_enc encoding, int standalone);
00142 static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
00143           bool preserve_whitespace, int encoding);
00144 static text *xml_xmlnodetoxmltype(xmlNodePtr cur);
00145 static int xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
00146                        ArrayBuildState **astate);
00147 #endif   /* USE_LIBXML */
00148 
00149 static StringInfo query_to_xml_internal(const char *query, char *tablename,
00150                       const char *xmlschema, bool nulls, bool tableforest,
00151                       const char *targetns, bool top_level);
00152 static const char *map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid,
00153                          bool nulls, bool tableforest, const char *targetns);
00154 static const char *map_sql_schema_to_xmlschema_types(Oid nspid,
00155                                   List *relid_list, bool nulls,
00156                                   bool tableforest, const char *targetns);
00157 static const char *map_sql_catalog_to_xmlschema_types(List *nspid_list,
00158                                    bool nulls, bool tableforest,
00159                                    const char *targetns);
00160 static const char *map_sql_type_to_xml_name(Oid typeoid, int typmod);
00161 static const char *map_sql_typecoll_to_xmlschema_types(List *tupdesc_list);
00162 static const char *map_sql_type_to_xmlschema_type(Oid typeoid, int typmod);
00163 static void SPI_sql_row_to_xmlelement(int rownum, StringInfo result,
00164                           char *tablename, bool nulls, bool tableforest,
00165                           const char *targetns, bool top_level);
00166 
00167 #define NO_XML_SUPPORT() \
00168     ereport(ERROR, \
00169             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
00170              errmsg("unsupported XML feature"), \
00171              errdetail("This functionality requires the server to be built with libxml support."), \
00172              errhint("You need to rebuild PostgreSQL using --with-libxml.")))
00173 
00174 
00175 /* from SQL/XML:2008 section 4.9 */
00176 #define NAMESPACE_XSD "http://www.w3.org/2001/XMLSchema"
00177 #define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance"
00178 #define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml"
00179 
00180 
00181 #ifdef USE_LIBXML
00182 
00183 static int
00184 xmlChar_to_encoding(const xmlChar *encoding_name)
00185 {
00186     int         encoding = pg_char_to_encoding((const char *) encoding_name);
00187 
00188     if (encoding < 0)
00189         ereport(ERROR,
00190                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00191                  errmsg("invalid encoding name \"%s\"",
00192                         (const char *) encoding_name)));
00193     return encoding;
00194 }
00195 #endif
00196 
00197 
00198 /*
00199  * xml_in uses a plain C string to VARDATA conversion, so for the time being
00200  * we use the conversion function for the text datatype.
00201  *
00202  * This is only acceptable so long as xmltype and text use the same
00203  * representation.
00204  */
00205 Datum
00206 xml_in(PG_FUNCTION_ARGS)
00207 {
00208 #ifdef USE_LIBXML
00209     char       *s = PG_GETARG_CSTRING(0);
00210     xmltype    *vardata;
00211     xmlDocPtr   doc;
00212 
00213     vardata = (xmltype *) cstring_to_text(s);
00214 
00215     /*
00216      * Parse the data to check if it is well-formed XML data.  Assume that
00217      * ERROR occurred if parsing failed.
00218      */
00219     doc = xml_parse(vardata, xmloption, true, GetDatabaseEncoding());
00220     xmlFreeDoc(doc);
00221 
00222     PG_RETURN_XML_P(vardata);
00223 #else
00224     NO_XML_SUPPORT();
00225     return 0;
00226 #endif
00227 }
00228 
00229 
00230 #define PG_XML_DEFAULT_VERSION "1.0"
00231 
00232 
00233 /*
00234  * xml_out_internal uses a plain VARDATA to C string conversion, so for the
00235  * time being we use the conversion function for the text datatype.
00236  *
00237  * This is only acceptable so long as xmltype and text use the same
00238  * representation.
00239  */
00240 static char *
00241 xml_out_internal(xmltype *x, pg_enc target_encoding)
00242 {
00243     char       *str = text_to_cstring((text *) x);
00244 
00245 #ifdef USE_LIBXML
00246     size_t      len = strlen(str);
00247     xmlChar    *version;
00248     int         standalone;
00249     int         res_code;
00250 
00251     if ((res_code = parse_xml_decl((xmlChar *) str,
00252                                    &len, &version, NULL, &standalone)) == 0)
00253     {
00254         StringInfoData buf;
00255 
00256         initStringInfo(&buf);
00257 
00258         if (!print_xml_decl(&buf, version, target_encoding, standalone))
00259         {
00260             /*
00261              * If we are not going to produce an XML declaration, eat a single
00262              * newline in the original string to prevent empty first lines in
00263              * the output.
00264              */
00265             if (*(str + len) == '\n')
00266                 len += 1;
00267         }
00268         appendStringInfoString(&buf, str + len);
00269 
00270         pfree(str);
00271 
00272         return buf.data;
00273     }
00274 
00275     xml_ereport_by_code(WARNING, ERRCODE_INTERNAL_ERROR,
00276                         "could not parse XML declaration in stored value",
00277                         res_code);
00278 #endif
00279     return str;
00280 }
00281 
00282 
00283 Datum
00284 xml_out(PG_FUNCTION_ARGS)
00285 {
00286     xmltype    *x = PG_GETARG_XML_P(0);
00287 
00288     /*
00289      * xml_out removes the encoding property in all cases.  This is because we
00290      * cannot control from here whether the datum will be converted to a
00291      * different client encoding, so we'd do more harm than good by including
00292      * it.
00293      */
00294     PG_RETURN_CSTRING(xml_out_internal(x, 0));
00295 }
00296 
00297 
00298 Datum
00299 xml_recv(PG_FUNCTION_ARGS)
00300 {
00301 #ifdef USE_LIBXML
00302     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
00303     xmltype    *result;
00304     char       *str;
00305     char       *newstr;
00306     int         nbytes;
00307     xmlDocPtr   doc;
00308     xmlChar    *encodingStr = NULL;
00309     int         encoding;
00310 
00311     /*
00312      * Read the data in raw format. We don't know yet what the encoding is, as
00313      * that information is embedded in the xml declaration; so we have to
00314      * parse that before converting to server encoding.
00315      */
00316     nbytes = buf->len - buf->cursor;
00317     str = (char *) pq_getmsgbytes(buf, nbytes);
00318 
00319     /*
00320      * We need a null-terminated string to pass to parse_xml_decl().  Rather
00321      * than make a separate copy, make the temporary result one byte bigger
00322      * than it needs to be.
00323      */
00324     result = palloc(nbytes + 1 + VARHDRSZ);
00325     SET_VARSIZE(result, nbytes + VARHDRSZ);
00326     memcpy(VARDATA(result), str, nbytes);
00327     str = VARDATA(result);
00328     str[nbytes] = '\0';
00329 
00330     parse_xml_decl((const xmlChar *) str, NULL, NULL, &encodingStr, NULL);
00331 
00332     /*
00333      * If encoding wasn't explicitly specified in the XML header, treat it as
00334      * UTF-8, as that's the default in XML. This is different from xml_in(),
00335      * where the input has to go through the normal client to server encoding
00336      * conversion.
00337      */
00338     encoding = encodingStr ? xmlChar_to_encoding(encodingStr) : PG_UTF8;
00339 
00340     /*
00341      * Parse the data to check if it is well-formed XML data.  Assume that
00342      * xml_parse will throw ERROR if not.
00343      */
00344     doc = xml_parse(result, xmloption, true, encoding);
00345     xmlFreeDoc(doc);
00346 
00347     /* Now that we know what we're dealing with, convert to server encoding */
00348     newstr = (char *) pg_do_encoding_conversion((unsigned char *) str,
00349                                                 nbytes,
00350                                                 encoding,
00351                                                 GetDatabaseEncoding());
00352 
00353     if (newstr != str)
00354     {
00355         pfree(result);
00356         result = (xmltype *) cstring_to_text(newstr);
00357         pfree(newstr);
00358     }
00359 
00360     PG_RETURN_XML_P(result);
00361 #else
00362     NO_XML_SUPPORT();
00363     return 0;
00364 #endif
00365 }
00366 
00367 
00368 Datum
00369 xml_send(PG_FUNCTION_ARGS)
00370 {
00371     xmltype    *x = PG_GETARG_XML_P(0);
00372     char       *outval;
00373     StringInfoData buf;
00374 
00375     /*
00376      * xml_out_internal doesn't convert the encoding, it just prints the right
00377      * declaration. pq_sendtext will do the conversion.
00378      */
00379     outval = xml_out_internal(x, pg_get_client_encoding());
00380 
00381     pq_begintypsend(&buf);
00382     pq_sendtext(&buf, outval, strlen(outval));
00383     pfree(outval);
00384     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
00385 }
00386 
00387 
00388 #ifdef USE_LIBXML
00389 static void
00390 appendStringInfoText(StringInfo str, const text *t)
00391 {
00392     appendBinaryStringInfo(str, VARDATA(t), VARSIZE(t) - VARHDRSZ);
00393 }
00394 #endif
00395 
00396 
00397 static xmltype *
00398 stringinfo_to_xmltype(StringInfo buf)
00399 {
00400     return (xmltype *) cstring_to_text_with_len(buf->data, buf->len);
00401 }
00402 
00403 
00404 static xmltype *
00405 cstring_to_xmltype(const char *string)
00406 {
00407     return (xmltype *) cstring_to_text(string);
00408 }
00409 
00410 
00411 #ifdef USE_LIBXML
00412 static xmltype *
00413 xmlBuffer_to_xmltype(xmlBufferPtr buf)
00414 {
00415     return (xmltype *) cstring_to_text_with_len((const char *) xmlBufferContent(buf),
00416                                                 xmlBufferLength(buf));
00417 }
00418 #endif
00419 
00420 
00421 Datum
00422 xmlcomment(PG_FUNCTION_ARGS)
00423 {
00424 #ifdef USE_LIBXML
00425     text       *arg = PG_GETARG_TEXT_P(0);
00426     char       *argdata = VARDATA(arg);
00427     int         len = VARSIZE(arg) - VARHDRSZ;
00428     StringInfoData buf;
00429     int         i;
00430 
00431     /* check for "--" in string or "-" at the end */
00432     for (i = 1; i < len; i++)
00433     {
00434         if (argdata[i] == '-' && argdata[i - 1] == '-')
00435             ereport(ERROR,
00436                     (errcode(ERRCODE_INVALID_XML_COMMENT),
00437                      errmsg("invalid XML comment")));
00438     }
00439     if (len > 0 && argdata[len - 1] == '-')
00440         ereport(ERROR,
00441                 (errcode(ERRCODE_INVALID_XML_COMMENT),
00442                  errmsg("invalid XML comment")));
00443 
00444     initStringInfo(&buf);
00445     appendStringInfo(&buf, "<!--");
00446     appendStringInfoText(&buf, arg);
00447     appendStringInfo(&buf, "-->");
00448 
00449     PG_RETURN_XML_P(stringinfo_to_xmltype(&buf));
00450 #else
00451     NO_XML_SUPPORT();
00452     return 0;
00453 #endif
00454 }
00455 
00456 
00457 
00458 /*
00459  * TODO: xmlconcat needs to merge the notations and unparsed entities
00460  * of the argument values.  Not very important in practice, though.
00461  */
00462 xmltype *
00463 xmlconcat(List *args)
00464 {
00465 #ifdef USE_LIBXML
00466     int         global_standalone = 1;
00467     xmlChar    *global_version = NULL;
00468     bool        global_version_no_value = false;
00469     StringInfoData buf;
00470     ListCell   *v;
00471 
00472     initStringInfo(&buf);
00473     foreach(v, args)
00474     {
00475         xmltype    *x = DatumGetXmlP(PointerGetDatum(lfirst(v)));
00476         size_t      len;
00477         xmlChar    *version;
00478         int         standalone;
00479         char       *str;
00480 
00481         len = VARSIZE(x) - VARHDRSZ;
00482         str = text_to_cstring((text *) x);
00483 
00484         parse_xml_decl((xmlChar *) str, &len, &version, NULL, &standalone);
00485 
00486         if (standalone == 0 && global_standalone == 1)
00487             global_standalone = 0;
00488         if (standalone < 0)
00489             global_standalone = -1;
00490 
00491         if (!version)
00492             global_version_no_value = true;
00493         else if (!global_version)
00494             global_version = version;
00495         else if (xmlStrcmp(version, global_version) != 0)
00496             global_version_no_value = true;
00497 
00498         appendStringInfoString(&buf, str + len);
00499         pfree(str);
00500     }
00501 
00502     if (!global_version_no_value || global_standalone >= 0)
00503     {
00504         StringInfoData buf2;
00505 
00506         initStringInfo(&buf2);
00507 
00508         print_xml_decl(&buf2,
00509                        (!global_version_no_value) ? global_version : NULL,
00510                        0,
00511                        global_standalone);
00512 
00513         appendStringInfoString(&buf2, buf.data);
00514         buf = buf2;
00515     }
00516 
00517     return stringinfo_to_xmltype(&buf);
00518 #else
00519     NO_XML_SUPPORT();
00520     return NULL;
00521 #endif
00522 }
00523 
00524 
00525 /*
00526  * XMLAGG support
00527  */
00528 Datum
00529 xmlconcat2(PG_FUNCTION_ARGS)
00530 {
00531     if (PG_ARGISNULL(0))
00532     {
00533         if (PG_ARGISNULL(1))
00534             PG_RETURN_NULL();
00535         else
00536             PG_RETURN_XML_P(PG_GETARG_XML_P(1));
00537     }
00538     else if (PG_ARGISNULL(1))
00539         PG_RETURN_XML_P(PG_GETARG_XML_P(0));
00540     else
00541         PG_RETURN_XML_P(xmlconcat(list_make2(PG_GETARG_XML_P(0),
00542                                              PG_GETARG_XML_P(1))));
00543 }
00544 
00545 
00546 Datum
00547 texttoxml(PG_FUNCTION_ARGS)
00548 {
00549     text       *data = PG_GETARG_TEXT_P(0);
00550 
00551     PG_RETURN_XML_P(xmlparse(data, xmloption, true));
00552 }
00553 
00554 
00555 Datum
00556 xmltotext(PG_FUNCTION_ARGS)
00557 {
00558     xmltype    *data = PG_GETARG_XML_P(0);
00559 
00560     /* It's actually binary compatible. */
00561     PG_RETURN_TEXT_P((text *) data);
00562 }
00563 
00564 
00565 text *
00566 xmltotext_with_xmloption(xmltype *data, XmlOptionType xmloption_arg)
00567 {
00568     if (xmloption_arg == XMLOPTION_DOCUMENT && !xml_is_document(data))
00569         ereport(ERROR,
00570                 (errcode(ERRCODE_NOT_AN_XML_DOCUMENT),
00571                  errmsg("not an XML document")));
00572 
00573     /* It's actually binary compatible, save for the above check. */
00574     return (text *) data;
00575 }
00576 
00577 
00578 xmltype *
00579 xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
00580 {
00581 #ifdef USE_LIBXML
00582     XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
00583     xmltype    *result;
00584     List       *named_arg_strings;
00585     List       *arg_strings;
00586     int         i;
00587     ListCell   *arg;
00588     ListCell   *narg;
00589     PgXmlErrorContext *xmlerrcxt;
00590     volatile xmlBufferPtr buf = NULL;
00591     volatile xmlTextWriterPtr writer = NULL;
00592 
00593     /*
00594      * We first evaluate all the arguments, then start up libxml and create
00595      * the result.  This avoids issues if one of the arguments involves a call
00596      * to some other function or subsystem that wants to use libxml on its own
00597      * terms.
00598      */
00599     named_arg_strings = NIL;
00600     i = 0;
00601     foreach(arg, xmlExpr->named_args)
00602     {
00603         ExprState  *e = (ExprState *) lfirst(arg);
00604         Datum       value;
00605         bool        isnull;
00606         char       *str;
00607 
00608         value = ExecEvalExpr(e, econtext, &isnull, NULL);
00609         if (isnull)
00610             str = NULL;
00611         else
00612             str = map_sql_value_to_xml_value(value, exprType((Node *) e->expr), false);
00613         named_arg_strings = lappend(named_arg_strings, str);
00614         i++;
00615     }
00616 
00617     arg_strings = NIL;
00618     foreach(arg, xmlExpr->args)
00619     {
00620         ExprState  *e = (ExprState *) lfirst(arg);
00621         Datum       value;
00622         bool        isnull;
00623         char       *str;
00624 
00625         value = ExecEvalExpr(e, econtext, &isnull, NULL);
00626         /* here we can just forget NULL elements immediately */
00627         if (!isnull)
00628         {
00629             str = map_sql_value_to_xml_value(value,
00630                                            exprType((Node *) e->expr), true);
00631             arg_strings = lappend(arg_strings, str);
00632         }
00633     }
00634 
00635     /* now safe to run libxml */
00636     xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
00637 
00638     PG_TRY();
00639     {
00640         buf = xmlBufferCreate();
00641         if (buf == NULL || xmlerrcxt->err_occurred)
00642             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
00643                         "could not allocate xmlBuffer");
00644         writer = xmlNewTextWriterMemory(buf, 0);
00645         if (writer == NULL || xmlerrcxt->err_occurred)
00646             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
00647                         "could not allocate xmlTextWriter");
00648 
00649         xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
00650 
00651         forboth(arg, named_arg_strings, narg, xexpr->arg_names)
00652         {
00653             char       *str = (char *) lfirst(arg);
00654             char       *argname = strVal(lfirst(narg));
00655 
00656             if (str)
00657                 xmlTextWriterWriteAttribute(writer,
00658                                             (xmlChar *) argname,
00659                                             (xmlChar *) str);
00660         }
00661 
00662         foreach(arg, arg_strings)
00663         {
00664             char       *str = (char *) lfirst(arg);
00665 
00666             xmlTextWriterWriteRaw(writer, (xmlChar *) str);
00667         }
00668 
00669         xmlTextWriterEndElement(writer);
00670 
00671         /* we MUST do this now to flush data out to the buffer ... */
00672         xmlFreeTextWriter(writer);
00673         writer = NULL;
00674 
00675         result = xmlBuffer_to_xmltype(buf);
00676     }
00677     PG_CATCH();
00678     {
00679         if (writer)
00680             xmlFreeTextWriter(writer);
00681         if (buf)
00682             xmlBufferFree(buf);
00683 
00684         pg_xml_done(xmlerrcxt, true);
00685 
00686         PG_RE_THROW();
00687     }
00688     PG_END_TRY();
00689 
00690     xmlBufferFree(buf);
00691 
00692     pg_xml_done(xmlerrcxt, false);
00693 
00694     return result;
00695 #else
00696     NO_XML_SUPPORT();
00697     return NULL;
00698 #endif
00699 }
00700 
00701 
00702 xmltype *
00703 xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace)
00704 {
00705 #ifdef USE_LIBXML
00706     xmlDocPtr   doc;
00707 
00708     doc = xml_parse(data, xmloption_arg, preserve_whitespace,
00709                     GetDatabaseEncoding());
00710     xmlFreeDoc(doc);
00711 
00712     return (xmltype *) data;
00713 #else
00714     NO_XML_SUPPORT();
00715     return NULL;
00716 #endif
00717 }
00718 
00719 
00720 xmltype *
00721 xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null)
00722 {
00723 #ifdef USE_LIBXML
00724     xmltype    *result;
00725     StringInfoData buf;
00726 
00727     if (pg_strcasecmp(target, "xml") == 0)
00728         ereport(ERROR,
00729                 (errcode(ERRCODE_SYNTAX_ERROR), /* really */
00730                  errmsg("invalid XML processing instruction"),
00731                  errdetail("XML processing instruction target name cannot be \"%s\".", target)));
00732 
00733     /*
00734      * Following the SQL standard, the null check comes after the syntax check
00735      * above.
00736      */
00737     *result_is_null = arg_is_null;
00738     if (*result_is_null)
00739         return NULL;
00740 
00741     initStringInfo(&buf);
00742 
00743     appendStringInfo(&buf, "<?%s", target);
00744 
00745     if (arg != NULL)
00746     {
00747         char       *string;
00748 
00749         string = text_to_cstring(arg);
00750         if (strstr(string, "?>") != NULL)
00751             ereport(ERROR,
00752                     (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
00753                      errmsg("invalid XML processing instruction"),
00754             errdetail("XML processing instruction cannot contain \"?>\".")));
00755 
00756         appendStringInfoChar(&buf, ' ');
00757         appendStringInfoString(&buf, string + strspn(string, " "));
00758         pfree(string);
00759     }
00760     appendStringInfoString(&buf, "?>");
00761 
00762     result = stringinfo_to_xmltype(&buf);
00763     pfree(buf.data);
00764     return result;
00765 #else
00766     NO_XML_SUPPORT();
00767     return NULL;
00768 #endif
00769 }
00770 
00771 
00772 xmltype *
00773 xmlroot(xmltype *data, text *version, int standalone)
00774 {
00775 #ifdef USE_LIBXML
00776     char       *str;
00777     size_t      len;
00778     xmlChar    *orig_version;
00779     int         orig_standalone;
00780     StringInfoData buf;
00781 
00782     len = VARSIZE(data) - VARHDRSZ;
00783     str = text_to_cstring((text *) data);
00784 
00785     parse_xml_decl((xmlChar *) str, &len, &orig_version, NULL, &orig_standalone);
00786 
00787     if (version)
00788         orig_version = xml_text2xmlChar(version);
00789     else
00790         orig_version = NULL;
00791 
00792     switch (standalone)
00793     {
00794         case XML_STANDALONE_YES:
00795             orig_standalone = 1;
00796             break;
00797         case XML_STANDALONE_NO:
00798             orig_standalone = 0;
00799             break;
00800         case XML_STANDALONE_NO_VALUE:
00801             orig_standalone = -1;
00802             break;
00803         case XML_STANDALONE_OMITTED:
00804             /* leave original value */
00805             break;
00806     }
00807 
00808     initStringInfo(&buf);
00809     print_xml_decl(&buf, orig_version, 0, orig_standalone);
00810     appendStringInfoString(&buf, str + len);
00811 
00812     return stringinfo_to_xmltype(&buf);
00813 #else
00814     NO_XML_SUPPORT();
00815     return NULL;
00816 #endif
00817 }
00818 
00819 
00820 /*
00821  * Validate document (given as string) against DTD (given as external link)
00822  *
00823  * This has been removed because it is a security hole: unprivileged users
00824  * should not be able to use Postgres to fetch arbitrary external files,
00825  * which unfortunately is exactly what libxml is willing to do with the DTD
00826  * parameter.
00827  */
00828 Datum
00829 xmlvalidate(PG_FUNCTION_ARGS)
00830 {
00831     ereport(ERROR,
00832             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00833              errmsg("xmlvalidate is not implemented")));
00834     return 0;
00835 }
00836 
00837 
00838 bool
00839 xml_is_document(xmltype *arg)
00840 {
00841 #ifdef USE_LIBXML
00842     bool        result;
00843     volatile xmlDocPtr doc = NULL;
00844     MemoryContext ccxt = CurrentMemoryContext;
00845 
00846     /* We want to catch ereport(INVALID_XML_DOCUMENT) and return false */
00847     PG_TRY();
00848     {
00849         doc = xml_parse((text *) arg, XMLOPTION_DOCUMENT, true,
00850                         GetDatabaseEncoding());
00851         result = true;
00852     }
00853     PG_CATCH();
00854     {
00855         ErrorData  *errdata;
00856         MemoryContext ecxt;
00857 
00858         ecxt = MemoryContextSwitchTo(ccxt);
00859         errdata = CopyErrorData();
00860         if (errdata->sqlerrcode == ERRCODE_INVALID_XML_DOCUMENT)
00861         {
00862             FlushErrorState();
00863             result = false;
00864         }
00865         else
00866         {
00867             MemoryContextSwitchTo(ecxt);
00868             PG_RE_THROW();
00869         }
00870     }
00871     PG_END_TRY();
00872 
00873     if (doc)
00874         xmlFreeDoc(doc);
00875 
00876     return result;
00877 #else                           /* not USE_LIBXML */
00878     NO_XML_SUPPORT();
00879     return false;
00880 #endif   /* not USE_LIBXML */
00881 }
00882 
00883 
00884 #ifdef USE_LIBXML
00885 
00886 /*
00887  * pg_xml_init_library --- set up for use of libxml
00888  *
00889  * This should be called by each function that is about to use libxml
00890  * facilities but doesn't require error handling.  It initializes libxml
00891  * and verifies compatibility with the loaded libxml version.  These are
00892  * once-per-session activities.
00893  *
00894  * TODO: xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and
00895  * check)
00896  */
00897 void
00898 pg_xml_init_library(void)
00899 {
00900     static bool first_time = true;
00901 
00902     if (first_time)
00903     {
00904         /* Stuff we need do only once per session */
00905 
00906         /*
00907          * Currently, we have no pure UTF-8 support for internals -- check if
00908          * we can work.
00909          */
00910         if (sizeof(char) != sizeof(xmlChar))
00911             ereport(ERROR,
00912                     (errmsg("could not initialize XML library"),
00913                      errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.",
00914                                (int) sizeof(char), (int) sizeof(xmlChar))));
00915 
00916 #ifdef USE_LIBXMLCONTEXT
00917         /* Set up libxml's memory allocation our way */
00918         xml_memory_init();
00919 #endif
00920 
00921         /* Check library compatibility */
00922         LIBXML_TEST_VERSION;
00923 
00924         first_time = false;
00925     }
00926 }
00927 
00928 /*
00929  * pg_xml_init --- set up for use of libxml and register an error handler
00930  *
00931  * This should be called by each function that is about to use libxml
00932  * facilities and requires error handling.  It initializes libxml with
00933  * pg_xml_init_library() and establishes our libxml error handler.
00934  *
00935  * strictness determines which errors are reported and which are ignored.
00936  *
00937  * Calls to this function MUST be followed by a PG_TRY block that guarantees
00938  * that pg_xml_done() is called during either normal or error exit.
00939  *
00940  * This is exported for use by contrib/xml2, as well as other code that might
00941  * wish to share use of this module's libxml error handler.
00942  */
00943 PgXmlErrorContext *
00944 pg_xml_init(PgXmlStrictness strictness)
00945 {
00946     PgXmlErrorContext *errcxt;
00947     void       *new_errcxt;
00948 
00949     /* Do one-time setup if needed */
00950     pg_xml_init_library();
00951 
00952     /* Create error handling context structure */
00953     errcxt = (PgXmlErrorContext *) palloc(sizeof(PgXmlErrorContext));
00954     errcxt->magic = ERRCXT_MAGIC;
00955     errcxt->strictness = strictness;
00956     errcxt->err_occurred = false;
00957     initStringInfo(&errcxt->err_buf);
00958 
00959     /*
00960      * Save original error handler and install ours. libxml originally didn't
00961      * distinguish between the contexts for generic and for structured error
00962      * handlers.  If we're using an old libxml version, we must thus save the
00963      * generic error context, even though we're using a structured error
00964      * handler.
00965      */
00966     errcxt->saved_errfunc = xmlStructuredError;
00967 
00968 #ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
00969     errcxt->saved_errcxt = xmlStructuredErrorContext;
00970 #else
00971     errcxt->saved_errcxt = xmlGenericErrorContext;
00972 #endif
00973 
00974     xmlSetStructuredErrorFunc((void *) errcxt, xml_errorHandler);
00975 
00976     /*
00977      * Verify that xmlSetStructuredErrorFunc set the context variable we
00978      * expected it to.  If not, the error context pointer we just saved is not
00979      * the correct thing to restore, and since that leaves us without a way to
00980      * restore the context in pg_xml_done, we must fail.
00981      *
00982      * The only known situation in which this test fails is if we compile with
00983      * headers from a libxml2 that doesn't track the structured error context
00984      * separately (< 2.7.4), but at runtime use a version that does, or vice
00985      * versa.  The libxml2 authors did not treat that change as constituting
00986      * an ABI break, so the LIBXML_TEST_VERSION test in pg_xml_init_library
00987      * fails to protect us from this.
00988      */
00989 
00990 #ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
00991     new_errcxt = xmlStructuredErrorContext;
00992 #else
00993     new_errcxt = xmlGenericErrorContext;
00994 #endif
00995 
00996     if (new_errcxt != (void *) errcxt)
00997         ereport(ERROR,
00998                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00999                  errmsg("could not set up XML error handler"),
01000                  errhint("This probably indicates that the version of libxml2"
01001                          " being used is not compatible with the libxml2"
01002                          " header files that PostgreSQL was built with.")));
01003 
01004     /*
01005      * Also, install an entity loader to prevent unwanted fetches of external
01006      * files and URLs.
01007      */
01008     errcxt->saved_entityfunc = xmlGetExternalEntityLoader();
01009     xmlSetExternalEntityLoader(xmlPgEntityLoader);
01010 
01011     return errcxt;
01012 }
01013 
01014 
01015 /*
01016  * pg_xml_done --- restore previous libxml error handling
01017  *
01018  * Resets libxml's global error-handling state to what it was before
01019  * pg_xml_init() was called.
01020  *
01021  * This routine verifies that all pending errors have been dealt with
01022  * (in assert-enabled builds, anyway).
01023  */
01024 void
01025 pg_xml_done(PgXmlErrorContext *errcxt, bool isError)
01026 {
01027     void       *cur_errcxt;
01028 
01029     /* An assert seems like enough protection here */
01030     Assert(errcxt->magic == ERRCXT_MAGIC);
01031 
01032     /*
01033      * In a normal exit, there should be no un-handled libxml errors.  But we
01034      * shouldn't try to enforce this during error recovery, since the longjmp
01035      * could have been thrown before xml_ereport had a chance to run.
01036      */
01037     Assert(!errcxt->err_occurred || isError);
01038 
01039     /*
01040      * Check that libxml's global state is correct, warn if not.  This is a
01041      * real test and not an Assert because it has a higher probability of
01042      * happening.
01043      */
01044 #ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
01045     cur_errcxt = xmlStructuredErrorContext;
01046 #else
01047     cur_errcxt = xmlGenericErrorContext;
01048 #endif
01049 
01050     if (cur_errcxt != (void *) errcxt)
01051         elog(WARNING, "libxml error handling state is out of sync with xml.c");
01052 
01053     /* Restore the saved handlers */
01054     xmlSetStructuredErrorFunc(errcxt->saved_errcxt, errcxt->saved_errfunc);
01055     xmlSetExternalEntityLoader(errcxt->saved_entityfunc);
01056 
01057     /*
01058      * Mark the struct as invalid, just in case somebody somehow manages to
01059      * call xml_errorHandler or xml_ereport with it.
01060      */
01061     errcxt->magic = 0;
01062 
01063     /* Release memory */
01064     pfree(errcxt->err_buf.data);
01065     pfree(errcxt);
01066 }
01067 
01068 
01069 /*
01070  * pg_xml_error_occurred() --- test the error flag
01071  */
01072 bool
01073 pg_xml_error_occurred(PgXmlErrorContext *errcxt)
01074 {
01075     return errcxt->err_occurred;
01076 }
01077 
01078 
01079 /*
01080  * SQL/XML allows storing "XML documents" or "XML content".  "XML
01081  * documents" are specified by the XML specification and are parsed
01082  * easily by libxml.  "XML content" is specified by SQL/XML as the
01083  * production "XMLDecl? content".  But libxml can only parse the
01084  * "content" part, so we have to parse the XML declaration ourselves
01085  * to complete this.
01086  */
01087 
01088 #define CHECK_XML_SPACE(p) \
01089     do { \
01090         if (!xmlIsBlank_ch(*(p))) \
01091             return XML_ERR_SPACE_REQUIRED; \
01092     } while (0)
01093 
01094 #define SKIP_XML_SPACE(p) \
01095     while (xmlIsBlank_ch(*(p))) (p)++
01096 
01097 /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
01098 /* Beware of multiple evaluations of argument! */
01099 #define PG_XMLISNAMECHAR(c) \
01100     (xmlIsBaseChar_ch(c) || xmlIsIdeographicQ(c) \
01101             || xmlIsDigit_ch(c) \
01102             || c == '.' || c == '-' || c == '_' || c == ':' \
01103             || xmlIsCombiningQ(c) \
01104             || xmlIsExtender_ch(c))
01105 
01106 /* pnstrdup, but deal with xmlChar not char; len is measured in xmlChars */
01107 static xmlChar *
01108 xml_pnstrdup(const xmlChar *str, size_t len)
01109 {
01110     xmlChar    *result;
01111 
01112     result = (xmlChar *) palloc((len + 1) * sizeof(xmlChar));
01113     memcpy(result, str, len * sizeof(xmlChar));
01114     result[len] = 0;
01115     return result;
01116 }
01117 
01118 /*
01119  * str is the null-terminated input string.  Remaining arguments are
01120  * output arguments; each can be NULL if value is not wanted.
01121  * version and encoding are returned as locally-palloc'd strings.
01122  * Result is 0 if OK, an error code if not.
01123  */
01124 static int
01125 parse_xml_decl(const xmlChar *str, size_t *lenp,
01126                xmlChar **version, xmlChar **encoding, int *standalone)
01127 {
01128     const xmlChar *p;
01129     const xmlChar *save_p;
01130     size_t      len;
01131     int         utf8char;
01132     int         utf8len;
01133 
01134     /*
01135      * Only initialize libxml.  We don't need error handling here, but we do
01136      * need to make sure libxml is initialized before calling any of its
01137      * functions.  Note that this is safe (and a no-op) if caller has already
01138      * done pg_xml_init().
01139      */
01140     pg_xml_init_library();
01141 
01142     /* Initialize output arguments to "not present" */
01143     if (version)
01144         *version = NULL;
01145     if (encoding)
01146         *encoding = NULL;
01147     if (standalone)
01148         *standalone = -1;
01149 
01150     p = str;
01151 
01152     if (xmlStrncmp(p, (xmlChar *) "<?xml", 5) != 0)
01153         goto finished;
01154 
01155     /* if next char is name char, it's a PI like <?xml-stylesheet ...?> */
01156     utf8len = strlen((const char *) (p + 5));
01157     utf8char = xmlGetUTF8Char(p + 5, &utf8len);
01158     if (PG_XMLISNAMECHAR(utf8char))
01159         goto finished;
01160 
01161     p += 5;
01162 
01163     /* version */
01164     CHECK_XML_SPACE(p);
01165     SKIP_XML_SPACE(p);
01166     if (xmlStrncmp(p, (xmlChar *) "version", 7) != 0)
01167         return XML_ERR_VERSION_MISSING;
01168     p += 7;
01169     SKIP_XML_SPACE(p);
01170     if (*p != '=')
01171         return XML_ERR_VERSION_MISSING;
01172     p += 1;
01173     SKIP_XML_SPACE(p);
01174 
01175     if (*p == '\'' || *p == '"')
01176     {
01177         const xmlChar *q;
01178 
01179         q = xmlStrchr(p + 1, *p);
01180         if (!q)
01181             return XML_ERR_VERSION_MISSING;
01182 
01183         if (version)
01184             *version = xml_pnstrdup(p + 1, q - p - 1);
01185         p = q + 1;
01186     }
01187     else
01188         return XML_ERR_VERSION_MISSING;
01189 
01190     /* encoding */
01191     save_p = p;
01192     SKIP_XML_SPACE(p);
01193     if (xmlStrncmp(p, (xmlChar *) "encoding", 8) == 0)
01194     {
01195         CHECK_XML_SPACE(save_p);
01196         p += 8;
01197         SKIP_XML_SPACE(p);
01198         if (*p != '=')
01199             return XML_ERR_MISSING_ENCODING;
01200         p += 1;
01201         SKIP_XML_SPACE(p);
01202 
01203         if (*p == '\'' || *p == '"')
01204         {
01205             const xmlChar *q;
01206 
01207             q = xmlStrchr(p + 1, *p);
01208             if (!q)
01209                 return XML_ERR_MISSING_ENCODING;
01210 
01211             if (encoding)
01212                 *encoding = xml_pnstrdup(p + 1, q - p - 1);
01213             p = q + 1;
01214         }
01215         else
01216             return XML_ERR_MISSING_ENCODING;
01217     }
01218     else
01219     {
01220         p = save_p;
01221     }
01222 
01223     /* standalone */
01224     save_p = p;
01225     SKIP_XML_SPACE(p);
01226     if (xmlStrncmp(p, (xmlChar *) "standalone", 10) == 0)
01227     {
01228         CHECK_XML_SPACE(save_p);
01229         p += 10;
01230         SKIP_XML_SPACE(p);
01231         if (*p != '=')
01232             return XML_ERR_STANDALONE_VALUE;
01233         p += 1;
01234         SKIP_XML_SPACE(p);
01235         if (xmlStrncmp(p, (xmlChar *) "'yes'", 5) == 0 ||
01236             xmlStrncmp(p, (xmlChar *) "\"yes\"", 5) == 0)
01237         {
01238             if (standalone)
01239                 *standalone = 1;
01240             p += 5;
01241         }
01242         else if (xmlStrncmp(p, (xmlChar *) "'no'", 4) == 0 ||
01243                  xmlStrncmp(p, (xmlChar *) "\"no\"", 4) == 0)
01244         {
01245             if (standalone)
01246                 *standalone = 0;
01247             p += 4;
01248         }
01249         else
01250             return XML_ERR_STANDALONE_VALUE;
01251     }
01252     else
01253     {
01254         p = save_p;
01255     }
01256 
01257     SKIP_XML_SPACE(p);
01258     if (xmlStrncmp(p, (xmlChar *) "?>", 2) != 0)
01259         return XML_ERR_XMLDECL_NOT_FINISHED;
01260     p += 2;
01261 
01262 finished:
01263     len = p - str;
01264 
01265     for (p = str; p < str + len; p++)
01266         if (*p > 127)
01267             return XML_ERR_INVALID_CHAR;
01268 
01269     if (lenp)
01270         *lenp = len;
01271 
01272     return XML_ERR_OK;
01273 }
01274 
01275 
01276 /*
01277  * Write an XML declaration.  On output, we adjust the XML declaration
01278  * as follows.  (These rules are the moral equivalent of the clause
01279  * "Serialization of an XML value" in the SQL standard.)
01280  *
01281  * We try to avoid generating an XML declaration if possible.  This is
01282  * so that you don't get trivial things like xml '<foo/>' resulting in
01283  * '<?xml version="1.0"?><foo/>', which would surely be annoying.  We
01284  * must provide a declaration if the standalone property is specified
01285  * or if we include an encoding declaration.  If we have a
01286  * declaration, we must specify a version (XML requires this).
01287  * Otherwise we only make a declaration if the version is not "1.0",
01288  * which is the default version specified in SQL:2003.
01289  */
01290 static bool
01291 print_xml_decl(StringInfo buf, const xmlChar *version,
01292                pg_enc encoding, int standalone)
01293 {
01294     if ((version && strcmp((const char *) version, PG_XML_DEFAULT_VERSION) != 0)
01295         || (encoding && encoding != PG_UTF8)
01296         || standalone != -1)
01297     {
01298         appendStringInfoString(buf, "<?xml");
01299 
01300         if (version)
01301             appendStringInfo(buf, " version=\"%s\"", version);
01302         else
01303             appendStringInfo(buf, " version=\"%s\"", PG_XML_DEFAULT_VERSION);
01304 
01305         if (encoding && encoding != PG_UTF8)
01306         {
01307             /*
01308              * XXX might be useful to convert this to IANA names (ISO-8859-1
01309              * instead of LATIN1 etc.); needs field experience
01310              */
01311             appendStringInfo(buf, " encoding=\"%s\"",
01312                              pg_encoding_to_char(encoding));
01313         }
01314 
01315         if (standalone == 1)
01316             appendStringInfoString(buf, " standalone=\"yes\"");
01317         else if (standalone == 0)
01318             appendStringInfoString(buf, " standalone=\"no\"");
01319         appendStringInfoString(buf, "?>");
01320 
01321         return true;
01322     }
01323     else
01324         return false;
01325 }
01326 
01327 
01328 /*
01329  * Convert a C string to XML internal representation
01330  *
01331  * Note: it is caller's responsibility to xmlFreeDoc() the result,
01332  * else a permanent memory leak will ensue!
01333  *
01334  * TODO maybe libxml2's xmlreader is better? (do not construct DOM,
01335  * yet do not use SAX - see xmlreader.c)
01336  */
01337 static xmlDocPtr
01338 xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
01339           int encoding)
01340 {
01341     int32       len;
01342     xmlChar    *string;
01343     xmlChar    *utf8string;
01344     PgXmlErrorContext *xmlerrcxt;
01345     volatile xmlParserCtxtPtr ctxt = NULL;
01346     volatile xmlDocPtr doc = NULL;
01347 
01348     len = VARSIZE(data) - VARHDRSZ;     /* will be useful later */
01349     string = xml_text2xmlChar(data);
01350 
01351     utf8string = pg_do_encoding_conversion(string,
01352                                            len,
01353                                            encoding,
01354                                            PG_UTF8);
01355 
01356     /* Start up libxml and its parser */
01357     xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_WELLFORMED);
01358 
01359     /* Use a TRY block to ensure we clean up correctly */
01360     PG_TRY();
01361     {
01362         xmlInitParser();
01363 
01364         ctxt = xmlNewParserCtxt();
01365         if (ctxt == NULL || xmlerrcxt->err_occurred)
01366             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
01367                         "could not allocate parser context");
01368 
01369         if (xmloption_arg == XMLOPTION_DOCUMENT)
01370         {
01371             /*
01372              * Note, that here we try to apply DTD defaults
01373              * (XML_PARSE_DTDATTR) according to SQL/XML:2008 GR 10.16.7.d:
01374              * 'Default values defined by internal DTD are applied'. As for
01375              * external DTDs, we try to support them too, (see SQL/XML:2008 GR
01376              * 10.16.7.e)
01377              */
01378             doc = xmlCtxtReadDoc(ctxt, utf8string,
01379                                  NULL,
01380                                  "UTF-8",
01381                                  XML_PARSE_NOENT | XML_PARSE_DTDATTR
01382                            | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS));
01383             if (doc == NULL || xmlerrcxt->err_occurred)
01384                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
01385                             "invalid XML document");
01386         }
01387         else
01388         {
01389             int         res_code;
01390             size_t      count;
01391             xmlChar    *version;
01392             int         standalone;
01393 
01394             res_code = parse_xml_decl(utf8string,
01395                                       &count, &version, NULL, &standalone);
01396             if (res_code != 0)
01397                 xml_ereport_by_code(ERROR, ERRCODE_INVALID_XML_CONTENT,
01398                               "invalid XML content: invalid XML declaration",
01399                                     res_code);
01400 
01401             doc = xmlNewDoc(version);
01402             Assert(doc->encoding == NULL);
01403             doc->encoding = xmlStrdup((const xmlChar *) "UTF-8");
01404             doc->standalone = standalone;
01405 
01406             res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0,
01407                                                    utf8string + count, NULL);
01408             if (res_code != 0 || xmlerrcxt->err_occurred)
01409                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_CONTENT,
01410                             "invalid XML content");
01411         }
01412     }
01413     PG_CATCH();
01414     {
01415         if (doc != NULL)
01416             xmlFreeDoc(doc);
01417         if (ctxt != NULL)
01418             xmlFreeParserCtxt(ctxt);
01419 
01420         pg_xml_done(xmlerrcxt, true);
01421 
01422         PG_RE_THROW();
01423     }
01424     PG_END_TRY();
01425 
01426     xmlFreeParserCtxt(ctxt);
01427 
01428     pg_xml_done(xmlerrcxt, false);
01429 
01430     return doc;
01431 }
01432 
01433 
01434 /*
01435  * xmlChar<->text conversions
01436  */
01437 static xmlChar *
01438 xml_text2xmlChar(text *in)
01439 {
01440     return (xmlChar *) text_to_cstring(in);
01441 }
01442 
01443 
01444 #ifdef USE_LIBXMLCONTEXT
01445 
01446 /*
01447  * Manage the special context used for all libxml allocations (but only
01448  * in special debug builds; see notes at top of file)
01449  */
01450 static void
01451 xml_memory_init(void)
01452 {
01453     /* Create memory context if not there already */
01454     if (LibxmlContext == NULL)
01455         LibxmlContext = AllocSetContextCreate(TopMemoryContext,
01456                                               "LibxmlContext",
01457                                               ALLOCSET_DEFAULT_MINSIZE,
01458                                               ALLOCSET_DEFAULT_INITSIZE,
01459                                               ALLOCSET_DEFAULT_MAXSIZE);
01460 
01461     /* Re-establish the callbacks even if already set */
01462     xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
01463 }
01464 
01465 /*
01466  * Wrappers for memory management functions
01467  */
01468 static void *
01469 xml_palloc(size_t size)
01470 {
01471     return MemoryContextAlloc(LibxmlContext, size);
01472 }
01473 
01474 
01475 static void *
01476 xml_repalloc(void *ptr, size_t size)
01477 {
01478     return repalloc(ptr, size);
01479 }
01480 
01481 
01482 static void
01483 xml_pfree(void *ptr)
01484 {
01485     /* At least some parts of libxml assume xmlFree(NULL) is allowed */
01486     if (ptr)
01487         pfree(ptr);
01488 }
01489 
01490 
01491 static char *
01492 xml_pstrdup(const char *string)
01493 {
01494     return MemoryContextStrdup(LibxmlContext, string);
01495 }
01496 #endif   /* USE_LIBXMLCONTEXT */
01497 
01498 
01499 /*
01500  * xmlPgEntityLoader --- entity loader callback function
01501  *
01502  * Silently prevent any external entity URL from being loaded.  We don't want
01503  * to throw an error, so instead make the entity appear to expand to an empty
01504  * string.
01505  *
01506  * We would prefer to allow loading entities that exist in the system's
01507  * global XML catalog; but the available libxml2 APIs make that a complex
01508  * and fragile task.  For now, just shut down all external access.
01509  */
01510 static xmlParserInputPtr
01511 xmlPgEntityLoader(const char *URL, const char *ID,
01512                   xmlParserCtxtPtr ctxt)
01513 {
01514     return xmlNewStringInputStream(ctxt, (const xmlChar *) "");
01515 }
01516 
01517 
01518 /*
01519  * xml_ereport --- report an XML-related error
01520  *
01521  * The "msg" is the SQL-level message; some can be adopted from the SQL/XML
01522  * standard.  This function adds libxml's native error message, if any, as
01523  * detail.
01524  *
01525  * This is exported for modules that want to share the core libxml error
01526  * handler.  Note that pg_xml_init() *must* have been called previously.
01527  */
01528 void
01529 xml_ereport(PgXmlErrorContext *errcxt, int level, int sqlcode, const char *msg)
01530 {
01531     char       *detail;
01532 
01533     /* Defend against someone passing us a bogus context struct */
01534     if (errcxt->magic != ERRCXT_MAGIC)
01535         elog(ERROR, "xml_ereport called with invalid PgXmlErrorContext");
01536 
01537     /* Flag that the current libxml error has been reported */
01538     errcxt->err_occurred = false;
01539 
01540     /* Include detail only if we have some text from libxml */
01541     if (errcxt->err_buf.len > 0)
01542         detail = errcxt->err_buf.data;
01543     else
01544         detail = NULL;
01545 
01546     ereport(level,
01547             (errcode(sqlcode),
01548              errmsg_internal("%s", msg),
01549              detail ? errdetail_internal("%s", detail) : 0));
01550 }
01551 
01552 
01553 /*
01554  * Error handler for libxml errors and warnings
01555  */
01556 static void
01557 xml_errorHandler(void *data, xmlErrorPtr error)
01558 {
01559     PgXmlErrorContext *xmlerrcxt = (PgXmlErrorContext *) data;
01560     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) error->ctxt;
01561     xmlParserInputPtr input = (ctxt != NULL) ? ctxt->input : NULL;
01562     xmlNodePtr  node = error->node;
01563     const xmlChar *name = (node != NULL &&
01564                          node->type == XML_ELEMENT_NODE) ? node->name : NULL;
01565     int         domain = error->domain;
01566     int         level = error->level;
01567     StringInfo  errorBuf;
01568 
01569     /*
01570      * Defend against someone passing us a bogus context struct.
01571      *
01572      * We force a backend exit if this check fails because longjmp'ing out of
01573      * libxml would likely render it unsafe to use further.
01574      */
01575     if (xmlerrcxt->magic != ERRCXT_MAGIC)
01576         elog(FATAL, "xml_errorHandler called with invalid PgXmlErrorContext");
01577 
01578     /*----------
01579      * Older libxml versions report some errors differently.
01580      * First, some errors were previously reported as coming from the parser
01581      * domain but are now reported as coming from the namespace domain.
01582      * Second, some warnings were upgraded to errors.
01583      * We attempt to compensate for that here.
01584      *----------
01585      */
01586     switch (error->code)
01587     {
01588         case XML_WAR_NS_URI:
01589             level = XML_ERR_ERROR;
01590             domain = XML_FROM_NAMESPACE;
01591             break;
01592 
01593         case XML_ERR_NS_DECL_ERROR:
01594         case XML_WAR_NS_URI_RELATIVE:
01595         case XML_WAR_NS_COLUMN:
01596         case XML_NS_ERR_XML_NAMESPACE:
01597         case XML_NS_ERR_UNDEFINED_NAMESPACE:
01598         case XML_NS_ERR_QNAME:
01599         case XML_NS_ERR_ATTRIBUTE_REDEFINED:
01600         case XML_NS_ERR_EMPTY:
01601             domain = XML_FROM_NAMESPACE;
01602             break;
01603     }
01604 
01605     /* Decide whether to act on the error or not */
01606     switch (domain)
01607     {
01608         case XML_FROM_PARSER:
01609         case XML_FROM_NONE:
01610         case XML_FROM_MEMORY:
01611         case XML_FROM_IO:
01612             /*
01613              * Suppress warnings about undeclared entities.  We need to do
01614              * this to avoid problems due to not loading DTD definitions.
01615              */
01616             if (error->code == XML_WAR_UNDECLARED_ENTITY)
01617                 return;
01618 
01619             /* Otherwise, accept error regardless of the parsing purpose */
01620             break;
01621 
01622         default:
01623             /* Ignore error if only doing well-formedness check */
01624             if (xmlerrcxt->strictness == PG_XML_STRICTNESS_WELLFORMED)
01625                 return;
01626             break;
01627     }
01628 
01629     /* Prepare error message in errorBuf */
01630     errorBuf = makeStringInfo();
01631 
01632     if (error->line > 0)
01633         appendStringInfo(errorBuf, "line %d: ", error->line);
01634     if (name != NULL)
01635         appendStringInfo(errorBuf, "element %s: ", name);
01636     appendStringInfoString(errorBuf, error->message);
01637 
01638     /*
01639      * Append context information to errorBuf.
01640      *
01641      * xmlParserPrintFileContext() uses libxml's "generic" error handler to
01642      * write the context.  Since we don't want to duplicate libxml
01643      * functionality here, we set up a generic error handler temporarily.
01644      *
01645      * We use appendStringInfo() directly as libxml's generic error handler.
01646      * This should work because it has essentially the same signature as
01647      * libxml expects, namely (void *ptr, const char *msg, ...).
01648      */
01649     if (input != NULL)
01650     {
01651         xmlGenericErrorFunc errFuncSaved = xmlGenericError;
01652         void       *errCtxSaved = xmlGenericErrorContext;
01653 
01654         xmlSetGenericErrorFunc((void *) errorBuf,
01655                                (xmlGenericErrorFunc) appendStringInfo);
01656 
01657         /* Add context information to errorBuf */
01658         appendStringInfoLineSeparator(errorBuf);
01659 
01660         xmlParserPrintFileContext(input);
01661 
01662         /* Restore generic error func */
01663         xmlSetGenericErrorFunc(errCtxSaved, errFuncSaved);
01664     }
01665 
01666     /* Get rid of any trailing newlines in errorBuf */
01667     chopStringInfoNewlines(errorBuf);
01668 
01669     /*
01670      * Legacy error handling mode.  err_occurred is never set, we just add the
01671      * message to err_buf.  This mode exists because the xml2 contrib module
01672      * uses our error-handling infrastructure, but we don't want to change its
01673      * behaviour since it's deprecated anyway.  This is also why we don't
01674      * distinguish between notices, warnings and errors here --- the old-style
01675      * generic error handler wouldn't have done that either.
01676      */
01677     if (xmlerrcxt->strictness == PG_XML_STRICTNESS_LEGACY)
01678     {
01679         appendStringInfoLineSeparator(&xmlerrcxt->err_buf);
01680         appendStringInfoString(&xmlerrcxt->err_buf, errorBuf->data);
01681 
01682         pfree(errorBuf->data);
01683         pfree(errorBuf);
01684         return;
01685     }
01686 
01687     /*
01688      * We don't want to ereport() here because that'd probably leave libxml in
01689      * an inconsistent state.  Instead, we remember the error and ereport()
01690      * from xml_ereport().
01691      *
01692      * Warnings and notices can be reported immediately since they won't cause
01693      * a longjmp() out of libxml.
01694      */
01695     if (level >= XML_ERR_ERROR)
01696     {
01697         appendStringInfoLineSeparator(&xmlerrcxt->err_buf);
01698         appendStringInfoString(&xmlerrcxt->err_buf, errorBuf->data);
01699 
01700         xmlerrcxt->err_occurred = true;
01701     }
01702     else if (level >= XML_ERR_WARNING)
01703     {
01704         ereport(WARNING,
01705                 (errmsg_internal("%s", errorBuf->data)));
01706     }
01707     else
01708     {
01709         ereport(NOTICE,
01710                 (errmsg_internal("%s", errorBuf->data)));
01711     }
01712 
01713     pfree(errorBuf->data);
01714     pfree(errorBuf);
01715 }
01716 
01717 
01718 /*
01719  * Wrapper for "ereport" function for XML-related errors.  The "msg"
01720  * is the SQL-level message; some can be adopted from the SQL/XML
01721  * standard.  This function uses "code" to create a textual detail
01722  * message.  At the moment, we only need to cover those codes that we
01723  * may raise in this file.
01724  */
01725 static void
01726 xml_ereport_by_code(int level, int sqlcode,
01727                     const char *msg, int code)
01728 {
01729     const char *det;
01730 
01731     switch (code)
01732     {
01733         case XML_ERR_INVALID_CHAR:
01734             det = gettext_noop("Invalid character value.");
01735             break;
01736         case XML_ERR_SPACE_REQUIRED:
01737             det = gettext_noop("Space required.");
01738             break;
01739         case XML_ERR_STANDALONE_VALUE:
01740             det = gettext_noop("standalone accepts only 'yes' or 'no'.");
01741             break;
01742         case XML_ERR_VERSION_MISSING:
01743             det = gettext_noop("Malformed declaration: missing version.");
01744             break;
01745         case XML_ERR_MISSING_ENCODING:
01746             det = gettext_noop("Missing encoding in text declaration.");
01747             break;
01748         case XML_ERR_XMLDECL_NOT_FINISHED:
01749             det = gettext_noop("Parsing XML declaration: '?>' expected.");
01750             break;
01751         default:
01752             det = gettext_noop("Unrecognized libxml error code: %d.");
01753             break;
01754     }
01755 
01756     ereport(level,
01757             (errcode(sqlcode),
01758              errmsg_internal("%s", msg),
01759              errdetail(det, code)));
01760 }
01761 
01762 
01763 /*
01764  * Remove all trailing newlines from a StringInfo string
01765  */
01766 static void
01767 chopStringInfoNewlines(StringInfo str)
01768 {
01769     while (str->len > 0 && str->data[str->len - 1] == '\n')
01770         str->data[--str->len] = '\0';
01771 }
01772 
01773 
01774 /*
01775  * Append a newline after removing any existing trailing newlines
01776  */
01777 static void
01778 appendStringInfoLineSeparator(StringInfo str)
01779 {
01780     chopStringInfoNewlines(str);
01781     if (str->len > 0)
01782         appendStringInfoChar(str, '\n');
01783 }
01784 
01785 
01786 /*
01787  * Convert one char in the current server encoding to a Unicode codepoint.
01788  */
01789 static pg_wchar
01790 sqlchar_to_unicode(char *s)
01791 {
01792     char       *utf8string;
01793     pg_wchar    ret[2];         /* need space for trailing zero */
01794 
01795     utf8string = (char *) pg_do_encoding_conversion((unsigned char *) s,
01796                                                     pg_mblen(s),
01797                                                     GetDatabaseEncoding(),
01798                                                     PG_UTF8);
01799 
01800     pg_encoding_mb2wchar_with_len(PG_UTF8, utf8string, ret,
01801                                   pg_encoding_mblen(PG_UTF8, utf8string));
01802 
01803     if (utf8string != s)
01804         pfree(utf8string);
01805 
01806     return ret[0];
01807 }
01808 
01809 
01810 static bool
01811 is_valid_xml_namefirst(pg_wchar c)
01812 {
01813     /* (Letter | '_' | ':') */
01814     return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
01815             || c == '_' || c == ':');
01816 }
01817 
01818 
01819 static bool
01820 is_valid_xml_namechar(pg_wchar c)
01821 {
01822     /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
01823     return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
01824             || xmlIsDigitQ(c)
01825             || c == '.' || c == '-' || c == '_' || c == ':'
01826             || xmlIsCombiningQ(c)
01827             || xmlIsExtenderQ(c));
01828 }
01829 #endif   /* USE_LIBXML */
01830 
01831 
01832 /*
01833  * Map SQL identifier to XML name; see SQL/XML:2008 section 9.1.
01834  */
01835 char *
01836 map_sql_identifier_to_xml_name(char *ident, bool fully_escaped,
01837                                bool escape_period)
01838 {
01839 #ifdef USE_LIBXML
01840     StringInfoData buf;
01841     char       *p;
01842 
01843     /*
01844      * SQL/XML doesn't make use of this case anywhere, so it's probably a
01845      * mistake.
01846      */
01847     Assert(fully_escaped || !escape_period);
01848 
01849     initStringInfo(&buf);
01850 
01851     for (p = ident; *p; p += pg_mblen(p))
01852     {
01853         if (*p == ':' && (p == ident || fully_escaped))
01854             appendStringInfo(&buf, "_x003A_");
01855         else if (*p == '_' && *(p + 1) == 'x')
01856             appendStringInfo(&buf, "_x005F_");
01857         else if (fully_escaped && p == ident &&
01858                  pg_strncasecmp(p, "xml", 3) == 0)
01859         {
01860             if (*p == 'x')
01861                 appendStringInfo(&buf, "_x0078_");
01862             else
01863                 appendStringInfo(&buf, "_x0058_");
01864         }
01865         else if (escape_period && *p == '.')
01866             appendStringInfo(&buf, "_x002E_");
01867         else
01868         {
01869             pg_wchar    u = sqlchar_to_unicode(p);
01870 
01871             if ((p == ident)
01872                 ? !is_valid_xml_namefirst(u)
01873                 : !is_valid_xml_namechar(u))
01874                 appendStringInfo(&buf, "_x%04X_", (unsigned int) u);
01875             else
01876                 appendBinaryStringInfo(&buf, p, pg_mblen(p));
01877         }
01878     }
01879 
01880     return buf.data;
01881 #else                           /* not USE_LIBXML */
01882     NO_XML_SUPPORT();
01883     return NULL;
01884 #endif   /* not USE_LIBXML */
01885 }
01886 
01887 
01888 /*
01889  * Map a Unicode codepoint into the current server encoding.
01890  */
01891 static char *
01892 unicode_to_sqlchar(pg_wchar c)
01893 {
01894     unsigned char utf8string[5];    /* need room for trailing zero */
01895     char       *result;
01896 
01897     memset(utf8string, 0, sizeof(utf8string));
01898     unicode_to_utf8(c, utf8string);
01899 
01900     result = (char *) pg_do_encoding_conversion(utf8string,
01901                                                 pg_encoding_mblen(PG_UTF8,
01902                                                         (char *) utf8string),
01903                                                 PG_UTF8,
01904                                                 GetDatabaseEncoding());
01905     /* if pg_do_encoding_conversion didn't strdup, we must */
01906     if (result == (char *) utf8string)
01907         result = pstrdup(result);
01908     return result;
01909 }
01910 
01911 
01912 /*
01913  * Map XML name to SQL identifier; see SQL/XML:2008 section 9.3.
01914  */
01915 char *
01916 map_xml_name_to_sql_identifier(char *name)
01917 {
01918     StringInfoData buf;
01919     char       *p;
01920 
01921     initStringInfo(&buf);
01922 
01923     for (p = name; *p; p += pg_mblen(p))
01924     {
01925         if (*p == '_' && *(p + 1) == 'x'
01926             && isxdigit((unsigned char) *(p + 2))
01927             && isxdigit((unsigned char) *(p + 3))
01928             && isxdigit((unsigned char) *(p + 4))
01929             && isxdigit((unsigned char) *(p + 5))
01930             && *(p + 6) == '_')
01931         {
01932             unsigned int u;
01933 
01934             sscanf(p + 2, "%X", &u);
01935             appendStringInfoString(&buf, unicode_to_sqlchar(u));
01936             p += 6;
01937         }
01938         else
01939             appendBinaryStringInfo(&buf, p, pg_mblen(p));
01940     }
01941 
01942     return buf.data;
01943 }
01944 
01945 /*
01946  * Map SQL value to XML value; see SQL/XML:2008 section 9.8.
01947  *
01948  * When xml_escape_strings is true, then certain characters in string
01949  * values are replaced by entity references (&lt; etc.), as specified
01950  * in SQL/XML:2008 section 9.8 GR 9) a) iii).   This is normally what is
01951  * wanted.  The false case is mainly useful when the resulting value
01952  * is used with xmlTextWriterWriteAttribute() to write out an
01953  * attribute, because that function does the escaping itself.
01954  */
01955 char *
01956 map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings)
01957 {
01958     if (type_is_array_domain(type))
01959     {
01960         ArrayType  *array;
01961         Oid         elmtype;
01962         int16       elmlen;
01963         bool        elmbyval;
01964         char        elmalign;
01965         int         num_elems;
01966         Datum      *elem_values;
01967         bool       *elem_nulls;
01968         StringInfoData buf;
01969         int         i;
01970 
01971         array = DatumGetArrayTypeP(value);
01972         elmtype = ARR_ELEMTYPE(array);
01973         get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
01974 
01975         deconstruct_array(array, elmtype,
01976                           elmlen, elmbyval, elmalign,
01977                           &elem_values, &elem_nulls,
01978                           &num_elems);
01979 
01980         initStringInfo(&buf);
01981 
01982         for (i = 0; i < num_elems; i++)
01983         {
01984             if (elem_nulls[i])
01985                 continue;
01986             appendStringInfoString(&buf, "<element>");
01987             appendStringInfoString(&buf,
01988                                    map_sql_value_to_xml_value(elem_values[i],
01989                                                               elmtype, true));
01990             appendStringInfoString(&buf, "</element>");
01991         }
01992 
01993         pfree(elem_values);
01994         pfree(elem_nulls);
01995 
01996         return buf.data;
01997     }
01998     else
01999     {
02000         Oid         typeOut;
02001         bool        isvarlena;
02002         char       *str;
02003 
02004         /*
02005          * Flatten domains; the special-case treatments below should apply
02006          * to, eg, domains over boolean not just boolean.
02007          */
02008         type = getBaseType(type);
02009 
02010         /*
02011          * Special XSD formatting for some data types
02012          */
02013         switch (type)
02014         {
02015             case BOOLOID:
02016                 if (DatumGetBool(value))
02017                     return "true";
02018                 else
02019                     return "false";
02020 
02021             case DATEOID:
02022                 {
02023                     DateADT     date;
02024                     struct pg_tm tm;
02025                     char        buf[MAXDATELEN + 1];
02026 
02027                     date = DatumGetDateADT(value);
02028                     /* XSD doesn't support infinite values */
02029                     if (DATE_NOT_FINITE(date))
02030                         ereport(ERROR,
02031                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
02032                                  errmsg("date out of range"),
02033                                  errdetail("XML does not support infinite date values.")));
02034                     j2date(date + POSTGRES_EPOCH_JDATE,
02035                            &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
02036                     EncodeDateOnly(&tm, USE_XSD_DATES, buf);
02037 
02038                     return pstrdup(buf);
02039                 }
02040 
02041             case TIMESTAMPOID:
02042                 {
02043                     Timestamp   timestamp;
02044                     struct pg_tm tm;
02045                     fsec_t      fsec;
02046                     char        buf[MAXDATELEN + 1];
02047 
02048                     timestamp = DatumGetTimestamp(value);
02049 
02050                     /* XSD doesn't support infinite values */
02051                     if (TIMESTAMP_NOT_FINITE(timestamp))
02052                         ereport(ERROR,
02053                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
02054                                  errmsg("timestamp out of range"),
02055                                  errdetail("XML does not support infinite timestamp values.")));
02056                     else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
02057                         EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
02058                     else
02059                         ereport(ERROR,
02060                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
02061                                  errmsg("timestamp out of range")));
02062 
02063                     return pstrdup(buf);
02064                 }
02065 
02066             case TIMESTAMPTZOID:
02067                 {
02068                     TimestampTz timestamp;
02069                     struct pg_tm tm;
02070                     int         tz;
02071                     fsec_t      fsec;
02072                     const char *tzn = NULL;
02073                     char        buf[MAXDATELEN + 1];
02074 
02075                     timestamp = DatumGetTimestamp(value);
02076 
02077                     /* XSD doesn't support infinite values */
02078                     if (TIMESTAMP_NOT_FINITE(timestamp))
02079                         ereport(ERROR,
02080                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
02081                                  errmsg("timestamp out of range"),
02082                                  errdetail("XML does not support infinite timestamp values.")));
02083                     else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
02084                         EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
02085                     else
02086                         ereport(ERROR,
02087                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
02088                                  errmsg("timestamp out of range")));
02089 
02090                     return pstrdup(buf);
02091                 }
02092 
02093 #ifdef USE_LIBXML
02094             case BYTEAOID:
02095                 {
02096                     bytea      *bstr = DatumGetByteaPP(value);
02097                     PgXmlErrorContext *xmlerrcxt;
02098                     volatile xmlBufferPtr buf = NULL;
02099                     volatile xmlTextWriterPtr writer = NULL;
02100                     char       *result;
02101 
02102                     xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
02103 
02104                     PG_TRY();
02105                     {
02106                         buf = xmlBufferCreate();
02107                         if (buf == NULL || xmlerrcxt->err_occurred)
02108                             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
02109                                         "could not allocate xmlBuffer");
02110                         writer = xmlNewTextWriterMemory(buf, 0);
02111                         if (writer == NULL || xmlerrcxt->err_occurred)
02112                             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
02113                                         "could not allocate xmlTextWriter");
02114 
02115                         if (xmlbinary == XMLBINARY_BASE64)
02116                             xmlTextWriterWriteBase64(writer, VARDATA_ANY(bstr),
02117                                                  0, VARSIZE_ANY_EXHDR(bstr));
02118                         else
02119                             xmlTextWriterWriteBinHex(writer, VARDATA_ANY(bstr),
02120                                                  0, VARSIZE_ANY_EXHDR(bstr));
02121 
02122                         /* we MUST do this now to flush data out to the buffer */
02123                         xmlFreeTextWriter(writer);
02124                         writer = NULL;
02125 
02126                         result = pstrdup((const char *) xmlBufferContent(buf));
02127                     }
02128                     PG_CATCH();
02129                     {
02130                         if (writer)
02131                             xmlFreeTextWriter(writer);
02132                         if (buf)
02133                             xmlBufferFree(buf);
02134 
02135                         pg_xml_done(xmlerrcxt, true);
02136 
02137                         PG_RE_THROW();
02138                     }
02139                     PG_END_TRY();
02140 
02141                     xmlBufferFree(buf);
02142 
02143                     pg_xml_done(xmlerrcxt, false);
02144 
02145                     return result;
02146                 }
02147 #endif   /* USE_LIBXML */
02148 
02149         }
02150 
02151         /*
02152          * otherwise, just use the type's native text representation
02153          */
02154         getTypeOutputInfo(type, &typeOut, &isvarlena);
02155         str = OidOutputFunctionCall(typeOut, value);
02156 
02157         /* ... exactly as-is for XML, and when escaping is not wanted */
02158         if (type == XMLOID || !xml_escape_strings)
02159             return str;
02160 
02161         /* otherwise, translate special characters as needed */
02162         return escape_xml(str);
02163     }
02164 }
02165 
02166 
02167 /*
02168  * Escape characters in text that have special meanings in XML.
02169  *
02170  * Returns a palloc'd string.
02171  *
02172  * NB: this is intentionally not dependent on libxml.
02173  */
02174 char *
02175 escape_xml(const char *str)
02176 {
02177     StringInfoData buf;
02178     const char *p;
02179 
02180     initStringInfo(&buf);
02181     for (p = str; *p; p++)
02182     {
02183         switch (*p)
02184         {
02185             case '&':
02186                 appendStringInfoString(&buf, "&amp;");
02187                 break;
02188             case '<':
02189                 appendStringInfoString(&buf, "&lt;");
02190                 break;
02191             case '>':
02192                 appendStringInfoString(&buf, "&gt;");
02193                 break;
02194             case '\r':
02195                 appendStringInfoString(&buf, "&#x0d;");
02196                 break;
02197             default:
02198                 appendStringInfoCharMacro(&buf, *p);
02199                 break;
02200         }
02201     }
02202     return buf.data;
02203 }
02204 
02205 
02206 static char *
02207 _SPI_strdup(const char *s)
02208 {
02209     size_t      len = strlen(s) + 1;
02210     char       *ret = SPI_palloc(len);
02211 
02212     memcpy(ret, s, len);
02213     return ret;
02214 }
02215 
02216 
02217 /*
02218  * SQL to XML mapping functions
02219  *
02220  * What follows below was at one point intentionally organized so that
02221  * you can read along in the SQL/XML standard. The functions are
02222  * mostly split up the way the clauses lay out in the standards
02223  * document, and the identifiers are also aligned with the standard
02224  * text.  Unfortunately, SQL/XML:2006 reordered the clauses
02225  * differently than SQL/XML:2003, so the order below doesn't make much
02226  * sense anymore.
02227  *
02228  * There are many things going on there:
02229  *
02230  * There are two kinds of mappings: Mapping SQL data (table contents)
02231  * to XML documents, and mapping SQL structure (the "schema") to XML
02232  * Schema.  And there are functions that do both at the same time.
02233  *
02234  * Then you can map a database, a schema, or a table, each in both
02235  * ways.  This breaks down recursively: Mapping a database invokes
02236  * mapping schemas, which invokes mapping tables, which invokes
02237  * mapping rows, which invokes mapping columns, although you can't
02238  * call the last two from the outside.  Because of this, there are a
02239  * number of xyz_internal() functions which are to be called both from
02240  * the function manager wrapper and from some upper layer in a
02241  * recursive call.
02242  *
02243  * See the documentation about what the common function arguments
02244  * nulls, tableforest, and targetns mean.
02245  *
02246  * Some style guidelines for XML output: Use double quotes for quoting
02247  * XML attributes.  Indent XML elements by two spaces, but remember
02248  * that a lot of code is called recursively at different levels, so
02249  * it's better not to indent rather than create output that indents
02250  * and outdents weirdly.  Add newlines to make the output look nice.
02251  */
02252 
02253 
02254 /*
02255  * Visibility of objects for XML mappings; see SQL/XML:2008 section
02256  * 4.10.8.
02257  */
02258 
02259 /*
02260  * Given a query, which must return type oid as first column, produce
02261  * a list of Oids with the query results.
02262  */
02263 static List *
02264 query_to_oid_list(const char *query)
02265 {
02266     int         i;
02267     List       *list = NIL;
02268 
02269     SPI_execute(query, true, 0);
02270 
02271     for (i = 0; i < SPI_processed; i++)
02272     {
02273         Datum       oid;
02274         bool        isnull;
02275 
02276         oid = SPI_getbinval(SPI_tuptable->vals[i],
02277                             SPI_tuptable->tupdesc,
02278                             1,
02279                             &isnull);
02280         if (!isnull)
02281             list = lappend_oid(list, DatumGetObjectId(oid));
02282     }
02283 
02284     return list;
02285 }
02286 
02287 
02288 static List *
02289 schema_get_xml_visible_tables(Oid nspid)
02290 {
02291     StringInfoData query;
02292 
02293     initStringInfo(&query);
02294     appendStringInfo(&query, "SELECT oid FROM pg_catalog.pg_class WHERE relnamespace = %u AND relkind IN ('r', 'm', 'v') AND pg_catalog.has_table_privilege (oid, 'SELECT') ORDER BY relname;", nspid);
02295 
02296     return query_to_oid_list(query.data);
02297 }
02298 
02299 
02300 /*
02301  * Including the system schemas is probably not useful for a database
02302  * mapping.
02303  */
02304 #define XML_VISIBLE_SCHEMAS_EXCLUDE "(nspname ~ '^pg_' OR nspname = 'information_schema')"
02305 
02306 #define XML_VISIBLE_SCHEMAS "SELECT oid FROM pg_catalog.pg_namespace WHERE pg_catalog.has_schema_privilege (oid, 'USAGE') AND NOT " XML_VISIBLE_SCHEMAS_EXCLUDE
02307 
02308 
02309 static List *
02310 database_get_xml_visible_schemas(void)
02311 {
02312     return query_to_oid_list(XML_VISIBLE_SCHEMAS " ORDER BY nspname;");
02313 }
02314 
02315 
02316 static List *
02317 database_get_xml_visible_tables(void)
02318 {
02319     /* At the moment there is no order required here. */
02320     return query_to_oid_list("SELECT oid FROM pg_catalog.pg_class WHERE relkind IN ('r', 'm', 'v') AND pg_catalog.has_table_privilege (pg_class.oid, 'SELECT') AND relnamespace IN (" XML_VISIBLE_SCHEMAS ");");
02321 }
02322 
02323 
02324 /*
02325  * Map SQL table to XML and/or XML Schema document; see SQL/XML:2008
02326  * section 9.11.
02327  */
02328 
02329 static StringInfo
02330 table_to_xml_internal(Oid relid,
02331                       const char *xmlschema, bool nulls, bool tableforest,
02332                       const char *targetns, bool top_level)
02333 {
02334     StringInfoData query;
02335 
02336     initStringInfo(&query);
02337     appendStringInfo(&query, "SELECT * FROM %s",
02338                      DatumGetCString(DirectFunctionCall1(regclassout,
02339                                                   ObjectIdGetDatum(relid))));
02340     return query_to_xml_internal(query.data, get_rel_name(relid),
02341                                  xmlschema, nulls, tableforest,
02342                                  targetns, top_level);
02343 }
02344 
02345 
02346 Datum
02347 table_to_xml(PG_FUNCTION_ARGS)
02348 {
02349     Oid         relid = PG_GETARG_OID(0);
02350     bool        nulls = PG_GETARG_BOOL(1);
02351     bool        tableforest = PG_GETARG_BOOL(2);
02352     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02353 
02354     PG_RETURN_XML_P(stringinfo_to_xmltype(table_to_xml_internal(relid, NULL,
02355                                                           nulls, tableforest,
02356                                                            targetns, true)));
02357 }
02358 
02359 
02360 Datum
02361 query_to_xml(PG_FUNCTION_ARGS)
02362 {
02363     char       *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
02364     bool        nulls = PG_GETARG_BOOL(1);
02365     bool        tableforest = PG_GETARG_BOOL(2);
02366     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02367 
02368     PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL,
02369                                                     NULL, nulls, tableforest,
02370                                                            targetns, true)));
02371 }
02372 
02373 
02374 Datum
02375 cursor_to_xml(PG_FUNCTION_ARGS)
02376 {
02377     char       *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
02378     int32       count = PG_GETARG_INT32(1);
02379     bool        nulls = PG_GETARG_BOOL(2);
02380     bool        tableforest = PG_GETARG_BOOL(3);
02381     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(4));
02382 
02383     StringInfoData result;
02384     Portal      portal;
02385     int         i;
02386 
02387     initStringInfo(&result);
02388 
02389     SPI_connect();
02390     portal = SPI_cursor_find(name);
02391     if (portal == NULL)
02392         ereport(ERROR,
02393                 (errcode(ERRCODE_UNDEFINED_CURSOR),
02394                  errmsg("cursor \"%s\" does not exist", name)));
02395 
02396     SPI_cursor_fetch(portal, true, count);
02397     for (i = 0; i < SPI_processed; i++)
02398         SPI_sql_row_to_xmlelement(i, &result, NULL, nulls,
02399                                   tableforest, targetns, true);
02400 
02401     SPI_finish();
02402 
02403     PG_RETURN_XML_P(stringinfo_to_xmltype(&result));
02404 }
02405 
02406 
02407 /*
02408  * Write the start tag of the root element of a data mapping.
02409  *
02410  * top_level means that this is the very top level of the eventual
02411  * output.  For example, when the user calls table_to_xml, then a call
02412  * with a table name to this function is the top level.  When the user
02413  * calls database_to_xml, then a call with a schema name to this
02414  * function is not the top level.  If top_level is false, then the XML
02415  * namespace declarations are omitted, because they supposedly already
02416  * appeared earlier in the output.  Repeating them is not wrong, but
02417  * it looks ugly.
02418  */
02419 static void
02420 xmldata_root_element_start(StringInfo result, const char *eltname,
02421                            const char *xmlschema, const char *targetns,
02422                            bool top_level)
02423 {
02424     /* This isn't really wrong but currently makes no sense. */
02425     Assert(top_level || !xmlschema);
02426 
02427     appendStringInfo(result, "<%s", eltname);
02428     if (top_level)
02429     {
02430         appendStringInfoString(result, " xmlns:xsi=\"" NAMESPACE_XSI "\"");
02431         if (strlen(targetns) > 0)
02432             appendStringInfo(result, " xmlns=\"%s\"", targetns);
02433     }
02434     if (xmlschema)
02435     {
02436         /* FIXME: better targets */
02437         if (strlen(targetns) > 0)
02438             appendStringInfo(result, " xsi:schemaLocation=\"%s #\"", targetns);
02439         else
02440             appendStringInfo(result, " xsi:noNamespaceSchemaLocation=\"#\"");
02441     }
02442     appendStringInfo(result, ">\n");
02443 }
02444 
02445 
02446 static void
02447 xmldata_root_element_end(StringInfo result, const char *eltname)
02448 {
02449     appendStringInfo(result, "</%s>\n", eltname);
02450 }
02451 
02452 
02453 static StringInfo
02454 query_to_xml_internal(const char *query, char *tablename,
02455                       const char *xmlschema, bool nulls, bool tableforest,
02456                       const char *targetns, bool top_level)
02457 {
02458     StringInfo  result;
02459     char       *xmltn;
02460     int         i;
02461 
02462     if (tablename)
02463         xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
02464     else
02465         xmltn = "table";
02466 
02467     result = makeStringInfo();
02468 
02469     SPI_connect();
02470     if (SPI_execute(query, true, 0) != SPI_OK_SELECT)
02471         ereport(ERROR,
02472                 (errcode(ERRCODE_DATA_EXCEPTION),
02473                  errmsg("invalid query")));
02474 
02475     if (!tableforest)
02476     {
02477         xmldata_root_element_start(result, xmltn, xmlschema,
02478                                    targetns, top_level);
02479         appendStringInfoString(result, "\n");
02480     }
02481 
02482     if (xmlschema)
02483         appendStringInfo(result, "%s\n\n", xmlschema);
02484 
02485     for (i = 0; i < SPI_processed; i++)
02486         SPI_sql_row_to_xmlelement(i, result, tablename, nulls,
02487                                   tableforest, targetns, top_level);
02488 
02489     if (!tableforest)
02490         xmldata_root_element_end(result, xmltn);
02491 
02492     SPI_finish();
02493 
02494     return result;
02495 }
02496 
02497 
02498 Datum
02499 table_to_xmlschema(PG_FUNCTION_ARGS)
02500 {
02501     Oid         relid = PG_GETARG_OID(0);
02502     bool        nulls = PG_GETARG_BOOL(1);
02503     bool        tableforest = PG_GETARG_BOOL(2);
02504     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02505     const char *result;
02506     Relation    rel;
02507 
02508     rel = heap_open(relid, AccessShareLock);
02509     result = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls,
02510                                         tableforest, targetns);
02511     heap_close(rel, NoLock);
02512 
02513     PG_RETURN_XML_P(cstring_to_xmltype(result));
02514 }
02515 
02516 
02517 Datum
02518 query_to_xmlschema(PG_FUNCTION_ARGS)
02519 {
02520     char       *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
02521     bool        nulls = PG_GETARG_BOOL(1);
02522     bool        tableforest = PG_GETARG_BOOL(2);
02523     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02524     const char *result;
02525     SPIPlanPtr  plan;
02526     Portal      portal;
02527 
02528     SPI_connect();
02529 
02530     if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
02531         elog(ERROR, "SPI_prepare(\"%s\") failed", query);
02532 
02533     if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
02534         elog(ERROR, "SPI_cursor_open(\"%s\") failed", query);
02535 
02536     result = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
02537                                                     InvalidOid, nulls,
02538                                                     tableforest, targetns));
02539     SPI_cursor_close(portal);
02540     SPI_finish();
02541 
02542     PG_RETURN_XML_P(cstring_to_xmltype(result));
02543 }
02544 
02545 
02546 Datum
02547 cursor_to_xmlschema(PG_FUNCTION_ARGS)
02548 {
02549     char       *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
02550     bool        nulls = PG_GETARG_BOOL(1);
02551     bool        tableforest = PG_GETARG_BOOL(2);
02552     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02553     const char *xmlschema;
02554     Portal      portal;
02555 
02556     SPI_connect();
02557     portal = SPI_cursor_find(name);
02558     if (portal == NULL)
02559         ereport(ERROR,
02560                 (errcode(ERRCODE_UNDEFINED_CURSOR),
02561                  errmsg("cursor \"%s\" does not exist", name)));
02562 
02563     xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
02564                                                        InvalidOid, nulls,
02565                                                      tableforest, targetns));
02566     SPI_finish();
02567 
02568     PG_RETURN_XML_P(cstring_to_xmltype(xmlschema));
02569 }
02570 
02571 
02572 Datum
02573 table_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
02574 {
02575     Oid         relid = PG_GETARG_OID(0);
02576     bool        nulls = PG_GETARG_BOOL(1);
02577     bool        tableforest = PG_GETARG_BOOL(2);
02578     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02579     Relation    rel;
02580     const char *xmlschema;
02581 
02582     rel = heap_open(relid, AccessShareLock);
02583     xmlschema = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls,
02584                                            tableforest, targetns);
02585     heap_close(rel, NoLock);
02586 
02587     PG_RETURN_XML_P(stringinfo_to_xmltype(table_to_xml_internal(relid,
02588                                                xmlschema, nulls, tableforest,
02589                                                            targetns, true)));
02590 }
02591 
02592 
02593 Datum
02594 query_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
02595 {
02596     char       *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
02597     bool        nulls = PG_GETARG_BOOL(1);
02598     bool        tableforest = PG_GETARG_BOOL(2);
02599     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02600 
02601     const char *xmlschema;
02602     SPIPlanPtr  plan;
02603     Portal      portal;
02604 
02605     SPI_connect();
02606 
02607     if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
02608         elog(ERROR, "SPI_prepare(\"%s\") failed", query);
02609 
02610     if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
02611         elog(ERROR, "SPI_cursor_open(\"%s\") failed", query);
02612 
02613     xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
02614                                   InvalidOid, nulls, tableforest, targetns));
02615     SPI_cursor_close(portal);
02616     SPI_finish();
02617 
02618     PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL,
02619                                                xmlschema, nulls, tableforest,
02620                                                            targetns, true)));
02621 }
02622 
02623 
02624 /*
02625  * Map SQL schema to XML and/or XML Schema document; see SQL/XML:2008
02626  * sections 9.13, 9.14.
02627  */
02628 
02629 static StringInfo
02630 schema_to_xml_internal(Oid nspid, const char *xmlschema, bool nulls,
02631                        bool tableforest, const char *targetns, bool top_level)
02632 {
02633     StringInfo  result;
02634     char       *xmlsn;
02635     List       *relid_list;
02636     ListCell   *cell;
02637 
02638     xmlsn = map_sql_identifier_to_xml_name(get_namespace_name(nspid),
02639                                            true, false);
02640     result = makeStringInfo();
02641 
02642     xmldata_root_element_start(result, xmlsn, xmlschema, targetns, top_level);
02643     appendStringInfoString(result, "\n");
02644 
02645     if (xmlschema)
02646         appendStringInfo(result, "%s\n\n", xmlschema);
02647 
02648     SPI_connect();
02649 
02650     relid_list = schema_get_xml_visible_tables(nspid);
02651 
02652     SPI_push();
02653 
02654     foreach(cell, relid_list)
02655     {
02656         Oid         relid = lfirst_oid(cell);
02657         StringInfo  subres;
02658 
02659         subres = table_to_xml_internal(relid, NULL, nulls, tableforest,
02660                                        targetns, false);
02661 
02662         appendStringInfoString(result, subres->data);
02663         appendStringInfoChar(result, '\n');
02664     }
02665 
02666     SPI_pop();
02667     SPI_finish();
02668 
02669     xmldata_root_element_end(result, xmlsn);
02670 
02671     return result;
02672 }
02673 
02674 
02675 Datum
02676 schema_to_xml(PG_FUNCTION_ARGS)
02677 {
02678     Name        name = PG_GETARG_NAME(0);
02679     bool        nulls = PG_GETARG_BOOL(1);
02680     bool        tableforest = PG_GETARG_BOOL(2);
02681     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02682 
02683     char       *schemaname;
02684     Oid         nspid;
02685 
02686     schemaname = NameStr(*name);
02687     nspid = LookupExplicitNamespace(schemaname, false);
02688 
02689     PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xml_internal(nspid, NULL,
02690                                        nulls, tableforest, targetns, true)));
02691 }
02692 
02693 
02694 /*
02695  * Write the start element of the root element of an XML Schema mapping.
02696  */
02697 static void
02698 xsd_schema_element_start(StringInfo result, const char *targetns)
02699 {
02700     appendStringInfoString(result,
02701                            "<xsd:schema\n"
02702                            "    xmlns:xsd=\"" NAMESPACE_XSD "\"");
02703     if (strlen(targetns) > 0)
02704         appendStringInfo(result,
02705                          "\n"
02706                          "    targetNamespace=\"%s\"\n"
02707                          "    elementFormDefault=\"qualified\"",
02708                          targetns);
02709     appendStringInfoString(result,
02710                            ">\n\n");
02711 }
02712 
02713 
02714 static void
02715 xsd_schema_element_end(StringInfo result)
02716 {
02717     appendStringInfoString(result, "</xsd:schema>");
02718 }
02719 
02720 
02721 static StringInfo
02722 schema_to_xmlschema_internal(const char *schemaname, bool nulls,
02723                              bool tableforest, const char *targetns)
02724 {
02725     Oid         nspid;
02726     List       *relid_list;
02727     List       *tupdesc_list;
02728     ListCell   *cell;
02729     StringInfo  result;
02730 
02731     result = makeStringInfo();
02732 
02733     nspid = LookupExplicitNamespace(schemaname, false);
02734 
02735     xsd_schema_element_start(result, targetns);
02736 
02737     SPI_connect();
02738 
02739     relid_list = schema_get_xml_visible_tables(nspid);
02740 
02741     tupdesc_list = NIL;
02742     foreach(cell, relid_list)
02743     {
02744         Relation    rel;
02745 
02746         rel = heap_open(lfirst_oid(cell), AccessShareLock);
02747         tupdesc_list = lappend(tupdesc_list, CreateTupleDescCopy(rel->rd_att));
02748         heap_close(rel, NoLock);
02749     }
02750 
02751     appendStringInfoString(result,
02752                            map_sql_typecoll_to_xmlschema_types(tupdesc_list));
02753 
02754     appendStringInfoString(result,
02755                          map_sql_schema_to_xmlschema_types(nspid, relid_list,
02756                                               nulls, tableforest, targetns));
02757 
02758     xsd_schema_element_end(result);
02759 
02760     SPI_finish();
02761 
02762     return result;
02763 }
02764 
02765 
02766 Datum
02767 schema_to_xmlschema(PG_FUNCTION_ARGS)
02768 {
02769     Name        name = PG_GETARG_NAME(0);
02770     bool        nulls = PG_GETARG_BOOL(1);
02771     bool        tableforest = PG_GETARG_BOOL(2);
02772     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02773 
02774     PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xmlschema_internal(NameStr(*name),
02775                                              nulls, tableforest, targetns)));
02776 }
02777 
02778 
02779 Datum
02780 schema_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
02781 {
02782     Name        name = PG_GETARG_NAME(0);
02783     bool        nulls = PG_GETARG_BOOL(1);
02784     bool        tableforest = PG_GETARG_BOOL(2);
02785     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02786     char       *schemaname;
02787     Oid         nspid;
02788     StringInfo  xmlschema;
02789 
02790     schemaname = NameStr(*name);
02791     nspid = LookupExplicitNamespace(schemaname, false);
02792 
02793     xmlschema = schema_to_xmlschema_internal(schemaname, nulls,
02794                                              tableforest, targetns);
02795 
02796     PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xml_internal(nspid,
02797                                                       xmlschema->data, nulls,
02798                                               tableforest, targetns, true)));
02799 }
02800 
02801 
02802 /*
02803  * Map SQL database to XML and/or XML Schema document; see SQL/XML:2008
02804  * sections 9.16, 9.17.
02805  */
02806 
02807 static StringInfo
02808 database_to_xml_internal(const char *xmlschema, bool nulls,
02809                          bool tableforest, const char *targetns)
02810 {
02811     StringInfo  result;
02812     List       *nspid_list;
02813     ListCell   *cell;
02814     char       *xmlcn;
02815 
02816     xmlcn = map_sql_identifier_to_xml_name(get_database_name(MyDatabaseId),
02817                                            true, false);
02818     result = makeStringInfo();
02819 
02820     xmldata_root_element_start(result, xmlcn, xmlschema, targetns, true);
02821     appendStringInfoString(result, "\n");
02822 
02823     if (xmlschema)
02824         appendStringInfo(result, "%s\n\n", xmlschema);
02825 
02826     SPI_connect();
02827 
02828     nspid_list = database_get_xml_visible_schemas();
02829 
02830     SPI_push();
02831 
02832     foreach(cell, nspid_list)
02833     {
02834         Oid         nspid = lfirst_oid(cell);
02835         StringInfo  subres;
02836 
02837         subres = schema_to_xml_internal(nspid, NULL, nulls,
02838                                         tableforest, targetns, false);
02839 
02840         appendStringInfoString(result, subres->data);
02841         appendStringInfoChar(result, '\n');
02842     }
02843 
02844     SPI_pop();
02845     SPI_finish();
02846 
02847     xmldata_root_element_end(result, xmlcn);
02848 
02849     return result;
02850 }
02851 
02852 
02853 Datum
02854 database_to_xml(PG_FUNCTION_ARGS)
02855 {
02856     bool        nulls = PG_GETARG_BOOL(0);
02857     bool        tableforest = PG_GETARG_BOOL(1);
02858     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
02859 
02860     PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xml_internal(NULL, nulls,
02861                                                     tableforest, targetns)));
02862 }
02863 
02864 
02865 static StringInfo
02866 database_to_xmlschema_internal(bool nulls, bool tableforest,
02867                                const char *targetns)
02868 {
02869     List       *relid_list;
02870     List       *nspid_list;
02871     List       *tupdesc_list;
02872     ListCell   *cell;
02873     StringInfo  result;
02874 
02875     result = makeStringInfo();
02876 
02877     xsd_schema_element_start(result, targetns);
02878 
02879     SPI_connect();
02880 
02881     relid_list = database_get_xml_visible_tables();
02882     nspid_list = database_get_xml_visible_schemas();
02883 
02884     tupdesc_list = NIL;
02885     foreach(cell, relid_list)
02886     {
02887         Relation    rel;
02888 
02889         rel = heap_open(lfirst_oid(cell), AccessShareLock);
02890         tupdesc_list = lappend(tupdesc_list, CreateTupleDescCopy(rel->rd_att));
02891         heap_close(rel, NoLock);
02892     }
02893 
02894     appendStringInfoString(result,
02895                            map_sql_typecoll_to_xmlschema_types(tupdesc_list));
02896 
02897     appendStringInfoString(result,
02898                            map_sql_catalog_to_xmlschema_types(nspid_list, nulls, tableforest, targetns));
02899 
02900     xsd_schema_element_end(result);
02901 
02902     SPI_finish();
02903 
02904     return result;
02905 }
02906 
02907 
02908 Datum
02909 database_to_xmlschema(PG_FUNCTION_ARGS)
02910 {
02911     bool        nulls = PG_GETARG_BOOL(0);
02912     bool        tableforest = PG_GETARG_BOOL(1);
02913     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
02914 
02915     PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xmlschema_internal(nulls,
02916                                                     tableforest, targetns)));
02917 }
02918 
02919 
02920 Datum
02921 database_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
02922 {
02923     bool        nulls = PG_GETARG_BOOL(0);
02924     bool        tableforest = PG_GETARG_BOOL(1);
02925     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
02926     StringInfo  xmlschema;
02927 
02928     xmlschema = database_to_xmlschema_internal(nulls, tableforest, targetns);
02929 
02930     PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xml_internal(xmlschema->data,
02931                                              nulls, tableforest, targetns)));
02932 }
02933 
02934 
02935 /*
02936  * Map a multi-part SQL name to an XML name; see SQL/XML:2008 section
02937  * 9.2.
02938  */
02939 static char *
02940 map_multipart_sql_identifier_to_xml_name(char *a, char *b, char *c, char *d)
02941 {
02942     StringInfoData result;
02943 
02944     initStringInfo(&result);
02945 
02946     if (a)
02947         appendStringInfo(&result, "%s",
02948                          map_sql_identifier_to_xml_name(a, true, true));
02949     if (b)
02950         appendStringInfo(&result, ".%s",
02951                          map_sql_identifier_to_xml_name(b, true, true));
02952     if (c)
02953         appendStringInfo(&result, ".%s",
02954                          map_sql_identifier_to_xml_name(c, true, true));
02955     if (d)
02956         appendStringInfo(&result, ".%s",
02957                          map_sql_identifier_to_xml_name(d, true, true));
02958 
02959     return result.data;
02960 }
02961 
02962 
02963 /*
02964  * Map an SQL table to an XML Schema document; see SQL/XML:2008
02965  * section 9.11.
02966  *
02967  * Map an SQL table to XML Schema data types; see SQL/XML:2008 section
02968  * 9.9.
02969  */
02970 static const char *
02971 map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls,
02972                            bool tableforest, const char *targetns)
02973 {
02974     int         i;
02975     char       *xmltn;
02976     char       *tabletypename;
02977     char       *rowtypename;
02978     StringInfoData result;
02979 
02980     initStringInfo(&result);
02981 
02982     if (OidIsValid(relid))
02983     {
02984         HeapTuple   tuple;
02985         Form_pg_class reltuple;
02986 
02987         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
02988         if (!HeapTupleIsValid(tuple))
02989             elog(ERROR, "cache lookup failed for relation %u", relid);
02990         reltuple = (Form_pg_class) GETSTRUCT(tuple);
02991 
02992         xmltn = map_sql_identifier_to_xml_name(NameStr(reltuple->relname),
02993                                                true, false);
02994 
02995         tabletypename = map_multipart_sql_identifier_to_xml_name("TableType",
02996                                              get_database_name(MyDatabaseId),
02997                                   get_namespace_name(reltuple->relnamespace),
02998                                                  NameStr(reltuple->relname));
02999 
03000         rowtypename = map_multipart_sql_identifier_to_xml_name("RowType",
03001                                              get_database_name(MyDatabaseId),
03002                                   get_namespace_name(reltuple->relnamespace),
03003                                                  NameStr(reltuple->relname));
03004 
03005         ReleaseSysCache(tuple);
03006     }
03007     else
03008     {
03009         if (tableforest)
03010             xmltn = "row";
03011         else
03012             xmltn = "table";
03013 
03014         tabletypename = "TableType";
03015         rowtypename = "RowType";
03016     }
03017 
03018     xsd_schema_element_start(&result, targetns);
03019 
03020     appendStringInfoString(&result,
03021                    map_sql_typecoll_to_xmlschema_types(list_make1(tupdesc)));
03022 
03023     appendStringInfo(&result,
03024                      "<xsd:complexType name=\"%s\">\n"
03025                      "  <xsd:sequence>\n",
03026                      rowtypename);
03027 
03028     for (i = 0; i < tupdesc->natts; i++)
03029     {
03030         if (tupdesc->attrs[i]->attisdropped)
03031             continue;
03032         appendStringInfo(&result,
03033                "    <xsd:element name=\"%s\" type=\"%s\"%s></xsd:element>\n",
03034           map_sql_identifier_to_xml_name(NameStr(tupdesc->attrs[i]->attname),
03035                                          true, false),
03036                    map_sql_type_to_xml_name(tupdesc->attrs[i]->atttypid, -1),
03037                          nulls ? " nillable=\"true\"" : " minOccurs=\"0\"");
03038     }
03039 
03040     appendStringInfoString(&result,
03041                            "  </xsd:sequence>\n"
03042                            "</xsd:complexType>\n\n");
03043 
03044     if (!tableforest)
03045     {
03046         appendStringInfo(&result,
03047                          "<xsd:complexType name=\"%s\">\n"
03048                          "  <xsd:sequence>\n"
03049                          "    <xsd:element name=\"row\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n"
03050                          "  </xsd:sequence>\n"
03051                          "</xsd:complexType>\n\n",
03052                          tabletypename, rowtypename);
03053 
03054         appendStringInfo(&result,
03055                          "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
03056                          xmltn, tabletypename);
03057     }
03058     else
03059         appendStringInfo(&result,
03060                          "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
03061                          xmltn, rowtypename);
03062 
03063     xsd_schema_element_end(&result);
03064 
03065     return result.data;
03066 }
03067 
03068 
03069 /*
03070  * Map an SQL schema to XML Schema data types; see SQL/XML:2008
03071  * section 9.12.
03072  */
03073 static const char *
03074 map_sql_schema_to_xmlschema_types(Oid nspid, List *relid_list, bool nulls,
03075                                   bool tableforest, const char *targetns)
03076 {
03077     char       *dbname;
03078     char       *nspname;
03079     char       *xmlsn;
03080     char       *schematypename;
03081     StringInfoData result;
03082     ListCell   *cell;
03083 
03084     dbname = get_database_name(MyDatabaseId);
03085     nspname = get_namespace_name(nspid);
03086 
03087     initStringInfo(&result);
03088 
03089     xmlsn = map_sql_identifier_to_xml_name(nspname, true, false);
03090 
03091     schematypename = map_multipart_sql_identifier_to_xml_name("SchemaType",
03092                                                               dbname,
03093                                                               nspname,
03094                                                               NULL);
03095 
03096     appendStringInfo(&result,
03097                      "<xsd:complexType name=\"%s\">\n", schematypename);
03098     if (!tableforest)
03099         appendStringInfoString(&result,
03100                                "  <xsd:all>\n");
03101     else
03102         appendStringInfoString(&result,
03103                                "  <xsd:sequence>\n");
03104 
03105     foreach(cell, relid_list)
03106     {
03107         Oid         relid = lfirst_oid(cell);
03108         char       *relname = get_rel_name(relid);
03109         char       *xmltn = map_sql_identifier_to_xml_name(relname, true, false);
03110         char       *tabletypename = map_multipart_sql_identifier_to_xml_name(tableforest ? "RowType" : "TableType",
03111                                                                       dbname,
03112                                                                      nspname,
03113                                                                     relname);
03114 
03115         if (!tableforest)
03116             appendStringInfo(&result,
03117                              "    <xsd:element name=\"%s\" type=\"%s\"/>\n",
03118                              xmltn, tabletypename);
03119         else
03120             appendStringInfo(&result,
03121                              "    <xsd:element name=\"%s\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n",
03122                              xmltn, tabletypename);
03123     }
03124 
03125     if (!tableforest)
03126         appendStringInfoString(&result,
03127                                "  </xsd:all>\n");
03128     else
03129         appendStringInfoString(&result,
03130                                "  </xsd:sequence>\n");
03131     appendStringInfoString(&result,
03132                            "</xsd:complexType>\n\n");
03133 
03134     appendStringInfo(&result,
03135                      "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
03136                      xmlsn, schematypename);
03137 
03138     return result.data;
03139 }
03140 
03141 
03142 /*
03143  * Map an SQL catalog to XML Schema data types; see SQL/XML:2008
03144  * section 9.15.
03145  */
03146 static const char *
03147 map_sql_catalog_to_xmlschema_types(List *nspid_list, bool nulls,
03148                                    bool tableforest, const char *targetns)
03149 {
03150     char       *dbname;
03151     char       *xmlcn;
03152     char       *catalogtypename;
03153     StringInfoData result;
03154     ListCell   *cell;
03155 
03156     dbname = get_database_name(MyDatabaseId);
03157 
03158     initStringInfo(&result);
03159 
03160     xmlcn = map_sql_identifier_to_xml_name(dbname, true, false);
03161 
03162     catalogtypename = map_multipart_sql_identifier_to_xml_name("CatalogType",
03163                                                                dbname,
03164                                                                NULL,
03165                                                                NULL);
03166 
03167     appendStringInfo(&result,
03168                      "<xsd:complexType name=\"%s\">\n", catalogtypename);
03169     appendStringInfoString(&result,
03170                            "  <xsd:all>\n");
03171 
03172     foreach(cell, nspid_list)
03173     {
03174         Oid         nspid = lfirst_oid(cell);
03175         char       *nspname = get_namespace_name(nspid);
03176         char       *xmlsn = map_sql_identifier_to_xml_name(nspname, true, false);
03177         char       *schematypename = map_multipart_sql_identifier_to_xml_name("SchemaType",
03178                                                                       dbname,
03179                                                                      nspname,
03180                                                                        NULL);
03181 
03182         appendStringInfo(&result,
03183                          "    <xsd:element name=\"%s\" type=\"%s\"/>\n",
03184                          xmlsn, schematypename);
03185     }
03186 
03187     appendStringInfoString(&result,
03188                            "  </xsd:all>\n");
03189     appendStringInfoString(&result,
03190                            "</xsd:complexType>\n\n");
03191 
03192     appendStringInfo(&result,
03193                      "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
03194                      xmlcn, catalogtypename);
03195 
03196     return result.data;
03197 }
03198 
03199 
03200 /*
03201  * Map an SQL data type to an XML name; see SQL/XML:2008 section 9.4.
03202  */
03203 static const char *
03204 map_sql_type_to_xml_name(Oid typeoid, int typmod)
03205 {
03206     StringInfoData result;
03207 
03208     initStringInfo(&result);
03209 
03210     switch (typeoid)
03211     {
03212         case BPCHAROID:
03213             if (typmod == -1)
03214                 appendStringInfo(&result, "CHAR");
03215             else
03216                 appendStringInfo(&result, "CHAR_%d", typmod - VARHDRSZ);
03217             break;
03218         case VARCHAROID:
03219             if (typmod == -1)
03220                 appendStringInfo(&result, "VARCHAR");
03221             else
03222                 appendStringInfo(&result, "VARCHAR_%d", typmod - VARHDRSZ);
03223             break;
03224         case NUMERICOID:
03225             if (typmod == -1)
03226                 appendStringInfo(&result, "NUMERIC");
03227             else
03228                 appendStringInfo(&result, "NUMERIC_%d_%d",
03229                                  ((typmod - VARHDRSZ) >> 16) & 0xffff,
03230                                  (typmod - VARHDRSZ) & 0xffff);
03231             break;
03232         case INT4OID:
03233             appendStringInfo(&result, "INTEGER");
03234             break;
03235         case INT2OID:
03236             appendStringInfo(&result, "SMALLINT");
03237             break;
03238         case INT8OID:
03239             appendStringInfo(&result, "BIGINT");
03240             break;
03241         case FLOAT4OID:
03242             appendStringInfo(&result, "REAL");
03243             break;
03244         case FLOAT8OID:
03245             appendStringInfo(&result, "DOUBLE");
03246             break;
03247         case BOOLOID:
03248             appendStringInfo(&result, "BOOLEAN");
03249             break;
03250         case TIMEOID:
03251             if (typmod == -1)
03252                 appendStringInfo(&result, "TIME");
03253             else
03254                 appendStringInfo(&result, "TIME_%d", typmod);
03255             break;
03256         case TIMETZOID:
03257             if (typmod == -1)
03258                 appendStringInfo(&result, "TIME_WTZ");
03259             else
03260                 appendStringInfo(&result, "TIME_WTZ_%d", typmod);
03261             break;
03262         case TIMESTAMPOID:
03263             if (typmod == -1)
03264                 appendStringInfo(&result, "TIMESTAMP");
03265             else
03266                 appendStringInfo(&result, "TIMESTAMP_%d", typmod);
03267             break;
03268         case TIMESTAMPTZOID:
03269             if (typmod == -1)
03270                 appendStringInfo(&result, "TIMESTAMP_WTZ");
03271             else
03272                 appendStringInfo(&result, "TIMESTAMP_WTZ_%d", typmod);
03273             break;
03274         case DATEOID:
03275             appendStringInfo(&result, "DATE");
03276             break;
03277         case XMLOID:
03278             appendStringInfo(&result, "XML");
03279             break;
03280         default:
03281             {
03282                 HeapTuple   tuple;
03283                 Form_pg_type typtuple;
03284 
03285                 tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeoid));
03286                 if (!HeapTupleIsValid(tuple))
03287                     elog(ERROR, "cache lookup failed for type %u", typeoid);
03288                 typtuple = (Form_pg_type) GETSTRUCT(tuple);
03289 
03290                 appendStringInfoString(&result,
03291                                        map_multipart_sql_identifier_to_xml_name((typtuple->typtype == TYPTYPE_DOMAIN) ? "Domain" : "UDT",
03292                                              get_database_name(MyDatabaseId),
03293                                   get_namespace_name(typtuple->typnamespace),
03294                                                 NameStr(typtuple->typname)));
03295 
03296                 ReleaseSysCache(tuple);
03297             }
03298     }
03299 
03300     return result.data;
03301 }
03302 
03303 
03304 /*
03305  * Map a collection of SQL data types to XML Schema data types; see
03306  * SQL/XML:2008 section 9.7.
03307  */
03308 static const char *
03309 map_sql_typecoll_to_xmlschema_types(List *tupdesc_list)
03310 {
03311     List       *uniquetypes = NIL;
03312     int         i;
03313     StringInfoData result;
03314     ListCell   *cell0;
03315 
03316     /* extract all column types used in the set of TupleDescs */
03317     foreach(cell0, tupdesc_list)
03318     {
03319         TupleDesc   tupdesc = (TupleDesc) lfirst(cell0);
03320 
03321         for (i = 0; i < tupdesc->natts; i++)
03322         {
03323             if (tupdesc->attrs[i]->attisdropped)
03324                 continue;
03325             uniquetypes = list_append_unique_oid(uniquetypes,
03326                                                  tupdesc->attrs[i]->atttypid);
03327         }
03328     }
03329 
03330     /* add base types of domains */
03331     foreach(cell0, uniquetypes)
03332     {
03333         Oid         typid = lfirst_oid(cell0);
03334         Oid         basetypid = getBaseType(typid);
03335 
03336         if (basetypid != typid)
03337             uniquetypes = list_append_unique_oid(uniquetypes, basetypid);
03338     }
03339 
03340     /* Convert to textual form */
03341     initStringInfo(&result);
03342 
03343     foreach(cell0, uniquetypes)
03344     {
03345         appendStringInfo(&result, "%s\n",
03346                          map_sql_type_to_xmlschema_type(lfirst_oid(cell0),
03347                                                         -1));
03348     }
03349 
03350     return result.data;
03351 }
03352 
03353 
03354 /*
03355  * Map an SQL data type to a named XML Schema data type; see
03356  * SQL/XML:2008 sections 9.5 and 9.6.
03357  *
03358  * (The distinction between 9.5 and 9.6 is basically that 9.6 adds
03359  * a name attribute, which this function does.  The name-less version
03360  * 9.5 doesn't appear to be required anywhere.)
03361  */
03362 static const char *
03363 map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
03364 {
03365     StringInfoData result;
03366     const char *typename = map_sql_type_to_xml_name(typeoid, typmod);
03367 
03368     initStringInfo(&result);
03369 
03370     if (typeoid == XMLOID)
03371     {
03372         appendStringInfo(&result,
03373                          "<xsd:complexType mixed=\"true\">\n"
03374                          "  <xsd:sequence>\n"
03375                          "    <xsd:any name=\"element\" minOccurs=\"0\" maxOccurs=\"unbounded\" processContents=\"skip\"/>\n"
03376                          "  </xsd:sequence>\n"
03377                          "</xsd:complexType>\n");
03378     }
03379     else
03380     {
03381         appendStringInfo(&result,
03382                          "<xsd:simpleType name=\"%s\">\n", typename);
03383 
03384         switch (typeoid)
03385         {
03386             case BPCHAROID:
03387             case VARCHAROID:
03388             case TEXTOID:
03389                 appendStringInfo(&result,
03390                                  "  <xsd:restriction base=\"xsd:string\">\n");
03391                 if (typmod != -1)
03392                     appendStringInfo(&result,
03393                                      "    <xsd:maxLength value=\"%d\"/>\n",
03394                                      typmod - VARHDRSZ);
03395                 appendStringInfo(&result,
03396                                  "  </xsd:restriction>\n");
03397                 break;
03398 
03399             case BYTEAOID:
03400                 appendStringInfo(&result,
03401                                  "  <xsd:restriction base=\"xsd:%s\">\n"
03402                                  "  </xsd:restriction>\n",
03403                 xmlbinary == XMLBINARY_BASE64 ? "base64Binary" : "hexBinary");
03404                 break;
03405 
03406             case NUMERICOID:
03407                 if (typmod != -1)
03408                     appendStringInfo(&result,
03409                                  "  <xsd:restriction base=\"xsd:decimal\">\n"
03410                                      "    <xsd:totalDigits value=\"%d\"/>\n"
03411                                    "    <xsd:fractionDigits value=\"%d\"/>\n"
03412                                      "  </xsd:restriction>\n",
03413                                      ((typmod - VARHDRSZ) >> 16) & 0xffff,
03414                                      (typmod - VARHDRSZ) & 0xffff);
03415                 break;
03416 
03417             case INT2OID:
03418                 appendStringInfo(&result,
03419                                  "  <xsd:restriction base=\"xsd:short\">\n"
03420                                  "    <xsd:maxInclusive value=\"%d\"/>\n"
03421                                  "    <xsd:minInclusive value=\"%d\"/>\n"
03422                                  "  </xsd:restriction>\n",
03423                                  SHRT_MAX, SHRT_MIN);
03424                 break;
03425 
03426             case INT4OID:
03427                 appendStringInfo(&result,
03428                                  "  <xsd:restriction base=\"xsd:int\">\n"
03429                                  "    <xsd:maxInclusive value=\"%d\"/>\n"
03430                                  "    <xsd:minInclusive value=\"%d\"/>\n"
03431                                  "  </xsd:restriction>\n",
03432                                  INT_MAX, INT_MIN);
03433                 break;
03434 
03435             case INT8OID:
03436                 appendStringInfo(&result,
03437                                  "  <xsd:restriction base=\"xsd:long\">\n"
03438                        "    <xsd:maxInclusive value=\"" INT64_FORMAT "\"/>\n"
03439                        "    <xsd:minInclusive value=\"" INT64_FORMAT "\"/>\n"
03440                                  "  </xsd:restriction>\n",
03441                                (((uint64) 1) << (sizeof(int64) * 8 - 1)) - 1,
03442                                  (((uint64) 1) << (sizeof(int64) * 8 - 1)));
03443                 break;
03444 
03445             case FLOAT4OID:
03446                 appendStringInfo(&result,
03447                 "  <xsd:restriction base=\"xsd:float\"></xsd:restriction>\n");
03448                 break;
03449 
03450             case FLOAT8OID:
03451                 appendStringInfo(&result,
03452                                  "  <xsd:restriction base=\"xsd:double\"></xsd:restriction>\n");
03453                 break;
03454 
03455             case BOOLOID:
03456                 appendStringInfo(&result,
03457                                  "  <xsd:restriction base=\"xsd:boolean\"></xsd:restriction>\n");
03458                 break;
03459 
03460             case TIMEOID:
03461             case TIMETZOID:
03462                 {
03463                     const char *tz = (typeoid == TIMETZOID ? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
03464 
03465                     if (typmod == -1)
03466                         appendStringInfo(&result,
03467                                     "  <xsd:restriction base=\"xsd:time\">\n"
03468                                          "    <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}(.\\p{Nd}+)?%s\"/>\n"
03469                                          "  </xsd:restriction>\n", tz);
03470                     else if (typmod == 0)
03471                         appendStringInfo(&result,
03472                                     "  <xsd:restriction base=\"xsd:time\">\n"
03473                                          "    <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
03474                                          "  </xsd:restriction>\n", tz);
03475                     else
03476                         appendStringInfo(&result,
03477                                     "  <xsd:restriction base=\"xsd:time\">\n"
03478                                          "    <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}.\\p{Nd}{%d}%s\"/>\n"
03479                             "  </xsd:restriction>\n", typmod - VARHDRSZ, tz);
03480                     break;
03481                 }
03482 
03483             case TIMESTAMPOID:
03484             case TIMESTAMPTZOID:
03485                 {
03486                     const char *tz = (typeoid == TIMESTAMPTZOID ? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
03487 
03488                     if (typmod == -1)
03489                         appendStringInfo(&result,
03490                                 "  <xsd:restriction base=\"xsd:dateTime\">\n"
03491                                          "    <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"
03492                                          "  </xsd:restriction>\n", tz);
03493                     else if (typmod == 0)
03494                         appendStringInfo(&result,
03495                                 "  <xsd:restriction base=\"xsd:dateTime\">\n"
03496                                          "    <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}T\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
03497                                          "  </xsd:restriction>\n", tz);
03498                     else
03499                         appendStringInfo(&result,
03500                                 "  <xsd:restriction base=\"xsd:dateTime\">\n"
03501                                          "    <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"
03502                             "  </xsd:restriction>\n", typmod - VARHDRSZ, tz);
03503                     break;
03504                 }
03505 
03506             case DATEOID:
03507                 appendStringInfo(&result,
03508                                  "  <xsd:restriction base=\"xsd:date\">\n"
03509                                  "    <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}\"/>\n"
03510                                  "  </xsd:restriction>\n");
03511                 break;
03512 
03513             default:
03514                 if (get_typtype(typeoid) == TYPTYPE_DOMAIN)
03515                 {
03516                     Oid         base_typeoid;
03517                     int32       base_typmod = -1;
03518 
03519                     base_typeoid = getBaseTypeAndTypmod(typeoid, &base_typmod);
03520 
03521                     appendStringInfo(&result,
03522                                      "  <xsd:restriction base=\"%s\"/>\n",
03523                         map_sql_type_to_xml_name(base_typeoid, base_typmod));
03524                 }
03525                 break;
03526         }
03527         appendStringInfo(&result,
03528                          "</xsd:simpleType>\n");
03529     }
03530 
03531     return result.data;
03532 }
03533 
03534 
03535 /*
03536  * Map an SQL row to an XML element, taking the row from the active
03537  * SPI cursor.  See also SQL/XML:2008 section 9.10.
03538  */
03539 static void
03540 SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename,
03541                           bool nulls, bool tableforest,
03542                           const char *targetns, bool top_level)
03543 {
03544     int         i;
03545     char       *xmltn;
03546 
03547     if (tablename)
03548         xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
03549     else
03550     {
03551         if (tableforest)
03552             xmltn = "row";
03553         else
03554             xmltn = "table";
03555     }
03556 
03557     if (tableforest)
03558         xmldata_root_element_start(result, xmltn, NULL, targetns, top_level);
03559     else
03560         appendStringInfoString(result, "<row>\n");
03561 
03562     for (i = 1; i <= SPI_tuptable->tupdesc->natts; i++)
03563     {
03564         char       *colname;
03565         Datum       colval;
03566         bool        isnull;
03567 
03568         colname = map_sql_identifier_to_xml_name(SPI_fname(SPI_tuptable->tupdesc, i),
03569                                                  true, false);
03570         colval = SPI_getbinval(SPI_tuptable->vals[rownum],
03571                                SPI_tuptable->tupdesc,
03572                                i,
03573                                &isnull);
03574         if (isnull)
03575         {
03576             if (nulls)
03577                 appendStringInfo(result, "  <%s xsi:nil=\"true\"/>\n", colname);
03578         }
03579         else
03580             appendStringInfo(result, "  <%s>%s</%s>\n",
03581                              colname,
03582                              map_sql_value_to_xml_value(colval,
03583                               SPI_gettypeid(SPI_tuptable->tupdesc, i), true),
03584                              colname);
03585     }
03586 
03587     if (tableforest)
03588     {
03589         xmldata_root_element_end(result, xmltn);
03590         appendStringInfoChar(result, '\n');
03591     }
03592     else
03593         appendStringInfoString(result, "</row>\n\n");
03594 }
03595 
03596 
03597 /*
03598  * XPath related functions
03599  */
03600 
03601 #ifdef USE_LIBXML
03602 
03603 /*
03604  * Convert XML node to text (dump subtree in case of element,
03605  * return value otherwise)
03606  */
03607 static text *
03608 xml_xmlnodetoxmltype(xmlNodePtr cur)
03609 {
03610     xmltype    *result;
03611 
03612     if (cur->type == XML_ELEMENT_NODE)
03613     {
03614         xmlBufferPtr buf;
03615 
03616         buf = xmlBufferCreate();
03617         PG_TRY();
03618         {
03619             xmlNodeDump(buf, NULL, cur, 0, 1);
03620             result = xmlBuffer_to_xmltype(buf);
03621         }
03622         PG_CATCH();
03623         {
03624             xmlBufferFree(buf);
03625             PG_RE_THROW();
03626         }
03627         PG_END_TRY();
03628         xmlBufferFree(buf);
03629     }
03630     else
03631     {
03632         xmlChar    *str;
03633 
03634         str = xmlXPathCastNodeToString(cur);
03635         PG_TRY();
03636         {
03637             /* Here we rely on XML having the same representation as TEXT */
03638             char       *escaped = escape_xml((char *) str);
03639 
03640             result = (xmltype *) cstring_to_text(escaped);
03641             pfree(escaped);
03642         }
03643         PG_CATCH();
03644         {
03645             xmlFree(str);
03646             PG_RE_THROW();
03647         }
03648         PG_END_TRY();
03649         xmlFree(str);
03650     }
03651 
03652     return result;
03653 }
03654 
03655 /*
03656  * Convert an XML XPath object (the result of evaluating an XPath expression)
03657  * to an array of xml values, which is returned at *astate.  The function
03658  * result value is the number of elements in the array.
03659  *
03660  * If "astate" is NULL then we don't generate the array value, but we still
03661  * return the number of elements it would have had.
03662  *
03663  * Nodesets are converted to an array containing the nodes' textual
03664  * representations.  Primitive values (float, double, string) are converted
03665  * to a single-element array containing the value's string representation.
03666  */
03667 static int
03668 xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
03669                        ArrayBuildState **astate)
03670 {
03671     int         result = 0;
03672     Datum       datum;
03673     Oid         datumtype;
03674     char       *result_str;
03675 
03676     if (astate != NULL)
03677         *astate = NULL;
03678 
03679     switch (xpathobj->type)
03680     {
03681         case XPATH_NODESET:
03682             if (xpathobj->nodesetval != NULL)
03683             {
03684                 result = xpathobj->nodesetval->nodeNr;
03685                 if (astate != NULL)
03686                 {
03687                     int         i;
03688 
03689                     for (i = 0; i < result; i++)
03690                     {
03691                         datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i]));
03692                         *astate = accumArrayResult(*astate, datum,
03693                                                    false, XMLOID,
03694                                                    CurrentMemoryContext);
03695                     }
03696                 }
03697             }
03698             return result;
03699 
03700         case XPATH_BOOLEAN:
03701             if (astate == NULL)
03702                 return 1;
03703             datum = BoolGetDatum(xpathobj->boolval);
03704             datumtype = BOOLOID;
03705             break;
03706 
03707         case XPATH_NUMBER:
03708             if (astate == NULL)
03709                 return 1;
03710             datum = Float8GetDatum(xpathobj->floatval);
03711             datumtype = FLOAT8OID;
03712             break;
03713 
03714         case XPATH_STRING:
03715             if (astate == NULL)
03716                 return 1;
03717             datum = CStringGetDatum((char *) xpathobj->stringval);
03718             datumtype = CSTRINGOID;
03719             break;
03720 
03721         default:
03722             elog(ERROR, "xpath expression result type %d is unsupported",
03723                  xpathobj->type);
03724             return 0;           /* keep compiler quiet */
03725     }
03726 
03727     /* Common code for scalar-value cases */
03728     result_str = map_sql_value_to_xml_value(datum, datumtype, true);
03729     datum = PointerGetDatum(cstring_to_xmltype(result_str));
03730     *astate = accumArrayResult(*astate, datum,
03731                                false, XMLOID,
03732                                CurrentMemoryContext);
03733     return 1;
03734 }
03735 
03736 
03737 /*
03738  * Common code for xpath() and xmlexists()
03739  *
03740  * Evaluate XPath expression and return number of nodes in res_items
03741  * and array of XML values in astate.  Either of those pointers can be
03742  * NULL if the corresponding result isn't wanted.
03743  *
03744  * It is up to the user to ensure that the XML passed is in fact
03745  * an XML document - XPath doesn't work easily on fragments without
03746  * a context node being known.
03747  */
03748 static void
03749 xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
03750                int *res_nitems, ArrayBuildState **astate)
03751 {
03752     PgXmlErrorContext *xmlerrcxt;
03753     volatile xmlParserCtxtPtr ctxt = NULL;
03754     volatile xmlDocPtr doc = NULL;
03755     volatile xmlXPathContextPtr xpathctx = NULL;
03756     volatile xmlXPathCompExprPtr xpathcomp = NULL;
03757     volatile xmlXPathObjectPtr xpathobj = NULL;
03758     char       *datastr;
03759     int32       len;
03760     int32       xpath_len;
03761     xmlChar    *string;
03762     xmlChar    *xpath_expr;
03763     int         i;
03764     int         ndim;
03765     Datum      *ns_names_uris;
03766     bool       *ns_names_uris_nulls;
03767     int         ns_count;
03768 
03769     /*
03770      * Namespace mappings are passed as text[].  If an empty array is passed
03771      * (ndim = 0, "0-dimensional"), then there are no namespace mappings.
03772      * Else, a 2-dimensional array with length of the second axis being equal
03773      * to 2 should be passed, i.e., every subarray contains 2 elements, the
03774      * first element defining the name, the second one the URI.  Example:
03775      * ARRAY[ARRAY['myns', 'http://example.com'], ARRAY['myns2',
03776      * 'http://example2.com']].
03777      */
03778     ndim = namespaces ? ARR_NDIM(namespaces) : 0;
03779     if (ndim != 0)
03780     {
03781         int        *dims;
03782 
03783         dims = ARR_DIMS(namespaces);
03784 
03785         if (ndim != 2 || dims[1] != 2)
03786             ereport(ERROR,
03787                     (errcode(ERRCODE_DATA_EXCEPTION),
03788                      errmsg("invalid array for XML namespace mapping"),
03789                      errdetail("The array must be two-dimensional with length of the second axis equal to 2.")));
03790 
03791         Assert(ARR_ELEMTYPE(namespaces) == TEXTOID);
03792 
03793         deconstruct_array(namespaces, TEXTOID, -1, false, 'i',
03794                           &ns_names_uris, &ns_names_uris_nulls,
03795                           &ns_count);
03796 
03797         Assert((ns_count % 2) == 0);    /* checked above */
03798         ns_count /= 2;          /* count pairs only */
03799     }
03800     else
03801     {
03802         ns_names_uris = NULL;
03803         ns_names_uris_nulls = NULL;
03804         ns_count = 0;
03805     }
03806 
03807     datastr = VARDATA(data);
03808     len = VARSIZE(data) - VARHDRSZ;
03809     xpath_len = VARSIZE(xpath_expr_text) - VARHDRSZ;
03810     if (xpath_len == 0)
03811         ereport(ERROR,
03812                 (errcode(ERRCODE_DATA_EXCEPTION),
03813                  errmsg("empty XPath expression")));
03814 
03815     string = (xmlChar *) palloc((len + 1) * sizeof(xmlChar));
03816     memcpy(string, datastr, len);
03817     string[len] = '\0';
03818 
03819     xpath_expr = (xmlChar *) palloc((xpath_len + 1) * sizeof(xmlChar));
03820     memcpy(xpath_expr, VARDATA(xpath_expr_text), xpath_len);
03821     xpath_expr[xpath_len] = '\0';
03822 
03823     xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
03824 
03825     PG_TRY();
03826     {
03827         xmlInitParser();
03828 
03829         /*
03830          * redundant XML parsing (two parsings for the same value during one
03831          * command execution are possible)
03832          */
03833         ctxt = xmlNewParserCtxt();
03834         if (ctxt == NULL || xmlerrcxt->err_occurred)
03835             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
03836                         "could not allocate parser context");
03837         doc = xmlCtxtReadMemory(ctxt, (char *) string, len, NULL, NULL, 0);
03838         if (doc == NULL || xmlerrcxt->err_occurred)
03839             xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
03840                         "could not parse XML document");
03841         xpathctx = xmlXPathNewContext(doc);
03842         if (xpathctx == NULL || xmlerrcxt->err_occurred)
03843             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
03844                         "could not allocate XPath context");
03845         xpathctx->node = xmlDocGetRootElement(doc);
03846         if (xpathctx->node == NULL || xmlerrcxt->err_occurred)
03847             xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
03848                         "could not find root XML element");
03849 
03850         /* register namespaces, if any */
03851         if (ns_count > 0)
03852         {
03853             for (i = 0; i < ns_count; i++)
03854             {
03855                 char       *ns_name;
03856                 char       *ns_uri;
03857 
03858                 if (ns_names_uris_nulls[i * 2] ||
03859                     ns_names_uris_nulls[i * 2 + 1])
03860                     ereport(ERROR,
03861                             (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
03862                       errmsg("neither namespace name nor URI may be null")));
03863                 ns_name = TextDatumGetCString(ns_names_uris[i * 2]);
03864                 ns_uri = TextDatumGetCString(ns_names_uris[i * 2 + 1]);
03865                 if (xmlXPathRegisterNs(xpathctx,
03866                                        (xmlChar *) ns_name,
03867                                        (xmlChar *) ns_uri) != 0)
03868                     ereport(ERROR,      /* is this an internal error??? */
03869                             (errmsg("could not register XML namespace with name \"%s\" and URI \"%s\"",
03870                                     ns_name, ns_uri)));
03871             }
03872         }
03873 
03874         xpathcomp = xmlXPathCompile(xpath_expr);
03875         if (xpathcomp == NULL || xmlerrcxt->err_occurred)
03876             xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
03877                         "invalid XPath expression");
03878 
03879         /*
03880          * Version 2.6.27 introduces a function named
03881          * xmlXPathCompiledEvalToBoolean, which would be enough for xmlexists,
03882          * but we can derive the existence by whether any nodes are returned,
03883          * thereby preventing a library version upgrade and keeping the code
03884          * the same.
03885          */
03886         xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
03887         if (xpathobj == NULL || xmlerrcxt->err_occurred)
03888             xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
03889                         "could not create XPath object");
03890 
03891         /*
03892          * Extract the results as requested.
03893          */
03894         if (res_nitems != NULL)
03895             *res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate);
03896         else
03897             (void) xml_xpathobjtoxmlarray(xpathobj, astate);
03898     }
03899     PG_CATCH();
03900     {
03901         if (xpathobj)
03902             xmlXPathFreeObject(xpathobj);
03903         if (xpathcomp)
03904             xmlXPathFreeCompExpr(xpathcomp);
03905         if (xpathctx)
03906             xmlXPathFreeContext(xpathctx);
03907         if (doc)
03908             xmlFreeDoc(doc);
03909         if (ctxt)
03910             xmlFreeParserCtxt(ctxt);
03911 
03912         pg_xml_done(xmlerrcxt, true);
03913 
03914         PG_RE_THROW();
03915     }
03916     PG_END_TRY();
03917 
03918     xmlXPathFreeObject(xpathobj);
03919     xmlXPathFreeCompExpr(xpathcomp);
03920     xmlXPathFreeContext(xpathctx);
03921     xmlFreeDoc(doc);
03922     xmlFreeParserCtxt(ctxt);
03923 
03924     pg_xml_done(xmlerrcxt, false);
03925 }
03926 #endif   /* USE_LIBXML */
03927 
03928 /*
03929  * Evaluate XPath expression and return array of XML values.
03930  *
03931  * As we have no support of XQuery sequences yet, this function seems
03932  * to be the most useful one (array of XML functions plays a role of
03933  * some kind of substitution for XQuery sequences).
03934  */
03935 Datum
03936 xpath(PG_FUNCTION_ARGS)
03937 {
03938 #ifdef USE_LIBXML
03939     text       *xpath_expr_text = PG_GETARG_TEXT_P(0);
03940     xmltype    *data = PG_GETARG_XML_P(1);
03941     ArrayType  *namespaces = PG_GETARG_ARRAYTYPE_P(2);
03942     int         res_nitems;
03943     ArrayBuildState *astate;
03944 
03945     xpath_internal(xpath_expr_text, data, namespaces,
03946                    &res_nitems, &astate);
03947 
03948     if (res_nitems == 0)
03949         PG_RETURN_ARRAYTYPE_P(construct_empty_array(XMLOID));
03950     else
03951         PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
03952 #else
03953     NO_XML_SUPPORT();
03954     return 0;
03955 #endif
03956 }
03957 
03958 /*
03959  * Determines if the node specified by the supplied XPath exists
03960  * in a given XML document, returning a boolean.
03961  */
03962 Datum
03963 xmlexists(PG_FUNCTION_ARGS)
03964 {
03965 #ifdef USE_LIBXML
03966     text       *xpath_expr_text = PG_GETARG_TEXT_P(0);
03967     xmltype    *data = PG_GETARG_XML_P(1);
03968     int         res_nitems;
03969 
03970     xpath_internal(xpath_expr_text, data, NULL,
03971                    &res_nitems, NULL);
03972 
03973     PG_RETURN_BOOL(res_nitems > 0);
03974 #else
03975     NO_XML_SUPPORT();
03976     return 0;
03977 #endif
03978 }
03979 
03980 /*
03981  * Determines if the node specified by the supplied XPath exists
03982  * in a given XML document, returning a boolean. Differs from
03983  * xmlexists as it supports namespaces and is not defined in SQL/XML.
03984  */
03985 Datum
03986 xpath_exists(PG_FUNCTION_ARGS)
03987 {
03988 #ifdef USE_LIBXML
03989     text       *xpath_expr_text = PG_GETARG_TEXT_P(0);
03990     xmltype    *data = PG_GETARG_XML_P(1);
03991     ArrayType  *namespaces = PG_GETARG_ARRAYTYPE_P(2);
03992     int         res_nitems;
03993 
03994     xpath_internal(xpath_expr_text, data, namespaces,
03995                    &res_nitems, NULL);
03996 
03997     PG_RETURN_BOOL(res_nitems > 0);
03998 #else
03999     NO_XML_SUPPORT();
04000     return 0;
04001 #endif
04002 }
04003 
04004 /*
04005  * Functions for checking well-formed-ness
04006  */
04007 
04008 #ifdef USE_LIBXML
04009 static bool
04010 wellformed_xml(text *data, XmlOptionType xmloption_arg)
04011 {
04012     bool        result;
04013     volatile xmlDocPtr doc = NULL;
04014 
04015     /* We want to catch any exceptions and return false */
04016     PG_TRY();
04017     {
04018         doc = xml_parse(data, xmloption_arg, true, GetDatabaseEncoding());
04019         result = true;
04020     }
04021     PG_CATCH();
04022     {
04023         FlushErrorState();
04024         result = false;
04025     }
04026     PG_END_TRY();
04027 
04028     if (doc)
04029         xmlFreeDoc(doc);
04030 
04031     return result;
04032 }
04033 #endif
04034 
04035 Datum
04036 xml_is_well_formed(PG_FUNCTION_ARGS)
04037 {
04038 #ifdef USE_LIBXML
04039     text       *data = PG_GETARG_TEXT_P(0);
04040 
04041     PG_RETURN_BOOL(wellformed_xml(data, xmloption));
04042 #else
04043     NO_XML_SUPPORT();
04044     return 0;
04045 #endif   /* not USE_LIBXML */
04046 }
04047 
04048 Datum
04049 xml_is_well_formed_document(PG_FUNCTION_ARGS)
04050 {
04051 #ifdef USE_LIBXML
04052     text       *data = PG_GETARG_TEXT_P(0);
04053 
04054     PG_RETURN_BOOL(wellformed_xml(data, XMLOPTION_DOCUMENT));
04055 #else
04056     NO_XML_SUPPORT();
04057     return 0;
04058 #endif   /* not USE_LIBXML */
04059 }
04060 
04061 Datum
04062 xml_is_well_formed_content(PG_FUNCTION_ARGS)
04063 {
04064 #ifdef USE_LIBXML
04065     text       *data = PG_GETARG_TEXT_P(0);
04066 
04067     PG_RETURN_BOOL(wellformed_xml(data, XMLOPTION_CONTENT));
04068 #else
04069     NO_XML_SUPPORT();
04070     return 0;
04071 #endif   /* not USE_LIBXML */
04072 }