summaryrefslogtreecommitdiffstats
path: root/plugin/type_uuid/sql_type_uuid.h
diff options
context:
space:
mode:
Diffstat (limited to 'plugin/type_uuid/sql_type_uuid.h')
-rw-r--r--plugin/type_uuid/sql_type_uuid.h330
1 files changed, 330 insertions, 0 deletions
diff --git a/plugin/type_uuid/sql_type_uuid.h b/plugin/type_uuid/sql_type_uuid.h
new file mode 100644
index 00000000..67d7471d
--- /dev/null
+++ b/plugin/type_uuid/sql_type_uuid.h
@@ -0,0 +1,330 @@
+#ifndef SQL_TYPE_UUID_INCLUDED
+#define SQL_TYPE_UUID_INCLUDED
+
+/* Copyright (c) 2019,2021 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_fixedbin_storage.h"
+
+template <bool force_swap>
+class UUID: public FixedBinTypeStorage<MY_UUID_SIZE, MY_UUID_STRING_LENGTH>
+{
+ bool get_digit(char ch, uint *val)
+ {
+ if (ch >= '0' && ch <= '9')
+ {
+ *val= (uint) ch - '0';
+ return false;
+ }
+ if (ch >= 'a' && ch <= 'f')
+ {
+ *val= (uint) ch - 'a' + 0x0a;
+ return false;
+ }
+ if (ch >= 'A' && ch <= 'F')
+ {
+ *val= (uint) ch - 'A' + 0x0a;
+ return false;
+ }
+ return true;
+ }
+
+ bool get_digit(uint *val, const char *str, const char *end)
+ {
+ if (str >= end)
+ return true;
+ return get_digit(*str, val);
+ }
+
+ size_t skip_hyphens(const char *str, const char *end)
+ {
+ const char *str0= str;
+ for ( ; str < end; str++)
+ {
+ if (str[0] != '-')
+ break;
+ }
+ return str - str0;
+ }
+
+ const char *get_two_digits(char *val, const char *str, const char *end)
+ {
+ uint hi, lo;
+ if (get_digit(&hi, str++, end))
+ return NULL;
+ str+= skip_hyphens(str, end);
+ if (get_digit(&lo, str++, end))
+ return NULL;
+ *val= (char) ((hi << 4) + lo);
+ return str;
+ }
+
+public:
+ using FixedBinTypeStorage::FixedBinTypeStorage;
+ bool ascii_to_fbt(const char *str, size_t str_length)
+ {
+ const char *end= str + str_length;
+ /*
+ The format understood:
+ - Hyphen is not allowed on the first and the last position.
+ - Otherwise, hyphens are allowed on any (odd and even) position,
+ with any amount.
+ */
+ if (str_length < 32)
+ goto err;
+
+ for (uint oidx= 0; oidx < binary_length(); oidx++)
+ {
+ if (!(str= get_two_digits(&m_buffer[oidx], str, end)))
+ goto err;
+ // Allow hypheps after two digits, but not after the last digit
+ if (oidx + 1 < binary_length())
+ str+= skip_hyphens(str, end);
+ }
+ if (str < end)
+ goto err; // Some input left
+ if (m_buffer[6] & -m_buffer[8] & 0x80)
+ goto err; // impossible combination: version >= 8, variant = 0
+ return false;
+ err:
+ bzero(m_buffer, sizeof(m_buffer));
+ return true;
+ }
+
+ size_t to_string(char *dst, size_t dstsize) const
+ {
+ my_uuid2str((const uchar *) m_buffer, dst, 1);
+ return MY_UUID_STRING_LENGTH;
+ }
+
+ static const Name &default_value()
+ {
+ static Name def(STRING_WITH_LEN("00000000-0000-0000-0000-000000000000"));
+ return def;
+ }
+
+ /*
+ Binary (in-memory) UUIDv1 representation:
+
+ llllllll-mmmm-Vhhh-vsss-nnnnnnnnnnnn
+
+ Binary sortable (in-record) representation:
+
+ nnnnnnnnnnnn-vsss-Vhhh-mmmm-llllllll
+
+ Sign Section Bits Bytes Pos PosBinSortable
+ ------------- ------- ---- ----- --- --------------
+ llllllll time low 32 4 0 12
+ mmmm time mid 16 2 4 10
+ Vhhh version and time hi 16 2 6 8
+ vsss variant and clock seq 16 2 8 6
+ nnnnnnnnnnnn node ID 48 6 10 0
+ */
+
+ class Segment
+ {
+ size_t m_memory_pos;
+ size_t m_record_pos;
+ size_t m_length;
+ public:
+ constexpr Segment(size_t memory_pos, size_t record_pos, size_t length)
+ :m_memory_pos(memory_pos), m_record_pos(record_pos), m_length(length)
+ { }
+ void mem2rec(char *to, const char *from) const
+ {
+ memcpy(to + m_record_pos, from + m_memory_pos, m_length);
+ }
+ void rec2mem(char *to, const char * from) const
+ {
+ memcpy(to + m_memory_pos, from + m_record_pos, m_length);
+ }
+ int cmp_memory(const char *a, const char *b) const
+ {
+ return memcmp(a + m_memory_pos, b + m_memory_pos, m_length);
+ }
+ void hash_record(const uchar *ptr, Hasher *hasher) const
+ {
+ hasher->add(&my_charset_bin, ptr + m_record_pos, m_length);
+ }
+ };
+
+ static const Segment & segment(uint i)
+ {
+ static Segment segments[]=
+ {
+ {0, 12, 4}, // llllllll
+ {4, 10, 2}, // mmmm
+ {6, 8, 2}, // Vhhh
+ {8, 6, 2}, // vsss
+ {10, 0, 6} // nnnnnnnnnnnn
+ };
+ return segments[i];
+ }
+
+ // version > 0 && version < 6 && variant != 0
+ static bool mem_need_swap(const char *s)
+ { return s[6] > 0 && s[6] < 0x60 && s[8] & 0x80; }
+
+ // s[6] & 0x80 && s[8] > 0: this means a swapped uuid
+ static bool rec_need_swap(const char *s)
+ { return s[6] & -s[8] & 0x80; }
+
+ // Convert the in-memory representation to the in-record representation
+ static void memory_to_record(char *to, const char *from)
+ {
+ if (force_swap || mem_need_swap(from))
+ {
+ segment(0).mem2rec(to, from);
+ segment(1).mem2rec(to, from);
+ segment(2).mem2rec(to, from);
+ segment(3).mem2rec(to, from);
+ segment(4).mem2rec(to, from);
+ }
+ else
+ memcpy(to, from, binary_length());
+ }
+
+ // Convert the in-record representation to the in-memory representation
+ static void record_to_memory(char *to, const char *from)
+ {
+ if (force_swap || rec_need_swap(from))
+ {
+ segment(0).rec2mem(to, from);
+ segment(1).rec2mem(to, from);
+ segment(2).rec2mem(to, from);
+ segment(3).rec2mem(to, from);
+ segment(4).rec2mem(to, from);
+ }
+ else
+ memcpy(to, from, binary_length());
+ }
+
+ /*
+ Calculate a hash of the in-record representation.
+ Used in Field_uuid::hash(), e.g. for KEY partitioning. This
+ makes partition distribution for UUID and BINARY(16) equal,
+ so for example:
+
+ CREATE OR REPLACE TABLE t1 (c1 UUID) PARTITION BY KEY(c1) PARTITIONS 5;
+ INSERT INTO t1 (c1) VALUES (UUID());
+
+ and
+
+ CREATE OR REPLACE TABLE t1 (c1 BINARY(16)) PARTITION BY KEY(c1) PARTITIONS 5;
+ INSERT INTO t1 (c1) VALUES (UUID());
+
+ put values into the same partition.
+ */
+ static void hash_record(const uchar *ptr, Hasher *hasher)
+ {
+ segment(0).hash_record(ptr, hasher);
+ segment(1).hash_record(ptr, hasher);
+ segment(2).hash_record(ptr, hasher);
+ segment(3).hash_record(ptr, hasher);
+ segment(4).hash_record(ptr, hasher);
+ }
+
+ // Compare two in-memory values
+ static int cmp(const LEX_CSTRING &a, const LEX_CSTRING &b)
+ {
+ DBUG_ASSERT(a.length == binary_length());
+ DBUG_ASSERT(b.length == binary_length());
+ bool swap_a= force_swap || mem_need_swap(a.str);
+ bool swap_b= force_swap || mem_need_swap(b.str);
+ if (swap_a && swap_b)
+ {
+ int res;
+ if ((res= segment(4).cmp_memory(a.str, b.str)) ||
+ (res= segment(3).cmp_memory(a.str, b.str)) ||
+ (res= segment(2).cmp_memory(a.str, b.str)) ||
+ (res= segment(1).cmp_memory(a.str, b.str)) ||
+ (res= segment(0).cmp_memory(a.str, b.str)))
+ return res;
+ return 0;
+ }
+ return memcmp(a.str, b.str, binary_length());
+ }
+
+ static ulong KEY_pack_flags(uint column_nr)
+ {
+ return HA_PACK_KEY;
+ }
+
+ /*
+ Convert in-record representation to binlog representation.
+ We tranfer UUID values in binlog by compressing in-memory representation.
+ This makes replication between UUID and BINARY(16) simpler:
+
+ Transferring by compressing the in-record representation would require
+ extending the binary log format to put the extact data type name into
+ the column metadata.
+ */
+ static uchar *pack(uchar *to, const uchar *from, uint max_length)
+ {
+ uchar buf[binary_length()];
+ record_to_memory((char *) buf, (const char *) from);
+ return StringPack(&my_charset_bin, binary_length()).
+ pack(to, buf, max_length);
+ }
+
+ // Convert binlog representation to in-record representation
+ static const uchar *unpack(uchar *to,
+ const uchar *from, const uchar *from_end,
+ uint param_data)
+ {
+ uchar buf[binary_length()];
+ const uchar *rc= StringPack(&my_charset_bin, binary_length()).
+ unpack(buf, from, from_end, param_data);
+ memory_to_record((char *) to, (const char *) buf);
+ return rc;
+ }
+
+};
+
+class Type_collection_uuid: public Type_collection
+{
+ const Type_handler *find_in_array(const Type_handler *what,
+ const Type_handler *stop,
+ bool for_comparison) const;
+public:
+ const Type_handler *aggregate_for_result(const Type_handler *a,
+ const Type_handler *b)
+ const override
+ { return find_in_array(a, b, false); }
+ const Type_handler *aggregate_for_min_max(const Type_handler *a,
+ const Type_handler *b)
+ const override
+ { return find_in_array(a, b, false); }
+ const Type_handler *aggregate_for_comparison(const Type_handler *a,
+ const Type_handler *b)
+ const override
+ { return find_in_array(a, b, true); }
+ const Type_handler *aggregate_for_num_op(const Type_handler *a,
+ const Type_handler *b)
+ const override
+ { return NULL; }
+
+ static Type_collection_uuid *singleton()
+ {
+ static Type_collection_uuid tc;
+ return &tc;
+ }
+};
+
+#include "sql_type_fixedbin.h"
+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;
+
+#endif // SQL_TYPE_UUID_INCLUDED