From 20611c8ae7899e8d252f94721345b628ca2ed6c4 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 4 Apr 2024 06:22:06 +0400 Subject: [PATCH] cleanup: MDEV-11339 Implement native UUID4 function - Moving the class UUIDv1 into a separate file sql_type_uuid_v1.h - Adding a new class UUIDv4, similar to UUIDv1 - Changing the way how my_random_bytes() failures are handled. Instead of raising an error it now raises a note. Reasoning: if we're in the middle of a multi-million row transaction and one UUIDv4 generation fails, it's not a good idea to throw away the entire transaction. Instead, let's generate bytes using a my_rnd() loop. - Adding a new test func_uuid_v4.test to demonstrate that the UUIDv4() returned type is "UUID NOT NULL". - Adding a new test func_uuidv4_debug.test to emulate my_random_bytes() failures - Adding a template Item_func_uuid_vx to share the code between the implementations of UUID() and UUIDv4(). --- plugin/type_uuid/item_uuidfunc.cc | 30 +------- plugin/type_uuid/item_uuidfunc.h | 45 +++++++----- .../mysql-test/type_uuid/func_uuid_v4.result | 10 +++ .../mysql-test/type_uuid/func_uuid_v4.test | 7 ++ .../type_uuid/func_uuidv4_debug.result | 44 ++++++++++++ .../type_uuid/func_uuidv4_debug.test | 32 +++++++++ plugin/type_uuid/plugin.cc | 3 +- plugin/type_uuid/sql_type_uuid.h | 21 +----- plugin/type_uuid/sql_type_uuid_v1.h | 37 ++++++++++ plugin/type_uuid/sql_type_uuid_v4.h | 72 ++++++++++++++----- 10 files changed, 213 insertions(+), 88 deletions(-) create mode 100644 plugin/type_uuid/mysql-test/type_uuid/func_uuid_v4.result create mode 100644 plugin/type_uuid/mysql-test/type_uuid/func_uuid_v4.test create mode 100644 plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_debug.result create mode 100644 plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_debug.test create mode 100644 plugin/type_uuid/sql_type_uuid_v1.h diff --git a/plugin/type_uuid/item_uuidfunc.cc b/plugin/type_uuid/item_uuidfunc.cc index 4edfbf4d341..c024cb30e3e 100644 --- a/plugin/type_uuid/item_uuidfunc.cc +++ b/plugin/type_uuid/item_uuidfunc.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2019,2021, MariaDB Corporation +/* Copyright (c) 2019,2024, 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 @@ -29,31 +29,3 @@ String *Item_func_sys_guid::val_str(String *str) my_uuid2str(buf, const_cast(str->ptr()), 0); return str; } - -String *Item_func_uuid_v4::val_str(String *str) -{ - DBUG_ASSERT(fixed()); - str->alloc(MY_UUID_STRING_LENGTH+1); - str->length(MY_UUID_STRING_LENGTH); - str->set_charset(collation.collation); - - uchar buf[MY_UUID_SIZE]; - if (!my_uuid_v4(buf)) { - my_error(ER_INTERNAL_ERROR, MYF(0), - "Failed to generate a random value for UUIDv4"); - } - my_uuid2str(buf, const_cast(str->ptr()), 1); - return str; -} - -bool Item_func_uuid_v4::val_native(THD *, Native *to) -{ - DBUG_ASSERT(fixed()); - to->alloc(MY_UUID_SIZE); - to->length(MY_UUID_SIZE); - if (!my_uuid_v4((uchar*)to->ptr())) { - my_error(ER_INTERNAL_ERROR, MYF(0), - "Failed to generate a random value for UUIDv4"); - } - return 0; -} diff --git a/plugin/type_uuid/item_uuidfunc.h b/plugin/type_uuid/item_uuidfunc.h index f7ccccd8b87..1b949c0bb50 100644 --- a/plugin/type_uuid/item_uuidfunc.h +++ b/plugin/type_uuid/item_uuidfunc.h @@ -1,7 +1,7 @@ #ifndef ITEM_UUIDFUNC_INCLUDED #define ITEM_UUIDFUNC_INCLUDED -/* Copyright (c) 2019,2021, MariaDB Corporation +/* Copyright (c) 2019,2024, 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 @@ -18,7 +18,8 @@ #include "item.h" -#include "sql_type_uuid.h" +#include "sql_type_uuid_v1.h" +#include "sql_type_uuid_v4.h" class Item_func_sys_guid: public Item_str_func { @@ -49,46 +50,54 @@ public: { return get_item_copy(thd, this); } }; -class Item_func_uuid: public Type_handler_uuid_new::Item_fbt_func + +template +class Item_func_uuid_vx: public Type_handler_uuid_new::Item_fbt_func { public: - Item_func_uuid(THD *thd): Item_fbt_func(thd) { } + using Item_fbt_func::Item_fbt_func; bool const_item() const override { return false; } table_map used_tables() const override { return RAND_TABLE_BIT; } bool check_vcol_func_processor(void *arg) override { return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC); } + String *val_str(String *str) override + { + DBUG_ASSERT(fixed()); + return UUIDvX().to_string(str) ? NULL : str; + } + bool val_native(THD *thd, Native *to) override + { + DBUG_ASSERT(fixed()); + return UUIDvX::construct_native(to); + } +}; + + +class Item_func_uuid: public Item_func_uuid_vx +{ +public: + using Item_func_uuid_vx::Item_func_uuid_vx; LEX_CSTRING func_name_cstring() const override { static LEX_CSTRING name= {STRING_WITH_LEN("uuid") }; return name; } - String *val_str(String *str) override - { - DBUG_ASSERT(fixed()); - return UUIDv1().to_string(str) ? NULL : str; - } - bool val_native(THD *thd, Native *to) override - { - DBUG_ASSERT(fixed()); - return UUIDv1::construct_native(to); - } Item *do_get_copy(THD *thd) const override { return get_item_copy(thd, this); } }; -class Item_func_uuid_v4: public Item_func_uuid + +class Item_func_uuid_v4: public Item_func_uuid_vx { public: - Item_func_uuid_v4(THD *thd): Item_func_uuid(thd) { } + using Item_func_uuid_vx::Item_func_uuid_vx; LEX_CSTRING func_name_cstring() const override { static LEX_CSTRING name= {STRING_WITH_LEN("uuidv4") }; return name; } - String *val_str(String *) override; - bool val_native(THD *thd, Native *to) override; Item *do_get_copy(THD *thd) const override { return get_item_copy(thd, this); } }; diff --git a/plugin/type_uuid/mysql-test/type_uuid/func_uuid_v4.result b/plugin/type_uuid/mysql-test/type_uuid/func_uuid_v4.result new file mode 100644 index 00000000000..3bd22a20734 --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuid_v4.result @@ -0,0 +1,10 @@ +# +# MDEV-33827 UUID() returns a NULL-able result +# +CREATE TABLE t1 AS SELECT UUIDv4(); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `UUIDv4()` uuid NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t1; diff --git a/plugin/type_uuid/mysql-test/type_uuid/func_uuid_v4.test b/plugin/type_uuid/mysql-test/type_uuid/func_uuid_v4.test new file mode 100644 index 00000000000..734936388c4 --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuid_v4.test @@ -0,0 +1,7 @@ +--echo # +--echo # MDEV-33827 UUID() returns a NULL-able result +--echo # + +CREATE TABLE t1 AS SELECT UUIDv4(); +SHOW CREATE TABLE t1; +DROP TABLE t1; diff --git a/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_debug.result b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_debug.result new file mode 100644 index 00000000000..9975e8c3358 --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_debug.result @@ -0,0 +1,44 @@ +SET debug_dbug="+d,simulate_uuidv4_my_random_bytes_failure"; +# +# Start of 11.6 tests +# +# +# MDEV-11339 Support UUID v4 generation +# +CREATE TABLE t1 ( +a int primary key not null, +u UUID DEFAULT UUIDv4(), +unique key(u) +); +INSERT INTO t1(a) SELECT seq FROM seq_1_to_16; +Warnings: +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +Note 1105 UUIDv4 generation failed; using a my_rnd fallback +SELECT COUNT(DISTINCT u) AS distinct_uuid_count FROM t1; +distinct_uuid_count +16 +SELECT +u REGEXP '[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}' AS is_correct_version_and_revision, +COUNT(*) +FROM t1 GROUP BY is_correct_version_and_revision; +is_correct_version_and_revision COUNT(*) +1 16 +DROP TABLE t1; +# +# End of 11.6 tests +# +SET debug_dbug=""; diff --git a/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_debug.test b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_debug.test new file mode 100644 index 00000000000..ebd9ce72bb3 --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_debug.test @@ -0,0 +1,32 @@ +--source include/have_debug.inc + +SET debug_dbug="+d,simulate_uuidv4_my_random_bytes_failure"; + +source include/have_sequence.inc; + +--echo # +--echo # Start of 11.6 tests +--echo # + +--echo # +--echo # MDEV-11339 Support UUID v4 generation +--echo # + +CREATE TABLE t1 ( + a int primary key not null, + u UUID DEFAULT UUIDv4(), + unique key(u) +); +INSERT INTO t1(a) SELECT seq FROM seq_1_to_16; +SELECT COUNT(DISTINCT u) AS distinct_uuid_count FROM t1; +SELECT + u REGEXP '[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}' AS is_correct_version_and_revision, + COUNT(*) +FROM t1 GROUP BY is_correct_version_and_revision; +DROP TABLE t1; + +--echo # +--echo # End of 11.6 tests +--echo # + +SET debug_dbug=""; diff --git a/plugin/type_uuid/plugin.cc b/plugin/type_uuid/plugin.cc index 61cd73d4438..aa5119f759f 100644 --- a/plugin/type_uuid/plugin.cc +++ b/plugin/type_uuid/plugin.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2019,2021, MariaDB Corporation +/* Copyright (c) 2019,2024, 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 @@ -16,7 +16,6 @@ #define MYSQL_SERVER #include "mariadb.h" #include "sql_class.h" -#include "sql_type_uuid.h" #include "item_uuidfunc.h" #include #include diff --git a/plugin/type_uuid/sql_type_uuid.h b/plugin/type_uuid/sql_type_uuid.h index ab801aa16fb..abb539b2ce5 100644 --- a/plugin/type_uuid/sql_type_uuid.h +++ b/plugin/type_uuid/sql_type_uuid.h @@ -1,7 +1,7 @@ #ifndef SQL_TYPE_UUID_INCLUDED #define SQL_TYPE_UUID_INCLUDED -/* Copyright (c) 2019,2021 MariaDB Corporation +/* Copyright (c) 2019,2024 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 @@ -331,23 +331,4 @@ typedef Type_handler_fbt, Type_collection_uuid> Type_handler_uuid_old; typedef Type_handler_fbt, Type_collection_uuid> Type_handler_uuid_new; -class UUIDv1: public Type_handler_uuid_new::Fbt -{ -public: - UUIDv1() - { - my_uuid((uchar*) m_buffer); - } - static bool construct_native(Native *to) - { - to->alloc(MY_UUID_SIZE); - to->length(MY_UUID_SIZE); - my_uuid((uchar*)to->ptr()); - return 0; - } -}; - -#include "sql_type_uuid_v4.h" - - #endif // SQL_TYPE_UUID_INCLUDED diff --git a/plugin/type_uuid/sql_type_uuid_v1.h b/plugin/type_uuid/sql_type_uuid_v1.h new file mode 100644 index 00000000000..b1ad48d9fb6 --- /dev/null +++ b/plugin/type_uuid/sql_type_uuid_v1.h @@ -0,0 +1,37 @@ +#ifndef SQL_TYPE_UUID_V1_INCLUDED +#define SQL_TYPE_UUID_V1_INCLUDED + +/* Copyright (c) 2024, 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 */ + +#include "sql_type_uuid.h" + +class UUIDv1: public Type_handler_uuid_new::Fbt +{ +public: + UUIDv1() + { + my_uuid((uchar*) m_buffer); + } + static bool construct_native(Native *to) + { + to->alloc(MY_UUID_SIZE); + to->length(MY_UUID_SIZE); + my_uuid((uchar*)to->ptr()); + return 0; + } +}; + +#endif // SQL_TYPE_UUID_V1_INCLUDED diff --git a/plugin/type_uuid/sql_type_uuid_v4.h b/plugin/type_uuid/sql_type_uuid_v4.h index 0a10d971aac..dbb7f1ca0ce 100644 --- a/plugin/type_uuid/sql_type_uuid_v4.h +++ b/plugin/type_uuid/sql_type_uuid_v4.h @@ -35,28 +35,62 @@ bits of random data. */ +#include "sql_type_uuid.h" #include "my_rnd.h" -#define UUID_VERSION 0x40 -#define UUID_VERSION_MASK 0x0F -#define UUID_VARIANT 0x80 -#define UUID_VARIANT_MASK 0x3F - -/** - Create a global unique identifier version 4 (uuidv4) - - @func my_uuid_v4() - @param to Store uuidv4 here. Must be of size MY_UUID_SIZE (16) - @return 1 in case of success and 0 in case of failure -*/ -static inline bool my_uuid_v4(uchar *to) +class UUIDv4: public Type_handler_uuid_new::Fbt { - if (my_random_bytes(to, 16) != MY_AES_OK) - return 0; + static constexpr uchar UUID_VERSION() { return 0x40; } + static constexpr uchar UUID_VERSION_MASK() { return 0x0F; } + static constexpr uchar UUID_VARIANT() { return 0x80; } + static constexpr uchar UUID_VARIANT_MASK() { return 0x3F; } - to[6]= ((to[6] & UUID_VERSION_MASK) | UUID_VERSION); - to[8]= ((to[8] & UUID_VARIANT_MASK) | UUID_VARIANT); - return 1; -} + static void inject_version_and_variant(uchar *to) + { + to[6]= ((to[6] & UUID_VERSION_MASK()) | UUID_VERSION()); + to[8]= ((to[8] & UUID_VARIANT_MASK()) | UUID_VARIANT()); + } + + // Construct using a my_rnd()-based fallback method + static void construct_fallback(char *to) + { + for (uint i= 0; i < 4; i++) + int4store(&to[i * 4], (uint32) (my_rnd(&sql_rand)*0xFFFFFFFF)); + } + + static void construct(char *to) + { + bool error= my_random_bytes((uchar*) to, 16) != MY_AES_OK; + DBUG_EXECUTE_IF("simulate_uuidv4_my_random_bytes_failure", error= true; ); + + if (error) // A very unlikely failure happened. + { + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_NOTE, + ER_UNKNOWN_ERROR, + "UUIDv4 generation failed; using a my_rnd fallback"); + construct_fallback(to); + } + /* + We have random bytes at to[6] and to[8]. + Let's inject proper version and variant to make it good UUIDv4. + */ + inject_version_and_variant((uchar*) to); + } + +public: + + UUIDv4() + { + construct(m_buffer); + } + static bool construct_native(Native *to) + { + to->alloc(MY_UUID_SIZE); + to->length(MY_UUID_SIZE); + construct((char*)to->ptr()); + return 0; + } + +}; #endif // SQL_TYPE_UUID_V4_INCLUDED