Skip to content

Commit fdac4b8

Browse files
committed
Feature #7980 - Option for GEN_UUID to generate v7 UUID.
1 parent 24d99bf commit fdac4b8

File tree

7 files changed

+160
-8
lines changed

7 files changed

+160
-8
lines changed

builds/win32/msvc15/common.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
<ClInclude Include="..\..\..\src\common\classes\tree.h" />
162162
<ClInclude Include="..\..\..\src\common\classes\TriState.h" />
163163
<ClInclude Include="..\..\..\src\common\classes\UserBlob.h" />
164+
<ClInclude Include="..\..\..\src\common\classes\Uuid.h" />
164165
<ClInclude Include="..\..\..\src\common\classes\VaryStr.h" />
165166
<ClInclude Include="..\..\..\src\common\classes\vector.h" />
166167
<ClInclude Include="..\..\..\src\common\classes\zip.h" />

builds/win32/msvc15/common.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,9 @@
491491
<ClInclude Include="..\..\..\src\common\classes\UserBlob.h">
492492
<Filter>headers</Filter>
493493
</ClInclude>
494+
<ClInclude Include="..\..\..\src\common\classes\Uuid.h">
495+
<Filter>headers</Filter>
496+
</ClInclude>
494497
<ClInclude Include="..\..\..\src\common\classes\VaryStr.h">
495498
<Filter>headers</Filter>
496499
</ClInclude>

doc/sql.extensions/README.builtin_functions.txt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -550,17 +550,20 @@ Function:
550550
Returns an universal unique number in CHAR(16) OCTETS type.
551551

552552
Format:
553-
GEN_UUID()
553+
GEN_UUID([<version>])
554554

555555
Important:
556556
Before Firebird 2.5.2, GEN_UUID was returning completely random strings. This is not compliant
557557
with the RFC-4122 (UUID specification).
558-
This was fixed in Firebird 2.5.2 and 3.0. Now GEN_UUID returns a compliant UUID version 4
559-
string, where some bits are reserved and the others are random. The string format of a compliant
560-
UUID is XXXXXXXX-XXXX-4XXX-YXXX-XXXXXXXXXXXX, where 4 is fixed (version) and Y is 8, 9, A or B.
558+
This was fixed in Firebird 2.5.2 and 3.0. Now GEN_UUID returns a compliant UUID accordingly to the specified
559+
version (4 or 7, with default being 4) string, where some bits are reserved and the others are random.
560+
The string format of a compliant UUID v4/v7 is XXXXXXXX-XXXX-YXXX-ZXXX-XXXXXXXXXXXX, where Y is the version (4 or 7)
561+
and Z is 8, 9, A or B.
562+
Before Firebird 6, this function does not accept the version argument.
561563

562564
Example:
563565
insert into records (id) value (gen_uuid());
566+
insert into records (id) value (gen_uuid(7));
564567

565568
See also: CHAR_TO_UUID and UUID_TO_CHAR
566569

