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:
parent
24d99bfd78
commit
fdac4b8152
@ -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" />
|
||||
|
@ -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>
|
||||
|
@ -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
116
src/common/classes/Uuid.h
Normal 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
|
@ -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")
|
||||
|
@ -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;
|
||||
|
@ -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},
|
||||
|
Loading…
Reference in New Issue
Block a user