exec-ddl
CREATE TABLE t (a INT PRIMARY KEY, b INT)
----

exec-ddl
CREATE TABLE u (a INT PRIMARY KEY, c INT)
----

exec-ddl
CREATE VIEW v AS SELECT a FROM t AS t2
----

exec-ddl
CREATE TABLE families (a INT PRIMARY KEY, b INT, c INT, FAMILY (a, b), FAMILY (c))
----

# ------------------------------------------------------------------------------
# Basic tests.
# ------------------------------------------------------------------------------

build
SELECT * FROM t FOR UPDATE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR UPDATE
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM t FOR NO KEY UPDATE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR NO KEY UPDATE
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-no-key-update
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM t FOR SHARE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR SHARE
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-share
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM t FOR KEY SHARE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR KEY SHARE
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-key-share
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM t FOR KEY SHARE FOR SHARE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR KEY SHARE FOR SHARE
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (9,10)
 ├── locking: for-share
 └── lock t
      ├── columns: a:1!null b:2
      ├── key columns: a:1
      ├── lock columns: (5,6)
      ├── locking: for-key-share
      └── project
           ├── columns: a:1!null b:2
           └── scan t
                └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM t FOR KEY SHARE FOR SHARE FOR NO KEY UPDATE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR KEY SHARE FOR SHARE FOR NO KEY UPDATE
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (13,14)
 ├── locking: for-no-key-update
 └── lock t
      ├── columns: a:1!null b:2
      ├── key columns: a:1
      ├── lock columns: (9,10)
      ├── locking: for-share
      └── lock t
           ├── columns: a:1!null b:2
           ├── key columns: a:1
           ├── lock columns: (5,6)
           ├── locking: for-key-share
           └── project
                ├── columns: a:1!null b:2
                └── scan t
                     └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM t FOR KEY SHARE FOR SHARE FOR NO KEY UPDATE FOR UPDATE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR KEY SHARE FOR SHARE FOR NO KEY UPDATE FOR UPDATE
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (17,18)
 ├── locking: for-update
 └── lock t
      ├── columns: a:1!null b:2
      ├── key columns: a:1
      ├── lock columns: (13,14)
      ├── locking: for-no-key-update
      └── lock t
           ├── columns: a:1!null b:2
           ├── key columns: a:1
           ├── lock columns: (9,10)
           ├── locking: for-share
           └── lock t
                ├── columns: a:1!null b:2
                ├── key columns: a:1
                ├── lock columns: (5,6)
                ├── locking: for-key-share
                └── project
                     ├── columns: a:1!null b:2
                     └── scan t
                          └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM t FOR UPDATE OF t
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR UPDATE OF t
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM t FOR UPDATE OF t2
----
error (42P01): relation "t2" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR UPDATE OF t2
----
error (42P01): relation "t2" in FOR UPDATE clause not found in FROM clause

build
SELECT 1 FROM t FOR UPDATE OF t
----
project
 ├── columns: "?column?":5!null
 ├── scan t
 │    ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
 │    └── locking: for-update
 └── projections
      └── 1 [as="?column?":5]

build set=optimizer_use_lock_op_for_serializable=true
SELECT 1 FROM t FOR UPDATE OF t
----
lock t
 ├── columns: "?column?":5!null  [hidden: a:1!null]
 ├── key columns: a:1
 ├── lock columns: (6,7)
 ├── locking: for-update
 └── project
      ├── columns: "?column?":5!null a:1!null
      ├── scan t
      │    └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
      └── projections
           └── 1 [as="?column?":5]

# ------------------------------------------------------------------------------
# Tests with table aliases.
# ------------------------------------------------------------------------------

build
SELECT * FROM t AS t2 FOR UPDATE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t AS t2 FOR UPDATE
----
lock t [as=t2]
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2
      └── scan t [as=t2]
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM t AS t2 FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t AS t2 FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM t AS t2 FOR UPDATE OF t2
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t AS t2 FOR UPDATE OF t2
----
lock t [as=t2]
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2
      └── scan t [as=t2]
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

# ------------------------------------------------------------------------------
# Tests with numeric table references.
# Cockroach numeric references start after 53 for user tables.
# ------------------------------------------------------------------------------

build
SELECT * FROM [53 AS t] FOR UPDATE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM [53 AS t] FOR UPDATE
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM [53 AS t] FOR UPDATE OF t
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM [53 AS t] FOR UPDATE OF t
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM [53 AS t] FOR UPDATE OF t2
----
error (42P01): relation "t2" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM [53 AS t] FOR UPDATE OF t2
----
error (42P01): relation "t2" in FOR UPDATE clause not found in FROM clause

# Test outer joins with numeric table references.

build
SELECT * FROM [53 AS t] LEFT JOIN [54 AS u] ON b = c FOR UPDATE
----
error (0A000): FOR UPDATE cannot be applied to the nullable side of an outer join

build
SELECT * FROM [53 AS t] LEFT JOIN [54 AS u] ON b = c FOR UPDATE of t
----
project
 ├── columns: a:1!null b:2 a:5 c:6
 └── left-join (hash)
      ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5 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
      ├── scan u
      │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      └── filters
           └── b:2 = c:6

build
SELECT * FROM [53 AS t] LEFT JOIN [54 AS u] ON b = c FOR SHARE of u
----
error (0A000): FOR SHARE cannot be applied to the nullable side of an outer join

build
SELECT * FROM [53 AS t] RIGHT JOIN [54 AS u] ON b = c FOR NO KEY UPDATE
----
error (0A000): FOR NO KEY UPDATE cannot be applied to the nullable side of an outer join

# ------------------------------------------------------------------------------
# Tests with views.
# ------------------------------------------------------------------------------

build
SELECT * FROM v FOR UPDATE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM v FOR UPDATE
----
lock t [as=t2]
 ├── columns: a:1!null
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null
      └── scan t [as=t2]
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM v FOR UPDATE OF v
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM v FOR UPDATE OF v
----
lock t [as=t2]
 ├── columns: a:1!null
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null
      └── scan t [as=t2]
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM v FOR UPDATE OF v2
----
error (42P01): relation "v2" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM v FOR UPDATE OF v2
----
error (42P01): relation "v2" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM v FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM v FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM v FOR UPDATE OF t2
----
error (42P01): relation "t2" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM v FOR UPDATE OF t2
----
error (42P01): relation "t2" in FOR UPDATE clause not found in FROM clause

# ------------------------------------------------------------------------------
# Tests with aliased views.
# ------------------------------------------------------------------------------

build
SELECT * FROM v AS v2 FOR UPDATE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM v AS v2 FOR UPDATE
----
lock t [as=t2]
 ├── columns: a:1!null
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null
      └── scan t [as=t2]
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM v AS v2 FOR UPDATE OF v
----
error (42P01): relation "v" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM v AS v2 FOR UPDATE OF v
----
error (42P01): relation "v" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM v AS v2 FOR UPDATE OF v2
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM v AS v2 FOR UPDATE OF v2
----
lock t [as=t2]
 ├── columns: a:1!null
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null
      └── scan t [as=t2]
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

# ------------------------------------------------------------------------------
# Tests with subqueries.
# 
# Row-level locking clauses only apply to subqueries in the FROM clause of a
# SELECT statement. They don't apply to subqueries in the projection or in
# the filter.
# ------------------------------------------------------------------------------

build
SELECT * FROM (SELECT a FROM t) FOR UPDATE
----
project
 ├── columns: a:1!null
 └── scan t
      ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
      └── locking: for-update

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (SELECT a FROM t) FOR UPDATE
----
lock t
 ├── columns: a:1!null
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null
      └── scan t
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM (SELECT a FROM t FOR UPDATE)
----
project
 ├── columns: a:1!null
 └── scan t
      ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
      └── locking: for-update

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (SELECT a FROM t FOR UPDATE)
----
lock t
 ├── columns: a:1!null
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null
      └── scan t
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM (SELECT a FROM t FOR NO KEY UPDATE) FOR KEY SHARE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (SELECT a FROM t FOR NO KEY UPDATE) FOR KEY SHARE
----
lock t
 ├── columns: a:1!null
 ├── key columns: a:1
 ├── lock columns: (9,10)
 ├── locking: for-key-share
 └── lock t
      ├── columns: a:1!null
      ├── key columns: a:1
      ├── lock columns: (5,6)
      ├── locking: for-no-key-update
      └── project
           ├── columns: a:1!null
           └── scan t
                └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM (SELECT a FROM t FOR KEY SHARE) FOR NO KEY UPDATE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (SELECT a FROM t FOR KEY SHARE) FOR NO KEY UPDATE
----
lock t
 ├── columns: a:1!null
 ├── key columns: a:1
 ├── lock columns: (9,10)
 ├── locking: for-no-key-update
 └── lock t
      ├── columns: a:1!null
      ├── key columns: a:1
      ├── lock columns: (5,6)
      ├── locking: for-key-share
      └── project
           ├── columns: a:1!null
           └── scan t
                └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM (SELECT a FROM t) FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (SELECT a FROM t) FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM (SELECT a FROM t FOR UPDATE OF t)
