From e030fc16e9081bc569e6e2b8cbd3b522e4f64160 Mon Sep 17 00:00:00 2001 From: Jim Jones Date: Thu, 5 Feb 2026 09:00:00 +0100 Subject: [PATCH v5 2/2] Add XMLVALIDATE --- doc/src/sgml/datatype.sgml | 7 +- doc/src/sgml/func/func-xml.sgml | 105 ++++++++++++ src/backend/catalog/dependency.c | 28 ++++ src/backend/executor/execExprInterp.c | 27 +++ src/backend/parser/gram.y | 19 ++- src/backend/parser/parse_expr.c | 43 +++++ src/backend/parser/parse_target.c | 3 + src/backend/utils/adt/ruleutils.c | 17 +- src/backend/utils/adt/xml.c | 144 +++++++++++++++- src/include/nodes/primnodes.h | 1 + src/include/parser/kwlist.h | 2 + src/include/utils/xml.h | 1 + src/test/regress/expected/xml.out | 233 ++++++++++++++++++++++++++ src/test/regress/expected/xml_1.out | 208 +++++++++++++++++++++++ src/test/regress/expected/xml_2.out | 233 ++++++++++++++++++++++++++ src/test/regress/sql/xml.sql | 209 +++++++++++++++++++++++ 16 files changed, 1272 insertions(+), 8 deletions(-) diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index f2ade57fe7..ddaf7c4d3d 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -4510,8 +4510,11 @@ xml 'bar' against a document type declaration (DTD),DTD even when the input value specifies a DTD. - There is also currently no built-in support for validating against - other XML schema languages such as XML Schema. + However, PostgreSQL provides the + XMLVALIDATE function for validating XML documents + against XML Schema (XSD) documents. See + and + for more information. diff --git a/doc/src/sgml/func/func-xml.sgml b/doc/src/sgml/func/func-xml.sgml index 511bc90852..c4a6e89d0e 100644 --- a/doc/src/sgml/func/func-xml.sgml +++ b/doc/src/sgml/func/func-xml.sgml @@ -1007,6 +1007,111 @@ SELECT xmltable.* 3 | 4 4 | 5 (3 rows) +]]> + + + + + <literal>xmlvalidate</literal> + + + xmlvalidate + + + +XMLVALIDATE ( DOCUMENT xml_value ACCORDING TO XMLSCHEMA schema_name ) xml + + + + The xmlvalidate function validates an XML document + against an XML Schema. The schema must have been previously created using + CREATE XMLSCHEMA. If the XML is valid according to the + schema, the function returns the XML value unchanged. If validation fails, + an error is raised. If xml_value is + NULL, the function returns NULL. + + + + The schema_name argument specifies the name + of an XML schema stored in the database. It can be schema-qualified + (e.g., myschema.person_schema) or unqualified, in + which case it will be searched for in the current schema search path. + The user must have USAGE privilege on the schema. + + + + Examples: + + + + + + + + + + +'; + +SELECT xmlvalidate(DOCUMENT 'John30' + ACCORDING TO XMLSCHEMA person_schema); + + xmlvalidate +--------------------------------------------- + John30 +(1 row) +]]> + + + + This example shows a validation failure due to a missing required element: +John' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML validation failed +]]> + + + + Schema-qualified names are supported: + + + + + + + + + + + +'; + +SELECT xmlvalidate(DOCUMENT 'Widget9.99' + ACCORDING TO XMLSCHEMA test_schema.product_schema); + + xmlvalidate +--------------------------------------------------------------- + Widget9.99 +(1 row) +]]> + + + + Since xmlvalidate returns xml, it can + be composed with other XML functions: +Alice25' + ACCORDING TO XMLSCHEMA person_schema) AS text); + + xmlserialize +------------------------------------------ + Alice25 +(1 row) ]]> diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 0b8d8e3152..50ce3871e7 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -2419,6 +2419,34 @@ find_expr_references_walker(Node *node, context->addrs); /* fall through to examine arguments */ } + else if (IsA(node, XmlExpr)) + { + XmlExpr *xmlexpr = (XmlExpr *) node; + + /* + * XMLVALIDATE's second argument is a Const containing the schema OID. + * Record a dependency on it. + */ + if (xmlexpr->op == IS_XMLVALIDATE && list_length(xmlexpr->args) == 2) + { + Node *schema_arg = (Node *) lsecond(xmlexpr->args); + + if (IsA(schema_arg, Const)) + { + Const *schema_const = (Const *) schema_arg; + Oid schema_oid; + + if (!schema_const->constisnull && + schema_const->consttype == OIDOID) + { + schema_oid = DatumGetObjectId(schema_const->constvalue); + add_object_address(XmlSchemaRelationId, schema_oid, 0, + context->addrs); + } + } + } + /* fall through to examine arguments */ + } return expression_tree_walker(node, find_expr_references_walker, context); diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 61ff5ddc74..7ab442d6bf 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -4630,6 +4630,33 @@ ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op) } break; + case IS_XMLVALIDATE: + { + Datum *argvalue = op->d.xmlexpr.argvalue; + bool *argnull = op->d.xmlexpr.argnull; + xmltype *data; + Oid schema_oid; + xmltype *result; + + /* Two arguments: XML data and schema OID */ + Assert(list_length(xexpr->args) == 2); + + if (argnull[0] || argnull[1]) + { + *op->resnull = true; + return; + } + + data = DatumGetXmlP(argvalue[0]); + schema_oid = DatumGetObjectId(argvalue[1]); + + result = xmlvalidate_schema(data, schema_oid); + + *op->resvalue = PointerGetDatum(result); + *op->resnull = false; + } + break; + case IS_DOCUMENT: { Datum *argvalue = op->d.xmlexpr.argvalue; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 9886250c25..9efc1e97d1 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -707,7 +707,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); */ /* ordinary key words in alphabetical order */ -%token ABORT_P ABSENT ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER +%token ABORT_P ABSENT ABSOLUTE_P ACCESS ACCORDING ACTION ADD_P ADMIN AFTER AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC ATOMIC AT ATTACH ATTRIBUTE AUTHORIZATION @@ -798,7 +798,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); WAIT WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLNAMESPACES - XMLPARSE XMLPI XMLROOT XMLSCHEMA XMLSERIALIZE XMLTABLE + XMLPARSE XMLPI XMLROOT XMLSCHEMA XMLSERIALIZE XMLTABLE XMLVALIDATE YEAR_P YES_P @@ -16348,6 +16348,17 @@ func_expr_common_subexpr: n->location = @1; $$ = (Node *) n; } + | XMLVALIDATE '(' DOCUMENT_P a_expr ACCORDING TO XMLSCHEMA any_name ')' + { + XmlExpr *x = (XmlExpr *) + makeXmlExpr(IS_XMLVALIDATE, NULL, $8, + list_make1($4), + @1); + + x->xmloption = XMLOPTION_DOCUMENT; + x->location = @1; + $$ = (Node *) x; + } | JSON_OBJECT '(' func_arg_list ')' { /* Support for legacy (non-standard) json_object() */ @@ -17958,6 +17969,7 @@ unreserved_keyword: | ABSENT | ABSOLUTE_P | ACCESS + | ACCORDING | ACTION | ADD_P | ADMIN @@ -18367,6 +18379,7 @@ col_name_keyword: | XMLROOT | XMLSERIALIZE | XMLTABLE + | XMLVALIDATE ; /* Type/function identifier --- keywords that can be type or function names. @@ -18506,6 +18519,7 @@ bare_label_keyword: | ABSENT | ABSOLUTE_P | ACCESS + | ACCORDING | ACTION | ADD_P | ADMIN @@ -18960,6 +18974,7 @@ bare_label_keyword: | XMLSCHEMA | XMLSERIALIZE | XMLTABLE + | XMLVALIDATE | YES_P | ZONE ; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index dcfe1acc4c..098f13f665 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -16,6 +16,7 @@ #include "postgres.h" #include "access/htup_details.h" +#include "catalog/namespace.h" #include "catalog/pg_aggregate.h" #include "catalog/pg_type.h" #include "miscadmin.h" @@ -2370,6 +2371,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) XmlExpr *newx; ListCell *lc; int i; + Oid xmlvalidate_schema_oid = InvalidOid; newx = makeNode(XmlExpr); newx->op = x->op; @@ -2382,6 +2384,22 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) newx->typmod = -1; newx->location = x->location; + /* + * XMLVALIDATE stores the schema name list in named_args, not ResTargets. + * Extract it before processing named arguments. + */ + if (x->op == IS_XMLVALIDATE && x->named_args != NIL) + { + List *schema_name_list; + + schema_name_list = x->named_args; + xmlvalidate_schema_oid = get_xmlschema_oid(schema_name_list, false); + /* Preserve schema name for deparsing */ + newx->name = NameListToString(schema_name_list); + /* Clear to avoid processing as ResTargets */ + x->named_args = NIL; + } + /* * gram.y built the named args as a list of ResTarget. Transform each, * and break the names out as a separate list. @@ -2481,6 +2499,11 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) /* not handled here */ Assert(false); break; + case IS_XMLVALIDATE: + /* First argument is the XML data */ + newe = coerce_to_specific_type(pstate, newe, XMLOID, + "XMLVALIDATE"); + break; case IS_DOCUMENT: newe = coerce_to_specific_type(pstate, newe, XMLOID, "IS DOCUMENT"); @@ -2490,6 +2513,26 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) i++; } + /* For XMLVALIDATE, add the schema OID as second argument */ + if (x->op == IS_XMLVALIDATE) + { + Const *schema_oid_const; + + Assert(OidIsValid(xmlvalidate_schema_oid)); + + schema_oid_const = makeConst(OIDOID, + -1, + InvalidOid, + sizeof(Oid), + ObjectIdGetDatum(xmlvalidate_schema_oid), + false, + true); + newx->args = lappend(newx->args, schema_oid_const); + + /* Return type is XML */ + newx->type = XMLOID; + } + return (Node *) newx; } diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index b5a2f915b6..b380bb39eb 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1976,6 +1976,9 @@ FigureColnameInternal(Node *node, char **name) case IS_XMLSERIALIZE: *name = "xmlserialize"; return 2; + case IS_XMLVALIDATE: + *name = "xmlvalidate"; + return 2; case IS_DOCUMENT: /* nothing */ break; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index b5a7ad9066..cd8a9169a6 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -10119,17 +10119,20 @@ get_rule_expr(Node *node, deparse_context *context, case IS_XMLSERIALIZE: appendStringInfoString(buf, "XMLSERIALIZE("); break; + case IS_XMLVALIDATE: + appendStringInfoString(buf, "XMLVALIDATE("); + break; case IS_DOCUMENT: break; } - if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE) + if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE || xexpr->op == IS_XMLVALIDATE) { if (xexpr->xmloption == XMLOPTION_DOCUMENT) appendStringInfoString(buf, "DOCUMENT "); else appendStringInfoString(buf, "CONTENT "); } - if (xexpr->name) + if (xexpr->name && xexpr->op != IS_XMLVALIDATE) { appendStringInfo(buf, "NAME %s", quote_identifier(map_xml_name_to_sql_identifier(xexpr->name))); @@ -10173,6 +10176,9 @@ get_rule_expr(Node *node, deparse_context *context, /* no extra decoration needed */ get_rule_expr((Node *) xexpr->args, context, true); break; + case IS_XMLVALIDATE: + get_rule_expr((Node *) linitial(xexpr->args), context, true); + break; case IS_XMLPARSE: Assert(list_length(xexpr->args) == 2); @@ -10242,6 +10248,13 @@ get_rule_expr(Node *node, deparse_context *context, appendStringInfoString(buf, " NO INDENT"); } + if (xexpr->op == IS_XMLVALIDATE) + { + /* Output the schema name stored during parse analysis */ + appendStringInfo(buf, " ACCORDING TO XMLSCHEMA %s", + quote_identifier(xexpr->name)); + } + if (xexpr->op == IS_DOCUMENT) appendStringInfoString(buf, " IS DOCUMENT"); else diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index f69dc68286..d57c3c07f9 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -58,6 +58,7 @@ #include #include #include +#include /* * We used to check for xmlStructuredErrorContext via a configure test; but @@ -84,6 +85,7 @@ #include "catalog/namespace.h" #include "catalog/pg_class.h" #include "catalog/pg_type.h" +#include "catalog/pg_xmlschema.h" #include "executor/spi.h" #include "executor/tablefunc.h" #include "fmgr.h" @@ -94,6 +96,7 @@ #include "nodes/execnodes.h" #include "nodes/miscnodes.h" #include "nodes/nodeFuncs.h" +#include "utils/acl.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/date.h" @@ -1158,10 +1161,147 @@ xmlvalidate(PG_FUNCTION_ARGS) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("xmlvalidate is not implemented"))); + errmsg("xmlvalidate is not implemented against generalized schema definitions"))); return 0; } +/* + * xmlvalidate_schema - validate XML document against a registered XML Schema + * + * Validates the given XML document against the schema identified by a schema_oid. + * Returns the validated XML value, or raises an error if the validation fails. + */ +xmltype * +xmlvalidate_schema(xmltype *data, Oid schema_oid) +{ +#ifdef USE_LIBXML + HeapTuple tuple; + Datum schema_datum; + bool isnull; + xmltype *schema_xml; + char *schemastr; + volatile xmlDocPtr doc = NULL; + volatile xmlSchemaParserCtxtPtr schema_parser_ctxt = NULL; + volatile xmlSchemaPtr schema_ptr = NULL; + volatile xmlSchemaValidCtxtPtr valid_ctxt = NULL; + int result; + PgXmlErrorContext *xmlerrcxt; + AclResult aclresult; + + /* Check usage permission first */ + aclresult = object_aclcheck(XmlSchemaRelationId, schema_oid, + GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + { + /* Fetch tuple only to get name for the error message */ + Form_pg_xmlschema schema_form; + + tuple = SearchSysCache1(XMLSCHEMAOID, ObjectIdGetDatum(schema_oid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for XML schema %u", schema_oid); + + schema_form = (Form_pg_xmlschema) GETSTRUCT(tuple); + ReleaseSysCache(tuple); + + aclcheck_error(aclresult, OBJECT_XMLSCHEMA, + NameStr(schema_form->schemaname)); + } + + tuple = SearchSysCache1(XMLSCHEMAOID, ObjectIdGetDatum(schema_oid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for XML schema %u", schema_oid); + + schema_datum = SysCacheGetAttr(XMLSCHEMAOID, tuple, + Anum_pg_xmlschema_schemadata, &isnull); + if (isnull) + elog(ERROR, "null schemadata for XML schema %u", schema_oid); + + schema_xml = DatumGetXmlP(schema_datum); + schemastr = text_to_cstring(schema_xml); + ReleaseSysCache(tuple); + + xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_WELLFORMED); + + PG_TRY(); + { + ErrorSaveContext escontext = {T_ErrorSaveContext}; + + doc = xml_parse((text *) data, XMLOPTION_DOCUMENT, true, + GetDatabaseEncoding(), NULL, NULL, (Node *) &escontext); + + if (escontext.error_occurred || doc == NULL) + { + if (escontext.error_occurred && escontext.error_data) + { + ErrorData *edata = escontext.error_data; + + ereport(ERROR, + (errcode(ERRCODE_INVALID_XML_DOCUMENT), + errmsg("invalid XML document"), + errdetail_internal("%s", edata->message ? edata->message : "unknown error"))); + } + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT, + "invalid XML document"); + } + + schema_parser_ctxt = xmlSchemaNewMemParserCtxt(schemastr, strlen(schemastr)); + if (schema_parser_ctxt == NULL) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR, + "failed to create schema parser context"); + + schema_ptr = xmlSchemaParse(schema_parser_ctxt); + if (schema_ptr == NULL) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT, + "failed to parse XML schema"); + + valid_ctxt = xmlSchemaNewValidCtxt(schema_ptr); + if (valid_ctxt == NULL) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, + "failed to create schema validation context"); + + xmlSchemaSetValidStructuredErrors(valid_ctxt, xml_errorHandler, xmlerrcxt); + + result = xmlSchemaValidateDoc(valid_ctxt, doc); + if (result < 0) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR, + "internal error during schema validation"); + if (result > 0) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT, + "XML validation failed"); + } + PG_CATCH(); + { + if (valid_ctxt) + xmlSchemaFreeValidCtxt(valid_ctxt); + if (schema_ptr) + xmlSchemaFree(schema_ptr); + if (schema_parser_ctxt) + xmlSchemaFreeParserCtxt(schema_parser_ctxt); + if (doc) + xmlFreeDoc(doc); + pg_xml_done(xmlerrcxt, true); + pfree(schemastr); + PG_RE_THROW(); + } + PG_END_TRY(); + + if (valid_ctxt) + xmlSchemaFreeValidCtxt(valid_ctxt); + if (schema_ptr) + xmlSchemaFree(schema_ptr); + if (schema_parser_ctxt) + xmlSchemaFreeParserCtxt(schema_parser_ctxt); + if (doc) + xmlFreeDoc(doc); + + pg_xml_done(xmlerrcxt, false); + pfree(schemastr); + return data; +#else + NO_XML_SUPPORT(); + return NULL; +#endif +} bool xml_is_document(xmltype *arg) @@ -1181,7 +1321,7 @@ xml_is_document(xmltype *arg) return !escontext.error_occurred; #else /* not USE_LIBXML */ NO_XML_SUPPORT(); - return false; + return NULL; #endif /* not USE_LIBXML */ } diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 5211cadc25..c40cbc8981 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1610,6 +1610,7 @@ typedef enum XmlExprOp IS_XMLROOT, /* XMLROOT(xml, version, standalone) */ IS_XMLSERIALIZE, /* XMLSERIALIZE(is_document, xmlval, indent) */ IS_DOCUMENT, /* xmlval IS DOCUMENT */ + IS_XMLVALIDATE, /* XMLVALIDATE(xmlval, schema) */ } XmlExprOp; typedef enum XmlOptionType diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index a018c2b9e0..4e8b5e1d7e 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -29,6 +29,7 @@ PG_KEYWORD("abort", ABORT_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("absent", ABSENT, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("absolute", ABSOLUTE_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("access", ACCESS, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("according", ACCORDING, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("action", ACTION, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("add", ADD_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("admin", ADMIN, UNRESERVED_KEYWORD, BARE_LABEL) @@ -523,6 +524,7 @@ PG_KEYWORD("xmlroot", XMLROOT, COL_NAME_KEYWORD, BARE_LABEL) PG_KEYWORD("xmlschema", XMLSCHEMA, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("xmlserialize", XMLSERIALIZE, COL_NAME_KEYWORD, BARE_LABEL) PG_KEYWORD("xmltable", XMLTABLE, COL_NAME_KEYWORD, BARE_LABEL) +PG_KEYWORD("xmlvalidate", XMLVALIDATE, COL_NAME_KEYWORD, BARE_LABEL) PG_KEYWORD("year", YEAR_P, UNRESERVED_KEYWORD, AS_LABEL) PG_KEYWORD("yes", YES_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("zone", ZONE, UNRESERVED_KEYWORD, BARE_LABEL) diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h index 03acb25544..dc6a4d3784 100644 --- a/src/include/utils/xml.h +++ b/src/include/utils/xml.h @@ -76,6 +76,7 @@ extern xmltype *xmlelement(XmlExpr *xexpr, extern xmltype *xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace); extern xmltype *xmlpi(const char *target, text *arg, bool arg_is_null, bool *result_is_null); extern xmltype *xmlroot(xmltype *data, text *version, int standalone); +extern xmltype *xmlvalidate_schema(xmltype *data, Oid schema_oid); extern bool xml_is_document(xmltype *arg); extern text *xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent); diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 4e24657d2d..121d86d85d 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -1945,3 +1945,236 @@ CREATE XMLSCHEMA book_schema AS ' '; +SELECT XMLVALIDATE(DOCUMENT 'John30' + ACCORDING TO XMLSCHEMA person_schema); + xmlvalidate +------------------------------------------------- + John30 +(1 row) + +SELECT XMLVALIDATE(DOCUMENT 'Widget9.99' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.product_schema); + xmlvalidate +--------------------------------------------------------------------- + Widget9.99 +(1 row) + +SELECT XMLVALIDATE(DOCUMENT + ' + PostgreSQL Internals + JohnTitor + Jane + 2024 + ' + ACCORDING TO XMLSCHEMA book_schema); +ERROR: XML validation failed +SELECT XMLSERIALIZE(DOCUMENT + XMLVALIDATE(DOCUMENT 'Alice25' + ACCORDING TO XMLSCHEMA person_schema) AS text); + xmlserialize +-------------------------------------------------- + Alice25 +(1 row) + +SELECT XMLVALIDATE(DOCUMENT NULL ACCORDING TO XMLSCHEMA person_schema); + xmlvalidate +------------- + +(1 row) + +SELECT XMLVALIDATE(DOCUMENT NULL ACCORDING TO XMLSCHEMA person_schema) IS NULL AS is_null; + is_null +--------- + t +(1 row) + +SELECT XMLVALIDATE(DOCUMENT 'John' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML validation failed +SELECT XMLVALIDATE(DOCUMENT 'Johnnot-a-number' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML validation failed +SELECT XMLVALIDATE(DOCUMENT 'Widget9.99' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.product_schema); +ERROR: XML validation failed +SELECT XMLVALIDATE(DOCUMENT 'John30data' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML validation failed +SELECT XMLVALIDATE(DOCUMENT 'John30' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML validation failed +SELECT XMLVALIDATE(DOCUMENT 'value' + ACCORDING TO XMLSCHEMA nonexistent_schema); +ERROR: XML schema "nonexistent_schema" does not exist +CREATE VIEW validated_people AS + SELECT XMLVALIDATE(DOCUMENT data ACCORDING TO XMLSCHEMA person_schema) AS validated_xml + FROM xmltest WHERE id = 1; +SELECT pg_get_viewdef('validated_people'::regclass, true); + pg_get_viewdef +------------------------------------------------------------------------------------------ + SELECT XMLVALIDATE(DOCUMENT data ACCORDING TO XMLSCHEMA person_schema) AS validated_xml+ + FROM xmltest + + WHERE id = 1; +(1 row) + +DROP VIEW validated_people; +CREATE VIEW validated_products AS + SELECT XMLVALIDATE(DOCUMENT 'Test1.99' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.product_schema) AS validated_xml; +SELECT pg_get_viewdef('validated_products'::regclass, true); + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + SELECT XMLVALIDATE(DOCUMENT 'Test1.99'::xml ACCORDING TO XMLSCHEMA "test_xmlschema_ns.product_schema") AS validated_xml; +(1 row) + +DROP VIEW validated_products; +ALTER XMLSCHEMA book_schema RENAME TO library_book_schema; +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA book_schema); +ERROR: XML schema "book_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA library_book_schema); + xmlvalidate +----------------------------------------------------------------------------------------------------------------------------- + TestAB2024 +(1 row) + +ALTER XMLSCHEMA library_book_schema SET SCHEMA test_xmlschema_ns; +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA library_book_schema); +ERROR: XML schema "library_book_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.library_book_schema); + xmlvalidate +----------------------------------------------------------------------------------------------------------------------------- + TestAB2024 +(1 row) + +CREATE ROLE regress_xmlschema_test_role; +ALTER XMLSCHEMA test_xmlschema_ns.library_book_schema OWNER TO regress_xmlschema_test_role; +SELECT schemaname, schemanamespace::regnamespace, schemaowner::regrole +FROM pg_xmlschema +WHERE schemaname = 'library_book_schema'; + schemaname | schemanamespace | schemaowner +---------------------+-------------------+----------------------------- + library_book_schema | test_xmlschema_ns | regress_xmlschema_test_role +(1 row) + +CREATE VIEW book_view AS + SELECT XMLVALIDATE(DOCUMENT 'Dep TestXY2025' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.library_book_schema) AS validated_book; +DROP XMLSCHEMA test_xmlschema_ns.library_book_schema; +ERROR: cannot drop XML schema test_xmlschema_ns.library_book_schema because other objects depend on it +DETAIL: view book_view depends on XML schema test_xmlschema_ns.library_book_schema +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP XMLSCHEMA test_xmlschema_ns.library_book_schema CASCADE; +NOTICE: drop cascades to view book_view +SELECT * FROM book_view; +ERROR: relation "book_view" does not exist +LINE 1: SELECT * FROM book_view; + ^ +DROP XMLSCHEMA person_schema; +DROP XMLSCHEMA IF EXISTS person_schema; +NOTICE: XML schema "person_schema" does not exist, skipping +DROP XMLSCHEMA person_schema; +ERROR: XML schema "person_schema" does not exist +DROP XMLSCHEMA test_xmlschema_ns.product_schema; +SET ROLE regress_xmlschema_test_role; +CREATE XMLSCHEMA public.should_fail_schema AS ' + + +'; +RESET ROLE; +GRANT CREATE ON SCHEMA test_xmlschema_ns TO regress_xmlschema_test_role; +SET ROLE regress_xmlschema_test_role; +CREATE XMLSCHEMA test_xmlschema_ns.role_schema AS ' + + +'; +RESET ROLE; +DROP XMLSCHEMA test_xmlschema_ns.role_schema; +DROP ROLE regress_xmlschema_test_role; +ERROR: role "regress_xmlschema_test_role" cannot be dropped because some objects depend on it +DETAIL: privileges for schema test_xmlschema_ns +owner of XML schema public.should_fail_schema +DROP SCHEMA test_xmlschema_ns CASCADE; +CREATE ROLE regress_xmlschema_user1; +CREATE ROLE regress_xmlschema_user2; +CREATE XMLSCHEMA permission_test_schema AS ' + + +'; +SET ROLE regress_xmlschema_user1; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); +ERROR: permission denied for XML schema permission_test_schema +RESET ROLE; +GRANT USAGE ON XMLSCHEMA permission_test_schema TO regress_xmlschema_user1; +SET ROLE regress_xmlschema_user1; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); + xmlvalidate +------------------- + data +(1 row) + +RESET ROLE; +SET ROLE regress_xmlschema_user2; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); +ERROR: permission denied for XML schema permission_test_schema +RESET ROLE; +GRANT USAGE ON XMLSCHEMA permission_test_schema TO regress_xmlschema_user2; +SET ROLE regress_xmlschema_user2; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); + xmlvalidate +------------------- + data +(1 row) + +RESET ROLE; +REVOKE USAGE ON XMLSCHEMA permission_test_schema FROM regress_xmlschema_user1; +SET ROLE regress_xmlschema_user1; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); +ERROR: permission denied for XML schema permission_test_schema +RESET ROLE; +DROP XMLSCHEMA permission_test_schema; +DROP ROLE regress_xmlschema_user1; +DROP ROLE regress_xmlschema_user2; +CREATE TABLE validated_xml_data ( + id serial PRIMARY KEY, + data xml +); +CREATE XMLSCHEMA data_schema AS ' + + + + + + + + +'; +INSERT INTO validated_xml_data (data) +VALUES (XMLVALIDATE(DOCUMENT '42' + ACCORDING TO XMLSCHEMA data_schema)); +INSERT INTO validated_xml_data (data) +VALUES (XMLVALIDATE(DOCUMENT '100' + ACCORDING TO XMLSCHEMA data_schema)); +SELECT id, data FROM validated_xml_data ORDER BY id; + id | data +----+--------------------------------- + 1 | 42 + 2 | 100 +(2 rows) + +INSERT INTO validated_xml_data (data) +VALUES (XMLVALIDATE(DOCUMENT 'not-an-int' + ACCORDING TO XMLSCHEMA data_schema)); +ERROR: XML validation failed +DROP TABLE validated_xml_data; +DROP XMLSCHEMA data_schema; +DROP XMLSCHEMA should_fail_schema; +DROP ROLE regress_xmlschema_test_role; diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out index aded57c78b..7b088380c4 100644 --- a/src/test/regress/expected/xml_1.out +++ b/src/test/regress/expected/xml_1.out @@ -1557,3 +1557,211 @@ CREATE XMLSCHEMA book_schema AS ' '; ERROR: xmlschema support requires libxml +SELECT XMLVALIDATE(DOCUMENT 'John30' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML schema "person_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'Widget9.99' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.product_schema); +ERROR: XML schema "test_xmlschema_ns.product_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT + ' + PostgreSQL Internals + JohnTitor + Jane + 2024 + ' + ACCORDING TO XMLSCHEMA book_schema); +ERROR: XML schema "book_schema" does not exist +SELECT XMLSERIALIZE(DOCUMENT + XMLVALIDATE(DOCUMENT 'Alice25' + ACCORDING TO XMLSCHEMA person_schema) AS text); +ERROR: XML schema "person_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT NULL ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML schema "person_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT NULL ACCORDING TO XMLSCHEMA person_schema) IS NULL AS is_null; +ERROR: XML schema "person_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'John' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML schema "person_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'Johnnot-a-number' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML schema "person_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'Widget9.99' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.product_schema); +ERROR: XML schema "test_xmlschema_ns.product_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'John30data' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML schema "person_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'John30' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML schema "person_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'value' + ACCORDING TO XMLSCHEMA nonexistent_schema); +ERROR: XML schema "nonexistent_schema" does not exist +CREATE VIEW validated_people AS + SELECT XMLVALIDATE(DOCUMENT data ACCORDING TO XMLSCHEMA person_schema) AS validated_xml + FROM xmltest WHERE id = 1; +ERROR: XML schema "person_schema" does not exist +SELECT pg_get_viewdef('validated_people'::regclass, true); +ERROR: relation "validated_people" does not exist +LINE 1: SELECT pg_get_viewdef('validated_people'::regclass, true); + ^ +DROP VIEW validated_people; +ERROR: view "validated_people" does not exist +CREATE VIEW validated_products AS + SELECT XMLVALIDATE(DOCUMENT 'Test1.99' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.product_schema) AS validated_xml; +ERROR: XML schema "test_xmlschema_ns.product_schema" does not exist +SELECT pg_get_viewdef('validated_products'::regclass, true); +ERROR: relation "validated_products" does not exist +LINE 1: SELECT pg_get_viewdef('validated_products'::regclass, true); + ^ +DROP VIEW validated_products; +ERROR: view "validated_products" does not exist +ALTER XMLSCHEMA book_schema RENAME TO library_book_schema; +ERROR: XML schema "book_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA book_schema); +ERROR: XML schema "book_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA library_book_schema); +ERROR: XML schema "library_book_schema" does not exist +ALTER XMLSCHEMA library_book_schema SET SCHEMA test_xmlschema_ns; +ERROR: XML schema "library_book_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA library_book_schema); +ERROR: XML schema "library_book_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.library_book_schema); +ERROR: XML schema "test_xmlschema_ns.library_book_schema" does not exist +CREATE ROLE regress_xmlschema_test_role; +ALTER XMLSCHEMA test_xmlschema_ns.library_book_schema OWNER TO regress_xmlschema_test_role; +ERROR: XML schema "test_xmlschema_ns.library_book_schema" does not exist +SELECT schemaname, schemanamespace::regnamespace, schemaowner::regrole +FROM pg_xmlschema +WHERE schemaname = 'library_book_schema'; + schemaname | schemanamespace | schemaowner +------------+-----------------+------------- +(0 rows) + +CREATE VIEW book_view AS + SELECT XMLVALIDATE(DOCUMENT 'Dep TestXY2025' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.library_book_schema) AS validated_book; +ERROR: XML schema "test_xmlschema_ns.library_book_schema" does not exist +DROP XMLSCHEMA test_xmlschema_ns.library_book_schema; +ERROR: XML schema "test_xmlschema_ns.library_book_schema" does not exist +DROP XMLSCHEMA test_xmlschema_ns.library_book_schema CASCADE; +ERROR: XML schema "test_xmlschema_ns.library_book_schema" does not exist +SELECT * FROM book_view; +ERROR: relation "book_view" does not exist +LINE 1: SELECT * FROM book_view; + ^ +DROP XMLSCHEMA person_schema; +ERROR: XML schema "person_schema" does not exist +DROP XMLSCHEMA IF EXISTS person_schema; +NOTICE: XML schema "person_schema" does not exist, skipping +DROP XMLSCHEMA person_schema; +ERROR: XML schema "person_schema" does not exist +DROP XMLSCHEMA test_xmlschema_ns.product_schema; +ERROR: XML schema "test_xmlschema_ns.product_schema" does not exist +SET ROLE regress_xmlschema_test_role; +CREATE XMLSCHEMA public.should_fail_schema AS ' + + +'; +ERROR: xmlschema support requires libxml +RESET ROLE; +GRANT CREATE ON SCHEMA test_xmlschema_ns TO regress_xmlschema_test_role; +SET ROLE regress_xmlschema_test_role; +CREATE XMLSCHEMA test_xmlschema_ns.role_schema AS ' + + +'; +ERROR: xmlschema support requires libxml +RESET ROLE; +DROP XMLSCHEMA test_xmlschema_ns.role_schema; +ERROR: XML schema "test_xmlschema_ns.role_schema" does not exist +DROP ROLE regress_xmlschema_test_role; +ERROR: role "regress_xmlschema_test_role" cannot be dropped because some objects depend on it +DETAIL: privileges for schema test_xmlschema_ns +DROP SCHEMA test_xmlschema_ns CASCADE; +CREATE ROLE regress_xmlschema_user1; +CREATE ROLE regress_xmlschema_user2; +CREATE XMLSCHEMA permission_test_schema AS ' + + +'; +ERROR: xmlschema support requires libxml +SET ROLE regress_xmlschema_user1; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); +ERROR: XML schema "permission_test_schema" does not exist +RESET ROLE; +GRANT USAGE ON XMLSCHEMA permission_test_schema TO regress_xmlschema_user1; +ERROR: XML schema "permission_test_schema" does not exist +SET ROLE regress_xmlschema_user1; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); +ERROR: XML schema "permission_test_schema" does not exist +RESET ROLE; +SET ROLE regress_xmlschema_user2; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); +ERROR: XML schema "permission_test_schema" does not exist +RESET ROLE; +GRANT USAGE ON XMLSCHEMA permission_test_schema TO regress_xmlschema_user2; +ERROR: XML schema "permission_test_schema" does not exist +SET ROLE regress_xmlschema_user2; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); +ERROR: XML schema "permission_test_schema" does not exist +RESET ROLE; +REVOKE USAGE ON XMLSCHEMA permission_test_schema FROM regress_xmlschema_user1; +ERROR: XML schema "permission_test_schema" does not exist +SET ROLE regress_xmlschema_user1; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); +ERROR: XML schema "permission_test_schema" does not exist +RESET ROLE; +DROP XMLSCHEMA permission_test_schema; +ERROR: XML schema "permission_test_schema" does not exist +DROP ROLE regress_xmlschema_user1; +DROP ROLE regress_xmlschema_user2; +CREATE TABLE validated_xml_data ( + id serial PRIMARY KEY, + data xml +); +CREATE XMLSCHEMA data_schema AS ' + + + + + + + + +'; +ERROR: xmlschema support requires libxml +INSERT INTO validated_xml_data (data) +VALUES (XMLVALIDATE(DOCUMENT '42' + ACCORDING TO XMLSCHEMA data_schema)); +ERROR: XML schema "data_schema" does not exist +INSERT INTO validated_xml_data (data) +VALUES (XMLVALIDATE(DOCUMENT '100' + ACCORDING TO XMLSCHEMA data_schema)); +ERROR: XML schema "data_schema" does not exist +SELECT id, data FROM validated_xml_data ORDER BY id; + id | data +----+------ +(0 rows) + +INSERT INTO validated_xml_data (data) +VALUES (XMLVALIDATE(DOCUMENT 'not-an-int' + ACCORDING TO XMLSCHEMA data_schema)); +ERROR: XML schema "data_schema" does not exist +DROP TABLE validated_xml_data; +DROP XMLSCHEMA data_schema; +ERROR: XML schema "data_schema" does not exist +DROP XMLSCHEMA should_fail_schema; +ERROR: XML schema "should_fail_schema" does not exist +DROP ROLE regress_xmlschema_test_role; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 4e24657d2d..121d86d85d 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -1945,3 +1945,236 @@ CREATE XMLSCHEMA book_schema AS ' '; +SELECT XMLVALIDATE(DOCUMENT 'John30' + ACCORDING TO XMLSCHEMA person_schema); + xmlvalidate +------------------------------------------------- + John30 +(1 row) + +SELECT XMLVALIDATE(DOCUMENT 'Widget9.99' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.product_schema); + xmlvalidate +--------------------------------------------------------------------- + Widget9.99 +(1 row) + +SELECT XMLVALIDATE(DOCUMENT + ' + PostgreSQL Internals + JohnTitor + Jane + 2024 + ' + ACCORDING TO XMLSCHEMA book_schema); +ERROR: XML validation failed +SELECT XMLSERIALIZE(DOCUMENT + XMLVALIDATE(DOCUMENT 'Alice25' + ACCORDING TO XMLSCHEMA person_schema) AS text); + xmlserialize +-------------------------------------------------- + Alice25 +(1 row) + +SELECT XMLVALIDATE(DOCUMENT NULL ACCORDING TO XMLSCHEMA person_schema); + xmlvalidate +------------- + +(1 row) + +SELECT XMLVALIDATE(DOCUMENT NULL ACCORDING TO XMLSCHEMA person_schema) IS NULL AS is_null; + is_null +--------- + t +(1 row) + +SELECT XMLVALIDATE(DOCUMENT 'John' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML validation failed +SELECT XMLVALIDATE(DOCUMENT 'Johnnot-a-number' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML validation failed +SELECT XMLVALIDATE(DOCUMENT 'Widget9.99' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.product_schema); +ERROR: XML validation failed +SELECT XMLVALIDATE(DOCUMENT 'John30data' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML validation failed +SELECT XMLVALIDATE(DOCUMENT 'John30' + ACCORDING TO XMLSCHEMA person_schema); +ERROR: XML validation failed +SELECT XMLVALIDATE(DOCUMENT 'value' + ACCORDING TO XMLSCHEMA nonexistent_schema); +ERROR: XML schema "nonexistent_schema" does not exist +CREATE VIEW validated_people AS + SELECT XMLVALIDATE(DOCUMENT data ACCORDING TO XMLSCHEMA person_schema) AS validated_xml + FROM xmltest WHERE id = 1; +SELECT pg_get_viewdef('validated_people'::regclass, true); + pg_get_viewdef +------------------------------------------------------------------------------------------ + SELECT XMLVALIDATE(DOCUMENT data ACCORDING TO XMLSCHEMA person_schema) AS validated_xml+ + FROM xmltest + + WHERE id = 1; +(1 row) + +DROP VIEW validated_people; +CREATE VIEW validated_products AS + SELECT XMLVALIDATE(DOCUMENT 'Test1.99' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.product_schema) AS validated_xml; +SELECT pg_get_viewdef('validated_products'::regclass, true); + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + SELECT XMLVALIDATE(DOCUMENT 'Test1.99'::xml ACCORDING TO XMLSCHEMA "test_xmlschema_ns.product_schema") AS validated_xml; +(1 row) + +DROP VIEW validated_products; +ALTER XMLSCHEMA book_schema RENAME TO library_book_schema; +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA book_schema); +ERROR: XML schema "book_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA library_book_schema); + xmlvalidate +----------------------------------------------------------------------------------------------------------------------------- + TestAB2024 +(1 row) + +ALTER XMLSCHEMA library_book_schema SET SCHEMA test_xmlschema_ns; +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA library_book_schema); +ERROR: XML schema "library_book_schema" does not exist +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.library_book_schema); + xmlvalidate +----------------------------------------------------------------------------------------------------------------------------- + TestAB2024 +(1 row) + +CREATE ROLE regress_xmlschema_test_role; +ALTER XMLSCHEMA test_xmlschema_ns.library_book_schema OWNER TO regress_xmlschema_test_role; +SELECT schemaname, schemanamespace::regnamespace, schemaowner::regrole +FROM pg_xmlschema +WHERE schemaname = 'library_book_schema'; + schemaname | schemanamespace | schemaowner +---------------------+-------------------+----------------------------- + library_book_schema | test_xmlschema_ns | regress_xmlschema_test_role +(1 row) + +CREATE VIEW book_view AS + SELECT XMLVALIDATE(DOCUMENT 'Dep TestXY2025' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.library_book_schema) AS validated_book; +DROP XMLSCHEMA test_xmlschema_ns.library_book_schema; +ERROR: cannot drop XML schema test_xmlschema_ns.library_book_schema because other objects depend on it +DETAIL: view book_view depends on XML schema test_xmlschema_ns.library_book_schema +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP XMLSCHEMA test_xmlschema_ns.library_book_schema CASCADE; +NOTICE: drop cascades to view book_view +SELECT * FROM book_view; +ERROR: relation "book_view" does not exist +LINE 1: SELECT * FROM book_view; + ^ +DROP XMLSCHEMA person_schema; +DROP XMLSCHEMA IF EXISTS person_schema; +NOTICE: XML schema "person_schema" does not exist, skipping +DROP XMLSCHEMA person_schema; +ERROR: XML schema "person_schema" does not exist +DROP XMLSCHEMA test_xmlschema_ns.product_schema; +SET ROLE regress_xmlschema_test_role; +CREATE XMLSCHEMA public.should_fail_schema AS ' + + +'; +RESET ROLE; +GRANT CREATE ON SCHEMA test_xmlschema_ns TO regress_xmlschema_test_role; +SET ROLE regress_xmlschema_test_role; +CREATE XMLSCHEMA test_xmlschema_ns.role_schema AS ' + + +'; +RESET ROLE; +DROP XMLSCHEMA test_xmlschema_ns.role_schema; +DROP ROLE regress_xmlschema_test_role; +ERROR: role "regress_xmlschema_test_role" cannot be dropped because some objects depend on it +DETAIL: privileges for schema test_xmlschema_ns +owner of XML schema public.should_fail_schema +DROP SCHEMA test_xmlschema_ns CASCADE; +CREATE ROLE regress_xmlschema_user1; +CREATE ROLE regress_xmlschema_user2; +CREATE XMLSCHEMA permission_test_schema AS ' + + +'; +SET ROLE regress_xmlschema_user1; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); +ERROR: permission denied for XML schema permission_test_schema +RESET ROLE; +GRANT USAGE ON XMLSCHEMA permission_test_schema TO regress_xmlschema_user1; +SET ROLE regress_xmlschema_user1; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); + xmlvalidate +------------------- + data +(1 row) + +RESET ROLE; +SET ROLE regress_xmlschema_user2; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); +ERROR: permission denied for XML schema permission_test_schema +RESET ROLE; +GRANT USAGE ON XMLSCHEMA permission_test_schema TO regress_xmlschema_user2; +SET ROLE regress_xmlschema_user2; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); + xmlvalidate +------------------- + data +(1 row) + +RESET ROLE; +REVOKE USAGE ON XMLSCHEMA permission_test_schema FROM regress_xmlschema_user1; +SET ROLE regress_xmlschema_user1; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); +ERROR: permission denied for XML schema permission_test_schema +RESET ROLE; +DROP XMLSCHEMA permission_test_schema; +DROP ROLE regress_xmlschema_user1; +DROP ROLE regress_xmlschema_user2; +CREATE TABLE validated_xml_data ( + id serial PRIMARY KEY, + data xml +); +CREATE XMLSCHEMA data_schema AS ' + + + + + + + + +'; +INSERT INTO validated_xml_data (data) +VALUES (XMLVALIDATE(DOCUMENT '42' + ACCORDING TO XMLSCHEMA data_schema)); +INSERT INTO validated_xml_data (data) +VALUES (XMLVALIDATE(DOCUMENT '100' + ACCORDING TO XMLSCHEMA data_schema)); +SELECT id, data FROM validated_xml_data ORDER BY id; + id | data +----+--------------------------------- + 1 | 42 + 2 | 100 +(2 rows) + +INSERT INTO validated_xml_data (data) +VALUES (XMLVALIDATE(DOCUMENT 'not-an-int' + ACCORDING TO XMLSCHEMA data_schema)); +ERROR: XML validation failed +DROP TABLE validated_xml_data; +DROP XMLSCHEMA data_schema; +DROP XMLSCHEMA should_fail_schema; +DROP ROLE regress_xmlschema_test_role; diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql index 667973420f..c1650ead94 100644 --- a/src/test/regress/sql/xml.sql +++ b/src/test/regress/sql/xml.sql @@ -742,3 +742,212 @@ CREATE XMLSCHEMA book_schema AS ' '; + +SELECT XMLVALIDATE(DOCUMENT 'John30' + ACCORDING TO XMLSCHEMA person_schema); + +SELECT XMLVALIDATE(DOCUMENT 'Widget9.99' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.product_schema); + +SELECT XMLVALIDATE(DOCUMENT + ' + PostgreSQL Internals + JohnTitor + Jane + 2024 + ' + ACCORDING TO XMLSCHEMA book_schema); + +SELECT XMLSERIALIZE(DOCUMENT + XMLVALIDATE(DOCUMENT 'Alice25' + ACCORDING TO XMLSCHEMA person_schema) AS text); + +SELECT XMLVALIDATE(DOCUMENT NULL ACCORDING TO XMLSCHEMA person_schema); + +SELECT XMLVALIDATE(DOCUMENT NULL ACCORDING TO XMLSCHEMA person_schema) IS NULL AS is_null; + +SELECT XMLVALIDATE(DOCUMENT 'John' + ACCORDING TO XMLSCHEMA person_schema); + +SELECT XMLVALIDATE(DOCUMENT 'Johnnot-a-number' + ACCORDING TO XMLSCHEMA person_schema); + +SELECT XMLVALIDATE(DOCUMENT 'Widget9.99' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.product_schema); + +SELECT XMLVALIDATE(DOCUMENT 'John30data' + ACCORDING TO XMLSCHEMA person_schema); + +SELECT XMLVALIDATE(DOCUMENT 'John30' + ACCORDING TO XMLSCHEMA person_schema); + +SELECT XMLVALIDATE(DOCUMENT 'value' + ACCORDING TO XMLSCHEMA nonexistent_schema); + +CREATE VIEW validated_people AS + SELECT XMLVALIDATE(DOCUMENT data ACCORDING TO XMLSCHEMA person_schema) AS validated_xml + FROM xmltest WHERE id = 1; + +SELECT pg_get_viewdef('validated_people'::regclass, true); + +DROP VIEW validated_people; + +CREATE VIEW validated_products AS + SELECT XMLVALIDATE(DOCUMENT 'Test1.99' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.product_schema) AS validated_xml; + +SELECT pg_get_viewdef('validated_products'::regclass, true); + +DROP VIEW validated_products; + +ALTER XMLSCHEMA book_schema RENAME TO library_book_schema; + +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA book_schema); + +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA library_book_schema); + +ALTER XMLSCHEMA library_book_schema SET SCHEMA test_xmlschema_ns; + +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA library_book_schema); + +SELECT XMLVALIDATE(DOCUMENT 'TestAB2024' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.library_book_schema); + +CREATE ROLE regress_xmlschema_test_role; + +ALTER XMLSCHEMA test_xmlschema_ns.library_book_schema OWNER TO regress_xmlschema_test_role; + +SELECT schemaname, schemanamespace::regnamespace, schemaowner::regrole +FROM pg_xmlschema +WHERE schemaname = 'library_book_schema'; + +CREATE VIEW book_view AS + SELECT XMLVALIDATE(DOCUMENT 'Dep TestXY2025' + ACCORDING TO XMLSCHEMA test_xmlschema_ns.library_book_schema) AS validated_book; + +DROP XMLSCHEMA test_xmlschema_ns.library_book_schema; + +DROP XMLSCHEMA test_xmlschema_ns.library_book_schema CASCADE; + +SELECT * FROM book_view; + +DROP XMLSCHEMA person_schema; + +DROP XMLSCHEMA IF EXISTS person_schema; + +DROP XMLSCHEMA person_schema; + +DROP XMLSCHEMA test_xmlschema_ns.product_schema; + +SET ROLE regress_xmlschema_test_role; + +CREATE XMLSCHEMA public.should_fail_schema AS ' + + +'; + +RESET ROLE; + +GRANT CREATE ON SCHEMA test_xmlschema_ns TO regress_xmlschema_test_role; + +SET ROLE regress_xmlschema_test_role; + +CREATE XMLSCHEMA test_xmlschema_ns.role_schema AS ' + + +'; + +RESET ROLE; + +DROP XMLSCHEMA test_xmlschema_ns.role_schema; + +DROP ROLE regress_xmlschema_test_role; + +DROP SCHEMA test_xmlschema_ns CASCADE; + +CREATE ROLE regress_xmlschema_user1; +CREATE ROLE regress_xmlschema_user2; + +CREATE XMLSCHEMA permission_test_schema AS ' + + +'; + +SET ROLE regress_xmlschema_user1; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); + +RESET ROLE; + +GRANT USAGE ON XMLSCHEMA permission_test_schema TO regress_xmlschema_user1; + +SET ROLE regress_xmlschema_user1; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); + +RESET ROLE; + +SET ROLE regress_xmlschema_user2; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); + +RESET ROLE; + +GRANT USAGE ON XMLSCHEMA permission_test_schema TO regress_xmlschema_user2; + +SET ROLE regress_xmlschema_user2; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); + +RESET ROLE; + +REVOKE USAGE ON XMLSCHEMA permission_test_schema FROM regress_xmlschema_user1; + +SET ROLE regress_xmlschema_user1; +SELECT XMLVALIDATE(DOCUMENT 'data' + ACCORDING TO XMLSCHEMA permission_test_schema); + +RESET ROLE; + +DROP XMLSCHEMA permission_test_schema; +DROP ROLE regress_xmlschema_user1; +DROP ROLE regress_xmlschema_user2; + +CREATE TABLE validated_xml_data ( + id serial PRIMARY KEY, + data xml +); + +CREATE XMLSCHEMA data_schema AS ' + + + + + + + + +'; + +INSERT INTO validated_xml_data (data) +VALUES (XMLVALIDATE(DOCUMENT '42' + ACCORDING TO XMLSCHEMA data_schema)); + +INSERT INTO validated_xml_data (data) +VALUES (XMLVALIDATE(DOCUMENT '100' + ACCORDING TO XMLSCHEMA data_schema)); + +SELECT id, data FROM validated_xml_data ORDER BY id; + +INSERT INTO validated_xml_data (data) +VALUES (XMLVALIDATE(DOCUMENT 'not-an-int' + ACCORDING TO XMLSCHEMA data_schema)); + +DROP TABLE validated_xml_data; +DROP XMLSCHEMA data_schema; +DROP XMLSCHEMA should_fail_schema; + +DROP ROLE regress_xmlschema_test_role; \ No newline at end of file -- 2.43.0