From ed379c3dd81071c7f571c2f047878a3024e78966 Mon Sep 17 00:00:00 2001 From: Narek Galstyan Date: Mon, 30 Jul 2018 15:28:43 -0400 Subject: [PATCH] Validate existing indexes before adding a new dimension If the argument column of add_dimension is not in all of hypertable indexes that have UNIQUE, PRIMARY KEY or EXCLUSION constraints, then add_dimension call should fail. This commit enforces the above. --- src/dimension.c | 11 ++++++ test/expected/partitioning.out | 67 ++++++++++++++++++++++++++++++++++ test/sql/partitioning.sql | 43 ++++++++++++++++++++++ 3 files changed, 121 insertions(+) diff --git a/src/dimension.c b/src/dimension.c index d44dffcab..99b92ad51 100644 --- a/src/dimension.c +++ b/src/dimension.c @@ -17,6 +17,7 @@ #include "dimension.h" #include "dimension_slice.h" #include "hypertable.h" +#include "indexing.h" #include "hypertable_cache.h" #include "partitioning.h" #include "scanner.h" @@ -1004,6 +1005,16 @@ dimension_add(PG_FUNCTION_ARGS) */ hypertable_set_num_dimensions(info.ht, info.ht->space->num_dimensions + 1); dimension_add_from_info(&info); + + /* Verify that existing indexes are compatible with a hypertable */ + + /* + * Need to get a fresh copy of hypertable from the database as cache + * does not reflect the changes in the previous 2 lines which add a + * new dimenison + */ + info.ht = hypertable_get_by_id(info.ht->fd.id); + indexing_verify_indexes(info.ht); } cache_release(hcache); diff --git a/test/expected/partitioning.out b/test/expected/partitioning.out index 0230812c0..d8919679b 100644 --- a/test/expected/partitioning.out +++ b/test/expected/partitioning.out @@ -194,3 +194,70 @@ SELECT * FROM test.show_subtables('part_custom_func'); _timescaledb_internal._hyper_5_7_chunk | (2 rows) +-- Test that index creation is handled correctly. +CREATE TABLE hyper_with_index(time timestamptz, temp float, device int); +CREATE UNIQUE INDEX temp_index ON hyper_with_index(temp); +\set ON_ERROR_STOP 0 +SELECT create_hypertable('hyper_with_index', 'time'); +NOTICE: adding not-null constraint to column "time" +ERROR: cannot create a unique index without the column "time" (used in partitioning) +SELECT create_hypertable('hyper_with_index', 'time', 'device', 2); +NOTICE: adding not-null constraint to column "time" +ERROR: cannot create a unique index without the column "time" (used in partitioning) +SELECT create_hypertable('hyper_with_index', 'time', 'temp', 2); +NOTICE: adding not-null constraint to column "time" +ERROR: cannot create a unique index without the column "time" (used in partitioning) +\set ON_ERROR_STOP 1 +DROP INDEX temp_index; +CREATE UNIQUE INDEX time_index ON hyper_with_index(time); +\set ON_ERROR_STOP 0 +-- should error because device not in index +SELECT create_hypertable('hyper_with_index', 'time', 'device', 4); +NOTICE: adding not-null constraint to column "time" +ERROR: cannot create a unique index without the column "device" (used in partitioning) +\set ON_ERROR_STOP 1 +SELECT create_hypertable('hyper_with_index', 'time'); +NOTICE: adding not-null constraint to column "time" + create_hypertable +------------------- + +(1 row) + +-- make sure user created index is used. +-- not using \d or \d+ because output syntax differs +-- between postgres 9 and postgres 10. +SELECT indexname FROM pg_indexes WHERE tablename = 'hyper_with_index'; + indexname +------------ + time_index +(1 row) + +\set ON_ERROR_STOP 0 +SELECT add_dimension('hyper_with_index', 'device', 4); +ERROR: cannot create a unique index without the column "device" (used in partitioning) +\set ON_ERROR_STOP 1 +DROP INDEX time_index; +CREATE UNIQUE INDEX time_space_index ON hyper_with_index(time, device); +SELECT add_dimension('hyper_with_index', 'device', 4); + add_dimension +--------------- + +(1 row) + +CREATE TABLE hyper_with_primary(time TIMESTAMPTZ PRIMARY KEY, temp float, device int); +\set ON_ERROR_STOP 0 +SELECT create_hypertable('hyper_with_primary', 'time', 'device', 4); +ERROR: cannot create a unique index without the column "device" (used in partitioning) +\set ON_ERROR_STOP 1 +SELECT create_hypertable('hyper_with_primary', 'time'); + create_hypertable +------------------- + +(1 row) + +\set ON_ERROR_STOP 0 +SELECT add_dimension('hyper_with_primary', 'device', 4); +ERROR: cannot create a unique index without the column "device" (used in partitioning) +\set ON_ERROR_STOP 1 +-- NON-unique indexes can still be created +CREATE INDEX temp_index ON hyper_with_index(temp); diff --git a/test/sql/partitioning.sql b/test/sql/partitioning.sql index 0ecb93d9f..16f1c7645 100644 --- a/test/sql/partitioning.sql +++ b/test/sql/partitioning.sql @@ -83,3 +83,46 @@ INSERT INTO part_custom_func VALUES ('2017-03-22T09:18:23', 23.4, 'dev1'), ('2017-03-22T09:18:23', 23.4, 'dev7'); SELECT * FROM test.show_subtables('part_custom_func'); +-- Test that index creation is handled correctly. +CREATE TABLE hyper_with_index(time timestamptz, temp float, device int); +CREATE UNIQUE INDEX temp_index ON hyper_with_index(temp); + +\set ON_ERROR_STOP 0 +SELECT create_hypertable('hyper_with_index', 'time'); +SELECT create_hypertable('hyper_with_index', 'time', 'device', 2); +SELECT create_hypertable('hyper_with_index', 'time', 'temp', 2); +\set ON_ERROR_STOP 1 + +DROP INDEX temp_index; +CREATE UNIQUE INDEX time_index ON hyper_with_index(time); + +\set ON_ERROR_STOP 0 +-- should error because device not in index +SELECT create_hypertable('hyper_with_index', 'time', 'device', 4); +\set ON_ERROR_STOP 1 +SELECT create_hypertable('hyper_with_index', 'time'); +-- make sure user created index is used. +-- not using \d or \d+ because output syntax differs +-- between postgres 9 and postgres 10. +SELECT indexname FROM pg_indexes WHERE tablename = 'hyper_with_index'; +\set ON_ERROR_STOP 0 +SELECT add_dimension('hyper_with_index', 'device', 4); +\set ON_ERROR_STOP 1 + +DROP INDEX time_index; +CREATE UNIQUE INDEX time_space_index ON hyper_with_index(time, device); +SELECT add_dimension('hyper_with_index', 'device', 4); + + +CREATE TABLE hyper_with_primary(time TIMESTAMPTZ PRIMARY KEY, temp float, device int); +\set ON_ERROR_STOP 0 +SELECT create_hypertable('hyper_with_primary', 'time', 'device', 4); +\set ON_ERROR_STOP 1 + +SELECT create_hypertable('hyper_with_primary', 'time'); +\set ON_ERROR_STOP 0 +SELECT add_dimension('hyper_with_primary', 'device', 4); +\set ON_ERROR_STOP 1 + +-- NON-unique indexes can still be created +CREATE INDEX temp_index ON hyper_with_index(temp);