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

Rework on CORE-3238 - Makes GEN_UUID return a compliant RFC-4122 binary UUID.

Fixed CORE-3887 - CHAR_TO_UUID and UUID_TO_CHAR works different in big/little endian architectures - problem similar to CORE-2898.
This commit is contained in:
asfernandes 2012-07-10 16:00:27 +00:00
parent 007955fa59
commit e6169577fb
6 changed files with 69 additions and 168 deletions

View File

@ -246,36 +246,16 @@ Function:
Format: Format:
CHAR_TO_UUID( <string> ) CHAR_TO_UUID( <string> )
Notes: Important (for big-endian servers):
If you have not used this function before, its usage is discouraged. CHAR_TO_UUID2 supersedes it. It has been discovered that before Firebird 2.5.2, CHAR_TO_UUID and UUID_TO_CHAR works
incorrectly in big-endian servers. In these machines, bytes/characters are swapped and goes in
wrong positions when converting. This bug was fixed in 2.5.2 and 3.0, but that means these
functions now returns different values (for the same input parameter) than before.
Example: Example:
select char_to_uuid('93519227-8D50-4E47-81AA-8F6678C096A1') from rdb$database; select char_to_uuid('93519227-8D50-4E47-81AA-8F6678C096A1') from rdb$database;
See also: GEN_UUID, CHAR_TO_UUID2, UUID_TO_CHAR and UUID_TO_CHAR2 See also: GEN_UUID and UUID_TO_CHAR
-------------
CHAR_TO_UUID2
-------------
Function:
Converts the CHAR(32) ASCII representation of an UUID
(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) to the CHAR(16) OCTETS
representation (optimized for storage).
Format:
CHAR_TO_UUID2( <string> )
Notes:
This function supersedes CHAR_TO_UUID. The difference between them is that CHAR_TO_UUID does a
byte-by-byte conversion of the ASCII string to the OCTETS one, while CHAR_TO_UUID2 converts
a RFC-4122 compliant ASCII UUID to a compliant OCTETS string.
Example:
select char_to_uuid2('93519227-8D50-4E47-81AA-8F6678C096A1') from rdb$database;
See also: GEN_UUID, UUID_TO_CHAR2
--- ---
@ -431,17 +411,17 @@ Function:
Format: Format:
GEN_UUID() GEN_UUID()
Notes: Important:
In Firebird 2.5.0 and 2.5.1, GEN_UUID was returning completely random strings. This is not Before Firebird 2.5.2, GEN_UUID was returning completely random strings. This is not compliant
compliant with the RFC-4122 (UUID specification). with the RFC-4122 (UUID specification).
In Firebird 2.5.2 and 3.0 this was fixed. Now GEN_UUID returns a compliant UUID version 4 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 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. UUID is XXXXXXXX-XXXX-4XXX-YXXX-XXXXXXXXXXXX, where 4 is fixed (version) and Y is 8, 9, A or B.
Example: Example:
insert into records (id) value (gen_uuid()); insert into records (id) value (gen_uuid());
See also: CHAR_TO_UUID, UUID_TO_CHAR, CHAR_TO_UUID2, UUID_TO_CHAR2 See also: CHAR_TO_UUID and UUID_TO_CHAR
---- ----
@ -869,33 +849,13 @@ Function:
Format: Format:
UUID_TO_CHAR( <string> ) UUID_TO_CHAR( <string> )
Notes: Important (for big-endian servers):
If you have not used this function before, its usage is discouraged. UUID_TO_CHAR2 supersedes it. It has been discovered that before Firebird 2.5.2, CHAR_TO_UUID and UUID_TO_CHAR works
incorrectly in big-endian servers. In these machines, bytes/characters are swapped and goes in
wrong positions when converting. This bug was fixed in 2.5.2 and 3.0, but that means these
functions now returns different values (for the same input parameter) than before.
Example: Example:
select uuid_to_char(gen_uuid()) from rdb$database; select uuid_to_char(gen_uuid()) from rdb$database;
See also: GEN_UUID, UUID_TO_CHAR2, CHAR_TO_UUID and CHAR_TO_UUID2 See also: GEN_UUID and CHAR_TO_UUID
-------------
UUID_TO_CHAR2
-------------
Function:
Converts a CHAR(16) OCTETS UUID (that's returned by GEN_UUID) to the
CHAR(32) ASCII representation (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
Format:
UUID_TO_CHAR2( <string> )
Notes:
This function supersedes UUID_TO_CHAR. The difference between them is that UUID_TO_CHAR does a
byte-by-byte conversion of the OCTETS string to the ASCII one, while UUID_TO_CHAR2 converts
a RFC-4122 compliant OCTETS UUID to a compliant ASCII string. Also, UUID_TO_CHAR returns
upper-cased string and UUID_TO_CHAR2 returns lower-cased string.
Example:
select uuid_to_char2(gen_uuid()) from rdb$database;
See also: GEN_UUID, CHAR_TO_UUID2

View File

@ -68,9 +68,7 @@ enum Function
funLPad, funLPad,
funRPad, funRPad,
funLnat, funLnat,
funLog10, funLog10
funUuidBroken,
funUuidRfc
}; };
enum TrigonFunction enum TrigonFunction
@ -1433,40 +1431,19 @@ dsc* evlCharToUuid(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_n
buffer[38] = '\0'; buffer[38] = '\0';
memcpy(buffer + 1, data, GUID_BODY_SIZE); memcpy(buffer + 1, data, GUID_BODY_SIZE);
FB_GUID guid;
switch ((Function)(IPTR) function->misc)
{
case funUuidBroken:
StringToGuid(&guid, buffer, false);
break;
case funUuidRfc:
{
USHORT bytes[16]; USHORT bytes[16];
sscanf(buffer, GUID_NEW_FORMAT_LOWER, sscanf(buffer, GUID_NEW_FORMAT,
&bytes[0], &bytes[1], &bytes[2], &bytes[3], &bytes[0], &bytes[1], &bytes[2], &bytes[3],
&bytes[4], &bytes[5], &bytes[6], &bytes[7], &bytes[4], &bytes[5], &bytes[6], &bytes[7],
&bytes[8], &bytes[9], &bytes[10], &bytes[11], &bytes[8], &bytes[9], &bytes[10], &bytes[11],
&bytes[12], &bytes[13], &bytes[14], &bytes[15]); &bytes[12], &bytes[13], &bytes[14], &bytes[15]);
guid.data1 = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; UCHAR resultData[16];
guid.data2 = (bytes[4] << 8) | bytes[5]; for (unsigned i = 0; i < 16; ++i)
guid.data3 = (bytes[6] << 8) | bytes[7]; resultData[i] = (UCHAR) bytes[i];
guid.data4[0] = bytes[8];
guid.data4[1] = bytes[9];
guid.data4[2] = bytes[10];
guid.data4[3] = bytes[11];
guid.data4[4] = bytes[12];
guid.data4[5] = bytes[13];
guid.data4[6] = bytes[14];
guid.data4[7] = bytes[15];
break;
}
}
dsc result; dsc result;
result.makeText(16, ttype_binary, reinterpret_cast<UCHAR*>(guid.data)); result.makeText(16, ttype_binary, resultData);
EVL_make_value(tdbb, &result, impure); EVL_make_value(tdbb, &result, impure);
return &impure->vlu_desc; return &impure->vlu_desc;
@ -1999,8 +1976,26 @@ dsc* evlGenUuid(Jrd::thread_db* tdbb, const SysFunction*, Jrd::jrd_nod* args,
GenerateGuid(&guid); GenerateGuid(&guid);
UCHAR data[16];
data[0] = (guid.data1 >> 24) & 0xFF;
data[1] = (guid.data1 >> 16) & 0xFF;
data[2] = (guid.data1 >> 8) & 0xFF;
data[3] = guid.data1 & 0xFF;
data[4] = (guid.data2 >> 8) & 0xFF;
data[5] = guid.data2 & 0xFF;
data[6] = (guid.data3 >> 8) & 0xFF;
data[7] = guid.data3 & 0xFF;
data[8] = guid.data4[0];
data[9] = guid.data4[1];
data[10] = guid.data4[2];
data[11] = guid.data4[3];
data[12] = guid.data4[4];
data[13] = guid.data4[5];
data[14] = guid.data4[6];
data[15] = guid.data4[7];
dsc result; dsc result;
result.makeText(16, ttype_binary, reinterpret_cast<UCHAR*>(guid.data)); result.makeText(16, ttype_binary, data);
EVL_make_value(tdbb, &result, impure); EVL_make_value(tdbb, &result, impure);
return &impure->vlu_desc; return &impure->vlu_desc;
@ -3319,27 +3314,12 @@ dsc* evlUuidToChar(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_n
Arg::Str(function->name)); Arg::Str(function->name));
} }
const FB_GUID* guid = reinterpret_cast<const FB_GUID*>(data);
char buffer[GUID_BUFF_SIZE]; char buffer[GUID_BUFF_SIZE];
sprintf(buffer, GUID_NEW_FORMAT,
switch ((Function)(IPTR) function->misc) USHORT(data[0]), USHORT(data[1]), USHORT(data[2]), USHORT(data[3]), USHORT(data[4]),
{ USHORT(data[5]), USHORT(data[6]), USHORT(data[7]), USHORT(data[8]), USHORT(data[9]),
case funUuidBroken: USHORT(data[10]), USHORT(data[11]), USHORT(data[12]), USHORT(data[13]), USHORT(data[14]),
GuidToString(buffer, guid, false); USHORT(data[15]));
break;
case funUuidRfc:
sprintf(buffer, GUID_NEW_FORMAT_LOWER,
USHORT((guid->data1 >> 24) & 0xFF), USHORT((guid->data1 >> 16) & 0xFF),
USHORT((guid->data1 >> 8) & 0xFF), USHORT(guid->data1 & 0xFF),
USHORT((guid->data2 >> 8) & 0xFF), USHORT(guid->data2 & 0xFF),
USHORT((guid->data3 >> 8) & 0xFF), USHORT(guid->data3 & 0xFF),
USHORT(guid->data4[0]), USHORT(guid->data4[1]),
USHORT(guid->data4[2]), USHORT(guid->data4[3]),
USHORT(guid->data4[4]), USHORT(guid->data4[5]),
USHORT(guid->data4[6]), USHORT(guid->data4[7]));
break;
}
dsc result; dsc result;
result.makeText(GUID_BODY_SIZE, ttype_ascii, reinterpret_cast<UCHAR*>(buffer) + 1); result.makeText(GUID_BODY_SIZE, ttype_ascii, reinterpret_cast<UCHAR*>(buffer) + 1);
@ -3374,8 +3354,7 @@ const SysFunction SysFunction::functions[] =
{"BIN_XOR", 2, -1, setParamsInteger, makeBin, evlBin, (void*) funBinXor}, {"BIN_XOR", 2, -1, setParamsInteger, makeBin, evlBin, (void*) funBinXor},
{"CEIL", 1, 1, setParamsDouble, makeCeilFloor, evlCeil, NULL}, {"CEIL", 1, 1, setParamsDouble, makeCeilFloor, evlCeil, NULL},
{"CEILING", 1, 1, setParamsDouble, makeCeilFloor, evlCeil, NULL}, {"CEILING", 1, 1, setParamsDouble, makeCeilFloor, evlCeil, NULL},
{"CHAR_TO_UUID", 1, 1, setParamsCharToUuid, makeUuid, evlCharToUuid, (void*) funUuidBroken}, {"CHAR_TO_UUID", 1, 1, setParamsCharToUuid, makeUuid, evlCharToUuid, NULL},
{"CHAR_TO_UUID2", 1, 1, setParamsCharToUuid, makeUuid, evlCharToUuid, (void*) funUuidRfc},
{"COS", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCos}, {"COS", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCos},
{"COSH", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCosh}, {"COSH", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCosh},
{"COT", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCot}, {"COT", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCot},
@ -3410,8 +3389,7 @@ const SysFunction SysFunction::functions[] =
{"TAN", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTan}, {"TAN", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTan},
{"TANH", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTanh}, {"TANH", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTanh},
{"TRUNC", 1, 2, setParamsRoundTrunc, makeTrunc, evlTrunc, NULL}, {"TRUNC", 1, 2, setParamsRoundTrunc, makeTrunc, evlTrunc, NULL},
{"UUID_TO_CHAR", 1, 1, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, (void*) funUuidBroken}, {"UUID_TO_CHAR", 1, 1, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, NULL},
{"UUID_TO_CHAR2", 1, 1, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, (void*) funUuidRfc},
{"", 0, 0, NULL, NULL, NULL, NULL} {"", 0, 0, NULL, NULL, NULL, NULL}
}; };