src/common/classes/Uuid.h

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* The contents of this file are subject to the Initial
3+
* Developer's Public License Version 1.0 (the "License");
4+
* you may not use this file except in compliance with the
5+
* License. You may obtain a copy of the License at
6+
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
7+
*
8+
* Software distributed under the License is distributed AS IS,
9+
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing rights
11+
* and limitations under the License.
12+
*
13+
* The Original Code was created by Adriano dos Santos Fernandes
14+
* for the Firebird Open Source RDBMS project.
15+
*
16+
* Copyright (c) 2024 Adriano dos Santos Fernandes <adrianosf@uol.com.br>
17+
* and all contributors signed below.
18+
*
19+
* All Rights Reserved.
20+
* Contributor(s): ______________________________________.
21+
*/
22+
23+
#ifndef CLASSES_UUID_H
24+
#define CLASSES_UUID_H
25+
26+
#include "firebird.h"
27+
#include "../common/gdsassert.h"
28+
#include "../common/os/guid.h"
29+
#include <algorithm>
30+
#include <array>
31+
#include <chrono>
32+
#include <cstddef>
33+
#include <cstdint>
34+
#include <cstdio>
35+
#include <random>
36+
37+
namespace Firebird
38+
{
39+
40+
class Uuid
41+
{
42+
private:
43+
explicit Uuid(unsigned version)
44+
{
45+
switch (version)
46+
{
47+
case 7:
48+
generateV7();
49+
break;
50+
51+
default:
52+
fb_assert(false);
53+
}
54+
}
55+
56+
public:
57+
static Uuid generate(unsigned version)
58+
{
59+
return Uuid(version);
60+
}
61+
62+
public:
63+
std::size_t extractBytes(std::uint8_t* buffer, std::size_t bufferSize) const
64+
{
65+
fb_assert(bufferSize >= bytes.size());
66+
std::copy(bytes.begin(), bytes.end(), buffer);
67+
return bytes.size();
68+
}
69+
70+
std::size_t toString(char* buffer, std::size_t bufferSize) const
71+
{
72+
fb_assert(bufferSize >= STR_LEN);
73+
74+
return snprintf(buffer, bufferSize, STR_FORMAT,
75+
bytes[0], bytes[1], bytes[2], bytes[3],
76+
bytes[4], bytes[5],
77+
bytes[6], bytes[7],
78+
bytes[8], bytes[9],
79+
bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]);
80+
}
81+
82+
private:
83+
void generateV7()
84+
{
85+
GenerateRandomBytes(bytes.data() + 6, bytes.size() - 6);
86+
87+
// current timestamp in ms
88+
const auto now = std::chrono::system_clock::now();
89+
const auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
90+
91+
// timestamp
92+
bytes[0] = (millis >> 40) & 0xFF;
93+
bytes[1] = (millis >> 32) & 0xFF;
94+
bytes[2] = (millis >> 24) & 0xFF;
95+
bytes[3] = (millis >> 16) & 0xFF;
96+
bytes[4] = (millis >> 8) & 0xFF;
97+
bytes[5] = millis & 0xFF;
98+
99+
// version and variant
100+
bytes[6] = (bytes[6] & 0x0F) | 0x70;
101+
bytes[8] = (bytes[8] & 0x3F) | 0x80;
102+
}
103+
104+
public:
105+
static constexpr std::size_t BYTE_LEN = 16;
106+
static constexpr std::size_t STR_LEN = 36;
107+
static constexpr const char* STR_FORMAT =
108+
"%02hhX%02hhX%02hhX%02hhX-%02hhX%02hhX-%02hhX%02hhX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX";
109+
110+
private:
111+
std::array<uint8_t, BYTE_LEN> bytes;
112+
};
113+
114+
} // namespace Firebird
115+
116+
#endif // CLASSES_UUID_H

