1 /*-------------------------------------------------------------------------
4 * XML data type support.
7 * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
10 * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.32 2007/02/27 23:48:09 tgl Exp $
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 * Note on memory management: Via callbacks, libxml is told to use
28 * palloc and friends for memory management. Sometimes, libxml
29 * allocates global structures in the hope that it can reuse them
30 * later on, but if "later" is much later, the memory context
31 * management of PostgreSQL will have blown those structures away
32 * without telling libxml about it. Therefore, it is important to
33 * call xmlCleanupParser() or perhaps some other cleanup function
34 * after using such functions, for example something from
35 * libxml/parser.h or libxml/xmlsave.h. Unfortunately, you cannot
36 * readily tell from the API documentation when that happens, so
37 * careful evaluation is necessary when introducing new libxml APIs
44 #include <libxml/chvalid.h>
45 #include <libxml/parser.h>
46 #include <libxml/tree.h>
47 #include <libxml/uri.h>
48 #include <libxml/xmlerror.h>
49 #include <libxml/xmlwriter.h>
50 #endif /* USE_LIBXML */
52 #include "catalog/namespace.h"
53 #include "catalog/pg_type.h"
54 #include "commands/dbcommands.h"
55 #include "executor/executor.h"
56 #include "executor/spi.h"
58 #include "lib/stringinfo.h"
59 #include "libpq/pqformat.h"
60 #include "mb/pg_wchar.h"
61 #include "miscadmin.h"
62 #include "nodes/execnodes.h"
63 #include "parser/parse_expr.h"
64 #include "utils/array.h"
65 #include "utils/builtins.h"
66 #include "utils/lsyscache.h"
67 #include "utils/memutils.h"
68 #include "utils/xml.h"
73 static StringInfo xml_err_buf = NULL;
75 static void xml_init(void);
76 static void *xml_palloc(size_t size);
77 static void *xml_repalloc(void *ptr, size_t size);
78 static void xml_pfree(void *ptr);
79 static char *xml_pstrdup(const char *string);
80 static void xml_ereport(int level, int sqlcode,
82 static void xml_errorHandler(void *ctxt, const char *msg, ...);
83 static void xml_ereport_by_code(int level, int sqlcode,
84 const char *msg, int errcode);
85 static xmlChar *xml_text2xmlChar(text *in);
86 static int parse_xml_decl(const xmlChar *str, size_t *lenp, xmlChar **version, xmlChar **encoding, int *standalone);
87 static bool print_xml_decl(StringInfo buf, const xmlChar *version, pg_enc encoding, int standalone);
88 static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, xmlChar *encoding);
90 #endif /* USE_LIBXML */
92 static StringInfo query_to_xml_internal(const char *query, char *tablename, const char *xmlschema, bool nulls, bool tableforest, const char *targetns);
93 static const char * map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls, bool tableforest, const char *targetns);
94 static const char * map_sql_type_to_xml_name(Oid typeoid, int typmod);
95 static const char * map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc);
96 static const char * map_sql_type_to_xmlschema_type(Oid typeoid, int typmod);
97 static void SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns);
100 XmlBinaryType xmlbinary;
101 XmlOptionType xmloption;
104 #define NO_XML_SUPPORT() \
106 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
107 errmsg("no XML support in this installation")))
110 #define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))
111 #define _textout(x) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(x)))
114 /* from SQL/XML:2003 section 4.7 */
115 #define NAMESPACE_XSD "http://www.w3.org/2001/XMLSchema"
116 #define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance"
117 #define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml"
121 xml_in(PG_FUNCTION_ARGS)
124 char *s = PG_GETARG_CSTRING(0);
130 vardata = palloc(len + VARHDRSZ);
131 SET_VARSIZE(vardata, len + VARHDRSZ);
132 memcpy(VARDATA(vardata), s, len);
135 * Parse the data to check if it is well-formed XML data. Assume
136 * that ERROR occurred if parsing failed.
138 doc = xml_parse(vardata, xmloption, true, NULL);
141 PG_RETURN_XML_P(vardata);
149 #define PG_XML_DEFAULT_VERSION "1.0"
153 xml_out_internal(xmltype *x, pg_enc target_encoding)
164 len = VARSIZE(x) - VARHDRSZ;
165 str = palloc(len + 1);
166 memcpy(str, VARDATA(x), len);
170 if ((res_code = parse_xml_decl((xmlChar *) str, &len, &version, &encoding, &standalone)) == 0)
174 initStringInfo(&buf);
176 if (!print_xml_decl(&buf, version, target_encoding, standalone))
179 * If we are not going to produce an XML declaration, eat
180 * a single newline in the original string to prevent
181 * empty first lines in the output.
183 if (*(str + len) == '\n')
186 appendStringInfoString(&buf, str + len);
191 xml_ereport_by_code(WARNING, ERRCODE_INTERNAL_ERROR,
192 "could not parse XML declaration in stored value", res_code);
199 xml_out(PG_FUNCTION_ARGS)
201 xmltype *x = PG_GETARG_XML_P(0);
204 * xml_out removes the encoding property in all cases. This is
205 * because we cannot control from here whether the datum will be
206 * converted to a different client encoding, so we'd do more harm
207 * than good by including it.
209 PG_RETURN_CSTRING(xml_out_internal(x, 0));
214 xml_recv(PG_FUNCTION_ARGS)
217 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
223 xmlChar *encoding = NULL;
225 str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
227 result = palloc(nbytes + VARHDRSZ);
228 SET_VARSIZE(result, nbytes + VARHDRSZ);
229 memcpy(VARDATA(result), str, nbytes);
231 parse_xml_decl((xmlChar *) str, NULL, NULL, &encoding, NULL);
234 * Parse the data to check if it is well-formed XML data. Assume
235 * that ERROR occurred if parsing failed.
237 doc = xml_parse(result, xmloption, true, encoding);
240 newstr = (char *) pg_do_encoding_conversion((unsigned char *) str,
242 encoding ? pg_char_to_encoding((char *) encoding) : PG_UTF8,
243 GetDatabaseEncoding());
251 nbytes = strlen(newstr);
253 result = palloc(nbytes + VARHDRSZ);
254 SET_VARSIZE(result, nbytes + VARHDRSZ);
255 memcpy(VARDATA(result), newstr, nbytes);
258 PG_RETURN_XML_P(result);
267 xml_send(PG_FUNCTION_ARGS)
269 xmltype *x = PG_GETARG_XML_P(0);
270 char *outval = xml_out_internal(x, pg_get_client_encoding());
273 pq_begintypsend(&buf);
274 pq_sendstring(&buf, outval);
275 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
281 appendStringInfoText(StringInfo str, const text *t)
283 appendBinaryStringInfo(str, VARDATA(t), VARSIZE(t) - VARHDRSZ);
289 stringinfo_to_xmltype(StringInfo buf)
294 len = buf->len + VARHDRSZ;
295 result = palloc(len);
296 SET_VARSIZE(result, len);
297 memcpy(VARDATA(result), buf->data, buf->len);
304 cstring_to_xmltype(const char *string)
309 len = strlen(string) + VARHDRSZ;
310 result = palloc(len);
311 SET_VARSIZE(result, len);
312 memcpy(VARDATA(result), string, len - VARHDRSZ);
320 xmlBuffer_to_xmltype(xmlBufferPtr buf)
325 len = xmlBufferLength(buf) + VARHDRSZ;
326 result = palloc(len);
327 SET_VARSIZE(result, len);
328 memcpy(VARDATA(result), xmlBufferContent(buf), len - VARHDRSZ);
336 xmlcomment(PG_FUNCTION_ARGS)
339 text *arg = PG_GETARG_TEXT_P(0);
340 int len = VARSIZE(arg) - VARHDRSZ;
344 /* check for "--" in string or "-" at the end */
345 for (i = 1; i < len; i++)
346 if ((VARDATA(arg)[i] == '-' && VARDATA(arg)[i - 1] == '-')
347 || (VARDATA(arg)[i] == '-' && i == len - 1))
349 (errcode(ERRCODE_INVALID_XML_COMMENT),
350 errmsg("invalid XML comment")));
352 initStringInfo(&buf);
353 appendStringInfo(&buf, "<!--");
354 appendStringInfoText(&buf, arg);
355 appendStringInfo(&buf, "-->");
357 PG_RETURN_XML_P(stringinfo_to_xmltype(&buf));
367 * TODO: xmlconcat needs to merge the notations and unparsed entities
368 * of the argument values. Not very important in practice, though.
371 xmlconcat(List *args)
377 int global_standalone = 1;
378 xmlChar *global_version = NULL;
379 bool global_version_no_value = false;
381 initStringInfo(&buf);
387 xmltype *x = DatumGetXmlP(PointerGetDatum(lfirst(v)));
390 len = VARSIZE(x) - VARHDRSZ;
391 str = palloc(len + 1);
392 memcpy(str, VARDATA(x), len);
395 parse_xml_decl((xmlChar *) str, &len, &version, NULL, &standalone);
397 if (standalone == 0 && global_standalone == 1)
398 global_standalone = 0;
400 global_standalone = -1;
403 global_version_no_value = true;
404 else if (!global_version)
405 global_version = xmlStrdup(version);
406 else if (xmlStrcmp(version, global_version) != 0)
407 global_version_no_value = true;
409 appendStringInfoString(&buf, str + len);
413 if (!global_version_no_value || global_standalone >= 0)
417 initStringInfo(&buf2);
419 print_xml_decl(&buf2,
420 (!global_version_no_value && global_version) ? global_version : NULL,
424 appendStringInfoString(&buf2, buf.data);
428 return stringinfo_to_xmltype(&buf);
440 xmlconcat2(PG_FUNCTION_ARGS)
447 PG_RETURN_XML_P(PG_GETARG_XML_P(1));
449 else if (PG_ARGISNULL(1))
450 PG_RETURN_XML_P(PG_GETARG_XML_P(0));
452 PG_RETURN_XML_P(xmlconcat(list_make2(PG_GETARG_XML_P(0), PG_GETARG_XML_P(1))));
457 texttoxml(PG_FUNCTION_ARGS)
459 text *data = PG_GETARG_TEXT_P(0);
461 PG_RETURN_XML_P(xmlparse(data, xmloption, true));
466 xmltotext(PG_FUNCTION_ARGS)
468 xmltype *data = PG_GETARG_XML_P(0);
470 PG_RETURN_TEXT_P(xmltotext_with_xmloption(data, xmloption));
475 xmltotext_with_xmloption(xmltype *data, XmlOptionType xmloption_arg)
477 if (xmloption_arg == XMLOPTION_DOCUMENT && !xml_is_document(data))
479 (errcode(ERRCODE_NOT_AN_XML_DOCUMENT),
480 errmsg("not an XML document")));
482 /* It's actually binary compatible, save for the above check. */
483 return (text *) data;
488 xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
491 XmlExpr *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
501 xmlTextWriterPtr writer;
503 buf = xmlBufferCreate();
504 writer = xmlNewTextWriterMemory(buf, 0);
506 xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
509 forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names)
511 ExprState *e = (ExprState *) lfirst(arg);
512 char *argname = strVal(lfirst(narg));
514 value = ExecEvalExpr(e, econtext, &isnull, NULL);
517 str = OutputFunctionCall(&xmlExpr->named_outfuncs[i], value);
518 xmlTextWriterWriteAttribute(writer, (xmlChar *) argname, (xmlChar *) str);
524 foreach(arg, xmlExpr->args)
526 ExprState *e = (ExprState *) lfirst(arg);
528 value = ExecEvalExpr(e, econtext, &isnull, NULL);
530 xmlTextWriterWriteRaw(writer, (xmlChar *) map_sql_value_to_xml_value(value, exprType((Node *) e->expr)));
533 xmlTextWriterEndElement(writer);
534 xmlFreeTextWriter(writer);
536 result = xmlBuffer_to_xmltype(buf);
547 xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace)
552 doc = xml_parse(data, xmloption_arg, preserve_whitespace, NULL);
555 return (xmltype *) data;
564 xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null)
570 if (pg_strncasecmp(target, "xml", 3) == 0)
572 (errcode(ERRCODE_SYNTAX_ERROR), /* really */
573 errmsg("invalid XML processing instruction"),
574 errdetail("XML processing instruction target name cannot start with \"xml\".")));
577 * Following the SQL standard, the null check comes after the
578 * syntax check above.
580 *result_is_null = arg_is_null;
584 initStringInfo(&buf);
586 appendStringInfo(&buf, "<?%s", target);
592 string = DatumGetCString(DirectFunctionCall1(textout,
593 PointerGetDatum(arg)));
594 if (strstr(string, "?>") != NULL)
596 (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
597 errmsg("invalid XML processing instruction"),
598 errdetail("XML processing instruction cannot contain \"?>\".")));
600 appendStringInfoChar(&buf, ' ');
601 appendStringInfoString(&buf, string + strspn(string, " "));
604 appendStringInfoString(&buf, "?>");
606 result = stringinfo_to_xmltype(&buf);
617 xmlroot(xmltype *data, text *version, int standalone)
622 xmlChar *orig_version;
626 len = VARSIZE(data) - VARHDRSZ;
627 str = palloc(len + 1);
628 memcpy(str, VARDATA(data), len);
631 parse_xml_decl((xmlChar *) str, &len, &orig_version, NULL, &orig_standalone);
634 orig_version = xml_text2xmlChar(version);
640 case XML_STANDALONE_YES:
643 case XML_STANDALONE_NO:
646 case XML_STANDALONE_NO_VALUE:
647 orig_standalone = -1;
649 case XML_STANDALONE_OMITTED:
650 /* leave original value */
654 initStringInfo(&buf);
655 print_xml_decl(&buf, orig_version, 0, orig_standalone);
656 appendStringInfoString(&buf, str + len);
658 return stringinfo_to_xmltype(&buf);
667 * Validate document (given as string) against DTD (given as external link)
668 * TODO !!! use text instead of cstring for second arg
669 * TODO allow passing DTD as a string value (not only as an URI)
670 * TODO redesign (see comment with '!!!' below)
673 xmlvalidate(PG_FUNCTION_ARGS)
676 text *data = PG_GETARG_TEXT_P(0);
677 text *dtdOrUri = PG_GETARG_TEXT_P(1);
679 xmlParserCtxtPtr ctxt = NULL;
680 xmlDocPtr doc = NULL;
681 xmlDtdPtr dtd = NULL;
685 /* We use a PG_TRY block to ensure libxml is cleaned up on error */
688 ctxt = xmlNewParserCtxt();
690 xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
691 "could not allocate parser context");
693 doc = xmlCtxtReadMemory(ctxt, (char *) VARDATA(data),
694 VARSIZE(data) - VARHDRSZ,
697 xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
698 "could not parse XML data");
701 uri = xmlCreateURI();
702 elog(NOTICE, "dtd - %s", dtdOrUri);
703 dtd = palloc(sizeof(xmlDtdPtr));
704 uri = xmlParseURI(dtdOrUri);
706 xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
707 "not implemented yet... (TODO)");
710 dtd = xmlParseDTD(NULL, xml_text2xmlChar(dtdOrUri));
713 xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
714 "could not load DTD");
716 if (xmlValidateDtd(xmlNewValidCtxt(), doc, dtd) == 1)
720 xml_ereport(NOTICE, ERRCODE_INVALID_XML_DOCUMENT,
721 "validation against DTD failed");
732 xmlFreeParserCtxt(ctxt);
746 xmlFreeParserCtxt(ctxt);
753 PG_RETURN_BOOL(result);
754 #else /* not USE_LIBXML */
757 #endif /* not USE_LIBXML */
762 xml_is_document(xmltype *arg)
766 xmlDocPtr doc = NULL;
767 MemoryContext ccxt = CurrentMemoryContext;
771 doc = xml_parse((text *) arg, XMLOPTION_DOCUMENT, true, NULL);
779 ecxt = MemoryContextSwitchTo(ccxt);
780 errdata = CopyErrorData();
781 if (errdata->sqlerrcode == ERRCODE_INVALID_XML_DOCUMENT)
788 MemoryContextSwitchTo(ecxt);
798 #else /* not USE_LIBXML */
801 #endif /* not USE_LIBXML */
808 * Container for some init stuff (not good design!)
809 * TODO xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and check)
815 * Currently, we have no pure UTF-8 support for internals -- check
818 if (sizeof (char) != sizeof (xmlChar))
820 (errmsg("could not initialize XML library"),
821 errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.",
822 (int) sizeof(char), (int) sizeof(xmlChar))));
824 if (xml_err_buf == NULL)
826 /* First time through: create error buffer in permanent context */
827 MemoryContext oldcontext;
829 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
830 xml_err_buf = makeStringInfo();
831 MemoryContextSwitchTo(oldcontext);
835 /* Reset pre-existing buffer to empty */
836 xml_err_buf->data[0] = '\0';
837 xml_err_buf->len = 0;
839 /* Now that xml_err_buf exists, safe to call xml_errorHandler */
840 xmlSetGenericErrorFunc(NULL, xml_errorHandler);
842 xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
850 * SQL/XML allows storing "XML documents" or "XML content". "XML
851 * documents" are specified by the XML specification and are parsed
852 * easily by libxml. "XML content" is specified by SQL/XML as the
853 * production "XMLDecl? content". But libxml can only parse the
854 * "content" part, so we have to parse the XML declaration ourselves
858 #define CHECK_XML_SPACE(p) if (!xmlIsBlank_ch(*(p))) return XML_ERR_SPACE_REQUIRED
859 #define SKIP_XML_SPACE(p) while (xmlIsBlank_ch(*(p))) (p)++
862 parse_xml_decl(const xmlChar *str, size_t *lenp, xmlChar **version, xmlChar **encoding, int *standalone)
865 const xmlChar *save_p;
877 if (xmlStrncmp(p, (xmlChar *)"<?xml", 5) != 0)
885 if (xmlStrncmp(p, (xmlChar *)"version", 7) != 0)
886 return XML_ERR_VERSION_MISSING;
890 return XML_ERR_VERSION_MISSING;
894 if (*p == '\'' || *p == '"')
898 q = xmlStrchr(p + 1, *p);
900 return XML_ERR_VERSION_MISSING;
903 *version = xmlStrndup(p + 1, q - p - 1);
907 return XML_ERR_VERSION_MISSING;
912 if (xmlStrncmp(p, (xmlChar *)"encoding", 8) == 0)
914 CHECK_XML_SPACE(save_p);
918 return XML_ERR_MISSING_ENCODING;
922 if (*p == '\'' || *p == '"')
926 q = xmlStrchr(p + 1, *p);
928 return XML_ERR_MISSING_ENCODING;
931 *encoding = xmlStrndup(p + 1, q - p - 1);
935 return XML_ERR_MISSING_ENCODING;
945 if (xmlStrncmp(p, (xmlChar *)"standalone", 10) == 0)
947 CHECK_XML_SPACE(save_p);
951 return XML_ERR_STANDALONE_VALUE;
954 if (xmlStrncmp(p, (xmlChar *)"'yes'", 5) == 0 || xmlStrncmp(p, (xmlChar *)"\"yes\"", 5) == 0)
959 else if (xmlStrncmp(p, (xmlChar *)"'no'", 4) == 0 || xmlStrncmp(p, (xmlChar *)"\"no\"", 4) == 0)
965 return XML_ERR_STANDALONE_VALUE;
973 if (xmlStrncmp(p, (xmlChar *)"?>", 2) != 0)
974 return XML_ERR_XMLDECL_NOT_FINISHED;
980 for (p = str; p < str + len; p++)
982 return XML_ERR_INVALID_CHAR;
992 * Write an XML declaration. On output, we adjust the XML declaration
993 * as follows. (These rules are the moral equivalent of the clause
994 * "Serialization of an XML value" in the SQL standard.)
996 * We try to avoid generating an XML declaration if possible. This is
997 * so that you don't get trivial things like xml '<foo/>' resulting in
998 * '<?xml version="1.0"?><foo/>', which would surely be annoying. We
999 * must provide a declaration if the standalone property is specified
1000 * or if we include an encoding declaration. If we have a
1001 * declaration, we must specify a version (XML requires this).
1002 * Otherwise we only make a declaration if the version is not "1.0",
1003 * which is the default version specified in SQL:2003.
1006 print_xml_decl(StringInfo buf, const xmlChar *version, pg_enc encoding, int standalone)
1008 if ((version && strcmp((char *) version, PG_XML_DEFAULT_VERSION) != 0)
1009 || (encoding && encoding != PG_UTF8)
1010 || standalone != -1)
1012 appendStringInfoString(buf, "<?xml");
1015 appendStringInfo(buf, " version=\"%s\"", version);
1017 appendStringInfo(buf, " version=\"%s\"", PG_XML_DEFAULT_VERSION);
1019 if (encoding && encoding != PG_UTF8)
1020 /* XXX might be useful to convert this to IANA names
1021 * (ISO-8859-1 instead of LATIN1 etc.); needs field
1023 appendStringInfo(buf, " encoding=\"%s\"", pg_encoding_to_char(encoding));
1025 if (standalone == 1)
1026 appendStringInfoString(buf, " standalone=\"yes\"");
1027 else if (standalone == 0)
1028 appendStringInfoString(buf, " standalone=\"no\"");
1029 appendStringInfoString(buf, "?>");
1039 * Convert a C string to XML internal representation
1041 * TODO maybe, libxml2's xmlreader is better? (do not construct DOM, yet do not use SAX - see xml_reader.c)
1044 xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, xmlChar *encoding)
1048 xmlChar *utf8string;
1049 xmlParserCtxtPtr ctxt = NULL;
1050 xmlDocPtr doc = NULL;
1052 len = VARSIZE(data) - VARHDRSZ; /* will be useful later */
1053 string = xml_text2xmlChar(data);
1055 utf8string = pg_do_encoding_conversion(string,
1058 ? pg_char_to_encoding((char *) encoding)
1059 : GetDatabaseEncoding(),
1064 /* We use a PG_TRY block to ensure libxml is cleaned up on error */
1067 ctxt = xmlNewParserCtxt();
1069 xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
1070 "could not allocate parser context");
1072 if (xmloption_arg == XMLOPTION_DOCUMENT)
1075 * Note, that here we try to apply DTD defaults
1076 * (XML_PARSE_DTDATTR) according to SQL/XML:10.16.7.d:
1077 * 'Default valies defined by internal DTD are applied'.
1078 * As for external DTDs, we try to support them too, (see
1079 * SQL/XML:10.16.7.e)
1081 doc = xmlCtxtReadDoc(ctxt, utf8string,
1084 XML_PARSE_NOENT | XML_PARSE_DTDATTR
1085 | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS));
1087 xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
1088 "invalid XML document");
1094 xmlChar *version = NULL;
1095 int standalone = -1;
1097 doc = xmlNewDoc(NULL);
1099 res_code = parse_xml_decl(utf8string, &count, &version, NULL, &standalone);
1101 xml_ereport_by_code(ERROR, ERRCODE_INVALID_XML_CONTENT,
1102 "invalid XML content: invalid XML declaration", res_code);
1104 res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, utf8string + count, NULL);
1106 xml_ereport(ERROR, ERRCODE_INVALID_XML_CONTENT,
1107 "invalid XML content");
1109 doc->version = xmlStrdup(version);
1110 doc->encoding = xmlStrdup((xmlChar *) "UTF-8");
1111 doc->standalone = standalone;
1115 xmlFreeParserCtxt(ctxt);
1124 xmlFreeParserCtxt(ctxt);
1136 * xmlChar<->text convertions
1139 xml_text2xmlChar(text *in)
1141 int32 len = VARSIZE(in) - VARHDRSZ;
1144 res = palloc(len + 1);
1145 memcpy(res, VARDATA(in), len);
1153 * Wrappers for memory management functions
1156 xml_palloc(size_t size)
1158 return palloc(size);
1163 xml_repalloc(void *ptr, size_t size)
1165 return repalloc(ptr, size);
1170 xml_pfree(void *ptr)
1177 xml_pstrdup(const char *string)
1179 return pstrdup(string);
1184 * Wrapper for "ereport" function for XML-related errors. The "msg"
1185 * is the SQL-level message; some can be adopted from the SQL/XML
1186 * standard. This function adds libxml's native error messages, if
1190 xml_ereport(int level, int sqlcode,
1195 if (xml_err_buf->len > 0)
1197 detail = pstrdup(xml_err_buf->data);
1198 xml_err_buf->data[0] = '\0';
1199 xml_err_buf->len = 0;
1204 /* libxml error messages end in '\n'; get rid of it */
1209 len = strlen(detail);
1210 if (len > 0 && detail[len-1] == '\n')
1211 detail[len-1] = '\0';
1216 errdetail("%s", detail)));
1222 errmsg("%s", msg)));
1228 * Error handler for libxml error messages
1231 xml_errorHandler(void *ctxt, const char *msg,...)
1233 /* Append the formatted text to xml_err_buf */
1239 /* Try to format the data. */
1240 va_start(args, msg);
1241 success = appendStringInfoVA(xml_err_buf, msg, args);
1247 /* Double the buffer size and try again. */
1248 enlargeStringInfo(xml_err_buf, xml_err_buf->maxlen);
1254 * Wrapper for "ereport" function for XML-related errors. The "msg"
1255 * is the SQL-level message; some can be adopted from the SQL/XML
1256 * standard. This function uses "code" to create a textual detail
1257 * message. At the moment, we only need to cover those codes that we
1258 * may raise in this file.
1261 xml_ereport_by_code(int level, int sqlcode,
1262 const char *msg, int code)
1268 case XML_ERR_INVALID_CHAR:
1269 det = "Invalid character value";
1271 case XML_ERR_SPACE_REQUIRED:
1272 det = "Space required";
1274 case XML_ERR_STANDALONE_VALUE:
1275 det = "standalone accepts only 'yes' or 'no'";
1277 case XML_ERR_VERSION_MISSING:
1278 det = "Malformed declaration expecting version";
1280 case XML_ERR_MISSING_ENCODING:
1281 det = "Missing encoding in text declaration";
1283 case XML_ERR_XMLDECL_NOT_FINISHED:
1284 det = "Parsing XML declaration: '?>' expected";
1287 det = "Unrecognized libxml error code: %d";
1294 errdetail(det, code)));
1299 * Convert one char in the current server encoding to a Unicode codepoint.
1302 sqlchar_to_unicode(char *s)
1305 pg_wchar ret[2]; /* need space for trailing zero */
1307 utf8string = (char *) pg_do_encoding_conversion((unsigned char *) s,
1309 GetDatabaseEncoding(),
1312 pg_encoding_mb2wchar_with_len(PG_UTF8, utf8string, ret, pg_mblen(s));
1319 is_valid_xml_namefirst(pg_wchar c)
1321 /* (Letter | '_' | ':') */
1322 return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
1323 || c == '_' || c == ':');
1328 is_valid_xml_namechar(pg_wchar c)
1330 /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
1331 return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
1333 || c == '.' || c == '-' || c == '_' || c == ':'
1334 || xmlIsCombiningQ(c)
1335 || xmlIsExtenderQ(c));
1337 #endif /* USE_LIBXML */
1341 * Map SQL identifier to XML name; see SQL/XML:2003 section 9.1.
1344 map_sql_identifier_to_xml_name(char *ident, bool fully_escaped, bool escape_period)
1351 * SQL/XML doesn't make use of this case anywhere, so it's
1352 * probably a mistake.
1354 Assert(fully_escaped || !escape_period);
1356 initStringInfo(&buf);
1358 for (p = ident; *p; p += pg_mblen(p))
1360 if (*p == ':' && (p == ident || fully_escaped))
1361 appendStringInfo(&buf, "_x003A_");
1362 else if (*p == '_' && *(p+1) == 'x')
1363 appendStringInfo(&buf, "_x005F_");
1364 else if (fully_escaped && p == ident &&
1365 pg_strncasecmp(p, "xml", 3) == 0)
1368 appendStringInfo(&buf, "_x0078_");
1370 appendStringInfo(&buf, "_x0058_");
1372 else if (escape_period && *p == '.')
1373 appendStringInfo(&buf, "_x002E_");
1376 pg_wchar u = sqlchar_to_unicode(p);
1379 ? !is_valid_xml_namefirst(u)
1380 : !is_valid_xml_namechar(u))
1381 appendStringInfo(&buf, "_x%04X_", (unsigned int) u);
1383 appendBinaryStringInfo(&buf, p, pg_mblen(p));
1388 #else /* not USE_LIBXML */
1391 #endif /* not USE_LIBXML */
1396 * Map a Unicode codepoint into the current server encoding.
1399 unicode_to_sqlchar(pg_wchar c)
1401 static unsigned char utf8string[5]; /* need trailing zero */
1407 else if (c <= 0x7FF)
1409 utf8string[0] = 0xC0 | ((c >> 6) & 0x1F);
1410 utf8string[1] = 0x80 | (c & 0x3F);
1412 else if (c <= 0xFFFF)
1414 utf8string[0] = 0xE0 | ((c >> 12) & 0x0F);
1415 utf8string[1] = 0x80 | ((c >> 6) & 0x3F);
1416 utf8string[2] = 0x80 | (c & 0x3F);
1420 utf8string[0] = 0xF0 | ((c >> 18) & 0x07);
1421 utf8string[1] = 0x80 | ((c >> 12) & 0x3F);
1422 utf8string[2] = 0x80 | ((c >> 6) & 0x3F);
1423 utf8string[3] = 0x80 | (c & 0x3F);
1426 return (char *) pg_do_encoding_conversion(utf8string,
1427 pg_mblen((char *) utf8string),
1429 GetDatabaseEncoding());
1434 * Map XML name to SQL identifier; see SQL/XML:2003 section 9.17.
1437 map_xml_name_to_sql_identifier(char *name)
1442 initStringInfo(&buf);
1444 for (p = name; *p; p += pg_mblen(p))
1446 if (*p == '_' && *(p+1) == 'x'
1447 && isxdigit((unsigned char) *(p+2))
1448 && isxdigit((unsigned char) *(p+3))
1449 && isxdigit((unsigned char) *(p+4))
1450 && isxdigit((unsigned char) *(p+5))
1455 sscanf(p + 2, "%X", &u);
1456 appendStringInfoString(&buf, unicode_to_sqlchar(u));
1460 appendBinaryStringInfo(&buf, p, pg_mblen(p));
1468 * Map SQL value to XML value; see SQL/XML:2003 section 9.16.
1471 map_sql_value_to_xml_value(Datum value, Oid type)
1475 initStringInfo(&buf);
1477 if (is_array_type(type))
1486 array = DatumGetArrayTypeP(value);
1488 /* TODO: need some code-fu here to remove this limitation */
1489 if (ARR_NDIM(array) != 1)
1491 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1492 errmsg("only supported for one-dimensional array")));
1494 elmtype = ARR_ELEMTYPE(array);
1495 get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
1497 for (i = ARR_LBOUND(array)[0];
1498 i < ARR_LBOUND(array)[0] + ARR_DIMS(array)[0];
1504 subval = array_ref(array, 1, &i, -1, elmlen, elmbyval, elmalign, &isnull);
1505 appendStringInfoString(&buf, "<element>");
1506 appendStringInfoString(&buf, map_sql_value_to_xml_value(subval, elmtype));
1507 appendStringInfoString(&buf, "</element>");
1516 if (type == BOOLOID)
1518 if (DatumGetBool(value))
1524 getTypeOutputInfo(type, &typeOut, &isvarlena);
1525 str = OidOutputFunctionCall(typeOut, value);
1531 if (type == BYTEAOID)
1534 xmlTextWriterPtr writer;
1537 buf = xmlBufferCreate();
1538 writer = xmlNewTextWriterMemory(buf, 0);
1540 if (xmlbinary == XMLBINARY_BASE64)
1541 xmlTextWriterWriteBase64(writer, VARDATA(value), 0, VARSIZE(value) - VARHDRSZ);
1543 xmlTextWriterWriteBinHex(writer, VARDATA(value), 0, VARSIZE(value) - VARHDRSZ);
1545 xmlFreeTextWriter(writer);
1546 result = pstrdup((const char *) xmlBufferContent(buf));
1550 #endif /* USE_LIBXML */
1552 for (p = str; *p; p += pg_mblen(p))
1557 appendStringInfo(&buf, "&");
1560 appendStringInfo(&buf, "<");
1563 appendStringInfo(&buf, ">");
1566 appendStringInfo(&buf, "
");
1569 appendBinaryStringInfo(&buf, p, pg_mblen(p));
1580 _SPI_strdup(const char *s)
1582 char *ret = SPI_palloc(strlen(s) + 1);
1589 * Map SQL table to XML and/or XML Schema document; see SQL/XML:2003
1594 table_to_xml(PG_FUNCTION_ARGS)
1596 Oid relid = PG_GETARG_OID(0);
1597 bool nulls = PG_GETARG_BOOL(1);
1598 bool tableforest = PG_GETARG_BOOL(2);
1599 const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1601 StringInfoData query;
1603 initStringInfo(&query);
1604 appendStringInfo(&query, "SELECT * FROM %s", DatumGetCString(DirectFunctionCall1(regclassout, ObjectIdGetDatum(relid))));
1606 PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query.data, get_rel_name(relid), NULL, nulls, tableforest, targetns)));
1611 query_to_xml(PG_FUNCTION_ARGS)
1613 char *query = _textout(PG_GETARG_TEXT_P(0));
1614 bool nulls = PG_GETARG_BOOL(1);
1615 bool tableforest = PG_GETARG_BOOL(2);
1616 const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1618 PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL, NULL, nulls, tableforest, targetns)));
1623 cursor_to_xml(PG_FUNCTION_ARGS)
1625 char *name = _textout(PG_GETARG_TEXT_P(0));
1626 int32 count = PG_GETARG_INT32(1);
1627 bool nulls = PG_GETARG_BOOL(2);
1628 bool tableforest = PG_GETARG_BOOL(3);
1629 const char *targetns = _textout(PG_GETARG_TEXT_P(4));
1631 StringInfoData result;
1635 initStringInfo(&result);
1638 portal = SPI_cursor_find(name);
1641 (errcode(ERRCODE_UNDEFINED_CURSOR),
1642 errmsg("cursor \"%s\" does not exist", name)));
1644 SPI_cursor_fetch(portal, true, count);
1645 for (i = 0; i < SPI_processed; i++)
1646 SPI_sql_row_to_xmlelement(i, &result, NULL, nulls, tableforest, targetns);
1650 PG_RETURN_XML_P(stringinfo_to_xmltype(&result));
1655 query_to_xml_internal(const char *query, char *tablename, const char *xmlschema, bool nulls, bool tableforest, const char *targetns)
1662 xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
1666 result = makeStringInfo();
1669 if (SPI_execute(query, true, 0) != SPI_OK_SELECT)
1671 (errcode(ERRCODE_DATA_EXCEPTION),
1672 errmsg("invalid query")));
1676 appendStringInfo(result, "<%s xmlns:xsi=\"" NAMESPACE_XSI "\"", xmltn);
1677 if (strlen(targetns) > 0)
1678 appendStringInfo(result, " xmlns=\"%s\"", targetns);
1679 if (strlen(targetns) > 0)
1680 appendStringInfo(result, " xmlns:xsd=\"%s\"", targetns);
1683 if (strlen(targetns) > 0)
1684 appendStringInfo(result, " xsi:schemaLocation=\"%s #\"", targetns);
1686 appendStringInfo(result, " xsi:noNamespaceSchemaLocation=\"#\"");
1688 appendStringInfo(result, ">\n\n");
1692 appendStringInfo(result, "%s\n\n", xmlschema);
1694 for(i = 0; i < SPI_processed; i++)
1695 SPI_sql_row_to_xmlelement(i, result, tablename, nulls, tableforest, targetns);
1698 appendStringInfo(result, "</%s>\n", xmltn);
1707 table_to_xmlschema(PG_FUNCTION_ARGS)
1709 Oid relid = PG_GETARG_OID(0);
1710 bool nulls = PG_GETARG_BOOL(1);
1711 bool tableforest = PG_GETARG_BOOL(2);
1712 const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1717 rel = heap_open(relid, AccessShareLock);
1718 result = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls, tableforest, targetns);
1719 heap_close(rel, NoLock);
1721 PG_RETURN_XML_P(cstring_to_xmltype(result));
1726 query_to_xmlschema(PG_FUNCTION_ARGS)
1728 char *query = _textout(PG_GETARG_TEXT_P(0));
1729 bool nulls = PG_GETARG_BOOL(1);
1730 bool tableforest = PG_GETARG_BOOL(2);
1731 const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1738 plan = SPI_prepare(query, 0, NULL);
1739 portal = SPI_cursor_open(NULL, plan, NULL, NULL, true);
1740 result = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc, InvalidOid, nulls, tableforest, targetns));
1741 SPI_cursor_close(portal);
1744 PG_RETURN_XML_P(cstring_to_xmltype(result));
1749 cursor_to_xmlschema(PG_FUNCTION_ARGS)
1751 char *name = _textout(PG_GETARG_TEXT_P(0));
1752 bool nulls = PG_GETARG_BOOL(1);
1753 bool tableforest = PG_GETARG_BOOL(2);
1754 const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1756 const char *xmlschema;
1760 portal = SPI_cursor_find(name);
1763 (errcode(ERRCODE_UNDEFINED_CURSOR),
1764 errmsg("cursor \"%s\" does not exist", name)));
1766 xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc, InvalidOid, nulls, tableforest, targetns));
1769 PG_RETURN_XML_P(cstring_to_xmltype(xmlschema));
1774 table_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
1776 Oid relid = PG_GETARG_OID(0);
1777 bool nulls = PG_GETARG_BOOL(1);
1778 bool tableforest = PG_GETARG_BOOL(2);
1779 const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1781 StringInfoData query;
1783 const char *xmlschema;
1785 rel = heap_open(relid, AccessShareLock);
1786 xmlschema = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls, tableforest, targetns);
1787 heap_close(rel, NoLock);
1789 initStringInfo(&query);
1790 appendStringInfo(&query, "SELECT * FROM %s", DatumGetCString(DirectFunctionCall1(regclassout, ObjectIdGetDatum(relid))));
1792 PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query.data, get_rel_name(relid), xmlschema, nulls, tableforest, targetns)));
1797 query_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
1799 char *query = _textout(PG_GETARG_TEXT_P(0));
1800 bool nulls = PG_GETARG_BOOL(1);
1801 bool tableforest = PG_GETARG_BOOL(2);
1802 const char *targetns = _textout(PG_GETARG_TEXT_P(3));
1804 const char *xmlschema;
1809 plan = SPI_prepare(query, 0, NULL);
1810 portal = SPI_cursor_open(NULL, plan, NULL, NULL, true);
1811 xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc, InvalidOid, nulls, tableforest, targetns));
1812 SPI_cursor_close(portal);
1815 PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL, xmlschema, nulls, tableforest, targetns)));
1820 * Map a multi-part SQL name to an XML name; see SQL/XML:2003 section
1824 map_multipart_sql_identifier_to_xml_name(char *a, char *b, char *c, char *d)
1826 StringInfoData result;
1828 initStringInfo(&result);
1831 appendStringInfo(&result, "%s", map_sql_identifier_to_xml_name(a, true, true));
1833 appendStringInfo(&result, ".%s", map_sql_identifier_to_xml_name(b, true, true));
1835 appendStringInfo(&result, ".%s", map_sql_identifier_to_xml_name(c, true, true));
1837 appendStringInfo(&result, ".%s", map_sql_identifier_to_xml_name(d, true, true));
1844 * Map an SQL table to an XML Schema document; see SQL/XML:2003
1847 * Map an SQL table to XML Schema data types; see SQL/XML:2003 section
1851 map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls, bool tableforest, const char *targetns)
1855 char *tabletypename;
1857 StringInfoData result;
1859 initStringInfo(&result);
1863 HeapTuple tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0);
1864 Form_pg_class reltuple = (Form_pg_class) GETSTRUCT(tuple);
1866 xmltn = map_sql_identifier_to_xml_name(NameStr(reltuple->relname), true, false);
1868 tabletypename = map_multipart_sql_identifier_to_xml_name("TableType",
1869 get_database_name(MyDatabaseId),
1870 get_namespace_name(reltuple->relnamespace),
1871 NameStr(reltuple->relname));
1873 rowtypename = map_multipart_sql_identifier_to_xml_name("RowType",
1874 get_database_name(MyDatabaseId),
1875 get_namespace_name(reltuple->relnamespace),
1876 NameStr(reltuple->relname));
1878 ReleaseSysCache(tuple);
1887 tabletypename = "TableType";
1888 rowtypename = "RowType";
1891 appendStringInfoString(&result,
1893 " xmlns:xsd=\"" NAMESPACE_XSD "\"");
1894 if (strlen(targetns) > 0)
1895 appendStringInfo(&result,
1897 " targetNamespace=\"%s\"\n"
1898 " elementFormDefault=\"qualified\"",
1900 appendStringInfoString(&result,
1903 appendStringInfoString(&result,
1904 map_sql_typecoll_to_xmlschema_types(tupdesc));
1906 appendStringInfo(&result,
1907 "<xsd:complexType name=\"%s\">\n"
1908 " <xsd:sequence>\n",
1911 for (i = 0; i < tupdesc->natts; i++)
1912 appendStringInfo(&result,
1913 " <xsd:element name=\"%s\" type=\"%s\"%s></xsd:element>\n",
1914 map_sql_identifier_to_xml_name(NameStr(tupdesc->attrs[i]->attname), true, false),
1915 map_sql_type_to_xml_name(tupdesc->attrs[i]->atttypid, -1),
1916 nulls ? " nillable=\"true\"" : " minOccurs=\"0\"");
1918 appendStringInfoString(&result,
1919 " </xsd:sequence>\n"
1920 "</xsd:complexType>\n\n");
1924 appendStringInfo(&result,
1925 "<xsd:complexType name=\"%s\">\n"
1927 " <xsd:element name=\"row\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n"
1928 " </xsd:sequence>\n"
1929 "</xsd:complexType>\n\n",
1930 tabletypename, rowtypename);
1932 appendStringInfo(&result,
1933 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
1934 xmltn, tabletypename);
1937 appendStringInfo(&result,
1938 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
1939 xmltn, rowtypename);
1941 appendStringInfoString(&result,
1949 * Map an SQL data type to an XML name; see SQL/XML:2003 section 9.9.
1952 map_sql_type_to_xml_name(Oid typeoid, int typmod)
1954 StringInfoData result;
1956 initStringInfo(&result);
1962 appendStringInfo(&result, "CHAR");
1964 appendStringInfo(&result, "CHAR_%d", typmod - VARHDRSZ);
1968 appendStringInfo(&result, "VARCHAR");
1970 appendStringInfo(&result, "VARCHAR_%d", typmod - VARHDRSZ);
1974 appendStringInfo(&result, "NUMERIC");
1976 appendStringInfo(&result, "NUMERIC_%d_%d",
1977 ((typmod - VARHDRSZ) >> 16) & 0xffff,
1978 (typmod - VARHDRSZ) & 0xffff);
1981 appendStringInfo(&result, "INTEGER");
1984 appendStringInfo(&result, "SMALLINT");
1987 appendStringInfo(&result, "BIGINT");
1990 appendStringInfo(&result, "REAL");
1993 appendStringInfo(&result, "DOUBLE");
1996 appendStringInfo(&result, "BOOLEAN");
2000 appendStringInfo(&result, "TIME");
2002 appendStringInfo(&result, "TIME_%d", typmod);
2006 appendStringInfo(&result, "TIME_WTZ");
2008 appendStringInfo(&result, "TIME_WTZ_%d", typmod);
2012 appendStringInfo(&result, "TIMESTAMP");
2014 appendStringInfo(&result, "TIMESTAMP_%d", typmod);
2016 case TIMESTAMPTZOID:
2018 appendStringInfo(&result, "TIMESTAMP_WTZ");
2020 appendStringInfo(&result, "TIMESTAMP_WTZ_%d", typmod);
2023 appendStringInfo(&result, "DATE");
2026 appendStringInfo(&result, "XML");
2030 HeapTuple tuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(typeoid), 0, 0, 0);
2031 Form_pg_type typtuple = (Form_pg_type) GETSTRUCT(tuple);
2033 appendStringInfoString(&result,
2034 map_multipart_sql_identifier_to_xml_name((typtuple->typtype == 'd') ? "Domain" : "UDT",
2035 get_database_name(MyDatabaseId),
2036 get_namespace_name(typtuple->typnamespace),
2037 NameStr(typtuple->typname)));
2039 ReleaseSysCache(tuple);
2048 * Map a collection of SQL data types to XML Schema data types; see
2049 * SQL/XML:2002 section 9.10.
2052 map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc)
2057 StringInfoData result;
2059 initStringInfo(&result);
2061 uniquetypes = palloc(2 * sizeof(*uniquetypes) * tupdesc->natts);
2064 for (i = 1; i <= tupdesc->natts; i++)
2066 bool already_done = false;
2067 Oid type = SPI_gettypeid(tupdesc, i);
2068 for (j = 0; j < len; j++)
2069 if (type == uniquetypes[j])
2071 already_done = true;
2077 uniquetypes[len++] = type;
2080 /* add base types of domains */
2081 for (i = 0; i < len; i++)
2083 bool already_done = false;
2084 Oid type = getBaseType(uniquetypes[i]);
2085 for (j = 0; j < len; j++)
2086 if (type == uniquetypes[j])
2088 already_done = true;
2094 uniquetypes[len++] = type;
2097 for (i = 0; i < len; i++)
2098 appendStringInfo(&result, "%s\n", map_sql_type_to_xmlschema_type(uniquetypes[i], -1));
2105 * Map an SQL data type to a named XML Schema data type; see SQL/XML
2106 * sections 9.11 and 9.15.
2108 * (The distinction between 9.11 and 9.15 is basically that 9.15 adds
2109 * a name attribute, which thsi function does. The name-less version
2110 * 9.11 doesn't appear to be required anywhere.)
2113 map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
2115 StringInfoData result;
2116 const char *typename = map_sql_type_to_xml_name(typeoid, typmod);
2118 initStringInfo(&result);
2120 if (typeoid == XMLOID)
2122 appendStringInfo(&result,
2123 "<xsd:complexType mixed=\"true\">\n"
2125 " <xsd:any name=\"element\" minOccurs=\"0\" maxOccurs=\"unbounded\" processContents=\"skip\"/>\n"
2126 " </xsd:sequence>\n"
2127 "</xsd:complexType>\n");
2131 appendStringInfo(&result,
2132 "<xsd:simpleType name=\"%s\">\n", typename);
2140 appendStringInfo(&result,
2141 " <xsd:restriction base=\"xsd:string\">\n"
2142 " <xsd:maxLength value=\"%d\"/>\n"
2143 " </xsd:restriction>\n",
2148 appendStringInfo(&result,
2149 " <xsd:restriction base=\"xsd:%s\">\n"
2150 " </xsd:restriction>\n",
2151 xmlbinary == XMLBINARY_BASE64 ? "base64Binary" : "hexBinary");
2155 appendStringInfo(&result,
2156 " <xsd:restriction base=\"xsd:decimal\">\n"
2157 " <xsd:totalDigits value=\"%d\"/>\n"
2158 " <xsd:fractionDigits value=\"%d\"/>\n"
2159 " </xsd:restriction>\n",
2160 ((typmod - VARHDRSZ) >> 16) & 0xffff,
2161 (typmod - VARHDRSZ) & 0xffff);
2165 appendStringInfo(&result,
2166 " <xsd:restriction base=\"xsd:short\">\n"
2167 " <xsd:maxInclusive value=\"%d\"/>\n"
2168 " <xsd:minInclusive value=\"%d\"/>\n"
2169 " </xsd:restriction>\n",
2170 SHRT_MAX, SHRT_MIN);
2174 appendStringInfo(&result,
2175 " <xsd:restriction base='xsd:int'>\n"
2176 " <xsd:maxInclusive value=\"%d\"/>\n"
2177 " <xsd:minInclusive value=\"%d\"/>\n"
2178 " </xsd:restriction>\n",
2183 appendStringInfo(&result,
2184 " <xsd:restriction base=\"xsd:long\">\n"
2185 " <xsd:maxInclusive value=\"" INT64_FORMAT "\"/>\n"
2186 " <xsd:minInclusive value=\"" INT64_FORMAT "\"/>\n"
2187 " </xsd:restriction>\n",
2188 -((INT64CONST(1) << (sizeof(int64) * 8 - 1)) + 1),
2189 (INT64CONST(1) << (sizeof(int64) * 8 - 1)));
2193 appendStringInfo(&result,
2194 " <xsd:restriction base=\"xsd:float\"></xsd:restriction>\n");
2198 appendStringInfo(&result,
2199 " <xsd:restriction base=\"xsd:double\"></xsd:restriction>\n");
2203 appendStringInfo(&result,
2204 " <xsd:restriction base=\"xsd:boolean\"></xsd:restriction>\n");
2210 const char *tz = (typeoid == TIMETZOID ? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
2213 appendStringInfo(&result,
2214 " <xsd:restriction base=\"xsd:time\">\n"
2215 " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}(.\\p{Nd}+)?%s\"/>\n"
2216 " </xsd:restriction>\n", tz);
2217 else if (typmod == 0)
2218 appendStringInfo(&result,
2219 " <xsd:restriction base=\"xsd:time\">\n"
2220 " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
2221 " </xsd:restriction>\n", tz);
2223 appendStringInfo(&result,
2224 " <xsd:restriction base=\"xsd:time\">\n"
2225 " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}.\\p{Nd}{%d}%s\"/>\n"
2226 " </xsd:restriction>\n", typmod - VARHDRSZ, tz);
2231 case TIMESTAMPTZOID:
2233 const char *tz = (typeoid == TIMESTAMPTZOID ? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
2236 appendStringInfo(&result,
2237 " <xsd:restriction base=\"xsd:time\">\n"
2238 " <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"
2239 " </xsd:restriction>\n", tz);
2240 else if (typmod == 0)
2241 appendStringInfo(&result,
2242 " <xsd:restriction base=\"xsd:time\">\n"
2243 " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}T\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
2244 " </xsd:restriction>\n", tz);
2246 appendStringInfo(&result,
2247 " <xsd:restriction base=\"xsd:time\">\n"
2248 " <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"
2249 " </xsd:restriction>\n", typmod - VARHDRSZ, tz);
2254 appendStringInfo(&result,
2255 " <xsd:restriction base=\"xsd:date\">\n"
2256 " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}\"/>\n"
2257 " </xsd:restriction>\n");
2261 if (get_typtype(typeoid) == 'd')
2264 int32 base_typmod = -1;
2266 base_typeoid = getBaseTypeAndTypmod(typeoid, &base_typmod);
2268 appendStringInfo(&result,
2269 " <xsd:restriction base=\"%s\">\n",
2270 map_sql_type_to_xml_name(base_typeoid, base_typmod));
2273 appendStringInfo(&result,
2274 "</xsd:simpleType>\n");
2282 * Map an SQL row to an XML element, taking the row from the active
2283 * SPI cursor. See also SQL/XML:2003 section 9.12.
2286 SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns)
2292 xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
2303 appendStringInfo(result, "<%s xmlns:xsi=\"" NAMESPACE_XSI "\"", xmltn);
2304 if (strlen(targetns) > 0)
2305 appendStringInfo(result, " xmlns=\"%s\"", targetns);
2306 appendStringInfo(result, ">\n");
2309 appendStringInfoString(result, "<row>\n");
2311 for(i = 1; i <= SPI_tuptable->tupdesc->natts; i++)
2317 colname = map_sql_identifier_to_xml_name(SPI_fname(SPI_tuptable->tupdesc, i), true, false);
2318 colval = SPI_getbinval(SPI_tuptable->vals[rownum], SPI_tuptable->tupdesc, i, &isnull);
2323 appendStringInfo(result, " <%s xsi:nil='true'/>\n", colname);
2327 appendStringInfo(result, " <%s>%s</%s>\n",
2328 colname, map_sql_value_to_xml_value(colval, SPI_gettypeid(SPI_tuptable->tupdesc, i)),
2333 appendStringInfo(result, "</%s>\n\n", xmltn);
2335 appendStringInfoString(result, "</row>\n\n");