Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/my_sys.h
Original file line number Diff line number Diff line change
Expand Up @@ -1107,7 +1107,7 @@ int my_msync(int, void *, size_t, int);
void my_uuid_init(ulong seed1, ulong seed2);
void my_uuid(uchar *guid);
void my_uuid_end(void);

int my_uuid_extract_ts(const char *uuid, my_time_t *seconds, ulong *usec);
static inline void my_uuid2str(const uchar *guid, char *s, int with_separators)
{
int i;
Expand Down
64 changes: 64 additions & 0 deletions mysys/my_uuid.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
identifier.
*/

#include <stdbool.h>
#include "mysys_priv.h"
#include <my_rnd.h>
#include <m_string.h>
Expand Down Expand Up @@ -224,3 +225,66 @@ void my_uuid_end()
mysql_mutex_destroy(&LOCK_uuid_generator);
}
}


/**
Extract Unix timestamp from a UUIDv1 or UUIDv7

@param[in] uuid UUID bytes (16 bytes, big-endian)
@param[out] seconds Unix timestamp seconds
@param[out] usec Microseconds part

@return
@retval 0 Success
@retval 1 UUID version doesn't contain timestamp or timestamp invalid

UUIDv1 format (RFC 9562 Section 5.1, big-endian):
Bytes 0-3: time_low (32 bits, low part of timestamp)
Bytes 4-5: time_mid (16 bits, middle part of timestamp)
Bytes 6-7: version (4 bits) + time_hi (12 bits, high part of timestamp)
Timestamp is 100-nanosecond intervals since 1582-10-15

UUIDv7 format (RFC 9562 Section 5.7, big-endian):
Bytes 0-5: Unix timestamp in milliseconds (48 bits)
Bytes 6-7: version (4 bits) + sub-millisecond precision (12 bits)
*/
int my_uuid_extract_ts(const char *uuid, my_time_t *seconds, ulong *usec)
{
char version= uuid[6] >> 4;
ulonglong ts;

switch (version)
{
case 7:
/* UUIDv7: bytes 0-5 are Unix timestamp in milliseconds (big-endian) */
ts= mi_uint6korr(uuid);
*seconds= ts / 1000;
*usec= (ts % 1000) * 1000;
return false;

case 1:
/*
UUIDv1: reconstruct 60-bit timestamp from three fields:
- time_low (bytes 0-3): bits 0-31 of timestamp
- time_mid (bytes 4-5): bits 32-47 of timestamp
- time_hi (bytes 6-7): bits 48-59 of timestamp (masked, 4 bits are version)
Formula: (time_hi << 48) | (time_mid << 32) | time_low
*/
ts= ((ulonglong)(mi_uint2korr(uuid + 6) & 0x0FFF) << 48) |
((ulonglong) mi_uint2korr(uuid + 4) << 32) |
(ulonglong) mi_uint4korr(uuid);

/* Timestamp before Unix epoch (1970-01-01) */
if (ts < UUID_TIME_OFFSET)
return true;

ts= (ts - UUID_TIME_OFFSET) / 10; /* Convert to microseconds */
*seconds= ts / 1000000;
*usec= ts % 1000000;
return false;

default:
/* Other versions (e.g., v4) don't contain timestamps */
return true;
}
}
29 changes: 29 additions & 0 deletions plugin/type_uuid/item_uuidfunc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,32 @@ String *Item_func_sys_guid::val_str(String *str)
my_uuid2str(buf, const_cast<char*>(str->ptr()), 0);
return str;
}


bool Item_func_uuid_timestamp::fix_length_and_dec(THD *thd)
{
Type_std_attributes::set(
Type_temporal_attributes_not_fixed_dec(MAX_DATETIME_WIDTH,
TIME_SECOND_PART_DIGITS, false),
DTCollation_numeric());
set_maybe_null();
return false;
}


bool Item_func_uuid_timestamp::get_timestamp(my_time_t *sec, ulong *usec)
{
Type_handler_uuid_new::Fbt_null uuid(args[0]);
if (uuid.is_null())
return true;
return my_uuid_extract_ts(uuid.to_lex_cstring().str, sec, usec);
}


bool Item_func_uuid_timestamp::val_native(THD *thd, Native *to)
{
my_time_t seconds;
ulong usec;
return (null_value= get_timestamp(&seconds, &usec)) ||
(null_value= Timestamp(seconds, usec).to_native(to, TIME_SECOND_PART_DIGITS));
}
24 changes: 24 additions & 0 deletions plugin/type_uuid/item_uuidfunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@


#include "item.h"
#include "item_timefunc.h"
#include "sql_type_uuid_v1.h"
#include "sql_type_uuid_v4.h"
#include "sql_type_uuid_v7.h"
Expand Down Expand Up @@ -115,4 +116,27 @@ class Item_func_uuid_v7: public Item_func_uuid_vx<UUIDv7>
Item *do_get_copy(THD *thd) const override
{ return get_item_copy<Item_func_uuid_v7>(thd, this); }
};


class Item_func_uuid_timestamp: public Item_timestampfunc
{
bool check_arguments() const override
{
return args[0]->check_type_can_return_str(func_name_cstring());
}
bool get_timestamp(my_time_t *sec, ulong *usec);
public:
Item_func_uuid_timestamp(THD *thd, Item *arg1)
: Item_timestampfunc(thd, arg1) {}

LEX_CSTRING func_name_cstring() const override
{ return {STRING_WITH_LEN("uuid_timestamp")}; }

bool fix_length_and_dec(THD *thd) override;
bool val_native(THD *thd, Native *to) override;

Item *do_get_copy(THD *thd) const override
{ return get_item_copy<Item_func_uuid_timestamp>(thd, this); }
};

