ALTER TYPE 7: avoid index rebuilds/FK validations

From: Noah Misch <noah(at)leadboat(dot)com>
To: pgsql-hackers(at)postgresql(dot)org
Subject: ALTER TYPE 7: avoid index rebuilds/FK validations
Date: 2011-01-13 23:01:24
Views: Raw Message | Whole Thread | Download mbox | Resend email
Lists: pgsql-hackers

When the earlier patches in this series allow ALTER TABLE ... ALTER COLUMN
... TYPE to skip the table rewrite, it still rebuilds indexes and rechecks
constraints depending on that column. With this patch, we'll skip an index
rebuild when the change does not cross operator families and the index has no
expression column or predicate. Also, we'll skip FOREIGN KEY validation
unconditionally (create-time foreign key compatibility checks still apply).
These policies have some formal hazards detailed in the code comments, but I
believe those hazards do not extend to any built-in types or credible
user-defined types. We may wish to remove the formal gap by extending the
contract of B-tree operator families: something along the lines that casts
between types in the family must preserve equality within the family.

The standing code handled index/constraint dependencies of changing columns by
extracting the SQL definition using pg_get_indexdef or pg_get_constraintdef,
dropping the object, and recreating it afresh. To implement this optimization
for indexes and index-backed constraints, we need to update the index definition
without perturbing its storage. I saw two major ways to do this and have
attached implementations of each, both having the same user-facing semantics.
I'd appreciate feedback on which is preferable. The first skips the drop and
updates pg_index.indclass, pg_attribute, and pg_constraint.conexclop. The
second keeps the drop and create, then resurrects the old relfilenode and
assigns it to the new object. The second strategy is significantly simpler and
smaller, but it seems less-like anything else we do. As a further variation on
the second approach, I considered drilling holes through the performDeletion()
and DefineIndex() stacks to avoid the drop-and-later-preserve dynamic, but that
ultimately appeared uglier.

ATPostAlterTypeCleanup has this comment:

* Re-parse the index and constraint definitions, and attach them to the
* appropriate work queue entries. We do this before dropping because in
* the case of a FOREIGN KEY constraint, we might not yet have exclusive
* lock on the table the constraint is attached to, and we need to get
* that before dropping. It's safe because the parser won't actually look
* at the catalogs to detect the existing entry.

Is the second sentence true? I don't think so, so I deleted it for now. Here
is the sequence of lock requests against the table possessing the FOREIGN KEY
constraint when we alter the parent/upstream column:

transformAlterTableStmt - ShareRowExclusiveLock
ATPostAlterTypeParse - lockmode of original ALTER TABLE
RemoveTriggerById() for update trigger - ShareRowExclusiveLock
RemoveTriggerById() for insert trigger - ShareRowExclusiveLock
RemoveConstraintById() - AccessExclusiveLock
CreateTrigger() for insert trigger - ShareRowExclusiveLock
CreateTrigger() for update trigger - ShareRowExclusiveLock
RI_Initial_Check() - AccessShareLock (3x)


Attachment Content-Type Size
at7-index-opfamily.patch text/plain 112.0 KB
at7-alt-index-opfamily.patch text/plain 91.7 KB

Browse pgsql-hackers by date

  From Date Subject
Next Message Josh Berkus 2011-01-13 23:02:12 Re: auto-sizing wal_buffers
Previous Message Florian Pflug 2011-01-13 22:59:54 Re: kill -KILL: What happens?