From f9bd92935263b3e9f7ea5b6c5aefc9d985e55fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabr=C3=ADzio=20de=20Royes=20Mello?= Date: Thu, 27 Jun 2024 10:16:13 -0300 Subject: [PATCH] Draft PR --- sql/pre_install/tables.sql | 23 --- sql/updates/latest-dev.sql | 6 + sql/updates/reverse-dev.sql | 32 +++ src/cross_module_fn.c | 9 + src/cross_module_fn.h | 1 + src/ts_catalog/catalog.c | 10 - src/ts_catalog/catalog.h | 33 --- src/ts_catalog/continuous_agg.c | 193 +----------------- src/ts_catalog/continuous_agg.h | 10 +- tsl/src/continuous_aggs/common.c | 7 +- tsl/src/continuous_aggs/common.h | 2 +- tsl/src/continuous_aggs/create.c | 92 --------- tsl/src/continuous_aggs/utils.c | 108 +--------- tsl/src/continuous_aggs/utils.h | 1 + tsl/src/init.c | 1 + tsl/test/expected/cagg_bgw-16.out | 2 +- tsl/test/expected/cagg_migrate.out | 5 - tsl/test/expected/cagg_utils.out | 8 +- tsl/test/sql/cagg_bgw.sql.in | 2 +- tsl/test/sql/exp_cagg_monthly.sql | 21 +- tsl/test/sql/exp_cagg_next_gen.sql | 5 +- tsl/test/sql/exp_cagg_origin.sql | 6 +- tsl/test/sql/exp_cagg_timezone.sql | 8 +- tsl/test/sql/include/cagg_query_common.sql | 14 +- .../include/cagg_run_timebucket_migration.sql | 10 +- .../include/data/cagg_migrate_integer.sql.gz | Bin 1650 -> 1601 bytes .../data/cagg_migrate_timestamp.sql.gz | Bin 1711 -> 1659 bytes .../data/cagg_migrate_timestamptz.sql.gz | Bin 1702 -> 1653 bytes 28 files changed, 106 insertions(+), 503 deletions(-) diff --git a/sql/pre_install/tables.sql b/sql/pre_install/tables.sql index 9389d315a9a..aa9b58e04fb 100644 --- a/sql/pre_install/tables.sql +++ b/sql/pre_install/tables.sql @@ -387,29 +387,6 @@ CREATE INDEX continuous_agg_raw_hypertable_id_idx ON _timescaledb_catalog.contin SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.continuous_agg', ''); --- See the comments for ContinuousAggsBucketFunction structure. -CREATE TABLE _timescaledb_catalog.continuous_aggs_bucket_function ( - mat_hypertable_id integer NOT NULL, - -- The bucket function - bucket_func text NOT NULL, - -- `bucket_width` argument of the function, e.g. "1 month" - bucket_width text NOT NULL, - -- optional `origin` argument of the function provided by the user - bucket_origin text, - -- optional `offset` argument of the function provided by the user - bucket_offset text, - -- optional `timezone` argument of the function provided by the user - bucket_timezone text, - -- fixed or variable sized bucket - bucket_fixed_width bool NOT NULL, - -- table constraints - CONSTRAINT continuous_aggs_bucket_function_pkey PRIMARY KEY (mat_hypertable_id), - CONSTRAINT continuous_aggs_bucket_function_mat_hypertable_id_fkey FOREIGN KEY (mat_hypertable_id) REFERENCES _timescaledb_catalog.hypertable (id) ON DELETE CASCADE, - CONSTRAINT continuous_aggs_bucket_function_func_check CHECK (pg_catalog.to_regprocedure(bucket_func) IS DISTINCT FROM 0) -); - -SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.continuous_aggs_bucket_function', ''); - CREATE TABLE _timescaledb_catalog.continuous_aggs_invalidation_threshold ( hypertable_id integer NOT NULL, watermark bigint NOT NULL, diff --git a/sql/updates/latest-dev.sql b/sql/updates/latest-dev.sql index d233b0ae5f8..5533c3c058b 100644 --- a/sql/updates/latest-dev.sql +++ b/sql/updates/latest-dev.sql @@ -114,3 +114,9 @@ CREATE FUNCTION @extschema@.hypertable_columnstore_stats (hypertable REGCLASS) STABLE STRICT AS 'SELECT * FROM @extschema@.hypertable_compression_stats($1)' SET search_path TO pg_catalog, pg_temp; + +-- Remove useless catalog metadata +ALTER EXTENSION timescaledb + DROP TABLE _timescaledb_catalog.continuous_aggs_bucket_function; + +DROP TABLE _timescaledb_catalog.continuous_aggs_bucket_function; diff --git a/sql/updates/reverse-dev.sql b/sql/updates/reverse-dev.sql index 6b75bc1c851..0837e77697b 100644 --- a/sql/updates/reverse-dev.sql +++ b/sql/updates/reverse-dev.sql @@ -57,3 +57,35 @@ ALTER EXTENSION timescaledb DROP VIEW timescaledb_information.chunk_columnstore_ DROP VIEW timescaledb_information.hypertable_columnstore_settings; DROP VIEW timescaledb_information.chunk_columnstore_settings; +-- Restore the removed metadata table +CREATE TABLE _timescaledb_catalog.continuous_aggs_bucket_function ( + mat_hypertable_id integer NOT NULL, + -- The bucket function + bucket_func text NOT NULL, + -- `bucket_width` argument of the function, e.g. "1 month" + bucket_width text NOT NULL, + -- optional `origin` argument of the function provided by the user + bucket_origin text, + -- optional `offset` argument of the function provided by the user + bucket_offset text, + -- optional `timezone` argument of the function provided by the user + bucket_timezone text, + -- fixed or variable sized bucket + bucket_fixed_width bool NOT NULL, + -- table constraints + CONSTRAINT continuous_aggs_bucket_function_pkey PRIMARY KEY (mat_hypertable_id), + CONSTRAINT continuous_aggs_bucket_function_mat_hypertable_id_fkey FOREIGN KEY (mat_hypertable_id) REFERENCES _timescaledb_catalog.hypertable (id) ON DELETE CASCADE, + CONSTRAINT continuous_aggs_bucket_function_func_check CHECK (pg_catalog.to_regprocedure(bucket_func) IS DISTINCT FROM 0) +); + +INSERT INTO _timescaledb_catalog.continuous_aggs_bucket_function + (mat_hypertable_id, bucket_func, bucket_width, bucket_origin, bucket_offset, bucket_timezone, bucket_fixed_width) +SELECT mat_hypertable_id, bf.bucket_func::text, bf.bucket_width, bf.bucket_origin, bf.bucket_offset, bf.bucket_timezone, bf.bucket_fixed_width +FROM _timescaledb_catalog.continuous_agg, LATERAL _timescaledb_functions.cagg_get_bucket_function_info(mat_hypertable_id) AS bf; + +SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.continuous_aggs_bucket_function', ''); + +GRANT SELECT ON _timescaledb_catalog.continuous_aggs_bucket_function TO PUBLIC; + +DROP FUNCTION IF EXISTS _timescaledb_functions.cagg_get_bucket_function_info(INTEGER); + diff --git a/src/cross_module_fn.c b/src/cross_module_fn.c index 5f1b5a0290e..1814ac94bf9 100644 --- a/src/cross_module_fn.c +++ b/src/cross_module_fn.c @@ -284,6 +284,13 @@ continuous_agg_call_invalidation_trigger_default(int32 hypertable_id, Relation c pg_unreachable(); } +static ContinuousAggsBucketFunction * +continuous_agg_get_bucket_function_info_internal_default(Oid view_oid) +{ + error_no_default_fn_community(); + pg_unreachable(); +} + TS_FUNCTION_INFO_V1(ts_tsl_loaded); PGDLLEXPORT Datum @@ -373,6 +380,8 @@ TSDLLEXPORT CrossModuleFunctions ts_cm_functions_default = { .continuous_agg_validate_query = error_no_default_fn_pg_community, .continuous_agg_get_bucket_function = error_no_default_fn_pg_community, .continuous_agg_get_bucket_function_info = error_no_default_fn_pg_community, + .continuous_agg_get_bucket_function_info_internal = + continuous_agg_get_bucket_function_info_internal_default, .continuous_agg_migrate_to_time_bucket = error_no_default_fn_pg_community, .cagg_try_repair = process_cagg_try_repair, diff --git a/src/cross_module_fn.h b/src/cross_module_fn.h index e471a867b25..c8bf2085928 100644 --- a/src/cross_module_fn.h +++ b/src/cross_module_fn.h @@ -117,6 +117,7 @@ typedef struct CrossModuleFunctions PGFunction continuous_agg_get_bucket_function_info; PGFunction continuous_agg_migrate_to_time_bucket; PGFunction cagg_try_repair; + ContinuousAggsBucketFunction *(*continuous_agg_get_bucket_function_info_internal)(Oid view_oid); PGFunction compressed_data_send; PGFunction compressed_data_recv; diff --git a/src/ts_catalog/catalog.c b/src/ts_catalog/catalog.c index 4278ac763c0..4645e3b77ea 100644 --- a/src/ts_catalog/catalog.c +++ b/src/ts_catalog/catalog.c @@ -97,10 +97,6 @@ static const TableInfoDef catalog_table_names[_MAX_CATALOG_TABLES + 1] = { .schema_name = CATALOG_SCHEMA_NAME, .table_name = COMPRESSION_CHUNK_SIZE_TABLE_NAME, }, - [CONTINUOUS_AGGS_BUCKET_FUNCTION] = { - .schema_name = CATALOG_SCHEMA_NAME, - .table_name = CONTINUOUS_AGGS_BUCKET_FUNCTION_TABLE_NAME, - }, [CONTINUOUS_AGGS_WATERMARK] = { .schema_name = CATALOG_SCHEMA_NAME, .table_name = CONTINUOUS_AGGS_WATERMARK_TABLE_NAME, @@ -255,12 +251,6 @@ static const TableIndexDef catalog_table_index_definitions[_MAX_CATALOG_TABLES] .names = (char *[]) { [COMPRESSION_CHUNK_SIZE_PKEY] = "compression_chunk_size_pkey", }, - }, - [CONTINUOUS_AGGS_BUCKET_FUNCTION] = { - .length = _MAX_CONTINUOUS_AGGS_BUCKET_FUNCTION_INDEX, - .names = (char *[]) { - [CONTINUOUS_AGGS_BUCKET_FUNCTION_PKEY_IDX] = "continuous_aggs_bucket_function_pkey", - }, } }; diff --git a/src/ts_catalog/catalog.h b/src/ts_catalog/catalog.h index 36d925acef4..7be84f27dd2 100644 --- a/src/ts_catalog/catalog.h +++ b/src/ts_catalog/catalog.h @@ -50,7 +50,6 @@ typedef enum CatalogTable CONTINUOUS_AGGS_MATERIALIZATION_INVALIDATION_LOG, COMPRESSION_SETTINGS, COMPRESSION_CHUNK_SIZE, - CONTINUOUS_AGGS_BUCKET_FUNCTION, CONTINUOUS_AGGS_WATERMARK, TELEMETRY_EVENT, CHUNK_COLUMN_STATS, @@ -972,38 +971,6 @@ typedef enum Anum_continuous_agg_raw_hypertable_id_idx #define Natts_continuous_agg_raw_hypertable_id_idx \ (_Anum_continuous_agg_raw_hypertable_id_idx_max - 1) -/*** continuous_aggs_bucket_function table definitions ***/ - -#define CONTINUOUS_AGGS_BUCKET_FUNCTION_TABLE_NAME "continuous_aggs_bucket_function" -typedef enum Anum_continuous_aggs_bucket_function -{ - Anum_continuous_aggs_bucket_function_mat_hypertable_id = 1, - Anum_continuous_aggs_bucket_function_function, - Anum_continuous_aggs_bucket_function_bucket_width, - Anum_continuous_aggs_bucket_function_bucket_origin, - Anum_continuous_aggs_bucket_function_bucket_offset, - Anum_continuous_aggs_bucket_function_bucket_timezone, - Anum_continuous_aggs_bucket_function_bucket_fixed_width, - _Anum_continuous_aggs_bucket_function_max, -} Anum_continuous_aggs_bucket_function; - -#define Natts_continuous_aggs_bucket_function (_Anum_continuous_aggs_bucket_function_max - 1) - -enum -{ - CONTINUOUS_AGGS_BUCKET_FUNCTION_PKEY_IDX = 0, - _MAX_CONTINUOUS_AGGS_BUCKET_FUNCTION_INDEX, -}; - -typedef enum Anum_continuous_aggs_bucket_function_pkey -{ - Anum_continuous_aggs_bucket_function_pkey_mat_hypertable_id = 1, - _Anum_continuous_aggs_bucket_function_pkey_max, -} Anum_continuous_aggs_bucket_function_pkey; - -#define Natts_continuous_aggs_bucket_function_pkey \ - (_Anum_continuous_aggs_bucket_function_pkey_max - 1) - /****** CONTINUOUS_AGGS_HYPERTABLE_INVALIDATION_LOG_TABLE definitions*/ #define CONTINUOUS_AGGS_HYPERTABLE_INVALIDATION_LOG_TABLE_NAME \ "continuous_aggs_hypertable_invalidation_log" diff --git a/src/ts_catalog/continuous_agg.c b/src/ts_catalog/continuous_agg.c index a786b4bf44a..f2e1a345df6 100644 --- a/src/ts_catalog/continuous_agg.c +++ b/src/ts_catalog/continuous_agg.c @@ -46,6 +46,8 @@ #define BUCKET_FUNCTION_SERIALIZE_VERSION 1 #define CHECK_NAME_MATCH(name1, name2) (namestrcmp(name1, name2) == 0) +static ObjectAddress get_and_lock_rel_by_name(const Name schema, const Name name, LOCKMODE mode); + static const WithClauseDefinition continuous_aggregate_with_clause_def[] = { [ContinuousEnabled] = { .arg_names = {"continuous", NULL}, @@ -149,21 +151,6 @@ init_scan_by_mat_hypertable_id(ScanIterator *iterator, const int32 mat_hypertabl Int32GetDatum(mat_hypertable_id)); } -static void -init_scan_cagg_bucket_function_by_mat_hypertable_id(ScanIterator *iterator, - const int32 mat_hypertable_id) -{ - iterator->ctx.index = catalog_get_index(ts_catalog_get(), - CONTINUOUS_AGGS_BUCKET_FUNCTION, - CONTINUOUS_AGGS_BUCKET_FUNCTION_PKEY_IDX); - - ts_scan_iterator_scan_key_init(iterator, - Anum_continuous_aggs_bucket_function_pkey_mat_hypertable_id, - BTEqualStrategyNumber, - F_INT4EQ, - Int32GetDatum(mat_hypertable_id)); -} - static void init_scan_by_raw_hypertable_id(ScanIterator *iterator, const int32 raw_hypertable_id) { @@ -252,22 +239,6 @@ invalidation_threshold_delete(int32 raw_hypertable_id) } } -static void -cagg_bucket_function_delete(int32 mat_hypertable_id) -{ - ScanIterator iterator = ts_scan_iterator_create(CONTINUOUS_AGGS_BUCKET_FUNCTION, - RowExclusiveLock, - CurrentMemoryContext); - - init_scan_cagg_bucket_function_by_mat_hypertable_id(&iterator, mat_hypertable_id); - - ts_scanner_foreach(&iterator) - { - TupleInfo *ti = ts_scan_iterator_tuple_info(&iterator); - ts_catalog_delete_tid(ti->scanrel, ts_scanner_get_tuple_tid(ti)); - } -} - static void hypertable_invalidation_log_delete(int32 raw_hypertable_id) { @@ -396,156 +367,6 @@ continuous_agg_formdata_fill(FormData_continuous_agg *fd, const TupleInfo *ti) heap_freetuple(tuple); } -/* - * Fill the fields of a integer based bucketing function - */ -static void -cagg_fill_bucket_function_integer_based(ContinuousAggsBucketFunction *bf, bool *isnull, - Datum *values) -{ - /* Bucket width */ - Assert(!isnull[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_width)]); - const char *bucket_width_str = TextDatumGetCString( - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_width)]); - Assert(strlen(bucket_width_str) > 0); - bf->bucket_integer_width = pg_strtoint64(bucket_width_str); - - /* Bucket origin cannot be used with integer based buckets */ - Assert(isnull[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_origin)] == - true); - - /* Bucket offset */ - if (!isnull[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_offset)]) - { - const char *offset_str = TextDatumGetCString( - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_offset)]); - bf->bucket_integer_offset = pg_strtoint64(offset_str); - } - - /* Timezones cannot be used with integer based buckets */ - Assert(isnull[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_timezone)] == - true); -} - -/* - * Fill the fields of a time based bucketing function - */ -static void -cagg_fill_bucket_function_time_based(ContinuousAggsBucketFunction *bf, bool *isnull, Datum *values) -{ - /* - * bucket_width - * - * The value is stored as TEXT since we have to store the interval value of time - * buckets and also the number value of integer based buckets. - */ - Assert(!isnull[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_width)]); - const char *bucket_width_str = TextDatumGetCString( - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_width)]); - Assert(strlen(bucket_width_str) > 0); - bf->bucket_time_width = DatumGetIntervalP( - DirectFunctionCall3(interval_in, CStringGetDatum(bucket_width_str), InvalidOid, -1)); - - /* Bucket origin */ - if (!isnull[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_origin)]) - { - const char *origin_str = TextDatumGetCString( - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_origin)]); - bf->bucket_time_origin = DatumGetTimestamp(DirectFunctionCall3(timestamptz_in, - CStringGetDatum(origin_str), - ObjectIdGetDatum(InvalidOid), - Int32GetDatum(-1))); - } - else - { - TIMESTAMP_NOBEGIN(bf->bucket_time_origin); - } - - /* Bucket offset */ - if (!isnull[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_offset)]) - { - const char *offset_str = TextDatumGetCString( - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_offset)]); - bf->bucket_time_offset = DatumGetIntervalP( - DirectFunctionCall3(interval_in, CStringGetDatum(offset_str), InvalidOid, -1)); - } - - /* Bucket timezone */ - if (!isnull[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_timezone)]) - { - bf->bucket_time_timezone = TextDatumGetCString( - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_timezone)]); - } -} - -static void -continuous_agg_fill_bucket_function(int32 mat_hypertable_id, ContinuousAggsBucketFunction *bf) -{ - ScanIterator iterator; - int count = 0; - - iterator = ts_scan_iterator_create(CONTINUOUS_AGGS_BUCKET_FUNCTION, - AccessShareLock, - CurrentMemoryContext); - init_scan_cagg_bucket_function_by_mat_hypertable_id(&iterator, mat_hypertable_id); - ts_scanner_foreach(&iterator) - { - Datum values[Natts_continuous_aggs_bucket_function]; - bool isnull[Natts_continuous_aggs_bucket_function]; - bool should_free; - - HeapTuple tuple = ts_scan_iterator_fetch_heap_tuple(&iterator, false, &should_free); - - /* - * Our usual GETSTRUCT() approach doesn't work when TEXT fields are involved, - * thus a more robust approach with heap_deform_tuple() is used here. - */ - heap_deform_tuple(tuple, ts_scan_iterator_tupledesc(&iterator), values, isnull); - - /* Bucket function */ - Assert(!isnull[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_function)]); - const char *bucket_function_str = TextDatumGetCString( - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_function)]); - bf->bucket_function = DatumGetObjectId( - DirectFunctionCall1(regprocedurein, CStringGetDatum(bucket_function_str))); - - bf->bucket_time_based = ts_continuous_agg_bucket_on_interval(bf->bucket_function); - - if (bf->bucket_time_based) - { - cagg_fill_bucket_function_time_based(bf, isnull, values); - } - else - { - cagg_fill_bucket_function_integer_based(bf, isnull, values); - } - - /* Bucket fixed width */ - Assert(!isnull[AttrNumberGetAttrOffset( - Anum_continuous_aggs_bucket_function_bucket_fixed_width)]); - bf->bucket_fixed_interval = DatumGetBool(values[AttrNumberGetAttrOffset( - Anum_continuous_aggs_bucket_function_bucket_fixed_width)]); - - count++; - - if (should_free) - heap_freetuple(tuple); - } - - /* - * This function should never be called unless we know that the corresponding - * cagg exists and uses a variable-sized bucket. There should be exactly one - * entry in .continuous_aggs_bucket_function catalog table for such a cagg. - */ - if (count != 1) - { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("invalid or missing information about the bucketing function for cagg"), - errdetail("%d", mat_hypertable_id))); - } -} - static void continuous_agg_init(ContinuousAgg *cagg, const Form_continuous_agg fd) { @@ -563,8 +384,12 @@ continuous_agg_init(ContinuousAgg *cagg, const Form_continuous_agg fd) Assert(OidIsValid(cagg->relid)); Assert(OidIsValid(cagg->partition_type)); - cagg->bucket_function = palloc0(sizeof(ContinuousAggsBucketFunction)); - continuous_agg_fill_bucket_function(cagg->data.mat_hypertable_id, cagg->bucket_function); + ObjectAddress direct_view = + get_and_lock_rel_by_name(&fd->direct_view_schema, &fd->direct_view_name, AccessShareLock); + Assert(OidIsValid(direct_view.objectId)); + + cagg->bucket_function = + ts_cm_functions->continuous_agg_get_bucket_function_info_internal(direct_view.objectId); } TSDLLEXPORT CaggsInfo @@ -1004,8 +829,6 @@ drop_continuous_agg(FormData_continuous_agg *cadata, bool drop_user_view) ts_cagg_watermark_delete_by_mat_hypertable_id(form.mat_hypertable_id); } - cagg_bucket_function_delete(cadata->mat_hypertable_id); - /* Perform actual deletions now */ if (OidIsValid(user_view.objectId)) performDeletion(&user_view, DROP_RESTRICT, 0); diff --git a/src/ts_catalog/continuous_agg.h b/src/ts_catalog/continuous_agg.h index 268c42488d3..a55a2780ae9 100644 --- a/src/ts_catalog/continuous_agg.h +++ b/src/ts_catalog/continuous_agg.h @@ -77,14 +77,7 @@ ts_continuous_agg_get_compression_defelems(const WithClauseResult *with_clauses) */ typedef struct ContinuousAggsBucketFunction { - /* Oid of the bucketing function. In the catalog table, the regprocedure is used. This ensures - * that the Oid is mapped to a string when a backup is taken and the string is converted back to - * the Oid when the backup is restored. This way, we can use an Oid in the catalog table even - * when a backup is restored and the Oid may have changed. However, the dependency management in - * PostgreSQL does not track the Oid. If the function is dropped and a new one is created, the - * Oid changes and this value points to a non-existing Oid. This can not happen in real-world - * situations since PostgreSQL protects the bucket_function from deletion until the CAgg is - * defined. */ + /* Oid of the bucketing function */ Oid bucket_function; Oid bucket_width_type; /* type of bucket_width */ @@ -98,6 +91,7 @@ typedef struct ContinuousAggsBucketFunction * Fields that are used for time based buckets */ Interval *bucket_time_width; + /* * Custom origin value stored as UTC timestamp. * If not specified, stores infinity. diff --git a/tsl/src/continuous_aggs/common.c b/tsl/src/continuous_aggs/common.c index 50b1b849164..b285ad7952b 100644 --- a/tsl/src/continuous_aggs/common.c +++ b/tsl/src/continuous_aggs/common.c @@ -1502,16 +1502,17 @@ cagg_get_by_relid_or_fail(const Oid cagg_relid) /* Get time bucket function info based on the view definition */ ContinuousAggsBucketFunction * -ts_cagg_get_bucket_function_info(Oid view_oid) +tsl_cagg_get_bucket_function_info(Oid view_oid) { - Relation view_rel = relation_open(view_oid, AccessShareLock); + Relation view_rel = table_open(view_oid, AccessShareLock); Query *query = copyObject(get_view_query(view_rel)); - relation_close(view_rel, NoLock); + table_close(view_rel, NoLock); Assert(query != NULL); Assert(query->commandType == CMD_SELECT); ContinuousAggsBucketFunction *bf = palloc0(sizeof(ContinuousAggsBucketFunction)); + TIMESTAMP_NOBEGIN(bf->bucket_time_origin); ListCell *l; foreach (l, query->groupClause) diff --git a/tsl/src/continuous_aggs/common.h b/tsl/src/continuous_aggs/common.h index 6051e0de7d4..94f7570155e 100644 --- a/tsl/src/continuous_aggs/common.h +++ b/tsl/src/continuous_aggs/common.h @@ -140,4 +140,4 @@ cagg_get_time_min(const ContinuousAgg *cagg) return ts_time_get_min(cagg->partition_type); } -ContinuousAggsBucketFunction *ts_cagg_get_bucket_function_info(Oid view_oid); +extern ContinuousAggsBucketFunction *tsl_cagg_get_bucket_function_info(Oid view_oid); diff --git a/tsl/src/continuous_aggs/create.c b/tsl/src/continuous_aggs/create.c index f290cd38956..587065007ea 100644 --- a/tsl/src/continuous_aggs/create.c +++ b/tsl/src/continuous_aggs/create.c @@ -81,10 +81,6 @@ static void create_cagg_catalog_entry(int32 matht_id, int32 rawht_id, const char const char *partial_view, bool materialized_only, const char *direct_schema, const char *direct_view, const bool finalized, const int32 parent_mat_hypertable_id); -static void create_bucket_function_catalog_entry(int32 matht_id, Oid bucket_function, - const char *bucket_width, const char *origin, - const char *offset, const char *timezone, - const bool bucket_fixed_width); static void cagg_create_hypertable(int32 hypertable_id, Oid mat_tbloid, const char *matpartcolname, int64 mat_tbltimecol_interval); static void cagg_add_trigger_hypertable(Oid relid, int32 hypertable_id); @@ -177,86 +173,6 @@ create_cagg_catalog_entry(int32 matht_id, int32 rawht_id, const char *user_schem table_close(rel, RowExclusiveLock); } -/* - * Create a entry for the materialization table in table - * CONTINUOUS_AGGS_BUCKET_FUNCTION. - */ -static void -create_bucket_function_catalog_entry(int32 matht_id, Oid bucket_function, const char *bucket_width, - const char *bucket_origin, const char *bucket_offset, - const char *bucket_timezone, const bool bucket_fixed_width) -{ - Catalog *catalog = ts_catalog_get(); - Relation rel; - TupleDesc desc; - Datum values[Natts_continuous_aggs_bucket_function]; - bool nulls[Natts_continuous_aggs_bucket_function] = { false }; - CatalogSecurityContext sec_ctx; - - Assert(OidIsValid(bucket_function)); - Assert(bucket_width != NULL); - - rel = table_open(catalog_get_table_id(catalog, CONTINUOUS_AGGS_BUCKET_FUNCTION), - RowExclusiveLock); - desc = RelationGetDescr(rel); - - memset(values, 0, sizeof(values)); - - /* Hypertable ID */ - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_mat_hypertable_id)] = - matht_id; - - /* Bucket function */ - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_function)] = - CStringGetTextDatum(format_procedure_qualified(bucket_function)); - - /* Bucket width */ - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_width)] = - CStringGetTextDatum(bucket_width); - - /* Bucket origin */ - if (bucket_origin != NULL) - { - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_origin)] = - CStringGetTextDatum(bucket_origin); - } - else - { - nulls[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_origin)] = true; - } - - /* Bucket offset */ - if (bucket_offset != NULL) - { - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_offset)] = - CStringGetTextDatum(bucket_offset); - } - else - { - nulls[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_offset)] = true; - } - - /* Bucket timezone */ - if (bucket_timezone != NULL) - { - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_timezone)] = - CStringGetTextDatum(bucket_timezone); - } - else - { - nulls[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_timezone)] = true; - } - - /* Bucket fixed width */ - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_fixed_width)] = - BoolGetDatum(bucket_fixed_width); - - ts_catalog_database_info_become_owner(ts_catalog_database_info_get(), &sec_ctx); - ts_catalog_insert_values(rel, desc, values, nulls); - ts_catalog_restore_user(&sec_ctx); - table_close(rel, RowExclusiveLock); -} - /* * Create hypertable for the table referred by mat_tbloid * matpartcolname - partition column for hypertable @@ -815,14 +731,6 @@ cagg_create(const CreateTableAsStmt *create_stmt, ViewStmt *stmt, Query *panquer } } - create_bucket_function_catalog_entry(materialize_hypertable_id, - bucket_info->bf->bucket_function, - bucket_width, - bucket_origin, - bucket_offset, - bucket_info->bf->bucket_time_timezone, - bucket_info->bf->bucket_fixed_interval); - /* Step 5: Create trigger on raw hypertable -specified in the user view query. */ cagg_add_trigger_hypertable(bucket_info->htoid, bucket_info->htid); } diff --git a/tsl/src/continuous_aggs/utils.c b/tsl/src/continuous_aggs/utils.c index 6b9771c3162..fb9353ce0c1 100644 --- a/tsl/src/continuous_aggs/utils.c +++ b/tsl/src/continuous_aggs/utils.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -292,88 +291,6 @@ get_replacement_timebucket_function(const ContinuousAgg *cagg, bool *need_parame return funcid; } -/* - * Update the cagg bucket function catalog table. During the migration, we set a new bucket - * function and a origin if the bucket function is time based. - */ -static ScanTupleResult -cagg_time_bucket_update(TupleInfo *ti, void *data) -{ - bool should_free; - HeapTuple tuple = ts_scanner_fetch_heap_tuple(ti, false, &should_free); - TupleDesc tupleDesc = ts_scanner_get_tupledesc(ti); - const ContinuousAgg *cagg = (ContinuousAgg *) data; - - Datum values[Natts_continuous_aggs_bucket_function] = { 0 }; - bool isnull[Natts_continuous_aggs_bucket_function] = { 0 }; - bool doReplace[Natts_continuous_aggs_bucket_function] = { 0 }; - - /* Update the bucket function */ - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_function)] = - CStringGetTextDatum(format_procedure_qualified(cagg->bucket_function->bucket_function)); - doReplace[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_function)] = true; - - /* Set new origin if not already present. Time_bucket and time_bucket_ng use different - * origin values for time based values. - */ - if (cagg->bucket_function->bucket_time_based) - { - char *origin_value = DatumGetCString( - DirectFunctionCall1(timestamptz_out, - TimestampTzGetDatum(cagg->bucket_function->bucket_time_origin))); - - values[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_origin)] = - CStringGetTextDatum(origin_value); - - doReplace[AttrNumberGetAttrOffset(Anum_continuous_aggs_bucket_function_bucket_origin)] = - true; - } - - HeapTuple new_tuple = heap_modify_tuple(tuple, tupleDesc, values, isnull, doReplace); - - ts_catalog_update(ti->scanrel, new_tuple); - - heap_freetuple(new_tuple); - - if (should_free) - heap_freetuple(tuple); - - return SCAN_DONE; -} - -/* - * Search for the bucket function entry in the catalog and update the values. - */ -static int -replace_time_bucket_function_in_catalog(ContinuousAgg *cagg) -{ - ScanKeyData scankey[1]; - - ScanKeyInit(&scankey[0], - Anum_continuous_aggs_bucket_function_pkey_mat_hypertable_id, - BTEqualStrategyNumber, - F_INT4EQ, - Int32GetDatum(cagg->data.mat_hypertable_id)); - - Catalog *catalog = ts_catalog_get(); - - ScannerCtx scanctx = { - .table = catalog_get_table_id(catalog, CONTINUOUS_AGGS_BUCKET_FUNCTION), - .index = catalog_get_index(catalog, - CONTINUOUS_AGGS_BUCKET_FUNCTION, - CONTINUOUS_AGGS_BUCKET_FUNCTION_PKEY_IDX), - .nkeys = 1, - .scankey = scankey, - .data = cagg, - .limit = 1, - .tuple_found = cagg_time_bucket_update, - .lockmode = AccessShareLock, - .scandirection = ForwardScanDirection, - }; - - return ts_scanner_scan(&scanctx); -} - typedef struct TimeBucketInfoContext { /* The updated cagg definition */ @@ -578,12 +495,12 @@ continuous_agg_replace_function(const ContinuousAgg *cagg, Oid function_to_repla * Get the default origin value for time_bucket to be compatible with * the default origin of time_bucket_ng. */ -static TimestampTz -continuous_agg_get_default_origin(Oid new_bucket_function) +TimestampTz +continuous_agg_get_default_origin(Oid bucket_function) { - Assert(OidIsValid(new_bucket_function)); + Assert(OidIsValid(bucket_function)); - Oid bucket_function_rettype = get_func_rettype(new_bucket_function); + Oid bucket_function_rettype = get_func_rettype(bucket_function); Assert(OidIsValid(bucket_function_rettype)); Datum origin; @@ -689,22 +606,19 @@ continuous_agg_migrate_to_time_bucket(PG_FUNCTION_ARGS) origin_added_during_migration = true; } - /* Update the catalog */ - replace_time_bucket_function_in_catalog(cagg); + /* Modify the CAgg view definition */ + continuous_agg_replace_function(cagg, + old_bucket_function, + origin_added_during_migration, + need_parameter_order_change); /* Fetch new CAgg definition from catalog */ ContinuousAgg PG_USED_FOR_ASSERTS_ONLY *new_cagg_definition = cagg_get_by_relid_or_fail(cagg_relid); - Assert(new_cagg_definition->bucket_function->bucket_function == new_bucket_function); + Assert(new_cagg_definition->bucket_function->bucket_function == old_bucket_function); Assert(cagg->bucket_function->bucket_time_origin == new_cagg_definition->bucket_function->bucket_time_origin); - /* Modify the CAgg view definition */ - continuous_agg_replace_function(cagg, - old_bucket_function, - origin_added_during_migration, - need_parameter_order_change); - /* The migration is a procedure, no return value is expected */ PG_RETURN_VOID(); } @@ -892,7 +806,7 @@ cagg_get_bucket_function_datum(int32 mat_hypertable_id, FunctionCallInfo fcinfo) if (fcinfo != NULL && get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "function returning record called in context that cannot accept type record"); - ContinuousAggsBucketFunction *bf = ts_cagg_get_bucket_function_info(direct_view_oid); + ContinuousAggsBucketFunction *bf = tsl_cagg_get_bucket_function_info(direct_view_oid); if (!OidIsValid(bf->bucket_function)) { diff --git a/tsl/src/continuous_aggs/utils.h b/tsl/src/continuous_aggs/utils.h index 99292f4dba5..a0ed1e34b9a 100644 --- a/tsl/src/continuous_aggs/utils.h +++ b/tsl/src/continuous_aggs/utils.h @@ -19,3 +19,4 @@ extern Datum continuous_agg_validate_query(PG_FUNCTION_ARGS); extern Datum continuous_agg_migrate_to_time_bucket(PG_FUNCTION_ARGS); extern Datum continuous_agg_get_bucket_function(PG_FUNCTION_ARGS); extern Datum continuous_agg_get_bucket_function_info(PG_FUNCTION_ARGS); +extern TimestampTz continuous_agg_get_default_origin(Oid new_bucket_function); diff --git a/tsl/src/init.c b/tsl/src/init.c index 24f5c917515..3ef73d2897b 100644 --- a/tsl/src/init.c +++ b/tsl/src/init.c @@ -147,6 +147,7 @@ CrossModuleFunctions tsl_cm_functions = { .continuous_agg_validate_query = continuous_agg_validate_query, .continuous_agg_get_bucket_function = continuous_agg_get_bucket_function, .continuous_agg_get_bucket_function_info = continuous_agg_get_bucket_function_info, + .continuous_agg_get_bucket_function_info_internal = tsl_cagg_get_bucket_function_info, .continuous_agg_migrate_to_time_bucket = continuous_agg_migrate_to_time_bucket, .cagg_try_repair = tsl_cagg_try_repair, diff --git a/tsl/test/expected/cagg_bgw-16.out b/tsl/test/expected/cagg_bgw-16.out index 15abb5ea074..10a9839fca3 100644 --- a/tsl/test/expected/cagg_bgw-16.out +++ b/tsl/test/expected/cagg_bgw-16.out @@ -109,7 +109,7 @@ SELECT mat_hypertable_id, user_view_schema, user_view_name FROM _timescaledb_cat 2 | public | test_continuous_agg_view (1 row) -SELECT mat_hypertable_id, bucket_width FROM _timescaledb_catalog.continuous_aggs_bucket_function; +SELECT mat_hypertable_id, bucket_width FROM _timescaledb_catalog.continuous_agg, LATERAL _timescaledb_functions.cagg_get_bucket_function_info(mat_hypertable_id); mat_hypertable_id | bucket_width -------------------+-------------- 2 | 2 diff --git a/tsl/test/expected/cagg_migrate.out b/tsl/test/expected/cagg_migrate.out index 492303e1ff2..92c0c0dd197 100644 --- a/tsl/test/expected/cagg_migrate.out +++ b/tsl/test/expected/cagg_migrate.out @@ -159,7 +159,6 @@ UNION ALL COPY _timescaledb_catalog.hypertable (id, schema_name, table_name, associated_schema_name, associated_table_prefix, num_dimensions, chunk_sizing_func_schema, chunk_sizing_func_name, chunk_target_size, compression_state, compressed_hypertable_id, status) FROM stdin; COPY _timescaledb_catalog.dimension (id, hypertable_id, column_name, column_type, aligned, num_slices, partitioning_func_schema, partitioning_func, interval_length, compress_interval_length, integer_now_func_schema, integer_now_func) FROM stdin; COPY _timescaledb_catalog.continuous_agg (mat_hypertable_id, raw_hypertable_id, parent_mat_hypertable_id, user_view_schema, user_view_name, partial_view_schema, partial_view_name, direct_view_schema, direct_view_name, materialized_only, finalized) FROM stdin; -COPY _timescaledb_catalog.continuous_aggs_bucket_function (mat_hypertable_id, bucket_func, bucket_width, bucket_fixed_width) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_invalidation_threshold (hypertable_id, watermark) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_materialization_invalidation_log (materialization_id, lowest_modified_value, greatest_modified_value) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_watermark (mat_hypertable_id, watermark) FROM stdin; @@ -1028,7 +1027,6 @@ UNION ALL COPY _timescaledb_catalog.hypertable (id, schema_name, table_name, associated_schema_name, associated_table_prefix, num_dimensions, chunk_sizing_func_schema, chunk_sizing_func_name, chunk_target_size, compression_state, compressed_hypertable_id, status) FROM stdin; COPY _timescaledb_catalog.dimension (id, hypertable_id, column_name, column_type, aligned, num_slices, partitioning_func_schema, partitioning_func, interval_length, compress_interval_length, integer_now_func_schema, integer_now_func) FROM stdin; COPY _timescaledb_catalog.continuous_agg (mat_hypertable_id, raw_hypertable_id, parent_mat_hypertable_id, user_view_schema, user_view_name, partial_view_schema, partial_view_name, direct_view_schema, direct_view_name, materialized_only, finalized) FROM stdin; -COPY _timescaledb_catalog.continuous_aggs_bucket_function (mat_hypertable_id, bucket_func, bucket_width, bucket_fixed_width) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_invalidation_threshold (hypertable_id, watermark) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_materialization_invalidation_log (materialization_id, lowest_modified_value, greatest_modified_value) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_watermark (mat_hypertable_id, watermark) FROM stdin; @@ -1874,7 +1872,6 @@ UNION ALL COPY _timescaledb_catalog.hypertable (id, schema_name, table_name, associated_schema_name, associated_table_prefix, num_dimensions, chunk_sizing_func_schema, chunk_sizing_func_name, chunk_target_size, compression_state, compressed_hypertable_id, status) FROM stdin; COPY _timescaledb_catalog.dimension (id, hypertable_id, column_name, column_type, aligned, num_slices, partitioning_func_schema, partitioning_func, interval_length, compress_interval_length, integer_now_func_schema, integer_now_func) FROM stdin; COPY _timescaledb_catalog.continuous_agg (mat_hypertable_id, raw_hypertable_id, parent_mat_hypertable_id, user_view_schema, user_view_name, partial_view_schema, partial_view_name, direct_view_schema, direct_view_name, materialized_only, finalized) FROM stdin; -COPY _timescaledb_catalog.continuous_aggs_bucket_function (mat_hypertable_id, bucket_func, bucket_width, bucket_fixed_width) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_invalidation_threshold (hypertable_id, watermark) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_materialization_invalidation_log (materialization_id, lowest_modified_value, greatest_modified_value) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_watermark (mat_hypertable_id, watermark) FROM stdin; @@ -2710,7 +2707,6 @@ UNION ALL COPY _timescaledb_catalog.hypertable (id, schema_name, table_name, associated_schema_name, associated_table_prefix, num_dimensions, chunk_sizing_func_schema, chunk_sizing_func_name, chunk_target_size, compression_state, compressed_hypertable_id, status) FROM stdin; COPY _timescaledb_catalog.dimension (id, hypertable_id, column_name, column_type, aligned, num_slices, partitioning_func_schema, partitioning_func, interval_length, compress_interval_length, integer_now_func_schema, integer_now_func) FROM stdin; COPY _timescaledb_catalog.continuous_agg (mat_hypertable_id, raw_hypertable_id, parent_mat_hypertable_id, user_view_schema, user_view_name, partial_view_schema, partial_view_name, direct_view_schema, direct_view_name, materialized_only, finalized) FROM stdin; -COPY _timescaledb_catalog.continuous_aggs_bucket_function (mat_hypertable_id, bucket_func, bucket_width, bucket_fixed_width) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_invalidation_threshold (hypertable_id, watermark) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_materialization_invalidation_log (materialization_id, lowest_modified_value, greatest_modified_value) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_watermark (mat_hypertable_id, watermark) FROM stdin; @@ -2910,7 +2906,6 @@ UNION ALL COPY _timescaledb_catalog.hypertable (id, schema_name, table_name, associated_schema_name, associated_table_prefix, num_dimensions, chunk_sizing_func_schema, chunk_sizing_func_name, chunk_target_size, compression_state, compressed_hypertable_id, status) FROM stdin; COPY _timescaledb_catalog.dimension (id, hypertable_id, column_name, column_type, aligned, num_slices, partitioning_func_schema, partitioning_func, interval_length, compress_interval_length, integer_now_func_schema, integer_now_func) FROM stdin; COPY _timescaledb_catalog.continuous_agg (mat_hypertable_id, raw_hypertable_id, parent_mat_hypertable_id, user_view_schema, user_view_name, partial_view_schema, partial_view_name, direct_view_schema, direct_view_name, materialized_only, finalized) FROM stdin; -COPY _timescaledb_catalog.continuous_aggs_bucket_function (mat_hypertable_id, bucket_func, bucket_width, bucket_fixed_width) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_invalidation_threshold (hypertable_id, watermark) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_materialization_invalidation_log (materialization_id, lowest_modified_value, greatest_modified_value) FROM stdin; COPY _timescaledb_catalog.continuous_aggs_watermark (mat_hypertable_id, watermark) FROM stdin; diff --git a/tsl/test/expected/cagg_utils.out b/tsl/test/expected/cagg_utils.out index 8ac4bf55243..9eee9ca0bf7 100644 --- a/tsl/test/expected/cagg_utils.out +++ b/tsl/test/expected/cagg_utils.out @@ -378,10 +378,10 @@ ORDER BY user_view_name; -----------------------------+---------------------------------------------------------------------------------------+--------------+------------------------------+---------------+-----------------+-------------------- integer_ht_cagg | time_bucket(integer,integer) | 1 | | | | t integer_ht_cagg_offset | time_bucket(integer,integer,integer) | 1 | | 10 | | t - temperature_4h | time_bucket(interval,timestamp without time zone) | @ 4 hours | Fri Dec 31 16:00:00 1999 PST | | | t - temperature_tz_4h | time_bucket(interval,timestamp with time zone) | @ 4 hours | Fri Dec 31 16:00:00 1999 PST | | | t - temperature_tz_4h_ts | time_bucket(interval,timestamp with time zone,text,timestamp with time zone,interval) | @ 4 hours | Fri Dec 31 16:00:00 1999 PST | | Europe/Berlin | f - temperature_tz_4h_ts_offset | time_bucket(interval,timestamp with time zone,text,timestamp with time zone,interval) | @ 4 hours | Fri Dec 31 16:00:00 1999 PST | @ 1 hour | Europe/Berlin | f + temperature_4h | time_bucket(interval,timestamp without time zone) | @ 4 hours | | | | t + temperature_tz_4h | time_bucket(interval,timestamp with time zone) | @ 4 hours | | | | t + temperature_tz_4h_ts | time_bucket(interval,timestamp with time zone,text,timestamp with time zone,interval) | @ 4 hours | | | Europe/Berlin | f + temperature_tz_4h_ts_offset | time_bucket(interval,timestamp with time zone,text,timestamp with time zone,interval) | @ 4 hours | | @ 1 hour | Europe/Berlin | f temperature_tz_4h_ts_origin | time_bucket(interval,timestamp with time zone,text,timestamp with time zone,interval) | @ 4 hours | Mon Jan 01 00:00:00 2001 PST | | Europe/Berlin | f (7 rows) diff --git a/tsl/test/sql/cagg_bgw.sql.in b/tsl/test/sql/cagg_bgw.sql.in index 4d1a0a34b74..4391eb2b3e0 100644 --- a/tsl/test/sql/cagg_bgw.sql.in +++ b/tsl/test/sql/cagg_bgw.sql.in @@ -95,7 +95,7 @@ SELECT id as raw_table_id FROM _timescaledb_catalog.hypertable WHERE table_name= -- min distance from end should be 1 SELECT mat_hypertable_id, user_view_schema, user_view_name FROM _timescaledb_catalog.continuous_agg; -SELECT mat_hypertable_id, bucket_width FROM _timescaledb_catalog.continuous_aggs_bucket_function; +SELECT mat_hypertable_id, bucket_width FROM _timescaledb_catalog.continuous_agg, LATERAL _timescaledb_functions.cagg_get_bucket_function_info(mat_hypertable_id); SELECT mat_hypertable_id FROM _timescaledb_catalog.continuous_agg \gset SELECT id AS job_id FROM _timescaledb_config.bgw_job where hypertable_id=:mat_hypertable_id \gset diff --git a/tsl/test/sql/exp_cagg_monthly.sql b/tsl/test/sql/exp_cagg_monthly.sql index ddfcb25d325..13774955fe7 100644 --- a/tsl/test/sql/exp_cagg_monthly.sql +++ b/tsl/test/sql/exp_cagg_monthly.sql @@ -76,8 +76,8 @@ WHERE user_view_name = 'conditions_summary' \gset \pset null -SELECT * -FROM _timescaledb_catalog.continuous_aggs_bucket_function +SELECT mat_hypertable_id, bf.* +FROM _timescaledb_catalog.continuous_agg, LATERAL _timescaledb_functions.cagg_get_bucket_function_info(mat_hypertable_id) AS bf WHERE mat_hypertable_id = :cagg_id; \pset null "" @@ -103,20 +103,6 @@ SELECT city, to_char(bucket, 'YYYY-MM-DD') AS month, min, max FROM conditions_summary ORDER by month, city; --- Special check for "invalid or missing information about the bucketing --- function" code path -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -CREATE TEMPORARY TABLE restore_table ( LIKE _timescaledb_catalog.continuous_aggs_bucket_function ); -INSERT INTO restore_table SELECT * FROM _timescaledb_catalog.continuous_aggs_bucket_function; -DELETE FROM _timescaledb_catalog.continuous_aggs_bucket_function; -\set ON_ERROR_STOP 0 --- should fail with "invalid or missing information..." -CALL refresh_continuous_aggregate('conditions_summary', '2021-06-01', '2021-07-01'); -\set ON_ERROR_STOP 1 -INSERT INTO _timescaledb_catalog.continuous_aggs_bucket_function SELECT * FROM restore_table; -DROP TABLE restore_table; --- should execute successfully -CALL refresh_continuous_aggregate('conditions_summary', '2021-06-01', '2021-07-01'); SET ROLE :ROLE_DEFAULT_PERM_USER; -- Check the invalidation threshold @@ -166,7 +152,8 @@ DROP MATERIALIZED VIEW conditions_summary; SELECT * FROM _timescaledb_catalog.continuous_agg WHERE mat_hypertable_id = :cagg_id; -SELECT * FROM _timescaledb_catalog.continuous_aggs_bucket_function +SELECT mat_hypertable_id, bf.* +FROM _timescaledb_catalog.continuous_agg, LATERAL _timescaledb_functions.cagg_get_bucket_function_info(mat_hypertable_id) AS bf WHERE mat_hypertable_id = :cagg_id; -- Re-create cagg, this time WITH DATA diff --git a/tsl/test/sql/exp_cagg_next_gen.sql b/tsl/test/sql/exp_cagg_next_gen.sql index 6aec2b6c784..d0cb03a770f 100644 --- a/tsl/test/sql/exp_cagg_next_gen.sql +++ b/tsl/test/sql/exp_cagg_next_gen.sql @@ -160,8 +160,9 @@ FROM conditions GROUP BY city, bucket WITH NO DATA; -SELECT bucket_func, bucket_width, bucket_origin, bucket_timezone, bucket_fixed_width -FROM _timescaledb_catalog.continuous_aggs_bucket_function ORDER BY 1; +SELECT bf.* +FROM _timescaledb_catalog.continuous_agg, LATERAL _timescaledb_functions.cagg_get_bucket_function_info(mat_hypertable_id) AS bf +ORDER BY 1; -- Try to toggle realtime feature on existing CAgg using timescaledb_experimental.time_bucket_ng ALTER MATERIALIZED VIEW conditions_summary_monthly SET (timescaledb.materialized_only=false); diff --git a/tsl/test/sql/exp_cagg_origin.sql b/tsl/test/sql/exp_cagg_origin.sql index 7641da0d8af..f66377ba5e2 100644 --- a/tsl/test/sql/exp_cagg_origin.sql +++ b/tsl/test/sql/exp_cagg_origin.sql @@ -526,9 +526,9 @@ SELECT mat_hypertable_id AS cagg_id WHERE user_view_name = 'conditions_summary_timestamptz' \gset -SELECT bucket_func, bucket_width, bucket_origin, bucket_timezone, bucket_fixed_width - FROM _timescaledb_catalog.continuous_aggs_bucket_function - WHERE mat_hypertable_id = :cagg_id; +SELECT bf.* +FROM _timescaledb_catalog.continuous_agg, LATERAL _timescaledb_functions.cagg_get_bucket_function_info(mat_hypertable_id) AS bf +WHERE mat_hypertable_id = :cagg_id; SELECT city, to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') AS b, min, max FROM conditions_summary_timestamptz diff --git a/tsl/test/sql/exp_cagg_timezone.sql b/tsl/test/sql/exp_cagg_timezone.sql index 9a93182562f..8c03e6c8949 100644 --- a/tsl/test/sql/exp_cagg_timezone.sql +++ b/tsl/test/sql/exp_cagg_timezone.sql @@ -126,8 +126,8 @@ WHERE user_view_name = 'conditions_summary_tz' \gset -- Make sure the timezone is saved in the catalog table -SELECT bucket_func, bucket_width, bucket_origin, bucket_timezone, bucket_fixed_width -FROM _timescaledb_catalog.continuous_aggs_bucket_function +SELECT bf.* +FROM _timescaledb_catalog.continuous_agg, LATERAL _timescaledb_functions.cagg_get_bucket_function_info(mat_hypertable_id) AS bf WHERE mat_hypertable_id = :cagg_id_tz; -- Make sure that buckets with specified timezone are always treated as @@ -153,8 +153,8 @@ WHERE user_view_name = 'conditions_summary_1w' \gset -- Make sure the timezone is saved in the catalog table -SELECT bucket_func, bucket_width, bucket_origin, bucket_timezone, bucket_fixed_width -FROM _timescaledb_catalog.continuous_aggs_bucket_function +SELECT bf.* +FROM _timescaledb_catalog.continuous_agg, LATERAL _timescaledb_functions.cagg_get_bucket_function_info(mat_hypertable_id) AS bf WHERE mat_hypertable_id = :cagg_id_1w; -- Check the invalidation threshold is -infinity diff --git a/tsl/test/sql/include/cagg_query_common.sql b/tsl/test/sql/include/cagg_query_common.sql index c1bcb3b9da4..e16da6ffec2 100644 --- a/tsl/test/sql/include/cagg_query_common.sql +++ b/tsl/test/sql/include/cagg_query_common.sql @@ -2,6 +2,7 @@ -- Please see the included NOTICE for copyright information and -- LICENSE-TIMESCALE for a copy of the license. +\set TEST_BASE_NAME cagg_query SELECT format('%s/results/%s_results_view.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_VIEW", format('%s/results/%s_results_view_hashagg.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_VIEW_HASHAGG", @@ -360,7 +361,7 @@ INSERT INTO table_bigint VALUES(1,2); CREATE VIEW caggs_info AS SELECT user_view_schema, user_view_name, bucket_func, bucket_width, bucket_origin, bucket_offset, bucket_timezone, bucket_fixed_width -FROM _timescaledb_catalog.continuous_aggs_bucket_function NATURAL JOIN _timescaledb_catalog.continuous_agg; +FROM _timescaledb_catalog.continuous_agg, LATERAL _timescaledb_functions.cagg_get_bucket_function_info(mat_hypertable_id); --- -- Tests with CAgg creation @@ -557,17 +558,6 @@ CREATE MATERIALIZED VIEW cagg_bigint_offset2 GROUP BY 1 WITH NO DATA; SELECT * FROM caggs_info WHERE user_view_name = 'cagg_bigint_offset2'; --- mess with the bucket_func signature to make sure it will raise an exception -SET ROLE :ROLE_CLUSTER_SUPERUSER; -\set ON_ERROR_STOP 0 -BEGIN; -UPDATE _timescaledb_catalog.continuous_aggs_bucket_function SET bucket_func = 'func_does_not_exist()'; --- should error because function does not exist -CALL refresh_continuous_aggregate('cagg_bigint_offset2', NULL, NULL); -ROLLBACK; -\set ON_ERROR_STOP 1 -SET ROLE :ROLE_DEFAULT_PERM_USER; - DROP MATERIALIZED VIEW cagg_bigint_offset2; -- Test invalid bucket definitions diff --git a/tsl/test/sql/include/cagg_run_timebucket_migration.sql b/tsl/test/sql/include/cagg_run_timebucket_migration.sql index 8d166adf8d4..fcab342deda 100644 --- a/tsl/test/sql/include/cagg_run_timebucket_migration.sql +++ b/tsl/test/sql/include/cagg_run_timebucket_migration.sql @@ -8,14 +8,20 @@ SELECT mat_hypertable_id, FROM _timescaledb_catalog.continuous_agg where user_view_name = :'CAGG_NAME' \gset -SELECT * FROM _timescaledb_catalog.continuous_aggs_bucket_function WHERE mat_hypertable_id = :mat_hypertable_id; +SELECT mat_hypertable_id, bf.* +FROM _timescaledb_catalog.continuous_agg, LATERAL _timescaledb_functions.cagg_get_bucket_function_info(mat_hypertable_id) AS bf +WHERE mat_hypertable_id = :mat_hypertable_id; + SELECT pg_get_viewdef(:'partial_view', true); SELECT pg_get_viewdef(:'direct_view', true); SELECT pg_get_viewdef(:'CAGG_NAME', true); CALL _timescaledb_functions.cagg_migrate_to_time_bucket(:'CAGG_NAME'); -SELECT * FROM _timescaledb_catalog.continuous_aggs_bucket_function WHERE mat_hypertable_id = :mat_hypertable_id; +SELECT mat_hypertable_id, bf.* +FROM _timescaledb_catalog.continuous_agg, LATERAL _timescaledb_functions.cagg_get_bucket_function_info(mat_hypertable_id) AS bf +WHERE mat_hypertable_id = :mat_hypertable_id; + SELECT pg_get_viewdef(:'partial_view', true); SELECT pg_get_viewdef(:'direct_view', true); SELECT pg_get_viewdef(:'CAGG_NAME', true); diff --git a/tsl/test/sql/include/data/cagg_migrate_integer.sql.gz b/tsl/test/sql/include/data/cagg_migrate_integer.sql.gz index 4a67fae9f5d655ace6c6399338b4362f75067afc..3a04dba4a6e6c7228d610eecaf4b5a2073cf0b08 100644 GIT binary patch delta 1592 zcmV-82FLmG48aTsABzYGqey&_2OfV>Z`w!@em1{i)fbFJQLvpRm)@O3sgp+HpdxT> zbv>;N-jG#{owL^^Y1RLJvtAp#Hg;@CqDD&72bi6m{dQ))*;$+Z*z8SAH0iw?nCK>) zIdtBcd#+6x_1pld4FvyQF}ftL5Oo<@5FZUk6EvI-2JL8&k>w5XEf)F&x#543_;lWA zy=^r5!t&qe<|l-?fnaVq#GYYp#&<2JgKg@QIm6#5Sz!(JE*j{<9GLw{0))@Pvv7V* zn5r4=7)J{+KWN>GLF?NhHwZE@7=+8*AjtUG>3H;4-diwo zHXcnsqIX}ATE2xH9HhTEsN8=+#yt+|zBzvO9!l>a&?vUxw~H!rOm|%|ilV2&BQ;+RM6$Vj5_ zFgeji>MlTb$)$Zxu9sx`Rg+0Ns~;$vav(5xU}^bx*tV#1m(HW&GqqD8e=e!J@pAKK zf#(*p9B%Bp2_v_x9i3z+C$6<5^5^D{ADgM|c-qgOO)>t@zwI)VNqQz0 z<3*+Oq&9lU-={;*NwE!ma^TfP&sp#{f_#h-oU)*n`$BPIm{I|1O&cT^;YZTB<|T@|F3_wu5n}>>u-$X=33rr zrHjtJC>qn@`DloGgTZ~B%BG-nO4_=VR%wS{Kb{~$ zMLkB|-8Bn6cFC$zvDf5cU#zw%sUki6zqz=lcP`r)Zi?{J`9h6b4gNkl?FQxub zLR5IOE~xdRk6+}pOvtl|E>fx8M#21wEG;a^BBCxnTS4H>DL}Tdq?3{h^&6jD(%UwF z*o|#i(G577cKqldpx>#xh+Gld*)}3f3pum=1z~)ci@bj&*blf#957gQBa%}!+6yRz zK}*CiU^aE%HoA)NoU(D0vX!rju!g%zSJ@s#1qAcZ0T4KTTEVA&gQ*Ab_jRZoG_)ey zg-u#H7;PUgdR;Ju69gE2A23E;FohEY7-JtWUpr+KStqezNG8eVo)a$JBxA)F1}eY| zhb~-V3nqUAkaGlB5Iw2mxN6mrc~tGNC0gz;m6uHcEOQTaF$t z2b&O;-HMCIBfs7%(vp6Wpj;}fp;ubD(yE13MtaQ;t6nyY`&JdVl}gJN))VRFd9-Yu zbmP(BB*%8b?Qm#|OtsUUmc-Q(4uhu3FDQYoBi| z3I!3-q=@L!HzOCVq#aIOPY46gsm=1Ug}>?u$bZc?g3A%}!O?H)+LE@DGnU-<&xh~L z&n1tc;%*X5+#=}lYyWy;dT~BHLo?~vlk=>(%{50xp*{&z{rNyO9uaDE!<3IApDy@sYYUZiS;ZyUpF4{XiJEc=La{kdu@CDF6T%KMCmo delta 1641 zcmV-v2A2834Dt*IABzYGB%e%?2OfWIPuoZk{_OmURlmfMQbg>faP;mZni`6fOR5mA zt*)n)iI=d#u{nFwK&$@uoAug>*KgPfpdwNFA(@?>eRgJ^*;zaNv2`@D(B$adz(O~R z3ztoIroQJe&U`OKdJDn7SDel1E5tlbXEZ>=(F6_82ZL@j$m#rs1~y*=6nTG(IStsf z)p^@$^`+&%Ppwag2m|5NcBykggqgsz-5uhvfKEC2#^{n5=;*A4&a8pepCmxUEON1! zUQ@0cxEtf>fF_bsiD1q=J%u6~2U$F5-{uGHTRCWdo8<;UCI`dCJU0k3F?Kv2{gw9? zjGT-|=O59#FG#Q6!Uhg9UmSl_?I3)QgL-ccp1p_Cdq}xR(znD%a~nP}+hyOWL$2-s z47V>_3XGHNmZnSC4Ngc{y>f;sfX%7`*kDm1*a)Lp2ctck5d*e!amT5>7MU%hHU#f9E4{cG774w%1ZyJB$-HFXZ0uczS zp7o=2hfI`4b}}ws*@5h8VkaA&l^saQCU&xcT-kvnW z_RWk;ZEm~%Y)52Le7b*k+J}dn-tumAk{up;_MEDp+dqD+r*`9MKYzC6_&@)4t57EC znOKY$RnC*f=plcf4!s~n5&Go7Yl>d5=IPK2Di)w$oybkm3$8vLdQkuh(5G95roAXk z{Pe*ZTR`RexP^X4{n63DI_q0{De(o&r7#2pzShl7=kPG@*7sCYMdvn=WRX#qpY1U8rwkxFMA1pfrTR@kFWGHZ zJnSY8tmuUTO*ejY5VG&gn?!SJ+y)G83Z`^|1cSE$^L3|+BC8~p49O(f)OQziFUeT>g@Xz( z!(}s%I+A}0A>m)Z4R|(?zOVw<4AWMgyCibkw zY8n?Fk^g&t$e(G^jpu7lS-_aB4#HVYC47>8!T8U>OlzM;Z_p*svS|+s=2Kb(P*F{P1=vRQ$+9soIQmz;L((j029BasUl-TYg(d@(n|XwJ zP^o{IV+(f>egy^W%6A>4XPmO?hq5Iq)ekjXc}*p`xPk;GM8Q!t0Ep`^X$Ymjci1I^ zD8QTr@NyPVP|gix!}Iz#!!XT#W7m9hu!r~e_YQUsw7hH|qElNH*H>-kpq0-H)3hWa zh7u7|`G#}RN-hYg3mfI&IlWzd+KQJyA^m@^-9>O+-dV?^u~_oV8c#L~E6hjMQU3!Pk3Lx; z>$BBAhXs$%hyBTE*4+Byr=rlG1giObpyEe_8r`|oqbOiA@vEbw;|ZuFpvRCz0}JND nr}K~Rg_Xke+X$&ze+%L0?OuB`XFm|e0&o5g@Qz^&5Gw!xv8pK7 diff --git a/tsl/test/sql/include/data/cagg_migrate_timestamp.sql.gz b/tsl/test/sql/include/data/cagg_migrate_timestamp.sql.gz index bb3cf45b41cbedc6e88ce72ccc194c359a604828..227d340538287a403e5084960e484f54b29eabdc 100644 GIT binary patch delta 1569 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 T*$2Xy;m!X54;c(Stu6ono1Yt4 diff --git a/tsl/test/sql/include/data/cagg_migrate_timestamptz.sql.gz b/tsl/test/sql/include/data/cagg_migrate_timestamptz.sql.gz index 32744f15b4f94a4e424a206409d39b27241751be..986ae0384bbc4c79e5b85456bae46a4f45c1e589 100644 GIT binary patch delta 1629 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 Yr`0@dZszSBVa)O7f10sqbQmrG0A6cAr2qf`