diff --git a/plugin/type_uuid/item_uuidfunc.cc b/plugin/type_uuid/item_uuidfunc.cc index c024cb30e3e5a..d4fb798b46f80 100644 --- a/plugin/type_uuid/item_uuidfunc.cc +++ b/plugin/type_uuid/item_uuidfunc.cc @@ -16,6 +16,8 @@ #define MYSQL_SERVER #include "mariadb.h" #include "item_uuidfunc.h" +#include +#include String *Item_func_sys_guid::val_str(String *str) { @@ -29,3 +31,65 @@ String *Item_func_sys_guid::val_str(String *str) my_uuid2str(buf, const_cast(str->ptr()), 0); return str; } + +static bool is_valid_uuid_format_any(const std::string &str) +{ + if (str.size() != 36) { + return false; + } + if (str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-') { + return false; + } + for (size_t i = 0; i < str.size(); ++i) { + if (str[i] == '-') { + continue; + } + if (!isxdigit(str[i])) { + return false; + } + } + return true; +} + +int return_uuid_version(const std::string &str) +{ + if (!is_valid_uuid_format_any(str)) { + return -1; + } + if (!isxdigit(str[14])) { + return -1; + } + // Currently the only supported versions are below 8, as per RFC 4122. + // If the version is above 7, it's either not a valid UUID or it's a + //future version that we don't support, so we return -1 in that case as well. + if (str[14] == '8' || str[14] == '9' ) { + return -1; + } + return (str[14] <= '9') + ? (str[14] - '0') + : (static_cast(std::tolower(static_cast(str[14]))) - 'a' + 10); +} + +longlong Item_func_uuid_version::val_int() +{ + String in_tmp; + String *uuid_arg = args[0]->val_str(&in_tmp); + if (!uuid_arg) { + null_value = true; + return 0; + } + + std::string in_str(uuid_arg->ptr(), uuid_arg->length()); + int version = return_uuid_version(in_str); + if (version < 0) { + my_printf_error(ER_TRUNCATED_WRONG_VALUE, + "Incorrect uuid value: '%-.128s'", + MYF(0), + in_str.c_str()); + null_value = true; + return 0; + } + + null_value = false; + return version; +} diff --git a/plugin/type_uuid/item_uuidfunc.h b/plugin/type_uuid/item_uuidfunc.h index b62ccf73fceba..527bd68f31396 100644 --- a/plugin/type_uuid/item_uuidfunc.h +++ b/plugin/type_uuid/item_uuidfunc.h @@ -115,4 +115,37 @@ class Item_func_uuid_v7: public Item_func_uuid_vx Item *shallow_copy(THD *thd) const override { return get_item_copy(thd, this); } }; + +class Item_func_uuid_version : public Item_int_func +{ +public: + Item_func_uuid_version(THD *thd, Item *arg1) + : Item_int_func(thd, arg1) {} + + LEX_CSTRING func_name_cstring() const override + { + static LEX_CSTRING name = {STRING_WITH_LEN("uuid_version") }; + return name; + } + + longlong val_int() override; + const Type_handler *type_handler() const override + { + if (unsigned_flag) { + return &type_handler_ulong; + } + return &type_handler_slong; + } + bool fix_length_and_dec(THD *thd) override + { + decimals = 0; + max_length = 1; + set_maybe_null(); + unsigned_flag = 1; + return FALSE; + } + + Item *shallow_copy(THD *thd) const override + { return get_item_copy(thd, this); } +}; #endif // ITEM_UUIDFUNC_INCLUDED diff --git a/plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.result b/plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.result new file mode 100644 index 0000000000000..e3d778d55a404 --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.result @@ -0,0 +1,86 @@ +# +# Start of 13.0 test +# +# +# MDEV-38983 Adding UUID_VERSION() +# +# +# UUIDv1 +# +SELECT UUID_VERSION(uuid()) AS version; +version +1 +# +# UUIDv4 +# +SELECT UUID_VERSION(uuid_v4()) AS version; +version +4 +# +# UUIDv7 +# +SELECT UUID_VERSION(uuid_v7()) AS version; +version +7 +# +# UUIDv5 +# +SELECT UUID_VERSION("3e1f44c0-1c3d-535d-ac3f-5218a74bf2df") AS version; +version +5 +# +# not valid with text +# +SELECT UUID_VERSION("lefred") AS version; +ERROR 22007: Incorrect uuid value: 'lefred' +# +# not valid with wrong uuid (version 8) +# +SELECT UUID_VERSION("AF39A01C-E29D-8C4C-8072-60CEB26A82F1") AS version; +ERROR 22007: Incorrect uuid value: 'AF39A01C-E29D-8C4C-8072-60CEB26A82F1' +# +# not valid with wrong uuid (version 9) +# +SELECT UUID_VERSION("AF39A01C-E29D-8C4C-8072-60CEB26A82F1") AS version; +ERROR 22007: Incorrect uuid value: 'AF39A01C-E29D-8C4C-8072-60CEB26A82F1' +# +# no argument +# +SELECT UUID_VERSION() AS version; +ERROR 42000: Incorrect parameter count in the call to native function 'UUID_VERSION' +# +# multiple arguments +# +SELECT UUID_VERSION(uuid(), 1) AS version; +ERROR 42000: Incorrect parameter count in the call to native function 'UUID_VERSION' +# +# use it in check constraint to force uuid_v1 +# +CREATE TABLE t1 (uuid CHAR(36) PRIMARY KEY CHECK(uuid_version(uuid) = 1)); +INSERT INTO t1 VALUES(UUID()); +INSERT INTO t1 VALUES(UUID_v4()); +ERROR 23000: CONSTRAINT `t1.uuid` failed for `test`.`t1` +INSERT INTO t1 VALUES(UUID_v7()); +ERROR 23000: CONSTRAINT `t1.uuid` failed for `test`.`t1` +DROP TABLE t1; +# +# use it in check constraint to force uuid_v4 +# +CREATE TABLE t1 (uuid CHAR(36) PRIMARY KEY CHECK(uuid_version(uuid) = 4)); +INSERT INTO t1 VALUES(UUID()); +ERROR 23000: CONSTRAINT `t1.uuid` failed for `test`.`t1` +INSERT INTO t1 VALUES(UUID_v4()); +INSERT INTO t1 VALUES(UUID_v7()); +ERROR 23000: CONSTRAINT `t1.uuid` failed for `test`.`t1` +DROP TABLE t1; +# +# use it in check constraint to force uuid_v7 +# +CREATE TABLE t1 (uuid CHAR(36) PRIMARY KEY CHECK(uuid_version(uuid) = 7)); +INSERT INTO t1 VALUES(UUID()); +ERROR 23000: CONSTRAINT `t1.uuid` failed for `test`.`t1` +INSERT INTO t1 VALUES(UUID_v4()); +ERROR 23000: CONSTRAINT `t1.uuid` failed for `test`.`t1` +INSERT INTO t1 VALUES(UUID_v7()); +DROP TABLE t1; +End of 13.0 test diff --git a/plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.test b/plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.test new file mode 100644 index 0000000000000..efde35b9dd58b --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.test @@ -0,0 +1,95 @@ +--echo # +--echo # Start of 13.0 test +--echo # + +--echo # +--echo # MDEV-38983 Adding UUID_VERSION() +--echo # + +--echo # +--echo # UUIDv1 +--echo # +SELECT UUID_VERSION(uuid()) AS version; + +--echo # +--echo # UUIDv4 +--echo # +SELECT UUID_VERSION(uuid_v4()) AS version; + +--echo # +--echo # UUIDv7 +--echo # +SELECT UUID_VERSION(uuid_v7()) AS version; + +--echo # +--echo # UUIDv5 +--echo # +SELECT UUID_VERSION("3e1f44c0-1c3d-535d-ac3f-5218a74bf2df") AS version; + +--echo # +--echo # not valid with text +--echo # +--error ER_TRUNCATED_WRONG_VALUE +SELECT UUID_VERSION("lefred") AS version; + +--echo # +--echo # not valid with wrong uuid (version 8) +--echo # +--error ER_TRUNCATED_WRONG_VALUE +SELECT UUID_VERSION("AF39A01C-E29D-8C4C-8072-60CEB26A82F1") AS version; + +--echo # +--echo # not valid with wrong uuid (version 9) +--echo # +--error ER_TRUNCATED_WRONG_VALUE +SELECT UUID_VERSION("AF39A01C-E29D-8C4C-8072-60CEB26A82F1") AS version; + +--echo # +--echo # no argument +--echo # +--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT +SELECT UUID_VERSION() AS version; + +--echo # +--echo # multiple arguments +--echo # +--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT +SELECT UUID_VERSION(uuid(), 1) AS version; + +--echo # +--echo # use it in check constraint to force uuid_v1 +--echo # + +CREATE TABLE t1 (uuid CHAR(36) PRIMARY KEY CHECK(uuid_version(uuid) = 1)); +INSERT INTO t1 VALUES(UUID()); +--error ER_CONSTRAINT_FAILED +INSERT INTO t1 VALUES(UUID_v4()); +--error ER_CONSTRAINT_FAILED +INSERT INTO t1 VALUES(UUID_v7()); +DROP TABLE t1; + +--echo # +--echo # use it in check constraint to force uuid_v4 +--echo # + +CREATE TABLE t1 (uuid CHAR(36) PRIMARY KEY CHECK(uuid_version(uuid) = 4)); +--error ER_CONSTRAINT_FAILED +INSERT INTO t1 VALUES(UUID()); +INSERT INTO t1 VALUES(UUID_v4()); +--error ER_CONSTRAINT_FAILED +INSERT INTO t1 VALUES(UUID_v7()); +DROP TABLE t1; + +--echo # +--echo # use it in check constraint to force uuid_v7 +--echo # + +CREATE TABLE t1 (uuid CHAR(36) PRIMARY KEY CHECK(uuid_version(uuid) = 7)); +--error ER_CONSTRAINT_FAILED +INSERT INTO t1 VALUES(UUID()); +--error ER_CONSTRAINT_FAILED +INSERT INTO t1 VALUES(UUID_v4()); +INSERT INTO t1 VALUES(UUID_v7()); +DROP TABLE t1; + +--echo End of 13.0 test diff --git a/plugin/type_uuid/plugin.cc b/plugin/type_uuid/plugin.cc index fa15d847d2356..e56a8141d4214 100644 --- a/plugin/type_uuid/plugin.cc +++ b/plugin/type_uuid/plugin.cc @@ -180,16 +180,33 @@ class Create_func_uuid_v7 : public Create_func_arg0 virtual ~Create_func_uuid_v7() {} }; +class Create_func_uuid_version : public Create_func_arg1 +{ +public: + Item *create_1_arg(THD *thd, Item *arg1) override + { + DBUG_ENTER("Create_func_uuid_version::create_1_arg"); + DBUG_RETURN(new (thd->mem_root) Item_func_uuid_version(thd, arg1)); + } + static Create_func_uuid_version s_singleton; + +protected: + Create_func_uuid_version() {} + virtual ~Create_func_uuid_version() {} +}; + Create_func_uuid Create_func_uuid::s_singleton; Create_func_sys_guid Create_func_sys_guid::s_singleton; Create_func_uuid_v4 Create_func_uuid_v4::s_singleton; Create_func_uuid_v7 Create_func_uuid_v7::s_singleton; +Create_func_uuid_version Create_func_uuid_version::s_singleton; static Plugin_function plugin_descriptor_function_uuid(&Create_func_uuid::s_singleton), plugin_descriptor_function_sys_guid(&Create_func_sys_guid::s_singleton), plugin_descriptor_function_uuid_v4(&Create_func_uuid_v4::s_singleton), - plugin_descriptor_function_uuid_v7(&Create_func_uuid_v7::s_singleton); + plugin_descriptor_function_uuid_v7(&Create_func_uuid_v7::s_singleton), + plugin_descriptor_function_uuid_version(&Create_func_uuid_version::s_singleton); static constexpr Name type_name={STRING_WITH_LEN("uuid")}; @@ -301,5 +318,20 @@ maria_declare_plugin(type_uuid) NULL, // System variables "1.0.1", // String version representation MariaDB_PLUGIN_MATURITY_STABLE// Maturity(see include/mysql/plugin.h)*/ +}, +{ + MariaDB_FUNCTION_PLUGIN, // the plugin type (see include/mysql/plugin.h) + &plugin_descriptor_function_uuid_version, // pointer to type-specific plugin descriptor + "uuid_version", // plugin name + "lefred", // plugin author + "Function UUID_VERSION()", // 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_BETA // Maturity(see include/mysql/plugin.h)*/ } maria_declare_plugin_end;