View File

@ -39,10 +39,8 @@ const int GUID_BODY_SIZE = 36;
const char* const GUID_LEGACY_FORMAT = const char* const GUID_LEGACY_FORMAT =
"{%04hX%04hX-%04hX-%04hX-%04hX-%04hX%04hX%04hX}"; "{%04hX%04hX-%04hX-%04hX-%04hX-%04hX%04hX%04hX}";
const char* const GUID_NEW_FORMAT_UPPER = const char* const GUID_NEW_FORMAT =
"{%02hX%02hX%02hX%02hX-%02hX%02hX-%02hX%02hX-%02hX%02hX-%02hX%02hX%02hX%02hX%02hX%02hX}"; "{%02hX%02hX%02hX%02hX-%02hX%02hX-%02hX%02hX-%02hX%02hX-%02hX%02hX%02hX%02hX%02hX%02hX}";
const char* const GUID_NEW_FORMAT_LOWER =
"{%02hx%02hx%02hx%02hx-%02hx%02hx-%02hx%02hx-%02hx%02hx-%02hx%02hx%02hx%02hx%02hx%02hx}";
struct FB_GUID struct FB_GUID
{ {
@ -63,56 +61,21 @@ struct FB_GUID
void GenerateRandomBytes(void* buffer, size_t size); void GenerateRandomBytes(void* buffer, size_t size);
void GenerateGuid(FB_GUID* guid); void GenerateGuid(FB_GUID* guid);
// These functions receive buffers of at least GUID_BUFF_SIZE length // These functions receive buffers of at least GUID_BUFF_SIZE length.
// Warning: they are BROKEN in little-endian and should not be used on new code.
inline void GuidToString(char* buffer, const FB_GUID* guid, bool legacy) inline void GuidToString(char* buffer, const FB_GUID* guid)
{
if (legacy) // nbackup guid
{ {
sprintf(buffer, GUID_LEGACY_FORMAT, sprintf(buffer, GUID_LEGACY_FORMAT,
guid->data[0], guid->data[1], guid->data[2], guid->data[3], guid->data[0], guid->data[1], guid->data[2], guid->data[3],
guid->data[4], guid->data[5], guid->data[6], guid->data[7]); guid->data[4], guid->data[5], guid->data[6], guid->data[7]);
} }
else
{
sprintf(buffer, GUID_NEW_FORMAT_UPPER,
USHORT(guid->data[0] & 0xFF), USHORT(guid->data[0] >> 8),
USHORT(guid->data[1] & 0xFF), USHORT(guid->data[1] >> 8),
USHORT(guid->data[2] & 0xFF), USHORT(guid->data[2] >> 8),
USHORT(guid->data[3] & 0xFF), USHORT(guid->data[3] >> 8),
USHORT(guid->data[4] & 0xFF), USHORT(guid->data[4] >> 8),
USHORT(guid->data[5] & 0xFF), USHORT(guid->data[5] >> 8),
USHORT(guid->data[6] & 0xFF), USHORT(guid->data[6] >> 8),
USHORT(guid->data[7] & 0xFF), USHORT(guid->data[7] >> 8));
}
}
inline void StringToGuid(FB_GUID* guid, const char* buffer, bool legacy) inline void StringToGuid(FB_GUID* guid, const char* buffer)
{
if (legacy) // nbackup guid
{ {
sscanf(buffer, GUID_LEGACY_FORMAT, sscanf(buffer, GUID_LEGACY_FORMAT,
&guid->data[0], &guid->data[1], &guid->data[2], &guid->data[3], &guid->data[0], &guid->data[1], &guid->data[2], &guid->data[3],
&guid->data[4], &guid->data[5], &guid->data[6], &guid->data[7]); &guid->data[4], &guid->data[5], &guid->data[6], &guid->data[7]);
} }
else
{
USHORT bytes[16];
sscanf(buffer, GUID_NEW_FORMAT_UPPER,
&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]);
guid->data[0] = bytes[0] | (bytes[1] << 8);
guid->data[1] = bytes[2] | (bytes[3] << 8);
guid->data[2] = bytes[4] | (bytes[5] << 8);
guid->data[3] = bytes[6] | (bytes[7] << 8);
guid->data[4] = bytes[8] | (bytes[9] << 8);
guid->data[5] = bytes[10] | (bytes[11] << 8);
guid->data[6] = bytes[12] | (bytes[13] << 8);
guid->data[7] = bytes[14] | (bytes[15] << 8);
}
}
#endif #endif

View File

@ -107,7 +107,7 @@ void TraceSvcJrd::startSession(TraceSession& session, bool interactive)
GenerateGuid(&guid); GenerateGuid(&guid);
char* buff = session.ses_logfile.getBuffer(GUID_BUFF_SIZE); char* buff = session.ses_logfile.getBuffer(GUID_BUFF_SIZE);
GuidToString(buff, &guid, true); GuidToString(buff, &guid);
session.ses_logfile.insert(0, "fb_trace."); session.ses_logfile.insert(0, "fb_trace.");
} }

