diff --git a/plugin/type_uuid/item_uuidfunc.cc b/plugin/type_uuid/item_uuidfunc.cc index 2189e36d57f..4edfbf4d341 100644 --- a/plugin/type_uuid/item_uuidfunc.cc +++ b/plugin/type_uuid/item_uuidfunc.cc @@ -29,3 +29,31 @@ 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 3dcb15bfa70..f7ccccd8b87 100644 --- a/plugin/type_uuid/item_uuidfunc.h +++ b/plugin/type_uuid/item_uuidfunc.h @@ -78,4 +78,19 @@ public: { return get_item_copy(thd, this); } }; +class Item_func_uuid_v4: public Item_func_uuid +{ +public: + Item_func_uuid_v4(THD *thd): Item_func_uuid(thd) { } + 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); } +}; + #endif // ITEM_UUIDFUNC_INCLUDED diff --git a/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_plugin.result b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_plugin.result new file mode 100644 index 00000000000..51e935c02c7 --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_plugin.result @@ -0,0 +1,35 @@ +# +# Start of 11.6 tests +# +# +# MDEV-11339 Support UUID v4 generation +# +SELECT +'----' AS `----`, +PLUGIN_NAME, +PLUGIN_VERSION, +PLUGIN_STATUS, +PLUGIN_TYPE, +PLUGIN_AUTHOR, +PLUGIN_DESCRIPTION, +PLUGIN_LICENSE, +PLUGIN_MATURITY, +PLUGIN_AUTH_VERSION +FROM INFORMATION_SCHEMA.PLUGINS +WHERE PLUGIN_TYPE='FUNCTION' + AND PLUGIN_NAME IN +('uuidv4') +ORDER BY PLUGIN_NAME; +---- ---- +PLUGIN_NAME uuidv4 +PLUGIN_VERSION 1.0 +PLUGIN_STATUS ACTIVE +PLUGIN_TYPE FUNCTION +PLUGIN_AUTHOR Stefano Petrilli +PLUGIN_DESCRIPTION Function UUIDv4() +PLUGIN_LICENSE GPL +PLUGIN_MATURITY Experimental +PLUGIN_AUTH_VERSION 1.0 +# +# End of 11.6 tests +# diff --git a/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_plugin.test b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_plugin.test new file mode 100644 index 00000000000..1e2209f19d9 --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_plugin.test @@ -0,0 +1,30 @@ +--echo # +--echo # Start of 11.6 tests +--echo # + +--echo # +--echo # MDEV-11339 Support UUID v4 generation +--echo # + +--vertical_results +SELECT + '----' AS `----`, + PLUGIN_NAME, + PLUGIN_VERSION, + PLUGIN_STATUS, + PLUGIN_TYPE, + PLUGIN_AUTHOR, + PLUGIN_DESCRIPTION, + PLUGIN_LICENSE, + PLUGIN_MATURITY, + PLUGIN_AUTH_VERSION +FROM INFORMATION_SCHEMA.PLUGINS +WHERE PLUGIN_TYPE='FUNCTION' + AND PLUGIN_NAME IN + ('uuidv4') +ORDER BY PLUGIN_NAME; +--horizontal_results + +--echo # +--echo # End of 11.6 tests +--echo # diff --git a/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_random.result b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_random.result new file mode 100644 index 00000000000..d6b3bc7ad59 --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_random.result @@ -0,0 +1,15 @@ +# +# 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_100; +select count(distinct u) AS distinct_uuid_count from t1; +distinct_uuid_count +100 +drop table t1; +# +# End of 11.6 tests +# diff --git a/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_random.test b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_random.test new file mode 100644 index 00000000000..67687a3e4e5 --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_random.test @@ -0,0 +1,18 @@ +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_100; +select count(distinct u) AS distinct_uuid_count from t1; +drop table t1; + +--echo # +--echo # End of 11.6 tests +--echo # diff --git a/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_version_revision.result b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_version_revision.result new file mode 100644 index 00000000000..075704b7e00 --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_version_revision.result @@ -0,0 +1,12 @@ +# +# Start of 11.6 tests +# +# +# MDEV-11339 Support UUID v4 generation +# +SELECT UUIDv4() 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; +is_correct_version_and_revision +1 +# +# End of 11.6 tests +# diff --git a/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_version_revision.test b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_version_revision.test new file mode 100644 index 00000000000..f977578b1ce --- /dev/null +++ b/plugin/type_uuid/mysql-test/type_uuid/func_uuidv4_version_revision.test @@ -0,0 +1,13 @@ +--echo # +--echo # Start of 11.6 tests +--echo # + +--echo # +--echo # MDEV-11339 Support UUID v4 generation +--echo # + +SELECT UUIDv4() 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; + +--echo # +--echo # End of 11.6 tests +--echo # diff --git a/plugin/type_uuid/plugin.cc b/plugin/type_uuid/plugin.cc index e35df1caf5f..61cd73d4438 100644 --- a/plugin/type_uuid/plugin.cc +++ b/plugin/type_uuid/plugin.cc @@ -144,12 +144,31 @@ protected: virtual ~Create_func_sys_guid() {} }; +class Create_func_uuid_v4 : public Create_func_arg0 +{ +public: + Item *create_builder(THD *thd) override + { + DBUG_ENTER("Create_func_uuid_v4::create"); + thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + thd->lex->safe_to_cache_query= 0; + DBUG_RETURN(new (thd->mem_root) Item_func_uuid_v4(thd)); + } + static Create_func_uuid_v4 s_singleton; + +protected: + Create_func_uuid_v4() {} + virtual ~Create_func_uuid_v4() {} +}; + 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; 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_sys_guid(&Create_func_sys_guid::s_singleton), + plugin_descriptor_function_uuid_v4(&Create_func_uuid_v4::s_singleton); static constexpr Name type_name={STRING_WITH_LEN("uuid")}; @@ -207,5 +226,20 @@ maria_declare_plugin(type_uuid) NULL, // System variables "1.0", // 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_v4, // pointer to type-specific plugin descriptor + "uuidv4", // plugin name + "Stefano Petrilli", // plugin author + "Function UUIDv4()", // 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_EXPERIMENTAL// Maturity(see include/mysql/plugin.h)*/ } -maria_declare_plugin_end; +maria_declare_plugin_end; \ No newline at end of file diff --git a/plugin/type_uuid/sql_type_uuid.h b/plugin/type_uuid/sql_type_uuid.h index 574c9c7e9d6..ab801aa16fb 100644 --- a/plugin/type_uuid/sql_type_uuid.h +++ b/plugin/type_uuid/sql_type_uuid.h @@ -347,5 +347,7 @@ public: } }; +#include "sql_type_uuid_v4.h" + #endif // SQL_TYPE_UUID_INCLUDED diff --git a/plugin/type_uuid/sql_type_uuid_v4.h b/plugin/type_uuid/sql_type_uuid_v4.h new file mode 100644 index 00000000000..0a10d971aac --- /dev/null +++ b/plugin/type_uuid/sql_type_uuid_v4.h @@ -0,0 +1,62 @@ +#ifndef SQL_TYPE_UUID_V4_INCLUDED +#define SQL_TYPE_UUID_V4_INCLUDED + +/* Copyright (c) 2024, Stefano Petrilli + + 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 */ + +/* + Implements Universal Unique Identifiers version 4, as described in + draft-ietf-uuidrev-rfc4122bis-14. + + Field Octet # Note + random_a 0-5 Random CSPRNG 48 bits. + ver 6 The 4 bit version field, set to + 0b0100. Occupies bits 48 through 51 of + octet 6. + random_b 6-7 Random CSPRNG 12 bits. + var 8 The 2 bit variant field, set to 0b10. + Occupies bits 64 and 65 of octet 8. + random_c 8-15 Random CSPRNG 62 bits. + + The structure of an UUIDv4 is: llllllll-mmmm-Vhhh-vsss-nnnnnnnnnnnn + The replacement of the version and variant field bits results in 122 + bits of random data. +*/ + +#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) +{ + if (my_random_bytes(to, 16) != MY_AES_OK) + return 0; + + to[6]= ((to[6] & UUID_VERSION_MASK) | UUID_VERSION); + to[8]= ((to[8] & UUID_VARIANT_MASK) | UUID_VARIANT); + return 1; +} + +#endif // SQL_TYPE_UUID_V4_INCLUDED