Confusing behaviour of UPDATE together with FOR UPDATE subquery

From: WGH <wgh(at)bushwhackers(dot)ru>
To: pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Confusing behaviour of UPDATE together with FOR UPDATE subquery
Date: 2026-08-24 20:35:57
Message-ID: 427b5a91-5233-4f2f-b7c3-a3c7790135f8@bushwhackers.ru
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hello!

I have recently encountered weird PostgreSQL behaviour which might be a bug (or not).

In short, it seems like a SELECT subquery with FOR UPDATE clause might silently skip rows if they're being updated by top-level UPDATE statement.

The following example simulates a job dependency graph. The

CREATE TYPE pg_temp.status AS ENUM('waiting', 'ready', 'done');

CREATE TABLE pg_temp.jobs(
    job_id BIGSERIAL PRIMARY KEY,
    status status NOT NULL DEFAULT 'waiting'
);

CREATE TABLE pg_temp.jobs_dependencies(
    dependent BIGSERIAL REFERENCES jobs(job_id),
    dependency BIGSERIAL REFERENCES jobs(job_id)
);

INSERT INTO jobs(job_id) VALUES
    (1),
    (20),
    (21);

INSERT INTO jobs_dependencies(dependent, dependency) VALUES
    (20, 1),
    (21, 1);

UPDATE jobs AS dependent
SET status='ready'
WHERE
    status='waiting'
    AND TRUE = ALL (
        SELECT (
            dependency.status = 'done'
        )
        FROM jobs AS dependency, jobs_dependencies
        WHERE jobs_dependencies.dependency = dependency.job_id AND
              jobs_dependencies.dependent  = dependent.job_id
        FOR UPDATE OF dependency
    );

SELECT * FROM jobs;

The result is wrong:

 job_id | status
--------+--------
      1 | ready
     20 | ready
     21 | ready
(3 rows)

The expected result, which is returned if you comment out "FOR UPDATE" clause, is this:

 job_id | status
--------+---------
     20 | waiting
     21 | waiting
      1 | ready
(3 rows)

I've asked some LLMs about this. I'll spare you of the details, but they came to the conclusion this is caused by TM_SelfModified handling in ExecLockRows.

Is this a bug that TM_SelfModified causes a SELECT ... FOR UPDATE subquery to skip rows silently, even though it was supposed to affect UPDATE itself only?

Browse pgsql-hackers by date

  From Date Subject
Next Message Mihail Nikalayeu 2026-08-24 20:50:45 Re: Apply worker can pick an invalid index for REPLICA IDENTITY FULL lookups
Previous Message Dmitry Dolgov 2026-08-24 20:03:56 Re: Randomize B-Tree page split location to avoid oscillating patterns