Fix pg_depend entry to AMs after ALTER TABLE .. SET ACCESS METHOD
ALTER TABLE .. SET ACCESS METHOD was not registering a dependency to the new access method with the relation altered in its rewrite phase, making possible the drop of an access method even if there are relations that depend on it. During the rewrite, a temporary relation is created to build the new relation files before swapping the new and old files, and, while the temporary relation was registering a correct dependency to the new AM, the old relation did not do that. A dependency on the access method is added when the relation files are swapped, which is the point where pg_class is updated. Materialized views and tables use the same code path, hence both were impacted. Backpatch down to 15, where this command has been introduced. Reported-by: Alexander Lakhin Reviewed-by: Nathan Bossart, Andres Freund Discussion: https://postgr.es/m/18000-9145c25b1af475ca@postgresql.org Backpatch-through: 15
This commit is contained in:
parent
5f87a02125
commit
97d8910104
@ -1070,6 +1070,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
|
||||
relfilenumber2;
|
||||
RelFileNumber swaptemp;
|
||||
char swptmpchr;
|
||||
Oid relam1,
|
||||
relam2;
|
||||
|
||||
/* We need writable copies of both pg_class tuples. */
|
||||
relRelation = table_open(RelationRelationId, RowExclusiveLock);
|
||||
@ -1086,6 +1088,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
|
||||
|
||||
relfilenumber1 = relform1->relfilenode;
|
||||
relfilenumber2 = relform2->relfilenode;
|
||||
relam1 = relform1->relam;
|
||||
relam2 = relform2->relam;
|
||||
|
||||
if (RelFileNumberIsValid(relfilenumber1) &&
|
||||
RelFileNumberIsValid(relfilenumber2))
|
||||
@ -1257,6 +1261,31 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
|
||||
CacheInvalidateRelcacheByTuple(reltup2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that pg_class has been updated with its relevant information for
|
||||
* the swap, update the dependency of the relations to point to their new
|
||||
* table AM, if it has changed.
|
||||
*/
|
||||
if (relam1 != relam2)
|
||||
{
|
||||
if (changeDependencyFor(RelationRelationId,
|
||||
r1,
|
||||
AccessMethodRelationId,
|
||||
relam1,
|
||||
relam2) != 1)
|
||||
elog(ERROR, "failed to change access method dependency for relation \"%s.%s\"",
|
||||
get_namespace_name(get_rel_namespace(r1)),
|
||||
get_rel_name(r1));
|
||||
if (changeDependencyFor(RelationRelationId,
|
||||
r2,
|
||||
AccessMethodRelationId,
|
||||
relam2,
|
||||
relam1) != 1)
|
||||
elog(ERROR, "failed to change access method dependency for relation \"%s.%s\"",
|
||||
get_namespace_name(get_rel_namespace(r2)),
|
||||
get_rel_name(r2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Post alter hook for modified relations. The change to r2 is always
|
||||
* internal, but r1 depends on the invocation context.
|
||||
|
@ -240,6 +240,35 @@ SELECT amname FROM pg_class c, pg_am am
|
||||
heap
|
||||
(1 row)
|
||||
|
||||
-- Switching to heap2 adds new dependency entry to the AM.
|
||||
ALTER TABLE heaptable SET ACCESS METHOD heap2;
|
||||
SELECT pg_describe_object(classid, objid, objsubid) as obj,
|
||||
pg_describe_object(refclassid, refobjid, refobjsubid) as objref,
|
||||
deptype
|
||||
FROM pg_depend
|
||||
WHERE classid = 'pg_class'::regclass AND
|
||||
objid = 'heaptable'::regclass
|
||||
ORDER BY 1, 2;
|
||||
obj | objref | deptype
|
||||
-----------------+---------------------+---------
|
||||
table heaptable | access method heap2 | n
|
||||
table heaptable | schema public | n
|
||||
(2 rows)
|
||||
|
||||
-- Switching to heap should not have a dependency entry to the AM.
|
||||
ALTER TABLE heaptable SET ACCESS METHOD heap;
|
||||
SELECT pg_describe_object(classid, objid, objsubid) as obj,
|
||||
pg_describe_object(refclassid, refobjid, refobjsubid) as objref,
|
||||
deptype
|
||||
FROM pg_depend
|
||||
WHERE classid = 'pg_class'::regclass AND
|
||||
objid = 'heaptable'::regclass
|
||||
ORDER BY 1, 2;
|
||||
obj | objref | deptype
|
||||
-----------------+---------------+---------
|
||||
table heaptable | schema public | n
|
||||
(1 row)
|
||||
|
||||
ALTER TABLE heaptable SET ACCESS METHOD heap2;
|
||||
SELECT amname FROM pg_class c, pg_am am
|
||||
WHERE c.relam = am.oid AND c.oid = 'heaptable'::regclass;
|
||||
|
@ -166,6 +166,24 @@ CREATE TABLE heaptable USING heap AS
|
||||
SELECT a, repeat(a::text, 100) FROM generate_series(1,9) AS a;
|
||||
SELECT amname FROM pg_class c, pg_am am
|
||||
WHERE c.relam = am.oid AND c.oid = 'heaptable'::regclass;
|
||||
-- Switching to heap2 adds new dependency entry to the AM.
|
||||
ALTER TABLE heaptable SET ACCESS METHOD heap2;
|
||||
SELECT pg_describe_object(classid, objid, objsubid) as obj,
|
||||
pg_describe_object(refclassid, refobjid, refobjsubid) as objref,
|
||||
deptype
|
||||
FROM pg_depend
|
||||
WHERE classid = 'pg_class'::regclass AND
|
||||
objid = 'heaptable'::regclass
|
||||
ORDER BY 1, 2;
|
||||
-- Switching to heap should not have a dependency entry to the AM.
|
||||
ALTER TABLE heaptable SET ACCESS METHOD heap;
|
||||
SELECT pg_describe_object(classid, objid, objsubid) as obj,
|
||||
pg_describe_object(refclassid, refobjid, refobjsubid) as objref,
|
||||
deptype
|
||||
FROM pg_depend
|
||||
WHERE classid = 'pg_class'::regclass AND
|
||||
objid = 'heaptable'::regclass
|
||||
ORDER BY 1, 2;
|
||||
ALTER TABLE heaptable SET ACCESS METHOD heap2;
|
||||
SELECT amname FROM pg_class c, pg_am am
|
||||
WHERE c.relam = am.oid AND c.oid = 'heaptable'::regclass;
|
||||
|
Loading…
x
Reference in New Issue
Block a user