Skip to content

Commit 32cc0d6

Browse files
committed
sql: use MakeSplitterForSideEffect in more places
In #116170 we added span.MakeSplitterForSideEffect which does not perform a column-family-skipping optimization. We were using this for locking lookup joins of multi-column-family tables, but forgot to use it in locking scans, locking index joins, and other places that could be performing a locked read of a multi-column-family table. This did not really matter until the previous commit, which made it more likely that we would use one of these other operations with locking on a multi-column-family table. Informs: #116838 Release note: None
1 parent 5800907 commit 32cc0d6

File tree

6 files changed

+128
-6
lines changed

6 files changed

+128
-6
lines changed

pkg/sql/distsql_physical_planner.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3363,7 +3363,15 @@ func (dsp *DistSQLPlanner) planIndexJoin(
33633363
return err
33643364
}
33653365

3366-
splitter := span.MakeSplitter(planInfo.fetch.desc, index, fetchOrdinals)
3366+
var splitter span.Splitter
3367+
// This logic matches opt.Locking.MustLockAllRequestedColumnFamilies.
3368+
if (joinReaderSpec.LockingStrength != descpb.ScanLockingStrength_FOR_NONE &&
3369+
joinReaderSpec.LockingDurability == descpb.ScanLockingDurability_GUARANTEED) ||
3370+
joinReaderSpec.LockingWaitPolicy != descpb.ScanLockingWaitPolicy_BLOCK {
3371+
splitter = span.MakeSplitterForSideEffect(planInfo.fetch.desc, index, fetchOrdinals)
3372+
} else {
3373+
splitter = span.MakeSplitter(planInfo.fetch.desc, index, fetchOrdinals)
3374+
}
33673375
joinReaderSpec.SplitFamilyIDs = splitter.FamilyIDs()
33683376

33693377
p.PlanToStreamColMap = identityMap(p.PlanToStreamColMap, len(fetchColIDs))
@@ -3458,8 +3466,10 @@ func (dsp *DistSQLPlanner) planLookupJoin(
34583466
}
34593467

