00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046 #include "postgres.h"
00047
00048 #ifdef USE_LIBXML
00049 #include <libxml/chvalid.h>
00050 #include <libxml/parser.h>
00051 #include <libxml/parserInternals.h>
00052 #include <libxml/tree.h>
00053 #include <libxml/uri.h>
00054 #include <libxml/xmlerror.h>
00055 #include <libxml/xmlversion.h>
00056 #include <libxml/xmlwriter.h>
00057 #include <libxml/xpath.h>
00058 #include <libxml/xpathInternals.h>
00059
00060
00061
00062
00063
00064
00065 #if LIBXML_VERSION >= 20704
00066 #define HAVE_XMLSTRUCTUREDERRORCONTEXT 1
00067 #endif
00068 #endif
00069
00070 #include "access/htup_details.h"
00071 #include "catalog/namespace.h"
00072 #include "catalog/pg_type.h"
00073 #include "commands/dbcommands.h"
00074 #include "executor/executor.h"
00075 #include "executor/spi.h"
00076 #include "fmgr.h"
00077 #include "lib/stringinfo.h"
00078 #include "libpq/pqformat.h"
00079 #include "mb/pg_wchar.h"
00080 #include "miscadmin.h"
00081 #include "nodes/execnodes.h"
00082 #include "nodes/nodeFuncs.h"
00083 #include "utils/array.h"
00084 #include "utils/builtins.h"
00085 #include "utils/date.h"
00086 #include "utils/datetime.h"
00087 #include "utils/lsyscache.h"
00088 #include "utils/memutils.h"
00089 #include "utils/rel.h"
00090 #include "utils/syscache.h"
00091 #include "utils/xml.h"
00092
00093
00094
00095 int xmlbinary;
00096 int xmloption;
00097
00098 #ifdef USE_LIBXML
00099
00100
00101 #define ERRCXT_MAGIC 68275028
00102
00103 struct PgXmlErrorContext
00104 {
00105 int magic;
00106
00107 PgXmlStrictness strictness;
00108
00109 bool err_occurred;
00110 StringInfoData err_buf;
00111
00112 xmlStructuredErrorFunc saved_errfunc;
00113 void *saved_errcxt;
00114
00115 xmlExternalEntityLoader saved_entityfunc;
00116 };
00117
00118 static xmlParserInputPtr xmlPgEntityLoader(const char *URL, const char *ID,
00119 xmlParserCtxtPtr ctxt);
00120 static void xml_errorHandler(void *data, xmlErrorPtr error);
00121 static void xml_ereport_by_code(int level, int sqlcode,
00122 const char *msg, int errcode);
00123 static void chopStringInfoNewlines(StringInfo str);
00124 static void appendStringInfoLineSeparator(StringInfo str);
00125
00126 #ifdef USE_LIBXMLCONTEXT
00127
00128 static MemoryContext LibxmlContext = NULL;
00129
00130 static void xml_memory_init(void);
00131 static void *xml_palloc(size_t size);
00132 static void *xml_repalloc(void *ptr, size_t size);
00133 static void xml_pfree(void *ptr);
00134 static char *xml_pstrdup(const char *string);
00135 #endif
00136
00137 static xmlChar *xml_text2xmlChar(text *in);
00138 static int parse_xml_decl(const xmlChar *str, size_t *lenp,
00139 xmlChar **version, xmlChar **encoding, int *standalone);
00140 static bool print_xml_decl(StringInfo buf, const xmlChar *version,
00141 pg_enc encoding, int standalone);
00142 static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
00143 bool preserve_whitespace, int encoding);
00144 static text *xml_xmlnodetoxmltype(xmlNodePtr cur);
00145 static int xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
00146 ArrayBuildState **astate);
00147 #endif
00148
00149 static StringInfo query_to_xml_internal(const char *query, char *tablename,
00150 const char *xmlschema, bool nulls, bool tableforest,
00151 const char *targetns, bool top_level);
00152 static const char *map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid,
00153 bool nulls, bool tableforest, const char *targetns);
00154 static const char *map_sql_schema_to_xmlschema_types(Oid nspid,
00155 List *relid_list, bool nulls,
00156 bool tableforest, const char *targetns);
00157 static const char *map_sql_catalog_to_xmlschema_types(List *nspid_list,
00158 bool nulls, bool tableforest,
00159 const char *targetns);
00160 static const char *map_sql_type_to_xml_name(Oid typeoid, int typmod);
00161 static const char *map_sql_typecoll_to_xmlschema_types(List *tupdesc_list);
00162 static const char *map_sql_type_to_xmlschema_type(Oid typeoid, int typmod);
00163 static void SPI_sql_row_to_xmlelement(int rownum, StringInfo result,
00164 char *tablename, bool nulls, bool tableforest,
00165 const char *targetns, bool top_level);
00166
00167 #define NO_XML_SUPPORT() \
00168 ereport(ERROR, \
00169 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
00170 errmsg("unsupported XML feature"), \
00171 errdetail("This functionality requires the server to be built with libxml support."), \
00172 errhint("You need to rebuild PostgreSQL using --with-libxml.")))
00173
00174
00175
00176 #define NAMESPACE_XSD "http://www.w3.org/2001/XMLSchema"
00177 #define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance"
00178 #define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml"
00179
00180
00181 #ifdef USE_LIBXML
00182
00183 static int
00184 xmlChar_to_encoding(const xmlChar *encoding_name)
00185 {
00186 int encoding = pg_char_to_encoding((const char *) encoding_name);
00187
00188 if (encoding < 0)
00189 ereport(ERROR,
00190 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00191 errmsg("invalid encoding name \"%s\"",
00192 (const char *) encoding_name)));
00193 return encoding;
00194 }
00195 #endif
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205 Datum
00206 xml_in(PG_FUNCTION_ARGS)
00207 {
00208 #ifdef USE_LIBXML
00209 char *s = PG_GETARG_CSTRING(0);
00210 xmltype *vardata;
00211 xmlDocPtr doc;
00212
00213 vardata = (xmltype *) cstring_to_text(s);
00214
00215
00216
00217
00218
00219 doc = xml_parse(vardata, xmloption, true, GetDatabaseEncoding());
00220 xmlFreeDoc(doc);
00221
00222 PG_RETURN_XML_P(vardata);
00223 #else
00224 NO_XML_SUPPORT();
00225 return 0;
00226 #endif
00227 }
00228
00229
00230 #define PG_XML_DEFAULT_VERSION "1.0"
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240 static char *
00241 xml_out_internal(xmltype *x, pg_enc target_encoding)
00242 {
00243 char *str = text_to_cstring((text *) x);
00244
00245 #ifdef USE_LIBXML
00246 size_t len = strlen(str);
00247 xmlChar *version;
00248 int standalone;
00249 int res_code;
00250
00251 if ((res_code = parse_xml_decl((xmlChar *) str,
00252 &len, &version, NULL, &standalone)) == 0)
00253 {
00254 StringInfoData buf;
00255
00256 initStringInfo(&buf);
00257
00258 if (!print_xml_decl(&buf, version, target_encoding, standalone))
00259 {
00260
00261
00262
00263
00264
00265 if (*(str + len) == '\n')
00266 len += 1;
00267 }
00268 appendStringInfoString(&buf, str + len);
00269
00270 pfree(str);
00271
00272 return buf.data;
00273 }
00274
00275 xml_ereport_by_code(WARNING, ERRCODE_INTERNAL_ERROR,
00276 "could not parse XML declaration in stored value",
00277 res_code);
00278 #endif
00279 return str;
00280 }
00281
00282
00283 Datum
00284 xml_out(PG_FUNCTION_ARGS)
00285 {
00286 xmltype *x = PG_GETARG_XML_P(0);
00287
00288
00289
00290
00291
00292
00293
00294 PG_RETURN_CSTRING(xml_out_internal(x, 0));
00295 }
00296
00297
00298 Datum
00299 xml_recv(PG_FUNCTION_ARGS)
00300 {
00301 #ifdef USE_LIBXML
00302 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
00303 xmltype *result;
00304 char *str;
00305 char *newstr;
00306 int nbytes;
00307 xmlDocPtr doc;
00308 xmlChar *encodingStr = NULL;
00309 int encoding;
00310
00311
00312
00313
00314
00315
00316 nbytes = buf->len - buf->cursor;
00317 str = (char *) pq_getmsgbytes(buf, nbytes);
00318
00319
00320
00321
00322
00323
00324 result = palloc(nbytes + 1 + VARHDRSZ);
00325 SET_VARSIZE(result, nbytes + VARHDRSZ);
00326 memcpy(VARDATA(result), str, nbytes);
00327 str = VARDATA(result);
00328 str[nbytes] = '\0';
00329
00330 parse_xml_decl((const xmlChar *) str, NULL, NULL, &encodingStr, NULL);
00331
00332
00333
00334
00335
00336
00337
00338 encoding = encodingStr ? xmlChar_to_encoding(encodingStr) : PG_UTF8;
00339
00340
00341
00342
00343
00344 doc = xml_parse(result, xmloption, true, encoding);
00345 xmlFreeDoc(doc);
00346
00347
00348 newstr = (char *) pg_do_encoding_conversion((unsigned char *) str,
00349 nbytes,
00350 encoding,
00351 GetDatabaseEncoding());
00352
00353 if (newstr != str)
00354 {
00355 pfree(result);
00356 result = (xmltype *) cstring_to_text(newstr);
00357 pfree(newstr);
00358 }
00359
00360 PG_RETURN_XML_P(result);
00361 #else
00362 NO_XML_SUPPORT();
00363 return 0;
00364 #endif
00365 }
00366
00367
00368 Datum
00369 xml_send(PG_FUNCTION_ARGS)
00370 {
00371 xmltype *x = PG_GETARG_XML_P(0);
00372 char *outval;
00373 StringInfoData buf;
00374
00375
00376
00377
00378
00379 outval = xml_out_internal(x, pg_get_client_encoding());
00380
00381 pq_begintypsend(&buf);
00382 pq_sendtext(&buf, outval, strlen(outval));
00383 pfree(outval);
00384 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
00385 }
00386
00387
00388 #ifdef USE_LIBXML
00389 static void
00390 appendStringInfoText(StringInfo str, const text *t)
00391 {
00392 appendBinaryStringInfo(str, VARDATA(t), VARSIZE(t) - VARHDRSZ);
00393 }
00394 #endif
00395
00396
00397 static xmltype *
00398 stringinfo_to_xmltype(StringInfo buf)
00399 {
00400 return (xmltype *) cstring_to_text_with_len(buf->data, buf->len);
00401 }
00402
00403
00404 static xmltype *
00405 cstring_to_xmltype(const char *string)
00406 {
00407 return (xmltype *) cstring_to_text(string);
00408 }
00409
00410
00411 #ifdef USE_LIBXML
00412 static xmltype *
00413 xmlBuffer_to_xmltype(xmlBufferPtr buf)
00414 {
00415 return (xmltype *) cstring_to_text_with_len((const char *) xmlBufferContent(buf),
00416 xmlBufferLength(buf));
00417 }
00418 #endif
00419
00420
00421 Datum
00422 xmlcomment(PG_FUNCTION_ARGS)
00423 {
00424 #ifdef USE_LIBXML
00425 text *arg = PG_GETARG_TEXT_P(0);
00426 char *argdata = VARDATA(arg);
00427 int len = VARSIZE(arg) - VARHDRSZ;
00428 StringInfoData buf;
00429 int i;
00430
00431
00432 for (i = 1; i < len; i++)
00433 {
00434 if (argdata[i] == '-' && argdata[i - 1] == '-')
00435 ereport(ERROR,
00436 (errcode(ERRCODE_INVALID_XML_COMMENT),
00437 errmsg("invalid XML comment")));
00438 }
00439 if (len > 0 && argdata[len - 1] == '-')
00440 ereport(ERROR,
00441 (errcode(ERRCODE_INVALID_XML_COMMENT),
00442 errmsg("invalid XML comment")));
00443
00444 initStringInfo(&buf);
00445 appendStringInfo(&buf, "<!--");
00446 appendStringInfoText(&buf, arg);
00447 appendStringInfo(&buf, "-->");
00448
00449 PG_RETURN_XML_P(stringinfo_to_xmltype(&buf));
00450 #else
00451 NO_XML_SUPPORT();
00452 return 0;
00453 #endif
00454 }
00455
00456
00457
00458
00459
00460
00461
00462 xmltype *
00463 xmlconcat(List *args)
00464 {
00465 #ifdef USE_LIBXML
00466 int global_standalone = 1;
00467 xmlChar *global_version = NULL;
00468 bool global_version_no_value = false;
00469 StringInfoData buf;
00470 ListCell *v;
00471
00472 initStringInfo(&buf);
00473 foreach(v, args)
00474 {
00475 xmltype *x = DatumGetXmlP(PointerGetDatum(lfirst(v)));
00476 size_t len;
00477 xmlChar *version;
00478 int standalone;
00479 char *str;
00480
00481 len = VARSIZE(x) - VARHDRSZ;
00482 str = text_to_cstring((text *) x);
00483
00484 parse_xml_decl((xmlChar *) str, &len, &version, NULL, &standalone);
00485
00486 if (standalone == 0 && global_standalone == 1)
00487 global_standalone = 0;
00488 if (standalone < 0)
00489 global_standalone = -1;
00490
00491 if (!version)
00492 global_version_no_value = true;
00493 else if (!global_version)
00494 global_version = version;
00495 else if (xmlStrcmp(version, global_version) != 0)
00496 global_version_no_value = true;
00497
00498 appendStringInfoString(&buf, str + len);
00499 pfree(str);
00500 }
00501
00502 if (!global_version_no_value || global_standalone >= 0)
00503 {
00504 StringInfoData buf2;
00505
00506 initStringInfo(&buf2);
00507
00508 print_xml_decl(&buf2,
00509 (!global_version_no_value) ? global_version : NULL,
00510 0,
00511 global_standalone);
00512
00513 appendStringInfoString(&buf2, buf.data);
00514 buf = buf2;
00515 }
00516
00517 return stringinfo_to_xmltype(&buf);
00518 #else
00519 NO_XML_SUPPORT();
00520 return NULL;
00521 #endif
00522 }
00523
00524
00525
00526
00527
00528 Datum
00529 xmlconcat2(PG_FUNCTION_ARGS)
00530 {
00531 if (PG_ARGISNULL(0))
00532 {
00533 if (PG_ARGISNULL(1))
00534 PG_RETURN_NULL();
00535 else
00536 PG_RETURN_XML_P(PG_GETARG_XML_P(1));
00537 }
00538 else if (PG_ARGISNULL(1))
00539 PG_RETURN_XML_P(PG_GETARG_XML_P(0));
00540 else
00541 PG_RETURN_XML_P(xmlconcat(list_make2(PG_GETARG_XML_P(0),
00542 PG_GETARG_XML_P(1))));
00543 }
00544
00545
00546 Datum
00547 texttoxml(PG_FUNCTION_ARGS)
00548 {
00549 text *data = PG_GETARG_TEXT_P(0);
00550
00551 PG_RETURN_XML_P(xmlparse(data, xmloption, true));
00552 }
00553
00554
00555 Datum
00556 xmltotext(PG_FUNCTION_ARGS)
00557 {
00558 xmltype *data = PG_GETARG_XML_P(0);
00559
00560
00561 PG_RETURN_TEXT_P((text *) data);
00562 }
00563
00564
00565 text *
00566 xmltotext_with_xmloption(xmltype *data, XmlOptionType xmloption_arg)
00567 {
00568 if (xmloption_arg == XMLOPTION_DOCUMENT && !xml_is_document(data))
00569 ereport(ERROR,
00570 (errcode(ERRCODE_NOT_AN_XML_DOCUMENT),
00571 errmsg("not an XML document")));
00572
00573
00574 return (text *) data;
00575 }
00576
00577
00578 xmltype *
00579 xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
00580 {
00581 #ifdef USE_LIBXML
00582 XmlExpr *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
00583 xmltype *result;
00584 List *named_arg_strings;
00585 List *arg_strings;
00586 int i;
00587 ListCell *arg;
00588 ListCell *narg;
00589 PgXmlErrorContext *xmlerrcxt;
00590 volatile xmlBufferPtr buf = NULL;
00591 volatile xmlTextWriterPtr writer = NULL;
00592
00593
00594
00595
00596
00597
00598
00599 named_arg_strings = NIL;
00600 i = 0;
00601 foreach(arg, xmlExpr->named_args)
00602 {
00603 ExprState *e = (ExprState *) lfirst(arg);
00604 Datum value;
00605 bool isnull;
00606 char *str;
00607
00608 value = ExecEvalExpr(e, econtext, &isnull, NULL);
00609 if (isnull)
00610 str = NULL;
00611 else
00612 str = map_sql_value_to_xml_value(value, exprType((Node *) e->expr), false);
00613 named_arg_strings = lappend(named_arg_strings, str);
00614 i++;
00615 }
00616
00617 arg_strings = NIL;
00618 foreach(arg, xmlExpr->args)
00619 {
00620 ExprState *e = (ExprState *) lfirst(arg);
00621 Datum value;
00622 bool isnull;
00623 char *str;
00624
00625 value = ExecEvalExpr(e, econtext, &isnull, NULL);
00626
00627 if (!isnull)
00628 {
00629 str = map_sql_value_to_xml_value(value,
00630 exprType((Node *) e->expr), true);
00631 arg_strings = lappend(arg_strings, str);
00632 }
00633 }
00634
00635
00636 xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
00637
00638 PG_TRY();
00639 {
00640 buf = xmlBufferCreate();
00641 if (buf == NULL || xmlerrcxt->err_occurred)
00642 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
00643 "could not allocate xmlBuffer");
00644 writer = xmlNewTextWriterMemory(buf, 0);
00645 if (writer == NULL || xmlerrcxt->err_occurred)
00646 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
00647 "could not allocate xmlTextWriter");
00648
00649 xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
00650
00651 forboth(arg, named_arg_strings, narg, xexpr->arg_names)
00652 {
00653 char *str = (char *) lfirst(arg);
00654 char *argname = strVal(lfirst(narg));
00655
00656 if (str)
00657 xmlTextWriterWriteAttribute(writer,
00658 (xmlChar *) argname,
00659 (xmlChar *) str);
00660 }
00661
00662 foreach(arg, arg_strings)
00663 {
00664 char *str = (char *) lfirst(arg);
00665
00666 xmlTextWriterWriteRaw(writer, (xmlChar *) str);
00667 }
00668
00669 xmlTextWriterEndElement(writer);
00670
00671
00672 xmlFreeTextWriter(writer);
00673 writer = NULL;
00674
00675 result = xmlBuffer_to_xmltype(buf);
00676 }
00677 PG_CATCH();
00678 {
00679 if (writer)
00680 xmlFreeTextWriter(writer);
00681 if (buf)
00682 xmlBufferFree(buf);
00683
00684 pg_xml_done(xmlerrcxt, true);
00685
00686 PG_RE_THROW();
00687 }
00688 PG_END_TRY();
00689
00690 xmlBufferFree(buf);
00691
00692 pg_xml_done(xmlerrcxt, false);
00693
00694 return result;
00695 #else
00696 NO_XML_SUPPORT();
00697 return NULL;
00698 #endif
00699 }
00700
00701
00702 xmltype *
00703 xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace)
00704 {
00705 #ifdef USE_LIBXML
00706 xmlDocPtr doc;
00707
00708 doc = xml_parse(data, xmloption_arg, preserve_whitespace,
00709 GetDatabaseEncoding());
00710 xmlFreeDoc(doc);
00711
00712 return (xmltype *) data;
00713 #else
00714 NO_XML_SUPPORT();
00715 return NULL;
00716 #endif
00717 }
00718
00719
00720 xmltype *
00721 xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null)
00722 {
00723 #ifdef USE_LIBXML
00724 xmltype *result;
00725 StringInfoData buf;
00726
00727 if (pg_strcasecmp(target, "xml") == 0)
00728 ereport(ERROR,
00729 (errcode(ERRCODE_SYNTAX_ERROR),
00730 errmsg("invalid XML processing instruction"),
00731 errdetail("XML processing instruction target name cannot be \"%s\".", target)));
00732
00733
00734
00735
00736
00737 *result_is_null = arg_is_null;
00738 if (*result_is_null)
00739 return NULL;
00740
00741 initStringInfo(&buf);
00742
00743 appendStringInfo(&buf, "<?%s", target);
00744
00745 if (arg != NULL)
00746 {
00747 char *string;
00748
00749 string = text_to_cstring(arg);
00750 if (strstr(string, "?>") != NULL)
00751 ereport(ERROR,
00752 (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
00753 errmsg("invalid XML processing instruction"),
00754 errdetail("XML processing instruction cannot contain \"?>\".")));
00755
00756 appendStringInfoChar(&buf, ' ');
00757 appendStringInfoString(&buf, string + strspn(string, " "));
00758 pfree(string);
00759 }
00760 appendStringInfoString(&buf, "?>");
00761
00762 result = stringinfo_to_xmltype(&buf);
00763 pfree(buf.data);
00764 return result;
00765 #else
00766 NO_XML_SUPPORT();
00767 return NULL;
00768 #endif
00769 }
00770
00771
00772 xmltype *
00773 xmlroot(xmltype *data, text *version, int standalone)
00774 {
00775 #ifdef USE_LIBXML
00776 char *str;
00777 size_t len;
00778 xmlChar *orig_version;
00779 int orig_standalone;
00780 StringInfoData buf;
00781
00782 len = VARSIZE(data) - VARHDRSZ;
00783 str = text_to_cstring((text *) data);
00784
00785 parse_xml_decl((xmlChar *) str, &len, &orig_version, NULL, &orig_standalone);
00786
00787 if (version)
00788 orig_version = xml_text2xmlChar(version);
00789 else
00790 orig_version = NULL;
00791
00792 switch (standalone)
00793 {
00794 case XML_STANDALONE_YES:
00795 orig_standalone = 1;
00796 break;
00797 case XML_STANDALONE_NO:
00798 orig_standalone = 0;
00799 break;
00800 case XML_STANDALONE_NO_VALUE:
00801 orig_standalone = -1;
00802 break;
00803 case XML_STANDALONE_OMITTED:
00804
00805 break;
00806 }
00807
00808 initStringInfo(&buf);
00809 print_xml_decl(&buf, orig_version, 0, orig_standalone);
00810 appendStringInfoString(&buf, str + len);
00811
00812 return stringinfo_to_xmltype(&buf);
00813 #else
00814 NO_XML_SUPPORT();
00815 return NULL;
00816 #endif
00817 }
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828 Datum
00829 xmlvalidate(PG_FUNCTION_ARGS)
00830 {
00831 ereport(ERROR,
00832 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00833 errmsg("xmlvalidate is not implemented")));
00834 return 0;
00835 }
00836
00837
00838 bool
00839 xml_is_document(xmltype *arg)
00840 {
00841 #ifdef USE_LIBXML
00842 bool result;
00843 volatile xmlDocPtr doc = NULL;
00844 MemoryContext ccxt = CurrentMemoryContext;
00845
00846
00847 PG_TRY();
00848 {
00849 doc = xml_parse((text *) arg, XMLOPTION_DOCUMENT, true,
00850 GetDatabaseEncoding());
00851 result = true;
00852 }
00853 PG_CATCH();
00854 {
00855 ErrorData *errdata;
00856 MemoryContext ecxt;
00857
00858 ecxt = MemoryContextSwitchTo(ccxt);
00859 errdata = CopyErrorData();
00860 if (errdata->sqlerrcode == ERRCODE_INVALID_XML_DOCUMENT)
00861 {
00862 FlushErrorState();
00863 result = false;
00864 }
00865 else
00866 {
00867 MemoryContextSwitchTo(ecxt);
00868 PG_RE_THROW();
00869 }
00870 }
00871 PG_END_TRY();
00872
00873 if (doc)
00874 xmlFreeDoc(doc);
00875
00876 return result;
00877 #else
00878 NO_XML_SUPPORT();
00879 return false;
00880 #endif
00881 }
00882
00883
00884 #ifdef USE_LIBXML
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897 void
00898 pg_xml_init_library(void)
00899 {
00900 static bool first_time = true;
00901
00902 if (first_time)
00903 {
00904
00905
00906
00907
00908
00909
00910 if (sizeof(char) != sizeof(xmlChar))
00911 ereport(ERROR,
00912 (errmsg("could not initialize XML library"),
00913 errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.",
00914 (int) sizeof(char), (int) sizeof(xmlChar))));
00915
00916 #ifdef USE_LIBXMLCONTEXT
00917
00918 xml_memory_init();
00919 #endif
00920
00921
00922 LIBXML_TEST_VERSION;
00923
00924 first_time = false;
00925 }
00926 }
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943 PgXmlErrorContext *
00944 pg_xml_init(PgXmlStrictness strictness)
00945 {
00946 PgXmlErrorContext *errcxt;
00947 void *new_errcxt;
00948
00949
00950 pg_xml_init_library();
00951
00952
00953 errcxt = (PgXmlErrorContext *) palloc(sizeof(PgXmlErrorContext));
00954 errcxt->magic = ERRCXT_MAGIC;
00955 errcxt->strictness = strictness;
00956 errcxt->err_occurred = false;
00957 initStringInfo(&errcxt->err_buf);
00958
00959
00960
00961
00962
00963
00964
00965
00966 errcxt->saved_errfunc = xmlStructuredError;
00967
00968 #ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
00969 errcxt->saved_errcxt = xmlStructuredErrorContext;
00970 #else
00971 errcxt->saved_errcxt = xmlGenericErrorContext;
00972 #endif
00973
00974 xmlSetStructuredErrorFunc((void *) errcxt, xml_errorHandler);
00975
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985
00986
00987
00988
00989
00990 #ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
00991 new_errcxt = xmlStructuredErrorContext;
00992 #else
00993 new_errcxt = xmlGenericErrorContext;
00994 #endif
00995
00996 if (new_errcxt != (void *) errcxt)
00997 ereport(ERROR,
00998 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00999 errmsg("could not set up XML error handler"),
01000 errhint("This probably indicates that the version of libxml2"
01001 " being used is not compatible with the libxml2"
01002 " header files that PostgreSQL was built with.")));
01003
01004
01005
01006
01007
01008 errcxt->saved_entityfunc = xmlGetExternalEntityLoader();
01009 xmlSetExternalEntityLoader(xmlPgEntityLoader);
01010
01011 return errcxt;
01012 }
01013
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023
01024 void
01025 pg_xml_done(PgXmlErrorContext *errcxt, bool isError)
01026 {
01027 void *cur_errcxt;
01028
01029
01030 Assert(errcxt->magic == ERRCXT_MAGIC);
01031
01032
01033
01034
01035
01036
01037 Assert(!errcxt->err_occurred || isError);
01038
01039
01040
01041
01042
01043
01044 #ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
01045 cur_errcxt = xmlStructuredErrorContext;
01046 #else
01047 cur_errcxt = xmlGenericErrorContext;
01048 #endif
01049
01050 if (cur_errcxt != (void *) errcxt)
01051 elog(WARNING, "libxml error handling state is out of sync with xml.c");
01052
01053
01054 xmlSetStructuredErrorFunc(errcxt->saved_errcxt, errcxt->saved_errfunc);
01055 xmlSetExternalEntityLoader(errcxt->saved_entityfunc);
01056
01057
01058
01059
01060
01061 errcxt->magic = 0;
01062
01063
01064 pfree(errcxt->err_buf.data);
01065 pfree(errcxt);
01066 }
01067
01068
01069
01070
01071
01072 bool
01073 pg_xml_error_occurred(PgXmlErrorContext *errcxt)
01074 {
01075 return errcxt->err_occurred;
01076 }
01077
01078
01079
01080
01081
01082
01083
01084
01085
01086
01087
01088 #define CHECK_XML_SPACE(p) \
01089 do { \
01090 if (!xmlIsBlank_ch(*(p))) \
01091 return XML_ERR_SPACE_REQUIRED; \
01092 } while (0)
01093
01094 #define SKIP_XML_SPACE(p) \
01095 while (xmlIsBlank_ch(*(p))) (p)++
01096
01097
01098
01099 #define PG_XMLISNAMECHAR(c) \
01100 (xmlIsBaseChar_ch(c) || xmlIsIdeographicQ(c) \
01101 || xmlIsDigit_ch(c) \
01102 || c == '.' || c == '-' || c == '_' || c == ':' \
01103 || xmlIsCombiningQ(c) \
01104 || xmlIsExtender_ch(c))
01105
01106
01107 static xmlChar *
01108 xml_pnstrdup(const xmlChar *str, size_t len)
01109 {
01110 xmlChar *result;
01111
01112 result = (xmlChar *) palloc((len + 1) * sizeof(xmlChar));
01113 memcpy(result, str, len * sizeof(xmlChar));
01114 result[len] = 0;
01115 return result;
01116 }
01117
01118
01119
01120
01121
01122
01123
01124 static int
01125 parse_xml_decl(const xmlChar *str, size_t *lenp,
01126 xmlChar **version, xmlChar **encoding, int *standalone)
01127 {
01128 const xmlChar *p;
01129 const xmlChar *save_p;
01130 size_t len;
01131 int utf8char;
01132 int utf8len;
01133
01134
01135
01136
01137
01138
01139
01140 pg_xml_init_library();
01141
01142
01143 if (version)
01144 *version = NULL;
01145 if (encoding)
01146 *encoding = NULL;
01147 if (standalone)
01148 *standalone = -1;
01149
01150 p = str;
01151
01152 if (xmlStrncmp(p, (xmlChar *) "<?xml", 5) != 0)
01153 goto finished;
01154
01155
01156 utf8len = strlen((const char *) (p + 5));
01157 utf8char = xmlGetUTF8Char(p + 5, &utf8len);
01158 if (PG_XMLISNAMECHAR(utf8char))
01159 goto finished;
01160
01161 p += 5;
01162
01163
01164 CHECK_XML_SPACE(p);
01165 SKIP_XML_SPACE(p);
01166 if (xmlStrncmp(p, (xmlChar *) "version", 7) != 0)
01167 return XML_ERR_VERSION_MISSING;
01168 p += 7;
01169 SKIP_XML_SPACE(p);
01170 if (*p != '=')
01171 return XML_ERR_VERSION_MISSING;
01172 p += 1;
01173 SKIP_XML_SPACE(p);
01174
01175 if (*p == '\'' || *p == '"')
01176 {
01177 const xmlChar *q;
01178
01179 q = xmlStrchr(p + 1, *p);
01180 if (!q)
01181 return XML_ERR_VERSION_MISSING;
01182
01183 if (version)
01184 *version = xml_pnstrdup(p + 1, q - p - 1);
01185 p = q + 1;
01186 }
01187 else
01188 return XML_ERR_VERSION_MISSING;
01189
01190
01191 save_p = p;
01192 SKIP_XML_SPACE(p);
01193 if (xmlStrncmp(p, (xmlChar *) "encoding", 8) == 0)
01194 {
01195 CHECK_XML_SPACE(save_p);
01196 p += 8;
01197 SKIP_XML_SPACE(p);
01198 if (*p != '=')
01199 return XML_ERR_MISSING_ENCODING;
01200 p += 1;
01201 SKIP_XML_SPACE(p);
01202
01203 if (*p == '\'' || *p == '"')
01204 {
01205 const xmlChar *q;
01206
01207 q = xmlStrchr(p + 1, *p);
01208 if (!q)
01209 return XML_ERR_MISSING_ENCODING;
01210
01211 if (encoding)
01212 *encoding = xml_pnstrdup(p + 1, q - p - 1);
01213 p = q + 1;
01214 }
01215 else
01216 return XML_ERR_MISSING_ENCODING;
01217 }
01218 else
01219 {
01220 p = save_p;
01221 }
01222
01223
01224 save_p = p;
01225 SKIP_XML_SPACE(p);
01226 if (xmlStrncmp(p, (xmlChar *) "standalone", 10) == 0)
01227 {
01228 CHECK_XML_SPACE(save_p);
01229 p += 10;
01230 SKIP_XML_SPACE(p);
01231 if (*p != '=')
01232 return XML_ERR_STANDALONE_VALUE;
01233 p += 1;
01234 SKIP_XML_SPACE(p);
01235 if (xmlStrncmp(p, (xmlChar *) "'yes'", 5) == 0 ||
01236 xmlStrncmp(p, (xmlChar *) "\"yes\"", 5) == 0)
01237 {
01238 if (standalone)
01239 *standalone = 1;
01240 p += 5;
01241 }
01242 else if (xmlStrncmp(p, (xmlChar *) "'no'", 4) == 0 ||
01243 xmlStrncmp(p, (xmlChar *) "\"no\"", 4) == 0)
01244 {
01245 if (standalone)
01246 *standalone = 0;
01247 p += 4;
01248 }
01249 else
01250 return XML_ERR_STANDALONE_VALUE;
01251 }
01252 else
01253 {
01254 p = save_p;
01255 }
01256
01257 SKIP_XML_SPACE(p);
01258 if (xmlStrncmp(p, (xmlChar *) "?>", 2) != 0)
01259 return XML_ERR_XMLDECL_NOT_FINISHED;
01260 p += 2;
01261
01262 finished:
01263 len = p - str;
01264
01265 for (p = str; p < str + len; p++)
01266 if (*p > 127)
01267 return XML_ERR_INVALID_CHAR;
01268
01269 if (lenp)
01270 *lenp = len;
01271
01272 return XML_ERR_OK;
01273 }
01274
01275
01276
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286
01287
01288
01289
01290 static bool
01291 print_xml_decl(StringInfo buf, const xmlChar *version,
01292 pg_enc encoding, int standalone)
01293 {
01294 if ((version && strcmp((const char *) version, PG_XML_DEFAULT_VERSION) != 0)
01295 || (encoding && encoding != PG_UTF8)
01296 || standalone != -1)
01297 {
01298 appendStringInfoString(buf, "<?xml");
01299
01300 if (version)
01301 appendStringInfo(buf, " version=\"%s\"", version);
01302 else
01303 appendStringInfo(buf, " version=\"%s\"", PG_XML_DEFAULT_VERSION);
01304
01305 if (encoding && encoding != PG_UTF8)
01306 {
01307
01308
01309
01310
01311 appendStringInfo(buf, " encoding=\"%s\"",
01312 pg_encoding_to_char(encoding));
01313 }
01314
01315 if (standalone == 1)
01316 appendStringInfoString(buf, " standalone=\"yes\"");
01317 else if (standalone == 0)
01318 appendStringInfoString(buf, " standalone=\"no\"");
01319 appendStringInfoString(buf, "?>");
01320
01321 return true;
01322 }
01323 else
01324 return false;
01325 }
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337 static xmlDocPtr
01338 xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
01339 int encoding)
01340 {
01341 int32 len;
01342 xmlChar *string;
01343 xmlChar *utf8string;
01344 PgXmlErrorContext *xmlerrcxt;
01345 volatile xmlParserCtxtPtr ctxt = NULL;
01346 volatile xmlDocPtr doc = NULL;
01347
01348 len = VARSIZE(data) - VARHDRSZ;
01349 string = xml_text2xmlChar(data);
01350
01351 utf8string = pg_do_encoding_conversion(string,
01352 len,
01353 encoding,
01354 PG_UTF8);
01355
01356
01357 xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_WELLFORMED);
01358
01359
01360 PG_TRY();
01361 {
01362 xmlInitParser();
01363
01364 ctxt = xmlNewParserCtxt();
01365 if (ctxt == NULL || xmlerrcxt->err_occurred)
01366 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
01367 "could not allocate parser context");
01368
01369 if (xmloption_arg == XMLOPTION_DOCUMENT)
01370 {
01371
01372
01373
01374
01375
01376
01377
01378 doc = xmlCtxtReadDoc(ctxt, utf8string,
01379 NULL,
01380 "UTF-8",
01381 XML_PARSE_NOENT | XML_PARSE_DTDATTR
01382 | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS));
01383 if (doc == NULL || xmlerrcxt->err_occurred)
01384 xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
01385 "invalid XML document");
01386 }
01387 else
01388 {
01389 int res_code;
01390 size_t count;
01391 xmlChar *version;
01392 int standalone;
01393
01394 res_code = parse_xml_decl(utf8string,
01395 &count, &version, NULL, &standalone);
01396 if (res_code != 0)
01397 xml_ereport_by_code(ERROR, ERRCODE_INVALID_XML_CONTENT,
01398 "invalid XML content: invalid XML declaration",
01399 res_code);
01400
01401 doc = xmlNewDoc(version);
01402 Assert(doc->encoding == NULL);
01403 doc->encoding = xmlStrdup((const xmlChar *) "UTF-8");
01404 doc->standalone = standalone;
01405
01406 res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0,
01407 utf8string + count, NULL);
01408 if (res_code != 0 || xmlerrcxt->err_occurred)
01409 xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_CONTENT,
01410 "invalid XML content");
01411 }
01412 }
01413 PG_CATCH();
01414 {
01415 if (doc != NULL)
01416 xmlFreeDoc(doc);
01417 if (ctxt != NULL)
01418 xmlFreeParserCtxt(ctxt);
01419
01420 pg_xml_done(xmlerrcxt, true);
01421
01422 PG_RE_THROW();
01423 }
01424 PG_END_TRY();
01425
01426 xmlFreeParserCtxt(ctxt);
01427
01428 pg_xml_done(xmlerrcxt, false);
01429
01430 return doc;
01431 }
01432
01433
01434
01435
01436
01437 static xmlChar *
01438 xml_text2xmlChar(text *in)
01439 {
01440 return (xmlChar *) text_to_cstring(in);
01441 }
01442
01443
01444 #ifdef USE_LIBXMLCONTEXT
01445
01446
01447
01448
01449
01450 static void
01451 xml_memory_init(void)
01452 {
01453
01454 if (LibxmlContext == NULL)
01455 LibxmlContext = AllocSetContextCreate(TopMemoryContext,
01456 "LibxmlContext",
01457 ALLOCSET_DEFAULT_MINSIZE,
01458 ALLOCSET_DEFAULT_INITSIZE,
01459 ALLOCSET_DEFAULT_MAXSIZE);
01460
01461
01462 xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
01463 }
01464
01465
01466
01467
01468 static void *
01469 xml_palloc(size_t size)
01470 {
01471 return MemoryContextAlloc(LibxmlContext, size);
01472 }
01473
01474
01475 static void *
01476 xml_repalloc(void *ptr, size_t size)
01477 {
01478 return repalloc(ptr, size);
01479 }
01480
01481
01482 static void
01483 xml_pfree(void *ptr)
01484 {
01485
01486 if (ptr)
01487 pfree(ptr);
01488 }
01489
01490
01491 static char *
01492 xml_pstrdup(const char *string)
01493 {
01494 return MemoryContextStrdup(LibxmlContext, string);
01495 }
01496 #endif
01497
01498
01499
01500
01501
01502
01503
01504
01505
01506
01507
01508
01509
01510 static xmlParserInputPtr
01511 xmlPgEntityLoader(const char *URL, const char *ID,
01512 xmlParserCtxtPtr ctxt)
01513 {
01514 return xmlNewStringInputStream(ctxt, (const xmlChar *) "");
01515 }
01516
01517
01518
01519
01520
01521
01522
01523
01524
01525
01526
01527
01528 void
01529 xml_ereport(PgXmlErrorContext *errcxt, int level, int sqlcode, const char *msg)
01530 {
01531 char *detail;
01532
01533
01534 if (errcxt->magic != ERRCXT_MAGIC)
01535 elog(ERROR, "xml_ereport called with invalid PgXmlErrorContext");
01536
01537
01538 errcxt->err_occurred = false;
01539
01540
01541 if (errcxt->err_buf.len > 0)
01542 detail = errcxt->err_buf.data;
01543 else
01544 detail = NULL;
01545
01546 ereport(level,
01547 (errcode(sqlcode),
01548 errmsg_internal("%s", msg),
01549 detail ? errdetail_internal("%s", detail) : 0));
01550 }
01551
01552
01553
01554
01555
01556 static void
01557 xml_errorHandler(void *data, xmlErrorPtr error)
01558 {
01559 PgXmlErrorContext *xmlerrcxt = (PgXmlErrorContext *) data;
01560 xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) error->ctxt;
01561 xmlParserInputPtr input = (ctxt != NULL) ? ctxt->input : NULL;
01562 xmlNodePtr node = error->node;
01563 const xmlChar *name = (node != NULL &&
01564 node->type == XML_ELEMENT_NODE) ? node->name : NULL;
01565 int domain = error->domain;
01566 int level = error->level;
01567 StringInfo errorBuf;
01568
01569
01570
01571
01572
01573
01574
01575 if (xmlerrcxt->magic != ERRCXT_MAGIC)
01576 elog(FATAL, "xml_errorHandler called with invalid PgXmlErrorContext");
01577
01578
01579
01580
01581
01582
01583
01584
01585
01586 switch (error->code)
01587 {
01588 case XML_WAR_NS_URI:
01589 level = XML_ERR_ERROR;
01590 domain = XML_FROM_NAMESPACE;
01591 break;
01592
01593 case XML_ERR_NS_DECL_ERROR:
01594 case XML_WAR_NS_URI_RELATIVE:
01595 case XML_WAR_NS_COLUMN:
01596 case XML_NS_ERR_XML_NAMESPACE:
01597 case XML_NS_ERR_UNDEFINED_NAMESPACE:
01598 case XML_NS_ERR_QNAME:
01599 case XML_NS_ERR_ATTRIBUTE_REDEFINED:
01600 case XML_NS_ERR_EMPTY:
01601 domain = XML_FROM_NAMESPACE;
01602 break;
01603 }
01604
01605
01606 switch (domain)
01607 {
01608 case XML_FROM_PARSER:
01609 case XML_FROM_NONE:
01610 case XML_FROM_MEMORY:
01611 case XML_FROM_IO:
01612
01613
01614
01615
01616 if (error->code == XML_WAR_UNDECLARED_ENTITY)
01617 return;
01618
01619
01620 break;
01621
01622 default:
01623
01624 if (xmlerrcxt->strictness == PG_XML_STRICTNESS_WELLFORMED)
01625 return;
01626 break;
01627 }
01628
01629
01630 errorBuf = makeStringInfo();
01631
01632 if (error->line > 0)
01633 appendStringInfo(errorBuf, "line %d: ", error->line);
01634 if (name != NULL)
01635 appendStringInfo(errorBuf, "element %s: ", name);
01636 appendStringInfoString(errorBuf, error->message);
01637
01638
01639
01640
01641
01642
01643
01644
01645
01646
01647
01648
01649 if (input != NULL)
01650 {
01651 xmlGenericErrorFunc errFuncSaved = xmlGenericError;
01652 void *errCtxSaved = xmlGenericErrorContext;
01653
01654 xmlSetGenericErrorFunc((void *) errorBuf,
01655 (xmlGenericErrorFunc) appendStringInfo);
01656
01657
01658 appendStringInfoLineSeparator(errorBuf);
01659
01660 xmlParserPrintFileContext(input);
01661
01662
01663 xmlSetGenericErrorFunc(errCtxSaved, errFuncSaved);
01664 }
01665
01666
01667 chopStringInfoNewlines(errorBuf);
01668
01669
01670
01671
01672
01673
01674
01675
01676
01677 if (xmlerrcxt->strictness == PG_XML_STRICTNESS_LEGACY)
01678 {
01679 appendStringInfoLineSeparator(&xmlerrcxt->err_buf);
01680 appendStringInfoString(&xmlerrcxt->err_buf, errorBuf->data);
01681
01682 pfree(errorBuf->data);
01683 pfree(errorBuf);
01684 return;
01685 }
01686
01687
01688
01689
01690
01691
01692
01693
01694
01695 if (level >= XML_ERR_ERROR)
01696 {
01697 appendStringInfoLineSeparator(&xmlerrcxt->err_buf);
01698 appendStringInfoString(&xmlerrcxt->err_buf, errorBuf->data);
01699
01700 xmlerrcxt->err_occurred = true;
01701 }
01702 else if (level >= XML_ERR_WARNING)
01703 {
01704 ereport(WARNING,
01705 (errmsg_internal("%s", errorBuf->data)));
01706 }
01707 else
01708 {
01709 ereport(NOTICE,
01710 (errmsg_internal("%s", errorBuf->data)));
01711 }
01712
01713 pfree(errorBuf->data);
01714 pfree(errorBuf);
01715 }
01716
01717
01718
01719
01720
01721
01722
01723
01724
01725 static void
01726 xml_ereport_by_code(int level, int sqlcode,
01727 const char *msg, int code)
01728 {
01729 const char *det;
01730
01731 switch (code)
01732 {
01733 case XML_ERR_INVALID_CHAR:
01734 det = gettext_noop("Invalid character value.");
01735 break;
01736 case XML_ERR_SPACE_REQUIRED:
01737 det = gettext_noop("Space required.");
01738 break;
01739 case XML_ERR_STANDALONE_VALUE:
01740 det = gettext_noop("standalone accepts only 'yes' or 'no'.");
01741 break;
01742 case XML_ERR_VERSION_MISSING:
01743 det = gettext_noop("Malformed declaration: missing version.");
01744 break;
01745 case XML_ERR_MISSING_ENCODING:
01746 det = gettext_noop("Missing encoding in text declaration.");
01747 break;
01748 case XML_ERR_XMLDECL_NOT_FINISHED:
01749 det = gettext_noop("Parsing XML declaration: '?>' expected.");
01750 break;
01751 default:
01752 det = gettext_noop("Unrecognized libxml error code: %d.");
01753 break;
01754 }
01755
01756 ereport(level,
01757 (errcode(sqlcode),
01758 errmsg_internal("%s", msg),
01759 errdetail(det, code)));
01760 }
01761
01762
01763
01764
01765
01766 static void
01767 chopStringInfoNewlines(StringInfo str)
01768 {
01769 while (str->len > 0 && str->data[str->len - 1] == '\n')
01770 str->data[--str->len] = '\0';
01771 }
01772
01773
01774
01775
01776
01777 static void
01778 appendStringInfoLineSeparator(StringInfo str)
01779 {
01780 chopStringInfoNewlines(str);
01781 if (str->len > 0)
01782 appendStringInfoChar(str, '\n');
01783 }
01784
01785
01786
01787
01788
01789 static pg_wchar
01790 sqlchar_to_unicode(char *s)
01791 {
01792 char *utf8string;
01793 pg_wchar ret[2];
01794
01795 utf8string = (char *) pg_do_encoding_conversion((unsigned char *) s,
01796 pg_mblen(s),
01797 GetDatabaseEncoding(),
01798 PG_UTF8);
01799
01800 pg_encoding_mb2wchar_with_len(PG_UTF8, utf8string, ret,
01801 pg_encoding_mblen(PG_UTF8, utf8string));
01802
01803 if (utf8string != s)
01804 pfree(utf8string);
01805
01806 return ret[0];
01807 }
01808
01809
01810 static bool
01811 is_valid_xml_namefirst(pg_wchar c)
01812 {
01813
01814 return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
01815 || c == '_' || c == ':');
01816 }
01817
01818
01819 static bool
01820 is_valid_xml_namechar(pg_wchar c)
01821 {
01822
01823 return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
01824 || xmlIsDigitQ(c)
01825 || c == '.' || c == '-' || c == '_' || c == ':'
01826 || xmlIsCombiningQ(c)
01827 || xmlIsExtenderQ(c));
01828 }
01829 #endif
01830
01831
01832
01833
01834
01835 char *
01836 map_sql_identifier_to_xml_name(char *ident, bool fully_escaped,
01837 bool escape_period)
01838 {
01839 #ifdef USE_LIBXML
01840 StringInfoData buf;
01841 char *p;
01842
01843
01844
01845
01846
01847 Assert(fully_escaped || !escape_period);
01848
01849 initStringInfo(&buf);
01850
01851 for (p = ident; *p; p += pg_mblen(p))
01852 {
01853 if (*p == ':' && (p == ident || fully_escaped))
01854 appendStringInfo(&buf, "_x003A_");
01855 else if (*p == '_' && *(p + 1) == 'x')
01856 appendStringInfo(&buf, "_x005F_");
01857 else if (fully_escaped && p == ident &&
01858 pg_strncasecmp(p, "xml", 3) == 0)
01859 {
01860 if (*p == 'x')
01861 appendStringInfo(&buf, "_x0078_");
01862 else
01863 appendStringInfo(&buf, "_x0058_");
01864 }
01865 else if (escape_period && *p == '.')
01866 appendStringInfo(&buf, "_x002E_");
01867 else
01868 {
01869 pg_wchar u = sqlchar_to_unicode(p);
01870
01871 if ((p == ident)
01872 ? !is_valid_xml_namefirst(u)
01873 : !is_valid_xml_namechar(u))
01874 appendStringInfo(&buf, "_x%04X_", (unsigned int) u);
01875 else
01876 appendBinaryStringInfo(&buf, p, pg_mblen(p));
01877 }
01878 }
01879
01880 return buf.data;
01881 #else
01882 NO_XML_SUPPORT();
01883 return NULL;
01884 #endif
01885 }
01886
01887
01888
01889
01890
01891 static char *
01892 unicode_to_sqlchar(pg_wchar c)
01893 {
01894 unsigned char utf8string[5];
01895 char *result;
01896
01897 memset(utf8string, 0, sizeof(utf8string));
01898 unicode_to_utf8(c, utf8string);
01899
01900 result = (char *) pg_do_encoding_conversion(utf8string,
01901 pg_encoding_mblen(PG_UTF8,
01902 (char *) utf8string),
01903 PG_UTF8,
01904 GetDatabaseEncoding());
01905
01906 if (result == (char *) utf8string)
01907 result = pstrdup(result);
01908 return result;
01909 }
01910
01911
01912
01913
01914
01915 char *
01916 map_xml_name_to_sql_identifier(char *name)
01917 {
01918 StringInfoData buf;
01919 char *p;
01920
01921 initStringInfo(&buf);
01922
01923 for (p = name; *p; p += pg_mblen(p))
01924 {
01925 if (*p == '_' && *(p + 1) == 'x'
01926 && isxdigit((unsigned char) *(p + 2))
01927 && isxdigit((unsigned char) *(p + 3))
01928 && isxdigit((unsigned char) *(p + 4))
01929 && isxdigit((unsigned char) *(p + 5))
01930 && *(p + 6) == '_')
01931 {
01932 unsigned int u;
01933
01934 sscanf(p + 2, "%X", &u);
01935 appendStringInfoString(&buf, unicode_to_sqlchar(u));
01936 p += 6;
01937 }
01938 else
01939 appendBinaryStringInfo(&buf, p, pg_mblen(p));
01940 }
01941
01942 return buf.data;
01943 }
01944
01945
01946
01947
01948
01949
01950
01951
01952
01953
01954
01955 char *
01956 map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings)
01957 {
01958 if (type_is_array_domain(type))
01959 {
01960 ArrayType *array;
01961 Oid elmtype;
01962 int16 elmlen;
01963 bool elmbyval;
01964 char elmalign;
01965 int num_elems;
01966 Datum *elem_values;
01967 bool *elem_nulls;
01968 StringInfoData buf;
01969 int i;
01970
01971 array = DatumGetArrayTypeP(value);
01972 elmtype = ARR_ELEMTYPE(array);
01973 get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
01974
01975 deconstruct_array(array, elmtype,
01976 elmlen, elmbyval, elmalign,
01977 &elem_values, &elem_nulls,
01978 &num_elems);
01979
01980 initStringInfo(&buf);
01981
01982 for (i = 0; i < num_elems; i++)
01983 {
01984 if (elem_nulls[i])
01985 continue;
01986 appendStringInfoString(&buf, "<element>");
01987 appendStringInfoString(&buf,
01988 map_sql_value_to_xml_value(elem_values[i],
01989 elmtype, true));
01990 appendStringInfoString(&buf, "</element>");
01991 }
01992
01993 pfree(elem_values);
01994 pfree(elem_nulls);
01995
01996 return buf.data;
01997 }
01998 else
01999 {
02000 Oid typeOut;
02001 bool isvarlena;
02002 char *str;
02003
02004
02005
02006
02007
02008 type = getBaseType(type);
02009
02010
02011
02012
02013 switch (type)
02014 {
02015 case BOOLOID:
02016 if (DatumGetBool(value))
02017 return "true";
02018 else
02019 return "false";
02020
02021 case DATEOID:
02022 {
02023 DateADT date;
02024 struct pg_tm tm;
02025 char buf[MAXDATELEN + 1];
02026
02027 date = DatumGetDateADT(value);
02028
02029 if (DATE_NOT_FINITE(date))
02030 ereport(ERROR,
02031 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
02032 errmsg("date out of range"),
02033 errdetail("XML does not support infinite date values.")));
02034 j2date(date + POSTGRES_EPOCH_JDATE,
02035 &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
02036 EncodeDateOnly(&tm, USE_XSD_DATES, buf);
02037
02038 return pstrdup(buf);
02039 }
02040
02041 case TIMESTAMPOID:
02042 {
02043 Timestamp timestamp;
02044 struct pg_tm tm;
02045 fsec_t fsec;
02046 char buf[MAXDATELEN + 1];
02047
02048 timestamp = DatumGetTimestamp(value);
02049
02050
02051 if (TIMESTAMP_NOT_FINITE(timestamp))
02052 ereport(ERROR,
02053 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
02054 errmsg("timestamp out of range"),
02055 errdetail("XML does not support infinite timestamp values.")));
02056 else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
02057 EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
02058 else
02059 ereport(ERROR,
02060 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
02061 errmsg("timestamp out of range")));
02062
02063 return pstrdup(buf);
02064 }
02065
02066 case TIMESTAMPTZOID:
02067 {
02068 TimestampTz timestamp;
02069 struct pg_tm tm;
02070 int tz;
02071 fsec_t fsec;
02072 const char *tzn = NULL;
02073 char buf[MAXDATELEN + 1];
02074
02075 timestamp = DatumGetTimestamp(value);
02076
02077
02078 if (TIMESTAMP_NOT_FINITE(timestamp))
02079 ereport(ERROR,
02080 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
02081 errmsg("timestamp out of range"),
02082 errdetail("XML does not support infinite timestamp values.")));
02083 else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
02084 EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
02085 else
02086 ereport(ERROR,
02087 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
02088 errmsg("timestamp out of range")));
02089
02090 return pstrdup(buf);
02091 }
02092
02093 #ifdef USE_LIBXML
02094 case BYTEAOID:
02095 {
02096 bytea *bstr = DatumGetByteaPP(value);
02097 PgXmlErrorContext *xmlerrcxt;
02098 volatile xmlBufferPtr buf = NULL;
02099 volatile xmlTextWriterPtr writer = NULL;
02100 char *result;
02101
02102 xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
02103
02104 PG_TRY();
02105 {
02106 buf = xmlBufferCreate();
02107 if (buf == NULL || xmlerrcxt->err_occurred)
02108 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
02109 "could not allocate xmlBuffer");
02110 writer = xmlNewTextWriterMemory(buf, 0);
02111 if (writer == NULL || xmlerrcxt->err_occurred)
02112 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
02113 "could not allocate xmlTextWriter");
02114
02115 if (xmlbinary == XMLBINARY_BASE64)
02116 xmlTextWriterWriteBase64(writer, VARDATA_ANY(bstr),
02117 0, VARSIZE_ANY_EXHDR(bstr));
02118 else
02119 xmlTextWriterWriteBinHex(writer, VARDATA_ANY(bstr),
02120 0, VARSIZE_ANY_EXHDR(bstr));
02121
02122
02123 xmlFreeTextWriter(writer);
02124 writer = NULL;
02125
02126 result = pstrdup((const char *) xmlBufferContent(buf));
02127 }
02128 PG_CATCH();
02129 {
02130 if (writer)
02131 xmlFreeTextWriter(writer);
02132 if (buf)
02133 xmlBufferFree(buf);
02134
02135 pg_xml_done(xmlerrcxt, true);
02136
02137 PG_RE_THROW();
02138 }
02139 PG_END_TRY();
02140
02141 xmlBufferFree(buf);
02142
02143 pg_xml_done(xmlerrcxt, false);
02144
02145 return result;
02146 }
02147 #endif
02148
02149 }
02150
02151
02152
02153
02154 getTypeOutputInfo(type, &typeOut, &isvarlena);
02155 str = OidOutputFunctionCall(typeOut, value);
02156
02157
02158 if (type == XMLOID || !xml_escape_strings)
02159 return str;
02160
02161
02162 return escape_xml(str);
02163 }
02164 }
02165
02166
02167
02168
02169
02170
02171
02172
02173
02174 char *
02175 escape_xml(const char *str)
02176 {
02177 StringInfoData buf;
02178 const char *p;
02179
02180 initStringInfo(&buf);
02181 for (p = str; *p; p++)
02182 {
02183 switch (*p)
02184 {
02185 case '&':
02186 appendStringInfoString(&buf, "&");
02187 break;
02188 case '<':
02189 appendStringInfoString(&buf, "<");
02190 break;
02191 case '>':
02192 appendStringInfoString(&buf, ">");
02193 break;
02194 case '\r':
02195 appendStringInfoString(&buf, "
");
02196 break;
02197 default:
02198 appendStringInfoCharMacro(&buf, *p);
02199 break;
02200 }
02201 }
02202 return buf.data;
02203 }
02204
02205
02206 static char *
02207 _SPI_strdup(const char *s)
02208 {
02209 size_t len = strlen(s) + 1;
02210 char *ret = SPI_palloc(len);
02211
02212 memcpy(ret, s, len);
02213 return ret;
02214 }
02215
02216
02217
02218
02219
02220
02221
02222
02223
02224
02225
02226
02227
02228
02229
02230
02231
02232
02233
02234
02235
02236
02237
02238
02239
02240
02241
02242
02243
02244
02245
02246
02247
02248
02249
02250
02251
02252
02253
02254
02255
02256
02257
02258
02259
02260
02261
02262
02263 static List *
02264 query_to_oid_list(const char *query)
02265 {
02266 int i;
02267 List *list = NIL;
02268
02269 SPI_execute(query, true, 0);
02270
02271 for (i = 0; i < SPI_processed; i++)
02272 {
02273 Datum oid;
02274 bool isnull;
02275
02276 oid = SPI_getbinval(SPI_tuptable->vals[i],
02277 SPI_tuptable->tupdesc,
02278 1,
02279 &isnull);
02280 if (!isnull)
02281 list = lappend_oid(list, DatumGetObjectId(oid));
02282 }
02283
02284 return list;
02285 }
02286
02287
02288 static List *
02289 schema_get_xml_visible_tables(Oid nspid)
02290 {
02291 StringInfoData query;
02292
02293 initStringInfo(&query);
02294 appendStringInfo(&query, "SELECT oid FROM pg_catalog.pg_class WHERE relnamespace = %u AND relkind IN ('r', 'm', 'v') AND pg_catalog.has_table_privilege (oid, 'SELECT') ORDER BY relname;", nspid);
02295
02296 return query_to_oid_list(query.data);
02297 }
02298
02299
02300
02301
02302
02303
02304 #define XML_VISIBLE_SCHEMAS_EXCLUDE "(nspname ~ '^pg_' OR nspname = 'information_schema')"
02305
02306 #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
02307
02308
02309 static List *
02310 database_get_xml_visible_schemas(void)
02311 {
02312 return query_to_oid_list(XML_VISIBLE_SCHEMAS " ORDER BY nspname;");
02313 }
02314
02315
02316 static List *
02317 database_get_xml_visible_tables(void)
02318 {
02319
02320 return query_to_oid_list("SELECT oid FROM pg_catalog.pg_class WHERE relkind IN ('r', 'm', 'v') AND pg_catalog.has_table_privilege (pg_class.oid, 'SELECT') AND relnamespace IN (" XML_VISIBLE_SCHEMAS ");");
02321 }
02322
02323
02324
02325
02326
02327
02328
02329 static StringInfo
02330 table_to_xml_internal(Oid relid,
02331 const char *xmlschema, bool nulls, bool tableforest,
02332 const char *targetns, bool top_level)
02333 {
02334 StringInfoData query;
02335
02336 initStringInfo(&query);
02337 appendStringInfo(&query, "SELECT * FROM %s",
02338 DatumGetCString(DirectFunctionCall1(regclassout,
02339 ObjectIdGetDatum(relid))));
02340 return query_to_xml_internal(query.data, get_rel_name(relid),
02341 xmlschema, nulls, tableforest,
02342 targetns, top_level);
02343 }
02344
02345
02346 Datum
02347 table_to_xml(PG_FUNCTION_ARGS)
02348 {
02349 Oid relid = PG_GETARG_OID(0);
02350 bool nulls = PG_GETARG_BOOL(1);
02351 bool tableforest = PG_GETARG_BOOL(2);
02352 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02353
02354 PG_RETURN_XML_P(stringinfo_to_xmltype(table_to_xml_internal(relid, NULL,
02355 nulls, tableforest,
02356 targetns, true)));
02357 }
02358
02359
02360 Datum
02361 query_to_xml(PG_FUNCTION_ARGS)
02362 {
02363 char *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
02364 bool nulls = PG_GETARG_BOOL(1);
02365 bool tableforest = PG_GETARG_BOOL(2);
02366 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02367
02368 PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL,
02369 NULL, nulls, tableforest,
02370 targetns, true)));
02371 }
02372
02373
02374 Datum
02375 cursor_to_xml(PG_FUNCTION_ARGS)
02376 {
02377 char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
02378 int32 count = PG_GETARG_INT32(1);
02379 bool nulls = PG_GETARG_BOOL(2);
02380 bool tableforest = PG_GETARG_BOOL(3);
02381 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(4));
02382
02383 StringInfoData result;
02384 Portal portal;
02385 int i;
02386
02387 initStringInfo(&result);
02388
02389 SPI_connect();
02390 portal = SPI_cursor_find(name);
02391 if (portal == NULL)
02392 ereport(ERROR,
02393 (errcode(ERRCODE_UNDEFINED_CURSOR),
02394 errmsg("cursor \"%s\" does not exist", name)));
02395
02396 SPI_cursor_fetch(portal, true, count);
02397 for (i = 0; i < SPI_processed; i++)
02398 SPI_sql_row_to_xmlelement(i, &result, NULL, nulls,
02399 tableforest, targetns, true);
02400
02401 SPI_finish();
02402
02403 PG_RETURN_XML_P(stringinfo_to_xmltype(&result));
02404 }
02405
02406
02407
02408
02409
02410
02411
02412
02413
02414
02415
02416
02417
02418
02419 static void
02420 xmldata_root_element_start(StringInfo result, const char *eltname,
02421 const char *xmlschema, const char *targetns,
02422 bool top_level)
02423 {
02424
02425 Assert(top_level || !xmlschema);
02426
02427 appendStringInfo(result, "<%s", eltname);
02428 if (top_level)
02429 {
02430 appendStringInfoString(result, " xmlns:xsi=\"" NAMESPACE_XSI "\"");
02431 if (strlen(targetns) > 0)
02432 appendStringInfo(result, " xmlns=\"%s\"", targetns);
02433 }
02434 if (xmlschema)
02435 {
02436
02437 if (strlen(targetns) > 0)
02438 appendStringInfo(result, " xsi:schemaLocation=\"%s #\"", targetns);
02439 else
02440 appendStringInfo(result, " xsi:noNamespaceSchemaLocation=\"#\"");
02441 }
02442 appendStringInfo(result, ">\n");
02443 }
02444
02445
02446 static void
02447 xmldata_root_element_end(StringInfo result, const char *eltname)
02448 {
02449 appendStringInfo(result, "</%s>\n", eltname);
02450 }
02451
02452
02453 static StringInfo
02454 query_to_xml_internal(const char *query, char *tablename,
02455 const char *xmlschema, bool nulls, bool tableforest,
02456 const char *targetns, bool top_level)
02457 {
02458 StringInfo result;
02459 char *xmltn;
02460 int i;
02461
02462 if (tablename)
02463 xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
02464 else
02465 xmltn = "table";
02466
02467 result = makeStringInfo();
02468
02469 SPI_connect();
02470 if (SPI_execute(query, true, 0) != SPI_OK_SELECT)
02471 ereport(ERROR,
02472 (errcode(ERRCODE_DATA_EXCEPTION),
02473 errmsg("invalid query")));
02474
02475 if (!tableforest)
02476 {
02477 xmldata_root_element_start(result, xmltn, xmlschema,
02478 targetns, top_level);
02479 appendStringInfoString(result, "\n");
02480 }
02481
02482 if (xmlschema)
02483 appendStringInfo(result, "%s\n\n", xmlschema);
02484
02485 for (i = 0; i < SPI_processed; i++)
02486 SPI_sql_row_to_xmlelement(i, result, tablename, nulls,
02487 tableforest, targetns, top_level);
02488
02489 if (!tableforest)
02490 xmldata_root_element_end(result, xmltn);
02491
02492 SPI_finish();
02493
02494 return result;
02495 }
02496
02497
02498 Datum
02499 table_to_xmlschema(PG_FUNCTION_ARGS)
02500 {
02501 Oid relid = PG_GETARG_OID(0);
02502 bool nulls = PG_GETARG_BOOL(1);
02503 bool tableforest = PG_GETARG_BOOL(2);
02504 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02505 const char *result;
02506 Relation rel;
02507
02508 rel = heap_open(relid, AccessShareLock);
02509 result = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls,
02510 tableforest, targetns);
02511 heap_close(rel, NoLock);
02512
02513 PG_RETURN_XML_P(cstring_to_xmltype(result));
02514 }
02515
02516
02517 Datum
02518 query_to_xmlschema(PG_FUNCTION_ARGS)
02519 {
02520 char *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
02521 bool nulls = PG_GETARG_BOOL(1);
02522 bool tableforest = PG_GETARG_BOOL(2);
02523 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02524 const char *result;
02525 SPIPlanPtr plan;
02526 Portal portal;
02527
02528 SPI_connect();
02529
02530 if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
02531 elog(ERROR, "SPI_prepare(\"%s\") failed", query);
02532
02533 if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
02534 elog(ERROR, "SPI_cursor_open(\"%s\") failed", query);
02535
02536 result = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
02537 InvalidOid, nulls,
02538 tableforest, targetns));
02539 SPI_cursor_close(portal);
02540 SPI_finish();
02541
02542 PG_RETURN_XML_P(cstring_to_xmltype(result));
02543 }
02544
02545
02546 Datum
02547 cursor_to_xmlschema(PG_FUNCTION_ARGS)
02548 {
02549 char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
02550 bool nulls = PG_GETARG_BOOL(1);
02551 bool tableforest = PG_GETARG_BOOL(2);
02552 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02553 const char *xmlschema;
02554 Portal portal;
02555
02556 SPI_connect();
02557 portal = SPI_cursor_find(name);
02558 if (portal == NULL)
02559 ereport(ERROR,
02560 (errcode(ERRCODE_UNDEFINED_CURSOR),
02561 errmsg("cursor \"%s\" does not exist", name)));
02562
02563 xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
02564 InvalidOid, nulls,
02565 tableforest, targetns));
02566 SPI_finish();
02567
02568 PG_RETURN_XML_P(cstring_to_xmltype(xmlschema));
02569 }
02570
02571
02572 Datum
02573 table_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
02574 {
02575 Oid relid = PG_GETARG_OID(0);
02576 bool nulls = PG_GETARG_BOOL(1);
02577 bool tableforest = PG_GETARG_BOOL(2);
02578 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02579 Relation rel;
02580 const char *xmlschema;
02581
02582 rel = heap_open(relid, AccessShareLock);
02583 xmlschema = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls,
02584 tableforest, targetns);
02585 heap_close(rel, NoLock);
02586
02587 PG_RETURN_XML_P(stringinfo_to_xmltype(table_to_xml_internal(relid,
02588 xmlschema, nulls, tableforest,
02589 targetns, true)));
02590 }
02591
02592
02593 Datum
02594 query_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
02595 {
02596 char *query = text_to_cstring(PG_GETARG_TEXT_PP(0));
02597 bool nulls = PG_GETARG_BOOL(1);
02598 bool tableforest = PG_GETARG_BOOL(2);
02599 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02600
02601 const char *xmlschema;
02602 SPIPlanPtr plan;
02603 Portal portal;
02604
02605 SPI_connect();
02606
02607 if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
02608 elog(ERROR, "SPI_prepare(\"%s\") failed", query);
02609
02610 if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
02611 elog(ERROR, "SPI_cursor_open(\"%s\") failed", query);
02612
02613 xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc,
02614 InvalidOid, nulls, tableforest, targetns));
02615 SPI_cursor_close(portal);
02616 SPI_finish();
02617
02618 PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL,
02619 xmlschema, nulls, tableforest,
02620 targetns, true)));
02621 }
02622
02623
02624
02625
02626
02627
02628
02629 static StringInfo
02630 schema_to_xml_internal(Oid nspid, const char *xmlschema, bool nulls,
02631 bool tableforest, const char *targetns, bool top_level)
02632 {
02633 StringInfo result;
02634 char *xmlsn;
02635 List *relid_list;
02636 ListCell *cell;
02637
02638 xmlsn = map_sql_identifier_to_xml_name(get_namespace_name(nspid),
02639 true, false);
02640 result = makeStringInfo();
02641
02642 xmldata_root_element_start(result, xmlsn, xmlschema, targetns, top_level);
02643 appendStringInfoString(result, "\n");
02644
02645 if (xmlschema)
02646 appendStringInfo(result, "%s\n\n", xmlschema);
02647
02648 SPI_connect();
02649
02650 relid_list = schema_get_xml_visible_tables(nspid);
02651
02652 SPI_push();
02653
02654 foreach(cell, relid_list)
02655 {
02656 Oid relid = lfirst_oid(cell);
02657 StringInfo subres;
02658
02659 subres = table_to_xml_internal(relid, NULL, nulls, tableforest,
02660 targetns, false);
02661
02662 appendStringInfoString(result, subres->data);
02663 appendStringInfoChar(result, '\n');
02664 }
02665
02666 SPI_pop();
02667 SPI_finish();
02668
02669 xmldata_root_element_end(result, xmlsn);
02670
02671 return result;
02672 }
02673
02674
02675 Datum
02676 schema_to_xml(PG_FUNCTION_ARGS)
02677 {
02678 Name name = PG_GETARG_NAME(0);
02679 bool nulls = PG_GETARG_BOOL(1);
02680 bool tableforest = PG_GETARG_BOOL(2);
02681 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02682
02683 char *schemaname;
02684 Oid nspid;
02685
02686 schemaname = NameStr(*name);
02687 nspid = LookupExplicitNamespace(schemaname, false);
02688
02689 PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xml_internal(nspid, NULL,
02690 nulls, tableforest, targetns, true)));
02691 }
02692
02693
02694
02695
02696
02697 static void
02698 xsd_schema_element_start(StringInfo result, const char *targetns)
02699 {
02700 appendStringInfoString(result,
02701 "<xsd:schema\n"
02702 " xmlns:xsd=\"" NAMESPACE_XSD "\"");
02703 if (strlen(targetns) > 0)
02704 appendStringInfo(result,
02705 "\n"
02706 " targetNamespace=\"%s\"\n"
02707 " elementFormDefault=\"qualified\"",
02708 targetns);
02709 appendStringInfoString(result,
02710 ">\n\n");
02711 }
02712
02713
02714 static void
02715 xsd_schema_element_end(StringInfo result)
02716 {
02717 appendStringInfoString(result, "</xsd:schema>");
02718 }
02719
02720
02721 static StringInfo
02722 schema_to_xmlschema_internal(const char *schemaname, bool nulls,
02723 bool tableforest, const char *targetns)
02724 {
02725 Oid nspid;
02726 List *relid_list;
02727 List *tupdesc_list;
02728 ListCell *cell;
02729 StringInfo result;
02730
02731 result = makeStringInfo();
02732
02733 nspid = LookupExplicitNamespace(schemaname, false);
02734
02735 xsd_schema_element_start(result, targetns);
02736
02737 SPI_connect();
02738
02739 relid_list = schema_get_xml_visible_tables(nspid);
02740
02741 tupdesc_list = NIL;
02742 foreach(cell, relid_list)
02743 {
02744 Relation rel;
02745
02746 rel = heap_open(lfirst_oid(cell), AccessShareLock);
02747 tupdesc_list = lappend(tupdesc_list, CreateTupleDescCopy(rel->rd_att));
02748 heap_close(rel, NoLock);
02749 }
02750
02751 appendStringInfoString(result,
02752 map_sql_typecoll_to_xmlschema_types(tupdesc_list));
02753
02754 appendStringInfoString(result,
02755 map_sql_schema_to_xmlschema_types(nspid, relid_list,
02756 nulls, tableforest, targetns));
02757
02758 xsd_schema_element_end(result);
02759
02760 SPI_finish();
02761
02762 return result;
02763 }
02764
02765
02766 Datum
02767 schema_to_xmlschema(PG_FUNCTION_ARGS)
02768 {
02769 Name name = PG_GETARG_NAME(0);
02770 bool nulls = PG_GETARG_BOOL(1);
02771 bool tableforest = PG_GETARG_BOOL(2);
02772 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02773
02774 PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xmlschema_internal(NameStr(*name),
02775 nulls, tableforest, targetns)));
02776 }
02777
02778
02779 Datum
02780 schema_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
02781 {
02782 Name name = PG_GETARG_NAME(0);
02783 bool nulls = PG_GETARG_BOOL(1);
02784 bool tableforest = PG_GETARG_BOOL(2);
02785 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(3));
02786 char *schemaname;
02787 Oid nspid;
02788 StringInfo xmlschema;
02789
02790 schemaname = NameStr(*name);
02791 nspid = LookupExplicitNamespace(schemaname, false);
02792
02793 xmlschema = schema_to_xmlschema_internal(schemaname, nulls,
02794 tableforest, targetns);
02795
02796 PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xml_internal(nspid,
02797 xmlschema->data, nulls,
02798 tableforest, targetns, true)));
02799 }
02800
02801
02802
02803
02804
02805
02806
02807 static StringInfo
02808 database_to_xml_internal(const char *xmlschema, bool nulls,
02809 bool tableforest, const char *targetns)
02810 {
02811 StringInfo result;
02812 List *nspid_list;
02813 ListCell *cell;
02814 char *xmlcn;
02815
02816 xmlcn = map_sql_identifier_to_xml_name(get_database_name(MyDatabaseId),
02817 true, false);
02818 result = makeStringInfo();
02819
02820 xmldata_root_element_start(result, xmlcn, xmlschema, targetns, true);
02821 appendStringInfoString(result, "\n");
02822
02823 if (xmlschema)
02824 appendStringInfo(result, "%s\n\n", xmlschema);
02825
02826 SPI_connect();
02827
02828 nspid_list = database_get_xml_visible_schemas();
02829
02830 SPI_push();
02831
02832 foreach(cell, nspid_list)
02833 {
02834 Oid nspid = lfirst_oid(cell);
02835 StringInfo subres;
02836
02837 subres = schema_to_xml_internal(nspid, NULL, nulls,
02838 tableforest, targetns, false);
02839
02840 appendStringInfoString(result, subres->data);
02841 appendStringInfoChar(result, '\n');
02842 }
02843
02844 SPI_pop();
02845 SPI_finish();
02846
02847 xmldata_root_element_end(result, xmlcn);
02848
02849 return result;
02850 }
02851
02852
02853 Datum
02854 database_to_xml(PG_FUNCTION_ARGS)
02855 {
02856 bool nulls = PG_GETARG_BOOL(0);
02857 bool tableforest = PG_GETARG_BOOL(1);
02858 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
02859
02860 PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xml_internal(NULL, nulls,
02861 tableforest, targetns)));
02862 }
02863
02864
02865 static StringInfo
02866 database_to_xmlschema_internal(bool nulls, bool tableforest,
02867 const char *targetns)
02868 {
02869 List *relid_list;
02870 List *nspid_list;
02871 List *tupdesc_list;
02872 ListCell *cell;
02873 StringInfo result;
02874
02875 result = makeStringInfo();
02876
02877 xsd_schema_element_start(result, targetns);
02878
02879 SPI_connect();
02880
02881 relid_list = database_get_xml_visible_tables();
02882 nspid_list = database_get_xml_visible_schemas();
02883
02884 tupdesc_list = NIL;
02885 foreach(cell, relid_list)
02886 {
02887 Relation rel;
02888
02889 rel = heap_open(lfirst_oid(cell), AccessShareLock);
02890 tupdesc_list = lappend(tupdesc_list, CreateTupleDescCopy(rel->rd_att));
02891 heap_close(rel, NoLock);
02892 }
02893
02894 appendStringInfoString(result,
02895 map_sql_typecoll_to_xmlschema_types(tupdesc_list));
02896
02897 appendStringInfoString(result,
02898 map_sql_catalog_to_xmlschema_types(nspid_list, nulls, tableforest, targetns));
02899
02900 xsd_schema_element_end(result);
02901
02902 SPI_finish();
02903
02904 return result;
02905 }
02906
02907
02908 Datum
02909 database_to_xmlschema(PG_FUNCTION_ARGS)
02910 {
02911 bool nulls = PG_GETARG_BOOL(0);
02912 bool tableforest = PG_GETARG_BOOL(1);
02913 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
02914
02915 PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xmlschema_internal(nulls,
02916 tableforest, targetns)));
02917 }
02918
02919
02920 Datum
02921 database_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
02922 {
02923 bool nulls = PG_GETARG_BOOL(0);
02924 bool tableforest = PG_GETARG_BOOL(1);
02925 const char *targetns = text_to_cstring(PG_GETARG_TEXT_PP(2));
02926 StringInfo xmlschema;
02927
02928 xmlschema = database_to_xmlschema_internal(nulls, tableforest, targetns);
02929
02930 PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xml_internal(xmlschema->data,
02931 nulls, tableforest, targetns)));
02932 }
02933
02934
02935
02936
02937
02938
02939 static char *
02940 map_multipart_sql_identifier_to_xml_name(char *a, char *b, char *c, char *d)
02941 {
02942 StringInfoData result;
02943
02944 initStringInfo(&result);
02945
02946 if (a)
02947 appendStringInfo(&result, "%s",
02948 map_sql_identifier_to_xml_name(a, true, true));
02949 if (b)
02950 appendStringInfo(&result, ".%s",
02951 map_sql_identifier_to_xml_name(b, true, true));
02952 if (c)
02953 appendStringInfo(&result, ".%s",
02954 map_sql_identifier_to_xml_name(c, true, true));
02955 if (d)
02956 appendStringInfo(&result, ".%s",
02957 map_sql_identifier_to_xml_name(d, true, true));
02958
02959 return result.data;
02960 }
02961
02962
02963
02964
02965
02966
02967
02968
02969
02970 static const char *
02971 map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls,
02972 bool tableforest, const char *targetns)
02973 {
02974 int i;
02975 char *xmltn;
02976 char *tabletypename;
02977 char *rowtypename;
02978 StringInfoData result;
02979
02980 initStringInfo(&result);
02981
02982 if (OidIsValid(relid))
02983 {
02984 HeapTuple tuple;
02985 Form_pg_class reltuple;
02986
02987 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
02988 if (!HeapTupleIsValid(tuple))
02989 elog(ERROR, "cache lookup failed for relation %u", relid);
02990 reltuple = (Form_pg_class) GETSTRUCT(tuple);
02991
02992 xmltn = map_sql_identifier_to_xml_name(NameStr(reltuple->relname),
02993 true, false);
02994
02995 tabletypename = map_multipart_sql_identifier_to_xml_name("TableType",
02996 get_database_name(MyDatabaseId),
02997 get_namespace_name(reltuple->relnamespace),
02998 NameStr(reltuple->relname));
02999
03000 rowtypename = map_multipart_sql_identifier_to_xml_name("RowType",
03001 get_database_name(MyDatabaseId),
03002 get_namespace_name(reltuple->relnamespace),
03003 NameStr(reltuple->relname));
03004
03005 ReleaseSysCache(tuple);
03006 }
03007 else
03008 {
03009 if (tableforest)
03010 xmltn = "row";
03011 else
03012 xmltn = "table";
03013
03014 tabletypename = "TableType";
03015 rowtypename = "RowType";
03016 }
03017
03018 xsd_schema_element_start(&result, targetns);
03019
03020 appendStringInfoString(&result,
03021 map_sql_typecoll_to_xmlschema_types(list_make1(tupdesc)));
03022
03023 appendStringInfo(&result,
03024 "<xsd:complexType name=\"%s\">\n"
03025 " <xsd:sequence>\n",
03026 rowtypename);
03027
03028 for (i = 0; i < tupdesc->natts; i++)
03029 {
03030 if (tupdesc->attrs[i]->attisdropped)
03031 continue;
03032 appendStringInfo(&result,
03033 " <xsd:element name=\"%s\" type=\"%s\"%s></xsd:element>\n",
03034 map_sql_identifier_to_xml_name(NameStr(tupdesc->attrs[i]->attname),
03035 true, false),
03036 map_sql_type_to_xml_name(tupdesc->attrs[i]->atttypid, -1),
03037 nulls ? " nillable=\"true\"" : " minOccurs=\"0\"");
03038 }
03039
03040 appendStringInfoString(&result,
03041 " </xsd:sequence>\n"
03042 "</xsd:complexType>\n\n");
03043
03044 if (!tableforest)
03045 {
03046 appendStringInfo(&result,
03047 "<xsd:complexType name=\"%s\">\n"
03048 " <xsd:sequence>\n"
03049 " <xsd:element name=\"row\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n"
03050 " </xsd:sequence>\n"
03051 "</xsd:complexType>\n\n",
03052 tabletypename, rowtypename);
03053
03054 appendStringInfo(&result,
03055 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
03056 xmltn, tabletypename);
03057 }
03058 else
03059 appendStringInfo(&result,
03060 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
03061 xmltn, rowtypename);
03062
03063 xsd_schema_element_end(&result);
03064
03065 return result.data;
03066 }
03067
03068
03069
03070
03071
03072
03073 static const char *
03074 map_sql_schema_to_xmlschema_types(Oid nspid, List *relid_list, bool nulls,
03075 bool tableforest, const char *targetns)
03076 {
03077 char *dbname;
03078 char *nspname;
03079 char *xmlsn;
03080 char *schematypename;
03081 StringInfoData result;
03082 ListCell *cell;
03083
03084 dbname = get_database_name(MyDatabaseId);
03085 nspname = get_namespace_name(nspid);
03086
03087 initStringInfo(&result);
03088
03089 xmlsn = map_sql_identifier_to_xml_name(nspname, true, false);
03090
03091 schematypename = map_multipart_sql_identifier_to_xml_name("SchemaType",
03092 dbname,
03093 nspname,
03094 NULL);
03095
03096 appendStringInfo(&result,
03097 "<xsd:complexType name=\"%s\">\n", schematypename);
03098 if (!tableforest)
03099 appendStringInfoString(&result,
03100 " <xsd:all>\n");
03101 else
03102 appendStringInfoString(&result,
03103 " <xsd:sequence>\n");
03104
03105 foreach(cell, relid_list)
03106 {
03107 Oid relid = lfirst_oid(cell);
03108 char *relname = get_rel_name(relid);
03109 char *xmltn = map_sql_identifier_to_xml_name(relname, true, false);
03110 char *tabletypename = map_multipart_sql_identifier_to_xml_name(tableforest ? "RowType" : "TableType",
03111 dbname,
03112 nspname,
03113 relname);
03114
03115 if (!tableforest)
03116 appendStringInfo(&result,
03117 " <xsd:element name=\"%s\" type=\"%s\"/>\n",
03118 xmltn, tabletypename);
03119 else
03120 appendStringInfo(&result,
03121 " <xsd:element name=\"%s\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n",
03122 xmltn, tabletypename);
03123 }
03124
03125 if (!tableforest)
03126 appendStringInfoString(&result,
03127 " </xsd:all>\n");
03128 else
03129 appendStringInfoString(&result,
03130 " </xsd:sequence>\n");
03131 appendStringInfoString(&result,
03132 "</xsd:complexType>\n\n");
03133
03134 appendStringInfo(&result,
03135 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
03136 xmlsn, schematypename);
03137
03138 return result.data;
03139 }
03140
03141
03142
03143
03144
03145
03146 static const char *
03147 map_sql_catalog_to_xmlschema_types(List *nspid_list, bool nulls,
03148 bool tableforest, const char *targetns)
03149 {
03150 char *dbname;
03151 char *xmlcn;
03152 char *catalogtypename;
03153 StringInfoData result;
03154 ListCell *cell;
03155
03156 dbname = get_database_name(MyDatabaseId);
03157
03158 initStringInfo(&result);
03159
03160 xmlcn = map_sql_identifier_to_xml_name(dbname, true, false);
03161
03162 catalogtypename = map_multipart_sql_identifier_to_xml_name("CatalogType",
03163 dbname,
03164 NULL,
03165 NULL);
03166
03167 appendStringInfo(&result,
03168 "<xsd:complexType name=\"%s\">\n", catalogtypename);
03169 appendStringInfoString(&result,
03170 " <xsd:all>\n");
03171
03172 foreach(cell, nspid_list)
03173 {
03174 Oid nspid = lfirst_oid(cell);
03175 char *nspname = get_namespace_name(nspid);
03176 char *xmlsn = map_sql_identifier_to_xml_name(nspname, true, false);
03177 char *schematypename = map_multipart_sql_identifier_to_xml_name("SchemaType",
03178 dbname,
03179 nspname,
03180 NULL);
03181
03182 appendStringInfo(&result,
03183 " <xsd:element name=\"%s\" type=\"%s\"/>\n",
03184 xmlsn, schematypename);
03185 }
03186
03187 appendStringInfoString(&result,
03188 " </xsd:all>\n");
03189 appendStringInfoString(&result,
03190 "</xsd:complexType>\n\n");
03191
03192 appendStringInfo(&result,
03193 "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
03194 xmlcn, catalogtypename);
03195
03196 return result.data;
03197 }
03198
03199
03200
03201
03202
03203 static const char *
03204 map_sql_type_to_xml_name(Oid typeoid, int typmod)
03205 {
03206 StringInfoData result;
03207
03208 initStringInfo(&result);
03209
03210 switch (typeoid)
03211 {
03212 case BPCHAROID:
03213 if (typmod == -1)
03214 appendStringInfo(&result, "CHAR");
03215 else
03216 appendStringInfo(&result, "CHAR_%d", typmod - VARHDRSZ);
03217 break;
03218 case VARCHAROID:
03219 if (typmod == -1)
03220 appendStringInfo(&result, "VARCHAR");
03221 else
03222 appendStringInfo(&result, "VARCHAR_%d", typmod - VARHDRSZ);
03223 break;
03224 case NUMERICOID:
03225 if (typmod == -1)
03226 appendStringInfo(&result, "NUMERIC");
03227 else
03228 appendStringInfo(&result, "NUMERIC_%d_%d",
03229 ((typmod - VARHDRSZ) >> 16) & 0xffff,
03230 (typmod - VARHDRSZ) & 0xffff);
03231 break;
03232 case INT4OID:
03233 appendStringInfo(&result, "INTEGER");
03234 break;
03235 case INT2OID:
03236 appendStringInfo(&result, "SMALLINT");
03237 break;
03238 case INT8OID:
03239 appendStringInfo(&result, "BIGINT");
03240 break;
03241 case FLOAT4OID:
03242 appendStringInfo(&result, "REAL");
03243 break;
03244 case FLOAT8OID:
03245 appendStringInfo(&result, "DOUBLE");
03246 break;
03247 case BOOLOID:
03248 appendStringInfo(&result, "BOOLEAN");
03249 break;
03250 case TIMEOID:
03251 if (typmod == -1)
03252 appendStringInfo(&result, "TIME");
03253 else
03254 appendStringInfo(&result, "TIME_%d", typmod);
03255 break;
03256 case TIMETZOID:
03257 if (typmod == -1)
03258 appendStringInfo(&result, "TIME_WTZ");
03259 else
03260 appendStringInfo(&result, "TIME_WTZ_%d", typmod);
03261 break;
03262 case TIMESTAMPOID:
03263 if (typmod == -1)
03264 appendStringInfo(&result, "TIMESTAMP");
03265 else
03266 appendStringInfo(&result, "TIMESTAMP_%d", typmod);
03267 break;
03268 case TIMESTAMPTZOID:
03269 if (typmod == -1)
03270 appendStringInfo(&result, "TIMESTAMP_WTZ");
03271 else
03272 appendStringInfo(&result, "TIMESTAMP_WTZ_%d", typmod);
03273 break;
03274 case DATEOID:
03275 appendStringInfo(&result, "DATE");
03276 break;
03277 case XMLOID:
03278 appendStringInfo(&result, "XML");
03279 break;
03280 default:
03281 {
03282 HeapTuple tuple;
03283 Form_pg_type typtuple;
03284
03285 tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeoid));
03286 if (!HeapTupleIsValid(tuple))
03287 elog(ERROR, "cache lookup failed for type %u", typeoid);
03288 typtuple = (Form_pg_type) GETSTRUCT(tuple);
03289
03290 appendStringInfoString(&result,
03291 map_multipart_sql_identifier_to_xml_name((typtuple->typtype == TYPTYPE_DOMAIN) ? "Domain" : "UDT",
03292 get_database_name(MyDatabaseId),
03293 get_namespace_name(typtuple->typnamespace),
03294 NameStr(typtuple->typname)));
03295
03296 ReleaseSysCache(tuple);
03297 }
03298 }
03299
03300 return result.data;
03301 }
03302
03303
03304
03305
03306
03307
03308 static const char *
03309 map_sql_typecoll_to_xmlschema_types(List *tupdesc_list)
03310 {
03311 List *uniquetypes = NIL;
03312 int i;
03313 StringInfoData result;
03314 ListCell *cell0;
03315
03316
03317 foreach(cell0, tupdesc_list)
03318 {
03319 TupleDesc tupdesc = (TupleDesc) lfirst(cell0);
03320
03321 for (i = 0; i < tupdesc->natts; i++)
03322 {
03323 if (tupdesc->attrs[i]->attisdropped)
03324 continue;
03325 uniquetypes = list_append_unique_oid(uniquetypes,
03326 tupdesc->attrs[i]->atttypid);
03327 }
03328 }
03329
03330
03331 foreach(cell0, uniquetypes)
03332 {
03333 Oid typid = lfirst_oid(cell0);
03334 Oid basetypid = getBaseType(typid);
03335
03336 if (basetypid != typid)
03337 uniquetypes = list_append_unique_oid(uniquetypes, basetypid);
03338 }
03339
03340
03341 initStringInfo(&result);
03342
03343 foreach(cell0, uniquetypes)
03344 {
03345 appendStringInfo(&result, "%s\n",
03346 map_sql_type_to_xmlschema_type(lfirst_oid(cell0),
03347 -1));
03348 }
03349
03350 return result.data;
03351 }
03352
03353
03354
03355
03356
03357
03358
03359
03360
03361
03362 static const char *
03363 map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
03364 {
03365 StringInfoData result;
03366 const char *typename = map_sql_type_to_xml_name(typeoid, typmod);
03367
03368 initStringInfo(&result);
03369
03370 if (typeoid == XMLOID)
03371 {
03372 appendStringInfo(&result,
03373 "<xsd:complexType mixed=\"true\">\n"
03374 " <xsd:sequence>\n"
03375 " <xsd:any name=\"element\" minOccurs=\"0\" maxOccurs=\"unbounded\" processContents=\"skip\"/>\n"
03376 " </xsd:sequence>\n"
03377 "</xsd:complexType>\n");
03378 }
03379 else
03380 {
03381 appendStringInfo(&result,
03382 "<xsd:simpleType name=\"%s\">\n", typename);
03383
03384 switch (typeoid)
03385 {
03386 case BPCHAROID:
03387 case VARCHAROID:
03388 case TEXTOID:
03389 appendStringInfo(&result,
03390 " <xsd:restriction base=\"xsd:string\">\n");
03391 if (typmod != -1)
03392 appendStringInfo(&result,
03393 " <xsd:maxLength value=\"%d\"/>\n",
03394 typmod - VARHDRSZ);
03395 appendStringInfo(&result,
03396 " </xsd:restriction>\n");
03397 break;
03398
03399 case BYTEAOID:
03400 appendStringInfo(&result,
03401 " <xsd:restriction base=\"xsd:%s\">\n"
03402 " </xsd:restriction>\n",
03403 xmlbinary == XMLBINARY_BASE64 ? "base64Binary" : "hexBinary");
03404 break;
03405
03406 case NUMERICOID:
03407 if (typmod != -1)
03408 appendStringInfo(&result,
03409 " <xsd:restriction base=\"xsd:decimal\">\n"
03410 " <xsd:totalDigits value=\"%d\"/>\n"
03411 " <xsd:fractionDigits value=\"%d\"/>\n"
03412 " </xsd:restriction>\n",
03413 ((typmod - VARHDRSZ) >> 16) & 0xffff,
03414 (typmod - VARHDRSZ) & 0xffff);
03415 break;
03416
03417 case INT2OID:
03418 appendStringInfo(&result,
03419 " <xsd:restriction base=\"xsd:short\">\n"
03420 " <xsd:maxInclusive value=\"%d\"/>\n"
03421 " <xsd:minInclusive value=\"%d\"/>\n"
03422 " </xsd:restriction>\n",
03423 SHRT_MAX, SHRT_MIN);
03424 break;
03425
03426 case INT4OID:
03427 appendStringInfo(&result,
03428 " <xsd:restriction base=\"xsd:int\">\n"
03429 " <xsd:maxInclusive value=\"%d\"/>\n"
03430 " <xsd:minInclusive value=\"%d\"/>\n"
03431 " </xsd:restriction>\n",
03432 INT_MAX, INT_MIN);
03433 break;
03434
03435 case INT8OID:
03436 appendStringInfo(&result,
03437 " <xsd:restriction base=\"xsd:long\">\n"
03438 " <xsd:maxInclusive value=\"" INT64_FORMAT "\"/>\n"
03439 " <xsd:minInclusive value=\"" INT64_FORMAT "\"/>\n"
03440 " </xsd:restriction>\n",
03441 (((uint64) 1) << (sizeof(int64) * 8 - 1)) - 1,
03442 (((uint64) 1) << (sizeof(int64) * 8 - 1)));
03443 break;
03444
03445 case FLOAT4OID:
03446 appendStringInfo(&result,
03447 " <xsd:restriction base=\"xsd:float\"></xsd:restriction>\n");
03448 break;
03449
03450 case FLOAT8OID:
03451 appendStringInfo(&result,
03452 " <xsd:restriction base=\"xsd:double\"></xsd:restriction>\n");
03453 break;
03454
03455 case BOOLOID:
03456 appendStringInfo(&result,
03457 " <xsd:restriction base=\"xsd:boolean\"></xsd:restriction>\n");
03458 break;
03459
03460 case TIMEOID:
03461 case TIMETZOID:
03462 {
03463 const char *tz = (typeoid == TIMETZOID ? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
03464
03465 if (typmod == -1)
03466 appendStringInfo(&result,
03467 " <xsd:restriction base=\"xsd:time\">\n"
03468 " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}(.\\p{Nd}+)?%s\"/>\n"
03469 " </xsd:restriction>\n", tz);
03470 else if (typmod == 0)
03471 appendStringInfo(&result,
03472 " <xsd:restriction base=\"xsd:time\">\n"
03473 " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
03474 " </xsd:restriction>\n", tz);
03475 else
03476 appendStringInfo(&result,
03477 " <xsd:restriction base=\"xsd:time\">\n"
03478 " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}.\\p{Nd}{%d}%s\"/>\n"
03479 " </xsd:restriction>\n", typmod - VARHDRSZ, tz);
03480 break;
03481 }
03482
03483 case TIMESTAMPOID:
03484 case TIMESTAMPTZOID:
03485 {
03486 const char *tz = (typeoid == TIMESTAMPTZOID ? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
03487
03488 if (typmod == -1)
03489 appendStringInfo(&result,
03490 " <xsd:restriction base=\"xsd:dateTime\">\n"
03491 " <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"
03492 " </xsd:restriction>\n", tz);
03493 else if (typmod == 0)
03494 appendStringInfo(&result,
03495 " <xsd:restriction base=\"xsd:dateTime\">\n"
03496 " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}T\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
03497 " </xsd:restriction>\n", tz);
03498 else
03499 appendStringInfo(&result,
03500 " <xsd:restriction base=\"xsd:dateTime\">\n"
03501 " <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"
03502 " </xsd:restriction>\n", typmod - VARHDRSZ, tz);
03503 break;
03504 }
03505
03506 case DATEOID:
03507 appendStringInfo(&result,
03508 " <xsd:restriction base=\"xsd:date\">\n"
03509 " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}\"/>\n"
03510 " </xsd:restriction>\n");
03511 break;
03512
03513 default:
03514 if (get_typtype(typeoid) == TYPTYPE_DOMAIN)
03515 {
03516 Oid base_typeoid;
03517 int32 base_typmod = -1;
03518
03519 base_typeoid = getBaseTypeAndTypmod(typeoid, &base_typmod);
03520
03521 appendStringInfo(&result,
03522 " <xsd:restriction base=\"%s\"/>\n",
03523 map_sql_type_to_xml_name(base_typeoid, base_typmod));
03524 }
03525 break;
03526 }
03527 appendStringInfo(&result,
03528 "</xsd:simpleType>\n");
03529 }
03530
03531 return result.data;
03532 }
03533
03534
03535
03536
03537
03538
03539 static void
03540 SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename,
03541 bool nulls, bool tableforest,
03542 const char *targetns, bool top_level)
03543 {
03544 int i;
03545 char *xmltn;
03546
03547 if (tablename)
03548 xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
03549 else
03550 {
03551 if (tableforest)
03552 xmltn = "row";
03553 else
03554 xmltn = "table";
03555 }
03556
03557 if (tableforest)
03558 xmldata_root_element_start(result, xmltn, NULL, targetns, top_level);
03559 else
03560 appendStringInfoString(result, "<row>\n");
03561
03562 for (i = 1; i <= SPI_tuptable->tupdesc->natts; i++)
03563 {
03564 char *colname;
03565 Datum colval;
03566 bool isnull;
03567
03568 colname = map_sql_identifier_to_xml_name(SPI_fname(SPI_tuptable->tupdesc, i),
03569 true, false);
03570 colval = SPI_getbinval(SPI_tuptable->vals[rownum],
03571 SPI_tuptable->tupdesc,
03572 i,
03573 &isnull);
03574 if (isnull)
03575 {
03576 if (nulls)
03577 appendStringInfo(result, " <%s xsi:nil=\"true\"/>\n", colname);
03578 }
03579 else
03580 appendStringInfo(result, " <%s>%s</%s>\n",
03581 colname,
03582 map_sql_value_to_xml_value(colval,
03583 SPI_gettypeid(SPI_tuptable->tupdesc, i), true),
03584 colname);
03585 }
03586
03587 if (tableforest)
03588 {
03589 xmldata_root_element_end(result, xmltn);
03590 appendStringInfoChar(result, '\n');
03591 }
03592 else
03593 appendStringInfoString(result, "</row>\n\n");
03594 }
03595
03596
03597
03598
03599
03600
03601 #ifdef USE_LIBXML
03602
03603
03604
03605
03606
03607 static text *
03608 xml_xmlnodetoxmltype(xmlNodePtr cur)
03609 {
03610 xmltype *result;
03611
03612 if (cur->type == XML_ELEMENT_NODE)
03613 {
03614 xmlBufferPtr buf;
03615
03616 buf = xmlBufferCreate();
03617 PG_TRY();
03618 {
03619 xmlNodeDump(buf, NULL, cur, 0, 1);
03620 result = xmlBuffer_to_xmltype(buf);
03621 }
03622 PG_CATCH();
03623 {
03624 xmlBufferFree(buf);
03625 PG_RE_THROW();
03626 }
03627 PG_END_TRY();
03628 xmlBufferFree(buf);
03629 }
03630 else
03631 {
03632 xmlChar *str;
03633
03634 str = xmlXPathCastNodeToString(cur);
03635 PG_TRY();
03636 {
03637
03638 char *escaped = escape_xml((char *) str);
03639
03640 result = (xmltype *) cstring_to_text(escaped);
03641 pfree(escaped);
03642 }
03643 PG_CATCH();
03644 {
03645 xmlFree(str);
03646 PG_RE_THROW();
03647 }
03648 PG_END_TRY();
03649 xmlFree(str);
03650 }
03651
03652 return result;
03653 }
03654
03655
03656
03657
03658
03659
03660
03661
03662
03663
03664
03665
03666
03667 static int
03668 xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
03669 ArrayBuildState **astate)
03670 {
03671 int result = 0;
03672 Datum datum;
03673 Oid datumtype;
03674 char *result_str;
03675
03676 if (astate != NULL)
03677 *astate = NULL;
03678
03679 switch (xpathobj->type)
03680 {
03681 case XPATH_NODESET:
03682 if (xpathobj->nodesetval != NULL)
03683 {
03684 result = xpathobj->nodesetval->nodeNr;
03685 if (astate != NULL)
03686 {
03687 int i;
03688
03689 for (i = 0; i < result; i++)
03690 {
03691 datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i]));
03692 *astate = accumArrayResult(*astate, datum,
03693 false, XMLOID,
03694 CurrentMemoryContext);
03695 }
03696 }
03697 }
03698 return result;
03699
03700 case XPATH_BOOLEAN:
03701 if (astate == NULL)
03702 return 1;
03703 datum = BoolGetDatum(xpathobj->boolval);
03704 datumtype = BOOLOID;
03705 break;
03706
03707 case XPATH_NUMBER:
03708 if (astate == NULL)
03709 return 1;
03710 datum = Float8GetDatum(xpathobj->floatval);
03711 datumtype = FLOAT8OID;
03712 break;
03713
03714 case XPATH_STRING:
03715 if (astate == NULL)
03716 return 1;
03717 datum = CStringGetDatum((char *) xpathobj->stringval);
03718 datumtype = CSTRINGOID;
03719 break;
03720
03721 default:
03722 elog(ERROR, "xpath expression result type %d is unsupported",
03723 xpathobj->type);
03724 return 0;
03725 }
03726
03727
03728 result_str = map_sql_value_to_xml_value(datum, datumtype, true);
03729 datum = PointerGetDatum(cstring_to_xmltype(result_str));
03730 *astate = accumArrayResult(*astate, datum,
03731 false, XMLOID,
03732 CurrentMemoryContext);
03733 return 1;
03734 }
03735
03736
03737
03738
03739
03740
03741
03742
03743
03744
03745
03746
03747
03748 static void
03749 xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
03750 int *res_nitems, ArrayBuildState **astate)
03751 {
03752 PgXmlErrorContext *xmlerrcxt;
03753 volatile xmlParserCtxtPtr ctxt = NULL;
03754 volatile xmlDocPtr doc = NULL;
03755 volatile xmlXPathContextPtr xpathctx = NULL;
03756 volatile xmlXPathCompExprPtr xpathcomp = NULL;
03757 volatile xmlXPathObjectPtr xpathobj = NULL;
03758 char *datastr;
03759 int32 len;
03760 int32 xpath_len;
03761 xmlChar *string;
03762 xmlChar *xpath_expr;
03763 int i;
03764 int ndim;
03765 Datum *ns_names_uris;
03766 bool *ns_names_uris_nulls;
03767 int ns_count;
03768
03769
03770
03771
03772
03773
03774
03775
03776
03777
03778 ndim = namespaces ? ARR_NDIM(namespaces) : 0;
03779 if (ndim != 0)
03780 {
03781 int *dims;
03782
03783 dims = ARR_DIMS(namespaces);
03784
03785 if (ndim != 2 || dims[1] != 2)
03786 ereport(ERROR,
03787 (errcode(ERRCODE_DATA_EXCEPTION),
03788 errmsg("invalid array for XML namespace mapping"),
03789 errdetail("The array must be two-dimensional with length of the second axis equal to 2.")));
03790
03791 Assert(ARR_ELEMTYPE(namespaces) == TEXTOID);
03792
03793 deconstruct_array(namespaces, TEXTOID, -1, false, 'i',
03794 &ns_names_uris, &ns_names_uris_nulls,
03795 &ns_count);
03796
03797 Assert((ns_count % 2) == 0);
03798 ns_count /= 2;
03799 }
03800 else
03801 {
03802 ns_names_uris = NULL;
03803 ns_names_uris_nulls = NULL;
03804 ns_count = 0;
03805 }
03806
03807 datastr = VARDATA(data);
03808 len = VARSIZE(data) - VARHDRSZ;
03809 xpath_len = VARSIZE(xpath_expr_text) - VARHDRSZ;
03810 if (xpath_len == 0)
03811 ereport(ERROR,
03812 (errcode(ERRCODE_DATA_EXCEPTION),
03813 errmsg("empty XPath expression")));
03814
03815 string = (xmlChar *) palloc((len + 1) * sizeof(xmlChar));
03816 memcpy(string, datastr, len);
03817 string[len] = '\0';
03818
03819 xpath_expr = (xmlChar *) palloc((xpath_len + 1) * sizeof(xmlChar));
03820 memcpy(xpath_expr, VARDATA(xpath_expr_text), xpath_len);
03821 xpath_expr[xpath_len] = '\0';
03822
03823 xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
03824
03825 PG_TRY();
03826 {
03827 xmlInitParser();
03828
03829
03830
03831
03832
03833 ctxt = xmlNewParserCtxt();
03834 if (ctxt == NULL || xmlerrcxt->err_occurred)
03835 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
03836 "could not allocate parser context");
03837 doc = xmlCtxtReadMemory(ctxt, (char *) string, len, NULL, NULL, 0);
03838 if (doc == NULL || xmlerrcxt->err_occurred)
03839 xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
03840 "could not parse XML document");
03841 xpathctx = xmlXPathNewContext(doc);
03842 if (xpathctx == NULL || xmlerrcxt->err_occurred)
03843 xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
03844 "could not allocate XPath context");
03845 xpathctx->node = xmlDocGetRootElement(doc);
03846 if (xpathctx->node == NULL || xmlerrcxt->err_occurred)
03847 xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
03848 "could not find root XML element");
03849
03850
03851 if (ns_count > 0)
03852 {
03853 for (i = 0; i < ns_count; i++)
03854 {
03855 char *ns_name;
03856 char *ns_uri;
03857
03858 if (ns_names_uris_nulls[i * 2] ||
03859 ns_names_uris_nulls[i * 2 + 1])
03860 ereport(ERROR,
03861 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
03862 errmsg("neither namespace name nor URI may be null")));
03863 ns_name = TextDatumGetCString(ns_names_uris[i * 2]);
03864 ns_uri = TextDatumGetCString(ns_names_uris[i * 2 + 1]);
03865 if (xmlXPathRegisterNs(xpathctx,
03866 (xmlChar *) ns_name,
03867 (xmlChar *) ns_uri) != 0)
03868 ereport(ERROR,
03869 (errmsg("could not register XML namespace with name \"%s\" and URI \"%s\"",
03870 ns_name, ns_uri)));
03871 }
03872 }
03873
03874 xpathcomp = xmlXPathCompile(xpath_expr);
03875 if (xpathcomp == NULL || xmlerrcxt->err_occurred)
03876 xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
03877 "invalid XPath expression");
03878
03879
03880
03881
03882
03883
03884
03885
03886 xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
03887 if (xpathobj == NULL || xmlerrcxt->err_occurred)
03888 xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
03889 "could not create XPath object");
03890
03891
03892
03893
03894 if (res_nitems != NULL)
03895 *res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate);
03896 else
03897 (void) xml_xpathobjtoxmlarray(xpathobj, astate);
03898 }
03899 PG_CATCH();
03900 {
03901 if (xpathobj)
03902 xmlXPathFreeObject(xpathobj);
03903 if (xpathcomp)
03904 xmlXPathFreeCompExpr(xpathcomp);
03905 if (xpathctx)
03906 xmlXPathFreeContext(xpathctx);
03907 if (doc)
03908 xmlFreeDoc(doc);
03909 if (ctxt)
03910 xmlFreeParserCtxt(ctxt);
03911
03912 pg_xml_done(xmlerrcxt, true);
03913
03914 PG_RE_THROW();
03915 }
03916 PG_END_TRY();
03917
03918 xmlXPathFreeObject(xpathobj);
03919 xmlXPathFreeCompExpr(xpathcomp);
03920 xmlXPathFreeContext(xpathctx);
03921 xmlFreeDoc(doc);
03922 xmlFreeParserCtxt(ctxt);
03923
03924 pg_xml_done(xmlerrcxt, false);
03925 }
03926 #endif
03927
03928
03929
03930
03931
03932
03933
03934
03935 Datum
03936 xpath(PG_FUNCTION_ARGS)
03937 {
03938 #ifdef USE_LIBXML
03939 text *xpath_expr_text = PG_GETARG_TEXT_P(0);
03940 xmltype *data = PG_GETARG_XML_P(1);
03941 ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
03942 int res_nitems;
03943 ArrayBuildState *astate;
03944
03945 xpath_internal(xpath_expr_text, data, namespaces,
03946 &res_nitems, &astate);
03947
03948 if (res_nitems == 0)
03949 PG_RETURN_ARRAYTYPE_P(construct_empty_array(XMLOID));
03950 else
03951 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
03952 #else
03953 NO_XML_SUPPORT();
03954 return 0;
03955 #endif
03956 }
03957
03958
03959
03960
03961
03962 Datum
03963 xmlexists(PG_FUNCTION_ARGS)
03964 {
03965 #ifdef USE_LIBXML
03966 text *xpath_expr_text = PG_GETARG_TEXT_P(0);
03967 xmltype *data = PG_GETARG_XML_P(1);
03968 int res_nitems;
03969
03970 xpath_internal(xpath_expr_text, data, NULL,
03971 &res_nitems, NULL);
03972
03973 PG_RETURN_BOOL(res_nitems > 0);
03974 #else
03975 NO_XML_SUPPORT();
03976 return 0;
03977 #endif
03978 }
03979
03980
03981
03982
03983
03984
03985 Datum
03986 xpath_exists(PG_FUNCTION_ARGS)
03987 {
03988 #ifdef USE_LIBXML
03989 text *xpath_expr_text = PG_GETARG_TEXT_P(0);
03990 xmltype *data = PG_GETARG_XML_P(1);
03991 ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
03992 int res_nitems;
03993
03994 xpath_internal(xpath_expr_text, data, namespaces,
03995 &res_nitems, NULL);
03996
03997 PG_RETURN_BOOL(res_nitems > 0);
03998 #else
03999 NO_XML_SUPPORT();
04000 return 0;
04001 #endif
04002 }
04003
04004
04005
04006
04007
04008 #ifdef USE_LIBXML
04009 static bool
04010 wellformed_xml(text *data, XmlOptionType xmloption_arg)
04011 {
04012 bool result;
04013 volatile xmlDocPtr doc = NULL;
04014
04015
04016 PG_TRY();
04017 {
04018 doc = xml_parse(data, xmloption_arg, true, GetDatabaseEncoding());
04019 result = true;
04020 }
04021 PG_CATCH();
04022 {
04023 FlushErrorState();
04024 result = false;
04025 }
04026 PG_END_TRY();
04027
04028 if (doc)
04029 xmlFreeDoc(doc);
04030
04031 return result;
04032 }
04033 #endif
04034
04035 Datum
04036 xml_is_well_formed(PG_FUNCTION_ARGS)
04037 {
04038 #ifdef USE_LIBXML
04039 text *data = PG_GETARG_TEXT_P(0);
04040
04041 PG_RETURN_BOOL(wellformed_xml(data, xmloption));
04042 #else
04043 NO_XML_SUPPORT();
04044 return 0;
04045 #endif
04046 }
04047
04048 Datum
04049 xml_is_well_formed_document(PG_FUNCTION_ARGS)
04050 {
04051 #ifdef USE_LIBXML
04052 text *data = PG_GETARG_TEXT_P(0);
04053
04054 PG_RETURN_BOOL(wellformed_xml(data, XMLOPTION_DOCUMENT));
04055 #else
04056 NO_XML_SUPPORT();
04057 return 0;
04058 #endif
04059 }
04060
04061 Datum
04062 xml_is_well_formed_content(PG_FUNCTION_ARGS)
04063 {
04064 #ifdef USE_LIBXML
04065 text *data = PG_GETARG_TEXT_P(0);
04066
04067 PG_RETURN_BOOL(wellformed_xml(data, XMLOPTION_CONTENT));
04068 #else
04069 NO_XML_SUPPORT();
04070 return 0;
04071 #endif
04072 }