Commit 3929c6b5 authored by craig[bot]'s avatar craig[bot]

Merge #46676

46676: opt: don't elide scans that have locking clauses r=RaduBerinde a=RaduBerinde

In this change we mark Scans as side-effecting when they have a locking clause.
This allows patterns like using WITH to lock some rows that are otherwise not
needed by the current statement.

Release note (bug fix): scans that lock rows (via FOR UPDATE) are no longer
elided when the results are unused.

Release justification: fix for new functionality.
Co-authored-by: default avatarRadu Berinde <[email protected]>
parents 31bce929 2f393c15
......@@ -604,6 +604,9 @@ merge-join · ·
# Unlike with FROM subqueries, row-level locking clauses do not apply to WITH
# queries referenced by the primary query. To achieve row locking within a WITH
# query, a locking clause should be specified within the WITH query.
#
# Note that scans with locking are considered to be side-effecting; CTEs that
# contain locking clauses are not inlined.
# ------------------------------------------------------------------------------
query TTT
......@@ -629,24 +632,63 @@ render · ·
query TTT
EXPLAIN SELECT * FROM [SELECT a FROM t FOR UPDATE]
----
· distributed false
· vectorized true
render · ·
└── scan · ·
· table [email protected]
· spans ALL
· locking strength for update
· distributed false
· vectorized false
root · ·
├── scan buffer node · ·
│ label buffer 1
└── subquery · ·
│ id @S1
│ original sql SELECT a FROM t FOR UPDATE
│ exec mode all rows
└── buffer node · ·
│ label buffer 1
└── scan · ·
· table [email protected]
· spans ALL
· locking strength for update
query TTT
EXPLAIN WITH cte AS (SELECT a FROM t FOR UPDATE) SELECT * FROM cte
----
· distributed false
· vectorized true
render · ·
└── scan · ·
· table [email protected]
· spans ALL
· locking strength for update
· distributed false
· vectorized false
root · ·
├── scan buffer node · ·
│ label buffer 1 (cte)
└── subquery · ·
│ id @S1
│ original sql SELECT a FROM t FOR UPDATE
│ exec mode all rows
└── buffer node · ·
│ label buffer 1 (cte)
└── scan · ·
· table [email protected]
· spans ALL
· locking strength for update
# Verify that the unused CTE doesn't get eliminated.
# TODO(radu): we should at least not buffer the rows in this case.
query TTT
EXPLAIN WITH sfu AS (SELECT a FROM t FOR UPDATE)
SELECT c FROM u
----
· distributed false
· vectorized true
root · ·
├── scan · ·
│ table [email protected]
│ spans ALL
└── subquery · ·
│ id @S1
│ original sql SELECT a FROM t FOR UPDATE
│ exec mode all rows
└── buffer node · ·
│ label buffer 1 (sfu)
└── scan · ·
· table [email protected]
· spans ALL
· locking strength for update
# ------------------------------------------------------------------------------
# Tests with joins.
......
......@@ -62,6 +62,13 @@ func (b *logicalPropsBuilder) buildScanProps(scan *ScanExpr, rel *props.Relation
md := scan.Memo().Metadata()
hardLimit := scan.HardLimit.RowCount()
// Side Effects
// ------------
// A Locking option is a side-effect (we don't want to elide this scan).
if scan.Locking != nil {
rel.CanHaveSideEffects = true
}
// Output Columns
// --------------
// Scan output columns are stored in the definition.
......
......@@ -197,3 +197,16 @@ select
└── eq [type=bool, outer=(3), constraints=(/3: [/2000 - /2000]; tight), fd=()-->(3)]
├── variable: notnull_value:3 [type=int]
└── const: 2000 [type=int]
# The scan should be marked as side-effecting if FOR UPDATE is used.
build
SELECT * FROM a FOR UPDATE
----
scan a
├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null)
├── locking: for-update
├── side-effects
├── key: (1)
├── fd: (1)-->(2-4), (3,4)~~>(1,2)
├── prune: (1-4)
└── interesting orderings: (+1) (-3,+4,+1)
......@@ -1985,14 +1985,17 @@ SELECT q,r FROM pqr WHERE q = 1 AND r = 2 FOR UPDATE
----
select
├── columns: q:2!null r:3!null
├── side-effects
├── fd: ()-->(2,3)
├── index-join pqr
│ ├── columns: q:2 r:3
│ ├── side-effects
│ ├── fd: ()-->(2)
│ └── scan [email protected]
│ ├── columns: p:1!null q:2!null
│ ├── constraint: /2/1: [/1 - /1]
│ ├── locking: for-update
│ ├── side-effects
│ ├── key: (1)
│ └── fd: ()-->(2)
└── filters
......@@ -2599,16 +2602,19 @@ SELECT b,a FROM t5 WHERE b @> '{"a":1, "c":2}' FOR UPDATE
----
select
├── columns: b:2 a:1!null
├── side-effects
├── key: (1)
├── fd: (1)-->(2)
├── index-join t5
│ ├── columns: a:1!null b:2
│ ├── side-effects
│ ├── key: (1)
│ ├── fd: (1)-->(2)
│ └── scan [email protected]_idx
│ ├── columns: a:1!null
│ ├── constraint: /2/1: [/'{"a": 1}' - /'{"a": 1}']
│ ├── locking: for-update
│ ├── side-effects
│ └── key: (1)
└── filters
└── b:2 @> '{"a": 1, "c": 2}' [outer=(2)]
......
......@@ -130,6 +130,7 @@ scan a
├── columns: k:1!null i:2 f:3 s:4 j:5
├── limit: 1
├── locking: for-update
├── side-effects
├── key: ()
└── fd: ()-->(1-5)
......@@ -142,6 +143,7 @@ scan [email protected]_idx
├── constraint: /4/1: [/'foo' - /'foo']
├── limit: 1
├── locking: for-update
├── side-effects
├── key: ()
└── fd: ()-->(4)
......@@ -538,6 +540,7 @@ SELECT * FROM kuv ORDER BY u LIMIT 5 FOR UPDATE
index-join kuv
├── columns: k:1!null u:2 v:3
├── cardinality: [0 - 5]
├── side-effects
├── key: (1)
├── fd: (1)-->(2,3)
├── ordering: +2
......@@ -545,6 +548,7 @@ index-join kuv
├── columns: k:1!null u:2
├── limit: 5
├── locking: for-update
├── side-effects
├── key: (1)
├── fd: (1)-->(2)
└── ordering: +2
......@@ -492,6 +492,7 @@ SELECT s, i, f FROM a ORDER BY s FOR UPDATE
scan [email protected]_idx
├── columns: s:4 i:2 f:3
├── locking: for-update
├── side-effects
└── ordering: +4
# Collated strings are treated properly.
......
......@@ -638,6 +638,7 @@ scan a
├── constraint: /1: [/1 - /1]
├── locking: for-update
├── cardinality: [0 - 1]
├── side-effects
├── key: ()
└── fd: ()-->(1)
......@@ -647,6 +648,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
├── cardinality: [0 - 10]
├── side-effects
├── key: (1)
├── fd: (1)-->(2-4), (3)-->(1,2,4)
└── scan [email protected]
......@@ -654,6 +656,7 @@ index-join b
├── constraint: /3: [/1 - /10]
├── locking: for-update
├── cardinality: [0 - 10]
├── side-effects
├── key: (1)
└── fd: (1)-->(3), (3)-->(1)
......@@ -663,11 +666,13 @@ SELECT * FROM b WHERE v >= 1 AND v <= 10 AND k+u = 1 FOR UPDATE
select
├── columns: k:1!null u:2 v:3!null j:4
├── cardinality: [0 - 10]
├── side-effects
├── key: (1)
├── fd: (1)-->(2-4), (3)-->(1,2,4)
├── index-join b
│ ├── columns: k:1!null u:2 v:3 j:4
│ ├── cardinality: [0 - 10]
│ ├── side-effects
│ ├── key: (1)
│ ├── fd: (1)-->(2-4), (3)-->(1), (3)~~>(1,2,4)
│ └── scan [email protected]
......@@ -675,6 +680,7 @@ select
│ ├── constraint: /3: [/1 - /10]
│ ├── locking: for-update
│ ├── cardinality: [0 - 10]
│ ├── side-effects
│ ├── key: (1)
│ └── fd: (1)-->(3), (3)-->(1)
└── filters
......@@ -918,15 +924,18 @@ SELECT k FROM b WHERE j @> '{"a": "b"}' FOR UPDATE
----
project
├── columns: k:1!null
├── side-effects
├── key: (1)
└── index-join b
├── columns: k:1!null j:4
├── side-effects
├── key: (1)
├── fd: (1)-->(4)
└── scan [email protected]_idx
├── columns: k:1!null
├── constraint: /4/1: [/'{"a": "b"}' - /'{"a": "b"}']
├── locking: for-update
├── side-effects
└── key: (1)
# Tests for array inverted indexes.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment