diff --git a/pkg/kv/kvclient/kvstreamer/streamer.go b/pkg/kv/kvclient/kvstreamer/streamer.go index ac97f0caacfd..0acf6fe33462 100644 --- a/pkg/kv/kvclient/kvstreamer/streamer.go +++ b/pkg/kv/kvclient/kvstreamer/streamer.go @@ -221,11 +221,12 @@ type Streamer struct { distSender *kvcoord.DistSender stopper *stop.Stopper - mode OperationMode - hints Hints - maxKeysPerRow int32 - budget *budget - keyLocking lock.Strength + mode OperationMode + hints Hints + maxKeysPerRow int32 + budget *budget + lockStrength lock.Strength + lockDurability lock.Durability streamerStatistics @@ -370,16 +371,18 @@ func NewStreamer( acc *mon.BoundAccount, kvPairsRead *int64, batchRequestsIssued *int64, - keyLocking lock.Strength, + lockStrength lock.Strength, + lockDurability lock.Durability, ) *Streamer { if txn.Type() != kv.LeafTxn { panic(errors.AssertionFailedf("RootTxn is given to the Streamer")) } s := &Streamer{ - distSender: distSender, - stopper: stopper, - budget: newBudget(acc, limitBytes), - keyLocking: keyLocking, + distSender: distSender, + stopper: stopper, + budget: newBudget(acc, limitBytes), + lockStrength: lockStrength, + lockDurability: lockDurability, } if kvPairsRead == nil { @@ -1744,7 +1747,8 @@ func buildResumeSingleRangeBatch( newGet := gets[0] gets = gets[1:] newGet.req.SetSpan(*get.ResumeSpan) - newGet.req.KeyLockingStrength = s.keyLocking + newGet.req.KeyLockingStrength = s.lockStrength + newGet.req.KeyLockingDurability = s.lockDurability newGet.union.Get = &newGet.req resumeReq.reqs[resumeReqIdx].Value = &newGet.union resumeReq.positions = append(resumeReq.positions, position) @@ -1772,7 +1776,8 @@ func buildResumeSingleRangeBatch( scans = scans[1:] newScan.req.SetSpan(*scan.ResumeSpan) newScan.req.ScanFormat = kvpb.BATCH_RESPONSE - newScan.req.KeyLockingStrength = s.keyLocking + newScan.req.KeyLockingStrength = s.lockStrength + newScan.req.KeyLockingDurability = s.lockDurability newScan.union.Scan = &newScan.req resumeReq.reqs[resumeReqIdx].Value = &newScan.union resumeReq.positions = append(resumeReq.positions, position) diff --git a/pkg/kv/kvclient/kvstreamer/streamer_accounting_test.go b/pkg/kv/kvclient/kvstreamer/streamer_accounting_test.go index c8caa524fd24..9b2bc2628679 100644 --- a/pkg/kv/kvclient/kvstreamer/streamer_accounting_test.go +++ b/pkg/kv/kvclient/kvstreamer/streamer_accounting_test.go @@ -101,6 +101,7 @@ func TestStreamerMemoryAccounting(t *testing.T) { nil, /* kvPairsRead */ nil, /* batchRequestsIssued */ lock.None, + lock.Unreplicated, ) s.Init(OutOfOrder, Hints{UniqueRequests: true, SingleRowLookup: singleRowLookup}, 1 /* maxKeysPerRow */, nil /* diskBuffer */) return s diff --git a/pkg/kv/kvclient/kvstreamer/streamer_test.go b/pkg/kv/kvclient/kvstreamer/streamer_test.go index d479e51297cf..795de2fbca79 100644 --- a/pkg/kv/kvclient/kvstreamer/streamer_test.go +++ b/pkg/kv/kvclient/kvstreamer/streamer_test.go @@ -59,6 +59,7 @@ func getStreamer( nil, /* kvPairsRead */ nil, /* batchRequestsIssued */ lock.None, + lock.Unreplicated, ) } @@ -115,6 +116,7 @@ func TestStreamerLimitations(t *testing.T) { nil, /* kvPairsRead */ nil, /* batchRequestsIssued */ lock.None, + lock.Unreplicated, ) }) }) diff --git a/pkg/sql/catalog/descpb/locking.go b/pkg/sql/catalog/descpb/locking.go index 7545a0a52e92..47195a8707d4 100644 --- a/pkg/sql/catalog/descpb/locking.go +++ b/pkg/sql/catalog/descpb/locking.go @@ -80,3 +80,28 @@ func ToScanLockingWaitPolicy(wp tree.LockingWaitPolicy) ScanLockingWaitPolicy { panic(errors.AssertionFailedf("unknown locking wait policy %s", wp)) } } + +// PrettyString returns the locking durability as a user-readable string. +func (s ScanLockingDurability) PrettyString() string { + switch s { + case ScanLockingDurability_BEST_EFFORT: + return "best effort" + case ScanLockingDurability_GUARANTEED: + return "guaranteed" + default: + panic(errors.AssertionFailedf("unexpected durability %s", s)) + } +} + +// ToScanLockingDurability converts a tree.LockingDurability to its +// corresponding ScanLockingDurability. +func ToScanLockingDurability(s tree.LockingDurability) ScanLockingDurability { + switch s { + case tree.LockDurabilityBestEffort: + return ScanLockingDurability_BEST_EFFORT + case tree.LockDurabilityGuaranteed: + return ScanLockingDurability_GUARANTEED + default: + panic(errors.AssertionFailedf("unknown locking durability %d", s)) + } +} diff --git a/pkg/sql/catalog/descpb/locking.proto b/pkg/sql/catalog/descpb/locking.proto index 79ba3bd07536..03d1cf61444f 100644 --- a/pkg/sql/catalog/descpb/locking.proto +++ b/pkg/sql/catalog/descpb/locking.proto @@ -129,3 +129,9 @@ enum ScanLockingWaitPolicy { // ERROR represents NOWAIT - raise an error if a row cannot be locked. ERROR = 2; } + +// LockingDurability controls the durability of locks. +enum ScanLockingDurability { + BEST_EFFORT = 0; + GUARANTEED = 1; +} diff --git a/pkg/sql/colfetcher/colbatch_direct_scan.go b/pkg/sql/colfetcher/colbatch_direct_scan.go index 058f72404671..63066ad04cfa 100644 --- a/pkg/sql/colfetcher/colbatch_direct_scan.go +++ b/pkg/sql/colfetcher/colbatch_direct_scan.go @@ -215,6 +215,7 @@ func NewColBatchDirectScan( spec.Reverse, spec.LockingStrength, spec.LockingWaitPolicy, + spec.LockingDurability, flowCtx.EvalCtx.SessionData().LockTimeout, kvFetcherMemAcc, flowCtx.EvalCtx.TestingKnobs.ForceProductionValues, diff --git a/pkg/sql/colfetcher/colbatch_scan.go b/pkg/sql/colfetcher/colbatch_scan.go index 199eeb7c4c0f..e6a726150e44 100644 --- a/pkg/sql/colfetcher/colbatch_scan.go +++ b/pkg/sql/colfetcher/colbatch_scan.go @@ -350,6 +350,7 @@ func NewColBatchScan( spec.Reverse, spec.LockingStrength, spec.LockingWaitPolicy, + spec.LockingDurability, flowCtx.EvalCtx.SessionData().LockTimeout, kvFetcherMemAcc, flowCtx.EvalCtx.TestingKnobs.ForceProductionValues, diff --git a/pkg/sql/colfetcher/index_join.go b/pkg/sql/colfetcher/index_join.go index 69524e38b973..eda146cc4cc0 100644 --- a/pkg/sql/colfetcher/index_join.go +++ b/pkg/sql/colfetcher/index_join.go @@ -563,6 +563,7 @@ func NewColIndexJoin( flowCtx.EvalCtx.Settings, spec.LockingWaitPolicy, spec.LockingStrength, + spec.LockingDurability, streamerBudgetLimit, streamerBudgetAcc, spec.MaintainOrdering, @@ -580,6 +581,7 @@ func NewColIndexJoin( false, /* reverse */ spec.LockingStrength, spec.LockingWaitPolicy, + spec.LockingDurability, flowCtx.EvalCtx.SessionData().LockTimeout, kvFetcherMemAcc, flowCtx.EvalCtx.TestingKnobs.ForceProductionValues, diff --git a/pkg/sql/distsql_physical_planner.go b/pkg/sql/distsql_physical_planner.go index 14013583ef0a..764bb2213f7f 100644 --- a/pkg/sql/distsql_physical_planner.go +++ b/pkg/sql/distsql_physical_planner.go @@ -1665,6 +1665,7 @@ func initTableReaderSpecTemplate( TableDescriptorModificationTime: n.desc.GetModificationTime(), LockingStrength: n.lockingStrength, LockingWaitPolicy: n.lockingWaitPolicy, + LockingDurability: n.lockingDurability, } if err := rowenc.InitIndexFetchSpec(&s.FetchSpec, codec, n.desc, n.index, colIDs); err != nil { return nil, execinfrapb.PostProcessSpec{}, err @@ -2740,6 +2741,7 @@ func (dsp *DistSQLPlanner) createPlanForIndexJoin( Type: descpb.InnerJoin, LockingStrength: n.table.lockingStrength, LockingWaitPolicy: n.table.lockingWaitPolicy, + LockingDurability: n.table.lockingDurability, MaintainOrdering: len(n.reqOrdering) > 0, LimitHint: n.limitHint, } @@ -2817,6 +2819,7 @@ func (dsp *DistSQLPlanner) createPlanForLookupJoin( Type: n.joinType, LockingStrength: n.table.lockingStrength, LockingWaitPolicy: n.table.lockingWaitPolicy, + LockingDurability: n.table.lockingDurability, // TODO(sumeer): specifying ordering here using isFirstJoinInPairedJoiner // is late in the sense that the cost of this has not been taken into // account. Make this decision earlier in CustomFuncs.GenerateLookupJoins. @@ -2949,6 +2952,7 @@ func (dsp *DistSQLPlanner) createPlanForInvertedJoin( OutputGroupContinuationForLeftRow: n.isFirstJoinInPairedJoiner, LockingStrength: n.table.lockingStrength, LockingWaitPolicy: n.table.lockingWaitPolicy, + LockingDurability: n.table.lockingDurability, } fetchColIDs := make([]descpb.ColumnID, len(n.table.cols)) @@ -3045,6 +3049,7 @@ func (dsp *DistSQLPlanner) createPlanForZigzagJoin( fixedValues: valuesSpec, lockingStrength: side.scan.lockingStrength, lockingWaitPolicy: side.scan.lockingWaitPolicy, + lockingDurability: side.scan.lockingDurability, } } @@ -3064,6 +3069,7 @@ type zigzagPlanningSide struct { fixedValues *execinfrapb.ValuesCoreSpec lockingStrength descpb.ScanLockingStrength lockingWaitPolicy descpb.ScanLockingWaitPolicy + lockingDurability descpb.ScanLockingDurability } type zigzagPlanningInfo struct { diff --git a/pkg/sql/distsql_spec_exec_factory.go b/pkg/sql/distsql_spec_exec_factory.go index 41cc4fdf1fd2..aee191992469 100644 --- a/pkg/sql/distsql_spec_exec_factory.go +++ b/pkg/sql/distsql_spec_exec_factory.go @@ -256,6 +256,7 @@ func (e *distSQLSpecExecFactory) ConstructScan( } trSpec.LockingStrength = descpb.ToScanLockingStrength(params.Locking.Strength) trSpec.LockingWaitPolicy = descpb.ToScanLockingWaitPolicy(params.Locking.WaitPolicy) + trSpec.LockingDurability = descpb.ToScanLockingDurability(params.Locking.Durability) if trSpec.LockingStrength != descpb.ScanLockingStrength_FOR_NONE { // Scans that are performing row-level locking cannot currently be // distributed because their locks would not be propagated back to @@ -736,6 +737,7 @@ func (e *distSQLSpecExecFactory) constructZigzagJoinSide( fixedValues: valuesSpec, lockingStrength: descpb.ToScanLockingStrength(locking.Strength), lockingWaitPolicy: descpb.ToScanLockingWaitPolicy(locking.WaitPolicy), + lockingDurability: descpb.ToScanLockingDurability(locking.Durability), }, nil } diff --git a/pkg/sql/execinfrapb/processors_sql.proto b/pkg/sql/execinfrapb/processors_sql.proto index d3e64c803ff9..9ba8d3e646a2 100644 --- a/pkg/sql/execinfrapb/processors_sql.proto +++ b/pkg/sql/execinfrapb/processors_sql.proto @@ -121,6 +121,9 @@ message TableReaderSpec { // to BLOCK when locking_strength is FOR_NONE. optional sqlbase.ScanLockingWaitPolicy locking_wait_policy = 11 [(gogoproto.nullable) = false]; + // Indicates the row-level locking durability to be used by the scan. + optional sqlbase.ScanLockingDurability locking_durability = 23 [(gogoproto.nullable) = false]; + // Indicates that misplanned ranges metadata should not be sent back to the // DistSQLReceiver. This will be set to true for the scan with a hard limit // (in which case we create a single processor that is placed at the @@ -138,36 +141,6 @@ message FiltererSpec { optional Expression filter = 1 [(gogoproto.nullable) = false]; } -// IndexSkipTableReaderSpec is the specification for a table reader that -// is performing a loose index scan over rows in the table. This means that -// this reader will return distinct rows from the table while using the index -// to skip unnecessary rows. This reader is used for different optimizations -// when operating on a prefix of a compound key. -message IndexSkipTableReaderSpec { - optional sqlbase.TableDescriptor table = 1 [(gogoproto.nullable) = false]; - // If 0, we use the primary index. If non-zero, we use the index_idx-th index, - // i.e. table.indexes[index_idx-1] - optional uint32 index_idx = 2 [(gogoproto.nullable) = false]; - - reserved 3; - repeated roachpb.Span spans = 8 [(gogoproto.nullable) = false]; - - // This field used to be a visibility level of the columns that should be - // produced. We now always produce all columns (public and not public). - reserved 4; - - optional bool reverse = 5 [(gogoproto.nullable) = false]; - - // Indicates the row-level locking strength to be used by the scan. If set to - // FOR_NONE, no row-level locking should be performed. - optional sqlbase.ScanLockingStrength locking_strength = 6 [(gogoproto.nullable) = false]; - - // Indicates the policy to be used by the scan for handling conflicting locks - // held by other active transactions when attempting to lock rows. Always set - // to BLOCK when locking_strength is FOR_NONE. - optional sqlbase.ScanLockingWaitPolicy locking_wait_policy = 7 [(gogoproto.nullable) = false]; -} - // JoinReaderSpec is the specification for a "join reader". A join reader // performs KV operations to retrieve specific rows that correspond to the // values in the input stream (join by lookup). The output can optionally @@ -357,6 +330,9 @@ message JoinReaderSpec { // to BLOCK when locking_strength is FOR_NONE. optional sqlbase.ScanLockingWaitPolicy locking_wait_policy = 10 [(gogoproto.nullable) = false]; + // Indicates the row-level locking durability to be used by the scan. + optional sqlbase.ScanLockingDurability locking_durability = 24 [(gogoproto.nullable) = false]; + // Indicates that the join reader should maintain the ordering of the input // stream. This is applicable to both lookup joins and index joins. For lookup // joins, maintaining order is expensive because it requires buffering. For @@ -499,6 +475,9 @@ message ZigzagJoinerSpec { // held by other active transactions when attempting to lock rows. Always set // to BLOCK when locking_strength is FOR_NONE. optional sqlbase.ScanLockingWaitPolicy locking_wait_policy = 5 [(gogoproto.nullable) = false]; + + // Indicates the row-level locking durability to be used by the scan. + optional sqlbase.ScanLockingDurability locking_durability = 8 [(gogoproto.nullable) = false]; } repeated Side sides = 7 [(gogoproto.nullable) = false]; @@ -747,6 +726,9 @@ message InvertedJoinerSpec { // held by other active transactions when attempting to lock rows. Always set // to BLOCK when locking_strength is FOR_NONE. optional sqlbase.ScanLockingWaitPolicy locking_wait_policy = 13 [(gogoproto.nullable) = false]; + + // Indicates the row-level locking durability to be used by the scan. + optional sqlbase.ScanLockingDurability locking_durability = 14 [(gogoproto.nullable) = false]; } // InvertedFiltererSpec is the specification of a processor that does filtering diff --git a/pkg/sql/insert_fast_path.go b/pkg/sql/insert_fast_path.go index 862837f3c3f7..df99e9c2af2f 100644 --- a/pkg/sql/insert_fast_path.go +++ b/pkg/sql/insert_fast_path.go @@ -191,6 +191,7 @@ func (r *insertFastPathRun) addFKChecks( } lockStrength := row.GetKeyLockingStrength(descpb.ToScanLockingStrength(c.Locking.Strength)) lockWaitPolicy := row.GetWaitPolicy(descpb.ToScanLockingWaitPolicy(c.Locking.WaitPolicy)) + lockDurability := row.GetKeyLockingDurability(descpb.ToScanLockingDurability(c.Locking.Durability)) if r.fkBatch.Header.WaitPolicy != lockWaitPolicy { return errors.AssertionFailedf( "FK check lock wait policy %s did not match %s", @@ -200,9 +201,9 @@ func (r *insertFastPathRun) addFKChecks( reqIdx := len(r.fkBatch.Requests) r.fkBatch.Requests = append(r.fkBatch.Requests, kvpb.RequestUnion{}) r.fkBatch.Requests[reqIdx].MustSetInner(&kvpb.ScanRequest{ - RequestHeader: kvpb.RequestHeaderFromSpan(span), - KeyLockingStrength: lockStrength, - // TODO(michae2): Once #100193 is finished, also include c.Locking.Durability. + RequestHeader: kvpb.RequestHeaderFromSpan(span), + KeyLockingStrength: lockStrength, + KeyLockingDurability: lockDurability, }) r.fkSpanInfo = append(r.fkSpanInfo, insertFastPathFKSpanInfo{ check: c, diff --git a/pkg/sql/logictest/testdata/logic_test/fk_read_committed b/pkg/sql/logictest/testdata/logic_test/fk_read_committed index a26d6b4367b0..35f9f160436c 100644 --- a/pkg/sql/logictest/testdata/logic_test/fk_read_committed +++ b/pkg/sql/logictest/testdata/logic_test/fk_read_committed @@ -3,14 +3,11 @@ statement ok SET CLUSTER SETTING sql.txn.read_committed_syntax.enabled = true -# Some foreign key checks are prohibited under weaker isolation levels until we -# improve locking. See #80683, #100156, #100193. - statement ok CREATE TABLE jars (j INT PRIMARY KEY) statement ok -CREATE TABLE cookies (c INT PRIMARY KEY, j INT REFERENCES jars (j)) +CREATE TABLE cookies (c INT PRIMARY KEY, j INT REFERENCES jars (j), FAMILY (c, j)) statement ok SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED @@ -18,32 +15,20 @@ SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED statement ok INSERT INTO jars VALUES (1), (2) -# Foreign key checks of the parent require durable shared locking under weaker -# isolation levels, and are not yet supported. -query error pgcode 0A000 guaranteed-durable locking not yet implemented -INSERT INTO cookies VALUES (1, 1) - -statement ok -BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE - statement ok INSERT INTO cookies VALUES (1, 1) statement ok -COMMIT - -query error pgcode 0A000 guaranteed-durable locking not yet implemented UPDATE cookies SET j = 2 WHERE c = 1 -# Foreign key checks of the child do not require locking. query error violates foreign key constraint UPDATE jars SET j = j + 4 query error violates foreign key constraint -DELETE FROM jars WHERE j = 1 +DELETE FROM jars WHERE j = 2 statement ok DELETE FROM cookies WHERE c = 1 statement ok -DELETE FROM jars WHERE j = 1 +DELETE FROM jars WHERE j = 2 diff --git a/pkg/sql/logictest/testdata/logic_test/read_committed b/pkg/sql/logictest/testdata/logic_test/read_committed index bb34d002d6d2..a4c1e4681e6e 100644 --- a/pkg/sql/logictest/testdata/logic_test/read_committed +++ b/pkg/sql/logictest/testdata/logic_test/read_committed @@ -5,9 +5,6 @@ SET CLUSTER SETTING sql.txn.read_committed_syntax.enabled = true subtest select_for_update -# SELECT FOR UPDATE is prohibited under weaker isolation levels until we improve -# locking. See #57031, #75457, #100144, #100193. - statement ok CREATE TABLE supermarket ( person STRING PRIMARY KEY, @@ -15,14 +12,15 @@ CREATE TABLE supermarket ( starts_with STRING GENERATED ALWAYS AS (left(person, 1)) STORED, ends_with STRING GENERATED ALWAYS AS (right(person, 3)) STORED, INDEX (starts_with), - INDEX (ends_with) + INDEX (ends_with), + FAMILY (person, aisle, starts_with, ends_with) ) statement ok INSERT INTO supermarket (person, aisle) VALUES ('abbie', 1), ('gideon', 2), ('matilda', 3), ('michael', 4) -# SELECT FOR UPDATE should still work under serializable isolation. +# Use SELECT FOR UPDATE under serializable isolation. statement ok BEGIN @@ -37,25 +35,29 @@ UPDATE supermarket SET aisle = 2 WHERE person = 'abbie' statement ok COMMIT -# It should fail under read committed isolation. +# Use SELECT FOR UPDATE under read committed isolation. statement ok BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED -query error pgcode 0A000 guaranteed-durable locking not yet implemented +query I SELECT aisle FROM supermarket WHERE person = 'matilda' FOR UPDATE +---- +3 statement ok ROLLBACK -# It should also fail under snapshot isolation. +# Use SELECT FOR UPDATE under snapshot isolation. statement ok SET CLUSTER SETTING sql.txn.snapshot_isolation_syntax.enabled = true statement ok BEGIN TRANSACTION ISOLATION LEVEL SNAPSHOT -query error pgcode 0A000 guaranteed-durable locking not yet implemented +query I SELECT aisle FROM supermarket WHERE person = 'matilda' FOR UPDATE +---- +3 statement ok ROLLBACK @@ -63,14 +65,14 @@ ROLLBACK statement ok RESET CLUSTER SETTING sql.txn.snapshot_isolation_syntax.enabled -# SELECT FOR UPDATE in a subquery should also fail under read committed. +# Use SELECT FOR UPDATE in a subquery under read committed isolation. statement ok BEGIN TRANSACTION statement ok SET TRANSACTION ISOLATION LEVEL READ COMMITTED; -query error pgcode 0A000 guaranteed-durable locking not yet implemented +statement ok UPDATE supermarket SET aisle = (SELECT aisle FROM supermarket WHERE person = 'matilda' FOR UPDATE) WHERE person = 'michael' @@ -78,21 +80,24 @@ UPDATE supermarket statement ok ROLLBACK -# It should also fail in a CTE. +# Use SELECT FOR UPDATE in a CTE under read committed isolation. statement ok BEGIN TRANSACTION statement ok SET TRANSACTION ISOLATION LEVEL READ COMMITTED; -query error pgcode 0A000 guaranteed-durable locking not yet implemented +query I WITH s AS (SELECT aisle FROM supermarket WHERE person = 'matilda' FOR UPDATE) SELECT aisle + 1 FROM s +---- +4 statement ok ROLLBACK +# Use SELECT FOR UPDATE in a UDF under read committed isolation. statement ok CREATE FUNCTION wrangle (name STRING) RETURNS INT LANGUAGE SQL AS $$ SELECT aisle FROM supermarket WHERE person = name FOR UPDATE @@ -101,8 +106,7 @@ $$ statement ok SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED -# But calling that function should fail. -query error pgcode 0A000 guaranteed-durable locking not yet implemented +statement ok INSERT INTO supermarket (person, aisle) VALUES ('grandma', wrangle('matilda')) statement ok @@ -114,42 +118,50 @@ DROP FUNCTION wrangle statement ok SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED -# Preparing a SELECT FOR UPDATE should succeed under read committed. +# Prepare and execute a SELECT FOR UPDATE under read committed isolation. statement ok PREPARE psa AS SELECT aisle FROM supermarket WHERE person = $1::STRING FOR UPDATE -# But execution should fail. -query error pgcode 0A000 guaranteed-durable locking not yet implemented +query I EXECUTE psa('matilda') +---- +3 statement ok DEALLOCATE psa -# SELECT FOR UPDATE using a lookup join should also fail. -query error pgcode 0A000 guaranteed-durable locking not yet implemented +# Use SELECT FOR UPDATE with a lookup join under read committed isolation. +query I WITH names AS MATERIALIZED (SELECT 'matilda' AS person) SELECT aisle FROM names NATURAL INNER LOOKUP JOIN supermarket FOR UPDATE +---- +3 -# SELECT FOR UPDATE using an index join should also fail. -query error pgcode 0A000 guaranteed-durable locking not yet implemented +# Use SELECT FOR UPDATE with an index join under read committed isolation. +query I rowsort SELECT aisle FROM supermarket@supermarket_starts_with_idx WHERE starts_with = 'm' FOR UPDATE +---- +3 +4 -# SELECT FOR UPDATE using a zigzag join should also fail. +# Use SELECT FOR UPDATE with a zigzag join under read committed isolation. statement ok SET enable_zigzag_join = true -query error pgcode 0A000 guaranteed-durable locking not yet implemented +query I SELECT aisle FROM supermarket@{FORCE_ZIGZAG} WHERE starts_with = 'm' AND ends_with = 'lda' FOR UPDATE +---- +3 statement ok RESET enable_zigzag_join diff --git a/pkg/sql/opt/exec/execbuilder/relational.go b/pkg/sql/opt/exec/execbuilder/relational.go index db5b289670f7..00a913532f9c 100644 --- a/pkg/sql/opt/exec/execbuilder/relational.go +++ b/pkg/sql/opt/exec/execbuilder/relational.go @@ -2919,11 +2919,6 @@ func (b *Builder) buildLocking(locking opt.Locking) (opt.Locking, error) { 110873, "explicit unique checks are not yet supported under read committed isolation", ) } - if locking.Durability == tree.LockDurabilityGuaranteed { - return opt.Locking{}, unimplemented.NewWithIssuef( - 100193, "guaranteed-durable locking not yet implemented", - ) - } // Check if we can actually use shared locks here, or we need to use // non-locking reads instead. if locking.Strength == tree.ForShare || locking.Strength == tree.ForKeyShare { @@ -2939,6 +2934,19 @@ func (b *Builder) buildLocking(locking opt.Locking) (opt.Locking, error) { return opt.Locking{}, nil // early return; do not set b.ContainsNonDefaultKeyLocking } } + // Check if we can actually use guaranteed-durable locking here. + if locking.Durability == tree.LockDurabilityGuaranteed { + // Guaranteed-durable locking didn't exist prior to v23.2. + if !b.evalCtx.Settings.Version.IsActive(b.ctx, clusterversion.V23_2) || + // Under serializable isolation we only use guaranteed-durable locks if + // enable_durable_locking_for_serializable is set. (Serializable + // isolation does not require locking for correctness, so by default we + // use best-effort locks for better performance.) + (b.evalCtx.TxnIsoLevel == isolation.Serializable && + !b.evalCtx.SessionData().DurableLockingForSerializable) { + locking.Durability = tree.LockDurabilityBestEffort + } + } b.ContainsNonDefaultKeyLocking = true } return locking, nil diff --git a/pkg/sql/opt/exec/execbuilder/testdata/fk_read_committed b/pkg/sql/opt/exec/execbuilder/testdata/fk_read_committed index 6c68b3c7915f..beeb98786907 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/fk_read_committed +++ b/pkg/sql/opt/exec/execbuilder/testdata/fk_read_committed @@ -7,7 +7,7 @@ statement ok CREATE TABLE jars (j INT PRIMARY KEY) statement ok -CREATE TABLE cookies (c INT PRIMARY KEY, j INT REFERENCES jars (j)) +CREATE TABLE cookies (c INT PRIMARY KEY, j INT REFERENCES jars (j), FAMILY (c, j)) statement ok SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED @@ -28,23 +28,43 @@ insert cookies ├── with-scan &1 └── filters (true) +query T +EXPLAIN (VERBOSE) INSERT INTO cookies VALUES (1, 1) +---- +distribution: local +vectorized: true +· +• insert fast path + columns: () + estimated row count: 0 (missing stats) + into: cookies(c, j) + auto commit + FK check: jars@jars_pkey + FK check locking strength: for share + FK check locking durability: guaranteed + size: 2 columns, 1 row + row 0, expr 0: 1 + row 0, expr 1: 1 + # Under serializable isolation, locking is not required, unless # enable_implicit_fk_locking_for_serializable is true. statement ok BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE query T -EXPLAIN (OPT) INSERT INTO cookies VALUES (1, 1) +EXPLAIN (VERBOSE) INSERT INTO cookies VALUES (1, 1) ---- -insert cookies - ├── values - │ └── (1, 1) - └── f-k-checks - └── f-k-checks-item: cookies(j) -> jars(j) - └── anti-join (lookup jars) - ├── lookup columns are key - ├── with-scan &1 - └── filters (true) +distribution: local +vectorized: true +· +• insert fast path + columns: () + estimated row count: 0 (missing stats) + into: cookies(c, j) + FK check: jars@jars_pkey + size: 2 columns, 1 row + row 0, expr 0: 1 + row 0, expr 1: 1 statement ok SET enable_implicit_fk_locking_for_serializable = true @@ -55,18 +75,43 @@ statement ok SET enable_shared_locking_for_serializable=true query T -EXPLAIN (OPT) INSERT INTO cookies VALUES (1, 1) +EXPLAIN (VERBOSE) INSERT INTO cookies VALUES (1, 1) ---- -insert cookies - ├── values - │ └── (1, 1) - └── f-k-checks - └── f-k-checks-item: cookies(j) -> jars(j) - └── anti-join (lookup jars) - ├── lookup columns are key - ├── locking: for-share - ├── with-scan &1 - └── filters (true) +distribution: local +vectorized: true +· +• insert fast path + columns: () + estimated row count: 0 (missing stats) + into: cookies(c, j) + FK check: jars@jars_pkey + FK check locking strength: for share + size: 2 columns, 1 row + row 0, expr 0: 1 + row 0, expr 1: 1 + +statement ok +SET enable_durable_locking_for_serializable = true + +query T +EXPLAIN (VERBOSE) INSERT INTO cookies VALUES (1, 1) +---- +distribution: local +vectorized: true +· +• insert fast path + columns: () + estimated row count: 0 (missing stats) + into: cookies(c, j) + FK check: jars@jars_pkey + FK check locking strength: for share + FK check locking durability: guaranteed + size: 2 columns, 1 row + row 0, expr 0: 1 + row 0, expr 1: 1 + +statement ok +RESET enable_durable_locking_for_serializable statement ok RESET enable_implicit_fk_locking_for_serializable @@ -91,6 +136,60 @@ update cookies ├── with-scan &1 └── filters (true) +query T +EXPLAIN (VERBOSE) UPDATE cookies SET j = 2 WHERE c = 1 +---- +distribution: local +vectorized: true +· +• root +│ columns: () +│ +├── • update +│ │ columns: () +│ │ estimated row count: 0 (missing stats) +│ │ table: cookies +│ │ set: j +│ │ +│ └── • buffer +│ │ columns: (c, j, j_new) +│ │ label: buffer 1 +│ │ +│ └── • render +│ │ columns: (c, j, j_new) +│ │ render j_new: 2 +│ │ render c: c +│ │ render j: j +│ │ +│ └── • scan +│ columns: (c, j) +│ estimated row count: 1 (missing stats) +│ table: cookies@cookies_pkey +│ spans: /1/0 +│ locking strength: for update +│ +└── • constraint-check + │ + └── • error if rows + │ columns: () + │ + └── • lookup join (anti) + │ columns: (j_new) + │ estimated row count: 0 (missing stats) + │ table: jars@jars_pkey + │ equality: (j_new) = (j) + │ equality cols are key + │ locking strength: for share + │ locking durability: guaranteed + │ + └── • project + │ columns: (j_new) + │ + └── • scan buffer + columns: (c, j, j_new) + estimated row count: 1 (missing stats) + label: buffer 1 + # Foreign key checks of the child do not require locking. query T EXPLAIN (OPT) UPDATE jars SET j = j + 4 @@ -112,6 +211,83 @@ update jars └── filters └── j = cookies.j +query T +EXPLAIN (VERBOSE) UPDATE jars SET j = j + 4 +---- +distribution: local +vectorized: true +· +• root +│ columns: () +│ +├── • update +│ │ columns: () +│ │ estimated row count: 0 (missing stats) +│ │ table: jars +│ │ set: j +│ │ +│ └── • buffer +│ │ columns: (j, j_new) +│ │ label: buffer 1 +│ │ +│ └── • render +│ │ columns: (j, j_new) +│ │ render j_new: j + 4 +│ │ render j: j +│ │ +│ └── • scan +│ columns: (j) +│ estimated row count: 1,000 (missing stats) +│ table: jars@jars_pkey +│ spans: FULL SCAN +│ locking strength: for update +│ +└── • constraint-check + │ + └── • error if rows + │ columns: () + │ + └── • project + │ columns: (j) + │ + └── • hash join (inner) + │ columns: (j, j) + │ estimated row count: 99 (missing stats) + │ equality: (j) = (j) + │ left cols are key + │ right cols are key + │ + ├── • except all + │ │ columns: (j) + │ │ estimated row count: 1,000 (missing stats) + │ │ + │ ├── • project + │ │ │ columns: (j) + │ │ │ + │ │ └── • scan buffer + │ │ columns: (j, j_new) + │ │ estimated row count: 1,000 (missing stats) + │ │ label: buffer 1 + │ │ + │ └── • project + │ │ columns: (j_new) + │ │ + │ └── • scan buffer + │ columns: (j, j_new) + │ estimated row count: 1,000 (missing stats) + │ label: buffer 1 + │ + └── • distinct + │ columns: (j) + │ estimated row count: 100 (missing stats) + │ distinct on: j + │ + └── • scan + columns: (j) + estimated row count: 1,000 (missing stats) + table: cookies@cookies_pkey + spans: FULL SCAN + query T EXPLAIN (OPT) DELETE FROM jars WHERE j = 1 ---- @@ -125,3 +301,49 @@ delete jars ├── scan cookies └── filters └── j = cookies.j + +query T +EXPLAIN (VERBOSE) DELETE FROM jars WHERE j = 1 +---- +distribution: local +vectorized: true +· +• root +│ columns: () +│ +├── • delete +│ │ columns: () +│ │ estimated row count: 0 (missing stats) +│ │ from: jars +│ │ +│ └── • buffer +│ │ columns: (j) +│ │ label: buffer 1 +│ │ +│ └── • scan +│ columns: (j) +│ estimated row count: 1 (missing stats) +│ table: jars@jars_pkey +│ spans: /1/0 +│ +└── • constraint-check + │ + └── • error if rows + │ columns: () + │ + └── • hash join (right semi) + │ columns: (j) + │ estimated row count: 1 (missing stats) + │ equality: (j) = (j) + │ right cols are key + │ + ├── • scan + │ columns: (j) + │ estimated row count: 1,000 (missing stats) + │ table: cookies@cookies_pkey + │ spans: FULL SCAN + │ + └── • scan buffer + columns: (j) + estimated row count: 1 (missing stats) + label: buffer 1 diff --git a/pkg/sql/opt/exec/execbuilder/testdata/select_for_update_read_committed b/pkg/sql/opt/exec/execbuilder/testdata/select_for_update_read_committed index abf1d2a98410..f4fa3e98dda8 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/select_for_update_read_committed +++ b/pkg/sql/opt/exec/execbuilder/testdata/select_for_update_read_committed @@ -10,7 +10,8 @@ CREATE TABLE supermarket ( starts_with STRING GENERATED ALWAYS AS (left(person, 1)) STORED, ends_with STRING GENERATED ALWAYS AS (right(person, 3)) STORED, INDEX (starts_with), - INDEX (ends_with) + INDEX (ends_with), + FAMILY (person, aisle, starts_with, ends_with) ) statement ok @@ -28,6 +29,23 @@ project ├── constraint: /1: [/'matilda' - /'matilda'] └── locking: for-update,durability-guaranteed +query T +EXPLAIN (VERBOSE) SELECT aisle FROM supermarket WHERE person = 'matilda' FOR UPDATE +---- +distribution: local +vectorized: true +· +• project +│ columns: (aisle) +│ +└── • scan + columns: (person, aisle) + estimated row count: 1 (missing stats) + table: supermarket@supermarket_pkey + spans: /"matilda"/0 + locking strength: for update + locking durability: guaranteed + query T EXPLAIN (OPT) UPDATE supermarket @@ -45,6 +63,56 @@ update supermarket ├── constraint: /13: [/'matilda' - /'matilda'] └── locking: for-update,durability-guaranteed +query T +EXPLAIN (VERBOSE) +UPDATE supermarket + SET aisle = (SELECT aisle FROM supermarket WHERE person = 'matilda' FOR UPDATE) + WHERE person = 'michael' +---- +distribution: local +vectorized: true +· +• root +│ columns: () +│ +├── • update +│ │ columns: () +│ │ estimated row count: 0 (missing stats) +│ │ table: supermarket +│ │ set: aisle +│ │ auto commit +│ │ +│ └── • render +│ │ columns: (person, aisle, starts_with, ends_with, aisle_new) +│ │ render aisle_new: @S1 +│ │ render person: person +│ │ render aisle: aisle +│ │ render starts_with: starts_with +│ │ render ends_with: ends_with +│ │ +│ └── • scan +│ columns: (person, aisle, starts_with, ends_with) +│ estimated row count: 1 (missing stats) +│ table: supermarket@supermarket_pkey +│ spans: /"michael"/0 +│ locking strength: for update +│ +└── • subquery + │ id: @S1 + │ original sql: (SELECT aisle FROM supermarket WHERE person = 'matilda' FOR UPDATE) + │ exec mode: one row + │ + └── • project + │ columns: (aisle) + │ + └── • scan + columns: (person, aisle) + estimated row count: 1 (missing stats) + table: supermarket@supermarket_pkey + spans: /"matilda"/0 + locking strength: for update + locking durability: guaranteed + query T EXPLAIN (OPT) WITH s AS @@ -61,6 +129,47 @@ with &1 (s) └── projections └── aisle + 1 +query T +EXPLAIN (VERBOSE) +WITH s AS + (SELECT aisle FROM supermarket WHERE person = 'matilda' FOR UPDATE) +SELECT aisle + 1 FROM s +---- +distribution: local +vectorized: true +· +• root +│ columns: ("?column?") +│ +├── • render +│ │ columns: ("?column?") +│ │ render ?column?: aisle + 1 +│ │ +│ └── • scan buffer +│ columns: (aisle) +│ estimated row count: 1 (missing stats) +│ label: buffer 1 (s) +│ +└── • subquery + │ id: @S1 + │ original sql: SELECT aisle FROM supermarket WHERE person = 'matilda' FOR UPDATE + │ exec mode: all rows + │ + └── • buffer + │ columns: (aisle) + │ label: buffer 1 (s) + │ + └── • project + │ columns: (aisle) + │ + └── • scan + columns: (person, aisle) + estimated row count: 1 (missing stats) + table: supermarket@supermarket_pkey + spans: /"matilda"/0 + locking strength: for update + locking durability: guaranteed + query T EXPLAIN (OPT) WITH names AS MATERIALIZED @@ -82,6 +191,52 @@ with &1 (names) ├── with-scan &1 (names) └── filters (true) +query T +EXPLAIN (VERBOSE) +WITH names AS MATERIALIZED + (SELECT 'matilda' AS person) +SELECT aisle + FROM names + NATURAL INNER LOOKUP JOIN supermarket + FOR UPDATE +---- +distribution: local +vectorized: true +· +• root +│ columns: (aisle) +│ +├── • project +│ │ columns: (aisle) +│ │ +│ └── • lookup join (inner) +│ │ columns: (person, person, aisle) +│ │ estimated row count: 1 (missing stats) +│ │ table: supermarket@supermarket_pkey +│ │ equality: (person) = (person) +│ │ equality cols are key +│ │ locking strength: for update +│ │ locking durability: guaranteed +│ │ +│ └── • scan buffer +│ columns: (person) +│ estimated row count: 1 +│ label: buffer 1 (names) +│ +└── • subquery + │ id: @S1 + │ original sql: SELECT 'matilda' AS person + │ exec mode: all rows + │ + └── • buffer + │ columns: (person) + │ label: buffer 1 (names) + │ + └── • values + columns: (person) + size: 1 column, 1 row + row 0, expr 0: 'matilda' + query T EXPLAIN (OPT) SELECT aisle @@ -97,6 +252,35 @@ project ├── flags: force-index=supermarket_starts_with_idx └── locking: for-update,durability-guaranteed +query T +EXPLAIN (VERBOSE) +SELECT aisle + FROM supermarket@supermarket_starts_with_idx + WHERE starts_with = 'm' + FOR UPDATE +---- +distribution: local +vectorized: true +· +• project +│ columns: (aisle) +│ +└── • index join + │ columns: (aisle, starts_with) + │ estimated row count: 10 (missing stats) + │ table: supermarket@supermarket_pkey + │ key columns: person + │ locking strength: for update + │ locking durability: guaranteed + │ + └── • scan + columns: (person, starts_with) + estimated row count: 10 (missing stats) + table: supermarket@supermarket_starts_with_idx + spans: /"m"-/"m"/PrefixEnd + locking strength: for update + locking durability: guaranteed + statement ok SET enable_zigzag_join = true @@ -119,8 +303,48 @@ project │ └── ends_with = 'lda' └── filters (true) -statement ok -RESET enable_zigzag_join +query T +EXPLAIN (VERBOSE) +SELECT aisle + FROM supermarket@{FORCE_ZIGZAG} + WHERE starts_with = 'm' AND ends_with = 'lda' + FOR UPDATE +---- +distribution: local +vectorized: true +· +• project +│ columns: (aisle) +│ +└── • project + │ columns: (aisle, starts_with, ends_with) + │ + └── • lookup join (inner) + │ columns: (person, starts_with, ends_with, aisle) + │ estimated row count: 1 (missing stats) + │ table: supermarket@supermarket_pkey + │ equality: (person) = (person) + │ equality cols are key + │ locking strength: for update + │ locking durability: guaranteed + │ + └── • project + │ columns: (person, starts_with, ends_with) + │ + └── • zigzag join + columns: (person, starts_with, person, ends_with) + estimated row count: 1 (missing stats) + pred: (starts_with = 'm') AND (ends_with = 'lda') + left table: supermarket@supermarket_starts_with_idx + left columns: (person, starts_with) + left fixed values: 1 column + left locking strength: for update + left locking durability: guaranteed + right table: supermarket@supermarket_ends_with_idx + right columns: (person, ends_with) + right fixed values: 1 column + right locking strength: for update + right locking durability: guaranteed statement ok -SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE +RESET enable_zigzag_join diff --git a/pkg/sql/opt/memo/testdata/logprops/join b/pkg/sql/opt/memo/testdata/logprops/join index 250bc02c9d31..a9bd9d4be035 100644 --- a/pkg/sql/opt/memo/testdata/logprops/join +++ b/pkg/sql/opt/memo/testdata/logprops/join @@ -1955,7 +1955,7 @@ inner-join (hash) ├── interesting orderings: (+1) (+7) (-9,+10,+7) ├── scan fk │ ├── columns: k:1(int!null) v:2(int) r1:3(int!null) r2:4(int) - │ ├── locking: for-update,skip-locked + │ ├── locking: for-update,skip-locked,durability-guaranteed │ ├── volatile │ ├── key: (1) │ ├── fd: (1)-->(2-4) @@ -1963,7 +1963,7 @@ inner-join (hash) │ └── interesting orderings: (+1) ├── scan xysd │ ├── columns: x:7(int!null) y:8(int) s:9(string) d:10(decimal!null) - │ ├── locking: for-update,skip-locked + │ ├── locking: for-update,skip-locked,durability-guaranteed │ ├── volatile │ ├── key: (7) │ ├── fd: (7)-->(8-10), (9,10)~~>(7,8) @@ -1994,7 +1994,7 @@ inner-join (hash) │ └── unfiltered-cols: (1-6) ├── scan xysd │ ├── columns: x:7(int!null) y:8(int) s:9(string) d:10(decimal!null) - │ ├── locking: for-share,skip-locked + │ ├── locking: for-share,skip-locked,durability-guaranteed │ ├── volatile │ ├── key: (7) │ ├── fd: (7)-->(8-10), (9,10)~~>(7,8) @@ -2019,7 +2019,7 @@ inner-join (hash) ├── interesting orderings: (+1) (+7) (-9,+10,+7) ├── scan fk │ ├── columns: k:1(int!null) v:2(int) r1:3(int!null) r2:4(int) - │ ├── locking: for-update,skip-locked + │ ├── locking: for-update,skip-locked,durability-guaranteed │ ├── volatile │ ├── key: (1) │ ├── fd: (1)-->(2-4) diff --git a/pkg/sql/opt/memo/testdata/logprops/scan b/pkg/sql/opt/memo/testdata/logprops/scan index 75a81f6b40f5..8450cc94f757 100644 --- a/pkg/sql/opt/memo/testdata/logprops/scan +++ b/pkg/sql/opt/memo/testdata/logprops/scan @@ -285,7 +285,7 @@ project ├── interesting orderings: (+1) (-3,+4,+1) └── scan a ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) crdb_internal_mvcc_timestamp:5(decimal) tableoid:6(oid) - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile ├── key: (1) ├── fd: (1)-->(2-6), (3,4)~~>(1,2,5,6) diff --git a/pkg/sql/opt/optbuilder/select.go b/pkg/sql/opt/optbuilder/select.go index 2b4433560b6b..c391cc2c93a0 100644 --- a/pkg/sql/opt/optbuilder/select.go +++ b/pkg/sql/opt/optbuilder/select.go @@ -14,7 +14,6 @@ import ( "context" "fmt" - "github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/isolation" "github.com/cockroachdb/cockroach/pkg/server/telemetry" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo" @@ -697,25 +696,19 @@ func (b *Builder) buildScan( } if locking.isSet() { private.Locking = locking.get() - // TODO(arul, michae2): This needs a cluster version check. - if b.evalCtx.TxnIsoLevel != isolation.Serializable || - b.evalCtx.SessionData().DurableLockingForSerializable { - // Under weaker isolation levels we use fully-durable locks for SELECT FOR - // UPDATE statements, SELECT FOR SHARE statements, and constraint checks - // (e.g. FK checks), regardless of locking strength and wait policy. - // Unlike mutation statements, SELECT FOR UPDATE statements do not lay - // down intents, so we cannot rely on the durability of intents to - // guarantee exclusion until commit as we do for mutation statements. And - // unlike serializable isolation, weaker isolation levels do not perform - // read refreshing, so we cannot rely on read refreshing to guarantee - // exclusion. - // - // Under serializable isolation we only use fully-durable locks if - // enable_durable_locking_for_serializable is set. (Serializable isolation - // does not require locking for correctness, so by default we use - // best-effort locks for better performance.) - private.Locking.Durability = tree.LockDurabilityGuaranteed - } + // Under weaker isolation levels we use fully-durable locks for SELECT FOR + // UPDATE statements, SELECT FOR SHARE statements, and constraint checks + // (e.g. FK checks), regardless of locking strength and wait policy. Unlike + // mutation statements, SELECT FOR UPDATE statements do not lay down + // intents, so we cannot rely on the durability of intents to guarantee + // exclusion until commit as we do for mutation statements. And unlike + // serializable isolation, weaker isolation levels do not perform read + // refreshing, so we cannot rely on read refreshing to guarantee exclusion. + // + // We set guaranteed durability here and then remove it in execbuilder if + // necessary, to allow for preparing and execution of statements in + // different isolation levels. + private.Locking.Durability = tree.LockDurabilityGuaranteed if private.Locking.WaitPolicy == tree.LockWaitSkipLocked && tab.FamilyCount() > 1 { // TODO(rytaft): We may be able to support this if enough columns are // pruned that only a single family is scanned. diff --git a/pkg/sql/opt/optbuilder/testdata/fk-checks-insert b/pkg/sql/opt/optbuilder/testdata/fk-checks-insert index 67db587a72b0..babc0e5c469e 100644 --- a/pkg/sql/opt/optbuilder/testdata/fk-checks-insert +++ b/pkg/sql/opt/optbuilder/testdata/fk-checks-insert @@ -710,6 +710,6 @@ insert child ├── scan parent │ ├── columns: parent.p:8!null │ ├── flags: disabled not visible index feature - │ └── locking: for-share + │ └── locking: for-share,durability-guaranteed └── filters └── p:7 = parent.p:8 diff --git a/pkg/sql/opt/optbuilder/testdata/fk-checks-update b/pkg/sql/opt/optbuilder/testdata/fk-checks-update index 84305434cc88..adbebdd5308a 100644 --- a/pkg/sql/opt/optbuilder/testdata/fk-checks-update +++ b/pkg/sql/opt/optbuilder/testdata/fk-checks-update @@ -600,7 +600,7 @@ update child │ ├── scan parent │ │ ├── columns: parent.p:13!null │ │ ├── flags: disabled not visible index feature - │ │ └── locking: for-share + │ │ └── locking: for-share,durability-guaranteed │ └── filters │ └── p:11 = parent.p:13 ├── f-k-checks-item: grandchild(c) -> child(c) @@ -670,7 +670,7 @@ update self ├── scan self │ ├── columns: x:11!null │ ├── flags: disabled not visible index feature - │ └── locking: for-share + │ └── locking: for-share,durability-guaranteed └── filters └── y:10 = x:11 diff --git a/pkg/sql/opt/optbuilder/testdata/select_for_update b/pkg/sql/opt/optbuilder/testdata/select_for_update index cf0c0d446461..e540e4504e1f 100644 --- a/pkg/sql/opt/optbuilder/testdata/select_for_update +++ b/pkg/sql/opt/optbuilder/testdata/select_for_update @@ -25,7 +25,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM t FOR NO KEY UPDATE @@ -34,7 +34,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-no-key-update + └── locking: for-no-key-update,durability-guaranteed build SELECT * FROM t FOR SHARE @@ -43,7 +43,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-share + └── locking: for-share,durability-guaranteed build SELECT * FROM t FOR KEY SHARE @@ -52,7 +52,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-key-share + └── locking: for-key-share,durability-guaranteed build SELECT * FROM t FOR KEY SHARE FOR SHARE @@ -61,7 +61,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-share + └── locking: for-share,durability-guaranteed build SELECT * FROM t FOR KEY SHARE FOR SHARE FOR NO KEY UPDATE @@ -70,7 +70,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-no-key-update + └── locking: for-no-key-update,durability-guaranteed build SELECT * FROM t FOR KEY SHARE FOR SHARE FOR NO KEY UPDATE FOR UPDATE @@ -79,7 +79,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM t FOR UPDATE OF t @@ -88,7 +88,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM t FOR UPDATE OF t2 @@ -102,7 +102,7 @@ project ├── columns: "?column?":5!null ├── scan t │ ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── projections └── 1 [as="?column?":5] @@ -117,7 +117,7 @@ project ├── columns: a:1!null b:2 └── scan t [as=t2] ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM t AS t2 FOR UPDATE OF t @@ -131,7 +131,7 @@ project ├── columns: a:1!null b:2 └── scan t [as=t2] ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed # ------------------------------------------------------------------------------ # Tests with numeric table references. @@ -145,7 +145,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM [53 AS t] FOR UPDATE OF t @@ -154,7 +154,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM [53 AS t] FOR UPDATE OF t2 @@ -172,7 +172,7 @@ project ├── columns: a:1!null └── scan t [as=t2] ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM v FOR UPDATE OF v @@ -181,7 +181,7 @@ project ├── columns: a:1!null └── scan t [as=t2] ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM v FOR UPDATE OF v2 @@ -209,7 +209,7 @@ project ├── columns: a:1!null └── scan t [as=t2] ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM v AS v2 FOR UPDATE OF v @@ -223,7 +223,7 @@ project ├── columns: a:1!null └── scan t [as=t2] ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed # ------------------------------------------------------------------------------ # Tests with subqueries. @@ -240,7 +240,7 @@ project ├── columns: a:1!null └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM (SELECT a FROM t FOR UPDATE) @@ -249,7 +249,7 @@ project ├── columns: a:1!null └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM (SELECT a FROM t FOR NO KEY UPDATE) FOR KEY SHARE @@ -258,7 +258,7 @@ project ├── columns: a:1!null └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-no-key-update + └── locking: for-no-key-update,durability-guaranteed build SELECT * FROM (SELECT a FROM t FOR KEY SHARE) FOR NO KEY UPDATE @@ -267,7 +267,7 @@ project ├── columns: a:1!null └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-no-key-update + └── locking: for-no-key-update,durability-guaranteed build SELECT * FROM (SELECT a FROM t) FOR UPDATE OF t @@ -281,7 +281,7 @@ project ├── columns: a:1!null └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM (SELECT a FROM t) AS r FOR UPDATE @@ -290,7 +290,7 @@ project ├── columns: a:1!null └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM (SELECT a FROM t FOR UPDATE) AS r @@ -299,7 +299,7 @@ project ├── columns: a:1!null └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM (SELECT a FROM t) AS r FOR UPDATE OF t @@ -313,7 +313,7 @@ project ├── columns: a:1!null └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT (SELECT a FROM t) FOR UPDATE @@ -346,7 +346,7 @@ project ├── columns: t.a:1!null └── scan t ├── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT (SELECT a FROM t) FOR UPDATE OF t @@ -368,7 +368,7 @@ project ├── columns: t.a:1!null └── scan t ├── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT (SELECT a FROM t) AS r FOR UPDATE @@ -401,7 +401,7 @@ project ├── columns: a:1!null └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT (SELECT a FROM t) AS r FOR UPDATE OF t @@ -423,7 +423,7 @@ project ├── columns: a:1!null └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update + └── locking: for-update,durability-guaranteed build SELECT * FROM t WHERE a IN (SELECT a FROM t) FOR UPDATE @@ -434,7 +434,7 @@ project ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 ├── scan t │ ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── any: eq ├── project @@ -458,7 +458,7 @@ project │ ├── columns: a:5!null │ └── scan t │ ├── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── a:1 build @@ -470,7 +470,7 @@ project ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 ├── scan t │ ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── any: eq ├── project @@ -494,7 +494,7 @@ project │ ├── columns: a:5!null │ └── scan t │ ├── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── a:1 build @@ -506,7 +506,7 @@ project ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 ├── scan t │ ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── any: eq ├── project @@ -530,7 +530,7 @@ project │ ├── columns: b:6 │ └── scan t │ ├── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── a:1 build @@ -542,7 +542,7 @@ project ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 ├── scan t │ ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── any: eq ├── project @@ -566,7 +566,7 @@ project │ ├── columns: b:6 │ └── scan t │ ├── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── a:1 # ------------------------------------------------------------------------------ @@ -614,7 +614,7 @@ with &1 │ ├── columns: t.a:1!null │ └── scan t │ ├── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── with-scan &1 ├── columns: a:5!null └── mapping: @@ -629,7 +629,7 @@ with &1 (cte) │ ├── columns: t.a:1!null │ └── scan t │ ├── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── with-scan &1 (cte) ├── columns: a:5!null └── mapping: @@ -648,10 +648,10 @@ project ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 ├── scan t │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── scan u │ ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── t.a:1 = u.a:5 @@ -664,7 +664,7 @@ project ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 ├── scan t │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── scan u │ └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 └── filters @@ -681,7 +681,7 @@ project │ └── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 ├── scan u │ ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── t.a:1 = u.a:5 @@ -694,10 +694,10 @@ project ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 ├── scan t │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── scan u │ ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── t.a:1 = u.a:5 @@ -710,10 +710,10 @@ project ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 ├── scan t │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── scan u │ ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 - │ └── locking: for-share + │ └── locking: for-share,durability-guaranteed └── filters └── t.a:1 = u.a:5 @@ -731,10 +731,10 @@ project ├── columns: t2.a:1!null b:2 t2.crdb_internal_mvcc_timestamp:3 t2.tableoid:4 u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8 ├── scan t [as=t2] │ ├── columns: t2.a:1!null b:2 t2.crdb_internal_mvcc_timestamp:3 t2.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── scan u [as=u2] │ ├── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8 - │ └── locking: for-share + │ └── locking: for-share,durability-guaranteed └── filters └── t2.a:1 = u2.a:5 @@ -747,10 +747,10 @@ project ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 ├── scan t │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── scan u │ ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── t.a:1 = u.a:5 @@ -763,10 +763,10 @@ project ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 ├── scan t │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ └── locking: for-no-key-update + │ └── locking: for-no-key-update,durability-guaranteed ├── scan u │ ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 - │ └── locking: for-key-share + │ └── locking: for-key-share,durability-guaranteed └── filters └── t.a:1 = u.a:5 @@ -779,10 +779,10 @@ project ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 ├── scan t │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ └── locking: for-no-key-update + │ └── locking: for-no-key-update,durability-guaranteed ├── scan u │ ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── t.a:1 = u.a:5 @@ -799,10 +799,10 @@ project ├── columns: t2.a:1!null b:2 t2.crdb_internal_mvcc_timestamp:3 t2.tableoid:4 u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8 ├── scan t [as=t2] │ ├── columns: t2.a:1!null b:2 t2.crdb_internal_mvcc_timestamp:3 t2.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── scan u [as=u2] │ ├── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── t2.a:1 = u2.a:5 @@ -830,7 +830,7 @@ project ├── columns: t2.a:1!null b:2 t2.crdb_internal_mvcc_timestamp:3 t2.tableoid:4 u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8 ├── scan t [as=t2] │ ├── columns: t2.a:1!null b:2 t2.crdb_internal_mvcc_timestamp:3 t2.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── scan u [as=u2] │ └── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8 └── filters @@ -847,7 +847,7 @@ project │ └── columns: t2.a:1!null b:2 t2.crdb_internal_mvcc_timestamp:3 t2.tableoid:4 ├── scan u [as=u2] │ ├── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── t2.a:1 = u2.a:5 @@ -860,10 +860,10 @@ project ├── columns: t2.a:1!null b:2 t2.crdb_internal_mvcc_timestamp:3 t2.tableoid:4 u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8 ├── scan t [as=t2] │ ├── columns: t2.a:1!null b:2 t2.crdb_internal_mvcc_timestamp:3 t2.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── scan u [as=u2] │ ├── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── t2.a:1 = u2.a:5 @@ -881,10 +881,10 @@ project ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8 ├── scan t │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── scan u [as=u2] │ ├── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── t.a:1 = u2.a:5 @@ -912,10 +912,10 @@ project ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8 ├── scan t │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── scan u [as=u2] │ ├── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── t.a:1 = u2.a:5 @@ -932,10 +932,10 @@ project ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 ├── scan t │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── scan u │ ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters (true) build @@ -947,7 +947,7 @@ project ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 ├── scan t │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── scan u │ └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 └── filters (true) @@ -961,10 +961,10 @@ project ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 ├── scan t │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ └── locking: for-share + │ └── locking: for-share,durability-guaranteed ├── scan u │ ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters (true) build @@ -976,12 +976,12 @@ project ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5!null c:6 ├── scan t │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed ├── project │ ├── columns: u.a:5!null c:6 │ └── scan u │ ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters (true) build @@ -1002,7 +1002,7 @@ project │ ├── columns: u.a:5!null c:6 │ └── scan u │ ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters (true) # ------------------------------------------------------------------------------ @@ -1027,7 +1027,7 @@ project ├── columns: a:1!null b:2!null c:3 crdb_internal_mvcc_timestamp:4 tableoid:5 ├── scan indexed │ ├── columns: a:1!null b:2 c:3 crdb_internal_mvcc_timestamp:4 tableoid:5 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── b:2 = 2 @@ -1040,7 +1040,7 @@ project ├── columns: a:1!null b:2!null c:3 crdb_internal_mvcc_timestamp:4 tableoid:5 ├── scan indexed │ ├── columns: a:1!null b:2 c:3 crdb_internal_mvcc_timestamp:4 tableoid:5 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── (b:2 >= 2) AND (b:2 <= 10) @@ -1059,10 +1059,10 @@ project │ ├── columns: t.a:1!null b:2!null t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 │ ├── scan t │ │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ │ └── locking: for-update + │ │ └── locking: for-update,durability-guaranteed │ ├── scan u │ │ ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 - │ │ └── locking: for-update + │ │ └── locking: for-update,durability-guaranteed │ └── filters │ └── b:2 = u.a:5 └── filters @@ -1079,10 +1079,10 @@ project │ ├── columns: t.a:1!null b:2!null t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 │ ├── scan t │ │ ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ │ └── locking: for-update + │ │ └── locking: for-update,durability-guaranteed │ ├── scan u │ │ ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8 - │ │ └── locking: for-update + │ │ └── locking: for-update,durability-guaranteed │ └── filters │ └── b:2 = u.a:5 └── filters @@ -1099,10 +1099,10 @@ project │ ├── columns: t.a:1!null t.b:2!null t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 indexed.a:5!null indexed.b:6!null c:7 indexed.crdb_internal_mvcc_timestamp:8 indexed.tableoid:9 │ ├── scan t │ │ ├── columns: t.a:1!null t.b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 - │ │ └── locking: for-update + │ │ └── locking: for-update,durability-guaranteed │ ├── scan indexed │ │ ├── columns: indexed.a:5!null indexed.b:6 c:7 indexed.crdb_internal_mvcc_timestamp:8 indexed.tableoid:9 - │ │ └── locking: for-update + │ │ └── locking: for-update,durability-guaranteed │ └── filters │ └── t.b:2 = indexed.b:6 └── filters @@ -1130,7 +1130,7 @@ project ├── columns: a:1!null b:2!null c:3 crdb_internal_mvcc_timestamp:4 tableoid:5 ├── scan inverted │ ├── columns: a:1!null b:2 c:3 crdb_internal_mvcc_timestamp:4 tableoid:5 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── b:2 @> ARRAY[1,2] @@ -1143,7 +1143,7 @@ project ├── columns: a:1!null b:2 c:3 crdb_internal_mvcc_timestamp:4 tableoid:5 ├── scan inverted │ ├── columns: a:1!null b:2 c:3 crdb_internal_mvcc_timestamp:4 tableoid:5 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── b:2 <@ ARRAY[1,2] @@ -1159,10 +1159,10 @@ project │ ├── scan inverted [as=i1] │ │ ├── columns: i1.a:1!null i1.b:2 i1.c:3 i1.crdb_internal_mvcc_timestamp:4 i1.tableoid:5 │ │ ├── flags: force-index=b_inv - │ │ └── locking: for-update + │ │ └── locking: for-update,durability-guaranteed │ ├── scan inverted [as=i2] │ │ ├── columns: i2.a:7!null i2.b:8 i2.c:9 i2.crdb_internal_mvcc_timestamp:10 i2.tableoid:11 - │ │ └── locking: for-update + │ │ └── locking: for-update,durability-guaranteed │ └── filters (true) └── filters └── i1.b:2 @> i2.b:8 @@ -1192,7 +1192,7 @@ project ├── columns: a:1!null b:2!null c:3!null d:4 crdb_internal_mvcc_timestamp:5 tableoid:6 ├── scan zigzag │ ├── columns: a:1!null b:2 c:3 d:4 crdb_internal_mvcc_timestamp:5 tableoid:6 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── (b:2 = 5) AND (c:3 = 6.0) @@ -1205,7 +1205,7 @@ project ├── columns: a:1!null b:2 c:3 d:4!null crdb_internal_mvcc_timestamp:5 tableoid:6 ├── scan zigzag │ ├── columns: a:1!null b:2 c:3 d:4 crdb_internal_mvcc_timestamp:5 tableoid:6 - │ └── locking: for-update + │ └── locking: for-update,durability-guaranteed └── filters └── d:4 @> '{"a": {"b": "c"}, "f": "g"}' @@ -1229,7 +1229,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update,nowait + └── locking: for-update,nowait,durability-guaranteed build SELECT * FROM t FOR NO KEY UPDATE NOWAIT @@ -1238,7 +1238,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-no-key-update,nowait + └── locking: for-no-key-update,nowait,durability-guaranteed build SELECT * FROM t FOR SHARE NOWAIT @@ -1247,7 +1247,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-share,nowait + └── locking: for-share,nowait,durability-guaranteed build SELECT * FROM t FOR KEY SHARE NOWAIT @@ -1256,7 +1256,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-key-share,nowait + └── locking: for-key-share,nowait,durability-guaranteed build SELECT * FROM t FOR KEY SHARE FOR SHARE NOWAIT @@ -1265,7 +1265,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-share,nowait + └── locking: for-share,nowait,durability-guaranteed build SELECT * FROM t FOR KEY SHARE FOR SHARE NOWAIT FOR NO KEY UPDATE @@ -1274,7 +1274,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-no-key-update,nowait + └── locking: for-no-key-update,nowait,durability-guaranteed build SELECT * FROM t FOR KEY SHARE FOR SHARE NOWAIT FOR NO KEY UPDATE FOR UPDATE NOWAIT @@ -1283,7 +1283,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update,nowait + └── locking: for-update,nowait,durability-guaranteed build SELECT * FROM t FOR UPDATE OF t NOWAIT @@ -1292,7 +1292,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update,nowait + └── locking: for-update,nowait,durability-guaranteed build SELECT * FROM t FOR UPDATE OF t2 NOWAIT @@ -1306,7 +1306,7 @@ project ├── columns: "?column?":5!null ├── scan t │ ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - │ └── locking: for-update,nowait + │ └── locking: for-update,nowait,durability-guaranteed └── projections └── 1 [as="?column?":5] @@ -1321,7 +1321,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update,skip-locked + └── locking: for-update,skip-locked,durability-guaranteed build SELECT * FROM t FOR NO KEY UPDATE SKIP LOCKED @@ -1330,7 +1330,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-no-key-update,skip-locked + └── locking: for-no-key-update,skip-locked,durability-guaranteed build SELECT * FROM t FOR SHARE SKIP LOCKED @@ -1339,7 +1339,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-share,skip-locked + └── locking: for-share,skip-locked,durability-guaranteed build SELECT * FROM t FOR KEY SHARE SKIP LOCKED @@ -1348,7 +1348,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-key-share,skip-locked + └── locking: for-key-share,skip-locked,durability-guaranteed build SELECT * FROM t FOR KEY SHARE FOR SHARE SKIP LOCKED @@ -1357,7 +1357,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-share,skip-locked + └── locking: for-share,skip-locked,durability-guaranteed build SELECT * FROM t FOR KEY SHARE FOR SHARE SKIP LOCKED FOR NO KEY UPDATE @@ -1366,7 +1366,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-no-key-update,skip-locked + └── locking: for-no-key-update,skip-locked,durability-guaranteed build SELECT * FROM t FOR KEY SHARE FOR SHARE SKIP LOCKED FOR NO KEY UPDATE FOR UPDATE SKIP LOCKED @@ -1375,7 +1375,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update,skip-locked + └── locking: for-update,skip-locked,durability-guaranteed build SELECT * FROM t FOR UPDATE OF t SKIP LOCKED @@ -1384,7 +1384,7 @@ project ├── columns: a:1!null b:2 └── scan t ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - └── locking: for-update,skip-locked + └── locking: for-update,skip-locked,durability-guaranteed build SELECT * FROM t FOR UPDATE OF t2 SKIP LOCKED @@ -1398,7 +1398,7 @@ project ├── columns: "?column?":5!null ├── scan t │ ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4 - │ └── locking: for-update,skip-locked + │ └── locking: for-update,skip-locked,durability-guaranteed └── projections └── 1 [as="?column?":5] diff --git a/pkg/sql/opt/xform/testdata/external/tpce b/pkg/sql/opt/xform/testdata/external/tpce index 54d1d831f48a..052d4aabd1c7 100644 --- a/pkg/sql/opt/xform/testdata/external/tpce +++ b/pkg/sql/opt/xform/testdata/external/tpce @@ -3529,7 +3529,7 @@ project │ │ ├── scan trade │ │ │ ├── columns: t_id:1!null t_tt_id:4!null t_is_cash:5!null t_s_symb:6!null t_qty:7!null t_ca_id:9!null trade.t_chrg:12!null t_lifo:15!null │ │ │ ├── constraint: /1: [/0 - /0] - │ │ │ ├── locking: for-update + │ │ │ ├── locking: for-update,durability-guaranteed │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── volatile │ │ │ ├── key: () @@ -3586,7 +3586,7 @@ project │ │ ├── scan trade │ │ │ ├── columns: t_id:1!null t_tt_id:4!null t_is_cash:5!null t_s_symb:6!null t_qty:7!null t_ca_id:9!null trade.t_chrg:12!null t_lifo:15!null │ │ │ ├── constraint: /1: [/0 - /0] - │ │ │ ├── locking: for-update + │ │ │ ├── locking: for-update,durability-guaranteed │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── volatile │ │ │ ├── key: () diff --git a/pkg/sql/opt/xform/testdata/external/tpce-no-stats b/pkg/sql/opt/xform/testdata/external/tpce-no-stats index 670617b3d177..bda8d42365a8 100644 --- a/pkg/sql/opt/xform/testdata/external/tpce-no-stats +++ b/pkg/sql/opt/xform/testdata/external/tpce-no-stats @@ -3559,7 +3559,7 @@ project │ │ ├── scan trade │ │ │ ├── columns: t_id:1!null t_tt_id:4!null t_is_cash:5!null t_s_symb:6!null t_qty:7!null t_ca_id:9!null trade.t_chrg:12!null t_lifo:15!null │ │ │ ├── constraint: /1: [/0 - /0] - │ │ │ ├── locking: for-update + │ │ │ ├── locking: for-update,durability-guaranteed │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── volatile │ │ │ ├── key: () @@ -3616,7 +3616,7 @@ project │ │ ├── scan trade │ │ │ ├── columns: t_id:1!null t_tt_id:4!null t_is_cash:5!null t_s_symb:6!null t_qty:7!null t_ca_id:9!null trade.t_chrg:12!null t_lifo:15!null │ │ │ ├── constraint: /1: [/0 - /0] - │ │ │ ├── locking: for-update + │ │ │ ├── locking: for-update,durability-guaranteed │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── volatile │ │ │ ├── key: () diff --git a/pkg/sql/opt/xform/testdata/placeholder-fast-path/scan b/pkg/sql/opt/xform/testdata/placeholder-fast-path/scan index f45c1337140b..364b5f6c74d8 100644 --- a/pkg/sql/opt/xform/testdata/placeholder-fast-path/scan +++ b/pkg/sql/opt/xform/testdata/placeholder-fast-path/scan @@ -41,7 +41,7 @@ SELECT * FROM kv WHERE k = $1 FOR UPDATE ---- placeholder-scan kv ├── columns: k:1!null v:2 - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── cardinality: [0 - 1] ├── volatile, has-placeholder ├── stats: [rows=1, distinct(1)=1, null(1)=0] diff --git a/pkg/sql/opt/xform/testdata/rules/limit b/pkg/sql/opt/xform/testdata/rules/limit index 5459a17437a8..d5445685f432 100644 --- a/pkg/sql/opt/xform/testdata/rules/limit +++ b/pkg/sql/opt/xform/testdata/rules/limit @@ -255,7 +255,7 @@ SELECT * FROM a LIMIT 1 FOR UPDATE scan a ├── columns: k:1!null i:2 f:3 s:4 j:5 ├── limit: 1 - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile ├── key: () └── fd: ()-->(1-5) @@ -268,7 +268,7 @@ scan a@s_idx ├── columns: s:4!null ├── constraint: /4/1: [/'foo' - /'foo'] ├── limit: 1 - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile ├── key: () └── fd: ()-->(4) @@ -352,7 +352,7 @@ limit ├── ordering: +2 opt(1) [actual: +2] ├── index-join kuv │ ├── columns: k:1!null u:2 v:3 - │ ├── locking: for-update,skip-locked + │ ├── locking: for-update,skip-locked,durability-guaranteed │ ├── volatile │ ├── fd: ()-->(1) │ ├── ordering: +2 opt(1) [actual: +2] @@ -360,7 +360,7 @@ limit │ └── scan kuv@kuv_k_u_idx │ ├── columns: k:1!null u:2 rowid:4!null │ ├── constraint: /1/2/4: [/1 - /1] - │ ├── locking: for-update,skip-locked + │ ├── locking: for-update,skip-locked,durability-guaranteed │ ├── volatile │ ├── key: (4) │ ├── fd: ()-->(1), (4)-->(2) @@ -884,7 +884,7 @@ top-k ├── ordering: +2 └── scan kuv ├── columns: k:1 u:2 v:3 - ├── locking: for-update + ├── locking: for-update,durability-guaranteed └── volatile # ------------------------- diff --git a/pkg/sql/opt/xform/testdata/rules/scan b/pkg/sql/opt/xform/testdata/rules/scan index 851fd9d86982..597b189060e6 100644 --- a/pkg/sql/opt/xform/testdata/rules/scan +++ b/pkg/sql/opt/xform/testdata/rules/scan @@ -369,7 +369,7 @@ SELECT s, i, f FROM a ORDER BY s FOR UPDATE ---- scan a@s_idx ├── columns: s:4 i:2 f:3 - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile └── ordering: +4 diff --git a/pkg/sql/opt/xform/testdata/rules/select b/pkg/sql/opt/xform/testdata/rules/select index 0d5aaf128d3e..720893f985a8 100644 --- a/pkg/sql/opt/xform/testdata/rules/select +++ b/pkg/sql/opt/xform/testdata/rules/select @@ -1145,7 +1145,7 @@ SELECT k FROM a WHERE k = 1 FOR UPDATE scan a ├── columns: k:1!null ├── constraint: /1: [/1 - /1] - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── cardinality: [0 - 1] ├── volatile ├── key: () @@ -1156,7 +1156,7 @@ SELECT * FROM b WHERE v >= 1 AND v <= 10 FOR UPDATE ---- index-join b ├── columns: k:1!null u:2 v:3!null j:4 - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── cardinality: [0 - 10] ├── volatile ├── key: (1) @@ -1164,7 +1164,7 @@ index-join b └── scan b@v ├── columns: k:1!null v:3!null ├── constraint: /3: [/1 - /10] - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── cardinality: [0 - 10] ├── volatile ├── key: (1) @@ -1181,7 +1181,7 @@ select ├── fd: (1)-->(2-4), (3)-->(1,2,4) ├── index-join b │ ├── columns: k:1!null u:2 v:3 j:4 - │ ├── locking: for-update + │ ├── locking: for-update,durability-guaranteed │ ├── cardinality: [0 - 10] │ ├── volatile │ ├── key: (1) @@ -1189,7 +1189,7 @@ select │ └── scan b@v │ ├── columns: k:1!null v:3!null │ ├── constraint: /3: [/1 - /10] - │ ├── locking: for-update + │ ├── locking: for-update,durability-guaranteed │ ├── cardinality: [0 - 10] │ ├── volatile │ ├── key: (1) @@ -5219,7 +5219,7 @@ project ├── columns: k:1!null ├── inverted constraint: /7/1 │ └── spans: ["7a\x00\x01\x12b\x00\x01", "7a\x00\x01\x12b\x00\x01"] - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile └── key: (1) @@ -9127,8 +9127,8 @@ inner-join (zigzag pqr@q pqr@r) ├── eq columns: [1] = [1] ├── left fixed columns: [2] = [1] ├── right fixed columns: [3] = [2] - ├── left locking: for-update - ├── right locking: for-update + ├── left locking: for-update,durability-guaranteed + ├── right locking: for-update,durability-guaranteed ├── volatile ├── fd: ()-->(2,3) └── filters @@ -9605,7 +9605,7 @@ inner-join (lookup b) ├── columns: k:1!null u:2 v:3 j:4!null ├── key columns: [1] = [1] ├── lookup columns are key - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile ├── key: (1) ├── fd: (1)-->(2-4), (3)~~>(1,2,4) @@ -9614,8 +9614,8 @@ inner-join (lookup b) │ ├── eq columns: [1] = [1] │ ├── left fixed columns: [9] = ['\x376100012a0200'] │ ├── right fixed columns: [9] = ['\x376300012a0400'] - │ ├── left locking: for-update - │ ├── right locking: for-update + │ ├── left locking: for-update,durability-guaranteed + │ ├── right locking: for-update,durability-guaranteed │ └── filters (true) └── filters (true) diff --git a/pkg/sql/opt/xform/testdata/rules/select_for_update b/pkg/sql/opt/xform/testdata/rules/select_for_update index b3480de405a8..361b780373d1 100644 --- a/pkg/sql/opt/xform/testdata/rules/select_for_update +++ b/pkg/sql/opt/xform/testdata/rules/select_for_update @@ -24,14 +24,14 @@ SELECT * FROM indexed WHERE b = 2 FOR UPDATE ---- index-join indexed ├── columns: a:1!null b:2!null c:3 - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile ├── key: (1) ├── fd: ()-->(2), (1)-->(3) └── scan indexed@b_idx ├── columns: a:1!null b:2!null ├── constraint: /2/1: [/2 - /2] - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile ├── key: (1) └── fd: ()-->(2) @@ -41,14 +41,14 @@ SELECT * FROM indexed WHERE b BETWEEN 2 AND 10 FOR UPDATE ---- index-join indexed ├── columns: a:1!null b:2!null c:3 - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile ├── key: (1) ├── fd: (1)-->(2,3) └── scan indexed@b_idx ├── columns: a:1!null b:2!null ├── constraint: /2/1: [/2 - /10] - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile ├── key: (1) └── fd: (1)-->(2) @@ -70,7 +70,7 @@ project ├── columns: t.a:1!null b:2!null u.a:5!null c:6 ├── key columns: [2] = [5] ├── lookup columns are key - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── cardinality: [0 - 1] ├── volatile ├── key: () @@ -78,7 +78,7 @@ project ├── scan t │ ├── columns: t.a:1!null b:2 │ ├── constraint: /1: [/2 - /2] - │ ├── locking: for-update + │ ├── locking: for-update,durability-guaranteed │ ├── cardinality: [0 - 1] │ ├── volatile │ ├── key: () @@ -96,7 +96,7 @@ project ├── columns: t.a:1!null b:2!null u.a:5!null c:6 ├── key columns: [2] = [5] ├── lookup columns are key - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── cardinality: [0 - 9] ├── volatile ├── key: (1) @@ -104,7 +104,7 @@ project ├── scan t │ ├── columns: t.a:1!null b:2 │ ├── constraint: /1: [/2 - /10] - │ ├── locking: for-update + │ ├── locking: for-update,durability-guaranteed │ ├── cardinality: [0 - 9] │ ├── volatile │ ├── key: (1) @@ -118,21 +118,21 @@ inner-join (lookup indexed) ├── columns: a:1!null b:2!null a:5!null b:6!null c:7 ├── key columns: [5] = [5] ├── lookup columns are key - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile ├── key: (5) ├── fd: ()-->(1,2,6), (5)-->(7), (2)==(6), (6)==(2) ├── inner-join (lookup indexed@b_idx) │ ├── columns: t.a:1!null t.b:2!null indexed.a:5!null indexed.b:6!null │ ├── key columns: [2] = [6] - │ ├── locking: for-update + │ ├── locking: for-update,durability-guaranteed │ ├── volatile │ ├── key: (5) │ ├── fd: ()-->(1,2,6), (2)==(6), (6)==(2) │ ├── scan t │ │ ├── columns: t.a:1!null t.b:2 │ │ ├── constraint: /1: [/2 - /2] - │ │ ├── locking: for-update + │ │ ├── locking: for-update,durability-guaranteed │ │ ├── cardinality: [0 - 1] │ │ ├── volatile │ │ ├── key: () @@ -160,7 +160,7 @@ inner-join (lookup inverted) ├── columns: a:1!null b:2!null c:3 ├── key columns: [1] = [1] ├── lookup columns are key - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile ├── key: (1) ├── fd: (1)-->(2,3) @@ -169,8 +169,8 @@ inner-join (lookup inverted) │ ├── eq columns: [1] = [1] │ ├── left fixed columns: [6] = ['\x89'] │ ├── right fixed columns: [6] = ['\x8a'] - │ ├── left locking: for-update - │ ├── right locking: for-update + │ ├── left locking: for-update,durability-guaranteed + │ ├── right locking: for-update,durability-guaranteed │ └── filters (true) └── filters (true) @@ -184,7 +184,7 @@ select ├── fd: (1)-->(2,3) ├── index-join inverted │ ├── columns: a:1!null b:2 c:3 - │ ├── locking: for-update + │ ├── locking: for-update,durability-guaranteed │ ├── volatile │ ├── key: (1) │ ├── fd: (1)-->(2,3) @@ -203,7 +203,7 @@ select │ │ └── spans │ │ ├── ["C", "C"] │ │ └── ["\x89", "\x8b") - │ ├── locking: for-update + │ ├── locking: for-update,durability-guaranteed │ ├── volatile │ ├── key: (1) │ └── fd: (1)-->(6) @@ -217,7 +217,7 @@ inner-join (lookup inverted [as=i1]) ├── columns: a:1!null b:2 c:3 a:7!null b:8 c:9 ├── key columns: [19] = [1] ├── lookup columns are key - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile ├── key: (1,7) ├── fd: (1)-->(2,3), (7)-->(8,9) @@ -225,13 +225,13 @@ inner-join (lookup inverted [as=i1]) │ ├── columns: i2.a:7!null i2.b:8 i2.c:9 i1.a:19!null │ ├── inverted-expr │ │ └── i1.b:20 @> i2.b:8 - │ ├── locking: for-update + │ ├── locking: for-update,durability-guaranteed │ ├── volatile │ ├── key: (7,19) │ ├── fd: (7)-->(8,9) │ ├── scan inverted [as=i2] │ │ ├── columns: i2.a:7!null i2.b:8 i2.c:9 - │ │ ├── locking: for-update + │ │ ├── locking: for-update,durability-guaranteed │ │ ├── volatile │ │ ├── key: (7) │ │ └── fd: (7)-->(8,9) @@ -263,8 +263,8 @@ inner-join (zigzag zigzag@b_idx zigzag@c_idx) ├── eq columns: [1] = [1] ├── left fixed columns: [2] = [5] ├── right fixed columns: [3] = [6.0] - ├── left locking: for-update - ├── right locking: for-update + ├── left locking: for-update,durability-guaranteed + ├── right locking: for-update,durability-guaranteed ├── volatile ├── key: (1) ├── fd: ()-->(2,3) @@ -279,7 +279,7 @@ inner-join (lookup zigzag) ├── columns: a:1!null b:2 c:3 d:4!null ├── key columns: [1] = [1] ├── lookup columns are key - ├── locking: for-update + ├── locking: for-update,durability-guaranteed ├── volatile ├── key: (1) ├── fd: (1)-->(2-4) @@ -288,7 +288,7 @@ inner-join (lookup zigzag) │ ├── eq columns: [1] = [1] │ ├── left fixed columns: [7] = ['\x3761000262000112630001'] │ ├── right fixed columns: [7] = ['\x3766000112670001'] - │ ├── left locking: for-update - │ ├── right locking: for-update + │ ├── left locking: for-update,durability-guaranteed + │ ├── right locking: for-update,durability-guaranteed │ └── filters (true) └── filters (true) diff --git a/pkg/sql/opt_exec_factory.go b/pkg/sql/opt_exec_factory.go index d794c7622210..879f5f2988f4 100644 --- a/pkg/sql/opt_exec_factory.go +++ b/pkg/sql/opt_exec_factory.go @@ -169,6 +169,7 @@ func (ef *execFactory) ConstructScan( scan.estimatedRowCount = uint64(params.EstimatedRowCount) scan.lockingStrength = descpb.ToScanLockingStrength(params.Locking.Strength) scan.lockingWaitPolicy = descpb.ToScanLockingWaitPolicy(params.Locking.WaitPolicy) + scan.lockingDurability = descpb.ToScanLockingDurability(params.Locking.Durability) scan.localityOptimized = params.LocalityOptimized if !ef.isExplain && !ef.planner.SessionData().Internal { idxUsageKey := roachpb.IndexUsageKey{ @@ -665,6 +666,7 @@ func (ef *execFactory) ConstructIndexJoin( tableScan.disableBatchLimit() tableScan.lockingStrength = descpb.ToScanLockingStrength(locking.Strength) tableScan.lockingWaitPolicy = descpb.ToScanLockingWaitPolicy(locking.WaitPolicy) + tableScan.lockingDurability = descpb.ToScanLockingDurability(locking.Durability) if !ef.isExplain && !ef.planner.SessionData().Internal { idxUsageKey := roachpb.IndexUsageKey{ @@ -725,6 +727,7 @@ func (ef *execFactory) ConstructLookupJoin( tableScan.index = idx tableScan.lockingStrength = descpb.ToScanLockingStrength(locking.Strength) tableScan.lockingWaitPolicy = descpb.ToScanLockingWaitPolicy(locking.WaitPolicy) + tableScan.lockingDurability = descpb.ToScanLockingDurability(locking.Durability) if !ef.isExplain && !ef.planner.SessionData().Internal { idxUsageKey := roachpb.IndexUsageKey{ @@ -865,6 +868,7 @@ func (ef *execFactory) ConstructInvertedJoin( tableScan.index = idx tableScan.lockingStrength = descpb.ToScanLockingStrength(locking.Strength) tableScan.lockingWaitPolicy = descpb.ToScanLockingWaitPolicy(locking.WaitPolicy) + tableScan.lockingDurability = descpb.ToScanLockingDurability(locking.Durability) if !ef.isExplain && !ef.planner.SessionData().Internal { idxUsageKey := roachpb.IndexUsageKey{ @@ -945,6 +949,7 @@ func (ef *execFactory) constructScanForZigzag( scan.index = idxDesc scan.lockingStrength = descpb.ToScanLockingStrength(locking.Strength) scan.lockingWaitPolicy = descpb.ToScanLockingWaitPolicy(locking.WaitPolicy) + scan.lockingDurability = descpb.ToScanLockingDurability(locking.Durability) return scan, eqColOrdinals, nil } diff --git a/pkg/sql/row/fetcher.go b/pkg/sql/row/fetcher.go index b2ed310d3ee9..eff9e8d8723d 100644 --- a/pkg/sql/row/fetcher.go +++ b/pkg/sql/row/fetcher.go @@ -299,6 +299,8 @@ type FetcherInitArgs struct { // LockWaitPolicy represents the policy to be used for handling conflicting // locks held by other active transactions. LockWaitPolicy descpb.ScanLockingWaitPolicy + // LockDurability represents the row-level locking durability to use. + LockDurability descpb.ScanLockingDurability // LockTimeout specifies the maximum amount of time that the fetcher will // wait while attempting to acquire a lock on a key or while blocking on an // existing lock in order to perform a non-locking read on a key. @@ -440,6 +442,7 @@ func (rf *Fetcher) Init(ctx context.Context, args FetcherInitArgs) error { reverse: args.Reverse, lockStrength: args.LockStrength, lockWaitPolicy: args.LockWaitPolicy, + lockDurability: args.LockDurability, lockTimeout: args.LockTimeout, acc: rf.kvFetcherMemAcc, forceProductionKVBatchSize: args.ForceProductionKVBatchSize, diff --git a/pkg/sql/row/kv_batch_fetcher.go b/pkg/sql/row/kv_batch_fetcher.go index ce49e7fe3ee8..a1a991c1f550 100644 --- a/pkg/sql/row/kv_batch_fetcher.go +++ b/pkg/sql/row/kv_batch_fetcher.go @@ -180,6 +180,8 @@ type txnKVFetcher struct { // lockWaitPolicy represents the policy to be used for handling conflicting // locks held by other active transactions. lockWaitPolicy lock.WaitPolicy + // lockDurability represents the locking durability to use. + lockDurability lock.Durability // lockTimeout specifies the maximum amount of time that the fetcher will // wait while attempting to acquire a lock on a key or while blocking on an // existing lock in order to perform a non-locking read on a key. @@ -287,6 +289,7 @@ type newTxnKVFetcherArgs struct { reverse bool lockStrength descpb.ScanLockingStrength lockWaitPolicy descpb.ScanLockingWaitPolicy + lockDurability descpb.ScanLockingDurability lockTimeout time.Duration acc *mon.BoundAccount forceProductionKVBatchSize bool @@ -314,6 +317,7 @@ func newTxnKVFetcherInternal(args newTxnKVFetcherArgs) *txnKVFetcher { reverse: args.reverse, lockStrength: GetKeyLockingStrength(args.lockStrength), lockWaitPolicy: GetWaitPolicy(args.lockWaitPolicy), + lockDurability: GetKeyLockingDurability(args.lockDurability), lockTimeout: args.lockTimeout, acc: args.acc, forceProductionKVBatchSize: args.forceProductionKVBatchSize, @@ -544,7 +548,9 @@ func (f *txnKVFetcher) fetch(ctx context.Context) error { ba.Header.WholeRowsOfSize = int32(f.indexFetchSpec.MaxKeysPerRow) } ba.AdmissionHeader = f.requestAdmissionHeader - ba.Requests = spansToRequests(f.spans.Spans, f.scanFormat, f.reverse, f.lockStrength, f.reqsScratch) + ba.Requests = spansToRequests( + f.spans.Spans, f.scanFormat, f.reverse, f.lockStrength, f.lockDurability, f.reqsScratch, + ) if log.ExpensiveLogEnabled(ctx, 2) { log.VEventf(ctx, 2, "Scan %s", f.spans) @@ -889,7 +895,8 @@ func spansToRequests( spans roachpb.Spans, scanFormat kvpb.ScanFormat, reverse bool, - keyLocking lock.Strength, + lockStrength lock.Strength, + lockDurability lock.Durability, reqsScratch []kvpb.RequestUnion, ) []kvpb.RequestUnion { var reqs []kvpb.RequestUnion @@ -923,8 +930,8 @@ func spansToRequests( // A span without an EndKey indicates that the caller is requesting a // single key fetch, which can be served using a GetRequest. gets[curGet].req.Key = spans[i].Key - gets[curGet].req.KeyLockingStrength = keyLocking - // TODO(michae2): Once #100193 is finished, also include locking durability. + gets[curGet].req.KeyLockingStrength = lockStrength + gets[curGet].req.KeyLockingDurability = lockDurability gets[curGet].union.Get = &gets[curGet].req reqs[i].Value = &gets[curGet].union curGet++ @@ -933,8 +940,8 @@ func spansToRequests( curScan := i - curGet scans[curScan].req.SetSpan(spans[i]) scans[curScan].req.ScanFormat = scanFormat - scans[curScan].req.KeyLockingStrength = keyLocking - // TODO(michae2): Once #100193 is finished, also include locking durability. + scans[curScan].req.KeyLockingStrength = lockStrength + scans[curScan].req.KeyLockingDurability = lockDurability scans[curScan].union.ReverseScan = &scans[curScan].req reqs[i].Value = &scans[curScan].union } @@ -948,8 +955,8 @@ func spansToRequests( // A span without an EndKey indicates that the caller is requesting a // single key fetch, which can be served using a GetRequest. gets[curGet].req.Key = spans[i].Key - gets[curGet].req.KeyLockingStrength = keyLocking - // TODO(michae2): Once #100193 is finished, also include locking durability. + gets[curGet].req.KeyLockingStrength = lockStrength + gets[curGet].req.KeyLockingDurability = lockDurability gets[curGet].union.Get = &gets[curGet].req reqs[i].Value = &gets[curGet].union curGet++ @@ -958,8 +965,8 @@ func spansToRequests( curScan := i - curGet scans[curScan].req.SetSpan(spans[i]) scans[curScan].req.ScanFormat = scanFormat - scans[curScan].req.KeyLockingStrength = keyLocking - // TODO(michae2): Once #100193 is finished, also include locking durability. + scans[curScan].req.KeyLockingStrength = lockStrength + scans[curScan].req.KeyLockingDurability = lockDurability scans[curScan].union.Scan = &scans[curScan].req reqs[i].Value = &scans[curScan].union } diff --git a/pkg/sql/row/kv_batch_streamer.go b/pkg/sql/row/kv_batch_streamer.go index 7cf8965299db..f181ef217c70 100644 --- a/pkg/sql/row/kv_batch_streamer.go +++ b/pkg/sql/row/kv_batch_streamer.go @@ -27,8 +27,9 @@ import ( // txnKVStreamer handles retrieval of key/values. type txnKVStreamer struct { kvBatchFetcherHelper - streamer *kvstreamer.Streamer - keyLocking lock.Strength + streamer *kvstreamer.Streamer + lockStrength lock.Strength + lockDurability lock.Durability spans roachpb.Spans spanIDs []int @@ -53,14 +54,16 @@ var _ KVBatchFetcher = &txnKVStreamer{} func newTxnKVStreamer( streamer *kvstreamer.Streamer, lockStrength descpb.ScanLockingStrength, + lockDurability descpb.ScanLockingDurability, acc *mon.BoundAccount, kvPairsRead *int64, batchRequestsIssued *int64, ) KVBatchFetcher { f := &txnKVStreamer{ - streamer: streamer, - keyLocking: GetKeyLockingStrength(lockStrength), - acc: acc, + streamer: streamer, + lockStrength: GetKeyLockingStrength(lockStrength), + lockDurability: GetKeyLockingDurability(lockDurability), + acc: acc, } f.kvBatchFetcherHelper.init(f.nextBatch, kvPairsRead, batchRequestsIssued) return f @@ -99,7 +102,7 @@ func (f *txnKVStreamer) SetupNextFetch( reqsScratch[i] = kvpb.RequestUnion{} } // TODO(yuzefovich): consider supporting COL_BATCH_RESPONSE scan format. - reqs := spansToRequests(spans, kvpb.BATCH_RESPONSE, false /* reverse */, f.keyLocking, reqsScratch) + reqs := spansToRequests(spans, kvpb.BATCH_RESPONSE, false /* reverse */, f.lockStrength, f.lockDurability, reqsScratch) if err := f.streamer.Enqueue(ctx, reqs); err != nil { return err } diff --git a/pkg/sql/row/kv_fetcher.go b/pkg/sql/row/kv_fetcher.go index dc762e2c7828..c3bc38b9658c 100644 --- a/pkg/sql/row/kv_fetcher.go +++ b/pkg/sql/row/kv_fetcher.go @@ -56,6 +56,7 @@ func newTxnKVFetcher( reverse bool, lockStrength descpb.ScanLockingStrength, lockWaitPolicy descpb.ScanLockingWaitPolicy, + lockDurability descpb.ScanLockingDurability, lockTimeout time.Duration, acc *mon.BoundAccount, forceProductionKVBatchSize bool, @@ -93,6 +94,7 @@ func newTxnKVFetcher( reverse: reverse, lockStrength: lockStrength, lockWaitPolicy: lockWaitPolicy, + lockDurability: lockDurability, lockTimeout: lockTimeout, acc: acc, forceProductionKVBatchSize: forceProductionKVBatchSize, @@ -122,12 +124,13 @@ func NewDirectKVBatchFetcher( reverse bool, lockStrength descpb.ScanLockingStrength, lockWaitPolicy descpb.ScanLockingWaitPolicy, + lockDurability descpb.ScanLockingDurability, lockTimeout time.Duration, acc *mon.BoundAccount, forceProductionKVBatchSize bool, ) KVBatchFetcher { f := newTxnKVFetcher( - txn, bsHeader, reverse, lockStrength, lockWaitPolicy, + txn, bsHeader, reverse, lockStrength, lockWaitPolicy, lockDurability, lockTimeout, acc, forceProductionKVBatchSize, ) f.scanFormat = kvpb.COL_BATCH_RESPONSE @@ -147,12 +150,13 @@ func NewKVFetcher( reverse bool, lockStrength descpb.ScanLockingStrength, lockWaitPolicy descpb.ScanLockingWaitPolicy, + lockDurability descpb.ScanLockingDurability, lockTimeout time.Duration, acc *mon.BoundAccount, forceProductionKVBatchSize bool, ) *KVFetcher { return newKVFetcher(newTxnKVFetcher( - txn, bsHeader, reverse, lockStrength, lockWaitPolicy, + txn, bsHeader, reverse, lockStrength, lockWaitPolicy, lockDurability, lockTimeout, acc, forceProductionKVBatchSize, )) } @@ -168,6 +172,7 @@ func NewStreamingKVFetcher( st *cluster.Settings, lockWaitPolicy descpb.ScanLockingWaitPolicy, lockStrength descpb.ScanLockingStrength, + lockDurability descpb.ScanLockingDurability, streamerBudgetLimit int64, streamerBudgetAcc *mon.BoundAccount, maintainOrdering bool, @@ -189,6 +194,7 @@ func NewStreamingKVFetcher( &kvPairsRead, &batchRequestsIssued, GetKeyLockingStrength(lockStrength), + GetKeyLockingDurability(lockDurability), ) mode := kvstreamer.OutOfOrder if maintainOrdering { @@ -203,7 +209,7 @@ func NewStreamingKVFetcher( maxKeysPerRow, diskBuffer, ) - return newKVFetcher(newTxnKVStreamer(streamer, lockStrength, kvFetcherMemAcc, &kvPairsRead, &batchRequestsIssued)) + return newKVFetcher(newTxnKVStreamer(streamer, lockStrength, lockDurability, kvFetcherMemAcc, &kvPairsRead, &batchRequestsIssued)) } func newKVFetcher(batchFetcher KVBatchFetcher) *KVFetcher { diff --git a/pkg/sql/row/locking.go b/pkg/sql/row/locking.go index adad0d61bbcd..8704f5a22d3a 100644 --- a/pkg/sql/row/locking.go +++ b/pkg/sql/row/locking.go @@ -57,3 +57,18 @@ func GetWaitPolicy(lockWaitPolicy descpb.ScanLockingWaitPolicy) lock.WaitPolicy panic(errors.AssertionFailedf("unknown wait policy %s", lockWaitPolicy)) } } + +// GetKeyLockingDurability returns the configured lock durability to use for +// key-value scans. +func GetKeyLockingDurability(lockDurability descpb.ScanLockingDurability) lock.Durability { + switch lockDurability { + case descpb.ScanLockingDurability_BEST_EFFORT: + return lock.Unreplicated + + case descpb.ScanLockingDurability_GUARANTEED: + return lock.Replicated + + default: + panic(errors.AssertionFailedf("unknown lock durability %s", lockDurability)) + } +} diff --git a/pkg/sql/rowexec/inverted_joiner.go b/pkg/sql/rowexec/inverted_joiner.go index 79e7057e0a76..b8f14c7a67fc 100644 --- a/pkg/sql/rowexec/inverted_joiner.go +++ b/pkg/sql/rowexec/inverted_joiner.go @@ -296,6 +296,7 @@ func newInvertedJoiner( Txn: flowCtx.Txn, LockStrength: spec.LockingStrength, LockWaitPolicy: spec.LockingWaitPolicy, + LockDurability: spec.LockingDurability, LockTimeout: flowCtx.EvalCtx.SessionData().LockTimeout, Alloc: &ij.alloc, MemMonitor: flowCtx.Mon, diff --git a/pkg/sql/rowexec/joinreader.go b/pkg/sql/rowexec/joinreader.go index cfda23f22dc7..2a79282e6393 100644 --- a/pkg/sql/rowexec/joinreader.go +++ b/pkg/sql/rowexec/joinreader.go @@ -536,6 +536,7 @@ func newJoinReader( flowCtx.EvalCtx.Settings, spec.LockingWaitPolicy, spec.LockingStrength, + spec.LockingDurability, streamerBudgetLimit, &jr.streamerInfo.budgetAcc, jr.streamerInfo.maintainOrdering, @@ -562,6 +563,7 @@ func newJoinReader( Txn: jr.txn, LockStrength: spec.LockingStrength, LockWaitPolicy: spec.LockingWaitPolicy, + LockDurability: spec.LockingDurability, LockTimeout: flowCtx.EvalCtx.SessionData().LockTimeout, Alloc: &jr.alloc, MemMonitor: flowCtx.Mon, diff --git a/pkg/sql/rowexec/tablereader.go b/pkg/sql/rowexec/tablereader.go index b9d82c65fb99..4e05efb339a0 100644 --- a/pkg/sql/rowexec/tablereader.go +++ b/pkg/sql/rowexec/tablereader.go @@ -153,6 +153,7 @@ func newTableReader( Reverse: spec.Reverse, LockStrength: spec.LockingStrength, LockWaitPolicy: spec.LockingWaitPolicy, + LockDurability: spec.LockingDurability, LockTimeout: flowCtx.EvalCtx.SessionData().LockTimeout, Alloc: &tr.alloc, MemMonitor: flowCtx.Mon, diff --git a/pkg/sql/rowexec/zigzagjoiner.go b/pkg/sql/rowexec/zigzagjoiner.go index 9117a0b327e9..521e25d8fa95 100644 --- a/pkg/sql/rowexec/zigzagjoiner.go +++ b/pkg/sql/rowexec/zigzagjoiner.go @@ -470,6 +470,7 @@ func (z *zigzagJoiner) setupInfo( Txn: flowCtx.Txn, LockStrength: spec.LockingStrength, LockWaitPolicy: spec.LockingWaitPolicy, + LockDurability: spec.LockingDurability, LockTimeout: flowCtx.EvalCtx.SessionData().LockTimeout, Alloc: &info.alloc, MemMonitor: flowCtx.Mon, diff --git a/pkg/sql/scan.go b/pkg/sql/scan.go index 3a2b345adcfc..c8a981fa6432 100644 --- a/pkg/sql/scan.go +++ b/pkg/sql/scan.go @@ -88,10 +88,11 @@ type scanNode struct { // set to zero. estimatedRowCount uint64 - // lockingStrength and lockingWaitPolicy represent the row-level locking - // mode of the Scan. + // lockingStrength, lockingWaitPolicy, and lockingDurability represent the + // row-level locking mode of the Scan. lockingStrength descpb.ScanLockingStrength lockingWaitPolicy descpb.ScanLockingWaitPolicy + lockingDurability descpb.ScanLockingDurability // containsSystemColumns holds whether or not this scan is expected to // produce any system columns.