Skip to content

Commit 6e35939

Browse files
committed
Change rewriter/planner/executor/plancache to depend on RTE rellockmode.
Instead of recomputing the required lock levels in all these places, just use what commit fdba460 made the parser store in the RTE fields. This already simplifies the code measurably in these places, and follow-on changes will remove a bunch of no-longer-needed infrastructure. In a few cases, this change causes us to acquire a higher lock level than we did before. This is OK primarily because said higher lock level should've been acquired already at query parse time; thus, we're saving a useless extra trip through the shared lock manager to acquire a lesser lock alongside the original lock. The only known exception to this is that re-execution of a previously planned SELECT FOR UPDATE/SHARE query, for a table that uses ROW_MARK_REFERENCE or ROW_MARK_COPY methods, might have gotten only AccessShareLock before. Now it will get RowShareLock like the first execution did, which seems fine. While there's more to do, push it in this state anyway, to let the buildfarm help verify that nothing bad happened. Amit Langote, reviewed by David Rowley and Jesper Pedersen, and whacked around a bit more by me Discussion: https://postgr.es/m/468c85d9-540e-66a2-1dde-fec2b741e688@lab.ntt.co.jp
1 parent cc2905e commit 6e35939

File tree

5 files changed

+27
-115
lines changed

5 files changed

+27
-115
lines changed

src/backend/executor/execMain.c

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -970,26 +970,16 @@ InitPlan(QueryDesc *queryDesc, int eflags)
970970

971971
/* get relation's OID (will produce InvalidOid if subquery) */
972972
relid = getrelid(rc->rti, rangeTable);
973-
rellockmode = rt_fetch(rc->rti, rangeTable)->rellockmode;
974973

