PostgreSQL Source Code git master
Loading...
Searching...
No Matches
xml.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "access/table.h"
#include "catalog/namespace.h"
#include "catalog/pg_class.h"
#include "catalog/pg_type.h"
#include "executor/spi.h"
#include "executor/tablefunc.h"
#include "fmgr.h"
#include "lib/stringinfo.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/execnodes.h"
#include "nodes/miscnodes.h"
#include "nodes/nodeFuncs.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/xml.h"
Include dependency graph for xml.c:

Go to the source code of this file.

Macros

#define NO_XML_SUPPORT()
 
#define NAMESPACE_XSD   "http://www.w3.org/2001/XMLSchema"
 
#define NAMESPACE_XSI   "http://www.w3.org/2001/XMLSchema-instance"
 
#define NAMESPACE_SQLXML   "http://standards.iso.org/iso/9075/2003/sqlxml"
 
#define PG_XML_DEFAULT_VERSION   "1.0"
 
#define XML_VISIBLE_SCHEMAS_EXCLUDE   "(nspname ~ '^pg_' OR nspname = 'information_schema')"
 
#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
 

Functions

static void xmldata_root_element_start (StringInfo result, const char *eltname, const char *xmlschema, const char *targetns, bool top_level)
 
static void xmldata_root_element_end (StringInfo result, const char *eltname)
 
static StringInfo query_to_xml_internal (const char *query, char *tablename, const char *xmlschema, bool nulls, bool tableforest, const char *targetns, bool top_level)
 
static const charmap_sql_table_to_xmlschema (TupleDesc tupdesc, Oid relid, bool nulls, bool tableforest, const char *targetns)
 
static const charmap_sql_schema_to_xmlschema_types (Oid nspid, List *relid_list, bool nulls, bool tableforest, const char *targetns)
 
static const charmap_sql_catalog_to_xmlschema_types (List *nspid_list, bool nulls, bool tableforest, const char *targetns)
 
static const charmap_sql_type_to_xml_name (Oid typeoid, int typmod)
 
static const charmap_sql_typecoll_to_xmlschema_types (List *tupdesc_list)
 
static const charmap_sql_type_to_xmlschema_type (Oid typeoid, int typmod)
 
static void SPI_sql_row_to_xmlelement (uint64 rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns, bool top_level)
 
static void XmlTableInitOpaque (struct TableFuncScanState *state, int natts)
 
static void XmlTableSetDocument (struct TableFuncScanState *state, Datum value)
 
static void XmlTableSetNamespace (struct TableFuncScanState *state, const char *name, const char *uri)
 
static void XmlTableSetRowFilter (struct TableFuncScanState *state, const char *path)
 
static void XmlTableSetColumnFilter (struct TableFuncScanState *state, const char *path, int colnum)
 
static bool XmlTableFetchRow (struct TableFuncScanState *state)
 
static Datum XmlTableGetValue (struct TableFuncScanState *state, int colnum, Oid typid, int32 typmod, bool *isnull)
 
static void XmlTableDestroyOpaque (struct TableFuncScanState *state)
 
Datum xml_in (PG_FUNCTION_ARGS)
 
static charxml_out_internal (xmltype *x, pg_enc target_encoding)
 
Datum xml_out (PG_FUNCTION_ARGS)
 
Datum xml_recv (PG_FUNCTION_ARGS)
 
Datum xml_send (PG_FUNCTION_ARGS)
 
static xmltypestringinfo_to_xmltype (StringInfo buf)
 
static xmltypecstring_to_xmltype (const char *string)
 
Datum xmlcomment (PG_FUNCTION_ARGS)
 
Datum xmltext (PG_FUNCTION_ARGS)
 
xmltypexmlconcat (List *args)
 
Datum xmlconcat2 (PG_FUNCTION_ARGS)
 
Datum texttoxml (PG_FUNCTION_ARGS)
 
Datum xmltotext (PG_FUNCTION_ARGS)
 
textxmltotext_with_options (xmltype *data, XmlOptionType xmloption_arg, bool indent)
 
xmltypexmlelement (XmlExpr *xexpr, const Datum *named_argvalue, const bool *named_argnull, const Datum *argvalue, const bool *argnull)
 
xmltypexmlparse (text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, Node *escontext)
 
xmltypexmlpi (const char *target, text *arg, bool arg_is_null, bool *result_is_null)
 
xmltypexmlroot (xmltype *data, text *version, int standalone)
 
Datum xmlvalidate (PG_FUNCTION_ARGS)
 
bool xml_is_document (xmltype *arg)
 
charmap_sql_identifier_to_xml_name (const char *ident, bool fully_escaped, bool escape_period)
 
charmap_xml_name_to_sql_identifier (const char *name)
 
charmap_sql_value_to_xml_value (Datum value, Oid type, bool xml_escape_strings)
 
charescape_xml (const char *str)
 
static char_SPI_strdup (const char *s)
 
static Listquery_to_oid_list (const char *query)
 
static Listschema_get_xml_visible_tables (Oid nspid)
 
static Listdatabase_get_xml_visible_schemas (void)
 
static Listdatabase_get_xml_visible_tables (void)
 
static StringInfo table_to_xml_internal (Oid relid, const char *xmlschema, bool nulls, bool tableforest, const char *targetns, bool top_level)
 
Datum table_to_xml (PG_FUNCTION_ARGS)
 
Datum query_to_xml (PG_FUNCTION_ARGS)
 
Datum cursor_to_xml (PG_FUNCTION_ARGS)
 
Datum table_to_xmlschema (PG_FUNCTION_ARGS)
 
Datum query_to_xmlschema (PG_FUNCTION_ARGS)
 
Datum cursor_to_xmlschema (PG_FUNCTION_ARGS)
 
Datum table_to_xml_and_xmlschema (PG_FUNCTION_ARGS)
 
Datum query_to_xml_and_xmlschema (PG_FUNCTION_ARGS)
 
static StringInfo schema_to_xml_internal (Oid nspid, const char *xmlschema, bool nulls, bool tableforest, const char *targetns, bool top_level)
 
Datum schema_to_xml (PG_FUNCTION_ARGS)
 
static void xsd_schema_element_start (StringInfo result, const char *targetns)
 
static void xsd_schema_element_end (StringInfo result)
 
static StringInfo schema_to_xmlschema_internal (const char *schemaname, bool nulls, bool tableforest, const char *targetns)
 
Datum schema_to_xmlschema (PG_FUNCTION_ARGS)
 
Datum schema_to_xml_and_xmlschema (PG_FUNCTION_ARGS)
 
static StringInfo database_to_xml_internal (const char *xmlschema, bool nulls, bool tableforest, const char *targetns)
 
Datum database_to_xml (PG_FUNCTION_ARGS)
 
static StringInfo database_to_xmlschema_internal (bool nulls, bool tableforest, const char *targetns)
 
Datum database_to_xmlschema (PG_FUNCTION_ARGS)
 
Datum database_to_xml_and_xmlschema (PG_FUNCTION_ARGS)
 
static charmap_multipart_sql_identifier_to_xml_name (const char *a, const char *b, const char *c, const char *d)
 
Datum xpath (PG_FUNCTION_ARGS)
 
Datum xmlexists (PG_FUNCTION_ARGS)
 
Datum xpath_exists (PG_FUNCTION_ARGS)
 
Datum xml_is_well_formed (PG_FUNCTION_ARGS)
 
Datum xml_is_well_formed_document (PG_FUNCTION_ARGS)
 
Datum xml_is_well_formed_content (PG_FUNCTION_ARGS)
 

Variables

int xmlbinary = XMLBINARY_BASE64
 
int xmloption = XMLOPTION_CONTENT
 
const TableFuncRoutine XmlTableRoutine
 

Macro Definition Documentation

◆ NAMESPACE_SQLXML

#define NAMESPACE_SQLXML   "http://standards.iso.org/iso/9075/2003/sqlxml"

Definition at line 244 of file xml.c.

◆ NAMESPACE_XSD

#define NAMESPACE_XSD   "http://www.w3.org/2001/XMLSchema"

Definition at line 242 of file xml.c.

◆ NAMESPACE_XSI

#define NAMESPACE_XSI   "http://www.w3.org/2001/XMLSchema-instance"

Definition at line 243 of file xml.c.

◆ NO_XML_SUPPORT

#define NO_XML_SUPPORT ( )
Value:
errmsg("unsupported XML feature"), \
errdetail("This functionality requires the server to be built with libxml support.")))
int errcode(int sqlerrcode)
Definition elog.c:874
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:40
#define ereport(elevel,...)
Definition elog.h:152
static char * errmsg
static int fb(int x)

Definition at line 234 of file xml.c.

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

◆ PG_XML_DEFAULT_VERSION

#define PG_XML_DEFAULT_VERSION   "1.0"

Definition at line 300 of file xml.c.

◆ XML_VISIBLE_SCHEMAS

#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

Definition at line 2880 of file xml.c.

◆ XML_VISIBLE_SCHEMAS_EXCLUDE

#define XML_VISIBLE_SCHEMAS_EXCLUDE   "(nspname ~ '^pg_' OR nspname = 'information_schema')"

Definition at line 2878 of file xml.c.

Function Documentation

◆ _SPI_strdup()

static char * _SPI_strdup ( const char s)
static

Definition at line 2771 of file xml.c.

2772{
2773 size_t len = strlen(s) + 1;
2774 char *ret = SPI_palloc(len);
2775
2776 memcpy(ret, s, len);
2777 return ret;
2778}

References fb(), len, memcpy(), and SPI_palloc().

Referenced by cursor_to_xmlschema(), query_to_xml_and_xmlschema(), and query_to_xmlschema().

◆ cstring_to_xmltype()

static xmltype * cstring_to_xmltype ( const char string)
static

Definition at line 474 of file xml.c.

475{
476 return (xmltype *) cstring_to_text(string);
477}

References cstring_to_text().

Referenced by cursor_to_xmlschema(), query_to_xmlschema(), and table_to_xmlschema().

◆ cursor_to_xml()

Datum cursor_to_xml ( PG_FUNCTION_ARGS  )

Definition at line 2955 of file xml.c.

2956{
2958 int32 count = PG_GETARG_INT32(1);
2959 bool nulls = PG_GETARG_BOOL(2);
2960 bool tableforest = PG_GETARG_BOOL(3);
2961 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(4));
2962
2964 Portal portal;
2965 uint64 i;
2966
2968
2969 if (!tableforest)
2970 {
2973 }
2974
2975 SPI_connect();
2976 portal = SPI_cursor_find(name);
2977 if (portal == NULL)
2978 ereport(ERROR,
2980 errmsg("cursor \"%s\" does not exist", name)));
2981
2982 SPI_cursor_fetch(portal, true, count);
2983 for (i = 0; i < SPI_processed; i++)
2985 tableforest, targetns, true);
2986
2987 SPI_finish();
2988
2989 if (!tableforest)
2991
2993}

References appendStringInfoChar(), ereport, errcode(), errmsg, ERROR, fb(), i, initStringInfo(), name, PG_GETARG_BOOL, PG_GETARG_INT32, PG_GETARG_TEXT_PP, PG_RETURN_XML_P, result, SPI_connect(), SPI_cursor_fetch(), SPI_cursor_find(), SPI_finish(), SPI_processed, SPI_sql_row_to_xmlelement(), stringinfo_to_xmltype(), text_to_cstring(), xmldata_root_element_end(), and xmldata_root_element_start().