----
project
 ├── columns: a:1!null
 └── scan t
      ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
      └── locking: for-update

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (SELECT a FROM t FOR UPDATE OF t)
----
lock t
 ├── columns: a:1!null
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null
      └── scan t
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM (SELECT a FROM t) AS r FOR UPDATE
----
project
 ├── columns: a:1!null
 └── scan t
      ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
      └── locking: for-update

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (SELECT a FROM t) AS r FOR UPDATE
----
lock t
 ├── columns: a:1!null
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null
      └── scan t
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM (SELECT a FROM t FOR UPDATE) AS r
----
project
 ├── columns: a:1!null
 └── scan t
      ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
      └── locking: for-update

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (SELECT a FROM t FOR UPDATE) AS r
----
lock t
 ├── columns: a:1!null
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null
      └── scan t
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM (SELECT a FROM t) AS r FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (SELECT a FROM t) AS r FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM (SELECT a FROM t FOR UPDATE OF t) AS r
----
project
 ├── columns: a:1!null
 └── scan t
      ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
      └── locking: for-update

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (SELECT a FROM t FOR UPDATE OF t) AS r
----
lock t
 ├── columns: a:1!null
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null
      └── scan t
           └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT (SELECT a FROM t) FOR UPDATE
----
project
 ├── columns: a:5
 ├── values
 │    └── ()
 └── projections
      └── subquery [as=a:5]
           └── max1-row
                ├── columns: t.a:1!null
                └── project
                     ├── columns: t.a:1!null
                     └── scan t
                          └── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build set=optimizer_use_lock_op_for_serializable=true
SELECT (SELECT a FROM t) FOR UPDATE
----
project
 ├── columns: a:5
 ├── values
 │    └── ()
 └── projections
      └── subquery [as=a:5]
           └── max1-row
                ├── columns: t.a:1!null
                └── project
                     ├── columns: t.a:1!null
                     └── scan t
                          └── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT (SELECT a FROM t FOR UPDATE)
----
project
 ├── columns: a:5
 ├── values
 │    └── ()
 └── projections
      └── subquery [as=a:5]
           └── max1-row
                ├── columns: t.a:1!null
                └── 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

build set=optimizer_use_lock_op_for_serializable=true
SELECT (SELECT a FROM t FOR UPDATE)
----
project
 ├── columns: a:9
 ├── values
 │    └── ()
 └── projections
      └── subquery [as=a:9]
           └── max1-row
                ├── columns: t.a:1!null
                └── lock t
                     ├── columns: t.a:1!null
                     ├── key columns: t.a:1
                     ├── lock columns: (5,6)
                     ├── locking: for-update
                     └── project
                          ├── columns: t.a:1!null
                          └── scan t
                               └── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT (SELECT a FROM t) FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT (SELECT a FROM t) FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build
SELECT (SELECT a FROM t FOR UPDATE OF t)
----
project
 ├── columns: a:5
 ├── values
 │    └── ()
 └── projections
      └── subquery [as=a:5]
           └── max1-row
                ├── columns: t.a:1!null
                └── 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

build set=optimizer_use_lock_op_for_serializable=true
SELECT (SELECT a FROM t FOR UPDATE OF t)
----
project
 ├── columns: a:9
 ├── values
 │    └── ()
 └── projections
      └── subquery [as=a:9]
           └── max1-row
                ├── columns: t.a:1!null
                └── lock t
                     ├── columns: t.a:1!null
                     ├── key columns: t.a:1
                     ├── lock columns: (5,6)
                     ├── locking: for-update
                     └── project
                          ├── columns: t.a:1!null
                          └── scan t
                               └── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT (SELECT a FROM t) AS r FOR UPDATE
----
project
 ├── columns: r:5
 ├── values
 │    └── ()
 └── projections
      └── subquery [as=r:5]
           └── max1-row
                ├── columns: a:1!null
                └── project
                     ├── columns: a:1!null
                     └── scan t
                          └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build set=optimizer_use_lock_op_for_serializable=true
SELECT (SELECT a FROM t) AS r FOR UPDATE
----
project
 ├── columns: r:5
 ├── values
 │    └── ()
 └── projections
      └── subquery [as=r:5]
           └── max1-row
                ├── columns: a:1!null
                └── project
                     ├── columns: a:1!null
                     └── scan t
                          └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT (SELECT a FROM t FOR UPDATE) AS r
----
project
 ├── columns: r:5
 ├── values
 │    └── ()
 └── projections
      └── subquery [as=r:5]
           └── max1-row
                ├── columns: a:1!null
                └── project
                     ├── columns: a:1!null
                     └── scan t
                          ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
                          └── locking: for-update

build set=optimizer_use_lock_op_for_serializable=true
SELECT (SELECT a FROM t FOR UPDATE) AS r
----
project
 ├── columns: r:9
 ├── values
 │    └── ()
 └── projections
      └── subquery [as=r:9]
           └── max1-row
                ├── columns: a:1!null
                └── lock t
                     ├── columns: a:1!null
                     ├── key columns: a:1
                     ├── lock columns: (5,6)
                     ├── locking: for-update
                     └── project
                          ├── columns: a:1!null
                          └── scan t
                               └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT (SELECT a FROM t) AS r FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT (SELECT a FROM t) AS r FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build
SELECT (SELECT a FROM t FOR UPDATE OF t) AS r
----
project
 ├── columns: r:5
 ├── values
 │    └── ()
 └── projections
      └── subquery [as=r:5]
           └── max1-row
                ├── columns: a:1!null
                └── project
                     ├── columns: a:1!null
                     └── scan t
                          ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
                          └── locking: for-update

build set=optimizer_use_lock_op_for_serializable=true
SELECT (SELECT a FROM t FOR UPDATE OF t) AS r
----
project
 ├── columns: r:9
 ├── values
 │    └── ()
 └── projections
      └── subquery [as=r:9]
           └── max1-row
                ├── columns: a:1!null
                └── lock t
                     ├── columns: a:1!null
                     ├── key columns: a:1
                     ├── lock columns: (5,6)
                     ├── locking: for-update
                     └── project
                          ├── columns: a:1!null
                          └── scan t
                               └── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4

build
SELECT * FROM t WHERE a IN (SELECT a FROM t) FOR UPDATE
----
project
 ├── columns: a:1!null b:2
 └── select
      ├── 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
      └── filters
           └── any: eq
                ├── project
                │    ├── columns: a:5!null
                │    └── scan t
                │         └── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                └── a:1

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t WHERE a IN (SELECT a FROM t) FOR UPDATE
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (9,10)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2
      └── select
           ├── 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
           └── filters
                └── any: eq
                     ├── project
                     │    ├── columns: a:5!null
                     │    └── scan t
                     │         └── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                     └── a:1

build
SELECT * FROM t WHERE a IN (SELECT a FROM t FOR UPDATE)
----
project
 ├── columns: a:1!null b:2
 └── select
      ├── 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
      └── filters
           └── any: eq
                ├── project
                │    ├── columns: a:5!null
                │    └── scan t
                │         ├── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                │         └── locking: for-update
                └── a:1

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t WHERE a IN (SELECT a FROM t FOR UPDATE)
----
project
 ├── columns: a:1!null b:2
 └── select
      ├── 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
      └── filters
           └── any: eq
                ├── lock t
                │    ├── columns: a:5!null
                │    ├── key columns: a:5
                │    ├── lock columns: (9,10)
                │    ├── locking: for-update
                │    └── project
                │         ├── columns: a:5!null
                │         └── scan t
                │              └── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                └── a:1

build
SELECT * FROM t WHERE a IN (SELECT a FROM t) FOR UPDATE OF t
----
project
 ├── columns: a:1!null b:2
 └── select
      ├── 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
      └── filters
           └── any: eq
                ├── project
                │    ├── columns: a:5!null
                │    └── scan t
                │         └── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                └── a:1

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t WHERE a IN (SELECT a FROM t) FOR UPDATE OF t
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (9,10)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2
      └── select
           ├── 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
           └── filters
                └── any: eq
                     ├── project
                     │    ├── columns: a:5!null
                     │    └── scan t
                     │         └── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                     └── a:1

build
SELECT * FROM t WHERE a IN (SELECT a FROM t FOR UPDATE OF t)
----
project
 ├── columns: a:1!null b:2
 └── select
      ├── 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
      └── filters
           └── any: eq
                ├── project
                │    ├── columns: a:5!null
                │    └── scan t
                │         ├── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                │         └── locking: for-update
                └── a:1

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t WHERE a IN (SELECT a FROM t FOR UPDATE OF t)
----
project
 ├── columns: a:1!null b:2
 └── select
      ├── 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
      └── filters
           └── any: eq
                ├── lock t
                │    ├── columns: a:5!null
                │    ├── key columns: a:5
                │    ├── lock columns: (9,10)
                │    ├── locking: for-update
                │    └── project
                │         ├── columns: a:5!null
                │         └── scan t
                │              └── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                └── a:1

build
SELECT * FROM t WHERE a IN (SELECT b FROM t) FOR UPDATE
----
project
 ├── columns: a:1!null b:2
 └── select
      ├── 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
      └── filters
           └── any: eq
                ├── project
                │    ├── columns: b:6
                │    └── scan t
                │         └── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                └── a:1

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t WHERE a IN (SELECT b FROM t) FOR UPDATE
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (9,10)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2
      └── select
           ├── 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
           └── filters
                └── any: eq
                     ├── project
                     │    ├── columns: b:6
                     │    └── scan t
                     │         └── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                     └── a:1

build
SELECT * FROM t WHERE a IN (SELECT b FROM t FOR UPDATE)
----
project
 ├── columns: a:1!null b:2
 └── select
      ├── 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
      └── filters
           └── any: eq
                ├── project
                │    ├── columns: b:6
                │    └── scan t
                │         ├── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                │         └── locking: for-update
                └── a:1

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t WHERE a IN (SELECT b FROM t FOR UPDATE)
----
project
 ├── columns: a:1!null b:2
 └── select
      ├── 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
      └── filters
           └── any: eq
                ├── project
                │    ├── columns: b:6
                │    └── lock t
                │         ├── columns: a:5!null b:6
                │         ├── key columns: a:5
                │         ├── lock columns: (9,10)
                │         ├── locking: for-update
                │         └── project
                │              ├── columns: a:5!null b:6
                │              └── scan t
                │                   └── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                └── a:1

build
SELECT * FROM t WHERE a IN (SELECT b FROM t) FOR UPDATE OF t
----
project
 ├── columns: a:1!null b:2
 └── select
      ├── 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
      └── filters
           └── any: eq
                ├── project
                │    ├── columns: b:6
                │    └── scan t
                │         └── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                └── a:1

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t WHERE a IN (SELECT b FROM t) FOR UPDATE OF t
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (9,10)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2
      └── select
           ├── 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
           └── filters
                └── any: eq
                     ├── project
                     │    ├── columns: b:6
                     │    └── scan t
                     │         └── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                     └── a:1

build
SELECT * FROM t WHERE a IN (SELECT b FROM t FOR UPDATE OF t)
----
project
 ├── columns: a:1!null b:2
 └── select
      ├── 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
      └── filters
           └── any: eq
                ├── project
                │    ├── columns: b:6
                │    └── scan t
                │         ├── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                │         └── locking: for-update
                └── a:1

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t WHERE a IN (SELECT b FROM t FOR UPDATE OF t)
----
project
 ├── columns: a:1!null b:2
 └── select
      ├── 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
      └── filters
           └── any: eq
                ├── project
                │    ├── columns: b:6
                │    └── lock t
                │         ├── columns: a:5!null b:6
                │         ├── key columns: a:5
                │         ├── lock columns: (9,10)
                │         ├── locking: for-update
                │         └── project
                │              ├── columns: a:5!null b:6
                │              └── scan t
                │                   └── columns: a:5!null b:6 crdb_internal_mvcc_timestamp:7 tableoid:8
                └── a:1

# ------------------------------------------------------------------------------
# Tests with common-table expressions.
#
# 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.
# ------------------------------------------------------------------------------

build
SELECT * FROM [SELECT a FROM t] FOR UPDATE
----
with &1
 ├── columns: a:5!null
 ├── project
 │    ├── columns: t.a:1!null
 │    └── scan t
 │         └── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
 └── with-scan &1
      ├── columns: a:5!null
      └── mapping:
           └──  t.a:1 => a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM [SELECT a FROM t] FOR UPDATE
----
with &1
 ├── columns: a:5!null
 ├── project
 │    ├── columns: t.a:1!null
 │    └── scan t
 │         └── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
 └── with-scan &1
      ├── columns: a:5!null
      └── mapping:
           └──  t.a:1 => a:5

build
WITH cte AS (SELECT a FROM t) SELECT * FROM cte FOR UPDATE
----
with &1 (cte)
 ├── columns: a:5!null
 ├── project
 │    ├── columns: t.a:1!null
 │    └── scan t
 │         └── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
 └── with-scan &1 (cte)
      ├── columns: a:5!null
      └── mapping:
           └──  t.a:1 => a:5

build set=optimizer_use_lock_op_for_serializable=true
WITH cte AS (SELECT a FROM t) SELECT * FROM cte FOR UPDATE
----
with &1 (cte)
 ├── columns: a:5!null
 ├── project
 │    ├── columns: t.a:1!null
 │    └── scan t
 │         └── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
 └── with-scan &1 (cte)
      ├── columns: a:5!null
      └── mapping:
           └──  t.a:1 => a:5

build
SELECT * FROM [SELECT a FROM t FOR UPDATE]
----
with &1
 ├── columns: a:5!null
 ├── 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
 └── with-scan &1
      ├── columns: a:5!null
      └── mapping:
           └──  t.a:1 => a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM [SELECT a FROM t FOR UPDATE]
----
with &1
 ├── columns: a:9!null
 ├── lock t
 │    ├── columns: t.a:1!null
 │    ├── key columns: t.a:1
 │    ├── lock columns: (5,6)
 │    ├── locking: for-update
 │    └── project
 │         ├── columns: t.a:1!null
 │         └── scan t
 │              └── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
 └── with-scan &1
      ├── columns: a:9!null
      └── mapping:
           └──  t.a:1 => a:9

build
WITH cte AS (SELECT a FROM t FOR UPDATE) SELECT * FROM cte
----
with &1 (cte)
 ├── columns: a:5!null
 ├── 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
 └── with-scan &1 (cte)
      ├── columns: a:5!null
      └── mapping:
           └──  t.a:1 => a:5

build set=optimizer_use_lock_op_for_serializable=true
WITH cte AS (SELECT a FROM t FOR UPDATE) SELECT * FROM cte
----
with &1 (cte)
 ├── columns: a:9!null
 ├── lock t
 │    ├── columns: t.a:1!null
 │    ├── key columns: t.a:1
 │    ├── lock columns: (5,6)
 │    ├── locking: for-update
 │    └── project
 │         ├── columns: t.a:1!null
 │         └── scan t
 │              └── columns: t.a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
 └── with-scan &1 (cte)
      ├── columns: a:9!null
      └── mapping:
           └──  t.a:1 => a:9

# ------------------------------------------------------------------------------
# Tests with joins.
# ------------------------------------------------------------------------------

build
SELECT * FROM t JOIN u USING (a) FOR UPDATE
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u
      │    ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      │    └── locking: for-update
      └── filters
           └── t.a:1 = u.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t JOIN u USING (a) FOR UPDATE
----
lock u
 ├── columns: a:1!null b:2 c:6  [hidden: u.a:5!null]
 ├── key columns: u.a:5
 ├── lock columns: (13,14)
 ├── locking: for-update
 └── lock t
      ├── columns: t.a:1!null b:2 u.a:5!null c:6
      ├── key columns: t.a:1
      ├── lock columns: (9,10)
      ├── locking: for-update
      └── project
           ├── columns: t.a:1!null b:2 u.a:5!null c:6
           └── inner-join (hash)
                ├── 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
                ├── scan u
                │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
                └── filters
                     └── t.a:1 = u.a:5

build
SELECT * FROM t JOIN u USING (a) FOR UPDATE OF t
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u
      │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      └── filters
           └── t.a:1 = u.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t JOIN u USING (a) FOR UPDATE OF t
----
lock t
 ├── columns: a:1!null b:2 c:6
 ├── key columns: t.a:1
 ├── lock columns: (9,10)
 ├── locking: for-update
 └── project
      ├── columns: t.a:1!null b:2 c:6
      └── inner-join (hash)
           ├── 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
           ├── scan u
           │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
           └── filters
                └── t.a:1 = u.a:5

build
SELECT * FROM t JOIN u USING (a) FOR UPDATE OF u
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u
      │    ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      │    └── locking: for-update
      └── filters
           └── t.a:1 = u.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t JOIN u USING (a) FOR UPDATE OF u
----
lock u
 ├── columns: a:1!null b:2 c:6  [hidden: u.a:5!null]
 ├── key columns: u.a:5
 ├── lock columns: (9,10)
 ├── locking: for-update
 └── project
      ├── columns: t.a:1!null b:2 u.a:5!null c:6
      └── inner-join (hash)
           ├── 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
           ├── scan u
           │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
           └── filters
                └── t.a:1 = u.a:5

build
SELECT * FROM t JOIN u USING (a) FOR UPDATE OF t, u
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u
      │    ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      │    └── locking: for-update
      └── filters
           └── t.a:1 = u.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t JOIN u USING (a) FOR UPDATE OF t, u
----
lock u
 ├── columns: a:1!null b:2 c:6  [hidden: u.a:5!null]
 ├── key columns: u.a:5
 ├── lock columns: (13,14)
 ├── locking: for-update
 └── lock t
      ├── columns: t.a:1!null b:2 u.a:5!null c:6
      ├── key columns: t.a:1
      ├── lock columns: (9,10)
      ├── locking: for-update
      └── project
           ├── columns: t.a:1!null b:2 u.a:5!null c:6
           └── inner-join (hash)
                ├── 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
                ├── scan u
                │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
                └── filters
                     └── t.a:1 = u.a:5

build
SELECT * FROM t JOIN u USING (a) FOR UPDATE OF t FOR SHARE OF u
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u
      │    ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      │    └── locking: for-share
      └── filters
           └── t.a:1 = u.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t JOIN u USING (a) FOR UPDATE OF t FOR SHARE OF u
----
lock u
 ├── columns: a:1!null b:2 c:6  [hidden: u.a:5!null]
 ├── key columns: u.a:5
 ├── lock columns: (13,14)
 ├── locking: for-share
 └── lock t
      ├── columns: t.a:1!null b:2 u.a:5!null c:6
      ├── key columns: t.a:1
      ├── lock columns: (9,10)
      ├── locking: for-update
      └── project
           ├── columns: t.a:1!null b:2 u.a:5!null c:6
           └── inner-join (hash)
                ├── 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
                ├── scan u
                │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
                └── filters
                     └── t.a:1 = u.a:5

build
SELECT * FROM t JOIN u USING (a) FOR UPDATE OF t2 FOR SHARE OF u2
----
error (42P01): relation "t2" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t JOIN u USING (a) FOR UPDATE OF t2 FOR SHARE OF u2
----
error (42P01): relation "t2" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF t2 FOR SHARE OF u2
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u [as=u2]
      │    ├── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
      │    └── locking: for-share
      └── filters
           └── t2.a:1 = u2.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF t2 FOR SHARE OF u2
----
lock u [as=u2]
 ├── columns: a:1!null b:2 c:6  [hidden: u2.a:5!null]
 ├── key columns: u2.a:5
 ├── lock columns: (13,14)
 ├── locking: for-share
 └── lock t [as=t2]
      ├── columns: t2.a:1!null b:2 u2.a:5!null c:6
      ├── key columns: t2.a:1
      ├── lock columns: (9,10)
      ├── locking: for-update
      └── project
           ├── columns: t2.a:1!null b:2 u2.a:5!null c:6
           └── inner-join (hash)
                ├── 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
                ├── scan u [as=u2]
                │    └── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
                └── filters
                     └── t2.a:1 = u2.a:5

build
SELECT * FROM t JOIN u USING (a) FOR KEY SHARE FOR UPDATE
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u
      │    ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      │    └── locking: for-update
      └── filters
           └── t.a:1 = u.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t JOIN u USING (a) FOR KEY SHARE FOR UPDATE
----
lock u
 ├── columns: a:1!null b:2 c:6  [hidden: u.a:5!null]
 ├── key columns: u.a:5
 ├── lock columns: (21,22)
 ├── locking: for-update
 └── lock t
      ├── columns: t.a:1!null b:2 u.a:5!null c:6
      ├── key columns: t.a:1
      ├── lock columns: (17,18)
      ├── locking: for-update
      └── lock u
           ├── columns: t.a:1!null b:2 u.a:5!null c:6
           ├── key columns: u.a:5
           ├── lock columns: (13,14)
           ├── locking: for-key-share
           └── lock t
                ├── columns: t.a:1!null b:2 u.a:5!null c:6
                ├── key columns: t.a:1
                ├── lock columns: (9,10)
                ├── locking: for-key-share
                └── project
                     ├── columns: t.a:1!null b:2 u.a:5!null c:6
                     └── inner-join (hash)
                          ├── 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
                          ├── scan u
                          │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
                          └── filters
                               └── t.a:1 = u.a:5

build
SELECT * FROM t JOIN u USING (a) FOR KEY SHARE FOR NO KEY UPDATE OF t
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u
      │    ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      │    └── locking: for-key-share
      └── filters
           └── t.a:1 = u.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t JOIN u USING (a) FOR KEY SHARE FOR NO KEY UPDATE OF t
----
lock t
 ├── columns: a:1!null b:2 c:6  [hidden: u.a:5!null]
 ├── key columns: t.a:1
 ├── lock columns: (17,18)
 ├── locking: for-no-key-update
 └── lock u
      ├── columns: t.a:1!null b:2 u.a:5!null c:6
      ├── key columns: u.a:5
      ├── lock columns: (13,14)
      ├── locking: for-key-share
      └── lock t
           ├── columns: t.a:1!null b:2 u.a:5!null c:6
           ├── key columns: t.a:1
           ├── lock columns: (9,10)
           ├── locking: for-key-share
           └── project
                ├── columns: t.a:1!null b:2 u.a:5!null c:6
                └── inner-join (hash)
                     ├── 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
                     ├── scan u
                     │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
                     └── filters
                          └── t.a:1 = u.a:5

build
SELECT * FROM t JOIN u USING (a) FOR SHARE FOR NO KEY UPDATE OF t FOR UPDATE OF u
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u
      │    ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      │    └── locking: for-update
      └── filters
           └── t.a:1 = u.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t JOIN u USING (a) FOR SHARE FOR NO KEY UPDATE OF t FOR UPDATE OF u
----
lock u
 ├── columns: a:1!null b:2 c:6  [hidden: u.a:5!null]
 ├── key columns: u.a:5
 ├── lock columns: (21,22)
 ├── locking: for-update
 └── lock t
      ├── columns: t.a:1!null b:2 u.a:5!null c:6
      ├── key columns: t.a:1
      ├── lock columns: (17,18)
      ├── locking: for-no-key-update
      └── lock u
           ├── columns: t.a:1!null b:2 u.a:5!null c:6
           ├── key columns: u.a:5
           ├── lock columns: (13,14)
           ├── locking: for-share
           └── lock t
                ├── columns: t.a:1!null b:2 u.a:5!null c:6
                ├── key columns: t.a:1
                ├── lock columns: (9,10)
                ├── locking: for-share
                └── project
                     ├── columns: t.a:1!null b:2 u.a:5!null c:6
                     └── inner-join (hash)
                          ├── 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
                          ├── scan u
                          │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
                          └── filters
                               └── t.a:1 = u.a:5

# ------------------------------------------------------------------------------
# Tests with outer joins.
# ------------------------------------------------------------------------------

build
SELECT * FROM t LEFT JOIN u ON b = c FOR UPDATE
----
error (0A000): FOR UPDATE cannot be applied to the nullable side of an outer join

build
SELECT * FROM t LEFT JOIN u ON b = c FOR UPDATE of t
----
project
 ├── columns: a:1!null b:2 a:5 c:6
 └── left-join (hash)
      ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5 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
      ├── scan u
      │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      └── filters
           └── b:2 = c:6

build
SELECT * FROM t LEFT JOIN u ON b = c FOR UPDATE of u
----
error (0A000): FOR UPDATE cannot be applied to the nullable side of an outer join

build
SELECT * FROM t RIGHT JOIN u ON b = c FOR UPDATE
----
error (0A000): FOR UPDATE cannot be applied to the nullable side of an outer join

build
SELECT * FROM t RIGHT JOIN u ON b = c FOR UPDATE of t
----
error (0A000): FOR UPDATE cannot be applied to the nullable side of an outer join

build
SELECT * FROM t RIGHT JOIN u ON b = c FOR UPDATE of u
----
project
 ├── columns: a:1 b:2 a:5!null c:6
 └── right-join (hash)
      ├── columns: t.a:1 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
      ├── scan u
      │    ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      │    └── locking: for-update
      └── filters
           └── b:2 = c:6

build
SELECT * FROM t FULL JOIN u ON b = c FOR UPDATE
----
error (0A000): FOR UPDATE cannot be applied to the nullable side of an outer join

build
SELECT * FROM t FULL JOIN u ON b = c FOR UPDATE of t
----
error (0A000): FOR UPDATE cannot be applied to the nullable side of an outer join

build
SELECT * FROM t FULL JOIN u ON b = c FOR UPDATE of u
----
error (0A000): FOR UPDATE cannot be applied to the nullable side of an outer join

build
SELECT * FROM t LEFT JOIN u ON b = c FOR KEY SHARE
----
error (0A000): FOR KEY SHARE cannot be applied to the nullable side of an outer join

build
SELECT * FROM t RIGHT JOIN u ON b = c FOR SHARE
----
error (0A000): FOR SHARE cannot be applied to the nullable side of an outer join

build
SELECT * FROM t FULL JOIN u ON b = c FOR NO KEY UPDATE
----
error (0A000): FOR NO KEY UPDATE cannot be applied to the nullable side of an outer join

build
SELECT * FROM t LEFT JOIN (SELECT t.a FROM t JOIN u ON b = c) j ON j.a = t.a FOR UPDATE
----
error (0A000): FOR UPDATE cannot be applied to the nullable side of an outer join

# Since the locking is happening below the join, this query should not error.
build
SELECT * FROM t LEFT JOIN (SELECT t.a FROM t JOIN u ON b = c FOR UPDATE) j ON j.a = t.a
----
project
 ├── columns: a:1!null b:2 a:5
 └── left-join (hash)
      ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 t.a:5
      ├── scan t
      │    └── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4
      ├── project
      │    ├── columns: t.a:5!null
      │    └── inner-join (hash)
      │         ├── columns: t.a:5!null b:6!null t.crdb_internal_mvcc_timestamp:7 t.tableoid:8 u.a:9!null c:10!null u.crdb_internal_mvcc_timestamp:11 u.tableoid:12
      │         ├── scan t
      │         │    ├── columns: t.a:5!null b:6 t.crdb_internal_mvcc_timestamp:7 t.tableoid:8
      │         │    └── locking: for-update
      │         ├── scan u
      │         │    ├── columns: u.a:9!null c:10 u.crdb_internal_mvcc_timestamp:11 u.tableoid:12
      │         │    └── locking: for-update
      │         └── filters
      │              └── b:6 = c:10
      └── filters
           └── t.a:5 = t.a:1

# Since the locking is happening below the join, this query should not error.
build
SELECT * FROM t RIGHT JOIN (SELECT t.a FROM t JOIN u ON b = c FOR UPDATE) j ON j.a = t.a
----
project
 ├── columns: a:1 b:2 a:5!null
 └── right-join (hash)
      ├── columns: t.a:1 b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 t.a:5!null
      ├── scan t
      │    └── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4
      ├── project
      │    ├── columns: t.a:5!null
      │    └── inner-join (hash)
      │         ├── columns: t.a:5!null b:6!null t.crdb_internal_mvcc_timestamp:7 t.tableoid:8 u.a:9!null c:10!null u.crdb_internal_mvcc_timestamp:11 u.tableoid:12
      │         ├── scan t
      │         │    ├── columns: t.a:5!null b:6 t.crdb_internal_mvcc_timestamp:7 t.tableoid:8
      │         │    └── locking: for-update
      │         ├── scan u
      │         │    ├── columns: u.a:9!null c:10 u.crdb_internal_mvcc_timestamp:11 u.tableoid:12
      │         │    └── locking: for-update
      │         └── filters
      │              └── b:6 = c:10
      └── filters
           └── t.a:5 = t.a:1

