diff --git a/.unreleased/pr_7295 b/.unreleased/pr_7295 new file mode 100644 index 00000000000..a2dc9541625 --- /dev/null +++ b/.unreleased/pr_7295 @@ -0,0 +1 @@ +Implements: #7295 Support ALTER TABLE SET ACCESS METHOD on hypertable diff --git a/src/process_utility.c b/src/process_utility.c index 7b8b4b23836..296f0d48a01 100644 --- a/src/process_utility.c +++ b/src/process_utility.c @@ -3374,6 +3374,41 @@ process_altertable_chunk_set_tablespace(AlterTableCmd *cmd, Oid relid) } } +/* + * Set the access method for a table. + * + * If called on a hypertable, this will set the compression flag on the + * hypertable in addition to running the set access method code. + */ +#if PG15_GE +static void +process_set_access_method(AlterTableCmd *cmd, ProcessUtilityArgs *args) +{ + AlterTableStmt *stmt = castNode(AlterTableStmt, args->parsetree); + Oid relid = AlterTableLookupRelation(stmt, NoLock); + Cache *hcache; + Hypertable *ht = ts_hypertable_cache_get_cache_and_entry(relid, CACHE_FLAG_MISSING_OK, &hcache); + if (ht && (strcmp(cmd->name, "hyperstore") == 0)) + { + /* For hypertables, we automatically add command to set the + * compression flag if we are setting the access method to be a + * hyperstore. */ + DefElem *elem = makeDefElemExtended(EXTENSION_NAMESPACE, + "compress", + (Node *) makeInteger(1), + DEFELEM_UNSPEC, + -1); + + AlterTableCmd *cmd = makeNode(AlterTableCmd); + cmd->type = T_AlterTableCmd; + cmd->subtype = AT_SetRelOptions; + cmd->def = (Node *) list_make1(elem); + stmt->cmds = lappend(stmt->cmds, cmd); + } + ts_cache_release(hcache); +} +#endif + static DDLResult process_altertable_start_table(ProcessUtilityArgs *args) { @@ -3382,8 +3417,6 @@ process_altertable_start_table(ProcessUtilityArgs *args) Cache *hcache; Hypertable *ht; ListCell *lc; - DDLResult result = DDL_CONTINUE; - int num_cmds; if (!OidIsValid(relid)) return DDL_CONTINUE; @@ -3399,7 +3432,6 @@ process_altertable_start_table(ProcessUtilityArgs *args) relation_not_only(stmt->relation); add_hypertable_to_process_args(args, ht); } - num_cmds = list_length(stmt->cmds); foreach (lc, stmt->cmds) { AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lc); @@ -3477,19 +3509,17 @@ process_altertable_start_table(ProcessUtilityArgs *args) } break; } + case AT_SetRelOptions: { if (ht != NULL) { - if (num_cmds != 1) - { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("ALTER TABLE SET does not support multiple " - "clauses"))); - } EventTriggerAlterTableStart(args->parsetree); - result = process_altertable_set_options(cmd, ht); + /* If we dealt with the option, we remove it from the + * list. We do not set the result variable since there + * could be other options that are not dealt with. */ + if (process_altertable_set_options(cmd, ht) == DDL_DONE) + stmt->cmds = foreach_delete_current(stmt->cmds, lc); } break; } @@ -3501,13 +3531,21 @@ process_altertable_start_table(ProcessUtilityArgs *args) if (NULL == ht) process_altertable_chunk_set_tablespace(cmd, relid); break; +#if PG15_GE + case AT_SetAccessMethod: + process_set_access_method(cmd, args); + break; +#endif default: break; } } ts_cache_release(hcache); - return result; + + /* If there are any commands remaining in the list, we need to deal with + * them. Otherwise, we just skip the rest. */ + return (list_length(stmt->cmds) > 0) ? DDL_CONTINUE : DDL_DONE; } static void diff --git a/test/expected/reloptions.out b/test/expected/reloptions.out index 621182d456d..dbbdb815bfd 100644 --- a/test/expected/reloptions.out +++ b/test/expected/reloptions.out @@ -20,12 +20,9 @@ WHERE relname ~ '^_hyper.*' AND relkind = 'r'; _hyper_1_2_chunk | {fillfactor=75,autovacuum_vacuum_threshold=100} (2 rows) --- Alter reloptions +-- Alter reloptions. We support multiple options for the ALTER TABLE ALTER TABLE reloptions_test SET (fillfactor=80, parallel_workers=8); -\set ON_ERROR_STOP 0 ALTER TABLE reloptions_test SET (fillfactor=80), SET (parallel_workers=8); -ERROR: ALTER TABLE SET does not support multiple clauses -\set ON_ERROR_STOP 1 SELECT relname, reloptions FROM pg_class WHERE relname ~ '^_hyper.*' AND relkind = 'r'; relname | reloptions diff --git a/test/expected/tableam_alter.out b/test/expected/tableam_alter.out new file mode 100644 index 00000000000..488bb6613bc --- /dev/null +++ b/test/expected/tableam_alter.out @@ -0,0 +1,77 @@ +-- This file and its contents are licensed under the Apache License 2.0. +-- Please see the included NOTICE for copyright information and +-- LICENSE-APACHE for a copy of the license. +-- Test support for setting table access method on hypertables using +-- ALTER TABLE. It should propagate to the chunks. +\c :TEST_DBNAME :ROLE_SUPERUSER +CREATE ACCESS METHOD testam TYPE TABLE HANDLER heap_tableam_handler; +SET ROLE :ROLE_DEFAULT_PERM_USER; +CREATE VIEW chunk_info AS +SELECT hypertable_name AS hypertable, + chunk_name AS chunk, + amname + FROM timescaledb_information.chunks ch + JOIN pg_class cl ON (format('%I.%I', ch.chunk_schema, ch.chunk_name)::regclass = cl.oid) + JOIN pg_am am ON (am.oid = cl.relam); +CREATE TABLE test_table (time timestamptz not null, device int, temp float); +SELECT create_hypertable('test_table', by_range('time')); + create_hypertable +------------------- + (1,t) +(1 row) + +INSERT INTO test_table +SELECT ts, 10 * random(), 100 * random() +FROM generate_series('2001-01-01'::timestamp, '2001-02-01', '1d'::interval) as x(ts); +SELECT cl.relname, amname + FROM pg_class cl JOIN pg_am am ON cl.relam = am.oid + WHERE cl.relname = 'test_table'; + relname | amname +------------+-------- + test_table | heap +(1 row) + +SELECT * FROM chunk_info WHERE hypertable = 'test_table'; + hypertable | chunk | amname +------------+------------------+-------- + test_table | _hyper_1_1_chunk | heap + test_table | _hyper_1_2_chunk | heap + test_table | _hyper_1_3_chunk | heap + test_table | _hyper_1_4_chunk | heap + test_table | _hyper_1_5_chunk | heap + test_table | _hyper_1_6_chunk | heap +(6 rows) + +-- Test setting the access method together with other options. This +-- should not generate an error. +ALTER TABLE test_table + SET ACCESS METHOD testam, + SET (autovacuum_vacuum_threshold = 100); +-- Create more chunks. These will use the new access method, but the +-- old chunks will use the old access method. +INSERT INTO test_table +SELECT ts, 10 * random(), 100 * random() +FROM generate_series('2001-02-01'::timestamp, '2001-03-01', '1d'::interval) as x(ts); +SELECT cl.relname, amname + FROM pg_class cl JOIN pg_am am ON cl.relam = am.oid + WHERE cl.relname = 'test_table'; + relname | amname +------------+-------- + test_table | testam +(1 row) + +SELECT * FROM chunk_info WHERE hypertable = 'test_table'; + hypertable | chunk | amname +------------+-------------------+-------- + test_table | _hyper_1_1_chunk | heap + test_table | _hyper_1_2_chunk | heap + test_table | _hyper_1_3_chunk | heap + test_table | _hyper_1_4_chunk | heap + test_table | _hyper_1_5_chunk | heap + test_table | _hyper_1_6_chunk | heap + test_table | _hyper_1_7_chunk | testam + test_table | _hyper_1_8_chunk | testam + test_table | _hyper_1_9_chunk | testam + test_table | _hyper_1_10_chunk | testam +(10 rows) + diff --git a/test/sql/CMakeLists.txt b/test/sql/CMakeLists.txt index e4b8df6adac..5ed75b5307f 100644 --- a/test/sql/CMakeLists.txt +++ b/test/sql/CMakeLists.txt @@ -118,7 +118,7 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) endif(CMAKE_BUILD_TYPE MATCHES Debug) if((${PG_VERSION_MAJOR} GREATER_EQUAL "15")) - list(APPEND TEST_FILES merge.sql) + list(APPEND TEST_FILES merge.sql tableam_alter.sql) list(APPEND TEST_TEMPLATES ts_merge.sql.in) endif() diff --git a/test/sql/reloptions.sql b/test/sql/reloptions.sql index aff63655376..21d4d048eb9 100644 --- a/test/sql/reloptions.sql +++ b/test/sql/reloptions.sql @@ -13,12 +13,9 @@ INSERT INTO reloptions_test VALUES (4, 24.3, 1), (9, 13.3, 2); SELECT relname, reloptions FROM pg_class WHERE relname ~ '^_hyper.*' AND relkind = 'r'; --- Alter reloptions +-- Alter reloptions. We support multiple options for the ALTER TABLE ALTER TABLE reloptions_test SET (fillfactor=80, parallel_workers=8); - -\set ON_ERROR_STOP 0 ALTER TABLE reloptions_test SET (fillfactor=80), SET (parallel_workers=8); -\set ON_ERROR_STOP 1 SELECT relname, reloptions FROM pg_class WHERE relname ~ '^_hyper.*' AND relkind = 'r'; diff --git a/test/sql/tableam_alter.sql b/test/sql/tableam_alter.sql new file mode 100644 index 00000000000..43c53fff570 --- /dev/null +++ b/test/sql/tableam_alter.sql @@ -0,0 +1,47 @@ +-- This file and its contents are licensed under the Apache License 2.0. +-- Please see the included NOTICE for copyright information and +-- LICENSE-APACHE for a copy of the license. + +-- Test support for setting table access method on hypertables using +-- ALTER TABLE. It should propagate to the chunks. +\c :TEST_DBNAME :ROLE_SUPERUSER +CREATE ACCESS METHOD testam TYPE TABLE HANDLER heap_tableam_handler; +SET ROLE :ROLE_DEFAULT_PERM_USER; + +CREATE VIEW chunk_info AS +SELECT hypertable_name AS hypertable, + chunk_name AS chunk, + amname + FROM timescaledb_information.chunks ch + JOIN pg_class cl ON (format('%I.%I', ch.chunk_schema, ch.chunk_name)::regclass = cl.oid) + JOIN pg_am am ON (am.oid = cl.relam); + +CREATE TABLE test_table (time timestamptz not null, device int, temp float); + +SELECT create_hypertable('test_table', by_range('time')); + +INSERT INTO test_table +SELECT ts, 10 * random(), 100 * random() +FROM generate_series('2001-01-01'::timestamp, '2001-02-01', '1d'::interval) as x(ts); + +SELECT cl.relname, amname + FROM pg_class cl JOIN pg_am am ON cl.relam = am.oid + WHERE cl.relname = 'test_table'; +SELECT * FROM chunk_info WHERE hypertable = 'test_table'; + +-- Test setting the access method together with other options. This +-- should not generate an error. +ALTER TABLE test_table + SET ACCESS METHOD testam, + SET (autovacuum_vacuum_threshold = 100); + +-- Create more chunks. These will use the new access method, but the +-- old chunks will use the old access method. +INSERT INTO test_table +SELECT ts, 10 * random(), 100 * random() +FROM generate_series('2001-02-01'::timestamp, '2001-03-01', '1d'::interval) as x(ts); + +SELECT cl.relname, amname + FROM pg_class cl JOIN pg_am am ON cl.relam = am.oid + WHERE cl.relname = 'test_table'; +SELECT * FROM chunk_info WHERE hypertable = 'test_table'; diff --git a/tsl/src/compression/create.h b/tsl/src/compression/create.h index 8b3ea7845d8..3854796bd1a 100644 --- a/tsl/src/compression/create.h +++ b/tsl/src/compression/create.h @@ -23,6 +23,8 @@ bool tsl_process_compress_table(AlterTableCmd *cmd, Hypertable *ht, void tsl_process_compress_table_add_column(Hypertable *ht, ColumnDef *orig_def); void tsl_process_compress_table_drop_column(Hypertable *ht, char *name); void tsl_process_compress_table_rename_column(Hypertable *ht, const RenameStmt *stmt); +bool tsl_set_compression_options(Hypertable *ht, bool enable, + WithClauseResult *with_clause_options); Chunk *create_compress_chunk(Hypertable *compress_ht, Chunk *src_chunk, Oid table_id); char *column_segment_min_name(int16 column_index); diff --git a/tsl/test/expected/compression_errors-14.out b/tsl/test/expected/compression_errors-14.out index 2733b3aa874..0a87d7b5ae8 100644 --- a/tsl/test/expected/compression_errors-14.out +++ b/tsl/test/expected/compression_errors-14.out @@ -112,9 +112,6 @@ insert into foo values( 3 , 16 , 20); insert into foo values( 10 , 10 , 20); insert into foo values( 20 , 11 , 20); insert into foo values( 30 , 12 , 20); --- should error out -- -ALTER TABLE foo ALTER b SET NOT NULL, set (timescaledb.compress); -ERROR: ALTER TABLE SET does not support multiple clauses ALTER TABLE foo ALTER b SET NOT NULL; select attname, attnotnull from pg_attribute where attrelid = (select oid from pg_class where relname like 'foo') and attname like 'b'; attname | attnotnull diff --git a/tsl/test/expected/compression_errors-15.out b/tsl/test/expected/compression_errors-15.out index 2733b3aa874..0a87d7b5ae8 100644 --- a/tsl/test/expected/compression_errors-15.out +++ b/tsl/test/expected/compression_errors-15.out @@ -112,9 +112,6 @@ insert into foo values( 3 , 16 , 20); insert into foo values( 10 , 10 , 20); insert into foo values( 20 , 11 , 20); insert into foo values( 30 , 12 , 20); --- should error out -- -ALTER TABLE foo ALTER b SET NOT NULL, set (timescaledb.compress); -ERROR: ALTER TABLE SET does not support multiple clauses ALTER TABLE foo ALTER b SET NOT NULL; select attname, attnotnull from pg_attribute where attrelid = (select oid from pg_class where relname like 'foo') and attname like 'b'; attname | attnotnull diff --git a/tsl/test/expected/compression_errors-16.out b/tsl/test/expected/compression_errors-16.out index 29ec64940dc..6874d4f9128 100644 --- a/tsl/test/expected/compression_errors-16.out +++ b/tsl/test/expected/compression_errors-16.out @@ -112,9 +112,6 @@ insert into foo values( 3 , 16 , 20); insert into foo values( 10 , 10 , 20); insert into foo values( 20 , 11 , 20); insert into foo values( 30 , 12 , 20); --- should error out -- -ALTER TABLE foo ALTER b SET NOT NULL, set (timescaledb.compress); -ERROR: ALTER TABLE SET does not support multiple clauses ALTER TABLE foo ALTER b SET NOT NULL; select attname, attnotnull from pg_attribute where attrelid = (select oid from pg_class where relname like 'foo') and attname like 'b'; attname | attnotnull diff --git a/tsl/test/expected/compression_errors-17.out b/tsl/test/expected/compression_errors-17.out index 29ec64940dc..6874d4f9128 100644 --- a/tsl/test/expected/compression_errors-17.out +++ b/tsl/test/expected/compression_errors-17.out @@ -112,9 +112,6 @@ insert into foo values( 3 , 16 , 20); insert into foo values( 10 , 10 , 20); insert into foo values( 20 , 11 , 20); insert into foo values( 30 , 12 , 20); --- should error out -- -ALTER TABLE foo ALTER b SET NOT NULL, set (timescaledb.compress); -ERROR: ALTER TABLE SET does not support multiple clauses ALTER TABLE foo ALTER b SET NOT NULL; select attname, attnotnull from pg_attribute where attrelid = (select oid from pg_class where relname like 'foo') and attname like 'b'; attname | attnotnull diff --git a/tsl/test/sql/compression_errors.sql.in b/tsl/test/sql/compression_errors.sql.in index 191ec1bbec0..12ab925a161 100644 --- a/tsl/test/sql/compression_errors.sql.in +++ b/tsl/test/sql/compression_errors.sql.in @@ -69,9 +69,6 @@ insert into foo values( 10 , 10 , 20); insert into foo values( 20 , 11 , 20); insert into foo values( 30 , 12 , 20); --- should error out -- -ALTER TABLE foo ALTER b SET NOT NULL, set (timescaledb.compress); - ALTER TABLE foo ALTER b SET NOT NULL; select attname, attnotnull from pg_attribute where attrelid = (select oid from pg_class where relname like 'foo') and attname like 'b';