Skip to content

Commit

Permalink
Simplify hypertable DDL APIs
Browse files Browse the repository at this point in the history
The current implementation of hypertable DDL APIs is very much inclined
towards tables having a "time" column. The idea with this patch is to
implement generic hypertable DDL APIs which can be used to convert a
regular PostgreSQL table with TIMESTAMP/SERIAL/BEGSERIAL columns into
a hypertable.

The new APIs are being added to "timescaledb_experimental" schema and
the transition from old to new APIs is based on the following factors:
- Remove/update attributes having specific time reference
- Drop attributes related to deprecated features
- Remove attributes with minimal usage patterns
- Remove attributes specific to distributed hypertable

Similar changes are done in DDL API code path to remove "time" specific
references. The APIs affected as part of these changes are:
- create_hypertable
- add_dimension
- set_chunk_time_interval

Also, added support for using SERIAL/BIGSERIAL as partition columns.
  • Loading branch information
pdipesh02 committed Sep 11, 2023
1 parent ef783c4 commit b45866b
Show file tree
Hide file tree
Showing 25 changed files with 1,263 additions and 270 deletions.
1 change: 1 addition & 0 deletions .unreleased/feature_5761
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implements: #5761 Simplify hypertable DDL API
52 changes: 52 additions & 0 deletions sql/ddl_experimental.sql
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,55 @@ CREATE OR REPLACE FUNCTION timescaledb_experimental.subscription_exec(
CREATE OR REPLACE PROCEDURE timescaledb_experimental.cleanup_copy_chunk_operation(
operation_id NAME)
AS '@MODULE_PATHNAME@', 'ts_copy_chunk_cleanup_proc' LANGUAGE C;

-- A generalized hypertable creation API that can be used to convert a PostgreSQL table
-- with TIME/SERIAL/BIGSERIAL columns to a hypertable.
--
-- relation - The OID of the table to be converted
-- partition_column - Name of the partition column
-- partition_interval (Optional) Initial interval for the chunks
-- partition_func (Optional) Partitioning function to be used for partition column
-- create_default_indexes (Optional) Whether or not to create the default indexes
-- if_not_exists (Optional) Do not fail if table is already a hypertable
-- migrate_data (Optional) Set to true to migrate any existing data in the table to chunks
CREATE OR REPLACE FUNCTION timescaledb_experimental.create_hypertable(
relation REGCLASS,
partition_column NAME,
partition_interval ANYELEMENT = NULL::BIGINT,
partition_func REGPROC = NULL,
create_default_indexes BOOLEAN = TRUE,
if_not_exists BOOLEAN = FALSE,
migrate_data BOOLEAN = FALSE
) RETURNS TABLE(hypertable_id INT, created BOOL) AS '@MODULE_PATHNAME@', 'ts_hypertable_create_general' LANGUAGE C VOLATILE;

-- Add a dimension (of partitioning) to a hypertable
--
-- hypertable - OID of the table to add a dimension to
-- column_name - NAME of the column to use in partitioning for this dimension
-- number_partitions - Number of partitions, for closed dimensions
-- partition_interval - Size of intervals for open dimensions (can be integral or INTERVAL)
-- partition_func - Function used to partition the column
-- if_not_exists - If set, and the dimension already exists, generate a notice instead of an error
CREATE OR REPLACE FUNCTION timescaledb_experimental.add_dimension(
hypertable REGCLASS,
column_name NAME,
number_partitions INTEGER = NULL,
partition_interval ANYELEMENT = NULL::BIGINT,
partition_func REGPROC = NULL,
if_not_exists BOOLEAN = FALSE
) RETURNS TABLE(dimension_id INT, created BOOL)
AS '@MODULE_PATHNAME@', 'ts_dimension_add_general' LANGUAGE C VOLATILE;

-- Update partition_interval for a hypertable.
--
-- hypertable - The OID of the table corresponding to a hypertable whose
-- partition interval should be updated
-- partition_interval - The new interval. For hypertables with integral/serial/bigserial
-- time columns, this must be an integral type. For hypertables with a
-- TIMESTAMP/TIMESTAMPTZ/DATE type, it can be integral which is treated as
-- microseconds, or an INTERVAL type.
CREATE OR REPLACE FUNCTION timescaledb_experimental.set_partitioning_interval(
hypertable REGCLASS,
partition_interval ANYELEMENT,
dimension_name NAME = NULL
) RETURNS VOID AS '@MODULE_PATHNAME@', 'ts_dimension_set_interval' LANGUAGE C VOLATILE;
26 changes: 26 additions & 0 deletions sql/updates/latest-dev.sql
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,29 @@ ALTER FUNCTION _timescaledb_internal.finalize_agg_ffunc(internal,text,name,name,
ALTER FUNCTION _timescaledb_internal.finalize_agg_sfunc(internal,text,name,name,name[],bytea,anyelement) SET SCHEMA _timescaledb_functions;
ALTER FUNCTION _timescaledb_internal.partialize_agg(anyelement) SET SCHEMA _timescaledb_functions;

-- API changes related to hypertable generalization
CREATE OR REPLACE FUNCTION timescaledb_experimental.create_hypertable(
relation REGCLASS,
partition_column NAME,
partition_interval ANYELEMENT = NULL::BIGINT,
partition_func REGPROC = NULL,
create_default_indexes BOOLEAN = TRUE,
if_not_exists BOOLEAN = FALSE,
migrate_data BOOLEAN = FALSE
) RETURNS TABLE(hypertable_id INT, created BOOL) AS '@MODULE_PATHNAME@', 'ts_hypertable_create_general' LANGUAGE C VOLATILE;

CREATE OR REPLACE FUNCTION timescaledb_experimental.add_dimension(
hypertable REGCLASS,
column_name NAME,
number_partitions INTEGER = NULL,
partition_interval ANYELEMENT = NULL::BIGINT,
partition_func REGPROC = NULL,
if_not_exists BOOLEAN = FALSE
) RETURNS TABLE(dimension_id INT, created BOOL)
AS '@MODULE_PATHNAME@', 'ts_dimension_add_general' LANGUAGE C VOLATILE;

CREATE OR REPLACE FUNCTION timescaledb_experimental.set_partitioning_interval(
hypertable REGCLASS,
partition_interval ANYELEMENT,
dimension_name NAME = NULL
) RETURNS VOID AS '@MODULE_PATHNAME@', 'ts_dimension_set_interval' LANGUAGE C VOLATILE;
4 changes: 4 additions & 0 deletions sql/updates/reverse-dev.sql
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,7 @@ ALTER FUNCTION _timescaledb_functions.finalize_agg_sfunc(internal,text,name,name
ALTER FUNCTION _timescaledb_functions.partialize_agg(anyelement) SET SCHEMA _timescaledb_internal;
ALTER AGGREGATE _timescaledb_functions.finalize_agg(text,name,name,name[][],bytea,anyelement) SET SCHEMA _timescaledb_internal;

-- API changes related to hypertable generalization
DROP FUNCTION IF EXISTS timescaledb_experimental.create_hypertable;
DROP FUNCTION IF EXISTS timescaledb_experimental.add_dimension;
DROP FUNCTION IF EXISTS timescaledb_experimental.set_partitioning_interval;
119 changes: 95 additions & 24 deletions src/dimension.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ enum Anum_add_dimension

#define Natts_add_dimension (_Anum_add_dimension_max - 1)

/*
* Generic add dimension attributes
*/
enum Anum_generic_add_dimension
{
Anum_generic_add_dimension_id = 1,
Anum_generic_add_dimension_created,
_Anum_generic_add_dimension_max,
};

#define Natts_generic_add_dimension (_Anum_generic_add_dimension_max - 1)

static int
cmp_dimension_id(const void *left, const void *right)
{
Expand Down Expand Up @@ -1032,6 +1044,42 @@ get_validated_integer_interval(Oid dimtype, int64 value)
return value;
}

/*
* Get the default chunk interval based on dimension type.
*/
static int64
get_default_interval(Oid dimtype, bool adaptive_chunking)
{
int64 interval;

switch (dimtype)
{
case INT2OID:
interval = DEFAULT_SMALLINT_INTERVAL;
break;
case INT4OID:
interval = DEFAULT_INT_INTERVAL;
break;
case INT8OID:
interval = DEFAULT_BIGINT_INTERVAL;
break;
case TIMESTAMPOID:
case TIMESTAMPTZOID:
case DATEOID:
interval = adaptive_chunking ? DEFAULT_CHUNK_TIME_INTERVAL_ADAPTIVE :
DEFAULT_CHUNK_TIME_INTERVAL;
break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot get default interval for %s dimension",
format_type_be(dimtype)),
errhint("Use a valid dimension type.")));
}

return interval;
}