build
SELECT * FROM t LEFT JOIN (SELECT t.a FROM t JOIN u ON b = c) j ON j.a = t.a FOR UPDATE of t
----
project
 ├── columns: a:1!null b:2 a:5
 └── left-join (hash)
      ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 t.a:5
      ├── scan t
      │    ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4
      │    └── locking: for-update
      ├── project
      │    ├── columns: t.a:5!null
      │    └── inner-join (hash)
      │         ├── columns: t.a:5!null b:6!null t.crdb_internal_mvcc_timestamp:7 t.tableoid:8 u.a:9!null c:10!null u.crdb_internal_mvcc_timestamp:11 u.tableoid:12
      │         ├── scan t
      │         │    └── columns: t.a:5!null b:6 t.crdb_internal_mvcc_timestamp:7 t.tableoid:8
      │         ├── scan u
      │         │    └── columns: u.a:9!null c:10 u.crdb_internal_mvcc_timestamp:11 u.tableoid:12
      │         └── filters
      │              └── b:6 = c:10
      └── filters
           └── t.a:5 = t.a:1

build
SELECT * FROM t LEFT JOIN (SELECT t.a FROM t JOIN u ON b = c) j ON j.a = t.a FOR UPDATE of j
----
error (0A000): FOR UPDATE cannot be applied to the nullable side of an outer join

build
SELECT * FROM t LEFT JOIN (SELECT t.a FROM t LEFT JOIN u ON b = c FOR UPDATE of t) j ON j.a = t.a FOR UPDATE of t
----
project
 ├── columns: a:1!null b:2 a:5
 └── left-join (hash)
      ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 t.a:5
      ├── scan t
      │    ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4
      │    └── locking: for-update
      ├── project
      │    ├── columns: t.a:5!null
      │    └── left-join (hash)
      │         ├── columns: t.a:5!null b:6 t.crdb_internal_mvcc_timestamp:7 t.tableoid:8 u.a:9 c:10 u.crdb_internal_mvcc_timestamp:11 u.tableoid:12
      │         ├── scan t
      │         │    ├── columns: t.a:5!null b:6 t.crdb_internal_mvcc_timestamp:7 t.tableoid:8
      │         │    └── locking: for-update
      │         ├── scan u
      │         │    └── columns: u.a:9!null c:10 u.crdb_internal_mvcc_timestamp:11 u.tableoid:12
      │         └── filters
      │              └── b:6 = c:10
      └── filters
           └── t.a:5 = t.a:1

build
SELECT * FROM (SELECT t.a FROM t LEFT JOIN u ON b = c FOR UPDATE of t) j RIGHT JOIN t ON j.a = t.a FOR UPDATE of t
----
project
 ├── columns: a:1 a:9!null b:10
 └── right-join (hash)
      ├── columns: t.a:1 t.a:9!null b:10 t.crdb_internal_mvcc_timestamp:11 t.tableoid:12
      ├── project
      │    ├── columns: t.a:1!null
      │    └── left-join (hash)
      │         ├── columns: t.a:1!null b:2 t.crdb_internal_mvcc_timestamp:3 t.tableoid:4 u.a:5 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
      │         ├── scan u
      │         │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      │         └── filters
      │              └── b:2 = c:6
      ├── scan t
      │    ├── columns: t.a:9!null b:10 t.crdb_internal_mvcc_timestamp:11 t.tableoid:12
      │    └── locking: for-update
      └── filters
           └── t.a:1 = t.a:9

# TODO(rytaft): Postgres does not error in this case because it determines that
# the null-extended rows are filtered out.
build
SELECT * FROM t LEFT JOIN u ON b = c WHERE c IS NOT NULL FOR UPDATE
----
error (0A000): FOR UPDATE cannot be applied to the nullable side of an outer join

# ------------------------------------------------------------------------------
# Tests with joins of aliased tables and aliased joins.
# ------------------------------------------------------------------------------

build
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u [as=u2]
      │    ├── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
      │    └── locking: for-update
      └── filters
           └── t2.a:1 = u2.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE
----
lock u [as=u2]
 ├── columns: a:1!null b:2 c:6  [hidden: u2.a:5!null]
 ├── key columns: u2.a:5
 ├── lock columns: (13,14)
 ├── locking: for-update
 └── lock t [as=t2]
      ├── columns: t2.a:1!null b:2 u2.a:5!null c:6
      ├── key columns: t2.a:1
      ├── lock columns: (9,10)
      ├── locking: for-update
      └── project
           ├── columns: t2.a:1!null b:2 u2.a:5!null c:6
           └── inner-join (hash)
                ├── 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
                ├── scan u [as=u2]
                │    └── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
                └── filters
                     └── t2.a:1 = u2.a:5

build
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF u
----
error (42P01): relation "u" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF u
----
error (42P01): relation "u" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF t, u
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF t, u
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF t2
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u [as=u2]
      │    └── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
      └── filters
           └── t2.a:1 = u2.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF t2
----
lock t [as=t2]
 ├── columns: a:1!null b:2 c:6
 ├── key columns: t2.a:1
 ├── lock columns: (9,10)
 ├── locking: for-update
 └── project
      ├── columns: t2.a:1!null b:2 c:6
      └── inner-join (hash)
           ├── 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
           ├── scan u [as=u2]
           │    └── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
           └── filters
                └── t2.a:1 = u2.a:5

build
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF u2
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u [as=u2]
      │    ├── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
      │    └── locking: for-update
      └── filters
           └── t2.a:1 = u2.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF u2
----
lock u [as=u2]
 ├── columns: a:1!null b:2 c:6  [hidden: u2.a:5!null]
 ├── key columns: u2.a:5
 ├── lock columns: (9,10)
 ├── locking: for-update
 └── project
      ├── columns: t2.a:1!null b:2 u2.a:5!null c:6
      └── inner-join (hash)
           ├── 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
           ├── scan u [as=u2]
           │    └── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
           └── filters
                └── t2.a:1 = u2.a:5

build
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF t2, u2
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u [as=u2]
      │    ├── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
      │    └── locking: for-update
      └── filters
           └── t2.a:1 = u2.a:5


# Postgres doesn't support applying locking clauses to joins. The following
# queries all return the error: "FOR UPDATE cannot be applied to a join".
# We could do the same, but it's not hard to support these, so we do.

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t AS t2 JOIN u AS u2 USING (a) FOR UPDATE OF t2, u2
----
lock u [as=u2]
 ├── columns: a:1!null b:2 c:6  [hidden: u2.a:5!null]
 ├── key columns: u2.a:5
 ├── lock columns: (13,14)
 ├── locking: for-update
 └── lock t [as=t2]
      ├── columns: t2.a:1!null b:2 u2.a:5!null c:6
      ├── key columns: t2.a:1
      ├── lock columns: (9,10)
      ├── locking: for-update
      └── project
           ├── columns: t2.a:1!null b:2 u2.a:5!null c:6
           └── inner-join (hash)
                ├── 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
                ├── scan u [as=u2]
                │    └── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
                └── filters
                     └── t2.a:1 = u2.a:5


# Postgres doesn't support applying locking clauses to joins. The following
# queries all return the error: "FOR UPDATE cannot be applied to a join".
# We could do the same, but it's not hard to support these, so we do.

build
SELECT * FROM (t JOIN u AS u2 USING (a)) j FOR UPDATE
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u [as=u2]
      │    ├── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
      │    └── locking: for-update
      └── filters
           └── t.a:1 = u2.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (t JOIN u AS u2 USING (a)) j FOR UPDATE
----
lock u [as=u2]
 ├── columns: a:1!null b:2 c:6  [hidden: u2.a:5!null]
 ├── key columns: u2.a:5
 ├── lock columns: (13,14)
 ├── locking: for-update
 └── lock t
      ├── columns: t.a:1!null b:2 u2.a:5!null c:6
      ├── key columns: t.a:1
      ├── lock columns: (9,10)
      ├── locking: for-update
      └── project
           ├── columns: t.a:1!null b:2 u2.a:5!null c:6
           └── inner-join (hash)
                ├── 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
                ├── scan u [as=u2]
                │    └── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
                └── filters
                     └── t.a:1 = u2.a:5

build
SELECT * FROM (t JOIN u AS u2 USING (a)) j FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (t JOIN u AS u2 USING (a)) j FOR UPDATE OF t
----
error (42P01): relation "t" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM (t JOIN u AS u2 USING (a)) j FOR UPDATE OF u
----
error (42P01): relation "u" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (t JOIN u AS u2 USING (a)) j FOR UPDATE OF u
----
error (42P01): relation "u" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM (t JOIN u AS u2 USING (a)) j FOR UPDATE OF u2
----
error (42P01): relation "u2" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (t JOIN u AS u2 USING (a)) j FOR UPDATE OF u2
----
error (42P01): relation "u2" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM (t JOIN u AS u2 USING (a)) j FOR UPDATE OF j
----
project
 ├── columns: a:1!null b:2 c:6
 └── inner-join (hash)
      ├── 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
      ├── scan u [as=u2]
      │    ├── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
      │    └── locking: for-update
      └── filters
           └── t.a:1 = u2.a:5

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM (t JOIN u AS u2 USING (a)) j FOR UPDATE OF j
----
lock u [as=u2]
 ├── columns: a:1!null b:2 c:6  [hidden: u2.a:5!null]
 ├── key columns: u2.a:5
 ├── lock columns: (13,14)
 ├── locking: for-update
 └── lock t
      ├── columns: t.a:1!null b:2 u2.a:5!null c:6
      ├── key columns: t.a:1
      ├── lock columns: (9,10)
      ├── locking: for-update
      └── project
           ├── columns: t.a:1!null b:2 u2.a:5!null c:6
           └── inner-join (hash)
                ├── 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
                ├── scan u [as=u2]
                │    └── columns: u2.a:5!null c:6 u2.crdb_internal_mvcc_timestamp:7 u2.tableoid:8
                └── filters
                     └── t.a:1 = u2.a:5

# ------------------------------------------------------------------------------
# Tests with lateral joins.
# ------------------------------------------------------------------------------

build
SELECT * FROM t, u FOR UPDATE
----
project
 ├── columns: a:1!null b:2 a:5!null c:6
 └── inner-join (cross)
      ├── 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
      ├── scan u
      │    ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      │    └── locking: for-update
      └── filters (true)

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t, u FOR UPDATE
----
lock u
 ├── columns: a:1!null b:2 a:5!null c:6
 ├── key columns: u.a:5
 ├── lock columns: (13,14)
 ├── locking: for-update
 └── lock t
      ├── columns: t.a:1!null b:2 u.a:5!null c:6
      ├── key columns: t.a:1
      ├── lock columns: (9,10)
      ├── locking: for-update
      └── project
           ├── columns: t.a:1!null b:2 u.a:5!null c:6
           └── inner-join (cross)
                ├── 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
                ├── scan u
                │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
                └── filters (true)

build
SELECT * FROM t, u FOR UPDATE OF t
----
project
 ├── columns: a:1!null b:2 a:5!null c:6
 └── inner-join (cross)
      ├── 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
      ├── scan u
      │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      └── filters (true)

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t, u FOR UPDATE OF t
----
lock t
 ├── columns: a:1!null b:2 a:5!null c:6
 ├── key columns: t.a:1
 ├── lock columns: (9,10)
 ├── locking: for-update
 └── project
      ├── columns: t.a:1!null b:2 u.a:5!null c:6
      └── inner-join (cross)
           ├── 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
           ├── scan u
           │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
           └── filters (true)

build
SELECT * FROM t, u FOR SHARE OF t FOR UPDATE OF u
----
project
 ├── columns: a:1!null b:2 a:5!null c:6
 └── inner-join (cross)
      ├── 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
      ├── scan u
      │    ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      │    └── locking: for-update
      └── filters (true)

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t, u FOR SHARE OF t FOR UPDATE OF u
----
lock u
 ├── columns: a:1!null b:2 a:5!null c:6
 ├── key columns: u.a:5
 ├── lock columns: (13,14)
 ├── locking: for-update
 └── lock t
      ├── columns: t.a:1!null b:2 u.a:5!null c:6
      ├── key columns: t.a:1
      ├── lock columns: (9,10)
      ├── locking: for-share
      └── project
           ├── columns: t.a:1!null b:2 u.a:5!null c:6
           └── inner-join (cross)
                ├── 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
                ├── scan u
                │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
                └── filters (true)

build
SELECT * FROM t, LATERAL (SELECT * FROM u) sub FOR UPDATE
----
project
 ├── columns: a:1!null b:2 a:5!null c:6
 └── inner-join-apply
      ├── 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
      ├── 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
      └── filters (true)

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t, LATERAL (SELECT * FROM u) sub FOR UPDATE
----
lock u
 ├── columns: a:1!null b:2 a:5!null c:6
 ├── key columns: u.a:5
 ├── lock columns: (13,14)
 ├── locking: for-update
 └── lock t
      ├── columns: t.a:1!null b:2 u.a:5!null c:6
      ├── key columns: t.a:1
      ├── lock columns: (9,10)
      ├── locking: for-update
      └── project
           ├── columns: t.a:1!null b:2 u.a:5!null c:6
           └── inner-join-apply
                ├── 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
                ├── 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
                └── filters (true)

build
SELECT * FROM t, LATERAL (SELECT * FROM u) sub FOR UPDATE OF u
----
error (42P01): relation "u" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t, LATERAL (SELECT * FROM u) sub FOR UPDATE OF u
----
error (42P01): relation "u" in FOR UPDATE clause not found in FROM clause

build
SELECT * FROM t, LATERAL (SELECT * FROM u) sub FOR UPDATE OF sub
----
project
 ├── columns: a:1!null b:2 a:5!null c:6
 └── inner-join-apply
      ├── 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
      ├── 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
      └── filters (true)

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t, LATERAL (SELECT * FROM u) sub FOR UPDATE OF sub
----
lock u
 ├── columns: a:1!null b:2 a:5!null c:6
 ├── key columns: u.a:5
 ├── lock columns: (9,10)
 ├── locking: for-update
 └── project
      ├── columns: t.a:1!null b:2 u.a:5!null c:6
      └── inner-join-apply
           ├── 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
           ├── 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
           └── filters (true)

# ------------------------------------------------------------------------------
# Tests with index joins.
# ------------------------------------------------------------------------------

exec-ddl
CREATE TABLE indexed (
  a INT PRIMARY KEY,
  b INT,
  c INT,
  INDEX b_idx(b)
)
----

build
SELECT * FROM indexed WHERE b = 2 FOR UPDATE
----
project
 ├── columns: a:1!null b:2!null c:3
 └── select
      ├── 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
      └── filters
           └── b:2 = 2

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM indexed WHERE b = 2 FOR UPDATE
----
lock indexed
 ├── columns: a:1!null b:2!null c:3
 ├── key columns: a:1
 ├── lock columns: (6-8)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2!null c:3
      └── select
           ├── 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
           └── filters
                └── b:2 = 2

build
SELECT * FROM indexed WHERE b BETWEEN 2 AND 10 FOR UPDATE
----
project
 ├── columns: a:1!null b:2!null c:3
 └── select
      ├── 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
      └── filters
           └── (b:2 >= 2) AND (b:2 <= 10)

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM indexed WHERE b BETWEEN 2 AND 10 FOR UPDATE
----
lock indexed
 ├── columns: a:1!null b:2!null c:3
 ├── key columns: a:1
 ├── lock columns: (6-8)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2!null c:3
      └── select
           ├── 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
           └── filters
                └── (b:2 >= 2) AND (b:2 <= 10)

# ------------------------------------------------------------------------------
# Tests with lookup joins.
# ------------------------------------------------------------------------------

build
SELECT c FROM t JOIN u ON t.b = u.a WHERE t.a = 2 FOR UPDATE
----
project
 ├── columns: c:6
 └── select
      ├── 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
      ├── inner-join (hash)
      │    ├── 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
      │    ├── scan u
      │    │    ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      │    │    └── locking: for-update
      │    └── filters
      │         └── b:2 = u.a:5
      └── filters
           └── t.a:1 = 2

build set=optimizer_use_lock_op_for_serializable=true
SELECT c FROM t JOIN u ON t.b = u.a WHERE t.a = 2 FOR UPDATE
----
lock u
 ├── columns: c:6  [hidden: t.a:1!null u.a:5!null]
 ├── key columns: u.a:5
 ├── lock columns: (13,14)
 ├── locking: for-update
 └── lock t
      ├── columns: t.a:1!null u.a:5!null c:6
      ├── key columns: t.a:1
      ├── lock columns: (9,10)
      ├── locking: for-update
      └── project
           ├── columns: t.a:1!null u.a:5!null c:6
           └── select
                ├── 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
                ├── inner-join (hash)
                │    ├── 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
                │    ├── scan u
                │    │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
                │    └── filters
                │         └── b:2 = u.a:5
                └── filters
                     └── t.a:1 = 2

build
SELECT c FROM t JOIN u ON t.b = u.a WHERE t.a BETWEEN 2 AND 10 FOR UPDATE
----
project
 ├── columns: c:6
 └── select
      ├── 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
      ├── inner-join (hash)
      │    ├── 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
      │    ├── scan u
      │    │    ├── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
      │    │    └── locking: for-update
      │    └── filters
      │         └── b:2 = u.a:5
      └── filters
           └── (t.a:1 >= 2) AND (t.a:1 <= 10)

build set=optimizer_use_lock_op_for_serializable=true
SELECT c FROM t JOIN u ON t.b = u.a WHERE t.a BETWEEN 2 AND 10 FOR UPDATE
----
lock u
 ├── columns: c:6  [hidden: t.a:1!null u.a:5!null]
 ├── key columns: u.a:5
 ├── lock columns: (13,14)
 ├── locking: for-update
 └── lock t
      ├── columns: t.a:1!null u.a:5!null c:6
      ├── key columns: t.a:1
      ├── lock columns: (9,10)
      ├── locking: for-update
      └── project
           ├── columns: t.a:1!null u.a:5!null c:6
           └── select
                ├── 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
                ├── inner-join (hash)
                │    ├── 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
                │    ├── scan u
                │    │    └── columns: u.a:5!null c:6 u.crdb_internal_mvcc_timestamp:7 u.tableoid:8
                │    └── filters
                │         └── b:2 = u.a:5
                └── filters
                     └── (t.a:1 >= 2) AND (t.a:1 <= 10)

