I wonder could there ever be a concurrent situation when phantom read occurs during single SQL statement execution?
Given: tx_test
is a table with 3 rows and id
column is primary key, value
is some text payload. Tables one_row_table1
, one_row_table2
, one_row_table3
all have 1 row at start of transaction. Consider SQL statement executed in READ COMMITTED
transaction:
BEGIN;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED ;
WITH cte1 AS (
UPDATE tx_test
SET value = 'updated'
WHERE id = (SELECT COUNT(*) FROM one_row_table1)
RETURNING id),
cte2 AS (
UPDATE tx_test
SET value = 'updated'
WHERE id = (SELECT COUNT(*) FROM one_row_table1)
/* what if one_row_table2 is used here? */
RETURNING id),
cte3 AS (
UPDATE tx_test
SET value = 'updated'
WHERE id = (SELECT COUNT(*) FROM one_row_table1)
/* what if one_row_table3 is used here? */
RETURNING id)
SELECT * FROM cte1
UNION ALL
SELECT * FROM cte2
UNION ALL
SELECT * FROM cte3;
COMMIT;
Could there be a case when the statement above updates more then 1 record?
In my experiments with similar situation but with 3 different statements inside one transaction I was easily able to get 2 rows updated. Of course it is true only when isolation level is lower than SERIALIZABLE
.
BEGIN;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 1
UPDATE tx_test
SET value = 'updated'
WHERE id = (SELECT COUNT(*) FROM one_row_table1);
-- 2
-- Another transaction inserts into one_row_table1
UPDATE tx_test
SET value = 'updated'
WHERE id = (SELECT COUNT(*) FROM one_row_table1);
-- 3
UPDATE tx_test
SET value = 'updated'
WHERE id = (SELECT COUNT(*) FROM one_row_table1);
COMMIT;
From similar question How do I set the isolation level for SELECT statement? I know that
... This means that each SQL statement sees a snapshot of data (a database version) as it was some time ago, regardless of the current state of the underlying data. This prevents statements from viewing inconsistent data produced by concurrent transactions performing updates on the same data rows, providing transaction isolation for each database session. ...
Can I interpret this as PostgreSQL always provides SERIALIZABLE
level of transaction isolation per statement? Or what kind of isolation?
UPDATE t SET a=1, a=1 ...
is not allowed.