summaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/xml.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/xml.c')
-rw-r--r--src/backend/utils/adt/xml.c275
1 files changed, 209 insertions, 66 deletions
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index eaf5b4d550..702b9e3e9f 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -4,10 +4,10 @@
* XML data type support.
*
*
- * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.98 2010/07/06 19:18:58 momjian Exp $
+ * src/backend/utils/adt/xml.c
*
*-------------------------------------------------------------------------
*/
@@ -137,7 +137,7 @@ static void SPI_sql_row_to_xmlelement(int rownum, StringInfo result,
errhint("You need to rebuild PostgreSQL using --with-libxml.")))
-/* from SQL/XML:2003 section 4.7 */
+/* from SQL/XML:2008 section 4.9 */
#define NAMESPACE_XSD "http://www.w3.org/2001/XMLSchema"
#define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance"
#define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml"
@@ -1067,13 +1067,15 @@ parse_xml_decl(const xmlChar *str, size_t *lenp,
if (xmlStrncmp(p, (xmlChar *) "'yes'", 5) == 0 ||
xmlStrncmp(p, (xmlChar *) "\"yes\"", 5) == 0)
{
- *standalone = 1;
+ if (standalone)
+ *standalone = 1;
p += 5;
}
else if (xmlStrncmp(p, (xmlChar *) "'no'", 4) == 0 ||
xmlStrncmp(p, (xmlChar *) "\"no\"", 4) == 0)
{
- *standalone = 0;
+ if (standalone)
+ *standalone = 0;
p += 4;
}
else
@@ -1200,9 +1202,10 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
{
/*
* Note, that here we try to apply DTD defaults
- * (XML_PARSE_DTDATTR) according to SQL/XML:10.16.7.d: 'Default
- * values defined by internal DTD are applied'. As for external
- * DTDs, we try to support them too, (see SQL/XML:10.16.7.e)
+ * (XML_PARSE_DTDATTR) according to SQL/XML:2008 GR 10.16.7.d:
+ * 'Default values defined by internal DTD are applied'. As for
+ * external DTDs, we try to support them too, (see SQL/XML:2008 GR
+ * 10.16.7.e)
*/
doc = xmlCtxtReadDoc(ctxt, utf8string,
NULL,
@@ -1217,8 +1220,8 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
{
int res_code;
size_t count;
- xmlChar *version = NULL;
- int standalone = -1;
+ xmlChar *version;
+ int standalone;
res_code = parse_xml_decl(utf8string,
&count, &version, NULL, &standalone);
@@ -1490,7 +1493,7 @@ is_valid_xml_namechar(pg_wchar c)
/*
- * Map SQL identifier to XML name; see SQL/XML:2003 section 9.1.
+ * Map SQL identifier to XML name; see SQL/XML:2008 section 9.1.
*/
char *
map_sql_identifier_to_xml_name(char *ident, bool fully_escaped,
@@ -1570,7 +1573,7 @@ unicode_to_sqlchar(pg_wchar c)
/*
- * Map XML name to SQL identifier; see SQL/XML:2003 section 9.17.
+ * Map XML name to SQL identifier; see SQL/XML:2008 section 9.3.
*/
char *
map_xml_name_to_sql_identifier(char *name)
@@ -1603,21 +1606,19 @@ map_xml_name_to_sql_identifier(char *name)
}
/*
- * Map SQL value to XML value; see SQL/XML:2003 section 9.16.
+ * Map SQL value to XML value; see SQL/XML:2008 section 9.8.
*
* When xml_escape_strings is true, then certain characters in string
* values are replaced by entity references (< etc.), as specified
- * in SQL/XML:2003 section 9.16 GR 8) ii). This is normally what is
+ * in SQL/XML:2008 section 9.8 GR 9) a) iii). This is normally what is
* wanted. The false case is mainly useful when the resulting value
* is used with xmlTextWriterWriteAttribute() to write out an
- * attribute, because that function does the escaping itself. The SQL
- * standard of 2003 is somewhat buggy in this regard, so we do our
- * best to make sense.
+ * attribute, because that function does the escaping itself.
*/
char *
map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings)
{
- if (type_is_array(type))
+ if (type_is_array_domain(type))
{
ArrayType *array;
Oid elmtype;
@@ -1868,12 +1869,13 @@ _SPI_strdup(const char *s)
/*
* SQL to XML mapping functions
*
- * What follows below is intentionally organized so that you can read
- * along in the SQL/XML:2003 standard. The functions are mostly split
- * up and ordered they way the clauses lay out in the standards
+ * What follows below was at one point intentionally organized so that
+ * you can read along in the SQL/XML standard. The functions are
+ * mostly split up the way the clauses lay out in the standards
* document, and the identifiers are also aligned with the standard
- * text. (SQL/XML:2006 appears to be ordered differently,
- * unfortunately.)
+ * text. Unfortunately, SQL/XML:2006 reordered the clauses
+ * differently than SQL/XML:2003, so the order below doesn't make much
+ * sense anymore.
*
* There are many things going on there:
*
@@ -1902,8 +1904,8 @@ _SPI_strdup(const char *s)
/*
- * Visibility of objects for XML mappings; see SQL/XML:2003 section
- * 4.8.5.
+ * Visibility of objects for XML mappings; see SQL/XML:2008 section
+ * 4.10.8.
*/
/*
@@ -1972,8 +1974,8 @@ database_get_xml_visible_tables(void)
/*
- * Map SQL table to XML and/or XML Schema document; see SQL/XML:2003
- * section 9.3.
+ * Map SQL table to XML and/or XML Schema document; see SQL/XML:2008
+ * section 9.11.
*/
static StringInfo
@@ -2269,8 +2271,8 @@ query_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
/*
- * Map SQL schema to XML and/or XML Schema document; see SQL/XML:2003
- * section 9.4.
+ * Map SQL schema to XML and/or XML Schema document; see SQL/XML:2008
+ * sections 9.13, 9.14.
*/
static StringInfo
@@ -2446,8 +2448,8 @@ schema_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
/*
- * Map SQL database to XML and/or XML Schema document; see SQL/XML:2003
- * section 9.5.
+ * Map SQL database to XML and/or XML Schema document; see SQL/XML:2008
+ * sections 9.16, 9.17.
*/
static StringInfo
@@ -2578,7 +2580,7 @@ database_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
/*
- * Map a multi-part SQL name to an XML name; see SQL/XML:2003 section
+ * Map a multi-part SQL name to an XML name; see SQL/XML:2008 section
* 9.2.
*/
static char *
@@ -2606,11 +2608,11 @@ map_multipart_sql_identifier_to_xml_name(char *a, char *b, char *c, char *d)
/*
- * Map an SQL table to an XML Schema document; see SQL/XML:2003
- * section 9.3.
+ * Map an SQL table to an XML Schema document; see SQL/XML:2008
+ * section 9.11.
*
- * Map an SQL table to XML Schema data types; see SQL/XML:2003 section
- * 9.6.
+ * Map an SQL table to XML Schema data types; see SQL/XML:2008 section
+ * 9.9.
*/
static const char *
map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls,
@@ -2712,8 +2714,8 @@ map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls,
/*
- * Map an SQL schema to XML Schema data types; see SQL/XML section
- * 9.7.
+ * Map an SQL schema to XML Schema data types; see SQL/XML:2008
+ * section 9.12.
*/
static const char *
map_sql_schema_to_xmlschema_types(Oid nspid, List *relid_list, bool nulls,
@@ -2785,8 +2787,8 @@ map_sql_schema_to_xmlschema_types(Oid nspid, List *relid_list, bool nulls,
/*
- * Map an SQL catalog to XML Schema data types; see SQL/XML section
- * 9.8.
+ * Map an SQL catalog to XML Schema data types; see SQL/XML:2008
+ * section 9.15.
*/
static const char *
map_sql_catalog_to_xmlschema_types(List *nspid_list, bool nulls,
@@ -2843,7 +2845,7 @@ map_sql_catalog_to_xmlschema_types(List *nspid_list, bool nulls,
/*
- * Map an SQL data type to an XML name; see SQL/XML:2003 section 9.9.
+ * Map an SQL data type to an XML name; see SQL/XML:2008 section 9.4.
*/
static const char *
map_sql_type_to_xml_name(Oid typeoid, int typmod)
@@ -2948,7 +2950,7 @@ map_sql_type_to_xml_name(Oid typeoid, int typmod)
/*
* Map a collection of SQL data types to XML Schema data types; see
- * SQL/XML:2002 section 9.10.
+ * SQL/XML:2008 section 9.7.
*/
static const char *
map_sql_typecoll_to_xmlschema_types(List *tupdesc_list)
@@ -2997,12 +2999,12 @@ map_sql_typecoll_to_xmlschema_types(List *tupdesc_list)
/*
- * Map an SQL data type to a named XML Schema data type; see SQL/XML
- * sections 9.11 and 9.15.
+ * Map an SQL data type to a named XML Schema data type; see
+ * SQL/XML:2008 sections 9.5 and 9.6.
*
- * (The distinction between 9.11 and 9.15 is basically that 9.15 adds
+ * (The distinction between 9.5 and 9.6 is basically that 9.6 adds
* a name attribute, which this function does. The name-less version
- * 9.11 doesn't appear to be required anywhere.)
+ * 9.5 doesn't appear to be required anywhere.)
*/
static const char *
map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
@@ -3179,7 +3181,7 @@ map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
/*
* Map an SQL row to an XML element, taking the row from the active
- * SPI cursor. See also SQL/XML:2003 section 9.12.
+ * SPI cursor. See also SQL/XML:2008 section 9.10.
*/
static void
SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename,
@@ -3295,24 +3297,20 @@ xml_xmlnodetoxmltype(xmlNodePtr cur)
/*
- * Evaluate XPath expression and return array of XML values.
+ * Common code for xpath() and xmlexists()
*
- * As we have no support of XQuery sequences yet, this function seems
- * to be the most useful one (array of XML functions plays a role of
- * some kind of substitution for XQuery sequences).
+ * Evaluate XPath expression and return number of nodes in res_items
+ * and array of XML values in astate.
*
* It is up to the user to ensure that the XML passed is in fact
* an XML document - XPath doesn't work easily on fragments without
* a context node being known.
*/
-Datum
-xpath(PG_FUNCTION_ARGS)
-{
#ifdef USE_LIBXML
- text *xpath_expr_text = PG_GETARG_TEXT_P(0);
- xmltype *data = PG_GETARG_XML_P(1);
- ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
- ArrayBuildState *astate = NULL;
+static void
+xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
+ int *res_nitems, ArrayBuildState **astate)
+{
xmlParserCtxtPtr ctxt = NULL;
xmlDocPtr doc = NULL;
xmlXPathContextPtr xpathctx = NULL;
@@ -3324,7 +3322,6 @@ xpath(PG_FUNCTION_ARGS)
xmlChar *string;
xmlChar *xpath_expr;
int i;
- int res_nitems;
int ndim;
Datum *ns_names_uris;
bool *ns_names_uris_nulls;
@@ -3339,7 +3336,7 @@ xpath(PG_FUNCTION_ARGS)
* ARRAY[ARRAY['myns', 'http://example.com'], ARRAY['myns2',
* 'http://example2.com']].
*/
- ndim = ARR_NDIM(namespaces);
+ ndim = namespaces ? ARR_NDIM(namespaces) : 0;
if (ndim != 0)
{
int *dims;
@@ -3439,6 +3436,13 @@ xpath(PG_FUNCTION_ARGS)
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
"invalid XPath expression");
+ /*
+ * Version 2.6.27 introduces a function named
+ * xmlXPathCompiledEvalToBoolean, which would be enough for xmlexists,
+ * but we can derive the existence by whether any nodes are returned,
+ * thereby preventing a library version upgrade and keeping the code
+ * the same.
+ */
xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
if (xpathobj == NULL) /* TODO: reason? */
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
@@ -3446,21 +3450,22 @@ xpath(PG_FUNCTION_ARGS)
/* return empty array in cases when nothing is found */
if (xpathobj->nodesetval == NULL)
- res_nitems = 0;
+ *res_nitems = 0;
else
- res_nitems = xpathobj->nodesetval->nodeNr;
+ *res_nitems = xpathobj->nodesetval->nodeNr;
- if (res_nitems)
+ if (*res_nitems && astate)
{
+ *astate = NULL;
for (i = 0; i < xpathobj->nodesetval->nodeNr; i++)
{
Datum elem;
bool elemisnull = false;
elem = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i]));
- astate = accumArrayResult(astate, elem,
- elemisnull, XMLOID,
- CurrentMemoryContext);
+ *astate = accumArrayResult(*astate, elem,
+ elemisnull, XMLOID,
+ CurrentMemoryContext);
}
}
}
@@ -3485,6 +3490,28 @@ xpath(PG_FUNCTION_ARGS)
xmlXPathFreeContext(xpathctx);
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctxt);
+}
+#endif /* USE_LIBXML */
+
+/*
+ * Evaluate XPath expression and return array of XML values.
+ *
+ * As we have no support of XQuery sequences yet, this function seems
+ * to be the most useful one (array of XML functions plays a role of
+ * some kind of substitution for XQuery sequences).
+ */
+Datum
+xpath(PG_FUNCTION_ARGS)
+{
+#ifdef USE_LIBXML
+ text *xpath_expr_text = PG_GETARG_TEXT_P(0);
+ xmltype *data = PG_GETARG_XML_P(1);
+ ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
+ int res_nitems;
+ ArrayBuildState *astate;
+
+ xpath_internal(xpath_expr_text, data, namespaces,
+ &res_nitems, &astate);
if (res_nitems == 0)
PG_RETURN_ARRAYTYPE_P(construct_empty_array(XMLOID));
@@ -3495,3 +3522,119 @@ xpath(PG_FUNCTION_ARGS)
return 0;
#endif
}
+
+/*
+ * Determines if the node specified by the supplied XPath exists
+ * in a given XML document, returning a boolean.
+ */
+Datum
+xmlexists(PG_FUNCTION_ARGS)
+{
+#ifdef USE_LIBXML
+ text *xpath_expr_text = PG_GETARG_TEXT_P(0);
+ xmltype *data = PG_GETARG_XML_P(1);
+ int res_nitems;
+
+ xpath_internal(xpath_expr_text, data, NULL,
+ &res_nitems, NULL);
+
+ PG_RETURN_BOOL(res_nitems > 0);
+#else
+ NO_XML_SUPPORT();
+ return 0;
+#endif
+}
+
+/*
+ * Determines if the node specified by the supplied XPath exists
+ * in a given XML document, returning a boolean. Differs from
+ * xmlexists as it supports namespaces and is not defined in SQL/XML.
+ */
+Datum
+xpath_exists(PG_FUNCTION_ARGS)
+{
+#ifdef USE_LIBXML
+ text *xpath_expr_text = PG_GETARG_TEXT_P(0);
+ xmltype *data = PG_GETARG_XML_P(1);
+ ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
+ int res_nitems;
+
+ xpath_internal(xpath_expr_text, data, namespaces,
+ &res_nitems, NULL);
+
+ PG_RETURN_BOOL(res_nitems > 0);
+#else
+ NO_XML_SUPPORT();
+ return 0;
+#endif
+}
+
+/*
+ * Functions for checking well-formed-ness
+ */
+
+#ifdef USE_LIBXML
+static bool
+wellformed_xml(text *data, XmlOptionType xmloption_arg)
+{
+ bool result;
+ xmlDocPtr doc = NULL;
+
+ /* We want to catch any exceptions and return false */
+ PG_TRY();
+ {
+ doc = xml_parse(data, xmloption_arg, true, GetDatabaseEncoding());
+ result = true;
+ }
+ PG_CATCH();
+ {
+ FlushErrorState();
+ result = false;
+ }
+ PG_END_TRY();
+
+ if (doc)
+ xmlFreeDoc(doc);
+
+ return result;
+}
+#endif
+
+Datum
+xml_is_well_formed(PG_FUNCTION_ARGS)
+{
+#ifdef USE_LIBXML
+ text *data = PG_GETARG_TEXT_P(0);
+
+ PG_RETURN_BOOL(wellformed_xml(data, xmloption));
+#else
+ NO_XML_SUPPORT();
+ return 0;
+#endif /* not USE_LIBXML */
+}
+
+Datum
+xml_is_well_formed_document(PG_FUNCTION_ARGS)
+{
+#ifdef USE_LIBXML
+ text *data = PG_GETARG_TEXT_P(0);
+
+ PG_RETURN_BOOL(wellformed_xml(data, XMLOPTION_DOCUMENT));
+#else
+ NO_XML_SUPPORT();
+ return 0;
+#endif /* not USE_LIBXML */
+}
+
+Datum
+xml_is_well_formed_content(PG_FUNCTION_ARGS)
+{
+#ifdef USE_LIBXML
+ text *data = PG_GETARG_TEXT_P(0);
+
+ PG_RETURN_BOOL(wellformed_xml(data, XMLOPTION_CONTENT));
+#else
+ NO_XML_SUPPORT();
+ return 0;
+#endif /* not USE_LIBXML */
+}