Skip to content

Commit 85f807d

Browse files
committed
Fix xmlelement() to initialize libxml correctly before using it, and to avoid
assuming that evaluation of its input expressions won't change the state of libxml. This requires refactoring xml_init() to not call xmlInitParser(), since now not all of its callers want that. I also tweaked things to avoid repeated execution of one-time-only tests inside xml_init(), though this is mostly for clarity rather than in hopes of saving any noticeable amount of runtime. Per report from Sheikh Amjad and subsequent discussion. In passing, fix a couple of inadequately schema-qualified queries.
1 parent bcb3852 commit 85f807d

File tree

1 file changed

+120
-55
lines changed
  • src/backend/utils/adt

1 file changed

+120
-55
lines changed

src/backend/utils/adt/xml.c

Lines changed: 120 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.49 2007/10/13 20:46:47 tgl Exp $
10+
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.50 2007/11/05 22:23:07 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -73,6 +73,10 @@
7373
#include "utils/xml.h"
7474

7575

76+
/* GUC variables */
77+
XmlBinaryType xmlbinary;
78+
XmlOptionType xmloption;
79+
7680
#ifdef USE_LIBXML
7781

7882
static StringInfo xml_err_buf = NULL;
@@ -104,11 +108,6 @@ static const char * map_sql_typecoll_to_xmlschema_types(List *tupdesc_list);
104108
static const char * map_sql_type_to_xmlschema_type(Oid typeoid, int typmod);
105109
static void SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns, bool top_level);
106110

107-
108-
XmlBinaryType xmlbinary;
109-
XmlOptionType xmloption;
110-
111-
112111
#define NO_XML_SUPPORT() \
113112
ereport(ERROR, \
114113
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
@@ -217,7 +216,8 @@ xml_out_internal(xmltype *x, pg_enc target_encoding)
217216
}
218217

219218
xml_ereport_by_code(WARNING, ERRCODE_INTERNAL_ERROR,
220-
"could not parse XML declaration in stored value", res_code);
219+
"could not parse XML declaration in stored value",
220+
res_code);
221221
#endif
222222
return str;
223223
}
@@ -540,52 +540,92 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
540540
{
541541
#ifdef USE_LIBXML
542542
XmlExpr *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
543+
xmltype *result;
544+
List *named_arg_strings;
545+
List *arg_strings;
543546
int i;
544547
ListCell *arg;
545548
ListCell *narg;
546-
bool isnull;
547-
xmltype *result;
548-
Datum value;
549-
char *str;
550-
551549
xmlBufferPtr buf;
552550
xmlTextWriterPtr writer;
553551

554-
buf = xmlBufferCreate();
555-
writer = xmlNewTextWriterMemory(buf, 0);
556-
557-
xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
558-
552+
/*
553+
* We first evaluate all the arguments, then start up libxml and
554+
* create the result. This avoids issues if one of the arguments
555+
* involves a call to some other function or subsystem that wants to use
556+
* libxml on its own terms.
557+
*/
558+
named_arg_strings = NIL;
559559
i = 0;
560-
forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names)
560+
foreach(arg, xmlExpr->named_args)
561561
{
562562
ExprState *e = (ExprState *) lfirst(arg);
563-
char *argname = strVal(lfirst(narg));
563+
Datum value;
564+
bool isnull;
565+
char *str;
564566

565567
value = ExecEvalExpr(e, econtext, &isnull, NULL);
566-
if (!isnull)
567-
{
568+
if (isnull)
569+
str = NULL;
570+
else
568571
str = OutputFunctionCall(&xmlExpr->named_outfuncs[i], value);
569-
xmlTextWriterWriteAttribute(writer, (xmlChar *) argname, (xmlChar *) str);
570-
pfree(str);
571-
}
572+
named_arg_strings = lappend(named_arg_strings, str);
572573
i++;
573574
}
574575

