Commit 85f807d7 authored by Tom Lane's avatar Tom Lane

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.
parent bcb3852c
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.49 2007/10/13 20:46:47 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.50 2007/11/05 22:23:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -73,6 +73,10 @@ ...@@ -73,6 +73,10 @@
#include "utils/xml.h" #include "utils/xml.h"
/* GUC variables */
XmlBinaryType xmlbinary;
XmlOptionType xmloption;
#ifdef USE_LIBXML #ifdef USE_LIBXML
static StringInfo xml_err_buf = NULL; static StringInfo xml_err_buf = NULL;
...@@ -104,11 +108,6 @@ static const char * map_sql_typecoll_to_xmlschema_types(List *tupdesc_list); ...@@ -104,11 +108,6 @@ static const char * map_sql_typecoll_to_xmlschema_types(List *tupdesc_list);
static const char * map_sql_type_to_xmlschema_type(Oid typeoid, int typmod); static const char * map_sql_type_to_xmlschema_type(Oid typeoid, int typmod);
static void SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns, bool top_level); static void SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns, bool top_level);
XmlBinaryType xmlbinary;
XmlOptionType xmloption;
#define NO_XML_SUPPORT() \ #define NO_XML_SUPPORT() \
ereport(ERROR, \ ereport(ERROR, \
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
...@@ -217,7 +216,8 @@ xml_out_internal(xmltype *x, pg_enc target_encoding) ...@@ -217,7 +216,8 @@ xml_out_internal(xmltype *x, pg_enc target_encoding)
} }
xml_ereport_by_code(WARNING, ERRCODE_INTERNAL_ERROR, xml_ereport_by_code(WARNING, ERRCODE_INTERNAL_ERROR,
"could not parse XML declaration in stored value", res_code); "could not parse XML declaration in stored value",
res_code);
#endif #endif
return str; return str;
} }
...@@ -540,45 +540,84 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext) ...@@ -540,45 +540,84 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
{ {
#ifdef USE_LIBXML #ifdef USE_LIBXML
XmlExpr *xexpr = (XmlExpr *) xmlExpr->xprstate.expr; XmlExpr *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
xmltype *result;
List *named_arg_strings;
List *arg_strings;
int i; int i;
ListCell *arg; ListCell *arg;
ListCell *narg; ListCell *narg;
bool isnull;
xmltype *result;
Datum value;
char *str;
xmlBufferPtr buf; xmlBufferPtr buf;
xmlTextWriterPtr writer; xmlTextWriterPtr writer;
buf = xmlBufferCreate(); /*
writer = xmlNewTextWriterMemory(buf, 0); * We first evaluate all the arguments, then start up libxml and
* create the result. This avoids issues if one of the arguments
xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name); * involves a call to some other function or subsystem that wants to use
* libxml on its own terms.
*/
named_arg_strings = NIL;
i = 0; i = 0;
forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names) foreach(arg, xmlExpr->named_args)
{ {
ExprState *e = (ExprState *) lfirst(arg); ExprState *e = (ExprState *) lfirst(arg);
char *argname = strVal(lfirst(narg)); Datum value;
bool isnull;
char *str;
value = ExecEvalExpr(e, econtext, &isnull, NULL); value = ExecEvalExpr(e, econtext, &isnull, NULL);
if (!isnull) if (isnull)
{ str = NULL;
else
str = OutputFunctionCall(&xmlExpr->named_outfuncs[i], value); str = OutputFunctionCall(&xmlExpr->named_outfuncs[i], value);
xmlTextWriterWriteAttribute(writer, (xmlChar *) argname, (xmlChar *) str); named_arg_strings = lappend(named_arg_strings, str);
pfree(str);
}
i++; i++;
} }
arg_strings = NIL;
foreach(arg, xmlExpr->args) foreach(arg, xmlExpr->args)
{ {
ExprState *e = (ExprState *) lfirst(arg); ExprState *e = (ExprState *) lfirst(arg);
Datum value;
bool isnull;
char *str;
value = ExecEvalExpr(e, econtext, &isnull, NULL); value = ExecEvalExpr(e, econtext, &isnull, NULL);
/* here we can just forget NULL elements immediately */
if (!isnull) if (!isnull)
xmlTextWriterWriteRaw(writer, (xmlChar *) map_sql_value_to_xml_value(value, exprType((Node *) e->expr))); {
str = map_sql_value_to_xml_value(value,
exprType((Node *) e->expr));
arg_strings = lappend(arg_strings, str);
}
}
/* now safe to run libxml */
xml_init();
buf = xmlBufferCreate();
writer = xmlNewTextWriterMemory(buf, 0);
xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
forboth(arg, named_arg_strings, narg, xexpr->arg_names)
{
char *str = (char *) lfirst(arg);
char *argname = strVal(lfirst(narg));
if (str)
{
xmlTextWriterWriteAttribute(writer,
(xmlChar *) argname,
(xmlChar *) str);
pfree(str);
}
}
foreach(arg, arg_strings)
{
char *str = (char *) lfirst(arg);
xmlTextWriterWriteRaw(writer, (xmlChar *) str);
} }
xmlTextWriterEndElement(writer); xmlTextWriterEndElement(writer);
...@@ -586,6 +625,7 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext) ...@@ -586,6 +625,7 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
result = xmlBuffer_to_xmltype(buf); result = xmlBuffer_to_xmltype(buf);
xmlBufferFree(buf); xmlBufferFree(buf);
return result; return result;
#else #else
NO_XML_SUPPORT(); NO_XML_SUPPORT();
...@@ -733,9 +773,10 @@ xmlvalidate(PG_FUNCTION_ARGS) ...@@ -733,9 +773,10 @@ xmlvalidate(PG_FUNCTION_ARGS)
xml_init(); xml_init();
/* We use a PG_TRY block to ensure libxml is cleaned up on error */ /* We use a PG_TRY block to ensure libxml parser is cleaned up on error */
PG_TRY(); PG_TRY();
{ {
xmlInitParser();
ctxt = xmlNewParserCtxt(); ctxt = xmlNewParserCtxt();
if (ctxt == NULL) if (ctxt == NULL)
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
...@@ -860,43 +901,64 @@ xml_is_document(xmltype *arg) ...@@ -860,43 +901,64 @@ xml_is_document(xmltype *arg)
#ifdef USE_LIBXML #ifdef USE_LIBXML
/* /*
* Container for some init stuff (not good design!) * Set up for use of libxml --- this should be called by each function that
* TODO xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and check) * is about to use libxml facilities.
*
* TODO: xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and
* check)
*/ */
static void static void
xml_init(void) xml_init(void)
{ {
/* static bool first_time = true;
* Currently, we have no pure UTF-8 support for internals -- check
* if we can work.
*/
if (sizeof (char) != sizeof (xmlChar))
ereport(ERROR,
(errmsg("could not initialize XML library"),
errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.",
(int) sizeof(char), (int) sizeof(xmlChar))));
if (xml_err_buf == NULL) if (first_time)
{ {
/* First time through: create error buffer in permanent context */ /* Stuff we need do only once per session */
MemoryContext oldcontext; MemoryContext oldcontext;
/*
* Currently, we have no pure UTF-8 support for internals -- check
* if we can work.
*/
if (sizeof(char) != sizeof(xmlChar))
ereport(ERROR,
(errmsg("could not initialize XML library"),
errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.",
(int) sizeof(char), (int) sizeof(xmlChar))));
/* create error buffer in permanent context */
oldcontext = MemoryContextSwitchTo(TopMemoryContext); oldcontext = MemoryContextSwitchTo(TopMemoryContext);
xml_err_buf = makeStringInfo(); xml_err_buf = makeStringInfo();
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
/* Now that xml_err_buf exists, safe to call xml_errorHandler */
xmlSetGenericErrorFunc(NULL, xml_errorHandler);
/* Set up memory allocation our way, too */
xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
/* Check library compatibility */
LIBXML_TEST_VERSION;
first_time = false;
} }
else else
{ {
/* Reset pre-existing buffer to empty */ /* Reset pre-existing buffer to empty */
Assert(xml_err_buf != NULL);
resetStringInfo(xml_err_buf); resetStringInfo(xml_err_buf);
}
/* Now that xml_err_buf exists, safe to call xml_errorHandler */
xmlSetGenericErrorFunc(NULL, xml_errorHandler);
xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
xmlInitParser(); /*
LIBXML_TEST_VERSION; * We re-establish the callback functions every time. This makes it
* safe for other subsystems (PL/Perl, say) to also use libxml with
* their own callbacks ... so long as they likewise set up the
* callbacks on every use. It's cheap enough to not be worth
* worrying about, anyway.
*/
xmlSetGenericErrorFunc(NULL, xml_errorHandler);
xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
}
} }
...@@ -1071,9 +1133,10 @@ print_xml_decl(StringInfo buf, const xmlChar *version, pg_enc encoding, int stan ...@@ -1071,9 +1133,10 @@ print_xml_decl(StringInfo buf, const xmlChar *version, pg_enc encoding, int stan
appendStringInfo(buf, " version=\"%s\"", PG_XML_DEFAULT_VERSION); appendStringInfo(buf, " version=\"%s\"", PG_XML_DEFAULT_VERSION);
if (encoding && encoding != PG_UTF8) if (encoding && encoding != PG_UTF8)
/* XXX might be useful to convert this to IANA names /*
* (ISO-8859-1 instead of LATIN1 etc.); needs field * XXX might be useful to convert this to IANA names
* experience */ * (ISO-8859-1 instead of LATIN1 etc.); needs field experience
*/
appendStringInfo(buf, " encoding=\"%s\"", pg_encoding_to_char(encoding)); appendStringInfo(buf, " encoding=\"%s\"", pg_encoding_to_char(encoding));
if (standalone == 1) if (standalone == 1)
...@@ -1115,9 +1178,10 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, xml ...@@ -1115,9 +1178,10 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, xml
xml_init(); xml_init();
/* We use a PG_TRY block to ensure libxml is cleaned up on error */ /* We use a PG_TRY block to ensure libxml parser is cleaned up on error */
PG_TRY(); PG_TRY();
{ {
xmlInitParser();
ctxt = xmlNewParserCtxt(); ctxt = xmlNewParserCtxt();
if (ctxt == NULL) if (ctxt == NULL)
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
...@@ -1780,7 +1844,7 @@ schema_get_xml_visible_tables(Oid nspid) ...@@ -1780,7 +1844,7 @@ schema_get_xml_visible_tables(Oid nspid)
StringInfoData query; StringInfoData query;
initStringInfo(&query); initStringInfo(&query);
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); 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);
return query_to_oid_list(query.data); return query_to_oid_list(query.data);
} }
...@@ -1792,7 +1856,7 @@ schema_get_xml_visible_tables(Oid nspid) ...@@ -1792,7 +1856,7 @@ schema_get_xml_visible_tables(Oid nspid)
*/ */
#define XML_VISIBLE_SCHEMAS_EXCLUDE "nspname LIKE 'pg_%' ESCAPE '' OR nspname = 'information_schema'" #define XML_VISIBLE_SCHEMAS_EXCLUDE "nspname LIKE 'pg_%' ESCAPE '' OR nspname = 'information_schema'"
#define XML_VISIBLE_SCHEMAS "SELECT oid FROM pg_namespace WHERE has_schema_privilege (oid, 'USAGE') AND NOT (" XML_VISIBLE_SCHEMAS_EXCLUDE ")" #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 ")"
static List * static List *
...@@ -1806,7 +1870,7 @@ static List * ...@@ -1806,7 +1870,7 @@ static List *
database_get_xml_visible_tables(void) database_get_xml_visible_tables(void)
{ {
/* At the moment there is no order required here. */ /* At the moment there is no order required here. */
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 ");"); 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 ");");
} }
...@@ -2973,7 +3037,6 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool n ...@@ -2973,7 +3037,6 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool n
{ {
if (nulls) if (nulls)
appendStringInfo(result, " <%s xsi:nil='true'/>\n", colname); appendStringInfo(result, " <%s xsi:nil='true'/>\n", colname);
} }
else else
appendStringInfo(result, " <%s>%s</%s>\n", appendStringInfo(result, " <%s>%s</%s>\n",
...@@ -3170,10 +3233,12 @@ xpath(PG_FUNCTION_ARGS) ...@@ -3170,10 +3233,12 @@ xpath(PG_FUNCTION_ARGS)
xml_init(); xml_init();
/* We use a PG_TRY block to ensure libxml parser is cleaned up on error */
PG_TRY(); PG_TRY();
{ {
xmlInitParser();
/* /*
* redundant XML parsing (two parsings for the same value * * redundant XML parsing (two parsings for the same value
* during one command execution are possible) * during one command execution are possible)
*/ */
ctxt = xmlNewParserCtxt(); ctxt = xmlNewParserCtxt();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment