Re: Fix missing EvalPlanQual recheck for TID scans

From: Chao Li <li(dot)evan(dot)chao(at)gmail(dot)com>
To: David Rowley <dgrowleyml(at)gmail(dot)com>, Sophie Alpert <pg(at)sophiebits(dot)com>
Cc: pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Re: Fix missing EvalPlanQual recheck for TID scans
Date: 2025-09-15 05:31:59
Message-ID: 91C3354F-CCF0-4691-9F79-6F13C4D38B0F@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi David and Sophie,

> If you need guidance about how this should be behaving, try with "SET
> enable_tidscan = 0;" and see what that does.
>
> David
>
> [1] https://www.postgresql.org/docs/current/transaction-iso.html#XACT-READ-COMMITTED

Thanks for pointing out “SET enable_tidsan=0”, with that, yes, (0,3) can be updated.

I was reading the official doc at https://www.postgresql.org/docs/current/transaction-iso.html:

```
Read Committed is the default isolation level in PostgreSQL. When a transaction uses this isolation level, a SELECT query (without a FOR UPDATE/SHARE clause) sees only data committed before the query began; it never sees either uncommitted data or changes committed by concurrent transactions during the query's execution. In effect, a SELECT query sees a snapshot of the database as of the instant the query begins to run. However, SELECT does see the effects of previous updates executed within its own transaction, even though they are not yet committed. Also note that two successive SELECT commands can see different data, even though they are within a single transaction, if other transactions commit changes after the first SELECT starts and before the second SELECT starts.

UPDATE, DELETE, SELECT FOR UPDATE, and SELECT FOR SHARE commands behave the same as SELECT in terms of searching for target rows: they will only find target rows that were committed as of the command start time.

```

It says that UPDATE will only find target rows that were committed as of the command start time. I think the statement implies that an “update” statement will never update a “future” tuple.

With Sophie’s example, when s2 start “update where ctid=(0,1) or (0,3)”, s1 has not committed yet, so based on the doc, (0,3) should not be updated by s2. But with enable_tidscan off, the implementation actually updated (0,3). Is it a bug of the doc or the implementation of SeqScan as SeqScan also doesn’t have recheck implemented? Or any part of my understanding is wrong?

Also, for the example I put in my previous email, in s1, I inserted a tuple, and s2 didn’t update the inserted row, which complied with the behavior the doc described. So, does PG treat CTID query condition specially? Maybe I missed that part?

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Peter Eisentraut 2025-09-15 05:40:20 Re: Only one version can be installed when using extension_control_path
Previous Message Shubham Khanna 2025-09-15 05:24:19 Re: Add support for specifying tables in pg_createsubscriber.