#endif // ITEM_UUIDFUNC_INCLUDED
88 changes: 88 additions & 0 deletions plugin/type_uuid/mysql-test/type_uuid/func_uuid_timestamp.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
SET time_zone='+00:00';
#
# UUIDv1 with known timestamps
#
SELECT '4bd352dc-e593-11f0-8de9-0242ac120002' AS uuid,
UUID_TIMESTAMP('4bd352dc-e593-11f0-8de9-0242ac120002') AS ts;
uuid ts
4bd352dc-e593-11f0-8de9-0242ac120002 2025-12-30 15:22:04.357910
SELECT 'bec1046a-e593-11f0-8de9-0242ac120002' AS uuid,
UUID_TIMESTAMP('bec1046a-e593-11f0-8de9-0242ac120002') AS ts;
uuid ts
bec1046a-e593-11f0-8de9-0242ac120002 2025-12-30 15:25:17.175921
SELECT 'c28c09a0-e593-11f0-8de9-0242ac120002' AS uuid,
UUID_TIMESTAMP('c28c09a0-e593-11f0-8de9-0242ac120002') AS ts;
uuid ts
c28c09a0-e593-11f0-8de9-0242ac120002 2025-12-30 15:25:23.539600
#
# UUIDv7 with known timestamps (ms precision)
#
SELECT '019b6fdd-4937-7bb5-95c7-53363c6df927' AS uuid,
UUID_TIMESTAMP('019b6fdd-4937-7bb5-95c7-53363c6df927') AS ts;
uuid ts
019b6fdd-4937-7bb5-95c7-53363c6df927 2025-12-30 15:25:31.831000
SELECT '019b6fdd-5f17-7cf2-b034-248b9c207db6' AS uuid,
UUID_TIMESTAMP('019b6fdd-5f17-7cf2-b034-248b9c207db6') AS ts;
uuid ts
019b6fdd-5f17-7cf2-b034-248b9c207db6 2025-12-30 15:25:37.431000
SELECT '019b6fdd-7327-7a5b-935c-4de00dd0e7c6' AS uuid,
UUID_TIMESTAMP('019b6fdd-7327-7a5b-935c-4de00dd0e7c6') AS ts;
uuid ts
019b6fdd-7327-7a5b-935c-4de00dd0e7c6 2025-12-30 15:25:42.567000
#
# UUIDv4 returns NULL (no timestamp)
#
SELECT UUID_TIMESTAMP(UUID_V4()) IS NULL AS v4_returns_null;
v4_returns_null
1
SELECT UUID_TIMESTAMP('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11') IS NULL AS v4_string_returns_null;
v4_string_returns_null
1
#
# NULL and invalid input
#
SELECT UUID_TIMESTAMP(NULL) IS NULL AS null_input;
null_input
1
SELECT UUID_TIMESTAMP('not-a-valid-uuid');
UUID_TIMESTAMP('not-a-valid-uuid')
NULL
Warnings:
Warning 1292 Incorrect uuid value: 'not-a-valid-uuid'
#
# Native UUID type
#
SELECT '019b6fdd-4937-7bb5-95c7-53363c6df927' AS uuid,
UUID_TIMESTAMP(CAST('019b6fdd-4937-7bb5-95c7-53363c6df927' AS UUID)) AS ts;
uuid ts
019b6fdd-4937-7bb5-95c7-53363c6df927 2025-12-30 15:25:31.831000
#
# Return type is TIMESTAMP(6)
#
CREATE TABLE t1 AS SELECT UUID_TIMESTAMP('019b6fdd-4937-7bb5-95c7-53363c6df927') AS ts;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`ts` timestamp(6) NULL DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci
DROP TABLE t1;
#
# Edge cases
#
SELECT '00000000-0000-1000-8000-000000000000' AS uuid,
UUID_TIMESTAMP('00000000-0000-1000-8000-000000000000') IS NULL AS before_unix_epoch;
uuid before_unix_epoch
00000000-0000-1000-8000-000000000000 1
SELECT '00000000-0000-7000-8000-000000000000' AS uuid,
UUID_TIMESTAMP('00000000-0000-7000-8000-000000000000') AS ts;
uuid ts
00000000-0000-7000-8000-000000000000 0000-00-00 00:00:00.000000
SELECT 'ffffffff-ffff-7fff-8000-000000000000' AS uuid,
UUID_TIMESTAMP('ffffffff-ffff-7fff-8000-000000000000') AS ts;
uuid ts
ffffffff-ffff-7fff-8000-000000000000 2042-12-13 16:54:30.655000
SELECT 'ffffffff-ffff-1fff-8000-000000000000' AS uuid,
UUID_TIMESTAMP('ffffffff-ffff-1fff-8000-000000000000') AS ts;
uuid ts
ffffffff-ffff-1fff-8000-000000000000 2105-11-25 16:30:52.684697
SET time_zone=DEFAULT;
66 changes: 66 additions & 0 deletions plugin/type_uuid/mysql-test/type_uuid/func_uuid_timestamp.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# MDEV-33710: UUID_TIMESTAMP() extracts timestamp from UUIDv1 and UUIDv7

SET time_zone='+00:00';

--echo #
--echo # UUIDv1 with known timestamps
--echo #
SELECT '4bd352dc-e593-11f0-8de9-0242ac120002' AS uuid,
UUID_TIMESTAMP('4bd352dc-e593-11f0-8de9-0242ac120002') AS ts;
SELECT 'bec1046a-e593-11f0-8de9-0242ac120002' AS uuid,
UUID_TIMESTAMP('bec1046a-e593-11f0-8de9-0242ac120002') AS ts;
SELECT 'c28c09a0-e593-11f0-8de9-0242ac120002' AS uuid,
UUID_TIMESTAMP('c28c09a0-e593-11f0-8de9-0242ac120002') AS ts;

