From c19a4af3f8dd392ce2b24a26be170da6b592c5ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Nordstr=C3=B6m?= Date: Wed, 27 Sep 2023 15:01:00 +0200 Subject: [PATCH 1/3] Initial catalog changes --- sql/ddl_api.sql | 13 ++- sql/pre_install/tables.sql | 5 +- src/dimension.c | 141 +++++++++++++++++++++----- src/dimension.h | 9 +- src/hypertable.c | 17 +++- src/ts_catalog/catalog.h | 22 +++- src/ts_catalog/compression_settings.c | 2 +- src/ts_catalog/continuous_agg.h | 2 +- tsl/src/continuous_aggs/create.c | 5 +- 9 files changed, 176 insertions(+), 40 deletions(-) diff --git a/sql/ddl_api.sql b/sql/ddl_api.sql index 1a37b3f7032..b5a402ebba5 100644 --- a/sql/ddl_api.sql +++ b/sql/ddl_api.sql @@ -34,7 +34,8 @@ CREATE OR REPLACE FUNCTION @extschema@.create_hypertable( migrate_data BOOLEAN = FALSE, chunk_target_size TEXT = NULL, chunk_sizing_func REGPROC = '_timescaledb_functions.calculate_chunk_interval'::regproc, - time_partitioning_func REGPROC = NULL + time_partitioning_func REGPROC = NULL, + origin ANYELEMENT = NULL::timestamptz ) RETURNS TABLE(hypertable_id INT, schema_name NAME, table_name NAME, created BOOL) AS '@MODULE_PATHNAME@', 'ts_hypertable_create' LANGUAGE C VOLATILE; -- A generalized hypertable creation API that can be used to convert a PostgreSQL table @@ -50,10 +51,10 @@ CREATE OR REPLACE FUNCTION @extschema@.create_hypertable( dimension _timescaledb_internal.dimension_info, create_default_indexes BOOLEAN = TRUE, if_not_exists BOOLEAN = FALSE, - migrate_data BOOLEAN = FALSE + migrate_data BOOLEAN = FALSE, + origin ANYELEMENT = NULL::timestamptz ) RETURNS TABLE(hypertable_id INT, created BOOL) AS '@MODULE_PATHNAME@', 'ts_hypertable_create_general' LANGUAGE C VOLATILE; - -- Set adaptive chunking. To disable, set chunk_target_size => 'off'. CREATE OR REPLACE FUNCTION @extschema@.set_adaptive_chunking( hypertable REGCLASS, @@ -73,7 +74,8 @@ CREATE OR REPLACE FUNCTION @extschema@.set_adaptive_chunking( CREATE OR REPLACE FUNCTION @extschema@.set_chunk_time_interval( hypertable REGCLASS, chunk_time_interval ANYELEMENT, - dimension_name NAME = NULL + dimension_name NAME = NULL, + origin TIMESTAMPTZ = NULL ) RETURNS VOID AS '@MODULE_PATHNAME@', 'ts_dimension_set_interval' LANGUAGE C VOLATILE; -- Update partition_interval for a hypertable. @@ -133,7 +135,8 @@ CREATE OR REPLACE FUNCTION @extschema@.add_dimension( number_partitions INTEGER = NULL, chunk_time_interval ANYELEMENT = NULL::BIGINT, partitioning_func REGPROC = NULL, - if_not_exists BOOLEAN = FALSE + if_not_exists BOOLEAN = FALSE, + origin TIMESTAMPTZ = NULL ) RETURNS TABLE(dimension_id INT, schema_name NAME, table_name NAME, column_name NAME, created BOOL) AS '@MODULE_PATHNAME@', 'ts_dimension_add' LANGUAGE C VOLATILE; diff --git a/sql/pre_install/tables.sql b/sql/pre_install/tables.sql index 08a315bcd95..ca3cc80ee67 100644 --- a/sql/pre_install/tables.sql +++ b/sql/pre_install/tables.sql @@ -94,9 +94,12 @@ CREATE TABLE _timescaledb_catalog.dimension ( partitioning_func_schema name NULL, partitioning_func name NULL, -- open dimensions (e.g., time) - interval_length bigint NULL, + interval_origin bigint NOT NULL, -- origin of when to start chunk interval. TimestampTz or int64 (but always stored as a bigint). + interval interval NULL, -- calendar-based interval. + interval_length bigint NULL, -- integer-based interval -- compress interval is used by rollup procedure during compression -- in order to merge multiple chunks into a single one + compress_interval interval NULL, compress_interval_length bigint NULL, integer_now_func_schema name NULL, integer_now_func name NULL, diff --git a/src/dimension.c b/src/dimension.c index f6684b4c5f4..bc4cb674a3d 100644 --- a/src/dimension.c +++ b/src/dimension.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -232,11 +233,33 @@ dimension_fill_in_from_tuple(Dimension *d, TupleInfo *ti, Oid main_table_relid) DatumGetInt16(values[AttrNumberGetAttrOffset(Anum_dimension_num_slices)]); else { - d->fd.interval_length = - DatumGetInt64(values[AttrNumberGetAttrOffset(Anum_dimension_interval_length)]); + Assert(isnull[AttrNumberGetAttrOffset(Anum_dimension_interval)] || + isnull[AttrNumberGetAttrOffset(Anum_dimension_interval_length)]); + + if (!isnull[AttrNumberGetAttrOffset(Anum_dimension_interval)]) + { + d->fd.interval = + *DatumGetIntervalP(values[AttrNumberGetAttrOffset(Anum_dimension_interval)]); + d->calendar_based = true; + } + else + { + d->calendar_based = false; + } + + if (!isnull[AttrNumberGetAttrOffset(Anum_dimension_interval_length)]) + d->fd.interval_length = + DatumGetInt64(values[AttrNumberGetAttrOffset(Anum_dimension_interval_length)]); + if (!isnull[AttrNumberGetAttrOffset(Anum_dimension_compress_interval_length)]) d->fd.compress_interval_length = DatumGetInt64( values[AttrNumberGetAttrOffset(Anum_dimension_compress_interval_length)]); + + if (!isnull[AttrNumberGetAttrOffset(Anum_dimension_interval_origin)]) + { + d->fd.interval_origin = + DatumGetInt64(values[AttrNumberGetAttrOffset(Anum_dimension_interval_origin)]); + } } d->column_attno = get_attnum(main_table_relid, NameStr(d->fd.column_name)); @@ -782,7 +805,8 @@ dimension_tuple_update(TupleInfo *ti, void *data) static int32 dimension_insert_relation(Relation rel, int32 hypertable_id, Name colname, Oid coltype, - int16 num_slices, regproc partitioning_func, int64 interval_length) + int16 num_slices, regproc partitioning_func, int64 interval_length, + const Interval *interval, int64 interval_origin) { TupleDesc desc = RelationGetDescr(rel); Datum values[Natts_dimension]; @@ -816,13 +840,33 @@ dimension_insert_relation(Relation rel, int32 hypertable_id, Name colname, Oid c values[AttrNumberGetAttrOffset(Anum_dimension_num_slices)] = Int16GetDatum(num_slices); values[AttrNumberGetAttrOffset(Anum_dimension_aligned)] = BoolGetDatum(false); nulls[AttrNumberGetAttrOffset(Anum_dimension_interval_length)] = true; + nulls[AttrNumberGetAttrOffset(Anum_dimension_interval)] = true; + nulls[AttrNumberGetAttrOffset(Anum_dimension_interval_origin)] = true; } else { /* Open (time) dimension */ - Assert(num_slices <= 0 && interval_length > 0); - values[AttrNumberGetAttrOffset(Anum_dimension_interval_length)] = - Int64GetDatum(interval_length); + Assert(num_slices <= 0); + Assert(interval != NULL || interval_length > 0); + + if (interval) + { + /* Calendar-based interval */ + values[AttrNumberGetAttrOffset(Anum_dimension_interval)] = IntervalPGetDatum(interval); + nulls[AttrNumberGetAttrOffset(Anum_dimension_interval_length)] = true; + values[AttrNumberGetAttrOffset(Anum_dimension_interval_origin)] = + TimestampTzGetDatum(interval_length); + } + else + { + /* Integer interval */ + values[AttrNumberGetAttrOffset(Anum_dimension_interval_length)] = + Int64GetDatum(interval_length); + nulls[AttrNumberGetAttrOffset(Anum_dimension_interval)] = true; + values[AttrNumberGetAttrOffset(Anum_dimension_interval_origin)] = + Int64GetDatum(interval_length); + } + values[AttrNumberGetAttrOffset(Anum_dimension_aligned)] = BoolGetDatum(true); nulls[AttrNumberGetAttrOffset(Anum_dimension_num_slices)] = true; } @@ -831,7 +875,8 @@ dimension_insert_relation(Relation rel, int32 hypertable_id, Name colname, Oid c nulls[AttrNumberGetAttrOffset(Anum_dimension_integer_now_func_schema)] = true; nulls[AttrNumberGetAttrOffset(Anum_dimension_integer_now_func)] = true; - /* no compress interval length by default */ + /* no compress interval by default */ + nulls[AttrNumberGetAttrOffset(Anum_dimension_compress_interval)] = true; nulls[AttrNumberGetAttrOffset(Anum_dimension_compress_interval_length)] = true; ts_catalog_database_info_become_owner(ts_catalog_database_info_get(), &sec_ctx); @@ -845,7 +890,8 @@ dimension_insert_relation(Relation rel, int32 hypertable_id, Name colname, Oid c static int32 dimension_insert(int32 hypertable_id, Name colname, Oid coltype, int16 num_slices, - regproc partitioning_func, int64 interval_length) + regproc partitioning_func, int64 interval_length, const Interval *interval, + int64 interval_origin) { Catalog *catalog = ts_catalog_get(); Relation rel; @@ -858,7 +904,9 @@ dimension_insert(int32 hypertable_id, Name colname, Oid coltype, int16 num_slice coltype, num_slices, partitioning_func, - interval_length); + interval_length, + interval, + interval_origin); table_close(rel, RowExclusiveLock); return dimension_id; } @@ -1304,16 +1352,42 @@ ts_dimension_set_interval(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } +static Datum +open_dim_default_calendar_origin(Oid coltype) +{ + switch (coltype) + { + case TIMESTAMPOID: + case TIMESTAMPTZOID: + return TimestampTzGetDatum((int64) POSTGRES_EPOCH_JDATE * SECS_PER_DAY); + case DATEOID: + return DateADTGetDatum(POSTGRES_EPOCH_JDATE); + default: + break; + } + + return Int64GetDatum(0); +} + DimensionInfo * ts_dimension_info_create_open(Oid table_relid, Name column_name, Datum interval, Oid interval_type, - regproc partitioning_func) + regproc partitioning_func, Datum interval_origin, + bool interval_origin_isnull, Oid interval_origin_type) { DimensionInfo *info = palloc(sizeof(*info)); + AttrNumber colnum = get_attnum(table_relid, NameStr(*column_name)); + Oid coltype = get_atttype(table_relid, colnum); + *info = (DimensionInfo){ .type = DIMENSION_TYPE_OPEN, .table_relid = table_relid, + .coltype = coltype, .interval_datum = interval, .interval_type = interval_type, + .interval_origin = + interval_origin_isnull ? open_dim_default_calendar_origin(coltype) : interval_origin, + .interval_origin_isnull = interval_origin_isnull, + .interval_origin_type = interval_origin_type, .partitioning_func = partitioning_func, }; namestrcpy(&info->colname, NameStr(*column_name)); @@ -1325,9 +1399,12 @@ ts_dimension_info_create_closed(Oid table_relid, Name column_name, int32 num_sli regproc partitioning_func) { DimensionInfo *info = palloc(sizeof(*info)); + AttrNumber colnum = get_attnum(table_relid, NameStr(*column_name)); + *info = (DimensionInfo){ .type = DIMENSION_TYPE_CLOSED, .table_relid = table_relid, + .coltype = get_atttype(table_relid, colnum), .num_slices = num_slices, .num_slices_is_set = (num_slices > 0), .partitioning_func = partitioning_func, @@ -1358,11 +1435,18 @@ dimension_info_validate_open(DimensionInfo *info) dimtype = get_func_rettype(info->partitioning_func); } - info->interval = dimension_interval_to_internal(NameStr(info->colname), - dimtype, - info->interval_type, - info->interval_datum, - info->adaptive_chunking); + if (IS_TIMESTAMP_TYPE(dimtype)) + { + info->interval = 0; + } + else + { + info->interval = dimension_interval_to_internal(NameStr(info->colname), + dimtype, + info->interval_type, + info->interval_datum, + info->adaptive_chunking); + } } /* Validate the configuration of a closed ("space") dimension */ @@ -1418,8 +1502,7 @@ ts_dimension_info_validate(DimensionInfo *info) datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_atttypid, &isnull); Assert(!isnull); - - info->coltype = DatumGetObjectId(datum); + Assert(info->coltype == DatumGetObjectId(datum)); datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attnotnull, &isnull); Assert(!isnull); @@ -1485,12 +1568,24 @@ ts_dimension_add_from_info(DimensionInfo *info) Assert(info->ht != NULL); - info->dimension_id = dimension_insert(info->ht->fd.id, - &info->colname, - info->coltype, - info->num_slices, - info->partitioning_func, - info->interval); + if (IS_TIMESTAMP_TYPE(info->interval_type)) + info->dimension_id = dimension_insert(info->ht->fd.id, + &info->colname, + info->coltype, + info->num_slices, + info->partitioning_func, + DatumGetInt64(info->interval_datum), + NULL, + DatumGetInt64(info->interval_origin)); + else + info->dimension_id = dimension_insert(info->ht->fd.id, + &info->colname, + info->coltype, + info->num_slices, + info->partitioning_func, + 0, + DatumGetIntervalP(info->interval_datum), + DatumGetTimestampTz(info->interval_origin)); return info->dimension_id; } diff --git a/src/dimension.h b/src/dimension.h index d20a51d64a0..d72c007c2fe 100644 --- a/src/dimension.h +++ b/src/dimension.h @@ -32,6 +32,7 @@ typedef struct Dimension DimensionType type; AttrNumber column_attno; Oid main_table_relid; + bool calendar_based; PartitioningInfo *partitioning; } Dimension; @@ -40,6 +41,7 @@ typedef struct Dimension #define IS_VALID_OPEN_DIM_TYPE(type) \ (IS_INTEGER_TYPE(type) || IS_TIMESTAMP_TYPE(type) || ts_type_is_int8_binary_compatible(type)) +#define IS_FLAT_INTERVAL(i) ((d)->day == 0 && (d)->month == 0) /* * A hyperspace defines how to partition in a N-dimensional space. */ @@ -106,6 +108,9 @@ typedef struct DimensionInfo Datum interval_datum; Oid interval_type; /* Type of the interval datum */ int64 interval; + Datum interval_origin; + bool interval_origin_isnull; + Oid interval_origin_type; int32 num_slices; regproc partitioning_func; bool if_not_exists; @@ -146,7 +151,9 @@ extern int ts_dimension_delete_by_hypertable_id(int32 hypertable_id, bool delete extern TSDLLEXPORT DimensionInfo *ts_dimension_info_create_open(Oid table_relid, Name column_name, Datum interval, Oid interval_type, - regproc partitioning_func); + regproc partitioning_func, + Datum origin, bool origin_isnull, + Oid origin_type); extern TSDLLEXPORT DimensionInfo *ts_dimension_info_create_closed(Oid table_relid, Name column_name, int32 num_slices, diff --git a/src/hypertable.c b/src/hypertable.c index 25465e27b0e..16b686846ba 100644 --- a/src/hypertable.c +++ b/src/hypertable.c @@ -1597,6 +1597,11 @@ ts_hypertable_create_time_prev(PG_FUNCTION_ARGS, bool is_dist_call) text *target_size = PG_ARGISNULL(11) ? NULL : PG_GETARG_TEXT_P(11); Oid sizing_func = PG_ARGISNULL(12) ? InvalidOid : PG_GETARG_OID(12); regproc open_partitioning_func = PG_ARGISNULL(13) ? InvalidOid : PG_GETARG_OID(13); + const int origin_paramnum = is_dist_call ? 17 : 16; + bool origin_isnull = PG_ARGISNULL(origin_paramnum); + Datum origin = origin_isnull ? 0 : PG_GETARG_DATUM(origin_paramnum); + Oid origin_type = + origin_isnull ? InvalidOid : get_fn_expr_argtype(fcinfo->flinfo, origin_paramnum); if (!OidIsValid(table_relid)) ereport(ERROR, @@ -1609,11 +1614,13 @@ ts_hypertable_create_time_prev(PG_FUNCTION_ARGS, bool is_dist_call) DimensionInfo *open_dim_info = ts_dimension_info_create_open(table_relid, - open_dim_name, /* column name */ - default_interval, /* interval */ - interval_type, /* interval type */ - open_partitioning_func /* partitioning func */ - ); + open_dim_name, /* column name */ + default_interval, /* interval */ + interval_type, /* interval type */ + open_partitioning_func, /* partitioning func */ + origin, + origin_isnull, + origin_type); DimensionInfo *closed_dim_info = NULL; if (closed_dim_name) diff --git a/src/ts_catalog/catalog.h b/src/ts_catalog/catalog.h index 901c01adc50..3f5f5641523 100644 --- a/src/ts_catalog/catalog.h +++ b/src/ts_catalog/catalog.h @@ -182,7 +182,10 @@ enum Anum_dimension Anum_dimension_num_slices, Anum_dimension_partitioning_func_schema, Anum_dimension_partitioning_func, + Anum_dimension_interval_origin, + Anum_dimension_interval, Anum_dimension_interval_length, + Anum_dimension_compress_interval, Anum_dimension_compress_interval_length, Anum_dimension_integer_now_func_schema, Anum_dimension_integer_now_func, @@ -198,12 +201,27 @@ typedef struct FormData_dimension NameData column_name; Oid column_type; bool aligned; - /* closed (space) columns */ + /* Closed (space) dimension information */ int16 num_slices; NameData partitioning_func_schema; NameData partitioning_func; - /* open (time) columns */ + /* + * Open (time) dimension information + * + * An open dimension is divided into intervals that can either be plain + * integer intervals or calendar-based intervals (days, months, years). An + * interval has an origin, which for integer intervals is an integer while + * calendar-based intervals have a TimestampTz origin (but encoded into + * the same int64). + * + * Integer intervals are encoded into interval_length. + * + * Calendar-based intervals are encoded into a PostgreSQL interval. + */ + int64 interval_origin; + Interval interval; int64 interval_length; + Interval compress_interval; int64 compress_interval_length; NameData integer_now_func_schema; NameData integer_now_func; diff --git a/src/ts_catalog/compression_settings.c b/src/ts_catalog/compression_settings.c index 1847e05866e..a7ae45928c7 100644 --- a/src/ts_catalog/compression_settings.c +++ b/src/ts_catalog/compression_settings.c @@ -6,13 +6,13 @@ #include #include -#include "chunk.h" #include "hypertable.h" #include "scan_iterator.h" #include "scanner.h" #include "ts_catalog/array_utils.h" #include "ts_catalog/catalog.h" #include "ts_catalog/compression_settings.h" +#include static ScanTupleResult compression_settings_tuple_update(TupleInfo *ti, void *data); static HeapTuple compression_settings_formdata_make_tuple(const FormData_compression_settings *fd, diff --git a/src/ts_catalog/continuous_agg.h b/src/ts_catalog/continuous_agg.h index 268c42488d3..f6f7a91398c 100644 --- a/src/ts_catalog/continuous_agg.h +++ b/src/ts_catalog/continuous_agg.h @@ -9,8 +9,8 @@ #include #include -#include "chunk.h" #include "ts_catalog/catalog.h" +#include #include "compat/compat.h" #include "with_clause_parser.h" diff --git a/tsl/src/continuous_aggs/create.c b/tsl/src/continuous_aggs/create.c index 792944d36e8..ea0f9559619 100644 --- a/tsl/src/continuous_aggs/create.c +++ b/tsl/src/continuous_aggs/create.c @@ -276,7 +276,10 @@ cagg_create_hypertable(int32 hypertable_id, Oid mat_tbloid, const char *matpartc &mat_tbltimecol, Int64GetDatum(mat_tbltimecol_interval), INT8OID, - InvalidOid); + InvalidOid, + 0, + true, + INT8OID); /* * Ideally would like to change/expand the API so setting the column name manually is * unnecessary, but not high priority. From 0cc77c46013f70de6a9baa81a8e98ab3a06b474d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Nordstr=C3=B6m?= Date: Wed, 5 Jun 2024 17:04:18 +0200 Subject: [PATCH 2/3] Fixes --- sql/ddl_api.sql | 6 +++--- src/dimension.c | 13 +++++++++++-- src/hypertable.c | 10 +++++++++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/sql/ddl_api.sql b/sql/ddl_api.sql index b5a402ebba5..a50e7b4a4e4 100644 --- a/sql/ddl_api.sql +++ b/sql/ddl_api.sql @@ -135,8 +135,7 @@ CREATE OR REPLACE FUNCTION @extschema@.add_dimension( number_partitions INTEGER = NULL, chunk_time_interval ANYELEMENT = NULL::BIGINT, partitioning_func REGPROC = NULL, - if_not_exists BOOLEAN = FALSE, - origin TIMESTAMPTZ = NULL + if_not_exists BOOLEAN = FALSE ) RETURNS TABLE(dimension_id INT, schema_name NAME, table_name NAME, column_name NAME, created BOOL) AS '@MODULE_PATHNAME@', 'ts_dimension_add' LANGUAGE C VOLATILE; @@ -159,7 +158,8 @@ CREATE OR REPLACE FUNCTION @extschema@.by_hash(column_name NAME, number_partitio CREATE OR REPLACE FUNCTION @extschema@.by_range(column_name NAME, partition_interval ANYELEMENT = NULL::bigint, - partition_func regproc = NULL) + partition_func regproc = NULL, + partition_origin TIMESTAMPTZ = NULL) RETURNS _timescaledb_internal.dimension_info LANGUAGE C AS '@MODULE_PATHNAME@', 'ts_range_dimension'; diff --git a/src/dimension.c b/src/dimension.c index bc4cb674a3d..775177adb0e 100644 --- a/src/dimension.c +++ b/src/dimension.c @@ -1759,6 +1759,7 @@ TS_FUNCTION_INFO_V1(ts_dimension_add_general); Datum ts_dimension_add(PG_FUNCTION_ARGS) { + bool origin_isnull = PG_ARGISNULL(6); DimensionInfo info = { .type = PG_ARGISNULL(2) ? DIMENSION_TYPE_OPEN : DIMENSION_TYPE_CLOSED, .table_relid = PG_GETARG_OID(0), @@ -1768,6 +1769,9 @@ ts_dimension_add(PG_FUNCTION_ARGS) .interval_type = PG_ARGISNULL(3) ? InvalidOid : get_fn_expr_argtype(fcinfo->flinfo, 3), .partitioning_func = PG_ARGISNULL(4) ? InvalidOid : PG_GETARG_OID(4), .if_not_exists = PG_ARGISNULL(5) ? false : PG_GETARG_BOOL(5), + .interval_origin_isnull = origin_isnull, + .interval_origin = origin_isnull ? 0 : PG_GETARG_DATUM(6), + .interval_origin_type = origin_isnull ? InvalidOid : get_fn_expr_argtype(fcinfo->flinfo, 6), }; TS_PREVENT_FUNC_IF_READ_ONLY(); @@ -1861,7 +1865,7 @@ make_dimension_info(Name colname, DimensionType dimtype) Datum ts_hash_dimension(PG_FUNCTION_ARGS) { - Ensure(PG_NARGS() > 2, "expected at most 3 arguments, invoked with %d arguments", PG_NARGS()); + Ensure(PG_NARGS() > 2, "expected at least 3 arguments, invoked with %d arguments", PG_NARGS()); Name column_name; GETARG_NOTNULL_NULLABLE(column_name, 0, "column_name", NAME); DimensionInfo *info = make_dimension_info(column_name, DIMENSION_TYPE_CLOSED); @@ -1880,13 +1884,18 @@ ts_hash_dimension(PG_FUNCTION_ARGS) Datum ts_range_dimension(PG_FUNCTION_ARGS) { - Ensure(PG_NARGS() > 2, "expected at most 3 arguments, invoked with %d arguments", PG_NARGS()); + Ensure(PG_NARGS() > 2, "expected at least 3 arguments, invoked with %d arguments", PG_NARGS()); Name column_name; GETARG_NOTNULL_NULLABLE(column_name, 0, "column_name", NAME); DimensionInfo *info = make_dimension_info(column_name, DIMENSION_TYPE_OPEN); info->interval_datum = PG_ARGISNULL(1) ? Int32GetDatum(-1) : PG_GETARG_DATUM(1); info->interval_type = PG_ARGISNULL(1) ? InvalidOid : get_fn_expr_argtype(fcinfo->flinfo, 1); info->partitioning_func = PG_ARGISNULL(2) ? InvalidOid : PG_GETARG_OID(2); + info->interval_origin_isnull = PG_ARGISNULL(3); + info->interval_origin = info->interval_origin_isnull ? 0 : PG_GETARG_DATUM(3); + info->interval_origin_type = + info->interval_origin_isnull ? InvalidOid : get_fn_expr_argtype(fcinfo->flinfo, 3); + PG_RETURN_POINTER(info); } diff --git a/src/hypertable.c b/src/hypertable.c index 16b686846ba..cc9aa023f4d 100644 --- a/src/hypertable.c +++ b/src/hypertable.c @@ -1597,12 +1597,14 @@ ts_hypertable_create_time_prev(PG_FUNCTION_ARGS, bool is_dist_call) text *target_size = PG_ARGISNULL(11) ? NULL : PG_GETARG_TEXT_P(11); Oid sizing_func = PG_ARGISNULL(12) ? InvalidOid : PG_GETARG_OID(12); regproc open_partitioning_func = PG_ARGISNULL(13) ? InvalidOid : PG_GETARG_OID(13); - const int origin_paramnum = is_dist_call ? 17 : 16; + const int origin_paramnum = 14; bool origin_isnull = PG_ARGISNULL(origin_paramnum); Datum origin = origin_isnull ? 0 : PG_GETARG_DATUM(origin_paramnum); Oid origin_type = origin_isnull ? InvalidOid : get_fn_expr_argtype(fcinfo->flinfo, origin_paramnum); + elog(NOTICE, "origin type is %s", format_type_be(origin_type)); + if (!OidIsValid(table_relid)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("relation cannot be NULL"))); @@ -1683,6 +1685,12 @@ ts_hypertable_create_general(PG_FUNCTION_ARGS) bool create_default_indexes = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2); bool if_not_exists = PG_ARGISNULL(3) ? false : PG_GETARG_BOOL(3); bool migrate_data = PG_ARGISNULL(4) ? false : PG_GETARG_BOOL(4); + const int origin_paramnum = 5; + bool origin_isnull = PG_ARGISNULL(5); + dim_info->interval_origin_isnull = origin_isnull; + dim_info->interval_origin = origin_isnull ? 0 : PG_GETARG_DATUM(5); + dim_info->interval_origin_type = + origin_isnull ? InvalidOid : get_fn_expr_argtype(fcinfo->flinfo, origin_paramnum); /* * We do not support closed (hash) dimensions for the main partitioning From dddd35f0c09f6ec00629a79174fa74dab45468a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Nordstr=C3=B6m?= Date: Wed, 5 Jun 2024 18:46:01 +0200 Subject: [PATCH 3/3] Add naive interval calculatio --- sql/ddl_api.sql | 8 ++--- src/dimension.c | 80 +++++++++++++++++++++++++++++++++++++++++++----- src/dimension.h | 1 + src/hypertable.c | 9 ++---- 4 files changed, 80 insertions(+), 18 deletions(-) diff --git a/sql/ddl_api.sql b/sql/ddl_api.sql index a50e7b4a4e4..c1f7d5f502d 100644 --- a/sql/ddl_api.sql +++ b/sql/ddl_api.sql @@ -51,8 +51,7 @@ CREATE OR REPLACE FUNCTION @extschema@.create_hypertable( dimension _timescaledb_internal.dimension_info, create_default_indexes BOOLEAN = TRUE, if_not_exists BOOLEAN = FALSE, - migrate_data BOOLEAN = FALSE, - origin ANYELEMENT = NULL::timestamptz + migrate_data BOOLEAN = FALSE ) RETURNS TABLE(hypertable_id INT, created BOOL) AS '@MODULE_PATHNAME@', 'ts_hypertable_create_general' LANGUAGE C VOLATILE; -- Set adaptive chunking. To disable, set chunk_target_size => 'off'. @@ -135,7 +134,8 @@ CREATE OR REPLACE FUNCTION @extschema@.add_dimension( number_partitions INTEGER = NULL, chunk_time_interval ANYELEMENT = NULL::BIGINT, partitioning_func REGPROC = NULL, - if_not_exists BOOLEAN = FALSE + if_not_exists BOOLEAN = FALSE, + origin ANYELEMENT = NULL::timestamptz ) RETURNS TABLE(dimension_id INT, schema_name NAME, table_name NAME, column_name NAME, created BOOL) AS '@MODULE_PATHNAME@', 'ts_dimension_add' LANGUAGE C VOLATILE; @@ -159,7 +159,7 @@ CREATE OR REPLACE FUNCTION @extschema@.by_hash(column_name NAME, number_partitio CREATE OR REPLACE FUNCTION @extschema@.by_range(column_name NAME, partition_interval ANYELEMENT = NULL::bigint, partition_func regproc = NULL, - partition_origin TIMESTAMPTZ = NULL) + partition_origin "any" = NULL::TIMESTAMPTZ) RETURNS _timescaledb_internal.dimension_info LANGUAGE C AS '@MODULE_PATHNAME@', 'ts_range_dimension'; diff --git a/src/dimension.c b/src/dimension.c index 775177adb0e..53be5f4c3e3 100644 --- a/src/dimension.c +++ b/src/dimension.c @@ -160,6 +160,12 @@ dimension_type(TupleInfo *ti) return DIMENSION_TYPE_CLOSED; if (!slot_attisnull(ti->slot, Anum_dimension_interval_length) && + slot_attisnull(ti->slot, Anum_dimension_interval) && + slot_attisnull(ti->slot, Anum_dimension_num_slices)) + return DIMENSION_TYPE_OPEN; + + if (slot_attisnull(ti->slot, Anum_dimension_interval_length) && + !slot_attisnull(ti->slot, Anum_dimension_interval) && slot_attisnull(ti->slot, Anum_dimension_num_slices)) return DIMENSION_TYPE_OPEN; @@ -292,9 +298,35 @@ create_range_datum(FunctionCallInfo fcinfo, DimensionSlice *slice) static DimensionSlice * calculate_open_range_default(const Dimension *dim, int64 value) { - int64 range_start, range_end; + int64 range_start = 0, range_end = 0; Oid dimtype = ts_dimension_get_partition_type(dim); + if (dim->calendar_based) + { + int tz; + struct pg_tm tt, *tm = &tt; + pg_tz *attimezone = session_timezone; + fsec_t fsec; + + if (timestamp2tm(value, &tz, tm, &fsec, NULL, attimezone) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + + // Hack to produce monthly chunks. Should check with interval to see + // it is really month-based. + tt.tm_hour = 0; + tt.tm_sec = 0; + tt.tm_min = 0; + tt.tm_mday = 0; + + tm2timestamp(tm, fsec, &tz, &range_start); + tt.tm_mon = tt.tm_mon + dim->fd.interval.month; // TODO mod 12 + tm2timestamp(tm, fsec, &tz, &range_end); + + return ts_dimension_slice_create(dim->fd.id, range_start, range_end); + } + if (value < 0) { const int64 dim_min = ts_time_get_min(dimtype); @@ -855,7 +887,7 @@ dimension_insert_relation(Relation rel, int32 hypertable_id, Name colname, Oid c values[AttrNumberGetAttrOffset(Anum_dimension_interval)] = IntervalPGetDatum(interval); nulls[AttrNumberGetAttrOffset(Anum_dimension_interval_length)] = true; values[AttrNumberGetAttrOffset(Anum_dimension_interval_origin)] = - TimestampTzGetDatum(interval_length); + TimestampTzGetDatum(interval_origin); } else { @@ -864,7 +896,7 @@ dimension_insert_relation(Relation rel, int32 hypertable_id, Name colname, Oid c Int64GetDatum(interval_length); nulls[AttrNumberGetAttrOffset(Anum_dimension_interval)] = true; values[AttrNumberGetAttrOffset(Anum_dimension_interval_origin)] = - Int64GetDatum(interval_length); + Int64GetDatum(interval_origin); } values[AttrNumberGetAttrOffset(Anum_dimension_aligned)] = BoolGetDatum(true); @@ -1384,8 +1416,7 @@ ts_dimension_info_create_open(Oid table_relid, Name column_name, Datum interval, .coltype = coltype, .interval_datum = interval, .interval_type = interval_type, - .interval_origin = - interval_origin_isnull ? open_dim_default_calendar_origin(coltype) : interval_origin, + .interval_origin = interval_origin_isnull ? 0 : interval_origin, .interval_origin_isnull = interval_origin_isnull, .interval_origin_type = interval_origin_type, .partitioning_func = partitioning_func, @@ -1638,6 +1669,31 @@ dimension_create_datum(FunctionCallInfo fcinfo, DimensionInfo *info, bool is_gen return HeapTupleGetDatum(tuple); } +void +ts_dimension_info_set_defaults(DimensionInfo *info) +{ + if (info->type == DIMENSION_TYPE_OPEN) + { + AttrNumber attnum = get_attnum(info->table_relid, NameStr(info->colname)); + Oid atttype = get_atttype(info->table_relid, attnum); + + if (info->interval_origin_isnull) + { + info->interval_origin_type = atttype; + info->interval_origin = open_dim_default_calendar_origin(atttype); + info->interval_origin_isnull = false; + elog(NOTICE, "set interval_datum default"); + } + else + { + /* TODO: coerce origin_type to column type */ + Ensure(info->interval_origin_type == atttype, + "origin must have the same as type as column \"%s\"", + NameStr(info->colname)); + } + } +} + /* * Add a new dimension to a hypertable. * @@ -1691,6 +1747,7 @@ ts_dimension_add_internal(FunctionCallInfo fcinfo, DimensionInfo *info, bool is_ errmsg("cannot omit both the number of partitions and the interval"))); ts_dimension_info_validate(info); + ts_dimension_info_set_defaults(info); if (!info->skip) { @@ -1760,6 +1817,7 @@ Datum ts_dimension_add(PG_FUNCTION_ARGS) { bool origin_isnull = PG_ARGISNULL(6); + Name colname = PG_ARGISNULL(1) ? NULL : PG_GETARG_NAME(1); DimensionInfo info = { .type = PG_ARGISNULL(2) ? DIMENSION_TYPE_OPEN : DIMENSION_TYPE_CLOSED, .table_relid = PG_GETARG_OID(0), @@ -1776,8 +1834,11 @@ ts_dimension_add(PG_FUNCTION_ARGS) TS_PREVENT_FUNC_IF_READ_ONLY(); - if (!PG_ARGISNULL(1)) - namestrcpy(&info.colname, NameStr(*PG_GETARG_NAME(1))); + if (NULL == colname) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("column_name cannot be NULL"))); + + namestrcpy(&info.colname, NameStr(*colname)); if (PG_ARGISNULL(0)) ereport(ERROR, @@ -1903,8 +1964,11 @@ Datum ts_dimension_add_general(PG_FUNCTION_ARGS) { DimensionInfo *info = NULL; + Oid relid = PG_GETARG_OID(0); GETARG_NOTNULL_POINTER(info, 1, "dimension", DimensionInfo); - info->table_relid = PG_GETARG_OID(0); + AttrNumber colattr = get_attnum(relid, NameStr(info->colname)); + info->coltype = get_atttype(relid, colattr); + info->table_relid = relid; if (PG_GETARG_BOOL(2)) info->if_not_exists = true; return ts_dimension_add_internal(fcinfo, info, true); diff --git a/src/dimension.h b/src/dimension.h index d72c007c2fe..ec05d3a3957 100644 --- a/src/dimension.h +++ b/src/dimension.h @@ -160,6 +160,7 @@ extern TSDLLEXPORT DimensionInfo *ts_dimension_info_create_closed(Oid table_reli regproc partitioning_func); extern void ts_dimension_info_validate(DimensionInfo *info); +extern void ts_dimension_info_set_defaults(DimensionInfo *info); extern int32 ts_dimension_add_from_info(DimensionInfo *info); extern void ts_dimensions_rename_schema_name(const char *old_name, const char *new_name); extern TSDLLEXPORT void ts_dimension_update(const Hypertable *ht, const NameData *dimname, diff --git a/src/hypertable.c b/src/hypertable.c index cc9aa023f4d..6895eae627f 100644 --- a/src/hypertable.c +++ b/src/hypertable.c @@ -1685,12 +1685,6 @@ ts_hypertable_create_general(PG_FUNCTION_ARGS) bool create_default_indexes = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2); bool if_not_exists = PG_ARGISNULL(3) ? false : PG_GETARG_BOOL(3); bool migrate_data = PG_ARGISNULL(4) ? false : PG_GETARG_BOOL(4); - const int origin_paramnum = 5; - bool origin_isnull = PG_ARGISNULL(5); - dim_info->interval_origin_isnull = origin_isnull; - dim_info->interval_origin = origin_isnull ? 0 : PG_GETARG_DATUM(5); - dim_info->interval_origin_type = - origin_isnull ? InvalidOid : get_fn_expr_argtype(fcinfo->flinfo, origin_paramnum); /* * We do not support closed (hash) dimensions for the main partitioning @@ -1711,7 +1705,10 @@ ts_hypertable_create_general(PG_FUNCTION_ARGS) /* * Fill in the rest of the info. */ + AttrNumber colattr = get_attnum(table_relid, NameStr(dim_info->colname)); dim_info->table_relid = table_relid; + dim_info->coltype = get_atttype(table_relid, colattr); + ts_dimension_info_set_defaults(dim_info); return ts_hypertable_create_internal(fcinfo, table_relid,