# LogicTest: !weak-iso-level-configs
# This test assumes that SERIALIZABLE isolation is being used. See
# select_for_update_read_committed for READ COMMITTED testing.

query I
SELECT 1 FOR UPDATE
----
1

query I
SELECT 1 FOR NO KEY UPDATE
----
1

query I
SELECT 1 FOR SHARE
----
1

query I
SELECT 1 FOR KEY SHARE
----
1

query I
SELECT 1 FOR UPDATE FOR SHARE FOR NO KEY UPDATE FOR KEY SHARE
----
1

query error pgcode 42P01 relation "a" in FOR UPDATE clause not found in FROM clause
SELECT 1 FOR UPDATE OF a

query error pgcode 42P01 relation "a" in FOR SHARE clause not found in FROM clause
SELECT 1 FOR SHARE OF a, b

query error pgcode 42P01 relation "a" in FOR UPDATE clause not found in FROM clause
SELECT 1 FOR UPDATE OF a FOR SHARE OF b, c FOR NO KEY UPDATE OF d FOR KEY SHARE OF e, f

query I
SELECT 1 FROM
    (SELECT 1) a,
    (SELECT 1) b,
    (SELECT 1) c,
    (SELECT 1) d,
    (SELECT 1) e,
    (SELECT 1) f
FOR UPDATE OF a FOR SHARE OF b, c FOR NO KEY UPDATE OF d FOR KEY SHARE OF e, f
----
1

# However, we do mirror Postgres in that we require FOR UPDATE targets to be
# unqualified names and reject anything else.

query error pgcode 42601 FOR UPDATE must specify unqualified relation names
SELECT 1 FOR UPDATE OF public.a

query error pgcode 42601 FOR UPDATE must specify unqualified relation names
SELECT 1 FOR UPDATE OF db.public.a

query I
SELECT 1 FOR UPDATE SKIP LOCKED
----
1

query I
SELECT 1 FOR NO KEY UPDATE SKIP LOCKED
----
1

query I
SELECT 1 FOR SHARE SKIP LOCKED
----
1

query I
SELECT 1 FOR KEY SHARE SKIP LOCKED
----
1

query error pgcode 42P01 relation "a" in FOR UPDATE clause not found in FROM clause
SELECT 1 FOR UPDATE OF a SKIP LOCKED

query error pgcode 42P01 relation "a" in FOR UPDATE clause not found in FROM clause
SELECT 1 FOR UPDATE OF a SKIP LOCKED FOR NO KEY UPDATE OF b SKIP LOCKED

query error pgcode 42P01 relation "a" in FOR UPDATE clause not found in FROM clause
SELECT 1 FOR UPDATE OF a SKIP LOCKED FOR NO KEY UPDATE OF b NOWAIT

query error pgcode 42P01 relation "a" in FOR UPDATE clause not found in FROM clause
SELECT 1 FOR UPDATE OF a SKIP LOCKED FOR SHARE OF b, c SKIP LOCKED FOR NO KEY UPDATE OF d SKIP LOCKED FOR KEY SHARE OF e, f SKIP LOCKED

query I
SELECT 1 FOR UPDATE NOWAIT
----
1

query I
SELECT 1 FOR NO KEY UPDATE NOWAIT
----
1

query I
SELECT 1 FOR SHARE NOWAIT
----
1

query I
SELECT 1 FOR KEY SHARE NOWAIT
----
1

query error pgcode 42P01 relation "a" in FOR UPDATE clause not found in FROM clause
SELECT 1 FOR UPDATE OF a NOWAIT

query error pgcode 42P01 relation "a" in FOR UPDATE clause not found in FROM clause
SELECT 1 FOR UPDATE OF a NOWAIT FOR NO KEY UPDATE OF b NOWAIT

query error pgcode 42P01 relation "a" in FOR UPDATE clause not found in FROM clause
SELECT 1 FOR UPDATE OF a NOWAIT FOR SHARE OF b, c NOWAIT FOR NO KEY UPDATE OF d NOWAIT FOR KEY SHARE OF e, f NOWAIT

# Locking clauses both inside and outside of parenthesis are handled correctly.

query I
((SELECT 1)) FOR UPDATE SKIP LOCKED
----
1

query I
((SELECT 1) FOR UPDATE SKIP LOCKED)
----
1

query I
((SELECT 1 FOR UPDATE SKIP LOCKED))
----
1

# Unlike Postgres, we do not support multiple locking clauses on parenthesized
# queries.

statement error pgcode 42601 multiple FOR UPDATE clauses not allowed
(SELECT 1 FOR UPDATE) FOR UPDATE

statement error pgcode 42601 multiple FOR UPDATE clauses not allowed
((SELECT 1 FOR UPDATE)) FOR UPDATE

statement error pgcode 42601 multiple FOR UPDATE clauses not allowed
((SELECT 1) FOR UPDATE) FOR UPDATE

# But we do support locking clauses both inside and outside subqueries.

statement ok
SELECT (SELECT 1 FOR UPDATE) FOR UPDATE

statement ok
SELECT * FROM (SELECT 1 FOR UPDATE) AS x FOR UPDATE

statement ok
SELECT * FROM (SELECT 1 FOR UPDATE) AS x WHERE EXISTS (SELECT 1 FOR UPDATE) FOR UPDATE

# FOR READ ONLY is ignored, like in Postgres.
query I
SELECT 1 FOR READ ONLY
----
1

# Various operations are not supported when locking clauses are provided.
# FeatureNotSupported errors are thrown for each of them.

statement ok
CREATE TABLE i (i PRIMARY KEY) AS SELECT 1

statement error pgcode 0A000 FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT
SELECT 1 UNION SELECT 1 FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT
SELECT * FROM (SELECT 1 UNION SELECT 1) a FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT
SELECT * FROM (SELECT 1 UNION SELECT 1) a, i FOR UPDATE

query II
SELECT * FROM (SELECT 1 UNION SELECT 1) a, i FOR UPDATE OF i
----
1  1

statement error pgcode 0A000 FOR UPDATE is not allowed with VALUES
VALUES (1) FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with VALUES
(VALUES (1)) FOR UPDATE

# VALUES within a subquery is allowed, like in Postgres.

query I
SELECT (VALUES (1)) FOR UPDATE
----
1

query I
SELECT * FROM (VALUES (1)) a FOR UPDATE
----
1

query II
SELECT * FROM (VALUES (1)) a, i FOR UPDATE
----
1  1

query II
SELECT * FROM (VALUES (1)) a, i FOR UPDATE OF a
----
1  1

query II
SELECT * FROM (VALUES (1)) a, i FOR UPDATE OF i
----
1  1

statement error pgcode 0A000 FOR UPDATE is not allowed with DISTINCT clause
SELECT DISTINCT 1 FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with DISTINCT clause
SELECT * FROM (SELECT DISTINCT 1) a FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with DISTINCT clause
SELECT * FROM (SELECT DISTINCT 1) a, i FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with DISTINCT clause
SELECT * FROM (SELECT DISTINCT 1) a, i FOR UPDATE OF a

query II
SELECT * FROM (SELECT DISTINCT 1) a, i FOR UPDATE OF i
----
1  1

statement error pgcode 0A000 FOR UPDATE is not allowed with GROUP BY clause
SELECT 1 GROUP BY 1 FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with GROUP BY clause
SELECT * FROM (SELECT 1 GROUP BY 1) a FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with GROUP BY clause
SELECT * FROM (SELECT 1 GROUP BY 1) a, i FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with GROUP BY clause
SELECT * FROM (SELECT 1 GROUP BY 1) a, i FOR UPDATE OF a

query II
SELECT * FROM (SELECT 1 GROUP BY 1) a, i FOR UPDATE OF i
----
1  1

statement error pgcode 0A000 FOR UPDATE is not allowed with HAVING clause
SELECT 1 HAVING TRUE FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with HAVING clause
SELECT * FROM (SELECT 1 HAVING TRUE) a FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with HAVING clause
SELECT * FROM (SELECT 1 HAVING TRUE) a, i FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with HAVING clause
SELECT * FROM (SELECT 1 HAVING TRUE) a, i FOR UPDATE OF a

query II
SELECT * FROM (SELECT 1 HAVING TRUE) a, i FOR UPDATE OF i
----
1  1

statement error pgcode 0A000 FOR UPDATE is not allowed with aggregate functions
SELECT count(1) FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with aggregate functions
SELECT * FROM (SELECT count(1)) a FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with aggregate functions
SELECT * FROM (SELECT count(1)) a, i FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with aggregate functions
SELECT * FROM (SELECT count(1)) a, i FOR UPDATE OF a

query II
SELECT * FROM (SELECT count(1)) a, i FOR UPDATE OF i
----
1  1

