From 8888a5996cf39ea58f23b9579a582e1003057d95 Mon Sep 17 00:00:00 2001 From: Frederic Descamps Date: Wed, 4 Mar 2026 16:16:34 +0100 Subject: [PATCH 1/4] Add uuid_version() to uuid type Signed-off-by: Frederic Descamps --- plugin/type_uuid/item_uuidfunc.cc | 54 ++++++++++++++ plugin/type_uuid/item_uuidfunc.h | 33 +++++++++ .../type_uuid/func_uuid_version.result | 63 +++++++++++++++++ .../type_uuid/func_uuid_version.test | 70 +++++++++++++++++++ plugin/type_uuid/plugin.cc | 34 ++++++++- 5 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.result create mode 100644 plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.test diff --git a/plugin/type_uuid/item_uuidfunc.cc b/plugin/type_uuid/item_uuidfunc.cc index c024cb30e3e5a..f6a14f683f07f 100644 --- a/plugin/type_uuid/item_uuidfunc.cc +++ b/plugin/type_uuid/item_uuidfunc.cc @@ -29,3 +29,57 @@ String *Item_func_sys_guid::val_str(String *str) my_uuid2str(buf, const_cast(str->ptr()), 0); return str; } + +bool is_hex_char(char c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} + +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 (i == 8 || i == 13 || i == 18 || i == 23) { + continue; + } + if (!is_hex_char(str[i])) { + return false; + } + } + return true; +} + +int return_uuid_version(const std::string &str) { + if (!is_valid_uuid_format_any(str)) { + return -1; + } + if (!is_hex_char(str[14])) { + return -1; + } + return (str[14] <= '9') ? (str[14] - '0') : (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_UNKNOWN_ERROR, + "uuid_version: not a valid UUID", + 0); + 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..451e8e2fcd744 --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.result @@ -0,0 +1,63 @@ +# +# 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 +# +# not valid +# +SELECT UUID_VERSION("lefred") AS version; +ERROR HY000: uuid_version: not a valid UUID +# +# 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; 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..4273d2105b40c --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.test @@ -0,0 +1,70 @@ +#UUID_VERSION() extract version from UUIDs + +--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 # not valid +--echo # +--error 1105 +SELECT UUID_VERSION("lefred") AS version; + +--echo # +--echo # no argument +--echo # +--error 1582 +SELECT UUID_VERSION() AS version; + +--echo # +--echo # multiple arguments +--echo # +--error 1582 +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 4025 +INSERT INTO t1 VALUES(UUID_v4()); +--error 4025 +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 4025 +INSERT INTO t1 VALUES(UUID()); +INSERT INTO t1 VALUES(UUID_v4()); +--error 4025 +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 4025 +INSERT INTO t1 VALUES(UUID()); +--error 4025 +INSERT INTO t1 VALUES(UUID_v4()); +INSERT INTO t1 VALUES(UUID_v7()); +DROP TABLE t1; \ No newline at end of file diff --git a/plugin/type_uuid/plugin.cc b/plugin/type_uuid/plugin.cc index fa15d847d2356..c0859c873fbec 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_STABLE // Maturity(see include/mysql/plugin.h)*/ } maria_declare_plugin_end; From 812b3c5d38e12c84ff4de73dfd587ead8c76c6c7 Mon Sep 17 00:00:00 2001 From: Frederic Descamps Date: Thu, 5 Mar 2026 11:20:34 +0100 Subject: [PATCH 2/4] add MDEV-38983 number and fix comments from grooverdan Signed-off-by: Frederic Descamps --- plugin/type_uuid/item_uuidfunc.cc | 32 +++++++----- .../type_uuid/func_uuid_version.result | 27 +++++++++- .../type_uuid/func_uuid_version.test | 49 ++++++++++++++----- plugin/type_uuid/plugin.cc | 4 +- 4 files changed, 83 insertions(+), 29 deletions(-) diff --git a/plugin/type_uuid/item_uuidfunc.cc b/plugin/type_uuid/item_uuidfunc.cc index f6a14f683f07f..7022472373e07 100644 --- a/plugin/type_uuid/item_uuidfunc.cc +++ b/plugin/type_uuid/item_uuidfunc.cc @@ -30,11 +30,8 @@ String *Item_func_sys_guid::val_str(String *str) return str; } -bool is_hex_char(char c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); -} - -static bool is_valid_uuid_format_any(const std::string &str) { +static bool is_valid_uuid_format_any(const std::string &str) +{ if (str.size() != 36) { return false; } @@ -42,27 +39,35 @@ static bool is_valid_uuid_format_any(const std::string &str) { return false; } for (size_t i = 0; i < str.size(); ++i) { - if (i == 8 || i == 13 || i == 18 || i == 23) { + if (str[i] == '-') { continue; } - if (!is_hex_char(str[i])) { + if (!isxdigit(str[i])) { return false; } } return true; } -int return_uuid_version(const std::string &str) { +int return_uuid_version(const std::string &str) +{ if (!is_valid_uuid_format_any(str)) { return -1; } - if (!is_hex_char(str[14])) { + 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') : (std::tolower(static_cast(str[14])) - 'a' + 10); } -longlong Item_func_uuid_version::val_int() { +longlong Item_func_uuid_version::val_int() +{ String in_tmp; String *uuid_arg = args[0]->val_str(&in_tmp); if (!uuid_arg) { @@ -73,9 +78,10 @@ longlong Item_func_uuid_version::val_int() { std::string in_str(uuid_arg->ptr(), uuid_arg->length()); int version = return_uuid_version(in_str); if (version < 0) { - my_printf_error(ER_UNKNOWN_ERROR, - "uuid_version: not a valid UUID", - 0); + my_printf_error(ER_TRUNCATED_WRONG_VALUE, + "Incorrect uuid value: '%-.128s'", + MYF(0), + in_str.c_str()); null_value = true; return 0; } 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 index 451e8e2fcd744..e3d778d55a404 100644 --- a/plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.result +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.result @@ -1,4 +1,10 @@ # +# Start of 13.0 test +# +# +# MDEV-38983 Adding UUID_VERSION() +# +# # UUIDv1 # SELECT UUID_VERSION(uuid()) AS version; @@ -17,10 +23,26 @@ SELECT UUID_VERSION(uuid_v7()) AS version; version 7 # -# not valid +# UUIDv5 +# +SELECT UUID_VERSION("3e1f44c0-1c3d-535d-ac3f-5218a74bf2df") AS version; +version +5 +# +# not valid with text # SELECT UUID_VERSION("lefred") AS version; -ERROR HY000: uuid_version: not a valid UUID +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 # @@ -61,3 +83,4 @@ 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 index 4273d2105b40c..efde35b9dd58b 100644 --- a/plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.test +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuid_version.test @@ -1,4 +1,10 @@ -#UUID_VERSION() extract version from UUIDs +--echo # +--echo # Start of 13.0 test +--echo # + +--echo # +--echo # MDEV-38983 Adding UUID_VERSION() +--echo # --echo # --echo # UUIDv1 @@ -16,21 +22,38 @@ SELECT UUID_VERSION(uuid_v4()) AS version; SELECT UUID_VERSION(uuid_v7()) AS version; --echo # ---echo # not valid +--echo # UUIDv5 +--echo # +SELECT UUID_VERSION("3e1f44c0-1c3d-535d-ac3f-5218a74bf2df") AS version; + +--echo # +--echo # not valid with text --echo # ---error 1105 +--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 1582 +--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT SELECT UUID_VERSION() AS version; --echo # --echo # multiple arguments --echo # ---error 1582 +--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT SELECT UUID_VERSION(uuid(), 1) AS version; --echo # @@ -39,9 +62,9 @@ SELECT UUID_VERSION(uuid(), 1) AS version; CREATE TABLE t1 (uuid CHAR(36) PRIMARY KEY CHECK(uuid_version(uuid) = 1)); INSERT INTO t1 VALUES(UUID()); ---error 4025 +--error ER_CONSTRAINT_FAILED INSERT INTO t1 VALUES(UUID_v4()); ---error 4025 +--error ER_CONSTRAINT_FAILED INSERT INTO t1 VALUES(UUID_v7()); DROP TABLE t1; @@ -50,10 +73,10 @@ DROP TABLE t1; --echo # CREATE TABLE t1 (uuid CHAR(36) PRIMARY KEY CHECK(uuid_version(uuid) = 4)); ---error 4025 +--error ER_CONSTRAINT_FAILED INSERT INTO t1 VALUES(UUID()); INSERT INTO t1 VALUES(UUID_v4()); ---error 4025 +--error ER_CONSTRAINT_FAILED INSERT INTO t1 VALUES(UUID_v7()); DROP TABLE t1; @@ -62,9 +85,11 @@ DROP TABLE t1; --echo # CREATE TABLE t1 (uuid CHAR(36) PRIMARY KEY CHECK(uuid_version(uuid) = 7)); ---error 4025 +--error ER_CONSTRAINT_FAILED INSERT INTO t1 VALUES(UUID()); ---error 4025 +--error ER_CONSTRAINT_FAILED INSERT INTO t1 VALUES(UUID_v4()); INSERT INTO t1 VALUES(UUID_v7()); -DROP TABLE t1; \ No newline at end of file +DROP TABLE t1; + +--echo End of 13.0 test diff --git a/plugin/type_uuid/plugin.cc b/plugin/type_uuid/plugin.cc index c0859c873fbec..1c1c7e6897cff 100644 --- a/plugin/type_uuid/plugin.cc +++ b/plugin/type_uuid/plugin.cc @@ -330,8 +330,8 @@ maria_declare_plugin(type_uuid) 0, // Pointer to plugin deinitialization function 0x0100, // Numeric version 0xAABB means AA.BB version NULL, // Status variables - NULL, // System variables + NULL, // System variables "1.0", // String version representation - MariaDB_PLUGIN_MATURITY_STABLE // Maturity(see include/mysql/plugin.h)*/ + MariaDB_PLUGIN_MATURITY_EXPERIMENTAL // Maturity(see include/mysql/plugin.h)*/ } maria_declare_plugin_end; From c6659e186d1d0a7c1e5477343a91398cffb75db6 Mon Sep 17 00:00:00 2001 From: Frederic Descamps Date: Thu, 5 Mar 2026 12:13:10 +0100 Subject: [PATCH 3/4] Fixing Windows compiling error Signed-off-by: Frederic Descamps --- plugin/type_uuid/item_uuidfunc.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugin/type_uuid/item_uuidfunc.cc b/plugin/type_uuid/item_uuidfunc.cc index 7022472373e07..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) { @@ -63,7 +65,9 @@ int return_uuid_version(const std::string &str) if (str[14] == '8' || str[14] == '9' ) { return -1; } - return (str[14] <= '9') ? (str[14] - '0') : (std::tolower(static_cast(str[14])) - 'a' + 10); + return (str[14] <= '9') + ? (str[14] - '0') + : (static_cast(std::tolower(static_cast(str[14]))) - 'a' + 10); } longlong Item_func_uuid_version::val_int() From 134d1aff3a2756077ac975bc46ed0c6f034e1abf Mon Sep 17 00:00:00 2001 From: Frederic Descamps Date: Fri, 6 Mar 2026 11:48:41 +0100 Subject: [PATCH 4/4] Change function maturity level to pass debug and embedded builds Signed-off-by: Frederic Descamps --- plugin/type_uuid/plugin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/type_uuid/plugin.cc b/plugin/type_uuid/plugin.cc index 1c1c7e6897cff..e56a8141d4214 100644 --- a/plugin/type_uuid/plugin.cc +++ b/plugin/type_uuid/plugin.cc @@ -332,6 +332,6 @@ maria_declare_plugin(type_uuid) NULL, // Status variables NULL, // System variables "1.0", // String version representation - MariaDB_PLUGIN_MATURITY_EXPERIMENTAL // Maturity(see include/mysql/plugin.h)*/ + MariaDB_PLUGIN_MATURITY_BETA // Maturity(see include/mysql/plugin.h)*/ } maria_declare_plugin_end;