Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vLoPf-00B8qQ-0Y for pgsql-general@arkaria.postgresql.org; Wed, 19 Nov 2025 20:04:23 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vLoPd-00FCxW-2S for pgsql-general@arkaria.postgresql.org; Wed, 19 Nov 2025 20:04:22 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vLoPd-00FCxH-1P for pgsql-general@lists.postgresql.org; Wed, 19 Nov 2025 20:04:21 +0000 Received: from sss.pgh.pa.us ([68.162.161.243]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vLoPb-000OdI-0c for pgsql-general@lists.postgresql.org; Wed, 19 Nov 2025 20:04:20 +0000 Received: from sss1.sss.pgh.pa.us (localhost [127.0.0.1]) by sss.pgh.pa.us (8.15.2/8.15.2) with ESMTP id 5AJK4I2X1645232; Wed, 19 Nov 2025 15:04:18 -0500 From: Tom Lane To: Bernice Southey cc: pgsql-general@lists.postgresql.org Subject: Re: Is this expected concurrency behaviour for EvalPlanQual and ctid? In-reply-to: References: Comments: In-reply-to Bernice Southey message dated "Wed, 19 Nov 2025 19:20:26 +0000" MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-ID: <1645230.1763582658.1@sss.pgh.pa.us> Content-Transfer-Encoding: quoted-printable Date: Wed, 19 Nov 2025 15:04:18 -0500 Message-ID: <1645231.1763582658@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Bernice Southey writes: > I've worked out what's happening, and it has nothing to do with ctid. > Here's a much simplified example: > create table t(i int); > insert into t(i) values (1); > --s1 > begin; > update t set i =3D 2 from (select i from t) x where t.i =3D x.i; > --------------- > UPDATE 1 > --s2 > update t set i =3D 2 from (select i from t) x where t.i =3D x.i; > ---------------- > UPDATE 0 (after commit s1) I'm reminded of Markus Winand's talk at PGConf.EU 2019 [1], where he was handing out coasters that said "Notice: All employees must wash hands after using self-joins". (Think I've still got mine, somewhere.) This is a mess because the initial table scans find the row version with i =3D 1, so the join qual succeeds, and then the UPDATE step blocks because it notices that the target row has an uncommitted update pending. After waiting for that to commit, we run the "EvalPlanQual" logic, which rechecks the updated version of the target row to see if it still satisfies the WHERE condition. But it doesn't, because as you say, we still think that x.i is 1. (EvalPlanQual is careful to re-use the same row versions from other tables that were used the first time.) > The same thing happens with > update t set i =3D 2 from (select i from t for update) x where t.i =3D = x.i; Right, the common advice if you need to make such scenarios work is to add FOR UPDATE to the non-target relations. But here, that just breaks in the opposite direction: the sub-select blocks waiting for the concurrent commit and then returns x.i =3D 2. But the UPDATE's initial scan of t only sees t.i =3D 1, so the join fails before we ever get to EvalPlanQual. I'm not sure there is any way to make self-join cases work when the concurrent update is changing the join column. > So don't use this pattern to avoid deadlocks if this is a one shot updat= e. > with x as (select ctid from t where ... order by id for update) > update t set ... where t.ctid =3D x.ctid; > Use an immutable unique column, or retry deadlocks. Yup. True SERIALIZABLE mode with retries on serialization failures is the best recipe if you need to deal with concurrent-update situations this complicated. In this particular example though, I wonder if you couldn't get rid of the self-join. regards, tom lane [1] https://www.postgresql.eu/events/pgconfeu2019/schedule/session/2644-mo= re-than-a-query-language-sql-in-the-21st-century/