Skip to content

Commit

Permalink
Add ALTER TABLE SET ACCESS METHOD support
Browse files Browse the repository at this point in the history
This commit allows `ALTER TABLE SET ACCESS METHOD` on a hypertable,
which will set the access method of the hypertable to the given access
method and also set the compression flag if changing to `hyperstore`
access method.

In order to set compression when setting the access method, it is
necessary to allow several ALTER TABLE commands, which was previously
not allowed. To support this, `ProcessUtility` will process each `ALTER
TABLE` command in turn and remove it from the list if it is dealt with.
If there are any remaining commands in the list after that, it will
assume that these are not timescaledb-specific and continue running the
normal command and also recurse the ALTER TABLE SET ACCESS METHOD
command to the chunks.
  • Loading branch information
mkindahl committed Sep 25, 2024
1 parent 80d061d commit 9c904c5
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 36 deletions.
1 change: 1 addition & 0 deletions .unreleased/pr_7295
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implements: #7295 Support ALTER TABLE SET ACCESS METHOD on hypertable
62 changes: 50 additions & 12 deletions src/process_utility.c
Original file line number Diff line number Diff line change
Expand Up @@ -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, "hypercore_tam") == 0))
{
/* For hypertables, we automatically add command to set the
* compression flag if we are setting the access method to be a
* hypercore table access method. */
DefElem *elem = makeDefElemExtended(EXTENSION_NAMESPACE,

Check warning on line 3396 in src/process_utility.c

View check run for this annotation

Codecov / codecov/patch

src/process_utility.c#L3396

Added line #L3396 was not covered by tests
"compress",
(Node *) makeInteger(1),

Check warning on line 3398 in src/process_utility.c

View check run for this annotation

Codecov / codecov/patch

src/process_utility.c#L3398

Added line #L3398 was not covered by tests
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);

Check warning on line 3406 in src/process_utility.c

View check run for this annotation

Codecov / codecov/patch

src/process_utility.c#L3402-L3406

Added lines #L3402 - L3406 were not covered by tests
}
ts_cache_release(hcache);
}
#endif

static DDLResult
process_altertable_start_table(ProcessUtilityArgs *args)
{
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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 <hypertable> 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;
}
Expand All @@ -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
Expand Down
5 changes: 1 addition & 4 deletions test/expected/reloptions.out
Original file line number Diff line number Diff line change
Expand Up @@ -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 <hypertable> 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
Expand Down
77 changes: 77 additions & 0 deletions test/expected/tableam_alter.out
Original file line number Diff line number Diff line change
@@ -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)

2 changes: 1 addition & 1 deletion test/sql/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
5 changes: 1 addition & 4 deletions test/sql/reloptions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
47 changes: 47 additions & 0 deletions test/sql/tableam_alter.sql
Original file line number Diff line number Diff line change
@@ -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';
2 changes: 2 additions & 0 deletions tsl/src/compression/create.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 0 additions & 3 deletions tsl/test/expected/compression_errors-14.out
Original file line number Diff line number Diff line change
Expand Up @@ -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 <hypertable> 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
Expand Down
3 changes: 0 additions & 3 deletions tsl/test/expected/compression_errors-15.out
Original file line number Diff line number Diff line change
Expand Up @@ -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 <hypertable> 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
Expand Down
3 changes: 0 additions & 3 deletions tsl/test/expected/compression_errors-16.out
Original file line number Diff line number Diff line change
Expand Up @@ -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 <hypertable> 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
Expand Down
3 changes: 0 additions & 3 deletions tsl/test/expected/compression_errors-17.out
Original file line number Diff line number Diff line change
Expand Up @@ -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 <hypertable> 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
Expand Down
3 changes: 0 additions & 3 deletions tsl/test/sql/compression_errors.sql.in
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down

0 comments on commit 9c904c5

Please sign in to comment.