Commit 59244b9c authored by Alexander Barkov's avatar Alexander Barkov Committed by Sergei Golubchik

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().
parent 287589ce
/* Copyright (c) 2019,2021, MariaDB Corporation /* Copyright (c) 2019,2024, MariaDB Corporation
This program is free software; you can redistribute it and/or modify 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 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) ...@@ -29,31 +29,3 @@ String *Item_func_sys_guid::val_str(String *str)
my_uuid2str(buf, const_cast<char*>(str->ptr()), 0); my_uuid2str(buf, const_cast<char*>(str->ptr()), 0);
return str; 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<char*>(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;
}
#ifndef ITEM_UUIDFUNC_INCLUDED #ifndef ITEM_UUIDFUNC_INCLUDED
#define 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 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 it under the terms of the GNU General Public License as published by
...@@ -18,7 +18,8 @@ ...@@ -18,7 +18,8 @@
#include "item.h" #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 class Item_func_sys_guid: public Item_str_func
{ {
...@@ -49,46 +50,54 @@ class Item_func_sys_guid: public Item_str_func ...@@ -49,46 +50,54 @@ class Item_func_sys_guid: public Item_str_func
{ return get_item_copy<Item_func_sys_guid>(thd, this); } { return get_item_copy<Item_func_sys_guid>(thd, this); }
}; };
class Item_func_uuid: public Type_handler_uuid_new::Item_fbt_func
template<class UUIDvX>
class Item_func_uuid_vx: public Type_handler_uuid_new::Item_fbt_func
{ {
public: public:
Item_func_uuid(THD *thd): Item_fbt_func(thd) { } using Item_fbt_func::Item_fbt_func;
bool const_item() const override { return false; } bool const_item() const override { return false; }
table_map used_tables() const override { return RAND_TABLE_BIT; } table_map used_tables() const override { return RAND_TABLE_BIT; }
bool check_vcol_func_processor(void *arg) override bool check_vcol_func_processor(void *arg) override
{ {
return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC); return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC);
} }
LEX_CSTRING func_name_cstring() const override
{
static LEX_CSTRING name= {STRING_WITH_LEN("uuid") };
return name;
}
String *val_str(String *str) override String *val_str(String *str) override
{ {
DBUG_ASSERT(fixed()); DBUG_ASSERT(fixed());
return UUIDv1().to_string(str) ? NULL : str; return UUIDvX().to_string(str) ? NULL : str;
} }
bool val_native(THD *thd, Native *to) override bool val_native(THD *thd, Native *to) override
{ {
DBUG_ASSERT(fixed()); DBUG_ASSERT(fixed());
return UUIDv1::construct_native(to); return UUIDvX::construct_native(to);
}
};
class Item_func_uuid: public Item_func_uuid_vx<UUIDv1>
{
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;
} }
Item *do_get_copy(THD *thd) const override Item *do_get_copy(THD *thd) const override
{ return get_item_copy<Item_func_uuid>(thd, this); } { return get_item_copy<Item_func_uuid>(thd, this); }
}; };
class Item_func_uuid_v4: public Item_func_uuid
class Item_func_uuid_v4: public Item_func_uuid_vx<UUIDv4>
{ {
public: 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 LEX_CSTRING func_name_cstring() const override
{ {
static LEX_CSTRING name= {STRING_WITH_LEN("uuidv4") }; static LEX_CSTRING name= {STRING_WITH_LEN("uuidv4") };
return name; return name;
} }
String *val_str(String *) override;
bool val_native(THD *thd, Native *to) override;
Item *do_get_copy(THD *thd) const override Item *do_get_copy(THD *thd) const override
{ return get_item_copy<Item_func_uuid_v4>(thd, this); } { return get_item_copy<Item_func_uuid_v4>(thd, this); }
}; };
......
#
# 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;
--echo #
--echo # MDEV-33827 UUID() returns a NULL-able result
--echo #
CREATE TABLE t1 AS SELECT UUIDv4();
SHOW CREATE TABLE t1;
DROP TABLE t1;
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="";
--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="";
/* Copyright (c) 2019,2021, MariaDB Corporation /* Copyright (c) 2019,2024, MariaDB Corporation
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#define MYSQL_SERVER #define MYSQL_SERVER
#include "mariadb.h" #include "mariadb.h"
#include "sql_class.h" #include "sql_class.h"
#include "sql_type_uuid.h"
#include "item_uuidfunc.h" #include "item_uuidfunc.h"
#include <mysql/plugin_data_type.h> #include <mysql/plugin_data_type.h>
#include <mysql/plugin_function.h> #include <mysql/plugin_function.h>
......
#ifndef SQL_TYPE_UUID_INCLUDED #ifndef SQL_TYPE_UUID_INCLUDED
#define 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 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 it under the terms of the GNU General Public License as published by
...@@ -331,23 +331,4 @@ typedef Type_handler_fbt<UUID<1>, Type_collection_uuid> Type_handler_uuid_old; ...@@ -331,23 +331,4 @@ typedef Type_handler_fbt<UUID<1>, Type_collection_uuid> Type_handler_uuid_old;
typedef Type_handler_fbt<UUID<0>, Type_collection_uuid> Type_handler_uuid_new; typedef Type_handler_fbt<UUID<0>, 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 #endif // SQL_TYPE_UUID_INCLUDED
#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
...@@ -35,28 +35,62 @@ ...@@ -35,28 +35,62 @@
bits of random data. bits of random data.
*/ */
#include "sql_type_uuid.h"
#include "my_rnd.h" #include "my_rnd.h"
#define UUID_VERSION 0x40 class UUIDv4: public Type_handler_uuid_new::Fbt
#define UUID_VERSION_MASK 0x0F {
#define UUID_VARIANT 0x80 static constexpr uchar UUID_VERSION() { return 0x40; }
#define UUID_VARIANT_MASK 0x3F static constexpr uchar UUID_VERSION_MASK() { return 0x0F; }
static constexpr uchar UUID_VARIANT() { return 0x80; }
static constexpr uchar UUID_VARIANT_MASK() { return 0x3F; }
/** static void inject_version_and_variant(uchar *to)
Create a global unique identifier version 4 (uuidv4) {
to[6]= ((to[6] & UUID_VERSION_MASK()) | UUID_VERSION());
to[8]= ((to[8] & UUID_VARIANT_MASK()) | UUID_VARIANT());
}
@func my_uuid_v4() // Construct using a my_rnd()-based fallback method
@param to Store uuidv4 here. Must be of size MY_UUID_SIZE (16) static void construct_fallback(char *to)
@return 1 in case of success and 0 in case of failure {
*/ for (uint i= 0; i < 4; i++)
static inline bool my_uuid_v4(uchar *to) int4store(&to[i * 4], (uint32) (my_rnd(&sql_rand)*0xFFFFFFFF));
{ }
if (my_random_bytes(to, 16) != MY_AES_OK)
return 0; 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;
}
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 #endif // SQL_TYPE_UUID_V4_INCLUDED
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment