Re: Fix missing EvalPlanQual recheck for TID scans
David Rowley <dgrowleyml@gmail.com>
From: David Rowley <dgrowleyml@gmail.com>
To: Chao Li <li.evan.chao@gmail.com>
Cc: Sophie Alpert <pg@sophiebits.com>, pgsql-hackers@lists.postgresql.org
Date: 2025-09-15T03:10:30Z
Lists: pgsql-hackers
On Mon, 15 Sept 2025 at 13:49, Chao Li <li.evan.chao@gmail.com> wrote: > This is a wrong example and (0,3) should NOT be updated. According to the definition of “read committed”: > > 13.2. Transaction Isolation > postgresql.org > > “A query sees only data committed before the query began”. I'm afraid your understanding of the documentation isn't correct. Maybe you've observed some translated version which isn't correct, or you've made a mistake in your translation back to English. For the quoted line, I'm not quite sure where that comes from, but I don't see it as it is in the documents. The particular part of the documentation that's relevant is in [1]. The following is the relevant text: "If the first updater commits, the second updater will ignore the row if the first updater deleted it, otherwise it will attempt to apply its operation to the updated version of the row. The search condition of the command (the WHERE clause) is re-evaluated to see if the updated version of the row still matches the search condition. If so, the second updater proceeds with its operation using the updated version of the row." > And this example also proves my solution of checking visibility works. The specific part that Sophie aims to fix is the incorrect behaviour regarding the "re-evaluation" of the updated row. The correct way to fix that is to ensure the updated tuple matches the WHERE clause of the statement. Adding some visibility checks in place of that is complete nonsense. What does need to happen is validation that the ctid of the updated tuple matches the WHERE clause of the UPDATE statement. The reason anything special must happen for TID Scan and TID Range Scan and not Seq Scan is that in Seq Scan the ctid qual will be part of the scan's qual, whereas in the TID Scan variants, that's been extracted as it's assumed the scan itself will handle only getting the required rows. It's just that's currently not enforced during the recheck. If you review the ExecScan() comments, you'll see the recheck function's role is the following: * A 'recheck method' must also be provided that can check an * arbitrary tuple of the relation against any qual conditions * that are implemented internal to the access method. 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
Commits
-
Add missing EPQ recheck for TID Range Scan
- f78a69034740 14.20 landed
- f00ad440a5b6 15.15 landed
- ba0203880a8f 16.11 landed
- 0fb06e893331 17.7 landed
- 78e6047dcea5 18.0 landed
- ac06ea8f7b6c 19 (unreleased) landed
-
Add missing EPQ recheck for TID Scan
- 940f3cd5df57 13.23 landed
- 2eb7ea97d8af 14.20 landed
- 005770203889 15.15 landed
- d6539f88b7c5 16.11 landed
- 3d939a9b1c72 17.7 landed
- bae6c74ba4e4 18.0 landed
- dee21ea6d617 19 (unreleased) landed
-
Repair problems with EvalPlanQual where target table is scanned as
- 6799a6ca21e5 7.2.1 cited
-
Tid access method feature from Hiroshi Inoue, Inoue@tpf.co.jp
- 6f9ff92cc0ff 7.1.1 cited