576+
arg_strings = NIL;
575577
foreach(arg, xmlExpr->args)
576578
{
577579
ExprState *e = (ExprState *) lfirst(arg);
580+
Datum value;
581+
bool isnull;
582+
char *str;
578583

579584
value = ExecEvalExpr(e, econtext, &isnull, NULL);
585+
/* here we can just forget NULL elements immediately */
580586
if (!isnull)
581-
xmlTextWriterWriteRaw(writer, (xmlChar *) map_sql_value_to_xml_value(value, exprType((Node *) e->expr)));
587+
{
588+
str = map_sql_value_to_xml_value(value,
589+
exprType((Node *) e->expr));
590+
arg_strings = lappend(arg_strings, str);
591+
}
592+
}
593+
594+
/* now safe to run libxml */
595+
xml_init();
596+
597+
buf = xmlBufferCreate();
598+
writer = xmlNewTextWriterMemory(buf, 0);
599+
600+
xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
601+
602+
forboth(arg, named_arg_strings, narg, xexpr->arg_names)
603+
{
604+
char *str = (char *) lfirst(arg);
605+
char *argname = strVal(lfirst(narg));
606+
607+
if (str)
608+
{
609+
xmlTextWriterWriteAttribute(writer,
610+
(xmlChar *) argname,
611+
(xmlChar *) str);
612+
pfree(str);
613+
}
614+
}
615+
616+
foreach(arg, arg_strings)
617+
{
618+
char *str = (char *) lfirst(arg);
619+
620+
xmlTextWriterWriteRaw(writer, (xmlChar *) str);
582621
}
583622

584623
xmlTextWriterEndElement(writer);
585624
xmlFreeTextWriter(writer);
586625

587626
result = xmlBuffer_to_xmltype(buf);
588627
xmlBufferFree(buf);
628+
589629
return result;
590630
#else
591631
NO_XML_SUPPORT();
@@ -733,9 +773,10 @@ xmlvalidate(PG_FUNCTION_ARGS)
733773

734774
xml_init();
735775

736-
/* We use a PG_TRY block to ensure libxml is cleaned up on error */
776+
/* We use a PG_TRY block to ensure libxml parser is cleaned up on error */
737777
PG_TRY();
738778
{
779+
xmlInitParser();
739780
ctxt = xmlNewParserCtxt();
740781
if (ctxt == NULL)
741782
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
@@ -860,43 +901,64 @@ xml_is_document(xmltype *arg)
860901
#ifdef USE_LIBXML
861902

862903
/*
863-
* Container for some init stuff (not good design!)
864-
* TODO xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and check)
904+
* Set up for use of libxml --- this should be called by each function that
905+
* is about to use libxml facilities.
906+
*
907+
* TODO: xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and
908+
* check)
865909
*/
866910
static void
867911
xml_init(void)
868912
{
869-
/*
870-
* Currently, we have no pure UTF-8 support for internals -- check
871-
* if we can work.
872-
*/
873-
if (sizeof (char) != sizeof (xmlChar))
874-
ereport(ERROR,
875-
(errmsg("could not initialize XML library"),
876-
errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.",
877-
(int) sizeof(char), (int) sizeof(xmlChar))));
913+
static bool first_time = true;
878914

879-
if (xml_err_buf == NULL)
915+
if (first_time)
880916
{
881-
/* First time through: create error buffer in permanent context */
917+
/* Stuff we need do only once per session */
882918
MemoryContext oldcontext;
883919

920+
/*
921+
* Currently, we have no pure UTF-8 support for internals -- check
922+
* if we can work.
923+
*/
924+
if (sizeof(char) != sizeof(xmlChar))
925+
ereport(ERROR,
926+
(errmsg("could not initialize XML library"),
927+
errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.",
928+
(int) sizeof(char), (int) sizeof(xmlChar))));
929+
930+
/* create error buffer in permanent context */
884931
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
885932
xml_err_buf = makeStringInfo();
886933
MemoryContextSwitchTo(oldcontext);
934+
935+
/* Now that xml_err_buf exists, safe to call xml_errorHandler */
936+
xmlSetGenericErrorFunc(NULL, xml_errorHandler);
937+
938+
/* Set up memory allocation our way, too */
939+
xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
940+
941+
/* Check library compatibility */
942+
LIBXML_TEST_VERSION;
943+
944+
first_time = false;
887945
}
888946
else
889947
{
890948
/* Reset pre-existing buffer to empty */
949+
Assert(xml_err_buf != NULL);
891950
resetStringInfo(xml_err_buf);
892-
}
893-
/* Now that xml_err_buf exists, safe to call xml_errorHandler */
894-
xmlSetGenericErrorFunc(NULL, xml_errorHandler);
895-
896-
xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
897951

898-
xmlInitParser();
899-
LIBXML_TEST_VERSION;
952+
/*
953+
* We re-establish the callback functions every time. This makes it
954+
* safe for other subsystems (PL/Perl, say) to also use libxml with
955+
* their own callbacks ... so long as they likewise set up the
956+
* callbacks on every use. It's cheap enough to not be worth
957+
* worrying about, anyway.
958+
*/
959+
xmlSetGenericErrorFunc(NULL, xml_errorHandler);
960+
xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
961+
}
900962
}
901963

