1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-02 09:02:37 +03:00

Add a new builtin type, "uuid". This implements a UUID type, similar to

that defined in RFC 4122. This patch includes the basic implementation,
plus regression tests. Documentation and perhaps some additional
functionality will come later. Catversion bumped.

Patch from Gevik Babakhani; review from Peter, Tom, and myself.
This commit is contained in:
Neil Conway
2007-01-28 16:16:54 +00:00
parent a8e0b66061
commit a534068e0e
17 changed files with 621 additions and 16 deletions

View File

@ -0,0 +1,279 @@
/*-------------------------------------------------------------------------
*
* uuid.c
* Functions for the built-in type "uuid".
*
* Copyright (c) 2007, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/uuid.c,v 1.1 2007/01/28 16:16:52 neilc Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/hash.h"
#include "libpq/pqformat.h"
#include "utils/builtins.h"
#include "utils/uuid.h"
/* Accepted GUID formats */
/* UUID_FMT1 is the default output format */
#define UUID_FMT1 "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
#define UUID_FMT2 "{%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx}"
#define UUID_FMT3 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
/* UUIDs are accepted in any of the following textual input formats. */
#define UUID_CHK_FMT1 "00000000-0000-0000-0000-000000000000"
#define UUID_CHK_FMT2 "{00000000-0000-0000-0000-000000000000}"
#define UUID_CHK_FMT3 "00000000000000000000000000000000"
#define PRINT_SIZE 40
/* uuid size in bytes */
#define UUID_LEN 16
/* The uuid_t type is declared as struct uuid_t in uuid.h */
struct uuid_t
{
char data[UUID_LEN];
};
static void uuid_data_from_string(const char *source, unsigned char *data);
static void string_from_uuid_data(const char *fmt, const char *data, char *uuid_str);
static bool parse_uuid_string(const char *fmt, const char *chk_fmt,
const char *source, unsigned char *data);
static bool is_valid_format(const char *source, const char *fmt);
static int32 uuid_internal_cmp(uuid_t *arg1, uuid_t *arg2);
Datum
uuid_in(PG_FUNCTION_ARGS)
{
char *uuid_str = PG_GETARG_CSTRING(0);
uuid_t *uuid;
uint8 data[UUID_LEN];
uuid_data_from_string(uuid_str, data);
uuid = (uuid_t *) palloc(sizeof(uuid_t));
memcpy(uuid->data, data, UUID_LEN);
PG_RETURN_UUID_P(uuid);
}
Datum
uuid_out(PG_FUNCTION_ARGS)
{
uuid_t *uuid = (uuid_t *) PG_GETARG_POINTER(0);
char *uuid_str;
uuid_str = (char *) palloc(PRINT_SIZE);
string_from_uuid_data(UUID_FMT1, uuid->data, uuid_str);
PG_RETURN_CSTRING(uuid_str);
}
/* string to uuid convertor by various format types */
static void
uuid_data_from_string(const char *source, unsigned char *data)
{
if (!parse_uuid_string(UUID_FMT1, UUID_CHK_FMT1, source, data) &&
!parse_uuid_string(UUID_FMT2, UUID_CHK_FMT2, source, data) &&
!parse_uuid_string(UUID_FMT3, UUID_CHK_FMT3, source, data))
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for uuid: \"%s\"",
source)));
}
}
/* check the validity of a uuid string by a given format */
static bool
is_valid_format(const char *source, const char *fmt)
{
int i;
int fmtlen = strlen(fmt);
/* check length first */
if (fmtlen != strlen(source))
return false;
for (i = 0; i < fmtlen; i++)
{
int fc;
int sc;
bool valid_chr;
fc = fmt[i];
sc = source[i];
/* false if format chr is { or - and source is not */
if (fc != '0' && fc != sc)
return false;
/* check for valid char in source */
valid_chr = (sc >= '0' && sc <= '9') ||
(sc >= 'a' && sc <= 'f' ) ||
(sc >= 'A' && sc <= 'F' );
if (fc == '0' && !valid_chr)
return false;
}
return true;
}
/* parse the uuid string to a format and return true if okay */
static bool
parse_uuid_string(const char *fmt, const char *chk_fmt,
const char *source, unsigned char *data)
{
int result = sscanf(source, fmt,
&data[0], &data[1], &data[2], &data[3], &data[4],
&data[5], &data[6], &data[7], &data[8], &data[9],
&data[10], &data[11], &data[12], &data[13],
&data[14], &data[15]);
return (result == 16) && is_valid_format(source, chk_fmt);
}
/* create a string representation of the uuid */
static void
string_from_uuid_data(const char *fmt, const char *data, char *uuid_str)
{
snprintf(uuid_str, PRINT_SIZE, fmt,
data[0], data[1], data[2], data[3], data[4],
data[5], data[6], data[7], data[8], data[9],
data[10], data[11], data[12], data[13],
data[14], data[15]);
}
Datum
uuid_recv(PG_FUNCTION_ARGS)
{
StringInfo buffer = (StringInfo) PG_GETARG_POINTER(0);
uuid_t *uuid;
uuid = (uuid_t *) palloc(UUID_LEN);
memcpy(uuid->data, pq_getmsgbytes(buffer, UUID_LEN), UUID_LEN);
PG_RETURN_POINTER(uuid);
}
Datum
uuid_send(PG_FUNCTION_ARGS)
{
uuid_t *uuid = PG_GETARG_UUID_P(0);
StringInfoData buffer;
pq_begintypsend(&buffer);
pq_sendbytes(&buffer, uuid->data, UUID_LEN);
PG_RETURN_BYTEA_P(pq_endtypsend(&buffer));
}
/* internal uuid compare function */
static int32
uuid_internal_cmp(uuid_t *arg1, uuid_t *arg2)
{
return memcmp(arg1->data, arg2->data, UUID_LEN);
}
Datum
uuid_lt(PG_FUNCTION_ARGS)
{
uuid_t *arg1 = PG_GETARG_UUID_P(0);
uuid_t *arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) < 0);
}
Datum
uuid_le(PG_FUNCTION_ARGS)
{
uuid_t *arg1 = PG_GETARG_UUID_P(0);
uuid_t *arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) <= 0);
}
Datum
uuid_eq(PG_FUNCTION_ARGS)
{
uuid_t *arg1 = PG_GETARG_UUID_P(0);
uuid_t *arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) == 0);
}
Datum
uuid_ge(PG_FUNCTION_ARGS)
{
uuid_t *arg1 = PG_GETARG_UUID_P(0);
uuid_t *arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) >= 0);
}
Datum
uuid_gt(PG_FUNCTION_ARGS)
{
uuid_t *arg1 = PG_GETARG_UUID_P(0);
uuid_t *arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) > 0);
}
Datum
uuid_ne(PG_FUNCTION_ARGS)
{
uuid_t *arg1 = PG_GETARG_UUID_P(0);
uuid_t *arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) != 0);
}
/* handler for btree index operator */
Datum
uuid_cmp(PG_FUNCTION_ARGS)
{
uuid_t *arg1 = PG_GETARG_UUID_P(0);
uuid_t *arg2 = PG_GETARG_UUID_P(1);
PG_RETURN_INT32(uuid_internal_cmp(arg1, arg2));
}
/* hash index support */
Datum
uuid_hash(PG_FUNCTION_ARGS)
{
uuid_t *key = PG_GETARG_UUID_P(0);
return hash_any((unsigned char *) key, sizeof(uuid_t));
}
/* cast text to uuid */
Datum
text_uuid(PG_FUNCTION_ARGS)
{
text *input = PG_GETARG_TEXT_P(0);
int length;
char *str;
Datum result;
length = VARSIZE(input) - VARHDRSZ;
str = palloc(length + 1);
memcpy(str, VARDATA(input), length);
*(str + length) = '\0';
result = DirectFunctionCall1(uuid_in, CStringGetDatum(str));
pfree(str);
PG_RETURN_DATUM(result);
}
/* cast uuid to text */
Datum
uuid_text(PG_FUNCTION_ARGS)
{
uuid_t *uuid = PG_GETARG_UUID_P(0);
Datum uuid_str = DirectFunctionCall1(uuid_out, UUIDPGetDatum(uuid));
PG_RETURN_DATUM(DirectFunctionCall1(textin, uuid_str));
}