cockroachdb / cockroachdb/cockroach
sql: SELECT FOR UPDATE not able to be optimized away
- Dominant language
- Go
- Stars
- 32.5k
- Forks
- 4.1k
- PR merge metrics
- PR metrics pending
Description
This exact combination of SELECT FOR UPDATE, EXISTS, and NULL parameter using a prepared statement is able to be optimized to a constant `false` in 22.2.16 but becomes a full table scan in 23.1.11:
```sql
CREATE TABLE a (a INT, INDEX (a));
PREPARE p AS SELECT EXISTS (SELECT NULL FROM a WHERE a = $1 FOR UPDATE);
EXPLAIN ANALYZE EXECUTE p (NULL);
```
Here's v22.2.16:
```
demo@127.0.0.1:26257/defaultdb> CREATE TABLE a (a INT, INDEX (a)); PREPARE p AS SELECT EXISTS (SELECT NULL FROM a WHERE a = $1 FOR UPDATE); EXPLAIN ANALYZE EXECUTE p (NULL);
CREATE TABLE
Time: 2ms total (execution 2ms / network 0ms)
PREPARE
Time: 5ms total (execution 4ms / network 0ms)
info
-----------------------------------
planning time: 406µs
execution time: 476µs
distribution: local
vectorized: true
maximum memory usage: 10 KiB
network usage: 0 B (0 messages)
regions: us-east1
• values
nodes: n1
regions: us-east1
actual row count: 1
size: 1 column, 1 row
(13 rows)
Time: 1ms total (execution 1ms / network 0ms)
```
Here's v23.1.11:
```
demo@127.0.0.1:26257/defaultdb> CREATE TABLE a (a INT, INDEX (a));
-> PREPARE p AS SELECT EXISTS (SELECT NULL FROM a WHERE a = $1 FOR UPDATE);
-> EXPLAIN ANALYZE EXECUTE p (NULL);
->
CREATE TABLE
Time: 2ms total (execution 2ms / network 0ms)
PREPARE
Time: 8ms total (execution 8ms / network 0ms)
info
--------------------------------------------------------------------
planning time: 124µs
execution time: 372µs
distribution: local
vectorized: true
cumulative time spent in KV: 101µs
maximum memory usage: 30 KiB
network usage: 0 B (0 messages)
regions: us-east1
sql cpu time: 39µs
• root
│
├── • values
│ nodes: n1
│ regions: us-east1
│ actual row count: 1
│ sql cpu time: 11µs
│ size: 1 column, 1 row
│
└── • subquery
│ id: @S1
│ original sql: (SELECT NULL FROM a WHERE a = $1 FOR UPDATE)
│ exec mode: one row
│
└── • render
│
└── • filter
│ nodes: n1
│ regions: us-east1
│ actual row count: 0
│ sql cpu time: 24µs
│ estimated row count: 0
│ filter: false
│
└── • scan
nodes: n1
regions: us-east1
actual row count: 0
KV time: 101µs
KV contention time: 0µs
KV rows read: 0
KV bytes read: 0 B
KV gRPC calls: 1
estimated max memory allocated: 20 KiB
sql cpu time: 4µs
missing stats
table: a@a_pkey
spans: FULL SCAN
locking strength: for update
(49 rows)
Time: 1ms total (execution 1ms / network 0ms)
```
In v23.2.0-alpha.6 it's better if we use the new SELECT FOR UPDATE behavior (`optimizer_use_lock_op_for_serializable`):
```
demo@127.0.0.1:26257/system/defaultdb> SET optimizer_use_lock_op_for_serializable = true;
SET
Time: 0ms total (execution 0ms / network 0ms)
demo@127.0.0.1:26257/system/defaultdb> EXPLAIN ANALYZE EXECUTE p (NULL);
info
--------------------------------------------------------------------
planning time: 1ms
execution time: 201µs
distribution: local
vectorized: true
maximum memory usage: 10 KiB
network usage: 0 B (0 messages)
regions: us-east1
isolation level: serializable
priority: normal
quality of service: regular
• root
│
├── • values
│ nodes: n1
│ regions: us-east1
│ actual row count: 1
│ size: 1 column, 1 row
│
└── • subquery
│ id: @S1
│ original sql: (SELECT NULL FROM a WHERE a = $1 FOR UPDATE)
│ exec mode: one row
│
└── • render
│
└── • lookup join (semi)
│ nodes: n1
│ regions: us-east1
│ actual row count: 0
│ KV time: 0µs
│ KV contention time: 0µs
│ KV rows decoded: 0
│ KV bytes read: 0 B
│ KV gRPC calls: 0
│ estimated max memory allocated: 0 B
│ estimated row count: 0
│ table: a@a_pkey
│ equality: (rowid) = (rowid)
│ equality cols are key
│ locking strength: for update
│
└── • norows
nodes: n1
regions: us-east1
actual row count: 0
(46 rows)
Time: 2ms total (execution 2ms / network 0ms)
```
Jira issue: CRDB-33431
Contributor guide
Assessment
This issue has not been assessed yet.