src/include/firebird/impl/msg/jrd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,3 +986,4 @@ FB_IMPL_MSG(JRD, 983, pattern_cant_be_used_without_other_pattern_and_vice_versa,
986986
FB_IMPL_MSG(JRD, 984, incompatible_format_patterns, -901, "HY", "000", "@1 incompatible with @2")
987987
FB_IMPL_MSG(JRD, 985, only_one_pattern_can_be_used, -901, "HY", "000", "Can use only one of these patterns @1")
988988
FB_IMPL_MSG(JRD, 986, can_not_use_same_pattern_twice, -901, "HY", "000", "Cannot use the same pattern twice: @1")
989+
FB_IMPL_MSG(JRD, 987, sysf_invalid_gen_uuid_version, -833, "42", "000", "Invalid GEN_UUID version (@1). Must be 4 or 7")

src/include/gen/Firebird.pas

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5729,6 +5729,7 @@ IProfilerStatsImpl = class(IProfilerStats)
57295729
isc_incompatible_format_patterns = 335545304;
57305730
isc_only_one_pattern_can_be_used = 335545305;
57315731
isc_can_not_use_same_pattern_twice = 335545306;
5732+
isc_sysf_invalid_gen_uuid_version = 335545307;
57325733
isc_gfix_db_name = 335740929;
57335734
isc_gfix_invalid_sw = 335740930;
57345735
isc_gfix_incmp_sw = 335740932;

src/jrd/SysFunction.cpp

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "../common/TimeZoneUtil.h"
3434
#include "../common/classes/VaryStr.h"
3535
#include "../common/classes/Hash.h"
36+
#include "../common/classes/Uuid.h"
3637
#include "../jrd/SysFunction.h"
3738
#include "../jrd/DataTypeUtil.h"
3839
#include "../include/fb_blk.h"
@@ -1920,7 +1921,7 @@ void makeUnicodeChar(DataTypeUtilBase*, const SysFunction* function, dsc* result
19201921
void makeUuid(DataTypeUtilBase*, const SysFunction* function, dsc* result,
19211922
int argsCount, const dsc** args)
19221923
{
1923-
fb_assert(argsCount == function->minArgCount);
1924+
fb_assert(argsCount >= function->minArgCount);
19241925

19251926
if (argsCount > 0 && args[0]->isNull())
19261927
result->makeNullString();
@@ -4519,12 +4520,38 @@ dsc* evlFloor(thread_db* tdbb, const SysFunction*, const NestValueArray& args,
45194520
dsc* evlGenUuid(thread_db* tdbb, const SysFunction*, const NestValueArray& args,
45204521
impure_value* impure)
45214522
{
4522-
fb_assert(args.isEmpty());
4523+
const auto request = tdbb->getRequest();
4524+
4525+
fb_assert(args.getCount() <= 1);
45234526

45244527
// Generate UUID and convert it into platform-independent format
45254528
UCHAR data[Guid::SIZE];
4529+
SLONG version = 4;
4530+
4531+
if (args.getCount() > 0)
4532+
{
4533+
const auto* const versionDsc = EVL_expr(tdbb, request, args[0]);
4534+
4535+
if (request->req_flags & req_null)
4536+
return nullptr;
4537+
4538+
version = MOV_get_long(tdbb, versionDsc, 0);
4539+
}
45264540

4527-
Guid::generate().convert(data);
4541+
switch (version)
4542+
{
4543+
case 4:
4544+
Guid::generate().convert(data);
4545+
break;
4546+
4547+
case 7:
4548+
Uuid::generate(version).extractBytes(data, sizeof(data));
4549+
break;
4550+
4551+
default:
4552+
status_exception::raise(Arg::Gds(isc_sysf_invalid_gen_uuid_version) << Arg::Num(version));
4553+
break;
4554+
}
45284555

45294556
dsc result;
45304557
result.makeText(Guid::SIZE, ttype_binary, data);
@@ -6885,7 +6912,7 @@ const SysFunction SysFunction::functions[] =
68856912
{"EXP", 1, 1, setParamsDblDec, makeDblDecResult, evlExp, NULL},
68866913
{"FIRST_DAY", 2, 2, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay},
68876914
{"FLOOR", 1, 1, setParamsDblDec, makeCeilFloor, evlFloor, NULL},
6888-
{"GEN_UUID", 0, 0, NULL, makeUuid, evlGenUuid, NULL},
6915+
{"GEN_UUID", 0, 1, NULL, makeUuid, evlGenUuid, NULL},
68896916
{"HASH", 1, 2, setParamsHash, makeHash, evlHash, NULL},
68906917
{"HEX_DECODE", 1, 1, NULL, makeDecodeHex, evlDecodeHex, NULL},
68916918
{"HEX_ENCODE", 1, 1, NULL, makeEncodeHex, evlEncodeHex, NULL},

0 commit comments

Comments
 (0)