mirror of
https://github.com/timescale/timescaledb.git
synced 2025-04-20 13:53:19 +08:00
Add support for ALTER TABLE SET TABLESPACE on hypertables
Previously setting tablespace was blocked for hypertables. This commit enables them and adds proper testing. Setting tablespaces of indexes was handled in a previous commit here: 097db3d58928ab4784c679b8308ab0ebf3bf1fe0
This commit is contained in:
parent
41d984696c
commit
2408a83150
@ -26,6 +26,7 @@
|
||||
#include <utils/guc.h>
|
||||
#include <utils/snapmgr.h>
|
||||
#include <parser/parse_utilcmd.h>
|
||||
#include <commands/tablespace.h>
|
||||
|
||||
#include <miscadmin.h>
|
||||
|
||||
@ -827,8 +828,6 @@ process_rename(Node *parsetree)
|
||||
if (!OidIsValid(relid))
|
||||
return;
|
||||
|
||||
/* TODO: forbid all rename op on chunk table */
|
||||
|
||||
hcache = hypertable_cache_pin();
|
||||
|
||||
switch (stmt->renameType)
|
||||
@ -1428,6 +1427,36 @@ process_altertable_chunk(Hypertable *ht, Oid chunk_relid, void *arg)
|
||||
AlterTableInternal(chunk_relid, list_make1(cmd), false);
|
||||
}
|
||||
|
||||
static void
|
||||
process_altertable_set_tablespace_end(Hypertable *ht, AlterTableCmd *cmd)
|
||||
{
|
||||
Oid tspc_oid = get_rel_tablespace(ht->main_table_relid);
|
||||
NameData tspc_name;
|
||||
Tablespaces *tspcs;
|
||||
|
||||
Assert(OidIsValid(tspc_oid));
|
||||
namestrcpy(&tspc_name, cmd->name);
|
||||
|
||||
tspcs = tablespace_scan(ht->fd.id);
|
||||
|
||||
if (tspcs->num_tablespaces > 1)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("cannot set new tablespace when multiple tablespaces are attached to hypertable \"%s\"",
|
||||
get_rel_name(ht->main_table_relid)),
|
||||
errhint("Detach tablespaces before altering the hypertable.")));
|
||||
|
||||
|
||||
if (tspcs->num_tablespaces == 1)
|
||||
{
|
||||
Assert(hypertable_has_tablespace(ht, tspcs->tablespaces[0].tablespace_oid));
|
||||
tablespace_delete(ht->fd.id, NameStr(tspcs->tablespaces[0].fd.tablespace_name));
|
||||
}
|
||||
|
||||
tablespace_attach_internal(&tspc_name, ht->main_table_relid, true);
|
||||
foreach_chunk(ht, process_altertable_chunk, cmd);
|
||||
}
|
||||
|
||||
static void
|
||||
process_altertable_end_index(Node *parsetree, CollectedCommand *cmd)
|
||||
{
|
||||
@ -1690,6 +1719,9 @@ process_altertable_end_subcmd(Hypertable *ht, Node *parsetree, ObjectAddress *ob
|
||||
case AT_DropCluster:
|
||||
foreach_chunk(ht, process_altertable_chunk, cmd);
|
||||
break;
|
||||
case AT_SetTableSpace:
|
||||
process_altertable_set_tablespace_end(ht, cmd);
|
||||
break;
|
||||
case AT_AddInherit:
|
||||
case AT_DropInherit:
|
||||
ereport(ERROR,
|
||||
@ -1743,8 +1775,6 @@ process_altertable_end_table(Node *parsetree, CollectedCommand *cmd)
|
||||
|
||||
hcache = hypertable_cache_pin();
|
||||
|
||||
/* TODO: forbid all alter_table on chunk table */
|
||||
|
||||
ht = hypertable_cache_get_entry(hcache, relid);
|
||||
|
||||
if (NULL != ht)
|
||||
|
@ -194,3 +194,220 @@ SELECT * FROM _timescaledb_catalog.chunk WHERE id = 2;
|
||||
ALTER TABLE public.new_chunk_name RENAME COLUMN time TO newtime;
|
||||
ERROR: cannot rename column "time" of hypertable chunk "new_chunk_name"
|
||||
\set ON_ERROR_STOP 1
|
||||
-- Test that we can set tablespace of a hypertable
|
||||
\c single :ROLE_SUPERUSER
|
||||
SET client_min_messages = ERROR;
|
||||
DROP TABLESPACE IF EXISTS tablespace1;
|
||||
DROP TABLESPACE IF EXISTS tablespace2;
|
||||
SET client_min_messages = NOTICE;
|
||||
--test hypertable with tables space
|
||||
CREATE TABLESPACE tablespace1 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE1_PATH;
|
||||
CREATE TABLESPACE tablespace2 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE2_PATH;
|
||||
\c single :ROLE_DEFAULT_PERM_USER
|
||||
-- Test that we cannot directly change chunk tablespace
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE public.new_chunk_name SET TABLESPACE tablespace1;
|
||||
ERROR: operation not supported on chunk tables
|
||||
\set ON_ERROR_STOP 1
|
||||
-- drop all tables to make checking the tests below easier
|
||||
DROP TABLE alter_before;
|
||||
DROP TABLE alter_after;
|
||||
-- should return 0 rows
|
||||
SELECT tablename, tablespace FROM pg_tables
|
||||
WHERE tablename = 'hyper_in_space' OR tablename LIKE '\_hyper\__\__\_chunk' ORDER BY tablename;
|
||||
tablename | tablespace
|
||||
-----------+------------
|
||||
(0 rows)
|
||||
|
||||
CREATE TABLE hyper_in_space(time bigint, temp float, device int);
|
||||
SELECT create_hypertable('hyper_in_space', 'time', 'device', 4, chunk_time_interval=>1);
|
||||
NOTICE: adding not-null constraint to column "time"
|
||||
create_hypertable
|
||||
-------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (1, 20, 1);
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (3, 21, 2);
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (5, 23, 1);
|
||||
SELECT tablename FROM pg_tables WHERE tablespace = 'tablespace1' ORDER BY tablename;
|
||||
tablename
|
||||
-----------
|
||||
(0 rows)
|
||||
|
||||
SET default_tablespace = tablespace1;
|
||||
-- should be inserted in tablespace1 which is now default
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (11, 24, 3);
|
||||
SELECT tablename, tablespace FROM pg_tables
|
||||
WHERE tablename = 'hyper_in_space' OR tablename LIKE '\_hyper\__\__\_chunk' ORDER BY tablename;
|
||||
tablename | tablespace
|
||||
------------------+-------------
|
||||
_hyper_3_5_chunk |
|
||||
_hyper_3_6_chunk |
|
||||
_hyper_3_7_chunk |
|
||||
_hyper_3_8_chunk | tablespace1
|
||||
hyper_in_space |
|
||||
(5 rows)
|
||||
|
||||
SET default_tablespace TO DEFAULT;
|
||||
ALTER TABLE hyper_in_space SET TABLESPACE tablespace1;
|
||||
SELECT tablename FROM pg_tables WHERE tablespace = 'tablespace1' ORDER BY tablename;
|
||||
tablename
|
||||
------------------
|
||||
_hyper_3_5_chunk
|
||||
_hyper_3_6_chunk
|
||||
_hyper_3_7_chunk
|
||||
_hyper_3_8_chunk
|
||||
hyper_in_space
|
||||
(5 rows)
|
||||
|
||||
-- should be inserted in an existing chunk in the new tablespace,
|
||||
-- no new chunks
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (5, 27, 1);
|
||||
-- the new chunk should be create in the new tablespace
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (8, 24, 2);
|
||||
SELECT tablename, tablespace FROM pg_tables
|
||||
WHERE tablename = 'hyper_in_space' OR tablename LIKE '\_hyper\__\__\_chunk' ORDER BY tablename;
|
||||
tablename | tablespace
|
||||
------------------+-------------
|
||||
_hyper_3_5_chunk | tablespace1
|
||||
_hyper_3_6_chunk | tablespace1
|
||||
_hyper_3_7_chunk | tablespace1
|
||||
_hyper_3_8_chunk | tablespace1
|
||||
_hyper_3_9_chunk | tablespace1
|
||||
hyper_in_space | tablespace1
|
||||
(6 rows)
|
||||
|
||||
-- should not fail (unlike attach_tablespace)
|
||||
ALTER TABLE hyper_in_space SET TABLESPACE tablespace1;
|
||||
\set ON_ERROR_STOP 0
|
||||
-- not an empty tablespace
|
||||
DROP TABLESPACE tablespace1;
|
||||
ERROR: tablespace "tablespace1" is still attached to 1 hypertables
|
||||
\set ON_ERROR_STOP 1
|
||||
SELECT drop_chunks(20, 'hyper_in_space');
|
||||
drop_chunks
|
||||
-------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT tablename, tablespace FROM pg_tables WHERE tablespace = 'tablespace1' ORDER BY tablename;
|
||||
tablename | tablespace
|
||||
----------------+-------------
|
||||
hyper_in_space | tablespace1
|
||||
(1 row)
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
-- should not be able to drop tablespace if a hypertable depends on it
|
||||
-- even when there are no chunks
|
||||
DROP TABLESPACE tablespace1;
|
||||
ERROR: tablespace "tablespace1" is still attached to 1 hypertables
|
||||
\set ON_ERROR_STOP 1
|
||||
DROP TABLE hyper_in_space;
|
||||
CREATE TABLE hyper_in_space(time bigint, temp float, device int) TABLESPACE tablespace1;
|
||||
SELECT create_hypertable('hyper_in_space', 'time', 'device', 4, chunk_time_interval=>1);
|
||||
NOTICE: adding not-null constraint to column "time"
|
||||
create_hypertable
|
||||
-------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (1, 20, 1);
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (3, 21, 2);
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (5, 23, 1);
|
||||
SELECT tablename, tablespace FROM pg_tables
|
||||
WHERE tablename = 'hyper_in_space' OR tablename ~ '_hyper_\d+_\d+_chunk' ORDER BY tablename;
|
||||
tablename | tablespace
|
||||
-------------------+-------------
|
||||
_hyper_4_10_chunk | tablespace1
|
||||
_hyper_4_11_chunk | tablespace1
|
||||
_hyper_4_12_chunk | tablespace1
|
||||
hyper_in_space | tablespace1
|
||||
(4 rows)
|
||||
|
||||
SELECT attach_tablespace('tablespace2', 'hyper_in_space');
|
||||
attach_tablespace
|
||||
-------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
-- should fail as >1 tablespaces are attached
|
||||
ALTER TABLE hyper_in_space SET TABLESPACE tablespace1;
|
||||
ERROR: cannot set new tablespace when multiple tablespaces are attached to hypertable "hyper_in_space"
|
||||
\set ON_ERROR_STOP 1
|
||||
SELECT detach_tablespace('tablespace2', 'hyper_in_space');
|
||||
detach_tablespace
|
||||
-------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
id | hypertable_id | tablespace_name
|
||||
----+---------------+-----------------
|
||||
3 | 4 | tablespace1
|
||||
(1 row)
|
||||
|
||||
-- make sure when using ALTER TABLE, table spaces are not accumulated
|
||||
-- as in case of attach_tablespace
|
||||
-- should have one result
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
id | hypertable_id | tablespace_name
|
||||
----+---------------+-----------------
|
||||
3 | 4 | tablespace1
|
||||
(1 row)
|
||||
|
||||
ALTER TABLE hyper_in_space SET TABLESPACE tablespace2;
|
||||
-- should have one result
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
id | hypertable_id | tablespace_name
|
||||
----+---------------+-----------------
|
||||
5 | 4 | tablespace2
|
||||
(1 row)
|
||||
|
||||
ALTER TABLE hyper_in_space SET TABLESPACE tablespace1;
|
||||
-- should have one result, (same as the first in the block)
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
id | hypertable_id | tablespace_name
|
||||
----+---------------+-----------------
|
||||
6 | 4 | tablespace1
|
||||
(1 row)
|
||||
|
||||
SELECT tablename, tablespace FROM pg_tables
|
||||
WHERE tablename = 'hyper_in_space' OR tablename ~ '_hyper_\d+_\d+_chunk' ORDER BY tablename;
|
||||
tablename | tablespace
|
||||
-------------------+-------------
|
||||
_hyper_4_10_chunk | tablespace1
|
||||
_hyper_4_11_chunk | tablespace1
|
||||
_hyper_4_12_chunk | tablespace1
|
||||
hyper_in_space | tablespace1
|
||||
(4 rows)
|
||||
|
||||
-- attach tb2 <-> ALTER SET tb1 <-> detach tb1 should work
|
||||
SELECT detach_tablespace('tablespace1', 'hyper_in_space');
|
||||
detach_tablespace
|
||||
-------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (5, 23, 1);
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (7, 23, 1);
|
||||
SELECT tablename, tablespace FROM pg_tables
|
||||
WHERE tablename = 'hyper_in_space' OR tablename ~ '_hyper_\d+_\d+_chunk' ORDER BY tablename;
|
||||
tablename | tablespace
|
||||
-------------------+-------------
|
||||
_hyper_4_10_chunk | tablespace1
|
||||
_hyper_4_11_chunk | tablespace1
|
||||
_hyper_4_12_chunk | tablespace1
|
||||
_hyper_4_13_chunk |
|
||||
hyper_in_space | tablespace1
|
||||
(5 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
id | hypertable_id | tablespace_name
|
||||
----+---------------+-----------------
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE hyper_in_space;
|
||||
DROP TABLESPACE tablespace1;
|
||||
DROP TABLESPACE tablespace2;
|
||||
|
@ -89,3 +89,122 @@ SELECT * FROM _timescaledb_catalog.chunk WHERE id = 2;
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE public.new_chunk_name RENAME COLUMN time TO newtime;
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
-- Test that we can set tablespace of a hypertable
|
||||
\c single :ROLE_SUPERUSER
|
||||
SET client_min_messages = ERROR;
|
||||
DROP TABLESPACE IF EXISTS tablespace1;
|
||||
DROP TABLESPACE IF EXISTS tablespace2;
|
||||
SET client_min_messages = NOTICE;
|
||||
--test hypertable with tables space
|
||||
CREATE TABLESPACE tablespace1 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE1_PATH;
|
||||
CREATE TABLESPACE tablespace2 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE2_PATH;
|
||||
\c single :ROLE_DEFAULT_PERM_USER
|
||||
|
||||
-- Test that we cannot directly change chunk tablespace
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE public.new_chunk_name SET TABLESPACE tablespace1;
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
-- drop all tables to make checking the tests below easier
|
||||
DROP TABLE alter_before;
|
||||
DROP TABLE alter_after;
|
||||
-- should return 0 rows
|
||||
SELECT tablename, tablespace FROM pg_tables
|
||||
WHERE tablename = 'hyper_in_space' OR tablename LIKE '\_hyper\__\__\_chunk' ORDER BY tablename;
|
||||
|
||||
CREATE TABLE hyper_in_space(time bigint, temp float, device int);
|
||||
SELECT create_hypertable('hyper_in_space', 'time', 'device', 4, chunk_time_interval=>1);
|
||||
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (1, 20, 1);
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (3, 21, 2);
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (5, 23, 1);
|
||||
|
||||
SELECT tablename FROM pg_tables WHERE tablespace = 'tablespace1' ORDER BY tablename;
|
||||
|
||||
SET default_tablespace = tablespace1;
|
||||
|
||||
-- should be inserted in tablespace1 which is now default
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (11, 24, 3);
|
||||
SELECT tablename, tablespace FROM pg_tables
|
||||
WHERE tablename = 'hyper_in_space' OR tablename LIKE '\_hyper\__\__\_chunk' ORDER BY tablename;
|
||||
|
||||
SET default_tablespace TO DEFAULT;
|
||||
|
||||
ALTER TABLE hyper_in_space SET TABLESPACE tablespace1;
|
||||
SELECT tablename FROM pg_tables WHERE tablespace = 'tablespace1' ORDER BY tablename;
|
||||
|
||||
-- should be inserted in an existing chunk in the new tablespace,
|
||||
-- no new chunks
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (5, 27, 1);
|
||||
|
||||
-- the new chunk should be create in the new tablespace
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (8, 24, 2);
|
||||
SELECT tablename, tablespace FROM pg_tables
|
||||
WHERE tablename = 'hyper_in_space' OR tablename LIKE '\_hyper\__\__\_chunk' ORDER BY tablename;
|
||||
|
||||
-- should not fail (unlike attach_tablespace)
|
||||
ALTER TABLE hyper_in_space SET TABLESPACE tablespace1;
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
-- not an empty tablespace
|
||||
DROP TABLESPACE tablespace1;
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
SELECT drop_chunks(20, 'hyper_in_space');
|
||||
SELECT tablename, tablespace FROM pg_tables WHERE tablespace = 'tablespace1' ORDER BY tablename;
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
-- should not be able to drop tablespace if a hypertable depends on it
|
||||
-- even when there are no chunks
|
||||
DROP TABLESPACE tablespace1;
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
DROP TABLE hyper_in_space;
|
||||
|
||||
CREATE TABLE hyper_in_space(time bigint, temp float, device int) TABLESPACE tablespace1;
|
||||
SELECT create_hypertable('hyper_in_space', 'time', 'device', 4, chunk_time_interval=>1);
|
||||
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (1, 20, 1);
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (3, 21, 2);
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (5, 23, 1);
|
||||
|
||||
SELECT tablename, tablespace FROM pg_tables
|
||||
WHERE tablename = 'hyper_in_space' OR tablename ~ '_hyper_\d+_\d+_chunk' ORDER BY tablename;
|
||||
|
||||
SELECT attach_tablespace('tablespace2', 'hyper_in_space');
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
-- should fail as >1 tablespaces are attached
|
||||
ALTER TABLE hyper_in_space SET TABLESPACE tablespace1;
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
SELECT detach_tablespace('tablespace2', 'hyper_in_space');
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
|
||||
-- make sure when using ALTER TABLE, table spaces are not accumulated
|
||||
-- as in case of attach_tablespace
|
||||
-- should have one result
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
ALTER TABLE hyper_in_space SET TABLESPACE tablespace2;
|
||||
-- should have one result
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
ALTER TABLE hyper_in_space SET TABLESPACE tablespace1;
|
||||
-- should have one result, (same as the first in the block)
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
|
||||
SELECT tablename, tablespace FROM pg_tables
|
||||
WHERE tablename = 'hyper_in_space' OR tablename ~ '_hyper_\d+_\d+_chunk' ORDER BY tablename;
|
||||
-- attach tb2 <-> ALTER SET tb1 <-> detach tb1 should work
|
||||
SELECT detach_tablespace('tablespace1', 'hyper_in_space');
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (5, 23, 1);
|
||||
INSERT INTO hyper_in_space(time, temp, device) VALUES (7, 23, 1);
|
||||
|
||||
SELECT tablename, tablespace FROM pg_tables
|
||||
WHERE tablename = 'hyper_in_space' OR tablename ~ '_hyper_\d+_\d+_chunk' ORDER BY tablename;
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
|
||||
DROP TABLE hyper_in_space;
|
||||
DROP TABLESPACE tablespace1;
|
||||
DROP TABLESPACE tablespace2;
|
||||
|
Loading…
x
Reference in New Issue
Block a user