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 1vLnjm-00An8r-1s for pgsql-general@arkaria.postgresql.org; Wed, 19 Nov 2025 19:21:06 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vLnjl-00Emo6-0a for pgsql-general@arkaria.postgresql.org; Wed, 19 Nov 2025 19:21:05 +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 1vLnjk-00Emnv-2i for pgsql-general@lists.postgresql.org; Wed, 19 Nov 2025 19:21:05 +0000 Received: from mail-yx1-xb12c.google.com ([2607:f8b0:4864:20::b12c]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vLnji-000OFB-2e for pgsql-general@lists.postgresql.org; Wed, 19 Nov 2025 19:21:04 +0000 Received: by mail-yx1-xb12c.google.com with SMTP id 956f58d0204a3-63fc6d9fde5so29625d50.3 for ; Wed, 19 Nov 2025 11:21:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763580063; x=1764184863; darn=lists.postgresql.org; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :from:to:cc:subject:date:message-id:reply-to; bh=8LDFi0+qWs2xrbRe9xLX6nbzeWeEQLu5Z9aOGG1LdZQ=; b=UK7QoIQ5bXtEyAQzxMSjcaj+p+FFuLSnHCGBnjhT9XWMwj8G0CJzfWQW7X0IAiDDKF FinFUApjMi1k4jUdhvAguJ7I9MsKGcNacJmsR1D/bIQpxSBTcfgWLHjMids8hpNFVdq/ V/zsvKYMt2HviGq3ZQevFSfJ2xkgG1G1f67yzzUPj1JcKkzMxWIXYl0cX/WcvVeeR+xF Z2q3riB2cZuayeHj3R7eqZBmsZViTjFpsgJLmXA2r5OLBMgcH33k12iQ1SWq3XIFHUKa RNQwFkF1hWM0sq3IVCSnCwUFJzbz8CNN2S1JFDAquBk51IzMkEiCjB7eITtxmpxgSGv0 rVJA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763580063; x=1764184863; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=8LDFi0+qWs2xrbRe9xLX6nbzeWeEQLu5Z9aOGG1LdZQ=; b=Qv40ZL3BNBx2WQFKtC2o6mEY2EygE8SjOSzIftoBKjV0V8a/9Ei0RzIRIAN/Bxf7zb dPzfvJ4t2Cyi0hxhUP3nu4REM3FF/6iailBdEZRD3yXpH8s2lJ3so+7mw2zyehoEbSuW 8dHRnUdBCmiklxw7CnKjZJaOU576OS0qeUvtbSPl429jYx7kbB/nKM6cV6j/rikwres2 T6ycUZ4AVZtvpLzztXA7q2XkIulrJka3PFnL6zpgeoVeCjnh8OVxj12bEpLN1pFwejvr oTX7+ABnfN7bizX8/uMHQvRgJjijotzeKCn8j76rBpCnx/900DGtQlv3f8vrJRqLdC87 bgeQ== X-Gm-Message-State: AOJu0YxVxlN3fptjX3dupt2jLoQzo7Bl0QruB60cz+D+xrVIPYOYryKg dPwk26A1Vu9NACXhlj8NHGxwegKbof2CmTqizOU02EnyfY3Mm6ywM1hv8w4dJwU3vxpZtVObZmv WAuz3u3N9YipRitHI5Q2sEBnpYmc+/0Qfj5k5 X-Gm-Gg: ASbGnctlQO+TrFUlLaw0S3PSLuGbdOlYWzitcG3/FSU1GNlVYa9HqOuLOk3OarxDjIR x3KIl+gLIruRZldDFoIMrfuYWgAC+mJ/4m7Xn0rWJqO9i6YCiLTDTOAaoerSVi7iMimTVJUrL+0 Fza28UU/FS3xJngeibceu3JC+QdxN6FxuH6pIYI9lyp1zehNUI8ldLYgAYq6ySELtDJrKe/pTFv HPbdfruyi5/HrLT/zk2UiqrUTjoNG9SODgythw8w/jMPMXmD8nrLPzjwbsXujS/MLuxASuVyFp6 o9eG26FothI26y5HY2fi X-Google-Smtp-Source: AGHT+IFBNzfN+WsH8/G9zqKXpULEMUkaFog9x6Bm/DES7yt/WVxUun+YJE231bFcP5a5NpNcbewRxjByY3WGTKwihfI= X-Received: by 2002:a05:690e:1904:b0:63f:b3aa:d9e with SMTP id 956f58d0204a3-642f77d6501mr298174d50.23.1763580062691; Wed, 19 Nov 2025 11:21:02 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: Bernice Southey Date: Wed, 19 Nov 2025 19:20:26 +0000 X-Gm-Features: AWmQ_bnCyfRppz1SykbfbMXRbGWK3hcJdrwuFZmOcfyXpjB8_UutT6tydrkT1LE Message-ID: Subject: Re: Is this expected concurrency behaviour for EvalPlanQual and ctid? To: pgsql-general@lists.postgresql.org Content-Type: text/plain; charset="UTF-8" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk For the record, I was wrong, the patch in [1] doesn't affect lost concurrent updates with ctid. It was applied in 17.7 and after looking much harder at the thread and code, I can see it's unrelated. 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 = 2 from (select i from t) x where t.i = x.i; --------------- UPDATE 1 --s2 update t set i = 2 from (select i from t) x where t.i = x.i; ---------------- UPDATE 0 (after commit s1) The same thing happens with update t set i = 2 from (select i from t for update) x where t.i = x.i; x.i is not updated when s1 releases the lock and so s2 is still looking for x.i = 1. Based on [2], I'm guessing that because the where clause is indirect, it doesn't qualify for re-evalution after the lock is released. So it continues to use the version of the record from the start of the transaction. But I don't know nearly enough about the internals to give a proper explanation. Here's the ctid version that helped me figure this out. with x as (select ctid from t for update), y as (update t set i = 2 from x where t.ctid = x.ctid returning t.ctid) select 'x', ctid from x union select 'y', ctid from y; --s1 x (0, 1) y (0, 2) --s2 x (0, 2) Even though x is updated in s2, the updated version isn't what's used by y. I suspect the x version is only updated here because of the final select. So don't use this pattern to avoid deadlocks if this is a one shot update. with x as (select ctid from t where ... order by id for update) update t set ... where t.ctid = x.ctid; Use an immutable unique column, or retry deadlocks. Thanks, Bernice [1] https://www.postgresql.org/message-id/flat/4a6268ff-3340-453a-9bf5-c98d51a6f729%40app.fastmail.com [2] https://www.postgresql.org/docs/current/transaction-iso.html#XACT-READ-COMMITTED