From 5878a37832e92311947ae02b80a094dba74ecbdd Mon Sep 17 00:00:00 2001 From: Arunprasad Rajkumar Date: Thu, 13 Jun 2024 19:00:45 +0530 Subject: [PATCH] Allow hypertable constraint creation USING INDEX Prior to this commit, we disallow ```sql ALTER TABLE foo ADD CONSTRAINT foo_pkey PRIMARY KEY USING INDEX foo_pkey ``` Signed-off-by: Arunprasad Rajkumar --- .unreleased/fix_7040 | 1 + sql/chunk_constraint.sql | 43 +++++++++++++++---- sql/updates/reverse-dev.sql | 75 +++++++++++++++++++++++++++++++++ src/chunk_constraint.c | 19 +++++---- src/chunk_constraint.h | 2 +- src/chunk_index.c | 7 ++-- src/chunk_index.h | 3 +- src/process_utility.c | 69 +++++++++++++++++++++++++++---- src/ts_catalog/catalog.c | 2 +- test/expected/constraint.out | 80 ++++++++++++++++++------------------ test/sql/constraint.sql | 6 +-- 11 files changed, 233 insertions(+), 74 deletions(-) create mode 100644 .unreleased/fix_7040 diff --git a/.unreleased/fix_7040 b/.unreleased/fix_7040 new file mode 100644 index 00000000000..c02091c5826 --- /dev/null +++ b/.unreleased/fix_7040 @@ -0,0 +1 @@ +Fixes: #7040 Allow hypertable constraint creation USING INDEX diff --git a/sql/chunk_constraint.sql b/sql/chunk_constraint.sql index 9431263ddfd..a1be47511f7 100644 --- a/sql/chunk_constraint.sql +++ b/sql/chunk_constraint.sql @@ -4,7 +4,8 @@ -- create constraint on newly created chunk based on hypertable constraint CREATE OR REPLACE FUNCTION _timescaledb_functions.chunk_constraint_add_table_constraint( - chunk_constraint_row _timescaledb_catalog.chunk_constraint + chunk_constraint_row _timescaledb_catalog.chunk_constraint, + using_index BOOLEAN ) RETURNS VOID LANGUAGE PLPGSQL AS $BODY$ @@ -17,6 +18,8 @@ DECLARE def TEXT; indx_tablespace NAME; tablespace_def TEXT; + chunk_constraint_index_name NAME; + row_record RECORD; BEGIN SELECT * INTO STRICT chunk_row FROM _timescaledb_catalog.chunk c WHERE c.id = chunk_constraint_row.chunk_id; SELECT * INTO STRICT hypertable_row FROM _timescaledb_catalog.hypertable h WHERE h.id = chunk_row.hypertable_id; @@ -30,15 +33,37 @@ BEGIN conrelid = format('%I.%I', hypertable_row.schema_name, hypertable_row.table_name)::regclass::oid; IF constraint_type IN ('p','u') THEN - -- since primary keys and unique constraints are backed by an index - -- they might have an index tablespace assigned - -- the tablspace is not part of the constraint definition so - -- we have to append it explicitly to preserve it - SELECT T.spcname INTO indx_tablespace - FROM pg_constraint C, pg_class I, pg_tablespace T - WHERE C.oid = constraint_oid AND C.contype IN ('p', 'u') AND I.oid = C.conindid AND I.reltablespace = T.oid; + IF using_index THEN + -- indexes created for constraints are named after the constraint + -- so we can find the index name by looking up the constraint name. + -- Drop the entry in chunk_index and it will be added back later + -- with the correct index name by the existing flow. + DELETE FROM _timescaledb_catalog.chunk_index + WHERE chunk_id = chunk_row.id AND hypertable_index_name = chunk_constraint_row.hypertable_constraint_name RETURNING index_name INTO STRICT chunk_constraint_index_name; - def := pg_get_constraintdef(constraint_oid); + CASE constraint_type + WHEN 'p' THEN + def := pg_catalog.format( + $$ PRIMARY KEY USING INDEX %I $$, + chunk_constraint_index_name + ); + WHEN 'u' THEN + def := pg_catalog.format( + $$ UNIQUE USING INDEX %I $$, + chunk_constraint_index_name + ); + END CASE; + ELSE + -- since primary keys and unique constraints are backed by an index + -- they might have an index tablespace assigned + -- the tablspace is not part of the constraint definition so + -- we have to append it explicitly to preserve it + SELECT T.spcname INTO indx_tablespace + FROM pg_constraint C, pg_class I, pg_tablespace T + WHERE C.oid = constraint_oid AND C.contype IN ('p', 'u') AND I.oid = C.conindid AND I.reltablespace = T.oid; + + def := pg_get_constraintdef(constraint_oid); + END IF; ELSIF constraint_type = 't' THEN -- constraint triggers are copied separately with normal triggers diff --git a/sql/updates/reverse-dev.sql b/sql/updates/reverse-dev.sql index e69de29bb2d..c52e9cc13bf 100644 --- a/sql/updates/reverse-dev.sql +++ b/sql/updates/reverse-dev.sql @@ -0,0 +1,75 @@ +DROP FUNCTION _timescaledb_functions.chunk_constraint_add_table_constraint( + chunk_constraint_row _timescaledb_catalog.chunk_constraint +); + +CREATE FUNCTION _timescaledb_functions.chunk_constraint_add_table_constraint( + chunk_constraint_row _timescaledb_catalog.chunk_constraint +) + RETURNS VOID LANGUAGE PLPGSQL AS +$BODY$ +DECLARE + chunk_row _timescaledb_catalog.chunk; + hypertable_row _timescaledb_catalog.hypertable; + constraint_oid OID; + constraint_type CHAR; + check_sql TEXT; + def TEXT; + indx_tablespace NAME; + tablespace_def TEXT; +BEGIN + SELECT * INTO STRICT chunk_row FROM _timescaledb_catalog.chunk c WHERE c.id = chunk_constraint_row.chunk_id; + SELECT * INTO STRICT hypertable_row FROM _timescaledb_catalog.hypertable h WHERE h.id = chunk_row.hypertable_id; + + IF chunk_constraint_row.dimension_slice_id IS NOT NULL THEN + RAISE 'cannot create dimension constraint %', chunk_constraint_row; + ELSIF chunk_constraint_row.hypertable_constraint_name IS NOT NULL THEN + + SELECT oid, contype INTO STRICT constraint_oid, constraint_type FROM pg_constraint + WHERE conname=chunk_constraint_row.hypertable_constraint_name AND + conrelid = format('%I.%I', hypertable_row.schema_name, hypertable_row.table_name)::regclass::oid; + + IF constraint_type IN ('p','u') THEN + -- since primary keys and unique constraints are backed by an index + -- they might have an index tablespace assigned + -- the tablspace is not part of the constraint definition so + -- we have to append it explicitly to preserve it + SELECT T.spcname INTO indx_tablespace + FROM pg_constraint C, pg_class I, pg_tablespace T + WHERE C.oid = constraint_oid AND C.contype IN ('p', 'u') AND I.oid = C.conindid AND I.reltablespace = T.oid; + + def := pg_get_constraintdef(constraint_oid); + + ELSIF constraint_type = 't' THEN + -- constraint triggers are copied separately with normal triggers + def := NULL; + ELSE + def := pg_get_constraintdef(constraint_oid); + END IF; + + ELSE + RAISE 'unknown constraint type'; + END IF; + + IF def IS NOT NULL THEN + -- to allow for custom types with operators outside of pg_catalog + -- we set search_path to @extschema@ + SET LOCAL search_path TO @extschema@, pg_temp; + EXECUTE pg_catalog.format( + $$ ALTER TABLE %I.%I ADD CONSTRAINT %I %s $$, + chunk_row.schema_name, chunk_row.table_name, chunk_constraint_row.constraint_name, def + ); + + -- if constraint (primary or unique) needs a tablespace then add it + -- via a separate ALTER INDEX SET TABLESPACE command. We cannot append it + -- to the "def" string above since it leads to a SYNTAX error when + -- "DEFERRABLE" or "INITIALLY DEFERRED" are used in the constraint + IF indx_tablespace IS NOT NULL THEN + EXECUTE pg_catalog.format( + $$ ALTER INDEX %I.%I SET TABLESPACE %I $$, + chunk_row.schema_name, chunk_constraint_row.constraint_name, indx_tablespace + ); + END IF; + + END IF; +END +$BODY$ SET search_path TO pg_catalog, pg_temp; diff --git a/src/chunk_constraint.c b/src/chunk_constraint.c index d6d9219b086..81ece991c07 100644 --- a/src/chunk_constraint.c +++ b/src/chunk_constraint.c @@ -373,7 +373,7 @@ create_dimension_check_constraint(const Dimension *dim, const DimensionSlice *sl * Add a constraint to a chunk table. */ static Oid -chunk_constraint_create_on_table(const ChunkConstraint *cc, Oid chunk_oid) +chunk_constraint_create_on_table(const ChunkConstraint *cc, Oid chunk_oid, bool using_index) { HeapTuple tuple; Datum values[Natts_chunk_constraint]; @@ -388,7 +388,9 @@ chunk_constraint_create_on_table(const ChunkConstraint *cc, Oid chunk_oid) RelationClose(rel); ts_catalog_database_info_become_owner(ts_catalog_database_info_get(), &sec_ctx); - CatalogInternalCall1(DDL_ADD_CHUNK_CONSTRAINT, HeapTupleGetDatum(tuple)); + CatalogInternalCall2(DDL_ADD_CHUNK_CONSTRAINT, + HeapTupleGetDatum(tuple), + BoolGetDatum(using_index)); ts_catalog_restore_user(&sec_ctx); heap_freetuple(tuple); @@ -401,14 +403,14 @@ chunk_constraint_create_on_table(const ChunkConstraint *cc, Oid chunk_oid) */ static Oid create_non_dimensional_constraint(const ChunkConstraint *cc, Oid chunk_oid, int32 chunk_id, - Oid hypertable_oid, int32 hypertable_id) + Oid hypertable_oid, int32 hypertable_id, bool using_index) { Oid chunk_constraint_oid; Assert(!is_dimension_constraint(cc)); ts_process_utility_set_expect_chunk_modification(true); - chunk_constraint_oid = chunk_constraint_create_on_table(cc, chunk_oid); + chunk_constraint_oid = chunk_constraint_create_on_table(cc, chunk_oid, using_index); ts_process_utility_set_expect_chunk_modification(false); /* @@ -493,7 +495,8 @@ ts_chunk_constraints_create(const Hypertable *ht, const Chunk *chunk) chunk->table_id, chunk->fd.id, ht->main_table_relid, - ht->fd.id); + ht->fd.id, + false); } } @@ -851,7 +854,8 @@ ts_chunk_constraints_add_inheritable_check_constraints(ChunkConstraints *ccs, in } void -ts_chunk_constraint_create_on_chunk(const Hypertable *ht, const Chunk *chunk, Oid constraint_oid) +ts_chunk_constraint_create_on_chunk(const Hypertable *ht, const Chunk *chunk, Oid constraint_oid, + bool using_index) { HeapTuple tuple; Form_pg_constraint con; @@ -876,7 +880,8 @@ ts_chunk_constraint_create_on_chunk(const Hypertable *ht, const Chunk *chunk, Oi chunk->table_id, chunk->fd.id, ht->main_table_relid, - ht->fd.id); + ht->fd.id, + using_index); } ReleaseSysCache(tuple); diff --git a/src/chunk_constraint.h b/src/chunk_constraint.h index 585e243ffdc..0545ca6caf2 100644 --- a/src/chunk_constraint.h +++ b/src/chunk_constraint.h @@ -60,7 +60,7 @@ extern TSDLLEXPORT int ts_chunk_constraints_add_inheritable_check_constraints( extern TSDLLEXPORT void ts_chunk_constraints_insert_metadata(const ChunkConstraints *ccs); extern TSDLLEXPORT void ts_chunk_constraints_create(const Hypertable *ht, const Chunk *chunk); extern void ts_chunk_constraint_create_on_chunk(const Hypertable *ht, const Chunk *chunk, - Oid constraint_oid); + Oid constraint_oid, bool using_index); extern int ts_chunk_constraint_delete_by_hypertable_constraint_name( int32 chunk_id, const char *hypertable_constraint_name, bool delete_metadata, bool drop_constraint); diff --git a/src/chunk_index.c b/src/chunk_index.c index 6cf4e4e78f2..e9f5d56fb62 100644 --- a/src/chunk_index.c +++ b/src/chunk_index.c @@ -1016,12 +1016,11 @@ ts_chunk_index_rename(Chunk *chunk, Oid chunk_indexrelid, const char *new_name) } int -ts_chunk_index_rename_parent(Hypertable *ht, Oid hypertable_indexrelid, const char *new_name) +ts_chunk_index_rename_parent(Hypertable *ht, const char *old_name, const char *new_name) { ScanKeyData scankey[2]; - const char *indexname = get_rel_name(hypertable_indexrelid); ChunkIndexRenameInfo renameinfo = { - .oldname = indexname, + .oldname = old_name, .newname = new_name, .isparent = true, }; @@ -1035,7 +1034,7 @@ ts_chunk_index_rename_parent(Hypertable *ht, Oid hypertable_indexrelid, const ch Anum_chunk_index_hypertable_id_hypertable_index_name_idx_hypertable_index_name, BTEqualStrategyNumber, F_NAMEEQ, - CStringGetDatum(indexname)); + CStringGetDatum(old_name)); return chunk_index_scan_update(CHUNK_INDEX_HYPERTABLE_ID_HYPERTABLE_INDEX_NAME_IDX, scankey, diff --git a/src/chunk_index.h b/src/chunk_index.h index 9b064a46e59..53501d65359 100644 --- a/src/chunk_index.h +++ b/src/chunk_index.h @@ -41,8 +41,7 @@ extern int ts_chunk_index_delete_by_chunk_id(int32 chunk_id, bool drop_index); extern void ts_chunk_index_delete_by_name(const char *schema, const char *index_name, bool drop_index); extern int ts_chunk_index_rename(Chunk *chunk, Oid chunk_indexrelid, const char *new_name); -extern int ts_chunk_index_rename_parent(Hypertable *ht, Oid hypertable_indexrelid, - const char *new_name); +extern int ts_chunk_index_rename_parent(Hypertable *ht, const char *oldname, const char *new_name); extern int ts_chunk_index_adjust_meta(int32 chunk_id, const char *ht_index_name, const char *old_name, const char *new_name); extern int ts_chunk_index_set_tablespace(Hypertable *ht, Oid hypertable_indexrelid, diff --git a/src/process_utility.c b/src/process_utility.c index 72cabb29925..2774d521229 100644 --- a/src/process_utility.c +++ b/src/process_utility.c @@ -1969,7 +1969,8 @@ process_rename_index(ProcessUtilityArgs *args, Cache *hcache, Oid relid, RenameS if (NULL != ht) { - ts_chunk_index_rename_parent(ht, relid, stmt->newname); + const char *indexname = get_rel_name(relid); + ts_chunk_index_rename_parent(ht, indexname, stmt->newname); add_hypertable_to_process_args(args, ht); } @@ -2180,13 +2181,33 @@ process_altertable_change_owner(Hypertable *ht, AlterTableCmd *cmd) } } +static void +process_add_constraint_chunk_using_index(Hypertable *ht, Oid chunk_relid, void *arg) +{ + Oid hypertable_constraint_oid = *((Oid *) arg); + Chunk *chunk = ts_chunk_get_by_relid(chunk_relid, true); + + ts_chunk_constraint_create_on_chunk(ht, chunk, hypertable_constraint_oid, true); +} + +static void +process_altertable_add_constraint_using_index(Hypertable *ht, const char *constraint_name) +{ + Oid hypertable_constraint_oid = + get_relation_constraint_oid(ht->main_table_relid, constraint_name, false); + + Assert(constraint_name != NULL); + + foreach_chunk(ht, process_add_constraint_chunk_using_index, &hypertable_constraint_oid); +} + static void process_add_constraint_chunk(Hypertable *ht, Oid chunk_relid, void *arg) { Oid hypertable_constraint_oid = *((Oid *) arg); Chunk *chunk = ts_chunk_get_by_relid(chunk_relid, true); - ts_chunk_constraint_create_on_chunk(ht, chunk, hypertable_constraint_oid); + ts_chunk_constraint_create_on_chunk(ht, chunk, hypertable_constraint_oid, false); } static void @@ -2276,6 +2297,25 @@ verify_constraint_plaintable(RangeVar *relation, Constraint *constr) ts_cache_release(hcache); } +/* + * Postgres does not allow having different names for the constraint and the + * index it is using, so we need to rename the index to match the constraint + * name. + */ +static void +rename_constraint_index(Hypertable *ht, Constraint *constr) +{ + ConstrType contype = constr->contype; + const char *conname = constr->conname; + const char *indexname = constr->indexname; + + if (contype == CONSTR_UNIQUE || contype == CONSTR_PRIMARY) + { + if (indexname != NULL) + ts_chunk_index_rename_parent(ht, indexname, conname); + } +} + /* * Verify that a constraint is supported on a hypertable. */ @@ -3390,9 +3430,14 @@ process_altertable_start_table(ProcessUtilityArgs *args) Assert(IsA(cmd->def, Constraint)); if (NULL == ht) + { verify_constraint_plaintable(stmt->relation, (Constraint *) cmd->def); + } else + { verify_constraint_hypertable(ht, cmd->def); + rename_constraint_index(ht, (Constraint *) cmd->def); + } break; case AT_AlterColumnType: Assert(IsA(cmd->def, ColumnDef)); @@ -3647,11 +3692,21 @@ process_altertable_end_subcmd(Hypertable *ht, Node *parsetree, ObjectAddress *ob process_altertable_change_owner(ht, cmd); break; case AT_AddIndexConstraint: - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("hypertables do not support adding a constraint " - "using an existing index"))); - break; + { + IndexStmt *stmt = (IndexStmt *) cmd->def; + + Assert(IsA(cmd->def, IndexStmt)); + + const char *idxname = stmt->idxname; + + if (idxname == NULL) + { + idxname = get_rel_name(obj->objectId); + } + + process_altertable_add_constraint_using_index(ht, idxname); + } + break; case AT_AddIndex: { IndexStmt *stmt = (IndexStmt *) cmd->def; diff --git a/src/ts_catalog/catalog.c b/src/ts_catalog/catalog.c index 312d0f30856..e7d76bd8009 100644 --- a/src/ts_catalog/catalog.c +++ b/src/ts_catalog/catalog.c @@ -280,7 +280,7 @@ typedef struct InternalFunctionDef static const InternalFunctionDef internal_function_definitions[_MAX_INTERNAL_FUNCTIONS] = { [DDL_ADD_CHUNK_CONSTRAINT] = { .name = "chunk_constraint_add_table_constraint", - .args = 1, + .args = 2, }, [DDL_CONSTRAINT_CLONE] = { .name = "constraint_clone", diff --git a/test/expected/constraint.out b/test/expected/constraint.out index b22baab9360..eb3eadf32e3 100644 --- a/test/expected/constraint.out +++ b/test/expected/constraint.out @@ -211,22 +211,22 @@ SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_2_4_chunk'); CREATE UNIQUE INDEX hyper_unique_with_looooooooooooooooooooooooooooooooo_time_idx ON hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name (time); -\set ON_ERROR_STOP 0 -- Try adding constraint using existing index ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name ADD CONSTRAINT hyper_unique_with_looooooooooooooooooooooooooooooooooo_time_key UNIQUE USING INDEX hyper_unique_with_looooooooooooooooooooooooooooooooo_time_idx; NOTICE: ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index "hyper_unique_with_looooooooooooooooooooooooooooooooo_time_idx" to "hyper_unique_with_looooooooooooooooooooooooooooooooooo_time_key" -ERROR: hypertables do not support adding a constraint using an existing index -\set ON_ERROR_STOP 1 -DROP INDEX hyper_unique_with_looooooooooooooooooooooooooooooooo_time_idx; +NOTICE: ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index "_hyper_2_4_chunk_hyper_unique_with_looooooooooooooooooooooooo_1" to "4_6_hyper_unique_with_looooooooooooooooooooooooooooooooooo_time" +NOTICE: ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index "_hyper_2_5_chunk_hyper_unique_with_looooooooooooooooooooooooo_1" to "5_7_hyper_unique_with_looooooooooooooooooooooooooooooooooo_time" +ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name +DROP CONSTRAINT hyper_unique_with_looooooooooooooooooooooooooooooooooo_time_key; --now can create ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name ADD CONSTRAINT hyper_unique_with_looooooooooooooooooooooooooooooooooo_time_key UNIQUE (time); SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_2_4_chunk'); Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated -----------------------------------------------------------------+------+------------+-----------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------+------------+----------+----------- - 4_6_hyper_unique_with_looooooooooooooooooooooooooooooooooo_time | u | {time} | _timescaledb_internal."4_6_hyper_unique_with_looooooooooooooooooooooooooooooooooo_time" | | f | f | t + 4_8_hyper_unique_with_looooooooooooooooooooooooooooooooooo_time | u | {time} | _timescaledb_internal."4_8_hyper_unique_with_looooooooooooooooooooooooooooooooooo_time" | | f | f | t constraint_4 | c | {time} | - | (("time" >= '1257987700000000000'::bigint) AND ("time" < '1257987700000000010'::bigint)) | f | f | t hyper_unique_with_looooooooooooooooooooooooooooo_sensor_1_check | c | {sensor_1} | - | (sensor_1 > (10)::numeric) | f | f | t (3 rows) @@ -241,7 +241,7 @@ ERROR: relation "hyper_unique_with_looooooooooooooooooooooooooooooooooo_time_ke \set ON_ERROR_STOP 0 INSERT INTO hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name(time, device_id,sensor_1) VALUES (1257987700000000000, 'dev2', 11); -ERROR: duplicate key value violates unique constraint "4_6_hyper_unique_with_looooooooooooooooooooooooooooooooooo_time" +ERROR: duplicate key value violates unique constraint "4_8_hyper_unique_with_looooooooooooooooooooooooooooooooooo_time" \set ON_ERROR_STOP 1 --cannot create unique constraint on non-partition column \set ON_ERROR_STOP 0 @@ -273,7 +273,7 @@ SELECT * FROM test.show_constraints('hyper_unique_with_loooooooooooooooooooooooo SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_2_4_chunk'); Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated ----------------+------+------------+----------------------------------------+------------------------------------------------------------------------------------------+------------+----------+----------- - 4_10_new_name2 | u | {time} | _timescaledb_internal."4_10_new_name2" | | f | f | t + 4_12_new_name2 | u | {time} | _timescaledb_internal."4_12_new_name2" | | f | f | t check_2 | c | {sensor_1} | - | (sensor_1 > (10)::numeric) | f | f | t constraint_4 | c | {time} | - | (("time" >= '1257987700000000000'::bigint) AND ("time" < '1257987700000000010'::bigint)) | f | f | t (3 rows) @@ -284,8 +284,8 @@ SELECT * FROM _timescaledb_catalog.chunk_constraint; 3 | 3 | constraint_3 | 4 | 4 | constraint_4 | 5 | 5 | constraint_5 | - 4 | | 4_10_new_name2 | new_name2 - 5 | | 5_11_new_name2 | new_name2 + 4 | | 4_12_new_name2 | new_name2 + 5 | | 5_13_new_name2 | new_name2 (5 rows) \set ON_ERROR_STOP 0 @@ -319,13 +319,13 @@ INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES \set ON_ERROR_STOP 0 INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES (1257987700000000000, 'dev2', 11); -ERROR: duplicate key value violates unique constraint "6_14_hyper_pk_pkey" +ERROR: duplicate key value violates unique constraint "6_16_hyper_pk_pkey" \set ON_ERROR_STOP 1 --should have unique constraint not just unique index SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_3_6_chunk'); Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated -------------------------+------+------------+--------------------------------------------+------------------------------------------------------------------------------------------+------------+----------+----------- - 6_14_hyper_pk_pkey | p | {time} | _timescaledb_internal."6_14_hyper_pk_pkey" | | f | f | t + 6_16_hyper_pk_pkey | p | {time} | _timescaledb_internal."6_16_hyper_pk_pkey" | | f | f | t constraint_6 | c | {time} | - | (("time" >= '1257987700000000000'::bigint) AND ("time" < '1257987700000000010'::bigint)) | f | f | t hyper_pk_sensor_1_check | c | {sensor_1} | - | (sensor_1 > (10)::numeric) | f | f | t (3 rows) @@ -344,7 +344,7 @@ INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES --shouldn't be able to create pk \set ON_ERROR_STOP 0 ALTER TABLE hyper_pk ADD CONSTRAINT hyper_pk_pkey PRIMARY KEY (time); -ERROR: could not create unique index "6_15_hyper_pk_pkey" +ERROR: could not create unique index "6_17_hyper_pk_pkey" ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name ADD COLUMN new_device_id int PRIMARY KEY; ERROR: cannot create a unique index without the column "time" (used in partitioning) @@ -360,7 +360,7 @@ ALTER TABLE hyper_pk ADD CONSTRAINT hyper_pk_pkey PRIMARY KEY (time) DEFERRABLE SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_3_6_chunk'); Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated -------------------------+------+------------+--------------------------------------------+------------------------------------------------------------------------------------------+------------+----------+----------- - 6_16_hyper_pk_pkey | p | {time} | _timescaledb_internal."6_16_hyper_pk_pkey" | | t | t | t + 6_18_hyper_pk_pkey | p | {time} | _timescaledb_internal."6_18_hyper_pk_pkey" | | t | t | t constraint_6 | c | {time} | - | (("time" >= '1257987700000000000'::bigint) AND ("time" < '1257987700000000010'::bigint)) | f | f | t hyper_pk_sensor_1_check | c | {sensor_1} | - | (sensor_1 > (10)::numeric) | f | f | t (3 rows) @@ -383,7 +383,7 @@ BEGIN; (1 row) COMMIT; -ERROR: duplicate key value violates unique constraint "6_16_hyper_pk_pkey" +ERROR: duplicate key value violates unique constraint "6_18_hyper_pk_pkey" \set ON_ERROR_STOP 1 ----------------------- FOREIGN KEY ------------------ CREATE TABLE devices( @@ -405,7 +405,7 @@ SELECT * FROM create_hypertable('hyper_fk', 'time', chunk_time_interval => 10); \set ON_ERROR_STOP 0 INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES (1257987700000000000, 'dev2', 11); -ERROR: insert or update on table "_hyper_4_7_chunk" violates foreign key constraint "7_17_hyper_fk_device_id_fkey" +ERROR: insert or update on table "_hyper_4_7_chunk" violates foreign key constraint "7_19_hyper_fk_device_id_fkey" \set ON_ERROR_STOP 1 INSERT INTO devices VALUES ('dev2'); INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES @@ -413,7 +413,7 @@ INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES --delete should fail \set ON_ERROR_STOP 0 DELETE FROM devices; -ERROR: update or delete on table "devices" violates foreign key constraint "8_19_hyper_fk_device_id_fkey" on table "_hyper_4_8_chunk" +ERROR: update or delete on table "devices" violates foreign key constraint "8_21_hyper_fk_device_id_fkey" on table "_hyper_4_8_chunk" \set ON_ERROR_STOP 1 ALTER TABLE hyper_fk DROP CONSTRAINT hyper_fk_device_id_fkey; --should now be able to add non-fk rows @@ -423,7 +423,7 @@ INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES \set ON_ERROR_STOP 0 ALTER TABLE hyper_fk ADD CONSTRAINT hyper_fk_device_id_fkey FOREIGN KEY (device_id) REFERENCES devices(device_id); -ERROR: insert or update on table "_hyper_4_8_chunk" violates foreign key constraint "8_21_hyper_fk_device_id_fkey" +ERROR: insert or update on table "_hyper_4_8_chunk" violates foreign key constraint "8_23_hyper_fk_device_id_fkey" \set ON_ERROR_STOP 1 --but can add a NOT VALID one ALTER TABLE hyper_fk ADD CONSTRAINT hyper_fk_device_id_fkey @@ -431,7 +431,7 @@ FOREIGN KEY (device_id) REFERENCES devices(device_id) NOT VALID; --which will fail when validated \set ON_ERROR_STOP 0 ALTER TABLE hyper_fk VALIDATE CONSTRAINT hyper_fk_device_id_fkey; -ERROR: insert or update on table "_hyper_4_8_chunk" violates foreign key constraint "8_22_hyper_fk_device_id_fkey" +ERROR: insert or update on table "_hyper_4_8_chunk" violates foreign key constraint "8_24_hyper_fk_device_id_fkey" \set ON_ERROR_STOP 1 ALTER TABLE hyper_fk DROP CONSTRAINT hyper_fk_device_id_fkey; DELETE FROM hyper_fk WHERE device_id = 'dev3'; @@ -440,13 +440,13 @@ FOREIGN KEY (device_id) REFERENCES devices(device_id); \set ON_ERROR_STOP 0 INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES (1257987700000000002, 'dev3', 11); -ERROR: insert or update on table "_hyper_4_8_chunk" violates foreign key constraint "8_23_hyper_fk_device_id_fkey" +ERROR: insert or update on table "_hyper_4_8_chunk" violates foreign key constraint "8_25_hyper_fk_device_id_fkey" \set ON_ERROR_STOP 1 SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_4_8_chunk'); Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated ------------------------------+------+-------------+--------------------------------------------+------------------------------------------------------------------------------------------+------------+----------+----------- - 8_20_hyper_fk_pkey | p | {time} | _timescaledb_internal."8_20_hyper_fk_pkey" | | f | f | t - 8_23_hyper_fk_device_id_fkey | f | {device_id} | devices_pkey | | f | f | t + 8_22_hyper_fk_pkey | p | {time} | _timescaledb_internal."8_22_hyper_fk_pkey" | | f | f | t + 8_25_hyper_fk_device_id_fkey | f | {device_id} | devices_pkey | | f | f | t constraint_8 | c | {time} | - | (("time" >= '1257987700000000000'::bigint) AND ("time" < '1257987700000000010'::bigint)) | f | f | t hyper_fk_sensor_1_check | c | {sensor_1} | - | (sensor_1 > (10)::numeric) | f | f | t (4 rows) @@ -457,13 +457,13 @@ SELECT * FROM _timescaledb_catalog.chunk_constraint; 3 | 3 | constraint_3 | 4 | 4 | constraint_4 | 5 | 5 | constraint_5 | - 4 | | 4_10_new_name2 | new_name2 - 5 | | 5_11_new_name2 | new_name2 + 4 | | 4_12_new_name2 | new_name2 + 5 | | 5_13_new_name2 | new_name2 6 | 6 | constraint_6 | - 6 | | 6_16_hyper_pk_pkey | hyper_pk_pkey + 6 | | 6_18_hyper_pk_pkey | hyper_pk_pkey 8 | 8 | constraint_8 | - 8 | | 8_20_hyper_fk_pkey | hyper_fk_pkey - 8 | | 8_23_hyper_fk_device_id_fkey | hyper_fk_device_id_fkey + 8 | | 8_22_hyper_fk_pkey | hyper_fk_pkey + 8 | | 8_25_hyper_fk_device_id_fkey | hyper_fk_device_id_fkey (10 rows) --test CASCADE drop behavior @@ -472,7 +472,7 @@ NOTICE: drop cascades to 2 other objects SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_4_8_chunk'); Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated -------------------------+------+------------+--------------------------------------------+------------------------------------------------------------------------------------------+------------+----------+----------- - 8_20_hyper_fk_pkey | p | {time} | _timescaledb_internal."8_20_hyper_fk_pkey" | | f | f | t + 8_22_hyper_fk_pkey | p | {time} | _timescaledb_internal."8_22_hyper_fk_pkey" | | f | f | t constraint_8 | c | {time} | - | (("time" >= '1257987700000000000'::bigint) AND ("time" < '1257987700000000010'::bigint)) | f | f | t hyper_fk_sensor_1_check | c | {sensor_1} | - | (sensor_1 > (10)::numeric) | f | f | t (3 rows) @@ -483,12 +483,12 @@ SELECT * FROM _timescaledb_catalog.chunk_constraint; 3 | 3 | constraint_3 | 4 | 4 | constraint_4 | 5 | 5 | constraint_5 | - 4 | | 4_10_new_name2 | new_name2 - 5 | | 5_11_new_name2 | new_name2 + 4 | | 4_12_new_name2 | new_name2 + 5 | | 5_13_new_name2 | new_name2 6 | 6 | constraint_6 | - 6 | | 6_16_hyper_pk_pkey | hyper_pk_pkey + 6 | | 6_18_hyper_pk_pkey | hyper_pk_pkey 8 | 8 | constraint_8 | - 8 | | 8_20_hyper_fk_pkey | hyper_fk_pkey + 8 | | 8_22_hyper_fk_pkey | hyper_fk_pkey (9 rows) --the fk went away. @@ -513,7 +513,7 @@ BEGIN; (1 row) COMMIT; -ERROR: insert or update on table "_hyper_4_8_chunk" violates foreign key constraint "8_24_hyper_fk_device_id_fkey" +ERROR: insert or update on table "_hyper_4_8_chunk" violates foreign key constraint "8_26_hyper_fk_device_id_fkey" \set ON_ERROR_STOP 1 ALTER TABLE hyper_fk ALTER CONSTRAINT hyper_fk_device_id_fkey NOT DEFERRABLE; \set ON_ERROR_STOP 0 @@ -521,7 +521,7 @@ BEGIN; --error detected right away INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES (1257987700000000003, 'dev4', 11); -ERROR: insert or update on table "_hyper_4_8_chunk" violates foreign key constraint "8_24_hyper_fk_device_id_fkey" +ERROR: insert or update on table "_hyper_4_8_chunk" violates foreign key constraint "8_26_hyper_fk_device_id_fkey" SELECT 1; ERROR: current transaction is aborted, commands ignored until end of transaction block COMMIT; @@ -580,7 +580,7 @@ INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES \set ON_ERROR_STOP 0 INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES (1257987700000000000, 'dev2', 12); -ERROR: conflicting key value violates exclusion constraint "9_26_hyper_ex_time_device_id_excl" +ERROR: conflicting key value violates exclusion constraint "9_28_hyper_ex_time_device_id_excl" \set ON_ERROR_STOP 1 ALTER TABLE hyper_ex DROP CONSTRAINT hyper_ex_time_device_id_excl; --can now add @@ -593,7 +593,7 @@ ALTER TABLE hyper_ex ADD CONSTRAINT hyper_ex_time_device_id_excl time WITH =, device_id WITH = ) WHERE (not canceled) ; -ERROR: could not create exclusion constraint "9_27_hyper_ex_time_device_id_excl" +ERROR: could not create exclusion constraint "9_29_hyper_ex_time_device_id_excl" \set ON_ERROR_STOP 1 DELETE FROM hyper_ex WHERE sensor_1 = 12; ALTER TABLE hyper_ex ADD CONSTRAINT hyper_ex_time_device_id_excl @@ -613,7 +613,7 @@ BEGIN; (1 row) COMMIT; -ERROR: conflicting key value violates exclusion constraint "9_28_hyper_ex_time_device_id_excl" +ERROR: conflicting key value violates exclusion constraint "9_30_hyper_ex_time_device_id_excl" \set ON_ERROR_STOP 1 --cannot add exclusion constraint without partition key. CREATE TABLE hyper_ex_invalid ( @@ -684,7 +684,7 @@ BEGIN; (1 row) COMMIT; -ERROR: duplicate key value violates unique constraint "10_29_hyper_unique_deferred_time_key" +ERROR: duplicate key value violates unique constraint "10_31_hyper_unique_deferred_time_key" \set ON_ERROR_STOP 1 --test deferred on create table CREATE TABLE hyper_pk_deferred ( @@ -710,7 +710,7 @@ BEGIN; (1 row) COMMIT; -ERROR: duplicate key value violates unique constraint "11_30_hyper_pk_deferred_pkey" +ERROR: duplicate key value violates unique constraint "11_32_hyper_pk_deferred_pkey" \set ON_ERROR_STOP 1 --test that deferred works on create table too CREATE TABLE hyper_fk_deferred ( @@ -735,7 +735,7 @@ BEGIN; (1 row) COMMIT; -ERROR: insert or update on table "_hyper_11_12_chunk" violates foreign key constraint "12_31_hyper_fk_deferred_device_id_fkey" +ERROR: insert or update on table "_hyper_11_12_chunk" violates foreign key constraint "12_33_hyper_fk_deferred_device_id_fkey" \set ON_ERROR_STOP 1 CREATE TABLE hyper_ex_deferred ( time BIGINT, @@ -765,7 +765,7 @@ BEGIN; (1 row) COMMIT; -ERROR: conflicting key value violates exclusion constraint "13_34_hyper_ex_deferred_time_device_id_excl" +ERROR: conflicting key value violates exclusion constraint "13_36_hyper_ex_deferred_time_device_id_excl" \set ON_ERROR_STOP 1 -- Make sure renaming schemas won't break dropping constraints \c :TEST_DBNAME :ROLE_SUPERUSER diff --git a/test/sql/constraint.sql b/test/sql/constraint.sql index 9861b798e9c..cfbe951b5f1 100644 --- a/test/sql/constraint.sql +++ b/test/sql/constraint.sql @@ -125,13 +125,13 @@ SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_2_4_chunk'); CREATE UNIQUE INDEX hyper_unique_with_looooooooooooooooooooooooooooooooo_time_idx ON hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name (time); -\set ON_ERROR_STOP 0 -- Try adding constraint using existing index ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name ADD CONSTRAINT hyper_unique_with_looooooooooooooooooooooooooooooooooo_time_key UNIQUE USING INDEX hyper_unique_with_looooooooooooooooooooooooooooooooo_time_idx; -\set ON_ERROR_STOP 1 -DROP INDEX hyper_unique_with_looooooooooooooooooooooooooooooooo_time_idx; + +ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name +DROP CONSTRAINT hyper_unique_with_looooooooooooooooooooooooooooooooooo_time_key; --now can create ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name