◆ cursor_to_xmlschema()

Datum cursor_to_xmlschema ( PG_FUNCTION_ARGS  )

Definition at line 3136 of file xml.c.

3137{
3139 bool nulls = PG_GETARG_BOOL(1);
3140 bool tableforest = PG_GETARG_BOOL(2);
3141 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
3142 const char *xmlschema;
3143 Portal portal;
3144
3145 SPI_connect();
3146 portal = SPI_cursor_find(name);
3147 if (portal == NULL)
3148 ereport(ERROR,
3150 errmsg("cursor \"%s\" does not exist", name)));
3151 if (portal->tupDesc == NULL)
3152 ereport(ERROR,
3154 errmsg("portal \"%s\" does not return tuples", name)));
3155
3157 InvalidOid, nulls,
3159 SPI_finish();
3160
3162}

References _SPI_strdup(), cstring_to_xmltype(), ereport, errcode(), errmsg, ERROR, fb(), InvalidOid, map_sql_table_to_xmlschema(), name, PG_GETARG_BOOL, PG_GETARG_TEXT_PP, PG_RETURN_XML_P, SPI_connect(), SPI_cursor_find(), SPI_finish(), text_to_cstring(), and PortalData::tupDesc.

◆ database_get_xml_visible_schemas()

static List * database_get_xml_visible_schemas ( void  )
static

Definition at line 2884 of file xml.c.

2885{
2886 return query_to_oid_list(XML_VISIBLE_SCHEMAS " ORDER BY nspname;");
2887}

References query_to_oid_list(), and XML_VISIBLE_SCHEMAS.

Referenced by database_to_xml_internal(), and database_to_xmlschema_internal().

◆ database_get_xml_visible_tables()

static List * database_get_xml_visible_tables ( void  )
static

Definition at line 2891 of file xml.c.

2892{
2893 /* At the moment there is no order required here. */
2894 return query_to_oid_list("SELECT oid FROM pg_catalog.pg_class"
2895 " WHERE relkind IN ("
2899 " AND pg_catalog.has_table_privilege(pg_class.oid, 'SELECT')"
2900 " AND relnamespace IN (" XML_VISIBLE_SCHEMAS ");");
2901}

References CppAsString2, fb(), query_to_oid_list(), and XML_VISIBLE_SCHEMAS.

Referenced by database_to_xmlschema_internal().

◆ database_to_xml()

◆ database_to_xml_and_xmlschema()

◆ database_to_xml_internal()

static StringInfo database_to_xml_internal ( const char xmlschema,
bool  nulls,
bool  tableforest,
const char targetns 
)
static

◆ database_to_xmlschema()

◆ database_to_xmlschema_internal()

static StringInfo database_to_xmlschema_internal ( bool  nulls,
bool  tableforest,
const char targetns 
)
static

◆ escape_xml()

char * escape_xml ( const char str)

Definition at line 2739 of file xml.c.

2740{
2742 const char *p;
2743
2745 for (p = str; *p; p++)
2746 {
2747 switch (*p)
2748 {
2749 case '&':
2750 appendStringInfoString(&buf, "&amp;");
2751 break;
2752 case '<':
2753 appendStringInfoString(&buf, "&lt;");
2754 break;
2755 case '>':
2756 appendStringInfoString(&buf, "&gt;");
2757 break;
2758 case '\r':
2759 appendStringInfoString(&buf, "&#x0d;");
2760 break;
2761 default:
2763 break;
2764 }
2765 }
2766 return buf.data;
2767}

References appendStringInfoCharMacro, appendStringInfoString(), buf, initStringInfo(), and str.

Referenced by ExplainProperty(), ExplainPropertyList(), map_sql_value_to_xml_value(), and XmlTableGetValue().

◆ map_multipart_sql_identifier_to_xml_name()

static char * map_multipart_sql_identifier_to_xml_name ( const char a,
const char b,
const char c,
const char d 
)
static

Definition at line 3527 of file xml.c.

3528{
3530
3532
3533 if (a)
3535 map_sql_identifier_to_xml_name(a, true, true));
3536 if (b)
3537 appendStringInfo(&result, ".%s",
3538 map_sql_identifier_to_xml_name(b, true, true));
3539 if (c)
3540 appendStringInfo(&result, ".%s",
3541 map_sql_identifier_to_xml_name(c, true, true));
3542 if (d)
3543 appendStringInfo(&result, ".%s",
3544 map_sql_identifier_to_xml_name(d, true, true));
3545
3546 return result.data;
3547}

References a, appendStringInfo(), appendStringInfoString(), b, initStringInfo(), map_sql_identifier_to_xml_name(), and result.

Referenced by map_sql_catalog_to_xmlschema_types(), map_sql_schema_to_xmlschema_types(), map_sql_table_to_xmlschema(), and map_sql_type_to_xml_name().

◆ map_sql_catalog_to_xmlschema_types()

static const char * map_sql_catalog_to_xmlschema_types ( List nspid_list,
bool  nulls,
bool  tableforest,
const char targetns 
)
static

Definition at line 3736 of file xml.c.

3738{
3739 char *dbname;
3740 char *xmlcn;
3741 char *catalogtypename;
3743 ListCell *cell;
3744
3746
3748
3750
3752 dbname,
3753 NULL,
3754 NULL);
3755
3757 "<xsd:complexType name=\"%s\">\n", catalogtypename);
3759 " <xsd:all>\n");
3760
3761 foreach(cell, nspid_list)
3762 {
3763 Oid nspid = lfirst_oid(cell);
3764 char *nspname = get_namespace_name(nspid);
3765 char *xmlsn = map_sql_identifier_to_xml_name(nspname, true, false);
3767 dbname,
3768 nspname,
3769 NULL);
3770
3772 " <xsd:element name=\"%s\" type=\"%s\"/>\n",
3774 }
3775
3777 " </xsd:all>\n");
3779 "</xsd:complexType>\n\n");
3780
3782 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
3784
3785 return result.data;
3786}

References appendStringInfo(), appendStringInfoString(), dbname, fb(), get_database_name(), get_namespace_name(), initStringInfo(), lfirst_oid, map_multipart_sql_identifier_to_xml_name(), map_sql_identifier_to_xml_name(), MyDatabaseId, nspid, and result.

Referenced by database_to_xmlschema_internal().

◆ map_sql_identifier_to_xml_name()

char * map_sql_identifier_to_xml_name ( const char ident,
bool  fully_escaped,
bool  escape_period 
)

Definition at line 2422 of file xml.c.

2424{
2425#ifdef USE_LIBXML
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 */
2434
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 {
2456
2457 if ((p == ident)
2460 appendStringInfo(&buf, "_x%04X_", (unsigned int) u);
2461 else
2463 }
2464 }
2465
2466 return buf.data;
2467#else /* not USE_LIBXML */
2469 return NULL;
2470#endif /* not USE_LIBXML */
2471}

References appendBinaryStringInfo(), appendStringInfo(), appendStringInfoString(), Assert, buf, fb(), ident, initStringInfo(), NO_XML_SUPPORT, pg_mblen_cstr(), and pg_strncasecmp().

Referenced by database_to_xml_internal(), map_multipart_sql_identifier_to_xml_name(), map_sql_catalog_to_xmlschema_types(), map_sql_schema_to_xmlschema_types(), map_sql_table_to_xmlschema(), query_to_xml_internal(), schema_to_xml_internal(), SPI_sql_row_to_xmlelement(), and transformXmlExpr().

◆ map_sql_schema_to_xmlschema_types()

static const char * map_sql_schema_to_xmlschema_types ( Oid  nspid,
List relid_list,
bool  nulls,
bool  tableforest,
const char targetns 
)
static

Definition at line 3663 of file xml.c.

3665{
3666 char *dbname;
3667 char *nspname;
3668 char *xmlsn;
3669 char *schematypename;
3671 ListCell *cell;
3672
3674 nspname = get_namespace_name(nspid);
3675
3677
3678 xmlsn = map_sql_identifier_to_xml_name(nspname, true, false);
3679
3681 dbname,
3682 nspname,
3683 NULL);
3684
3686 "<xsd:complexType name=\"%s\">\n", schematypename);
3687 if (!tableforest)
3689 " <xsd:all>\n");
3690 else
3692 " <xsd:sequence>\n");
3693
3694 foreach(cell, relid_list)
3695 {
3696 Oid relid = lfirst_oid(cell);
3697 char *relname = get_rel_name(relid);
3698 char *xmltn = map_sql_identifier_to_xml_name(relname, true, false);
3699 char *tabletypename = map_multipart_sql_identifier_to_xml_name(tableforest ? "RowType" : "TableType",
3700 dbname,
3701 nspname,
3702 relname);
3703
3704 if (!tableforest)
3706 " <xsd:element name=\"%s\" type=\"%s\"/>\n",
3708 else
3710 " <xsd:element name=\"%s\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n",
3712 }
3713
3714 if (!tableforest)
3716 " </xsd:all>\n");
3717 else
3719 " </xsd:sequence>\n");
3721 "</xsd:complexType>\n\n");
3722
3724 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
3726
3727 return result.data;
3728}

References appendStringInfo(), appendStringInfoString(), dbname, fb(), get_database_name(), get_namespace_name(), get_rel_name(), initStringInfo(), lfirst_oid, map_multipart_sql_identifier_to_xml_name(), map_sql_identifier_to_xml_name(), MyDatabaseId, nspid, relname, and result.

Referenced by schema_to_xmlschema_internal().

◆ map_sql_table_to_xmlschema()

static const char * map_sql_table_to_xmlschema ( TupleDesc  tupdesc,
Oid  relid,
bool  nulls,
bool  tableforest,
const char targetns 
)
static

Definition at line 3558 of file xml.c.

3560{
3561 int i;
3562 char *xmltn;
3563 char *tabletypename;
3564 char *rowtypename;
3566
3568
3569 if (OidIsValid(relid))
3570 {
3571 HeapTuple tuple;
3573
3574 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3575 if (!HeapTupleIsValid(tuple))
3576 elog(ERROR, "cache lookup failed for relation %u", relid);
3577 reltuple = (Form_pg_class) GETSTRUCT(tuple);
3578
3580 true, false);
3581
3584 get_namespace_name(reltuple->relnamespace),
3585 NameStr(reltuple->relname));
3586
3589 get_namespace_name(reltuple->relnamespace),
3590 NameStr(reltuple->relname));
3591
3592 ReleaseSysCache(tuple);
3593 }
3594 else
3595 {
3596 if (tableforest)
3597 xmltn = "row";
3598 else
3599 xmltn = "table";
3600
3601 tabletypename = "TableType";
3602 rowtypename = "RowType";
3603 }
3604
3606
3609
3611 "<xsd:complexType name=\"%s\">\n"
3612 " <xsd:sequence>\n",
3613 rowtypename);
3614
3615 for (i = 0; i < tupdesc->natts; i++)
3616 {
3617 Form_pg_attribute att = TupleDescAttr(tupdesc, i);
3618
3619 if (att->attisdropped)
3620 continue;
3622 " <xsd:element name=\"%s\" type=\"%s\"%s></xsd:element>\n",
3624 true, false),
3625 map_sql_type_to_xml_name(att->atttypid, -1),
3626 nulls ? " nillable=\"true\"" : " minOccurs=\"0\"");
3627 }
3628
3630 " </xsd:sequence>\n"
3631 "</xsd:complexType>\n\n");
3632
3633 if (!tableforest)
3634 {
3636 "<xsd:complexType name=\"%s\">\n"
3637 " <xsd:sequence>\n"
3638 " <xsd:element name=\"row\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n"
3639 " </xsd:sequence>\n"
3640 "</xsd:complexType>\n\n",
3642
3644 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
3646 }
3647 else
3649 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
3651
3653
3654 return result.data;
3655}

References appendStringInfo(), appendStringInfoString(), elog, ERROR, fb(), get_database_name(), get_namespace_name(), GETSTRUCT(), HeapTupleIsValid, i, initStringInfo(), list_make1, map_multipart_sql_identifier_to_xml_name(), map_sql_identifier_to_xml_name(), map_sql_type_to_xml_name(), map_sql_typecoll_to_xmlschema_types(), MyDatabaseId, NameStr, TupleDescData::natts, ObjectIdGetDatum(), OidIsValid, ReleaseSysCache(), result, SearchSysCache1(), TupleDescAttr(), xsd_schema_element_end(), and xsd_schema_element_start().

Referenced by cursor_to_xmlschema(), query_to_xml_and_xmlschema(), query_to_xmlschema(), table_to_xml_and_xmlschema(), and table_to_xmlschema().

◆ map_sql_type_to_xml_name()

static const char * map_sql_type_to_xml_name ( Oid  typeoid,
int  typmod 
)
static

Definition at line 3793 of file xml.c.

3794{
3796
3798
3799 switch (typeoid)
3800 {
3801 case BPCHAROID:
3802 if (typmod == -1)
3804 else
3805 appendStringInfo(&result, "CHAR_%d", typmod - VARHDRSZ);
3806 break;
3807 case VARCHAROID:
3808 if (typmod == -1)
3809 appendStringInfoString(&result, "VARCHAR");
3810 else
3811 appendStringInfo(&result, "VARCHAR_%d", typmod - VARHDRSZ);
3812 break;
3813 case NUMERICOID:
3814 if (typmod == -1)
3815 appendStringInfoString(&result, "NUMERIC");
3816 else
3817 appendStringInfo(&result, "NUMERIC_%d_%d",
3818 ((typmod - VARHDRSZ) >> 16) & 0xffff,
3819 (typmod - VARHDRSZ) & 0xffff);
3820 break;
3821 case INT4OID:
3822 appendStringInfoString(&result, "INTEGER");
3823 break;
3824 case INT2OID:
3825 appendStringInfoString(&result, "SMALLINT");
3826 break;
3827 case INT8OID:
3828 appendStringInfoString(&result, "BIGINT");
3829 break;
3830 case FLOAT4OID:
3832 break;
3833 case FLOAT8OID:
3834 appendStringInfoString(&result, "DOUBLE");
3835 break;
3836 case BOOLOID:
3837 appendStringInfoString(&result, "BOOLEAN");
3838 break;
3839 case TIMEOID:
3840 if (typmod == -1)
3842 else
3843 appendStringInfo(&result, "TIME_%d", typmod);
3844 break;
3845 case TIMETZOID:
3846 if (typmod == -1)
3847 appendStringInfoString(&result, "TIME_WTZ");
3848 else
3849 appendStringInfo(&result, "TIME_WTZ_%d", typmod);
3850 break;
3851 case TIMESTAMPOID:
3852 if (typmod == -1)
3853 appendStringInfoString(&result, "TIMESTAMP");
3854 else
3855 appendStringInfo(&result, "TIMESTAMP_%d", typmod);
3856 break;
3857 case TIMESTAMPTZOID:
3858 if (typmod == -1)
3859 appendStringInfoString(&result, "TIMESTAMP_WTZ");
3860 else
3861 appendStringInfo(&result, "TIMESTAMP_WTZ_%d", typmod);
3862 break;
3863 case DATEOID:
3865 break;
3866 case XMLOID:
3868 break;
3869 default:
3870 {
3871 HeapTuple tuple;
3873
3874 tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeoid));
3875 if (!HeapTupleIsValid(tuple))
3876 elog(ERROR, "cache lookup failed for type %u", typeoid);
3877 typtuple = (Form_pg_type) GETSTRUCT(tuple);
3878
3880 map_multipart_sql_identifier_to_xml_name((typtuple->typtype == TYPTYPE_DOMAIN) ? "Domain" : "UDT",
3884
3885 ReleaseSysCache(tuple);
3886 }
3887 }
3888
3889 return result.data;
3890}

References appendStringInfo(), appendStringInfoString(), elog, ERROR, fb(), Form_pg_type, get_database_name(), get_namespace_name(), GETSTRUCT(), HeapTupleIsValid, initStringInfo(), map_multipart_sql_identifier_to_xml_name(), MyDatabaseId, NameStr, ObjectIdGetDatum(), ReleaseSysCache(), result, SearchSysCache1(), and VARHDRSZ.

Referenced by map_sql_table_to_xmlschema(), and map_sql_type_to_xmlschema_type().

◆ map_sql_type_to_xmlschema_type()

static const char * map_sql_type_to_xmlschema_type ( Oid  typeoid,
int  typmod 
)
static

Definition at line 3953 of file xml.c.

3954{
3956 const char *typename = map_sql_type_to_xml_name(typeoid, typmod);
3957
3959
3960 if (typeoid == XMLOID)
3961 {
3963 "<xsd:complexType mixed=\"true\">\n"
3964 " <xsd:sequence>\n"
3965 " <xsd:any name=\"element\" minOccurs=\"0\" maxOccurs=\"unbounded\" processContents=\"skip\"/>\n"
3966 " </xsd:sequence>\n"
3967 "</xsd:complexType>\n");
3968 }
3969 else
3970 {
3972 "<xsd:simpleType name=\"%s\">\n", typename);
3973
3974 switch (typeoid)
3975 {
3976 case BPCHAROID:
3977 case VARCHAROID:
3978 case TEXTOID:
3980 " <xsd:restriction base=\"xsd:string\">\n");
3981 if (typmod != -1)
3983 " <xsd:maxLength value=\"%d\"/>\n",
3984 typmod - VARHDRSZ);
3985 appendStringInfoString(&result, " </xsd:restriction>\n");
3986 break;
3987
3988 case BYTEAOID:
3990 " <xsd:restriction base=\"xsd:%s\">\n"
3991 " </xsd:restriction>\n",
3992 xmlbinary == XMLBINARY_BASE64 ? "base64Binary" : "hexBinary");
3993 break;
3994
3995 case NUMERICOID:
3996 if (typmod != -1)
3998 " <xsd:restriction base=\"xsd:decimal\">\n"
3999 " <xsd:totalDigits value=\"%d\"/>\n"
4000 " <xsd:fractionDigits value=\"%d\"/>\n"
4001 " </xsd:restriction>\n",
4002 ((typmod - VARHDRSZ) >> 16) & 0xffff,
4003 (typmod - VARHDRSZ) & 0xffff);
4004 break;
4005
4006 case INT2OID:
4008 " <xsd:restriction base=\"xsd:short\">\n"
4009 " <xsd:maxInclusive value=\"%d\"/>\n"
4010 " <xsd:minInclusive value=\"%d\"/>\n"
4011 " </xsd:restriction>\n",
4013 break;
4014
4015 case INT4OID:
4017 " <xsd:restriction base=\"xsd:int\">\n"
4018 " <xsd:maxInclusive value=\"%d\"/>\n"
4019 " <xsd:minInclusive value=\"%d\"/>\n"
4020 " </xsd:restriction>\n",
4021 INT_MAX, INT_MIN);
4022 break;
4023
4024 case INT8OID:
4026 " <xsd:restriction base=\"xsd:long\">\n"
4027 " <xsd:maxInclusive value=\"" INT64_FORMAT "\"/>\n"
4028 " <xsd:minInclusive value=\"" INT64_FORMAT "\"/>\n"
4029 " </xsd:restriction>\n",
4031 PG_INT64_MIN);
4032 break;
4033
4034 case FLOAT4OID:
4036 " <xsd:restriction base=\"xsd:float\"></xsd:restriction>\n");
4037 break;
4038
4039 case FLOAT8OID:
4041 " <xsd:restriction base=\"xsd:double\"></xsd:restriction>\n");
4042 break;
4043
4044 case BOOLOID:
4046 " <xsd:restriction base=\"xsd:boolean\"></xsd:restriction>\n");
4047 break;
4048
4049 case TIMEOID:
4050 case TIMETZOID:
4051 {
4052 const char *tz = (typeoid == TIMETZOID ? "(\\+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
4053
4054 if (typmod == -1)
4056 " <xsd:restriction base=\"xsd:time\">\n"
4057 " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}(.\\p{Nd}+)?%s\"/>\n"
4058 " </xsd:restriction>\n", tz);
4059 else if (typmod == 0)
4061 " <xsd:restriction base=\"xsd:time\">\n"
4062 " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
4063 " </xsd:restriction>\n", tz);
4064 else
4066 " <xsd:restriction base=\"xsd:time\">\n"
4067 " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}.\\p{Nd}{%d}%s\"/>\n"
4068 " </xsd:restriction>\n", typmod - VARHDRSZ, tz);
4069 break;
4070 }
4071
4072 case TIMESTAMPOID:
4073 case TIMESTAMPTZOID:
4074 {
4075 const char *tz = (typeoid == TIMESTAMPTZOID ? "(\\+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
4076
4077 if (typmod == -1)
4079 " <xsd:restriction base=\"xsd:dateTime\">\n"
4080 " <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 " </xsd:restriction>\n", tz);
4082 else if (typmod == 0)
4084 " <xsd:restriction base=\"xsd:dateTime\">\n"
4085 " <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 " </xsd:restriction>\n", tz);
4087 else
4089 " <xsd:restriction base=\"xsd:dateTime\">\n"
4090 " <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 " </xsd:restriction>\n", typmod - VARHDRSZ, tz);
4092 break;
4093 }
4094
4095 case DATEOID:
4097 " <xsd:restriction base=\"xsd:date\">\n"
4098 " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}\"/>\n"
4099 " </xsd:restriction>\n");
4100 break;
4101
4102 default:
4103 if (get_typtype(typeoid) == TYPTYPE_DOMAIN)
4104 {
4106 int32 base_typmod = -1;
4107
4108 base_typeoid = getBaseTypeAndTypmod(typeoid, &base_typmod);
4109
4111 " <xsd:restriction base=\"%s\"/>\n",
4113 }
4114 break;
4115 }
4116 appendStringInfoString(&result, "</xsd:simpleType>\n");
4117 }
4118
4119 return result.data;
4120}

References appendStringInfo(), appendStringInfoString(), fb(), get_typtype(), getBaseTypeAndTypmod(), initStringInfo(), INT64_FORMAT, map_sql_type_to_xml_name(), PG_INT64_MAX, PG_INT64_MIN, result, VARHDRSZ, xmlbinary, and XMLBINARY_BASE64.

Referenced by map_sql_typecoll_to_xmlschema_types().

◆ map_sql_typecoll_to_xmlschema_types()

static const char * map_sql_typecoll_to_xmlschema_types ( List tupdesc_list)
static

Definition at line 3898 of file xml.c.

3899{
3900 List *uniquetypes = NIL;
3901 int i;
3903 ListCell *cell0;
3904
3905 /* extract all column types used in the set of TupleDescs */
3906 foreach(cell0, tupdesc_list)
3907 {
3908 TupleDesc tupdesc = (TupleDesc) lfirst(cell0);
3909
3910 for (i = 0; i < tupdesc->natts; i++)
3911 {
3912 Form_pg_attribute att = TupleDescAttr(tupdesc, i);
3913
3914 if (att->attisdropped)
3915 continue;
3917 }
3918 }
3919
3920 /* add base types of domains */
3921 foreach(cell0, uniquetypes)
3922 {
3923 Oid typid = lfirst_oid(cell0);
3924 Oid basetypid = getBaseType(typid);
3925
3926 if (basetypid != typid)
3928 }
3929
3930 /* Convert to textual form */
3932
3933 foreach(cell0, uniquetypes)
3934 {
3935 appendStringInfo(&result, "%s\n",
3937 -1));
3938 }
3939
3940 return result.data;
3941}

References appendStringInfo(), fb(), getBaseType(), i, initStringInfo(), lfirst, lfirst_oid, list_append_unique_oid(), map_sql_type_to_xmlschema_type(), TupleDescData::natts, NIL, result, and TupleDescAttr().

Referenced by database_to_xmlschema_internal(), map_sql_table_to_xmlschema(), and schema_to_xmlschema_internal().

◆ map_sql_value_to_xml_value()

char * map_sql_value_to_xml_value ( Datum  value,
Oid  type,
bool  xml_escape_strings 
)

Definition at line 2520 of file xml.c.

2521{
2523 {
2524 ArrayType *array;
2525 Oid elmtype;
2526 int16 elmlen;
2527 bool elmbyval;
2528 char elmalign;
2529 int num_elems;
2530 Datum *elem_values;
2531 bool *elem_nulls;
2533 int i;
2534
2535 array = DatumGetArrayTypeP(value);
2536 elmtype = ARR_ELEMTYPE(array);
2537 get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
2538
2540 elmlen, elmbyval, elmalign,
2541 &elem_values, &elem_nulls,
2542 &num_elems);
2543
2545
2546 for (i = 0; i < num_elems; i++)
2547 {
2548 if (elem_nulls[i])
2549 continue;
2550 appendStringInfoString(&buf, "<element>");
2552 map_sql_value_to_xml_value(elem_values[i],
2553 elmtype, true));
2554 appendStringInfoString(&buf, "</element>");
2555 }
2556
2557 pfree(elem_values);
2558 pfree(elem_nulls);
2559
2560 return buf.data;
2561 }
2562 else
2563 {
2564 Oid typeOut;
2565 bool isvarlena;
2566 char *str;
2567
2568 /*
2569 * Flatten domains; the special-case treatments below should apply to,
2570 * eg, domains over boolean not just boolean.
2571 */
2573
2574 /*
2575 * Special XSD formatting for some data types
2576 */
2577 switch (type)
2578 {
2579 case BOOLOID:
2580 if (DatumGetBool(value))
2581 return "true";
2582 else
2583 return "false";
2584
2585 case DATEOID:
2586 {
2587 DateADT date;
2588 struct pg_tm tm;
2589 char buf[MAXDATELEN + 1];
2590
2592 /* XSD doesn't support infinite values */
2593 if (DATE_NOT_FINITE(date))
2594 ereport(ERROR,
2596 errmsg("date out of range"),
2597 errdetail("XML does not support infinite date values.")));
2599 &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
2601
2602 return pstrdup(buf);
2603 }
2604
2605 case TIMESTAMPOID:
2606 {
2608 struct pg_tm tm;
2609 fsec_t fsec;
2610 char buf[MAXDATELEN + 1];
2611
2613
2614 /* XSD doesn't support infinite values */
2616 ereport(ERROR,
2618 errmsg("timestamp out of range"),
2619 errdetail("XML does not support infinite timestamp values.")));
2620 else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
2621 EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
2622 else
2623 ereport(ERROR,
2625 errmsg("timestamp out of range")));
2626
2627 return pstrdup(buf);
2628 }
2629
2630 case TIMESTAMPTZOID:
2631 {
2633 struct pg_tm tm;
2634 int tz;
2635 fsec_t fsec;
2636 const char *tzn = NULL;
2637 char buf[MAXDATELEN + 1];
2638
2640
2641 /* XSD doesn't support infinite values */
2643 ereport(ERROR,
2645 errmsg("timestamp out of range"),
2646 errdetail("XML does not support infinite timestamp values.")));
2647 else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
2648 EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
2649 else
2650 ereport(ERROR,
2652 errmsg("timestamp out of range")));
2653
2654 return pstrdup(buf);
2655 }
2656
2657#ifdef USE_LIBXML
2658 case BYTEAOID:
2659 {
2662 volatile xmlBufferPtr buf = NULL;
2663 volatile xmlTextWriterPtr writer = NULL;
2664 char *result;
2665
2667
2668 PG_TRY();
2669 {
2670 buf = xmlBufferCreate();
2671 if (buf == NULL || xmlerrcxt->err_occurred)
2673 "could not allocate xmlBuffer");
2675 if (writer == NULL || xmlerrcxt->err_occurred)
2677 "could not allocate xmlTextWriter");
2678
2682 else
2685
2686 /* we MUST do this now to flush data out to the buffer */
2688 writer = NULL;
2689
2690 result = pstrdup((const char *) xmlBufferContent(buf));
2691 }
2692 PG_CATCH();
2693 {
2694 if (writer)
2696 if (buf)
2698
2699 pg_xml_done(xmlerrcxt, true);
2700
2701 PG_RE_THROW();
2702 }
2703 PG_END_TRY();
2704
2706
2707 pg_xml_done(xmlerrcxt, false);
2708
2709 return result;
2710 }
2711#endif /* USE_LIBXML */
2712
2713 }
2714
2715 /*
2716 * otherwise, just use the type's native text representation
2717 */
2720
2721 /* ... exactly as-is for XML, and when escaping is not wanted */
2722 if (type == XMLOID || !xml_escape_strings)
2723 return str;
2724
2725 /* otherwise, translate special characters as needed */
2726 return escape_xml(str);
2727 }
2728}

References appendStringInfoString(), ARR_ELEMTYPE, buf, DATE_NOT_FINITE, DatumGetArrayTypeP, DatumGetBool(), DatumGetByteaPP, DatumGetDateADT(), DatumGetTimestamp(), deconstruct_array(), EncodeDateOnly(), EncodeDateTime(), ereport, errcode(), errdetail(), errmsg, ERROR, escape_xml(), fb(), get_typlenbyvalalign(), getBaseType(), getTypeOutputInfo(), i, initStringInfo(), j2date(), map_sql_value_to_xml_value(), MAXDATELEN, OidOutputFunctionCall(), pfree(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pg_xml_done(), pg_xml_init(), PG_XML_STRICTNESS_ALL, POSTGRES_EPOCH_JDATE, pstrdup(), result, str, timestamp2tm(), TIMESTAMP_NOT_FINITE, tm, pg_tm::tm_mday, pg_tm::tm_mon, pg_tm::tm_year, type, type_is_array_domain, USE_XSD_DATES, value, VARDATA_ANY(), VARSIZE_ANY_EXHDR(), xml_ereport(), xmlbinary, and XMLBINARY_BASE64.

Referenced by ExecEvalXmlExpr(), map_sql_value_to_xml_value(), SPI_sql_row_to_xmlelement(), and xmlelement().

◆ map_xml_name_to_sql_identifier()

char * map_xml_name_to_sql_identifier ( const char name)

Definition at line 2478 of file xml.c.

2479{
2481 const char *p;
2482
2484
2485 for (p = name; *p; p += pg_mblen_cstr(p))
2486 {
2487 if (*p == '_' && *(p + 1) == 'x'
2488 && isxdigit((unsigned char) *(p + 2))
2489 && isxdigit((unsigned char) *(p + 3))
2490 && isxdigit((unsigned char) *(p + 4))
2491 && isxdigit((unsigned char) *(p + 5))
2492 && *(p + 6) == '_')
2493 {
2495 unsigned int u;
2496
2497 sscanf(p + 2, "%X", &u);
2498 pg_unicode_to_server(u, (unsigned char *) cbuf);
2500 p += 6;
2501 }
2502 else
2504 }
2505
2506 return buf.data;
2507}

References appendBinaryStringInfo(), appendStringInfoString(), buf, fb(), initStringInfo(), MAX_UNICODE_EQUIVALENT_STRING, name, pg_mblen_cstr(), and pg_unicode_to_server().

Referenced by get_rule_expr().

◆ query_to_oid_list()

static List * query_to_oid_list ( const char query)
static

Definition at line 2828 of file xml.c.

2829{
2830 uint64 i;
2831 List *list = NIL;
2832 int spi_result;
2833
2834 spi_result = SPI_execute(query, true, 0);
2836 elog(ERROR, "SPI_execute returned %s for %s",
2838
2839 for (i = 0; i < SPI_processed; i++)
2840 {
2841 Datum oid;
2842 bool isnull;
2843
2846 1,
2847 &isnull);
2848 if (!isnull)
2849 list = lappend_oid(list, DatumGetObjectId(oid));
2850 }
2851
2852 return list;
2853}

References DatumGetObjectId(), elog, ERROR, fb(), i, lappend_oid(), NIL, SPI_execute(), SPI_getbinval(), SPI_OK_SELECT, SPI_processed, SPI_result_code_string(), SPI_tuptable, SPITupleTable::tupdesc, and SPITupleTable::vals.

Referenced by database_get_xml_visible_schemas(), database_get_xml_visible_tables(), and schema_get_xml_visible_tables().

◆ query_to_xml()

Datum query_to_xml ( PG_FUNCTION_ARGS  )

Definition at line 2941 of file xml.c.

2942{
2943 char *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
2944 bool nulls = PG_GETARG_BOOL(1);
2945 bool tableforest = PG_GETARG_BOOL(2);
2946 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2947
2949 NULL, nulls, tableforest,
2950 targetns, true)));
2951}

References fb(), PG_GETARG_BOOL, PG_GETARG_TEXT_PP, PG_RETURN_XML_P, query_to_xml_internal(), stringinfo_to_xmltype(), and text_to_cstring().

◆ query_to_xml_and_xmlschema()

Datum query_to_xml_and_xmlschema ( PG_FUNCTION_ARGS  )

Definition at line 3187 of file xml.c.

3188{
3189 char *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
3190 bool nulls = PG_GETARG_BOOL(1);
3191 bool tableforest = PG_GETARG_BOOL(2);
3192 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
3193
3194 const char *xmlschema;
3196 Portal portal;
3197
3198 SPI_connect();
3199
3200 if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
3201 elog(ERROR, "SPI_prepare(\"%s\") failed", query);
3202
3203 if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
3204 elog(ERROR, "SPI_cursor_open(\"%s\") failed", query);
3205
3207 InvalidOid, nulls, tableforest, targetns));
3208 SPI_cursor_close(portal);
3209 SPI_finish();
3210
3212 xmlschema, nulls, tableforest,
3213 targetns, true)));
3214}

References _SPI_strdup(), elog, ERROR, fb(), InvalidOid, map_sql_table_to_xmlschema(), PG_GETARG_BOOL, PG_GETARG_TEXT_PP, PG_RETURN_XML_P, plan, query_to_xml_internal(), SPI_connect(), SPI_cursor_close(), SPI_cursor_open(), SPI_finish(), SPI_prepare(), stringinfo_to_xmltype(), text_to_cstring(), and PortalData::tupDesc.

◆ query_to_xml_internal()

static StringInfo query_to_xml_internal ( const char query,
char tablename,
const char xmlschema,
bool  nulls,
bool  tableforest,
const char targetns,
bool  top_level 
)
static

Definition at line 3043 of file xml.c.

3046{
3048 char *xmltn;
3049 uint64 i;
3050
3051 if (tablename)
3052 xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
3053 else
3054 xmltn = "table";
3055
3057
3058 SPI_connect();
3059 if (SPI_execute(query, true, 0) != SPI_OK_SELECT)
3060 ereport(ERROR,
3062 errmsg("invalid query")));
3063
3064 if (!tableforest)
3065 {
3067 targetns, top_level);
3069 }
3070
3071 if (xmlschema)
3072 appendStringInfo(result, "%s\n\n", xmlschema);
3073
3074 for (i = 0; i < SPI_processed; i++)
3075 SPI_sql_row_to_xmlelement(i, result, tablename, nulls,
3076 tableforest, targetns, top_level);
3077
3078 if (!tableforest)
3080
3081 SPI_finish();
3082
3083 return result;
3084}

References appendStringInfo(), appendStringInfoChar(), ereport, errcode(), errmsg, ERROR, fb(), i, makeStringInfo(), map_sql_identifier_to_xml_name(), result, SPI_connect(), SPI_execute(), SPI_finish(), SPI_OK_SELECT, SPI_processed, SPI_sql_row_to_xmlelement(), xmldata_root_element_end(), and xmldata_root_element_start().

Referenced by query_to_xml(), query_to_xml_and_xmlschema(), and table_to_xml_internal().

◆ query_to_xmlschema()

Datum query_to_xmlschema ( PG_FUNCTION_ARGS  )

Definition at line 3107 of file xml.c.

3108{
3109 char *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
3110 bool nulls = PG_GETARG_BOOL(1);
3111 bool tableforest = PG_GETARG_BOOL(2);
3112 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
3113 const char *result;
3115 Portal portal;
3116
3117 SPI_connect();
3118
3119 if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
3120 elog(ERROR, "SPI_prepare(\"%s\") failed", query);
3121
3122 if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
3123 elog(ERROR, "SPI_cursor_open(\"%s\") failed", query);
3124
3126 InvalidOid, nulls,
3128 SPI_cursor_close(portal);
3129 SPI_finish();
3130
3132}

References _SPI_strdup(), cstring_to_xmltype(), elog, ERROR, fb(), InvalidOid, map_sql_table_to_xmlschema(), PG_GETARG_BOOL, PG_GETARG_TEXT_PP, PG_RETURN_XML_P, plan, result, SPI_connect(), SPI_cursor_close(), SPI_cursor_open(), SPI_finish(), SPI_prepare(), text_to_cstring(), and PortalData::tupDesc.

◆ schema_get_xml_visible_tables()

static List * schema_get_xml_visible_tables ( Oid  nspid)
static

Definition at line 2857 of file xml.c.

2858{
2859 StringInfoData query;
2860
2861 initStringInfo(&query);
2862 appendStringInfo(&query, "SELECT oid FROM pg_catalog.pg_class"
2863 " WHERE relnamespace = %u AND relkind IN ("
2867 " AND pg_catalog.has_table_privilege (oid, 'SELECT')"
2868 " ORDER BY relname;", nspid);
2869
2870 return query_to_oid_list(query.data);
2871}

References appendStringInfo(), CppAsString2, StringInfoData::data, fb(), initStringInfo(), nspid, and query_to_oid_list().

Referenced by schema_to_xml_internal(), and schema_to_xmlschema_internal().

◆ schema_to_xml()

Datum schema_to_xml ( PG_FUNCTION_ARGS  )

Definition at line 3266 of file xml.c.

3267{
3269 bool nulls = PG_GETARG_BOOL(1);
3270 bool tableforest = PG_GETARG_BOOL(2);
3271 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
3272
3273 char *schemaname;
3274 Oid nspid;
3275
3276 schemaname = NameStr(*name);
3277 nspid = LookupExplicitNamespace(schemaname, false);
3278
3280 nulls, tableforest, targetns, true)));
3281}

References fb(), LookupExplicitNamespace(), name, NameStr, nspid, PG_GETARG_BOOL, PG_GETARG_NAME, PG_GETARG_TEXT_PP, PG_RETURN_XML_P, schema_to_xml_internal(), stringinfo_to_xmltype(), and text_to_cstring().

◆ schema_to_xml_and_xmlschema()

Datum schema_to_xml_and_xmlschema ( PG_FUNCTION_ARGS  )

Definition at line 3370 of file xml.c.

3371{
3373 bool nulls = PG_GETARG_BOOL(1);
3374 bool tableforest = PG_GETARG_BOOL(2);
3375 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
3376 char *schemaname;
3377 Oid nspid;
3379
3380 schemaname = NameStr(*name);
3381 nspid = LookupExplicitNamespace(schemaname, false);
3382
3383 xmlschema = schema_to_xmlschema_internal(schemaname, nulls,
3385
3387 xmlschema->data, nulls,
3388 tableforest, targetns, true)));
3389}

References fb(), LookupExplicitNamespace(), name, NameStr, nspid, PG_GETARG_BOOL, PG_GETARG_NAME, PG_GETARG_TEXT_PP, PG_RETURN_XML_P, schema_to_xml_internal(), schema_to_xmlschema_internal(), stringinfo_to_xmltype(), and text_to_cstring().

◆ schema_to_xml_internal()

static StringInfo schema_to_xml_internal ( Oid  nspid,
const char xmlschema,
bool  nulls,
bool  tableforest,
const char targetns,
bool  top_level 
)
static

◆ schema_to_xmlschema()

◆ schema_to_xmlschema_internal()

static StringInfo schema_to_xmlschema_internal ( const char schemaname,
bool  nulls,
bool  tableforest,
const char targetns 
)
static

◆ SPI_sql_row_to_xmlelement()

static void SPI_sql_row_to_xmlelement ( uint64  rownum,
StringInfo  result,
char tablename,
bool  nulls,
bool  tableforest,
const char targetns,
bool  top_level 
)
static

Definition at line 4128 of file xml.c.

4131{
4132 int i;
4133 char *xmltn;
4134
4135 if (tablename)
4136 xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
4137 else
4138 {
4139 if (tableforest)
4140 xmltn = "row";
4141 else
4142 xmltn = "table";
4143 }
4144
4145 if (tableforest)
4147 else
4148 appendStringInfoString(result, "<row>\n");
4149
4150 for (i = 1; i <= SPI_tuptable->tupdesc->natts; i++)
4151 {
4152 char *colname;
4153 Datum colval;
4154 bool isnull;
4155
4157 true, false);
4160 i,
4161 &isnull);
4162 if (isnull)
4163 {
4164 if (nulls)
4165 appendStringInfo(result, " <%s xsi:nil=\"true\"/>\n", colname);
4166 }
4167 else
4168 appendStringInfo(result, " <%s>%s</%s>\n",
4169 colname,
4172 colname);
4173 }
4174
4175 if (tableforest)
4176 {
4179 }
4180 else
4181 appendStringInfoString(result, "</row>\n\n");
4182}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), fb(), i, map_sql_identifier_to_xml_name(), map_sql_value_to_xml_value(), TupleDescData::natts, result, SPI_fname(), SPI_getbinval(), SPI_gettypeid(), SPI_tuptable, SPITupleTable::tupdesc, SPITupleTable::vals, xmldata_root_element_end(), and xmldata_root_element_start().

Referenced by cursor_to_xml(), and query_to_xml_internal().

◆ stringinfo_to_xmltype()

◆ table_to_xml()

Datum table_to_xml ( PG_FUNCTION_ARGS  )

Definition at line 2927 of file xml.c.

2928{
2929 Oid relid = PG_GETARG_OID(0);
2930 bool nulls = PG_GETARG_BOOL(1);
2931 bool tableforest = PG_GETARG_BOOL(2);
2932 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
2933
2935 nulls, tableforest,
2936 targetns, true)));
2937}

References fb(), PG_GETARG_BOOL, PG_GETARG_OID, PG_GETARG_TEXT_PP, PG_RETURN_XML_P, stringinfo_to_xmltype(), table_to_xml_internal(), and text_to_cstring().

◆ table_to_xml_and_xmlschema()

Datum table_to_xml_and_xmlschema ( PG_FUNCTION_ARGS  )

◆ table_to_xml_internal()

static StringInfo table_to_xml_internal ( Oid  relid,
const char xmlschema,
bool  nulls,
bool  tableforest,
const char targetns,
bool  top_level 
)
static

◆ table_to_xmlschema()

Datum table_to_xmlschema ( PG_FUNCTION_ARGS  )

◆ texttoxml()

Datum texttoxml ( PG_FUNCTION_ARGS  )

Definition at line 659 of file xml.c.

660{
662
663 PG_RETURN_XML_P(xmlparse(data, xmloption, true, fcinfo->context));
664}

References data, PG_GETARG_TEXT_PP, PG_RETURN_XML_P, xmloption, and xmlparse().

◆ xml_in()

Datum xml_in ( PG_FUNCTION_ARGS  )

Definition at line 272 of file xml.c.

273{
274#ifdef USE_LIBXML
275 char *s = PG_GETARG_CSTRING(0);
278
279 /* Build the result object. */
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 */
288 NULL, NULL, fcinfo->context);
289 if (doc != NULL)
291
293#else
295 return 0;
296#endif
297}

References cstring_to_text(), fb(), GetDatabaseEncoding(), NO_XML_SUPPORT, PG_GETARG_CSTRING, PG_RETURN_XML_P, and xmloption.

◆ xml_is_document()

bool xml_is_document ( xmltype arg)

Definition at line 1172 of file xml.c.

1173{
1174#ifdef USE_LIBXML
1175 xmlDocPtr doc;
1177
1178 /*
1179 * We'll report "true" if no soft error is reported by xml_parse().
1180 */
1182 GetDatabaseEncoding(), NULL, NULL, (Node *) &escontext);
1183 if (doc)
1184 xmlFreeDoc(doc);
1185
1186 return !escontext.error_occurred;
1187#else /* not USE_LIBXML */
1189 return false;
1190#endif /* not USE_LIBXML */
1191}

References arg, ErrorSaveContext::error_occurred, fb(), GetDatabaseEncoding(), NO_XML_SUPPORT, and XMLOPTION_DOCUMENT.

Referenced by ExecEvalXmlExpr().

◆ xml_is_well_formed()

Datum xml_is_well_formed ( PG_FUNCTION_ARGS  )

Definition at line 4658 of file xml.c.

4659{
4660#ifdef USE_LIBXML
4662
4664#else
4666 return 0;
4667#endif /* not USE_LIBXML */
4668}

References data, fb(), NO_XML_SUPPORT, PG_GETARG_TEXT_PP, PG_RETURN_BOOL, and xmloption.

◆ xml_is_well_formed_content()

Datum xml_is_well_formed_content ( PG_FUNCTION_ARGS  )

Definition at line 4684 of file xml.c.

4685{
4686#ifdef USE_LIBXML
4688
4690#else
4692 return 0;
4693#endif /* not USE_LIBXML */
4694}

References data, fb(), NO_XML_SUPPORT, PG_GETARG_TEXT_PP, PG_RETURN_BOOL, and XMLOPTION_CONTENT.

◆ xml_is_well_formed_document()

Datum xml_is_well_formed_document ( PG_FUNCTION_ARGS  )

Definition at line 4671 of file xml.c.

4672{
4673#ifdef USE_LIBXML
4675
4677#else
4679 return 0;
4680#endif /* not USE_LIBXML */
4681}

References data, fb(), NO_XML_SUPPORT, PG_GETARG_TEXT_PP, PG_RETURN_BOOL, and XMLOPTION_DOCUMENT.

◆ xml_out()

Datum xml_out ( PG_FUNCTION_ARGS  )

Definition at line 355 of file xml.c.

356{
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 */
366}

References PG_GETARG_XML_P, PG_RETURN_CSTRING, x, and xml_out_internal().

◆ xml_out_internal()

static char * xml_out_internal ( xmltype x,
pg_enc  target_encoding 
)
static

Definition at line 311 of file xml.c.

312{
313 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
322 &len, &version, NULL, &standalone)) == 0)
323 {
325
327
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 }
339
340 pfree(str);
341
342 return buf.data;
343 }
344
347 errmsg_internal("could not parse XML declaration in stored value"),
349#endif
350 return str;
351}

References appendStringInfoString(), buf, ereport, errcode(), ERRCODE_DATA_CORRUPTED, errmsg_internal(), fb(), initStringInfo(), len, pfree(), str, text_to_cstring(), WARNING, and x.

Referenced by xml_out(), xml_send(), and XmlTableSetDocument().

◆ xml_recv()

Datum xml_recv ( PG_FUNCTION_ARGS  )

Definition at line 370 of file xml.c.

371{
372#ifdef USE_LIBXML
375 const char *input;
376 char *str;
377 char *newstr;
378 int nbytes;
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
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 */
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 */
418
419 /* Now that we know what we're dealing with, convert to server encoding */
421
422 if (newstr != str)
423 {
424 pfree(result);
426 pfree(newstr);
427 }
428
430#else
432 return 0;
433#endif
434}

References buf, cstring_to_text(), encoding, fb(), input, memcpy(), NO_XML_SUPPORT, palloc(), pfree(), pg_any_to_server(), PG_GETARG_POINTER, PG_RETURN_XML_P, PG_UTF8, pq_getmsgbytes(), result, SET_VARSIZE(), str, VARDATA(), VARHDRSZ, and xmloption.

◆ xml_send()

Datum xml_send ( PG_FUNCTION_ARGS  )

Definition at line 438 of file xml.c.

439{
441 char *outval;
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 */
449
452 pfree(outval);
454}

References buf, fb(), pfree(), pg_get_client_encoding(), PG_GETARG_XML_P, PG_RETURN_BYTEA_P, pq_begintypsend(), pq_endtypsend(), pq_sendtext(), x, and xml_out_internal().

◆ xmlcomment()

Datum xmlcomment ( PG_FUNCTION_ARGS  )

Definition at line 491 of file xml.c.

492{
493#ifdef USE_LIBXML
495 char *argdata = VARDATA_ANY(arg);
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] == '-')
506 errmsg("invalid XML comment")));
507 }
508 if (len > 0 && argdata[len - 1] == '-')
511 errmsg("invalid XML comment")));
512
514 appendStringInfoString(&buf, "<!--");
517
519#else
521 return 0;
522#endif
523}

References appendStringInfoString(), appendStringInfoText(), arg, buf, ereport, errcode(), errmsg, ERROR, fb(), i, initStringInfo(), len, NO_XML_SUPPORT, PG_GETARG_TEXT_PP, PG_RETURN_XML_P, stringinfo_to_xmltype(), VARDATA_ANY(), and VARSIZE_ANY_EXHDR().

◆ xmlconcat()

xmltype * xmlconcat ( List args)

Definition at line 575 of file xml.c.

576{
577#ifdef USE_LIBXML
578 int global_standalone = 1;
580 bool global_version_no_value = false;
582 ListCell *v;
583
585 foreach(v, args)
586 {
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)
600 if (standalone < 0)
602
603 if (!version)
605 else if (!global_version)
606 global_version = version;
607 else if (xmlStrcmp(version, global_version) != 0)
609
611 pfree(str);
612 }
613
615 {
616 StringInfoData buf2;
617
618 initStringInfo(&buf2);
619
620 print_xml_decl(&buf2,
622 0,
624
625 appendBinaryStringInfo(&buf2, buf.data, buf.len);
626 buf = buf2;
627 }
628
629 return stringinfo_to_xmltype(&buf);
630#else
632 return NULL;
633#endif
634}

References appendBinaryStringInfo(), appendStringInfoString(), buf, StringInfoData::data, DatumGetXmlP(), fb(), initStringInfo(), len, lfirst, NO_XML_SUPPORT, pfree(), PointerGetDatum(), str, stringinfo_to_xmltype(), text_to_cstring(), VARHDRSZ, VARSIZE(), and x.

Referenced by ExecEvalXmlExpr(), and xmlconcat2().

◆ xmlconcat2()

Datum xmlconcat2 ( PG_FUNCTION_ARGS  )

Definition at line 641 of file xml.c.

642{
643 if (PG_ARGISNULL(0))
644 {
645 if (PG_ARGISNULL(1))
647 else
649 }
650 else if (PG_ARGISNULL(1))
652 else
654 PG_GETARG_XML_P(1))));
655}

References list_make2, PG_ARGISNULL, PG_GETARG_XML_P, PG_RETURN_NULL, PG_RETURN_XML_P, and xmlconcat().

◆ xmldata_root_element_end()

static void xmldata_root_element_end ( StringInfo  result,
const char eltname 
)
static

◆ xmldata_root_element_start()

static void xmldata_root_element_start ( StringInfo  result,
const char eltname,
const char xmlschema,
const char targetns,
bool  top_level 
)
static

Definition at line 3009 of file xml.c.

3012{
3013 /* This isn't really wrong but currently makes no sense. */
3014 Assert(top_level || !xmlschema);
3015
3017 if (top_level)
3018 {
3019 appendStringInfoString(result, " xmlns:xsi=\"" NAMESPACE_XSI "\"");
3020 if (strlen(targetns) > 0)
3021 appendStringInfo(result, " xmlns=\"%s\"", targetns);
3022 }
3023 if (xmlschema)
3024 {
3025 /* FIXME: better targets */
3026 if (strlen(targetns) > 0)
3027 appendStringInfo(result, " xsi:schemaLocation=\"%s #\"", targetns);
3028 else
3029 appendStringInfoString(result, " xsi:noNamespaceSchemaLocation=\"#\"");
3030 }
3032}

References appendStringInfo(), appendStringInfoString(), Assert, fb(), NAMESPACE_XSI, and result.

Referenced by cursor_to_xml(), database_to_xml_internal(), query_to_xml_internal(), schema_to_xml_internal(), and SPI_sql_row_to_xmlelement().

◆ xmlelement()

xmltype * xmlelement ( XmlExpr xexpr,
const Datum named_argvalue,
const bool named_argnull,
const Datum argvalue,
const bool argnull 
)

Definition at line 894 of file xml.c.

897{
898#ifdef USE_LIBXML
902 int i;
903 ListCell *arg;
904 ListCell *narg;
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 */
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);
930 i++;
931 }
932
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);
947 }
948 i++;
949 }
950
952
953 PG_TRY();
954 {
956 if (buf == NULL || xmlerrcxt->err_occurred)
958 "could not allocate xmlBuffer");
960 if (writer == NULL || xmlerrcxt->err_occurred)
962 "could not allocate xmlTextWriter");
963
964 if (xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name) < 0 ||
965 xmlerrcxt->err_occurred)
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 {
977 (xmlChar *) argname,
978 (xmlChar *) str) < 0 ||
979 xmlerrcxt->err_occurred)
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)
992 "could not write raw xml text");
993 }
994
996 xmlerrcxt->err_occurred)
998 "could not end xml element");
999
1000 /* we MUST do this now to flush data out to the buffer ... */
1002 writer = NULL;
1003
1005 }
1006 PG_CATCH();
1007 {
1008 if (writer)
1010 if (buf)
1012
1013 pg_xml_done(xmlerrcxt, true);
1014
1015 PG_RE_THROW();
1016 }
1017 PG_END_TRY();
1018
1020
1021 pg_xml_done(xmlerrcxt, false);
1022
1023 return result;
1024#else
1026 return NULL;
1027#endif
1028}

References arg, XmlExpr::args, buf, ERROR, exprType(), fb(), forboth, i, lappend(), lfirst, map_sql_value_to_xml_value(), XmlExpr::named_args, NIL, NO_XML_SUPPORT, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pg_xml_done(), pg_xml_init(), PG_XML_STRICTNESS_ALL, result, str, strVal, and xml_ereport().

Referenced by ExecEvalXmlExpr().

◆ xmlexists()

Datum xmlexists ( PG_FUNCTION_ARGS  )

Definition at line 4593 of file xml.c.

4594{
4595#ifdef USE_LIBXML
4598 int res_nitems;
4599
4601 &res_nitems, NULL);
4602
4604#else
4606 return 0;
4607#endif
4608}

References data, fb(), NO_XML_SUPPORT, PG_GETARG_TEXT_PP, PG_GETARG_XML_P, and PG_RETURN_BOOL.

◆ xmlparse()

xmltype * xmlparse ( text data,
XmlOptionType  xmloption_arg,
bool  preserve_whitespace,
Node escontext 
)

Definition at line 1032 of file xml.c.

1033{
1034#ifdef USE_LIBXML
1035 xmlDocPtr doc;
1036
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
1048 return NULL;
1049#endif
1050}

References data, fb(), GetDatabaseEncoding(), NO_XML_SUPPORT, and SOFT_ERROR_OCCURRED.

Referenced by ExecEvalXmlExpr(), and texttoxml().

◆ xmlpi()

xmltype * xmlpi ( const char target,
text arg,
bool  arg_is_null,
bool result_is_null 
)

Definition at line 1054 of file xml.c.

1055{
1056#ifdef USE_LIBXML
1057 xmltype *result;
1059
1060 if (pg_strcasecmp(target, "xml") == 0)
1061 ereport(ERROR,
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 */
1071 if (*result_is_null)
1072 return NULL;
1073
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,
1086 errmsg("invalid XML processing instruction"),
1087 errdetail("XML processing instruction cannot contain \"?>\".")));
1088
1090 appendStringInfoString(&buf, string + strspn(string, " "));
1091 pfree(string);
1092 }
1094
1096 pfree(buf.data);
1097 return result;
1098#else
1100 return NULL;
1101#endif
1102}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), arg, buf, ereport, errcode(), errdetail(), errmsg, ERROR, fb(), initStringInfo(), NO_XML_SUPPORT, pfree(), pg_strcasecmp(), result, stringinfo_to_xmltype(), and text_to_cstring().

Referenced by ExecEvalXmlExpr().

◆ xmlroot()

xmltype * xmlroot ( xmltype data,
text version,
int  standalone 
)

Definition at line 1106 of file xml.c.

1107{
1108#ifdef USE_LIBXML
1109 char *str;
1110 size_t len;
1112 int orig_standalone;
1114
1115 len = VARSIZE(data) - VARHDRSZ;
1117
1119
1120 if (version)
1121 orig_version = xml_text2xmlChar(version);
1122 else
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;
1134 orig_standalone = -1;
1135 break;
1137 /* leave original value */
1138 break;
1139 }
1140
1144
1145 return stringinfo_to_xmltype(&buf);
1146#else
1148 return NULL;
1149#endif
1150}

