mirror of
https://github.com/postgres/postgres.git
synced 2025-10-27 00:12:01 +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:
@@ -946,25 +946,18 @@ EventTriggerOnLogin(void)
|
||||
{
|
||||
Relation pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
|
||||
HeapTuple tuple;
|
||||
void *state;
|
||||
Form_pg_database db;
|
||||
ScanKeyData key[1];
|
||||
SysScanDesc scan;
|
||||
|
||||
/*
|
||||
* Get the pg_database tuple to scribble on. Note that this does
|
||||
* not directly rely on the syscache to avoid issues with
|
||||
* flattened toast values for the in-place update.
|
||||
*/
|
||||
/* Fetch a copy of the tuple to scribble on */
|
||||
ScanKeyInit(&key[0],
|
||||
Anum_pg_database_oid,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(MyDatabaseId));
|
||||
|
||||
scan = systable_beginscan(pg_db, DatabaseOidIndexId, true,
|
||||
NULL, 1, key);
|
||||
tuple = systable_getnext(scan);
|
||||
tuple = heap_copytuple(tuple);
|
||||
systable_endscan(scan);
|
||||
systable_inplace_update_begin(pg_db, DatabaseOidIndexId, true,
|
||||
NULL, 1, key, &tuple, &state);
|
||||
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
|
||||
@@ -980,13 +973,15 @@ EventTriggerOnLogin(void)
|
||||
* that avoids possible waiting on the row-level lock. Second,
|
||||
* that avoids dealing with TOAST.
|
||||
*
|
||||
* It's known that changes made by heap_inplace_update() may
|
||||
* be lost due to concurrent normal updates. However, we are
|
||||
* OK with that. The subsequent connections will still have a
|
||||
* chance to set "dathasloginevt" to false.
|
||||
* Changes made by inplace update may be lost due to
|
||||
* concurrent normal updates; see inplace-inval.spec. However,
|
||||
* we are OK with that. The subsequent connections will still
|
||||
* have a chance to set "dathasloginevt" to false.
|
||||
*/
|
||||
heap_inplace_update(pg_db, tuple);
|
||||
systable_inplace_update_finish(state, tuple);
|
||||
}
|
||||
else
|
||||
systable_inplace_update_cancel(state);
|
||||
table_close(pg_db, RowExclusiveLock);
|
||||
heap_freetuple(tuple);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user