8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 14:03:03 +01:00

Feature #7980 - Option for GEN_UUID to generate v7 UUID.

This commit is contained in:
Adriano dos Santos Fernandes 2024-06-22 14:16:01 -03:00 committed by Adriano dos Santos Fernandes
parent 24d99bfd78
commit fdac4b8152
7 changed files with 160 additions and 8 deletions

View File

@ -161,6 +161,7 @@
<ClInclude Include="..\..\..\src\common\classes\tree.h" />
<ClInclude Include="..\..\..\src\common\classes\TriState.h" />
<ClInclude Include="..\..\..\src\common\classes\UserBlob.h" />
<ClInclude Include="..\..\..\src\common\classes\Uuid.h" />
<ClInclude Include="..\..\..\src\common\classes\VaryStr.h" />
<ClInclude Include="..\..\..\src\common\classes\vector.h" />
<ClInclude Include="..\..\..\src\common\classes\zip.h" />

View File

@ -491,6 +491,9 @@
<ClInclude Include="..\..\..\src\common\classes\UserBlob.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\common\classes\Uuid.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\common\classes\VaryStr.h">
<Filter>headers</Filter>
</ClInclude>

View File

@ -550,17 +550,20 @@ Function:
Returns an universal unique number in CHAR(16) OCTETS type.
Format:
GEN_UUID()
GEN_UUID([<version>])
Important:
Before Firebird 2.5.2, GEN_UUID was returning completely random strings. This is not compliant
with the RFC-4122 (UUID specification).
This was fixed in Firebird 2.5.2 and 3.0. Now GEN_UUID returns a compliant UUID version 4
string, where some bits are reserved and the others are random. The string format of a compliant
UUID is XXXXXXXX-XXXX-4XXX-YXXX-XXXXXXXXXXXX, where 4 is fixed (version) and Y is 8, 9, A or B.
This was fixed in Firebird 2.5.2 and 3.0. Now GEN_UUID returns a compliant UUID accordingly to the specified
version (4 or 7, with default being 4) string, where some bits are reserved and the others are random.
The string format of a compliant UUID v4/v7 is XXXXXXXX-XXXX-YXXX-ZXXX-XXXXXXXXXXXX, where Y is the version (4 or 7)
and Z is 8, 9, A or B.
Before Firebird 6, this function does not accept the version argument.
Example:
insert into records (id) value (gen_uuid());
insert into records (id) value (gen_uuid(7));
See also: CHAR_TO_UUID and UUID_TO_CHAR

116
src/common/classes/Uuid.h Normal file
View File