References appendStringInfoString(), buf, data, fb(), initStringInfo(), len, NO_XML_SUPPORT, str, stringinfo_to_xmltype(), text_to_cstring(), VARHDRSZ, VARSIZE(), XML_STANDALONE_NO, XML_STANDALONE_NO_VALUE, XML_STANDALONE_OMITTED, and XML_STANDALONE_YES.

Referenced by ExecEvalXmlExpr().

◆ XmlTableDestroyOpaque()

static void XmlTableDestroyOpaque ( struct TableFuncScanState state)
static

Definition at line 5128 of file xml.c.

5129{
5130#ifdef USE_LIBXML
5132
5133 xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableDestroyOpaque");
5134
5135 /* Propagate our own error context to libxml2 */
5137
5138 if (xtCxt->xpathscomp != NULL)
5139 {
5140 int i;
5141
5142 for (i = 0; i < xtCxt->natts; i++)
5143 if (xtCxt->xpathscomp[i] != NULL)
5144 xmlXPathFreeCompExpr(xtCxt->xpathscomp[i]);
5145 }
5146
5147 if (xtCxt->xpathobj != NULL)
5148 xmlXPathFreeObject(xtCxt->xpathobj);
5149 if (xtCxt->xpathcomp != NULL)
5150 xmlXPathFreeCompExpr(xtCxt->xpathcomp);
5151 if (xtCxt->xpathcxt != NULL)
5152 xmlXPathFreeContext(xtCxt->xpathcxt);
5153 if (xtCxt->doc != NULL)
5154 xmlFreeDoc(xtCxt->doc);
5155 if (xtCxt->ctxt != NULL)
5156 xmlFreeParserCtxt(xtCxt->ctxt);
5157
5158 pg_xml_done(xtCxt->xmlerrcxt, true);
5159
5160 /* not valid anymore */
5161 xtCxt->magic = 0;
5162 state->opaque = NULL;
5163
5164#else
5166#endif /* not USE_LIBXML */
5167}

References fb(), i, NO_XML_SUPPORT, and pg_xml_done().

◆ XmlTableFetchRow()

static bool XmlTableFetchRow ( struct TableFuncScanState state)
static

Definition at line 4931 of file xml.c.

4932{
4933#ifdef USE_LIBXML
4935
4936 xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableFetchRow");
4937
4938 /* Propagate our own error context to libxml2 */
4940
4941 if (xtCxt->xpathobj == NULL)
4942 {
4943 xtCxt->xpathobj = xmlXPathCompiledEval(xtCxt->xpathcomp, xtCxt->xpathcxt);
4944 if (xtCxt->xpathobj == NULL || xtCxt->xmlerrcxt->err_occurred)
4946 "could not create XPath object");
4947
4948 xtCxt->row_count = 0;
4949 }
4950
4951 if (xtCxt->xpathobj->type == XPATH_NODESET)
4952 {
4953 if (xtCxt->xpathobj->nodesetval != NULL)
4954 {
4955 if (xtCxt->row_count++ < xtCxt->xpathobj->nodesetval->nodeNr)
4956 return true;
4957 }
4958 }
4959
4960 return false;
4961#else
4963 return false;
4964#endif /* not USE_LIBXML */
4965}

References ERROR, fb(), NO_XML_SUPPORT, and xml_ereport().

◆ XmlTableGetValue()

static Datum XmlTableGetValue ( struct TableFuncScanState state,
int  colnum,
Oid  typid,
int32  typmod,
bool isnull 
)
static

Definition at line 4976 of file xml.c.

4978{
4979#ifdef USE_LIBXML
4980 Datum result = (Datum) 0;
4982 volatile xmlXPathObjectPtr xpathobj = NULL;
4983
4984 xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableGetValue");
4985
4986 Assert(xtCxt->xpathobj &&
4987 xtCxt->xpathobj->type == XPATH_NODESET &&
4988 xtCxt->xpathobj->nodesetval != NULL);
4989
4990 /* Propagate our own error context to libxml2 */
4992
4993 *isnull = false;
4994
4995 Assert(xtCxt->xpathscomp[colnum] != NULL);
4996
4997 PG_TRY();
4998 {
5000 char *cstr = NULL;
5001
5002 /* Set current node as entry point for XPath evaluation */
5003 cur = xtCxt->xpathobj->nodesetval->nodeTab[xtCxt->row_count - 1];
5004 xtCxt->xpathcxt->node = cur;
5005
5006 /* Evaluate column path */
5007 xpathobj = xmlXPathCompiledEval(xtCxt->xpathscomp[colnum], xtCxt->xpathcxt);
5008 if (xpathobj == NULL || xtCxt->xmlerrcxt->err_occurred)
5010 "could not create XPath object");
5011
5012 /*
5013 * There are four possible cases, depending on the number of nodes
5014 * returned by the XPath expression and the type of the target column:
5015 * a) XPath returns no nodes. b) The target type is XML (return all
5016 * as XML). For non-XML return types: c) One node (return content).
5017 * d) Multiple nodes (error).
5018 */
5019 if (xpathobj->type == XPATH_NODESET)
5020 {
5021 int count = 0;
5022
5023 if (xpathobj->nodesetval != NULL)
5024 count = xpathobj->nodesetval->nodeNr;
5025
5026 if (xpathobj->nodesetval == NULL || count == 0)
5027 {
5028 *isnull = true;
5029 }
5030 else
5031 {
5032 if (typid == XMLOID)
5033 {
5034 text *textstr;
5036
5037 /* Concatenate serialized values */
5039 for (int i = 0; i < count; i++)
5040 {
5041 textstr =
5042 xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
5043 xtCxt->xmlerrcxt);
5044
5046 }
5047 cstr = str.data;
5048 }
5049 else
5050 {
5051 xmlChar *str;
5052
5053 if (count > 1)
5054 ereport(ERROR,
5056 errmsg("more than one value returned by column XPath expression")));
5057
5059 cstr = str ? xml_pstrdup_and_free(str) : "";
5060 }
5061 }
5062 }
5063 else if (xpathobj->type == XPATH_STRING)
5064 {
5065 /* Content should be escaped when target will be XML */
5066 if (typid == XMLOID)
5067 cstr = escape_xml((char *) xpathobj->stringval);
5068 else
5069 cstr = (char *) xpathobj->stringval;
5070 }
5071 else if (xpathobj->type == XPATH_BOOLEAN)
5072 {
5073 char typcategory;
5074 bool typispreferred;
5075 xmlChar *str;
5076
5077 /* Allow implicit casting from boolean to numbers */
5079
5082 else
5084
5086 }
5087 else if (xpathobj->type == XPATH_NUMBER)
5088 {
5089 xmlChar *str;
5090
5093 }
5094 else
5095 elog(ERROR, "unexpected XPath object type %u", xpathobj->type);
5096
5097 /*
5098 * By here, either cstr contains the result value, or the isnull flag
5099 * has been set.
5100 */
5101 Assert(cstr || *isnull);
5102
5103 if (!*isnull)
5104 result = InputFunctionCall(&state->in_functions[colnum],
5105 cstr,
5106 state->typioparams[colnum],
5107 typmod);
5108 }
5109 PG_FINALLY();
5110 {
5111 if (xpathobj != NULL)
5113 }
5114 PG_END_TRY();
5115
5116 return result;
5117#else
5119 return 0;
5120#endif /* not USE_LIBXML */
5121}

References appendStringInfoText(), Assert, cur, elog, ereport, errcode(), errmsg, ERROR, escape_xml(), fb(), get_type_category_preferred(), i, initStringInfo(), InputFunctionCall(), NO_XML_SUPPORT, PG_END_TRY, PG_FINALLY, PG_TRY, result, str, and xml_ereport().

◆ XmlTableInitOpaque()

static void XmlTableInitOpaque ( struct TableFuncScanState state,
int  natts 
)
static

Definition at line 4733 of file xml.c.

4734{
4735#ifdef USE_LIBXML
4736 volatile xmlParserCtxtPtr ctxt = NULL;
4739
4742 xtCxt->natts = natts;
4743 xtCxt->xpathscomp = palloc0_array(xmlXPathCompExprPtr, natts);
4744
4746
4747 PG_TRY();
4748 {
4749 xmlInitParser();
4750
4751 ctxt = xmlNewParserCtxt();
4752 if (ctxt == NULL || xmlerrcxt->err_occurred)
4754 "could not allocate parser context");
4755 }
4756 PG_CATCH();
4757 {
4758 if (ctxt != NULL)
4759 xmlFreeParserCtxt(ctxt);
4760
4761 pg_xml_done(xmlerrcxt, true);
4762
4763 PG_RE_THROW();
4764 }
4765 PG_END_TRY();
4766
4767 xtCxt->xmlerrcxt = xmlerrcxt;
4768 xtCxt->ctxt = ctxt;
4769
4770 state->opaque = xtCxt;
4771#else
4773#endif /* not USE_LIBXML */
4774}

References ERROR, fb(), NO_XML_SUPPORT, palloc0_array, palloc0_object, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pg_xml_done(), pg_xml_init(), PG_XML_STRICTNESS_ALL, and xml_ereport().

◆ XmlTableSetColumnFilter()

static void XmlTableSetColumnFilter ( struct TableFuncScanState state,
const char path,
int  colnum 
)
static

Definition at line 4896 of file xml.c.

4897{
4898#ifdef USE_LIBXML
4900 xmlChar *xstr;
4901
4902 Assert(path);
4903
4904 xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableSetColumnFilter");
4905
4906 if (*path == '\0')
4907 ereport(ERROR,
4909 errmsg("column path filter must not be empty string")));
4910
4911 xstr = pg_xmlCharStrndup(path, strlen(path));
4912
4913 /* We require XmlTableSetDocument to have been done already */
4914 Assert(xtCxt->xpathcxt != NULL);
4915
4916 xtCxt->xpathscomp[colnum] = xmlXPathCtxtCompile(xtCxt->xpathcxt, xstr);
4917 if (xtCxt->xpathscomp[colnum] == NULL || xtCxt->xmlerrcxt->err_occurred)
4919 "invalid XPath expression");
4920#else
4922#endif /* not USE_LIBXML */
4923}

References Assert, ereport, errcode(), errmsg, ERROR, fb(), NO_XML_SUPPORT, and xml_ereport().

◆ XmlTableSetDocument()

static void XmlTableSetDocument ( struct TableFuncScanState state,
Datum  value 
)
static

Definition at line 4781 of file xml.c.