build
SELECT * FROM t JOIN indexed ON t.b = indexed.b WHERE t.a = 2 FOR UPDATE
----
project
 ├── columns: a:1!null b:2!null a:5!null b:6!null c:7
 └── select
      ├── 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
      ├── inner-join (hash)
      │    ├── 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
      │    ├── scan indexed
      │    │    ├── columns: indexed.a:5!null indexed.b:6 c:7 indexed.crdb_internal_mvcc_timestamp:8 indexed.tableoid:9
      │    │    └── locking: for-update
      │    └── filters
      │         └── t.b:2 = indexed.b:6
      └── filters
           └── t.a:1 = 2

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t JOIN indexed ON t.b = indexed.b WHERE t.a = 2 FOR UPDATE
----
lock indexed
 ├── columns: a:1!null b:2!null a:5!null b:6!null c:7
 ├── key columns: indexed.a:5
 ├── lock columns: (14-16)
 ├── locking: for-update
 └── lock t
      ├── columns: t.a:1!null t.b:2!null indexed.a:5!null indexed.b:6!null c:7
      ├── key columns: t.a:1
      ├── lock columns: (10,11)
      ├── locking: for-update
      └── project
           ├── columns: t.a:1!null t.b:2!null indexed.a:5!null indexed.b:6!null c:7
           └── select
                ├── 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
                ├── inner-join (hash)
                │    ├── 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
                │    ├── scan indexed
                │    │    └── columns: indexed.a:5!null indexed.b:6 c:7 indexed.crdb_internal_mvcc_timestamp:8 indexed.tableoid:9
                │    └── filters
                │         └── t.b:2 = indexed.b:6
                └── filters
                     └── t.a:1 = 2

# ------------------------------------------------------------------------------
# Tests with inverted filters and joins.
# ------------------------------------------------------------------------------

exec-ddl
CREATE TABLE inverted (
  a INT PRIMARY KEY,
  b INT[],
  c INT,
  INVERTED INDEX b_inv(b)
)
----

build
SELECT * FROM inverted WHERE b @> '{1, 2}' FOR UPDATE
----
project
 ├── columns: a:1!null b:2!null c:3
 └── select
      ├── 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
      └── filters
           └── b:2 @> ARRAY[1,2]

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM inverted WHERE b @> '{1, 2}' FOR UPDATE
----
lock inverted
 ├── columns: a:1!null b:2!null c:3
 ├── key columns: a:1
 ├── lock columns: (7-9)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2!null c:3
      └── select
           ├── 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
           └── filters
                └── b:2 @> ARRAY[1,2]

build
SELECT * FROM inverted WHERE b <@ '{1, 2}' FOR UPDATE
----
project
 ├── columns: a:1!null b:2 c:3
 └── select
      ├── 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
      └── filters
           └── b:2 <@ ARRAY[1,2]

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM inverted WHERE b <@ '{1, 2}' FOR UPDATE
----
lock inverted
 ├── columns: a:1!null b:2 c:3
 ├── key columns: a:1
 ├── lock columns: (7-9)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2 c:3
      └── select
           ├── 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
           └── filters
                └── b:2 <@ ARRAY[1,2]

build
SELECT * FROM inverted@b_inv AS i1, inverted AS i2 WHERE i1.b @> i2.b FOR UPDATE
----
project
 ├── columns: a:1!null b:2 c:3 a:7!null b:8 c:9
 └── select
      ├── columns: i1.a:1!null i1.b:2 i1.c:3 i1.crdb_internal_mvcc_timestamp:4 i1.tableoid:5 i2.a:7!null i2.b:8 i2.c:9 i2.crdb_internal_mvcc_timestamp:10 i2.tableoid:11
      ├── inner-join (cross)
      │    ├── columns: i1.a:1!null i1.b:2 i1.c:3 i1.crdb_internal_mvcc_timestamp:4 i1.tableoid:5 i2.a:7!null i2.b:8 i2.c:9 i2.crdb_internal_mvcc_timestamp:10 i2.tableoid:11
      │    ├── 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
      │    ├── 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
      │    └── filters (true)
      └── filters
           └── i1.b:2 @> i2.b:8

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM inverted@b_inv AS i1, inverted AS i2 WHERE i1.b @> i2.b FOR UPDATE
----
lock inverted [as=i2]
 ├── columns: a:1!null b:2 c:3 a:7!null b:8 c:9
 ├── key columns: i2.a:7
 ├── lock columns: (19-21)
 ├── locking: for-update
 └── lock inverted [as=i1]
      ├── columns: i1.a:1!null i1.b:2 i1.c:3 i2.a:7!null i2.b:8 i2.c:9
      ├── key columns: i1.a:1
      ├── lock columns: (13-15)
      ├── locking: for-update
      └── project
           ├── columns: i1.a:1!null i1.b:2 i1.c:3 i2.a:7!null i2.b:8 i2.c:9
           └── select
                ├── columns: i1.a:1!null i1.b:2 i1.c:3 i1.crdb_internal_mvcc_timestamp:4 i1.tableoid:5 i2.a:7!null i2.b:8 i2.c:9 i2.crdb_internal_mvcc_timestamp:10 i2.tableoid:11
                ├── inner-join (cross)
                │    ├── columns: i1.a:1!null i1.b:2 i1.c:3 i1.crdb_internal_mvcc_timestamp:4 i1.tableoid:5 i2.a:7!null i2.b:8 i2.c:9 i2.crdb_internal_mvcc_timestamp:10 i2.tableoid:11
                │    ├── 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
                │    ├── scan inverted [as=i2]
                │    │    └── columns: i2.a:7!null i2.b:8 i2.c:9 i2.crdb_internal_mvcc_timestamp:10 i2.tableoid:11
                │    └── filters (true)
                └── filters
                     └── i1.b:2 @> i2.b:8

# ------------------------------------------------------------------------------
# Tests with zigzag joins.
# ------------------------------------------------------------------------------

exec-ddl
CREATE TABLE zigzag (
  a INT PRIMARY KEY,
  b INT,
  c FLOAT,
  d JSONB,
  INDEX b_idx(b),
  INDEX c_idx(c),
  INVERTED INDEX d_idx(d)
)
----

build
SELECT a,b,c FROM zigzag WHERE b = 5 AND c = 6.0 FOR UPDATE
----
project
 ├── columns: a:1!null b:2!null c:3!null
 └── select
      ├── 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
      └── filters
           └── (b:2 = 5) AND (c:3 = 6.0)

build set=optimizer_use_lock_op_for_serializable=true
SELECT a,b,c FROM zigzag WHERE b = 5 AND c = 6.0 FOR UPDATE
----
lock zigzag
 ├── columns: a:1!null b:2!null c:3!null
 ├── key columns: a:1
 ├── lock columns: (8-11)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2!null c:3!null
      └── select
           ├── 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
           └── filters
                └── (b:2 = 5) AND (c:3 = 6.0)

build
SELECT * from zigzag where d @> '{"a": {"b": "c"}, "f": "g"}' FOR UPDATE
----
project
 ├── columns: a:1!null b:2 c:3 d:4!null
 └── select
      ├── 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
      └── filters
           └── d:4 @> '{"a": {"b": "c"}, "f": "g"}'

build set=optimizer_use_lock_op_for_serializable=true
SELECT * from zigzag where d @> '{"a": {"b": "c"}, "f": "g"}' FOR UPDATE
----
lock zigzag
 ├── columns: a:1!null b:2 c:3 d:4!null
 ├── key columns: a:1
 ├── lock columns: (8-11)
 ├── locking: for-update
 └── project
      ├── columns: a:1!null b:2 c:3 d:4!null
      └── select
           ├── 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
           └── filters
                └── d:4 @> '{"a": {"b": "c"}, "f": "g"}'

# ------------------------------------------------------------------------------
# Tests with virtual tables.
# ------------------------------------------------------------------------------

build
SELECT * FROM information_schema.columns FOR UPDATE
----
error (42601): FOR UPDATE not allowed with virtual tables

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM information_schema.columns FOR UPDATE
----
lock columns
 ├── columns: table_catalog:2!null table_schema:3!null table_name:4!null column_name:5!null column_comment:6 ordinal_position:7!null column_default:8 is_nullable:9!null data_type:10!null character_maximum_length:11 character_octet_length:12 numeric_precision:13 numeric_precision_radix:14 numeric_scale:15 datetime_precision:16 interval_type:17 interval_precision:18 character_set_catalog:19 character_set_schema:20 character_set_name:21 collation_catalog:22 collation_schema:23 collation_name:24 domain_catalog:25 domain_schema:26 domain_name:27 udt_catalog:28 udt_schema:29 udt_name:30 scope_catalog:31 scope_schema:32 scope_name:33 maximum_cardinality:34 dtd_identifier:35 is_self_referencing:36 is_identity:37 identity_generation:38 identity_start:39 identity_increment:40 identity_maximum:41 identity_minimum:42 identity_cycle:43 is_generated:44 generation_expression:45 is_updatable:46 is_hidden:47!null crdb_sql_type:48!null  [hidden: crdb_internal_vtable_pk:1!null]
 ├── key columns: crdb_internal_vtable_pk:1
 ├── lock columns: (49-96)
 ├── locking: for-update
 └── scan columns
      └── columns: crdb_internal_vtable_pk:1!null table_catalog:2!null table_schema:3!null table_name:4!null column_name:5!null column_comment:6 ordinal_position:7!null column_default:8 is_nullable:9!null data_type:10!null character_maximum_length:11 character_octet_length:12 numeric_precision:13 numeric_precision_radix:14 numeric_scale:15 datetime_precision:16 interval_type:17 interval_precision:18 character_set_catalog:19 character_set_schema:20 character_set_name:21 collation_catalog:22 collation_schema:23 collation_name:24 domain_catalog:25 domain_schema:26 domain_name:27 udt_catalog:28 udt_schema:29 udt_name:30 scope_catalog:31 scope_schema:32 scope_name:33 maximum_cardinality:34 dtd_identifier:35 is_self_referencing:36 is_identity:37 identity_generation:38 identity_start:39 identity_increment:40 identity_maximum:41 identity_minimum:42 identity_cycle:43 is_generated:44 generation_expression:45 is_updatable:46 is_hidden:47!null crdb_sql_type:48!null

