static xmlParserInputPtr xmlPgEntityLoader(const char *URL, const char *ID,
xmlParserCtxtPtr ctxt);
+static void xml_errsave(Node *escontext, PgXmlErrorContext *errcxt,
+ int sqlcode, const char *msg);
static void xml_errorHandler(void *data, xmlErrorPtr error);
-static void xml_ereport_by_code(int level, int sqlcode,
- const char *msg, int code);
+static int errdetail_for_xml_code(int code);
static void chopStringInfoNewlines(StringInfo str);
static void appendStringInfoLineSeparator(StringInfo str);
pg_enc encoding, int standalone);
static bool xml_doctype_in_content(const xmlChar *str);
static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
- bool preserve_whitespace, int encoding);
+ bool preserve_whitespace, int encoding,
+ Node *escontext);
static text *xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt);
static int xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
ArrayBuildState *astate,
xmltype *vardata;
xmlDocPtr doc;
+ /* Build the result object. */
vardata = (xmltype *) cstring_to_text(s);
/*
- * Parse the data to check if it is well-formed XML data. Assume that
- * ERROR occurred if parsing failed.
+ * Parse the data to check if it is well-formed XML data.
+ *
+ * Note: we don't need to worry about whether a soft error is detected.
*/
- doc = xml_parse(vardata, xmloption, true, GetDatabaseEncoding());
- xmlFreeDoc(doc);
+ doc = xml_parse(vardata, xmloption, true, GetDatabaseEncoding(),
+ fcinfo->context);
+ if (doc != NULL)
+ xmlFreeDoc(doc);
PG_RETURN_XML_P(vardata);
#else
return buf.data;
}
- xml_ereport_by_code(WARNING, ERRCODE_INTERNAL_ERROR,
- "could not parse XML declaration in stored value",
- res_code);
+ ereport(WARNING,
+ errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg_internal("could not parse XML declaration in stored value"),
+ errdetail_for_xml_code(res_code));
#endif
return str;
}
* Parse the data to check if it is well-formed XML data. Assume that
* xml_parse will throw ERROR if not.
*/
- doc = xml_parse(result, xmloption, true, encoding);
+ doc = xml_parse(result, xmloption, true, encoding, NULL);
xmlFreeDoc(doc);
/* Now that we know what we're dealing with, convert to server encoding */
xmlDocPtr doc;
doc = xml_parse(data, xmloption_arg, preserve_whitespace,
- GetDatabaseEncoding());
+ GetDatabaseEncoding(), NULL);
xmlFreeDoc(doc);
return (xmltype *) data;
PG_TRY();
{
doc = xml_parse((text *) arg, XMLOPTION_DOCUMENT, true,
- GetDatabaseEncoding());
+ GetDatabaseEncoding(), NULL);
result = true;
}
PG_CATCH();
/*
- * Convert a C string to XML internal representation
+ * Convert a text object to XML internal representation
+ *
+ * data is the source data (must not be toasted!), encoding is its encoding,
+ * and xmloption_arg and preserve_whitespace are options for the
+ * transformation.
+ *
+ * Errors normally result in ereport(ERROR), but if escontext is an
+ * ErrorSaveContext, then "safe" errors are reported there instead, and the
+ * caller must check SOFT_ERROR_OCCURRED() to see whether that happened.
*
* Note: it is caller's responsibility to xmlFreeDoc() the result,
- * else a permanent memory leak will ensue!
+ * else a permanent memory leak will ensue! But note the result could
+ * be NULL after a soft error.
*
* TODO maybe libxml2's xmlreader is better? (do not construct DOM,
* yet do not use SAX - see xmlreader.c)
*/
static xmlDocPtr
xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
- int encoding)
+ int encoding, Node *escontext)
{
int32 len;
xmlChar *string;
volatile xmlParserCtxtPtr ctxt = NULL;
volatile xmlDocPtr doc = NULL;
+ /*
+ * This step looks annoyingly redundant, but we must do it to have a
+ * null-terminated string in case encoding conversion isn't required.
+ */
len = VARSIZE_ANY_EXHDR(data); /* will be useful later */
string = xml_text2xmlChar(data);
+ /*
+ * If the data isn't UTF8, we must translate before giving it to libxml.
+ *
+ * XXX ideally, we'd catch any encoding conversion failure and return a
+ * soft error. However, failure to convert to UTF8 should be pretty darn
+ * rare, so for now this is left undone.
+ */
utf8string = pg_do_encoding_conversion(string,
len,
encoding,
xmlChar *version = NULL;
int standalone = 0;
+ /* Any errors here are reported as hard ereport's */
xmlInitParser();
ctxt = xmlNewParserCtxt();
res_code = parse_xml_decl(utf8string,
&count, &version, NULL, &standalone);
if (res_code != 0)
- xml_ereport_by_code(ERROR, ERRCODE_INVALID_XML_CONTENT,
- "invalid XML content: invalid XML declaration",
- res_code);
+ {
+ errsave(escontext,
+ errcode(ERRCODE_INVALID_XML_CONTENT),
+ errmsg_internal("invalid XML content: invalid XML declaration"),
+ errdetail_for_xml_code(res_code));
+ goto fail;
+ }
/* Is there a DOCTYPE element? */
if (xml_doctype_in_content(utf8string + count))
| (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS));
if (doc == NULL || xmlerrcxt->err_occurred)
{
- /* Use original option to decide which error code to throw */
+ /* Use original option to decide which error code to report */
if (xmloption_arg == XMLOPTION_DOCUMENT)
- xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
+ xml_errsave(escontext, xmlerrcxt,
+ ERRCODE_INVALID_XML_DOCUMENT,
"invalid XML document");
else
- xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_CONTENT,
+ xml_errsave(escontext, xmlerrcxt,
+ ERRCODE_INVALID_XML_CONTENT,
"invalid XML content");
+ goto fail;
}
}
else
{
doc = xmlNewDoc(version);
+ if (doc == NULL || xmlerrcxt->err_occurred)
+ xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+ "could not allocate XML document");
+
Assert(doc->encoding == NULL);
doc->encoding = xmlStrdup((const xmlChar *) "UTF-8");
+ if (doc->encoding == NULL || xmlerrcxt->err_occurred)
+ xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+ "could not allocate XML document");
doc->standalone = standalone;
/* allow empty content */
res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0,
utf8string + count, NULL);
if (res_code != 0 || xmlerrcxt->err_occurred)
- xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_CONTENT,
+ {
+ xml_errsave(escontext, xmlerrcxt,
+ ERRCODE_INVALID_XML_CONTENT,
"invalid XML content");
+ goto fail;
+ }
}
}
+
+fail:
+ ;
}
PG_CATCH();
{
}
+/*
+ * xml_errsave --- save an XML-related error
+ *
+ * If escontext is an ErrorSaveContext, error details are saved into it,
+ * and control returns normally.
+ *
+ * Otherwise, the error is thrown, so that this is equivalent to
+ * xml_ereport() with level == ERROR.
+ *
+ * This should be used only for errors that we're sure we do not need
+ * a transaction abort to clean up after.
+ */
+static void
+xml_errsave(Node *escontext, PgXmlErrorContext *errcxt,
+ int sqlcode, const char *msg)
+{
+ char *detail;
+
+ /* Defend against someone passing us a bogus context struct */
+ if (errcxt->magic != ERRCXT_MAGIC)
+ elog(ERROR, "xml_errsave called with invalid PgXmlErrorContext");
+
+ /* Flag that the current libxml error has been reported */
+ errcxt->err_occurred = false;
+
+ /* Include detail only if we have some text from libxml */
+ if (errcxt->err_buf.len > 0)
+ detail = errcxt->err_buf.data;
+ else
+ detail = NULL;
+
+ errsave(escontext,
+ (errcode(sqlcode),
+ errmsg_internal("%s", msg),
+ detail ? errdetail_internal("%s", detail) : 0));
+}
+
+
/*
* Error handler for libxml errors and warnings
*/
/*
- * Wrapper for "ereport" function for XML-related errors. The "msg"
- * is the SQL-level message; some can be adopted from the SQL/XML
- * standard. This function uses "code" to create a textual detail
- * message. At the moment, we only need to cover those codes that we
+ * Convert libxml error codes into textual errdetail messages.
+ *
+ * This should be called within an ereport or errsave invocation,
+ * just as errdetail would be.
+ *
+ * At the moment, we only need to cover those codes that we
* may raise in this file.
*/
-static void
-xml_ereport_by_code(int level, int sqlcode,
- const char *msg, int code)
+static int
+errdetail_for_xml_code(int code)
{
const char *det;
break;
}
- ereport(level,
- (errcode(sqlcode),
- errmsg_internal("%s", msg),
- errdetail(det, code)));
+ return errdetail(det, code);
}
/* We want to catch any exceptions and return false */
PG_TRY();
{
- doc = xml_parse(data, xmloption_arg, true, GetDatabaseEncoding());
+ doc = xml_parse(data, xmloption_arg, true, GetDatabaseEncoding(), NULL);
result = true;
}
PG_CATCH();