Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .unreleased/pr_8505
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implements: #8505 Add support for partitioning on UUIDv7
3 changes: 2 additions & 1 deletion src/dimension.c
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,7 @@ get_default_interval(Oid dimtype, bool adaptive_chunking)
case TIMESTAMPOID:
case TIMESTAMPTZOID:
case DATEOID:
case UUIDOID:
interval = adaptive_chunking ? DEFAULT_CHUNK_TIME_INTERVAL_ADAPTIVE :
DEFAULT_CHUNK_TIME_INTERVAL;
break;
Expand Down Expand Up @@ -1085,7 +1086,7 @@ dimension_interval_to_internal(const char *colname, Oid dimtype, Oid valuetype,
interval = get_validated_integer_interval(dimtype, DatumGetInt64(value));
break;
case INTERVALOID:
if (!IS_TIMESTAMP_TYPE(dimtype))
if (!IS_TIMESTAMP_TYPE(dimtype) && !IS_UUID_TYPE(dimtype))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid interval type for %s dimension", format_type_be(dimtype)),
Expand Down
3 changes: 2 additions & 1 deletion src/dimension.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ typedef struct Dimension
#define IS_OPEN_DIMENSION(d) ((d)->type == DIMENSION_TYPE_OPEN)
#define IS_CLOSED_DIMENSION(d) ((d)->type == DIMENSION_TYPE_CLOSED)
#define IS_VALID_OPEN_DIM_TYPE(type) \
(IS_INTEGER_TYPE(type) || IS_TIMESTAMP_TYPE(type) || ts_type_is_int8_binary_compatible(type))
(IS_INTEGER_TYPE(type) || IS_TIMESTAMP_TYPE(type) || IS_UUID_TYPE(type) || \
ts_type_is_int8_binary_compatible(type))

/*
* A hyperspace defines how to partition in a N-dimensional space.
Expand Down
15 changes: 14 additions & 1 deletion src/time_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ ts_time_datum_get_min(Oid timetype)
return Int32GetDatum(PG_INT32_MIN);
case INT8OID:
return Int64GetDatum(PG_INT64_MIN);
case UUIDOID:
return Int64GetDatum(TS_TIME_UUID_MIN);
default:
break;
}
Expand Down Expand Up @@ -229,6 +231,7 @@ ts_time_datum_get_end(Oid timetype)
case INT2OID:
case INT4OID:
case INT8OID:
case UUIDOID:
elog(ERROR, "END is not defined for \"%s\"", format_type_be(timetype));
break;
default:
Expand All @@ -255,7 +258,8 @@ ts_time_datum_get_max(Oid timetype)
return Int32GetDatum(PG_INT32_MAX);
case INT8OID:
return Int64GetDatum(PG_INT64_MAX);
break;
case UUIDOID:
return Int64GetDatum(TS_TIME_UUID_MAX);
default:
break;
}
Expand All @@ -277,6 +281,7 @@ ts_time_datum_get_nobegin(Oid timetype)
case INT2OID:
case INT4OID:
case INT8OID:
case UUIDOID:
elog(ERROR, "NOBEGIN is not defined for \"%s\"", format_type_be(timetype));
break;
default:
Expand Down Expand Up @@ -309,6 +314,7 @@ ts_time_datum_get_noend(Oid timetype)
case INT2OID:
case INT4OID:
case INT8OID:
case UUIDOID:
elog(ERROR, "NOEND is not defined for \"%s\"", format_type_be(timetype));
break;
default:
Expand Down Expand Up @@ -338,6 +344,8 @@ ts_time_get_min(Oid timetype)
return PG_INT32_MIN;
case INT8OID:
return PG_INT64_MIN;
case UUIDOID:
return TS_TIME_UUID_MIN;
default:
break;
}
Expand Down Expand Up @@ -365,6 +373,8 @@ ts_time_get_max(Oid timetype)
return PG_INT32_MAX;
case INT8OID:
return PG_INT64_MAX;
case UUIDOID:
return TS_TIME_UUID_MAX;
default:
break;
}
Expand All @@ -391,6 +401,7 @@ ts_time_get_end(Oid timetype)
case INT2OID:
case INT4OID:
case INT8OID:
case UUIDOID:
elog(ERROR, "END is not defined for \"%s\"", format_type_be(timetype));
break;
default:
Expand Down Expand Up @@ -426,6 +437,7 @@ ts_time_get_nobegin(Oid timetype)
case INT2OID:
case INT4OID:
case INT8OID:
case UUIDOID:
elog(ERROR, "-Infinity not defined for \"%s\"", format_type_be(timetype));
break;
default:
Expand Down Expand Up @@ -456,6 +468,7 @@ ts_time_get_noend(Oid timetype)
case INT2OID:
case INT4OID:
case INT8OID:
case UUIDOID:
elog(ERROR, "+Infinity not defined for \"%s\"", format_type_be(timetype));
break;
default:
Expand Down
17 changes: 16 additions & 1 deletion src/time_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,24 @@
#define TS_TIME_NOBEGIN (PG_INT64_MIN)
#define TS_TIME_NOEND (PG_INT64_MAX)

/*
* A UUIDv7 timestamp is 6 bytes milliseconds in Unix epoch (unsigned).
*
* Since RFC9562 specifies the timestamp as unsigned, the minimum value is
* 0. Further, the sub-millisecond part cannot be used as time since the bits
* are optional and it is not possible to know if they are random or represent
* a time fraction. Therefore, the max value is limited to the milliseconds.
*/
#define TS_TIME_UUID_MS_MIN (0x000000000000)
#define TS_TIME_UUID_MIN (0x000000000000 * 1000) /* microseconds */
#define TS_TIME_UUID_MS_MAX (0xFFFFFFFFFFFF)
#define TS_TIME_UUID_MAX (TS_TIME_UUID_MS_MAX * 1000) /* microseconds */

#define IS_INTEGER_TYPE(type) (type == INT2OID || type == INT4OID || type == INT8OID)
#define IS_TIMESTAMP_TYPE(type) (type == TIMESTAMPOID || type == TIMESTAMPTZOID || type == DATEOID)
#define IS_VALID_TIME_TYPE(type) (IS_INTEGER_TYPE(type) || IS_TIMESTAMP_TYPE(type))
#define IS_UUID_TYPE(type) (type == UUIDOID)
#define IS_VALID_TIME_TYPE(type) \
(IS_INTEGER_TYPE(type) || IS_TIMESTAMP_TYPE(type) || IS_UUID_TYPE(type))

#define TS_TIME_DATUM_IS_MIN(timeval, type) (timeval == ts_time_datum_get_min(type))
#define TS_TIME_DATUM_IS_MAX(timeval, type) (timeval == ts_time_datum_get_max(type))
Expand Down
42 changes: 41 additions & 1 deletion src/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@
#include <utils/builtins.h>
#include <utils/catcache.h>
#include <utils/date.h>
#include <utils/elog.h>
#include <utils/fmgroids.h>
#include <utils/fmgrprotos.h>
#include <utils/lsyscache.h>
#include <utils/relcache.h>
#include <utils/snapmgr.h>
#include <utils/syscache.h>
#include <utils/timestamp.h>
#include <utils/uuid.h>

#include "compat/compat.h"
#include "chunk.h"
Expand All @@ -47,6 +50,7 @@
#include "jsonb_utils.h"
#include "time_utils.h"
#include "utils.h"
#include "uuid.h"

typedef struct
{
Expand Down Expand Up @@ -148,7 +152,7 @@ ts_time_value_to_internal(Datum time_val, Oid type_oid)
elog(ERROR, "unknown time type \"%s\"", format_type_be(type_oid));
}

if (IS_INTEGER_TYPE(type_oid))
if (IS_INTEGER_TYPE(type_oid) || IS_UUID_TYPE(type_oid))
{
/* Integer time types have no distinction between min, max and
* infinity. We don't want min and max to be turned into infinity for
Expand Down Expand Up @@ -189,6 +193,30 @@ ts_time_value_to_internal(Datum time_val, Oid type_oid)
res = DirectFunctionCall1(ts_pg_timestamp_to_unix_microseconds, tz);

return DatumGetInt64(res);
case UUIDOID:
{
uint64 unixtime_ms = 0;

/*
* Extract the unix timestamp from the UUID. Note that we cannot
* use the (optional) sub-milliseconds part because there is no
* way to know whether it represents time or is random.
*
* If the UUID is not v7, error out.
*/
if (!ts_uuid_v7_extract_unixtime(DatumGetUUIDP(time_val), &unixtime_ms, NULL))
{
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("%s is not a version 7 UUID",
DatumGetCString(DirectFunctionCall1(uuid_out, time_val))),
errdetail(
"UUID \"time\" partitioning columns only support version 7 UUIDs."));
}

/* Convert to microseconds */
return unixtime_ms * 1000;
}
default:
elog(ERROR, "unknown time type \"%s\"", format_type_be(type_oid));
return -1;
Expand Down Expand Up @@ -338,6 +366,17 @@ ts_internal_to_time_value(int64 value, Oid type)
return DirectFunctionCall1(ts_pg_unix_microseconds_to_timestamp, Int64GetDatum(value));
case DATEOID:
return DirectFunctionCall1(ts_pg_unix_microseconds_to_date, Int64GetDatum(value));
case UUIDOID:
{
/*
* Convert the internal unixtime in ms to a UUID with the
* non-timestamp bits set to zero. We do not set the version
* either, because for ranges we only care about the prefix in
* order to divide the whole UUID space into a set of slices.
*/
const pg_uuid_t *uuid = ts_create_uuid_v7_from_unixtime_us(value, true, false);
return UUIDPGetDatum(uuid);
}
default:
if (ts_type_is_int8_binary_compatible(type))
return Int64GetDatum(value);
Expand Down Expand Up @@ -365,6 +404,7 @@ ts_internal_to_time_int64(int64 value, Oid type)
return value;
case TIMESTAMPOID:
case TIMESTAMPTZOID:
case UUIDOID:
/* we continue ts_time_value_to_internal's incorrect handling of TIMESTAMPs for
* compatibility */
return DatumGetInt64(
Expand Down
15 changes: 9 additions & 6 deletions src/uuid.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ ts_uuid_generate(PG_FUNCTION_ARGS)
}