static int64
dimension_interval_to_internal(const char *colname, Oid dimtype, Oid valuetype, Datum value,
bool adaptive_chunking)
Expand All @@ -1046,13 +1094,7 @@ dimension_interval_to_internal(const char *colname, Oid dimtype, Oid valuetype,

if (!OidIsValid(valuetype))
{
if (IS_INTEGER_TYPE(dimtype))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("integer dimensions require an explicit interval")));

value = Int64GetDatum(adaptive_chunking ? DEFAULT_CHUNK_TIME_INTERVAL_ADAPTIVE :
DEFAULT_CHUNK_TIME_INTERVAL);
value = Int64GetDatum(get_default_interval(dimtype, adaptive_chunking));
valuetype = INT8OID;
}

Expand Down Expand Up @@ -1121,7 +1163,7 @@ dimension_add_not_null_on_column(Oid table_relid, char *colname)

ereport(NOTICE,
(errmsg("adding not-null constraint to column \"%s\"", colname),
errdetail("Time dimensions cannot have NULL values.")));
errdetail("Dimensions cannot have NULL values.")));

ts_alter_table_with_event_trigger(table_relid, (Node *) &cmd, list_make1(&cmd), false);
}
Expand Down Expand Up @@ -1476,12 +1518,10 @@ ts_dimension_add_from_info(DimensionInfo *info)
* Create a datum to be returned by add_dimension DDL function
*/
static Datum
dimension_create_datum(FunctionCallInfo fcinfo, DimensionInfo *info)
dimension_create_datum(FunctionCallInfo fcinfo, DimensionInfo *info, bool is_generic)
{
TupleDesc tupdesc;
HeapTuple tuple;
Datum values[Natts_add_dimension];
bool nulls[Natts_add_dimension] = { false };

if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
ereport(ERROR,
Expand All @@ -1490,20 +1530,36 @@ dimension_create_datum(FunctionCallInfo fcinfo, DimensionInfo *info)
"context that cannot accept type record")));

tupdesc = BlessTupleDesc(tupdesc);
values[AttrNumberGetAttrOffset(Anum_add_dimension_id)] = info->dimension_id;
values[AttrNumberGetAttrOffset(Anum_add_dimension_schema_name)] =
NameGetDatum(&info->ht->fd.schema_name);
values[AttrNumberGetAttrOffset(Anum_add_dimension_table_name)] =
NameGetDatum(&info->ht->fd.table_name);
values[AttrNumberGetAttrOffset(Anum_add_dimension_column_name)] = NameGetDatum(info->colname);
values[AttrNumberGetAttrOffset(Anum_add_dimension_created)] = BoolGetDatum(!info->skip);
tuple = heap_form_tuple(tupdesc, values, nulls);

if (is_generic)
{
Datum values[Natts_generic_add_dimension];
bool nulls[Natts_generic_add_dimension] = { false };

values[AttrNumberGetAttrOffset(Anum_generic_add_dimension_id)] = info->dimension_id;
values[AttrNumberGetAttrOffset(Anum_generic_add_dimension_created)] =
BoolGetDatum(!info->skip);
tuple = heap_form_tuple(tupdesc, values, nulls);
}
else
{
Datum values[Natts_add_dimension];
bool nulls[Natts_add_dimension] = { false };

values[AttrNumberGetAttrOffset(Anum_add_dimension_id)] = info->dimension_id;
values[AttrNumberGetAttrOffset(Anum_add_dimension_schema_name)] =
NameGetDatum(&info->ht->fd.schema_name);
values[AttrNumberGetAttrOffset(Anum_add_dimension_table_name)] =
NameGetDatum(&info->ht->fd.table_name);
values[AttrNumberGetAttrOffset(Anum_add_dimension_column_name)] =
NameGetDatum(info->colname);
values[AttrNumberGetAttrOffset(Anum_add_dimension_created)] = BoolGetDatum(!info->skip);
tuple = heap_form_tuple(tupdesc, values, nulls);
}

return HeapTupleGetDatum(tuple);
}

TS_FUNCTION_INFO_V1(ts_dimension_add);

/*
* Add a new dimension to a hypertable.
*
Expand All @@ -1515,8 +1571,8 @@ TS_FUNCTION_INFO_V1(ts_dimension_add);
* 4. Partitioning function
* 5. IF NOT EXISTS option (bool)
*/
Datum
ts_dimension_add(PG_FUNCTION_ARGS)
static Datum
ts_dimension_add_internal(PG_FUNCTION_ARGS, bool is_generic)
{
Cache *hcache;
DimensionInfo info = {
Expand Down Expand Up @@ -1648,12 +1704,27 @@ ts_dimension_add(PG_FUNCTION_ARGS)

ts_hypertable_func_call_on_data_nodes(info.ht, fcinfo);

retval = dimension_create_datum(fcinfo, &info);
retval = dimension_create_datum(fcinfo, &info, is_generic);
ts_cache_release(hcache);

PG_RETURN_DATUM(retval);
}

TS_FUNCTION_INFO_V1(ts_dimension_add);
TS_FUNCTION_INFO_V1(ts_dimension_add_general);

Datum
ts_dimension_add(PG_FUNCTION_ARGS)
{
return ts_dimension_add_internal(fcinfo, false);
}

Datum
ts_dimension_add_general(PG_FUNCTION_ARGS)
{
return ts_dimension_add_internal(fcinfo, true);
}

/* Used as a tuple found function */
static ScanTupleResult
dimension_rename_schema_name(TupleInfo *ti, void *data)
Expand Down
5 changes: 5 additions & 0 deletions src/dimension.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ typedef struct Point
(USECS_PER_DAY) /* 1 day with adaptive \
* chunking enabled */

/* Default intervals for integer types */
#define DEFAULT_SMALLINT_INTERVAL 10000
#define DEFAULT_INT_INTERVAL 100000
#define DEFAULT_BIGINT_INTERVAL 1000000

typedef struct Hypertable Hypertable;

/*
Expand Down
Loading

0 comments on commit b45866b

Please sign in to comment.