Commit 8f9622bb authored by Heikki Linnakangas's avatar Heikki Linnakangas

Make DDL operations play nicely with Serializable Snapshot Isolation.

Truncating or dropping a table is treated like deletion of all tuples, and
check for conflicts accordingly. If a table is clustered or rewritten by
ALTER TABLE, all predicate locks on the heap are promoted to relation-level
locks, because the tuple or page ids of any existing tuples will change and
won't be valid after rewriting the table. Arguably ALTER TABLE should be
treated like a mass-UPDATE of every row, but if you e.g change the datatype
of a column, you could also argue that it's just a change to the physical
layout, not a logical change. Reindexing promotes all locks on the index to
relation-level lock on the heap.

Kevin Grittner, with a lot of cosmetic changes by me.
parent 16925c1e
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
#include "parser/parse_relation.h" #include "parser/parse_relation.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h" #include "storage/freespace.h"
#include "storage/predicate.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/acl.h" #include "utils/acl.h"
#include "utils/builtins.h" #include "utils/builtins.h"
...@@ -1657,6 +1658,14 @@ heap_drop_with_catalog(Oid relid) ...@@ -1657,6 +1658,14 @@ heap_drop_with_catalog(Oid relid)
*/ */
CheckTableNotInUse(rel, "DROP TABLE"); CheckTableNotInUse(rel, "DROP TABLE");
/*
* This effectively deletes all rows in the table, and may be done in a
* serializable transaction. In that case we must record a rw-conflict in
* to this transaction from each transaction holding a predicate lock on
* the table.
*/
CheckTableForSerializableConflictIn(rel);
/* /*
* Delete pg_foreign_table tuple first. * Delete pg_foreign_table tuple first.
*/ */
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
#include "parser/parser.h" #include "parser/parser.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "storage/predicate.h"
#include "storage/procarray.h" #include "storage/procarray.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/builtins.h" #include "utils/builtins.h"
...@@ -1311,6 +1312,12 @@ index_drop(Oid indexId) ...@@ -1311,6 +1312,12 @@ index_drop(Oid indexId)
*/ */
CheckTableNotInUse(userIndexRelation, "DROP INDEX"); CheckTableNotInUse(userIndexRelation, "DROP INDEX");
/*
* All predicate locks on the index are about to be made invalid. Promote
* them to relation locks on the heap.
*/
TransferPredicateLocksToHeapRelation(userIndexRelation);
/* /*
* Schedule physical removal of the files * Schedule physical removal of the files
*/ */
...@@ -2799,6 +2806,12 @@ reindex_index(Oid indexId, bool skip_constraint_checks) ...@@ -2799,6 +2806,12 @@ reindex_index(Oid indexId, bool skip_constraint_checks)
*/ */
CheckTableNotInUse(iRel, "REINDEX INDEX"); CheckTableNotInUse(iRel, "REINDEX INDEX");
/*
* All predicate locks on the index are about to be made invalid. Promote
* them to relation locks on the heap.
*/
TransferPredicateLocksToHeapRelation(iRel);
PG_TRY(); PG_TRY();
{ {
/* Suppress use of the target index while rebuilding it */ /* Suppress use of the target index while rebuilding it */
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include "optimizer/planner.h" #include "optimizer/planner.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "storage/predicate.h"
#include "storage/procarray.h" #include "storage/procarray.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/acl.h" #include "utils/acl.h"
...@@ -385,6 +386,14 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose, ...@@ -385,6 +386,14 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose,
if (OidIsValid(indexOid)) if (OidIsValid(indexOid))
check_index_is_clusterable(OldHeap, indexOid, recheck, AccessExclusiveLock); check_index_is_clusterable(OldHeap, indexOid, recheck, AccessExclusiveLock);
/*
* All predicate locks on the tuples or pages are about to be made
* invalid, because we move tuples around. Promote them to relation
* locks. Predicate locks on indexes will be promoted when they are
* reindexed.
*/
TransferPredicateLocksToHeapRelation(OldHeap);
/* rebuild_relation does all the dirty work */ /* rebuild_relation does all the dirty work */
rebuild_relation(OldHeap, indexOid, freeze_min_age, freeze_table_age, rebuild_relation(OldHeap, indexOid, freeze_min_age, freeze_table_age,
verbose); verbose);
......
...@@ -70,6 +70,7 @@ ...@@ -70,6 +70,7 @@
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "storage/lock.h" #include "storage/lock.h"
#include "storage/predicate.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/acl.h" #include "utils/acl.h"
#include "utils/builtins.h" #include "utils/builtins.h"
...@@ -1039,6 +1040,14 @@ ExecuteTruncate(TruncateStmt *stmt) ...@@ -1039,6 +1040,14 @@ ExecuteTruncate(TruncateStmt *stmt)
Oid heap_relid; Oid heap_relid;
Oid toast_relid; Oid toast_relid;
/*
* This effectively deletes all rows in the table, and may be done
* in a serializable transaction. In that case we must record a
* rw-conflict in to this transaction from each transaction
* holding a predicate lock on the table.
*/
CheckTableForSerializableConflictIn(rel);
/* /*
* Need the full transaction-safe pushups. * Need the full transaction-safe pushups.
* *
...@@ -3529,6 +3538,16 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) ...@@ -3529,6 +3538,16 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
(errmsg("verifying table \"%s\"", (errmsg("verifying table \"%s\"",
RelationGetRelationName(oldrel)))); RelationGetRelationName(oldrel))));
if (newrel)
{
/*
* All predicate locks on the tuples or pages are about to be made
* invalid, because we move tuples around. Promote them to
* relation locks.
*/
TransferPredicateLocksToHeapRelation(oldrel);
}
econtext = GetPerTupleExprContext(estate); econtext = GetPerTupleExprContext(estate);
/* /*
......
This diff is collapsed.
...@@ -49,11 +49,13 @@ extern void PredicateLockPage(const Relation relation, const BlockNumber blkno); ...@@ -49,11 +49,13 @@ extern void PredicateLockPage(const Relation relation, const BlockNumber blkno);
extern void PredicateLockTuple(const Relation relation, const HeapTuple tuple); extern void PredicateLockTuple(const Relation relation, const HeapTuple tuple);
extern void PredicateLockPageSplit(const Relation relation, const BlockNumber oldblkno, const BlockNumber newblkno); extern void PredicateLockPageSplit(const Relation relation, const BlockNumber oldblkno, const BlockNumber newblkno);
extern void PredicateLockPageCombine(const Relation relation, const BlockNumber oldblkno, const BlockNumber newblkno); extern void PredicateLockPageCombine(const Relation relation, const BlockNumber oldblkno, const BlockNumber newblkno);
extern void TransferPredicateLocksToHeapRelation(const Relation relation);
extern void ReleasePredicateLocks(const bool isCommit); extern void ReleasePredicateLocks(const bool isCommit);
/* conflict detection (may also trigger rollback) */ /* conflict detection (may also trigger rollback) */
extern void CheckForSerializableConflictOut(const bool valid, const Relation relation, const HeapTuple tuple, const Buffer buffer); extern void CheckForSerializableConflictOut(const bool valid, const Relation relation, const HeapTuple tuple, const Buffer buffer);
extern void CheckForSerializableConflictIn(const Relation relation, const HeapTuple tuple, const Buffer buffer); extern void CheckForSerializableConflictIn(const Relation relation, const HeapTuple tuple, const Buffer buffer);
extern void CheckTableForSerializableConflictIn(const Relation relation);
/* final rollback checking */ /* final rollback checking */
extern void PreCommit_CheckForSerializationFailure(void); extern void PreCommit_CheckForSerializationFailure(void);
......
...@@ -273,9 +273,7 @@ typedef struct PREDICATELOCKTARGETTAG ...@@ -273,9 +273,7 @@ typedef struct PREDICATELOCKTARGETTAG
* up the targets as the related tuples are pruned or vacuumed, we check the * up the targets as the related tuples are pruned or vacuumed, we check the
* xmin on access. This should be far less costly. * xmin on access. This should be far less costly.
*/ */
typedef struct PREDICATELOCKTARGET PREDICATELOCKTARGET; typedef struct PREDICATELOCKTARGET
struct PREDICATELOCKTARGET
{ {
/* hash key */ /* hash key */
PREDICATELOCKTARGETTAG tag; /* unique identifier of lockable object */ PREDICATELOCKTARGETTAG tag; /* unique identifier of lockable object */
...@@ -283,7 +281,7 @@ struct PREDICATELOCKTARGET ...@@ -283,7 +281,7 @@ struct PREDICATELOCKTARGET
/* data */ /* data */
SHM_QUEUE predicateLocks; /* list of PREDICATELOCK objects assoc. with SHM_QUEUE predicateLocks; /* list of PREDICATELOCK objects assoc. with
* predicate lock target */ * predicate lock target */
}; } PREDICATELOCKTARGET;
/* /*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment