Поддержка Javascript в MySQL: пример UUID

Возможно, вы заметили, что MySQL теперь поддерживает создание функций (и процедур хранения) в Javascript с использованием GraalVM.
Эта новая функциональность доступна только в MySQL Enterprise и MySQL HeatWave.
Как разработчик, вы также можете получить бесплатный доступ к MySQL Enterprise из Oracle Technology Network (OTN): Загрузка MySQL Enterprise.
Зачем использовать JS-функцию?
Как вы, возможно, знаете, UUID становятся все более популярными, и их использование в MySQL ограничено UUID V1.
Итак, мы знаем, что можно извлечь временную метку из UUIDv1. Есть компонент MySQL (C ++), который предоставляет 3 UDF (определяемые пользователем функции) для этой цели. Компонент доступен на GitHub: mysql-component-uuid_v1.
При использовании MySQL DBaaS невозможно установить такие компоненты, и то же самое происходит с MySQL HeatWave. Следовательно, возможность использования функций JavaScript становится особенно выгодной в этом сценарии.
Функции для обработки UUIDv1
Затем мы создадим функции для анализа UUID MySQL и отображения метки времени.
Давайте начнем с извлечения временной метки как времени Unix в миллисекундах:
USE test;
DROP function IF EXISTS js_uuid_to_unixtime;
CREATE FUNCTION js_uuid_to_unixtime (uuid_in CHAR(36))
RETURNS CHAR(23) LANGUAGE JAVASCRIPT AS $$
const UUID_T_LENGTH = 16;
const UNIX_TS_LENGTH = 6;
function uuidToUnixTs(uuid_str) {
const MS_FROM_100NS_FACTOR = 10000;
const OFFSET_FROM_15_10_1582_TO_EPOCH = 122192928000000000n;
// Разделите UUID символом "-" в качестве разделителя
let uuid_parts = uuid_str.split('-');
// Создайте временную метку UUID из ее частей
let uuid_timestamp =
uuid_parts[2].substring(1) +
uuid_parts[1] +
uuid_parts[0];
// Преобразовать строку шестнадцатеричной метки времени в целое число
let timestamp = BigInt('0x' + uuid_timestamp);
// Вычислить временную метку Unix в миллисекундах
let unixTimestampMs = Number((timestamp - OFFSET_FROM_15_10_1582_TO_EPOCH) / BigInt(MS_FROM_100NS_FACTOR));
return unixTimestampMs;
}
function stringToUuid(str) {
if (str.length !== 36) {
return 1;
}
if (str[14] !== '1') {
return 1;
}
return 0;
}
let result = stringToUuid(uuid_in);
let timestamp_out;
if (result === 0) {
timestamp_out = uuidToUnixTs(uuid_in)/1000;
} else {
timestamp_out="Error parsing UUID";
}
return (timestamp_out);
$$
;
Давайте протестируем это:
MySQL > select now(), from_unixtime(js_uuid_to_unixtime(uuid())); +---------------------+--------------------------------------------+ | now() | from_unixtime(js_uuid_to_unixtime(uuid())) | +---------------------+--------------------------------------------+ | 2024-03-20 22:24:13 | 2024-03-20 22:24:13.503000 | +---------------------+--------------------------------------------+ 1 row in set (0.0009 sec)
Мы создали другие функции Javascript, предлагающие другой результат:
- js_uuid_to_datetime()
- js_uuid_to_datetime_long()
Давайте посмотрим на них в действии:
MySQL > select js_uuid_to_datetime(uuid()); +-----------------------------+ | js_uuid_to_datetime(uuid()) | +-----------------------------+ | 2024-03-20 22:31:20.824 | +-----------------------------+ 1 row in set (0.0009 sec) MySQL > select js_uuid_to_datetime_long(uuid()); +----------------------------------------------+ | js_uuid_to_datetime_long(uuid()) | +----------------------------------------------+ | Wednesday, March 20, 2024 at 10:31:30 PM GMT | +----------------------------------------------+ 1 row in set (0.0012 sec)
UUIDv7
Все больше и больше людей используют UUIDv7. К сожалению, они недоступны в MySQL. UUIDv7 являются последовательными и не используют Mac-адрес.
Мы также можем создавать функции Javascript для генерации и обработки UUIDv7 в MySQL HeatWave.
Функции доступны здесь.
Давайте протестируем UUIDv7 в качестве первичного ключа для таблицы:
MySQL > CREATE TABLE `item` (
`id` varbinary(16) NOT NULL,
`name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
MySQL > insert into item values (uuid_to_bin(js_uuidv7()),'item01'),
(uuid_to_bin(js_uuidv7()),'item02'),
(uuid_to_bin(js_uuidv7()),'item03');
Query OK, 3 rows affected (0.0060 sec)
MySQL > select * from item;
+------------------------------------+--------+
| id | name |
+------------------------------------+--------+
| 0x018E5E04BA3376AED34A5B5EB51720A5 | item01 |
| 0x018E5E04BA34732799E01BED0C1A06F9 | item03 |
| 0x018E5E04BA34780F6A683BA37F00CA27 | item02 |
+------------------------------------+--------+
3 rows in set (0.0007 sec)
MySQL > select bin_to_uuid(id) uuid, name from item;
+--------------------------------------+--------+
| uuid | name |
+--------------------------------------+--------+
| 018e5e04-ba33-76ae-d34a-5b5eb51720a5 | item01 |
| 018e5e04-ba34-7327-99e0-1bed0c1a06f9 | item03 |
| 018e5e04-ba34-780f-6a68-3ba37f00ca27 | item02 |
+--------------------------------------+--------+
3 rows in set (0.0008 sec)
Мы также можем получить время создания каждой записи:
MySQL > insert into item values (uuid_to_bin(js_uuidv7()),'item04');
Query OK, 1 row affected (0.0035 sec)
MySQL > select js_uuidv7_to_datetime(bin_to_uuid(id)) insert_date,
name from item;
+-------------------------+--------+
| insert_date | name |
+-------------------------+--------+
| 2024-03-20 22:39:11.923 | item01 |
| 2024-03-20 22:39:11.924 | item03 |
| 2024-03-20 22:39:11.924 | item02 |
| 2024-03-20 22:41:54.567 | item04 |
+-------------------------+--------+
4 rows in set (0.0041 sec)
Для повышения производительности лучше хранить UUID с использованием uuid_to_bin() как BINARY(16). Конечно, если мы планируем использовать и вторичные индексы.
Но, возможно, вы предпочитаете напрямую видеть представление (значение) UUID при запросе таблицы.
Затем мы можем изменить нашу таблицу следующим образом:
MySQL > alter table item add column uuid char(36)
generated always as (bin_to_uuid(id)) virtual after id,
alter column id set invisible;
MySQL > insert into item (id, name)
values (uuid_to_bin(js_uuidv7()),'item05');
MySQL > select * from item;
+--------------------------------------+--------+
| uuid | name |
+--------------------------------------+--------+
| 018e5e04-ba33-76ae-d34a-5b5eb51720a5 | item01 |
| 018e5e04-ba34-7327-99e0-1bed0c1a06f9 | item03 |
| 018e5e04-ba34-780f-6a68-3ba37f00ca27 | item02 |
| 018e5e07-3587-73af-6535-f805bee0cc04 | item04 |
| 018e5e15-12bf-7590-a010-7a5b5457a6c3 | item05 |
+--------------------------------------+--------+
5 rows in set (0.0006 sec)
MySQL > select *, js_uuidv7_to_datetime(uuid) inserted_at from item;
+--------------------------------------+--------+-------------------------+
| uuid | name | inserted_at |
+--------------------------------------+--------+-------------------------+
| 018e5e04-ba33-76ae-d34a-5b5eb51720a5 | item01 | 2024-03-20 22:39:11.923 |
| 018e5e04-ba34-7327-99e0-1bed0c1a06f9 | item03 | 2024-03-20 22:39:11.924 |
| 018e5e04-ba34-780f-6a68-3ba37f00ca27 | item02 | 2024-03-20 22:39:11.924 |
| 018e5e07-3587-73af-6535-f805bee0cc04 | item04 | 2024-03-20 22:41:54.567 |
| 018e5e15-12bf-7590-a010-7a5b5457a6c3 | item05 | 2024-03-20 22:57:03.167 |
+--------------------------------------+--------+-------------------------+
5 rows in set (0.0014 sec)
Заключение
Возможность писать программы на Javascript непосредственно в MySQL является приятным дополнением, особенно при запуске MySQL HeatWave в облаке, где невозможно установить дополнительные компоненты или плагины.
Для тех, кто владеет такими пользовательскими функциями (UDF), теперь вы можете переписать их в Javascript и использовать там, где MySQL поддерживает GraalVM: MySQL Enterprise и MySQL Heatwave.
Наслаждайтесь написанием функций Javascript в MySQL!
Редактор: AndreyEx