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:
Narek Galstyan 2018-09-26 12:15:38 -04:00 committed by Matvey Arye
parent 41d984696c
commit 2408a83150
3 changed files with 370 additions and 4 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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;