statement ok
CREATE TABLE abc (a INT PRIMARY KEY, b INT, c INT, INDEX (b), FAMILY (a, b, c))

statement ok
INSERT INTO abc VALUES (1, 10, 100), (2, 20, 200), (3, 30, 300)

statement ok
CREATE TABLE bcd (b INT PRIMARY KEY, c INT, d INT, INDEX (c), FAMILY (b, c, d))

statement ok
INSERT INTO bcd VALUES (20, 200, 2000), (30, 300, 3000), (40, 400, 4000)

statement ok
GRANT ALL on abc TO testuser

statement ok
GRANT ALL on bcd TO testuser

# First, hold locks on some rows of abc and bcd. We'll update abc at the end.

user testuser

statement ok
BEGIN ISOLATION LEVEL READ COMMITTED

query III rowsort
SELECT * FROM abc WHERE a != 3 FOR UPDATE
----
1  10  100
2  20  200

query III
SELECT * FROM bcd ORDER BY c DESC LIMIT 2 FOR SHARE
----
40  400  4000
30  300  3000

# Then ensure we wait on the locks and see the updated rows after commit.

user root

# Normal reads do not block.

query III rowsort
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM abc;
COMMIT;
----
1  10  100
2  20  200
3  30  300

query III rowsort
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM bcd;
COMMIT;
----
20  200  2000
30  300  3000
40  400  4000

# SKIP LOCKED reads do not block.

query III rowsort
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM abc FOR UPDATE SKIP LOCKED;
COMMIT;
----
3 30 300

query III rowsort
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM bcd FOR UPDATE SKIP LOCKED;
COMMIT;
----
20 200 2000

# Shared reads block on exclusive locks but not on shared locks.

query III async,rowsort q00
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM abc FOR SHARE;
COMMIT;
----
1  11  101
2  21  201
3  30  300

query III rowsort
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM bcd FOR SHARE;
COMMIT;
----
20  200  2000
30  300  3000
40  400  4000

# Exclusive reads block on both.

query III async,rowsort q01
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM abc FOR UPDATE;
COMMIT;
----
1  11  101
2  21  201
3  30  300

query III async,rowsort q02
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM bcd FOR UPDATE;
COMMIT;
----
20  200  2000
30  300  3000
40  400  4000

# Try more exclusive-locking queries.

query I async q03
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT a FROM abc WHERE a = 2 FOR UPDATE;
COMMIT;
----
2

query I async q04
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT b FROM abc WHERE a = 2 FOR UPDATE;
COMMIT;
----
21

query I async q05
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT c FROM abc WHERE a = 2 FOR UPDATE;
COMMIT;
----
201

query I async q06
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT c FROM abc ORDER BY a DESC LIMIT 2 FOR UPDATE;
COMMIT;
----
300
201

query I async,rowsort q07
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT a + b + c FROM abc FOR UPDATE;
COMMIT;
----
113
224
333

# Try some joins

query IIIII async,rowsort q08
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM abc JOIN bcd USING (b) FOR SHARE;
COMMIT;
----
30  3  300  300  3000

query IIIII async,rowsort q09
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM abc JOIN bcd USING (c) FOR UPDATE;
COMMIT;
----
300  3  30  30  3000

user testuser

statement ok
UPDATE abc SET b = b + 1, c = c + 1 WHERE a != 3

statement ok
COMMIT

user root

awaitquery q00

awaitquery q01

awaitquery q02

awaitquery q03

awaitquery q04

awaitquery q05

awaitquery q06

awaitquery q07

awaitquery q08

awaitquery q09

subtest multi_lock

statement ok
INSERT INTO abc VALUES (5, 1, 7), (6, 1, 7)

statement ok
INSERT INTO bcd VALUES (6, 1, 5), (7, 1, 5)

# Check that we can acquire the same lock multiple times.

user testuser

statement ok
BEGIN ISOLATION LEVEL READ COMMITTED

# Try locking the same row twice from a single statement.
query IIIIII rowsort
SELECT * FROM bcd JOIN abc ON a = d WHERE bcd.b IN (6, 7) FOR UPDATE OF abc
----
6  1  5  5  1  7
7  1  5  5  1  7

# Try locking the same row twice from the same transaction, with another waiter
# in between the two.

query III rowsort
SELECT * FROM bcd WHERE b = 7 FOR UPDATE
----
7  1  5

user root

query III rowsort
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM abc WHERE a > 4 FOR UPDATE SKIP LOCKED;
COMMIT;
----
6 1 7

query III rowsort
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM bcd WHERE b < 8 FOR UPDATE SKIP LOCKED;
COMMIT;
----
6 1 5

query III async,rowsort q10
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM abc WHERE a > 4 FOR UPDATE;
COMMIT;
----
5  2  7
6  1  7

query III async,rowsort q11
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM bcd WHERE b < 8 FOR UPDATE;
COMMIT;
----
6  1  5
7  2  5

user testuser

query IIIIII rowsort
SELECT * FROM abc JOIN bcd ON bcd.b = abc.c WHERE a IN (5, 6) FOR UPDATE OF bcd
----
5  1  7  7  1  5
6  1  7  7  1  5

statement ok
UPDATE abc SET b = b + 1 WHERE a = 5

statement ok
UPDATE bcd SET c = c + 1 WHERE b = 7

query III rowsort
SELECT * FROM abc WHERE a > 4
----
5  2  7
6  1  7

query III rowsort
SELECT * FROM bcd WHERE b < 8
----
6  1  5
7  2  5

statement ok
COMMIT

user root

awaitquery q10

awaitquery q11

# Test the same as above, but with UPDATE instead of SELECT FOR UPDATE.

user testuser

statement ok
BEGIN ISOLATION LEVEL READ COMMITTED

# Try locking the same row twice from a single statement.
query IIIIII rowsort
SELECT * FROM bcd JOIN abc ON a = d WHERE bcd.b IN (6, 7) FOR UPDATE OF abc
----
6  1  5  5  2  7
7  2  5  5  2  7

# Try locking the same row twice from the same transaction, with another waiter
# in between the two.

query III rowsort
SELECT * FROM bcd WHERE b = 7 FOR UPDATE
----
7  2  5

user root

query III rowsort
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM abc WHERE a > 4 FOR UPDATE SKIP LOCKED;
COMMIT;
----
6 1 7

query III rowsort
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM bcd WHERE b < 8 FOR UPDATE SKIP LOCKED;
COMMIT;
----
6 1 5

query III async,rowsort q12
BEGIN ISOLATION LEVEL READ COMMITTED;
UPDATE abc SET b = b + 1 WHERE a > 4 RETURNING *;
COMMIT;
----
5  4  7
6  2  7

query III async,rowsort q13
BEGIN ISOLATION LEVEL READ COMMITTED;
UPDATE bcd SET c = c + 1 WHERE b < 8 RETURNING *;
COMMIT;
----
6  2  5
7  4  5

user testuser

query IIIIII rowsort
SELECT * FROM abc JOIN bcd ON bcd.b = abc.c WHERE a IN (5, 6) FOR UPDATE OF bcd
----
5  2  7  7  2  5
6  1  7  7  2  5

statement ok
UPDATE abc SET b = b + 1 WHERE a = 5

statement ok
UPDATE bcd SET c = c + 1 WHERE b = 7

query III rowsort
SELECT * FROM abc WHERE a > 4
----
5  3  7
6  1  7

query III rowsort
SELECT * FROM bcd WHERE b < 8
----
6  1  5
7  3  5

statement ok
COMMIT

user root

awaitquery q12

awaitquery q13

subtest lock_column_family

# Check that we lock all column families.

statement ok
CREATE TABLE abcde (
  a INT NOT NULL,
  b INT NOT NULL,
  c INT NOT NULL,
  d INT NULL,
  e INT NOT NULL,
  PRIMARY KEY (a, b),
  FAMILY (a),
  FAMILY (b),
  FAMILY (c),
  FAMILY (d),
  FAMILY (e)
)

statement ok
GRANT ALL on abcde TO testuser

statement ok
INSERT INTO abcde VALUES (5, 6, 7, 8, 9)

user testuser

statement ok
BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED

# Only read one column family in the initial scan, but lock all of them.
query I rowsort
SELECT e FROM abcde WHERE a = 5 AND b = 6 FOR UPDATE
----
9

user root

# Updating the same column family should block and then retry.
query I async,rowsort q14
BEGIN ISOLATION LEVEL READ COMMITTED;
UPDATE abcde SET e = e + 10 WHERE a = 5 AND b = 6 RETURNING e;
COMMIT;
----
29

