mirror of
https://github.com/postgres/postgres.git
synced 2025-11-22 12:22:45 +03:00
Fix data loss at inplace update after heap_update().
As previously-added tests demonstrated, heap_inplace_update() could instead update an unrelated tuple of the same catalog. It could lose the update. Losing relhasindex=t was a source of index corruption. Inplace-updating commands like VACUUM will now wait for heap_update() commands like GRANT TABLE and GRANT DATABASE. That isn't ideal, but a long-running GRANT already hurts VACUUM progress more just by keeping an XID running. The VACUUM will behave like a DELETE or UPDATE waiting for the uncommitted change. For implementation details, start at the systable_inplace_update_begin() header comment and README.tuplock. Back-patch to v12 (all supported versions). In back branches, retain a deprecated heap_inplace_update(), for extensions. Reported by Smolkin Grigory. Reviewed by Nitin Motiani, (in earlier versions) Heikki Linnakangas, and (in earlier versions) Alexander Lakhin. Discussion: https://postgr.es/m/CAMp+ueZQz3yDk7qg42hk6-9gxniYbp-=bG2mgqecErqR5gGGOA@mail.gmail.com
This commit is contained in:
@@ -2792,7 +2792,9 @@ index_update_stats(Relation rel,
|
||||
{
|
||||
Oid relid = RelationGetRelid(rel);
|
||||
Relation pg_class;
|
||||
ScanKeyData key[1];
|
||||
HeapTuple tuple;
|
||||
void *state;
|
||||
Form_pg_class rd_rel;
|
||||
bool dirty;
|
||||
|
||||
@@ -2826,33 +2828,12 @@ index_update_stats(Relation rel,
|
||||
|
||||
pg_class = table_open(RelationRelationId, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* Make a copy of the tuple to update. Normally we use the syscache, but
|
||||
* we can't rely on that during bootstrap or while reindexing pg_class
|
||||
* itself.
|
||||
*/
|
||||
if (IsBootstrapProcessingMode() ||
|
||||
ReindexIsProcessingHeap(RelationRelationId))
|
||||
{
|
||||
/* don't assume syscache will work */
|
||||
TableScanDesc pg_class_scan;
|
||||
ScanKeyData key[1];
|
||||
|
||||
ScanKeyInit(&key[0],
|
||||
Anum_pg_class_oid,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(relid));
|
||||
|
||||
pg_class_scan = table_beginscan_catalog(pg_class, 1, key);
|
||||
tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
|
||||
tuple = heap_copytuple(tuple);
|
||||
table_endscan(pg_class_scan);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* normal case, use syscache */
|
||||
tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
|
||||
}
|
||||
ScanKeyInit(&key[0],
|
||||
Anum_pg_class_oid,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(relid));
|
||||
systable_inplace_update_begin(pg_class, ClassOidIndexId, true, NULL,
|
||||
1, key, &tuple, &state);
|
||||
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "could not find tuple for relation %u", relid);
|
||||
@@ -2915,11 +2896,12 @@ index_update_stats(Relation rel,
|
||||
*/
|
||||
if (dirty)
|
||||
{
|
||||
heap_inplace_update(pg_class, tuple);
|
||||
systable_inplace_update_finish(state, tuple);
|
||||
/* the above sends a cache inval message */
|
||||
}
|
||||
else
|
||||
{
|
||||
systable_inplace_update_cancel(state);
|
||||
/* no need to change tuple, but force relcache inval anyway */
|
||||
CacheInvalidateRelcacheByTuple(tuple);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user