statement error pgcode 0A000 FOR UPDATE is not allowed with window functions
SELECT count(1) OVER () FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with window functions
SELECT * FROM (SELECT count(1) OVER ()) a FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with window functions
SELECT * FROM (SELECT count(1) OVER ()) a, i FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with window functions
SELECT * FROM (SELECT count(1) OVER ()) a, i FOR UPDATE OF a

query II
SELECT * FROM (SELECT count(1) OVER ()) a, i FOR UPDATE OF i
----
1  1

statement error pgcode 0A000 FOR UPDATE is not allowed with set-returning functions in the target list
SELECT generate_series(1, 2) FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with set-returning functions in the target list
SELECT * FROM (SELECT generate_series(1, 2)) a FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with set-returning functions in the target list
SELECT * FROM (SELECT generate_series(1, 2)) a, i FOR UPDATE

statement error pgcode 0A000 FOR UPDATE is not allowed with set-returning functions in the target list
SELECT * FROM (SELECT generate_series(1, 2)) a, i FOR UPDATE OF a

query II nosort
SELECT * FROM (SELECT generate_series(1, 2)) a, i FOR UPDATE OF i
----
1  1
2  1

# Set-returning functions in the from list are allowed.
query I nosort
SELECT * FROM generate_series(1, 2) FOR UPDATE
----
1
2

query I nosort
SELECT * FROM (SELECT * FROM generate_series(1, 2)) a FOR UPDATE
----
1
2

query II nosort
SELECT * FROM (SELECT * FROM generate_series(1, 2)) a, i FOR UPDATE
----
1  1
2  1

# Use of SELECT FOR UPDATE/SHARE requires SELECT and UPDATE privileges.

statement ok
CREATE TABLE t (k INT PRIMARY KEY, v int, FAMILY (k, v))

user testuser

statement error pgcode 42501 user testuser does not have SELECT privilege on relation t
SELECT * FROM t

statement error pgcode 42501 user testuser does not have SELECT privilege on relation t
SELECT * FROM t FOR UPDATE

statement error pgcode 42501 user testuser does not have SELECT privilege on relation t
SELECT * FROM t FOR SHARE

user root

statement ok
GRANT SELECT ON t TO testuser

user testuser

statement ok
SELECT * FROM t

statement error pgcode 42501 user testuser does not have UPDATE privilege on relation t
SELECT * FROM t FOR UPDATE

statement error pgcode 42501 user testuser does not have UPDATE privilege on relation t
SELECT * FROM t FOR SHARE

user root

statement ok
REVOKE SELECT ON t FROM testuser

statement ok
GRANT UPDATE ON t TO testuser

user testuser

statement error pgcode 42501 user testuser does not have SELECT privilege on relation t
SELECT * FROM t

statement error pgcode 42501 user testuser does not have SELECT privilege on relation t
SELECT * FROM t FOR UPDATE

statement error pgcode 42501 user testuser does not have SELECT privilege on relation t
SELECT * FROM t FOR SHARE

user root

statement ok
GRANT SELECT ON t TO testuser

user testuser

statement ok
SELECT * FROM t

statement ok
SELECT * FROM t FOR UPDATE

statement ok
SELECT * FROM t FOR SHARE

user root

# Use of SELECT FOR UPDATE/SHARE in ReadOnly Transaction

statement ok
BEGIN READ ONLY

statement error cannot execute SELECT FOR UPDATE in a read-only transaction
SELECT * FROM t FOR UPDATE

statement ok
ROLLBACK

statement ok
BEGIN READ ONLY

statement error cannot execute SELECT FOR NO KEY UPDATE in a read-only transaction
SELECT * FROM t FOR NO KEY UPDATE

statement ok
ROLLBACK

statement ok
BEGIN READ ONLY

statement error cannot execute SELECT FOR SHARE in a read-only transaction
SELECT * FROM t FOR SHARE

statement ok
ROLLBACK

statement ok
BEGIN READ ONLY

statement error cannot execute SELECT FOR KEY SHARE in a read-only transaction
SELECT * FROM t FOR KEY SHARE

statement ok
ROLLBACK

# The NOWAIT wait policy returns error when a conflicting lock is encountered.

statement ok
INSERT INTO t VALUES (1, 1)

statement ok
BEGIN; UPDATE t SET v = 2 WHERE k = 1

user testuser

query error pgcode 55P03 could not obtain lock on row \(k\)=\(1\) in t@t_pkey
SELECT * FROM t FOR UPDATE NOWAIT

query error pgcode 55P03 could not obtain lock on row \(k\)=\(1\) in t@t_pkey
SELECT * FROM t FOR SHARE FOR UPDATE OF t NOWAIT

query error pgcode 55P03 could not obtain lock on row \(k\)=\(1\) in t@t_pkey
SELECT * FROM t FOR SHARE NOWAIT FOR UPDATE OF t

query error pgcode 55P03 could not obtain lock on row \(k\)=\(1\) in t@t_pkey
BEGIN; SELECT * FROM t FOR UPDATE NOWAIT

statement ok
ROLLBACK

statement ok
SET optimizer_use_lock_op_for_serializable = on

query error pgcode 55P03 could not obtain lock on row \(k\)=\(1\) in t@t_pkey
SELECT * FROM t FOR UPDATE NOWAIT

query error pgcode 55P03 could not obtain lock on row \(k\)=\(1\) in t@t_pkey
SELECT * FROM t FOR SHARE FOR UPDATE OF t NOWAIT

query error pgcode 55P03 could not obtain lock on row \(k\)=\(1\) in t@t_pkey
SELECT * FROM t FOR SHARE NOWAIT FOR UPDATE OF t

query error pgcode 55P03 could not obtain lock on row \(k\)=\(1\) in t@t_pkey
BEGIN; SELECT * FROM t FOR UPDATE NOWAIT

statement ok
ROLLBACK

statement ok
RESET optimizer_use_lock_op_for_serializable

user root

statement ok
ROLLBACK

# The NOWAIT wait policy can be applied to a subset of the tables being locked.

statement ok
CREATE TABLE t2 (k INT PRIMARY KEY, v2 int)

statement ok
GRANT SELECT ON t2 TO testuser

statement ok
GRANT UPDATE ON t2 TO testuser

statement ok
INSERT INTO t2 VALUES (1, 11)

statement ok
BEGIN; UPDATE t SET v = 2 WHERE k = 1

user testuser

statement ok
SET enable_shared_locking_for_serializable = true

query error pgcode 55P03 could not obtain lock on row \(k\)=\(1\) in t@t_pkey
SELECT v, v2 FROM t JOIN t2 USING (k) FOR SHARE FOR SHARE OF t NOWAIT

query error pgcode 55P03 could not obtain lock on row \(k\)=\(1\) in t@t_pkey
SELECT v, v2 FROM t JOIN t2 USING (k) FOR SHARE OF t2 FOR SHARE OF t NOWAIT

query error pgcode 55P03 could not obtain lock on row \(k\)=\(1\) in t@t_pkey
SELECT v, v2 FROM t JOIN t2 USING (k) FOR SHARE NOWAIT FOR SHARE OF t

query error pgcode 55P03 could not obtain lock on row \(k\)=\(1\) in t@t_pkey
SELECT v, v2 FROM t JOIN t2 USING (k) FOR SHARE NOWAIT FOR SHARE OF t2

statement ok
SET statement_timeout = '10ms'

query error pgcode 57014 query execution canceled due to statement timeout
SELECT v, v2 FROM t JOIN t2 USING (k) FOR SHARE FOR SHARE OF t2 NOWAIT

query error pgcode 57014 query execution canceled due to statement timeout
SELECT v, v2 FROM t JOIN t2 USING (k) FOR SHARE OF t FOR SHARE OF t2 NOWAIT

statement ok
SET statement_timeout = 0

user root

statement ok
ROLLBACK

# The SKIP LOCKED wait policy skip rows when a conflicting lock is encountered.

statement ok
INSERT INTO t VALUES (2, 2), (3, 3), (4, 4)

statement ok
CREATE TABLE t3 (
  k INT PRIMARY KEY,
  v INT,
  u INT,
  INDEX (u),
  FAMILY (k, v, u)
)

statement ok
INSERT INTO t3 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 2)

statement ok
GRANT SELECT ON t3 TO testuser

statement ok
GRANT UPDATE ON t3 TO testuser

statement ok
BEGIN; UPDATE t SET v = 3 WHERE k = 2; UPDATE t3 SET v = 3 WHERE k = 2

user testuser

statement ok
BEGIN

query II rowsort
SELECT * FROM t FOR UPDATE SKIP LOCKED
----
1  1
3  3
4  4

statement ok
UPDATE t SET v = 4 WHERE k = 3

