From 7a4191bd84a48ca8438a685da009fdbf00f13e18 Mon Sep 17 00:00:00 2001 From: Matvey Arye Date: Tue, 2 Apr 2019 16:39:46 -0400 Subject: [PATCH] Handle drops on continuous agg views and tables Previously we used postgres dependency tracking to ensure consistent deletes between various continuous agg postgres objects (views and tables). This does not work with dump/restore and thus this PR removes that dependency tracking in favor of handling these deps ourselves in various drop hooks. This PR also adds logic for deleting rows in the continuous_agg metadata table when the view is dropped. It does not yet handle deleting associated threshold metadata, that's left for a future PR. Most of this logic is in the apache-licensed code and not in the TSL since we want people who downgraded from TSL to apache to still be able to drop continuous views. --- src/CMakeLists.txt | 1 + src/catalog.c | 6 + src/continuous_agg.c | 208 ++++++++++++++++++++++++++ src/continuous_agg.h | 28 ++++ src/event_trigger.c | 21 +++ src/event_trigger.h | 8 + src/hypertable.c | 18 +++ src/hypertable.h | 1 + src/process_utility.c | 56 +++++++ src/scan_iterator.h | 8 +- src/scanner.h | 7 +- tsl/src/continuous_aggs/cagg_create.c | 9 +- tsl/src/continuous_aggs/cagg_create.h | 8 +- tsl/test/expected/contaggviews.out | 156 +++++++++++++++++-- tsl/test/sql/contaggviews.sql | 88 ++++++++++- tsl/test/src/test_ddl_hook.c | 5 + 16 files changed, 602 insertions(+), 26 deletions(-) create mode 100644 src/continuous_agg.c create mode 100644 src/continuous_agg.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e8875071d..8375c0797 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ set(SOURCES cache.c cache_invalidate.c catalog.c + continuous_agg.c chunk.c chunk_adaptive.c chunk_constraint.c diff --git a/src/catalog.c b/src/catalog.c index 604eda140..3dd18e895 100644 --- a/src/catalog.c +++ b/src/catalog.c @@ -189,6 +189,12 @@ static const TableIndexDef catalog_table_index_definitions[_MAX_CATALOG_TABLES] [BGW_POLICY_CHUNK_STATS_JOB_ID_CHUNK_ID_IDX] = "bgw_policy_chunk_stats_job_id_chunk_id_key", }, }, + [CONTINUOUS_AGG] = { + .length = _MAX_CONTINUOUS_AGG_INDEX, + .names = (char *[]) { + [CONTINUOUS_AGG_PKEY] = "continuous_agg_pkey" + }, + }, [CONTINUOUS_AGGS_COMPLETED_THRESHOLD] = { .length = _MAX_CONTINUOUS_AGGS_COMPLETED_THRESHOLD_INDEX, .names = (char *[]) { diff --git a/src/continuous_agg.c b/src/continuous_agg.c new file mode 100644 index 000000000..07643d927 --- /dev/null +++ b/src/continuous_agg.c @@ -0,0 +1,208 @@ +/* + * This file and its contents are licensed under the Timescale License. + * Please see the included NOTICE for copyright information and + * LICENSE-APACHE for a copy of the license. + */ + +/* This file handles commands on continuous aggs that should be allowed in + * apache only mode. Right now this consists mostly of drop commands + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "scan_iterator.h" +#include "continuous_agg.h" +#include "hypertable.h" +#include "compat.h" + +#if !PG96 +#include +#endif + +static void +init_scan_by_mat_hypertable_id(ScanIterator *iterator, const int32 mat_hypertable_id) +{ + iterator->ctx.index = catalog_get_index(ts_catalog_get(), CONTINUOUS_AGG, CONTINUOUS_AGG_PKEY); + + ts_scan_iterator_scan_key_init(iterator, + Anum_continuous_agg_pkey_mat_hypertable_id, + BTEqualStrategyNumber, + F_INT4EQ, + Int32GetDatum(mat_hypertable_id)); +} + +static void +continuous_agg_init(ContinuousAgg *cagg, FormData_continuous_agg *fd) +{ + memcpy(&cagg->data, fd, sizeof(cagg->data)); +} + +ContinuousAgg * +ts_continuous_agg_find_by_view_name(const char *schema, const char *name) +{ + ScanIterator iterator = + ts_scan_iterator_create(CONTINUOUS_AGG, AccessShareLock, CurrentMemoryContext); + ContinuousAgg *ca = NULL; + int count = 0; + + ts_scanner_foreach(&iterator) + { + FormData_continuous_agg *data = + (FormData_continuous_agg *) GETSTRUCT(ts_scan_iterator_tuple(&iterator)); + if (ts_continuous_agg_is_user_view(data, schema, name) || + ts_continuous_agg_is_partial_view(data, schema, name)) + { + ca = palloc0(sizeof(*ca)); + continuous_agg_init(ca, data); + count++; + } + } + Assert(count <= 1); + return ca; +} + +/* + * Drops continuous aggs and all related objects. + * + * These objects are: the user view itself, the catalog entry, the partial view, + * the materialization hypertable. + * + * drop_user_view indicates whether to drop the user view. + * (should be false if called as part of the drop-user-view callback) + */ +static void +drop_continuous_agg(ContinuousAgg *agg, bool drop_user_view) +{ + ScanIterator iterator = + ts_scan_iterator_create(CONTINUOUS_AGG, RowExclusiveLock, CurrentMemoryContext); + ObjectAddress user_view, partial_view; + Hypertable *mat_hypertable; + int count = 0; + + /* Delete view itself */ + if (drop_user_view) + { + user_view = (ObjectAddress){ + .classId = RelationRelationId, + .objectId = + get_relname_relid(NameStr(agg->data.user_view_name), + get_namespace_oid(NameStr(agg->data.user_view_schema), false)), + }; + performDeletion(&user_view, DROP_RESTRICT, 0); + } + + /* Delete catalog entry. */ + init_scan_by_mat_hypertable_id(&iterator, agg->data.mat_hypertable_id); + ts_scanner_foreach(&iterator) + { + TupleInfo *ti = ts_scan_iterator_tuple_info(&iterator); + ts_catalog_delete(ti->scanrel, ti->tuple); + count++; + } + Assert(count == 1); + + /* Drop partial view, materialized table */ + partial_view = (ObjectAddress){ + .classId = RelationRelationId, + .objectId = + get_relname_relid(NameStr(agg->data.partial_view_name), + get_namespace_oid(NameStr(agg->data.partial_view_schema), false)), + }; + + /* The partial view may already be dropped by PG's dependency system (e.g. the raw table was + * dropped) */ + if (OidIsValid(partial_view.objectId)) + performDeletion(&partial_view, DROP_RESTRICT, 0); + + mat_hypertable = ts_hypertable_get_by_id(agg->data.mat_hypertable_id); + + /* Drop materialized hypertable */ + ts_hypertable_drop(mat_hypertable); +} + +/* + * This is a called when a hypertable gets dropped. + * + * If the hypertable is a raw hypertable for a continuous agg, + * drop the continuous agg. + * + * If the hypertable is a materialization hypertable, error out + * and force the user to drop the continuous agg instead. + */ +void +ts_continuous_agg_drop_hypertable_callback(int32 hypertable_id) +{ + ScanIterator iterator = + ts_scan_iterator_create(CONTINUOUS_AGG, AccessShareLock, CurrentMemoryContext); + ContinuousAgg ca; + + ts_scanner_foreach(&iterator) + { + FormData_continuous_agg *data = + (FormData_continuous_agg *) GETSTRUCT(ts_scan_iterator_tuple(&iterator)); + if (data->raw_hypertable_id == hypertable_id) + { + continuous_agg_init(&ca, data); + drop_continuous_agg(&ca, true); + } + if (data->mat_hypertable_id == hypertable_id) + ereport(ERROR, + (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), + errmsg("cannot drop the materialized table because it is required by a " + "continuous aggregate"))); + } +} + +/* Block dropping the partial view if the continuous aggregate still exists */ +static void +drop_partial_view(ContinuousAgg *agg) +{ + ScanIterator iterator = + ts_scan_iterator_create(CONTINUOUS_AGG, AccessShareLock, CurrentMemoryContext); + int count = 0; + init_scan_by_mat_hypertable_id(&iterator, agg->data.mat_hypertable_id); + ts_scanner_foreach(&iterator) + { + TupleInfo *ti = ts_scan_iterator_tuple_info(&iterator); + ts_catalog_delete(ti->scanrel, ti->tuple); + count++; + } + if (count > 0) + ereport(ERROR, + (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), + errmsg("cannot drop the partial view because it is required by a continuous " + "aggregate"))); +} + +/* This gets called when a view gets dropped. */ +void +ts_continuous_agg_drop_view_callback(ContinuousAgg *ca, const char *schema, const char *name) +{ + if (ts_continuous_agg_is_user_view(&ca->data, schema, name)) + drop_continuous_agg(ca, false /* The user view has already been dropped */); + else if (ts_continuous_agg_is_partial_view(&ca->data, schema, name)) + drop_partial_view(ca); + else + elog(ERROR, "unknown continuous aggregate view type"); +} + +bool +ts_continuous_agg_is_user_view(FormData_continuous_agg *data, const char *schema, const char *name) +{ + return (namestrcmp(&data->user_view_schema, schema) == 0) && + (namestrcmp(&data->user_view_name, name) == 0); +} + +bool +ts_continuous_agg_is_partial_view(FormData_continuous_agg *data, const char *schema, + const char *name) +{ + return (namestrcmp(&data->partial_view_schema, schema) == 0) && + (namestrcmp(&data->partial_view_name, name) == 0); +} diff --git a/src/continuous_agg.h b/src/continuous_agg.h new file mode 100644 index 000000000..64dcd7281 --- /dev/null +++ b/src/continuous_agg.h @@ -0,0 +1,28 @@ +/* + * This file and its contents are licensed under the Timescale License. + * Please see the included NOTICE for copyright information and + * LICENSE-APACHE for a copy of the license. + */ +#ifndef TIMESCALEDB_TSL_CONTINUOUS_AGGS_CONTIGUOUS_AGG_H +#define TIMESCALEDB_TSL_CONTINUOUS_AGGS_CONTIGUOUS_AGG_H +#include + +#include + +typedef struct ContinuousAgg +{ + FormData_continuous_agg data; +} ContinuousAgg; + +extern ContinuousAgg *ts_continuous_agg_find_by_view_name(const char *schema, const char *name); +extern void ts_continuous_agg_drop_view_callback(ContinuousAgg *ca, const char *schema, + const char *name); + +extern void ts_continuous_agg_drop_hypertable_callback(int32 hypertable_id); + +extern bool ts_continuous_agg_is_user_view(FormData_continuous_agg *data, const char *schema, + const char *name); +extern bool ts_continuous_agg_is_partial_view(FormData_continuous_agg *data, const char *schema, + const char *name); + +#endif /* TIMESCALEDB_TSL_CONTINUOUS_AGGS_CONTIGUOUS_AGG_H */ diff --git a/src/event_trigger.c b/src/event_trigger.c index b7b4c787d..898ef7c59 100644 --- a/src/event_trigger.c +++ b/src/event_trigger.c @@ -142,6 +142,19 @@ make_event_trigger_drop_table(char *table_name, char *schema) return obj; } +static EventTriggerDropView * +make_event_trigger_drop_view(char *view_name, char *schema) +{ + EventTriggerDropView *obj = palloc(sizeof(*obj)); + + *obj = (EventTriggerDropView){ + .obj = { .type = EVENT_TRIGGER_DROP_VIEW }, + .view_name = view_name, + .schema = schema, + }; + return obj; +} + static EventTriggerDropSchema * make_event_trigger_drop_schema(char *schema) { @@ -233,6 +246,14 @@ ts_event_trigger_dropped_objects(void) make_event_trigger_drop_table(lsecond(addrnames), linitial(addrnames))); } + else if (strcmp(objtype, "view") == 0) + { + List *addrnames = extract_addrnames(DatumGetArrayTypeP(values[10])); + + objects = lappend(objects, + make_event_trigger_drop_view(lsecond(addrnames), + linitial(addrnames))); + } break; case NamespaceRelationId: { diff --git a/src/event_trigger.h b/src/event_trigger.h index 1fe316fd1..5a1768a72 100644 --- a/src/event_trigger.h +++ b/src/event_trigger.h @@ -14,6 +14,7 @@ typedef enum EventTriggerDropType EVENT_TRIGGER_DROP_TABLE_CONSTRAINT, EVENT_TRIGGER_DROP_INDEX, EVENT_TRIGGER_DROP_TABLE, + EVENT_TRIGGER_DROP_VIEW, EVENT_TRIGGER_DROP_SCHEMA, EVENT_TRIGGER_DROP_TRIGGER } EventTriggerDropType; @@ -45,6 +46,13 @@ typedef struct EventTriggerDropTable char *schema; } EventTriggerDropTable; +typedef struct EventTriggerDropView +{ + EventTriggerDropObject obj; + char *view_name; + char *schema; +} EventTriggerDropView; + typedef struct EventTriggerDropSchema { EventTriggerDropObject obj; diff --git a/src/hypertable.c b/src/hypertable.c index e3acfa140..1bc7eae97 100644 --- a/src/hypertable.c +++ b/src/hypertable.c @@ -57,6 +57,7 @@ #include "funcapi.h" #include "utils.h" #include "bgw_policy/policy.h" +#include "continuous_agg.h" Oid ts_rel_get_owner(Oid relid) @@ -411,6 +412,9 @@ hypertable_tuple_delete(TupleInfo *ti, void *data) /* Also remove any policy argument / job that uses this hypertable */ ts_bgw_policy_delete_by_hypertable_id(hypertable_id); + /* Remove any dependent continuous aggs */ + ts_continuous_agg_drop_hypertable_callback(hypertable_id); + ts_catalog_database_info_become_owner(ts_catalog_database_info_get(), &sec_ctx); ts_catalog_delete(ti->scanrel, ti->tuple); ts_catalog_restore_user(&sec_ctx); @@ -446,6 +450,20 @@ ts_hypertable_delete_by_name(const char *schema_name, const char *table_name) CurrentMemoryContext); } +void +ts_hypertable_drop(Hypertable *hypertable) +{ + ObjectAddress hypertable_addr = (ObjectAddress){ + .classId = RelationRelationId, + .objectId = hypertable->main_table_relid, + }; + + /* Drop the postgres table */ + performDeletion(&hypertable_addr, DROP_CASCADE, 0); + /* Clean up catalog */ + ts_hypertable_delete_by_name(hypertable->fd.schema_name.data, hypertable->fd.table_name.data); +} + static ScanTupleResult reset_associated_tuple_found(TupleInfo *ti, void *data) { diff --git a/src/hypertable.h b/src/hypertable.h index bf01440a6..b41724a8e 100644 --- a/src/hypertable.h +++ b/src/hypertable.h @@ -76,6 +76,7 @@ extern int ts_hypertable_set_name(Hypertable *ht, const char *newname); extern int ts_hypertable_set_schema(Hypertable *ht, const char *newname); extern int ts_hypertable_set_num_dimensions(Hypertable *ht, int16 num_dimensions); extern int ts_hypertable_delete_by_name(const char *schema_name, const char *table_name); +extern void ts_hypertable_drop(Hypertable *hypertable); extern int ts_hypertable_reset_associated_schema_name(const char *associated_schema); extern TSDLLEXPORT Oid ts_hypertable_id_to_relid(int32 hypertable_id); extern TSDLLEXPORT int32 ts_hypertable_relid_to_id(Oid relid); diff --git a/src/process_utility.c b/src/process_utility.c index 8ca6e0c1e..d5e3d16e3 100644 --- a/src/process_utility.c +++ b/src/process_utility.c @@ -61,6 +61,7 @@ #include "utils.h" #include "with_clause_parser.h" #include "cross_module_fn.h" +#include "continuous_agg.h" #include "cross_module_fn.h" @@ -770,6 +771,45 @@ process_grant_and_revoke_role(ProcessUtilityArgs *args) return true; } +/* Force the use of CASCADE to drop continuous aggregates */ +static void +block_dropping_continuous_aggregates_without_cascade(ProcessUtilityArgs *args, DropStmt *stmt) +{ + ListCell *lc; + + if (stmt->behavior == DROP_CASCADE) + return; + + foreach (lc, stmt->objects) + { + List *object = lfirst(lc); + RangeVar *relation = makeRangeVarFromNameList(object); + Oid relid; + char *schema; + char *name; + ContinuousAgg *cagg; + + if (NULL == relation) + continue; + + relid = RangeVarGetRelid(relation, NoLock, true); + if (!OidIsValid(relid)) + continue; + + schema = get_namespace_name(get_rel_namespace(relid)); + name = get_rel_name(relid); + + cagg = ts_continuous_agg_find_by_view_name(schema, name); + if (cagg == NULL) + continue; + + if (ts_continuous_agg_is_user_view(&cagg->data, schema, name)) + ereport(ERROR, + (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), + errmsg("dropping a continous aggregate requires using CASCADE"))); + } +} + static void process_drop(ProcessUtilityArgs *args) { @@ -783,6 +823,9 @@ process_drop(ProcessUtilityArgs *args) case OBJECT_INDEX: process_drop_hypertable_index(args, stmt); break; + case OBJECT_VIEW: + block_dropping_continuous_aggregates_without_cascade(args, stmt); + break; default: break; } @@ -2769,6 +2812,16 @@ process_drop_trigger(EventTriggerDropObject *obj) } } +static void +process_drop_view(EventTriggerDropView *dropped_view) +{ + ContinuousAgg *ca; + + ca = ts_continuous_agg_find_by_view_name(dropped_view->schema, dropped_view->view_name); + if (ca != NULL) + ts_continuous_agg_drop_view_callback(ca, dropped_view->schema, dropped_view->view_name); +} + static void process_ddl_sql_drop(EventTriggerDropObject *obj) { @@ -2789,6 +2842,9 @@ process_ddl_sql_drop(EventTriggerDropObject *obj) case EVENT_TRIGGER_DROP_TRIGGER: process_drop_trigger(obj); break; + case EVENT_TRIGGER_DROP_VIEW: + process_drop_view((EventTriggerDropView *) obj); + break; } } diff --git a/src/scan_iterator.h b/src/scan_iterator.h index 8171052ee..18a69572b 100644 --- a/src/scan_iterator.h +++ b/src/scan_iterator.h @@ -49,11 +49,11 @@ ts_scan_iterator_tupledesc(ScanIterator *iterator) return iterator->tinfo->desc; } -void ts_scan_iterator_close(ScanIterator *iterator); +void TSDLLEXPORT ts_scan_iterator_close(ScanIterator *iterator); -void ts_scan_iterator_scan_key_init(ScanIterator *iterator, AttrNumber attributeNumber, - StrategyNumber strategy, RegProcedure procedure, - Datum argument); +void TSDLLEXPORT ts_scan_iterator_scan_key_init(ScanIterator *iterator, AttrNumber attributeNumber, + StrategyNumber strategy, RegProcedure procedure, + Datum argument); /* You must use `ts_scan_iterator_close` if terminating this loop early */ #define ts_scanner_foreach(scan_iterator) \ diff --git a/src/scanner.h b/src/scanner.h index 9b016d830..b3664ae00 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -136,8 +136,9 @@ typedef struct InternalScannerCtx bool closed; } InternalScannerCtx; -void ts_scanner_start_scan(ScannerCtx *ctx, InternalScannerCtx *ictx); -void ts_scanner_end_scan(ScannerCtx *ctx, InternalScannerCtx *ictx); -TupleInfo *ts_scanner_next(ScannerCtx *ctx, InternalScannerCtx *ictx); +extern TSDLLEXPORT void ts_scanner_start_scan(ScannerCtx *ctx, InternalScannerCtx *ictx); + +extern TSDLLEXPORT void ts_scanner_end_scan(ScannerCtx *ctx, InternalScannerCtx *ictx); +extern TSDLLEXPORT TupleInfo *ts_scanner_next(ScannerCtx *ctx, InternalScannerCtx *ictx); #endif /* TIMESCALEDB_SCANNER_H */ diff --git a/tsl/src/continuous_aggs/cagg_create.c b/tsl/src/continuous_aggs/cagg_create.c index eb5202b3d..ba0584cc0 100644 --- a/tsl/src/continuous_aggs/cagg_create.c +++ b/tsl/src/continuous_aggs/cagg_create.c @@ -1307,7 +1307,7 @@ finalizequery_get_select_query(FinalizeQueryInfo *inp, List *matcollist, static void cagg_create(ViewStmt *stmt, Query *panquery, CAggTimebucketInfo *origquery_ht) { - ObjectAddress userview_address, mataddress, internal_viewaddress; + ObjectAddress mataddress; char relnamebuf[NAMEDATALEN]; MatTableColumnInfo mattblinfo; FinalizeQueryInfo finalqinfo; @@ -1345,7 +1345,7 @@ cagg_create(ViewStmt *stmt, Query *panquery, CAggTimebucketInfo *origquery_ht) */ final_selquery = finalizequery_get_select_query(&finalqinfo, mattblinfo.matcollist, &mataddress); - userview_address = create_view_for_query(final_selquery, stmt->view); + create_view_for_query(final_selquery, stmt->view); /* Step 3: create the internal view with select partialize(..) */ @@ -1354,7 +1354,7 @@ cagg_create(ViewStmt *stmt, Query *panquery, CAggTimebucketInfo *origquery_ht) PRINT_MATINTERNAL_NAME(relnamebuf, "ts_internal_%sview", stmt->view->relname); part_rel = makeRangeVar(pstrdup(INTERNAL_SCHEMA_NAME), pstrdup(relnamebuf), -1); - internal_viewaddress = create_view_for_query(partial_selquery, part_rel); + create_view_for_query(partial_selquery, part_rel); Assert(mat_rel != NULL); /* Step 4 add catalog table entry for the objects we just created */ @@ -1366,9 +1366,6 @@ cagg_create(ViewStmt *stmt, Query *panquery, CAggTimebucketInfo *origquery_ht) part_rel->schemaname, part_rel->relname, origquery_ht->bucket_width); - /* record dependency: user view depends on materialization table and internal view */ - recordDependencyOn(&mataddress, &userview_address, DEPENDENCY_INTERNAL); - recordDependencyOn(&internal_viewaddress, &userview_address, DEPENDENCY_INTERNAL); return; } diff --git a/tsl/src/continuous_aggs/cagg_create.h b/tsl/src/continuous_aggs/cagg_create.h index cd8c890e7..fbee3b482 100644 --- a/tsl/src/continuous_aggs/cagg_create.h +++ b/tsl/src/continuous_aggs/cagg_create.h @@ -3,8 +3,10 @@ * Please see the included NOTICE for copyright information and * LICENSE-TIMESCALE for a copy of the license. */ -#ifndef CONT_CAGG_H -#define CONT_CAGG_H +#ifndef TIMESCALEDB_TSL_CONTINUOUS_AGGS_CAGG_CREATE_H +#define TIMESCALEDB_TSL_CONTINUOUS_AGGS_CAGG_CREATE_H +#include +#include bool tsl_process_continuous_agg_viewstmt(ViewStmt *stmt, const char *query_string, void *pstmt); -#endif +#endif /* TIMESCALEDB_TSL_CONTINUOUS_AGGS_CAGG_CREATE_H */ diff --git a/tsl/test/expected/contaggviews.out b/tsl/test/expected/contaggviews.out index a951a7abb..ad9a3a9e4 100644 --- a/tsl/test/expected/contaggviews.out +++ b/tsl/test/expected/contaggviews.out @@ -105,7 +105,8 @@ order by 1; -- drop on table conditions should cascade to materialized mat_v1 -- Oid used in timescaledb_catalog table from catalog table -- TODO drop table conditions cascade; -NOTICE: drop cascades to 3 other objects +NOTICE: drop cascades to view _timescaledb_internal.ts_internal_mat_m1view +NOTICE: drop cascades to 2 other objects CREATE TABLE conditions ( timec TIMESTAMPTZ NOT NULL, location TEXT NOT NULL, @@ -263,7 +264,8 @@ order by sum(temperature)+sum(humidity); --group by with more than 1 group column -- having clause with a mix of columns from select list + others drop table conditions cascade; -NOTICE: drop cascades to 3 other objects +NOTICE: drop cascades to view _timescaledb_internal.ts_internal_mat_m1view +NOTICE: drop cascades to 2 other objects CREATE TABLE conditions ( timec TIMESTAMPTZ NOT NULL, location TEXT NOT NULL, @@ -345,17 +347,11 @@ order by time_bucket('1week', timec), min(location); --TEST6 -- catalog entries and select from internal view --check the entry in the catalog tables -- ---TODO wrong results select partial_view_name from _timescaledb_catalog.continuous_agg where user_view_name like 'mat_m1'; partial_view_name ------------------------ ts_internal_mat_m1view - ts_internal_mat_m1view - ts_internal_mat_m1view - ts_internal_mat_m1view - ts_internal_mat_m1view - ts_internal_mat_m1view -(6 rows) +(1 row) \c :TEST_DBNAME :ROLE_SUPERUSER insert into _timescaledb_internal.ts_internal_mat_m1tab @@ -421,3 +417,145 @@ psql:include/cont_agg_equal.sql:13: NOTICE: adding not-null constraint to colum Number of rows different between view and original (expect 0) | 0 (1 row) +--DROP tests +\set ON_ERROR_STOP 0 +SELECT h.schema_name AS "MAT_SCHEMA_NAME", + h.table_name AS "MAT_TABLE_NAME", + partial_view_name as "PART_VIEW_NAME", + partial_view_schema as "PART_VIEW_SCHEMA" +FROM _timescaledb_catalog.continuous_agg ca +INNER JOIN _timescaledb_catalog.hypertable h ON(h.id = ca.mat_hypertable_id) +WHERE user_view_name = 'mat_test' +\gset +DROP TABLE :"MAT_SCHEMA_NAME".:"MAT_TABLE_NAME"; +ERROR: cannot drop table _timescaledb_internal.ts_internal_mat_testtab because other objects depend on it +DROP VIEW :"PART_VIEW_SCHEMA".:"PART_VIEW_NAME"; +ERROR: cannot drop the partial view because it is required by a continuous aggregate +DROP VIEW mat_test; +ERROR: dropping a continous aggregate requires using CASCADE +\set ON_ERROR_STOP 1 +--catalog entry still there; +SELECT count(*) +FROM _timescaledb_catalog.continuous_agg ca +WHERE user_view_name = 'mat_test'; + count +------- + 1 +(1 row) + +--mat table, user_view, and partial view all there +select count(*) from pg_class where relname = :'PART_VIEW_NAME'; + count +------- + 1 +(1 row) + +select count(*) from pg_class where relname = :'MAT_TABLE_NAME'; + count +------- + 1 +(1 row) + +select count(*) from pg_class where relname = 'mat_test'; + count +------- + 1 +(1 row) + +DROP VIEW mat_test CASCADE; +NOTICE: drop cascades to 2 other objects +--catalog entry should be gone +SELECT count(*) +FROM _timescaledb_catalog.continuous_agg ca +WHERE user_view_name = 'mat_test'; + count +------- + 0 +(1 row) + +--mat table, user_view, and partial view all gone +select count(*) from pg_class where relname = :'PART_VIEW_NAME'; + count +------- + 0 +(1 row) + +select count(*) from pg_class where relname = :'MAT_TABLE_NAME'; + count +------- + 0 +(1 row) + +select count(*) from pg_class where relname = 'mat_test'; + count +------- + 0 +(1 row) + +--test dropping raw table +DROP TABLE conditions; +CREATE TABLE conditions ( + timec TIMESTAMPTZ NOT NULL, + location TEXT NOT NULL, + temperature DOUBLE PRECISION NULL, + humidity DOUBLE PRECISION NULL, + lowp double precision NULL, + highp double precision null, + allnull double precision null + ); +select table_name from create_hypertable( 'conditions', 'timec'); + table_name +------------ + conditions +(1 row) + +--no data in hyper table on purpose so that CASCADE is not required because of chunks +create or replace view mat_drop_test( timec, minl, sumt , sumh) +WITH ( timescaledb.continuous_agg = 'start') +as +select time_bucket('1day', timec), min(location), sum(temperature),sum(humidity) +from conditions +group by time_bucket('1day', timec); +NOTICE: adding not-null constraint to column "time_partition_col" +\set ON_ERROR_STOP 0 +DROP TABLE conditions; +ERROR: cannot drop table conditions because other objects depend on it +\set ON_ERROR_STOP 1 +SELECT h.schema_name AS "MAT_SCHEMA_NAME", + h.table_name AS "MAT_TABLE_NAME", + partial_view_name as "PART_VIEW_NAME", + partial_view_schema as "PART_VIEW_SCHEMA" +FROM _timescaledb_catalog.continuous_agg ca +INNER JOIN _timescaledb_catalog.hypertable h ON(h.id = ca.mat_hypertable_id) +WHERE user_view_name = 'mat_drop_test' +\gset +DROP TABLE conditions CASCADE; +NOTICE: drop cascades to view _timescaledb_internal.ts_internal_mat_drop_testview +--catalog entry should be gone +SELECT count(*) +FROM _timescaledb_catalog.continuous_agg ca +WHERE user_view_name = 'mat_drop_test'; + count +------- + 0 +(1 row) + +--mat table, user_view, and partial view all gone +select count(*) from pg_class where relname = :'PART_VIEW_NAME'; + count +------- + 0 +(1 row) + +select count(*) from pg_class where relname = :'MAT_TABLE_NAME'; + count +------- + 0 +(1 row) + +select count(*) from pg_class where relname = 'mat_drop_test'; + count +------- + 0 +(1 row) + diff --git a/tsl/test/sql/contaggviews.sql b/tsl/test/sql/contaggviews.sql index f555cf6a5..f224b5153 100644 --- a/tsl/test/sql/contaggviews.sql +++ b/tsl/test/sql/contaggviews.sql @@ -272,7 +272,6 @@ order by time_bucket('1week', timec), min(location); --TEST6 -- catalog entries and select from internal view --check the entry in the catalog tables -- ---TODO wrong results select partial_view_name from _timescaledb_catalog.continuous_agg where user_view_name like 'mat_m1'; \c :TEST_DBNAME :ROLE_SUPERUSER @@ -333,3 +332,90 @@ SELECT \set ECHO errors \ir include/cont_agg_equal.sql \set ECHO all + +--DROP tests +\set ON_ERROR_STOP 0 +SELECT h.schema_name AS "MAT_SCHEMA_NAME", + h.table_name AS "MAT_TABLE_NAME", + partial_view_name as "PART_VIEW_NAME", + partial_view_schema as "PART_VIEW_SCHEMA" +FROM _timescaledb_catalog.continuous_agg ca +INNER JOIN _timescaledb_catalog.hypertable h ON(h.id = ca.mat_hypertable_id) +WHERE user_view_name = 'mat_test' +\gset + +DROP TABLE :"MAT_SCHEMA_NAME".:"MAT_TABLE_NAME"; +DROP VIEW :"PART_VIEW_SCHEMA".:"PART_VIEW_NAME"; +DROP VIEW mat_test; +\set ON_ERROR_STOP 1 + +--catalog entry still there; +SELECT count(*) +FROM _timescaledb_catalog.continuous_agg ca +WHERE user_view_name = 'mat_test'; + +--mat table, user_view, and partial view all there +select count(*) from pg_class where relname = :'PART_VIEW_NAME'; +select count(*) from pg_class where relname = :'MAT_TABLE_NAME'; +select count(*) from pg_class where relname = 'mat_test'; + +DROP VIEW mat_test CASCADE; + +--catalog entry should be gone +SELECT count(*) +FROM _timescaledb_catalog.continuous_agg ca +WHERE user_view_name = 'mat_test'; + +--mat table, user_view, and partial view all gone +select count(*) from pg_class where relname = :'PART_VIEW_NAME'; +select count(*) from pg_class where relname = :'MAT_TABLE_NAME'; +select count(*) from pg_class where relname = 'mat_test'; + + +--test dropping raw table +DROP TABLE conditions; +CREATE TABLE conditions ( + timec TIMESTAMPTZ NOT NULL, + location TEXT NOT NULL, + temperature DOUBLE PRECISION NULL, + humidity DOUBLE PRECISION NULL, + lowp double precision NULL, + highp double precision null, + allnull double precision null + ); + +select table_name from create_hypertable( 'conditions', 'timec'); + +--no data in hyper table on purpose so that CASCADE is not required because of chunks + +create or replace view mat_drop_test( timec, minl, sumt , sumh) +WITH ( timescaledb.continuous_agg = 'start') +as +select time_bucket('1day', timec), min(location), sum(temperature),sum(humidity) +from conditions +group by time_bucket('1day', timec); + +\set ON_ERROR_STOP 0 +DROP TABLE conditions; +\set ON_ERROR_STOP 1 + +SELECT h.schema_name AS "MAT_SCHEMA_NAME", + h.table_name AS "MAT_TABLE_NAME", + partial_view_name as "PART_VIEW_NAME", + partial_view_schema as "PART_VIEW_SCHEMA" +FROM _timescaledb_catalog.continuous_agg ca +INNER JOIN _timescaledb_catalog.hypertable h ON(h.id = ca.mat_hypertable_id) +WHERE user_view_name = 'mat_drop_test' +\gset + +DROP TABLE conditions CASCADE; + +--catalog entry should be gone +SELECT count(*) +FROM _timescaledb_catalog.continuous_agg ca +WHERE user_view_name = 'mat_drop_test'; + +--mat table, user_view, and partial view all gone +select count(*) from pg_class where relname = :'PART_VIEW_NAME'; +select count(*) from pg_class where relname = :'MAT_TABLE_NAME'; +select count(*) from pg_class where relname = 'mat_drop_test'; diff --git a/tsl/test/src/test_ddl_hook.c b/tsl/test/src/test_ddl_hook.c index a0561d549..139725a12 100644 --- a/tsl/test/src/test_ddl_hook.c +++ b/tsl/test/src/test_ddl_hook.c @@ -152,6 +152,11 @@ test_sql_drop(List *dropped_objects) elog(NOTICE, "test_sql_drop: trigger"); break; } + case EVENT_TRIGGER_DROP_VIEW: + { + elog(NOTICE, "test_sql_drop: view"); + break; + } } } }