34603468
var splitter span.Splitter
3461-
if joinReaderSpec.LockingStrength != descpb.ScanLockingStrength_FOR_NONE &&
3462-
joinReaderSpec.LockingDurability == descpb.ScanLockingDurability_GUARANTEED {
3469+
// This logic matches opt.Locking.MustLockAllRequestedColumnFamilies.
3470+
if (joinReaderSpec.LockingStrength != descpb.ScanLockingStrength_FOR_NONE &&
3471+
joinReaderSpec.LockingDurability == descpb.ScanLockingDurability_GUARANTEED) ||
3472+
joinReaderSpec.LockingWaitPolicy != descpb.ScanLockingWaitPolicy_BLOCK {
34633473
splitter = span.MakeSplitterForSideEffect(planInfo.fetch.desc, planInfo.fetch.index, fetchOrdinals)
34643474
} else {
34653475
splitter = span.MakeSplitter(planInfo.fetch.desc, planInfo.fetch.index, fetchOrdinals)

pkg/sql/distsql_spec_exec_factory.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,12 @@ func (e *distSQLSpecExecFactory) ConstructScan(
284284
if params.InvertedConstraint != nil {
285285
spans, err = sb.SpansFromInvertedSpans(e.ctx, params.InvertedConstraint, params.IndexConstraint, nil /* scratch */)
286286
} else {
287-
splitter := span.MakeSplitter(tabDesc, idx, params.NeededCols)
287+
var splitter span.Splitter
288+
if params.Locking.MustLockAllRequestedColumnFamilies() {
289+
splitter = span.MakeSplitterForSideEffect(tabDesc, idx, params.NeededCols)
290+
} else {
291+
splitter = span.MakeSplitter(tabDesc, idx, params.NeededCols)
292+
}
288293
spans, err = sb.SpansFromConstraint(params.IndexConstraint, splitter)
289294
}
290295
if err != nil {

pkg/sql/insert_fast_path.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,14 @@ func (c *insertFastPathCheck) init(params runParams) error {
112112
codec := params.ExecCfg().Codec
113113
c.keyPrefix = rowenc.MakeIndexKeyPrefix(codec, c.tabDesc.GetID(), c.idx.GetID())
114114
c.spanBuilder.InitAllowingExternalRowData(params.EvalContext(), codec, c.tabDesc, c.idx)
115-
c.spanSplitter = span.MakeSplitter(c.tabDesc, c.idx, intsets.Fast{} /* neededColOrdinals */)
115+
116+
if c.Locking.MustLockAllRequestedColumnFamilies() {
117+
c.spanSplitter = span.MakeSplitterForSideEffect(
118+
c.tabDesc, c.idx, intsets.Fast{}, /* neededColOrdinals */
119+
)
120+
} else {
121+
c.spanSplitter = span.MakeSplitter(c.tabDesc, c.idx, intsets.Fast{} /* neededColOrdinals */)
122+
}
116123

117124
if len(c.InsertCols) > idx.numLaxKeyCols {
118125
return errors.AssertionFailedf(

pkg/sql/logictest/testdata/logic_test/select_for_update

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,3 +750,82 @@ ROLLBACK
750750

751751
statement ok
752752
RESET enable_durable_locking_for_serializable
753+
754+
# Ensure that we lock all column families of a multi-family table when using
755+
# durable locking.
756+
757+
statement ok
758+
CREATE TABLE abc (
759+
a INT NOT NULL,
760+
b INT NOT NULL,
761+
c INT NOT NULL,
762+
PRIMARY KEY (a),
763+
INDEX (b),
764+
FAMILY f0 (a),
765+
FAMILY f1 (b),
766+
FAMILY f2 (c)
767+
)
768+
769+
statement ok
770+
INSERT INTO abc VALUES (6, 7, 8)
771+
772+
statement ok
773+
SET optimizer_use_lock_op_for_serializable = on
774+
775+
statement ok
776+
SET enable_durable_locking_for_serializable = on
777+
778+
statement ok
779+
SET distsql = off
780+
781+
# A scan where we normally skip reading family 0.
782+
query T kvtrace
783+
SELECT * FROM abc WHERE a = 6
784+
----
785+
Scan /Table/113/1/6/{1/1-2/2}
786+
787+
# But with FOR UPDATE we must read all families.
788+
query T kvtrace
789+
SELECT * FROM abc WHERE a = 6 FOR UPDATE
790+
----
791+
Scan /Table/113/1/{6-7} lock Exclusive (Block, Replicated)
792+
793+
# An index join where we normally skip reading family 0.
794+
query T kvtrace
795+
SELECT * FROM abc WHERE b = 7
796+
----
797+
Scan /Table/113/2/{7-8}
798+
Scan /Table/113/1/6/{1/1-2/2}
799+
800+
# But with FOR UPDATE we must read all families.
801+
query T kvtrace
802+
SELECT * FROM abc WHERE b = 7 FOR UPDATE
803+
----
804+
Scan /Table/113/2/{7-8}
805+
Scan /Table/113/1/{6-7} lock Exclusive (Block, Replicated)
806+
807+
statement ok
808+
INSERT INTO t129145 VALUES (6, 7)
809+
810+
# A lookup join where we normally skip reading family 0.
811+
query T kvtrace
812+
SELECT * FROM t129145 INNER LOOKUP JOIN abc ON abc.a = t129145.a
813+
----
814+
Scan /Table/112/{1-2}
815+
Scan /Table/113/1/1/{1/1-2/2}, /Table/113/1/6/{1/1-2/2}
816+
817+
# But with FOR UPDATE we must read all families.
818+
query T kvtrace
819+
SELECT * FROM t129145 INNER LOOKUP JOIN abc ON abc.a = t129145.a FOR UPDATE OF abc
820+
----
821+
Scan /Table/112/{1-2}
822+
Scan /Table/113/1/{1-2}, /Table/113/1/{6-7} lock Exclusive (Block, Replicated)
823+
824+
statement ok
825+
RESET distsql
826+
827+
statement ok
828+
RESET enable_durable_locking_for_serializable
829+
830+
statement ok
831+
RESET optimizer_use_lock_op_for_serializable

pkg/sql/opt/locking.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,19 @@ func (l Locking) IsLocking() bool {
9393
func (l Locking) IsNoOp() bool {
9494
return l == Locking{}
9595
}
96+
97+
// MustLockAllRequestedColumnFamilies returns true if the locking semantics
98+
// require actually acquiring locks on all requested column families. If this
99+
// returns false, then locking is best-effort, and it's ok to skip over some
100+
// requested column families.
101+
func (l Locking) MustLockAllRequestedColumnFamilies() bool {
102+
// If durability = guaranteed, then we've promised that locks will be held
103+
// until transaction commit. It's important in this case to actually acquire
104+
// all the requested locks.
105+
return (l.Strength != tree.ForNone && l.Durability == tree.LockDurabilityGuaranteed) ||
106+
// If this read is using SKIP LOCKED or NOWAIT then we want to be sure to
107+
// check all requested column families for locks. Otherwise we might assume
108+
// there are no locks and subsequently hit an unexpected write-write
109+
// conflict later in the same transaction.
110+
l.WaitPolicy != tree.LockWaitBlock
111+
}

pkg/sql/opt_exec_factory.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,12 @@ func generateScanSpans(
183183
if params.InvertedConstraint != nil {
184184
return sb.SpansFromInvertedSpans(ctx, params.InvertedConstraint, params.IndexConstraint, nil /* scratch */)
185185
}
186-
splitter := span.MakeSplitter(tabDesc, index, params.NeededCols)
186+
var splitter span.Splitter
187+
if params.Locking.MustLockAllRequestedColumnFamilies() {
188+
splitter = span.MakeSplitterForSideEffect(tabDesc, index, params.NeededCols)
189+
} else {
190+
splitter = span.MakeSplitter(tabDesc, index, params.NeededCols)
191+
}
187192
return sb.SpansFromConstraint(params.IndexConstraint, splitter)
188193
}
189194

0 commit comments

Comments
 (0)