# Updating a different column family should also block.
query I async,rowsort q15
BEGIN ISOLATION LEVEL READ COMMITTED;
UPDATE abcde SET c = c + 10 WHERE a = 5 AND b = 6 RETURNING c;
COMMIT;
----
17

user testuser

query II rowsort
SELECT c, e FROM abcde WHERE a = 5 AND b = 6
----
7  9

statement ok
UPDATE abcde SET e = e + 10 WHERE a = 5 AND b = 6

statement ok
COMMIT

user root

awaitquery q14

awaitquery q15

# Now try again, but lock a nullable column. Again, we should be locking all
# column families.

statement ok
INSERT INTO abcde VALUES (45, 46, 47, NULL, 49)

user testuser

statement ok
BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED

# Only read one nullable column in the initial scan, which requires also reading
# the first column family, but lock all of them.
query I rowsort
SELECT d FROM abcde WHERE a = 45 AND b = 46 FOR UPDATE
----
NULL

user root

# Locked-reading from the same column family should block on the lock.
query I async,rowsort q16
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT d FROM abcde WHERE a = 45 AND b = 46 FOR UPDATE;
COMMIT;
----
68

# Locked-reading from a different column family should also block.
query I async,rowsort q17
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT c FROM abcde WHERE a = 45 AND b = 46 FOR UPDATE;
INSERT INTO abcde VALUES (45, 66, 67, 68, 69);
COMMIT;
----
47

user testuser

query I rowsort
SELECT d FROM abcde WHERE a = 45
----
NULL

statement ok
UPDATE abcde SET d = 68 WHERE a = 45 AND b = 46

statement ok
COMMIT

user root

awaitquery q16

awaitquery q17

# Now try again, but lock one of the PK columns. Again, we should be locking all
# column families.

statement ok
INSERT INTO abcde VALUES (85, 86, 87, 88, 89)

user testuser

statement ok
BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED

statement ok
SET tracing = kv

# Only read one of the PK column in the initial scan, which might only require
# reading the first column family but should still lock all column families.
query I rowsort
SELECT b FROM abcde WHERE a = 85 AND b = 86 FOR UPDATE
----
86

user root

# Locked-reading from the same column family should block on the lock.
query I async,rowsort q18
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT b FROM abcde WHERE a = 85 AND b = 86 FOR UPDATE;
INSERT INTO abcde VALUES (85, 96, 97, 98, 99);
COMMIT;
----
86

user testuser

query I rowsort
SELECT b FROM abcde WHERE a = 85
----
86

statement ok
COMMIT

user root

awaitquery q18

subtest skip_locked_nowait

statement ok
CREATE TABLE xyz (x INT PRIMARY KEY, y INT, z INT, INDEX (y), FAMILY (x, y, z))

statement ok
GRANT ALL ON xyz TO testuser

statement ok
INSERT INTO xyz VALUES (1, 10, 100), (2, 10, 200), (3, 30, 100)

user testuser

statement ok
SET optimizer_use_lock_op_for_serializable = true

statement ok
SET enable_durable_locking_for_serializable = true

statement ok
BEGIN ISOLATION LEVEL SERIALIZABLE

# Lock the first row.

query III
SELECT * FROM xyz WHERE x = 1 FOR UPDATE
----
1  10  100

user root

# A limited SKIP LOCKED should be able to skip over the locked first row without
# stopping early or blocking.

query III
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT * FROM xyz@xyz_y_idx WHERE y = 10 ORDER BY y, x LIMIT 1 FOR UPDATE SKIP LOCKED;
COMMIT;
----
2  10  200

# Any unlocked reads underneath SKIP LOCKED or NOWAIT should not block on locks,
# either, even under serializable isolation.

statement ok
SET optimizer_use_lock_op_for_serializable = true

statement ok
SET enable_durable_locking_for_serializable = true

query III
SELECT * FROM xyz WHERE z = 100 ORDER BY x FOR UPDATE SKIP LOCKED
----
3  30  100

query error pgcode 55P03 could not obtain lock on row \(x\)=\(1\) in xyz@xyz_pkey
SELECT * FROM xyz WHERE z = 100 ORDER BY x FOR UPDATE NOWAIT

user testuser

statement ok
COMMIT

statement ok
RESET optimizer_use_lock_op_for_serializable

statement ok
RESET enable_durable_locking_for_serializable

user root

statement ok
RESET optimizer_use_lock_op_for_serializable

statement ok
RESET enable_durable_locking_for_serializable
