diff --git a/contrib/test_decoding/expected/toast.out b/contrib/test_decoding/expected/toast.out index 75c4d22d80..cd03e9d50a 100644 --- a/contrib/test_decoding/expected/toast.out +++ b/contrib/test_decoding/expected/toast.out @@ -360,6 +360,28 @@ WHERE data NOT LIKE '%INSERT: %'; COMMIT (4 rows) +/* + * Test decoding relation rewrite with toast. The insert into tbl2 within the + * same transaction is there to check that there is no remaining toast_hash not + * being reset. + */ +CREATE TABLE tbl1 (a INT, b TEXT); +CREATE TABLE tbl2 (a INT); +ALTER TABLE tbl1 ALTER COLUMN b SET STORAGE EXTERNAL; +BEGIN; +INSERT INTO tbl1 VALUES(1, repeat('a', 4000)) ; +ALTER TABLE tbl1 ADD COLUMN id serial primary key; +INSERT INTO tbl2 VALUES(1); +commit; +SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); + substr +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + BEGIN + table public.tbl1: INSERT: a[integer]:1 b[text]:'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + table public.tbl2: INSERT: a[integer]:1 + COMMIT +(4 rows) + SELECT pg_drop_replication_slot('regression_slot'); pg_drop_replication_slot -------------------------- diff --git a/contrib/test_decoding/sql/toast.sql b/contrib/test_decoding/sql/toast.sql index 016c3ab784..d1c560a174 100644 --- a/contrib/test_decoding/sql/toast.sql +++ b/contrib/test_decoding/sql/toast.sql @@ -308,4 +308,20 @@ DROP TABLE toasted_several; SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1') WHERE data NOT LIKE '%INSERT: %'; + +/* + * Test decoding relation rewrite with toast. The insert into tbl2 within the + * same transaction is there to check that there is no remaining toast_hash not + * being reset. + */ +CREATE TABLE tbl1 (a INT, b TEXT); +CREATE TABLE tbl2 (a INT); +ALTER TABLE tbl1 ALTER COLUMN b SET STORAGE EXTERNAL; +BEGIN; +INSERT INTO tbl1 VALUES(1, repeat('a', 4000)) ; +ALTER TABLE tbl1 ADD COLUMN id serial primary key; +INSERT INTO tbl2 VALUES(1); +commit; +SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); + SELECT pg_drop_replication_slot('regression_slot'); diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 147b5abc19..0db90c2011 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -36,9 +36,11 @@ #include "utils/syscache.h" static void CheckAndCreateToastTable(Oid relOid, Datum reloptions, - LOCKMODE lockmode, bool check); + LOCKMODE lockmode, bool check, + Oid OIDOldToast); static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, - Datum reloptions, LOCKMODE lockmode, bool check); + Datum reloptions, LOCKMODE lockmode, bool check, + Oid OIDOldToast); static bool needs_toast_table(Relation rel); @@ -57,30 +59,34 @@ static bool needs_toast_table(Relation rel); void AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode) { - CheckAndCreateToastTable(relOid, reloptions, lockmode, true); + CheckAndCreateToastTable(relOid, reloptions, lockmode, true, InvalidOid); } void -NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode) +NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, + Oid OIDOldToast) { - CheckAndCreateToastTable(relOid, reloptions, lockmode, false); + CheckAndCreateToastTable(relOid, reloptions, lockmode, false, OIDOldToast); } void NewRelationCreateToastTable(Oid relOid, Datum reloptions) { - CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false); + CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false, + InvalidOid); } static void -CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, bool check) +CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, + bool check, Oid OIDOldToast) { Relation rel; rel = table_open(relOid, lockmode); /* create_toast_table does all the work */ - (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check); + (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, + check, OIDOldToast); table_close(rel, NoLock); } @@ -104,7 +110,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid) /* create_toast_table does all the work */ if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0, - AccessExclusiveLock, false)) + AccessExclusiveLock, false, InvalidOid)) elog(ERROR, "\"%s\" does not require a toast table", relName); @@ -121,7 +127,8 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid) */ static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, - Datum reloptions, LOCKMODE lockmode, bool check) + Datum reloptions, LOCKMODE lockmode, bool check, + Oid OIDOldToast) { Oid relOid = RelationGetRelid(rel); HeapTuple reltup; @@ -258,7 +265,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, false, true, true, - InvalidOid, + OIDOldToast, NULL); Assert(toast_relid != InvalidOid); diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 39185cc5b9..9d22f648a8 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -735,7 +735,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, if (isNull) reloptions = (Datum) 0; - NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode); + NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode, toastid); ReleaseSysCache(tuple); } @@ -1526,6 +1526,14 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, RenameRelationInternal(toastidx, NewToastName, true, true); + + /* + * Reset the relrewrite for the toast. The command-counter + * increment is required here as we are about to update + * the tuple that is updated as part of RenameRelationInternal. + */ + CommandCounterIncrement(); + ResetRelRewrite(newrel->rd_rel->reltoastrelid); } relation_close(newrel, NoLock); } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index a03077139d..dbee6ae199 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -3849,6 +3849,37 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bo relation_close(targetrelation, NoLock); } +/* + * ResetRelRewrite - reset relrewrite + */ +void +ResetRelRewrite(Oid myrelid) +{ + Relation relrelation; /* for RELATION relation */ + HeapTuple reltup; + Form_pg_class relform; + + /* + * Find relation's pg_class tuple. + */ + relrelation = table_open(RelationRelationId, RowExclusiveLock); + + reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid)); + if (!HeapTupleIsValid(reltup)) /* shouldn't happen */ + elog(ERROR, "cache lookup failed for relation %u", myrelid); + relform = (Form_pg_class) GETSTRUCT(reltup); + + /* + * Update pg_class tuple. + */ + relform->relrewrite = InvalidOid; + + CatalogTupleUpdate(relrelation, &reltup->t_self, reltup); + + heap_freetuple(reltup); + table_close(relrelation, RowExclusiveLock); +} + /* * Disallow ALTER TABLE (and similar commands) when the current backend has * any open reference to the target table besides the one just acquired by diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h index f3fa85f64e..7067c4c61b 100644 --- a/src/include/catalog/toasting.h +++ b/src/include/catalog/toasting.h @@ -21,7 +21,7 @@ */ extern void NewRelationCreateToastTable(Oid relOid, Datum reloptions); extern void NewHeapCreateToastTable(Oid relOid, Datum reloptions, - LOCKMODE lockmode); + LOCKMODE lockmode, Oid OIDOldToast); extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode); extern void BootstrapToastTable(char *relName, diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index 14f4b4882f..336549cc5f 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -78,6 +78,8 @@ extern void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index); +extern void ResetRelRewrite(Oid myrelid); + extern void find_composite_type_dependencies(Oid typeOid, Relation origRelation, const char *origTypeName);