diff --git a/include/my_xml.h b/include/my_xml.h index c13f91a976ba7..c25cfe91380fc 100644 --- a/include/my_xml.h +++ b/include/my_xml.h @@ -39,6 +39,7 @@ extern "C" { \r, \n, \t characters. */ #define MY_XML_FLAG_SKIP_TEXT_NORMALIZATION 2 +#define MY_XML_FLAG_ASSERT_WELL_FORMED 4 enum my_xml_node_type { diff --git a/mysql-test/main/vector2.result b/mysql-test/main/vector2.result index 81160e8eb18dd..55f875f63fe11 100644 --- a/mysql-test/main/vector2.result +++ b/mysql-test/main/vector2.result @@ -6,7 +6,7 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp create table t1 (a int, b vector(0)); ERROR 42000: Incorrect column specifier for column 'b' create table t1 (a int, b vector(10) collate utf8mb3_general_ci); -ERROR 42000: Incorrect column specifier for column 'b' +ERROR HY000: Data type 'vector' doesn't support CHARACTER SET attribute. create table t1 (a int, b vector(10)); show create table t1; Table Create Table diff --git a/mysql-test/main/vector2.test b/mysql-test/main/vector2.test index e41a2f408ef52..03abbfb43742b 100644 --- a/mysql-test/main/vector2.test +++ b/mysql-test/main/vector2.test @@ -10,7 +10,7 @@ create table t1 (a int, b vector); --error ER_WRONG_FIELD_SPEC create table t1 (a int, b vector(0)); ---error ER_WRONG_FIELD_SPEC +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE create table t1 (a int, b vector(10) collate utf8mb3_general_ci); create table t1 (a int, b vector(10)); show create table t1; diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet4.result b/plugin/type_inet/mysql-test/type_inet/type_inet4.result index a18f56858957d..7c2d9e4f87dd5 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet4.result +++ b/plugin/type_inet/mysql-test/type_inet/type_inet4.result @@ -9,6 +9,12 @@ # CREATE TABLE t1 (a INET4 AUTO_INCREMENT); ERROR 42000: Incorrect column specifier for column 'a' +CREATE TABLE t1 (a INET4(6)); +ERROR HY000: Data type 'INET4' doesn't support LENGTH attribute. +CREATE TABLE t1 (a INET4 CHARACTER SET latin1); +ERROR HY000: Data type 'INET4' doesn't support CHARACTER SET attribute. +CREATE TABLE t1 (a INET4 REF_SYSTEM_ID=4); +ERROR HY000: Data type 'INET4' doesn't support REF_SYSTEM_ID attribute. CREATE TABLE t1 (a INET4); SHOW CREATE TABLE t1; Table Create Table @@ -239,6 +245,8 @@ CAST(REPEAT(0x11,16) AS INET4) NULL Warnings: Warning 1292 Incorrect inet4 value: '\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11' +SELECT CAST('0.0.0.0' AS INET4 CHARACTER SET latin1); +ERROR HY000: Data type 'INET4' doesn't support CHARACTER SET attribute. CREATE TABLE t1 AS SELECT CAST('0.0.0.0' AS INET4); SHOW CREATE TABLE t1; Table Create Table diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet4.test b/plugin/type_inet/mysql-test/type_inet/type_inet4.test index 1d71b86f9be46..592135296519f 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet4.test +++ b/plugin/type_inet/mysql-test/type_inet/type_inet4.test @@ -15,6 +15,16 @@ --error ER_WRONG_FIELD_SPEC CREATE TABLE t1 (a INET4 AUTO_INCREMENT); +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a INET4(6)); + +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a INET4 CHARACTER SET latin1); + +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a INET4 REF_SYSTEM_ID=4); + + CREATE TABLE t1 (a INET4); SHOW CREATE TABLE t1; DESCRIBE t1; @@ -76,6 +86,9 @@ SELECT CAST(0x01 AS INET4); SELECT CAST(REPEAT(0x00,16) AS INET4); SELECT CAST(REPEAT(0x11,16) AS INET4); +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +SELECT CAST('0.0.0.0' AS INET4 CHARACTER SET latin1); + CREATE TABLE t1 AS SELECT CAST('0.0.0.0' AS INET4); SHOW CREATE TABLE t1; DROP TABLE t1; diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6.result b/plugin/type_inet/mysql-test/type_inet/type_inet6.result index a739a8af0e756..2d75d26d9f003 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet6.result +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6.result @@ -3,6 +3,12 @@ # CREATE TABLE t1 (a INET6 AUTO_INCREMENT); ERROR 42000: Incorrect column specifier for column 'a' +CREATE TABLE t1 (a INET6(6)); +ERROR HY000: Data type 'INET6' doesn't support LENGTH attribute. +CREATE TABLE t1 (a INET6 CHARACTER SET latin1); +ERROR HY000: Data type 'INET6' doesn't support CHARACTER SET attribute. +CREATE TABLE t1 (a INET6 REF_SYSTEM_ID=4); +ERROR HY000: Data type 'INET6' doesn't support REF_SYSTEM_ID attribute. CREATE TABLE t1 (a INET6); SHOW CREATE TABLE t1; Table Create Table @@ -229,6 +235,8 @@ CAST(REPEAT(0x00,16) AS INET6) SELECT CAST(REPEAT(0x11,16) AS INET6); CAST(REPEAT(0x11,16) AS INET6) 1111:1111:1111:1111:1111:1111:1111:1111 +SELECT CAST('::' AS INET6 CHARACTER SET latin1); +ERROR HY000: Data type 'INET6' doesn't support CHARACTER SET attribute. CREATE TABLE t1 AS SELECT CAST('::' AS INET6); SHOW CREATE TABLE t1; Table Create Table @@ -2249,7 +2257,7 @@ DROP TABLE t1; # # MDEV-27015 Assertion `!is_null()' failed in FixedBinTypeBundle::Fbt FixedBinTypeBundle::Field_fbt::to_fbt() # -CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, a INET6(6) DEFAULT '::10'); +CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, a INET6 DEFAULT '::10'); INSERT INTO t1(id) VALUES (1), (2), (3), (4); INSERT INTO t1 VALUES (5,'::5'), (6,'::6'); SELECT * FROM t1 ORDER BY a; diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6.test b/plugin/type_inet/mysql-test/type_inet/type_inet6.test index 7d190aec148ef..ddef472661573 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet6.test +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6.test @@ -6,6 +6,15 @@ --error ER_WRONG_FIELD_SPEC CREATE TABLE t1 (a INET6 AUTO_INCREMENT); + +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a INET6(6)); + +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a INET6 CHARACTER SET latin1); + +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a INET6 REF_SYSTEM_ID=4); CREATE TABLE t1 (a INET6); SHOW CREATE TABLE t1; @@ -69,6 +78,9 @@ SELECT CAST(0x01 AS INET6); SELECT CAST(REPEAT(0x00,16) AS INET6); SELECT CAST(REPEAT(0x11,16) AS INET6); +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +SELECT CAST('::' AS INET6 CHARACTER SET latin1); + CREATE TABLE t1 AS SELECT CAST('::' AS INET6); SHOW CREATE TABLE t1; DROP TABLE t1; @@ -1649,7 +1661,7 @@ DROP TABLE t1; --echo # MDEV-27015 Assertion `!is_null()' failed in FixedBinTypeBundle::Fbt FixedBinTypeBundle::Field_fbt::to_fbt() --echo # -CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, a INET6(6) DEFAULT '::10'); +CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, a INET6 DEFAULT '::10'); INSERT INTO t1(id) VALUES (1), (2), (3), (4); INSERT INTO t1 VALUES (5,'::5'), (6,'::6'); SELECT * FROM t1 ORDER BY a; diff --git a/plugin/type_test/mysql-test/type_test/type_test_int8.result b/plugin/type_test/mysql-test/type_test/type_test_int8.result index f55211446ec21..45e9d5d2672c2 100644 --- a/plugin/type_test/mysql-test/type_test/type_test_int8.result +++ b/plugin/type_test/mysql-test/type_test/type_test_int8.result @@ -23,6 +23,12 @@ PLUGIN_DESCRIPTION Data type TEST_INT8 PLUGIN_LICENSE GPL PLUGIN_MATURITY Experimental PLUGIN_AUTH_VERSION 1.0 +CREATE TABLE t1 (a TEST_INT8(6,2)); +ERROR HY000: Data type 'TEST_INT8' doesn't support DECIMALS attribute. +CREATE TABLE t1 (a TEST_INT8 CHARACTER SET latin1); +ERROR HY000: Data type 'TEST_INT8' doesn't support CHARACTER SET attribute. +CREATE TABLE t1 (a TEST_INT8 REF_SYSTEM_ID=4); +ERROR HY000: Data type 'TEST_INT8' doesn't support REF_SYSTEM_ID attribute. CREATE TABLE t1 (a TEST_INT8); SHOW CREATE TABLE t1; Table Create Table diff --git a/plugin/type_test/mysql-test/type_test/type_test_int8.test b/plugin/type_test/mysql-test/type_test/type_test_int8.test index 6b5496c30fa11..22e326b22b29b 100644 --- a/plugin/type_test/mysql-test/type_test/type_test_int8.test +++ b/plugin/type_test/mysql-test/type_test/type_test_int8.test @@ -18,6 +18,15 @@ FROM INFORMATION_SCHEMA.PLUGINS AND PLUGIN_NAME='test_int8'; --horizontal_results +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a TEST_INT8(6,2)); + +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a TEST_INT8 CHARACTER SET latin1); + +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a TEST_INT8 REF_SYSTEM_ID=4); + CREATE TABLE t1 (a TEST_INT8); SHOW CREATE TABLE t1; DROP TABLE t1; diff --git a/plugin/type_test/plugin.cc b/plugin/type_test/plugin.cc index a649ebb3e4168..81e5b0e53ad2c 100644 --- a/plugin/type_test/plugin.cc +++ b/plugin/type_test/plugin.cc @@ -64,6 +64,7 @@ class Type_handler_test_int8: public Type_handler_longlong { return &type_collection_test; } + uint get_column_attributes() const override { return ATTR_LENGTH; } const Type_handler *type_handler_signed() const override { return this; @@ -121,6 +122,7 @@ class Type_handler_test_double: public Type_handler_double { return &type_collection_test; } + uint get_column_attributes() const override { return ATTR_LENGTH | ATTR_DEC; } const Type_handler *type_handler_signed() const override { return this; diff --git a/plugin/type_uuid/mysql-test/type_uuid/type_uuid.result b/plugin/type_uuid/mysql-test/type_uuid/type_uuid.result index da2cd2e12e80e..baba268035940 100644 --- a/plugin/type_uuid/mysql-test/type_uuid/type_uuid.result +++ b/plugin/type_uuid/mysql-test/type_uuid/type_uuid.result @@ -6,6 +6,12 @@ # CREATE TABLE t1 (a UUID AUTO_INCREMENT); ERROR 42000: Incorrect column specifier for column 'a' +CREATE TABLE t1 (a UUID(6)); +ERROR HY000: Data type 'UUID' doesn't support LENGTH attribute. +CREATE TABLE t1 (a UUID CHARACTER SET latin1); +ERROR HY000: Data type 'UUID' doesn't support CHARACTER SET attribute. +CREATE TABLE t1 (a UUID REF_SYSTEM_ID=4); +ERROR HY000: Data type 'UUID' doesn't support REF_SYSTEM_ID attribute. CREATE TABLE t1 (a UUID); SHOW CREATE TABLE t1; Table Create Table diff --git a/plugin/type_uuid/mysql-test/type_uuid/type_uuid.test b/plugin/type_uuid/mysql-test/type_uuid/type_uuid.test index 2bf06d0535a21..2d49538eafb23 100644 --- a/plugin/type_uuid/mysql-test/type_uuid/type_uuid.test +++ b/plugin/type_uuid/mysql-test/type_uuid/type_uuid.test @@ -11,6 +11,16 @@ --error ER_WRONG_FIELD_SPEC CREATE TABLE t1 (a UUID AUTO_INCREMENT); +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a UUID(6)); + +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a UUID CHARACTER SET latin1); + +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a UUID REF_SYSTEM_ID=4); + + CREATE TABLE t1 (a UUID); SHOW CREATE TABLE t1; DESCRIBE t1; diff --git a/plugin/type_xmltype/CMakeLists.txt b/plugin/type_xmltype/CMakeLists.txt new file mode 100644 index 0000000000000..d9e9feb599d14 --- /dev/null +++ b/plugin/type_xmltype/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (c) 2025, MariaDB corporation. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +MYSQL_ADD_PLUGIN(type_xmltype + plugin.cc sql_type_xmltype.cc + MANDATORY RECOMPILE_FOR_EMBEDDED) diff --git a/plugin/type_xmltype/item_func_xml_isvalid.cc b/plugin/type_xmltype/item_func_xml_isvalid.cc new file mode 100644 index 0000000000000..197b8b87857ac --- /dev/null +++ b/plugin/type_xmltype/item_func_xml_isvalid.cc @@ -0,0 +1,3564 @@ +/* Copyright (c) 2026, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "mariadb.h" +#include "sql_priv.h" +/* + It is necessary to include set_var.h instead of item.h because there + are dependencies on include order for set_var.h and item.h. This + will be resolved later. +*/ +#ifdef MDEV37262 +#include "sql_class.h" // set_var.h: THD +//#include "set_var.h" +#include "my_xml.h" +//#include "sp_pcontext.h" +#include "sql_class.h" // THD +#include "sql_lex.h" +#include "sql_type_xmltype.h" +#endif /* MDEV37262*/ + +#define MYSQL_SERVER +#include "mariadb.h" +#include "my_xml.h" +#include "sql_class.h" +#include "sql_lex.h" +#include "sql_type_xmltype.h" + +/* XML Schema validation. */ + +struct xs_word +{ + LEX_CSTRING m_w; + xs_word(const char *word, size_t len): m_w(LEX_CSTRING{word, len}) {} + bool eq(const char *name, size_t len) const + { + return len == m_w.length && memcmp(m_w.str, name, len) == 0; + } + + size_t length() const { return m_w.length; } + const char *str() const { return m_w.str; } +}; + +/* XML schema keywords. */ +static xs_word xs_abstract(STRING_WITH_LEN("abstract")); +static xs_word xs_all(STRING_WITH_LEN("all")); +static xs_word xs_any(STRING_WITH_LEN("any")); +static xs_word xs_anyAttribute(STRING_WITH_LEN("anyAttribute")); +static xs_word xs_attribute(STRING_WITH_LEN("attribute")); +static xs_word xs_attributeFormDefault( + STRING_WITH_LEN("attributeFormDefault")); +static xs_word xs_attributeGroup(STRING_WITH_LEN("attributeGroup")); +static xs_word xs_base(STRING_WITH_LEN("base")); +static xs_word xs_block(STRING_WITH_LEN("block")); +static xs_word xs_choice(STRING_WITH_LEN("choice")); +static xs_word xs_complexContent(STRING_WITH_LEN("complexContent")); +static xs_word xs_complexType(STRING_WITH_LEN("complexType")); +static xs_word xs_default(STRING_WITH_LEN("default")); +static xs_word xs_encoding(STRING_WITH_LEN("encoding")); +static xs_word xs_element(STRING_WITH_LEN("element")); +static xs_word xs_elementFormDefault(STRING_WITH_LEN("elementFormDefault")); +static xs_word xs_enumeration(STRING_WITH_LEN("enumeration")); +static xs_word xs_extension(STRING_WITH_LEN("extension")); +static xs_word xs_final(STRING_WITH_LEN("final")); +static xs_word xs_fixed(STRING_WITH_LEN("fixed")); +static xs_word xs_form(STRING_WITH_LEN("form")); +static xs_word xs_fractionDigits(STRING_WITH_LEN("fractionDigits")); +static xs_word xs_group(STRING_WITH_LEN("group")); +static xs_word xs_id(STRING_WITH_LEN("id")); +static xs_word xs_key(STRING_WITH_LEN("key")); +static xs_word xs_keyref(STRING_WITH_LEN("keyref")); +static xs_word xs_itemType(STRING_WITH_LEN("itemType")); +static xs_word xs_length(STRING_WITH_LEN("length")); +static xs_word xs_list(STRING_WITH_LEN("list")); +static xs_word xs_memberTypes(STRING_WITH_LEN("memberTypes")); +static xs_word xs_maxExclusive(STRING_WITH_LEN("maxExclusive")); +static xs_word xs_maxInclusive(STRING_WITH_LEN("maxInclusive")); +static xs_word xs_maxLength(STRING_WITH_LEN("maxLength")); +static xs_word xs_maxOccurs(STRING_WITH_LEN("maxOccurs")); +static xs_word xs_minExclusive(STRING_WITH_LEN("minExclusive")); +static xs_word xs_minInclusive(STRING_WITH_LEN("minInclusive")); +static xs_word xs_minLength(STRING_WITH_LEN("minLength")); +static xs_word xs_minOccurs(STRING_WITH_LEN("minOccurs")); +static xs_word xs_mixed(STRING_WITH_LEN("mixed")); +static xs_word xs_name(STRING_WITH_LEN("name")); +static xs_word xs_namespace(STRING_WITH_LEN("namespace")); +static xs_word xs_notation(STRING_WITH_LEN("annotation")); +static xs_word xs_nillable(STRING_WITH_LEN("nillable")); +static xs_word xs_ref(STRING_WITH_LEN("ref")); +static xs_word xs_pattern(STRING_WITH_LEN("pattrern")); +static xs_word xs_processContent(STRING_WITH_LEN("processContent")); +static xs_word xs_restriction(STRING_WITH_LEN("restriction")); +static xs_word xs_schema(STRING_WITH_LEN("schema")); +static xs_word xs_sequence(STRING_WITH_LEN("sequence")); +static xs_word xs_smipleContent(STRING_WITH_LEN("simpleContent")); +static xs_word xs_simpleType(STRING_WITH_LEN("simpleType")); +static xs_word xs_substitutionGroup(STRING_WITH_LEN("substitutionGroup")); +static xs_word xs_targetNamespace(STRING_WITH_LEN("targetNamespace")); +static xs_word xs_totalDigits(STRING_WITH_LEN("totalDigits")); +static xs_word xs_type(STRING_WITH_LEN("type")); +static xs_word xs_unbounded(STRING_WITH_LEN("unbounded")); +static xs_word xs_unique(STRING_WITH_LEN("unique")); +static xs_word xs_union(STRING_WITH_LEN("union")); +static xs_word xs_use(STRING_WITH_LEN("use")); +static xs_word xs_value(STRING_WITH_LEN("value")); +static xs_word xs_version(STRING_WITH_LEN("version")); +static xs_word xs_whiteSpace(STRING_WITH_LEN("whiteSpace")); +static xs_word xs_xml(STRING_WITH_LEN("xml")); +static xs_word xs_xmlns(STRING_WITH_LEN("xmlns")); +static xs_word xs_xml_lang(STRING_WITH_LEN("xml:lang")); + +static xs_word xs_uri_short(STRING_WITH_LEN("http://w3.org")); +static xs_word xs_uri_www(STRING_WITH_LEN("http://www.w3.org")); +static xs_word xs_uri(STRING_WITH_LEN("http://www.w3.org/2001/XMLSchema")); + +class XMLSchema_tag; +class XMLSchema_attribute; +class XMLSchema_xml; +class XMLSchema_schema; +class XMLSchema_group_def; +class XMLSchema_attributeGroup_def; +class XMLSchema_attributeGroup_reference; +class XMLSchema_type; + +class XMLSchema_item: public Sql_alloc +{ +public: + XMLSchema_item(const XMLSchema_item &)= delete; + void operator=(const XMLSchema_item &) = delete; + + XMLSchema_item() {} + virtual ~XMLSchema_item() = default; + + + /* Parsing of the schema. */ + virtual bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len); + virtual bool enter_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len); + virtual bool value(MY_XML_VALIDATION_DATA *st, const char *attr, size_t len) + { + return MY_XML_OK; + } + virtual bool leave(MY_XML_VALIDATION_DATA *st, const char *attr, size_t len); + /* + If the type can't be resolved, the function returns TRUE and + sets the unresolved name in the 'bad_type'. + */ + virtual bool resolve_type(MY_XML_VALIDATION_DATA *st, LEX_CSTRING *bad_type) + { + DBUG_ASSERT(0); + return false; + } + + /* Validation of an XML. */ + + /* check the name of the rule. */ + virtual bool validate_name(const char *attr, size_t len) + { + DBUG_ASSERT(0); + return false; + } + + virtual void validate_prepare() {} + enum vtn_result{ + VTN_ACCEPTED, /* tag accepted by this rule. */ + VTN_CONTINUE, /* tag wasn't accepted, need to check other rules. */ + VTN_ERROR /* XML is invalid. */ + }; + + virtual enum vtn_result validate_tag_name(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) + { + return VTN_CONTINUE; + } + + virtual bool is_validate_done() { return true; } + + + virtual bool validate_value(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) + { + return MY_XML_OK; + } + virtual bool validate_leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) + { + return MY_XML_OK; + } + virtual bool validate_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) + { + DBUG_ASSERT(0); + return MY_XML_OK; + } + virtual bool validate_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) + { + return MY_XML_ERROR; + } + + int validate_failed(MY_XML_VALIDATION_DATA *st); + + class XMLSchema_item *m_next; +}; + + +class XMLSchema_annotation: public XMLSchema_item +{ + int m_level; +public: + XMLSchema_annotation(): XMLSchema_item(), m_level(0) {} + + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + m_level++; + return MY_XML_OK; + } + bool enter_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return XMLSchema_annotation::enter_tag(st, attr, len); + } + bool leave(MY_XML_VALIDATION_DATA *st, const char *attr, size_t len) override + { + if (m_level == 0) + return XMLSchema_item::leave(st, attr, len); + m_level--; + return MY_XML_OK; + } + + bool validate_leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return XMLSchema_annotation::leave(st, attr, len); + } + bool validate_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return XMLSchema_annotation::enter_tag(st, attr, len); + } + bool validate_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return XMLSchema_annotation::enter_tag(st, attr, len); + } +}; + + +class XMLSchema_root: public XMLSchema_item +{ +public: + XMLSchema_root(): XMLSchema_item() {} + + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + + bool validate_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; +#ifndef DBUG_OFF + bool enter_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + DBUG_ASSERT(0); /* should never be called. */ + return MY_XML_OK; + } + bool leave(MY_XML_VALIDATION_DATA *st, const char *attr, size_t len) override + { + DBUG_ASSERT(0); /* should never be called. */ + return MY_XML_OK; + } + bool validate_leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + DBUG_ASSERT(0); /* should never be called. */ + return MY_XML_OK; + } +#endif /* DBUG_OFF */ +}; + + +/* + That item reads the attribute value of some Schema tag. + pops from stack after it. +*/ +class XMLSchema_tag_attribute: public XMLSchema_item +{ +public: + const xs_word *m_name; + const char *m_val; + size_t m_val_len; + XMLSchema_tag_attribute *m_next_attribute; + + XMLSchema_tag_attribute(const xs_word *name): XMLSchema_item(), + m_name(name), m_val(NULL), m_val_len(0) {} + +#ifndef DBUG_OFF + /* should never happen. */ + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + DBUG_ASSERT(0); + return MY_XML_OK; + } + bool enter_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + DBUG_ASSERT(0); + return MY_XML_OK; + } +#endif /*DBUG_OFF*/ + + bool value(MY_XML_VALIDATION_DATA *st, const char *attr, size_t len) override + { + m_val= attr; + m_val_len= len; + return MY_XML_OK; + } + + bool eq_name(const char *name, size_t len) const + { + return m_name->eq(name, len); + } + bool is_set() const + { + return m_val_len > 0; + } + bool eq_value(const char *name, size_t len) const + { + return len == m_val_len && memcmp(m_val, name, len) == 0; + } +}; + + +class XMLSchema_tag_xmlns_attribute: public XMLSchema_tag_attribute +{ +public: + LEX_CSTRING m_ns_name; + XMLSchema_tag_xmlns_attribute(); + bool value(MY_XML_VALIDATION_DATA *st, const char *attr, size_t len) override; +}; + + +class XMLSchema_tag_integer_attribute: public XMLSchema_tag_attribute +{ + int m_error; +public: + longlong m_value_int; + + XMLSchema_tag_integer_attribute(const xs_word *name, longlong def_value= 1): + XMLSchema_tag_attribute(name), m_error(0), m_value_int(def_value) {} + bool value(MY_XML_VALIDATION_DATA *st, const char *attr, size_t len) override + { + char *tmp= (char *) attr + len; + m_value_int= (longlong) my_strtoll10(attr, &tmp, &m_error); + + (void) XMLSchema_tag_attribute::value(st, attr, len); + + return m_error ? MY_XML_ERROR : MY_XML_OK; + } +}; + + +class XMLSchema_tag_unbounded_integer_attribute: + public XMLSchema_tag_integer_attribute +{ +public: + XMLSchema_tag_unbounded_integer_attribute(const xs_word *name, + longlong def_value= 1): + XMLSchema_tag_integer_attribute(name, def_value) {} + bool value(MY_XML_VALIDATION_DATA *st, const char *attr, size_t len) override + { + if (XMLSchema_tag_integer_attribute::value(st, attr, len) == MY_XML_OK) + return MY_XML_OK; + + if (xs_unbounded.eq(attr, len)) + { + m_value_int= LONGLONG_MAX; + return MY_XML_OK; + } + + return MY_XML_ERROR; + } +}; + + +#define MY_XPATH_LEX_COLON ':' + +class XMLSchema_tag_namespaced_attribute:public XMLSchema_tag_attribute +{ +public: + XMLSchema_tag_namespaced_attribute(const xs_word *name): + XMLSchema_tag_attribute(name) {} + bool value(MY_XML_VALIDATION_DATA *st, const char *attr, size_t len) override + { + size_t col_pos= 0; + while (col_pos < len) + { + if (attr[col_pos++] == MY_XPATH_LEX_COLON) + { + attr+= col_pos; + len-= col_pos; + break; + } + } + + m_val= attr; + m_val_len= len; + return MY_XML_OK; + } +}; + + +class MY_XML_VALIDATION_DATA: public Sql_alloc +{ +public: + int validation_failed; + uint attr; + XMLSchema_item skipped_attr; + XMLSchema_annotation annotation; + + XMLSchema_root root; + XMLSchema_item *s_stack; + XMLSchema_schema *schema; + XMLSchema_xml *xml; + MEM_ROOT *mem_root; + + LEX_CSTRING schema_namespace{NULL, 0}; + + MY_XML_VALIDATION_DATA(): + s_stack(&root), schema(NULL), xml(NULL), mem_root(NULL) + { + root.m_next= NULL; + }; + + void push(XMLSchema_item *s) + { + s->m_next= s_stack; + s_stack= s; + } + + void pop() { s_stack= s_stack->m_next; } + + void add_item_to_resolve(XMLSchema_item *t); + bool set_schema_namespace(const LEX_CSTRING *ns) + { + /* + It's weird feature of the XML Schema. + + Here the namespace for the and the namespace + declared with the xmlns must exactly coincide. + So we don't actually set anything. It supposed to be set at this point. + Just do the necessary check. + */ + + if (ns->length == 0) + { + return schema_namespace.length; + } + + if (schema_namespace.length != ns->length+1 || + memcmp(schema_namespace.str, ns->str, ns->length) != 0) + return true; + + return false; + } + bool namespace_not_specified() const + { + return schema_namespace.str == NULL; + } + bool xs_namespace(const char **name, size_t *len) const; + bool namespace_empty() const + { + return schema_namespace.str != NULL && schema_namespace.length == 0; + } + XMLSchema_attributeGroup_def *find_attribute_group_by_name(const char *name, + size_t len) const; + XMLSchema_group_def *find_element_group_by_name(const char *name, + size_t len) const; +}; + + +bool XMLSchema_item::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + st->push(&st->annotation); + return MY_XML_OK; +} + + +bool XMLSchema_item::enter_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + st->push(&st->skipped_attr); + return MY_XML_OK; +} + + +bool XMLSchema_item::leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + st->pop(); + return MY_XML_OK; +} + + +int XMLSchema_item::validate_failed(MY_XML_VALIDATION_DATA *st) +{ + st->validation_failed= 1; + return MY_XML_ERROR; +} + + +/* + Parsing schema's tag. Handling tag's attributes. +*/ + +class XMLSchema_tag: public XMLSchema_item +{ +public: + XMLSchema_tag_attribute *m_tag_attributes; + XMLSchema_tag_attribute m_id; /* eveny tag in schema has the "id" attr */ + XMLSchema_tag *m_next_tag; + + XMLSchema_annotation *m_annotation; + bool declare_attribute(XMLSchema_tag_attribute *attr) + { + attr->m_next_attribute= m_tag_attributes; + m_tag_attributes= attr; + return FALSE; + } + + XMLSchema_tag(): XMLSchema_item(), + m_tag_attributes(&m_id), m_id(&xs_id) + { + m_id.m_next_attribute= NULL; + } + XMLSchema_tag_attribute *find_attr(const char *attr, size_t len); + + bool enter_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + + virtual void push_self(MY_XML_VALIDATION_DATA *st) + { + st->push(this); + } +}; + + +XMLSchema_tag_attribute *XMLSchema_tag::find_attr(const char *name, size_t len) +{ + for (XMLSchema_tag_attribute *atr= m_tag_attributes; + atr; + atr= atr->m_next_attribute) + { + if (atr->eq_name(name, len)) + return atr; + } + return NULL; +} + + +bool XMLSchema_tag::enter_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + XMLSchema_tag_attribute *atr= find_attr(attr, len); + if (!atr) + return XMLSchema_item::enter_attr(st, attr, len); + st->push(atr); + return MY_XML_OK; +} + + +/* + Stores the description of an XML attribute and then validates it. +*/ +class XMLSchema_attribute: public XMLSchema_tag +{ +public: + XMLSchema_tag_attribute m_atr_name; + XMLSchema_tag_attribute m_atr_type; + XMLSchema_tag_attribute m_atr_default; + XMLSchema_tag_attribute m_atr_fixed; + XMLSchema_tag_attribute m_atr_use; + XMLSchema_tag_attribute m_atr_ref; + + XMLSchema_type *m_type; + XMLSchema_attribute *m_next_attribute; + XMLSchema_attribute(): XMLSchema_tag(), + m_atr_name(&xs_name), + m_atr_type(&xs_type), + m_atr_default(&xs_default), + m_atr_fixed(&xs_fixed), + m_atr_use(&xs_use), + m_atr_ref(&xs_ref), + m_type(NULL) + { + declare_attribute(&m_atr_name); + declare_attribute(&m_atr_type); + declare_attribute(&m_atr_default); + declare_attribute(&m_atr_fixed); + declare_attribute(&m_atr_use); + declare_attribute(&m_atr_ref); + } + + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool resolve_type(MY_XML_VALIDATION_DATA *st, + LEX_CSTRING *bad_type) override; + + void validate_prepare() override; + bool validate_value(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool validate_leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool validate_name(const char *attr, size_t len) override + { + return m_atr_name.eq_value(attr, len); + } +}; + + +class XMLSchema_anyAttribute: public XMLSchema_tag +{ +public: + XMLSchema_tag_attribute m_atr_namespace; + XMLSchema_tag_attribute m_atr_processContent; + + XMLSchema_anyAttribute(): XMLSchema_tag(), + m_atr_namespace(&xs_namespace), + m_atr_processContent(&xs_processContent) + { + declare_attribute(&m_atr_namespace); + declare_attribute(&m_atr_processContent); + } + bool validate_leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + st->pop(); + return MY_XML_OK; + } +}; + + +class XMLSchema_any: public XMLSchema_anyAttribute +{ +public: + XMLSchema_tag_integer_attribute m_minOccurs; + XMLSchema_tag_unbounded_integer_attribute m_maxOccurs; + + XMLSchema_any(): XMLSchema_anyAttribute(), + m_minOccurs(&xs_minOccurs), + m_maxOccurs(&xs_maxOccurs) + { + declare_attribute(&m_minOccurs); + declare_attribute(&m_maxOccurs); + } + enum vtn_result validate_tag_name(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + st->push(&st->annotation); + return VTN_ACCEPTED; + } +}; + + +/* + Supposed to be a member of tags supporting these inside: + + + +*/ +class XMLSchema_std_attributes +{ +public: + XMLSchema_attribute *m_attributes; /* nested attributes. */ + XMLSchema_attributeGroup_reference *m_groups; /* nested goups. */ + XMLSchema_anyAttribute *m_anyAttribute; + + XMLSchema_std_attributes(): + m_attributes(NULL), m_groups(NULL), + m_anyAttribute(NULL) {} + + /* + returns + 1 if the tag recognised and handled + 0 if the tag wasn't recognised + -1 if an error happened + */ + int enter_tag(MY_XML_VALIDATION_DATA *st, const char *attr, size_t len); + int validate_attr(MY_XML_VALIDATION_DATA *st, const char *attr, size_t len); +}; + + +class XMLSchema_attributeGroup_def: public XMLSchema_tag +{ +public: + XMLSchema_std_attributes m_attributes; + XMLSchema_attributeGroup_def *m_next_group; + XMLSchema_tag_attribute m_atr_name; + + XMLSchema_type *m_type; + XMLSchema_attributeGroup_def(): XMLSchema_tag(), + m_atr_name(&xs_name) + { + declare_attribute(&m_atr_name); + } + + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + int res; + + if (!(res= m_attributes.enter_tag(st, attr, len))) + return XMLSchema_tag::enter_tag(st, attr, len); + + return res > 0 ? MY_XML_OK : MY_XML_ERROR; + } +}; + + +class XMLSchema_attributeGroup_reference: public XMLSchema_tag +{ + XMLSchema_attributeGroup_def *m_group; +public: + XMLSchema_attributeGroup_reference *m_next_ref; + XMLSchema_tag_attribute m_atr_ref; + + XMLSchema_type *m_type; + XMLSchema_attributeGroup_reference(): XMLSchema_tag(), + m_atr_ref(&xs_ref) + { + declare_attribute(&m_atr_ref); + } + + bool leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool resolve_type(MY_XML_VALIDATION_DATA *st, + LEX_CSTRING *bad_type) override; + + bool validate_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + if (!m_group) + return MY_XML_ERROR; + + return m_group->m_attributes.validate_attr(st, attr, len); + } +}; + + +class XMLSchema_builtin_type: public Sql_alloc +{ +protected: + virtual ~XMLSchema_builtin_type() = default; +public: + virtual bool valid_value(const char *value, size_t len) { return TRUE; } + static XMLSchema_builtin_type *get_builtin_type_by_name( + MY_XML_VALIDATION_DATA *st, const char *name, size_t len); + + XMLSchema_builtin_type() {} +}; + + +class XMLSchema_string_builtin_type: public XMLSchema_builtin_type +{ +public: +}; + + +enum xml_num_char_classes { + N_MNS, + N_PLS, + N_DIG, + N_PNT, + N_EXP, + N_SPC, + N_EOF, + n_er, + N_NUM_CLASSES +}; + + +static enum xml_num_char_classes xml_num_chr_map[104] = { + n_er, n_er, n_er, n_er, n_er, n_er, n_er, n_er, + n_er, N_SPC, N_SPC, n_er, n_er, N_SPC, n_er, n_er, + n_er, n_er, n_er, n_er, n_er, n_er, n_er, n_er, + n_er, n_er, n_er, n_er, n_er, n_er, n_er, n_er, + + N_SPC, n_er, n_er, n_er, n_er, n_er, n_er, n_er, /* !"#$%&'*/ + n_er, n_er, n_er, N_PLS, n_er, N_MNS, N_PNT, n_er, /*()*+,-./ */ + N_DIG, N_DIG, N_DIG, N_DIG, N_DIG, N_DIG, N_DIG, N_DIG,/*01234567*/ + N_DIG, N_DIG, n_er, n_er, n_er, n_er, n_er, n_er, /*89:;<=>?*/ + + n_er, n_er, n_er, n_er, n_er, N_EXP, n_er, n_er, /*@ABCDEFG*/ + n_er, n_er, n_er, n_er, n_er, n_er, n_er, n_er, /*HIJKLMNO*/ + n_er, n_er, n_er, n_er, n_er, n_er, n_er, n_er, /*PQRSTUVW*/ + n_er, n_er, n_er, n_er, n_er, n_er, n_er, n_er, /*XYZ[\]^_*/ + + n_er, n_er, n_er, n_er, n_er, N_EXP, n_er, n_er /*`abcdefg*/ +}; + + +enum xml_num_states { + NS_GO, /* Initial state. */ + NS_END, /* Number ended. */ + NS_GMI, /* If the number starts with '-'. */ + NS_GPL, /* If the number starts with '+'. */ + NS_INT, /* Integer part. */ + NS_FRC, /* Fractional part. */ + NS_EXP, /* Exponential part begins. */ + NS_EX1, /* Exponential part started with + or -. */ + NS_EX2, /* Exponential part continues. */ + NS_NUM_STATES, + E_SYN /* Syntax error. */ +}; + + +static int xml_num_states[NS_NUM_STATES][N_NUM_CLASSES]= +{ +/* - + 0..9 POINT E SPACE EOF BAD_SYM*/ +/*GO*/ { NS_GMI, NS_GPL, NS_INT, NS_FRC, E_SYN, NS_GO, E_SYN, E_SYN}, +/*END*/ { E_SYN, E_SYN, E_SYN, E_SYN, E_SYN, NS_END, NS_END, E_SYN}, +/*GMI*/ { E_SYN, E_SYN, NS_INT, NS_FRC, E_SYN, E_SYN, E_SYN, E_SYN}, +/*GPL*/ { E_SYN, E_SYN, NS_INT, NS_FRC, E_SYN, E_SYN, E_SYN, E_SYN}, +/*INT*/ { E_SYN, E_SYN, NS_INT, NS_FRC, NS_EXP, NS_END, NS_END, E_SYN}, +/*FRC*/ { E_SYN, E_SYN, NS_FRC, E_SYN, NS_EXP, NS_END, NS_END, E_SYN}, +/*EXP*/ { NS_EX1, NS_EX1, NS_EX2, E_SYN, E_SYN, E_SYN, E_SYN, E_SYN}, +/*EX1*/ { E_SYN, E_SYN, NS_EX2, E_SYN, E_SYN, E_SYN, E_SYN, E_SYN}, +/*EX2*/ { E_SYN, E_SYN, NS_EX2, E_SYN, E_SYN, NS_END, NS_END, E_SYN} +}; + + +enum xml_num_types +{ + NUM_TYPE_NEG=1, /* Number is negative. */ + NUM_TYPE_FRAC_PART=2, /* The fractional part is not empty. */ + NUM_TYPE_EXP=4, /* The number has the 'e' part. */ +}; +const uint NUM_TYPE_UINT= NUM_TYPE_NEG | NUM_TYPE_FRAC_PART | NUM_TYPE_EXP; +const uint NUM_TYPE_INT= NUM_TYPE_FRAC_PART | NUM_TYPE_EXP; +const uint NUM_TYPE_DEC= NUM_TYPE_EXP; +const uint NUM_TYPE_FLOAT= 0; + +static uint xml_num_state_types[NS_NUM_STATES]= +{ +/*GO*/ 0, +/*END*/ 0, +/*GMI*/ NUM_TYPE_NEG, +/*GPL*/ 0, +/*INT*/ 0, +/*FRC*/ NUM_TYPE_FRAC_PART, +/*EXP*/ NUM_TYPE_EXP, +/*EX1*/ 0, +/*EX2*/ 0, +}; + + +class XMLSchema_num_builtin_type: public XMLSchema_builtin_type +{ +public: + int m_disallowed_types; + XMLSchema_num_builtin_type(int disallowed_types): XMLSchema_builtin_type(), + m_disallowed_types(disallowed_types) {} + bool valid_value(const char *value, size_t len) override + { + int state= NS_GO; + size_t pos= 0; + + while (len > pos) + { + int c= (int) value[pos++]; + if (c > 103) + return 0; + + state= xml_num_states[state][xml_num_chr_map[c]]; + if (state == E_SYN || + xml_num_state_types[state] & m_disallowed_types) + return 0; + } + + return xml_num_states[state][N_EOF] == NS_END; + } +}; + + +enum xml_time_char_classes { + TC_MNS, + TC_PLS, + TC_DIG, + TC_PNT, + TC_T, + TC_Z, + TC_CLN, + TC_SPC, + TC_EOF, + tc_er, + TC_NUM_CLASSES +}; + + +static enum xml_time_char_classes xml_time_chr_map[96] = { + tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, + tc_er, TC_SPC, TC_SPC, tc_er, tc_er, TC_SPC, tc_er, tc_er, + tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, + tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, + + TC_SPC, tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, /* !"#$%&'*/ + tc_er, tc_er, tc_er, TC_PLS, tc_er, TC_MNS, TC_PNT, tc_er, /*()*+,-./ */ + TC_DIG, TC_DIG, TC_DIG, TC_DIG, TC_DIG, TC_DIG, TC_DIG, TC_DIG,/*01234567*/ + TC_DIG, TC_DIG, TC_CLN, tc_er, tc_er, tc_er, tc_er, tc_er, /*89:;<=>?*/ + + tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, /*@ABCDEFG*/ + tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, tc_er, /*HIJKLMNO*/ + tc_er, tc_er, tc_er, tc_er, TC_T, tc_er, tc_er, tc_er, /*PQRSTUVW*/ + tc_er, tc_er, TC_Z, tc_er, tc_er, tc_er, tc_er, tc_er /*XYZ[\]^_*/ +}; + + +enum xml_time_states { + /* datetime */ + T_GO, /* Initial state. */ + T_END, /* Datetime ended. */ + T_YMI, /* If the year starts with '-'. */ + T_Y1, + T_Y2, + T_Y3, + T_Y4, + T_YE, + T_M1, + T_M2, + T_ME, + T_D1, + T_D2, + T_DE, + T_H1, + T_H2, + T_HE, + T_MI1, + T_MI2, + T_MIE, + T_S1, + T_S2, + T_SFP, + T_SFR, + T_ZH0, + T_ZH1, + T_ZH2, + T_ZHE, + T_ZM1, + T_ZM2, + T_Z, + + /* date */ + T_dGO, /* Initial state. */ + T_dYMI, /* If the year starts with '-'. */ + T_dY1, + T_dY2, + T_dY3, + T_dY4, + T_dYE, + T_dM1, + T_dM2, + T_dME, + T_dD1, + T_dD2, + + /* time */ + T_tGO, + + /* gYear */ + T_yGO, + T_yYMI, + T_yY1, + T_yY2, + T_yY3, + T_yY4, + + /* gMonth */ + T_mGO, + T_mG1, + T_mG2, + T_mM1, + T_mM2, + T_mE1, + T_mE2, + + /* gDay */ + T_aGO, + T_aG1, + T_aG2, + T_aG3, + T_aD1, + T_aD2, + + /* gYearMonth */ + T_hGO, + T_hYMI, + T_hY1, + T_hY2, + T_hY3, + T_hY4, + T_hM1, + T_hM2, + + /* gMonthDay */ + T_nGO, + T_nG1, + T_nG2, + T_nM1, + T_nM2, + + T_NUM_STATES, + T_SYN /* Syntax error. */ +}; + + +static int xml_time_states[T_NUM_STATES][TC_NUM_CLASSES]= { +/* - + 0..9 . T Z : SPACE EOF BAD_SYM*/ +/* dateTime */ +/*GO*/ {T_YMI, T_SYN, T_Y1, T_SYN, T_SYN, T_SYN, T_SYN, T_GO, T_SYN, T_SYN}, +/*END*/{T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_END, T_SYN, T_SYN}, +/*YMI*/{T_SYN, T_SYN, T_Y1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y1*/ {T_SYN, T_SYN, T_Y2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y2*/ {T_SYN, T_SYN, T_Y3, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y3*/ {T_SYN, T_SYN, T_Y4, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y4*/ {T_YE, T_SYN, T_Y4, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*YE*/ {T_SYN, T_SYN, T_M1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*M1*/ {T_SYN, T_SYN, T_M2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*M2*/ {T_ME, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*ME*/ {T_SYN, T_SYN, T_D1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*D1*/ {T_SYN, T_SYN, T_D2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*D2*/ {T_SYN, T_SYN, T_SYN, T_SYN, T_DE, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*DE*/ {T_SYN, T_SYN, T_H1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, + +/*H1*/ {T_SYN, T_SYN, T_H2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*H2*/ {T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_HE, T_SYN, T_SYN, T_SYN}, +/*HE*/ {T_SYN, T_SYN, T_MI1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*MI1*/{T_SYN, T_SYN, T_MI2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*MI2*/{T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_MIE, T_SYN, T_SYN, T_SYN}, +/*MIE*/{T_SYN, T_SYN, T_S1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*S1*/ {T_SYN, T_SYN, T_S2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*S2*/ {T_ZH0, T_ZH0, T_SYN, T_SFP, T_SYN, T_Z, T_SYN, T_END, T_END, T_SYN}, +/*SFP*/{T_SYN, T_SYN, T_SFR, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*SFR*/{T_ZH0, T_ZH0, T_SFR, T_SYN, T_SYN, T_Z, T_SYN, T_END, T_END, T_SYN}, +/*ZH0*/{T_SYN, T_SYN, T_ZH1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*ZH1*/{T_SYN, T_SYN, T_ZH2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*ZH2*/{T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_ZHE, T_SYN, T_SYN, T_SYN}, +/*ZHE*/{T_SYN, T_SYN, T_ZM1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*ZM1*/{T_SYN, T_SYN, T_ZM2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*ZM2*/{T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_END, T_END, T_SYN}, +/*Z*/ {T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_END, T_END, T_SYN}, + +/* date */ +/* - + 0..9 . T Z : SPACE EOF BAD_SYM*/ +/*GO*/ {T_dYMI,T_SYN, T_dY1, T_SYN, T_SYN, T_SYN, T_SYN, T_dGO, T_SYN, T_SYN}, +/*YMI*/{T_SYN, T_SYN, T_dY1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y1*/ {T_SYN, T_SYN, T_dY2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y2*/ {T_SYN, T_SYN, T_dY3, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y3*/ {T_SYN, T_SYN, T_dY4, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y4*/ {T_dYE, T_SYN, T_dY4, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*YE*/ {T_SYN, T_SYN, T_dM1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*M1*/ {T_SYN, T_SYN, T_dM2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*M2*/ {T_dME, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*ME*/ {T_SYN, T_SYN, T_dD1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*D1*/ {T_SYN, T_SYN, T_dD2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*D2*/ {T_ZH0, T_ZH0, T_SYN, T_SYN, T_SYN, T_Z, T_SYN, T_END, T_END, T_SYN}, + +/* time */ +/* - + 0..9 . T Z : SPACE EOF BAD_SYM*/ +/*GO*/ {T_SYN, T_SYN, T_H1, T_SYN, T_SYN, T_SYN, T_SYN, T_tGO, T_SYN, T_SYN}, + +/* gYear "2026+01:00" */ +/* - + 0..9 . T Z : SPACE EOF BAD_SYM*/ +/*GO*/ {T_yYMI,T_SYN, T_yY1, T_SYN, T_SYN, T_SYN, T_SYN, T_yGO, T_SYN, T_SYN}, +/*YMI*/{T_SYN, T_SYN, T_yY1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y1*/ {T_SYN, T_SYN, T_yY2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y2*/ {T_SYN, T_SYN, T_yY3, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y3*/ {T_SYN, T_SYN, T_yY4, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y4*/ {T_ZH0, T_ZH0, T_yY4, T_SYN, T_SYN, T_Z, T_SYN, T_END, T_END, T_SYN}, + +/* gMonth "--02--+01:00" */ +/* - + 0..9 . T Z : SPACE EOF BAD_SYM*/ +/*GO*/ {T_mG1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_mGO, T_SYN, T_SYN}, +/*G1*/ {T_mG2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*G2*/ {T_SYN, T_SYN, T_mM1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*M1*/ {T_SYN, T_SYN, T_mM2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*M2*/ {T_mE1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*E1*/ {T_mE2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*E2*/ {T_ZH0, T_ZH0, T_SYN, T_SYN, T_SYN, T_Z, T_SYN, T_END, T_END, T_SYN}, + +/* gDay "---10+02:00" */ +/* - + 0..9 . T Z : SPACE EOF BAD_SYM*/ +/*GO*/ {T_aG1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_aGO, T_SYN, T_SYN}, +/*G1*/ {T_aG2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*G2*/ {T_aG3, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*G3*/ {T_SYN, T_SYN, T_aD1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*D1*/ {T_SYN, T_SYN, T_aD2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*D2*/ {T_ZH0, T_ZH0, T_SYN, T_SYN, T_SYN, T_Z, T_SYN, T_END, T_END, T_SYN}, + +/* gYearMonth "2026-10+02:00" */ +/* - + 0..9 . T Z : SPACE EOF BAD_SYM*/ +/*GO*/ {T_hYMI,T_SYN, T_hY1, T_SYN, T_SYN, T_SYN, T_SYN, T_hGO, T_SYN, T_SYN}, +/*YMI*/{T_SYN, T_SYN, T_hY1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y1*/ {T_SYN, T_SYN, T_hY2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y2*/ {T_SYN, T_SYN, T_hY3, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y3*/ {T_SYN, T_SYN, T_hY4, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*Y4*/ {T_hM1, T_SYN, T_hY4, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*M1*/ {T_SYN, T_SYN, T_hM2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*M2*/ {T_ZH0, T_ZH0, T_SYN, T_SYN, T_SYN, T_Z, T_SYN, T_END, T_END, T_SYN}, + +/* gMonthDay "--05-02+01:00" */ +/* - + 0..9 . T Z : SPACE EOF BAD_SYM*/ +/*GO*/ {T_nG1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_nGO, T_SYN, T_SYN}, +/*G1*/ {T_nG2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*G2*/ {T_SYN, T_SYN, T_nM1, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*M1*/ {T_SYN, T_SYN, T_nM2, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +/*M2*/ {T_aG3, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN, T_SYN}, +}; + + +class XMLSchema_datetime_builtin_type: public XMLSchema_builtin_type +{ +public: + enum xml_time_states m_state0; + bool m_spaces_allowed; + + XMLSchema_datetime_builtin_type( + enum xml_time_states state0, bool spaces_allowed= TRUE): + XMLSchema_builtin_type(), + m_state0(state0), m_spaces_allowed(spaces_allowed) {} + + bool valid_value(const char *value, size_t len) override + { + int state= m_state0; + + size_t pos= 0; + + while (len > pos) + { + int c= (int) value[pos++]; + if (c > 96) + return 0; + + state= xml_time_states[state][xml_time_chr_map[c]]; + if (state == T_SYN) + return 0; + } + + return xml_time_states[state][TC_EOF] == T_END; + } +}; + + +enum xml_bin_char_classes { + BC_HEX, + BC_B64, + BC_EQ, + BC_SPC, + BC_EOF, + bc_er, + BC_NUM_CLASSES +}; + + +static enum xml_bin_char_classes xml_bin_chr_map[128]= +{ + bc_er, bc_er, bc_er, bc_er, bc_er, bc_er, bc_er, bc_er, + bc_er,BC_SPC,BC_SPC, bc_er, bc_er,BC_SPC, bc_er, bc_er, + bc_er, bc_er, bc_er, bc_er, bc_er, bc_er, bc_er, bc_er, + bc_er, bc_er, bc_er, bc_er, bc_er, bc_er, bc_er, bc_er, + BC_SPC, bc_er, bc_er, bc_er, bc_er, bc_er, bc_er, bc_er, /* !"#$%&' */ + bc_er, bc_er, bc_er,BC_B64, bc_er, bc_er, bc_er,BC_B64, /* ()*+,-./ */ + BC_HEX,BC_HEX,BC_HEX,BC_HEX,BC_HEX,BC_HEX,BC_HEX,BC_HEX, /* 01234567 */ + BC_HEX,BC_HEX, bc_er, bc_er, bc_er, BC_EQ, bc_er, bc_er, /* 89:;<=>? */ + bc_er,BC_HEX,BC_HEX,BC_HEX,BC_HEX,BC_HEX,BC_HEX,BC_B64, /* @ABCDEFG */ + BC_B64,BC_B64,BC_B64,BC_B64,BC_B64,BC_B64,BC_B64,BC_B64, /* HIJKLMNO */ + BC_B64,BC_B64,BC_B64,BC_B64,BC_B64,BC_B64,BC_B64,BC_B64, /* PQRSTUVW */ + BC_B64,BC_B64,BC_B64, bc_er, bc_er, bc_er, bc_er, bc_er, /* XYZ[\]^_ */ + bc_er,BC_HEX,BC_HEX,BC_HEX,BC_HEX,BC_HEX,BC_HEX,BC_B64, /* `abcdefg */ + BC_B64,BC_B64,BC_B64,BC_B64,BC_B64,BC_B64,BC_B64,BC_B64, /* hijklmno */ + BC_B64,BC_B64,BC_B64,BC_B64,BC_B64,BC_B64,BC_B64,BC_B64, /* pqrstuvwxyz{|}~ */ + BC_B64,BC_B64,BC_B64, bc_er, bc_er, bc_er, bc_er, bc_er /* pqrstuvwxyz{|}~ */ +}; + + +enum xml_bin_states { + B_END, + B_HEX0, + B_HEX1, + B_B640, + B_B641, + B_B642, + B_B643, + B_NUM_STATES, + B_SYN /* Syntax error. */ +}; + + +static int xml_bin_states[B_NUM_STATES][BC_NUM_CLASSES]= { +/* HEX B64 = SPACE EOF BAD_SYM */ +/*END*/ { B_SYN, B_SYN, B_SYN, B_END, B_END, T_SYN }, +/*HEX0*/ { B_HEX1, B_SYN, B_SYN, B_HEX0, B_END, T_SYN }, +/*HEX1*/ { B_HEX0, B_SYN, B_SYN, B_HEX1, B_SYN, T_SYN }, +/*B640*/ { B_B641, B_B641, B_SYN, B_B640, B_END, T_SYN }, +/*B641*/ { B_B642, B_B642, B_SYN, B_B641, B_SYN, T_SYN }, +/*B642*/ { B_B643, B_B643, B_SYN, B_B642, B_SYN, T_SYN }, +/*B643*/ { B_B640, B_B640, B_END, B_B643, B_SYN, T_SYN } +}; + + +class XMLSchema_binary_builtin_type: public XMLSchema_builtin_type +{ +public: + enum xml_bin_states m_state0; + + XMLSchema_binary_builtin_type(enum xml_bin_states state0): + XMLSchema_builtin_type(), m_state0(state0) {} + + bool valid_value(const char *value, size_t len) override + { + int state= m_state0; + + size_t pos= 0; + + while (len > pos) + { + int c= (int) value[pos++]; + if (c > 127) + return 0; + + state= xml_bin_states[state][xml_bin_chr_map[c]]; + if (state == B_SYN) + return 0; + } + + return xml_bin_states[state][BC_EOF] == B_END; + } +}; + + +/* Just to make type control possible. */ +class XMLSchema_type: public XMLSchema_tag +{ +}; + + +class XMLSchema_any_type: public XMLSchema_type +{ +public: + int m_level; + XMLSchema_any_type(): XMLSchema_type(), m_level(0) {} + + bool validate_leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + if (m_level == 0) + return MY_XML_ERROR; + + m_level--; + return MY_XML_OK; + } + bool validate_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + m_level++; + return MY_XML_OK; + } + enum vtn_result validate_tag_name(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + st->push(&st->annotation); + return VTN_ACCEPTED; + } + bool is_validate_done() override + { + return true; + } +}; + + +class XMLSchema_simple_builtin_type: public XMLSchema_type +{ +public: + XMLSchema_builtin_type *m_int_type; + XMLSchema_simple_builtin_type(XMLSchema_builtin_type *int_type): + XMLSchema_type(), m_int_type(int_type) {} + bool validate_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return validate_failed(st); + } + virtual bool validate_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) + { + return validate_failed(st); + } + bool validate_value(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return m_int_type->valid_value(attr, len) ? MY_XML_OK : + validate_failed(st); + } +}; + + +class XMLSchema_user_type: public XMLSchema_type +{ +protected: + XMLSchema_schema *m_schema; + XMLSchema_tag_attribute m_type_name; + XMLSchema_tag_attribute m_final; +public: + XMLSchema_tag *m_compositor; + XMLSchema_user_type *m_next_type; + + XMLSchema_user_type(XMLSchema_schema *schema): XMLSchema_type(), + m_schema(schema), + m_type_name(&xs_name), + m_final(&xs_final), + m_compositor(NULL) + { + declare_attribute(&m_type_name); + declare_attribute(&m_final); + } + bool validate_name(const char *attr, size_t len) override + { + return m_type_name.eq_value(attr, len); + } + void validate_prepare() override + { + m_compositor->validate_prepare(); + } + bool is_validate_done() override + { + return m_compositor->is_validate_done(); + } + bool validate_value(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return m_compositor->validate_value(st, attr, len); + } +}; + + +class XMLSchema_simpleType: public XMLSchema_user_type +{ +public: + XMLSchema_simpleType(XMLSchema_schema *schema=NULL): + XMLSchema_user_type(schema) {} + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + +}; + + +class XMLSchema_complexType: public XMLSchema_user_type +{ + XMLSchema_tag_attribute m_mixed; + XMLSchema_tag_attribute m_abstract; + XMLSchema_tag_attribute m_block; + /* XMLSchema_tag_attribute m_defaultAttributesApply; for 1.1 schema */ + + XMLSchema_std_attributes m_attributes; +public: + XMLSchema_complexType(XMLSchema_schema *schema=NULL): + XMLSchema_user_type(schema), + m_mixed(&xs_mixed), + m_abstract(&xs_abstract), + m_block(&xs_block) + { + declare_attribute(&m_mixed); + declare_attribute(&m_abstract); + declare_attribute(&m_block); + } + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + + bool validate_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + if (m_attributes.validate_attr(st, attr, len) == MY_XML_OK || + m_compositor->validate_attr(st, attr, len) == MY_XML_OK) + return MY_XML_OK; + return MY_XML_ERROR; + } + enum vtn_result validate_tag_name(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return m_compositor->validate_tag_name(st, attr, len); + } +}; + + +class XMLSchema_facet: public XMLSchema_tag +{ +protected: + XMLSchema_tag_attribute m_value; + XMLSchema_tag_attribute m_fixed; +public: + XMLSchema_facet(): XMLSchema_tag(), + m_value(&xs_value), + m_fixed(&xs_fixed) + { + declare_attribute(&m_value); + declare_attribute(&m_fixed); + } + + bool is_set() const { return m_value.is_set(); } +}; + + +class XMLSchema_enum_facet: public XMLSchema_facet +{ +public: + XMLSchema_enum_facet *m_next_enum; + bool eq(const char *value, size_t len) const + { + return m_value.eq_value(value, len); + } +}; + + +class XMLSchema_restriction_in_simpleType: public XMLSchema_tag +{ + XMLSchema_type *m_base_type; + XMLSchema_tag_attribute m_base; + + /* for numeric types. */ + XMLSchema_facet m_minInclusive, m_maxInclusive, + m_minExclusive, m_maxExclusive, + m_totalDigits, m_fractionDigits; + + /* for strings. */ + XMLSchema_facet m_length, m_minLength, m_maxLength, + m_pattern, m_whiteSpace; + + /* enum */ + XMLSchema_enum_facet *m_enumeration; + +public: + XMLSchema_restriction_in_simpleType(): XMLSchema_tag(), + m_base_type(NULL), + m_base(&xs_base), + m_enumeration(NULL) + { + declare_attribute(&m_base); + } + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool resolve_type(MY_XML_VALIDATION_DATA *st, + LEX_CSTRING *bad_type) override; + void validate_prepare() override + { + m_base_type->validate_prepare(); + } + bool validate_value(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; +}; + + +class XMLSchema_restriction_in_simpleContent: + public XMLSchema_restriction_in_simpleType +{ + XMLSchema_std_attributes m_attributes; +public: + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + int res; + + if (!(res= m_attributes.enter_tag(st, attr, len))) + return XMLSchema_restriction_in_simpleType::enter_tag(st, attr, len); + + return res > 0 ? MY_XML_OK : MY_XML_ERROR; + } + bool validate_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return m_attributes.validate_attr(st, attr, len); + } +}; + + +/* TODO implement and enable the complexContext +class XMLSchema_restriction_in_complexContent: public XMLSchema_complexType +{ +public: + XMLSchema_tag_attribute m_base; + XMLSchema_restriction_in_complexContent(): XMLSchema_complexType(), + m_base(&xs_base) + { + declare_attribute(&m_base); + } +}; +*/ + + +class XMLSchema_extension_in_simpleContent: public XMLSchema_tag +{ + XMLSchema_tag_attribute m_base; + XMLSchema_std_attributes m_attributes; +public: + XMLSchema_type *m_base_type; + XMLSchema_extension_in_simpleContent(): XMLSchema_tag(), + m_base(&xs_base), m_base_type(NULL) + { + declare_attribute(&m_base); + } + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + int res; + + if (!(res= m_attributes.enter_tag(st, attr, len))) + return XMLSchema_tag::enter_tag(st, attr, len); + + return res > 0 ? MY_XML_OK : MY_XML_ERROR; + } + bool leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool resolve_type(MY_XML_VALIDATION_DATA *st, + LEX_CSTRING *bad_type) override; + + void validate_prepare() override + { + m_base_type->validate_prepare(); + } + + bool validate_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return m_attributes.validate_attr(st, attr, len); + } + bool validate_value(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; +}; + + +/* TODO implement and enable the complexContext +class XMLSchema_extension_in_complexContent: + public XMLSchema_restriction_in_complexContent +{ +}; +*/ + + +class XMLSchema_list: public XMLSchema_tag +{ + XMLSchema_tag_attribute m_attr_itemType; + XMLSchema_type *m_type; +public: + XMLSchema_list(): XMLSchema_tag(), + m_attr_itemType(&xs_itemType), + m_type(NULL) + { + declare_attribute(&m_attr_itemType); + } + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool resolve_type(MY_XML_VALIDATION_DATA *st, + LEX_CSTRING *bad_type) override; +}; + + +class XMLSchema_union: public XMLSchema_tag +{ + XMLSchema_tag_attribute m_attr_memberTypes; + XMLSchema_user_type *m_nested_types; /* we have to preserver the order here */ + XMLSchema_user_type **m_nested_hook; +public: + XMLSchema_union(): XMLSchema_tag(), + m_attr_memberTypes(&xs_memberTypes), + m_nested_types(NULL), + m_nested_hook(&m_nested_types) + { + declare_attribute(&m_attr_memberTypes); + } + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; +}; + + +class XMLSchema_simpleContent: public XMLSchema_tag +{ + XMLSchema_tag *m_nested; /* extension or restriction */ +public: + XMLSchema_simpleContent(): XMLSchema_tag(), + m_nested(NULL) {} + + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + void validate_prepare() override { m_nested->validate_prepare(); } + bool validate_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return m_nested->validate_attr(st, attr, len); + } + bool validate_value(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return m_nested->validate_value(st, attr, len); + } +}; + + +/* TODO implement and enable the complexContext +class XMLSchema_complexContent: public XMLSchema_simpleContent +{ + XMLSchema_tag_attribute m_atr_mixed; + XMLSchema_tag *m_nested; // extension or restriction + +public: + XMLSchema_complexContent(): XMLSchema_simpleContent(), + m_atr_mixed(&xs_mixed) + { + declare_attribute(&m_atr_mixed); + } + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; +}; +*/ + + +class XMLSchema_element_global; + +class XMLSchema_all: public XMLSchema_tag +{ +protected: + XMLSchema_tag_integer_attribute m_minOccurs; + XMLSchema_tag_unbounded_integer_attribute m_maxOccurs; + XMLSchema_tag **m_tags_hook; + +public: + int m_counter; + XMLSchema_tag *m_tags; + + XMLSchema_all(): XMLSchema_tag(), + m_minOccurs(&xs_minOccurs), + m_maxOccurs(&xs_maxOccurs), + m_tags_hook(&m_tags), m_tags(NULL) + { + declare_attribute(&m_minOccurs); + declare_attribute(&m_maxOccurs); + } + + void append_tag(XMLSchema_tag *tag); + + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + *m_tags_hook= NULL; + return XMLSchema_tag::leave(st, attr, len); + } + void validate_prepare() override + { + m_counter= 0; + for (XMLSchema_tag *cur= m_tags; cur; cur= cur->m_next_tag) + cur->validate_prepare(); + } + + enum vtn_result validate_tag_name(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + enum vtn_result tn_result; + for (XMLSchema_tag *cur= m_tags; cur; cur= cur->m_next_tag) + { + if ((tn_result= cur->validate_tag_name(st, attr, len)) != VTN_CONTINUE) + { + if (tn_result == VTN_ACCEPTED) + m_counter= 1; + return tn_result; + } + } + return is_validate_done() ? VTN_CONTINUE : VTN_ERROR; + } + bool is_validate_done() override + { + if (m_counter == 0) + { + return m_minOccurs.m_value_int == 0; + } + + for (XMLSchema_tag *cur= m_tags; cur; cur= cur->m_next_tag) + { + if (!cur->is_validate_done()) + { + return false; + } + } + return true; + } +}; + + +class XMLSchema_sequence: public XMLSchema_all +{ + XMLSchema_tag *m_cur_tag; +public: + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + void validate_prepare() override + { + m_cur_tag= NULL; + m_counter= 0; + } + enum vtn_result validate_tag_name(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + bool full_loop= false; + bool beginning= false; + enum vtn_result tn_result; + + if (!m_cur_tag) + { + if (!m_tags) + return VTN_CONTINUE; +turn_over: + if (m_counter >= m_maxOccurs.m_value_int) + return VTN_CONTINUE; + + m_cur_tag= m_tags; + m_counter++; + beginning= true; + m_cur_tag->validate_prepare(); + } + + while ((tn_result= m_cur_tag->validate_tag_name(st, attr, len)) == + VTN_CONTINUE) + { + if (!m_cur_tag->is_validate_done()) + { + /* + If we already applied some elements of the sequence, + then we have to break the validation with the error. + Otherwise we still can try other rules. + */ + return beginning ? VTN_CONTINUE : VTN_ERROR; + } + + m_cur_tag= m_cur_tag->m_next_tag; + if (!m_cur_tag) + { + if (full_loop) + { + /* + Weird case when all elements are optional but + maxOccurs set to "unbound". Cut the endless loop. + */ + m_counter= m_maxOccurs.m_value_int; + } + full_loop= true; + goto turn_over; + } + m_cur_tag->validate_prepare(); + } + + return tn_result; + } + bool is_validate_done() override + { + if (m_counter < m_minOccurs.m_value_int) + return false; + while (m_cur_tag) + { + if (!m_cur_tag->is_validate_done()) + return false; + m_cur_tag= m_cur_tag->m_next_tag; + } + + return true; + } +}; + + +class XMLSchema_choice: public XMLSchema_sequence +{ + XMLSchema_tag *m_found; +public: + void validate_prepare() override + { + m_counter= 0; + m_found= NULL; + for (XMLSchema_tag *cur= m_tags; cur; cur= cur->m_next_tag) + { + cur->validate_prepare(); + } + } + enum vtn_result validate_tag_name(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + enum vtn_result tn_result; + + if (m_found) + { + if ((tn_result= m_found->validate_tag_name(st, attr, len)) == + VTN_CONTINUE) + { + if (!m_found->is_validate_done()) + return VTN_ERROR; + m_found= NULL; + } + else + return tn_result; + } + + if (m_counter >= m_maxOccurs.m_value_int) + return VTN_CONTINUE; + + + for (XMLSchema_tag *cur= m_tags; cur; cur= cur->m_next_tag) + { + if ((tn_result= cur->validate_tag_name(st, attr, len)) == VTN_ACCEPTED) + { + m_found= cur; + m_counter++; + return VTN_ACCEPTED; + } + + if (tn_result == VTN_ERROR) + return VTN_ERROR; + } + + return VTN_CONTINUE; + } + bool is_validate_done() override + { + if (m_found) + { + if (!m_found->is_validate_done()) + return false; + } + return m_counter >= m_minOccurs.m_value_int; + } +}; + + +class XMLSchema_group_def: public XMLSchema_tag +{ +public: + XMLSchema_tag *m_compositor; + XMLSchema_tag_attribute m_atr_name; + XMLSchema_group_def *m_next_group; + + XMLSchema_type *m_type; + XMLSchema_group_def(): XMLSchema_tag(), + m_compositor(NULL), + m_atr_name(&xs_name) + { + declare_attribute(&m_atr_name); + } + + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; +}; + + +class XMLSchema_group_reference: public XMLSchema_tag +{ + XMLSchema_tag_integer_attribute m_minOccurs; + XMLSchema_tag_unbounded_integer_attribute m_maxOccurs; + XMLSchema_tag_attribute m_ref; + XMLSchema_group_def *m_group; +public: + XMLSchema_group_reference(): XMLSchema_tag(), + m_minOccurs(&xs_minOccurs), + m_maxOccurs(&xs_maxOccurs), + m_ref(&xs_ref), + m_group(NULL) + { + declare_attribute(&m_minOccurs); + declare_attribute(&m_maxOccurs); + declare_attribute(&m_ref); + } + + bool leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + st->add_item_to_resolve(this); + return XMLSchema_tag::leave(st, attr, len); + } + bool resolve_type(MY_XML_VALIDATION_DATA *st, + LEX_CSTRING *bad_type) override; + + void validate_prepare() override + { + m_group->m_compositor->validate_prepare(); + } + enum vtn_result validate_tag_name(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return m_group->m_compositor->validate_tag_name(st, attr, len); + } + bool is_validate_done() override + { + return m_group->m_compositor->is_validate_done(); + } +}; + + +class XMLSchema_element_global: public XMLSchema_attribute +{ +public: + XMLSchema_element_global *m_next_element_global; + XMLSchema_tag_attribute m_atr_nillable; + XMLSchema_tag_attribute m_atr_abstract; + XMLSchema_tag_attribute m_atr_substitutionGroup; + XMLSchema_tag_attribute m_atr_block; + XMLSchema_tag_attribute m_atr_final; + + XMLSchema_element_global(): XMLSchema_attribute(), + m_atr_nillable(&xs_nillable), + m_atr_abstract(&xs_abstract), + m_atr_substitutionGroup(&xs_substitutionGroup), + m_atr_block(&xs_block), + m_atr_final(&xs_final) + { + declare_attribute(&m_atr_nillable); + declare_attribute(&m_atr_abstract); + declare_attribute(&m_atr_substitutionGroup); + declare_attribute(&m_atr_block); + declare_attribute(&m_atr_final); + } + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool resolve_type(MY_XML_VALIDATION_DATA *st, + LEX_CSTRING *bad_type) override; + bool validate_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool validate_value(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool validate_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool validate_leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + +}; + + +class XMLSchema_element_local: public XMLSchema_element_global +{ +public: + int m_counter; + XMLSchema_tag_attribute m_atr_ref; + XMLSchema_tag_integer_attribute m_atr_minOccurs; + XMLSchema_tag_unbounded_integer_attribute m_atr_maxOccurs; + XMLSchema_tag_attribute m_atr_form; + + XMLSchema_element_local(): XMLSchema_element_global(), + m_atr_ref(&xs_ref), + m_atr_minOccurs(&xs_minOccurs), + m_atr_maxOccurs(&xs_maxOccurs), + m_atr_form(&xs_form) + { + declare_attribute(&m_atr_ref); + declare_attribute(&m_atr_minOccurs); + declare_attribute(&m_atr_maxOccurs); + declare_attribute(&m_atr_form); + } + void validate_prepare() override + { + m_counter= 0; + } + enum vtn_result validate_tag_name(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + if (m_counter >= m_atr_maxOccurs.m_value_int) + return VTN_CONTINUE; + + if (m_atr_name.eq_value(attr, len)) + { + m_counter++; + m_type->validate_prepare(); + st->push(this); + return VTN_ACCEPTED; + } + + return VTN_CONTINUE; + } + bool is_validate_done() override + { + return m_counter >= m_atr_minOccurs.m_value_int; + } +}; + + +/* + This tag can appear both in the SCHEMA and the XML + itself. So the rules are equal for both schema parsing and + validation. +*/ +class XMLSchema_xml_tag_attribute: public XMLSchema_tag_attribute +{ +public: + XMLSchema_xml_tag_attribute(const xs_word *name): + XMLSchema_tag_attribute(name) {} + + bool validate_value(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return value(st, attr, len); + } + bool validate_leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return leave(st, attr, len); + } +}; + + +class XMLSchema_xml: public XMLSchema_tag +{ +public: + XMLSchema_xml_tag_attribute m_atr_version; + XMLSchema_xml_tag_attribute m_atr_encoding; + XMLSchema_xml(): XMLSchema_tag(), + m_atr_version(&xs_version), + m_atr_encoding(&xs_encoding) + { + declare_attribute(&m_atr_version); + declare_attribute(&m_atr_encoding); + } + bool validate_leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return leave(st, attr, len); + } + bool validate_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override + { + return enter_attr(st, attr, len); + } +}; + + +class XMLSchema_schema: public XMLSchema_tag +{ + XMLSchema_tag_attribute m_atr_elementFormDefault; + XMLSchema_tag_attribute m_atr_targetNamespace; + XMLSchema_tag_attribute m_atr_attributeFormDefault; + XMLSchema_tag_attribute m_atr_version; + XMLSchema_tag_attribute m_atr_xml_lang; + XMLSchema_tag_xmlns_attribute m_atr_xmlns; + +public: + XMLSchema_user_type *m_global_simpleTypes; + XMLSchema_user_type *m_global_complexTypes; + XMLSchema_attribute *m_global_attributes; + XMLSchema_element_global *m_global_elements; + XMLSchema_attributeGroup_def *m_global_attr_groups; + XMLSchema_group_def *m_global_element_groups; + + List m_items_to_resolve; + + XMLSchema_schema(): XMLSchema_tag(), + m_atr_elementFormDefault(&xs_elementFormDefault), + m_atr_targetNamespace(&xs_targetNamespace), + m_atr_attributeFormDefault(&xs_attributeFormDefault), + m_atr_version(&xs_version), + m_atr_xml_lang(&xs_xml_lang), + m_global_simpleTypes(NULL), + m_global_complexTypes(NULL), + m_global_attributes(NULL), + m_global_elements(NULL), + m_global_attr_groups(NULL), + m_global_element_groups(NULL) + { + declare_attribute(&m_atr_elementFormDefault); + declare_attribute(&m_atr_attributeFormDefault); + declare_attribute(&m_atr_targetNamespace); + declare_attribute(&m_atr_version); + declare_attribute(&m_atr_xml_lang); + } + + XMLSchema_type *find_simple_type_by_name(MY_XML_VALIDATION_DATA *st, + const char *name, size_t len) const; + XMLSchema_type *find_type_by_name(MY_XML_VALIDATION_DATA *st, + const char *name, size_t len) const; + XMLSchema_element_global *find_element(const char *name, size_t len) const + { + XMLSchema_element_global *el= m_global_elements; + for(; el; el= el->m_next_element_global) + { + if (el->validate_name(name, len)) + return el; + } + return NULL; + } + + bool validate_element(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len); + bool enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + bool enter_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) override; + + XMLSchema_type *find_simple_type(const char *name, size_t len) const; + XMLSchema_type *find_complex_type(const char *name, size_t len) const; +}; + + +/* implementations */ + +/* XML schema builtin types. */ +static xs_word xs_anyType(STRING_WITH_LEN("anyType")); +static xs_word xs_anySimpleType(STRING_WITH_LEN("anySimpleType")); + +/* integers */ +static xs_word xs_integer(STRING_WITH_LEN("integer")); +static xs_word xs_long(STRING_WITH_LEN("long")); +static xs_word xs_int(STRING_WITH_LEN("int")); +static xs_word xs_short( STRING_WITH_LEN("short")); +static xs_word xs_byte(STRING_WITH_LEN("byte")); + +/* non-negative integers */ +static xs_word xs_nonNegativeInteger(STRING_WITH_LEN("nonNegativeInteger")); +static xs_word xs_positiveInteger(STRING_WITH_LEN("positiveInteger")); +static xs_word xs_unsignedInt(STRING_WITH_LEN("unsignedInt")); +static xs_word xs_unsignedLong(STRING_WITH_LEN("unsignedLong")); + +/* numeric */ +static xs_word xs_decimal(STRING_WITH_LEN("decimal")); +static xs_word xs_double(STRING_WITH_LEN("double")); +static xs_word xs_float(STRING_WITH_LEN("float")); +static xs_word xs_boolean(STRING_WITH_LEN("boolean")); +static xs_word xs_true(STRING_WITH_LEN("true")); +static xs_word xs_false(STRING_WITH_LEN("false")); + +/* strings */ +static xs_word xs_string(STRING_WITH_LEN("string")); +static xs_word xs_anyURI(STRING_WITH_LEN("anyURI")); +static xs_word xs_QName(STRING_WITH_LEN("QName")); +static xs_word xs_NOTATION(STRING_WITH_LEN("NOTATION")); +static xs_word xs_normalizedString(STRING_WITH_LEN("normalizedString")); +static xs_word xs_language(STRING_WITH_LEN("language")); +static xs_word xs_NMTOKEN(STRING_WITH_LEN("NMTOKEN")); + +/* date/time */ +static xs_word xs_date(STRING_WITH_LEN("date")); +static xs_word xs_time(STRING_WITH_LEN("time")); +static xs_word xs_dateTime(STRING_WITH_LEN("dateTime")); +static xs_word xs_duration(STRING_WITH_LEN("duration")); +static xs_word xs_gDay(STRING_WITH_LEN("gDay")); +static xs_word xs_gMonth(STRING_WITH_LEN("gMonth")); +static xs_word xs_gMonthDay(STRING_WITH_LEN("gMonthDay")); +static xs_word xs_gYear(STRING_WITH_LEN("gYear")); +static xs_word xs_gYearMonth(STRING_WITH_LEN("gYearMonth")); + +/* identifiers */ +static xs_word xs_ID(STRING_WITH_LEN("ID")); +static xs_word xs_IDREF(STRING_WITH_LEN("IDREF")); +static xs_word xs_ENTITY(STRING_WITH_LEN("ENTITY")); + +/* binary */ +static xs_word xs_base64Binary(STRING_WITH_LEN("base64Binary")); +static xs_word xs_hexBinary(STRING_WITH_LEN("hexBinary")); + + +class XMLSchema_bool_builtin_type: public XMLSchema_builtin_type +{ +public: + bool valid_value(const char *value, size_t len) override + { + return xs_true.eq(value, len) || xs_false.eq(value, len); + } +}; + + +XMLSchema_builtin_type *XMLSchema_builtin_type::get_builtin_type_by_name( + MY_XML_VALIDATION_DATA *st, const char *name, size_t len) +{ + /* strings */ + if (xs_string.eq(name, len) || + xs_anyURI.eq(name, len) || + xs_QName.eq(name, len) || + xs_NOTATION.eq(name, len) || + xs_normalizedString.eq(name, len) || + xs_language.eq(name, len) || + xs_NMTOKEN.eq(name, len)) + return new(st->mem_root) XMLSchema_string_builtin_type; + + /* integers */ + if (xs_integer.eq(name, len)) + return new(st->mem_root) XMLSchema_num_builtin_type(NUM_TYPE_INT); + + if (xs_long.eq(name, len)) + return new(st->mem_root) XMLSchema_num_builtin_type(NUM_TYPE_INT); + + if (xs_int.eq(name, len)) + return new(st->mem_root) XMLSchema_num_builtin_type(NUM_TYPE_INT); + + if (xs_short.eq(name, len)) + return new(st->mem_root) XMLSchema_num_builtin_type(NUM_TYPE_INT); + + if (xs_byte.eq(name, len)) + return new(st->mem_root) XMLSchema_num_builtin_type(NUM_TYPE_INT); + +/* non-negative integers */ + if (xs_nonNegativeInteger.eq(name, len)) + return new(st->mem_root) XMLSchema_num_builtin_type(NUM_TYPE_UINT); + + if (xs_positiveInteger.eq(name, len)) + return new(st->mem_root) XMLSchema_num_builtin_type(NUM_TYPE_UINT); + + if (xs_unsignedInt.eq(name, len)) + return new(st->mem_root) XMLSchema_num_builtin_type(NUM_TYPE_UINT); + + if (xs_unsignedLong.eq(name, len)) + return new(st->mem_root) XMLSchema_num_builtin_type(NUM_TYPE_UINT); + +/* numeric */ + if (xs_decimal.eq(name, len)) + return new(st->mem_root) XMLSchema_num_builtin_type(NUM_TYPE_DEC); + + if (xs_double.eq(name, len)) + return new(st->mem_root) XMLSchema_num_builtin_type(NUM_TYPE_FLOAT); + + if (xs_float.eq(name, len)) + return new(st->mem_root) XMLSchema_num_builtin_type(NUM_TYPE_FLOAT); + + if (xs_boolean.eq(name, len)) + return new(st->mem_root) XMLSchema_bool_builtin_type; + + /* date/time types */ + if (xs_dateTime.eq(name, len)) + return new(st->mem_root) XMLSchema_datetime_builtin_type(T_GO); + + if (xs_date.eq(name, len)) + return new(st->mem_root) XMLSchema_datetime_builtin_type(T_dGO); + + if (xs_time.eq(name, len)) + return new(st->mem_root) XMLSchema_datetime_builtin_type(T_tGO); + + if (xs_gYear.eq(name, len)) + return new(st->mem_root) XMLSchema_datetime_builtin_type(T_yGO); + + if (xs_gMonth.eq(name, len)) + return new(st->mem_root) XMLSchema_datetime_builtin_type(T_mGO); + + if (xs_gDay.eq(name, len)) + return new(st->mem_root) XMLSchema_datetime_builtin_type(T_aGO); + + if (xs_gMonthDay.eq(name, len)) + return new(st->mem_root) XMLSchema_datetime_builtin_type(T_nGO); + + if (xs_base64Binary.eq(name, len)) + return new(st->mem_root) XMLSchema_binary_builtin_type(B_B640); + + if (xs_hexBinary.eq(name, len)) + return new(st->mem_root) XMLSchema_binary_builtin_type(B_HEX0); + + /* various types */ + if (xs_anySimpleType.eq(name, len)) + return new(st->mem_root) XMLSchema_builtin_type; + + return NULL; +} + + +void MY_XML_VALIDATION_DATA::add_item_to_resolve(XMLSchema_item *t) +{ + schema->m_items_to_resolve.push_back(t, mem_root); +} + + +bool MY_XML_VALIDATION_DATA::xs_namespace(const char **name, size_t *len) const +{ + if (*len > schema_namespace.length && + memcmp(*name, schema_namespace.str, schema_namespace.length) == 0) + { + (*name)+= schema_namespace.length; + (*len)-= schema_namespace.length; + return true; + } + + return false; +} + + +bool XMLSchema_root::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + if (xs_xml.eq(attr, len)) + { + st->push(st->xml); + } + else if (xs_schema.eq(attr, len)) + { + st->schema= new(st->mem_root) XMLSchema_schema; + st->push(st->schema); + } + else + return XMLSchema_item::enter_tag(st, attr, len); + + return MY_XML_OK; +} + + +bool XMLSchema_root::validate_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + if (xs_xml.eq(attr, len)) + { + st->push(st->xml); + } + else + { + return st->schema->validate_element(st, attr, len); + } + + return MY_XML_OK; +} + + +XMLSchema_tag_xmlns_attribute::XMLSchema_tag_xmlns_attribute(): + XMLSchema_tag_attribute(&xs_xmlns) +{} + + +bool XMLSchema_tag_xmlns_attribute::value( + MY_XML_VALIDATION_DATA *st, const char *attr, size_t len) +{ + m_val= attr; + m_val_len= len; + + if ((len >= xs_uri_short.length() && + memcmp(xs_uri_short.str(), attr, xs_uri_short.length()) == 0) || + (len >= xs_uri_www.length() && + memcmp(xs_uri_www.str(), attr, xs_uri_www.length()) == 0)) + { + if (st->set_schema_namespace(&m_ns_name)) + return MY_XML_ERROR; + } + + return MY_XML_OK; +} + + +int XMLSchema_std_attributes::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + if (xs_attribute.eq(attr, len)) + { + XMLSchema_attribute *atr= new(st->mem_root) XMLSchema_attribute; + + atr->m_next_attribute= m_attributes; + m_attributes= atr; + + st->push(atr); + } + else if (xs_attributeGroup.eq(attr, len)) + { + XMLSchema_attributeGroup_reference *ref= + new(st->mem_root) XMLSchema_attributeGroup_reference; + ref->m_next_ref= m_groups; + m_groups= ref; + + st->push(ref); + } + else if (xs_anyAttribute.eq(attr, len)) + { + if (m_anyAttribute) + return -1; /* can't have two anyAttribute-s */ + + m_anyAttribute= new(st->mem_root) XMLSchema_anyAttribute; + st->push(m_anyAttribute); + } + else + return 0; + + return 1; +} + + +int XMLSchema_std_attributes::validate_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + for (XMLSchema_attribute *atr= m_attributes; + atr; atr= atr->m_next_attribute) + { + if (atr->validate_name(attr, len)) + { + atr->push_self(st); + return MY_XML_OK; + } + } + + for (XMLSchema_attributeGroup_reference *g= m_groups; + g; g= g->m_next_ref) + { + if (g->validate_attr(st, attr, len) == MY_XML_OK) + return MY_XML_OK; + + /* attribute wasn't found in the group. */ + } + + if (m_anyAttribute) + { + m_anyAttribute->push_self(st); + return MY_XML_OK; + } + return MY_XML_ERROR; +} + + +bool XMLSchema_attribute::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + XMLSchema_type *t; + + if (xs_simpleType.eq(attr, len)) + { + if (m_type) + return MY_XML_ERROR; /* several types specified. */ + + t= new(st->mem_root) XMLSchema_simpleType; + m_type= t; + st->push(t); + return MY_XML_OK; + } + + return XMLSchema_tag::enter_tag(st, attr, len); +} + + +bool XMLSchema_attribute::leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + if (!m_atr_name.is_set()) + return MY_XML_ERROR; /* name must be specified. */ + + if (m_type) + { + if (m_atr_type.is_set()) + return MY_XML_ERROR; /* only one type should be specified. */ + } + else + { + st->add_item_to_resolve(this); + } + + return XMLSchema_item::leave(st, attr, len); +} + + +bool XMLSchema_attribute::resolve_type( + MY_XML_VALIDATION_DATA *st, LEX_CSTRING *bad_type) +{ + m_type= st->schema->find_simple_type_by_name( + st, m_atr_type.m_val, m_atr_type.m_val_len); + if (m_type) + return MY_XML_OK; + + bad_type->str= m_atr_type.m_val; + bad_type->length= m_atr_type.m_val_len; + return MY_XML_ERROR; +} + + +bool XMLSchema_attributeGroup_reference::leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + if (!m_atr_ref.is_set()) + return MY_XML_ERROR; + + st->add_item_to_resolve(this); + + return XMLSchema_tag::leave(st, attr, len); +} + + +bool XMLSchema_attributeGroup_reference::resolve_type( + MY_XML_VALIDATION_DATA *st, LEX_CSTRING *bad_type) +{ + m_group= st->find_attribute_group_by_name( + m_atr_ref.m_val, m_atr_ref.m_val_len); + if (m_group) + return MY_XML_OK; + bad_type->str= m_atr_ref.m_val; + bad_type->length= m_atr_ref.m_val_len; + return MY_XML_ERROR; +} + + +bool XMLSchema_simpleType::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + XMLSchema_tag *def= NULL; + + if (xs_restriction.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_restriction_in_simpleType; + } + else if (xs_list.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_list; + } + else if (xs_union.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_union; + } + + if (def) + { + if (m_compositor) + return MY_XML_ERROR; /* exactly one tag allowed. */ + + m_compositor= def; + st->push(def); + + return MY_XML_OK; + } + + return XMLSchema_user_type::enter_tag(st, attr, len); +} + + +bool XMLSchema_simpleType::leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + if (m_schema) + { + if (!m_type_name.is_set()) + return MY_XML_OK; /* type neme should be specified here. */ + + m_next_type= m_schema->m_global_simpleTypes; + m_schema->m_global_simpleTypes= this; + } + return XMLSchema_user_type::leave(st, attr, len); +} + + +bool XMLSchema_complexType::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + int res; + XMLSchema_tag *def= NULL; + + if (xs_smipleContent.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_simpleContent; + } +/* TODO implement and enable the complexContext + else if (xs_complexContent.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_complexContent; + } +*/ + else if (xs_sequence.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_sequence; + } + else if (xs_choice.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_choice; + } + else if (xs_all.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_all; + } + else if (xs_group.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_group_reference; + } + else if ((res= m_attributes.enter_tag(st, attr, len))) + { + if (res < 0) + return MY_XML_ERROR; + } + else + return XMLSchema_user_type::enter_tag(st, attr, len); + + /* Can have exactly one of these tags.. */ + if (def) + { + if (m_compositor) + return MY_XML_ERROR; + + st->push(def); + m_compositor= def; + } + + return MY_XML_OK; +} + + +bool XMLSchema_complexType::leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + if (m_schema) + { + if (!m_type_name.is_set()) + return MY_XML_OK; /* type neme should be specified here. */ + + m_next_type= m_schema->m_global_complexTypes; + m_schema->m_global_complexTypes= this; + } + return XMLSchema_user_type::leave(st, attr, len); +} + + +bool XMLSchema_restriction_in_simpleType::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, + size_t len) +{ + if (xs_minInclusive.eq(attr, len)) + { + st->push(&m_minInclusive); + } + else if (xs_maxInclusive.eq(attr, len)) + { + st->push(&m_maxInclusive); + } + else if (xs_minExclusive.eq(attr, len)) + { + st->push(&m_minExclusive); + } + else if (xs_maxExclusive.eq(attr, len)) + { + st->push(&m_maxExclusive); + } + else if (xs_totalDigits.eq(attr, len)) + { + st->push(&m_totalDigits); + } + else if (xs_fractionDigits.eq(attr, len)) + { + st->push(&m_fractionDigits); + } + else if (xs_length.eq(attr, len)) + { + st->push(&m_length); + } + else if (xs_minLength.eq(attr, len)) + { + st->push(&m_minLength); + } + else if (xs_maxLength.eq(attr, len)) + { + st->push(&m_maxLength); + } + else if (xs_pattern.eq(attr, len)) + { + st->push(&m_pattern); + } + else if (xs_whiteSpace.eq(attr, len)) + { + st->push(&m_whiteSpace); + } + else if (xs_enumeration.eq(attr, len)) + { + XMLSchema_enum_facet *en= new(st->mem_root) XMLSchema_enum_facet; + en->m_next_enum= m_enumeration; + m_enumeration= en; + st->push(en); + } + else + return XMLSchema_tag::enter_tag(st, attr, len); + + return MY_XML_OK; +} + + +bool XMLSchema_restriction_in_simpleType::leave( + MY_XML_VALIDATION_DATA *st, const char *attr, size_t len) +{ + if (m_base.is_set()) + { + if (m_base_type) + return MY_XML_ERROR; /* type should be specified only once. */ + + st->add_item_to_resolve(this); + } + else + { + if (!m_base_type) + return MY_XML_ERROR; /* no type specified. */ + } + + return XMLSchema_tag::leave(st, attr, len); +} + + +bool XMLSchema_restriction_in_simpleType::resolve_type( + MY_XML_VALIDATION_DATA *st, LEX_CSTRING *bad_type) +{ + m_base_type= st->schema->find_type_by_name( + st, m_base.m_val, m_base.m_val_len); + if (!m_base_type) + return MY_XML_ERROR; + return MY_XML_OK; +} + + +bool XMLSchema_restriction_in_simpleType::validate_value( + MY_XML_VALIDATION_DATA *st, const char *attr, size_t len) +{ + if (m_enumeration) + { + for (XMLSchema_enum_facet *en= m_enumeration; en; en= en->m_next_enum) + { + if (en->eq(attr, len)) + return MY_XML_OK; + } + return MY_XML_ERROR; + } + + /* TODO check other facets. */ + return m_base_type->validate_value(st, attr, len); +} + + +bool XMLSchema_extension_in_simpleContent::leave( + MY_XML_VALIDATION_DATA *st, const char *attr, size_t len) +{ + if (m_base.is_set()) + { + if (m_base_type) + return MY_XML_ERROR; /* type should be specified only once. */ + + st->add_item_to_resolve(this); + } + else + { + if (!m_base_type) + return MY_XML_ERROR; /* no type specified. */ + } + + return XMLSchema_tag::leave(st, attr, len); +} + + +bool XMLSchema_extension_in_simpleContent::resolve_type( + MY_XML_VALIDATION_DATA *st, LEX_CSTRING *bad_type) +{ + m_base_type= st->schema->find_type_by_name( + st, m_base.m_val, m_base.m_val_len); + if (!m_base_type) + return MY_XML_ERROR; + return MY_XML_OK; +} + + +bool XMLSchema_extension_in_simpleContent::validate_value( + MY_XML_VALIDATION_DATA *st, const char *attr, size_t len) +{ + return m_base_type->validate_value(st, attr, len); +} + + +bool XMLSchema_list::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + if (xs_simpleType.eq(attr, len)) + { + if (m_type) + return MY_XML_ERROR; /* several types specified. */ + + m_type= new(st->mem_root) XMLSchema_simpleType; + st->push(m_type); + + return MY_XML_OK; + } + + return XMLSchema_tag::enter_tag(st, attr, len); +} + + +bool XMLSchema_list::leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + bool res= XMLSchema_tag::leave(st, attr, len); + + if (m_type) + { + return m_attr_itemType.is_set() ? MY_XML_ERROR /* can't have two */ : + res; + } + if (!m_attr_itemType.is_set()) + return MY_XML_ERROR; /* type must be specified. */ + + st->add_item_to_resolve(this); + return res; +} + + +bool XMLSchema_list::resolve_type( + MY_XML_VALIDATION_DATA *st, LEX_CSTRING *bad_type) +{ + m_type= st->schema->find_type_by_name(st, + m_attr_itemType.m_val, m_attr_itemType.m_val_len); + + if (m_type) + return MY_XML_OK; + + bad_type->str= m_attr_itemType.m_val; + bad_type->length= m_attr_itemType.m_val_len; + return MY_XML_ERROR; +} + + +bool XMLSchema_union::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + if (xs_simpleType.eq(attr, len)) + { + XMLSchema_simpleType *t= new(st->mem_root) XMLSchema_simpleType; + st->push(t); + *m_nested_hook= t->m_next_type; + m_nested_hook= &t->m_next_type; + return MY_XML_OK; + } + + return XMLSchema_tag::enter_tag(st, attr, len); +} + + +bool XMLSchema_union::leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + bool res= XMLSchema_tag::leave(st, attr, len); + + *m_nested_hook= NULL; + + if (m_nested_types) + return m_attr_memberTypes.is_set() ? MY_XML_ERROR : res; + + return m_attr_memberTypes.is_set() ? res : MY_XML_ERROR; +} + + +bool XMLSchema_simpleContent::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + XMLSchema_tag *t= NULL; + + if (xs_restriction.eq(attr, len)) + { + t= new(st->mem_root) XMLSchema_restriction_in_simpleContent; + } + else if (xs_extension.eq(attr, len)) + { + t= new(st->mem_root) XMLSchema_extension_in_simpleContent; + } + + if (!t) + return XMLSchema_tag::enter_tag(st, attr, len); + + if (m_nested) + return MY_XML_ERROR; /* only one nested allowed. */ + + st->push(t); + m_nested= t; + return MY_XML_OK; +} + + +/* TODO implement and enable the complexContext +bool XMLSchema_complexContent::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + XMLSchema_tag *t= NULL; + + if (xs_restriction.eq(attr, len)) + { + t= new(st->mem_root) XMLSchema_restriction_in_complexContent; + } + else if (xs_extension.eq(attr, len)) + { + t= new(st->mem_root) XMLSchema_extension_in_complexContent; + } + + if (!t) + return XMLSchema_simpleContent::enter_tag(st, attr, len); + + if (m_nested) + return MY_XML_ERROR; // + + st->push(t); + m_nested= t; + return MY_XML_OK; +} +*/ + + +void XMLSchema_all::append_tag(XMLSchema_tag *tag) +{ + *m_tags_hook= tag; + m_tags_hook= &tag->m_next_tag; +} + + +bool XMLSchema_all::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + XMLSchema_tag *def= NULL; + + if (xs_element.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_element_local; + } + else + return XMLSchema_tag::enter_tag(st, attr, len); + + if (def == NULL) + return MY_XML_ERROR; /* OOM */ + + append_tag(def); + st->push(def); + + return MY_XML_OK; +} + + +bool XMLSchema_sequence::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + XMLSchema_tag *def= NULL; + + if (xs_sequence.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_sequence; + } + else if (xs_choice.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_choice; + } + else if (xs_any.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_any; + } + else if (xs_group.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_group_reference; + } + else + return XMLSchema_all::enter_tag(st, attr, len); + + if (def == NULL) + return MY_XML_ERROR; /* OOM */ + + append_tag(def); + st->push(def); + + return MY_XML_OK; +} + + +bool XMLSchema_group_def::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + XMLSchema_tag *def= NULL; + + if (xs_sequence.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_sequence; + } + else if (xs_choice.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_choice; + } + else if (xs_all.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_all; + } + else + return XMLSchema_tag::enter_tag(st, attr, len); + + if (def == NULL || m_compositor != NULL) + return MY_XML_ERROR; + + m_compositor= def; + st->push(def); + + return MY_XML_OK; +} + + +bool XMLSchema_group_reference::resolve_type(MY_XML_VALIDATION_DATA *st, + LEX_CSTRING *bad_type) +{ + m_group= st->find_element_group_by_name( m_ref.m_val, m_ref.m_val_len); + if (m_group) + return MY_XML_OK; + bad_type->str= m_ref.m_val; + bad_type->length= m_ref.m_val_len; + return MY_XML_ERROR; +} + + +bool XMLSchema_element_global::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + XMLSchema_type *def= NULL; + + if (xs_complexType.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_complexType; + } +/* TODO implement these + else if (xs_unique.eq(attr, len)) + { + } + else if (xs_key.eq(attr, len)) + { + } + else if (xs_keyref.eq(attr, len)) + { + } +*/ + else + return XMLSchema_attribute::enter_tag(st, attr, len); + + if (def == NULL || m_type != NULL) + return MY_XML_ERROR; + + m_type= def; + st->push(def); + + return MY_XML_OK; +} + + +bool XMLSchema_element_global::resolve_type( + MY_XML_VALIDATION_DATA *st, LEX_CSTRING *bad_type) +{ + m_type= st->schema->find_type_by_name( + st, m_atr_type.m_val, m_atr_type.m_val_len); + if (m_type) + return MY_XML_OK; + + bad_type->str= m_atr_type.m_val; + bad_type->length= m_atr_type.m_val_len; + return MY_XML_ERROR; +} + + +bool XMLSchema_element_global::validate_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + return m_type->validate_attr(st, attr, len); +} + + +bool XMLSchema_element_global::validate_value(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + return m_type->validate_value(st, attr, len); +} + + +bool XMLSchema_element_global::validate_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + if (m_type->validate_tag_name(st, attr, len) != VTN_ACCEPTED) + return validate_failed(st); + + return MY_XML_OK; +} + + +bool XMLSchema_element_global::validate_leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + st->pop(); + return m_type->is_validate_done() ? MY_XML_OK : MY_XML_ERROR; +} + + +bool XMLSchema_schema::enter_tag(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + XMLSchema_item *def= NULL; + + if (xs_element.eq(attr, len)) + { + XMLSchema_element_global *el= new(st->mem_root) XMLSchema_element_global; + el->m_next_element_global= m_global_elements; + m_global_elements= el; + def= el; + } + else if (xs_attribute.eq(attr, len)) + { + XMLSchema_attribute *atr= new(st->mem_root) XMLSchema_attribute; + atr->m_next_attribute= m_global_attributes;; + m_global_attributes= atr; + def= atr; + } + else if (xs_complexType.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_complexType(this); + } + else if (xs_simpleType.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_simpleType(this); + } + else if (xs_group.eq(attr, len)) + { + XMLSchema_group_def *g= new(st->mem_root) XMLSchema_group_def; + g->m_next_group= m_global_element_groups; + m_global_element_groups= g; + def= g; + } + else if (xs_attributeGroup.eq(attr, len)) + { + XMLSchema_attributeGroup_def *g= + new(st->mem_root) XMLSchema_attributeGroup_def; + g->m_next_group= m_global_attr_groups; + m_global_attr_groups= g; + def= g; + } + else if (xs_notation.eq(attr, len)) + { + def= new(st->mem_root) XMLSchema_annotation; + } + else + return XMLSchema_tag::enter_tag(st, attr, len); + + if (def == NULL) + return MY_XML_ERROR; /*OOM*/ + + st->push(def); + return MY_XML_OK; +} + + +bool XMLSchema_schema::enter_attr(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + if (len >= xs_xmlns.length() && + memcmp(attr, xs_xmlns.str(), xs_xmlns.length()) == 0) + { + /* set namespace for the schema */ + if (len == xs_xmlns.length()) + { + /* empty namespace name. */ + m_atr_xmlns.m_ns_name.str= ""; + m_atr_xmlns.m_ns_name.length= 0; + } + else if (attr[xs_xmlns.length()] == MY_XPATH_LEX_COLON) + { + m_atr_xmlns.m_ns_name.str= attr + xs_xmlns.length()+1; + m_atr_xmlns.m_ns_name.length= len - (xs_xmlns.length()+1); + } + else + goto run_inherited; + + st->push(&m_atr_xmlns); + return MY_XML_OK; + } +run_inherited: + return XMLSchema_tag::enter_attr(st, attr, len); +} + + +XMLSchema_type *XMLSchema_schema::find_simple_type( + const char *name, size_t len) const +{ + XMLSchema_user_type *t= m_global_simpleTypes; + for(; t; t= t->m_next_type) + { + if (t->validate_name(name, len)) + return t; + } + return NULL; +} + + +XMLSchema_type *XMLSchema_schema::find_complex_type( + const char *name, size_t len) const +{ + XMLSchema_user_type *t= m_global_complexTypes; + for(; t; t= t->m_next_type) + { + if (t->validate_name(name, len)) + return t; + } + return NULL; +} + + +XMLSchema_type *XMLSchema_schema::find_simple_type_by_name( + MY_XML_VALIDATION_DATA *st, const char *name, size_t len) const +{ + XMLSchema_type *result= NULL; + + if (st->xs_namespace(&name, &len)) + { + XMLSchema_builtin_type *builtin_type; + builtin_type= XMLSchema_builtin_type::get_builtin_type_by_name( + st, name, len); + + if (builtin_type) + { + result= new(st->mem_root) XMLSchema_simple_builtin_type(builtin_type); + } + + if (xs_anyType.eq(name, len)) + result= new(st->mem_root) XMLSchema_any_type; + + if (result || !st->namespace_empty()) + goto exit; + } + + result= find_simple_type(name, len); + +exit: + return result; +} + + +XMLSchema_type *XMLSchema_schema::find_type_by_name( + MY_XML_VALIDATION_DATA *st, const char *name, size_t len) const +{ + XMLSchema_type *result; + + if ((result= find_simple_type_by_name(st, name, len))) + return result; + + result= find_complex_type(name, len); + + return result; +} + + +XMLSchema_attributeGroup_def * +MY_XML_VALIDATION_DATA::find_attribute_group_by_name( + const char *name, size_t len) const +{ + for (XMLSchema_attributeGroup_def *g=schema->m_global_attr_groups; + g; g=g->m_next_group) + { + if (g->m_atr_name.eq_value(name, len)) + return g; + } + return NULL; +} + + +XMLSchema_group_def *MY_XML_VALIDATION_DATA::find_element_group_by_name( + const char *name, size_t len) const +{ + for (XMLSchema_group_def *g=schema->m_global_element_groups; + g; g=g->m_next_group) + { + if (g->m_atr_name.eq_value(name, len)) + return g; + } + return NULL; +} + + +bool XMLSchema_schema::validate_element(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + XMLSchema_element_global *e= find_element(attr, len); + if (!e) + return validate_failed(st); + + e->validate_prepare(); + st->push(e); + return MY_XML_OK; +} + + +void XMLSchema_attribute::validate_prepare() +{ + m_type->validate_prepare(); +} + + +bool XMLSchema_attribute::validate_value(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + return m_type->validate_value(st, attr, len); +} + + +bool XMLSchema_attribute::validate_leave(MY_XML_VALIDATION_DATA *st, + const char *attr, size_t len) +{ + st->pop(); + return m_type->validate_leave(st, attr, len); +} + + +extern "C" { +static int schema_enter(MY_XML_PARSER *st,const char *attr, size_t len) +{ + MY_XML_VALIDATION_DATA *data= (MY_XML_VALIDATION_DATA *) st->user_data; + + if (st->current_node_type == MY_XML_NODE_TAG) + { + if (data->namespace_not_specified() && !xs_xml.eq(attr, len)) + { + size_t col_pos= 0; + data->schema_namespace.str= ""; + data->schema_namespace.length= 0; + + while (col_pos < len) + { + if (attr[col_pos++] == MY_XPATH_LEX_COLON) + { + data->schema_namespace.str= attr; + data->schema_namespace.length= col_pos; + + attr+= col_pos; + len-= col_pos; + break; + } + } + } + else if (!data->xs_namespace(&attr, &len)) + { + /* all the schema tags must be in same schema namespace. */ + return MY_XML_ERROR; + } + + if (!data->s_stack) + { + DBUG_ASSERT(0); /* Shouldn't happen. */ + return MY_XML_ERROR; + } + return data->s_stack->enter_tag(data, attr, len); + } + else if (st->current_node_type == MY_XML_NODE_TEXT) + { + } + else if (st->current_node_type == MY_XML_NODE_ATTR) + { + if (!data->s_stack) + { + DBUG_ASSERT(0); /* Shouldn't happen. */ + return MY_XML_ERROR; + } + return data->s_stack->enter_attr(data, attr, len); + } + + return MY_XML_OK; +} + + +static int schema_value(MY_XML_PARSER *st,const char *attr, size_t len) +{ + MY_XML_VALIDATION_DATA *data= (MY_XML_VALIDATION_DATA *) st->user_data; + + return data->s_stack->value(data, attr, len); +} + + +static int schema_leave(MY_XML_PARSER *st,const char *attr, size_t len) +{ + MY_XML_VALIDATION_DATA *data= (MY_XML_VALIDATION_DATA *) st->user_data; + + return data->s_stack->leave(data, attr, len); +} + +} /* extern "C" */ + + +static int schema_parse(THD *thd, const String *xml, + MY_XML_VALIDATION_DATA *user_data) +{ + MY_XML_PARSER p; + int rc; + + user_data->mem_root= thd->mem_root; + + /* Prepare XML parser */ + my_xml_parser_create(&p); + p.flags= MY_XML_FLAG_RELATIVE_NAMES | MY_XML_FLAG_SKIP_TEXT_NORMALIZATION; + + my_xml_set_enter_handler(&p, schema_enter); + my_xml_set_value_handler(&p, schema_value); + my_xml_set_leave_handler(&p, schema_leave); + my_xml_set_user_data(&p, (void*) user_data); + + /* Execute XML parser */ + rc= my_xml_parse(&p, xml->ptr(), xml->length()) != MY_XML_OK; + my_xml_parser_free(&p); + + if (rc) + { + char buf[128]; + my_snprintf(buf, sizeof(buf)-1, + "XML Schema parse error at line %d pos %lu: %s", + my_xml_error_lineno(&p) + 1, + (ulong) my_xml_error_pos(&p) + 1, + my_xml_error_string(&p)); + my_printf_error(ER_WRONG_VALUE, ER_THD(thd, ER_WRONG_VALUE), MYF(0), + "XML", buf); + return 1; + } + + if (user_data->schema == NULL) + { + my_printf_error(ER_UNKNOWN_ERROR, "Invalid XML Schema.", MYF(0)); + return 1; + } + + /* resolve typenames */ + { + List_iterator_fast li( + user_data->schema->m_items_to_resolve); + LEX_CSTRING bad_type; + XMLSchema_item *i; + + while ((i= li++)) + { + if (i->resolve_type(user_data, &bad_type)) + { + char er_buf[512]; + my_snprintf(er_buf, sizeof(er_buf), + "Invalid XML schema, type %.*s not defined.", + (int) bad_type.length, bad_type.str); + my_printf_error(ER_UNKNOWN_ERROR, er_buf, MYF(0)); + return 1; + } + } + } + + return 0; +} + + +bool Item_func_xml_isvalid::fix_length_and_dec(THD *thd) +{ + String *schema_str; + + if (Item_bool_func::fix_length_and_dec(thd)) + return TRUE; + + status_var_increment(current_thd->status_var.feature_xml); + + set_maybe_null(); + + m_data= NULL; + if (!args[1]->const_item()) + { + my_printf_error(ER_UNKNOWN_ERROR, + "Only constant XML Schema-s are supported.", MYF(0)); + return TRUE; + } + if (!(schema_str= args[1]->val_str(&m_tmp_schema))) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE, + ER_THD(thd, ER_WRONG_VALUE), + "XML", "NULL as XML Schema"); + return FALSE; + } + + m_data= new(thd->mem_root) MY_XML_VALIDATION_DATA; + m_data->xml= new(thd->mem_root) XMLSchema_xml; + + if (schema_parse(thd, schema_str, m_data)) + return TRUE; + + return FALSE; +} + + +extern "C" { +static int validation_enter(MY_XML_PARSER *st,const char *attr, size_t len) +{ + MY_XML_VALIDATION_DATA *data= (MY_XML_VALIDATION_DATA *) st->user_data; + int result= MY_XML_OK; + + if (st->current_node_type == MY_XML_NODE_TAG) + { + result= data->s_stack->validate_tag(data, attr, len); + } + else if (st->current_node_type == MY_XML_NODE_ATTR) + { + result= data->s_stack->validate_attr(data, attr, len); + } + else if (st->current_node_type == MY_XML_NODE_TEXT) + { + } + + return result; +} + + +int validation_value(MY_XML_PARSER *st,const char *attr, size_t len) +{ + MY_XML_VALIDATION_DATA *data= (MY_XML_VALIDATION_DATA *) st->user_data; + + return data->s_stack->validate_value(data, attr, len); +} + + +int validation_leave(MY_XML_PARSER *st,const char *attr, size_t len) +{ + MY_XML_VALIDATION_DATA *data= (MY_XML_VALIDATION_DATA *) st->user_data; + + return data->s_stack->validate_leave(data, attr, len); +} + +} /* extern "C" */ + + +static int validate_schema(const String *xml, + MY_XML_VALIDATION_DATA *user_data) +{ + MY_XML_PARSER p; + int rc; + + /* Prepare XML parser */ + my_xml_parser_create(&p); + p.flags= MY_XML_FLAG_RELATIVE_NAMES | MY_XML_FLAG_SKIP_TEXT_NORMALIZATION; + + my_xml_set_enter_handler(&p, validation_enter); + my_xml_set_value_handler(&p, validation_value); + my_xml_set_leave_handler(&p, validation_leave); + my_xml_set_user_data(&p, (void*) user_data); + + user_data->validation_failed= 0; + user_data->schema->m_next= NULL; + user_data->s_stack= &user_data->root; + + /* Execute XML parser */ + rc= my_xml_parse(&p, xml->ptr(), xml->length()) == MY_XML_OK; + my_xml_parser_free(&p); + + return rc; +} + + +bool Item_func_xml_isvalid::val_bool() +{ + String *xml= args[0]->val_str(&m_tmp_xml); + + if ((null_value= !xml || m_data == NULL)) + return FALSE; + + return validate_schema(xml, m_data); +} diff --git a/plugin/type_xmltype/mysql-test/type_xmltype/suite.pm b/plugin/type_xmltype/mysql-test/type_xmltype/suite.pm new file mode 100644 index 0000000000000..192dac9d168a6 --- /dev/null +++ b/plugin/type_xmltype/mysql-test/type_xmltype/suite.pm @@ -0,0 +1,7 @@ +package My::Suite::Type_xmltype; + +@ISA = qw(My::Suite); + +sub is_default { 1 } + +bless { }; diff --git a/plugin/type_xmltype/mysql-test/type_xmltype/type_xmltype.result b/plugin/type_xmltype/mysql-test/type_xmltype/type_xmltype.result new file mode 100644 index 0000000000000..680870c5d0e8e --- /dev/null +++ b/plugin/type_xmltype/mysql-test/type_xmltype/type_xmltype.result @@ -0,0 +1,250 @@ +CREATE TABLE t1(id INT, x xmltype); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) DEFAULT NULL, + `x` xmltype DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t1; +CREATE TABLE t1 (a XMLTYPE(6)); +ERROR HY000: Data type 'XMLTYPE' doesn't support LENGTH attribute. +CREATE TABLE t1 (a XMLTYPE REF_SYSTEM_ID=4); +ERROR HY000: Data type 'XMLTYPE' doesn't support REF_SYSTEM_ID attribute. +CREATE TABLE t1(id INT, x xmltype CHARACTER SET utf8mb3) CHARACTER SET utf8mb4; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) DEFAULT NULL, + `x` xmltype CHARACTER SET utf8mb3 COLLATE utf8mb3_uca1400_ai_ci DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t1; +CREATE TABLE t1 (a xmltype binary) CHARACTER SET utf8mb4; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` xmltype CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t1; +CREATE TABLE t1 (a xmltype CHARACTER SET binary); +ERROR HY000: Illegal parameter data type xmltype for operation 'CHARACTER SET binary' +CREATE TABLE t1 (a xmltype collate default) CHARACTER SET utf8mb4 collate utf8mb4_bin; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` xmltype CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin +DROP TABLE t1; +CREATE TABLE t1(id INT, x xmltype(10, 2)); +ERROR HY000: Data type 'xmltype' doesn't support LENGTH attribute. +CREATE TABLE t1(id INT, x xmltype REF_SYSTEM_ID=1001); +ERROR HY000: Data type 'xmltype' doesn't support REF_SYSTEM_ID attribute. +CREATE TABLE t1(id int, x xmltype, INDEX ix(x(12))); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) DEFAULT NULL, + `x` xmltype DEFAULT NULL, + KEY `ix` (`x`(12)) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t1; +CREATE TABLE t1(id INT, x xmltype); +INSERT INTO t1 VALUES (1, ''), (2, ''), (3, ''); +SELECT * FROM t1; +id x +1 +2 +3 +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) DEFAULT NULL, + `x` xmltype DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +CREATE TABLE t2 SELECT * FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `id` int(11) DEFAULT NULL, + `x` xmltype DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SELECT * FROM t2; +id x +1 +2 +3 +DROP TABLE t2; +INSERT INTO t1 VALUES (1, ''), (2, ''), (3, ''), (4, ''); +EXPLAIN SELECT * FROM t1 GROUP BY id; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 7 Using temporary; Using filesort +CREATE TABLE t2 SELECT * FROM t1 GROUP BY id; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `id` int(11) DEFAULT NULL, + `x` xmltype DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t2; +CREATE TABLE t2 SELECT id, COALESCE(x, x) as ce FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `id` int(11) DEFAULT NULL, + `ce` xmltype DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t2; +CREATE TABLE t2 SELECT id, LEAST(x, x) as ce FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `id` int(11) DEFAULT NULL, + `ce` xmltype DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t2; +CREATE TABLE t2 SELECT id, MIN(x), MAX(x) FROM t1 GROUP BY id; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `id` int(11) DEFAULT NULL, + `MIN(x)` xmltype DEFAULT NULL, + `MAX(x)` xmltype DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t2; +CREATE TABLE t2 SELECT DISTINCT(x) FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `x` xmltype DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t2; +CREATE TABLE t2 AS SELECT x FROM t1 UNION SELECT x FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `x` xmltype DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t2; +DROP TABLE t1; +CREATE TABLE t1(id int); +ALTER TABLE t1 ADD COLUMN x xmltype; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) DEFAULT NULL, + `x` xmltype DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t1; +CREATE FUNCTION xmi(id int) RETURNS xmltype RETURN 'aaa'; +SELECT xmi(1); +xmi(1) +aaa +CREATE TABLE t1 SELECT xmi(10) as x; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` xmltype DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t1; +DROP FUNCTION xmi; +create procedure p1(in a int) +begin +declare x xmltype; +end $ +SHOW CREATE PROCEDURE p1; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +p1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`(in a int) +begin +declare x xmltype; +end latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci +DROP PROCEDURE p1; +create procedure p1(in a int) +begin +declare x xmltype; +end $ +SHOW CREATE PROCEDURE p1; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +p1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`(in a int) +begin +declare x xmltype; +end latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci +DROP PROCEDURE p1; +CREATE TABLE t1 SELECT cast('bc' as XMLTYPE) as CA; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `CA` xmltype CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t1; +CREATE TABLE t1(id INT, x xmltype); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) DEFAULT NULL, + `x` xmltype DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +INSERT INTO t1 VALUES (1, CONCAT("", REPEAT("", 16384), "")); +SELECT LENGTH(x) FROM t1; +LENGTH(x) +114695 +DROP TABLE t1; +EXPLAIN EXTENDED SELECT CAST('a' as xmltype); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select cast('a' as xmltype charset latin1) AS `CAST('a' as xmltype)` +SELECT CAST('' AS xmltype CHARACTER SET utf8mb4) as c; +c + +SELECT CAST('' AS xmltype CHARACTER SET binary) as c; +ERROR HY000: Illegal parameter data type xmltype for operation 'CHARACTER SET binary' +CREATE OR REPLACE TABLE t1 (a text); +INSERT INTO t1 VALUES (''),(''),(''); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE CAST(a AS char)='a' and CAST(a AS xmltype)='a'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where cast(`test`.`t1`.`a` as char charset latin1) = 'a' and cast(`test`.`t1`.`a` as xmltype charset latin1) = 'a' +DROP TABLE t1; +# +# MDEV-38548 Xmltype default values inconsistency is not checked. +# +CREATE TABLE t1(id INT, x xmltype); +INSERT INTO t1 VALUES (1, ""); +SELECT x + 1 from t1; +ERROR HY000: Illegal parameter data types xmltype and int for operation '+' +DROP TABLE t1; +CREATE TABLE t1 (a INT, b XMLTYPE DEFAULT (a+1)); +ERROR HY000: Cannot cast 'bigint' as 'xmltype' in assignment of `test`.`t1`.`b` +CREATE SEQUENCE s; +CREATE TABLE t ( a XMLTYPE DEFAULT NEXTVAL(s)); +ERROR HY000: Cannot cast 'bigint' as 'xmltype' in assignment of `test`.`t`.`a` +DROP SEQUENCE s; +CREATE TABLE t1 AS SELECT UpdateXML('ccc', '/a', 'fff') as x; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` xmltype CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t1; +validation tests +set @xs= ' + + + + + + + + + + + + + +'; +select XMLISVALID(@xs, ' If 123 '); +ERROR HY000: Invalid XML Schema. +select XMLISVALID(' If 123 ', @xs); +XMLISVALID(' If 123 ', @xs) +0 +# End of 11.8 tests diff --git a/plugin/type_xmltype/mysql-test/type_xmltype/type_xmltype.test b/plugin/type_xmltype/mysql-test/type_xmltype/type_xmltype.test new file mode 100644 index 0000000000000..c0769d84d70b9 --- /dev/null +++ b/plugin/type_xmltype/mysql-test/type_xmltype/type_xmltype.test @@ -0,0 +1,180 @@ +replace_result InnoDB MyISAM; + +CREATE TABLE t1(id INT, x xmltype); +SHOW CREATE TABLE t1; +DROP TABLE t1; + +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a XMLTYPE(6)); + +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1 (a XMLTYPE REF_SYSTEM_ID=4); + +CREATE TABLE t1(id INT, x xmltype CHARACTER SET utf8mb3) CHARACTER SET utf8mb4; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +# Checking that contextually typed "binary" attribute works +CREATE TABLE t1 (a xmltype binary) CHARACTER SET utf8mb4; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION +CREATE TABLE t1 (a xmltype CHARACTER SET binary); + +# Checking that contextually typed COLLATE DEFAULT works +CREATE TABLE t1 (a xmltype collate default) CHARACTER SET utf8mb4 collate utf8mb4_bin; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1(id INT, x xmltype(10, 2)); + +--error ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE TABLE t1(id INT, x xmltype REF_SYSTEM_ID=1001); + +CREATE TABLE t1(id int, x xmltype, INDEX ix(x(12))); +SHOW CREATE TABLE t1; +DROP TABLE t1; + + +CREATE TABLE t1(id INT, x xmltype); +INSERT INTO t1 VALUES (1, ''), (2, ''), (3, ''); +SELECT * FROM t1; +SHOW CREATE TABLE t1; +CREATE TABLE t2 SELECT * FROM t1; +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2; + +INSERT INTO t1 VALUES (1, ''), (2, ''), (3, ''), (4, ''); +EXPLAIN SELECT * FROM t1 GROUP BY id; +CREATE TABLE t2 SELECT * FROM t1 GROUP BY id; +SHOW CREATE TABLE t2; + +DROP TABLE t2; + +CREATE TABLE t2 SELECT id, COALESCE(x, x) as ce FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +CREATE TABLE t2 SELECT id, LEAST(x, x) as ce FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +CREATE TABLE t2 SELECT id, MIN(x), MAX(x) FROM t1 GROUP BY id; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +CREATE TABLE t2 SELECT DISTINCT(x) FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +CREATE TABLE t2 AS SELECT x FROM t1 UNION SELECT x FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +DROP TABLE t1; + +CREATE TABLE t1(id int); +ALTER TABLE t1 ADD COLUMN x xmltype; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE FUNCTION xmi(id int) RETURNS xmltype RETURN 'aaa'; + +SELECT xmi(1); +CREATE TABLE t1 SELECT xmi(10) as x; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +DROP FUNCTION xmi; + +delimiter $; +create procedure p1(in a int) + begin + declare x xmltype; + end $ +delimiter ;$ +SHOW CREATE PROCEDURE p1; +DROP PROCEDURE p1; + +delimiter $; +create procedure p1(in a int) + begin + declare x xmltype; + end $ +delimiter ;$ +SHOW CREATE PROCEDURE p1; +DROP PROCEDURE p1; + + +CREATE TABLE t1 SELECT cast('bc' as XMLTYPE) as CA; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +# check long field + +CREATE TABLE t1(id INT, x xmltype); +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (1, CONCAT("", REPEAT("", 16384), "")); +SELECT LENGTH(x) FROM t1; +DROP TABLE t1; + +EXPLAIN EXTENDED SELECT CAST('a' as xmltype); + +SELECT CAST('' AS xmltype CHARACTER SET utf8mb4) as c; + +--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION +SELECT CAST('' AS xmltype CHARACTER SET binary) as c; + +CREATE OR REPLACE TABLE t1 (a text); +INSERT INTO t1 VALUES (''),(''),(''); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE CAST(a AS char)='a' and CAST(a AS xmltype)='a'; +DROP TABLE t1; + +--echo # +--echo # MDEV-38548 Xmltype default values inconsistency is not checked. +--echo # + +CREATE TABLE t1(id INT, x xmltype); +INSERT INTO t1 VALUES (1, ""); +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT x + 1 from t1; +DROP TABLE t1; + +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +CREATE TABLE t1 (a INT, b XMLTYPE DEFAULT (a+1)); + +CREATE SEQUENCE s; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +CREATE TABLE t ( a XMLTYPE DEFAULT NEXTVAL(s)); +DROP SEQUENCE s; + +CREATE TABLE t1 AS SELECT UpdateXML('ccc', '/a', 'fff') as x; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +--echo validation tests +set @xs= ' + + + + + + + + + + + + + +'; + +--error ER_UNKNOWN_ERROR +select XMLISVALID(@xs, ' If 123 '); +select XMLISVALID(' If 123 ', @xs); + +--echo # End of 11.8 tests + diff --git a/plugin/type_xmltype/mysql-test/type_xmltype/type_xmltype_validation.result b/plugin/type_xmltype/mysql-test/type_xmltype/type_xmltype_validation.result new file mode 100644 index 0000000000000..5e7d2578046b5 --- /dev/null +++ b/plugin/type_xmltype/mysql-test/type_xmltype/type_xmltype_validation.result @@ -0,0 +1,1952 @@ +set @xml= ' If 123 '; +select XMLISVALID(@xml, NULL); +XMLISVALID(@xml, NULL) +NULL +Warnings: +Warning 1525 Incorrect XML value: 'NULL as XML Schema' +CREATE TABLE t1(v varchar(100)); +INSERT INTO t1 VALUES ("one"), ("two"); +SELECT XMLISVALID(@xml, v) FROM t1; +ERROR HY000: Only constant XML Schema-s are supported. +DROP TABLE t1; +set @schema= ' + + +'; +set @xml= ' If 123 '; +select XMLISVALID(@xml, @schema); +ERROR HY000: Invalid XML Schema. +set @schema= ' + + + + + + + + + + + + + +'; +set @xml= ' If 123 '; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + + JOHN + MARY + Reminder + Take care! +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 + +Check 'xs:' namespace handling + +set @schema= ' + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Incorrect XML value: 'XML Schema parse error at line 2 pos 57: ' +set @schema= ' + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Incorrect XML value: 'XML Schema parse error at line 8 pos 20: ' +set @schema= ' + + + + + + + <:element name="from" type="string"/> + + + + + + +'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Invalid XML schema, type wrong-type not defined. +set @schema= ' + + + + + + + <:element name="from" type="string"/> + + + + + + +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @schema= ' + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Invalid XML schema, type xs:string not defined. + + + +set @schema= ' + + + + + This element represents a retail product item in the inventory system. + + + + + inventory_products + + + + + + + + + + + This element represents a retail product item in the inventory system. + + + + + inventory_products + + + + + + + + + + + + +'; +set @xml= ' + + John + Doe + 30 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 + + + +set @schema= ' + + + + + + + + + + + + + + + +'; +set @xml= ' + + John + Doe + 30 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= ' + + 30 + Doe + John +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= ' + + 30 + John +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + + 30 + Doe + John +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @schema= ' + + + + + + + + + + + + + + + +'; +set @xml= ' + '; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 + + + +set @schema= ' + + + + + + + + + + + + + + +'; +set @xml= ' + + +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + + 1234-5678-9012-3456 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= ' + + 1234-5678-9012-3456 + 1234-5678-9012-3456 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + + 1234-5678-9012-3456 + 1234-5678-9012-3456 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + '; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @schema= ' + + + + + + + + + + + + + + +'; +set @xml= ' + + 1234-5678-9012-3456 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= ' '; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @schema= ' + + + + + + + + + + + + + + +'; +set @xml= ' + + 1234-5678-9012-3456 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= ' + + 1234-5678-9012-3456 + 1234-5678-9012-3456 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + + 1234-5678-9012-3456 + 1234-5678-9012-3456 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + '; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @schema= ' + + + + + + + + + + + + + + +'; +set @xml= ' + + 1234-5678-9012-3456 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= ' '; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @schema= ' + + + + + + + + + + + + + +'; +set @xml= ' + + War and peace. Volume1 + 2002 + '; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 + + + +set @schema= ' + + + + + + + + + + + + + + + + + + + + +'; +set @xml= ' + +
Equipment.
+ + High performance laptop. + Gaming Laptop + + 16GB RAM, 1TB SSD + Intel i7, RTX 4060 +
'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + + + + + + + + +'; +set @xml= ' + +
Equipment.
+ + Gaming Laptop + Intel i7, RTX 4060 +
'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= ' + +
Equipment.
+ + High performance laptop. + 16GB RAM, 1TB SSD +
'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + +'; +set @xml= ' + '; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 + + + +set @schema= ' + + + + + + + + + + + + + + + + + +'; +set @xml= ' + + + 1234-5678-9012-3456 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + + 1234-5678-9012-3456 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + + + + + + + + + +'; +set @xml= ' + + + 1234-5678-9012-3456 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Invalid XML schema, type not defined. +set @schema= ' + + + + + + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Invalid XML schema, type not defined. +set @schema= ' + + + + + + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Invalid XML schema, type xs:noType not defined. +set @schema= ' + + + + + + + + + + + + + + + +'; +set @xml= ' + + 1234-5678-9012-3456 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 + + + +set @schema= ' + + + + + + + + + + + + + + '; +set @xml= ' + + John + Doe + '; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + + + '; +select XMLISVALID(@xml, @schema); +ERROR HY000: Incorrect XML value: 'XML Schema parse error at line 12 pos 24: ' + + + +set @schema= ' + + + + + + + + + + + + + + '; +set @xml= ' + + John + Doe + + 30 + New York + '; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 + + + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; +set @xml= ' + + Jane Doe +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= ' + + Acme Corporation +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + + '; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 + + + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; +set @xml= ' + + + Alex Mercer + alex.m@example.com + + Lont st. 123 + 0123456 + + + 2026-06-01 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; +set @xml= ' + + + Alex Mercer + alex.m@example.com + Lont st. 123 + + 2026-06-01 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; +set @xml= ' + + + alex.m@example.com + + 2026-06-01 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + '; +set @xml= ' + + + Alex Mercer + alex.m@example.com + + Lont st. 123 + 0123456 + + + 2026-06-01 +'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Invalid XML schema, type badRef not defined. +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + '; +set @xml= ' + + + Alex Mercer + alex.m@example.com + + Lont st. 123 + 0123456 + + + 2026-06-01 +'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Invalid XML schema, type not defined. + + + +set @schema= ' + + + + + '; +set @xml= ' The regular order. '; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= ' + + + Alex Mercer + alex.m@example.com + 123 Maple Street, New York, NY + + 2026-06-01 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 + + + +set @schema= ' + + + + + + + + + '; +set @xml= '100 34 56 -23 1567'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + + + + + '; +set @xml= '100 34 56 23 15'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + '; +set @xml= '100 34 56 -23 1567'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Invalid XML schema, type xs:badType not defined. + + + +set @schema= ' + + + + + + + + + + + '; +set @xml= '95'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= 'absent'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + '; +set @xml= '95'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= 'absent'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 + + + +set @schema= ' + + + + + + + + + + + + '; +set @xml= '95'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= 'absent'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @schema= ' + + + + + + + + + + + + '; +set @xml= '95'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @schema= ' + + + + + + + + + + + + + + + + '; +set @xml= '95'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Incorrect XML value: 'XML Schema parse error at line 9 pos 20: ' +set @schema= ' + + + + + + + + + + + + '; +set @xml= '95'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Incorrect XML value: 'XML Schema parse error at line 8 pos 21: ' +set @schema= ' + + + + + + + + + + + + '; +set @xml= '95'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Invalid XML schema, type not defined. + + + +set @schema= ' + + + + + + + + + + + + +'; +set @xml= '29.99'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= '29.99'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @schema= ' + + + + + + + + + + + +'; +set @xml= '85'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= '"fast"'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= '"fast"'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 + +Check basic types. + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + + 1001 + In process + 100.50 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + + 1001 + + In process + + 100.50 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + + 1001 + In process + XXX.50 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + + 1001 + In process + 2500.50 +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +ERROR HY000: Incorrect XML value: 'XML Schema parse error at line 19 pos 73: ' +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + + + + War and peace. Volume1 + L.Tolstoy + 1966 + 750.00 + + + War and peace. Volume2 + L.Tolstoy + 1967 + 650.00 + + + War and peace. Volume3 + L.Tolstoy + 1968 + 550.00 + +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @schema= ' + + + + + + + + + + + +'; +set @xml= ' + + 1001 + 2026-XX-10 + 2026-04-10T11:31:00Z +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xml= ' + + 1001 + 2026-04-10 + 2026-04-10T11:31:00Z +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +set @xml= ' + + 1001 + 2026-4-10 + 2026-04-10T11:31:00Z +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @xs= ' + + + + + + + + + + + + + +'; +set @xml= ' + + 2026-04-12T10:11:20.33+01:00 + -2026-04-12T10:11:20+01:00 + 2026-04-12T10:11:20Z + 22026-04-12T10:11:20.33Z +'; +select XMLISVALID(@xml, @xs); +XMLISVALID(@xml, @xs) +1 +set @xs= ' + + + + + + + + + + + + + +'; +set @xml= ' + + 2026-04-12 + -2026-04-12+01:00 + 2026-04-12Z + 22026-04-12Z +'; +select XMLISVALID(@xml, @xs); +XMLISVALID(@xml, @xs) +1 +set @xs= ' + + + + + + + + + + + + + +'; +set @xml= ' + + 10:11:20.33+01:00 + 10:11:20+01:00 + 10:11:20Z + 10:11:20.33Z +'; +select XMLISVALID(@xml, @xs); +XMLISVALID(@xml, @xs) +1 +set @xs= ' + + + + + + + + + + + + + +'; +set @xml= ' + + 2026+01:00 + -2026+01:00 + 2026 + 2226Z +'; +select XMLISVALID(@xml, @xs); +XMLISVALID(@xml, @xs) +1 +set @xs= ' + + + + + + + + + + + + + +'; +set @xml= ' + + --01--+01:00 + --12--+01:00 + --02-- + --03--Z +'; +select XMLISVALID(@xml, @xs); +XMLISVALID(@xml, @xs) +1 +set @xs= ' + + + + + + + + + + + + + +'; +set @xml= ' + + ---01+01:00 + ---12+01:00 + ---02 + ---03Z +'; +select XMLISVALID(@xml, @xs); +XMLISVALID(@xml, @xs) +1 +set @xs= ' + + + + + + + + + + + + + +'; +set @xml= ' + + --01-01+01:00 + --12-12+01:00 + --11-02 + --01-03Z +'; +select XMLISVALID(@xml, @xs); +XMLISVALID(@xml, @xs) +1 +set @xs= ' + + + + + + + + + + + + + +'; +set @xml= ' + + + SGVsbG8= + SGVsbG8= + VGhpcyBpcyBhbiBleG + FtcGxlIG9mIGJhc2U2 + NCBlbmNvZGVkIHRleHQu + +'; +select XMLISVALID(@xml, @xs); +XMLISVALID(@xml, @xs) +1 +set @xs= ' + + + + + + + + + + + + + +'; +set @xml= ' + + + 48656c6c6f + 48656C6C6F + 48656c6c6f + 48656c6c6f + 48656c6c6f + +'; +select XMLISVALID(@xml, @xs); +XMLISVALID(@xml, @xs) +1 +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +0 +set @schema= ' + + + + + + + + + + + + + + + + + + + +'; +set @xml= ' + + true + 100 + 100 + 100 + 100 + 100 + 100 + 100e3 + 100e4 + JOHN +'; +select XMLISVALID(@xml, @schema); +XMLISVALID(@xml, @schema) +1 +# End of 11.8 tests diff --git a/plugin/type_xmltype/mysql-test/type_xmltype/type_xmltype_validation.test b/plugin/type_xmltype/mysql-test/type_xmltype/type_xmltype_validation.test new file mode 100644 index 0000000000000..8d977582e0886 --- /dev/null +++ b/plugin/type_xmltype/mysql-test/type_xmltype/type_xmltype_validation.test @@ -0,0 +1,2088 @@ +replace_result InnoDB MyISAM; + +set @xml= ' If 123 '; + +select XMLISVALID(@xml, NULL); + +CREATE TABLE t1(v varchar(100)); +INSERT INTO t1 VALUES ("one"), ("two"); + +--error ER_UNKNOWN_ERROR +SELECT XMLISVALID(@xml, v) FROM t1; +DROP TABLE t1; + +set @schema= ' + + +'; + +set @xml= ' If 123 '; + +--error ER_UNKNOWN_ERROR +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + +'; + + +set @xml= ' If 123 '; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + JOHN + MARY + Reminder + Take care! +'; + +select XMLISVALID(@xml, @schema); + +--echo +--echo trigger with XMLISVALID. +--echo + +CREATE TABLE t1 (a XMLType); + +--delimiter $$ + +CREATE OR REPLACE TRIGGER before_insert_t1 +BEFORE INSERT ON t1 +FOR EACH ROW +BEGIN +  DECLARE xmlschema TEXT DEFAULT +' + +   +     +       +         +         +         +         +       +     +   +'; + +  IF (XMLISVALID(NEW.a, xmlschema) = 0) THEN +      SIGNAL SQLSTATE '45000' +      SET MESSAGE_TEXT = 'XML Schema validation failed'; +  END IF; +END +$$ + +--delimiter ; + +--error ER_SIGNAL_EXCEPTION +INSERT INTO t1 VALUES (' If 123 '); +INSERT INTO t1 VALUES( + ' + +   JOHN +   MARY +   Reminder +   Take care! + '); + +DROP TABLE t1; + +--echo +--echo Check 'xs:' namespace handling +--echo +set @schema= ' + + + + + + + + + + + + + +'; +--error ER_WRONG_VALUE +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + +'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + +'; +--error ER_WRONG_VALUE +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + <:element name="from" type="string"/> + + + + + + +'; +--error ER_UNKNOWN_ERROR +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + <:element name="from" type="string"/> + + + + + + +'; +select XMLISVALID(@xml, @schema); +set @schema= ' + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + +'; +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + +'; +--error ER_UNKNOWN_ERROR +select XMLISVALID(@xml, @schema); + +--echo +--echo +--echo +set @schema= ' + + + + + This element represents a retail product item in the inventory system. + + + + + inventory_products + + + + + + + + + + + This element represents a retail product item in the inventory system. + + + + + inventory_products + + + + + + + + + + + + +'; + +set @xml= ' + + John + Doe + 30 +'; + +select XMLISVALID(@xml, @schema); + +--echo +--echo +--echo + +set @schema= ' + + + + + + + + + + + + + + + +'; + +set @xml= ' + + John + Doe + 30 +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 30 + Doe + John +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 30 + John +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 30 + Doe + John +'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + +'; + +set @xml= ' + '; + +select XMLISVALID(@xml, @schema); + + + +--echo +--echo +--echo + +set @schema= ' + + + + + + + + + + + + + + +'; + +set @xml= ' + + +'; + +select XMLISVALID(@xml, @schema); +set @xml= ' + + 1234-5678-9012-3456 +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 1234-5678-9012-3456 + 1234-5678-9012-3456 +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 1234-5678-9012-3456 + 1234-5678-9012-3456 +'; + +select XMLISVALID(@xml, @schema); +set @xml= ' + '; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + +'; + +set @xml= ' + + 1234-5678-9012-3456 +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' '; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + +'; + +set @xml= ' + + 1234-5678-9012-3456 +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 1234-5678-9012-3456 + 1234-5678-9012-3456 +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 1234-5678-9012-3456 + 1234-5678-9012-3456 +'; + +select XMLISVALID(@xml, @schema); +set @xml= ' + '; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + +'; + +set @xml= ' + + 1234-5678-9012-3456 +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' '; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + +'; + +set @xml= ' + + War and peace. Volume1 + 2002 + '; + +select XMLISVALID(@xml, @schema); + +--echo +--echo +--echo + +set @schema= ' + + + + + + + + + + + + + + + + + + + + +'; + +set @xml= ' + +
Equipment.
+ + High performance laptop. + Gaming Laptop + + 16GB RAM, 1TB SSD + Intel i7, RTX 4060 +
'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + + + +'; + +set @xml= ' + +
Equipment.
+ + Gaming Laptop + Intel i7, RTX 4060 +
'; + +select XMLISVALID(@xml, @schema); + + +set @xml= ' + +
Equipment.
+ + High performance laptop. + 16GB RAM, 1TB SSD +
'; + +select XMLISVALID(@xml, @schema); + + +set @schema= ' + + + + + + + +'; + +set @xml= ' + '; + +select XMLISVALID(@xml, @schema); + +--echo +--echo +--echo + +set @schema= ' + + + + + + + + + + + + + + + + + +'; + +set @xml= ' + + + 1234-5678-9012-3456 +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 1234-5678-9012-3456 +'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + +'; + +set @xml= ' + + + 1234-5678-9012-3456 +'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + +'; + +--error ER_UNKNOWN_ERROR +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + +'; + +--error ER_UNKNOWN_ERROR +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + +'; + +--error ER_UNKNOWN_ERROR +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + +'; + +set @xml= ' + + 1234-5678-9012-3456 +'; + +select XMLISVALID(@xml, @schema); + + +--echo +--echo +--echo + +set @schema= ' + + + + + + + + + + + + + + '; + +set @xml= ' + + John + Doe + '; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + '; + +--error ER_WRONG_VALUE +select XMLISVALID(@xml, @schema); + + +--echo +--echo +--echo + +set @schema= ' + + + + + + + + + + + + + + '; + +set @xml= ' + + John + Doe + + 30 + New York + '; + +select XMLISVALID(@xml, @schema); + + +--echo +--echo +--echo + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + +set @xml= ' + + Jane Doe +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + Acme Corporation +'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + +select XMLISVALID(@xml, @schema); + + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + +select XMLISVALID(@xml, @schema); + +--echo +--echo +--echo + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + +set @xml= ' + + + Alex Mercer + alex.m@example.com + + Lont st. 123 + 0123456 + + + 2026-06-01 +'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + +set @xml= ' + + + Alex Mercer + alex.m@example.com + Lont st. 123 + + 2026-06-01 +'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + +set @xml= ' + + + alex.m@example.com + + 2026-06-01 +'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + '; + +set @xml= ' + + + Alex Mercer + alex.m@example.com + + Lont st. 123 + 0123456 + + + 2026-06-01 +'; + +--error ER_UNKNOWN_ERROR +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + '; + +set @xml= ' + + + Alex Mercer + alex.m@example.com + + Lont st. 123 + 0123456 + + + 2026-06-01 +'; + +--error ER_UNKNOWN_ERROR +select XMLISVALID(@xml, @schema); + + +--echo +--echo +--echo + +set @schema= ' + + + + + '; + +set @xml= ' The regular order. '; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + + Alex Mercer + alex.m@example.com + 123 Maple Street, New York, NY + + 2026-06-01 +'; + +select XMLISVALID(@xml, @schema); + + +--echo +--echo +--echo +set @schema= ' + + + + + + + + + '; + +set @xml= '100 34 56 -23 1567'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + '; + +set @xml= '100 34 56 23 15'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + '; + +set @xml= '100 34 56 -23 1567'; + +--error ER_UNKNOWN_ERROR +select XMLISVALID(@xml, @schema); + +--echo +--echo +--echo +set @schema= ' + + + + + + + + + + + '; + +set @xml= '95'; +select XMLISVALID(@xml, @schema); + +set @xml= 'absent'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + + + '; + +set @xml= '95'; +select XMLISVALID(@xml, @schema); + +set @xml= 'absent'; + +select XMLISVALID(@xml, @schema); + +--echo +--echo +--echo + +set @schema= ' + + + + + + + + + + + + '; + +set @xml= '95'; +select XMLISVALID(@xml, @schema); + +set @xml= 'absent'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + '; + +set @xml= '95'; +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + '; + +set @xml= '95'; +--error ER_WRONG_VALUE +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + '; + +set @xml= '95'; +--error ER_WRONG_VALUE +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + '; + +set @xml= '95'; +--error ER_UNKNOWN_ERROR +select XMLISVALID(@xml, @schema); + + +--echo +--echo +--echo + +set @schema= ' + + + + + + + + + + + + +'; + +set @xml= '29.99'; + +select XMLISVALID(@xml, @schema); + +set @xml= '29.99'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + +'; + +set @xml= '85'; + +select XMLISVALID(@xml, @schema); + +set @xml= '"fast"'; + +select XMLISVALID(@xml, @schema); + +set @xml= '"fast"'; + +select XMLISVALID(@xml, @schema); + +--echo +--echo Check basic types. +--echo + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 1001 + In process + 100.50 +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 1001 + + In process + + 100.50 +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 1001 + In process + XXX.50 +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 1001 + In process + 2500.50 +'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + + + +'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + +'; + +--error ER_WRONG_VALUE +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + + + + + + + + + + + + +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + + + War and peace. Volume1 + L.Tolstoy + 1966 + 750.00 + + + War and peace. Volume2 + L.Tolstoy + 1967 + 650.00 + + + War and peace. Volume3 + L.Tolstoy + 1968 + 550.00 + +'; + +select XMLISVALID(@xml, @schema); + +set @schema= ' + + + + + + + + + + + +'; + +set @xml= ' + + 1001 + 2026-XX-10 + 2026-04-10T11:31:00Z +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 1001 + 2026-04-10 + 2026-04-10T11:31:00Z +'; + +select XMLISVALID(@xml, @schema); + +set @xml= ' + + 1001 + 2026-4-10 + 2026-04-10T11:31:00Z +'; + +select XMLISVALID(@xml, @schema); + +set @xs= ' + + + + + + + + + + + + + +'; + +set @xml= ' + + 2026-04-12T10:11:20.33+01:00 + -2026-04-12T10:11:20+01:00 + 2026-04-12T10:11:20Z + 22026-04-12T10:11:20.33Z +'; + +select XMLISVALID(@xml, @xs); + +set @xs= ' + + + + + + + + + + + + + +'; + +set @xml= ' + + 2026-04-12 + -2026-04-12+01:00 + 2026-04-12Z + 22026-04-12Z +'; + +select XMLISVALID(@xml, @xs); + +set @xs= ' + + + + + + + + + + + + + +'; + +set @xml= ' + + 10:11:20.33+01:00 + 10:11:20+01:00 + 10:11:20Z + 10:11:20.33Z +'; + +select XMLISVALID(@xml, @xs); + +set @xs= ' + + + + + + + + + + + + + +'; + +set @xml= ' + + 2026+01:00 + -2026+01:00 + 2026 + 2226Z +'; + +select XMLISVALID(@xml, @xs); + +set @xs= ' + + + + + + + + + + + + + +'; + +set @xml= ' + + --01--+01:00 + --12--+01:00 + --02-- + --03--Z +'; + +select XMLISVALID(@xml, @xs); + +set @xs= ' + + + + + + + + + + + + + +'; + +set @xml= ' + + ---01+01:00 + ---12+01:00 + ---02 + ---03Z +'; + +select XMLISVALID(@xml, @xs); + +set @xs= ' + + + + + + + + + + + + + +'; + +set @xml= ' + + --01-01+01:00 + --12-12+01:00 + --11-02 + --01-03Z +'; + +select XMLISVALID(@xml, @xs); + +set @xs= ' + + + + + + + + + + + + + +'; + +set @xml= ' + + + SGVsbG8= + SGVsbG8= + VGhpcyBpcyBhbiBleG + FtcGxlIG9mIGJhc2U2 + NCBlbmNvZGVkIHRleHQu + +'; + +select XMLISVALID(@xml, @xs); + +set @xs= ' + + + + + + + + + + + + + +'; + +set @xml= ' + + + 48656c6c6f + 48656C6C6F + 48656c6c6f + 48656c6c6f + 48656c6c6f + +'; + +select XMLISVALID(@xml, @xs); + +select XMLISVALID(@xml, @schema); +set @schema= ' + + + + + + + + + + + + + + + + + + + +'; + +set @xml= ' + + true + 100 + 100 + 100 + 100 + 100 + 100 + 100e3 + 100e4 + JOHN +'; + +select XMLISVALID(@xml, @schema); + + +--echo # End of 11.8 tests + diff --git a/plugin/type_xmltype/plugin.cc b/plugin/type_xmltype/plugin.cc new file mode 100644 index 0000000000000..65d3b1e58e397 --- /dev/null +++ b/plugin/type_xmltype/plugin.cc @@ -0,0 +1,48 @@ +/* Copyright (c) 2025 MariaDB Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#define MYSQL_SERVER +#include "mariadb.h" +#include "sql_class.h" +#include "sql_type_xmltype.h" +#include +#include + + +static struct st_mariadb_data_type plugin_descriptor_xmltype= +{ + MariaDB_DATA_TYPE_INTERFACE_VERSION, + &type_handler_xmltype +}; + + +maria_declare_plugin(type_xmltype) +{ + MariaDB_DATA_TYPE_PLUGIN, // the plugin type (see include/mysql/plugin.h) + &plugin_descriptor_xmltype, // pointer to type-specific plugin descriptor + "xmltype", // plugin name + "MariaDB Corporation", // plugin author + "Data type XMLTYPE", // the plugin description + PLUGIN_LICENSE_GPL, // the plugin license (see include/mysql/plugin.h) + 0, // Pointer to plugin initialization function + 0, // Pointer to plugin deinitialization function + 0x0100, // Numeric version 0xAABB means AA.BB version + NULL, // Status variables + NULL, // System variables + "1.0", // String version representation + MariaDB_PLUGIN_MATURITY_STABLE// Maturity(see include/mysql/plugin.h)*/ +} +maria_declare_plugin_end; + diff --git a/plugin/type_xmltype/sql_type_xmltype.cc b/plugin/type_xmltype/sql_type_xmltype.cc new file mode 100644 index 0000000000000..89c94315444af --- /dev/null +++ b/plugin/type_xmltype/sql_type_xmltype.cc @@ -0,0 +1,326 @@ +/* + Copyright (c) 2025, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ + +#define MYSQL_SERVER +#include "mariadb.h" +#include "my_xml.h" +#include "sql_class.h" +#include "sql_lex.h" +#include "sql_type_xmltype.h" + + +Type_handler_xmltype type_handler_xmltype; +Type_collection_xmltype type_collection_xmltype; + +const Type_collection *Type_handler_xmltype::type_collection() const +{ + return &type_collection_xmltype; +} + +const Type_handler *Type_collection_xmltype::aggregate_for_comparison( + const Type_handler *a, const Type_handler *b) const +{ + if (a->type_collection() == this) + swap_variables(const Type_handler *, a, b); + if (a == &type_handler_xmltype || a == &type_handler_hex_hybrid || + a == &type_handler_tiny_blob || a == &type_handler_blob || + a == &type_handler_medium_blob || a == &type_handler_long_blob || + a == &type_handler_varchar || a == &type_handler_string || + a == &type_handler_null) + return b; + return NULL; +} + +const Type_handler *Type_collection_xmltype::aggregate_for_result( + const Type_handler *a, const Type_handler *b) const +{ + return aggregate_for_comparison(a,b); +} + +const Type_handler *Type_collection_xmltype::aggregate_for_min_max( + const Type_handler *a, const Type_handler *b) const +{ + return aggregate_for_comparison(a,b); +} + +const Type_handler *Type_collection_xmltype::aggregate_for_num_op( + const Type_handler *a, const Type_handler *b) const +{ + return NULL; +} + + +constexpr LEX_CSTRING Type_handler_xmltype::name_on_client; + +const Type_handler *Type_handler_xmltype::type_handler_for_comparison() const +{ + return &type_handler_xmltype; +} + + +Field *Type_handler_xmltype::make_conversion_table_field( + MEM_ROOT *root, TABLE *table, uint metadata, const Field *target) const +{ + /* Copied from Type_handler_blob_common. */ + uint pack_length= metadata & 0x00ff; + if (pack_length != 4) + return NULL; // Broken binary log? + + return new(root) + Field_xmltype(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str, + table->s, target->charset()); +} + + +Item *Type_handler_xmltype::create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const +{ + CHARSET_INFO *real_cs= attr.charset() ? + attr.charset() : thd->variables.collation_connection; + + if (real_cs == &my_charset_bin) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + name().ptr(), "CHARACTER SET binary"); + return NULL; + } + + return new (thd->mem_root) Item_xmltype_typecast(thd, item, real_cs); +} + + +bool Type_handler_xmltype:: Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, Column_definition *def, column_definition_type_t type, + const Column_derived_attributes *derived_attr) const +{ + if (Type_handler_long_blob:: + Column_definition_prepare_stage1(thd, mem_root, def, type, derived_attr)) + return true; + if (def->charset == &my_charset_bin) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + name().ptr(), "CHARACTER SET binary"); + return true; + } + return false; +} + + +Field *Type_handler_xmltype::make_table_field(MEM_ROOT *root, + const LEX_CSTRING *name, const Record_addr &addr, + const Type_all_attributes &attr, TABLE_SHARE *share) const +{ + return new (root) Field_xmltype(addr.ptr(), addr.null_ptr(), addr.null_bit(), + Field::NONE, name, share, attr.collation); +} + + +Field *Type_handler_xmltype::make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *root, const LEX_CSTRING *name, const Record_addr &rec, + const Bit_addr &bit, const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (root) Field_xmltype(rec.ptr(), rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, share, attr->charset); +} + + +Item * +Type_handler_xmltype::make_constructor_item(THD *thd, List *args) const +{ + if (!args || args->elements != 1) + return NULL; + Item_args tmp(thd, *args); + return new (thd->mem_root) + Item_xmltype_typecast(thd, tmp.arguments()[0], + thd->variables.collation_connection); +} + + +bool Type_handler_xmltype:: + Item_hybrid_func_fix_attributes(THD *thd, const LEX_CSTRING &func_name, + Type_handler_hybrid_field_type *handler, Type_all_attributes *func, + Item **items, uint nitems) const +{ + if (func->aggregate_attributes_string(func_name, items, nitems)) + return true; + + handler->set_handler(&type_handler_xmltype); + return false; +} + + +const Type_handler *Type_handler_xmltype:: + type_handler_for_tmp_table(const Item *item) const +{ + return &type_handler_xmltype; +} + + + +/*****************************************************************/ +void Field_xmltype::sql_type(String &res) const +{ + res.set_ascii(STRING_WITH_LEN("xmltype")); +} + + +int Field_xmltype::report_wrong_value(const ErrConv &val) +{ + get_thd()->push_warning_truncated_value_for_field( + Sql_condition::WARN_LEVEL_WARN, "xmltype", val.ptr(), + table->s->db.str, table->s->table_name.str, field_name.str); + reset(); + return 1; +} + + +static int check_parse_xml(const char *xml, size_t length, CHARSET_INFO *cs) +{ + MY_XML_PARSER p; + int result; + + /* Prepare XML parser */ + my_xml_parser_create(&p); + p.flags= MY_XML_FLAG_RELATIVE_NAMES | + MY_XML_FLAG_SKIP_TEXT_NORMALIZATION | + MY_XML_FLAG_ASSERT_WELL_FORMED; + + result= my_xml_parse(&p, xml, length); + my_xml_parser_free(&p); + + return result; +} + + +int Field_xmltype::store(const char *from, size_t length, CHARSET_INFO *cs) +{ + if (length < 4 || + check_parse_xml(from, length, cs) != MY_XML_OK) + goto err; + + + return Field_blob::store(from, length, cs); + +err: + my_error(ER_WRONG_VALUE, MYF(0), + "XMLTYPE", ErrConvString(from, length, cs).ptr()); + + if (maybe_null()) + set_null(); + else + Field_blob::store(STRING_WITH_LEN(""), cs); + return -1; +} + + +/* + We allow any string input into the XMLTYPE, + as it can fit into LONG BLOB without any loss. + But in any case values themselves can be invalid XML-s. + + TODO: when the replication start sending UDT informatio, + we should only return CONV_TYPE_PRECISE for the XMLTYPE. +*/ +enum_conv_type +Field_xmltype::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + const Type_handler *th= source.type_handler(); + if (th == &type_handler_tiny_blob || + th == &type_handler_medium_blob || + th == &type_handler_long_blob || + th == &type_handler_blob || + th == &type_handler_blob_compressed || + th == &type_handler_string || + th == &type_handler_var_string || + th == &type_handler_varchar || + th == &type_handler_varchar_compressed) + { + return CONV_TYPE_PRECISE; + } + + return CONV_TYPE_IMPOSSIBLE; +} + + +class Item_xmltype_typecast_func_handler: public Item_handled_func::Handler_str +{ +public: + const Type_handler * + return_type_handler(const Item_handled_func *item) const override + { return &type_handler_xmltype; } + + const Type_handler * + type_handler_for_create_select(const Item_handled_func *item) const override + { return &type_handler_xmltype; } + + bool fix_length_and_dec(Item_handled_func *item) const override + { + return false; + } + String *val_str(Item_handled_func *item, String *to) const override + { + DBUG_ASSERT(dynamic_cast(item)); + return static_cast(item)->val_str_generic(to); + } +}; + + +static Item_xmltype_typecast_func_handler item_xmltype_typecast_func_handler; + +bool Item_xmltype_typecast::fix_length_and_dec(THD *thd) +{ + Item_char_typecast::fix_length_and_dec_str(); + set_func_handler(&item_xmltype_typecast_func_handler); + + if (cast_charset()->mbminlen > 1) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "CAST(AS XMLTYPE CHARACTER SET ucs2/utf16/utf32)"); + return true; + } + + return false; +} + + +String *Item_xmltype_typecast::val_str(String *to) +{ + String *res= Item_char_typecast::val_str(to); + if (!res) + return NULL; + + if (check_parse_xml(res->ptr(), res->length(), res->charset()) != MY_XML_OK) + { + null_value= TRUE; + return NULL; + } + + return res; +} + + +void Item_xmltype_typecast::print(String *str, enum_query_type query_type) +{ + str->append(STRING_WITH_LEN("cast(")); + args[0]->print(str, query_type); + str->append(STRING_WITH_LEN(" as xmltype")); + print_charset(str); + str->append(')'); +} + diff --git a/plugin/type_xmltype/sql_type_xmltype.h b/plugin/type_xmltype/sql_type_xmltype.h new file mode 100644 index 0000000000000..ed1565d2207e1 --- /dev/null +++ b/plugin/type_xmltype/sql_type_xmltype.h @@ -0,0 +1,150 @@ +#ifndef SQL_TYPE_XMLTYPE_INCLUDED +#define SQL_TYPE_XMLTYPE_INCLUDED +/* + Copyright (c) 2025 MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ + +#include "sql_type.h" + +class Type_handler_xmltype: public Type_handler_long_blob +{ +public: + static constexpr LEX_CSTRING name_on_client{STRING_WITH_LEN("xml")}; + + virtual ~Type_handler_xmltype() {} + const Type_collection *type_collection() const override; + uint get_column_attributes() const override { return ATTR_CHARSET; } + const Type_handler *type_handler_for_comparison() const override; + virtual Item *create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const override; + Field *make_conversion_table_field(MEM_ROOT *root, + TABLE *table, uint metadata, + const Field *target) const override; + Log_event_data_type user_var_log_event_data_type(uint charset_nr) + const override + { + return Log_event_data_type(name().lex_cstring(), result_type(), + charset_nr, false); + } + + bool Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *def, + column_definition_type_t type, + const Column_derived_attributes + *derived_attr) const override; + + Field *make_table_field(MEM_ROOT *root, const LEX_CSTRING *name, + const Record_addr &addr, const Type_all_attributes &attr, + TABLE_SHARE *share) const override; + + Field *make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, const Record_addr &addr, + const Bit_addr &bit, const Column_definition_attributes *attr, + uint32 flags) const override; + + Item *make_constructor_item(THD *thd, List *args) const override; + + bool Item_hybrid_func_fix_attributes(THD *thd, const LEX_CSTRING &func_name, + Type_handler_hybrid_field_type *handler, Type_all_attributes *func, + Item **items, uint nitems) const override; + + const Type_handler *type_handler_for_tmp_table(const Item *item) const + override; + + bool Item_append_extended_type_info(Send_field_extended_metadata *to, + const Item *item) const override + { + + return to->set_data_type_name(name_on_client); + } + + bool can_return_int() const override { return false; } + bool can_return_decimal() const override { return false; } + bool can_return_real() const override { return false; } + bool can_return_date() const override { return false; } + bool can_return_time() const override { return false; } + +}; + +extern Type_handler_xmltype type_handler_xmltype; + +class Type_collection_xmltype: public Type_collection +{ +public: + const Type_handler *aggregate_for_result( + const Type_handler *a, const Type_handler *b) const override; + const Type_handler *aggregate_for_comparison( + const Type_handler *a, const Type_handler *b) const override; + const Type_handler *aggregate_for_min_max( + const Type_handler *a, const Type_handler *b) const override; + const Type_handler *aggregate_for_num_op( + const Type_handler *a, const Type_handler *b) const override; +}; + +#include "field.h" + +class Field_xmltype :public Field_blob +{ + int report_wrong_value(const ErrConv &val); +public: + Field_xmltype(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, + TABLE_SHARE *share, const DTCollation &collation) + :Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, + field_name_arg, share, 4 /*blob_pack_length*/, collation) + {} + const Type_handler *type_handler() const override + { return &type_handler_xmltype; } + + void make_send_field(Send_field *to) override + { + Field_longstr::make_send_field(to); + to->set_data_type_name(Type_handler_xmltype::name_on_client); + } + + void sql_type(String &str) const override; + uint size_of() const override { return sizeof(*this); } + int store(const char *to, size_t length, CHARSET_INFO *charset) override; + using Field_str::store; + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const override; +}; + + +class Item_xmltype_typecast :public Item_char_typecast +{ +public: + Item_xmltype_typecast(THD *thd, Item *a, CHARSET_INFO *cs_arg): + Item_char_typecast(thd, a, -1, cs_arg) {} + + const Type_handler *type_handler() const override + { return &type_handler_xmltype; } + + LEX_CSTRING func_name_cstring() const override + { + static LEX_CSTRING name= {STRING_WITH_LEN("cast_as_xmltype")}; + return name; + } + bool fix_length_and_dec(THD *thd) override; + String *val_str(String *to) override; + Item *do_get_copy(THD *thd) const override + { return get_item_copy(thd, this); } + + void print(String *str, enum_query_type query_type) override; +}; + +#endif // SQL_TYPE_XMLTYPE_INCLUDED diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index b9795d3c71307..6a19900609cd5 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -3135,8 +3135,7 @@ bool Item_char_typecast::eq(const Item *item, const Eq_config &config) const { if (this == item) return 1; - if (item->type() != FUNC_ITEM || - functype() != ((Item_func*)item)->functype()) + if (typeid(this) != typeid(item)) return 0; Item_char_typecast *cast= (Item_char_typecast*)item; @@ -3168,19 +3167,8 @@ void Item_func::print_cast_temporal(String *str, enum_query_type query_type) } -void Item_char_typecast::print(String *str, enum_query_type query_type) +void Item_char_typecast::print_charset(String *str) { - str->append(STRING_WITH_LEN("cast(")); - args[0]->print(str, query_type); - str->append(STRING_WITH_LEN(" as char")); - if (cast_length != ~0U) - { - char buf[20]; - size_t length= (size_t) (longlong10_to_str(cast_length, buf, 10) - buf); - str->append('('); - str->append(buf, length); - str->append(')'); - } if (cast_cs) { str->append(STRING_WITH_LEN(" charset ")); @@ -3193,6 +3181,23 @@ void Item_char_typecast::print(String *str, enum_query_type query_type) Charset(cast_cs).can_have_collate_clause()) str->append(STRING_WITH_LEN(" binary")); } +} + + +void Item_char_typecast::print(String *str, enum_query_type query_type) +{ + str->append(STRING_WITH_LEN("cast(")); + args[0]->print(str, query_type); + str->append(STRING_WITH_LEN(" as char")); + if (cast_length != ~0U) + { + char buf[20]; + size_t length= (size_t) (longlong10_to_str(cast_length, buf, 10) - buf); + str->append('('); + str->append(buf, length); + str->append(')'); + } + print_charset(str); str->append(')'); } diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 762288fade8cc..f7a9e4d6635bf 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -1412,6 +1412,7 @@ class Item_char_typecast :public Item_handled_func { return args[0]->type_handler()->Item_char_typecast_fix_length_and_dec(this); } + void print_charset(String *str); void print(String *str, enum_query_type query_type) override; bool need_parentheses_in_default() override { return true; } Item *do_get_copy(THD *thd) const override diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 47705c9ac6728..d55c5f755e201 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -44,8 +44,8 @@ - substring-before() 3. add lacking axis: - following-sibling - - following, - - preceding-sibling + - following, + - preceding-sibling - preceding */ @@ -287,7 +287,7 @@ class Item_nodeset_func_childbyname: public Item_nodeset_func_axisbyname { public: Item_nodeset_func_childbyname(THD *thd, Item *a, const char *n_arg, uint l_arg, - String *pxml): + String *pxml): Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {} LEX_CSTRING func_name_cstring() const override { @@ -546,7 +546,7 @@ class Item_func_xpath_sum :public Item_real_func MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) tmp_native_value.end(); uint numnodes= pxml->length() / sizeof(MY_XML_NODE); MY_XML_NODE *nodebeg= (MY_XML_NODE*) pxml->ptr(); - + for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++) { MY_XML_NODE *self= &nodebeg[flt->num]; @@ -1142,7 +1142,7 @@ static char simpletok[128]= */ 0,1,0,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0, 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0 }; @@ -1165,7 +1165,7 @@ static struct my_xpath_keyword_names_st my_keyword_names[] = {MY_XPATH_LEX_DIV , "div" , 3, 0 }, {MY_XPATH_LEX_MOD , "mod" , 3, 0 }, {0,NULL,0,0} -}; +}; static struct my_xpath_keyword_names_st my_axis_names[]= @@ -1493,7 +1493,7 @@ my_xpath_lex_scan(MY_XPATH *xpath, int ch, ctype, length; for ( ; beg < end && *beg == ' ' ; beg++) ; // skip leading spaces lex->beg= beg; - + if (beg >= end) { lex->end= beg; @@ -1508,7 +1508,7 @@ my_xpath_lex_scan(MY_XPATH *xpath, ((ctype & (_MY_L | _MY_U)) || *beg == '_')) { // scan until the end of the identifier - for (beg+= length; + for (beg+= length; (length= xpath->cs->ctype(&ctype, (const uchar*) beg, (const uchar*) end)) > 0 && @@ -1548,7 +1548,7 @@ my_xpath_lex_scan(MY_XPATH *xpath, ch= *beg++; - + if (ch > 0 && ch < 128 && simpletok[ch]) { // a token consisting of one character found @@ -1574,7 +1574,7 @@ my_xpath_lex_scan(MY_XPATH *xpath, lex->end= beg+1; lex->term= MY_XPATH_LEX_STRING; return; - } + } else { // unexpected end-of-line, without closing quot sign @@ -1687,7 +1687,7 @@ static int my_xpath_parse_LocationPath(MY_XPATH *xpath) if (!xpath->context) xpath->context= xpath->rootelement; - int rc= my_xpath_parse_RelativeLocationPath(xpath) || + int rc= my_xpath_parse_RelativeLocationPath(xpath) || my_xpath_parse_AbsoluteLocationPath(xpath); xpath->item= xpath->context; @@ -1732,7 +1732,7 @@ static int my_xpath_parse_AbsoluteLocationPath(MY_XPATH *xpath) } my_xpath_parse_RelativeLocationPath(xpath); - + return (xpath->error == 0); } @@ -1742,7 +1742,7 @@ static int my_xpath_parse_AbsoluteLocationPath(MY_XPATH *xpath) SYNOPSYS - For better performance we combine these two rules + For better performance we combine these two rules [3] RelativeLocationPath ::= Step | RelativeLocationPath '/' Step @@ -1794,12 +1794,12 @@ static int my_xpath_parse_RelativeLocationPath(MY_XPATH *xpath) 1 - success 0 - failure */ -static int +static int my_xpath_parse_AxisSpecifier_NodeTest_opt_Predicate_list(MY_XPATH *xpath) { if (!my_xpath_parse_AxisSpecifier(xpath)) return 0; - + if (!my_xpath_parse_NodeTest(xpath)) return 0; @@ -1859,7 +1859,7 @@ static int my_xpath_parse_Step(MY_XPATH *xpath) Scan Abbreviated Axis Specifier SYNOPSYS - [5] AxisSpecifier ::= AxisName '::' + [5] AxisSpecifier ::= AxisName '::' | AbbreviatedAxisSpecifier RETURN @@ -2002,7 +2002,7 @@ static int my_xpath_parse_PrimaryExpr_literal(MY_XPATH *xpath) } static int my_xpath_parse_PrimaryExpr(MY_XPATH *xpath) { - return + return my_xpath_parse_lp_Expr_rp(xpath) || my_xpath_parse_VariableReference(xpath) || my_xpath_parse_PrimaryExpr_literal(xpath) || @@ -2077,13 +2077,13 @@ static int my_xpath_parse_UnionExpr(MY_XPATH *xpath) { if (!my_xpath_parse_PathExpr(xpath)) return 0; - + while (my_xpath_parse_term(xpath, MY_XPATH_LEX_VLINE)) { Item *prev= xpath->item; if (prev->fixed_type_handler() != &type_handler_xpath_nodeset) return 0; - + if (!my_xpath_parse_PathExpr(xpath) || xpath->item->fixed_type_handler() != &type_handler_xpath_nodeset) { @@ -2152,7 +2152,7 @@ my_xpath_parse_FilterExpr_opt_slashes_RelativeLocationPath(MY_XPATH *xpath) } static int my_xpath_parse_PathExpr(MY_XPATH *xpath) { - return my_xpath_parse_LocationPath(xpath) || + return my_xpath_parse_LocationPath(xpath) || my_xpath_parse_FilterExpr_opt_slashes_RelativeLocationPath(xpath); } @@ -2261,7 +2261,7 @@ static int my_xpath_parse_AndExpr(MY_XPATH *xpath) 0 - failure */ static int my_xpath_parse_ne(MY_XPATH *xpath) -{ +{ MY_XPATH_LEX prevtok= xpath->prevtok; if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_EXCL)) return 0; @@ -2337,7 +2337,7 @@ static int my_xpath_parse_RelationalOperator(MY_XPATH *xpath) { if (my_xpath_parse_term(xpath, MY_XPATH_LEX_LESS)) { - xpath->extra= my_xpath_parse_term(xpath, MY_XPATH_LEX_EQ) ? + xpath->extra= my_xpath_parse_term(xpath, MY_XPATH_LEX_EQ) ? MY_XPATH_LEX_LE : MY_XPATH_LEX_LESS; return 1; } @@ -2429,10 +2429,10 @@ static int my_xpath_parse_AdditiveExpr(MY_XPATH *xpath) | MultiplicativeExpr MultiplyOperator UnaryExpr | MultiplicativeExpr 'div' UnaryExpr | MultiplicativeExpr 'mod' UnaryExpr - or in other words: - + or in other words: + [26] MultiplicativeExpr ::= UnaryExpr (MulOper MultiplicativeExpr)* - + RETURN 1 - success 0 - failure @@ -2563,17 +2563,17 @@ static int my_xpath_parse_Number(MY_XPATH *xpath) /* Scan NCName. - + SYNOPSYS - + The keywords AND, OR, MOD, DIV are valid identifiers when they are in identifier context: - + SELECT ExtractValue('
VALUE
', '/and/or/mod/div') -> VALUE - + RETURN 1 - success 0 - failure @@ -2623,7 +2623,7 @@ my_xpath_parse_QName(MY_XPATH *xpath) 1. Standard XPath syntax [36], for SP variables: - VariableReference ::= '$' QName + VariableReference ::= '$' QName Finds a SP variable with the given name. If outside of a SP context, or variable with @@ -2746,7 +2746,7 @@ my_xpath_parse_NameTest(MY_XPATH *xpath) SYNOPSYS Scan xpath expression. - The expression is returned in xpath->expr. + The expression is returned in xpath->expr RETURN 1 - success @@ -2783,7 +2783,7 @@ bool Item_xml_str_func::fix_fields(THD *thd, Item **ref) if (Item_str_func::fix_fields(thd, ref)) return true; - + status_var_increment(current_thd->status_var.feature_xml); nodeset_func= 0; @@ -2854,7 +2854,7 @@ bool Item_xml_str_func::fix_fields(THD *thd, Item **ref) #define MAX_LEVEL 256 -typedef struct +typedef struct { uint level; String *pxml; // parsed XML @@ -2937,7 +2937,7 @@ int xml_value(MY_XML_PARSER *st,const char *attr, size_t len) { MY_XML_USER_DATA *data= (MY_XML_USER_DATA*)st->user_data; MY_XML_NODE node; - + node.parent= data->parent; // Set parent for the new text node to old parent node.level= data->level; node.type= MY_XML_NODE_TEXT; @@ -3068,7 +3068,27 @@ String *Item_func_xml_extractvalue::val_str(String *str) null_value= 1; return 0; } - return res; + return res; +} + + +const Type_handler *Item_func_xml_update::xml_handler= NULL; + + +bool Item_func_xml_update::fix_length_and_dec(THD *thd) +{ + static LEX_CSTRING name= {STRING_WITH_LEN("XMLTYPE")}; + + if (!xml_handler) + xml_handler= Type_handler::handler_by_name(thd, name); + + return Item_xml_str_func::fix_length_and_dec(thd); +} + + +const Type_handler *Item_func_xml_update::type_handler() const +{ + return xml_handler ? xml_handler : Item_xml_str_func::type_handler(); } @@ -3128,3 +3148,5 @@ String *Item_func_xml_update::val_str(String *str) return collect_result(str, nodebeg, rep) ? (String *) NULL : str; } + + diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h index 2fbae54ca31e2..c8f0d8f0ffe4b 100644 --- a/sql/item_xmlfunc.h +++ b/sql/item_xmlfunc.h @@ -157,6 +157,10 @@ class Item_func_xml_update: public Item_xml_str_func static LEX_CSTRING name= {STRING_WITH_LEN("updatexml") }; return name; } + static const Type_handler *xml_handler; + const Type_handler *type_handler() const override; + bool fix_length_and_dec(THD *thd) override; + String *val_str(String *) override; Item *do_get_copy(THD *thd) const override { return get_item_copy(thd, this); } diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index a6da937e88346..7c8b76961d4e6 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -12386,3 +12386,5 @@ ER_PARTIAL_ROWS_LOG_EVENT_BAD_STREAM eng "Broken Partial_rows_log_event stream. Found %s, expected Partial_rows_log_event %u / %u" ER_SLAVE_INCOMPATIBLE_TABLE_DEF eng "Table structure for binlog event is not compatible with the table definition on this slave: %s" +ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE + eng "Data type '%-.64s' doesn't support %s attribute." diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 60e96c4fe2064..de688e99ddbbb 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -13078,30 +13078,74 @@ bool LEX::set_field_type_udt_or_typedef(Lex_field_type_st *type, if (is_typedef) return false; - return set_field_type_udt(type, name, attr); + return set_field_type_udt(type, name, attr, + Lex_column_charset_collation_attrs()); } bool LEX::set_field_type_udt(Lex_field_type_st *type, const LEX_CSTRING &name, - const Lex_length_and_dec_st &attr) + const Lex_length_and_dec_st &attr, + const Lex_column_charset_collation_attrs_st &coll) { const Type_handler *h; + uint column_attributes; + if (!(h= Type_handler::handler_by_name_or_error(thd, name))) return true; - type->set(h, attr, &my_charset_bin); + + column_attributes= attr.has_explicit_length() ? Type_handler::ATTR_LENGTH :0; + column_attributes|= attr.has_explicit_dec() ? Type_handler::ATTR_DEC :0; + column_attributes|= coll.is_empty() ? 0 : Type_handler::ATTR_CHARSET; + column_attributes|= last_field->get_attr_uint32(0) ? + Type_handler::ATTR_SRID : 0; + + if ((column_attributes&= ~h->get_column_attributes())) + { + const char *attr_name= "UNKNOWN"; + if (column_attributes & Type_handler::ATTR_LENGTH) + attr_name= "LENGTH"; + else if (column_attributes & Type_handler::ATTR_DEC) + attr_name= "DECIMALS"; + else if (column_attributes & Type_handler::ATTR_SRID) + attr_name= "REF_SYSTEM_ID"; + else if (column_attributes & Type_handler::ATTR_CHARSET) + attr_name= "CHARACTER SET"; + + my_error(ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE, MYF(0), + ErrConvString(name.str, name.length,system_charset_info).ptr(), + attr_name); + return true; + } + + type->set(h, attr, coll); return false; } bool LEX::set_cast_type_udt(Lex_cast_type_st *type, - const LEX_CSTRING &name) + const LEX_CSTRING &name, + const Lex_exact_charset_extended_collation_attrs_st &coll) + { const Type_handler *h; if (!(h= Type_handler::handler_by_name_or_error(thd, name))) return true; - type->set(h); - return false; + + if (!coll.is_empty() && + (h->get_column_attributes() & Type_handler::ATTR_CHARSET) == 0) + { + my_error(ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE, MYF(0), + ErrConvString(name.str, name.length,system_charset_info).ptr(), + "CHARACTER SET"); + return true; + } + + Lex_length_and_dec_st length_and_dec; + length_and_dec.reset(); + return type->set(h, length_and_dec, thd, + thd->variables.character_set_collations, coll, + thd->variables.collation_connection); } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index ce520b9825b49..8f5d9152483a3 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -5045,9 +5045,12 @@ struct LEX: public Query_tables_list bool set_field_type_udt(Lex_field_type_st *type, const LEX_CSTRING &name, - const Lex_length_and_dec_st &attr); + const Lex_length_and_dec_st &attr, + const Lex_column_charset_collation_attrs_st &coll); + bool set_cast_type_udt(Lex_cast_type_st *type, - const LEX_CSTRING &name); + const LEX_CSTRING &name, + const Lex_exact_charset_extended_collation_attrs_st &coll); bool declare_type_record(THD *thd, const Lex_ident_sys_st &type_name, diff --git a/sql/sql_type.h b/sql/sql_type.h index a2d1aae7d7646..31d630575f550 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -3947,6 +3947,17 @@ class Type_handler PROCEDURE }; + enum column_attributes + { + ATTR_NONE= 0, + ATTR_LENGTH= 1, + ATTR_DEC= 2, + ATTR_CHARSET= 4, + ATTR_SRID= 8, + + ATTR_ALL= ATTR_LENGTH | ATTR_DEC | ATTR_CHARSET | ATTR_SRID + }; + static const Type_handler *handler_by_name(THD *thd, const LEX_CSTRING &name); static const Type_handler *handler_by_name_or_error(THD *thd, const LEX_CSTRING &name); @@ -3989,6 +4000,7 @@ class Type_handler virtual const Name version() const; virtual const Name &default_value() const= 0; virtual uint32 flags() const { return 0; } + virtual uint get_column_attributes() const { return ATTR_ALL; } virtual ulong KEY_pack_flags(uint column_nr) const { return 0; } bool is_unsigned() const { return flags() & UNSIGNED_FLAG; } virtual enum_field_types field_type() const= 0; diff --git a/sql/sql_type_fixedbin.h b/sql/sql_type_fixedbin.h index bc0fb13c4cf58..5e2bfa9268c8a 100644 --- a/sql/sql_type_fixedbin.h +++ b/sql/sql_type_fixedbin.h @@ -1107,6 +1107,9 @@ class Type_handler_fbt: public Type_handler { return FbtImpl::default_value(); } + + uint get_column_attributes() const override { return ATTR_NONE; } + ulong KEY_pack_flags(uint column_nr) const override { return FbtImpl::KEY_pack_flags(column_nr); diff --git a/sql/sql_type_geom.h b/sql/sql_type_geom.h index 9dc4a55edaa46..a4e535b6227fc 100644 --- a/sql/sql_type_geom.h +++ b/sql/sql_type_geom.h @@ -72,6 +72,8 @@ class Type_handler_geometry: public Type_handler_string_result public: virtual ~Type_handler_geometry() {} enum_field_types field_type() const override { return MYSQL_TYPE_GEOMETRY; } + uint get_column_attributes() const override + { return ATTR_LENGTH | ATTR_DEC | ATTR_SRID; } bool Item_append_extended_type_info(Send_field_extended_metadata *to, const Item *item) const override { diff --git a/sql/sql_type_vector.cc b/sql/sql_type_vector.cc index 609fdd2fa5886..ed5c79fff4933 100644 --- a/sql/sql_type_vector.cc +++ b/sql/sql_type_vector.cc @@ -73,7 +73,9 @@ Field *Type_handler_vector::make_conversion_table_field( bool Type_handler_vector::Column_definition_fix_attributes( Column_definition *def) const { - if (def->length == 0 || def->charset != &my_charset_bin) + DBUG_ASSERT(!(def->flags & CONTEXT_COLLATION_FLAG)); + DBUG_ASSERT(def->charset == nullptr); + if (def->length == 0) { my_error(ER_WRONG_FIELD_SPEC, MYF(0), def->field_name.str); return true; @@ -84,6 +86,7 @@ bool Type_handler_vector::Column_definition_fix_attributes( static_cast(MAX_FIELD_VARCHARLENGTH / sizeof(float))); return true; } + def->charset= &my_charset_bin; def->length*= sizeof(float); return false; } diff --git a/sql/sql_type_vector.h b/sql/sql_type_vector.h index ceda9499d41f7..8fbf583719f04 100644 --- a/sql/sql_type_vector.h +++ b/sql/sql_type_vector.h @@ -23,6 +23,7 @@ class Type_handler_vector: public Type_handler_varchar public: virtual ~Type_handler_vector() {} const Type_collection *type_collection() const override; + uint get_column_attributes() const override { return ATTR_LENGTH; } const Type_handler *type_handler_for_comparison() const override; virtual Item *create_typecast_item(THD *thd, Item *item, const Type_cast_attributes &attr) const override; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 2696647aa9261..d49d414a6878e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -366,9 +366,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); */ %ifdef MARIADB -%expect 69 -%else %expect 70 +%else +%expect 71 %endif /* @@ -6541,9 +6541,9 @@ field_type_all_builtin: field_type_all: field_type_all_builtin - | udt_name float_options srid_option + | udt_name float_options srid_option opt_binary { - if (Lex->set_field_type_udt(&$$, $1, $2)) + if (Lex->set_field_type_udt(&$$, $1, $2, $4)) MYSQL_YYABORT; } ; @@ -11937,9 +11937,9 @@ cast_type: } | cast_type_numeric { $$= $1; } | cast_type_temporal { $$= $1; } - | udt_name + | udt_name opt_binary { - if (Lex->set_cast_type_udt(&$$, $1)) + if (Lex->set_cast_type_udt(&$$, $1, $2)) MYSQL_YYABORT; } ; diff --git a/sql/table.cc b/sql/table.cc index 472e41e7fb445..e81ca13a4db4d 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1297,6 +1297,13 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table, vcol= unpack_vcol_info_from_frm(thd, table, &expr_str, &((*field_ptr)->default_value), error_reported); + if (vcol && + field_ptr[0]->check_assignability_from(vcol->expr->type_handler(), + false)) + { + *error_reported= true; + goto end; + } *(dfield_ptr++)= *field_ptr; if (vcol && (vcol->flags & (VCOL_NON_DETERMINISTIC | VCOL_SESSION_FUNC))) table->s->non_determinstic_insert= true; diff --git a/strings/xml.c b/strings/xml.c index 9f2f6b4651c51..b61292c8600ea 100644 --- a/strings/xml.c +++ b/strings/xml.c @@ -479,7 +479,8 @@ int my_xml_parse(MY_XML_PARSER *p,const char *str, size_t len) my_xml_norm_text(&a); if (a.beg != a.end) { - my_xml_value(p,a.beg,(size_t) (a.end-a.beg)); + if (MY_XML_OK != my_xml_value(p,a.beg,(size_t) (a.end-a.beg))) + return MY_XML_ERROR; } } }