*
* uuid.c
* Functions for the built-in type "uuid".
*
* Copyright (c) 2007-2012, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/backend/utils/adt/uuid.c
*
* -------------------------------------------------------------------------
*/
#include <cstdio>
#include <openssl/rand.h>
#include "postgres.h"
#include "pgxc/pgxcnode.h"
#include "knl/knl_variable.h"
#include "access/hash.h"
#include "access/xlog.h"
#include "executor/executor.h"
#include "libpq/pqformat.h"
#include "utils/builtins.h"
#include "utils/uuid.h"
#include "utils/timestamp.h"
#include "utils/inet.h"
#include <net/if.h>
#include "plugin_postgres.h"
static void string_to_uuid(const char* source, pg_uuid_t* uuid, bool can_ignore = false);
static int uuid_internal_cmp(const pg_uuid_t* arg1, const pg_uuid_t* arg2);
PG_FUNCTION_INFO_V1_PUBLIC(uuid_generate);
extern "C" DLL_PUBLIC Datum uuid_generate(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1_PUBLIC(gen_random_uuid);
#ifdef DOLPHIN
PG_FUNCTION_INFO_V1_PUBLIC(uuid_short);
extern "C" DLL_PUBLIC Datum uuid_short(PG_FUNCTION_ARGS);
#endif
Datum uuid_in(PG_FUNCTION_ARGS)
{
char* uuid_str = PG_GETARG_CSTRING(0);
pg_uuid_t* uuid = NULL;
uuid = (pg_uuid_t*)palloc(sizeof(*uuid));
string_to_uuid(uuid_str, uuid, fcinfo->can_ignore);
PG_RETURN_UUID_P(uuid);
}
#define HEX_CHARS "0123456789abcdef"
Datum uuid_out(PG_FUNCTION_ARGS)
{
pg_uuid_t* uuid = PG_GETARG_UUID_P(0);
StringInfoData buf;
int i;
initStringInfo(&buf);
for (i = 0; i < UUID_LEN; i++) {
int hi;
int lo;
* We print uuid values as a string of 8, 4, 4, 4, and then 12
* hexadecimal characters, with each group is separated by a hyphen
* ("-"). Therefore, add the hyphens at the appropriate places here.
*/
if (i == 4 || i == 6 || i == 8 || i == 10)
appendStringInfoChar(&buf, '-');
hi = uuid->data[i] >> 4;
lo = uuid->data[i] & 0x0F;
appendStringInfoChar(&buf, HEX_CHARS[hi]);
appendStringInfoChar(&buf, HEX_CHARS[lo]);
}
PG_RETURN_CSTRING(buf.data);
}
* We allow UUIDs as a series of 32 hexadecimal digits with an optional dash
* after each group of 4 hexadecimal digits, and optionally surrounded by {}.
* (The canonical format 8x-4x-4x-4x-12x, where "nx" means n hexadecimal
* digits, is the only one used for output.)
*/
static void string_to_uuid(const char* source, pg_uuid_t* uuid, bool can_ignore)
{
const char* src = source;
bool braces = false;
int i;
errno_t rc = EOK;
if (src[0] == '{') {
src++;
braces = true;
}
for (i = 0; i < UUID_LEN; i++) {
char str_buf[3];
if (src[0] == '\0' || src[1] == '\0')
goto syntax_error;
rc = memcpy_s(str_buf, sizeof(str_buf), src, 2);
securec_check(rc, "\0", "\0");
if (!isxdigit((unsigned char)str_buf[0]) || !isxdigit((unsigned char)str_buf[1]))
goto syntax_error;
str_buf[2] = '\0';
uuid->data[i] = (unsigned char)strtoul(str_buf, NULL, 16);
src += 2;
if (src[0] == '-' && (i % 2) == 1 && i < UUID_LEN - 1)
src++;
}
if (braces) {
if (*src != '}')
goto syntax_error;
src++;
}
if (*src != '\0')
goto syntax_error;
return;
syntax_error:
if (can_ignore) {
ereport(
WARNING, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for uuid: \"%s\"", source)));
rc = memset_s(uuid->data, UUID_LEN, 0, UUID_LEN);
securec_check(rc, "\0", "\0");
return;
}
ereport(
ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for uuid: \"%s\"", source)));
}
Datum uuid_recv(PG_FUNCTION_ARGS)
{
StringInfo buffer = (StringInfo)PG_GETARG_POINTER(0);
pg_uuid_t* uuid = NULL;
uuid = (pg_uuid_t*)palloc(UUID_LEN);
errno_t rc = memcpy_s(uuid->data, UUID_LEN, pq_getmsgbytes(buffer, UUID_LEN), UUID_LEN);
securec_check(rc, "\0", "\0");
PG_RETURN_POINTER(uuid);
}
Datum uuid_send(PG_FUNCTION_ARGS)
{
pg_uuid_t* uuid = PG_GETARG_UUID_P(0);
StringInfoData buffer;
pq_begintypsend(&buffer);
pq_sendbytes(&buffer, (char*)uuid->data, UUID_LEN);
PG_RETURN_BYTEA_P(pq_endtypsend(&buffer));
}
static int uuid_internal_cmp(const pg_uuid_t* arg1, const pg_uuid_t* arg2)
{
return memcmp(arg1->data, arg2->data, UUID_LEN);
}
Datum uuid_lt(PG_FUNCTION_ARGS)
{
pg_uuid_t* arg1 = PG_GETARG_UUID_P(0);
pg_uuid_t* arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) < 0);
}
Datum uuid_le(PG_FUNCTION_ARGS)
{
pg_uuid_t* arg1 = PG_GETARG_UUID_P(0);
pg_uuid_t* arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) <= 0);
}
Datum uuid_eq(PG_FUNCTION_ARGS)
{
pg_uuid_t* arg1 = PG_GETARG_UUID_P(0);
pg_uuid_t* arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) == 0);
}
Datum uuid_ge(PG_FUNCTION_ARGS)
{
pg_uuid_t* arg1 = PG_GETARG_UUID_P(0);
pg_uuid_t* arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) >= 0);
}
Datum uuid_gt(PG_FUNCTION_ARGS)
{
pg_uuid_t* arg1 = PG_GETARG_UUID_P(0);
pg_uuid_t* arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) > 0);
}
Datum uuid_ne(PG_FUNCTION_ARGS)
{
pg_uuid_t* arg1 = PG_GETARG_UUID_P(0);
pg_uuid_t* arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) != 0);
}
Datum uuid_cmp(PG_FUNCTION_ARGS)
{
pg_uuid_t* arg1 = PG_GETARG_UUID_P(0);
pg_uuid_t* arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_INT32(uuid_internal_cmp(arg1, arg2));
}
Datum uuid_hash(PG_FUNCTION_ARGS)
{
pg_uuid_t* key = PG_GETARG_UUID_P(0);
return hash_any(key->data, UUID_LEN);
}
static int hash_ctoa(char ch)
{
int res = 0;
if (ch >= 'a' && ch <= 'f') {
res = 10 + (ch - 'a');
} else {
res = ch - '0';
}
return res;
}
Datum hash16in(PG_FUNCTION_ARGS)
{
char* str = PG_GETARG_CSTRING(0);
int len = strlen(str);
uint64 res = 0;
if (len > 16) {
ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for hash16: \"%s\"", str)));
}
for (int i = 0; i < len; ++i) {
if (str[0] == '-') {
res = 0;
break;
}
if (!((str[i] >= 'a' && str[i] <= 'f') || (str[i] >= '0' && str[i] <= '9'))) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for hash16: \"%s\"", str)));
}
res = res * 16 + hash_ctoa(str[i]);
}
PG_RETURN_TRANSACTIONID(res);
}
Datum hash16out(PG_FUNCTION_ARGS)
{
uint64 hash16 = PG_GETARG_TRANSACTIONID(0);
StringInfoData buf;
StringInfoData res;
initStringInfo(&buf);
initStringInfo(&res);
if (hash16 >= 0) {
for (int i = 0; i < 16; ++i) {
int ho = hash16 & 0x0F;
hash16 = hash16 >> 4;
appendStringInfoChar(&buf, HEX_CHARS[ho]);
}
for (int j = strlen(buf.data) - 1; j >= 0; --j) {
appendStringInfoChar(&res, buf.data[j]);
}
}
PG_RETURN_CSTRING(res.data);
}
static void string_to_hash32(char* source, hash32_t* hash32, bool can_ignore)
{
char* src = source;
int len = strlen(src);
int level = can_ignore ? WARNING : ERROR;
if (len != HASH32_LEN * 2) {
ereport(level, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for hash32: \"%s\"", source)));
src = "00000000000000000000000000000000";
}
for (int i = 0; i < HASH32_LEN; ++i) {
hash32->data[i] = hash_ctoa(src[i * 2]) * 16 + hash_ctoa(src[i * 2 + 1]);
}
}
Datum hash32in(PG_FUNCTION_ARGS)
{
char *hash32_str = PG_GETARG_CSTRING(0);
hash32_t *hash32 = NULL;
hash32 = (hash32_t*)palloc(sizeof(hash32_t));
string_to_hash32(hash32_str, hash32, fcinfo->can_ignore);
PG_RETURN_HASH32_P(hash32);
}
Datum hash32out(PG_FUNCTION_ARGS)
{
hash32_t* hash32 = PG_GETARG_HASH32_P(0);
StringInfoData buf;
int i;
initStringInfo(&buf);
for (i = 0; i < HASH32_LEN; i++) {
int h_low;
int h_high;
h_low = hash32->data[i] & 0x0F;
h_high = (hash32->data[i] >> 4) & 0x0F;
appendStringInfoChar(&buf, HEX_CHARS[h_high]);
appendStringInfoChar(&buf, HEX_CHARS[h_low]);
}
PG_RETURN_CSTRING(buf.data);
}
Datum hash16_add(PG_FUNCTION_ARGS)
{
uint64 arg1 = DatumGetUInt64(PG_GETARG_DATUM(0));
uint64 arg2 = DatumGetUInt64(PG_GETARG_DATUM(1));
PG_RETURN_DATUM(UInt64GetDatum(arg1 + arg2));
}
Datum hash16_eq(PG_FUNCTION_ARGS)
{
uint64 arg1 = DatumGetUInt64(PG_GETARG_DATUM(0));
uint64 arg2 = DatumGetUInt64(PG_GETARG_DATUM(1));
PG_RETURN_BOOL(arg1 == arg2);
}
#ifdef DOLPHIN
#define UUID_VERSION 0x1000
#define FORMATTED_UUID_LEN 36
#define UUID_FIRST_PART_LEN 8
#define UUID_MID_PART_LEN 4
#define UUID_LAST_PART_LEN 12
#define UUID_TIME_OFFSET ((uint64)141427 * 24 * 60 * 60 * 1000 * 1000 * 10)
#define INT_RANGE_REVISE_PARAM 128
#define HEX_BASE 16
#define URANDOM_FILE_PATH "/dev/urandom"
#define MAC_CHAR_NUM 6
#define CLOCK_SEQ_CHAR_NUM 2
#define MaxMacAddrList 10
static uint64 uuid_time = 0;
static uint nano_seq = 0;
static char g_clockSeqAndMac[]="0000-000000000000";
static pthread_mutex_t gUuidMutex = PTHREAD_MUTEX_INITIALIZER;
* transfer char* into hexadecimal style.
* offset is used to modify the range of source char in case of getting range of signed int8. set to 0 if not needed.
*/
static void string_to_hex(char* source, char* dest, int src_len, int offset)
{
int dest_index = 0;
for (int i = 0; i < src_len; i++) {
dest[dest_index++] = HEX_CHARS[(source[i] + offset) / HEX_BASE];
dest[dest_index++] = HEX_CHARS[(source[i] + offset) % HEX_BASE];
}
}
static void int_to_hex(uint64 source, char* dest, int src_len)
{
char* dest_ptr = dest + src_len;
for (int i = 0; i < src_len; i++) {
*(--dest_ptr) = HEX_CHARS[source & 0xF];
source >>= 4;
}
}
* get specific num of character read from /dev/urandom, storing in rand_buf.
*/
static void pseudo_rand_read(char* rand_buf, int bytes_read)
{
FILE* f = fopen(URANDOM_FILE_PATH, "r");
if (!f) {
ereport(ERROR, (errcode(ERRCODE_FILE_READ_FAILED), (errmsg("cannot open urandom file"))));
}
size_t b_read;
char* buf_ptr = rand_buf;
while (bytes_read) {
b_read = fread(buf_ptr, 1, bytes_read, f);
if (b_read <= 0) {
fclose(f);
ereport(ERROR, (errcode(ERRCODE_FILE_READ_FAILED), (errmsg("failed to get random number"))));
}
buf_ptr += b_read;
bytes_read -= (int)b_read;
}
fclose(f);
}
static uint64 GetMACAddr(void)
{
macaddr mac;
uint64 macAddr;
int sockFd = NO_SOCKET;
struct ifconf ifconfInfo;
struct ifreq ifreqInfo;
char *buf = NULL;
errno_t ss_rc = EOK;
uint32 i;
ss_rc = memset_s((void *)&mac, sizeof(macaddr), 0, sizeof(macaddr));
securec_check(ss_rc, "\0", "\0");
sockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sockFd != NO_SOCKET) {
buf = (char *)palloc(MaxMacAddrList * sizeof(ifreq));
ifconfInfo.ifc_len = MaxMacAddrList * sizeof(ifreq);
ifconfInfo.ifc_buf = buf;
if (ioctl(sockFd, SIOCGIFCONF, &ifconfInfo) != -1) {
struct ifreq *ifrepTmp = ifconfInfo.ifc_req;
for (i = 0; i < (ifconfInfo.ifc_len / sizeof(struct ifreq)); i++) {
ss_rc = strcpy_s(ifreqInfo.ifr_name, strlen(ifrepTmp->ifr_name) + 1, ifrepTmp->ifr_name);
securec_check(ss_rc, "\0", "\0");
if (ioctl(sockFd, SIOCGIFFLAGS, &ifreqInfo) == 0) {
if (!(ifreqInfo.ifr_flags & IFF_LOOPBACK)) {
if (ioctl(sockFd, SIOCGIFHWADDR, &ifreqInfo) == 0) {
mac.a = (unsigned char)ifreqInfo.ifr_hwaddr.sa_data[0];
mac.b = (unsigned char)ifreqInfo.ifr_hwaddr.sa_data[1];
mac.c = (unsigned char)ifreqInfo.ifr_hwaddr.sa_data[2];
mac.d = (unsigned char)ifreqInfo.ifr_hwaddr.sa_data[3];
mac.e = (unsigned char)ifreqInfo.ifr_hwaddr.sa_data[4];
mac.f = (unsigned char)ifreqInfo.ifr_hwaddr.sa_data[5];
break;
}
}
}
ifrepTmp++;
}
}
pfree_ext(buf);
close(sockFd);
}
macAddr = ((uint64)mac.a << 40) | ((uint64)mac.b << 32) | ((uint64)mac.c << 24) | ((uint64)mac.d << 16) |
((uint64)mac.e << 8) | (uint64)mac.f;
return macAddr;
}
* generate uuid in v1 style.
*/
Datum uuid_generate(PG_FUNCTION_ARGS)
{
char clockSeq[CLOCK_SEQ_CHAR_NUM];
char clockSeqAndMac[UUID_MID_PART_LEN + UUID_LAST_PART_LEN + 2];
AutoMutexLock localeLock(&gUuidMutex);
localeLock.lock();
if (unlikely(!uuid_time)) {
pseudo_rand_read(clockSeq, CLOCK_SEQ_CHAR_NUM);
string_to_hex(clockSeq, g_clockSeqAndMac, CLOCK_SEQ_CHAR_NUM, INT_RANGE_REVISE_PARAM);
uint64 mac_addr = GetMACAddr();
if (!mac_addr) {
char virtual_mac_string[6] = {0};
if (strlen(virtual_mac_string) == 0) {
pseudo_rand_read(virtual_mac_string, MAC_CHAR_NUM);
}
mac_addr = ((uint64)virtual_mac_string[0] << 40) | ((uint64)virtual_mac_string[1] << 32) |
((uint64)virtual_mac_string[2] << 24) | ((uint64)virtual_mac_string[3] << 16) |
((uint64)virtual_mac_string[4] << 8) | (uint64)virtual_mac_string[5];
}
int_to_hex(mac_addr, g_clockSeqAndMac + UUID_MID_PART_LEN + 1, UUID_LAST_PART_LEN);
}
uint64 curr_time;
* this is why nano_seq should be uint but not uint64, cause nano_seq will increase, if it is uint64
* add increase to UINT64_MAX, the final result of curr_time will be overflow.
*/
#ifdef HAVE_INT64_TIMESTAMP
curr_time = GetCurrentTimestamp() * 10 + UUID_TIME_OFFSET + nano_seq;
#else
curr_time = GetCurrentTimestamp() * 1000 * 1000 * 10 + UUID_TIME_OFFSET + nano_seq;
#endif
if (curr_time > uuid_time && nano_seq > 0) {
uint64 give_back = Min(nano_seq, curr_time - uuid_time - 1);
curr_time -= give_back;
nano_seq -= give_back;
}
if (curr_time == uuid_time) {
* If nanoseq overflows, we need to start over with a new numberspace, cause in the next loop,
* the value of curr_time may collide with an already generated value. So if nano_seq overflows,
* we won't increase curr_time, then the curr_time will equal to uuid_time, which will lead to
* new numberspace.
*/
if (likely(++nano_seq)) {
++curr_time;
}
}
if (unlikely(curr_time <= uuid_time)) {
pseudo_rand_read(clockSeq, CLOCK_SEQ_CHAR_NUM);
string_to_hex(clockSeq, g_clockSeqAndMac, CLOCK_SEQ_CHAR_NUM, INT_RANGE_REVISE_PARAM);
nano_seq = 0;
}
int rc = strcpy_s(clockSeqAndMac, UUID_MID_PART_LEN + UUID_LAST_PART_LEN + 2, g_clockSeqAndMac);
securec_check(rc, "", "");
uuid_time = curr_time;
localeLock.unLock();
uint32 timestamp_low = (uint32)(curr_time & 0xFFFFFFFF);
uint16 timestamp_mid = (uint16)((curr_time >> 32) & 0xFFFF);
uint16 timestamp_high_and_v = (uint16)((curr_time >> 48) | UUID_VERSION);
char* res = (char*)palloc(FORMATTED_UUID_LEN + 1);
res[FORMATTED_UUID_LEN] = '\0';
int cursor = 0;
int_to_hex(timestamp_low, res, UUID_FIRST_PART_LEN);
cursor += UUID_FIRST_PART_LEN;
res[cursor++] = '-';
int_to_hex(timestamp_mid, res + cursor, UUID_MID_PART_LEN);
cursor += UUID_MID_PART_LEN;
res[cursor++] = '-';
int_to_hex(timestamp_high_and_v, res + cursor, UUID_MID_PART_LEN);
cursor += UUID_MID_PART_LEN;
res[cursor++] = '-';
rc = strcpy_s(res + cursor, FORMATTED_UUID_LEN + 1 - cursor, clockSeqAndMac);
securec_check(rc, "", "");
VarChar* result = (VarChar*)cstring_to_text_with_len(res, FORMATTED_UUID_LEN);
PG_RETURN_VARCHAR_P(result);
}
uint64 uuidShortInit()
{
int transBits = 24;
int timeMultiple = 10;
#ifndef HAVE_INT64_TIMESTAMP
int timeMultipleToSecond = 1000;
#endif
uint64 uuid = 0;
uint64 curr_time = 0;
uint64 start_time = 0;
uint64 start_timeup = 0;
TimestampTz startTimeStampTz = t_thrd.time_cxt.pg_start_time;
#ifdef HAVE_INT64_TIMESTAMP
curr_time = GetCurrentTimestamp() * timeMultiple;
start_time = startTimeStampTz * timeMultiple;
#else
curr_time = GetCurrentTimestamp() * timeMultipleToSecond * timeMultipleToSecond * timeMultiple;
start_time = startTimeStampTz * timeMultipleToSecond * timeMultipleToSecond * timeMultiple;
#endif
start_timeup = curr_time-start_time;
uuid = start_timeup << transBits;
return uuid;
}
uint64 uuid_value = uuidShortInit();
Datum uuid_short(PG_FUNCTION_ARGS)
{
uint64 uuid = 0;
uuid = pg_atomic_fetch_add_u64(&uuid_value, 1);
PG_RETURN_UINT64(uuid);
}
#endif
* PGStrongRandom
*
* Generate requested number of random bytes. The returned bytes are
* cryptographically secure, suitable for use e.g. in authentication.
*
* We rely on system facilities for actually generating the numbers:
*
* OpenSSL's RAND_bytes()
*
*/
static void PGStrongRandom(pg_uuid_t *buf, size_t len)
{
int i;
* Check that OpenSSL's CSPRNG has been sufficiently seeded, and if not
* add more seed data using RAND_poll(). With some older versions of
* OpenSSL, it may be necessary to call RAND_poll() a number of times. If
* RAND_poll() fails to generate seed data within the given amount of
* retries, subsequent RAND_bytes() calls will fail, but we allow that to
* happen to let PGStrongRandom() callers handle that with appropriate
* error handling.
*/
#define NUM_RAND_POLL_RETRIES 8
for (i = 0; i < NUM_RAND_POLL_RETRIES; i++) {
if (RAND_status() == 1) {
break;
}
RAND_poll();
}
int ret = RAND_bytes((unsigned char*)buf, len);
if (ret != 1) {
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("could not generate random values, errcode:%d", ret)));
}
}
* Set the given UUID version and the variant bits
*/
static inline void UUIDSetVersion(pg_uuid_t *uuid, unsigned char version)
{
int versionField = 6;
int variantField = 8;
int hibit = 4;
uuid->data[versionField] = (uuid->data[versionField] & 0x0f) | (version << hibit);
uuid->data[variantField] = (uuid->data[variantField] & 0x3f) | 0x80;
}
* Generate UUID version 4.
*
* All UUID bytes are filled with strong random numbers except version and
* variant bits.
*/
Datum gen_random_uuid(PG_FUNCTION_ARGS)
{
pg_uuid_t *uuid = (pg_uuid_t*)palloc(UUID_LEN);
PGStrongRandom(uuid, UUID_LEN);
* Set magic numbers for a "version 4" (pseudorandom) UUID and variant,
* see https://datatracker.ietf.org/doc/html/rfc9562#name-uuid-version-4
*/
UUIDSetVersion(uuid, 4);
PG_RETURN_UUID_P(uuid);
}