902964

@@ -1071,9 +1133,10 @@ print_xml_decl(StringInfo buf, const xmlChar *version, pg_enc encoding, int stan
10711133
appendStringInfo(buf, " version=\"%s\"", PG_XML_DEFAULT_VERSION);
10721134

10731135
if (encoding && encoding != PG_UTF8)
1074-
/* XXX might be useful to convert this to IANA names
1075-
* (ISO-8859-1 instead of LATIN1 etc.); needs field
1076-
* experience */
1136+
/*
1137+
* XXX might be useful to convert this to IANA names
1138+
* (ISO-8859-1 instead of LATIN1 etc.); needs field experience
1139+
*/
10771140
appendStringInfo(buf, " encoding=\"%s\"", pg_encoding_to_char(encoding));
10781141

10791142
if (standalone == 1)
@@ -1115,9 +1178,10 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, xml
11151178

11161179
xml_init();
11171180

1118-
/* We use a PG_TRY block to ensure libxml is cleaned up on error */
1181+
/* We use a PG_TRY block to ensure libxml parser is cleaned up on error */
11191182
PG_TRY();
11201183
{
1184+
xmlInitParser();
11211185
ctxt = xmlNewParserCtxt();
11221186
if (ctxt == NULL)
11231187
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
@@ -1780,7 +1844,7 @@ schema_get_xml_visible_tables(Oid nspid)
17801844
StringInfoData query;
17811845

17821846
initStringInfo(&query);
1783-
appendStringInfo(&query, "SELECT oid FROM pg_class WHERE relnamespace = %u AND relkind IN ('r', 'v') AND has_table_privilege (oid, 'SELECT') ORDER BY relname;", nspid);
1847+
appendStringInfo(&query, "SELECT oid FROM pg_catalog.pg_class WHERE relnamespace = %u AND relkind IN ('r', 'v') AND pg_catalog.has_table_privilege (oid, 'SELECT') ORDER BY relname;", nspid);
17841848

17851849
return query_to_oid_list(query.data);
17861850
}
@@ -1792,7 +1856,7 @@ schema_get_xml_visible_tables(Oid nspid)
17921856
*/
17931857
#define XML_VISIBLE_SCHEMAS_EXCLUDE "nspname LIKE 'pg_%' ESCAPE '' OR nspname = 'information_schema'"
17941858

1795-
#define XML_VISIBLE_SCHEMAS "SELECT oid FROM pg_namespace WHERE has_schema_privilege (oid, 'USAGE') AND NOT (" XML_VISIBLE_SCHEMAS_EXCLUDE ")"
1859+
#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 ")"
17961860

17971861

17981862
static List *
@@ -1806,7 +1870,7 @@ static List *
18061870
database_get_xml_visible_tables(void)
18071871
{
18081872
/* At the moment there is no order required here. */
1809-
return query_to_oid_list("SELECT oid FROM pg_class WHERE relkind IN ('r', 'v') AND has_table_privilege (pg_class.oid, 'SELECT') AND relnamespace IN (" XML_VISIBLE_SCHEMAS ");");
1873+
return query_to_oid_list("SELECT oid FROM pg_catalog.pg_class WHERE relkind IN ('r', 'v') AND pg_catalog.has_table_privilege (pg_class.oid, 'SELECT') AND relnamespace IN (" XML_VISIBLE_SCHEMAS ");");
18101874
}
18111875

18121876

@@ -2973,7 +3037,6 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool n
29733037
{
29743038
if (nulls)
29753039
appendStringInfo(result, " <%s xsi:nil='true'/>\n", colname);
2976-
29773040
}
29783041
else
29793042
appendStringInfo(result, " <%s>%s</%s>\n",
@@ -3170,10 +3233,12 @@ xpath(PG_FUNCTION_ARGS)
31703233

31713234
xml_init();
31723235

3236+
/* We use a PG_TRY block to ensure libxml parser is cleaned up on error */
31733237
PG_TRY();
31743238
{
3239+
xmlInitParser();
31753240
/*
3176-
* redundant XML parsing (two parsings for the same value *
3241+
* redundant XML parsing (two parsings for the same value
31773242
* during one command execution are possible)
31783243
*/
31793244
ctxt = xmlNewParserCtxt();

0 commit comments

Comments
 (0)