From 6d7edb99bad168cd633534a93e469fbace75fc20 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sat, 5 Sep 2020 17:34:08 +0200 Subject: [PATCH] Fix rename constraint/rename index When a constraint is backed by an index like a unique constraint or a primary key constraint the constraint can be renamed by either ALTER TABLE RENAME CONSTRAINT or by ALTER INDEX RENAME. Depending on the command used to rename different internal metadata tables would be adjusted leading to corrupt metadata. This patch makes ALTER TABLE RENAME CONSTRAINT and ALTER INDEX RENAME adjust the same metadata tables. --- src/chunk_constraint.c | 52 +++++++++++++++++++++++++++++++ src/chunk_constraint.h | 2 ++ src/chunk_index.c | 68 +++++++++++++++++++++++++++++++++++++++++ src/chunk_index.h | 2 ++ test/expected/alter.out | 48 +++++++++++++++++++++++++++++ test/sql/alter.sql | 28 +++++++++++++++++ 6 files changed, 200 insertions(+) diff --git a/src/chunk_constraint.c b/src/chunk_constraint.c index f7ffcdd48..fa1daa9d1 100644 --- a/src/chunk_constraint.c +++ b/src/chunk_constraint.c @@ -853,6 +853,12 @@ chunk_constraint_rename_hypertable_from_tuple(TupleInfo *ti, const char *newname NameStr(new_chunk_constraint_name)); new_tuple = heap_modify_tuple(tuple, tupdesc, values, nulls, repl); + + ts_chunk_index_adjust_meta(chunk_id, + newname, + NameStr(*old_chunk_constraint_name), + NameStr(new_chunk_constraint_name)); + ts_catalog_update(ti->scanrel, new_tuple); heap_freetuple(new_tuple); @@ -860,6 +866,52 @@ chunk_constraint_rename_hypertable_from_tuple(TupleInfo *ti, const char *newname heap_freetuple(tuple); } +/* + * Adjust internal metadata after index/constraint rename + */ +int +ts_chunk_constraint_adjust_meta(int32 chunk_id, const char *ht_constraint_name, const char *oldname, + const char *newname) +{ + ScanIterator iterator = + ts_scan_iterator_create(CHUNK_CONSTRAINT, RowExclusiveLock, CurrentMemoryContext); + int count = 0; + + init_scan_by_chunk_id_constraint_name(&iterator, chunk_id, oldname); + + ts_scanner_foreach(&iterator) + { + bool nulls[Natts_chunk_constraint]; + bool repl[Natts_chunk_constraint] = { false }; + Datum values[Natts_chunk_constraint]; + bool should_free; + TupleInfo *ti = ts_scan_iterator_tuple_info(&iterator); + HeapTuple tuple = ts_scanner_fetch_heap_tuple(ti, false, &should_free); + HeapTuple new_tuple; + + heap_deform_tuple(tuple, ts_scanner_get_tupledesc(ti), values, nulls); + + values[AttrNumberGetAttrOffset(Anum_chunk_constraint_hypertable_constraint_name)] = + CStringGetDatum(ht_constraint_name); + repl[AttrNumberGetAttrOffset(Anum_chunk_constraint_hypertable_constraint_name)] = true; + values[AttrNumberGetAttrOffset(Anum_chunk_constraint_constraint_name)] = + CStringGetDatum(newname); + repl[AttrNumberGetAttrOffset(Anum_chunk_constraint_constraint_name)] = true; + + new_tuple = heap_modify_tuple(tuple, ts_scanner_get_tupledesc(ti), values, nulls, repl); + + ts_catalog_update(ti->scanrel, new_tuple); + heap_freetuple(new_tuple); + + if (should_free) + heap_freetuple(tuple); + + count++; + } + + return count; +} + int ts_chunk_constraint_rename_hypertable_constraint(int32 chunk_id, const char *oldname, const char *newname) diff --git a/src/chunk_constraint.h b/src/chunk_constraint.h index 1c85e7a01..03618429e 100644 --- a/src/chunk_constraint.h +++ b/src/chunk_constraint.h @@ -69,6 +69,8 @@ extern int ts_chunk_constraint_delete_by_constraint_name(int32 chunk_id, extern void ts_chunk_constraint_recreate(ChunkConstraint *cc, Oid chunk_oid); extern int ts_chunk_constraint_rename_hypertable_constraint(int32 chunk_id, const char *oldname, const char *newname); +extern int ts_chunk_constraint_adjust_meta(int32 chunk_id, const char *ht_constraint_name, + const char *oldname, const char *newname); extern char * ts_chunk_constraint_get_name_from_hypertable_constraint(Oid chunk_relid, diff --git a/src/chunk_index.c b/src/chunk_index.c index 85269a321..c7d555880 100644 --- a/src/chunk_index.c +++ b/src/chunk_index.c @@ -37,6 +37,7 @@ #include "hypertable_cache.h" #include "catalog.h" #include "scanner.h" +#include "scan_iterator.h" #include "chunk.h" static bool chunk_index_insert(int32 chunk_id, const char *chunk_index, int32 hypertable_id, @@ -888,6 +889,11 @@ chunk_index_tuple_rename(TupleInfo *ti, void *data) chunk_index_choose_name(NameStr(chunk->fd.table_name), info->newname, chunk_schemaoid); Oid chunk_indexrelid = get_relname_relid(NameStr(chunk_index->index_name), chunk_schemaoid); + ts_chunk_constraint_adjust_meta(chunk->fd.id, + info->newname, + NameStr(chunk_index->index_name), + chunk_index_name); + /* Update the metadata */ namestrcpy(&chunk_index->index_name, chunk_index_name); namestrcpy(&chunk_index->hypertable_index_name, info->newname); @@ -907,6 +913,68 @@ chunk_index_tuple_rename(TupleInfo *ti, void *data) return SCAN_DONE; } +static void +init_scan_by_chunk_id_index_name(ScanIterator *iterator, int32 chunk_id, const char *index_name) +{ + iterator->ctx.index = + catalog_get_index(ts_catalog_get(), CHUNK_INDEX, CHUNK_INDEX_CHUNK_ID_INDEX_NAME_IDX); + + ts_scan_iterator_scan_key_init(iterator, + Anum_chunk_index_chunk_id, + BTEqualStrategyNumber, + F_INT4EQ, + Int32GetDatum(chunk_id)); + ts_scan_iterator_scan_key_init(iterator, + Anum_chunk_index_index_name, + BTEqualStrategyNumber, + F_NAMEEQ, + CStringGetDatum(index_name)); +} + +/* + * Adjust internal metadata after index/constraint rename + */ +int +ts_chunk_index_adjust_meta(int32 chunk_id, const char *ht_index_name, const char *oldname, + const char *newname) +{ + ScanIterator iterator = + ts_scan_iterator_create(CHUNK_INDEX, RowExclusiveLock, CurrentMemoryContext); + int count = 0; + + init_scan_by_chunk_id_index_name(&iterator, chunk_id, oldname); + + ts_scanner_foreach(&iterator) + { + bool nulls[Natts_chunk_index]; + bool repl[Natts_chunk_index] = { false }; + Datum values[Natts_chunk_index]; + bool should_free; + TupleInfo *ti = ts_scan_iterator_tuple_info(&iterator); + HeapTuple tuple = ts_scanner_fetch_heap_tuple(ti, false, &should_free); + HeapTuple new_tuple; + + heap_deform_tuple(tuple, ts_scanner_get_tupledesc(ti), values, nulls); + + values[AttrNumberGetAttrOffset(Anum_chunk_index_hypertable_index_name)] = + CStringGetDatum(ht_index_name); + repl[AttrNumberGetAttrOffset(Anum_chunk_index_hypertable_index_name)] = true; + values[AttrNumberGetAttrOffset(Anum_chunk_index_index_name)] = CStringGetDatum(newname); + repl[AttrNumberGetAttrOffset(Anum_chunk_index_index_name)] = true; + + new_tuple = heap_modify_tuple(tuple, ts_scanner_get_tupledesc(ti), values, nulls, repl); + + ts_catalog_update(ti->scanrel, new_tuple); + heap_freetuple(new_tuple); + + if (should_free) + heap_freetuple(tuple); + + count++; + } + return count; +} + int ts_chunk_index_rename(Chunk *chunk, Oid chunk_indexrelid, const char *newname) { diff --git a/src/chunk_index.h b/src/chunk_index.h index c64aac74e..7751c70a3 100644 --- a/src/chunk_index.h +++ b/src/chunk_index.h @@ -45,6 +45,8 @@ extern void ts_chunk_index_delete_by_name(const char *schema, const char *index_ extern int ts_chunk_index_rename(Chunk *chunk, Oid chunk_indexrelid, const char *newname); extern int ts_chunk_index_rename_parent(Hypertable *ht, Oid hypertable_indexrelid, const char *newname); +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, const char *tablespace); extern void ts_chunk_index_create_from_constraint(int32 hypertable_id, Oid hypertable_constraint, diff --git a/test/expected/alter.out b/test/expected/alter.out index 22819cb31..347c2edd1 100644 --- a/test/expected/alter.out +++ b/test/expected/alter.out @@ -686,3 +686,51 @@ SELECT * from _timescaledb_catalog.chunk; (2 rows) DROP TABLE my_table; +-- test renaming unique constraints/indexes +CREATE TABLE t_hypertable ( id INTEGER NOT NULL, time TIMESTAMPTZ NOT NULL, value FLOAT NOT NULL CHECK (value > 0), UNIQUE(id, time)); +SELECT create_hypertable('t_hypertable', 'time'); + create_hypertable +---------------------------- + (13,public,t_hypertable,t) +(1 row) + +INSERT INTO t_hypertable AS h VALUES ( 1, '2020-01-01 00:00:00', 3.2) ON CONFLICT (id, time) DO UPDATE SET value = h.value + EXCLUDED.value; +INSERT INTO t_hypertable AS h VALUES ( 1, '2021-01-01 00:00:00', 3.2) ON CONFLICT (id, time) DO UPDATE SET value = h.value + EXCLUDED.value; +BEGIN; +ALTER INDEX t_hypertable_id_time_key RENAME TO t_new_constraint; +-- chunk_index and chunk_constraint should both have updated constraint names +SELECT hypertable_index_name, index_name from _timescaledb_catalog.chunk_index WHERE hypertable_index_name = 't_new_constraint' ORDER BY 1,2; + hypertable_index_name | index_name +-----------------------+------------------------------------- + t_new_constraint | _hyper_13_26_chunk_t_new_constraint + t_new_constraint | _hyper_13_27_chunk_t_new_constraint +(2 rows) + +SELECT hypertable_constraint_name, constraint_name from _timescaledb_catalog.chunk_constraint WHERE hypertable_constraint_name = 't_new_constraint' ORDER BY 1,2; + hypertable_constraint_name | constraint_name +----------------------------+------------------------------------- + t_new_constraint | _hyper_13_26_chunk_t_new_constraint + t_new_constraint | _hyper_13_27_chunk_t_new_constraint +(2 rows) + +INSERT INTO t_hypertable AS h VALUES ( 1, '2020-01-01 00:01:00', 3.2) ON CONFLICT (id, time) DO UPDATE SET value = h.value + EXCLUDED.value; +ROLLBACK; +BEGIN; +ALTER TABLE t_hypertable RENAME CONSTRAINT t_hypertable_id_time_key TO t_new_constraint; +-- chunk_index and chunk_constraint should both have updated constraint names +SELECT hypertable_index_name, index_name from _timescaledb_catalog.chunk_index WHERE hypertable_index_name = 't_new_constraint' ORDER BY 1,2; + hypertable_index_name | index_name +-----------------------+----------------------- + t_new_constraint | 26_5_t_new_constraint + t_new_constraint | 27_6_t_new_constraint +(2 rows) + +SELECT hypertable_constraint_name, constraint_name from _timescaledb_catalog.chunk_constraint WHERE hypertable_constraint_name = 't_new_constraint' ORDER BY 1,2; + hypertable_constraint_name | constraint_name +----------------------------+----------------------- + t_new_constraint | 26_5_t_new_constraint + t_new_constraint | 27_6_t_new_constraint +(2 rows) + +INSERT INTO t_hypertable AS h VALUES ( 1, '2020-01-01 00:01:00', 3.2) ON CONFLICT (id, time) DO UPDATE SET value = h.value + EXCLUDED.value; +ROLLBACK; diff --git a/test/sql/alter.sql b/test/sql/alter.sql index 70aeaeac9..ea76595b8 100644 --- a/test/sql/alter.sql +++ b/test/sql/alter.sql @@ -378,3 +378,31 @@ SELECT * from _timescaledb_catalog.hypertable; SELECT * from _timescaledb_catalog.chunk; DROP TABLE my_table; + +-- test renaming unique constraints/indexes +CREATE TABLE t_hypertable ( id INTEGER NOT NULL, time TIMESTAMPTZ NOT NULL, value FLOAT NOT NULL CHECK (value > 0), UNIQUE(id, time)); +SELECT create_hypertable('t_hypertable', 'time'); + +INSERT INTO t_hypertable AS h VALUES ( 1, '2020-01-01 00:00:00', 3.2) ON CONFLICT (id, time) DO UPDATE SET value = h.value + EXCLUDED.value; +INSERT INTO t_hypertable AS h VALUES ( 1, '2021-01-01 00:00:00', 3.2) ON CONFLICT (id, time) DO UPDATE SET value = h.value + EXCLUDED.value; + +BEGIN; +ALTER INDEX t_hypertable_id_time_key RENAME TO t_new_constraint; + +-- chunk_index and chunk_constraint should both have updated constraint names +SELECT hypertable_index_name, index_name from _timescaledb_catalog.chunk_index WHERE hypertable_index_name = 't_new_constraint' ORDER BY 1,2; +SELECT hypertable_constraint_name, constraint_name from _timescaledb_catalog.chunk_constraint WHERE hypertable_constraint_name = 't_new_constraint' ORDER BY 1,2; + +INSERT INTO t_hypertable AS h VALUES ( 1, '2020-01-01 00:01:00', 3.2) ON CONFLICT (id, time) DO UPDATE SET value = h.value + EXCLUDED.value; +ROLLBACK; + +BEGIN; +ALTER TABLE t_hypertable RENAME CONSTRAINT t_hypertable_id_time_key TO t_new_constraint; + +-- chunk_index and chunk_constraint should both have updated constraint names +SELECT hypertable_index_name, index_name from _timescaledb_catalog.chunk_index WHERE hypertable_index_name = 't_new_constraint' ORDER BY 1,2; +SELECT hypertable_constraint_name, constraint_name from _timescaledb_catalog.chunk_constraint WHERE hypertable_constraint_name = 't_new_constraint' ORDER BY 1,2; + +INSERT INTO t_hypertable AS h VALUES ( 1, '2020-01-01 00:01:00', 3.2) ON CONFLICT (id, time) DO UPDATE SET value = h.value + EXCLUDED.value; +ROLLBACK; +