Coverage Report

Created: 2026-08-14 06:37

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