4782{
4783#ifdef USE_LIBXML
4786 char *str;
4787 xmlChar *xstr;
4788 int length;
4789 volatile xmlDocPtr doc = NULL;
4790 volatile xmlXPathContextPtr xpathcxt = NULL;
4791
4792 xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableSetDocument");
4793
4794 /*
4795 * Use out function for casting to string (remove encoding property). See
4796 * comment in xml_out.
4797 */
4799
4800 length = strlen(str);
4801 xstr = pg_xmlCharStrndup(str, length);
4802
4803 PG_TRY();
4804 {
4805 doc = xmlCtxtReadMemory(xtCxt->ctxt, (char *) xstr, length, NULL, NULL, 0);
4806 if (doc == NULL || xtCxt->xmlerrcxt->err_occurred)
4808 "could not parse XML document");
4810 if (xpathcxt == NULL || xtCxt->xmlerrcxt->err_occurred)
4812 "could not allocate XPath context");
4813 xpathcxt->node = (xmlNodePtr) doc;
4814 }
4815 PG_CATCH();
4816 {
4817 if (xpathcxt != NULL)
4819 if (doc != NULL)
4820 xmlFreeDoc(doc);
4821
4822 PG_RE_THROW();
4823 }
4824 PG_END_TRY();
4825
4826 xtCxt->doc = doc;
4827 xtCxt->xpathcxt = xpathcxt;
4828#else
4830#endif /* not USE_LIBXML */
4831}

References DatumGetXmlP(), ERROR, fb(), NO_XML_SUPPORT, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, str, value, xml_ereport(), and xml_out_internal().

◆ XmlTableSetNamespace()

static void XmlTableSetNamespace ( struct TableFuncScanState state,
const char name,
const char uri 
)
static

Definition at line 4838 of file xml.c.

4839{
4840#ifdef USE_LIBXML
4842
4843 if (name == NULL)
4844 ereport(ERROR,
4846 errmsg("DEFAULT namespace is not supported")));
4847 xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableSetNamespace");
4848
4849 if (xmlXPathRegisterNs(xtCxt->xpathcxt,
4853 "could not set XML namespace");
4854#else
4856#endif /* not USE_LIBXML */
4857}

References ereport, errcode(), errmsg, ERROR, fb(), name, NO_XML_SUPPORT, and xml_ereport().

◆ XmlTableSetRowFilter()

static void XmlTableSetRowFilter ( struct TableFuncScanState state,
const char path 
)
static

Definition at line 4864 of file xml.c.

4865{
4866#ifdef USE_LIBXML
4868 xmlChar *xstr;
4869
4870 xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableSetRowFilter");
4871
4872 if (*path == '\0')
4873 ereport(ERROR,
4875 errmsg("row path filter must not be empty string")));
4876
4877 xstr = pg_xmlCharStrndup(path, strlen(path));
4878
4879 /* We require XmlTableSetDocument to have been done already */
4880 Assert(xtCxt->xpathcxt != NULL);
4881
4882 xtCxt->xpathcomp = xmlXPathCtxtCompile(xtCxt->xpathcxt, xstr);
4883 if (xtCxt->xpathcomp == NULL || xtCxt->xmlerrcxt->err_occurred)
4885 "invalid XPath expression");
4886#else
4888#endif /* not USE_LIBXML */
4889}

References Assert, ereport, errcode(), errmsg, ERROR, fb(), NO_XML_SUPPORT, and xml_ereport().

◆ xmltext()

Datum xmltext ( PG_FUNCTION_ARGS  )

Definition at line 527 of file xml.c.

528{
529#ifdef USE_LIBXML
531 text *result;
532 xmlChar *volatile xmlbuf = NULL;
534
535 /* First we gotta spin up some error handling. */
537
538 PG_TRY();
539 {
541
542 if (xmlbuf == NULL || xmlerrcxt->err_occurred)
544 "could not allocate xmlChar");
545
546 result = cstring_to_text_with_len((const char *) xmlbuf,
548 }
549 PG_CATCH();
550 {
551 if (xmlbuf)
553
554 pg_xml_done(xmlerrcxt, true);
555 PG_RE_THROW();
556 }
557 PG_END_TRY();
558
560 pg_xml_done(xmlerrcxt, false);
561
563#else
565 return 0;
566#endif /* not USE_LIBXML */
567}

References arg, cstring_to_text_with_len(), ERROR, fb(), NO_XML_SUPPORT, PG_CATCH, PG_END_TRY, PG_GETARG_TEXT_PP, PG_RE_THROW, PG_RETURN_XML_P, PG_TRY, pg_xml_done(), pg_xml_init(), PG_XML_STRICTNESS_ALL, result, and xml_ereport().

◆ xmltotext()

Datum xmltotext ( PG_FUNCTION_ARGS  )

Definition at line 668 of file xml.c.

669{
671
672 /* It's actually binary compatible. */
674}

References data, PG_GETARG_XML_P, and PG_RETURN_TEXT_P.

◆ xmltotext_with_options()

text * xmltotext_with_options ( xmltype data,
XmlOptionType  xmloption_arg,
bool  indent 
)

Definition at line 678 of file xml.c.

679{
680#ifdef USE_LIBXML
681 text *volatile result;
685 volatile xmlBufferPtr buf = NULL;
686 volatile xmlSaveCtxtPtr ctxt = NULL;
688 PgXmlErrorContext *volatile xmlerrcxt = NULL;
689#endif
690
691 if (xmloption_arg != XMLOPTION_DOCUMENT && !indent)
692 {
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 return (text *) data;
699 }
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 */
711 (Node *) &escontext);
712 if (doc == NULL || escontext.error_occurred)
713 {
714 if (doc)
716 /* A soft error must be failure to conform to XMLOPTION_DOCUMENT */
719 errmsg("not an XML document")));
720 }
721
722 /* If we weren't asked to indent, we're done. */
723 if (!indent)
724 {
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
740
741 /* The serialized data will go into this buffer. */
743
744 if (buf == NULL || xmlerrcxt->err_occurred)
746 "could not allocate xmlBuffer");
747
748 /* Detect whether there's an XML declaration */
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,
760 else
761 ctxt = xmlSaveToBuffer(buf, NULL,
763
764 if (ctxt == NULL || xmlerrcxt->err_occurred)
766 "could not allocate xmlSaveCtxt");
767
769 {
770 /* If it's a document, saving is easy. */
771 if (xmlSaveDoc(ctxt, doc) == -1 || xmlerrcxt->err_occurred)
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 */
786
787 root = xmlNewNode(NULL, (const xmlChar *) "content-root");
788 if (root == NULL || xmlerrcxt->err_occurred)
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 */
797 if (oldroot != NULL)
799
801 xmlerrcxt->err_occurred)
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)
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 {
826 "could not save newline to xmlBuffer");
827 }
828 }
829
830 if (xmlSaveTree(ctxt, node) == -1 || xmlerrcxt->err_occurred)
831 {
834 "could not save content to xmlBuffer");
835 }
836 }
837
839 }
840
841 if (xmlSaveClose(ctxt) == -1 || xmlerrcxt->err_occurred)
842 {
843 ctxt = NULL; /* don't try to close it again */
845 "could not close xmlSaveCtxtPtr");
846 }
847
848 /*
849 * xmlDocContentDumpOutput may add a trailing newline, so remove that.
850 */
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
861 }
862 else
864 }
865 PG_CATCH();
866 {
867 if (ctxt)
868 xmlSaveClose(ctxt);
869 if (buf)
872
873 if (xmlerrcxt)
874 pg_xml_done(xmlerrcxt, true);
875
876 PG_RE_THROW();
877 }
878 PG_END_TRY();
879
882
883 pg_xml_done(xmlerrcxt, false);
884
885 return result;
886#else
888 return NULL;
889#endif
890}

References buf, cstring_to_text_with_len(), data, ereport, errcode(), errmsg, ERROR, ErrorSaveContext::error_occurred, fb(), GetDatabaseEncoding(), len, newline, NO_XML_SUPPORT, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pg_xml_done(), pg_xml_init(), PG_XML_STRICTNESS_ALL, result, root, str, xml_ereport(), and XMLOPTION_DOCUMENT.

Referenced by ExecEvalXmlExpr().

◆ xmlvalidate()

Datum xmlvalidate ( PG_FUNCTION_ARGS  )

Definition at line 1162 of file xml.c.

1163{
1164 ereport(ERROR,
1166 errmsg("xmlvalidate is not implemented")));
1167 return 0;
1168}

References ereport, errcode(), errmsg, ERROR, and fb().

◆ xpath()

Datum xpath ( PG_FUNCTION_ARGS  )

Definition at line 4570 of file xml.c.

4571{
4572#ifdef USE_LIBXML
4575 ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
4576 ArrayBuildState *astate;
4577
4579 xpath_internal(xpath_expr_text, data, namespaces,
4580 NULL, astate);
4582#else
4584 return 0;
4585#endif
4586}

References CurrentMemoryContext, data, fb(), initArrayResult(), makeArrayResult(), NO_XML_SUPPORT, PG_GETARG_ARRAYTYPE_P, PG_GETARG_TEXT_PP, PG_GETARG_XML_P, and PG_RETURN_DATUM.

Referenced by pgxml_xpath(), xpath_bool(), xpath_list(), xpath_nodeset(), xpath_number(), and xpath_string().

◆ xpath_exists()

Datum xpath_exists ( PG_FUNCTION_ARGS  )

Definition at line 4616 of file xml.c.

4617{
4618#ifdef USE_LIBXML
4621 ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
4622 int res_nitems;
4623
4624 xpath_internal(xpath_expr_text, data, namespaces,
4625 &res_nitems, NULL);
4626
4628#else
4630 return 0;
4631#endif
4632}

References data, fb(), NO_XML_SUPPORT, PG_GETARG_ARRAYTYPE_P, PG_GETARG_TEXT_PP, PG_GETARG_XML_P, and PG_RETURN_BOOL.

◆ xsd_schema_element_end()

static void xsd_schema_element_end ( StringInfo  result)
static

Definition at line 3305 of file xml.c.

3306{
3307 appendStringInfoString(result, "</xsd:schema>");
3308}

References appendStringInfoString(), and result.

Referenced by database_to_xmlschema_internal(), map_sql_table_to_xmlschema(), and schema_to_xmlschema_internal().

◆ xsd_schema_element_start()

static void xsd_schema_element_start ( StringInfo  result,
const char targetns 
)
static

Definition at line 3288 of file xml.c.

3289{
3291 "<xsd:schema\n"
3292 " xmlns:xsd=\"" NAMESPACE_XSD "\"");
3293 if (strlen(targetns) > 0)
3295 "\n"
3296 " targetNamespace=\"%s\"\n"
3297 " elementFormDefault=\"qualified\"",
3298 targetns);
3300 ">\n\n");
3301}

References appendStringInfo(), appendStringInfoString(), fb(), NAMESPACE_XSD, and result.

Referenced by database_to_xmlschema_internal(), map_sql_table_to_xmlschema(), and schema_to_xmlschema_internal().

Variable Documentation

◆ xmlbinary

int xmlbinary = XMLBINARY_BASE64

Definition at line 108 of file xml.c.

Referenced by map_sql_type_to_xmlschema_type(), and map_sql_value_to_xml_value().

◆ xmloption

int xmloption = XMLOPTION_CONTENT

Definition at line 109 of file xml.c.

Referenced by texttoxml(), xml_in(), xml_is_well_formed(), and xml_recv().

◆ XmlTableRoutine

const TableFuncRoutine XmlTableRoutine
Initial value:
=
{
.InitOpaque = XmlTableInitOpaque,
.SetDocument = XmlTableSetDocument,
.SetNamespace = XmlTableSetNamespace,
.SetRowFilter = XmlTableSetRowFilter,
.SetColumnFilter = XmlTableSetColumnFilter,
.FetchRow = XmlTableFetchRow,
.GetValue = XmlTableGetValue,
.DestroyOpaque = XmlTableDestroyOpaque
}

Definition at line 222 of file xml.c.

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};

Referenced by ExecInitTableFuncScan().