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..5c92308b6bd 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,35 @@ 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. + SELECT index_name INTO STRICT chunk_constraint_index_name FROM _timescaledb_catalog.chunk_index + WHERE chunk_id = chunk_row.id AND hypertable_index_name = chunk_constraint_row.hypertable_constraint_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..b162b9b4ae7 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); /* @@ -430,7 +432,7 @@ create_non_dimensional_constraint(const ChunkConstraint *cc, Oid chunk_oid, int3 { FormData_pg_constraint *constr = (FormData_pg_constraint *) GETSTRUCT(tuple); - if (OidIsValid(constr->conindid) && constr->contype != CONSTRAINT_FOREIGN) + if (OidIsValid(constr->conindid) && constr->contype != CONSTRAINT_FOREIGN && !using_index) ts_chunk_index_create_from_constraint(hypertable_id, hypertable_constraint_oid, chunk_id, @@ -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..8dac7462d95 100644 --- a/src/chunk_index.c +++ b/src/chunk_index.c @@ -987,12 +987,11 @@ ts_chunk_index_adjust_meta(int32 chunk_id, const char *ht_index_name, const char } int -ts_chunk_index_rename(Chunk *chunk, Oid chunk_indexrelid, const char *new_name) +ts_chunk_index_rename(Chunk *chunk, const char *old_name, const char *new_name) { ScanKeyData scankey[2]; - const char *indexname = get_rel_name(chunk_indexrelid); ChunkIndexRenameInfo renameinfo = { - .oldname = indexname, + .oldname = old_name, .newname = new_name, }; @@ -1005,7 +1004,7 @@ ts_chunk_index_rename(Chunk *chunk, Oid chunk_indexrelid, const char *new_name) Anum_chunk_index_chunk_id_index_name_idx_index_name, BTEqualStrategyNumber, F_NAMEEQ, - CStringGetDatum(indexname)); + CStringGetDatum(old_name)); return chunk_index_scan_update(CHUNK_INDEX_CHUNK_ID_INDEX_NAME_IDX, scankey, @@ -1016,12 +1015,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 +1033,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..6f85ab7e9fc 100644 --- a/src/chunk_index.h +++ b/src/chunk_index.h @@ -40,9 +40,8 @@ extern int ts_chunk_index_delete(int32 chunk_id, const char *indexname, bool dro 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(Chunk *chunk, const char *old_name, const char *new_name); +extern int ts_chunk_index_rename_parent(Hypertable *ht, const char *old_name, 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..669b172bd12 100644 --- a/src/process_utility.c +++ b/src/process_utility.c @@ -1967,9 +1967,10 @@ process_rename_index(ProcessUtilityArgs *args, Cache *hcache, Oid relid, RenameS ht = ts_hypertable_cache_get_entry(hcache, tablerelid, CACHE_FLAG_MISSING_OK); + const char *indexname = get_rel_name(relid); if (NULL != ht) { - ts_chunk_index_rename_parent(ht, relid, stmt->newname); + ts_chunk_index_rename_parent(ht, indexname, stmt->newname); add_hypertable_to_process_args(args, ht); } @@ -1978,7 +1979,7 @@ process_rename_index(ProcessUtilityArgs *args, Cache *hcache, Oid relid, RenameS Chunk *chunk = ts_chunk_get_by_relid(tablerelid, false); if (NULL != chunk) - ts_chunk_index_rename(chunk, relid, stmt->newname); + ts_chunk_index_rename(chunk, indexname, stmt->newname); } } @@ -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 @@ -3393,6 +3414,33 @@ process_altertable_start_table(ProcessUtilityArgs *args) verify_constraint_plaintable(stmt->relation, (Constraint *) cmd->def); else verify_constraint_hypertable(ht, cmd->def); + + Constraint *constr = (Constraint *) cmd->def; + ConstrType contype = constr->contype; + const char *conname = constr->conname; + const char *indexname = constr->indexname; + + /* This is a unique or primary constraint using an existing index */ + if ((contype == CONSTR_UNIQUE || contype == CONSTR_PRIMARY) && indexname != NULL) + { + /* + * Postgres renames the index to match the constraint name. + * We need to do the same on our catalog tables to maintain + * consistency. + */ + if (ht != NULL) + { + ts_chunk_index_rename_parent(ht, indexname, conname); + } + else + { + Chunk *chunk = ts_chunk_get_by_relid(relid, false); + + if (NULL != chunk) + ts_chunk_index_rename(chunk, indexname, conname); + } + } + break; case AT_AlterColumnType: Assert(IsA(cmd->def, ColumnDef)); @@ -3647,11 +3695,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