@ -0,0 +1,116 @@
/*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights
* and limitations under the License.
*
* The Original Code was created by Adriano dos Santos Fernandes
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2024 Adriano dos Santos Fernandes <adrianosf@uol.com.br>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#ifndef CLASSES_UUID_H
#define CLASSES_UUID_H
#include "firebird.h"
#include "../common/gdsassert.h"
#include "../common/os/guid.h"
#include <algorithm>
#include <array>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <random>
namespace Firebird
{
class Uuid
{
private:
explicit Uuid(unsigned version)
{
switch (version)
{
case 7:
generateV7();
break;
default:
fb_assert(false);
}
}
public:
static Uuid generate(unsigned version)
{
return Uuid(version);
}
public:
std::size_t extractBytes(std::uint8_t* buffer, std::size_t bufferSize) const
{
fb_assert(bufferSize >= bytes.size());
std::copy(bytes.begin(), bytes.end(), buffer);
return bytes.size();
}
std::size_t toString(char* buffer, std::size_t bufferSize) const
{
fb_assert(bufferSize >= STR_LEN);
return snprintf(buffer, bufferSize, STR_FORMAT,
bytes[0], bytes[1], bytes[2], bytes[3],
bytes[4], bytes[5],
bytes[6], bytes[7],
bytes[8], bytes[9],
bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]);
}
private:
void generateV7()
{
GenerateRandomBytes(bytes.data() + 6, bytes.size() - 6);
// current timestamp in ms
const auto now = std::chrono::system_clock::now();
const auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
// timestamp
bytes[0] = (millis >> 40) & 0xFF;
bytes[1] = (millis >> 32) & 0xFF;
bytes[2] = (millis >> 24) & 0xFF;
bytes[3] = (millis >> 16) & 0xFF;
bytes[4] = (millis >> 8) & 0xFF;
bytes[5] = millis & 0xFF;
// version and variant
bytes[6] = (bytes[6] & 0x0F) | 0x70;
bytes[8] = (bytes[8] & 0x3F) | 0x80;
}
public:
static constexpr std::size_t BYTE_LEN = 16;
static constexpr std::size_t STR_LEN = 36;
static constexpr const char* STR_FORMAT =
"%02hhX%02hhX%02hhX%02hhX-%02hhX%02hhX-%02hhX%02hhX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX";
private:
std::array<uint8_t, BYTE_LEN> bytes;
};
} // namespace Firebird
#endif // CLASSES_UUID_H

View File

@ -986,3 +986,4 @@ FB_IMPL_MSG(JRD, 983, pattern_cant_be_used_without_other_pattern_and_vice_versa,
FB_IMPL_MSG(JRD, 984, incompatible_format_patterns, -901, "HY", "000", "@1 incompatible with @2")
FB_IMPL_MSG(JRD, 985, only_one_pattern_can_be_used, -901, "HY", "000", "Can use only one of these patterns @1")
FB_IMPL_MSG(JRD, 986, can_not_use_same_pattern_twice, -901, "HY", "000", "Cannot use the same pattern twice: @1")
FB_IMPL_MSG(JRD, 987, sysf_invalid_gen_uuid_version, -833, "42", "000", "Invalid GEN_UUID version (@1). Must be 4 or 7")

View File

@ -5729,6 +5729,7 @@ const
isc_incompatible_format_patterns = 335545304;
isc_only_one_pattern_can_be_used = 335545305;
isc_can_not_use_same_pattern_twice = 335545306;
isc_sysf_invalid_gen_uuid_version = 335545307;
isc_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930;
isc_gfix_incmp_sw = 335740932;

View File

@ -33,6 +33,7 @@
#include "../common/TimeZoneUtil.h"
#include "../common/classes/VaryStr.h"
#include "../common/classes/Hash.h"
#include "../common/classes/Uuid.h"
#include "../jrd/SysFunction.h"
#include "../jrd/DataTypeUtil.h"
#include "../include/fb_blk.h"
@ -1920,7 +1921,7 @@ void makeUnicodeChar(DataTypeUtilBase*, const SysFunction* function, dsc* result
void makeUuid(DataTypeUtilBase*, const SysFunction* function, dsc* result,
int argsCount, const dsc** args)
{
fb_assert(argsCount == function->minArgCount);
fb_assert(argsCount >= function->minArgCount);
if (argsCount > 0 && args[0]->isNull())
result->makeNullString();
@ -4519,12 +4520,38 @@ dsc* evlFloor(thread_db* tdbb, const SysFunction*, const NestValueArray& args,
dsc* evlGenUuid(thread_db* tdbb, const SysFunction*, const NestValueArray& args,
impure_value* impure)
{
fb_assert(args.isEmpty());
const auto request = tdbb->getRequest();
fb_assert(args.getCount() <= 1);
// Generate UUID and convert it into platform-independent format
UCHAR data[Guid::SIZE];
SLONG version = 4;
Guid::generate().convert(data);
if (args.getCount() > 0)
{
const auto* const versionDsc = EVL_expr(tdbb, request, args[0]);
if (request->req_flags & req_null)
return nullptr;
version = MOV_get_long(tdbb, versionDsc, 0);
}
switch (version)
{
case 4:
Guid::generate().convert(data);
break;
case 7:
Uuid::generate(version).extractBytes(data, sizeof(data));
break;
default:
status_exception::raise(Arg::Gds(isc_sysf_invalid_gen_uuid_version) << Arg::Num(version));
break;
}
dsc result;
result.makeText(Guid::SIZE, ttype_binary, data);
@ -6885,7 +6912,7 @@ const SysFunction SysFunction::functions[] =
{"EXP", 1, 1, setParamsDblDec, makeDblDecResult, evlExp, NULL},
{"FIRST_DAY", 2, 2, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay},
{"FLOOR", 1, 1, setParamsDblDec, makeCeilFloor, evlFloor, NULL},
{"GEN_UUID", 0, 0, NULL, makeUuid, evlGenUuid, NULL},
{"GEN_UUID", 0, 1, NULL, makeUuid, evlGenUuid, NULL},
{"HASH", 1, 2, setParamsHash, makeHash, evlHash, NULL},
{"HEX_DECODE", 1, 1, NULL, makeDecodeHex, evlDecodeHex, NULL},
{"HEX_ENCODE", 1, 1, NULL, makeEncodeHex, evlEncodeHex, NULL},