pg_uuid_t *
ts_create_uuid_v7_from_unixtime_us(int64 unixtime_us, bool zeroed)
ts_create_uuid_v7_from_unixtime_us(int64 unixtime_us, bool zeroed, bool set_version)
{
pg_uuid_t *uuid;
uint64_t timestamp_be = pg_hton64((unixtime_us / 1000) << 16);
Expand Down Expand Up @@ -87,11 +87,14 @@ ts_create_uuid_v7_from_unixtime_us(int64 unixtime_us, bool zeroed)
uuid->data[6] = (unsigned char) (ts_micros >> 8);
uuid->data[7] = (unsigned char) ts_micros;

/* Set version 7 (0111) in bits 6-7 of byte 6, keep random bits 0-5 */
uuid->data[6] = (uuid->data[6] & 0x0F) | 0x70;
if (set_version)
{
/* Set version 7 (0111) in bits 6-7 of byte 6, keep random bits 0-5 */
uuid->data[6] = (uuid->data[6] & 0x0F) | 0x70;

/* Set variant (10) in bits 4-5 of byte 8, keep random bits 0-3 and 6-7 */
uuid->data[8] = (uuid->data[8] & 0x3F) | 0x80;
/* Set variant (10) in bits 4-5 of byte 8, keep random bits 0-3 and 6-7 */
uuid->data[8] = (uuid->data[8] & 0x3F) | 0x80;
}

return uuid;
}
Expand All @@ -102,7 +105,7 @@ ts_create_uuid_v7_from_timestamptz(TimestampTz ts, bool zeroed)
int64 epoch_diff_us = ((int64) (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * USECS_PER_DAY);
int64 unixtime_us = ts + epoch_diff_us;

return ts_create_uuid_v7_from_unixtime_us(unixtime_us, zeroed);
return ts_create_uuid_v7_from_unixtime_us(unixtime_us, zeroed, true);
}

TS_FUNCTION_INFO_V1(ts_uuid_generate_v7);
Expand Down
3 changes: 2 additions & 1 deletion src/uuid.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
#include <utils/uuid.h>

extern pg_uuid_t *ts_uuid_create(void);
extern pg_uuid_t *ts_create_uuid_v7_from_unixtime_us(int64 unixtime_us, bool zeroed);
extern pg_uuid_t *ts_create_uuid_v7_from_unixtime_us(int64 unixtime_us, bool zeroed,
bool set_version);
extern TSDLLEXPORT pg_uuid_t *ts_create_uuid_v7_from_timestamptz(TimestampTz ts, bool zero);
extern bool ts_uuid_v7_extract_unixtime(const pg_uuid_t *uuid, uint64 *unixtime_ms,
uint16 *extra_us);
Loading
Loading