View File

@ -251,7 +251,7 @@ void PPG_print_header(const header_page* header, SLONG page,
case HDR_backup_guid: case HDR_backup_guid:
{ {
char buff[GUID_BUFF_SIZE]; char buff[GUID_BUFF_SIZE];
GuidToString(buff, reinterpret_cast<const FB_GUID*>(p + 2), true); GuidToString(buff, reinterpret_cast<const FB_GUID*>(p + 2));
uSvc->printf(false, "\tDatabase backup GUID:\t%s\n", buff); uSvc->printf(false, "\tDatabase backup GUID:\t%s\n", buff);
break; break;
} }

View File

@ -890,7 +890,7 @@ void NBackup::backup_database(int level, const PathName& fname)
bh.version = 1; bh.version = 1;
bh.level = level; bh.level = level;
bh.backup_guid = backup_guid; bh.backup_guid = backup_guid;
StringToGuid(&bh.prev_guid, prev_guid, true); StringToGuid(&bh.prev_guid, prev_guid);
bh.page_size = header->hdr_page_size; bh.page_size = header->hdr_page_size;
bh.backup_scn = backup_scn; bh.backup_scn = backup_scn;
bh.prev_scn = prev_scn; bh.prev_scn = prev_scn;
@ -990,7 +990,7 @@ void NBackup::backup_database(int level, const PathName& fname)
in_sqlda->sqlvar[0].sqldata = (char*)&level; in_sqlda->sqlvar[0].sqldata = (char*)&level;
in_sqlda->sqlvar[0].sqlind = &null_flag; in_sqlda->sqlvar[0].sqlind = &null_flag;
char temp[GUID_BUFF_SIZE]; char temp[GUID_BUFF_SIZE];
GuidToString(temp, &backup_guid, true); GuidToString(temp, &backup_guid);
in_sqlda->sqlvar[1].sqldata = temp; in_sqlda->sqlvar[1].sqldata = temp;
in_sqlda->sqlvar[1].sqlind = &null_flag; in_sqlda->sqlvar[1].sqlind = &null_flag;
in_sqlda->sqlvar[2].sqldata = (char*)&backup_scn; in_sqlda->sqlvar[2].sqldata = (char*)&backup_scn;