975-
/*
976-
* If you change the conditions under which rel locks are acquired
977-
* here, be sure to adjust ExecOpenScanRelation to match.
978-
*/
979974
switch (rc->markType)
980975
{
981976
case ROW_MARK_EXCLUSIVE:
982977
case ROW_MARK_NOKEYEXCLUSIVE:
983978
case ROW_MARK_SHARE:
984979
case ROW_MARK_KEYSHARE:
985-
Assert(rellockmode == RowShareLock);
986-
relation = heap_open(relid, RowShareLock);
987-
break;
988980
case ROW_MARK_REFERENCE:
989-
/* RTE might be a query target table */
990-
Assert(rellockmode == AccessShareLock ||
991-
rellockmode == RowExclusiveLock);
992-
relation = heap_open(relid, AccessShareLock);
981+
rellockmode = rt_fetch(rc->rti, rangeTable)->rellockmode;
982+
relation = heap_open(relid, rellockmode);
993983
break;
994984
case ROW_MARK_COPY:
995985
/* no physical table access is required */

src/backend/executor/execUtils.c

Lines changed: 8 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -641,10 +641,6 @@ ExecRelationIsTargetRelation(EState *estate, Index scanrelid)
641641
*
642642
* Open the heap relation to be scanned by a base-level scan plan node.
643643
* This should be called during the node's ExecInit routine.
644-
*
645-
* By default, this acquires AccessShareLock on the relation. However,
646-
* if the relation was already locked by InitPlan, we don't need to acquire
647-
* any additional lock. This saves trips to the shared lock manager.
648644
* ----------------------------------------------------------------
649645
*/
650646
Relation
@@ -654,37 +650,9 @@ ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
654650
Oid reloid;
655651
LOCKMODE lockmode;
656652

657-
/*
658-
* Determine the lock type we need. First, scan to see if target relation
659-
* is a result relation. If not, check if it's a FOR UPDATE/FOR SHARE
660-
* relation.
661-
*
662-
* Note: we may have already gotten the desired lock type, but for now
663-
* don't try to optimize; this logic is going away soon anyhow.
664-
*/
665-
lockmode = AccessShareLock;
666-
if (ExecRelationIsTargetRelation(estate, scanrelid))
667-
lockmode = RowExclusiveLock;
668-
else
669-
{
670-
/* Keep this check in sync with InitPlan! */
671-
ExecRowMark *erm = ExecFindRowMark(estate, scanrelid, true);
672-
673-
if (erm != NULL)
674-
{
675-
if (erm->markType == ROW_MARK_REFERENCE ||
676-
erm->markType == ROW_MARK_COPY)
677-
lockmode = AccessShareLock;
678-
else
679-
lockmode = RowShareLock;
680-
}
681-
}
682-
683-
/* lockmode per above logic must not be more than we previously acquired */
684-
Assert(lockmode <= rt_fetch(scanrelid, estate->es_range_table)->rellockmode);
685-
686653
/* Open the relation and acquire lock as needed */
687654
reloid = getrelid(scanrelid, estate->es_range_table);
655+
lockmode = rt_fetch(scanrelid, estate->es_range_table)->rellockmode;
688656
rel = heap_open(reloid, lockmode);
689657

690658
/*
@@ -912,6 +880,7 @@ ExecLockNonLeafAppendTables(List *partitioned_rels, EState *estate)
912880
if (!is_result_rel)
913881
{
914882
PlanRowMark *rc = NULL;
883+
LOCKMODE lockmode;
915884

916885
foreach(l, stmt->rowMarks)
917886
{
@@ -923,9 +892,13 @@ ExecLockNonLeafAppendTables(List *partitioned_rels, EState *estate)
923892
}
924893

925894
if (rc && RowMarkRequiresRowShareLock(rc->markType))
926-
LockRelationOid(relid, RowShareLock);
895+
lockmode = RowShareLock;
927896
else
928-
LockRelationOid(relid, AccessShareLock);
897+
lockmode = AccessShareLock;
898+
899+
Assert(lockmode == rt_fetch(rti, estate->es_range_table)->rellockmode);
900+
901+
LockRelationOid(relid, lockmode);
929902
}
930903
}
931904
}

src/backend/optimizer/prep/prepunion.c

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,7 +1515,6 @@ expand_inherited_tables(PlannerInfo *root)
15151515
static void
15161516
expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
15171517
{
1518-
Query *parse = root->parse;
15191518
Oid parentOID;
15201519
PlanRowMark *oldrc;
15211520
Relation oldrelation;
@@ -1546,21 +1545,9 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
15461545
* relation named in the query. However, for each child relation we add
15471546
* to the query, we must obtain an appropriate lock, because this will be
15481547
* the first use of those relations in the parse/rewrite/plan pipeline.
1549-
*
1550-
* If the parent relation is the query's result relation, then we need
1551-
* RowExclusiveLock. Otherwise, if it's accessed FOR UPDATE/SHARE, we
1552-
* need RowShareLock; otherwise AccessShareLock. We can't just grab
1553-
* AccessShareLock because then the executor would be trying to upgrade
1554-
* the lock, leading to possible deadlocks. (This code should match the
1555-
* parser and rewriter.)
1548+
* Child rels should use the same lockmode as their parent.
15561549
*/
1557-
oldrc = get_plan_rowmark(root->rowMarks, rti);
1558-
if (rti == parse->resultRelation)
1559-
lockmode = RowExclusiveLock;
1560-
else if (oldrc && RowMarkRequiresRowShareLock(oldrc->markType))
1561-
lockmode = RowShareLock;
1562-
else
1563-
lockmode = AccessShareLock;
1550+
lockmode = rte->rellockmode;
15641551

15651552
/* Scan for all members of inheritance set, acquire needed locks */
15661553
inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
@@ -1582,6 +1569,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
15821569
* PlanRowMark as isParent = true, and generate a new PlanRowMark for each
15831570
* child.
15841571
*/
1572+
oldrc = get_plan_rowmark(root->rowMarks, rti);
15851573
if (oldrc)
15861574
oldrc->isParent = true;
15871575

src/backend/rewrite/rewriteHandler.c

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,14 @@ static Bitmapset *adjust_view_column_set(Bitmapset *cols, List *targetlist);
8787
* AcquireRewriteLocks -
8888
* Acquire suitable locks on all the relations mentioned in the Query.
8989
* These locks will ensure that the relation schemas don't change under us
90-
* while we are rewriting and planning the query.
90+
* while we are rewriting, planning, and executing the query.
9191
*
9292
* Caution: this may modify the querytree, therefore caller should usually
9393
* have done a copyObject() to make a writable copy of the querytree in the
9494
* current memory context.
9595
*
96-
* forExecute indicates that the query is about to be executed.
97-
* If so, we'll acquire RowExclusiveLock on the query's resultRelation,
98-
* RowShareLock on any relation accessed FOR [KEY] UPDATE/SHARE, and
99-
* AccessShareLock on all other relations mentioned.
100-
*
96+
* forExecute indicates that the query is about to be executed. If so,
97+
* we'll acquire the lock modes specified in the RTE rellockmode fields.
10198
* If forExecute is false, AccessShareLock is acquired on all relations.
10299
* This case is suitable for ruleutils.c, for example, where we only need
103100
* schema stability and we don't intend to actually modify any relations.
@@ -162,31 +159,24 @@ AcquireRewriteLocks(Query *parsetree,
162159

163160
/*
164161
* Grab the appropriate lock type for the relation, and do not
165-
* release it until end of transaction. This protects the
166-
* rewriter and planner against schema changes mid-query.
162+
* release it until end of transaction. This protects the
163+
* rewriter, planner, and executor against schema changes
164+
* mid-query.
167165
*
168-
* Assuming forExecute is true, this logic must match what the
169-
* executor will do, else we risk lock-upgrade deadlocks.
166+
* If forExecute is false, ignore rellockmode and just use
167+
* AccessShareLock.
170168
*/
171169
if (!forExecute)
172170
lockmode = AccessShareLock;
173-
else if (rt_index == parsetree->resultRelation)
174-
lockmode = RowExclusiveLock;
175171
else if (forUpdatePushedDown)
176172
{
177-
lockmode = RowShareLock;
178173
/* Upgrade RTE's lock mode to reflect pushed-down lock */
179174
if (rte->rellockmode == AccessShareLock)
180175
rte->rellockmode = RowShareLock;
176+
lockmode = rte->rellockmode;
181177
}
182-
else if (get_parse_rowmark(parsetree, rt_index) != NULL)
183-
lockmode = RowShareLock;
184178
else
185-
lockmode = AccessShareLock;
186-
187-
Assert(!forExecute || lockmode == rte->rellockmode ||
188-
(lockmode == AccessShareLock &&
189-
rte->rellockmode == RowExclusiveLock));
179+
lockmode = rte->rellockmode;
190180

191181
rel = heap_open(rte->relid, lockmode);
192182

src/backend/utils/cache/plancache.c

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,7 +1493,6 @@ AcquireExecutorLocks(List *stmt_list, bool acquire)
14931493
foreach(lc1, stmt_list)
14941494
{
14951495
PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc1);
1496-
int rt_index;
14971496
ListCell *lc2;
14981497

14991498
if (plannedstmt->commandType == CMD_UTILITY)
@@ -1512,14 +1511,9 @@ AcquireExecutorLocks(List *stmt_list, bool acquire)
15121511
continue;
15131512
}
15141513

1515-
rt_index = 0;
15161514
foreach(lc2, plannedstmt->rtable)
15171515
{
15181516
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
1519-
LOCKMODE lockmode;
1520-
PlanRowMark *rc;
1521-
1522-
rt_index++;
15231517

15241518
if (rte->rtekind != RTE_RELATION)
15251519
continue;
@@ -1530,21 +1524,10 @@ AcquireExecutorLocks(List *stmt_list, bool acquire)
15301524
* fail if it's been dropped entirely --- we'll just transiently
15311525
* acquire a non-conflicting lock.
15321526
*/
1533-
if (list_member_int(plannedstmt->resultRelations, rt_index) ||
1534-
list_member_int(plannedstmt->nonleafResultRelations, rt_index))
1535-
lockmode = RowExclusiveLock;
1536-
else if ((rc = get_plan_rowmark(plannedstmt->rowMarks, rt_index)) != NULL &&
1537-
RowMarkRequiresRowShareLock(rc->markType))
1538-
lockmode = RowShareLock;
1539-
else
1540-
lockmode = AccessShareLock;
1541-
1542-
Assert(lockmode == rte->rellockmode);
1543-
15441527
if (acquire)
1545-
LockRelationOid(rte->relid, lockmode);
1528+
LockRelationOid(rte->relid, rte->rellockmode);
15461529
else
1547-
UnlockRelationOid(rte->relid, lockmode);
1530+
UnlockRelationOid(rte->relid, rte->rellockmode);
15481531
}
15491532
}
15501533
}
@@ -1586,37 +1569,25 @@ static void
15861569
ScanQueryForLocks(Query *parsetree, bool acquire)
15871570
{
15881571
ListCell *lc;
1589-
int rt_index;
15901572

15911573
/* Shouldn't get called on utility commands */
15921574
Assert(parsetree->commandType != CMD_UTILITY);
15931575

15941576
/*
15951577
* First, process RTEs of the current query level.
15961578
*/
1597-
rt_index = 0;
15981579
foreach(lc, parsetree->rtable)
15991580
{
16001581
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
1601-
LOCKMODE lockmode;
16021582

1603-
rt_index++;
16041583
switch (rte->rtekind)
16051584
{
16061585
case RTE_RELATION:
16071586
/* Acquire or release the appropriate type of lock */
1608-
if (rt_index == parsetree->resultRelation)
1609-
lockmode = RowExclusiveLock;
1610-
else if (get_parse_rowmark(parsetree, rt_index) != NULL)
1611-
lockmode = RowShareLock;
1612-
else
1613-
lockmode = AccessShareLock;
1614-
Assert(lockmode == rte->rellockmode ||
1615-
(lockmode == AccessShareLock && rte->rellockmode == RowExclusiveLock));
16161587
if (acquire)
1617-
LockRelationOid(rte->relid, lockmode);
1588+
LockRelationOid(rte->relid, rte->rellockmode);
16181589
else
1619-
UnlockRelationOid(rte->relid, lockmode);
1590+
UnlockRelationOid(rte->relid, rte->rellockmode);
16201591
break;
16211592

16221593
case RTE_SUBQUERY:

0 commit comments

Comments
 (0)