Cleaning up ERRCODE usage in our XML code - Mailing list pgsql-hackers

From Tom Lane
Subject Cleaning up ERRCODE usage in our XML code
Date
Msg-id 417250.1726341268@sss.pgh.pa.us
Whole thread Raw
Responses Re: Cleaning up ERRCODE usage in our XML code
List pgsql-hackers
I noticed while working on bug #18617 [1] that we are fairly slipshod
about which SQLSTATE to report when libxml2 returns an error.  There
are places using ERRCODE_INTERNAL_ERROR for easily-triggered errors;
there are different places that use different ERRCODEs for exactly
the same condition; and there are places that use different ERRCODEs
for failures from xmlXPathCompile and xmlXPathCompiledEval.  I found
that this last is problematic because some errors you might expect
to be reported during XPath compilation are not detected till
execution, notably namespace-related errors.  That seems more like
a libxml2 implementation artifact than something we should expect to
be stable behavior, so I think we should avoid using different
ERRCODEs.

A lot of this can be blamed on there not being any especially on-point
SQLSTATE values back when this code was first written.  I learned that
recent revisions of SQL have a whole new SQLSTATE class, class 10 =
"XQuery Error", so we have an opportunity to sync up with that as well
as be more self-consistent.  The spec's subclass codes in this class
seem quite fine-grained.  It might be an interesting exercise to try
to teach xml_errorHandler() to translate libxml2's error->code values
into fine-grained SQLSTATEs, but I've not attempted that; I'm not
sure whether there is a close mapping between what libxml2 reports
and the set of codes the SQL committee chose.  What I've done in the
attached first-draft patch is just to select one relatively generic
code in class 10, 10608 = invalid_argument_for_xquery, and use that
where it seemed apropos.

This is pretty low-priority, so I'll stash it in the next CF.

            regards, tom lane

[1] https://www.postgresql.org/message-id/356363.1726333674%40sss.pgh.pa.us

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index f94b622d92..cc40fa5624 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -388,7 +388,7 @@ pgxml_xpath(text *document, xmlChar *xpath, xpath_workspace *workspace)
             /* compile the path */
             comppath = xmlXPathCompile(xpath);
             if (comppath == NULL)
-                xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
+                xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
                             "XPath Syntax Error");

             /* Now evaluate the path expression. */
@@ -652,7 +652,7 @@ xpath_table(PG_FUNCTION_ARGS)
                         comppath = xmlXPathCompile(xpaths[j]);
                         if (comppath == NULL)
                             xml_ereport(xmlerrcxt, ERROR,
-                                        ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
+                                        ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
                                         "XPath Syntax Error");

                         /* Now evaluate the path expression. */
diff --git a/contrib/xml2/xslt_proc.c b/contrib/xml2/xslt_proc.c
index f30a3a42c0..e761ca5cb5 100644
--- a/contrib/xml2/xslt_proc.c
+++ b/contrib/xml2/xslt_proc.c
@@ -90,7 +90,7 @@ xslt_process(PG_FUNCTION_ARGS)
                                 XML_PARSE_NOENT);

         if (doctree == NULL)
-            xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
+            xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
                         "error parsing XML document");

         /* Same for stylesheet */
@@ -99,14 +99,14 @@ xslt_process(PG_FUNCTION_ARGS)
                               XML_PARSE_NOENT);

         if (ssdoc == NULL)
-            xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
+            xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
                         "error parsing stylesheet as XML document");

         /* After this call we need not free ssdoc separately */
         stylesheet = xsltParseStylesheetDoc(ssdoc);

         if (stylesheet == NULL)
-            xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
+            xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
                         "failed to parse stylesheet");

         xslt_ctxt = xsltNewTransformContext(stylesheet, doctree);
@@ -141,7 +141,7 @@ xslt_process(PG_FUNCTION_ARGS)
                                           NULL, NULL, xslt_ctxt);

         if (restree == NULL)
-            xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
+            xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
                         "failed to apply stylesheet");

         resstat = xsltSaveResultToString(&resstr, &reslen, restree, stylesheet);
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 1a07876cd5..3eaf9f78a8 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -742,7 +742,7 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
         {
             /* If it's a document, saving is easy. */
             if (xmlSaveDoc(ctxt, doc) == -1 || xmlerrcxt->err_occurred)
-                xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
+                xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
                             "could not save document to xmlBuffer");
         }
         else if (content_nodes != NULL)
@@ -785,7 +785,7 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
                     if (xmlSaveTree(ctxt, newline) == -1 || xmlerrcxt->err_occurred)
                     {
                         xmlFreeNode(newline);
-                        xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
+                        xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
                                     "could not save newline to xmlBuffer");
                     }
                 }
@@ -793,7 +793,7 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
                 if (xmlSaveTree(ctxt, node) == -1 || xmlerrcxt->err_occurred)
                 {
                     xmlFreeNode(newline);
-                    xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
+                    xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
                                 "could not save content to xmlBuffer");
                 }
             }
@@ -1004,7 +1004,7 @@ xmlpi(const char *target, text *arg, bool arg_is_null, bool *result_is_null)

     if (pg_strcasecmp(target, "xml") == 0)
         ereport(ERROR,
-                (errcode(ERRCODE_SYNTAX_ERROR), /* really */
+                (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
                  errmsg("invalid XML processing instruction"),
                  errdetail("XML processing instruction target name cannot be \"%s\".", target)));

@@ -4383,7 +4383,7 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
     xpath_len = VARSIZE_ANY_EXHDR(xpath_expr_text);
     if (xpath_len == 0)
         ereport(ERROR,
-                (errcode(ERRCODE_DATA_EXCEPTION),
+                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_XQUERY),
                  errmsg("empty XPath expression")));

     string = pg_xmlCharStrndup(datastr, len);
@@ -4450,7 +4450,7 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,

         xpathcomp = xmlXPathCompile(xpath_expr);
         if (xpathcomp == NULL || xmlerrcxt->err_occurred)
-            xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
+            xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
                         "invalid XPath expression");

         /*
@@ -4462,7 +4462,7 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
          */
         xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
         if (xpathobj == NULL || xmlerrcxt->err_occurred)
-            xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
+            xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
                         "could not create XPath object");

         /*
@@ -4792,7 +4792,7 @@ XmlTableSetNamespace(TableFuncScanState *state, const char *name, const char *ur
     if (xmlXPathRegisterNs(xtCxt->xpathcxt,
                            pg_xmlCharStrndup(name, strlen(name)),
                            pg_xmlCharStrndup(uri, strlen(uri))))
-        xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_DATA_EXCEPTION,
+        xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
                     "could not set XML namespace");
 #else
     NO_XML_SUPPORT();
@@ -4814,14 +4814,14 @@ XmlTableSetRowFilter(TableFuncScanState *state, const char *path)

     if (*path == '\0')
         ereport(ERROR,
-                (errcode(ERRCODE_DATA_EXCEPTION),
+                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_XQUERY),
                  errmsg("row path filter must not be empty string")));

     xstr = pg_xmlCharStrndup(path, strlen(path));

     xtCxt->xpathcomp = xmlXPathCompile(xstr);
     if (xtCxt->xpathcomp == NULL || xtCxt->xmlerrcxt->err_occurred)
-        xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_SYNTAX_ERROR,
+        xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
                     "invalid XPath expression");
 #else
     NO_XML_SUPPORT();
@@ -4845,14 +4845,14 @@ XmlTableSetColumnFilter(TableFuncScanState *state, const char *path, int colnum)

     if (*path == '\0')
         ereport(ERROR,
-                (errcode(ERRCODE_DATA_EXCEPTION),
+                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_XQUERY),
                  errmsg("column path filter must not be empty string")));

     xstr = pg_xmlCharStrndup(path, strlen(path));

     xtCxt->xpathscomp[colnum] = xmlXPathCompile(xstr);
     if (xtCxt->xpathscomp[colnum] == NULL || xtCxt->xmlerrcxt->err_occurred)
-        xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_DATA_EXCEPTION,
+        xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
                     "invalid XPath expression");
 #else
     NO_XML_SUPPORT();
@@ -4879,7 +4879,7 @@ XmlTableFetchRow(TableFuncScanState *state)
     {
         xtCxt->xpathobj = xmlXPathCompiledEval(xtCxt->xpathcomp, xtCxt->xpathcxt);
         if (xtCxt->xpathobj == NULL || xtCxt->xmlerrcxt->err_occurred)
-            xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
+            xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
                         "could not create XPath object");

         xtCxt->row_count = 0;
@@ -4943,7 +4943,7 @@ XmlTableGetValue(TableFuncScanState *state, int colnum,
         /* Evaluate column path */
         xpathobj = xmlXPathCompiledEval(xtCxt->xpathscomp[colnum], xtCxt->xpathcxt);
         if (xpathobj == NULL || xtCxt->xmlerrcxt->err_occurred)
-            xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
+            xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
                         "could not create XPath object");

         /*
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index b43a24d4bc..97d91eb1e9 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -141,6 +141,12 @@ Section: Class 0Z - Diagnostics Exception
 0Z000    E    ERRCODE_DIAGNOSTICS_EXCEPTION                                  diagnostics_exception
 0Z002    E    ERRCODE_STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER
stacked_diagnostics_accessed_without_active_handler

+Section: Class 10 - XQuery Error
+
+# recent SQL versions define quite a few codes in this class, but for now
+# we are only using this generic one
+10608    E    ERRCODE_INVALID_ARGUMENT_FOR_XQUERY                            invalid_argument_for_xquery
+
 Section: Class 20 - Case Not Found

 20000    E    ERRCODE_CASE_NOT_FOUND                                         case_not_found

pgsql-hackers by date:

Previous
From: Akshat Jaimini
Date:
Subject: Re: WAL_LOG CREATE DATABASE strategy broken for non-standard page layouts
Next
From: Thomas Munro
Date:
Subject: Re: Robocopy might be not robust enough for never-ending testing on Windows