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 @@
#include "parser/parse_relation.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/predicate.h"
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
......@@ -1657,6 +1658,14 @@ heap_drop_with_catalog(Oid relid)
*/
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.
*/
......
......@@ -54,6 +54,7 @@
#include "parser/parser.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
......@@ -1311,6 +1312,12 @@ index_drop(Oid indexId)
*/
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
*/
......@@ -2799,6 +2806,12 @@ reindex_index(Oid indexId, bool skip_constraint_checks)
*/
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();
{
/* Suppress use of the target index while rebuilding it */
......
......@@ -39,6 +39,7 @@
#include "optimizer/planner.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/acl.h"
......@@ -385,6 +386,14 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose,
if (OidIsValid(indexOid))
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(OldHeap, indexOid, freeze_min_age, freeze_table_age,
verbose);
......
......@@ -70,6 +70,7 @@
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/lock.h"
#include "storage/predicate.h"
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
......@@ -1039,6 +1040,14 @@ ExecuteTruncate(TruncateStmt *stmt)
Oid heap_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.
*
......@@ -3529,6 +3538,16 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
(errmsg("verifying table \"%s\"",
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);
/*
......
This diff is collapsed.
......@@ -49,11 +49,13 @@ extern void PredicateLockPage(const Relation relation, const BlockNumber blkno);
extern void PredicateLockTuple(const Relation relation, const HeapTuple tuple);
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 TransferPredicateLocksToHeapRelation(const Relation relation);
extern void ReleasePredicateLocks(const bool isCommit);
/* conflict detection (may also trigger rollback) */
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 CheckTableForSerializableConflictIn(const Relation relation);
/* final rollback checking */
extern void PreCommit_CheckForSerializationFailure(void);
......
......@@ -273,9 +273,7 @@ typedef struct PREDICATELOCKTARGETTAG
* up the targets as the related tuples are pruned or vacuumed, we check the
* xmin on access. This should be far less costly.
*/
typedef struct PREDICATELOCKTARGET PREDICATELOCKTARGET;
struct PREDICATELOCKTARGET
typedef struct PREDICATELOCKTARGET
{
/* hash key */
PREDICATELOCKTARGETTAG tag; /* unique identifier of lockable object */
......@@ -283,7 +281,7 @@ struct PREDICATELOCKTARGET
/* data */
SHM_QUEUE predicateLocks; /* list of PREDICATELOCK objects assoc. with
* 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