query II rowsort
SELECT * FROM t FOR UPDATE SKIP LOCKED
----
1  1
3  4
4  4

# All columns are available from the secondary index on u, so no index join is
# needed. The secondary index can produce the row where k=2 since the row is
# only locked in the primary index.
query II rowsort
SELECT k, u FROM t3 WHERE u = 2 FOR UPDATE SKIP LOCKED
----
2  2
4  2

# An index join is needed to fetch column v. The index join filters out the
# first row, which is locked.
query III
SELECT * FROM t3 WHERE u = 2 FOR UPDATE SKIP LOCKED
----
4  4  2

# Since the limit is not pushed below the index join, we still see the second row.
query III
SELECT * FROM t3 WHERE u = 2 LIMIT 1 FOR UPDATE SKIP LOCKED
----
4  4  2

user root

query II
SELECT * FROM t FOR UPDATE SKIP LOCKED
----
2  3

statement ok
ROLLBACK

user testuser

statement ok
ROLLBACK

user root

# Regression test for not propagating lock spans with leaf txns (#94290).
statement ok
CREATE TABLE t94290 (a INT, b INT, c INT, PRIMARY KEY(a), UNIQUE INDEX(b));
INSERT INTO t94290 VALUES (1,2,3);

# If the lock spans are not propagated, then the second query would take almost
# 5 seconds.
statement ok
SET statement_timeout = '2s';

statement ok
SELECT * FROM t94290 WHERE b = 2 FOR UPDATE;

statement ok
SELECT * FROM t94290 WHERE b = 2 FOR UPDATE;

statement ok
RESET statement_timeout;

# Ensure FOR UPDATE SKIP LOCKED works correctly with replicated locks.

statement ok
DROP TABLE IF EXISTS t;
CREATE TABLE t(a INT PRIMARY KEY);
INSERT INTO t VALUES(1), (2);
GRANT ALL ON t TO testuser;
GRANT SYSTEM MODIFYCLUSTERSETTING TO testuser;

user testuser

statement ok
SET enable_durable_locking_for_serializable = true;

query I
BEGIN;
SELECT * FROM t WHERE a = 1 FOR UPDATE;
----
1

user root

query TTTTTTTBB colnames,retry,rowsort
SELECT database_name, schema_name, table_name, lock_key_pretty, lock_strength, durability, isolation_level, granted, contended FROM crdb_internal.cluster_locks
----
database_name  schema_name  table_name  lock_key_pretty  lock_strength  durability  isolation_level  granted  contended

query I
SELECT * FROM t FOR UPDATE SKIP LOCKED
----
2

# Ensure the replicated lock isn't pulled into the in-memory lock table, even
# after the skip locked statement has run.
query TTTTTTTBB colnames,retry,rowsort
SELECT database_name, schema_name, table_name, lock_key_pretty, lock_strength, durability, isolation_level, granted, contended FROM crdb_internal.cluster_locks
----
database_name  schema_name  table_name  lock_key_pretty   lock_strength  durability    isolation_level  granted  contended

user testuser

statement ok
COMMIT

user root

# Regression test for #121917 and #129145: ensure SKIP LOCKED and NOWAIT both
# work when locking comes after the initial scan, due to
# optimizer_use_lock_op_for_serializable.

statement ok
CREATE TABLE t129145 (a INT NOT NULL PRIMARY KEY, b INT NOT NULL, FAMILY (a, b))

statement ok
INSERT INTO t129145 VALUES (1, 1)

statement ok
GRANT SELECT ON t129145 TO testuser

statement ok
GRANT UPDATE ON t129145 TO testuser

statement ok
SET enable_durable_locking_for_serializable = true

statement ok
BEGIN; SELECT * FROM t129145 WHERE a = 1 FOR UPDATE

user testuser

statement ok
SET optimizer_use_lock_op_for_serializable = on

statement ok
SET enable_durable_locking_for_serializable = true

query II
SELECT * FROM t129145 WHERE b = 1 FOR UPDATE SKIP LOCKED
----

query error pgcode 55P03 could not obtain lock on row \(a\)=\(1\) in t129145@t129145_pkey
SELECT * FROM t129145 WHERE b = 1 FOR UPDATE NOWAIT

statement ok
RESET optimizer_use_lock_op_for_serializable

statement ok
RESET enable_durable_locking_for_serializable

user root

statement ok
ROLLBACK

statement ok
RESET enable_durable_locking_for_serializable