--echo #
--echo # UUIDv7 with known timestamps (ms precision)
--echo #
SELECT '019b6fdd-4937-7bb5-95c7-53363c6df927' AS uuid,
UUID_TIMESTAMP('019b6fdd-4937-7bb5-95c7-53363c6df927') AS ts;
SELECT '019b6fdd-5f17-7cf2-b034-248b9c207db6' AS uuid,
UUID_TIMESTAMP('019b6fdd-5f17-7cf2-b034-248b9c207db6') AS ts;
SELECT '019b6fdd-7327-7a5b-935c-4de00dd0e7c6' AS uuid,
UUID_TIMESTAMP('019b6fdd-7327-7a5b-935c-4de00dd0e7c6') AS ts;

--echo #
--echo # UUIDv4 returns NULL (no timestamp)
--echo #
SELECT UUID_TIMESTAMP(UUID_V4()) IS NULL AS v4_returns_null;
SELECT UUID_TIMESTAMP('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11') IS NULL AS v4_string_returns_null;

--echo #
--echo # NULL and invalid input
--echo #
SELECT UUID_TIMESTAMP(NULL) IS NULL AS null_input;
SELECT UUID_TIMESTAMP('not-a-valid-uuid');

--echo #
--echo # Native UUID type
--echo #
SELECT '019b6fdd-4937-7bb5-95c7-53363c6df927' AS uuid,
UUID_TIMESTAMP(CAST('019b6fdd-4937-7bb5-95c7-53363c6df927' AS UUID)) AS ts;

--echo #
--echo # Return type is TIMESTAMP(6)
--echo #
CREATE TABLE t1 AS SELECT UUID_TIMESTAMP('019b6fdd-4937-7bb5-95c7-53363c6df927') AS ts;
SHOW CREATE TABLE t1;
DROP TABLE t1;

--echo #
--echo # Edge cases
--echo #
# UUIDv1 before Unix epoch
SELECT '00000000-0000-1000-8000-000000000000' AS uuid,
UUID_TIMESTAMP('00000000-0000-1000-8000-000000000000') IS NULL AS before_unix_epoch;
# UUIDv7 at Unix epoch (ts=0)
SELECT '00000000-0000-7000-8000-000000000000' AS uuid,
UUID_TIMESTAMP('00000000-0000-7000-8000-000000000000') AS ts;
# UUIDv7 max 48-bit timestamp
SELECT 'ffffffff-ffff-7fff-8000-000000000000' AS uuid,
UUID_TIMESTAMP('ffffffff-ffff-7fff-8000-000000000000') AS ts;
# UUIDv1 max timestamp
SELECT 'ffffffff-ffff-1fff-8000-000000000000' AS uuid,
UUID_TIMESTAMP('ffffffff-ffff-1fff-8000-000000000000') AS ts;

SET time_zone=DEFAULT;
34 changes: 33 additions & 1 deletion plugin/type_uuid/plugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -180,16 +180,33 @@ class Create_func_uuid_v7 : public Create_func_arg0
virtual ~Create_func_uuid_v7() {}
};

class Create_func_uuid_timestamp : public Create_func_arg1
{
public:
Item *create_1_arg(THD *thd, Item *arg1) override
{
DBUG_ENTER("Create_func_uuid_timestamp::create_1_arg");
DBUG_RETURN(new (thd->mem_root) Item_func_uuid_timestamp(thd, arg1));
}
static Create_func_uuid_timestamp s_singleton;

protected:
Create_func_uuid_timestamp() {}
virtual ~Create_func_uuid_timestamp() {}
};

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_timestamp Create_func_uuid_timestamp::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_timestamp(&Create_func_uuid_timestamp::s_singleton);

static constexpr Name type_name={STRING_WITH_LEN("uuid")};

Expand Down Expand Up @@ -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_timestamp, // pointer to type-specific plugin descriptor
"uuid_timestamp", // plugin name
"Varun Deep Saini", // plugin author
"Function UUID_TIMESTAMP()", // 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;