LCOV - code coverage report
Current view: top level - src/backend/utils/adt - xml.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 1406 1707 82.4 %
Date: 2023-12-11 14:10:49 Functions: 91 105 86.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * xml.c
       4             :  *    XML data type support.
       5             :  *
       6             :  *
       7             :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  * src/backend/utils/adt/xml.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : 
      15             : /*
      16             :  * Generally, XML type support is only available when libxml use was
      17             :  * configured during the build.  But even if that is not done, the
      18             :  * type and all the functions are available, but most of them will
      19             :  * fail.  For one thing, this avoids having to manage variant catalog
      20             :  * installations.  But it also has nice effects such as that you can
      21             :  * dump a database containing XML type data even if the server is not
      22             :  * linked with libxml.  Thus, make sure xml_out() works even if nothing
      23             :  * else does.
      24             :  */
      25             : 
      26             : /*
      27             :  * Notes on memory management:
      28             :  *
      29             :  * Sometimes libxml allocates global structures in the hope that it can reuse
      30             :  * them later on.  This makes it impractical to change the xmlMemSetup
      31             :  * functions on-the-fly; that is likely to lead to trying to pfree() chunks
      32             :  * allocated with malloc() or vice versa.  Since libxml might be used by
      33             :  * loadable modules, eg libperl, our only safe choices are to change the
      34             :  * functions at postmaster/backend launch or not at all.  Since we'd rather
      35             :  * not activate libxml in sessions that might never use it, the latter choice
      36             :  * is the preferred one.  However, for debugging purposes it can be awfully
      37             :  * handy to constrain libxml's allocations to be done in a specific palloc
      38             :  * context, where they're easy to track.  Therefore there is code here that
      39             :  * can be enabled in debug builds to redirect libxml's allocations into a
      40             :  * special context LibxmlContext.  It's not recommended to turn this on in
      41             :  * a production build because of the possibility of bad interactions with
      42             :  * external modules.
      43             :  */
      44             : /* #define USE_LIBXMLCONTEXT */
      45             : 
      46             : #include "postgres.h"
      47             : 
      48             : #ifdef USE_LIBXML
      49             : #include <libxml/chvalid.h>
      50             : #include <libxml/entities.h>
      51             : #include <libxml/parser.h>
      52             : #include <libxml/parserInternals.h>
      53             : #include <libxml/tree.h>
      54             : #include <libxml/uri.h>
      55             : #include <libxml/xmlerror.h>
      56             : #include <libxml/xmlsave.h>
      57             : #include <libxml/xmlversion.h>
      58             : #include <libxml/xmlwriter.h>
      59             : #include <libxml/xpath.h>
      60             : #include <libxml/xpathInternals.h>
      61             : 
      62             : /*
      63             :  * We used to check for xmlStructuredErrorContext via a configure test; but
      64             :  * that doesn't work on Windows, so instead use this grottier method of
      65             :  * testing the library version number.
      66             :  */
      67             : #if LIBXML_VERSION >= 20704
      68             : #define HAVE_XMLSTRUCTUREDERRORCONTEXT 1
      69             : #endif
      70             : #endif                          /* USE_LIBXML */
      71             : 
      72             : #include "access/htup_details.h"
      73             : #include "access/table.h"
      74             : #include "catalog/namespace.h"
      75             : #include "catalog/pg_class.h"
      76             : #include "catalog/pg_type.h"
      77             : #include "commands/dbcommands.h"
      78             : #include "executor/spi.h"
      79             : #include "executor/tablefunc.h"
      80             : #include "fmgr.h"
      81             : #include "lib/stringinfo.h"
      82             : #include "libpq/pqformat.h"
      83             : #include "mb/pg_wchar.h"
      84             : #include "miscadmin.h"
      85             : #include "nodes/execnodes.h"
      86             : #include "nodes/miscnodes.h"
      87             : #include "nodes/nodeFuncs.h"
      88             : #include "utils/array.h"
      89             : #include "utils/builtins.h"
      90             : #include "utils/date.h"
      91             : #include "utils/datetime.h"
      92             : #include "utils/lsyscache.h"
      93             : #include "utils/memutils.h"
      94             : #include "utils/rel.h"
      95             : #include "utils/syscache.h"
      96             : #include "utils/xml.h"
      97             : 
      98             : 
      99             : /* GUC variables */
     100             : int         xmlbinary = XMLBINARY_BASE64;
     101             : int         xmloption = XMLOPTION_CONTENT;
     102             : 
     103             : #ifdef USE_LIBXML
     104             : 
     105             : /* random number to identify PgXmlErrorContext */
     106             : #define ERRCXT_MAGIC    68275028
     107             : 
     108             : struct PgXmlErrorContext
     109             : {
     110             :     int         magic;
     111             :     /* strictness argument passed to pg_xml_init */
     112             :     PgXmlStrictness strictness;
     113             :     /* current error status and accumulated message, if any */
     114             :     bool        err_occurred;
     115             :     StringInfoData err_buf;
     116             :     /* previous libxml error handling state (saved by pg_xml_init) */
     117             :     xmlStructuredErrorFunc saved_errfunc;
     118             :     void       *saved_errcxt;
     119             :     /* previous libxml entity handler (saved by pg_xml_init) */
     120             :     xmlExternalEntityLoader saved_entityfunc;
     121             : };
     122             : 
     123             : static xmlParserInputPtr xmlPgEntityLoader(const char *URL, const char *ID,
     124             :                                            xmlParserCtxtPtr ctxt);
     125             : static void xml_errsave(Node *escontext, PgXmlErrorContext *errcxt,
     126             :                         int sqlcode, const char *msg);
     127             : static void xml_errorHandler(void *data, xmlErrorPtr error);
     128             : static int  errdetail_for_xml_code(int code);
     129             : static void chopStringInfoNewlines(StringInfo str);
     130             : static void appendStringInfoLineSeparator(StringInfo str);
     131             : 
     132             : #ifdef USE_LIBXMLCONTEXT
     133             : 
     134             : static MemoryContext LibxmlContext = NULL;
     135             : 
     136             : static void xml_memory_init(void);
     137             : static void *xml_palloc(size_t size);
     138             : static void *xml_repalloc(void *ptr, size_t size);
     139             : static void xml_pfree(void *ptr);
     140             : static char *xml_pstrdup(const char *string);
     141             : #endif                          /* USE_LIBXMLCONTEXT */
     142             : 
     143             : static xmlChar *xml_text2xmlChar(text *in);
     144             : static int  parse_xml_decl(const xmlChar *str, size_t *lenp,
     145             :                            xmlChar **version, xmlChar **encoding, int *standalone);
     146             : static bool print_xml_decl(StringInfo buf, const xmlChar *version,
     147             :                            pg_enc encoding, int standalone);
     148             : static bool xml_doctype_in_content(const xmlChar *str);
     149             : static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
     150             :                            bool preserve_whitespace, int encoding,
     151             :                            XmlOptionType *parsed_xmloptiontype,
     152             :                            xmlNodePtr *parsed_nodes,
     153             :                            Node *escontext);
     154             : static text *xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt);
     155             : static int  xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
     156             :                                    ArrayBuildState *astate,
     157             :                                    PgXmlErrorContext *xmlerrcxt);
     158             : static xmlChar *pg_xmlCharStrndup(const char *str, size_t len);
     159             : #endif                          /* USE_LIBXML */
     160             : 
     161             : static void xmldata_root_element_start(StringInfo result, const char *eltname,
     162             :                                        const char *xmlschema, const char *targetns,
     163             :                                        bool top_level);
     164             : static void xmldata_root_element_end(StringInfo result, const char *eltname);
     165             : static StringInfo query_to_xml_internal(const char *query, char *tablename,
     166             :                                         const char *xmlschema, bool nulls, bool tableforest,
     167             :                                         const char *targetns, bool top_level);
     168             : static const char *map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid,
     169             :                                               bool nulls, bool tableforest, const char *targetns);
     170             : static const char *map_sql_schema_to_xmlschema_types(Oid nspid,
     171             :                                                      List *relid_list, bool nulls,
     172             :                                                      bool tableforest, const char *targetns);
     173             : static const char *map_sql_catalog_to_xmlschema_types(List *nspid_list,
     174             :                                                       bool nulls, bool tableforest,
     175             :                                                       const char *targetns);
     176             : static const char *map_sql_type_to_xml_name(Oid typeoid, int typmod);
     177             : static const char *map_sql_typecoll_to_xmlschema_types(List *tupdesc_list);
     178             : static const char *map_sql_type_to_xmlschema_type(Oid typeoid, int typmod);
     179             : static void SPI_sql_row_to_xmlelement(uint64 rownum, StringInfo result,
     180             :                                       char *tablename, bool nulls, bool tableforest,
     181             :                                       const char *targetns, bool top_level);
     182             : 
     183             : /* XMLTABLE support */
     184             : #ifdef USE_LIBXML
     185             : /* random number to identify XmlTableContext */
     186             : #define XMLTABLE_CONTEXT_MAGIC  46922182
     187             : typedef struct XmlTableBuilderData
     188             : {
     189             :     int         magic;
     190             :     int         natts;
     191             :     long int    row_count;
     192             :     PgXmlErrorContext *xmlerrcxt;
     193             :     xmlParserCtxtPtr ctxt;
     194             :     xmlDocPtr   doc;
     195             :     xmlXPathContextPtr xpathcxt;
     196             :     xmlXPathCompExprPtr xpathcomp;
     197             :     xmlXPathObjectPtr xpathobj;
     198             :     xmlXPathCompExprPtr *xpathscomp;
     199             : } XmlTableBuilderData;
     200             : #endif
     201             : 
     202             : static void XmlTableInitOpaque(struct TableFuncScanState *state, int natts);
     203             : static void XmlTableSetDocument(struct TableFuncScanState *state, Datum value);
     204             : static void XmlTableSetNamespace(struct TableFuncScanState *state, const char *name,
     205             :                                  const char *uri);
     206             : static void XmlTableSetRowFilter(struct TableFuncScanState *state, const char *path);
     207             : static void XmlTableSetColumnFilter(struct TableFuncScanState *state,
     208             :                                     const char *path, int colnum);
     209             : static bool XmlTableFetchRow(struct TableFuncScanState *state);
     210             : static Datum XmlTableGetValue(struct TableFuncScanState *state, int colnum,
     211             :                               Oid typid, int32 typmod, bool *isnull);
     212             : static void XmlTableDestroyOpaque(struct TableFuncScanState *state);
     213             : 
     214             : const TableFuncRoutine XmlTableRoutine =
     215             : {
     216             :     XmlTableInitOpaque,
     217             :     XmlTableSetDocument,
     218             :     XmlTableSetNamespace,
     219             :     XmlTableSetRowFilter,
     220             :     XmlTableSetColumnFilter,
     221             :     XmlTableFetchRow,
     222             :     XmlTableGetValue,
     223             :     XmlTableDestroyOpaque
     224             : };
     225             : 
     226             : #define NO_XML_SUPPORT() \
     227             :     ereport(ERROR, \
     228             :             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
     229             :              errmsg("unsupported XML feature"), \
     230             :              errdetail("This functionality requires the server to be built with libxml support.")))
     231             : 
     232             : 
     233             : /* from SQL/XML:2008 section 4.9 */
     234             : #define NAMESPACE_XSD "http://www.w3.org/2001/XMLSchema"
     235             : #define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance"
     236             : #define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml"
     237             : 
     238             : 
     239             : #ifdef USE_LIBXML
     240             : 
     241             : static int
     242           0 : xmlChar_to_encoding(const xmlChar *encoding_name)
     243             : {
     244           0 :     int         encoding = pg_char_to_encoding((const char *) encoding_name);
     245             : 
     246           0 :     if (encoding < 0)
     247           0 :         ereport(ERROR,
     248             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     249             :                  errmsg("invalid encoding name \"%s\"",
     250             :                         (const char *) encoding_name)));
     251           0 :     return encoding;
     252             : }
     253             : #endif
     254             : 
     255             : 
     256             : /*
     257             :  * xml_in uses a plain C string to VARDATA conversion, so for the time being
     258             :  * we use the conversion function for the text datatype.
     259             :  *
     260             :  * This is only acceptable so long as xmltype and text use the same
     261             :  * representation.
     262             :  */
     263             : Datum
     264         828 : xml_in(PG_FUNCTION_ARGS)
     265             : {
     266             : #ifdef USE_LIBXML
     267         828 :     char       *s = PG_GETARG_CSTRING(0);
     268             :     xmltype    *vardata;
     269             :     xmlDocPtr   doc;
     270             : 
     271             :     /* Build the result object. */
     272         828 :     vardata = (xmltype *) cstring_to_text(s);
     273             : 
     274             :     /*
     275             :      * Parse the data to check if it is well-formed XML data.
     276             :      *
     277             :      * Note: we don't need to worry about whether a soft error is detected.
     278             :      */
     279         828 :     doc = xml_parse(vardata, xmloption, true, GetDatabaseEncoding(),
     280         828 :                     NULL, NULL, fcinfo->context);
     281         780 :     if (doc != NULL)
     282         768 :         xmlFreeDoc(doc);
     283             : 
     284         780 :     PG_RETURN_XML_P(vardata);
     285             : #else
     286             :     NO_XML_SUPPORT();
     287             :     return 0;
     288             : #endif
     289             : }
     290             : 
     291             : 
     292             : #define PG_XML_DEFAULT_VERSION "1.0"
     293             : 
     294             : 
     295             : /*
     296             :  * xml_out_internal uses a plain VARDATA to C string conversion, so for the
     297             :  * time being we use the conversion function for the text datatype.
     298             :  *
     299             :  * This is only acceptable so long as xmltype and text use the same
     300             :  * representation.
     301             :  */
     302             : static char *
     303       22862 : xml_out_internal(xmltype *x, pg_enc target_encoding)
     304             : {
     305       22862 :     char       *str = text_to_cstring((text *) x);
     306             : 
     307             : #ifdef USE_LIBXML
     308       22862 :     size_t      len = strlen(str);
     309             :     xmlChar    *version;
     310             :     int         standalone;
     311             :     int         res_code;
     312             : 
     313       22862 :     if ((res_code = parse_xml_decl((xmlChar *) str,
     314             :                                    &len, &version, NULL, &standalone)) == 0)
     315             :     {
     316             :         StringInfoData buf;
     317             : 
     318       22862 :         initStringInfo(&buf);
     319             : 
     320       22862 :         if (!print_xml_decl(&buf, version, target_encoding, standalone))
     321             :         {
     322             :             /*
     323             :              * If we are not going to produce an XML declaration, eat a single
     324             :              * newline in the original string to prevent empty first lines in
     325             :              * the output.
     326             :              */
     327       22814 :             if (*(str + len) == '\n')
     328           6 :                 len += 1;
     329             :         }
     330       22862 :         appendStringInfoString(&buf, str + len);
     331             : 
     332       22862 :         pfree(str);
     333             : 
     334       22862 :         return buf.data;
     335             :     }
     336             : 
     337           0 :     ereport(WARNING,
     338             :             errcode(ERRCODE_INTERNAL_ERROR),
     339             :             errmsg_internal("could not parse XML declaration in stored value"),
     340             :             errdetail_for_xml_code(res_code));
     341             : #endif
     342           0 :     return str;
     343             : }
     344             : 
     345             : 
     346             : Datum
     347       22598 : xml_out(PG_FUNCTION_ARGS)
     348             : {
     349       22598 :     xmltype    *x = PG_GETARG_XML_P(0);
     350             : 
     351             :     /*
     352             :      * xml_out removes the encoding property in all cases.  This is because we
     353             :      * cannot control from here whether the datum will be converted to a
     354             :      * different client encoding, so we'd do more harm than good by including
     355             :      * it.
     356             :      */
     357       22598 :     PG_RETURN_CSTRING(xml_out_internal(x, 0));
     358             : }
     359             : 
     360             : 
     361             : Datum
     362           0 : xml_recv(PG_FUNCTION_ARGS)
     363             : {
     364             : #ifdef USE_LIBXML
     365           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     366             :     xmltype    *result;
     367             :     char       *str;
     368             :     char       *newstr;
     369             :     int         nbytes;
     370             :     xmlDocPtr   doc;
     371           0 :     xmlChar    *encodingStr = NULL;
     372             :     int         encoding;
     373             : 
     374             :     /*
     375             :      * Read the data in raw format. We don't know yet what the encoding is, as
     376             :      * that information is embedded in the xml declaration; so we have to
     377             :      * parse that before converting to server encoding.
     378             :      */
     379           0 :     nbytes = buf->len - buf->cursor;
     380           0 :     str = (char *) pq_getmsgbytes(buf, nbytes);
     381             : 
     382             :     /*
     383             :      * We need a null-terminated string to pass to parse_xml_decl().  Rather
     384             :      * than make a separate copy, make the temporary result one byte bigger
     385             :      * than it needs to be.
     386             :      */
     387           0 :     result = palloc(nbytes + 1 + VARHDRSZ);
     388           0 :     SET_VARSIZE(result, nbytes + VARHDRSZ);
     389           0 :     memcpy(VARDATA(result), str, nbytes);
     390           0 :     str = VARDATA(result);
     391           0 :     str[nbytes] = '\0';
     392             : 
     393           0 :     parse_xml_decl((const xmlChar *) str, NULL, NULL, &encodingStr, NULL);
     394             : 
     395             :     /*
     396             :      * If encoding wasn't explicitly specified in the XML header, treat it as
     397             :      * UTF-8, as that's the default in XML. This is different from xml_in(),
     398             :      * where the input has to go through the normal client to server encoding
     399             :      * conversion.
     400             :      */
     401           0 :     encoding = encodingStr ? xmlChar_to_encoding(encodingStr) : PG_UTF8;
     402             : 
     403             :     /*
     404             :      * Parse the data to check if it is well-formed XML data.  Assume that
     405             :      * xml_parse will throw ERROR if not.
     406             :      */
     407           0 :     doc = xml_parse(result, xmloption, true, encoding, NULL, NULL, NULL);
     408           0 :     xmlFreeDoc(doc);
     409             : 
     410             :     /* Now that we know what we're dealing with, convert to server encoding */
     411           0 :     newstr = pg_any_to_server(str, nbytes, encoding);
     412             : 
     413           0 :     if (newstr != str)
     414             :     {
     415           0 :         pfree(result);
     416           0 :         result = (xmltype *) cstring_to_text(newstr);
     417           0 :         pfree(newstr);
     418             :     }
     419             : 
     420           0 :     PG_RETURN_XML_P(result);
     421             : #else
     422             :     NO_XML_SUPPORT();
     423             :     return 0;
     424             : #endif
     425             : }
     426             : 
     427             : 
     428             : Datum
     429           0 : xml_send(PG_FUNCTION_ARGS)
     430             : {
     431           0 :     xmltype    *x = PG_GETARG_XML_P(0);
     432             :     char       *outval;
     433             :     StringInfoData buf;
     434             : 
     435             :     /*
     436             :      * xml_out_internal doesn't convert the encoding, it just prints the right
     437             :      * declaration. pq_sendtext will do the conversion.
     438             :      */
     439           0 :     outval = xml_out_internal(x, pg_get_client_encoding());
     440             : 
     441           0 :     pq_begintypsend(&buf);
     442           0 :     pq_sendtext(&buf, outval, strlen(outval));
     443           0 :     pfree(outval);
     444           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     445             : }
     446             : 
     447             : 
     448             : #ifdef USE_LIBXML
     449             : static void
     450         132 : appendStringInfoText(StringInfo str, const text *t)
     451             : {
     452         132 :     appendBinaryStringInfo(str, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
     453         132 : }
     454             : #endif
     455             : 
     456             : 
     457             : static xmltype *
     458       21842 : stringinfo_to_xmltype(StringInfo buf)
     459             : {
     460       21842 :     return (xmltype *) cstring_to_text_with_len(buf->data, buf->len);
     461             : }
     462             : 
     463             : 
     464             : static xmltype *
     465          78 : cstring_to_xmltype(const char *string)
     466             : {
     467          78 :     return (xmltype *) cstring_to_text(string);
     468             : }
     469             : 
     470             : 
     471             : #ifdef USE_LIBXML
     472             : static xmltype *
     473       21964 : xmlBuffer_to_xmltype(xmlBufferPtr buf)
     474             : {
     475       21964 :     return (xmltype *) cstring_to_text_with_len((const char *) xmlBufferContent(buf),
     476             :                                                 xmlBufferLength(buf));
     477             : }
     478             : #endif
     479             : 
     480             : 
     481             : Datum
     482          42 : xmlcomment(PG_FUNCTION_ARGS)
     483             : {
     484             : #ifdef USE_LIBXML
     485          42 :     text       *arg = PG_GETARG_TEXT_PP(0);
     486          42 :     char       *argdata = VARDATA_ANY(arg);
     487          42 :     int         len = VARSIZE_ANY_EXHDR(arg);
     488             :     StringInfoData buf;
     489             :     int         i;
     490             : 
     491             :     /* check for "--" in string or "-" at the end */
     492         180 :     for (i = 1; i < len; i++)
     493             :     {
     494         144 :         if (argdata[i] == '-' && argdata[i - 1] == '-')
     495           6 :             ereport(ERROR,
     496             :                     (errcode(ERRCODE_INVALID_XML_COMMENT),
     497             :                      errmsg("invalid XML comment")));
     498             :     }
     499          36 :     if (len > 0 && argdata[len - 1] == '-')
     500           6 :         ereport(ERROR,
     501             :                 (errcode(ERRCODE_INVALID_XML_COMMENT),
     502             :                  errmsg("invalid XML comment")));
     503             : 
     504          30 :     initStringInfo(&buf);
     505          30 :     appendStringInfoString(&buf, "<!--");
     506          30 :     appendStringInfoText(&buf, arg);
     507          30 :     appendStringInfoString(&buf, "-->");
     508             : 
     509          30 :     PG_RETURN_XML_P(stringinfo_to_xmltype(&buf));
     510             : #else
     511             :     NO_XML_SUPPORT();
     512             :     return 0;
     513             : #endif
     514             : }
     515             : 
     516             : 
     517             : Datum
     518          30 : xmltext(PG_FUNCTION_ARGS)
     519             : {
     520             : #ifdef USE_LIBXML
     521          30 :     text       *arg = PG_GETARG_TEXT_PP(0);
     522             :     text       *result;
     523          30 :     xmlChar    *xmlbuf = NULL;
     524             : 
     525          30 :     xmlbuf = xmlEncodeSpecialChars(NULL, xml_text2xmlChar(arg));
     526             : 
     527             :     Assert(xmlbuf);
     528             : 
     529          30 :     result = cstring_to_text_with_len((const char *) xmlbuf, xmlStrlen(xmlbuf));
     530          30 :     xmlFree(xmlbuf);
     531          30 :     PG_RETURN_XML_P(result);
     532             : #else
     533             :     NO_XML_SUPPORT();
     534             :     return 0;
     535             : #endif                          /* not USE_LIBXML */
     536             : }
     537             : 
     538             : 
     539             : /*
     540             :  * TODO: xmlconcat needs to merge the notations and unparsed entities
     541             :  * of the argument values.  Not very important in practice, though.
     542             :  */
     543             : xmltype *
     544       21592 : xmlconcat(List *args)
     545             : {
     546             : #ifdef USE_LIBXML
     547       21592 :     int         global_standalone = 1;
     548       21592 :     xmlChar    *global_version = NULL;
     549       21592 :     bool        global_version_no_value = false;
     550             :     StringInfoData buf;
     551             :     ListCell   *v;
     552             : 
     553       21592 :     initStringInfo(&buf);
     554       64782 :     foreach(v, args)
     555             :     {
     556       43190 :         xmltype    *x = DatumGetXmlP(PointerGetDatum(lfirst(v)));
     557             :         size_t      len;
     558             :         xmlChar    *version;
     559             :         int         standalone;
     560             :         char       *str;
     561             : 
     562       43190 :         len = VARSIZE(x) - VARHDRSZ;
     563       43190 :         str = text_to_cstring((text *) x);
     564             : 
     565       43190 :         parse_xml_decl((xmlChar *) str, &len, &version, NULL, &standalone);
     566             : 
     567       43190 :         if (standalone == 0 && global_standalone == 1)
     568           0 :             global_standalone = 0;
     569       43190 :         if (standalone < 0)
     570       43178 :             global_standalone = -1;
     571             : 
     572       43190 :         if (!version)
     573       43172 :             global_version_no_value = true;
     574          18 :         else if (!global_version)
     575          12 :             global_version = version;
     576           6 :         else if (xmlStrcmp(version, global_version) != 0)
     577           0 :             global_version_no_value = true;
     578             : 
     579       43190 :         appendStringInfoString(&buf, str + len);
     580       43190 :         pfree(str);
     581             :     }
     582             : 
     583       21592 :     if (!global_version_no_value || global_standalone >= 0)
     584             :     {
     585             :         StringInfoData buf2;
     586             : 
     587           6 :         initStringInfo(&buf2);
     588             : 
     589           6 :         print_xml_decl(&buf2,
     590           6 :                        (!global_version_no_value) ? global_version : NULL,
     591             :                        0,
     592             :                        global_standalone);
     593             : 
     594           6 :         appendBinaryStringInfo(&buf2, buf.data, buf.len);
     595           6 :         buf = buf2;
     596             :     }
     597             : 
     598       21592 :     return stringinfo_to_xmltype(&buf);
     599             : #else
     600             :     NO_XML_SUPPORT();
     601             :     return NULL;
     602             : #endif
     603             : }
     604             : 
     605             : 
     606             : /*
     607             :  * XMLAGG support
     608             :  */
     609             : Datum
     610       21568 : xmlconcat2(PG_FUNCTION_ARGS)
     611             : {
     612       21568 :     if (PG_ARGISNULL(0))
     613             :     {
     614          18 :         if (PG_ARGISNULL(1))
     615           0 :             PG_RETURN_NULL();
     616             :         else
     617          18 :             PG_RETURN_XML_P(PG_GETARG_XML_P(1));
     618             :     }
     619       21550 :     else if (PG_ARGISNULL(1))
     620           0 :         PG_RETURN_XML_P(PG_GETARG_XML_P(0));
     621             :     else
     622       21550 :         PG_RETURN_XML_P(xmlconcat(list_make2(PG_GETARG_XML_P(0),
     623             :                                              PG_GETARG_XML_P(1))));
     624             : }
     625             : 
     626             : 
     627             : Datum
     628           2 : texttoxml(PG_FUNCTION_ARGS)
     629             : {
     630           2 :     text       *data = PG_GETARG_TEXT_PP(0);
     631             : 
     632           2 :     PG_RETURN_XML_P(xmlparse(data, xmloption, true));
     633             : }
     634             : 
     635             : 
     636             : Datum
     637           0 : xmltotext(PG_FUNCTION_ARGS)
     638             : {
     639           0 :     xmltype    *data = PG_GETARG_XML_P(0);
     640             : 
     641             :     /* It's actually binary compatible. */
     642           0 :     PG_RETURN_TEXT_P((text *) data);
     643             : }
     644             : 
     645             : 
     646             : text *
     647         168 : xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
     648             : {
     649             : #ifdef USE_LIBXML
     650             :     text       *volatile result;
     651             :     xmlDocPtr   doc;
     652             :     XmlOptionType parsed_xmloptiontype;
     653             :     xmlNodePtr  content_nodes;
     654         168 :     volatile xmlBufferPtr buf = NULL;
     655         168 :     volatile xmlSaveCtxtPtr ctxt = NULL;
     656         168 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
     657             :     PgXmlErrorContext *xmlerrcxt;
     658             : #endif
     659             : 
     660         168 :     if (xmloption_arg != XMLOPTION_DOCUMENT && !indent)
     661             :     {
     662             :         /*
     663             :          * We don't actually need to do anything, so just return the
     664             :          * binary-compatible input.  For backwards-compatibility reasons,
     665             :          * allow such cases to succeed even without USE_LIBXML.
     666             :          */
     667          36 :         return (text *) data;
     668             :     }
     669             : 
     670             : #ifdef USE_LIBXML
     671             :     /* Parse the input according to the xmloption */
     672         132 :     doc = xml_parse(data, xmloption_arg, true, GetDatabaseEncoding(),
     673             :                     &parsed_xmloptiontype, &content_nodes,
     674             :                     (Node *) &escontext);
     675         132 :     if (doc == NULL || escontext.error_occurred)
     676             :     {
     677          30 :         if (doc)
     678           0 :             xmlFreeDoc(doc);
     679             :         /* A soft error must be failure to conform to XMLOPTION_DOCUMENT */
     680          30 :         ereport(ERROR,
     681             :                 (errcode(ERRCODE_NOT_AN_XML_DOCUMENT),
     682             :                  errmsg("not an XML document")));
     683             :     }
     684             : 
     685             :     /* If we weren't asked to indent, we're done. */
     686         102 :     if (!indent)
     687             :     {
     688          18 :         xmlFreeDoc(doc);
     689          18 :         return (text *) data;
     690             :     }
     691             : 
     692             :     /* Otherwise, we gotta spin up some error handling. */
     693          84 :     xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
     694             : 
     695          84 :     PG_TRY();
     696             :     {
     697          84 :         size_t      decl_len = 0;
     698             : 
     699             :         /* The serialized data will go into this buffer. */
     700          84 :         buf = xmlBufferCreate();
     701             : 
     702          84 :         if (buf == NULL || xmlerrcxt->err_occurred)
     703           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
     704             :                         "could not allocate xmlBuffer");
     705             : 
     706             :         /* Detect whether there's an XML declaration */
     707          84 :         parse_xml_decl(xml_text2xmlChar(data), &decl_len, NULL, NULL, NULL);
     708             : 
     709             :         /*
     710             :          * Emit declaration only if the input had one.  Note: some versions of
     711             :          * xmlSaveToBuffer leak memory if a non-null encoding argument is
     712             :          * passed, so don't do that.  We don't want any encoding conversion
     713             :          * anyway.
     714             :          */
     715          84 :         if (decl_len == 0)
     716          72 :             ctxt = xmlSaveToBuffer(buf, NULL,
     717             :                                    XML_SAVE_NO_DECL | XML_SAVE_FORMAT);
     718             :         else
     719          12 :             ctxt = xmlSaveToBuffer(buf, NULL,
     720             :                                    XML_SAVE_FORMAT);
     721             : 
     722          84 :         if (ctxt == NULL || xmlerrcxt->err_occurred)
     723           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
     724             :                         "could not allocate xmlSaveCtxt");
     725             : 
     726          84 :         if (parsed_xmloptiontype == XMLOPTION_DOCUMENT)
     727             :         {
     728             :             /* If it's a document, saving is easy. */
     729          36 :             if (xmlSaveDoc(ctxt, doc) == -1 || xmlerrcxt->err_occurred)
     730           0 :                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
     731             :                             "could not save document to xmlBuffer");
     732             :         }
     733          48 :         else if (content_nodes != NULL)
     734             :         {
     735             :             /*
     736             :              * Deal with the case where we have non-singly-rooted XML.
     737             :              * libxml's dump functions don't work well for that without help.
     738             :              * We build a fake root node that serves as a container for the
     739             :              * content nodes, and then iterate over the nodes.
     740             :              */
     741             :             xmlNodePtr  root;
     742             :             xmlNodePtr  newline;
     743             : 
     744          42 :             root = xmlNewNode(NULL, (const xmlChar *) "content-root");
     745          42 :             if (root == NULL || xmlerrcxt->err_occurred)
     746           0 :                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
     747             :                             "could not allocate xml node");
     748             : 
     749             :             /* This attaches root to doc, so we need not free it separately. */
     750          42 :             xmlDocSetRootElement(doc, root);
     751          42 :             xmlAddChild(root, content_nodes);
     752             : 
     753             :             /*
     754             :              * We use this node to insert newlines in the dump.  Note: in at
     755             :              * least some libxml versions, xmlNewDocText would not attach the
     756             :              * node to the document even if we passed it.  Therefore, manage
     757             :              * freeing of this node manually, and pass NULL here to make sure
     758             :              * there's not a dangling link.
     759             :              */
     760          42 :             newline = xmlNewDocText(NULL, (const xmlChar *) "\n");
     761          42 :             if (newline == NULL || xmlerrcxt->err_occurred)
     762           0 :                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
     763             :                             "could not allocate xml node");
     764             : 
     765         108 :             for (xmlNodePtr node = root->children; node; node = node->next)
     766             :             {
     767             :                 /* insert newlines between nodes */
     768          66 :                 if (node->type != XML_TEXT_NODE && node->prev != NULL)
     769             :                 {
     770          18 :                     if (xmlSaveTree(ctxt, newline) == -1 || xmlerrcxt->err_occurred)
     771             :                     {
     772           0 :                         xmlFreeNode(newline);
     773           0 :                         xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
     774             :                                     "could not save newline to xmlBuffer");
     775             :                     }
     776             :                 }
     777             : 
     778          66 :                 if (xmlSaveTree(ctxt, node) == -1 || xmlerrcxt->err_occurred)
     779             :                 {
     780           0 :                     xmlFreeNode(newline);
     781           0 :                     xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
     782             :                                 "could not save content to xmlBuffer");
     783             :                 }
     784             :             }
     785             : 
     786          42 :             xmlFreeNode(newline);
     787             :         }
     788             : 
     789          84 :         if (xmlSaveClose(ctxt) == -1 || xmlerrcxt->err_occurred)
     790             :         {
     791           0 :             ctxt = NULL;        /* don't try to close it again */
     792           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
     793             :                         "could not close xmlSaveCtxtPtr");
     794             :         }
     795             : 
     796          84 :         result = (text *) xmlBuffer_to_xmltype(buf);
     797             :     }
     798           0 :     PG_CATCH();
     799             :     {
     800           0 :         if (ctxt)
     801           0 :             xmlSaveClose(ctxt);
     802           0 :         if (buf)
     803           0 :             xmlBufferFree(buf);
     804           0 :         if (doc)
     805           0 :             xmlFreeDoc(doc);
     806             : 
     807           0 :         pg_xml_done(xmlerrcxt, true);
     808             : 
     809           0 :         PG_RE_THROW();
     810             :     }
     811          84 :     PG_END_TRY();
     812             : 
     813          84 :     xmlBufferFree(buf);
     814          84 :     xmlFreeDoc(doc);
     815             : 
     816          84 :     pg_xml_done(xmlerrcxt, false);
     817             : 
     818          84 :     return result;
     819             : #else
     820             :     NO_XML_SUPPORT();
     821             :     return NULL;
     822             : #endif
     823             : }
     824             : 
     825             : 
     826             : xmltype *
     827       21724 : xmlelement(XmlExpr *xexpr,
     828             :            Datum *named_argvalue, bool *named_argnull,
     829             :            Datum *argvalue, bool *argnull)
     830             : {
     831             : #ifdef USE_LIBXML
     832             :     xmltype    *result;
     833             :     List       *named_arg_strings;
     834             :     List       *arg_strings;
     835             :     int         i;
     836             :     ListCell   *arg;
     837             :     ListCell   *narg;
     838             :     PgXmlErrorContext *xmlerrcxt;
     839       21724 :     volatile xmlBufferPtr buf = NULL;
     840       21724 :     volatile xmlTextWriterPtr writer = NULL;
     841             : 
     842             :     /*
     843             :      * All arguments are already evaluated, and their values are passed in the
     844             :      * named_argvalue/named_argnull or argvalue/argnull arrays.  This avoids
     845             :      * issues if one of the arguments involves a call to some other function
     846             :      * or subsystem that wants to use libxml on its own terms.  We examine the
     847             :      * original XmlExpr to identify the numbers and types of the arguments.
     848             :      */
     849       21724 :     named_arg_strings = NIL;
     850       21724 :     i = 0;
     851       21772 :     foreach(arg, xexpr->named_args)
     852             :     {
     853          54 :         Expr       *e = (Expr *) lfirst(arg);
     854             :         char       *str;
     855             : 
     856          54 :         if (named_argnull[i])
     857           0 :             str = NULL;
     858             :         else
     859          54 :             str = map_sql_value_to_xml_value(named_argvalue[i],
     860             :                                              exprType((Node *) e),
     861             :                                              false);
     862          48 :         named_arg_strings = lappend(named_arg_strings, str);
     863          48 :         i++;
     864             :     }
     865             : 
     866       21718 :     arg_strings = NIL;
     867       21718 :     i = 0;
     868       43412 :     foreach(arg, xexpr->args)
     869             :     {
     870       21694 :         Expr       *e = (Expr *) lfirst(arg);
     871             :         char       *str;
     872             : 
     873             :         /* here we can just forget NULL elements immediately */
     874       21694 :         if (!argnull[i])
     875             :         {
     876       21694 :             str = map_sql_value_to_xml_value(argvalue[i],
     877             :                                              exprType((Node *) e),
     878             :                                              true);
     879       21694 :             arg_strings = lappend(arg_strings, str);
     880             :         }
     881       21694 :         i++;
     882             :     }
     883             : 
     884       21718 :     xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
     885             : 
     886       21718 :     PG_TRY();
     887             :     {
     888       21718 :         buf = xmlBufferCreate();
     889       21718 :         if (buf == NULL || xmlerrcxt->err_occurred)
     890           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
     891             :                         "could not allocate xmlBuffer");
     892       21718 :         writer = xmlNewTextWriterMemory(buf, 0);
     893       21718 :         if (writer == NULL || xmlerrcxt->err_occurred)
     894           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
     895             :                         "could not allocate xmlTextWriter");
     896             : 
     897       21718 :         xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
     898             : 
     899       21766 :         forboth(arg, named_arg_strings, narg, xexpr->arg_names)
     900             :         {
     901          48 :             char       *str = (char *) lfirst(arg);
     902          48 :             char       *argname = strVal(lfirst(narg));
     903             : 
     904          48 :             if (str)
     905          48 :                 xmlTextWriterWriteAttribute(writer,
     906             :                                             (xmlChar *) argname,
     907             :                                             (xmlChar *) str);
     908             :         }
     909             : 
     910       43412 :         foreach(arg, arg_strings)
     911             :         {
     912       21694 :             char       *str = (char *) lfirst(arg);
     913             : 
     914       21694 :             xmlTextWriterWriteRaw(writer, (xmlChar *) str);
     915             :         }
     916             : 
     917       21718 :         xmlTextWriterEndElement(writer);
     918             : 
     919             :         /* we MUST do this now to flush data out to the buffer ... */
     920       21718 :         xmlFreeTextWriter(writer);
     921       21718 :         writer = NULL;
     922             : 
     923       21718 :         result = xmlBuffer_to_xmltype(buf);
     924             :     }
     925           0 :     PG_CATCH();
     926             :     {
     927           0 :         if (writer)
     928           0 :             xmlFreeTextWriter(writer);
     929           0 :         if (buf)
     930           0 :             xmlBufferFree(buf);
     931             : 
     932           0 :         pg_xml_done(xmlerrcxt, true);
     933             : 
     934           0 :         PG_RE_THROW();
     935             :     }
     936       21718 :     PG_END_TRY();
     937             : 
     938       21718 :     xmlBufferFree(buf);
     939             : 
     940       21718 :     pg_xml_done(xmlerrcxt, false);
     941             : 
     942       21718 :     return result;
     943             : #else
     944             :     NO_XML_SUPPORT();
     945             :     return NULL;
     946             : #endif
     947             : }
     948             : 
     949             : 
     950             : xmltype *
     951         134 : xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace)
     952             : {
     953             : #ifdef USE_LIBXML
     954             :     xmlDocPtr   doc;
     955             : 
     956         134 :     doc = xml_parse(data, xmloption_arg, preserve_whitespace,
     957             :                     GetDatabaseEncoding(), NULL, NULL, NULL);
     958          86 :     xmlFreeDoc(doc);
     959             : 
     960          86 :     return (xmltype *) data;
     961             : #else
     962             :     NO_XML_SUPPORT();
     963             :     return NULL;
     964             : #endif
     965             : }
     966             : 
     967             : 
     968             : xmltype *
     969          72 : xmlpi(const char *target, text *arg, bool arg_is_null, bool *result_is_null)
     970             : {
     971             : #ifdef USE_LIBXML
     972             :     xmltype    *result;
     973             :     StringInfoData buf;
     974             : 
     975          72 :     if (pg_strcasecmp(target, "xml") == 0)
     976          12 :         ereport(ERROR,
     977             :                 (errcode(ERRCODE_SYNTAX_ERROR), /* really */
     978             :                  errmsg("invalid XML processing instruction"),
     979             :                  errdetail("XML processing instruction target name cannot be \"%s\".", target)));
     980             : 
     981             :     /*
     982             :      * Following the SQL standard, the null check comes after the syntax check
     983             :      * above.
     984             :      */
     985          60 :     *result_is_null = arg_is_null;
     986          60 :     if (*result_is_null)
     987          12 :         return NULL;
     988             : 
     989          48 :     initStringInfo(&buf);
     990             : 
     991          48 :     appendStringInfo(&buf, "<?%s", target);
     992             : 
     993          48 :     if (arg != NULL)
     994             :     {
     995             :         char       *string;
     996             : 
     997          24 :         string = text_to_cstring(arg);
     998          24 :         if (strstr(string, "?>") != NULL)
     999           6 :             ereport(ERROR,
    1000             :                     (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
    1001             :                      errmsg("invalid XML processing instruction"),
    1002             :                      errdetail("XML processing instruction cannot contain \"?>\".")));
    1003             : 
    1004          18 :         appendStringInfoChar(&buf, ' ');
    1005          18 :         appendStringInfoString(&buf, string + strspn(string, " "));
    1006          18 :         pfree(string);
    1007             :     }
    1008          42 :     appendStringInfoString(&buf, "?>");
    1009             : 
    1010          42 :     result = stringinfo_to_xmltype(&buf);
    1011          42 :     pfree(buf.data);
    1012          42 :     return result;
    1013             : #else
    1014             :     NO_XML_SUPPORT();
    1015             :     return NULL;
    1016             : #endif
    1017             : }
    1018             : 
    1019             : 
    1020             : xmltype *
    1021          60 : xmlroot(xmltype *data, text *version, int standalone)
    1022             : {
    1023             : #ifdef USE_LIBXML
    1024             :     char       *str;
    1025             :     size_t      len;
    1026             :     xmlChar    *orig_version;
    1027             :     int         orig_standalone;
    1028             :     StringInfoData buf;
    1029             : 
    1030          60 :     len = VARSIZE(data) - VARHDRSZ;
    1031          60 :     str = text_to_cstring((text *) data);
    1032             : 
    1033          60 :     parse_xml_decl((xmlChar *) str, &len, &orig_version, NULL, &orig_standalone);
    1034             : 
    1035          60 :     if (version)
    1036          24 :         orig_version = xml_text2xmlChar(version);
    1037             :     else
    1038          36 :         orig_version = NULL;
    1039             : 
    1040          60 :     switch (standalone)
    1041             :     {
    1042          18 :         case XML_STANDALONE_YES:
    1043          18 :             orig_standalone = 1;
    1044          18 :             break;
    1045          12 :         case XML_STANDALONE_NO:
    1046          12 :             orig_standalone = 0;
    1047          12 :             break;
    1048          12 :         case XML_STANDALONE_NO_VALUE:
    1049          12 :             orig_standalone = -1;
    1050          12 :             break;
    1051          18 :         case XML_STANDALONE_OMITTED:
    1052             :             /* leave original value */
    1053          18 :             break;
    1054             :     }
    1055             : 
    1056          60 :     initStringInfo(&buf);
    1057          60 :     print_xml_decl(&buf, orig_version, 0, orig_standalone);
    1058          60 :     appendStringInfoString(&buf, str + len);
    1059             : 
    1060          60 :     return stringinfo_to_xmltype(&buf);
    1061             : #else
    1062             :     NO_XML_SUPPORT();
    1063             :     return NULL;
    1064             : #endif
    1065             : }
    1066             : 
    1067             : 
    1068             : /*
    1069             :  * Validate document (given as string) against DTD (given as external link)
    1070             :  *
    1071             :  * This has been removed because it is a security hole: unprivileged users
    1072             :  * should not be able to use Postgres to fetch arbitrary external files,
    1073             :  * which unfortunately is exactly what libxml is willing to do with the DTD
    1074             :  * parameter.
    1075             :  */
    1076             : Datum
    1077           0 : xmlvalidate(PG_FUNCTION_ARGS)
    1078             : {
    1079           0 :     ereport(ERROR,
    1080             :             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1081             :              errmsg("xmlvalidate is not implemented")));
    1082             :     return 0;
    1083             : }
    1084             : 
    1085             : 
    1086             : bool
    1087          24 : xml_is_document(xmltype *arg)
    1088             : {
    1089             : #ifdef USE_LIBXML
    1090             :     xmlDocPtr   doc;
    1091          24 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
    1092             : 
    1093             :     /*
    1094             :      * We'll report "true" if no soft error is reported by xml_parse().
    1095             :      */
    1096          24 :     doc = xml_parse((text *) arg, XMLOPTION_DOCUMENT, true,
    1097             :                     GetDatabaseEncoding(), NULL, NULL, (Node *) &escontext);
    1098          24 :     if (doc)
    1099          12 :         xmlFreeDoc(doc);
    1100             : 
    1101          24 :     return !escontext.error_occurred;
    1102             : #else                           /* not USE_LIBXML */
    1103             :     NO_XML_SUPPORT();
    1104             :     return false;
    1105             : #endif                          /* not USE_LIBXML */
    1106             : }
    1107             : 
    1108             : 
    1109             : #ifdef USE_LIBXML
    1110             : 
    1111             : /*
    1112             :  * pg_xml_init_library --- set up for use of libxml
    1113             :  *
    1114             :  * This should be called by each function that is about to use libxml
    1115             :  * facilities but doesn't require error handling.  It initializes libxml
    1116             :  * and verifies compatibility with the loaded libxml version.  These are
    1117             :  * once-per-session activities.
    1118             :  *
    1119             :  * TODO: xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and
    1120             :  * check)
    1121             :  */
    1122             : void
    1123       91218 : pg_xml_init_library(void)
    1124             : {
    1125             :     static bool first_time = true;
    1126             : 
    1127       91218 :     if (first_time)
    1128             :     {
    1129             :         /* Stuff we need do only once per session */
    1130             : 
    1131             :         /*
    1132             :          * Currently, we have no pure UTF-8 support for internals -- check if
    1133             :          * we can work.
    1134             :          */
    1135             :         if (sizeof(char) != sizeof(xmlChar))
    1136             :             ereport(ERROR,
    1137             :                     (errmsg("could not initialize XML library"),
    1138             :                      errdetail("libxml2 has incompatible char type: sizeof(char)=%zu, sizeof(xmlChar)=%zu.",
    1139             :                                sizeof(char), sizeof(xmlChar))));
    1140             : 
    1141             : #ifdef USE_LIBXMLCONTEXT
    1142             :         /* Set up libxml's memory allocation our way */
    1143             :         xml_memory_init();
    1144             : #endif
    1145             : 
    1146             :         /* Check library compatibility */
    1147          26 :         LIBXML_TEST_VERSION;
    1148             : 
    1149          26 :         first_time = false;
    1150             :     }
    1151       91218 : }
    1152             : 
    1153             : /*
    1154             :  * pg_xml_init --- set up for use of libxml and register an error handler
    1155             :  *
    1156             :  * This should be called by each function that is about to use libxml
    1157             :  * facilities and requires error handling.  It initializes libxml with
    1158             :  * pg_xml_init_library() and establishes our libxml error handler.
    1159             :  *
    1160             :  * strictness determines which errors are reported and which are ignored.
    1161             :  *
    1162             :  * Calls to this function MUST be followed by a PG_TRY block that guarantees
    1163             :  * that pg_xml_done() is called during either normal or error exit.
    1164             :  *
    1165             :  * This is exported for use by contrib/xml2, as well as other code that might
    1166             :  * wish to share use of this module's libxml error handler.
    1167             :  */
    1168             : PgXmlErrorContext *
    1169       23894 : pg_xml_init(PgXmlStrictness strictness)
    1170             : {
    1171             :     PgXmlErrorContext *errcxt;
    1172             :     void       *new_errcxt;
    1173             : 
    1174             :     /* Do one-time setup if needed */
    1175       23894 :     pg_xml_init_library();
    1176             : 
    1177             :     /* Create error handling context structure */
    1178       23894 :     errcxt = (PgXmlErrorContext *) palloc(sizeof(PgXmlErrorContext));
    1179       23894 :     errcxt->magic = ERRCXT_MAGIC;
    1180       23894 :     errcxt->strictness = strictness;
    1181       23894 :     errcxt->err_occurred = false;
    1182       23894 :     initStringInfo(&errcxt->err_buf);
    1183             : 
    1184             :     /*
    1185             :      * Save original error handler and install ours. libxml originally didn't
    1186             :      * distinguish between the contexts for generic and for structured error
    1187             :      * handlers.  If we're using an old libxml version, we must thus save the
    1188             :      * generic error context, even though we're using a structured error
    1189             :      * handler.
    1190             :      */
    1191       23894 :     errcxt->saved_errfunc = xmlStructuredError;
    1192             : 
    1193             : #ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
    1194       23894 :     errcxt->saved_errcxt = xmlStructuredErrorContext;
    1195             : #else
    1196             :     errcxt->saved_errcxt = xmlGenericErrorContext;
    1197             : #endif
    1198             : 
    1199       23894 :     xmlSetStructuredErrorFunc((void *) errcxt, xml_errorHandler);
    1200             : 
    1201             :     /*
    1202             :      * Verify that xmlSetStructuredErrorFunc set the context variable we
    1203             :      * expected it to.  If not, the error context pointer we just saved is not
    1204             :      * the correct thing to restore, and since that leaves us without a way to
    1205             :      * restore the context in pg_xml_done, we must fail.
    1206             :      *
    1207             :      * The only known situation in which this test fails is if we compile with
    1208             :      * headers from a libxml2 that doesn't track the structured error context
    1209             :      * separately (< 2.7.4), but at runtime use a version that does, or vice
    1210             :      * versa.  The libxml2 authors did not treat that change as constituting
    1211             :      * an ABI break, so the LIBXML_TEST_VERSION test in pg_xml_init_library
    1212             :      * fails to protect us from this.
    1213             :      */
    1214             : 
    1215             : #ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
    1216       23894 :     new_errcxt = xmlStructuredErrorContext;
    1217             : #else
    1218             :     new_errcxt = xmlGenericErrorContext;
    1219             : #endif
    1220             : 
    1221       23894 :     if (new_errcxt != (void *) errcxt)
    1222           0 :         ereport(ERROR,
    1223             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1224             :                  errmsg("could not set up XML error handler"),
    1225             :                  errhint("This probably indicates that the version of libxml2"
    1226             :                          " being used is not compatible with the libxml2"
    1227             :                          " header files that PostgreSQL was built with.")));
    1228             : 
    1229             :     /*
    1230             :      * Also, install an entity loader to prevent unwanted fetches of external
    1231             :      * files and URLs.
    1232             :      */
    1233       23894 :     errcxt->saved_entityfunc = xmlGetExternalEntityLoader();
    1234       23894 :     xmlSetExternalEntityLoader(xmlPgEntityLoader);
    1235             : 
    1236       23894 :     return errcxt;
    1237             : }
    1238             : 
    1239             : 
    1240             : /*
    1241             :  * pg_xml_done --- restore previous libxml error handling
    1242             :  *
    1243             :  * Resets libxml's global error-handling state to what it was before
    1244             :  * pg_xml_init() was called.
    1245             :  *
    1246             :  * This routine verifies that all pending errors have been dealt with
    1247             :  * (in assert-enabled builds, anyway).
    1248             :  */
    1249             : void
    1250       23894 : pg_xml_done(PgXmlErrorContext *errcxt, bool isError)
    1251             : {
    1252             :     void       *cur_errcxt;
    1253             : 
    1254             :     /* An assert seems like enough protection here */
    1255             :     Assert(errcxt->magic == ERRCXT_MAGIC);
    1256             : 
    1257             :     /*
    1258             :      * In a normal exit, there should be no un-handled libxml errors.  But we
    1259             :      * shouldn't try to enforce this during error recovery, since the longjmp
    1260             :      * could have been thrown before xml_ereport had a chance to run.
    1261             :      */
    1262             :     Assert(!errcxt->err_occurred || isError);
    1263             : 
    1264             :     /*
    1265             :      * Check that libxml's global state is correct, warn if not.  This is a
    1266             :      * real test and not an Assert because it has a higher probability of
    1267             :      * happening.
    1268             :      */
    1269             : #ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
    1270       23894 :     cur_errcxt = xmlStructuredErrorContext;
    1271             : #else
    1272             :     cur_errcxt = xmlGenericErrorContext;
    1273             : #endif
    1274             : 
    1275       23894 :     if (cur_errcxt != (void *) errcxt)
    1276           0 :         elog(WARNING, "libxml error handling state is out of sync with xml.c");
    1277             : 
    1278             :     /* Restore the saved handlers */
    1279       23894 :     xmlSetStructuredErrorFunc(errcxt->saved_errcxt, errcxt->saved_errfunc);
    1280       23894 :     xmlSetExternalEntityLoader(errcxt->saved_entityfunc);
    1281             : 
    1282             :     /*
    1283             :      * Mark the struct as invalid, just in case somebody somehow manages to
    1284             :      * call xml_errorHandler or xml_ereport with it.
    1285             :      */
    1286       23894 :     errcxt->magic = 0;
    1287             : 
    1288             :     /* Release memory */
    1289       23894 :     pfree(errcxt->err_buf.data);
    1290       23894 :     pfree(errcxt);
    1291       23894 : }
    1292             : 
    1293             : 
    1294             : /*
    1295             :  * pg_xml_error_occurred() --- test the error flag
    1296             :  */
    1297             : bool
    1298           0 : pg_xml_error_occurred(PgXmlErrorContext *errcxt)
    1299             : {
    1300           0 :     return errcxt->err_occurred;
    1301             : }
    1302             : 
    1303             : 
    1304             : /*
    1305             :  * SQL/XML allows storing "XML documents" or "XML content".  "XML
    1306             :  * documents" are specified by the XML specification and are parsed
    1307             :  * easily by libxml.  "XML content" is specified by SQL/XML as the
    1308             :  * production "XMLDecl? content".  But libxml can only parse the
    1309             :  * "content" part, so we have to parse the XML declaration ourselves
    1310             :  * to complete this.
    1311             :  */
    1312             : 
    1313             : #define CHECK_XML_SPACE(p) \
    1314             :     do { \
    1315             :         if (!xmlIsBlank_ch(*(p))) \
    1316             :             return XML_ERR_SPACE_REQUIRED; \
    1317             :     } while (0)
    1318             : 
    1319             : #define SKIP_XML_SPACE(p) \
    1320             :     while (xmlIsBlank_ch(*(p))) (p)++
    1321             : 
    1322             : /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
    1323             : /* Beware of multiple evaluations of argument! */
    1324             : #define PG_XMLISNAMECHAR(c) \
    1325             :     (xmlIsBaseChar_ch(c) || xmlIsIdeographicQ(c) \
    1326             :             || xmlIsDigit_ch(c) \
    1327             :             || c == '.' || c == '-' || c == '_' || c == ':' \
    1328             :             || xmlIsCombiningQ(c) \
    1329             :             || xmlIsExtender_ch(c))
    1330             : 
    1331             : /* pnstrdup, but deal with xmlChar not char; len is measured in xmlChars */
    1332             : static xmlChar *
    1333         188 : xml_pnstrdup(const xmlChar *str, size_t len)
    1334             : {
    1335             :     xmlChar    *result;
    1336             : 
    1337         188 :     result = (xmlChar *) palloc((len + 1) * sizeof(xmlChar));
    1338         188 :     memcpy(result, str, len * sizeof(xmlChar));
    1339         188 :     result[len] = 0;
    1340         188 :     return result;
    1341             : }
    1342             : 
    1343             : /* Ditto, except input is char* */
    1344             : static xmlChar *
    1345        2416 : pg_xmlCharStrndup(const char *str, size_t len)
    1346             : {
    1347             :     xmlChar    *result;
    1348             : 
    1349        2416 :     result = (xmlChar *) palloc((len + 1) * sizeof(xmlChar));
    1350        2416 :     memcpy(result, str, len);
    1351        2416 :     result[len] = '\0';
    1352             : 
    1353        2416 :     return result;
    1354             : }
    1355             : 
    1356             : /*
    1357             :  * Copy xmlChar string to PostgreSQL-owned memory, freeing the input.
    1358             :  *
    1359             :  * The input xmlChar is freed regardless of success of the copy.
    1360             :  */
    1361             : static char *
    1362      109020 : xml_pstrdup_and_free(xmlChar *str)
    1363             : {
    1364             :     char       *result;
    1365             : 
    1366      109020 :     if (str)
    1367             :     {
    1368      109020 :         PG_TRY();
    1369             :         {
    1370      109020 :             result = pstrdup((char *) str);
    1371             :         }
    1372           0 :         PG_FINALLY();
    1373             :         {
    1374      109020 :             xmlFree(str);
    1375             :         }
    1376      109020 :         PG_END_TRY();
    1377             :     }
    1378             :     else
    1379           0 :         result = NULL;
    1380             : 
    1381      109020 :     return result;
    1382             : }
    1383             : 
    1384             : /*
    1385             :  * str is the null-terminated input string.  Remaining arguments are
    1386             :  * output arguments; each can be NULL if value is not wanted.
    1387             :  * version and encoding are returned as locally-palloc'd strings.
    1388             :  * Result is 0 if OK, an error code if not.
    1389             :  */
    1390             : static int
    1391       67324 : parse_xml_decl(const xmlChar *str, size_t *lenp,
    1392             :                xmlChar **version, xmlChar **encoding, int *standalone)
    1393             : {
    1394             :     const xmlChar *p;
    1395             :     const xmlChar *save_p;
    1396             :     size_t      len;
    1397             :     int         utf8char;
    1398             :     int         utf8len;
    1399             : 
    1400             :     /*
    1401             :      * Only initialize libxml.  We don't need error handling here, but we do
    1402             :      * need to make sure libxml is initialized before calling any of its
    1403             :      * functions.  Note that this is safe (and a no-op) if caller has already
    1404             :      * done pg_xml_init().
    1405             :      */
    1406       67324 :     pg_xml_init_library();
    1407             : 
    1408             :     /* Initialize output arguments to "not present" */
    1409       67324 :     if (version)
    1410       67056 :         *version = NULL;
    1411       67324 :     if (encoding)
    1412           0 :         *encoding = NULL;
    1413       67324 :     if (standalone)
    1414       67056 :         *standalone = -1;
    1415             : 
    1416       67324 :     p = str;
    1417             : 
    1418       67324 :     if (xmlStrncmp(p, (xmlChar *) "<?xml", 5) != 0)
    1419       67110 :         goto finished;
    1420             : 
    1421             :     /*
    1422             :      * If next char is a name char, it's a PI like <?xml-stylesheet ...?>
    1423             :      * rather than an XMLDecl, so we have done what we came to do and found no
    1424             :      * XMLDecl.
    1425             :      *
    1426             :      * We need an input length value for xmlGetUTF8Char, but there's no need
    1427             :      * to count the whole document size, so use strnlen not strlen.
    1428             :      */
    1429         214 :     utf8len = strnlen((const char *) (p + 5), MAX_MULTIBYTE_CHAR_LEN);
    1430         214 :     utf8char = xmlGetUTF8Char(p + 5, &utf8len);
    1431         214 :     if (PG_XMLISNAMECHAR(utf8char))
    1432          12 :         goto finished;
    1433             : 
    1434         202 :     p += 5;
    1435             : 
    1436             :     /* version */
    1437         202 :     CHECK_XML_SPACE(p);
    1438         404 :     SKIP_XML_SPACE(p);
    1439         202 :     if (xmlStrncmp(p, (xmlChar *) "version", 7) != 0)
    1440           0 :         return XML_ERR_VERSION_MISSING;
    1441         202 :     p += 7;
    1442         202 :     SKIP_XML_SPACE(p);
    1443         202 :     if (*p != '=')
    1444           0 :         return XML_ERR_VERSION_MISSING;
    1445         202 :     p += 1;
    1446         202 :     SKIP_XML_SPACE(p);
    1447             : 
    1448         202 :     if (*p == '\'' || *p == '"')
    1449         202 :     {
    1450             :         const xmlChar *q;
    1451             : 
    1452         202 :         q = xmlStrchr(p + 1, *p);
    1453         202 :         if (!q)
    1454           0 :             return XML_ERR_VERSION_MISSING;
    1455             : 
    1456         202 :         if (version)
    1457         188 :             *version = xml_pnstrdup(p + 1, q - p - 1);
    1458         202 :         p = q + 1;
    1459             :     }
    1460             :     else
    1461           0 :         return XML_ERR_VERSION_MISSING;
    1462             : 
    1463             :     /* encoding */
    1464         202 :     save_p = p;
    1465         356 :     SKIP_XML_SPACE(p);
    1466         202 :     if (xmlStrncmp(p, (xmlChar *) "encoding", 8) == 0)
    1467             :     {
    1468          46 :         CHECK_XML_SPACE(save_p);
    1469          46 :         p += 8;
    1470          46 :         SKIP_XML_SPACE(p);
    1471          46 :         if (*p != '=')
    1472           0 :             return XML_ERR_MISSING_ENCODING;
    1473          46 :         p += 1;
    1474          46 :         SKIP_XML_SPACE(p);
    1475             : 
    1476          46 :         if (*p == '\'' || *p == '"')
    1477          46 :         {
    1478             :             const xmlChar *q;
    1479             : 
    1480          46 :             q = xmlStrchr(p + 1, *p);
    1481          46 :             if (!q)
    1482           0 :                 return XML_ERR_MISSING_ENCODING;
    1483             : 
    1484          46 :             if (encoding)
    1485           0 :                 *encoding = xml_pnstrdup(p + 1, q - p - 1);
    1486          46 :             p = q + 1;
    1487             :         }
    1488             :         else
    1489           0 :             return XML_ERR_MISSING_ENCODING;
    1490             :     }
    1491             :     else
    1492             :     {
    1493         156 :         p = save_p;
    1494             :     }
    1495             : 
    1496             :     /* standalone */
    1497         202 :     save_p = p;
    1498         310 :     SKIP_XML_SPACE(p);
    1499         202 :     if (xmlStrncmp(p, (xmlChar *) "standalone", 10) == 0)
    1500             :     {
    1501         108 :         CHECK_XML_SPACE(save_p);
    1502         108 :         p += 10;
    1503         108 :         SKIP_XML_SPACE(p);
    1504         108 :         if (*p != '=')
    1505           0 :             return XML_ERR_STANDALONE_VALUE;
    1506         108 :         p += 1;
    1507         108 :         SKIP_XML_SPACE(p);
    1508         216 :         if (xmlStrncmp(p, (xmlChar *) "'yes'", 5) == 0 ||
    1509         108 :             xmlStrncmp(p, (xmlChar *) "\"yes\"", 5) == 0)
    1510             :         {
    1511          60 :             if (standalone)
    1512          60 :                 *standalone = 1;
    1513          60 :             p += 5;
    1514             :         }
    1515          96 :         else if (xmlStrncmp(p, (xmlChar *) "'no'", 4) == 0 ||
    1516          48 :                  xmlStrncmp(p, (xmlChar *) "\"no\"", 4) == 0)
    1517             :         {
    1518          36 :             if (standalone)
    1519          36 :                 *standalone = 0;
    1520          36 :             p += 4;
    1521             :         }
    1522             :         else
    1523          12 :             return XML_ERR_STANDALONE_VALUE;
    1524             :     }
    1525             :     else
    1526             :     {
    1527          94 :         p = save_p;
    1528             :     }
    1529             : 
    1530         190 :     SKIP_XML_SPACE(p);
    1531         190 :     if (xmlStrncmp(p, (xmlChar *) "?>", 2) != 0)
    1532           0 :         return XML_ERR_XMLDECL_NOT_FINISHED;
    1533         190 :     p += 2;
    1534             : 
    1535       67312 : finished:
    1536       67312 :     len = p - str;
    1537             : 
    1538       73700 :     for (p = str; p < str + len; p++)
    1539        6388 :         if (*p > 127)
    1540           0 :             return XML_ERR_INVALID_CHAR;
    1541             : 
    1542       67312 :     if (lenp)
    1543       67312 :         *lenp = len;
    1544             : 
    1545       67312 :     return XML_ERR_OK;
    1546             : }
    1547             : 
    1548             : 
    1549             : /*
    1550             :  * Write an XML declaration.  On output, we adjust the XML declaration
    1551             :  * as follows.  (These rules are the moral equivalent of the clause
    1552             :  * "Serialization of an XML value" in the SQL standard.)
    1553             :  *
    1554             :  * We try to avoid generating an XML declaration if possible.  This is
    1555             :  * so that you don't get trivial things like xml '<foo/>' resulting in
    1556             :  * '<?xml version="1.0"?><foo/>', which would surely be annoying.  We
    1557             :  * must provide a declaration if the standalone property is specified
    1558             :  * or if we include an encoding declaration.  If we have a
    1559             :  * declaration, we must specify a version (XML requires this).
    1560             :  * Otherwise we only make a declaration if the version is not "1.0",
    1561             :  * which is the default version specified in SQL:2003.
    1562             :  */
    1563             : static bool
    1564       22928 : print_xml_decl(StringInfo buf, const xmlChar *version,
    1565             :                pg_enc encoding, int standalone)
    1566             : {
    1567       22928 :     if ((version && strcmp((const char *) version, PG_XML_DEFAULT_VERSION) != 0)
    1568       22892 :         || (encoding && encoding != PG_UTF8)
    1569       22892 :         || standalone != -1)
    1570             :     {
    1571          96 :         appendStringInfoString(buf, "<?xml");
    1572             : 
    1573          96 :         if (version)
    1574          72 :             appendStringInfo(buf, " version=\"%s\"", version);
    1575             :         else
    1576          24 :             appendStringInfo(buf, " version=\"%s\"", PG_XML_DEFAULT_VERSION);
    1577             : 
    1578          96 :         if (encoding && encoding != PG_UTF8)
    1579             :         {
    1580             :             /*
    1581             :              * XXX might be useful to convert this to IANA names (ISO-8859-1
    1582             :              * instead of LATIN1 etc.); needs field experience
    1583             :              */
    1584           0 :             appendStringInfo(buf, " encoding=\"%s\"",
    1585             :                              pg_encoding_to_char(encoding));
    1586             :         }
    1587             : 
    1588          96 :         if (standalone == 1)
    1589          48 :             appendStringInfoString(buf, " standalone=\"yes\"");
    1590          48 :         else if (standalone == 0)
    1591          24 :             appendStringInfoString(buf, " standalone=\"no\"");
    1592          96 :         appendStringInfoString(buf, "?>");
    1593             : 
    1594          96 :         return true;
    1595             :     }
    1596             :     else
    1597       22832 :         return false;
    1598             : }
    1599             : 
    1600             : /*
    1601             :  * Test whether an input that is to be parsed as CONTENT contains a DTD.
    1602             :  *
    1603             :  * The SQL/XML:2003 definition of CONTENT ("XMLDecl? content") is not
    1604             :  * satisfied by a document with a DTD, which is a bit of a wart, as it means
    1605             :  * the CONTENT type is not a proper superset of DOCUMENT.  SQL/XML:2006 and
    1606             :  * later fix that, by redefining content with reference to the "more
    1607             :  * permissive" Document Node of the XQuery/XPath Data Model, such that any
    1608             :  * DOCUMENT value is indeed also a CONTENT value.  That definition is more
    1609             :  * useful, as CONTENT becomes usable for parsing input of unknown form (think
    1610             :  * pg_restore).
    1611             :  *
    1612             :  * As used below in parse_xml when parsing for CONTENT, libxml does not give
    1613             :  * us the 2006+ behavior, but only the 2003; it will choke if the input has
    1614             :  * a DTD.  But we can provide the 2006+ definition of CONTENT easily enough,
    1615             :  * by detecting this case first and simply doing the parse as DOCUMENT.
    1616             :  *
    1617             :  * A DTD can be found arbitrarily far in, but that would be a contrived case;
    1618             :  * it will ordinarily start within a few dozen characters.  The only things
    1619             :  * that can precede it are an XMLDecl (here, the caller will have called
    1620             :  * parse_xml_decl already), whitespace, comments, and processing instructions.
    1621             :  * This function need only return true if it sees a valid sequence of such
    1622             :  * things leading to <!DOCTYPE.  It can simply return false in any other
    1623             :  * cases, including malformed input; that will mean the input gets parsed as
    1624             :  * CONTENT as originally planned, with libxml reporting any errors.
    1625             :  *
    1626             :  * This is only to be called from xml_parse, when pg_xml_init has already
    1627             :  * been called.  The input is already in UTF8 encoding.
    1628             :  */
    1629             : static bool
    1630         932 : xml_doctype_in_content(const xmlChar *str)
    1631             : {
    1632         932 :     const xmlChar *p = str;
    1633             : 
    1634             :     for (;;)
    1635          36 :     {
    1636             :         const xmlChar *e;
    1637             : 
    1638        1058 :         SKIP_XML_SPACE(p);
    1639         968 :         if (*p != '<')
    1640         194 :             return false;
    1641         774 :         p++;
    1642             : 
    1643         774 :         if (*p == '!')
    1644             :         {
    1645          72 :             p++;
    1646             : 
    1647             :             /* if we see <!DOCTYPE, we can return true */
    1648          72 :             if (xmlStrncmp(p, (xmlChar *) "DOCTYPE", 7) == 0)
    1649          42 :                 return true;
    1650             : 
    1651             :             /* otherwise, if it's not a comment, fail */
    1652          30 :             if (xmlStrncmp(p, (xmlChar *) "--", 2) != 0)
    1653           0 :                 return false;
    1654             :             /* find end of comment: find -- and a > must follow */
    1655          30 :             p = xmlStrstr(p + 2, (xmlChar *) "--");
    1656          30 :             if (!p || p[2] != '>')
    1657           0 :                 return false;
    1658             :             /* advance over comment, and keep scanning */
    1659          30 :             p += 3;
    1660          30 :             continue;
    1661             :         }
    1662             : 
    1663             :         /* otherwise, if it's not a PI <?target something?>, fail */
    1664         702 :         if (*p != '?')
    1665         696 :             return false;
    1666           6 :         p++;
    1667             : 
    1668             :         /* find end of PI (the string ?> is forbidden within a PI) */
    1669           6 :         e = xmlStrstr(p, (xmlChar *) "?>");
    1670           6 :         if (!e)
    1671           0 :             return false;
    1672             : 
    1673             :         /* advance over PI, keep scanning */
    1674           6 :         p = e + 2;
    1675             :     }
    1676             : }
    1677             : 
    1678             : 
    1679             : /*
    1680             :  * Convert a text object to XML internal representation
    1681             :  *
    1682             :  * data is the source data (must not be toasted!), encoding is its encoding,
    1683             :  * and xmloption_arg and preserve_whitespace are options for the
    1684             :  * transformation.
    1685             :  *
    1686             :  * If parsed_xmloptiontype isn't NULL, *parsed_xmloptiontype is set to the
    1687             :  * XmlOptionType actually used to parse the input (typically the same as
    1688             :  * xmloption_arg, but a DOCTYPE node in the input can force DOCUMENT mode).
    1689             :  *
    1690             :  * If parsed_nodes isn't NULL and the input is not an XML document, the list
    1691             :  * of parsed nodes from the xmlParseBalancedChunkMemory call will be returned
    1692             :  * to *parsed_nodes.
    1693             :  *
    1694             :  * Errors normally result in ereport(ERROR), but if escontext is an
    1695             :  * ErrorSaveContext, then "safe" errors are reported there instead, and the
    1696             :  * caller must check SOFT_ERROR_OCCURRED() to see whether that happened.
    1697             :  *
    1698             :  * Note: it is caller's responsibility to xmlFreeDoc() the result,
    1699             :  * else a permanent memory leak will ensue!  But note the result could
    1700             :  * be NULL after a soft error.
    1701             :  *
    1702             :  * TODO maybe libxml2's xmlreader is better? (do not construct DOM,
    1703             :  * yet do not use SAX - see xmlreader.c)
    1704             :  */
    1705             : static xmlDocPtr
    1706        1232 : xml_parse(text *data, XmlOptionType xmloption_arg,
    1707             :           bool preserve_whitespace, int encoding,
    1708             :           XmlOptionType *parsed_xmloptiontype, xmlNodePtr *parsed_nodes,
    1709             :           Node *escontext)
    1710             : {
    1711             :     int32       len;
    1712             :     xmlChar    *string;
    1713             :     xmlChar    *utf8string;
    1714             :     PgXmlErrorContext *xmlerrcxt;
    1715        1232 :     volatile xmlParserCtxtPtr ctxt = NULL;
    1716        1232 :     volatile xmlDocPtr doc = NULL;
    1717             : 
    1718             :     /*
    1719             :      * This step looks annoyingly redundant, but we must do it to have a
    1720             :      * null-terminated string in case encoding conversion isn't required.
    1721             :      */
    1722        1232 :     len = VARSIZE_ANY_EXHDR(data);  /* will be useful later */
    1723        1232 :     string = xml_text2xmlChar(data);
    1724             : 
    1725             :     /*
    1726             :      * If the data isn't UTF8, we must translate before giving it to libxml.
    1727             :      *
    1728             :      * XXX ideally, we'd catch any encoding conversion failure and return a
    1729             :      * soft error.  However, failure to convert to UTF8 should be pretty darn
    1730             :      * rare, so for now this is left undone.
    1731             :      */
    1732        1232 :     utf8string = pg_do_encoding_conversion(string,
    1733             :                                            len,
    1734             :                                            encoding,
    1735             :                                            PG_UTF8);
    1736             : 
    1737             :     /* Start up libxml and its parser */
    1738        1232 :     xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_WELLFORMED);
    1739             : 
    1740             :     /* Use a TRY block to ensure we clean up correctly */
    1741        1232 :     PG_TRY();
    1742             :     {
    1743        1232 :         bool        parse_as_document = false;
    1744             :         int         res_code;
    1745        1232 :         size_t      count = 0;
    1746        1232 :         xmlChar    *version = NULL;
    1747        1232 :         int         standalone = 0;
    1748             : 
    1749             :         /* Any errors here are reported as hard ereport's */
    1750        1232 :         xmlInitParser();
    1751             : 
    1752        1232 :         ctxt = xmlNewParserCtxt();
    1753        1232 :         if (ctxt == NULL || xmlerrcxt->err_occurred)
    1754           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
    1755             :                         "could not allocate parser context");
    1756             : 
    1757             :         /* Decide whether to parse as document or content */
    1758        1232 :         if (xmloption_arg == XMLOPTION_DOCUMENT)
    1759         288 :             parse_as_document = true;
    1760             :         else
    1761             :         {
    1762             :             /* Parse and skip over the XML declaration, if any */
    1763         944 :             res_code = parse_xml_decl(utf8string,
    1764             :                                       &count, &version, NULL, &standalone);
    1765         944 :             if (res_code != 0)
    1766             :             {
    1767          12 :                 errsave(escontext,
    1768             :                         errcode(ERRCODE_INVALID_XML_CONTENT),
    1769             :                         errmsg_internal("invalid XML content: invalid XML declaration"),
    1770             :                         errdetail_for_xml_code(res_code));
    1771          12 :                 goto fail;
    1772             :             }
    1773             : 
    1774             :             /* Is there a DOCTYPE element? */
    1775         932 :             if (xml_doctype_in_content(utf8string + count))
    1776          42 :                 parse_as_document = true;
    1777             :         }
    1778             : 
    1779             :         /* initialize output parameters */
    1780        1220 :         if (parsed_xmloptiontype != NULL)
    1781         132 :             *parsed_xmloptiontype = parse_as_document ? XMLOPTION_DOCUMENT :
    1782             :                 XMLOPTION_CONTENT;
    1783        1220 :         if (parsed_nodes != NULL)
    1784         132 :             *parsed_nodes = NULL;
    1785             : 
    1786        1220 :         if (parse_as_document)
    1787             :         {
    1788             :             /*
    1789             :              * Note, that here we try to apply DTD defaults
    1790             :              * (XML_PARSE_DTDATTR) according to SQL/XML:2008 GR 10.16.7.d:
    1791             :              * 'Default values defined by internal DTD are applied'. As for
    1792             :              * external DTDs, we try to support them too, (see SQL/XML:2008 GR
    1793             :              * 10.16.7.e)
    1794             :              */
    1795         330 :             doc = xmlCtxtReadDoc(ctxt, utf8string,
    1796             :                                  NULL,
    1797             :                                  "UTF-8",
    1798             :                                  XML_PARSE_NOENT | XML_PARSE_DTDATTR
    1799             :                                  | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS));
    1800         330 :             if (doc == NULL || xmlerrcxt->err_occurred)
    1801             :             {
    1802             :                 /* Use original option to decide which error code to report */
    1803         144 :                 if (xmloption_arg == XMLOPTION_DOCUMENT)
    1804         138 :                     xml_errsave(escontext, xmlerrcxt,
    1805             :                                 ERRCODE_INVALID_XML_DOCUMENT,
    1806             :                                 "invalid XML document");
    1807             :                 else
    1808           6 :                     xml_errsave(escontext, xmlerrcxt,
    1809             :                                 ERRCODE_INVALID_XML_CONTENT,
    1810             :                                 "invalid XML content");
    1811          96 :                 goto fail;
    1812             :             }
    1813             :         }
    1814             :         else
    1815             :         {
    1816         890 :             doc = xmlNewDoc(version);
    1817         890 :             if (doc == NULL || xmlerrcxt->err_occurred)
    1818           0 :                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
    1819             :                             "could not allocate XML document");
    1820             : 
    1821             :             Assert(doc->encoding == NULL);
    1822         890 :             doc->encoding = xmlStrdup((const xmlChar *) "UTF-8");
    1823         890 :             if (doc->encoding == NULL || xmlerrcxt->err_occurred)
    1824           0 :                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
    1825             :                             "could not allocate XML document");
    1826         890 :             doc->standalone = standalone;
    1827             : 
    1828             :             /* allow empty content */
    1829         890 :             if (*(utf8string + count))
    1830             :             {
    1831        1732 :                 res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0,
    1832         866 :                                                        utf8string + count,
    1833             :                                                        parsed_nodes);
    1834         866 :                 if (res_code != 0 || xmlerrcxt->err_occurred)
    1835             :                 {
    1836          60 :                     xml_errsave(escontext, xmlerrcxt,
    1837             :                                 ERRCODE_INVALID_XML_CONTENT,
    1838             :                                 "invalid XML content");
    1839          12 :                     goto fail;
    1840             :                 }
    1841             :             }
    1842             :         }
    1843             : 
    1844        1136 : fail:
    1845             :         ;
    1846             :     }
    1847          96 :     PG_CATCH();
    1848             :     {
    1849          96 :         if (doc != NULL)
    1850          48 :             xmlFreeDoc(doc);
    1851          96 :         if (ctxt != NULL)
    1852          96 :             xmlFreeParserCtxt(ctxt);
    1853             : 
    1854          96 :         pg_xml_done(xmlerrcxt, true);
    1855             : 
    1856          96 :         PG_RE_THROW();
    1857             :     }
    1858        1136 :     PG_END_TRY();
    1859             : 
    1860        1136 :     xmlFreeParserCtxt(ctxt);
    1861             : 
    1862        1136 :     pg_xml_done(xmlerrcxt, false);
    1863             : 
    1864        1136 :     return doc;
    1865             : }
    1866             : 
    1867             : 
    1868             : /*
    1869             :  * xmlChar<->text conversions
    1870             :  */
    1871             : static xmlChar *
    1872        1370 : xml_text2xmlChar(text *in)
    1873             : {
    1874        1370 :     return (xmlChar *) text_to_cstring(in);
    1875             : }
    1876             : 
    1877             : 
    1878             : #ifdef USE_LIBXMLCONTEXT
    1879             : 
    1880             : /*
    1881             :  * Manage the special context used for all libxml allocations (but only
    1882             :  * in special debug builds; see notes at top of file)
    1883             :  */
    1884             : static void
    1885             : xml_memory_init(void)
    1886             : {
    1887             :     /* Create memory context if not there already */
    1888             :     if (LibxmlContext == NULL)
    1889             :         LibxmlContext = AllocSetContextCreate(TopMemoryContext,
    1890             :                                               "Libxml context",
    1891             :                                               ALLOCSET_DEFAULT_SIZES);
    1892             : 
    1893             :     /* Re-establish the callbacks even if already set */
    1894             :     xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
    1895             : }
    1896             : 
    1897             : /*
    1898             :  * Wrappers for memory management functions
    1899             :  */
    1900             : static void *
    1901             : xml_palloc(size_t size)
    1902             : {
    1903             :     return MemoryContextAlloc(LibxmlContext, size);
    1904             : }
    1905             : 
    1906             : 
    1907             : static void *
    1908             : xml_repalloc(void *ptr, size_t size)
    1909             : {
    1910             :     return repalloc(ptr, size);
    1911             : }
    1912             : 
    1913             : 
    1914             : static void
    1915             : xml_pfree(void *ptr)
    1916             : {
    1917             :     /* At least some parts of libxml assume xmlFree(NULL) is allowed */
    1918             :     if (ptr)
    1919             :         pfree(ptr);
    1920             : }
    1921             : 
    1922             : 
    1923             : static char *
    1924             : xml_pstrdup(const char *string)
    1925             : {
    1926             :     return MemoryContextStrdup(LibxmlContext, string);
    1927             : }
    1928             : #endif                          /* USE_LIBXMLCONTEXT */
    1929             : 
    1930             : 
    1931             : /*
    1932             :  * xmlPgEntityLoader --- entity loader callback function
    1933             :  *
    1934             :  * Silently prevent any external entity URL from being loaded.  We don't want
    1935             :  * to throw an error, so instead make the entity appear to expand to an empty
    1936             :  * string.
    1937             :  *
    1938             :  * We would prefer to allow loading entities that exist in the system's
    1939             :  * global XML catalog; but the available libxml2 APIs make that a complex
    1940             :  * and fragile task.  For now, just shut down all external access.
    1941             :  */
    1942             : static xmlParserInputPtr
    1943          30 : xmlPgEntityLoader(const char *URL, const char *ID,
    1944             :                   xmlParserCtxtPtr ctxt)
    1945             : {
    1946          30 :     return xmlNewStringInputStream(ctxt, (const xmlChar *) "");
    1947             : }
    1948             : 
    1949             : 
    1950             : /*
    1951             :  * xml_ereport --- report an XML-related error
    1952             :  *
    1953             :  * The "msg" is the SQL-level message; some can be adopted from the SQL/XML
    1954             :  * standard.  This function adds libxml's native error message, if any, as
    1955             :  * detail.
    1956             :  *
    1957             :  * This is exported for modules that want to share the core libxml error
    1958             :  * handler.  Note that pg_xml_init() *must* have been called previously.
    1959             :  */
    1960             : void
    1961          12 : xml_ereport(PgXmlErrorContext *errcxt, int level, int sqlcode, const char *msg)
    1962             : {
    1963             :     char       *detail;
    1964             : 
    1965             :     /* Defend against someone passing us a bogus context struct */
    1966          12 :     if (errcxt->magic != ERRCXT_MAGIC)
    1967           0 :         elog(ERROR, "xml_ereport called with invalid PgXmlErrorContext");
    1968             : 
    1969             :     /* Flag that the current libxml error has been reported */
    1970          12 :     errcxt->err_occurred = false;
    1971             : 
    1972             :     /* Include detail only if we have some text from libxml */
    1973          12 :     if (errcxt->err_buf.len > 0)
    1974          12 :         detail = errcxt->err_buf.data;
    1975             :     else
    1976           0 :         detail = NULL;
    1977             : 
    1978          12 :     ereport(level,
    1979             :             (errcode(sqlcode),
    1980             :              errmsg_internal("%s", msg),
    1981             :              detail ? errdetail_internal("%s", detail) : 0));
    1982           0 : }
    1983             : 
    1984             : 
    1985             : /*
    1986             :  * xml_errsave --- save an XML-related error
    1987             :  *
    1988             :  * If escontext is an ErrorSaveContext, error details are saved into it,
    1989             :  * and control returns normally.
    1990             :  *
    1991             :  * Otherwise, the error is thrown, so that this is equivalent to
    1992             :  * xml_ereport() with level == ERROR.
    1993             :  *
    1994             :  * This should be used only for errors that we're sure we do not need
    1995             :  * a transaction abort to clean up after.
    1996             :  */
    1997             : static void
    1998         204 : xml_errsave(Node *escontext, PgXmlErrorContext *errcxt,
    1999             :             int sqlcode, const char *msg)
    2000             : {
    2001             :     char       *detail;
    2002             : 
    2003             :     /* Defend against someone passing us a bogus context struct */
    2004         204 :     if (errcxt->magic != ERRCXT_MAGIC)
    2005           0 :         elog(ERROR, "xml_errsave called with invalid PgXmlErrorContext");
    2006             : 
    2007             :     /* Flag that the current libxml error has been reported */
    2008         204 :     errcxt->err_occurred = false;
    2009             : 
    2010             :     /* Include detail only if we have some text from libxml */
    2011         204 :     if (errcxt->err_buf.len > 0)
    2012         204 :         detail = errcxt->err_buf.data;
    2013             :     else
    2014           0 :         detail = NULL;
    2015             : 
    2016         204 :     errsave(escontext,
    2017             :             (errcode(sqlcode),
    2018             :              errmsg_internal("%s", msg),
    2019             :              detail ? errdetail_internal("%s", detail) : 0));
    2020         108 : }
    2021             : 
    2022             : 
    2023             : /*
    2024             :  * Error handler for libxml errors and warnings
    2025             :  */
    2026             : static void
    2027         398 : xml_errorHandler(void *data, xmlErrorPtr error)
    2028             : {
    2029         398 :     PgXmlErrorContext *xmlerrcxt = (PgXmlErrorContext *) data;
    2030         398 :     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) error->ctxt;
    2031         398 :     xmlParserInputPtr input = (ctxt != NULL) ? ctxt->input : NULL;
    2032         398 :     xmlNodePtr  node = error->node;
    2033         398 :     const xmlChar *name = (node != NULL &&
    2034         398 :                            node->type == XML_ELEMENT_NODE) ? node->name : NULL;
    2035         398 :     int         domain = error->domain;
    2036         398 :     int         level = error->level;
    2037             :     StringInfo  errorBuf;
    2038             : 
    2039             :     /*
    2040             :      * Defend against someone passing us a bogus context struct.
    2041             :      *
    2042             :      * We force a backend exit if this check fails because longjmp'ing out of
    2043             :      * libxml would likely render it unsafe to use further.
    2044             :      */
    2045         398 :     if (xmlerrcxt->magic != ERRCXT_MAGIC)
    2046           0 :         elog(FATAL, "xml_errorHandler called with invalid PgXmlErrorContext");
    2047             : 
    2048             :     /*----------
    2049             :      * Older libxml versions report some errors differently.
    2050             :      * First, some errors were previously reported as coming from the parser
    2051             :      * domain but are now reported as coming from the namespace domain.
    2052             :      * Second, some warnings were upgraded to errors.
    2053             :      * We attempt to compensate for that here.
    2054             :      *----------
    2055             :      */
    2056         398 :     switch (error->code)
    2057             :     {
    2058          30 :         case XML_WAR_NS_URI:
    2059          30 :             level = XML_ERR_ERROR;
    2060          30 :             domain = XML_FROM_NAMESPACE;
    2061          30 :             break;
    2062             : 
    2063          54 :         case XML_ERR_NS_DECL_ERROR:
    2064             :         case XML_WAR_NS_URI_RELATIVE:
    2065             :         case XML_WAR_NS_COLUMN:
    2066             :         case XML_NS_ERR_XML_NAMESPACE:
    2067             :         case XML_NS_ERR_UNDEFINED_NAMESPACE:
    2068             :         case XML_NS_ERR_QNAME:
    2069             :         case XML_NS_ERR_ATTRIBUTE_REDEFINED:
    2070             :         case XML_NS_ERR_EMPTY:
    2071          54 :             domain = XML_FROM_NAMESPACE;
    2072          54 :             break;
    2073             :     }
    2074             : 
    2075             :     /* Decide whether to act on the error or not */
    2076         398 :     switch (domain)
    2077             :     {
    2078         314 :         case XML_FROM_PARSER:
    2079             :         case XML_FROM_NONE:
    2080             :         case XML_FROM_MEMORY:
    2081             :         case XML_FROM_IO:
    2082             : 
    2083             :             /*
    2084             :              * Suppress warnings about undeclared entities.  We need to do
    2085             :              * this to avoid problems due to not loading DTD definitions.
    2086             :              */
    2087         314 :             if (error->code == XML_WAR_UNDECLARED_ENTITY)
    2088           6 :                 return;
    2089             : 
    2090             :             /* Otherwise, accept error regardless of the parsing purpose */
    2091         308 :             break;
    2092             : 
    2093          84 :         default:
    2094             :             /* Ignore error if only doing well-formedness check */
    2095          84 :             if (xmlerrcxt->strictness == PG_XML_STRICTNESS_WELLFORMED)
    2096          66 :                 return;
    2097          18 :             break;
    2098             :     }
    2099             : 
    2100             :     /* Prepare error message in errorBuf */
    2101         326 :     errorBuf = makeStringInfo();
    2102             : 
    2103         326 :     if (error->line > 0)
    2104         326 :         appendStringInfo(errorBuf, "line %d: ", error->line);
    2105         326 :     if (name != NULL)
    2106           0 :         appendStringInfo(errorBuf, "element %s: ", name);
    2107         326 :     if (error->message != NULL)
    2108         326 :         appendStringInfoString(errorBuf, error->message);
    2109             :     else
    2110           0 :         appendStringInfoString(errorBuf, "(no message provided)");
    2111             : 
    2112             :     /*
    2113             :      * Append context information to errorBuf.
    2114             :      *
    2115             :      * xmlParserPrintFileContext() uses libxml's "generic" error handler to
    2116             :      * write the context.  Since we don't want to duplicate libxml
    2117             :      * functionality here, we set up a generic error handler temporarily.
    2118             :      *
    2119             :      * We use appendStringInfo() directly as libxml's generic error handler.
    2120             :      * This should work because it has essentially the same signature as
    2121             :      * libxml expects, namely (void *ptr, const char *msg, ...).
    2122             :      */
    2123         326 :     if (input != NULL)
    2124             :     {
    2125         326 :         xmlGenericErrorFunc errFuncSaved = xmlGenericError;
    2126         326 :         void       *errCtxSaved = xmlGenericErrorContext;
    2127             : 
    2128         326 :         xmlSetGenericErrorFunc((void *) errorBuf,
    2129             :                                (xmlGenericErrorFunc) appendStringInfo);
    2130             : 
    2131             :         /* Add context information to errorBuf */
    2132         326 :         appendStringInfoLineSeparator(errorBuf);
    2133             : 
    2134         326 :         xmlParserPrintFileContext(input);
    2135             : 
    2136             :         /* Restore generic error func */
    2137         326 :         xmlSetGenericErrorFunc(errCtxSaved, errFuncSaved);
    2138             :     }
    2139             : 
    2140             :     /* Get rid of any trailing newlines in errorBuf */
    2141         326 :     chopStringInfoNewlines(errorBuf);
    2142             : 
    2143             :     /*
    2144             :      * Legacy error handling mode.  err_occurred is never set, we just add the
    2145             :      * message to err_buf.  This mode exists because the xml2 contrib module
    2146             :      * uses our error-handling infrastructure, but we don't want to change its
    2147             :      * behaviour since it's deprecated anyway.  This is also why we don't
    2148             :      * distinguish between notices, warnings and errors here --- the old-style
    2149             :      * generic error handler wouldn't have done that either.
    2150             :      */
    2151         326 :     if (xmlerrcxt->strictness == PG_XML_STRICTNESS_LEGACY)
    2152             :     {
    2153           2 :         appendStringInfoLineSeparator(&xmlerrcxt->err_buf);
    2154           2 :         appendBinaryStringInfo(&xmlerrcxt->err_buf, errorBuf->data,
    2155             :                                errorBuf->len);
    2156             : 
    2157           2 :         pfree(errorBuf->data);
    2158           2 :         pfree(errorBuf);
    2159           2 :         return;
    2160             :     }
    2161             : 
    2162             :     /*
    2163             :      * We don't want to ereport() here because that'd probably leave libxml in
    2164             :      * an inconsistent state.  Instead, we remember the error and ereport()
    2165             :      * from xml_ereport().
    2166             :      *
    2167             :      * Warnings and notices can be reported immediately since they won't cause
    2168             :      * a longjmp() out of libxml.
    2169             :      */
    2170         324 :     if (level >= XML_ERR_ERROR)
    2171             :     {
    2172         318 :         appendStringInfoLineSeparator(&xmlerrcxt->err_buf);
    2173         318 :         appendBinaryStringInfo(&xmlerrcxt->err_buf, errorBuf->data,
    2174             :                                errorBuf->len);
    2175             : 
    2176         318 :         xmlerrcxt->err_occurred = true;
    2177             :     }
    2178           6 :     else if (level >= XML_ERR_WARNING)
    2179             :     {
    2180           6 :         ereport(WARNING,
    2181             :                 (errmsg_internal("%s", errorBuf->data)));
    2182             :     }
    2183             :     else
    2184             :     {
    2185           0 :         ereport(NOTICE,
    2186             :                 (errmsg_internal("%s", errorBuf->data)));
    2187             :     }
    2188             : 
    2189         324 :     pfree(errorBuf->data);
    2190         324 :     pfree(errorBuf);
    2191             : }
    2192             : 
    2193             : 
    2194             : /*
    2195             :  * Convert libxml error codes into textual errdetail messages.
    2196             :  *
    2197             :  * This should be called within an ereport or errsave invocation,
    2198             :  * just as errdetail would be.
    2199             :  *
    2200             :  * At the moment, we only need to cover those codes that we
    2201             :  * may raise in this file.
    2202             :  */
    2203             : static int
    2204           6 : errdetail_for_xml_code(int code)
    2205             : {
    2206             :     const char *det;
    2207             : 
    2208           6 :     switch (code)
    2209             :     {
    2210           0 :         case XML_ERR_INVALID_CHAR:
    2211           0 :             det = gettext_noop("Invalid character value.");
    2212           0 :             break;
    2213           0 :         case XML_ERR_SPACE_REQUIRED:
    2214           0 :             det = gettext_noop("Space required.");
    2215           0 :             break;
    2216           6 :         case XML_ERR_STANDALONE_VALUE:
    2217           6 :             det = gettext_noop("standalone accepts only 'yes' or 'no'.");
    2218           6 :             break;
    2219           0 :         case XML_ERR_VERSION_MISSING:
    2220           0 :             det = gettext_noop("Malformed declaration: missing version.");
    2221           0 :             break;
    2222           0 :         case XML_ERR_MISSING_ENCODING:
    2223           0 :             det = gettext_noop("Missing encoding in text declaration.");
    2224           0 :             break;
    2225           0 :         case XML_ERR_XMLDECL_NOT_FINISHED:
    2226           0 :             det = gettext_noop("Parsing XML declaration: '?>' expected.");
    2227           0 :             break;
    2228           0 :         default:
    2229           0 :             det = gettext_noop("Unrecognized libxml error code: %d.");
    2230           0 :             break;
    2231             :     }
    2232             : 
    2233           6 :     return errdetail(det, code);
    2234             : }
    2235             : 
    2236             : 
    2237             : /*
    2238             :  * Remove all trailing newlines from a StringInfo string
    2239             :  */
    2240             : static void
    2241         972 : chopStringInfoNewlines(StringInfo str)
    2242             : {
    2243        1624 :     while (str->len > 0 && str->data[str->len - 1] == '\n')
    2244         652 :         str->data[--str->len] = '\0';
    2245         972 : }
    2246             : 
    2247             : 
    2248             : /*
    2249             :  * Append a newline after removing any existing trailing newlines
    2250             :  */
    2251             : static void
    2252         646 : appendStringInfoLineSeparator(StringInfo str)
    2253             : {
    2254         646 :     chopStringInfoNewlines(str);
    2255         646 :     if (str->len > 0)
    2256         428 :         appendStringInfoChar(str, '\n');
    2257         646 : }
    2258             : 
    2259             : 
    2260             : /*
    2261             :  * Convert one char in the current server encoding to a Unicode codepoint.
    2262             :  */
    2263             : static pg_wchar
    2264       18280 : sqlchar_to_unicode(const char *s)
    2265             : {
    2266             :     char       *utf8string;
    2267             :     pg_wchar    ret[2];         /* need space for trailing zero */
    2268             : 
    2269             :     /* note we're not assuming s is null-terminated */
    2270       18280 :     utf8string = pg_server_to_any(s, pg_mblen(s), PG_UTF8);
    2271             : 
    2272       18280 :     pg_encoding_mb2wchar_with_len(PG_UTF8, utf8string, ret,
    2273             :                                   pg_encoding_mblen(PG_UTF8, utf8string));
    2274             : 
    2275       18280 :     if (utf8string != s)
    2276           0 :         pfree(utf8string);
    2277             : 
    2278       18280 :     return ret[0];
    2279             : }
    2280             : 
    2281             : 
    2282             : static bool
    2283        3638 : is_valid_xml_namefirst(pg_wchar c)
    2284             : {
    2285             :     /* (Letter | '_' | ':') */
    2286        3644 :     return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
    2287        7282 :             || c == '_' || c == ':');
    2288             : }
    2289             : 
    2290             : 
    2291             : static bool
    2292       14642 : is_valid_xml_namechar(pg_wchar c)
    2293             : {
    2294             :     /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
    2295       15532 :     return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
    2296         890 :             || xmlIsDigitQ(c)
    2297         254 :             || c == '.' || c == '-' || c == '_' || c == ':'
    2298          12 :             || xmlIsCombiningQ(c)
    2299       31064 :             || xmlIsExtenderQ(c));
    2300             : }
    2301             : #endif                          /* USE_LIBXML */
    2302             : 
    2303             : 
    2304             : /*
    2305             :  * Map SQL identifier to XML name; see SQL/XML:2008 section 9.1.
    2306             :  */
    2307             : char *
    2308        3652 : map_sql_identifier_to_xml_name(const char *ident, bool fully_escaped,
    2309             :                                bool escape_period)
    2310             : {
    2311             : #ifdef USE_LIBXML
    2312             :     StringInfoData buf;
    2313             :     const char *p;
    2314             : 
    2315             :     /*
    2316             :      * SQL/XML doesn't make use of this case anywhere, so it's probably a
    2317             :      * mistake.
    2318             :      */
    2319             :     Assert(fully_escaped || !escape_period);
    2320             : 
    2321        3652 :     initStringInfo(&buf);
    2322             : 
    2323       21952 :     for (p = ident; *p; p += pg_mblen(p))
    2324             :     {
    2325       18300 :         if (*p == ':' && (p == ident || fully_escaped))
    2326          14 :             appendStringInfoString(&buf, "_x003A_");
    2327       18286 :         else if (*p == '_' && *(p + 1) == 'x')
    2328           6 :             appendStringInfoString(&buf, "_x005F_");
    2329       21568 :         else if (fully_escaped && p == ident &&
    2330        3288 :                  pg_strncasecmp(p, "xml", 3) == 0)
    2331             :         {
    2332           0 :             if (*p == 'x')
    2333           0 :                 appendStringInfoString(&buf, "_x0078_");
    2334             :             else
    2335           0 :                 appendStringInfoString(&buf, "_x0058_");
    2336             :         }
    2337       18280 :         else if (escape_period && *p == '.')
    2338           0 :             appendStringInfoString(&buf, "_x002E_");
    2339             :         else
    2340             :         {
    2341       18280 :             pg_wchar    u = sqlchar_to_unicode(p);
    2342             : 
    2343       36560 :             if ((p == ident)
    2344        3638 :                 ? !is_valid_xml_namefirst(u)
    2345       14642 :                 : !is_valid_xml_namechar(u))
    2346          18 :                 appendStringInfo(&buf, "_x%04X_", (unsigned int) u);
    2347             :             else
    2348       18262 :                 appendBinaryStringInfo(&buf, p, pg_mblen(p));
    2349             :         }
    2350             :     }
    2351             : 
    2352        3652 :     return buf.data;
    2353             : #else                           /* not USE_LIBXML */
    2354             :     NO_XML_SUPPORT();
    2355             :     return NULL;
    2356             : #endif                          /* not USE_LIBXML */
    2357             : }
    2358             : 
    2359             : 
    2360             : /*
    2361             :  * Map XML name to SQL identifier; see SQL/XML:2008 section 9.3.
    2362             :  */
    2363             : char *
    2364         128 : map_xml_name_to_sql_identifier(const char *name)
    2365             : {
    2366             :     StringInfoData buf;
    2367             :     const char *p;
    2368             : 
    2369         128 :     initStringInfo(&buf);
    2370             : 
    2371         704 :     for (p = name; *p; p += pg_mblen(p))
    2372             :     {
    2373         576 :         if (*p == '_' && *(p + 1) == 'x'
    2374          16 :             && isxdigit((unsigned char) *(p + 2))
    2375          16 :             && isxdigit((unsigned char) *(p + 3))
    2376          16 :             && isxdigit((unsigned char) *(p + 4))
    2377          16 :             && isxdigit((unsigned char) *(p + 5))
    2378          16 :             && *(p + 6) == '_')
    2379          16 :         {
    2380             :             char        cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1];
    2381             :             unsigned int u;
    2382             : 
    2383          16 :             sscanf(p + 2, "%X", &u);
    2384          16 :             pg_unicode_to_server(u, (unsigned char *) cbuf);
    2385          16 :             appendStringInfoString(&buf, cbuf);
    2386          16 :             p += 6;
    2387             :         }
    2388             :         else
    2389         560 :             appendBinaryStringInfo(&buf, p, pg_mblen(p));
    2390             :     }
    2391             : 
    2392         128 :     return buf.data;
    2393             : }
    2394             : 
    2395             : /*
    2396             :  * Map SQL value to XML value; see SQL/XML:2008 section 9.8.
    2397             :  *
    2398             :  * When xml_escape_strings is true, then certain characters in string
    2399             :  * values are replaced by entity references (&lt; etc.), as specified
    2400             :  * in SQL/XML:2008 section 9.8 GR 9) a) iii).   This is normally what is
    2401             :  * wanted.  The false case is mainly useful when the resulting value
    2402             :  * is used with xmlTextWriterWriteAttribute() to write out an
    2403             :  * attribute, because that function does the escaping itself.
    2404             :  */
    2405             : char *
    2406      130384 : map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings)
    2407             : {
    2408      130384 :     if (type_is_array_domain(type))
    2409             :     {
    2410             :         ArrayType  *array;
    2411             :         Oid         elmtype;
    2412             :         int16       elmlen;
    2413             :         bool        elmbyval;
    2414             :         char        elmalign;
    2415             :         int         num_elems;
    2416             :         Datum      *elem_values;
    2417             :         bool       *elem_nulls;
    2418             :         StringInfoData buf;
    2419             :         int         i;
    2420             : 
    2421           6 :         array = DatumGetArrayTypeP(value);
    2422           6 :         elmtype = ARR_ELEMTYPE(array);
    2423           6 :         get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
    2424             : 
    2425           6 :         deconstruct_array(array, elmtype,
    2426             :                           elmlen, elmbyval, elmalign,
    2427             :                           &elem_values, &elem_nulls,
    2428             :                           &num_elems);
    2429             : 
    2430           6 :         initStringInfo(&buf);
    2431             : 
    2432          24 :         for (i = 0; i < num_elems; i++)
    2433             :         {
    2434          18 :             if (elem_nulls[i])
    2435           0 :                 continue;
    2436          18 :             appendStringInfoString(&buf, "<element>");
    2437          18 :             appendStringInfoString(&buf,
    2438          18 :                                    map_sql_value_to_xml_value(elem_values[i],
    2439             :                                                               elmtype, true));
    2440          18 :             appendStringInfoString(&buf, "</element>");
    2441             :         }
    2442             : 
    2443           6 :         pfree(elem_values);
    2444           6 :         pfree(elem_nulls);
    2445             : 
    2446           6 :         return buf.data;
    2447             :     }
    2448             :     else
    2449             :     {
    2450             :         Oid         typeOut;
    2451             :         bool        isvarlena;
    2452             :         char       *str;
    2453             : 
    2454             :         /*
    2455             :          * Flatten domains; the special-case treatments below should apply to,
    2456             :          * eg, domains over boolean not just boolean.
    2457             :          */
    2458      130378 :         type = getBaseType(type);
    2459             : 
    2460             :         /*
    2461             :          * Special XSD formatting for some data types
    2462             :          */
    2463      130378 :         switch (type)
    2464             :         {
    2465          66 :             case BOOLOID:
    2466          66 :                 if (DatumGetBool(value))
    2467          60 :                     return "true";
    2468             :                 else
    2469           6 :                     return "false";
    2470             : 
    2471          48 :             case DATEOID:
    2472             :                 {
    2473             :                     DateADT     date;
    2474             :                     struct pg_tm tm;
    2475             :                     char        buf[MAXDATELEN + 1];
    2476             : 
    2477          48 :                     date = DatumGetDateADT(value);
    2478             :                     /* XSD doesn't support infinite values */
    2479          48 :                     if (DATE_NOT_FINITE(date))
    2480           0 :                         ereport(ERROR,
    2481             :                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
    2482             :                                  errmsg("date out of range"),
    2483             :                                  errdetail("XML does not support infinite date values.")));
    2484          48 :                     j2date(date + POSTGRES_EPOCH_JDATE,
    2485             :                            &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
    2486          48 :                     EncodeDateOnly(&tm, USE_XSD_DATES, buf);
    2487             : 
    2488          48 :                     return pstrdup(buf);
    2489             :                 }
    2490             : 
    2491          36 :             case TIMESTAMPOID:
    2492             :                 {
    2493             :                     Timestamp   timestamp;
    2494             :                     struct pg_tm tm;
    2495             :                     fsec_t      fsec;
    2496             :                     char        buf[MAXDATELEN + 1];
    2497             : 
    2498          36 :                     timestamp = DatumGetTimestamp(value);
    2499             : 
    2500             :                     /* XSD doesn't support infinite values */
    2501          36 :                     if (TIMESTAMP_NOT_FINITE(timestamp))
    2502           6 :                         ereport(ERROR,
    2503             :                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
    2504             :                                  errmsg("timestamp out of range"),
    2505             :                                  errdetail("XML does not support infinite timestamp values.")));
    2506          30 :                     else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
    2507          30 :                         EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
    2508             :                     else
    2509           0 :                         ereport(ERROR,
    2510             :                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
    2511             :                                  errmsg("timestamp out of range")));
    2512             : 
    2513          30 :                     return pstrdup(buf);
    2514             :                 }
    2515             : 
    2516          24 :             case TIMESTAMPTZOID:
    2517             :                 {
    2518             :                     TimestampTz timestamp;
    2519             :                     struct pg_tm tm;
    2520             :                     int         tz;
    2521             :                     fsec_t      fsec;
    2522          24 :                     const char *tzn = NULL;
    2523             :                     char        buf[MAXDATELEN + 1];
    2524             : 
    2525          24 :                     timestamp = DatumGetTimestamp(value);
    2526             : 
    2527             :                     /* XSD doesn't support infinite values */
    2528          24 :                     if (TIMESTAMP_NOT_FINITE(timestamp))
    2529           0 :                         ereport(ERROR,
    2530             :                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
    2531             :                                  errmsg("timestamp out of range"),
    2532             :                                  errdetail("XML does not support infinite timestamp values.")));
    2533          24 :                     else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
    2534          24 :                         EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
    2535             :                     else
    2536           0 :                         ereport(ERROR,
    2537             :                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
    2538             :                                  errmsg("timestamp out of range")));
    2539             : 
    2540          24 :                     return pstrdup(buf);
    2541             :                 }
    2542             : 
    2543             : #ifdef USE_LIBXML
    2544          36 :             case BYTEAOID:
    2545             :                 {
    2546          36 :                     bytea      *bstr = DatumGetByteaPP(value);
    2547             :                     PgXmlErrorContext *xmlerrcxt;
    2548          36 :                     volatile xmlBufferPtr buf = NULL;
    2549          36 :                     volatile xmlTextWriterPtr writer = NULL;
    2550             :                     char       *result;
    2551             : 
    2552          36 :                     xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
    2553             : 
    2554          36 :                     PG_TRY();
    2555             :                     {
    2556          36 :                         buf = xmlBufferCreate();
    2557          36 :                         if (buf == NULL || xmlerrcxt->err_occurred)
    2558           0 :                             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
    2559             :                                         "could not allocate xmlBuffer");
    2560          36 :                         writer = xmlNewTextWriterMemory(buf, 0);
    2561          36 :                         if (writer == NULL || xmlerrcxt->err_occurred)
    2562           0 :                             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
    2563             :                                         "could not allocate xmlTextWriter");
    2564             : 
    2565          36 :                         if (xmlbinary == XMLBINARY_BASE64)
    2566          30 :                             xmlTextWriterWriteBase64(writer, VARDATA_ANY(bstr),
    2567          30 :                                                      0, VARSIZE_ANY_EXHDR(bstr));
    2568             :                         else
    2569           6 :                             xmlTextWriterWriteBinHex(writer, VARDATA_ANY(bstr),
    2570           6 :                                                      0, VARSIZE_ANY_EXHDR(bstr));
    2571             : 
    2572             :                         /* we MUST do this now to flush data out to the buffer */
    2573          36 :                         xmlFreeTextWriter(writer);
    2574          36 :                         writer = NULL;
    2575             : 
    2576          36 :                         result = pstrdup((const char *) xmlBufferContent(buf));
    2577             :                     }
    2578           0 :                     PG_CATCH();
    2579             :                     {
    2580           0 :                         if (writer)
    2581           0 :                             xmlFreeTextWriter(writer);
    2582           0 :                         if (buf)
    2583           0 :                             xmlBufferFree(buf);
    2584             : 
    2585           0 :                         pg_xml_done(xmlerrcxt, true);
    2586             : 
    2587           0 :                         PG_RE_THROW();
    2588             :                     }
    2589          36 :                     PG_END_TRY();
    2590             : 
    2591          36 :                     xmlBufferFree(buf);
    2592             : 
    2593          36 :                     pg_xml_done(xmlerrcxt, false);
    2594             : 
    2595          36 :                     return result;
    2596             :                 }
    2597             : #endif                          /* USE_LIBXML */
    2598             : 
    2599             :         }
    2600             : 
    2601             :         /*
    2602             :          * otherwise, just use the type's native text representation
    2603             :          */
    2604      130168 :         getTypeOutputInfo(type, &typeOut, &isvarlena);
    2605      130168 :         str = OidOutputFunctionCall(typeOut, value);
    2606             : 
    2607             :         /* ... exactly as-is for XML, and when escaping is not wanted */
    2608      130168 :         if (type == XMLOID || !xml_escape_strings)
    2609       21634 :             return str;
    2610             : 
    2611             :         /* otherwise, translate special characters as needed */
    2612      108534 :         return escape_xml(str);
    2613             :     }
    2614             : }
    2615             : 
    2616             : 
    2617             : /*
    2618             :  * Escape characters in text that have special meanings in XML.
    2619             :  *
    2620             :  * Returns a palloc'd string.
    2621             :  *
    2622             :  * NB: this is intentionally not dependent on libxml.
    2623             :  */
    2624             : char *
    2625      108780 : escape_xml(const char *str)
    2626             : {
    2627             :     StringInfoData buf;
    2628             :     const char *p;
    2629             : 
    2630      108780 :     initStringInfo(&buf);
    2631      672500 :     for (p = str; *p; p++)
    2632             :     {
    2633      563720 :         switch (*p)
    2634             :         {
    2635           0 :             case '&':
    2636           0 :                 appendStringInfoString(&buf, "&amp;");
    2637           0 :                 break;
    2638          36 :             case '<':
    2639          36 :                 appendStringInfoString(&buf, "&lt;");
    2640          36 :                 break;
    2641          24 :             case '>':
    2642          24 :                 appendStringInfoString(&buf, "&gt;");
    2643          24 :                 break;
    2644           0 :             case '\r':
    2645           0 :                 appendStringInfoString(&buf, "&#x0d;");
    2646           0 :                 break;
    2647      563660 :             default:
    2648      563660 :                 appendStringInfoCharMacro(&buf, *p);
    2649      563660 :                 break;
    2650             :         }
    2651             :     }
    2652      108780 :     return buf.data;
    2653             : }
    2654             : 
    2655             : 
    2656             : static char *
    2657          24 : _SPI_strdup(const char *s)
    2658             : {
    2659          24 :     size_t      len = strlen(s) + 1;
    2660          24 :     char       *ret = SPI_palloc(len);
    2661             : 
    2662          24 :     memcpy(ret, s, len);
    2663          24 :     return ret;
    2664             : }
    2665             : 
    2666             : 
    2667             : /*
    2668             :  * SQL to XML mapping functions
    2669             :  *
    2670             :  * What follows below was at one point intentionally organized so that
    2671             :  * you can read along in the SQL/XML standard. The functions are
    2672             :  * mostly split up the way the clauses lay out in the standards
    2673             :  * document, and the identifiers are also aligned with the standard
    2674             :  * text.  Unfortunately, SQL/XML:2006 reordered the clauses
    2675             :  * differently than SQL/XML:2003, so the order below doesn't make much
    2676             :  * sense anymore.
    2677             :  *
    2678             :  * There are many things going on there:
    2679             :  *
    2680             :  * There are two kinds of mappings: Mapping SQL data (table contents)
    2681             :  * to XML documents, and mapping SQL structure (the "schema") to XML
    2682             :  * Schema.  And there are functions that do both at the same time.
    2683             :  *
    2684             :  * Then you can map a database, a schema, or a table, each in both
    2685             :  * ways.  This breaks down recursively: Mapping a database invokes
    2686             :  * mapping schemas, which invokes mapping tables, which invokes
    2687             :  * mapping rows, which invokes mapping columns, although you can't
    2688             :  * call the last two from the outside.  Because of this, there are a
    2689             :  * number of xyz_internal() functions which are to be called both from
    2690             :  * the function manager wrapper and from some upper layer in a
    2691             :  * recursive call.
    2692             :  *
    2693             :  * See the documentation about what the common function arguments
    2694             :  * nulls, tableforest, and targetns mean.
    2695             :  *
    2696             :  * Some style guidelines for XML output: Use double quotes for quoting
    2697             :  * XML attributes.  Indent XML elements by two spaces, but remember
    2698             :  * that a lot of code is called recursively at different levels, so
    2699             :  * it's better not to indent rather than create output that indents
    2700             :  * and outdents weirdly.  Add newlines to make the output look nice.
    2701             :  */
    2702             : 
    2703             : 
    2704             : /*
    2705             :  * Visibility of objects for XML mappings; see SQL/XML:2008 section
    2706             :  * 4.10.8.
    2707             :  */
    2708             : 
    2709             : /*
    2710             :  * Given a query, which must return type oid as first column, produce
    2711             :  * a list of Oids with the query results.
    2712             :  */
    2713             : static List *
    2714          36 : query_to_oid_list(const char *query)
    2715             : {
    2716             :     uint64      i;
    2717          36 :     List       *list = NIL;
    2718             :     int         spi_result;
    2719             : 
    2720          36 :     spi_result = SPI_execute(query, true, 0);
    2721          36 :     if (spi_result != SPI_OK_SELECT)
    2722           0 :         elog(ERROR, "SPI_execute returned %s for %s",
    2723             :              SPI_result_code_string(spi_result), query);
    2724             : 
    2725         108 :     for (i = 0; i < SPI_processed; i++)
    2726             :     {
    2727             :         Datum       oid;
    2728             :         bool        isnull;
    2729             : 
    2730          72 :         oid = SPI_getbinval(SPI_tuptable->vals[i],
    2731          72 :                             SPI_tuptable->tupdesc,
    2732             :                             1,
    2733             :                             &isnull);
    2734          72 :         if (!isnull)
    2735          72 :             list = lappend_oid(list, DatumGetObjectId(oid));
    2736             :     }
    2737             : 
    2738          36 :     return list;
    2739             : }
    2740             : 
    2741             : 
    2742             : static List *
    2743          36 : schema_get_xml_visible_tables(Oid nspid)
    2744             : {
    2745             :     StringInfoData query;
    2746             : 
    2747          36 :     initStringInfo(&query);
    2748          36 :     appendStringInfo(&query, "SELECT oid FROM pg_catalog.pg_class"
    2749             :                      " WHERE relnamespace = %u AND relkind IN ("
    2750             :                      CppAsString2(RELKIND_RELATION) ","
    2751             :                      CppAsString2(RELKIND_MATVIEW) ","
    2752             :                      CppAsString2(RELKIND_VIEW) ")"
    2753             :                      " AND pg_catalog.has_table_privilege (oid, 'SELECT')"
    2754             :                      " ORDER BY relname;", nspid);
    2755             : 
    2756          36 :     return query_to_oid_list(query.data);
    2757             : }
    2758             : 
    2759             : 
    2760             : /*
    2761             :  * Including the system schemas is probably not useful for a database
    2762             :  * mapping.
    2763             :  */
    2764             : #define XML_VISIBLE_SCHEMAS_EXCLUDE "(nspname ~ '^pg_' OR nspname = 'information_schema')"
    2765             : 
    2766             : #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
    2767             : 
    2768             : 
    2769             : static List *
    2770           0 : database_get_xml_visible_schemas(void)
    2771             : {
    2772           0 :     return query_to_oid_list(XML_VISIBLE_SCHEMAS " ORDER BY nspname;");
    2773             : }
    2774             : 
    2775             : 
    2776             : static List *
    2777           0 : database_get_xml_visible_tables(void)
    2778             : {
    2779             :     /* At the moment there is no order required here. */
    2780           0 :     return query_to_oid_list("SELECT oid FROM pg_catalog.pg_class"
    2781             :                              " WHERE relkind IN ("
    2782             :                              CppAsString2(RELKIND_RELATION) ","
    2783             :                              CppAsString2(RELKIND_MATVIEW) ","
    2784             :                              CppAsString2(RELKIND_VIEW) ")"
    2785             :                              " AND pg_catalog.has_table_privilege(pg_class.oid, 'SELECT')"
    2786             :                              " AND relnamespace IN (" XML_VISIBLE_SCHEMAS ");");
    2787             : }
    2788             : 
    2789             : 
    2790             : /*
    2791             :  * Map SQL table to XML and/or XML Schema document; see SQL/XML:2008
    2792             :  * section 9.11.
    2793             :  */
    2794             : 
    2795             : static StringInfo
    2796          96 : table_to_xml_internal(Oid relid,
    2797             :                       const char *xmlschema, bool nulls, bool tableforest,
    2798             :                       const char *targetns, bool top_level)
    2799             : {
    2800             :     StringInfoData query;
    2801             : 
    2802          96 :     initStringInfo(&query);
    2803          96 :     appendStringInfo(&query, "SELECT * FROM %s",
    2804             :                      DatumGetCString(DirectFunctionCall1(regclassout,
    2805             :                                                          ObjectIdGetDatum(relid))));
    2806          96 :     return query_to_xml_internal(query.data, get_rel_name(relid),
    2807             :                                  xmlschema, nulls, tableforest,
    2808             :                                  targetns, top_level);
    2809             : }
    2810             : 
    2811             : 
    2812             : Datum
    2813          36 : table_to_xml(PG_FUNCTION_ARGS)
    2814             : {
    2815          36 :     Oid         relid = PG_GETARG_OID(0);
    2816          36 :     bool        nulls = PG_GETARG_BOOL(1);
    2817          36 :     bool        tableforest = PG_GETARG_BOOL(2);
    2818          36 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
    2819             : 
    2820          36 :     PG_RETURN_XML_P(stringinfo_to_xmltype(table_to_xml_internal(relid, NULL,
    2821             :                                                                 nulls, tableforest,
    2822             :                                                                 targetns, true)));
    2823             : }
    2824             : 
    2825             : 
    2826             : Datum
    2827          10 : query_to_xml(PG_FUNCTION_ARGS)
    2828             : {
    2829          10 :     char       *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
    2830          10 :     bool        nulls = PG_GETARG_BOOL(1);
    2831          10 :     bool        tableforest = PG_GETARG_BOOL(2);
    2832          10 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
    2833             : 
    2834          10 :     PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL,
    2835             :                                                                 NULL, nulls, tableforest,
    2836             :                                                                 targetns, true)));
    2837             : }
    2838             : 
    2839             : 
    2840             : Datum
    2841          12 : cursor_to_xml(PG_FUNCTION_ARGS)
    2842             : {
    2843          12 :     char       *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
    2844          12 :     int32       count = PG_GETARG_INT32(1);
    2845          12 :     bool        nulls = PG_GETARG_BOOL(2);
    2846          12 :     bool        tableforest = PG_GETARG_BOOL(3);
    2847          12 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(4));
    2848             : 
    2849             :     StringInfoData result;
    2850             :     Portal      portal;
    2851             :     uint64      i;
    2852             : 
    2853          12 :     initStringInfo(&result);
    2854             : 
    2855          12 :     if (!tableforest)
    2856             :     {
    2857           6 :         xmldata_root_element_start(&result, "table", NULL, targetns, true);
    2858           6 :         appendStringInfoChar(&result, '\n');
    2859             :     }
    2860             : 
    2861          12 :     SPI_connect();
    2862          12 :     portal = SPI_cursor_find(name);
    2863          12 :     if (portal == NULL)
    2864           0 :         ereport(ERROR,
    2865             :                 (errcode(ERRCODE_UNDEFINED_CURSOR),
    2866             :                  errmsg("cursor \"%s\" does not exist", name)));
    2867             : 
    2868          12 :     SPI_cursor_fetch(portal, true, count);
    2869          48 :     for (i = 0; i < SPI_processed; i++)
    2870          36 :         SPI_sql_row_to_xmlelement(i, &result, NULL, nulls,
    2871             :                                   tableforest, targetns, true);
    2872             : 
    2873          12 :     SPI_finish();
    2874             : 
    2875          12 :     if (!tableforest)
    2876           6 :         xmldata_root_element_end(&result, "table");
    2877             : 
    2878          12 :     PG_RETURN_XML_P(stringinfo_to_xmltype(&result));
    2879             : }
    2880             : 
    2881             : 
    2882             : /*
    2883             :  * Write the start tag of the root element of a data mapping.
    2884             :  *
    2885             :  * top_level means that this is the very top level of the eventual
    2886             :  * output.  For example, when the user calls table_to_xml, then a call
    2887             :  * with a table name to this function is the top level.  When the user
    2888             :  * calls database_to_xml, then a call with a schema name to this
    2889             :  * function is not the top level.  If top_level is false, then the XML
    2890             :  * namespace declarations are omitted, because they supposedly already
    2891             :  * appeared earlier in the output.  Repeating them is not wrong, but
    2892             :  * it looks ugly.
    2893             :  */
    2894             : static void
    2895         238 : xmldata_root_element_start(StringInfo result, const char *eltname,
    2896             :                            const char *xmlschema, const char *targetns,
    2897             :                            bool top_level)
    2898             : {
    2899             :     /* This isn't really wrong but currently makes no sense. */
    2900             :     Assert(top_level || !xmlschema);
    2901             : 
    2902         238 :     appendStringInfo(result, "<%s", eltname);
    2903         238 :     if (top_level)
    2904             :     {
    2905         178 :         appendStringInfoString(result, " xmlns:xsi=\"" NAMESPACE_XSI "\"");
    2906         178 :         if (strlen(targetns) > 0)
    2907          30 :             appendStringInfo(result, " xmlns=\"%s\"", targetns);
    2908             :     }
    2909         238 :     if (xmlschema)
    2910             :     {
    2911             :         /* FIXME: better targets */
    2912          18 :         if (strlen(targetns) > 0)
    2913           6 :             appendStringInfo(result, " xsi:schemaLocation=\"%s #\"", targetns);
    2914             :         else
    2915          12 :             appendStringInfoString(result, " xsi:noNamespaceSchemaLocation=\"#\"");
    2916             :     }
    2917         238 :     appendStringInfoString(result, ">\n");
    2918         238 : }
    2919             : 
    2920             : 
    2921             : static void
    2922         238 : xmldata_root_element_end(StringInfo result, const char *eltname)
    2923             : {
    2924         238 :     appendStringInfo(result, "</%s>\n", eltname);
    2925         238 : }
    2926             : 
    2927             : 
    2928             : static StringInfo
    2929         112 : query_to_xml_internal(const char *query, char *tablename,
    2930             :                       const char *xmlschema, bool nulls, bool tableforest,
    2931             :                       const char *targetns, bool top_level)
    2932             : {
    2933             :     StringInfo  result;
    2934             :     char       *xmltn;
    2935             :     uint64      i;
    2936             : 
    2937         112 :     if (tablename)
    2938          96 :         xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
    2939             :     else
    2940          16 :         xmltn = "table";
    2941             : 
    2942         112 :     result = makeStringInfo();
    2943             : 
    2944         112 :     SPI_connect();
    2945         112 :     if (SPI_execute(query, true, 0) != SPI_OK_SELECT)
    2946           0 :         ereport(ERROR,
    2947             :                 (errcode(ERRCODE_DATA_EXCEPTION),
    2948             :                  errmsg("invalid query")));
    2949             : 
    2950         112 :     if (!tableforest)
    2951             :     {
    2952          52 :         xmldata_root_element_start(result, xmltn, xmlschema,
    2953             :                                    targetns, top_level);
    2954          52 :         appendStringInfoChar(result, '\n');
    2955             :     }
    2956             : 
    2957         112 :     if (xmlschema)
    2958          30 :         appendStringInfo(result, "%s\n\n", xmlschema);
    2959             : 
    2960         388 :     for (i = 0; i < SPI_processed; i++)
    2961         276 :         SPI_sql_row_to_xmlelement(i, result, tablename, nulls,
    2962             :                                   tableforest, targetns, top_level);
    2963             : 
    2964         112 :     if (!tableforest)
    2965          52 :         xmldata_root_element_end(result, xmltn);
    2966             : 
    2967         112 :     SPI_finish();
    2968             : 
    2969         112 :     return result;
    2970             : }
    2971             : 
    2972             : 
    2973             : Datum
    2974          30 : table_to_xmlschema(PG_FUNCTION_ARGS)
    2975             : {
    2976          30 :     Oid         relid = PG_GETARG_OID(0);
    2977          30 :     bool        nulls = PG_GETARG_BOOL(1);
    2978          30 :     bool        tableforest = PG_GETARG_BOOL(2);
    2979          30 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
    2980             :     const char *result;
    2981             :     Relation    rel;
    2982             : 
    2983          30 :     rel = table_open(relid, AccessShareLock);
    2984          30 :     result = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls,
    2985             :                                         tableforest, targetns);
    2986          30 :     table_close(rel, NoLock);
    2987             : 
    2988          30 :     PG_RETURN_XML_P(cstring_to_xmltype(result));
    2989             : }
    2990             : 
    2991             : 
    2992             : Datum
    2993           6 : query_to_xmlschema(PG_FUNCTION_ARGS)
    2994             : {
    2995           6 :     char       *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
    2996           6 :     bool        nulls = PG_GETARG_BOOL(1);
    2997           6 :     bool        tableforest = PG_GETARG_BOOL(2);
    2998           6 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
    2999             :     const char *result;
    3000             :     SPIPlanPtr  plan;
    3001             :     Portal      portal;
    3002             : 
    3003           6 :     SPI_connect();
    3004             : 
    3005           6 :     if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
    3006           0 :         elog(ERROR, "SPI_prepare(\"%s\") failed", query);
    3007             : 
    3008           6 :     if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
    3009           0 :         elog(ERROR, "SPI_cursor_open(\"%s\") failed", query);
    3010             : 
    3011           6 :     result = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
    3012             :                                                     InvalidOid, nulls,
    3013             :                                                     tableforest, targetns));
    3014           6 :     SPI_cursor_close(portal);
    3015           6 :     SPI_finish();
    3016             : 
    3017           6 :     PG_RETURN_XML_P(cstring_to_xmltype(result));
    3018             : }
    3019             : 
    3020             : 
    3021             : Datum
    3022          12 : cursor_to_xmlschema(PG_FUNCTION_ARGS)
    3023             : {
    3024          12 :     char       *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
    3025          12 :     bool        nulls = PG_GETARG_BOOL(1);
    3026          12 :     bool        tableforest = PG_GETARG_BOOL(2);
    3027          12 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
    3028             :     const char *xmlschema;
    3029             :     Portal      portal;
    3030             : 
    3031          12 :     SPI_connect();
    3032          12 :     portal = SPI_cursor_find(name);
    3033          12 :     if (portal == NULL)
    3034           0 :         ereport(ERROR,
    3035             :                 (errcode(ERRCODE_UNDEFINED_CURSOR),
    3036             :                  errmsg("cursor \"%s\" does not exist", name)));
    3037          12 :     if (portal->tupDesc == NULL)
    3038           0 :         ereport(ERROR,
    3039             :                 (errcode(ERRCODE_INVALID_CURSOR_STATE),
    3040             :                  errmsg("portal \"%s\" does not return tuples", name)));
    3041             : 
    3042          12 :     xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
    3043             :                                                        InvalidOid, nulls,
    3044             :                                                        tableforest, targetns));
    3045          12 :     SPI_finish();
    3046             : 
    3047          12 :     PG_RETURN_XML_P(cstring_to_xmltype(xmlschema));
    3048             : }
    3049             : 
    3050             : 
    3051             : Datum
    3052          24 : table_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
    3053             : {
    3054          24 :     Oid         relid = PG_GETARG_OID(0);
    3055          24 :     bool        nulls = PG_GETARG_BOOL(1);
    3056          24 :     bool        tableforest = PG_GETARG_BOOL(2);
    3057          24 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
    3058             :     Relation    rel;
    3059             :     const char *xmlschema;
    3060             : 
    3061          24 :     rel = table_open(relid, AccessShareLock);
    3062          24 :     xmlschema = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls,
    3063             :                                            tableforest, targetns);
    3064          24 :     table_close(rel, NoLock);
    3065             : 
    3066          24 :     PG_RETURN_XML_P(stringinfo_to_xmltype(table_to_xml_internal(relid,
    3067             :                                                                 xmlschema, nulls, tableforest,
    3068             :                                                                 targetns, true)));
    3069             : }
    3070             : 
    3071             : 
    3072             : Datum
    3073           6 : query_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
    3074             : {
    3075           6 :     char       *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
    3076           6 :     bool        nulls = PG_GETARG_BOOL(1);
    3077           6 :     bool        tableforest = PG_GETARG_BOOL(2);
    3078           6 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
    3079             : 
    3080             :     const char *xmlschema;
    3081             :     SPIPlanPtr  plan;
    3082             :     Portal      portal;
    3083             : 
    3084           6 :     SPI_connect();
    3085             : 
    3086           6 :     if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
    3087           0 :         elog(ERROR, "SPI_prepare(\"%s\") failed", query);
    3088             : 
    3089           6 :     if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
    3090           0 :         elog(ERROR, "SPI_cursor_open(\"%s\") failed", query);
    3091             : 
    3092           6 :     xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
    3093             :                                                        InvalidOid, nulls, tableforest, targetns));
    3094           6 :     SPI_cursor_close(portal);
    3095           6 :     SPI_finish();
    3096             : 
    3097           6 :     PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL,
    3098             :                                                                 xmlschema, nulls, tableforest,
    3099             :                                                                 targetns, true)));
    3100             : }
    3101             : 
    3102             : 
    3103             : /*
    3104             :  * Map SQL schema to XML and/or XML Schema document; see SQL/XML:2008
    3105             :  * sections 9.13, 9.14.
    3106             :  */
    3107             : 
    3108             : static StringInfo
    3109          18 : schema_to_xml_internal(Oid nspid, const char *xmlschema, bool nulls,
    3110             :                        bool tableforest, const char *targetns, bool top_level)
    3111             : {
    3112             :     StringInfo  result;
    3113             :     char       *xmlsn;
    3114             :     List       *relid_list;
    3115             :     ListCell   *cell;
    3116             : 
    3117          18 :     xmlsn = map_sql_identifier_to_xml_name(get_namespace_name(nspid),
    3118             :                                            true, false);
    3119          18 :     result = makeStringInfo();
    3120             : 
    3121          18 :     xmldata_root_element_start(result, xmlsn, xmlschema, targetns, top_level);
    3122          18 :     appendStringInfoChar(result, '\n');
    3123             : 
    3124          18 :     if (xmlschema)
    3125           6 :         appendStringInfo(result, "%s\n\n", xmlschema);
    3126             : 
    3127          18 :     SPI_connect();
    3128             : 
    3129          18 :     relid_list = schema_get_xml_visible_tables(nspid);
    3130             : 
    3131          54 :     foreach(cell, relid_list)
    3132             :     {
    3133          36 :         Oid         relid = lfirst_oid(cell);
    3134             :         StringInfo  subres;
    3135             : 
    3136          36 :         subres = table_to_xml_internal(relid, NULL, nulls, tableforest,
    3137             :                                        targetns, false);
    3138             : 
    3139          36 :         appendBinaryStringInfo(result, subres->data, subres->len);
    3140          36 :         appendStringInfoChar(result, '\n');
    3141             :     }
    3142             : 
    3143          18 :     SPI_finish();
    3144             : 
    3145          18 :     xmldata_root_element_end(result, xmlsn);
    3146             : 
    3147          18 :     return result;
    3148             : }
    3149             : 
    3150             : 
    3151             : Datum
    3152          12 : schema_to_xml(PG_FUNCTION_ARGS)
    3153             : {
    3154          12 :     Name        name = PG_GETARG_NAME(0);
    3155          12 :     bool        nulls = PG_GETARG_BOOL(1);
    3156          12 :     bool        tableforest = PG_GETARG_BOOL(2);
    3157          12 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
    3158             : 
    3159             :     char       *schemaname;
    3160             :     Oid         nspid;
    3161             : 
    3162          12 :     schemaname = NameStr(*name);
    3163          12 :     nspid = LookupExplicitNamespace(schemaname, false);
    3164             : 
    3165          12 :     PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xml_internal(nspid, NULL,
    3166             :                                                                  nulls, tableforest, targetns, true)));
    3167             : }
    3168             : 
    3169             : 
    3170             : /*
    3171             :  * Write the start element of the root element of an XML Schema mapping.
    3172             :  */
    3173             : static void
    3174          96 : xsd_schema_element_start(StringInfo result, const char *targetns)
    3175             : {
    3176          96 :     appendStringInfoString(result,
    3177             :                            "<xsd:schema\n"
    3178             :                            "    xmlns:xsd=\"" NAMESPACE_XSD "\"");
    3179          96 :     if (strlen(targetns) > 0)
    3180          18 :         appendStringInfo(result,
    3181             :                          "\n"
    3182             :                          "    targetNamespace=\"%s\"\n"
    3183             :                          "    elementFormDefault=\"qualified\"",
    3184             :                          targetns);
    3185          96 :     appendStringInfoString(result,
    3186             :                            ">\n\n");
    3187          96 : }
    3188             : 
    3189             : 
    3190             : static void
    3191          96 : xsd_schema_element_end(StringInfo result)
    3192             : {
    3193          96 :     appendStringInfoString(result, "</xsd:schema>");
    3194          96 : }
    3195             : 
    3196             : 
    3197             : static StringInfo
    3198          18 : schema_to_xmlschema_internal(const char *schemaname, bool nulls,
    3199             :                              bool tableforest, const char *targetns)
    3200             : {
    3201             :     Oid         nspid;
    3202             :     List       *relid_list;
    3203             :     List       *tupdesc_list;
    3204             :     ListCell   *cell;
    3205             :     StringInfo  result;
    3206             : 
    3207          18 :     result = makeStringInfo();
    3208             : 
    3209          18 :     nspid = LookupExplicitNamespace(schemaname, false);
    3210             : 
    3211          18 :     xsd_schema_element_start(result, targetns);
    3212             : 
    3213          18 :     SPI_connect();
    3214             : 
    3215          18 :     relid_list = schema_get_xml_visible_tables(nspid);
    3216             : 
    3217          18 :     tupdesc_list = NIL;
    3218          54 :     foreach(cell, relid_list)
    3219             :     {
    3220             :         Relation    rel;
    3221             : 
    3222          36 :         rel = table_open(lfirst_oid(cell), AccessShareLock);
    3223          36 :         tupdesc_list = lappend(tupdesc_list, CreateTupleDescCopy(rel->rd_att));
    3224          36 :         table_close(rel, NoLock);
    3225             :     }
    3226             : 
    3227          18 :     appendStringInfoString(result,
    3228             :                            map_sql_typecoll_to_xmlschema_types(tupdesc_list));
    3229             : 
    3230          18 :     appendStringInfoString(result,
    3231             :                            map_sql_schema_to_xmlschema_types(nspid, relid_list,
    3232             :                                                              nulls, tableforest, targetns));
    3233             : 
    3234          18 :     xsd_schema_element_end(result);
    3235             : 
    3236          18 :     SPI_finish();
    3237             : 
    3238          18 :     return result;
    3239             : }
    3240             : 
    3241             : 
    3242             : Datum
    3243          12 : schema_to_xmlschema(PG_FUNCTION_ARGS)
    3244             : {
    3245          12 :     Name        name = PG_GETARG_NAME(0);
    3246          12 :     bool        nulls = PG_GETARG_BOOL(1);
    3247          12 :     bool        tableforest = PG_GETARG_BOOL(2);
    3248          12 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
    3249             : 
    3250          12 :     PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xmlschema_internal(NameStr(*name),
    3251             :                                                                        nulls, tableforest, targetns)));
    3252             : }
    3253             : 
    3254             : 
    3255             : Datum
    3256           6 : schema_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
    3257             : {
    3258           6 :     Name        name = PG_GETARG_NAME(0);
    3259           6 :     bool        nulls = PG_GETARG_BOOL(1);
    3260           6 :     bool        tableforest = PG_GETARG_BOOL(2);
    3261           6 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
    3262             :     char       *schemaname;
    3263             :     Oid         nspid;
    3264             :     StringInfo  xmlschema;
    3265             : 
    3266           6 :     schemaname = NameStr(*name);
    3267           6 :     nspid = LookupExplicitNamespace(schemaname, false);
    3268             : 
    3269           6 :     xmlschema = schema_to_xmlschema_internal(schemaname, nulls,
    3270             :                                              tableforest, targetns);
    3271             : 
    3272           6 :     PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xml_internal(nspid,
    3273             :                                                                  xmlschema->data, nulls,
    3274             :                                                                  tableforest, targetns, true)));
    3275             : }
    3276             : 
    3277             : 
    3278             : /*
    3279             :  * Map SQL database to XML and/or XML Schema document; see SQL/XML:2008
    3280             :  * sections 9.16, 9.17.
    3281             :  */
    3282             : 
    3283             : static StringInfo
    3284           0 : database_to_xml_internal(const char *xmlschema, bool nulls,
    3285             :                          bool tableforest, const char *targetns)
    3286             : {
    3287             :     StringInfo  result;
    3288             :     List       *nspid_list;
    3289             :     ListCell   *cell;
    3290             :     char       *xmlcn;
    3291             : 
    3292           0 :     xmlcn = map_sql_identifier_to_xml_name(get_database_name(MyDatabaseId),
    3293             :                                            true, false);
    3294           0 :     result = makeStringInfo();
    3295             : 
    3296           0 :     xmldata_root_element_start(result, xmlcn, xmlschema, targetns, true);
    3297           0 :     appendStringInfoChar(result, '\n');
    3298             : 
    3299           0 :     if (xmlschema)
    3300           0 :         appendStringInfo(result, "%s\n\n", xmlschema);
    3301             : 
    3302           0 :     SPI_connect();
    3303             : 
    3304           0 :     nspid_list = database_get_xml_visible_schemas();
    3305             : 
    3306           0 :     foreach(cell, nspid_list)
    3307             :     {
    3308           0 :         Oid         nspid = lfirst_oid(cell);
    3309             :         StringInfo  subres;
    3310             : 
    3311           0 :         subres = schema_to_xml_internal(nspid, NULL, nulls,
    3312             :                                         tableforest, targetns, false);
    3313             : 
    3314           0 :         appendBinaryStringInfo(result, subres->data, subres->len);
    3315           0 :         appendStringInfoChar(result, '\n');
    3316             :     }
    3317             : 
    3318           0 :     SPI_finish();
    3319             : 
    3320           0 :     xmldata_root_element_end(result, xmlcn);
    3321             : 
    3322           0 :     return result;
    3323             : }
    3324             : 
    3325             : 
    3326             : Datum
    3327           0 : database_to_xml(PG_FUNCTION_ARGS)
    3328             : {
    3329           0 :     bool        nulls = PG_GETARG_BOOL(0);
    3330           0 :     bool        tableforest = PG_GETARG_BOOL(1);
    3331           0 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
    3332             : 
    3333           0 :     PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xml_internal(NULL, nulls,
    3334             :                                                                    tableforest, targetns)));
    3335             : }
    3336             : 
    3337             : 
    3338             : static StringInfo
    3339           0 : database_to_xmlschema_internal(bool nulls, bool tableforest,
    3340             :                                const char *targetns)
    3341             : {
    3342             :     List       *relid_list;
    3343             :     List       *nspid_list;
    3344             :     List       *tupdesc_list;
    3345             :     ListCell   *cell;
    3346             :     StringInfo  result;
    3347             : 
    3348           0 :     result = makeStringInfo();
    3349             : 
    3350           0 :     xsd_schema_element_start(result, targetns);
    3351             : 
    3352           0 :     SPI_connect();
    3353             : 
    3354           0 :     relid_list = database_get_xml_visible_tables();
    3355           0 :     nspid_list = database_get_xml_visible_schemas();
    3356             : 
    3357           0 :     tupdesc_list = NIL;
    3358           0 :     foreach(cell, relid_list)
    3359             :     {
    3360             :         Relation    rel;
    3361             : 
    3362           0 :         rel = table_open(lfirst_oid(cell), AccessShareLock);
    3363           0 :         tupdesc_list = lappend(tupdesc_list, CreateTupleDescCopy(rel->rd_att));
    3364           0 :         table_close(rel, NoLock);
    3365             :     }
    3366             : 
    3367           0 :     appendStringInfoString(result,
    3368             :                            map_sql_typecoll_to_xmlschema_types(tupdesc_list));
    3369             : 
    3370           0 :     appendStringInfoString(result,
    3371             :                            map_sql_catalog_to_xmlschema_types(nspid_list, nulls, tableforest, targetns));
    3372             : 
    3373           0 :     xsd_schema_element_end(result);
    3374             : 
    3375           0 :     SPI_finish();
    3376             : 
    3377           0 :     return result;
    3378             : }
    3379             : 
    3380             : 
    3381             : Datum
    3382           0 : database_to_xmlschema(PG_FUNCTION_ARGS)
    3383             : {
    3384           0 :     bool        nulls = PG_GETARG_BOOL(0);
    3385           0 :     bool        tableforest = PG_GETARG_BOOL(1);
    3386           0 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
    3387             : 
    3388           0 :     PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xmlschema_internal(nulls,
    3389             :                                                                          tableforest, targetns)));
    3390             : }
    3391             : 
    3392             : 
    3393             : Datum
    3394           0 : database_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
    3395             : {
    3396           0 :     bool        nulls = PG_GETARG_BOOL(0);
    3397           0 :     bool        tableforest = PG_GETARG_BOOL(1);
    3398           0 :     const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
    3399             :     StringInfo  xmlschema;
    3400             : 
    3401           0 :     xmlschema = database_to_xmlschema_internal(nulls, tableforest, targetns);
    3402             : 
    3403           0 :     PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xml_internal(xmlschema->data,
    3404             :                                                                    nulls, tableforest, targetns)));
    3405             : }
    3406             : 
    3407             : 
    3408             : /*
    3409             :  * Map a multi-part SQL name to an XML name; see SQL/XML:2008 section
    3410             :  * 9.2.
    3411             :  */
    3412             : static char *
    3413         384 : map_multipart_sql_identifier_to_xml_name(const char *a, const char *b, const char *c, const char *d)
    3414             : {
    3415             :     StringInfoData result;
    3416             : 
    3417         384 :     initStringInfo(&result);
    3418             : 
    3419         384 :     if (a)
    3420         384 :         appendStringInfoString(&result,
    3421         384 :                                map_sql_identifier_to_xml_name(a, true, true));
    3422         384 :     if (b)
    3423         384 :         appendStringInfo(&result, ".%s",
    3424             :                          map_sql_identifier_to_xml_name(b, true, true));
    3425         384 :     if (c)
    3426         384 :         appendStringInfo(&result, ".%s",
    3427             :                          map_sql_identifier_to_xml_name(c, true, true));
    3428         384 :     if (d)
    3429         366 :         appendStringInfo(&result, ".%s",
    3430             :                          map_sql_identifier_to_xml_name(d, true, true));
    3431             : 
    3432         384 :     return result.data;
    3433             : }
    3434             : 
    3435             : 
    3436             : /*
    3437             :  * Map an SQL table to an XML Schema document; see SQL/XML:2008
    3438             :  * section 9.11.
    3439             :  *
    3440             :  * Map an SQL table to XML Schema data types; see SQL/XML:2008 section
    3441             :  * 9.9.
    3442             :  */
    3443             : static const char *
    3444          78 : map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls,
    3445             :                            bool tableforest, const char *targetns)
    3446             : {
    3447             :     int         i;
    3448             :     char       *xmltn;
    3449             :     char       *tabletypename;
    3450             :     char       *rowtypename;
    3451             :     StringInfoData result;
    3452             : 
    3453          78 :     initStringInfo(&result);
    3454             : 
    3455          78 :     if (OidIsValid(relid))
    3456             :     {
    3457             :         HeapTuple   tuple;
    3458             :         Form_pg_class reltuple;
    3459             : 
    3460          54 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
    3461          54 :         if (!HeapTupleIsValid(tuple))
    3462           0 :             elog(ERROR, "cache lookup failed for relation %u", relid);
    3463          54 :         reltuple = (Form_pg_class) GETSTRUCT(tuple);
    3464             : 
    3465          54 :         xmltn = map_sql_identifier_to_xml_name(NameStr(reltuple->relname),
    3466             :                                                true, false);
    3467             : 
    3468          54 :         tabletypename = map_multipart_sql_identifier_to_xml_name("TableType",
    3469          54 :                                                                  get_database_name(MyDatabaseId),
    3470          54 :                                                                  get_namespace_name(reltuple->relnamespace),
    3471          54 :                                                                  NameStr(reltuple->relname));
    3472             : 
    3473          54 :         rowtypename = map_multipart_sql_identifier_to_xml_name("RowType",
    3474          54 :                                                                get_database_name(MyDatabaseId),
    3475          54 :                                                                get_namespace_name(reltuple->relnamespace),
    3476          54 :                                                                NameStr(reltuple->relname));
    3477             : 
    3478          54 :         ReleaseSysCache(tuple);
    3479             :     }
    3480             :     else
    3481             :     {
    3482          24 :         if (tableforest)
    3483          12 :             xmltn = "row";
    3484             :         else
    3485          12 :             xmltn = "table";
    3486             : 
    3487          24 :         tabletypename = "TableType";
    3488          24 :         rowtypename = "RowType";
    3489             :     }
    3490             : 
    3491          78 :     xsd_schema_element_start(&result, targetns);
    3492             : 
    3493          78 :     appendStringInfoString(&result,
    3494          78 :                            map_sql_typecoll_to_xmlschema_types(list_make1(tupdesc)));
    3495             : 
    3496          78 :     appendStringInfo(&result,
    3497             :                      "<xsd:complexType name=\"%s\">\n"
    3498             :                      "  <xsd:sequence>\n",
    3499             :                      rowtypename);
    3500             : 
    3501         324 :     for (i = 0; i < tupdesc->natts; i++)
    3502             :     {
    3503         246 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
    3504             : 
    3505         246 :         if (att->attisdropped)
    3506           6 :             continue;
    3507         480 :         appendStringInfo(&result,
    3508             :                          "    <xsd:element name=\"%s\" type=\"%s\"%s></xsd:element>\n",
    3509         240 :                          map_sql_identifier_to_xml_name(NameStr(att->attname),
    3510             :                                                         true, false),
    3511             :                          map_sql_type_to_xml_name(att->atttypid, -1),
    3512             :                          nulls ? " nillable=\"true\"" : " minOccurs=\"0\"");
    3513             :     }
    3514             : 
    3515          78 :     appendStringInfoString(&result,
    3516             :                            "  </xsd:sequence>\n"
    3517             :                            "</xsd:complexType>\n\n");
    3518             : 
    3519          78 :     if (!tableforest)
    3520             :     {
    3521          42 :         appendStringInfo(&result,
    3522             :                          "<xsd:complexType name=\"%s\">\n"
    3523             :                          "  <xsd:sequence>\n"
    3524             :                          "    <xsd:element name=\"row\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n"
    3525             :                          "  </xsd:sequence>\n"
    3526             :                          "</xsd:complexType>\n\n",
    3527             :                          tabletypename, rowtypename);
    3528             : 
    3529          42 :         appendStringInfo(&result,
    3530             :                          "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
    3531             :                          xmltn, tabletypename);
    3532             :     }
    3533             :     else
    3534          36 :         appendStringInfo(&result,
    3535             :                          "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
    3536             :                          xmltn, rowtypename);
    3537             : 
    3538          78 :     xsd_schema_element_end(&result);
    3539             : 
    3540          78 :     return result.data;
    3541             : }
    3542             : 
    3543             : 
    3544             : /*
    3545             :  * Map an SQL schema to XML Schema data types; see SQL/XML:2008
    3546             :  * section 9.12.
    3547             :  */
    3548             : static const char *
    3549          18 : map_sql_schema_to_xmlschema_types(Oid nspid, List *relid_list, bool nulls,
    3550             :                                   bool tableforest, const char *targetns)
    3551             : {
    3552             :     char       *dbname;
    3553             :     char       *nspname;
    3554             :     char       *xmlsn;
    3555             :     char       *schematypename;
    3556             :     StringInfoData result;
    3557             :     ListCell   *cell;
    3558             : 
    3559          18 :     dbname = get_database_name(MyDatabaseId);
    3560          18 :     nspname = get_namespace_name(nspid);
    3561             : 
    3562          18 :     initStringInfo(&result);
    3563             : 
    3564          18 :     xmlsn = map_sql_identifier_to_xml_name(nspname, true, false);
    3565             : 
    3566          18 :     schematypename = map_multipart_sql_identifier_to_xml_name("SchemaType",
    3567             :                                                               dbname,
    3568             :                                                               nspname,
    3569             :                                                               NULL);
    3570             : 
    3571          18 :     appendStringInfo(&result,
    3572             :                      "<xsd:complexType name=\"%s\">\n", schematypename);
    3573          18 :     if (!tableforest)
    3574           6 :         appendStringInfoString(&result,
    3575             :                                "  <xsd:all>\n");
    3576             :     else
    3577          12 :         appendStringInfoString(&result,
    3578             :                                "  <xsd:sequence>\n");
    3579             : 
    3580          54 :     foreach(cell, relid_list)
    3581             :     {
    3582          36 :         Oid         relid = lfirst_oid(cell);
    3583          36 :         char       *relname = get_rel_name(relid);
    3584          36 :         char       *xmltn = map_sql_identifier_to_xml_name(relname, true, false);
    3585          36 :         char       *tabletypename = map_multipart_sql_identifier_to_xml_name(tableforest ? "RowType" : "TableType",
    3586             :                                                                              dbname,
    3587             :                                                                              nspname,
    3588             :                                                                              relname);
    3589             : 
    3590          36 :         if (!tableforest)
    3591          12 :             appendStringInfo(&result,
    3592             :                              "    <xsd:element name=\"%s\" type=\"%s\"/>\n",
    3593             :                              xmltn, tabletypename);
    3594             :         else
    3595          24 :             appendStringInfo(&result,
    3596             :                              "    <xsd:element name=\"%s\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n",
    3597             :                              xmltn, tabletypename);
    3598             :     }
    3599             : 
    3600          18 :     if (!tableforest)
    3601           6 :         appendStringInfoString(&result,
    3602             :                                "  </xsd:all>\n");
    3603             :     else
    3604          12 :         appendStringInfoString(&result,
    3605             :                                "  </xsd:sequence>\n");
    3606          18 :     appendStringInfoString(&result,
    3607             :                            "</xsd:complexType>\n\n");
    3608             : 
    3609          18 :     appendStringInfo(&result,
    3610             :                      "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
    3611             :                      xmlsn, schematypename);
    3612             : 
    3613          18 :     return result.data;
    3614             : }
    3615             : 
    3616             : 
    3617             : /*
    3618             :  * Map an SQL catalog to XML Schema data types; see SQL/XML:2008
    3619             :  * section 9.15.
    3620             :  */
    3621             : static const char *
    3622           0 : map_sql_catalog_to_xmlschema_types(List *nspid_list, bool nulls,
    3623             :                                    bool tableforest, const char *targetns)
    3624             : {
    3625             :     char       *dbname;
    3626             :     char       *xmlcn;
    3627             :     char       *catalogtypename;
    3628             :     StringInfoData result;
    3629             :     ListCell   *cell;
    3630             : 
    3631           0 :     dbname = get_database_name(MyDatabaseId);
    3632             : 
    3633           0 :     initStringInfo(&result);
    3634             : 
    3635           0 :     xmlcn = map_sql_identifier_to_xml_name(dbname, true, false);
    3636             : 
    3637           0 :     catalogtypename = map_multipart_sql_identifier_to_xml_name("CatalogType",
    3638             :                                                                dbname,
    3639             :                                                                NULL,
    3640             :                                                                NULL);
    3641             : 
    3642           0 :     appendStringInfo(&result,
    3643             :                      "<xsd:complexType name=\"%s\">\n", catalogtypename);
    3644           0 :     appendStringInfoString(&result,
    3645             :                            "  <xsd:all>\n");
    3646             : 
    3647           0 :     foreach(cell, nspid_list)
    3648             :     {
    3649           0 :         Oid         nspid = lfirst_oid(cell);
    3650           0 :         char       *nspname = get_namespace_name(nspid);
    3651           0 :         char       *xmlsn = map_sql_identifier_to_xml_name(nspname, true, false);
    3652           0 :         char       *schematypename = map_multipart_sql_identifier_to_xml_name("SchemaType",
    3653             :                                                                               dbname,
    3654             :                                                                               nspname,
    3655             :                                                                               NULL);
    3656             : 
    3657           0 :         appendStringInfo(&result,
    3658             :                          "    <xsd:element name=\"%s\" type=\"%s\"/>\n",
    3659             :                          xmlsn, schematypename);
    3660             :     }
    3661             : 
    3662           0 :     appendStringInfoString(&result,
    3663             :                            "  </xsd:all>\n");
    3664           0 :     appendStringInfoString(&result,
    3665             :                            "</xsd:complexType>\n\n");
    3666             : 
    3667           0 :     appendStringInfo(&result,
    3668             :                      "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
    3669             :                      xmlcn, catalogtypename);
    3670             : 
    3671           0 :     return result.data;
    3672             : }
    3673             : 
    3674             : 
    3675             : /*
    3676             :  * Map an SQL data type to an XML name; see SQL/XML:2008 section 9.4.
    3677             :  */
    3678             : static const char *
    3679         810 : map_sql_type_to_xml_name(Oid typeoid, int typmod)
    3680             : {
    3681             :     StringInfoData result;
    3682             : 
    3683         810 :     initStringInfo(&result);
    3684             : 
    3685         810 :     switch (typeoid)
    3686             :     {
    3687          30 :         case BPCHAROID:
    3688          30 :             if (typmod == -1)
    3689          30 :                 appendStringInfoString(&result, "CHAR");
    3690             :             else
    3691           0 :                 appendStringInfo(&result, "CHAR_%d", typmod - VARHDRSZ);
    3692          30 :             break;
    3693          54 :         case VARCHAROID:
    3694          54 :             if (typmod == -1)
    3695          54 :                 appendStringInfoString(&result, "VARCHAR");
    3696             :             else
    3697           0 :                 appendStringInfo(&result, "VARCHAR_%d", typmod - VARHDRSZ);
    3698          54 :             break;
    3699          30 :         case NUMERICOID:
    3700          30 :             if (typmod == -1)
    3701          30 :                 appendStringInfoString(&result, "NUMERIC");
    3702             :             else
    3703           0 :                 appendStringInfo(&result, "NUMERIC_%d_%d",
    3704           0 :                                  ((typmod - VARHDRSZ) >> 16) & 0xffff,
    3705           0 :                                  (typmod - VARHDRSZ) & 0xffff);
    3706          30 :             break;
    3707         174 :         case INT4OID:
    3708         174 :             appendStringInfoString(&result, "INTEGER");
    3709         174 :             break;
    3710          30 :         case INT2OID:
    3711          30 :             appendStringInfoString(&result, "SMALLINT");
    3712          30 :             break;
    3713          30 :         case INT8OID:
    3714          30 :             appendStringInfoString(&result, "BIGINT");
    3715          30 :             break;
    3716          30 :         case FLOAT4OID:
    3717          30 :             appendStringInfoString(&result, "REAL");
    3718          30 :             break;
    3719           0 :         case FLOAT8OID:
    3720           0 :             appendStringInfoString(&result, "DOUBLE");
    3721           0 :             break;
    3722          30 :         case BOOLOID:
    3723          30 :             appendStringInfoString(&result, "BOOLEAN");
    3724          30 :             break;
    3725          30 :         case TIMEOID:
    3726          30 :             if (typmod == -1)
    3727          30 :                 appendStringInfoString(&result, "TIME");
    3728             :             else
    3729           0 :                 appendStringInfo(&result, "TIME_%d", typmod);
    3730          30 :             break;
    3731          30 :         case TIMETZOID:
    3732          30 :             if (typmod == -1)
    3733          30 :                 appendStringInfoString(&result, "TIME_WTZ");
    3734             :             else
    3735           0 :                 appendStringInfo(&result, "TIME_WTZ_%d", typmod);
    3736          30 :             break;
    3737          30 :         case TIMESTAMPOID:
    3738          30 :             if (typmod == -1)
    3739          30 :                 appendStringInfoString(&result, "TIMESTAMP");
    3740             :             else
    3741           0 :                 appendStringInfo(&result, "TIMESTAMP_%d", typmod);
    3742          30 :             break;
    3743          30 :         case TIMESTAMPTZOID:
    3744          30 :             if (typmod == -1)
    3745          30 :                 appendStringInfoString(&result, "TIMESTAMP_WTZ");
    3746             :             else
    3747           0 :                 appendStringInfo(&result, "TIMESTAMP_WTZ_%d", typmod);
    3748          30 :             break;
    3749          30 :         case DATEOID:
    3750          30 :             appendStringInfoString(&result, "DATE");
    3751          30 :             break;
    3752          30 :         case XMLOID:
    3753          30 :             appendStringInfoString(&result, "XML");
    3754          30 :             break;
    3755         222 :         default:
    3756             :             {
    3757             :                 HeapTuple   tuple;
    3758             :                 Form_pg_type typtuple;
    3759             : 
    3760         222 :                 tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeoid));
    3761         222 :                 if (!HeapTupleIsValid(tuple))
    3762           0 :                     elog(ERROR, "cache lookup failed for type %u", typeoid);
    3763         222 :                 typtuple = (Form_pg_type) GETSTRUCT(tuple);
    3764             : 
    3765         222 :                 appendStringInfoString(&result,
    3766         222 :                                        map_multipart_sql_identifier_to_xml_name((typtuple->typtype == TYPTYPE_DOMAIN) ? "Domain" : "UDT",
    3767         222 :                                                                                 get_database_name(MyDatabaseId),
    3768         222 :                                                                                 get_namespace_name(typtuple->typnamespace),
    3769         222 :                                                                                 NameStr(typtuple->typname)));
    3770             : 
    3771         222 :                 ReleaseSysCache(tuple);
    3772             :             }
    3773             :     }
    3774             : 
    3775         810 :     return result.data;
    3776             : }
    3777             : 
    3778             : 
    3779             : /*
    3780             :  * Map a collection of SQL data types to XML Schema data types; see
    3781             :  * SQL/XML:2008 section 9.7.
    3782             :  */
    3783             : static const char *
    3784          96 : map_sql_typecoll_to_xmlschema_types(List *tupdesc_list)
    3785             : {
    3786          96 :     List       *uniquetypes = NIL;
    3787             :     int         i;
    3788             :     StringInfoData result;
    3789             :     ListCell   *cell0;
    3790             : 
    3791             :     /* extract all column types used in the set of TupleDescs */
    3792         210 :     foreach(cell0, tupdesc_list)
    3793             :     {
    3794         114 :         TupleDesc   tupdesc = (TupleDesc) lfirst(cell0);
    3795             : 
    3796         702 :         for (i = 0; i < tupdesc->natts; i++)
    3797             :         {
    3798         588 :             Form_pg_attribute att = TupleDescAttr(tupdesc, i);
    3799             : 
    3800         588 :             if (att->attisdropped)
    3801          24 :                 continue;
    3802         564 :             uniquetypes = list_append_unique_oid(uniquetypes, att->atttypid);
    3803             :         }
    3804             :     }
    3805             : 
    3806             :     /* add base types of domains */
    3807         642 :     foreach(cell0, uniquetypes)
    3808             :     {
    3809         546 :         Oid         typid = lfirst_oid(cell0);
    3810         546 :         Oid         basetypid = getBaseType(typid);
    3811             : 
    3812         546 :         if (basetypid != typid)
    3813          24 :             uniquetypes = list_append_unique_oid(uniquetypes, basetypid);
    3814             :     }
    3815             : 
    3816             :     /* Convert to textual form */
    3817          96 :     initStringInfo(&result);
    3818             : 
    3819         642 :     foreach(cell0, uniquetypes)
    3820             :     {
    3821         546 :         appendStringInfo(&result, "%s\n",
    3822             :                          map_sql_type_to_xmlschema_type(lfirst_oid(cell0),
    3823             :                                                         -1));
    3824             :     }
    3825             : 
    3826          96 :     return result.data;
    3827             : }
    3828             : 
    3829             : 
    3830             : /*
    3831             :  * Map an SQL data type to a named XML Schema data type; see
    3832             :  * SQL/XML:2008 sections 9.5 and 9.6.
    3833             :  *
    3834             :  * (The distinction between 9.5 and 9.6 is basically that 9.6 adds
    3835             :  * a name attribute, which this function does.  The name-less version
    3836             :  * 9.5 doesn't appear to be required anywhere.)
    3837             :  */
    3838             : static const char *
    3839         546 : map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
    3840             : {
    3841             :     StringInfoData result;
    3842         546 :     const char *typename = map_sql_type_to_xml_name(typeoid, typmod);
    3843             : 
    3844         546 :     initStringInfo(&result);
    3845             : 
    3846         546 :     if (typeoid == XMLOID)
    3847             :     {
    3848          24 :         appendStringInfoString(&result,
    3849             :                                "<xsd:complexType mixed=\"true\">\n"
    3850             :                                "  <xsd:sequence>\n"
    3851             :                                "    <xsd:any name=\"element\" minOccurs=\"0\" maxOccurs=\"unbounded\" processContents=\"skip\"/>\n"
    3852             :                                "  </xsd:sequence>\n"
    3853             :                                "</xsd:complexType>\n");
    3854             :     }
    3855             :     else
    3856             :     {
    3857         522 :         appendStringInfo(&result,
    3858             :                          "<xsd:simpleType name=\"%s\">\n", typename);
    3859             : 
    3860         522 :         switch (typeoid)
    3861             :         {
    3862         138 :             case BPCHAROID:
    3863             :             case VARCHAROID:
    3864             :             case TEXTOID:
    3865         138 :                 appendStringInfoString(&result,
    3866             :                                        "  <xsd:restriction base=\"xsd:string\">\n");
    3867         138 :                 if (typmod != -1)
    3868           0 :                     appendStringInfo(&result,
    3869             :                                      "    <xsd:maxLength value=\"%d\"/>\n",
    3870             :                                      typmod - VARHDRSZ);
    3871         138 :                 appendStringInfoString(&result, "  </xsd:restriction>\n");
    3872         138 :                 break;
    3873             : 
    3874          24 :             case BYTEAOID:
    3875          24 :                 appendStringInfo(&result,
    3876             :                                  "  <xsd:restriction base=\"xsd:%s\">\n"
    3877             :                                  "  </xsd:restriction>\n",
    3878          24 :                                  xmlbinary == XMLBINARY_BASE64 ? "base64Binary" : "hexBinary");
    3879          24 :                 break;
    3880             : 
    3881          24 :             case NUMERICOID:
    3882          24 :                 if (typmod != -1)
    3883           0 :                     appendStringInfo(&result,
    3884             :                                      "  <xsd:restriction base=\"xsd:decimal\">\n"
    3885             :                                      "    <xsd:totalDigits value=\"%d\"/>\n"
    3886             :                                      "    <xsd:fractionDigits value=\"%d\"/>\n"
    3887             :                                      "  </xsd:restriction>\n",
    3888           0 :                                      ((typmod - VARHDRSZ) >> 16) & 0xffff,
    3889           0 :                                      (typmod - VARHDRSZ) & 0xffff);
    3890          24 :                 break;
    3891             : 
    3892          24 :             case INT2OID:
    3893          24 :                 appendStringInfo(&result,
    3894             :                                  "  <xsd:restriction base=\"xsd:short\">\n"
    3895             :                                  "    <xsd:maxInclusive value=\"%d\"/>\n"
    3896             :                                  "    <xsd:minInclusive value=\"%d\"/>\n"
    3897             :                                  "  </xsd:restriction>\n",
    3898             :                                  SHRT_MAX, SHRT_MIN);
    3899          24 :                 break;
    3900             : 
    3901          96 :             case INT4OID:
    3902          96 :                 appendStringInfo(&result,
    3903             :                                  "  <xsd:restriction base=\"xsd:int\">\n"
    3904             :                                  "    <xsd:maxInclusive value=\"%d\"/>\n"
    3905             :                                  "    <xsd:minInclusive value=\"%d\"/>\n"
    3906             :                                  "  </xsd:restriction>\n",
    3907             :                                  INT_MAX, INT_MIN);
    3908          96 :                 break;
    3909             : 
    3910          24 :             case INT8OID:
    3911          24 :                 appendStringInfo(&result,
    3912             :                                  "  <xsd:restriction base=\"xsd:long\">\n"
    3913             :                                  "    <xsd:maxInclusive value=\"" INT64_FORMAT "\"/>\n"
    3914             :                                  "    <xsd:minInclusive value=\"" INT64_FORMAT "\"/>\n"
    3915             :                                  "  </xsd:restriction>\n",
    3916             :                                  PG_INT64_MAX,
    3917             :                                  PG_INT64_MIN);
    3918          24 :                 break;
    3919             : 
    3920          24 :             case FLOAT4OID:
    3921          24 :                 appendStringInfoString(&result,
    3922             :                                        "  <xsd:restriction base=\"xsd:float\"></xsd:restriction>\n");
    3923          24 :                 break;
    3924             : 
    3925           0 :             case FLOAT8OID:
    3926           0 :                 appendStringInfoString(&result,
    3927             :                                        "  <xsd:restriction base=\"xsd:double\"></xsd:restriction>\n");
    3928           0 :                 break;
    3929             : 
    3930          24 :             case BOOLOID:
    3931          24 :                 appendStringInfoString(&result,
    3932             :                                        "  <xsd:restriction base=\"xsd:boolean\"></xsd:restriction>\n");
    3933          24 :                 break;
    3934             : 
    3935          48 :             case TIMEOID:
    3936             :             case TIMETZOID:
    3937             :                 {
    3938          48 :                     const char *tz = (typeoid == TIMETZOID ? "(\\+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
    3939             : 
    3940          48 :                     if (typmod == -1)
    3941          48 :                         appendStringInfo(&result,
    3942             :                                          "  <xsd:restriction base=\"xsd:time\">\n"
    3943             :                                          "    <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}(.\\p{Nd}+)?%s\"/>\n"
    3944             :                                          "  </xsd:restriction>\n", tz);
    3945           0 :                     else if (typmod == 0)
    3946           0 :                         appendStringInfo(&result,
    3947             :                                          "  <xsd:restriction base=\"xsd:time\">\n"
    3948             :                                          "    <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
    3949             :                                          "  </xsd:restriction>\n", tz);
    3950             :                     else
    3951           0 :                         appendStringInfo(&result,
    3952             :                                          "  <xsd:restriction base=\"xsd:time\">\n"
    3953             :                                          "    <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}.\\p{Nd}{%d}%s\"/>\n"
    3954             :                                          "  </xsd:restriction>\n", typmod - VARHDRSZ, tz);
    3955          48 :                     break;
    3956             :                 }
    3957             : 
    3958          48 :             case TIMESTAMPOID:
    3959             :             case TIMESTAMPTZOID:
    3960             :                 {
    3961          48 :                     const char *tz = (typeoid == TIMESTAMPTZOID ? "(\\+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
    3962             : 
    3963          48 :                     if (typmod == -1)
    3964          48 :                         appendStringInfo(&result,
    3965             :                                          "  <xsd:restriction base=\"xsd:dateTime\">\n"
    3966             :                                          "    <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"
    3967             :                                          "  </xsd:restriction>\n", tz);
    3968           0 :                     else if (typmod == 0)
    3969           0 :                         appendStringInfo(&result,
    3970             :                                          "  <xsd:restriction base=\"xsd:dateTime\">\n"
    3971             :                                          "    <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}T\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
    3972             :                                          "  </xsd:restriction>\n", tz);
    3973             :                     else
    3974           0 :                         appendStringInfo(&result,
    3975             :                                          "  <xsd:restriction base=\"xsd:dateTime\">\n"
    3976             :                                          "    <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"
    3977             :                                          "  </xsd:restriction>\n", typmod - VARHDRSZ, tz);
    3978          48 :                     break;
    3979             :                 }
    3980             : 
    3981          24 :             case DATEOID:
    3982          24 :                 appendStringInfoString(&result,
    3983             :                                        "  <xsd:restriction base=\"xsd:date\">\n"
    3984             :                                        "    <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}\"/>\n"
    3985             :                                        "  </xsd:restriction>\n");
    3986          24 :                 break;
    3987             : 
    3988          24 :             default:
    3989          24 :                 if (get_typtype(typeoid) == TYPTYPE_DOMAIN)
    3990             :                 {
    3991             :                     Oid         base_typeoid;
    3992          24 :                     int32       base_typmod = -1;
    3993             : 
    3994          24 :                     base_typeoid = getBaseTypeAndTypmod(typeoid, &base_typmod);
    3995             : 
    3996          24 :                     appendStringInfo(&result,
    3997             :                                      "  <xsd:restriction base=\"%s\"/>\n",
    3998             :                                      map_sql_type_to_xml_name(base_typeoid, base_typmod));
    3999             :                 }
    4000          24 :                 break;
    4001             :         }
    4002         522 :         appendStringInfoString(&result, "</xsd:simpleType>\n");
    4003             :     }
    4004             : 
    4005         546 :     return result.data;
    4006             : }
    4007             : 
    4008             : 
    4009             : /*
    4010             :  * Map an SQL row to an XML element, taking the row from the active
    4011             :  * SPI cursor.  See also SQL/XML:2008 section 9.10.
    4012             :  */
    4013             : static void
    4014         312 : SPI_sql_row_to_xmlelement(uint64 rownum, StringInfo result, char *tablename,
    4015             :                           bool nulls, bool tableforest,
    4016             :                           const char *targetns, bool top_level)
    4017             : {
    4018             :     int         i;
    4019             :     char       *xmltn;
    4020             : 
    4021         312 :     if (tablename)
    4022         228 :         xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
    4023             :     else
    4024             :     {
    4025          84 :         if (tableforest)
    4026          36 :             xmltn = "row";
    4027             :         else
    4028          48 :             xmltn = "table";
    4029             :     }
    4030             : 
    4031         312 :     if (tableforest)
    4032         162 :         xmldata_root_element_start(result, xmltn, NULL, targetns, top_level);
    4033             :     else
    4034         150 :         appendStringInfoString(result, "<row>\n");
    4035             : 
    4036        1272 :     for (i = 1; i <= SPI_tuptable->tupdesc->natts; i++)
    4037             :     {
    4038             :         char       *colname;
    4039             :         Datum       colval;
    4040             :         bool        isnull;
    4041             : 
    4042         960 :         colname = map_sql_identifier_to_xml_name(SPI_fname(SPI_tuptable->tupdesc, i),
    4043             :                                                  true, false);
    4044         960 :         colval = SPI_getbinval(SPI_tuptable->vals[rownum],
    4045         960 :                                SPI_tuptable->tupdesc,
    4046             :                                i,
    4047             :                                &isnull);
    4048         960 :         if (isnull)
    4049             :         {
    4050         114 :             if (nulls)
    4051          60 :                 appendStringInfo(result, "  <%s xsi:nil=\"true\"/>\n", colname);
    4052             :         }
    4053             :         else
    4054         846 :             appendStringInfo(result, "  <%s>%s</%s>\n",
    4055             :                              colname,
    4056             :                              map_sql_value_to_xml_value(colval,
    4057         846 :                                                         SPI_gettypeid(SPI_tuptable->tupdesc, i), true),
    4058             :                              colname);
    4059             :     }
    4060             : 
    4061         312 :     if (tableforest)
    4062             :     {
    4063         162 :         xmldata_root_element_end(result, xmltn);
    4064         162 :         appendStringInfoChar(result, '\n');
    4065             :     }
    4066             :     else
    4067         150 :         appendStringInfoString(result, "</row>\n\n");
    4068         312 : }
    4069             : 
    4070             : 
    4071             : /*
    4072             :  * XPath related functions
    4073             :  */
    4074             : 
    4075             : #ifdef USE_LIBXML
    4076             : 
    4077             : /*
    4078             :  * Convert XML node to text.
    4079             :  *
    4080             :  * For attribute and text nodes, return the escaped text.  For anything else,
    4081             :  * dump the whole subtree.
    4082             :  */
    4083             : static text *
    4084         192 : xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
    4085             : {
    4086         192 :     xmltype    *result = NULL;
    4087             : 
    4088         192 :     if (cur->type != XML_ATTRIBUTE_NODE && cur->type != XML_TEXT_NODE)
    4089         162 :     {
    4090         162 :         void        (*volatile nodefree) (xmlNodePtr) = NULL;
    4091         162 :         volatile xmlBufferPtr buf = NULL;
    4092         162 :         volatile xmlNodePtr cur_copy = NULL;
    4093             : 
    4094         162 :         PG_TRY();
    4095             :         {
    4096             :             int         bytes;
    4097             : 
    4098         162 :             buf = xmlBufferCreate();
    4099         162 :             if (buf == NULL || xmlerrcxt->err_occurred)
    4100           0 :                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
    4101             :                             "could not allocate xmlBuffer");
    4102             : 
    4103             :             /*
    4104             :              * Produce a dump of the node that we can serialize.  xmlNodeDump
    4105             :              * does that, but the result of that function won't contain
    4106             :              * namespace definitions from ancestor nodes, so we first do a
    4107             :              * xmlCopyNode() which duplicates the node along with its required
    4108             :              * namespace definitions.
    4109             :              *
    4110             :              * Some old libxml2 versions such as 2.7.6 produce partially
    4111             :              * broken XML_DOCUMENT_NODE nodes (unset content field) when
    4112             :              * copying them.  xmlNodeDump of such a node works fine, but
    4113             :              * xmlFreeNode crashes; set us up to call xmlFreeDoc instead.
    4114             :              */
    4115         162 :             cur_copy = xmlCopyNode(cur, 1);
    4116         162 :             if (cur_copy == NULL || xmlerrcxt->err_occurred)
    4117           0 :                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
    4118             :                             "could not copy node");
    4119         324 :             nodefree = (cur_copy->type == XML_DOCUMENT_NODE) ?
    4120         162 :                 (void (*) (xmlNodePtr)) xmlFreeDoc : xmlFreeNode;
    4121             : 
    4122         162 :             bytes = xmlNodeDump(buf, NULL, cur_copy, 0, 0);
    4123         162 :             if (bytes == -1 || xmlerrcxt->err_occurred)
    4124           0 :                 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
    4125             :                             "could not dump node");
    4126             : 
    4127         162 :             result = xmlBuffer_to_xmltype(buf);
    4128             :         }
    4129           0 :         PG_FINALLY();
    4130             :         {
    4131         162 :             if (nodefree)
    4132         162 :                 nodefree(cur_copy);
    4133         162 :             if (buf)
    4134         162 :                 xmlBufferFree(buf);
    4135             :         }
    4136         162 :         PG_END_TRY();
    4137             :     }
    4138             :     else
    4139             :     {
    4140             :         xmlChar    *str;
    4141             : 
    4142          30 :         str = xmlXPathCastNodeToString(cur);
    4143          30 :         PG_TRY();
    4144             :         {
    4145             :             /* Here we rely on XML having the same representation as TEXT */
    4146          30 :             char       *escaped = escape_xml((char *) str);
    4147             : 
    4148          30 :             result = (xmltype *) cstring_to_text(escaped);
    4149          30 :             pfree(escaped);
    4150             :         }
    4151           0 :         PG_FINALLY();
    4152             :         {
    4153          30 :             xmlFree(str);
    4154             :         }
    4155          30 :         PG_END_TRY();
    4156             :     }
    4157             : 
    4158         192 :     return result;
    4159             : }
    4160             : 
    4161             : /*
    4162             :  * Convert an XML XPath object (the result of evaluating an XPath expression)
    4163             :  * to an array of xml values, which are appended to astate.  The function
    4164             :  * result value is the number of elements in the array.
    4165             :  *
    4166             :  * If "astate" is NULL then we don't generate the array value, but we still
    4167             :  * return the number of elements it would have had.
    4168             :  *
    4169             :  * Nodesets are converted to an array containing the nodes' textual
    4170             :  * representations.  Primitive values (float, double, string) are converted
    4171             :  * to a single-element array containing the value's string representation.
    4172             :  */
    4173             : static int
    4174         536 : xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
    4175             :                        ArrayBuildState *astate,
    4176             :                        PgXmlErrorContext *xmlerrcxt)
    4177             : {
    4178         536 :     int         result = 0;
    4179             :     Datum       datum;
    4180             :     Oid         datumtype;
    4181             :     char       *result_str;
    4182             : 
    4183         536 :     switch (xpathobj->type)
    4184             :     {
    4185         494 :         case XPATH_NODESET:
    4186         494 :             if (xpathobj->nodesetval != NULL)
    4187             :             {
    4188         350 :                 result = xpathobj->nodesetval->nodeNr;
    4189         350 :                 if (astate != NULL)
    4190             :                 {
    4191             :                     int         i;
    4192             : 
    4193         164 :                     for (i = 0; i < result; i++)
    4194             :                     {
    4195          90 :                         datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
    4196             :                                                                      xmlerrcxt));
    4197          90 :                         (void) accumArrayResult(astate, datum, false,
    4198             :                                                 XMLOID, CurrentMemoryContext);
    4199             :                     }
    4200             :                 }
    4201             :             }
    4202         494 :             return result;
    4203             : 
    4204          12 :         case XPATH_BOOLEAN:
    4205          12 :             if (astate == NULL)
    4206           0 :                 return 1;
    4207          12 :             datum = BoolGetDatum(xpathobj->boolval);
    4208          12 :             datumtype = BOOLOID;
    4209          12 :             break;
    4210             : 
    4211          18 :         case XPATH_NUMBER:
    4212          18 :             if (astate == NULL)
    4213          12 :                 return 1;
    4214           6 :             datum = Float8GetDatum(xpathobj->floatval);
    4215           6 :             datumtype = FLOAT8OID;
    4216           6 :             break;
    4217             : 
    4218          12 :         case XPATH_STRING:
    4219          12 :             if (astate == NULL)
    4220           0 :                 return 1;
    4221          12 :             datum = CStringGetDatum((char *) xpathobj->stringval);
    4222          12 :             datumtype = CSTRINGOID;
    4223          12 :             break;
    4224             : 
    4225           0 :         default:
    4226           0 :             elog(ERROR, "xpath expression result type %d is unsupported",
    4227             :                  xpathobj->type);
    4228             :             return 0;           /* keep compiler quiet */
    4229             :     }
    4230             : 
    4231             :     /* Common code for scalar-value cases */
    4232          30 :     result_str = map_sql_value_to_xml_value(datum, datumtype, true);
    4233          30 :     datum = PointerGetDatum(cstring_to_xmltype(result_str));
    4234          30 :     (void) accumArrayResult(astate, datum, false,
    4235             :                             XMLOID, CurrentMemoryContext);
    4236          30 :     return 1;
    4237             : }
    4238             : 
    4239             : 
    4240             : /*
    4241             :  * Common code for xpath() and xmlexists()
    4242             :  *
    4243             :  * Evaluate XPath expression and return number of nodes in res_nitems
    4244             :  * and array of XML values in astate.  Either of those pointers can be
    4245             :  * NULL if the corresponding result isn't wanted.
    4246             :  *
    4247             :  * It is up to the user to ensure that the XML passed is in fact
    4248             :  * an XML document - XPath doesn't work easily on fragments without
    4249             :  * a context node being known.
    4250             :  */
    4251             : static void
    4252         554 : xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
    4253             :                int *res_nitems, ArrayBuildState *astate)
    4254             : {
    4255             :     PgXmlErrorContext *xmlerrcxt;
    4256         554 :     volatile xmlParserCtxtPtr ctxt = NULL;
    4257         554 :     volatile xmlDocPtr doc = NULL;
    4258         554 :     volatile xmlXPathContextPtr xpathctx = NULL;
    4259         554 :     volatile xmlXPathCompExprPtr xpathcomp = NULL;
    4260         554 :     volatile xmlXPathObjectPtr xpathobj = NULL;
    4261             :     char       *datastr;
    4262             :     int32       len;
    4263             :     int32       xpath_len;
    4264             :     xmlChar    *string;
    4265             :     xmlChar    *xpath_expr;
    4266         554 :     size_t      xmldecl_len = 0;
    4267             :     int         i;
    4268             :     int         ndim;
    4269             :     Datum      *ns_names_uris;
    4270             :     bool       *ns_names_uris_nulls;
    4271             :     int         ns_count;
    4272             : 
    4273             :     /*
    4274             :      * Namespace mappings are passed as text[].  If an empty array is passed
    4275             :      * (ndim = 0, "0-dimensional"), then there are no namespace mappings.
    4276             :      * Else, a 2-dimensional array with length of the second axis being equal
    4277             :      * to 2 should be passed, i.e., every subarray contains 2 elements, the
    4278             :      * first element defining the name, the second one the URI.  Example:
    4279             :      * ARRAY[ARRAY['myns', 'http://example.com'], ARRAY['myns2',
    4280             :      * 'http://example2.com']].
    4281             :      */
    4282         554 :     ndim = namespaces ? ARR_NDIM(namespaces) : 0;
    4283         554 :     if (ndim != 0)
    4284             :     {
    4285             :         int        *dims;
    4286             : 
    4287         126 :         dims = ARR_DIMS(namespaces);
    4288             : 
    4289         126 :         if (ndim != 2 || dims[1] != 2)
    4290           0 :             ereport(ERROR,
    4291             :                     (errcode(ERRCODE_DATA_EXCEPTION),
    4292             :                      errmsg("invalid array for XML namespace mapping"),
    4293             :                      errdetail("The array must be two-dimensional with length of the second axis equal to 2.")));
    4294             : 
    4295             :         Assert(ARR_ELEMTYPE(namespaces) == TEXTOID);
    4296             : 
    4297         126 :         deconstruct_array_builtin(namespaces, TEXTOID,
    4298             :                                   &ns_names_uris, &ns_names_uris_nulls,
    4299             :                                   &ns_count);
    4300             : 
    4301             :         Assert((ns_count % 2) == 0);    /* checked above */
    4302         126 :         ns_count /= 2;          /* count pairs only */
    4303             :     }
    4304             :     else
    4305             :     {
    4306         428 :         ns_names_uris = NULL;
    4307         428 :         ns_names_uris_nulls = NULL;
    4308         428 :         ns_count = 0;
    4309             :     }
    4310             : 
    4311         554 :     datastr = VARDATA(data);
    4312         554 :     len = VARSIZE(data) - VARHDRSZ;
    4313         554 :     xpath_len = VARSIZE_ANY_EXHDR(xpath_expr_text);
    4314         554 :     if (xpath_len == 0)
    4315           6 :         ereport(ERROR,
    4316             :                 (errcode(ERRCODE_DATA_EXCEPTION),
    4317             :                  errmsg("empty XPath expression")));
    4318             : 
    4319         548 :     string = pg_xmlCharStrndup(datastr, len);
    4320         548 :     xpath_expr = pg_xmlCharStrndup(VARDATA_ANY(xpath_expr_text), xpath_len);
    4321             : 
    4322             :     /*
    4323             :      * In a UTF8 database, skip any xml declaration, which might assert
    4324             :      * another encoding.  Ignore parse_xml_decl() failure, letting
    4325             :      * xmlCtxtReadMemory() report parse errors.  Documentation disclaims
    4326             :      * xpath() support for non-ASCII data in non-UTF8 databases, so leave
    4327             :      * those scenarios bug-compatible with historical behavior.
    4328             :      */
    4329         548 :     if (GetDatabaseEncoding() == PG_UTF8)
    4330         184 :         parse_xml_decl(string, &xmldecl_len, NULL, NULL, NULL);
    4331             : 
    4332         548 :     xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
    4333             : 
    4334         548 :     PG_TRY();
    4335             :     {
    4336         548 :         xmlInitParser();
    4337             : 
    4338             :         /*
    4339             :          * redundant XML parsing (two parsings for the same value during one
    4340             :          * command execution are possible)
    4341             :          */
    4342         548 :         ctxt = xmlNewParserCtxt();
    4343         548 :         if (ctxt == NULL || xmlerrcxt->err_occurred)
    4344           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
    4345             :                         "could not allocate parser context");
    4346        1096 :         doc = xmlCtxtReadMemory(ctxt, (char *) string + xmldecl_len,
    4347         548 :                                 len - xmldecl_len, NULL, NULL, 0);
    4348         548 :         if (doc == NULL || xmlerrcxt->err_occurred)
    4349          12 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
    4350             :                         "could not parse XML document");
    4351         536 :         xpathctx = xmlXPathNewContext(doc);
    4352         536 :         if (xpathctx == NULL || xmlerrcxt->err_occurred)
    4353           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
    4354             :                         "could not allocate XPath context");
    4355         536 :         xpathctx->node = (xmlNodePtr) doc;
    4356             : 
    4357             :         /* register namespaces, if any */
    4358         536 :         if (ns_count > 0)
    4359             :         {
    4360         252 :             for (i = 0; i < ns_count; i++)
    4361             :             {
    4362             :                 char       *ns_name;
    4363             :                 char       *ns_uri;
    4364             : 
    4365         126 :                 if (ns_names_uris_nulls[i * 2] ||
    4366         126 :                     ns_names_uris_nulls[i * 2 + 1])
    4367           0 :                     ereport(ERROR,
    4368             :                             (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    4369             :                              errmsg("neither namespace name nor URI may be null")));
    4370         126 :                 ns_name = TextDatumGetCString(ns_names_uris[i * 2]);
    4371         126 :                 ns_uri = TextDatumGetCString(ns_names_uris[i * 2 + 1]);
    4372         126 :                 if (xmlXPathRegisterNs(xpathctx,
    4373             :                                        (xmlChar *) ns_name,
    4374             :                                        (xmlChar *) ns_uri) != 0)
    4375           0 :                     ereport(ERROR,  /* is this an internal error??? */
    4376             :                             (errmsg("could not register XML namespace with name \"%s\" and URI \"%s\"",
    4377             :                                     ns_name, ns_uri)));
    4378             :             }
    4379             :         }
    4380             : 
    4381         536 :         xpathcomp = xmlXPathCompile(xpath_expr);
    4382         536 :         if (xpathcomp == NULL || xmlerrcxt->err_occurred)
    4383           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
    4384             :                         "invalid XPath expression");
    4385             : 
    4386             :         /*
    4387             :          * Version 2.6.27 introduces a function named
    4388             :          * xmlXPathCompiledEvalToBoolean, which would be enough for xmlexists,
    4389             :          * but we can derive the existence by whether any nodes are returned,
    4390             :          * thereby preventing a library version upgrade and keeping the code
    4391             :          * the same.
    4392             :          */
    4393         536 :         xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
    4394         536 :         if (xpathobj == NULL || xmlerrcxt->err_occurred)
    4395           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
    4396             :                         "could not create XPath object");
    4397             : 
    4398             :         /*
    4399             :          * Extract the results as requested.
    4400             :          */
    4401         536 :         if (res_nitems != NULL)
    4402         432 :             *res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
    4403             :         else
    4404         104 :             (void) xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
    4405             :     }
    4406          12 :     PG_CATCH();
    4407             :     {
    4408          12 :         if (xpathobj)
    4409           0 :             xmlXPathFreeObject(xpathobj);
    4410          12 :         if (xpathcomp)
    4411           0 :             xmlXPathFreeCompExpr(xpathcomp);
    4412          12 :         if (xpathctx)
    4413           0 :             xmlXPathFreeContext(xpathctx);
    4414          12 :         if (doc)
    4415          12 :             xmlFreeDoc(doc);
    4416          12 :         if (ctxt)
    4417          12 :             xmlFreeParserCtxt(ctxt);
    4418             : 
    4419          12 :         pg_xml_done(xmlerrcxt, true);
    4420             : 
    4421          12 :         PG_RE_THROW();
    4422             :     }
    4423         536 :     PG_END_TRY();
    4424             : 
    4425         536 :     xmlXPathFreeObject(xpathobj);
    4426         536 :     xmlXPathFreeCompExpr(xpathcomp);
    4427         536 :     xmlXPathFreeContext(xpathctx);
    4428         536 :     xmlFreeDoc(doc);
    4429         536 :     xmlFreeParserCtxt(ctxt);
    4430             : 
    4431         536 :     pg_xml_done(xmlerrcxt, false);
    4432         536 : }
    4433             : #endif                          /* USE_LIBXML */
    4434             : 
    4435             : /*
    4436             :  * Evaluate XPath expression and return array of XML values.
    4437             :  *
    4438             :  * As we have no support of XQuery sequences yet, this function seems
    4439             :  * to be the most useful one (array of XML functions plays a role of
    4440             :  * some kind of substitution for XQuery sequences).
    4441             :  */
    4442             : Datum
    4443         122 : xpath(PG_FUNCTION_ARGS)
    4444             : {
    4445             : #ifdef USE_LIBXML
    4446         122 :     text       *xpath_expr_text = PG_GETARG_TEXT_PP(0);
    4447         122 :     xmltype    *data = PG_GETARG_XML_P(1);
    4448         122 :     ArrayType  *namespaces = PG_GETARG_ARRAYTYPE_P(2);
    4449             :     ArrayBuildState *astate;
    4450             : 
    4451         122 :     astate = initArrayResult(XMLOID, CurrentMemoryContext, true);
    4452         122 :     xpath_internal(xpath_expr_text, data, namespaces,
    4453             :                    NULL, astate);
    4454         104 :     PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
    4455             : #else
    4456             :     NO_XML_SUPPORT();
    4457             :     return 0;
    4458             : #endif
    4459             : }
    4460             : 
    4461             : /*
    4462             :  * Determines if the node specified by the supplied XPath exists
    4463             :  * in a given XML document, returning a boolean.
    4464             :  */
    4465             : Datum
    4466         198 : xmlexists(PG_FUNCTION_ARGS)
    4467             : {
    4468             : #ifdef USE_LIBXML
    4469         198 :     text       *xpath_expr_text = PG_GETARG_TEXT_PP(0);
    4470         198 :     xmltype    *data = PG_GETARG_XML_P(1);
    4471             :     int         res_nitems;
    4472             : 
    4473         198 :     xpath_internal(xpath_expr_text, data, NULL,
    4474             :                    &res_nitems, NULL);
    4475             : 
    4476         198 :     PG_RETURN_BOOL(res_nitems > 0);
    4477             : #else
    4478             :     NO_XML_SUPPORT();
    4479             :     return 0;
    4480             : #endif
    4481             : }
    4482             : 
    4483             : /*
    4484             :  * Determines if the node specified by the supplied XPath exists
    4485             :  * in a given XML document, returning a boolean. Differs from
    4486             :  * xmlexists as it supports namespaces and is not defined in SQL/XML.
    4487             :  */
    4488             : Datum
    4489         234 : xpath_exists(PG_FUNCTION_ARGS)
    4490             : {
    4491             : #ifdef USE_LIBXML
    4492         234 :     text       *xpath_expr_text = PG_GETARG_TEXT_PP(0);
    4493         234 :     xmltype    *data = PG_GETARG_XML_P(1);
    4494         234 :     ArrayType  *namespaces = PG_GETARG_ARRAYTYPE_P(2);
    4495             :     int         res_nitems;
    4496             : 
    4497         234 :     xpath_internal(xpath_expr_text, data, namespaces,
    4498             :                    &res_nitems, NULL);
    4499             : 
    4500         234 :     PG_RETURN_BOOL(res_nitems > 0);
    4501             : #else
    4502             :     NO_XML_SUPPORT();
    4503             :     return 0;
    4504             : #endif
    4505             : }
    4506             : 
    4507             : /*
    4508             :  * Functions for checking well-formed-ness
    4509             :  */
    4510             : 
    4511             : #ifdef USE_LIBXML
    4512             : static bool
    4513         114 : wellformed_xml(text *data, XmlOptionType xmloption_arg)
    4514             : {
    4515             :     xmlDocPtr   doc;
    4516         114 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
    4517             : 
    4518             :     /*
    4519             :      * We'll report "true" if no soft error is reported by xml_parse().
    4520             :      */
    4521         114 :     doc = xml_parse(data, xmloption_arg, true,
    4522             :                     GetDatabaseEncoding(), NULL, NULL, (Node *) &escontext);
    4523         114 :     if (doc)
    4524          60 :         xmlFreeDoc(doc);
    4525             : 
    4526         114 :     return !escontext.error_occurred;
    4527             : }
    4528             : #endif
    4529             : 
    4530             : Datum
    4531          90 : xml_is_well_formed(PG_FUNCTION_ARGS)
    4532             : {
    4533             : #ifdef USE_LIBXML
    4534          90 :     text       *data = PG_GETARG_TEXT_PP(0);
    4535             : 
    4536          90 :     PG_RETURN_BOOL(wellformed_xml(data, xmloption));
    4537             : #else
    4538             :     NO_XML_SUPPORT();
    4539             :     return 0;
    4540             : #endif                          /* not USE_LIBXML */
    4541             : }
    4542             : 
    4543             : Datum
    4544          12 : xml_is_well_formed_document(PG_FUNCTION_ARGS)
    4545             : {
    4546             : #ifdef USE_LIBXML
    4547          12 :     text       *data = PG_GETARG_TEXT_PP(0);
    4548             : 
    4549          12 :     PG_RETURN_BOOL(wellformed_xml(data, XMLOPTION_DOCUMENT));
    4550             : #else
    4551             :     NO_XML_SUPPORT();
    4552             :     return 0;
    4553             : #endif                          /* not USE_LIBXML */
    4554             : }
    4555             : 
    4556             : Datum
    4557          12 : xml_is_well_formed_content(PG_FUNCTION_ARGS)
    4558             : {
    4559             : #ifdef USE_LIBXML
    4560          12 :     text       *data = PG_GETARG_TEXT_PP(0);
    4561             : 
    4562          12 :     PG_RETURN_BOOL(wellformed_xml(data, XMLOPTION_CONTENT));
    4563             : #else
    4564             :     NO_XML_SUPPORT();
    4565             :     return 0;
    4566             : #endif                          /* not USE_LIBXML */
    4567             : }
    4568             : 
    4569             : /*
    4570             :  * support functions for XMLTABLE
    4571             :  *
    4572             :  */
    4573             : #ifdef USE_LIBXML
    4574             : 
    4575             : /*
    4576             :  * Returns private data from executor state. Ensure validity by check with
    4577             :  * MAGIC number.
    4578             :  */
    4579             : static inline XmlTableBuilderData *
    4580      155188 : GetXmlTableBuilderPrivateData(TableFuncScanState *state, const char *fname)
    4581             : {
    4582             :     XmlTableBuilderData *result;
    4583             : 
    4584      155188 :     if (!IsA(state, TableFuncScanState))
    4585           0 :         elog(ERROR, "%s called with invalid TableFuncScanState", fname);
    4586      155188 :     result = (XmlTableBuilderData *) state->opaque;
    4587      155188 :     if (result->magic != XMLTABLE_CONTEXT_MAGIC)
    4588           0 :         elog(ERROR, "%s called with invalid TableFuncScanState", fname);
    4589             : 
    4590      155188 :     return result;
    4591             : }
    4592             : #endif
    4593             : 
    4594             : /*
    4595             :  * XmlTableInitOpaque
    4596             :  *      Fill in TableFuncScanState->opaque for XmlTable processor; initialize
    4597             :  *      the XML parser.
    4598             :  *
    4599             :  * Note: Because we call pg_xml_init() here and pg_xml_done() in
    4600             :  * XmlTableDestroyOpaque, it is critical for robustness that no other
    4601             :  * executor nodes run until this node is processed to completion.  Caller
    4602             :  * must execute this to completion (probably filling a tuplestore to exhaust
    4603             :  * this node in a single pass) instead of using row-per-call mode.
    4604             :  */
    4605             : static void
    4606         264 : XmlTableInitOpaque(TableFuncScanState *state, int natts)
    4607             : {
    4608             : #ifdef USE_LIBXML
    4609         264 :     volatile xmlParserCtxtPtr ctxt = NULL;
    4610             :     XmlTableBuilderData *xtCxt;
    4611             :     PgXmlErrorContext *xmlerrcxt;
    4612             : 
    4613         264 :     xtCxt = palloc0(sizeof(XmlTableBuilderData));
    4614         264 :     xtCxt->magic = XMLTABLE_CONTEXT_MAGIC;
    4615         264 :     xtCxt->natts = natts;
    4616         264 :     xtCxt->xpathscomp = palloc0(sizeof(xmlXPathCompExprPtr) * natts);
    4617             : 
    4618         264 :     xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
    4619             : 
    4620         264 :     PG_TRY();
    4621             :     {
    4622         264 :         xmlInitParser();
    4623             : 
    4624         264 :         ctxt = xmlNewParserCtxt();
    4625         264 :         if (ctxt == NULL || xmlerrcxt->err_occurred)
    4626           0 :             xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
    4627             :                         "could not allocate parser context");
    4628             :     }
    4629           0 :     PG_CATCH();
    4630             :     {
    4631           0 :         if (ctxt != NULL)
    4632           0 :             xmlFreeParserCtxt(ctxt);
    4633             : 
    4634           0 :         pg_xml_done(xmlerrcxt, true);
    4635             : 
    4636           0 :         PG_RE_THROW();
    4637             :     }
    4638         264 :     PG_END_TRY();
    4639             : 
    4640         264 :     xtCxt->xmlerrcxt = xmlerrcxt;
    4641         264 :     xtCxt->ctxt = ctxt;
    4642             : 
    4643         264 :     state->opaque = xtCxt;
    4644             : #else
    4645             :     NO_XML_SUPPORT();
    4646             : #endif                          /* not USE_LIBXML */
    4647         264 : }
    4648             : 
    4649             : /*
    4650             :  * XmlTableSetDocument
    4651             :  *      Install the input document
    4652             :  */
    4653             : static void
    4654         264 : XmlTableSetDocument(TableFuncScanState *state, Datum value)
    4655             : {
    4656             : #ifdef USE_LIBXML
    4657             :     XmlTableBuilderData *xtCxt;
    4658         264 :     xmltype    *xmlval = DatumGetXmlP(value);
    4659             :     char       *str;
    4660             :     xmlChar    *xstr;
    4661             :     int         length;
    4662         264 :     volatile xmlDocPtr doc = NULL;
    4663         264 :     volatile xmlXPathContextPtr xpathcxt = NULL;
    4664             : 
    4665         264 :     xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableSetDocument");
    4666             : 
    4667             :     /*
    4668             :      * Use out function for casting to string (remove encoding property). See
    4669             :      * comment in xml_out.
    4670             :      */
    4671         264 :     str = xml_out_internal(xmlval, 0);
    4672             : 
    4673         264 :     length = strlen(str);
    4674         264 :     xstr = pg_xmlCharStrndup(str, length);
    4675             : 
    4676         264 :     PG_TRY();
    4677             :     {
    4678         264 :         doc = xmlCtxtReadMemory(xtCxt->ctxt, (char *) xstr, length, NULL, NULL, 0);
    4679         264 :         if (doc == NULL || xtCxt->xmlerrcxt->err_occurred)
    4680           0 :             xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
    4681             :                         "could not parse XML document");
    4682         264 :         xpathcxt = xmlXPathNewContext(doc);
    4683         264 :         if (xpathcxt == NULL || xtCxt->xmlerrcxt->err_occurred)
    4684           0 :             xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
    4685             :                         "could not allocate XPath context");
    4686         264 :         xpathcxt->node = (xmlNodePtr) doc;
    4687             :     }
    4688           0 :     PG_CATCH();
    4689             :     {
    4690           0 :         if (xpathcxt != NULL)
    4691           0 :             xmlXPathFreeContext(xpathcxt);
    4692           0 :         if (doc != NULL)
    4693           0 :             xmlFreeDoc(doc);
    4694             : 
    4695           0 :         PG_RE_THROW();
    4696             :     }
    4697         264 :     PG_END_TRY();
    4698             : 
    4699         264 :     xtCxt->doc = doc;
    4700         264 :     xtCxt->xpathcxt = xpathcxt;
    4701             : #else
    4702             :     NO_XML_SUPPORT();
    4703             : #endif                          /* not USE_LIBXML */
    4704         264 : }
    4705             : 
    4706             : /*
    4707             :  * XmlTableSetNamespace
    4708             :  *      Add a namespace declaration
    4709             :  */
    4710             : static void
    4711          18 : XmlTableSetNamespace(TableFuncScanState *state, const char *name, const char *uri)
    4712             : {
    4713             : #ifdef USE_LIBXML
    4714             :     XmlTableBuilderData *xtCxt;
    4715             : 
    4716          18 :     if (name == NULL)
    4717           6 :         ereport(ERROR,
    4718             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4719             :                  errmsg("DEFAULT namespace is not supported")));
    4720          12 :     xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableSetNamespace");
    4721             : 
    4722          12 :     if (xmlXPathRegisterNs(xtCxt->xpathcxt,
    4723          12 :                            pg_xmlCharStrndup(name, strlen(name)),
    4724          12 :                            pg_xmlCharStrndup(uri, strlen(uri))))
    4725           0 :         xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_DATA_EXCEPTION,
    4726             :                     "could not set XML namespace");
    4727             : #else
    4728             :     NO_XML_SUPPORT();
    4729             : #endif                          /* not USE_LIBXML */
    4730          12 : }
    4731             : 
    4732             : /*
    4733             :  * XmlTableSetRowFilter
    4734             :  *      Install the row-filter Xpath expression.
    4735             :  */
    4736             : static void
    4737         258 : XmlTableSetRowFilter(TableFuncScanState *state, const char *path)
    4738             : {
    4739             : #ifdef USE_LIBXML
    4740             :     XmlTableBuilderData *xtCxt;
    4741             :     xmlChar    *xstr;
    4742             : 
    4743         258 :     xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableSetRowFilter");
    4744             : 
    4745         258 :     if (*path == '\0')
    4746           0 :         ereport(ERROR,
    4747             :                 (errcode(ERRCODE_DATA_EXCEPTION),
    4748             :                  errmsg("row path filter must not be empty string")));
    4749             : 
    4750         258 :     xstr = pg_xmlCharStrndup(path, strlen(path));
    4751             : 
    4752         258 :     xtCxt->xpathcomp = xmlXPathCompile(xstr);
    4753         258 :     if (xtCxt->xpathcomp == NULL || xtCxt->xmlerrcxt->err_occurred)
    4754           0 :         xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_SYNTAX_ERROR,
    4755             :                     "invalid XPath expression");
    4756             : #else
    4757             :     NO_XML_SUPPORT();
    4758             : #endif                          /* not USE_LIBXML */
    4759         258 : }
    4760             : 
    4761             : /*
    4762             :  * XmlTableSetColumnFilter
    4763             :  *      Install the column-filter Xpath expression, for the given column.
    4764             :  */
    4765             : static void
    4766         774 : XmlTableSetColumnFilter(TableFuncScanState *state, const char *path, int colnum)
    4767             : {
    4768             : #ifdef USE_LIBXML
    4769             :     XmlTableBuilderData *xtCxt;
    4770             :     xmlChar    *xstr;
    4771             : 
    4772             :     Assert(PointerIsValid(path));
    4773             : 
    4774         774 :     xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableSetColumnFilter");
    4775             : 
    4776         774 :     if (*path == '\0')
    4777           0 :         ereport(ERROR,
    4778             :                 (errcode(ERRCODE_DATA_EXCEPTION),
    4779             :                  errmsg("column path filter must not be empty string")));
    4780             : 
    4781         774 :     xstr = pg_xmlCharStrndup(path, strlen(path));
    4782             : 
    4783         774 :     xtCxt->xpathscomp[colnum] = xmlXPathCompile(xstr);
    4784         774 :     if (xtCxt->xpathscomp[colnum] == NULL || xtCxt->xmlerrcxt->err_occurred)
    4785           0 :         xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_DATA_EXCEPTION,
    4786             :                     "invalid XPath expression");
    4787             : #else
    4788             :     NO_XML_SUPPORT();
    4789             : #endif                          /* not USE_LIBXML */
    4790         774 : }
    4791             : 
    4792             : /*
    4793             :  * XmlTableFetchRow
    4794             :  *      Prepare the next "current" tuple for upcoming GetValue calls.
    4795             :  *      Returns false if the row-filter expression returned no more rows.
    4796             :  */
    4797             : static bool
    4798       22306 : XmlTableFetchRow(TableFuncScanState *state)
    4799             : {
    4800             : #ifdef USE_LIBXML
    4801             :     XmlTableBuilderData *xtCxt;
    4802             : 
    4803       22306 :     xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableFetchRow");
    4804             : 
    4805             :     /* Propagate our own error context to libxml2 */
    4806       22306 :     xmlSetStructuredErrorFunc((void *) xtCxt->xmlerrcxt, xml_errorHandler);
    4807             : 
    4808       22306 :     if (xtCxt->xpathobj == NULL)
    4809             :     {
    4810         258 :         xtCxt->xpathobj = xmlXPathCompiledEval(xtCxt->xpathcomp, xtCxt->xpathcxt);
    4811         258 :         if (xtCxt->xpathobj == NULL || xtCxt->xmlerrcxt->err_occurred)
    4812           0 :             xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
    4813             :                         "could not create XPath object");
    4814             : 
    4815         258 :         xtCxt->row_count = 0;
    4816             :     }
    4817             : 
    4818       22306 :     if (xtCxt->xpathobj->type == XPATH_NODESET)
    4819             :     {
    4820       22306 :         if (xtCxt->xpathobj->nodesetval != NULL)
    4821             :         {
    4822       22306 :             if (xtCxt->row_count++ < xtCxt->xpathobj->nodesetval->nodeNr)
    4823       22060 :                 return true;
    4824             :         }
    4825             :     }
    4826             : 
    4827         246 :     return false;
    4828             : #else
    4829             :     NO_XML_SUPPORT();
    4830             :     return false;
    4831             : #endif                          /* not USE_LIBXML */
    4832             : }
    4833             : 
    4834             : /*
    4835             :  * XmlTableGetValue
    4836             :  *      Return the value for column number 'colnum' for the current row.  If
    4837             :  *      column -1 is requested, return representation of the whole row.
    4838             :  *
    4839             :  * This leaks memory, so be sure to reset often the context in which it's
    4840             :  * called.
    4841             :  */
    4842             : static Datum
    4843      131310 : XmlTableGetValue(TableFuncScanState *state, int colnum,
    4844             :                  Oid typid, int32 typmod, bool *isnull)
    4845             : {
    4846             : #ifdef USE_LIBXML
    4847             :     XmlTableBuilderData *xtCxt;
    4848      131310 :     Datum       result = (Datum) 0;
    4849             :     xmlNodePtr  cur;
    4850      131310 :     char       *cstr = NULL;
    4851      131310 :     volatile xmlXPathObjectPtr xpathobj = NULL;
    4852             : 
    4853      131310 :     xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableGetValue");
    4854             : 
    4855             :     Assert(xtCxt->xpathobj &&
    4856             :            xtCxt->xpathobj->type == XPATH_NODESET &&
    4857             :            xtCxt->xpathobj->nodesetval != NULL);
    4858             : 
    4859             :     /* Propagate our own error context to libxml2 */
    4860      131310 :     xmlSetStructuredErrorFunc((void *) xtCxt->xmlerrcxt, xml_errorHandler);
    4861             : 
    4862      131310 :     *isnull = false;
    4863             : 
    4864      131310 :     cur = xtCxt->xpathobj->nodesetval->nodeTab[xtCxt->row_count - 1];
    4865             : 
    4866             :     Assert(xtCxt->xpathscomp[colnum] != NULL);
    4867             : 
    4868      131310 :     PG_TRY();
    4869             :     {
    4870             :         /* Set current node as entry point for XPath evaluation */
    4871      131310 :         xtCxt->xpathcxt->node = cur;
    4872             : 
    4873             :         /* Evaluate column path */
    4874      131310 :         xpathobj = xmlXPathCompiledEval(xtCxt->xpathscomp[colnum], xtCxt->xpathcxt);
    4875      131310 :         if (xpathobj == NULL || xtCxt->xmlerrcxt->err_occurred)
    4876           0 :             xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
    4877             :                         "could not create XPath object");
    4878             : 
    4879             :         /*
    4880             :          * There are four possible cases, depending on the number of nodes
    4881             :          * returned by the XPath expression and the type of the target column:
    4882             :          * a) XPath returns no nodes.  b) The target type is XML (return all
    4883             :          * as XML).  For non-XML return types:  c) One node (return content).
    4884             :          * d) Multiple nodes (error).
    4885             :          */
    4886      131310 :         if (xpathobj->type == XPATH_NODESET)
    4887             :         {
    4888      131280 :             int         count = 0;
    4889             : 
    4890      131280 :             if (xpathobj->nodesetval != NULL)
    4891      131070 :                 count = xpathobj->nodesetval->nodeNr;
    4892             : 
    4893      131280 :             if (xpathobj->nodesetval == NULL || count == 0)
    4894             :             {
    4895       22194 :                 *isnull = true;
    4896             :             }
    4897             :             else
    4898             :             {
    4899      109086 :                 if (typid == XMLOID)
    4900             :                 {
    4901             :                     text       *textstr;
    4902             :                     StringInfoData str;
    4903             : 
    4904             :                     /* Concatenate serialized values */
    4905          72 :                     initStringInfo(&str);
    4906         174 :                     for (int i = 0; i < count; i++)
    4907             :                     {
    4908             :                         textstr =
    4909         102 :                             xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
    4910             :                                                  xtCxt->xmlerrcxt);
    4911             : 
    4912         102 :                         appendStringInfoText(&str, textstr);
    4913             :                     }
    4914          72 :                     cstr = str.data;
    4915             :                 }
    4916             :                 else
    4917             :                 {
    4918             :                     xmlChar    *str;
    4919             : 
    4920      109014 :                     if (count > 1)
    4921           6 :                         ereport(ERROR,
    4922             :                                 (errcode(ERRCODE_CARDINALITY_VIOLATION),
    4923             :                                  errmsg("more than one value returned by column XPath expression")));
    4924             : 
    4925      109008 :                     str = xmlXPathCastNodeSetToString(xpathobj->nodesetval);
    4926      109008 :                     cstr = str ? xml_pstrdup_and_free(str) : "";
    4927             :                 }
    4928             :             }
    4929             :         }
    4930          30 :         else if (xpathobj->type == XPATH_STRING)
    4931             :         {
    4932             :             /* Content should be escaped when target will be XML */
    4933          18 :             if (typid == XMLOID)
    4934           6 :                 cstr = escape_xml((char *) xpathobj->stringval);
    4935             :             else
    4936          12 :                 cstr = (char *) xpathobj->stringval;
    4937             :         }
    4938          12 :         else if (xpathobj->type == XPATH_BOOLEAN)
    4939             :         {
    4940             :             char        typcategory;
    4941             :             bool        typispreferred;
    4942             :             xmlChar    *str;
    4943             : 
    4944             :             /* Allow implicit casting from boolean to numbers */
    4945           6 :             get_type_category_preferred(typid, &typcategory, &typispreferred);
    4946             : 
    4947           6 :             if (typcategory != TYPCATEGORY_NUMERIC)
    4948           6 :                 str = xmlXPathCastBooleanToString(xpathobj->boolval);
    4949             :             else
    4950           0 :                 str = xmlXPathCastNumberToString(xmlXPathCastBooleanToNumber(xpathobj->boolval));
    4951             : 
    4952           6 :             cstr = xml_pstrdup_and_free(str);
    4953             :         }
    4954           6 :         else if (xpathobj->type == XPATH_NUMBER)
    4955             :         {
    4956             :             xmlChar    *str;
    4957             : 
    4958           6 :             str = xmlXPathCastNumberToString(xpathobj->floatval);
    4959           6 :             cstr = xml_pstrdup_and_free(str);
    4960             :         }
    4961             :         else
    4962           0 :             elog(ERROR, "unexpected XPath object type %u", xpathobj->type);
    4963             : 
    4964             :         /*
    4965             :          * By here, either cstr contains the result value, or the isnull flag
    4966             :          * has been set.
    4967             :          */
    4968             :         Assert(cstr || *isnull);
    4969             : 
    4970      131304 :         if (!*isnull)
    4971      109110 :             result = InputFunctionCall(&state->in_functions[colnum],
    4972             :                                        cstr,
    4973      109110 :                                        state->typioparams[colnum],
    4974             :                                        typmod);
    4975             :     }
    4976           6 :     PG_FINALLY();
    4977             :     {
    4978      131310 :         if (xpathobj != NULL)
    4979      131310 :             xmlXPathFreeObject(xpathobj);
    4980             :     }
    4981      131310 :     PG_END_TRY();
    4982             : 
    4983      131304 :     return result;
    4984             : #else
    4985             :     NO_XML_SUPPORT();
    4986             :     return 0;
    4987             : #endif                          /* not USE_LIBXML */
    4988             : }
    4989             : 
    4990             : /*
    4991             :  * XmlTableDestroyOpaque
    4992             :  *      Release all libxml2 resources
    4993             :  */
    4994             : static void
    4995         264 : XmlTableDestroyOpaque(TableFuncScanState *state)
    4996             : {
    4997             : #ifdef USE_LIBXML
    4998             :     XmlTableBuilderData *xtCxt;
    4999             : 
    5000         264 :     xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableDestroyOpaque");
    5001             : 
    5002             :     /* Propagate our own error context to libxml2 */
    5003         264 :     xmlSetStructuredErrorFunc((void *) xtCxt->xmlerrcxt, xml_errorHandler);
    5004             : 
    5005         264 :     if (xtCxt->xpathscomp != NULL)
    5006             :     {
    5007             :         int         i;
    5008             : 
    5009        1116 :         for (i = 0; i < xtCxt->natts; i++)
    5010         852 :             if (xtCxt->xpathscomp[i] != NULL)
    5011         774 :                 xmlXPathFreeCompExpr(xtCxt->xpathscomp[i]);
    5012             :     }
    5013             : 
    5014         264 :     if (xtCxt->xpathobj != NULL)
    5015         258 :         xmlXPathFreeObject(xtCxt->xpathobj);
    5016         264 :     if (xtCxt->xpathcomp != NULL)
    5017         258 :         xmlXPathFreeCompExpr(xtCxt->xpathcomp);
    5018         264 :     if (xtCxt->xpathcxt != NULL)
    5019         264 :         xmlXPathFreeContext(xtCxt->xpathcxt);
    5020         264 :     if (xtCxt->doc != NULL)
    5021         264 :         xmlFreeDoc(xtCxt->doc);
    5022         264 :     if (xtCxt->ctxt != NULL)
    5023         264 :         xmlFreeParserCtxt(xtCxt->ctxt);
    5024             : 
    5025         264 :     pg_xml_done(xtCxt->xmlerrcxt, true);
    5026             : 
    5027             :     /* not valid anymore */
    5028         264 :     xtCxt->magic = 0;
    5029         264 :     state->opaque = NULL;
    5030             : 
    5031             : #else
    5032             :     NO_XML_SUPPORT();
    5033             : #endif                          /* not USE_LIBXML */
    5034         264 : }

Generated by: LCOV version 1.14