# ------------------------------------------------------------------------------
# Tests with the NOWAIT lock wait policy.
# ------------------------------------------------------------------------------

build
SELECT * FROM t FOR UPDATE NOWAIT
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR UPDATE NOWAIT
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update,nowait
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
           └── locking: none,nowait

build
SELECT * FROM t FOR NO KEY UPDATE NOWAIT
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR NO KEY UPDATE NOWAIT
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-no-key-update,nowait
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
           └── locking: none,nowait

build
SELECT * FROM t FOR SHARE NOWAIT
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR SHARE NOWAIT
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-share,nowait
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
           └── locking: none,nowait

build
SELECT * FROM t FOR KEY SHARE NOWAIT
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR KEY SHARE NOWAIT
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-key-share,nowait
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
           └── locking: none,nowait

build
SELECT * FROM t FOR KEY SHARE FOR SHARE NOWAIT
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR KEY SHARE FOR SHARE NOWAIT
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (9,10)
 ├── locking: for-share,nowait
 └── lock t
      ├── columns: a:1!null b:2
      ├── key columns: a:1
      ├── lock columns: (5,6)
      ├── locking: for-key-share
      └── project
           ├── columns: a:1!null b:2
           └── scan t
                ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
                └── locking: none,nowait

build
SELECT * FROM t FOR KEY SHARE FOR SHARE NOWAIT FOR NO KEY UPDATE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR KEY SHARE FOR SHARE NOWAIT FOR NO KEY UPDATE
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (13,14)
 ├── locking: for-no-key-update
 └── lock t
      ├── columns: a:1!null b:2
      ├── key columns: a:1
      ├── lock columns: (9,10)
      ├── locking: for-share,nowait
      └── lock t
           ├── columns: a:1!null b:2
           ├── key columns: a:1
           ├── lock columns: (5,6)
           ├── locking: for-key-share
           └── project
                ├── columns: a:1!null b:2
                └── scan t
                     ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
                     └── locking: none,nowait

build
SELECT * FROM t FOR KEY SHARE FOR SHARE NOWAIT FOR NO KEY UPDATE FOR UPDATE NOWAIT
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR KEY SHARE FOR SHARE NOWAIT FOR NO KEY UPDATE FOR UPDATE NOWAIT
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (17,18)
 ├── locking: for-update,nowait
 └── lock t
      ├── columns: a:1!null b:2
      ├── key columns: a:1
      ├── lock columns: (13,14)
      ├── locking: for-no-key-update
      └── lock t
           ├── columns: a:1!null b:2
           ├── key columns: a:1
           ├── lock columns: (9,10)
           ├── locking: for-share,nowait
           └── lock t
                ├── columns: a:1!null b:2
                ├── key columns: a:1
                ├── lock columns: (5,6)
                ├── locking: for-key-share
                └── project
                     ├── columns: a:1!null b:2
                     └── scan t
                          ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
                          └── locking: none,nowait

build
SELECT * FROM t FOR UPDATE OF t NOWAIT
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR UPDATE OF t NOWAIT
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update,nowait
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
           └── locking: none,nowait

build
SELECT * FROM t FOR UPDATE OF t2 NOWAIT
----
error (42P01): relation "t2" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR UPDATE OF t2 NOWAIT
----
error (42P01): relation "t2" in FOR UPDATE clause not found in FROM clause

build
SELECT 1 FROM t FOR UPDATE OF t NOWAIT
----
project
 ├── columns: "?column?":5!null
 ├── scan t
 │    ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
 │    └── locking: for-update,nowait
 └── projections
      └── 1 [as="?column?":5]

build set=optimizer_use_lock_op_for_serializable=true
SELECT 1 FROM t FOR UPDATE OF t NOWAIT
----
lock t
 ├── columns: "?column?":5!null  [hidden: a:1!null]
 ├── key columns: a:1
 ├── lock columns: (6,7)
 ├── locking: for-update,nowait
 └── project
      ├── columns: "?column?":5!null a:1!null
      ├── scan t
      │    ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
      │    └── locking: none,nowait
      └── projections
           └── 1 [as="?column?":5]

# ------------------------------------------------------------------------------
# Tests with the SKIP LOCKED lock wait policy.
# ------------------------------------------------------------------------------

build
SELECT * FROM t FOR UPDATE SKIP LOCKED
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR UPDATE SKIP LOCKED
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update,skip-locked
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
           └── locking: none,skip-locked

build
SELECT * FROM t FOR NO KEY UPDATE SKIP LOCKED
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR NO KEY UPDATE SKIP LOCKED
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-no-key-update,skip-locked
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
           └── locking: none,skip-locked

build
SELECT * FROM t FOR SHARE SKIP LOCKED
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR SHARE SKIP LOCKED
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-share,skip-locked
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
           └── locking: none,skip-locked

build
SELECT * FROM t FOR KEY SHARE SKIP LOCKED
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR KEY SHARE SKIP LOCKED
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-key-share,skip-locked
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
           └── locking: none,skip-locked

build
SELECT * FROM t FOR KEY SHARE FOR SHARE SKIP LOCKED
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR KEY SHARE FOR SHARE SKIP LOCKED
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (9,10)
 ├── locking: for-share,skip-locked
 └── lock t
      ├── columns: a:1!null b:2
      ├── key columns: a:1
      ├── lock columns: (5,6)
      ├── locking: for-key-share
      └── project
           ├── columns: a:1!null b:2
           └── scan t
                ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
                └── locking: none,skip-locked

build
SELECT * FROM t FOR KEY SHARE FOR SHARE SKIP LOCKED FOR NO KEY UPDATE
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR KEY SHARE FOR SHARE SKIP LOCKED FOR NO KEY UPDATE
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (13,14)
 ├── locking: for-no-key-update
 └── lock t
      ├── columns: a:1!null b:2
      ├── key columns: a:1
      ├── lock columns: (9,10)
      ├── locking: for-share,skip-locked
      └── lock t
           ├── columns: a:1!null b:2
           ├── key columns: a:1
           ├── lock columns: (5,6)
           ├── locking: for-key-share
           └── project
                ├── columns: a:1!null b:2
                └── scan t
                     ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
                     └── locking: none,skip-locked

build
SELECT * FROM t FOR KEY SHARE FOR SHARE SKIP LOCKED FOR NO KEY UPDATE FOR UPDATE SKIP LOCKED
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR KEY SHARE FOR SHARE SKIP LOCKED FOR NO KEY UPDATE FOR UPDATE SKIP LOCKED
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (17,18)
 ├── locking: for-update,skip-locked
 └── lock t
      ├── columns: a:1!null b:2
      ├── key columns: a:1
      ├── lock columns: (13,14)
      ├── locking: for-no-key-update
      └── lock t
           ├── columns: a:1!null b:2
           ├── key columns: a:1
           ├── lock columns: (9,10)
           ├── locking: for-share,skip-locked
           └── lock t
                ├── columns: a:1!null b:2
                ├── key columns: a:1
                ├── lock columns: (5,6)
                ├── locking: for-key-share
                └── project
                     ├── columns: a:1!null b:2
                     └── scan t
                          ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
                          └── locking: none,skip-locked

build
SELECT * FROM t FOR UPDATE OF t SKIP LOCKED
----
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

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR UPDATE OF t SKIP LOCKED
----
lock t
 ├── columns: a:1!null b:2
 ├── key columns: a:1
 ├── lock columns: (5,6)
 ├── locking: for-update,skip-locked
 └── project
      ├── columns: a:1!null b:2
      └── scan t
           ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
           └── locking: none,skip-locked

build
SELECT * FROM t FOR UPDATE OF t2 SKIP LOCKED
----
error (42P01): relation "t2" in FOR UPDATE clause not found in FROM clause

build set=optimizer_use_lock_op_for_serializable=true
SELECT * FROM t FOR UPDATE OF t2 SKIP LOCKED
----
error (42P01): relation "t2" in FOR UPDATE clause not found in FROM clause

build
SELECT 1 FROM t FOR UPDATE OF t SKIP LOCKED
----
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
 └── projections
      └── 1 [as="?column?":5]

build set=optimizer_use_lock_op_for_serializable=true
SELECT 1 FROM t FOR UPDATE OF t SKIP LOCKED
----
lock t
 ├── columns: "?column?":5!null  [hidden: a:1!null]
 ├── key columns: a:1
 ├── lock columns: (6,7)
 ├── locking: for-update,skip-locked
 └── project
      ├── columns: "?column?":5!null a:1!null
      ├── scan t
      │    ├── columns: a:1!null b:2 crdb_internal_mvcc_timestamp:3 tableoid:4
      │    └── locking: none,skip-locked
      └── projections
           └── 1 [as="?column?":5]

# SKIP LOCKED cannot be used with multiple column families.
build
SELECT 1 FROM families FOR UPDATE OF families SKIP LOCKED
----
error (0A000): SKIP LOCKED cannot be used for tables with multiple column families

build set=optimizer_use_lock_op_for_serializable=true
SELECT 1 FROM families FOR UPDATE OF families SKIP LOCKED
----
error (0A000): SKIP LOCKED cannot be used for tables with multiple column families

build
SELECT 1 FROM families FOR SHARE SKIP LOCKED
----
error (0A000): SKIP LOCKED cannot be used for tables with multiple column families

build set=optimizer_use_lock_op_for_serializable=true
SELECT 1 FROM families FOR SHARE SKIP LOCKED
----
error (0A000): SKIP LOCKED cannot be used for tables with multiple column families
