From d5a6a5b1934b81a966eb54be902b7268734037aa Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Fri, 28 Aug 2020 15:51:25 +0200 Subject: [PATCH] Support tablespaces for continuous aggregates Adding support for tablespaces when creating a continuous aggregate using `CREATE MATERIALIZED VIEW` and when altering a continuous aggregate using `ALTER MATERIALIZED VIEW`. Fixes #2122 --- src/process_utility.c | 15 ++++ tsl/src/continuous_aggs/create.c | 13 ++-- tsl/test/expected/continuous_aggs_ddl.out | 84 +++++++++++++++++++++++ tsl/test/sql/CMakeLists.txt | 1 + tsl/test/sql/continuous_aggs_ddl.sql | 77 +++++++++++++++++++++ 5 files changed, 185 insertions(+), 5 deletions(-) diff --git a/src/process_utility.c b/src/process_utility.c index 306913fbd..c1394f7f0 100644 --- a/src/process_utility.c +++ b/src/process_utility.c @@ -2762,6 +2762,8 @@ process_altertable_start_matview(ProcessUtilityArgs *args) NameData view_schema; ContinuousAgg *cagg; ListCell *lc; + Hypertable *ht; + Cache *hcache; if (!OidIsValid(view_relid)) return DDL_CONTINUE; @@ -2788,6 +2790,19 @@ process_altertable_start_matview(ProcessUtilityArgs *args) errmsg("expected set options to contain a list"))); process_altercontinuousagg_set_with(cagg, view_relid, (List *) cmd->def); break; + + case AT_SetTableSpace: + hcache = ts_hypertable_cache_pin(); + ht = ts_hypertable_cache_get_entry_by_id(hcache, cagg->data.mat_hypertable_id); + Assert(ht); /* Broken continuous aggregate */ + ts_hypertable_permissions_check_by_id(ht->fd.id); + check_alter_table_allowed_on_ht_with_compression(ht, stmt); + relation_not_only(stmt->relation); + process_altertable_set_tablespace_end(ht, cmd); + AlterTableInternal(ht->main_table_relid, list_make1(cmd), false); + ts_cache_release(hcache); + break; + default: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), diff --git a/tsl/src/continuous_aggs/create.c b/tsl/src/continuous_aggs/create.c index 5741a133f..4b85be8a0 100644 --- a/tsl/src/continuous_aggs/create.c +++ b/tsl/src/continuous_aggs/create.c @@ -203,6 +203,7 @@ static int32 mattablecolumninfo_create_materialization_table(MatTableColumnInfo int32 hypertable_id, RangeVar *mat_rel, CAggTimebucketInfo *origquery_tblinfo, bool create_addl_index, + char *tablespacename, ObjectAddress *mataddress); static Query *mattablecolumninfo_get_partial_select_query(MatTableColumnInfo *matcolinfo, Query *userview_query); @@ -460,7 +461,8 @@ static int32 mattablecolumninfo_create_materialization_table(MatTableColumnInfo *matcolinfo, int32 hypertable_id, RangeVar *mat_rel, CAggTimebucketInfo *origquery_tblinfo, - bool create_addl_index, ObjectAddress *mataddress) + bool create_addl_index, char *const tablespacename, + ObjectAddress *mataddress) { Oid uid, saved_uid; int sec_ctx; @@ -484,7 +486,7 @@ mattablecolumninfo_create_materialization_table(MatTableColumnInfo *matcolinfo, create->constraints = NIL; create->options = NULL; create->oncommit = ONCOMMIT_NOOP; - create->tablespacename = NULL; + create->tablespacename = tablespacename; create->if_not_exists = false; /* Create the materialization table. */ @@ -1662,8 +1664,8 @@ fixup_userview_query_tlist(Query *userquery, List *tlist_aliases) * ) */ static void -cagg_create(ViewStmt *stmt, Query *panquery, CAggTimebucketInfo *origquery_ht, - WithClauseResult *with_clause_options) +cagg_create(const CreateTableAsStmt *create_stmt, ViewStmt *stmt, Query *panquery, + CAggTimebucketInfo *origquery_ht, WithClauseResult *with_clause_options) { ObjectAddress mataddress; char relnamebuf[NAMEDATALEN]; @@ -1721,6 +1723,7 @@ cagg_create(ViewStmt *stmt, Query *panquery, CAggTimebucketInfo *origquery_ht, mat_rel, origquery_ht, is_create_mattbl_index, + create_stmt->into->tableSpaceName, &mataddress); /* Step 2: create view with select finalize from materialization * table @@ -1819,7 +1822,7 @@ tsl_process_continuous_agg_viewstmt(Node *node, const char *query_string, void * } timebucket_exprinfo = cagg_validate_query((Query *) stmt->into->viewQuery); - cagg_create(&viewstmt, (Query *) stmt->query, &timebucket_exprinfo, with_clause_options); + cagg_create(stmt, &viewstmt, (Query *) stmt->query, &timebucket_exprinfo, with_clause_options); return DDL_DONE; } diff --git a/tsl/test/expected/continuous_aggs_ddl.out b/tsl/test/expected/continuous_aggs_ddl.out index ad5bfa84a..738a792ed 100644 --- a/tsl/test/expected/continuous_aggs_ddl.out +++ b/tsl/test/expected/continuous_aggs_ddl.out @@ -22,6 +22,8 @@ select table_name from create_hypertable('conditions', 'timec'); -- schema tests \c :TEST_DBNAME :ROLE_SUPERUSER +CREATE TABLESPACE tablespace1 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE1_PATH; +CREATE TABLESPACE tablespace2 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE2_PATH; CREATE SCHEMA rename_schema; GRANT ALL ON SCHEMA rename_schema TO :ROLE_DEFAULT_PERM_USER; \c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER @@ -876,3 +878,85 @@ DETAIL: Hypertable "_materialized_hypertable_11" is a materialized hypertable. HINT: Try the operation on the continuous aggregate instead. \set VERBOSITY terse \set ON_ERROR_STOP 1 +-------------------------------------------------------------------- +-- Check that we can create a materialized table in a tablespace. We +-- create one with tablespace and one without and compare them. +CREATE VIEW cagg_info AS +WITH + caggs AS ( + SELECT format('%s.%s', user_view_schema, user_view_name)::regclass AS user_view, + format('%s.%s', ht.schema_name, ht.table_name)::regclass AS mat_relid + FROM _timescaledb_catalog.hypertable ht, + _timescaledb_catalog.continuous_agg cagg + WHERE ht.id = cagg.mat_hypertable_id + ) +SELECT user_view, + relname AS mat_table, + (SELECT spcname FROM pg_tablespace WHERE oid = reltablespace) AS tablespace + FROM pg_class JOIN caggs ON pg_class.oid = caggs.mat_relid; +CREATE VIEW chunk_info AS +SELECT ht.schema_name, ht.table_name, relname AS chunk_name, + (SELECT spcname FROM pg_tablespace WHERE oid = reltablespace) AS tablespace + FROM pg_class c, + _timescaledb_catalog.hypertable ht, + _timescaledb_catalog.chunk ch + WHERE ch.table_name = c.relname AND ht.id = ch.hypertable_id; +CREATE TABLE whatever(time BIGINT NOT NULL, data INTEGER); +SELECT hypertable_id AS whatever_nid + FROM create_hypertable('whatever', 'time', chunk_time_interval => 10) +\gset +SELECT set_integer_now_func('whatever', 'integer_now_test'); + set_integer_now_func +---------------------- + +(1 row) + +CREATE MATERIALIZED VIEW whatever_view_1 +WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS +SELECT time_bucket('2', time), COUNT(data) + FROM whatever GROUP BY 1; +CREATE MATERIALIZED VIEW whatever_view_2 +WITH (timescaledb.continuous, timescaledb.materialized_only=true) +TABLESPACE tablespace1 AS +SELECT time_bucket('2', time), COUNT(data) + FROM whatever GROUP BY 1; +INSERT INTO whatever SELECT i, i FROM generate_series(0, 29) AS i; +REFRESH MATERIALIZED VIEW whatever_view_1; +LOG: materializing continuous aggregate public.whatever_view_1: nothing to invalidate, new range up to 30 +REFRESH MATERIALIZED VIEW whatever_view_2; +LOG: materializing continuous aggregate public.whatever_view_2: nothing to invalidate, new range up to 30 +SELECT user_view, + mat_table, + cagg_info.tablespace AS mat_tablespace, + chunk_name, + chunk_info.tablespace AS chunk_tablespace + FROM cagg_info, chunk_info + WHERE mat_table::text = table_name + AND user_view::text LIKE 'whatever_view%'; + user_view | mat_table | mat_tablespace | chunk_name | chunk_tablespace +-----------------+-----------------------------+----------------+--------------------+------------------ + whatever_view_1 | _materialized_hypertable_13 | | _hyper_13_24_chunk | + whatever_view_2 | _materialized_hypertable_14 | tablespace1 | _hyper_14_25_chunk | tablespace1 +(2 rows) + +ALTER MATERIALIZED VIEW whatever_view_1 SET TABLESPACE tablespace2; +SELECT user_view, + mat_table, + cagg_info.tablespace AS mat_tablespace, + chunk_name, + chunk_info.tablespace AS chunk_tablespace + FROM cagg_info, chunk_info + WHERE mat_table::text = table_name + AND user_view::text LIKE 'whatever_view%'; + user_view | mat_table | mat_tablespace | chunk_name | chunk_tablespace +-----------------+-----------------------------+----------------+--------------------+------------------ + whatever_view_1 | _materialized_hypertable_13 | tablespace2 | _hyper_13_24_chunk | tablespace2 + whatever_view_2 | _materialized_hypertable_14 | tablespace1 | _hyper_14_25_chunk | tablespace1 +(2 rows) + +DROP MATERIALIZED VIEW whatever_view_1; +NOTICE: drop cascades to table _timescaledb_internal._hyper_13_24_chunk +DROP MATERIALIZED VIEW whatever_view_2; +NOTICE: drop cascades to table _timescaledb_internal._hyper_14_25_chunk +DROP TABLESPACE tablespace1; +DROP TABLESPACE tablespace2; diff --git a/tsl/test/sql/CMakeLists.txt b/tsl/test/sql/CMakeLists.txt index 4cd4d45fc..3733a7002 100644 --- a/tsl/test/sql/CMakeLists.txt +++ b/tsl/test/sql/CMakeLists.txt @@ -114,6 +114,7 @@ set(SOLO_TESTS compress_bgw_reorder_drop_chunks compression_ddl continuous_aggs_bgw + continuous_aggs_ddl continuous_aggs_dump data_fetcher data_node diff --git a/tsl/test/sql/continuous_aggs_ddl.sql b/tsl/test/sql/continuous_aggs_ddl.sql index cf8e3a9d9..a191a6c12 100644 --- a/tsl/test/sql/continuous_aggs_ddl.sql +++ b/tsl/test/sql/continuous_aggs_ddl.sql @@ -24,6 +24,8 @@ select table_name from create_hypertable('conditions', 'timec'); -- schema tests \c :TEST_DBNAME :ROLE_SUPERUSER +CREATE TABLESPACE tablespace1 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE1_PATH; +CREATE TABLESPACE tablespace2 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE2_PATH; CREATE SCHEMA rename_schema; GRANT ALL ON SCHEMA rename_schema TO :ROLE_DEFAULT_PERM_USER; @@ -478,3 +480,78 @@ WHERE hypertable_name = :'drop_chunks_mat_table_name' ORDER BY range_start_integ SELECT drop_chunks(:'drop_chunks_mat_tablen', older_than => 60); \set VERBOSITY terse \set ON_ERROR_STOP 1 + +-------------------------------------------------------------------- +-- Check that we can create a materialized table in a tablespace. We +-- create one with tablespace and one without and compare them. + +CREATE VIEW cagg_info AS +WITH + caggs AS ( + SELECT format('%s.%s', user_view_schema, user_view_name)::regclass AS user_view, + format('%s.%s', ht.schema_name, ht.table_name)::regclass AS mat_relid + FROM _timescaledb_catalog.hypertable ht, + _timescaledb_catalog.continuous_agg cagg + WHERE ht.id = cagg.mat_hypertable_id + ) +SELECT user_view, + relname AS mat_table, + (SELECT spcname FROM pg_tablespace WHERE oid = reltablespace) AS tablespace + FROM pg_class JOIN caggs ON pg_class.oid = caggs.mat_relid; + +CREATE VIEW chunk_info AS +SELECT ht.schema_name, ht.table_name, relname AS chunk_name, + (SELECT spcname FROM pg_tablespace WHERE oid = reltablespace) AS tablespace + FROM pg_class c, + _timescaledb_catalog.hypertable ht, + _timescaledb_catalog.chunk ch + WHERE ch.table_name = c.relname AND ht.id = ch.hypertable_id; + +CREATE TABLE whatever(time BIGINT NOT NULL, data INTEGER); + +SELECT hypertable_id AS whatever_nid + FROM create_hypertable('whatever', 'time', chunk_time_interval => 10) +\gset + +SELECT set_integer_now_func('whatever', 'integer_now_test'); + +CREATE MATERIALIZED VIEW whatever_view_1 +WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS +SELECT time_bucket('2', time), COUNT(data) + FROM whatever GROUP BY 1; + +CREATE MATERIALIZED VIEW whatever_view_2 +WITH (timescaledb.continuous, timescaledb.materialized_only=true) +TABLESPACE tablespace1 AS +SELECT time_bucket('2', time), COUNT(data) + FROM whatever GROUP BY 1; + +INSERT INTO whatever SELECT i, i FROM generate_series(0, 29) AS i; +REFRESH MATERIALIZED VIEW whatever_view_1; +REFRESH MATERIALIZED VIEW whatever_view_2; + +SELECT user_view, + mat_table, + cagg_info.tablespace AS mat_tablespace, + chunk_name, + chunk_info.tablespace AS chunk_tablespace + FROM cagg_info, chunk_info + WHERE mat_table::text = table_name + AND user_view::text LIKE 'whatever_view%'; + +ALTER MATERIALIZED VIEW whatever_view_1 SET TABLESPACE tablespace2; + +SELECT user_view, + mat_table, + cagg_info.tablespace AS mat_tablespace, + chunk_name, + chunk_info.tablespace AS chunk_tablespace + FROM cagg_info, chunk_info + WHERE mat_table::text = table_name + AND user_view::text LIKE 'whatever_view%'; + +DROP MATERIALIZED VIEW whatever_view_1; +DROP MATERIALIZED VIEW whatever_view_2; + +DROP TABLESPACE tablespace1; +DROP TABLESPACE tablespace2;