1 /*-------------------------------------------------------------------------
4 * XML data type support.
7 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
12 *-------------------------------------------------------------------------
16 * Generally, XML type support is only available when libxml use was
17 * configured during the build. But even if that is not done, the
18 * type and all the functions are available, but most of them will
19 * fail. For one thing, this avoids having to manage variant catalog
20 * installations. But it also has nice effects such as that you can
21 * dump a database containing XML type data even if the server is not
22 * linked with libxml. Thus, make sure xml_out() works even if nothing
27 * Notes on memory management:
29 * Sometimes libxml allocates global structures in the hope that it can reuse
30 * them later on. This makes it impractical to change the xmlMemSetup
31 * functions on-the-fly; that is likely to lead to trying to pfree() chunks
32 * allocated with malloc() or vice versa. Since libxml might be used by
33 * loadable modules, eg libperl, our only safe choices are to change the
34 * functions at postmaster/backend launch or not at all. Since we'd rather
35 * not activate libxml in sessions that might never use it, the latter choice
36 * is the preferred one. However, for debugging purposes it can be awfully
37 * handy to constrain libxml's allocations to be done in a specific palloc
38 * context, where they're easy to track. Therefore there is code here that
39 * can be enabled in debug builds to redirect libxml's allocations into a
40 * special context LibxmlContext. It's not recommended to turn this on in
41 * a production build because of the possibility of bad interactions with
44 /* #define USE_LIBXMLCONTEXT */
49 #include <libxml/chvalid.h>
50 #include <libxml/parser.h>
51 #include <libxml/tree.h>
52 #include <libxml/uri.h>
53 #include <libxml/xmlerror.h>
54 #include <libxml/xmlwriter.h>
55 #include <libxml/xpath.h>
56 #include <libxml/xpathInternals.h>
57 #endif /* USE_LIBXML */
59 #include "catalog/namespace.h"
60 #include "catalog/pg_type.h"
61 #include "commands/dbcommands.h"
62 #include "executor/executor.h"
63 #include "executor/spi.h"
65 #include "lib/stringinfo.h"
66 #include "libpq/pqformat.h"
67 #include "mb/pg_wchar.h"
68 #include "miscadmin.h"
69 #include "nodes/execnodes.h"
70 #include "nodes/nodeFuncs.h"
71 #include "utils/array.h"
72 #include "utils/builtins.h"
73 #include "utils/date.h"
74 #include "utils/datetime.h"
75 #include "utils/lsyscache.h"
76 #include "utils/memutils.h"
77 #include "utils/syscache.h"
78 #include "utils/xml.h"
87 static StringInfo xml_err_buf
= NULL
;
89 static void xml_ereport(int level
, int sqlcode
, const char *msg
);
90 static void xml_errorHandler(void *ctxt
, const char *msg
,...);
91 static void xml_ereport_by_code(int level
, int sqlcode
,
92 const char *msg
, int errcode
);
94 #ifdef USE_LIBXMLCONTEXT
96 static MemoryContext LibxmlContext
= NULL
;
98 static void xml_memory_init(void);
99 static void *xml_palloc(size_t size
);
100 static void *xml_repalloc(void *ptr
, size_t size
);
101 static void xml_pfree(void *ptr
);
102 static char *xml_pstrdup(const char *string
);
104 #endif /* USE_LIBXMLCONTEXT */
106 static void xml_init(void);
107 static xmlChar
*xml_text2xmlChar(text
*in
);
108 static int parse_xml_decl(const xmlChar
* str
, size_t *lenp
,
109 xmlChar
** version
, xmlChar
** encoding
, int *standalone
);
110 static bool print_xml_decl(StringInfo buf
, const xmlChar
* version
,
111 pg_enc encoding
, int standalone
);
112 static xmlDocPtr
xml_parse(text
*data
, XmlOptionType xmloption_arg
,
113 bool preserve_whitespace
, xmlChar
* encoding
);
114 static text
*xml_xmlnodetoxmltype(xmlNodePtr cur
);
115 #endif /* USE_LIBXML */
117 static StringInfo
query_to_xml_internal(const char *query
, char *tablename
,
118 const char *xmlschema
, bool nulls
, bool tableforest
,
119 const char *targetns
, bool top_level
);
120 static const char *map_sql_table_to_xmlschema(TupleDesc tupdesc
, Oid relid
,
121 bool nulls
, bool tableforest
, const char *targetns
);
122 static const char *map_sql_schema_to_xmlschema_types(Oid nspid
,
123 List
*relid_list
, bool nulls
,
124 bool tableforest
, const char *targetns
);
125 static const char *map_sql_catalog_to_xmlschema_types(List
*nspid_list
,
126 bool nulls
, bool tableforest
,
127 const char *targetns
);
128 static const char *map_sql_type_to_xml_name(Oid typeoid
, int typmod
);
129 static const char *map_sql_typecoll_to_xmlschema_types(List
*tupdesc_list
);
130 static const char *map_sql_type_to_xmlschema_type(Oid typeoid
, int typmod
);
131 static void SPI_sql_row_to_xmlelement(int rownum
, StringInfo result
,
132 char *tablename
, bool nulls
, bool tableforest
,
133 const char *targetns
, bool top_level
);
135 #define NO_XML_SUPPORT() \
137 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
138 errmsg("unsupported XML feature"), \
139 errdetail("This functionality requires the server to be built with libxml support."), \
140 errhint("You need to rebuild PostgreSQL using --with-libxml.")))
143 /* from SQL/XML:2003 section 4.7 */
144 #define NAMESPACE_XSD "http://www.w3.org/2001/XMLSchema"
145 #define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance"
146 #define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml"
152 xmlChar_to_encoding(const xmlChar
*encoding_name
)
154 int encoding
= pg_char_to_encoding((const char *) encoding_name
);
158 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
159 errmsg("invalid encoding name \"%s\"",
160 (const char *) encoding_name
)));
167 * xml_in uses a plain C string to VARDATA conversion, so for the time being
168 * we use the conversion function for the text datatype.
170 * This is only acceptable so long as xmltype and text use the same
174 xml_in(PG_FUNCTION_ARGS
)
177 char *s
= PG_GETARG_CSTRING(0);
181 vardata
= (xmltype
*) cstring_to_text(s
);
184 * Parse the data to check if it is well-formed XML data. Assume that
185 * ERROR occurred if parsing failed.
187 doc
= xml_parse(vardata
, xmloption
, true, NULL
);
190 PG_RETURN_XML_P(vardata
);
198 #define PG_XML_DEFAULT_VERSION "1.0"
202 * xml_out_internal uses a plain VARDATA to C string conversion, so for the
203 * time being we use the conversion function for the text datatype.
205 * This is only acceptable so long as xmltype and text use the same
209 xml_out_internal(xmltype
*x
, pg_enc target_encoding
)
211 char *str
= text_to_cstring((text
*) x
);
214 size_t len
= strlen(str
);
219 if ((res_code
= parse_xml_decl((xmlChar
*) str
,
220 &len
, &version
, NULL
, &standalone
)) == 0)
224 initStringInfo(&buf
);
226 if (!print_xml_decl(&buf
, version
, target_encoding
, standalone
))
229 * If we are not going to produce an XML declaration, eat a single
230 * newline in the original string to prevent empty first lines in
233 if (*(str
+ len
) == '\n')
236 appendStringInfoString(&buf
, str
+ len
);
243 xml_ereport_by_code(WARNING
, ERRCODE_INTERNAL_ERROR
,
244 "could not parse XML declaration in stored value",
252 xml_out(PG_FUNCTION_ARGS
)
254 xmltype
*x
= PG_GETARG_XML_P(0);
257 * xml_out removes the encoding property in all cases. This is because we
258 * cannot control from here whether the datum will be converted to a
259 * different client encoding, so we'd do more harm than good by including
262 PG_RETURN_CSTRING(xml_out_internal(x
, 0));
267 xml_recv(PG_FUNCTION_ARGS
)
270 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
276 xmlChar
*encoding
= NULL
;
279 * Read the data in raw format. We don't know yet what the encoding is, as
280 * that information is embedded in the xml declaration; so we have to
281 * parse that before converting to server encoding.
283 nbytes
= buf
->len
- buf
->cursor
;
284 str
= (char *) pq_getmsgbytes(buf
, nbytes
);
287 * We need a null-terminated string to pass to parse_xml_decl(). Rather
288 * than make a separate copy, make the temporary result one byte bigger
289 * than it needs to be.
291 result
= palloc(nbytes
+ 1 + VARHDRSZ
);
292 SET_VARSIZE(result
, nbytes
+ VARHDRSZ
);
293 memcpy(VARDATA(result
), str
, nbytes
);
294 str
= VARDATA(result
);
297 parse_xml_decl((xmlChar
*) str
, NULL
, NULL
, &encoding
, NULL
);
300 * Parse the data to check if it is well-formed XML data. Assume that
301 * xml_parse will throw ERROR if not.
303 doc
= xml_parse(result
, xmloption
, true, encoding
);
306 /* Now that we know what we're dealing with, convert to server encoding */
307 newstr
= (char *) pg_do_encoding_conversion((unsigned char *) str
,
310 xmlChar_to_encoding(encoding
) :
312 GetDatabaseEncoding());
317 result
= (xmltype
*) cstring_to_text(newstr
);
321 PG_RETURN_XML_P(result
);
330 xml_send(PG_FUNCTION_ARGS
)
332 xmltype
*x
= PG_GETARG_XML_P(0);
337 * xml_out_internal doesn't convert the encoding, it just prints the right
338 * declaration. pq_sendtext will do the conversion.
340 outval
= xml_out_internal(x
, pg_get_client_encoding());
342 pq_begintypsend(&buf
);
343 pq_sendtext(&buf
, outval
, strlen(outval
));
345 PG_RETURN_BYTEA_P(pq_endtypsend(&buf
));
351 appendStringInfoText(StringInfo str
, const text
*t
)
353 appendBinaryStringInfo(str
, VARDATA(t
), VARSIZE(t
) - VARHDRSZ
);
359 stringinfo_to_xmltype(StringInfo buf
)
361 return (xmltype
*) cstring_to_text_with_len(buf
->data
, buf
->len
);
366 cstring_to_xmltype(const char *string
)
368 return (xmltype
*) cstring_to_text(string
);
374 xmlBuffer_to_xmltype(xmlBufferPtr buf
)
376 return (xmltype
*) cstring_to_text_with_len((char *) xmlBufferContent(buf
),
377 xmlBufferLength(buf
));
383 xmlcomment(PG_FUNCTION_ARGS
)
386 text
*arg
= PG_GETARG_TEXT_P(0);
387 char *argdata
= VARDATA(arg
);
388 int len
= VARSIZE(arg
) - VARHDRSZ
;
392 /* check for "--" in string or "-" at the end */
393 for (i
= 1; i
< len
; i
++)
395 if (argdata
[i
] == '-' && argdata
[i
- 1] == '-')
397 (errcode(ERRCODE_INVALID_XML_COMMENT
),
398 errmsg("invalid XML comment")));
400 if (len
> 0 && argdata
[len
- 1] == '-')
402 (errcode(ERRCODE_INVALID_XML_COMMENT
),
403 errmsg("invalid XML comment")));
405 initStringInfo(&buf
);
406 appendStringInfo(&buf
, "<!--");
407 appendStringInfoText(&buf
, arg
);
408 appendStringInfo(&buf
, "-->");
410 PG_RETURN_XML_P(stringinfo_to_xmltype(&buf
));
420 * TODO: xmlconcat needs to merge the notations and unparsed entities
421 * of the argument values. Not very important in practice, though.
424 xmlconcat(List
*args
)
427 int global_standalone
= 1;
428 xmlChar
*global_version
= NULL
;
429 bool global_version_no_value
= false;
433 initStringInfo(&buf
);
436 xmltype
*x
= DatumGetXmlP(PointerGetDatum(lfirst(v
)));
442 len
= VARSIZE(x
) - VARHDRSZ
;
443 str
= text_to_cstring((text
*) x
);
445 parse_xml_decl((xmlChar
*) str
, &len
, &version
, NULL
, &standalone
);
447 if (standalone
== 0 && global_standalone
== 1)
448 global_standalone
= 0;
450 global_standalone
= -1;
453 global_version_no_value
= true;
454 else if (!global_version
)
455 global_version
= version
;
456 else if (xmlStrcmp(version
, global_version
) != 0)
457 global_version_no_value
= true;
459 appendStringInfoString(&buf
, str
+ len
);
463 if (!global_version_no_value
|| global_standalone
>= 0)
467 initStringInfo(&buf2
);
469 print_xml_decl(&buf2
,
470 (!global_version_no_value
) ? global_version
: NULL
,
474 appendStringInfoString(&buf2
, buf
.data
);
478 return stringinfo_to_xmltype(&buf
);
490 xmlconcat2(PG_FUNCTION_ARGS
)
497 PG_RETURN_XML_P(PG_GETARG_XML_P(1));
499 else if (PG_ARGISNULL(1))
500 PG_RETURN_XML_P(PG_GETARG_XML_P(0));
502 PG_RETURN_XML_P(xmlconcat(list_make2(PG_GETARG_XML_P(0),
503 PG_GETARG_XML_P(1))));
508 texttoxml(PG_FUNCTION_ARGS
)
510 text
*data
= PG_GETARG_TEXT_P(0);
512 PG_RETURN_XML_P(xmlparse(data
, xmloption
, true));
517 xmltotext(PG_FUNCTION_ARGS
)
519 xmltype
*data
= PG_GETARG_XML_P(0);
521 /* It's actually binary compatible. */
522 PG_RETURN_TEXT_P((text
*) data
);
527 xmltotext_with_xmloption(xmltype
*data
, XmlOptionType xmloption_arg
)
529 if (xmloption_arg
== XMLOPTION_DOCUMENT
&& !xml_is_document(data
))
531 (errcode(ERRCODE_NOT_AN_XML_DOCUMENT
),
532 errmsg("not an XML document")));
534 /* It's actually binary compatible, save for the above check. */
535 return (text
*) data
;
540 xmlelement(XmlExprState
*xmlExpr
, ExprContext
*econtext
)
543 XmlExpr
*xexpr
= (XmlExpr
*) xmlExpr
->xprstate
.expr
;
545 List
*named_arg_strings
;
550 xmlBufferPtr buf
= NULL
;
551 xmlTextWriterPtr writer
= NULL
;
554 * We first evaluate all the arguments, then start up libxml and create
555 * the result. This avoids issues if one of the arguments involves a call
556 * to some other function or subsystem that wants to use libxml on its own
559 named_arg_strings
= NIL
;
561 foreach(arg
, xmlExpr
->named_args
)
563 ExprState
*e
= (ExprState
*) lfirst(arg
);
568 value
= ExecEvalExpr(e
, econtext
, &isnull
, NULL
);
572 str
= map_sql_value_to_xml_value(value
, exprType((Node
*) e
->expr
));
573 named_arg_strings
= lappend(named_arg_strings
, str
);
578 foreach(arg
, xmlExpr
->args
)
580 ExprState
*e
= (ExprState
*) lfirst(arg
);
585 value
= ExecEvalExpr(e
, econtext
, &isnull
, NULL
);
586 /* here we can just forget NULL elements immediately */
589 str
= map_sql_value_to_xml_value(value
,
590 exprType((Node
*) e
->expr
));
591 arg_strings
= lappend(arg_strings
, str
);
595 /* now safe to run libxml */
600 buf
= xmlBufferCreate();
602 xml_ereport(ERROR
, ERRCODE_OUT_OF_MEMORY
,
603 "could not allocate xmlBuffer");
604 writer
= xmlNewTextWriterMemory(buf
, 0);
606 xml_ereport(ERROR
, ERRCODE_OUT_OF_MEMORY
,
607 "could not allocate xmlTextWriter");
609 xmlTextWriterStartElement(writer
, (xmlChar
*) xexpr
->name
);
611 forboth(arg
, named_arg_strings
, narg
, xexpr
->arg_names
)
613 char *str
= (char *) lfirst(arg
);
614 char *argname
= strVal(lfirst(narg
));
617 xmlTextWriterWriteAttribute(writer
,
622 foreach(arg
, arg_strings
)
624 char *str
= (char *) lfirst(arg
);
626 xmlTextWriterWriteRaw(writer
, (xmlChar
*) str
);
629 xmlTextWriterEndElement(writer
);
631 /* we MUST do this now to flush data out to the buffer ... */
632 xmlFreeTextWriter(writer
);
635 result
= xmlBuffer_to_xmltype(buf
);
640 xmlFreeTextWriter(writer
);
658 xmlparse(text
*data
, XmlOptionType xmloption_arg
, bool preserve_whitespace
)
663 doc
= xml_parse(data
, xmloption_arg
, preserve_whitespace
, NULL
);
666 return (xmltype
*) data
;
675 xmlpi(char *target
, text
*arg
, bool arg_is_null
, bool *result_is_null
)
681 if (pg_strcasecmp(target
, "xml") == 0)
683 (errcode(ERRCODE_SYNTAX_ERROR
), /* really */
684 errmsg("invalid XML processing instruction"),
685 errdetail("XML processing instruction target name cannot be \"%s\".", target
)));
688 * Following the SQL standard, the null check comes after the syntax check
691 *result_is_null
= arg_is_null
;
695 initStringInfo(&buf
);
697 appendStringInfo(&buf
, "<?%s", target
);
703 string
= text_to_cstring(arg
);
704 if (strstr(string
, "?>") != NULL
)
706 (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION
),
707 errmsg("invalid XML processing instruction"),
708 errdetail("XML processing instruction cannot contain \"?>\".")));
710 appendStringInfoChar(&buf
, ' ');
711 appendStringInfoString(&buf
, string
+ strspn(string
, " "));
714 appendStringInfoString(&buf
, "?>");
716 result
= stringinfo_to_xmltype(&buf
);
727 xmlroot(xmltype
*data
, text
*version
, int standalone
)
732 xmlChar
*orig_version
;
736 len
= VARSIZE(data
) - VARHDRSZ
;
737 str
= text_to_cstring((text
*) data
);
739 parse_xml_decl((xmlChar
*) str
, &len
, &orig_version
, NULL
, &orig_standalone
);
742 orig_version
= xml_text2xmlChar(version
);
748 case XML_STANDALONE_YES
:
751 case XML_STANDALONE_NO
:
754 case XML_STANDALONE_NO_VALUE
:
755 orig_standalone
= -1;
757 case XML_STANDALONE_OMITTED
:
758 /* leave original value */
762 initStringInfo(&buf
);
763 print_xml_decl(&buf
, orig_version
, 0, orig_standalone
);
764 appendStringInfoString(&buf
, str
+ len
);
766 return stringinfo_to_xmltype(&buf
);
775 * Validate document (given as string) against DTD (given as external link)
777 * This has been removed because it is a security hole: unprivileged users
778 * should not be able to use Postgres to fetch arbitrary external files,
779 * which unfortunately is exactly what libxml is willing to do with the DTD
783 xmlvalidate(PG_FUNCTION_ARGS
)
786 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
787 errmsg("xmlvalidate is not implemented")));
793 xml_is_document(xmltype
*arg
)
797 xmlDocPtr doc
= NULL
;
798 MemoryContext ccxt
= CurrentMemoryContext
;
800 /* We want to catch ereport(INVALID_XML_DOCUMENT) and return false */
803 doc
= xml_parse((text
*) arg
, XMLOPTION_DOCUMENT
, true, NULL
);
811 ecxt
= MemoryContextSwitchTo(ccxt
);
812 errdata
= CopyErrorData();
813 if (errdata
->sqlerrcode
== ERRCODE_INVALID_XML_DOCUMENT
)
820 MemoryContextSwitchTo(ecxt
);
830 #else /* not USE_LIBXML */
833 #endif /* not USE_LIBXML */
840 * Set up for use of libxml --- this should be called by each function that
841 * is about to use libxml facilities.
843 * TODO: xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and
849 static bool first_time
= true;
853 /* Stuff we need do only once per session */
854 MemoryContext oldcontext
;
857 * Currently, we have no pure UTF-8 support for internals -- check if
860 if (sizeof(char) != sizeof(xmlChar
))
862 (errmsg("could not initialize XML library"),
863 errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.",
864 (int) sizeof(char), (int) sizeof(xmlChar
))));
866 /* create error buffer in permanent context */
867 oldcontext
= MemoryContextSwitchTo(TopMemoryContext
);
868 xml_err_buf
= makeStringInfo();
869 MemoryContextSwitchTo(oldcontext
);
871 /* Now that xml_err_buf exists, safe to call xml_errorHandler */
872 xmlSetGenericErrorFunc(NULL
, xml_errorHandler
);
874 #ifdef USE_LIBXMLCONTEXT
875 /* Set up memory allocation our way, too */
879 /* Check library compatibility */
886 /* Reset pre-existing buffer to empty */
887 Assert(xml_err_buf
!= NULL
);
888 resetStringInfo(xml_err_buf
);
891 * We re-establish the error callback function every time. This makes
892 * it safe for other subsystems (PL/Perl, say) to also use libxml with
893 * their own callbacks ... so long as they likewise set up the
894 * callbacks on every use. It's cheap enough to not be worth worrying
897 xmlSetGenericErrorFunc(NULL
, xml_errorHandler
);
903 * SQL/XML allows storing "XML documents" or "XML content". "XML
904 * documents" are specified by the XML specification and are parsed
905 * easily by libxml. "XML content" is specified by SQL/XML as the
906 * production "XMLDecl? content". But libxml can only parse the
907 * "content" part, so we have to parse the XML declaration ourselves
911 #define CHECK_XML_SPACE(p) \
913 if (!xmlIsBlank_ch(*(p))) \
914 return XML_ERR_SPACE_REQUIRED; \
917 #define SKIP_XML_SPACE(p) \
918 while (xmlIsBlank_ch(*(p))) (p)++
920 /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
921 /* Beware of multiple evaluations of argument! */
922 #define PG_XMLISNAMECHAR(c) \
923 (xmlIsBaseChar_ch(c) || xmlIsIdeographicQ(c) \
924 || xmlIsDigit_ch(c) \
925 || c == '.' || c == '-' || c == '_' || c == ':' \
926 || xmlIsCombiningQ(c) \
927 || xmlIsExtender_ch(c))
929 /* pnstrdup, but deal with xmlChar not char; len is measured in xmlChars */
931 xml_pnstrdup(const xmlChar
*str
, size_t len
)
935 result
= (xmlChar
*) palloc((len
+ 1) * sizeof(xmlChar
));
936 memcpy(result
, str
, len
* sizeof(xmlChar
));
942 * str is the null-terminated input string. Remaining arguments are
943 * output arguments; each can be NULL if value is not wanted.
944 * version and encoding are returned as locally-palloc'd strings.
945 * Result is 0 if OK, an error code if not.
948 parse_xml_decl(const xmlChar
* str
, size_t *lenp
,
949 xmlChar
** version
, xmlChar
** encoding
, int *standalone
)
952 const xmlChar
*save_p
;
959 /* Initialize output arguments to "not present" */
969 if (xmlStrncmp(p
, (xmlChar
*) "<?xml", 5) != 0)
972 /* if next char is name char, it's a PI like <?xml-stylesheet ...?> */
973 utf8len
= strlen((const char *) (p
+ 5));
974 utf8char
= xmlGetUTF8Char(p
+ 5, &utf8len
);
975 if (PG_XMLISNAMECHAR(utf8char
))
983 if (xmlStrncmp(p
, (xmlChar
*) "version", 7) != 0)
984 return XML_ERR_VERSION_MISSING
;
988 return XML_ERR_VERSION_MISSING
;
992 if (*p
== '\'' || *p
== '"')
996 q
= xmlStrchr(p
+ 1, *p
);
998 return XML_ERR_VERSION_MISSING
;
1001 *version
= xml_pnstrdup(p
+ 1, q
- p
- 1);
1005 return XML_ERR_VERSION_MISSING
;
1010 if (xmlStrncmp(p
, (xmlChar
*) "encoding", 8) == 0)
1012 CHECK_XML_SPACE(save_p
);
1016 return XML_ERR_MISSING_ENCODING
;
1020 if (*p
== '\'' || *p
== '"')
1024 q
= xmlStrchr(p
+ 1, *p
);
1026 return XML_ERR_MISSING_ENCODING
;
1029 *encoding
= xml_pnstrdup(p
+ 1, q
- p
- 1);
1033 return XML_ERR_MISSING_ENCODING
;
1043 if (xmlStrncmp(p
, (xmlChar
*) "standalone", 10) == 0)
1045 CHECK_XML_SPACE(save_p
);
1049 return XML_ERR_STANDALONE_VALUE
;
1052 if (xmlStrncmp(p
, (xmlChar
*) "'yes'", 5) == 0 ||
1053 xmlStrncmp(p
, (xmlChar
*) "\"yes\"", 5) == 0)
1058 else if (xmlStrncmp(p
, (xmlChar
*) "'no'", 4) == 0 ||
1059 xmlStrncmp(p
, (xmlChar
*) "\"no\"", 4) == 0)
1065 return XML_ERR_STANDALONE_VALUE
;
1073 if (xmlStrncmp(p
, (xmlChar
*) "?>", 2) != 0)
1074 return XML_ERR_XMLDECL_NOT_FINISHED
;
1080 for (p
= str
; p
< str
+ len
; p
++)
1082 return XML_ERR_INVALID_CHAR
;
1092 * Write an XML declaration. On output, we adjust the XML declaration
1093 * as follows. (These rules are the moral equivalent of the clause
1094 * "Serialization of an XML value" in the SQL standard.)
1096 * We try to avoid generating an XML declaration if possible. This is
1097 * so that you don't get trivial things like xml '<foo/>' resulting in
1098 * '<?xml version="1.0"?><foo/>', which would surely be annoying. We
1099 * must provide a declaration if the standalone property is specified
1100 * or if we include an encoding declaration. If we have a
1101 * declaration, we must specify a version (XML requires this).
1102 * Otherwise we only make a declaration if the version is not "1.0",
1103 * which is the default version specified in SQL:2003.
1106 print_xml_decl(StringInfo buf
, const xmlChar
* version
,
1107 pg_enc encoding
, int standalone
)
1109 xml_init(); /* why is this here? */
1111 if ((version
&& strcmp((char *) version
, PG_XML_DEFAULT_VERSION
) != 0)
1112 || (encoding
&& encoding
!= PG_UTF8
)
1113 || standalone
!= -1)
1115 appendStringInfoString(buf
, "<?xml");
1118 appendStringInfo(buf
, " version=\"%s\"", version
);
1120 appendStringInfo(buf
, " version=\"%s\"", PG_XML_DEFAULT_VERSION
);
1122 if (encoding
&& encoding
!= PG_UTF8
)
1125 * XXX might be useful to convert this to IANA names (ISO-8859-1
1126 * instead of LATIN1 etc.); needs field experience
1128 appendStringInfo(buf
, " encoding=\"%s\"",
1129 pg_encoding_to_char(encoding
));
1132 if (standalone
== 1)
1133 appendStringInfoString(buf
, " standalone=\"yes\"");
1134 else if (standalone
== 0)
1135 appendStringInfoString(buf
, " standalone=\"no\"");
1136 appendStringInfoString(buf
, "?>");
1146 * Convert a C string to XML internal representation
1148 * Note: it is caller's responsibility to xmlFreeDoc() the result,
1149 * else a permanent memory leak will ensue!
1151 * TODO maybe libxml2's xmlreader is better? (do not construct DOM,
1152 * yet do not use SAX - see xmlreader.c)
1155 xml_parse(text
*data
, XmlOptionType xmloption_arg
, bool preserve_whitespace
,
1160 xmlChar
*utf8string
;
1161 xmlParserCtxtPtr ctxt
;
1164 len
= VARSIZE(data
) - VARHDRSZ
; /* will be useful later */
1165 string
= xml_text2xmlChar(data
);
1167 utf8string
= pg_do_encoding_conversion(string
,
1170 xmlChar_to_encoding(encoding
) :
1171 GetDatabaseEncoding(),
1174 /* Start up libxml and its parser (no-ops if already done) */
1178 ctxt
= xmlNewParserCtxt();
1180 xml_ereport(ERROR
, ERRCODE_OUT_OF_MEMORY
,
1181 "could not allocate parser context");
1183 /* Use a TRY block to ensure the ctxt is released */
1186 if (xmloption_arg
== XMLOPTION_DOCUMENT
)
1189 * Note, that here we try to apply DTD defaults
1190 * (XML_PARSE_DTDATTR) according to SQL/XML:10.16.7.d: 'Default
1191 * values defined by internal DTD are applied'. As for external
1192 * DTDs, we try to support them too, (see SQL/XML:10.16.7.e)
1194 doc
= xmlCtxtReadDoc(ctxt
, utf8string
,
1197 XML_PARSE_NOENT
| XML_PARSE_DTDATTR
1198 | (preserve_whitespace
? 0 : XML_PARSE_NOBLANKS
));
1200 xml_ereport(ERROR
, ERRCODE_INVALID_XML_DOCUMENT
,
1201 "invalid XML document");
1207 xmlChar
*version
= NULL
;
1208 int standalone
= -1;
1210 res_code
= parse_xml_decl(utf8string
,
1211 &count
, &version
, NULL
, &standalone
);
1213 xml_ereport_by_code(ERROR
, ERRCODE_INVALID_XML_CONTENT
,
1214 "invalid XML content: invalid XML declaration",
1217 doc
= xmlNewDoc(version
);
1218 Assert(doc
->encoding
== NULL
);
1219 doc
->encoding
= xmlStrdup((const xmlChar
*) "UTF-8");
1220 doc
->standalone
= standalone
;
1222 res_code
= xmlParseBalancedChunkMemory(doc
, NULL
, NULL
, 0,
1223 utf8string
+ count
, NULL
);
1227 xml_ereport(ERROR
, ERRCODE_INVALID_XML_CONTENT
,
1228 "invalid XML content");
1234 xmlFreeParserCtxt(ctxt
);
1239 xmlFreeParserCtxt(ctxt
);
1246 * xmlChar<->text conversions
1249 xml_text2xmlChar(text
*in
)
1251 return (xmlChar
*) text_to_cstring(in
);
1255 #ifdef USE_LIBXMLCONTEXT
1258 * Manage the special context used for all libxml allocations (but only
1259 * in special debug builds; see notes at top of file)
1262 xml_memory_init(void)
1264 /* Create memory context if not there already */
1265 if (LibxmlContext
== NULL
)
1266 LibxmlContext
= AllocSetContextCreate(TopMemoryContext
,
1268 ALLOCSET_DEFAULT_MINSIZE
,
1269 ALLOCSET_DEFAULT_INITSIZE
,
1270 ALLOCSET_DEFAULT_MAXSIZE
);
1272 /* Re-establish the callbacks even if already set */
1273 xmlMemSetup(xml_pfree
, xml_palloc
, xml_repalloc
, xml_pstrdup
);
1277 * Wrappers for memory management functions
1280 xml_palloc(size_t size
)
1282 return MemoryContextAlloc(LibxmlContext
, size
);
1287 xml_repalloc(void *ptr
, size_t size
)
1289 return repalloc(ptr
, size
);
1294 xml_pfree(void *ptr
)
1301 xml_pstrdup(const char *string
)
1303 return MemoryContextStrdup(LibxmlContext
, string
);
1306 #endif /* USE_LIBXMLCONTEXT */
1310 * Wrapper for "ereport" function for XML-related errors. The "msg"
1311 * is the SQL-level message; some can be adopted from the SQL/XML
1312 * standard. This function adds libxml's native error messages, if
1316 xml_ereport(int level
, int sqlcode
, const char *msg
)
1320 if (xml_err_buf
->len
> 0)
1322 detail
= pstrdup(xml_err_buf
->data
);
1323 resetStringInfo(xml_err_buf
);
1328 /* libxml error messages end in '\n'; get rid of it */
1333 len
= strlen(detail
);
1334 if (len
> 0 && detail
[len
- 1] == '\n')
1335 detail
[len
- 1] = '\0';
1340 errdetail("%s", detail
)));
1346 errmsg("%s", msg
)));
1352 * Error handler for libxml error messages
1355 xml_errorHandler(void *ctxt
, const char *msg
,...)
1357 /* Append the formatted text to xml_err_buf */
1363 /* Try to format the data. */
1364 va_start(args
, msg
);
1365 success
= appendStringInfoVA(xml_err_buf
, msg
, args
);
1371 /* Double the buffer size and try again. */
1372 enlargeStringInfo(xml_err_buf
, xml_err_buf
->maxlen
);
1378 * Wrapper for "ereport" function for XML-related errors. The "msg"
1379 * is the SQL-level message; some can be adopted from the SQL/XML
1380 * standard. This function uses "code" to create a textual detail
1381 * message. At the moment, we only need to cover those codes that we
1382 * may raise in this file.
1385 xml_ereport_by_code(int level
, int sqlcode
,
1386 const char *msg
, int code
)
1392 case XML_ERR_INVALID_CHAR
:
1393 det
= gettext_noop("Invalid character value.");
1395 case XML_ERR_SPACE_REQUIRED
:
1396 det
= gettext_noop("Space required.");
1398 case XML_ERR_STANDALONE_VALUE
:
1399 det
= gettext_noop("standalone accepts only 'yes' or 'no'.");
1401 case XML_ERR_VERSION_MISSING
:
1402 det
= gettext_noop("Malformed declaration: missing version.");
1404 case XML_ERR_MISSING_ENCODING
:
1405 det
= gettext_noop("Missing encoding in text declaration.");
1407 case XML_ERR_XMLDECL_NOT_FINISHED
:
1408 det
= gettext_noop("Parsing XML declaration: '?>' expected.");
1411 det
= gettext_noop("Unrecognized libxml error code: %d.");
1418 errdetail(det
, code
)));
1423 * Convert one char in the current server encoding to a Unicode codepoint.
1426 sqlchar_to_unicode(char *s
)
1429 pg_wchar ret
[2]; /* need space for trailing zero */
1431 utf8string
= (char *) pg_do_encoding_conversion((unsigned char *) s
,
1433 GetDatabaseEncoding(),
1436 pg_encoding_mb2wchar_with_len(PG_UTF8
, utf8string
, ret
,
1437 pg_encoding_mblen(PG_UTF8
, utf8string
));
1439 if (utf8string
!= s
)
1447 is_valid_xml_namefirst(pg_wchar c
)
1449 /* (Letter | '_' | ':') */
1450 return (xmlIsBaseCharQ(c
) || xmlIsIdeographicQ(c
)
1451 || c
== '_' || c
== ':');
1456 is_valid_xml_namechar(pg_wchar c
)
1458 /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
1459 return (xmlIsBaseCharQ(c
) || xmlIsIdeographicQ(c
)
1461 || c
== '.' || c
== '-' || c
== '_' || c
== ':'
1462 || xmlIsCombiningQ(c
)
1463 || xmlIsExtenderQ(c
));
1465 #endif /* USE_LIBXML */
1469 * Map SQL identifier to XML name; see SQL/XML:2003 section 9.1.
1472 map_sql_identifier_to_xml_name(char *ident
, bool fully_escaped
,
1480 * SQL/XML doesn't make use of this case anywhere, so it's probably a
1483 Assert(fully_escaped
|| !escape_period
);
1485 initStringInfo(&buf
);
1487 for (p
= ident
; *p
; p
+= pg_mblen(p
))
1489 if (*p
== ':' && (p
== ident
|| fully_escaped
))
1490 appendStringInfo(&buf
, "_x003A_");
1491 else if (*p
== '_' && *(p
+ 1) == 'x')
1492 appendStringInfo(&buf
, "_x005F_");
1493 else if (fully_escaped
&& p
== ident
&&
1494 pg_strncasecmp(p
, "xml", 3) == 0)
1497 appendStringInfo(&buf
, "_x0078_");
1499 appendStringInfo(&buf
, "_x0058_");
1501 else if (escape_period
&& *p
== '.')
1502 appendStringInfo(&buf
, "_x002E_");
1505 pg_wchar u
= sqlchar_to_unicode(p
);
1508 ? !is_valid_xml_namefirst(u
)
1509 : !is_valid_xml_namechar(u
))
1510 appendStringInfo(&buf
, "_x%04X_", (unsigned int) u
);
1512 appendBinaryStringInfo(&buf
, p
, pg_mblen(p
));
1517 #else /* not USE_LIBXML */
1520 #endif /* not USE_LIBXML */
1525 * Map a Unicode codepoint into the current server encoding.
1528 unicode_to_sqlchar(pg_wchar c
)
1530 unsigned char utf8string
[5]; /* need room for trailing zero */
1533 memset(utf8string
, 0, sizeof(utf8string
));
1534 unicode_to_utf8(c
, utf8string
);
1536 result
= (char *) pg_do_encoding_conversion(utf8string
,
1537 pg_encoding_mblen(PG_UTF8
,
1538 (char *) utf8string
),
1540 GetDatabaseEncoding());
1541 /* if pg_do_encoding_conversion didn't strdup, we must */
1542 if (result
== (char *) utf8string
)
1543 result
= pstrdup(result
);
1549 * Map XML name to SQL identifier; see SQL/XML:2003 section 9.17.
1552 map_xml_name_to_sql_identifier(char *name
)
1557 initStringInfo(&buf
);
1559 for (p
= name
; *p
; p
+= pg_mblen(p
))
1561 if (*p
== '_' && *(p
+ 1) == 'x'
1562 && isxdigit((unsigned char) *(p
+ 2))
1563 && isxdigit((unsigned char) *(p
+ 3))
1564 && isxdigit((unsigned char) *(p
+ 4))
1565 && isxdigit((unsigned char) *(p
+ 5))
1570 sscanf(p
+ 2, "%X", &u
);
1571 appendStringInfoString(&buf
, unicode_to_sqlchar(u
));
1575 appendBinaryStringInfo(&buf
, p
, pg_mblen(p
));
1582 * Map SQL value to XML value; see SQL/XML:2003 section 9.16.
1585 map_sql_value_to_xml_value(Datum value
, Oid type
)
1589 if (type_is_array(type
))
1601 array
= DatumGetArrayTypeP(value
);
1602 elmtype
= ARR_ELEMTYPE(array
);
1603 get_typlenbyvalalign(elmtype
, &elmlen
, &elmbyval
, &elmalign
);
1605 deconstruct_array(array
, elmtype
,
1606 elmlen
, elmbyval
, elmalign
,
1607 &elem_values
, &elem_nulls
,
1610 initStringInfo(&buf
);
1612 for (i
= 0; i
< num_elems
; i
++)
1616 appendStringInfoString(&buf
, "<element>");
1617 appendStringInfoString(&buf
,
1618 map_sql_value_to_xml_value(elem_values
[i
],
1620 appendStringInfoString(&buf
, "</element>");
1636 * Special XSD formatting for some data types
1641 if (DatumGetBool(value
))
1650 char buf
[MAXDATELEN
+ 1];
1652 date
= DatumGetDateADT(value
);
1653 /* XSD doesn't support infinite values */
1654 if (DATE_NOT_FINITE(date
))
1656 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
1657 errmsg("date out of range"),
1658 errdetail("XML does not support infinite date values.")));
1659 j2date(date
+ POSTGRES_EPOCH_JDATE
,
1660 &(tm
.tm_year
), &(tm
.tm_mon
), &(tm
.tm_mday
));
1661 EncodeDateOnly(&tm
, USE_XSD_DATES
, buf
);
1663 return pstrdup(buf
);
1668 Timestamp timestamp
;
1672 char buf
[MAXDATELEN
+ 1];
1674 timestamp
= DatumGetTimestamp(value
);
1676 /* XSD doesn't support infinite values */
1677 if (TIMESTAMP_NOT_FINITE(timestamp
))
1679 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
1680 errmsg("timestamp out of range"),
1681 errdetail("XML does not support infinite timestamp values.")));
1682 else if (timestamp2tm(timestamp
, NULL
, &tm
, &fsec
, NULL
, NULL
) == 0)
1683 EncodeDateTime(&tm
, fsec
, NULL
, &tzn
, USE_XSD_DATES
, buf
);
1686 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
1687 errmsg("timestamp out of range")));
1689 return pstrdup(buf
);
1692 case TIMESTAMPTZOID
:
1694 TimestampTz timestamp
;
1699 char buf
[MAXDATELEN
+ 1];
1701 timestamp
= DatumGetTimestamp(value
);
1703 /* XSD doesn't support infinite values */
1704 if (TIMESTAMP_NOT_FINITE(timestamp
))
1706 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
1707 errmsg("timestamp out of range"),
1708 errdetail("XML does not support infinite timestamp values.")));
1709 else if (timestamp2tm(timestamp
, &tz
, &tm
, &fsec
, &tzn
, NULL
) == 0)
1710 EncodeDateTime(&tm
, fsec
, &tz
, &tzn
, USE_XSD_DATES
, buf
);
1713 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
1714 errmsg("timestamp out of range")));
1716 return pstrdup(buf
);
1722 bytea
*bstr
= DatumGetByteaPP(value
);
1723 xmlBufferPtr buf
= NULL
;
1724 xmlTextWriterPtr writer
= NULL
;
1731 buf
= xmlBufferCreate();
1733 xml_ereport(ERROR
, ERRCODE_OUT_OF_MEMORY
,
1734 "could not allocate xmlBuffer");
1735 writer
= xmlNewTextWriterMemory(buf
, 0);
1737 xml_ereport(ERROR
, ERRCODE_OUT_OF_MEMORY
,
1738 "could not allocate xmlTextWriter");
1740 if (xmlbinary
== XMLBINARY_BASE64
)
1741 xmlTextWriterWriteBase64(writer
, VARDATA_ANY(bstr
),
1742 0, VARSIZE_ANY_EXHDR(bstr
));
1744 xmlTextWriterWriteBinHex(writer
, VARDATA_ANY(bstr
),
1745 0, VARSIZE_ANY_EXHDR(bstr
));
1747 /* we MUST do this now to flush data out to the buffer */
1748 xmlFreeTextWriter(writer
);
1751 result
= pstrdup((const char *) xmlBufferContent(buf
));
1756 xmlFreeTextWriter(writer
);
1767 #endif /* USE_LIBXML */
1772 * otherwise, just use the type's native text representation
1774 getTypeOutputInfo(type
, &typeOut
, &isvarlena
);
1775 str
= OidOutputFunctionCall(typeOut
, value
);
1777 /* ... exactly as-is for XML */
1781 /* otherwise, translate special characters as needed */
1782 initStringInfo(&buf
);
1784 for (p
= str
; *p
; p
++)
1789 appendStringInfoString(&buf
, "&");
1792 appendStringInfoString(&buf
, "<");
1795 appendStringInfoString(&buf
, ">");
1798 appendStringInfoString(&buf
, "
");
1801 appendStringInfoCharMacro(&buf
, *p
);
1812 _SPI_strdup(const char *s
)
1814 size_t len
= strlen(s
) + 1;
1815 char *ret
= SPI_palloc(len
);
1817 memcpy(ret
, s
, len
);
1823 * SQL to XML mapping functions
1825 * What follows below is intentionally organized so that you can read
1826 * along in the SQL/XML:2003 standard. The functions are mostly split
1827 * up and ordered they way the clauses lay out in the standards
1828 * document, and the identifiers are also aligned with the standard
1829 * text. (SQL/XML:2006 appears to be ordered differently,
1832 * There are many things going on there:
1834 * There are two kinds of mappings: Mapping SQL data (table contents)
1835 * to XML documents, and mapping SQL structure (the "schema") to XML
1836 * Schema. And there are functions that do both at the same time.
1838 * Then you can map a database, a schema, or a table, each in both
1839 * ways. This breaks down recursively: Mapping a database invokes
1840 * mapping schemas, which invokes mapping tables, which invokes
1841 * mapping rows, which invokes mapping columns, although you can't
1842 * call the last two from the outside. Because of this, there are a
1843 * number of xyz_internal() functions which are to be called both from
1844 * the function manager wrapper and from some upper layer in a
1847 * See the documentation about what the common function arguments
1848 * nulls, tableforest, and targetns mean.
1850 * Some style guidelines for XML output: Use double quotes for quoting
1851 * XML attributes. Indent XML elements by two spaces, but remember
1852 * that a lot of code is called recursively at different levels, so
1853 * it's better not to indent rather than create output that indents
1854 * and outdents weirdly. Add newlines to make the output look nice.
1859 * Visibility of objects for XML mappings; see SQL/XML:2003 section
1864 * Given a query, which must return type oid as first column, produce
1865 * a list of Oids with the query results.
1868 query_to_oid_list(const char *query
)
1873 SPI_execute(query
, true, 0);
1875 for (i
= 0; i
< SPI_processed
; i
++)
1880 oid
= SPI_getbinval(SPI_tuptable
->vals
[i
],
1881 SPI_tuptable
->tupdesc
,
1885 list
= lappend_oid(list
, DatumGetObjectId(oid
));
1893 schema_get_xml_visible_tables(Oid nspid
)
1895 StringInfoData query
;
1897 initStringInfo(&query
);
1898 appendStringInfo(&query
, "SELECT oid FROM pg_catalog.pg_class WHERE relnamespace = %u AND relkind IN ('r', 'v') AND pg_catalog.has_table_privilege (oid, 'SELECT') ORDER BY relname;", nspid
);
1900 return query_to_oid_list(query
.data
);
1905 * Including the system schemas is probably not useful for a database
1908 #define XML_VISIBLE_SCHEMAS_EXCLUDE "(nspname ~ '^pg_' OR nspname = 'information_schema')"
1910 #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
1914 database_get_xml_visible_schemas(void)
1916 return query_to_oid_list(XML_VISIBLE_SCHEMAS
" ORDER BY nspname;");
1921 database_get_xml_visible_tables(void)
1923 /* At the moment there is no order required here. */
1924 return query_to_oid_list("SELECT oid FROM pg_catalog.pg_class WHERE relkind IN ('r', 'v') AND pg_catalog.has_table_privilege (pg_class.oid, 'SELECT') AND relnamespace IN (" XML_VISIBLE_SCHEMAS
");");
1929 * Map SQL table to XML and/or XML Schema document; see SQL/XML:2003
1934 table_to_xml_internal(Oid relid
,
1935 const char *xmlschema
, bool nulls
, bool tableforest
,
1936 const char *targetns
, bool top_level
)
1938 StringInfoData query
;
1940 initStringInfo(&query
);
1941 appendStringInfo(&query
, "SELECT * FROM %s",
1942 DatumGetCString(DirectFunctionCall1(regclassout
,
1943 ObjectIdGetDatum(relid
))));
1944 return query_to_xml_internal(query
.data
, get_rel_name(relid
),
1945 xmlschema
, nulls
, tableforest
,
1946 targetns
, top_level
);
1951 table_to_xml(PG_FUNCTION_ARGS
)
1953 Oid relid
= PG_GETARG_OID(0);
1954 bool nulls
= PG_GETARG_BOOL(1);
1955 bool tableforest
= PG_GETARG_BOOL(2);
1956 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(3));
1958 PG_RETURN_XML_P(stringinfo_to_xmltype(table_to_xml_internal(relid
, NULL
,
1965 query_to_xml(PG_FUNCTION_ARGS
)
1967 char *query
= text_to_cstring(PG_GETARG_TEXT_PP(0));
1968 bool nulls
= PG_GETARG_BOOL(1);
1969 bool tableforest
= PG_GETARG_BOOL(2);
1970 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(3));
1972 PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query
, NULL
,
1973 NULL
, nulls
, tableforest
,
1979 cursor_to_xml(PG_FUNCTION_ARGS
)
1981 char *name
= text_to_cstring(PG_GETARG_TEXT_PP(0));
1982 int32 count
= PG_GETARG_INT32(1);
1983 bool nulls
= PG_GETARG_BOOL(2);
1984 bool tableforest
= PG_GETARG_BOOL(3);
1985 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(4));
1987 StringInfoData result
;
1991 initStringInfo(&result
);
1994 portal
= SPI_cursor_find(name
);
1997 (errcode(ERRCODE_UNDEFINED_CURSOR
),
1998 errmsg("cursor \"%s\" does not exist", name
)));
2000 SPI_cursor_fetch(portal
, true, count
);
2001 for (i
= 0; i
< SPI_processed
; i
++)
2002 SPI_sql_row_to_xmlelement(i
, &result
, NULL
, nulls
,
2003 tableforest
, targetns
, true);
2007 PG_RETURN_XML_P(stringinfo_to_xmltype(&result
));
2012 * Write the start tag of the root element of a data mapping.
2014 * top_level means that this is the very top level of the eventual
2015 * output. For example, when the user calls table_to_xml, then a call
2016 * with a table name to this function is the top level. When the user
2017 * calls database_to_xml, then a call with a schema name to this
2018 * function is not the top level. If top_level is false, then the XML
2019 * namespace declarations are omitted, because they supposedly already
2020 * appeared earlier in the output. Repeating them is not wrong, but
2024 xmldata_root_element_start(StringInfo result
, const char *eltname
,
2025 const char *xmlschema
, const char *targetns
,
2028 /* This isn't really wrong but currently makes no sense. */
2029 Assert(top_level
|| !xmlschema
);
2031 appendStringInfo(result
, "<%s", eltname
);
2034 appendStringInfoString(result
, " xmlns:xsi=\"" NAMESPACE_XSI
"\"");
2035 if (strlen(targetns
) > 0)
2036 appendStringInfo(result
, " xmlns=\"%s\"", targetns
);
2040 /* FIXME: better targets */
2041 if (strlen(targetns
) > 0)
2042 appendStringInfo(result
, " xsi:schemaLocation=\"%s #\"", targetns
);
2044 appendStringInfo(result
, " xsi:noNamespaceSchemaLocation=\"#\"");
2046 appendStringInfo(result
, ">\n\n");
2051 xmldata_root_element_end(StringInfo result
, const char *eltname
)
2053 appendStringInfo(result
, "</%s>\n", eltname
);
2058 query_to_xml_internal(const char *query
, char *tablename
,
2059 const char *xmlschema
, bool nulls
, bool tableforest
,
2060 const char *targetns
, bool top_level
)
2067 xmltn
= map_sql_identifier_to_xml_name(tablename
, true, false);
2071 result
= makeStringInfo();
2074 if (SPI_execute(query
, true, 0) != SPI_OK_SELECT
)
2076 (errcode(ERRCODE_DATA_EXCEPTION
),
2077 errmsg("invalid query")));
2080 xmldata_root_element_start(result
, xmltn
, xmlschema
,
2081 targetns
, top_level
);
2084 appendStringInfo(result
, "%s\n\n", xmlschema
);
2086 for (i
= 0; i
< SPI_processed
; i
++)
2087 SPI_sql_row_to_xmlelement(i
, result
, tablename
, nulls
,
2088 tableforest
, targetns
, top_level
);
2091 xmldata_root_element_end(result
, xmltn
);
2100 table_to_xmlschema(PG_FUNCTION_ARGS
)
2102 Oid relid
= PG_GETARG_OID(0);
2103 bool nulls
= PG_GETARG_BOOL(1);
2104 bool tableforest
= PG_GETARG_BOOL(2);
2105 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(3));
2109 rel
= heap_open(relid
, AccessShareLock
);
2110 result
= map_sql_table_to_xmlschema(rel
->rd_att
, relid
, nulls
,
2111 tableforest
, targetns
);
2112 heap_close(rel
, NoLock
);
2114 PG_RETURN_XML_P(cstring_to_xmltype(result
));
2119 query_to_xmlschema(PG_FUNCTION_ARGS
)
2121 char *query
= text_to_cstring(PG_GETARG_TEXT_PP(0));
2122 bool nulls
= PG_GETARG_BOOL(1);
2123 bool tableforest
= PG_GETARG_BOOL(2);
2124 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(3));
2131 if ((plan
= SPI_prepare(query
, 0, NULL
)) == NULL
)
2132 elog(ERROR
, "SPI_prepare(\"%s\") failed", query
);
2134 if ((portal
= SPI_cursor_open(NULL
, plan
, NULL
, NULL
, true)) == NULL
)
2135 elog(ERROR
, "SPI_cursor_open(\"%s\") failed", query
);
2137 result
= _SPI_strdup(map_sql_table_to_xmlschema(portal
->tupDesc
,
2139 tableforest
, targetns
));
2140 SPI_cursor_close(portal
);
2143 PG_RETURN_XML_P(cstring_to_xmltype(result
));
2148 cursor_to_xmlschema(PG_FUNCTION_ARGS
)
2150 char *name
= text_to_cstring(PG_GETARG_TEXT_PP(0));
2151 bool nulls
= PG_GETARG_BOOL(1);
2152 bool tableforest
= PG_GETARG_BOOL(2);
2153 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(3));
2154 const char *xmlschema
;
2158 portal
= SPI_cursor_find(name
);
2161 (errcode(ERRCODE_UNDEFINED_CURSOR
),
2162 errmsg("cursor \"%s\" does not exist", name
)));
2164 xmlschema
= _SPI_strdup(map_sql_table_to_xmlschema(portal
->tupDesc
,
2166 tableforest
, targetns
));
2169 PG_RETURN_XML_P(cstring_to_xmltype(xmlschema
));
2174 table_to_xml_and_xmlschema(PG_FUNCTION_ARGS
)
2176 Oid relid
= PG_GETARG_OID(0);
2177 bool nulls
= PG_GETARG_BOOL(1);
2178 bool tableforest
= PG_GETARG_BOOL(2);
2179 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(3));
2181 const char *xmlschema
;
2183 rel
= heap_open(relid
, AccessShareLock
);
2184 xmlschema
= map_sql_table_to_xmlschema(rel
->rd_att
, relid
, nulls
,
2185 tableforest
, targetns
);
2186 heap_close(rel
, NoLock
);
2188 PG_RETURN_XML_P(stringinfo_to_xmltype(table_to_xml_internal(relid
,
2189 xmlschema
, nulls
, tableforest
,
2195 query_to_xml_and_xmlschema(PG_FUNCTION_ARGS
)
2197 char *query
= text_to_cstring(PG_GETARG_TEXT_PP(0));
2198 bool nulls
= PG_GETARG_BOOL(1);
2199 bool tableforest
= PG_GETARG_BOOL(2);
2200 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(3));
2202 const char *xmlschema
;
2208 if ((plan
= SPI_prepare(query
, 0, NULL
)) == NULL
)
2209 elog(ERROR
, "SPI_prepare(\"%s\") failed", query
);
2211 if ((portal
= SPI_cursor_open(NULL
, plan
, NULL
, NULL
, true)) == NULL
)
2212 elog(ERROR
, "SPI_cursor_open(\"%s\") failed", query
);
2214 xmlschema
= _SPI_strdup(map_sql_table_to_xmlschema(portal
->tupDesc
,
2215 InvalidOid
, nulls
, tableforest
, targetns
));
2216 SPI_cursor_close(portal
);
2219 PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query
, NULL
,
2220 xmlschema
, nulls
, tableforest
,
2226 * Map SQL schema to XML and/or XML Schema document; see SQL/XML:2003
2231 schema_to_xml_internal(Oid nspid
, const char *xmlschema
, bool nulls
,
2232 bool tableforest
, const char *targetns
, bool top_level
)
2239 xmlsn
= map_sql_identifier_to_xml_name(get_namespace_name(nspid
),
2241 result
= makeStringInfo();
2243 xmldata_root_element_start(result
, xmlsn
, xmlschema
, targetns
, top_level
);
2246 appendStringInfo(result
, "%s\n\n", xmlschema
);
2250 relid_list
= schema_get_xml_visible_tables(nspid
);
2254 foreach(cell
, relid_list
)
2256 Oid relid
= lfirst_oid(cell
);
2259 subres
= table_to_xml_internal(relid
, NULL
, nulls
, tableforest
,
2262 appendStringInfoString(result
, subres
->data
);
2263 appendStringInfoChar(result
, '\n');
2269 xmldata_root_element_end(result
, xmlsn
);
2276 schema_to_xml(PG_FUNCTION_ARGS
)
2278 Name name
= PG_GETARG_NAME(0);
2279 bool nulls
= PG_GETARG_BOOL(1);
2280 bool tableforest
= PG_GETARG_BOOL(2);
2281 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(3));
2286 schemaname
= NameStr(*name
);
2287 nspid
= LookupExplicitNamespace(schemaname
);
2289 PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xml_internal(nspid
, NULL
,
2290 nulls
, tableforest
, targetns
, true)));
2295 * Write the start element of the root element of an XML Schema mapping.
2298 xsd_schema_element_start(StringInfo result
, const char *targetns
)
2300 appendStringInfoString(result
,
2302 " xmlns:xsd=\"" NAMESPACE_XSD
"\"");
2303 if (strlen(targetns
) > 0)
2304 appendStringInfo(result
,
2306 " targetNamespace=\"%s\"\n"
2307 " elementFormDefault=\"qualified\"",
2309 appendStringInfoString(result
,
2315 xsd_schema_element_end(StringInfo result
)
2317 appendStringInfoString(result
, "</xsd:schema>");
2322 schema_to_xmlschema_internal(const char *schemaname
, bool nulls
,
2323 bool tableforest
, const char *targetns
)
2331 result
= makeStringInfo();
2333 nspid
= LookupExplicitNamespace(schemaname
);
2335 xsd_schema_element_start(result
, targetns
);
2339 relid_list
= schema_get_xml_visible_tables(nspid
);
2342 foreach(cell
, relid_list
)
2346 rel
= heap_open(lfirst_oid(cell
), AccessShareLock
);
2347 tupdesc_list
= lappend(tupdesc_list
, CreateTupleDescCopy(rel
->rd_att
));
2348 heap_close(rel
, NoLock
);
2351 appendStringInfoString(result
,
2352 map_sql_typecoll_to_xmlschema_types(tupdesc_list
));
2354 appendStringInfoString(result
,
2355 map_sql_schema_to_xmlschema_types(nspid
, relid_list
,
2356 nulls
, tableforest
, targetns
));
2358 xsd_schema_element_end(result
);
2367 schema_to_xmlschema(PG_FUNCTION_ARGS
)
2369 Name name
= PG_GETARG_NAME(0);
2370 bool nulls
= PG_GETARG_BOOL(1);
2371 bool tableforest
= PG_GETARG_BOOL(2);
2372 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(3));
2374 PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xmlschema_internal(NameStr(*name
),
2375 nulls
, tableforest
, targetns
)));
2380 schema_to_xml_and_xmlschema(PG_FUNCTION_ARGS
)
2382 Name name
= PG_GETARG_NAME(0);
2383 bool nulls
= PG_GETARG_BOOL(1);
2384 bool tableforest
= PG_GETARG_BOOL(2);
2385 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(3));
2388 StringInfo xmlschema
;
2390 schemaname
= NameStr(*name
);
2391 nspid
= LookupExplicitNamespace(schemaname
);
2393 xmlschema
= schema_to_xmlschema_internal(schemaname
, nulls
,
2394 tableforest
, targetns
);
2396 PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xml_internal(nspid
,
2397 xmlschema
->data
, nulls
,
2398 tableforest
, targetns
, true)));
2403 * Map SQL database to XML and/or XML Schema document; see SQL/XML:2003
2408 database_to_xml_internal(const char *xmlschema
, bool nulls
,
2409 bool tableforest
, const char *targetns
)
2416 xmlcn
= map_sql_identifier_to_xml_name(get_database_name(MyDatabaseId
),
2418 result
= makeStringInfo();
2420 xmldata_root_element_start(result
, xmlcn
, xmlschema
, targetns
, true);
2423 appendStringInfo(result
, "%s\n\n", xmlschema
);
2427 nspid_list
= database_get_xml_visible_schemas();
2431 foreach(cell
, nspid_list
)
2433 Oid nspid
= lfirst_oid(cell
);
2436 subres
= schema_to_xml_internal(nspid
, NULL
, nulls
,
2437 tableforest
, targetns
, false);
2439 appendStringInfoString(result
, subres
->data
);
2440 appendStringInfoChar(result
, '\n');
2446 xmldata_root_element_end(result
, xmlcn
);
2453 database_to_xml(PG_FUNCTION_ARGS
)
2455 bool nulls
= PG_GETARG_BOOL(0);
2456 bool tableforest
= PG_GETARG_BOOL(1);
2457 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(2));
2459 PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xml_internal(NULL
, nulls
,
2460 tableforest
, targetns
)));
2465 database_to_xmlschema_internal(bool nulls
, bool tableforest
,
2466 const char *targetns
)
2474 result
= makeStringInfo();
2476 xsd_schema_element_start(result
, targetns
);
2480 relid_list
= database_get_xml_visible_tables();
2481 nspid_list
= database_get_xml_visible_schemas();
2484 foreach(cell
, relid_list
)
2488 rel
= heap_open(lfirst_oid(cell
), AccessShareLock
);
2489 tupdesc_list
= lappend(tupdesc_list
, CreateTupleDescCopy(rel
->rd_att
));
2490 heap_close(rel
, NoLock
);
2493 appendStringInfoString(result
,
2494 map_sql_typecoll_to_xmlschema_types(tupdesc_list
));
2496 appendStringInfoString(result
,
2497 map_sql_catalog_to_xmlschema_types(nspid_list
, nulls
, tableforest
, targetns
));
2499 xsd_schema_element_end(result
);
2508 database_to_xmlschema(PG_FUNCTION_ARGS
)
2510 bool nulls
= PG_GETARG_BOOL(0);
2511 bool tableforest
= PG_GETARG_BOOL(1);
2512 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(2));
2514 PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xmlschema_internal(nulls
,
2515 tableforest
, targetns
)));
2520 database_to_xml_and_xmlschema(PG_FUNCTION_ARGS
)
2522 bool nulls
= PG_GETARG_BOOL(0);
2523 bool tableforest
= PG_GETARG_BOOL(1);
2524 const char *targetns
= text_to_cstring(PG_GETARG_TEXT_PP(2));
2525 StringInfo xmlschema
;
2527 xmlschema
= database_to_xmlschema_internal(nulls
, tableforest
, targetns
);
2529 PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xml_internal(xmlschema
->data
,
2530 nulls
, tableforest
, targetns
)));
2535 * Map a multi-part SQL name to an XML name; see SQL/XML:2003 section
2539 map_multipart_sql_identifier_to_xml_name(char *a
, char *b
, char *c
, char *d
)
2541 StringInfoData result
;
2543 initStringInfo(&result
);
2546 appendStringInfo(&result
, "%s",
2547 map_sql_identifier_to_xml_name(a
, true, true));
2549 appendStringInfo(&result
, ".%s",
2550 map_sql_identifier_to_xml_name(b
, true, true));
2552 appendStringInfo(&result
, ".%s",
2553 map_sql_identifier_to_xml_name(c
, true, true));
2555 appendStringInfo(&result
, ".%s",
2556 map_sql_identifier_to_xml_name(d
, true, true));
2563 * Map an SQL table to an XML Schema document; see SQL/XML:2003
2566 * Map an SQL table to XML Schema data types; see SQL/XML:2003 section
2570 map_sql_table_to_xmlschema(TupleDesc tupdesc
, Oid relid
, bool nulls
,
2571 bool tableforest
, const char *targetns
)
2575 char *tabletypename
;
2577 StringInfoData result
;
2579 initStringInfo(&result
);
2581 if (OidIsValid(relid
))
2584 Form_pg_class reltuple
;
2586 tuple
= SearchSysCache(RELOID
,
2587 ObjectIdGetDatum(relid
),
2589 if (!HeapTupleIsValid(tuple
))
2590 elog(ERROR
, "cache lookup failed for relation %u", relid
);
2591 reltuple
= (Form_pg_class
) GETSTRUCT(tuple
);
2593 xmltn
= map_sql_identifier_to_xml_name(NameStr(reltuple
->relname
),
2596 tabletypename
= map_multipart_sql_identifier_to_xml_name("TableType",
2597 get_database_name(MyDatabaseId
),
2598 get_namespace_name(reltuple
->relnamespace
),
2599 NameStr(reltuple
->relname
));
2601 rowtypename
= map_multipart_sql_identifier_to_xml_name("RowType",
2602 get_database_name(MyDatabaseId
),
2603 get_namespace_name(reltuple
->relnamespace
),
2604 NameStr(reltuple
->relname
));
2606 ReleaseSysCache(tuple
);
2615 tabletypename
= "TableType";
2616 rowtypename
= "RowType";
2619 xsd_schema_element_start(&result
, targetns
);
2621 appendStringInfoString(&result
,
2622 map_sql_typecoll_to_xmlschema_types(list_make1(tupdesc
)));
2624 appendStringInfo(&result
,
2625 "<xsd:complexType name=\"%s\">\n"
2626 " <xsd:sequence>\n",
2629 for (i
= 0; i
< tupdesc
->natts
; i
++)
2631 if (tupdesc
->attrs
[i
]->attisdropped
)
2633 appendStringInfo(&result
,
2634 " <xsd:element name=\"%s\" type=\"%s\"%s></xsd:element>\n",
2635 map_sql_identifier_to_xml_name(NameStr(tupdesc
->attrs
[i
]->attname
),
2637 map_sql_type_to_xml_name(tupdesc
->attrs
[i
]->atttypid
, -1),
2638 nulls
? " nillable=\"true\"" : " minOccurs=\"0\"");
2641 appendStringInfoString(&result
,
2642 " </xsd:sequence>\n"
2643 "</xsd:complexType>\n\n");
2647 appendStringInfo(&result
,
2648 "<xsd:complexType name=\"%s\">\n"
2650 " <xsd:element name=\"row\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n"
2651 " </xsd:sequence>\n"
2652 "</xsd:complexType>\n\n",
2653 tabletypename
, rowtypename
);
2655 appendStringInfo(&result
,
2656 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
2657 xmltn
, tabletypename
);
2660 appendStringInfo(&result
,
2661 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
2662 xmltn
, rowtypename
);
2664 xsd_schema_element_end(&result
);
2671 * Map an SQL schema to XML Schema data types; see SQL/XML section
2675 map_sql_schema_to_xmlschema_types(Oid nspid
, List
*relid_list
, bool nulls
,
2676 bool tableforest
, const char *targetns
)
2681 char *schematypename
;
2682 StringInfoData result
;
2685 dbname
= get_database_name(MyDatabaseId
);
2686 nspname
= get_namespace_name(nspid
);
2688 initStringInfo(&result
);
2690 xmlsn
= map_sql_identifier_to_xml_name(nspname
, true, false);
2692 schematypename
= map_multipart_sql_identifier_to_xml_name("SchemaType",
2697 appendStringInfo(&result
,
2698 "<xsd:complexType name=\"%s\">\n", schematypename
);
2700 appendStringInfoString(&result
,
2703 appendStringInfoString(&result
,
2704 " <xsd:sequence>\n");
2706 foreach(cell
, relid_list
)
2708 Oid relid
= lfirst_oid(cell
);
2709 char *relname
= get_rel_name(relid
);
2710 char *xmltn
= map_sql_identifier_to_xml_name(relname
, true, false);
2711 char *tabletypename
= map_multipart_sql_identifier_to_xml_name(tableforest
? "RowType" : "TableType",
2717 appendStringInfo(&result
,
2718 " <xsd:element name=\"%s\" type=\"%s\"/>\n",
2719 xmltn
, tabletypename
);
2721 appendStringInfo(&result
,
2722 " <xsd:element name=\"%s\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n",
2723 xmltn
, tabletypename
);
2727 appendStringInfoString(&result
,
2730 appendStringInfoString(&result
,
2731 " </xsd:sequence>\n");
2732 appendStringInfoString(&result
,
2733 "</xsd:complexType>\n\n");
2735 appendStringInfo(&result
,
2736 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
2737 xmlsn
, schematypename
);
2744 * Map an SQL catalog to XML Schema data types; see SQL/XML section
2748 map_sql_catalog_to_xmlschema_types(List
*nspid_list
, bool nulls
,
2749 bool tableforest
, const char *targetns
)
2753 char *catalogtypename
;
2754 StringInfoData result
;
2757 dbname
= get_database_name(MyDatabaseId
);
2759 initStringInfo(&result
);
2761 xmlcn
= map_sql_identifier_to_xml_name(dbname
, true, false);
2763 catalogtypename
= map_multipart_sql_identifier_to_xml_name("CatalogType",
2768 appendStringInfo(&result
,
2769 "<xsd:complexType name=\"%s\">\n", catalogtypename
);
2770 appendStringInfoString(&result
,
2773 foreach(cell
, nspid_list
)
2775 Oid nspid
= lfirst_oid(cell
);
2776 char *nspname
= get_namespace_name(nspid
);
2777 char *xmlsn
= map_sql_identifier_to_xml_name(nspname
, true, false);
2778 char *schematypename
= map_multipart_sql_identifier_to_xml_name("SchemaType",
2783 appendStringInfo(&result
,
2784 " <xsd:element name=\"%s\" type=\"%s\"/>\n",
2785 xmlsn
, schematypename
);
2788 appendStringInfoString(&result
,
2790 appendStringInfoString(&result
,
2791 "</xsd:complexType>\n\n");
2793 appendStringInfo(&result
,
2794 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
2795 xmlcn
, catalogtypename
);
2802 * Map an SQL data type to an XML name; see SQL/XML:2003 section 9.9.
2805 map_sql_type_to_xml_name(Oid typeoid
, int typmod
)
2807 StringInfoData result
;
2809 initStringInfo(&result
);
2815 appendStringInfo(&result
, "CHAR");
2817 appendStringInfo(&result
, "CHAR_%d", typmod
- VARHDRSZ
);
2821 appendStringInfo(&result
, "VARCHAR");
2823 appendStringInfo(&result
, "VARCHAR_%d", typmod
- VARHDRSZ
);
2827 appendStringInfo(&result
, "NUMERIC");
2829 appendStringInfo(&result
, "NUMERIC_%d_%d",
2830 ((typmod
- VARHDRSZ
) >> 16) & 0xffff,
2831 (typmod
- VARHDRSZ
) & 0xffff);
2834 appendStringInfo(&result
, "INTEGER");
2837 appendStringInfo(&result
, "SMALLINT");
2840 appendStringInfo(&result
, "BIGINT");
2843 appendStringInfo(&result
, "REAL");
2846 appendStringInfo(&result
, "DOUBLE");
2849 appendStringInfo(&result
, "BOOLEAN");
2853 appendStringInfo(&result
, "TIME");
2855 appendStringInfo(&result
, "TIME_%d", typmod
);
2859 appendStringInfo(&result
, "TIME_WTZ");
2861 appendStringInfo(&result
, "TIME_WTZ_%d", typmod
);
2865 appendStringInfo(&result
, "TIMESTAMP");
2867 appendStringInfo(&result
, "TIMESTAMP_%d", typmod
);
2869 case TIMESTAMPTZOID
:
2871 appendStringInfo(&result
, "TIMESTAMP_WTZ");
2873 appendStringInfo(&result
, "TIMESTAMP_WTZ_%d", typmod
);
2876 appendStringInfo(&result
, "DATE");
2879 appendStringInfo(&result
, "XML");
2884 Form_pg_type typtuple
;
2886 tuple
= SearchSysCache(TYPEOID
,
2887 ObjectIdGetDatum(typeoid
),
2889 if (!HeapTupleIsValid(tuple
))
2890 elog(ERROR
, "cache lookup failed for type %u", typeoid
);
2891 typtuple
= (Form_pg_type
) GETSTRUCT(tuple
);
2893 appendStringInfoString(&result
,
2894 map_multipart_sql_identifier_to_xml_name((typtuple
->typtype
== TYPTYPE_DOMAIN
) ? "Domain" : "UDT",
2895 get_database_name(MyDatabaseId
),
2896 get_namespace_name(typtuple
->typnamespace
),
2897 NameStr(typtuple
->typname
)));
2899 ReleaseSysCache(tuple
);
2908 * Map a collection of SQL data types to XML Schema data types; see
2909 * SQL/XML:2002 section 9.10.
2912 map_sql_typecoll_to_xmlschema_types(List
*tupdesc_list
)
2914 List
*uniquetypes
= NIL
;
2916 StringInfoData result
;
2919 /* extract all column types used in the set of TupleDescs */
2920 foreach(cell0
, tupdesc_list
)
2922 TupleDesc tupdesc
= (TupleDesc
) lfirst(cell0
);
2924 for (i
= 0; i
< tupdesc
->natts
; i
++)
2926 if (tupdesc
->attrs
[i
]->attisdropped
)
2928 uniquetypes
= list_append_unique_oid(uniquetypes
,
2929 tupdesc
->attrs
[i
]->atttypid
);
2933 /* add base types of domains */
2934 foreach(cell0
, uniquetypes
)
2936 Oid typid
= lfirst_oid(cell0
);
2937 Oid basetypid
= getBaseType(typid
);
2939 if (basetypid
!= typid
)
2940 uniquetypes
= list_append_unique_oid(uniquetypes
, basetypid
);
2943 /* Convert to textual form */
2944 initStringInfo(&result
);
2946 foreach(cell0
, uniquetypes
)
2948 appendStringInfo(&result
, "%s\n",
2949 map_sql_type_to_xmlschema_type(lfirst_oid(cell0
),
2958 * Map an SQL data type to a named XML Schema data type; see SQL/XML
2959 * sections 9.11 and 9.15.
2961 * (The distinction between 9.11 and 9.15 is basically that 9.15 adds
2962 * a name attribute, which this function does. The name-less version
2963 * 9.11 doesn't appear to be required anywhere.)
2966 map_sql_type_to_xmlschema_type(Oid typeoid
, int typmod
)
2968 StringInfoData result
;
2969 const char *typename
= map_sql_type_to_xml_name(typeoid
, typmod
);
2971 initStringInfo(&result
);
2973 if (typeoid
== XMLOID
)
2975 appendStringInfo(&result
,
2976 "<xsd:complexType mixed=\"true\">\n"
2978 " <xsd:any name=\"element\" minOccurs=\"0\" maxOccurs=\"unbounded\" processContents=\"skip\"/>\n"
2979 " </xsd:sequence>\n"
2980 "</xsd:complexType>\n");
2984 appendStringInfo(&result
,
2985 "<xsd:simpleType name=\"%s\">\n", typename
);
2992 appendStringInfo(&result
,
2993 " <xsd:restriction base=\"xsd:string\">\n");
2995 appendStringInfo(&result
,
2996 " <xsd:maxLength value=\"%d\"/>\n",
2998 appendStringInfo(&result
,
2999 " </xsd:restriction>\n");
3003 appendStringInfo(&result
,
3004 " <xsd:restriction base=\"xsd:%s\">\n"
3005 " </xsd:restriction>\n",
3006 xmlbinary
== XMLBINARY_BASE64
? "base64Binary" : "hexBinary");
3011 appendStringInfo(&result
,
3012 " <xsd:restriction base=\"xsd:decimal\">\n"
3013 " <xsd:totalDigits value=\"%d\"/>\n"
3014 " <xsd:fractionDigits value=\"%d\"/>\n"
3015 " </xsd:restriction>\n",
3016 ((typmod
- VARHDRSZ
) >> 16) & 0xffff,
3017 (typmod
- VARHDRSZ
) & 0xffff);
3021 appendStringInfo(&result
,
3022 " <xsd:restriction base=\"xsd:short\">\n"
3023 " <xsd:maxInclusive value=\"%d\"/>\n"
3024 " <xsd:minInclusive value=\"%d\"/>\n"
3025 " </xsd:restriction>\n",
3026 SHRT_MAX
, SHRT_MIN
);
3030 appendStringInfo(&result
,
3031 " <xsd:restriction base=\"xsd:int\">\n"
3032 " <xsd:maxInclusive value=\"%d\"/>\n"
3033 " <xsd:minInclusive value=\"%d\"/>\n"
3034 " </xsd:restriction>\n",
3039 appendStringInfo(&result
,
3040 " <xsd:restriction base=\"xsd:long\">\n"
3041 " <xsd:maxInclusive value=\"" INT64_FORMAT
"\"/>\n"
3042 " <xsd:minInclusive value=\"" INT64_FORMAT
"\"/>\n"
3043 " </xsd:restriction>\n",
3044 (((uint64
) 1) << (sizeof(int64
) * 8 - 1)) - 1,
3045 (((uint64
) 1) << (sizeof(int64
) * 8 - 1)));
3049 appendStringInfo(&result
,
3050 " <xsd:restriction base=\"xsd:float\"></xsd:restriction>\n");
3054 appendStringInfo(&result
,
3055 " <xsd:restriction base=\"xsd:double\"></xsd:restriction>\n");
3059 appendStringInfo(&result
,
3060 " <xsd:restriction base=\"xsd:boolean\"></xsd:restriction>\n");
3066 const char *tz
= (typeoid
== TIMETZOID
? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
3069 appendStringInfo(&result
,
3070 " <xsd:restriction base=\"xsd:time\">\n"
3071 " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}(.\\p{Nd}+)?%s\"/>\n"
3072 " </xsd:restriction>\n", tz
);
3073 else if (typmod
== 0)
3074 appendStringInfo(&result
,
3075 " <xsd:restriction base=\"xsd:time\">\n"
3076 " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
3077 " </xsd:restriction>\n", tz
);
3079 appendStringInfo(&result
,
3080 " <xsd:restriction base=\"xsd:time\">\n"
3081 " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}.\\p{Nd}{%d}%s\"/>\n"
3082 " </xsd:restriction>\n", typmod
- VARHDRSZ
, tz
);
3087 case TIMESTAMPTZOID
:
3089 const char *tz
= (typeoid
== TIMESTAMPTZOID
? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
3092 appendStringInfo(&result
,
3093 " <xsd:restriction base=\"xsd:dateTime\">\n"
3094 " <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"
3095 " </xsd:restriction>\n", tz
);
3096 else if (typmod
== 0)
3097 appendStringInfo(&result
,
3098 " <xsd:restriction base=\"xsd:dateTime\">\n"
3099 " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}T\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
3100 " </xsd:restriction>\n", tz
);
3102 appendStringInfo(&result
,
3103 " <xsd:restriction base=\"xsd:dateTime\">\n"
3104 " <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"
3105 " </xsd:restriction>\n", typmod
- VARHDRSZ
, tz
);
3110 appendStringInfo(&result
,
3111 " <xsd:restriction base=\"xsd:date\">\n"
3112 " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}\"/>\n"
3113 " </xsd:restriction>\n");
3117 if (get_typtype(typeoid
) == TYPTYPE_DOMAIN
)
3120 int32 base_typmod
= -1;
3122 base_typeoid
= getBaseTypeAndTypmod(typeoid
, &base_typmod
);
3124 appendStringInfo(&result
,
3125 " <xsd:restriction base=\"%s\"/>\n",
3126 map_sql_type_to_xml_name(base_typeoid
, base_typmod
));
3130 appendStringInfo(&result
,
3131 "</xsd:simpleType>\n");
3139 * Map an SQL row to an XML element, taking the row from the active
3140 * SPI cursor. See also SQL/XML:2003 section 9.12.
3143 SPI_sql_row_to_xmlelement(int rownum
, StringInfo result
, char *tablename
,
3144 bool nulls
, bool tableforest
,
3145 const char *targetns
, bool top_level
)
3151 xmltn
= map_sql_identifier_to_xml_name(tablename
, true, false);
3161 xmldata_root_element_start(result
, xmltn
, NULL
, targetns
, top_level
);
3163 appendStringInfoString(result
, "<row>\n");
3165 for (i
= 1; i
<= SPI_tuptable
->tupdesc
->natts
; i
++)
3171 colname
= map_sql_identifier_to_xml_name(SPI_fname(SPI_tuptable
->tupdesc
, i
),
3173 colval
= SPI_getbinval(SPI_tuptable
->vals
[rownum
],
3174 SPI_tuptable
->tupdesc
,
3180 appendStringInfo(result
, " <%s xsi:nil=\"true\"/>\n", colname
);
3183 appendStringInfo(result
, " <%s>%s</%s>\n",
3185 map_sql_value_to_xml_value(colval
,
3186 SPI_gettypeid(SPI_tuptable
->tupdesc
, i
)),
3192 xmldata_root_element_end(result
, xmltn
);
3193 appendStringInfoChar(result
, '\n');
3196 appendStringInfoString(result
, "</row>\n\n");
3201 * XPath related functions
3206 * Convert XML node to text (dump subtree in case of element,
3207 * return value otherwise)
3210 xml_xmlnodetoxmltype(xmlNodePtr cur
)
3214 if (cur
->type
== XML_ELEMENT_NODE
)
3218 buf
= xmlBufferCreate();
3221 xmlNodeDump(buf
, NULL
, cur
, 0, 1);
3222 result
= xmlBuffer_to_xmltype(buf
);
3236 str
= xmlXPathCastNodeToString(cur
);
3239 result
= (xmltype
*) cstring_to_text((char *) str
);
3256 * Evaluate XPath expression and return array of XML values.
3258 * As we have no support of XQuery sequences yet, this function seems
3259 * to be the most useful one (array of XML functions plays a role of
3260 * some kind of substitution for XQuery sequences).
3262 * It is up to the user to ensure that the XML passed is in fact
3263 * an XML document - XPath doesn't work easily on fragments without
3264 * a context node being known.
3267 xpath(PG_FUNCTION_ARGS
)
3270 text
*xpath_expr_text
= PG_GETARG_TEXT_P(0);
3271 xmltype
*data
= PG_GETARG_XML_P(1);
3272 ArrayType
*namespaces
= PG_GETARG_ARRAYTYPE_P(2);
3273 ArrayBuildState
*astate
= NULL
;
3274 xmlParserCtxtPtr ctxt
= NULL
;
3275 xmlDocPtr doc
= NULL
;
3276 xmlXPathContextPtr xpathctx
= NULL
;
3277 xmlXPathCompExprPtr xpathcomp
= NULL
;
3278 xmlXPathObjectPtr xpathobj
= NULL
;
3283 xmlChar
*xpath_expr
;
3287 Datum
*ns_names_uris
;
3288 bool *ns_names_uris_nulls
;
3292 * Namespace mappings are passed as text[]. If an empty array is passed
3293 * (ndim = 0, "0-dimensional"), then there are no namespace mappings.
3294 * Else, a 2-dimensional array with length of the second axis being equal
3295 * to 2 should be passed, i.e., every subarray contains 2 elements, the
3296 * first element defining the name, the second one the URI. Example:
3297 * ARRAY[ARRAY['myns', 'http://example.com'], ARRAY['myns2',
3298 * 'http://example2.com']].
3300 ndim
= ARR_NDIM(namespaces
);
3305 dims
= ARR_DIMS(namespaces
);
3307 if (ndim
!= 2 || dims
[1] != 2)
3309 (errcode(ERRCODE_DATA_EXCEPTION
),
3310 errmsg("invalid array for XML namespace mapping"),
3311 errdetail("The array must be two-dimensional with length of the second axis equal to 2.")));
3313 Assert(ARR_ELEMTYPE(namespaces
) == TEXTOID
);
3315 deconstruct_array(namespaces
, TEXTOID
, -1, false, 'i',
3316 &ns_names_uris
, &ns_names_uris_nulls
,
3319 Assert((ns_count
% 2) == 0); /* checked above */
3320 ns_count
/= 2; /* count pairs only */
3324 ns_names_uris
= NULL
;
3325 ns_names_uris_nulls
= NULL
;
3329 datastr
= VARDATA(data
);
3330 len
= VARSIZE(data
) - VARHDRSZ
;
3331 xpath_len
= VARSIZE(xpath_expr_text
) - VARHDRSZ
;
3334 (errcode(ERRCODE_DATA_EXCEPTION
),
3335 errmsg("empty XPath expression")));
3337 string
= (xmlChar
*) palloc((len
+ 1) * sizeof(xmlChar
));
3338 memcpy(string
, datastr
, len
);
3341 xpath_expr
= (xmlChar
*) palloc((xpath_len
+1) * sizeof(xmlChar
));
3342 memcpy(xpath_expr
, VARDATA(xpath_expr_text
), xpath_len
);
3343 xpath_expr
[xpath_len
] = '\0';
3351 * redundant XML parsing (two parsings for the same value during one
3352 * command execution are possible)
3354 ctxt
= xmlNewParserCtxt();
3356 xml_ereport(ERROR
, ERRCODE_OUT_OF_MEMORY
,
3357 "could not allocate parser context");
3358 doc
= xmlCtxtReadMemory(ctxt
, (char *) string
, len
, NULL
, NULL
, 0);
3360 xml_ereport(ERROR
, ERRCODE_INVALID_XML_DOCUMENT
,
3361 "could not parse XML document");
3362 xpathctx
= xmlXPathNewContext(doc
);
3363 if (xpathctx
== NULL
)
3364 xml_ereport(ERROR
, ERRCODE_OUT_OF_MEMORY
,
3365 "could not allocate XPath context");
3366 xpathctx
->node
= xmlDocGetRootElement(doc
);
3367 if (xpathctx
->node
== NULL
)
3368 xml_ereport(ERROR
, ERRCODE_INTERNAL_ERROR
,
3369 "could not find root XML element");
3371 /* register namespaces, if any */
3374 for (i
= 0; i
< ns_count
; i
++)
3379 if (ns_names_uris_nulls
[i
* 2] ||
3380 ns_names_uris_nulls
[i
* 2 + 1])
3382 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
3383 errmsg("neither namespace name nor URI may be null")));
3384 ns_name
= TextDatumGetCString(ns_names_uris
[i
* 2]);
3385 ns_uri
= TextDatumGetCString(ns_names_uris
[i
* 2 + 1]);
3386 if (xmlXPathRegisterNs(xpathctx
,
3387 (xmlChar
*) ns_name
,
3388 (xmlChar
*) ns_uri
) != 0)
3389 ereport(ERROR
, /* is this an internal error??? */
3390 (errmsg("could not register XML namespace with name \"%s\" and URI \"%s\"",
3395 xpathcomp
= xmlXPathCompile(xpath_expr
);
3396 if (xpathcomp
== NULL
) /* TODO: show proper XPath error details */
3397 xml_ereport(ERROR
, ERRCODE_INTERNAL_ERROR
,
3398 "invalid XPath expression");
3400 xpathobj
= xmlXPathCompiledEval(xpathcomp
, xpathctx
);
3401 if (xpathobj
== NULL
) /* TODO: reason? */
3402 xml_ereport(ERROR
, ERRCODE_INTERNAL_ERROR
,
3403 "could not create XPath object");
3405 /* return empty array in cases when nothing is found */
3406 if (xpathobj
->nodesetval
== NULL
)
3409 res_nitems
= xpathobj
->nodesetval
->nodeNr
;
3413 for (i
= 0; i
< xpathobj
->nodesetval
->nodeNr
; i
++)
3416 bool elemisnull
= false;
3418 elem
= PointerGetDatum(xml_xmlnodetoxmltype(xpathobj
->nodesetval
->nodeTab
[i
]));
3419 astate
= accumArrayResult(astate
, elem
,
3421 CurrentMemoryContext
);
3428 xmlXPathFreeObject(xpathobj
);
3430 xmlXPathFreeCompExpr(xpathcomp
);
3432 xmlXPathFreeContext(xpathctx
);
3436 xmlFreeParserCtxt(ctxt
);
3441 xmlXPathFreeObject(xpathobj
);
3442 xmlXPathFreeCompExpr(xpathcomp
);
3443 xmlXPathFreeContext(xpathctx
);
3445 xmlFreeParserCtxt(ctxt
);
3447 if (res_nitems
== 0)
3448 PG_RETURN_ARRAYTYPE_P(construct_empty_array(XMLOID
));
3450 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate
, CurrentMemoryContext
));