|
|
|
@ -119,9 +119,10 @@ struct PgXmlErrorContext
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
@ -143,7 +144,8 @@ static bool print_xml_decl(StringInfo buf, const xmlChar *version,
|
|
|
|
|
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,
|
|
|
|
@ -261,14 +263,18 @@ xml_in(PG_FUNCTION_ARGS)
|
|
|
|
|
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
|
|
|
|
@ -323,9 +329,10 @@ xml_out_internal(xmltype *x, pg_enc target_encoding)
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
@ -392,7 +399,7 @@ xml_recv(PG_FUNCTION_ARGS)
|
|
|
|
|
* 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 */
|
|
|
|
@ -754,7 +761,7 @@ xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace)
|
|
|
|
|
xmlDocPtr doc;
|
|
|
|
|
|
|
|
|
|
doc = xml_parse(data, xmloption_arg, preserve_whitespace,
|
|
|
|
|
GetDatabaseEncoding());
|
|
|
|
|
GetDatabaseEncoding(), NULL);
|
|
|
|
|
xmlFreeDoc(doc);
|
|
|
|
|
|
|
|
|
|
return (xmltype *) data;
|
|
|
|
@ -895,7 +902,7 @@ xml_is_document(xmltype *arg)
|
|
|
|
|
PG_TRY();
|
|
|
|
|
{
|
|
|
|
|
doc = xml_parse((text *) arg, XMLOPTION_DOCUMENT, true,
|
|
|
|
|
GetDatabaseEncoding());
|
|
|
|
|
GetDatabaseEncoding(), NULL);
|
|
|
|
|
result = true;
|
|
|
|
|
}
|
|
|
|
|
PG_CATCH();
|
|
|
|
@ -1500,17 +1507,26 @@ xml_doctype_in_content(const xmlChar *str)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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;
|
|
|
|
@ -1519,9 +1535,20 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
|
|
|
|
|
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,
|
|
|
|
@ -1539,6 +1566,7 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
|
|
|
|
|
xmlChar *version = NULL;
|
|
|
|
|
int standalone = 0;
|
|
|
|
|
|
|
|
|
|
/* Any errors here are reported as hard ereport's */
|
|
|
|
|
xmlInitParser();
|
|
|
|
|
|
|
|
|
|
ctxt = xmlNewParserCtxt();
|
|
|
|
@ -1555,9 +1583,13 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
|
|
|
|
|
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))
|
|
|
|
@ -1580,20 +1612,30 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
|
|
|
|
|
| (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 */
|
|
|
|
@ -1602,10 +1644,17 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
|
|
|
|
|
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();
|
|
|
|
|
{
|
|
|
|
@ -1745,6 +1794,44 @@ xml_ereport(PgXmlErrorContext *errcxt, int level, int sqlcode, const char *msg)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
@ -1917,15 +2004,16 @@ xml_errorHandler(void *data, xmlErrorPtr error)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
|
@ -1954,10 +2042,7 @@ xml_ereport_by_code(int level, int sqlcode,
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ereport(level,
|
|
|
|
|
(errcode(sqlcode),
|
|
|
|
|
errmsg_internal("%s", msg),
|
|
|
|
|
errdetail(det, code)));
|
|
|
|
|
return errdetail(det, code);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -4241,7 +4326,7 @@ wellformed_xml(text *data, XmlOptionType xmloption_arg)
|
|
|
|
|
/* 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();
|
|
|
|
|