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 1wIETF-007tnD-04 for pgsql-general@arkaria.postgresql.org; Wed, 29 Apr 2026 23:37:33 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wIETD-0056o5-36 for pgsql-general@arkaria.postgresql.org; Wed, 29 Apr 2026 23:37:31 +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 1wIETC-0056nx-2e for pgsql-general@lists.postgresql.org; Wed, 29 Apr 2026 23:37:31 +0000 Received: from fout-b1-smtp.messagingengine.com ([202.12.124.144]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wIET7-00000003Pz8-1HoN for pgsql-general@lists.postgresql.org; Wed, 29 Apr 2026 23:37:29 +0000 Received: from phl-compute-07.internal (phl-compute-07.internal [10.202.2.47]) by mailfout.stl.internal (Postfix) with ESMTP id 76CD81D00128; Wed, 29 Apr 2026 19:37:24 -0400 (EDT) Received: from phl-frontend-03 ([10.202.2.162]) by phl-compute-07.internal (MEProxy); Wed, 29 Apr 2026 19:37:24 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aklaver.com; h= cc:content-transfer-encoding:content-type:content-type:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:subject:subject:to:to; s=fm1; t=1777505844; x=1777592244; bh=agy/r2nKTNdoLv869ojNSlb8BSq/3DDSxg7vNzhPrQk=; b= f+TDlJex3TyLITBFh2+wAapkEjcpAdN3+TmXXK0L1cToeDAcTsDoCVG3ui7c6K1V fpOlx66no4bln/k81MUsPjXjrnKD3DF+PAnh423vwfTJYZP6t9m5udpUvYX1VCr/ 6pyiWxa58f2oPcLVUV4tBhqOI7ud9dl7NUPzxTIHH3GJZHk9aTlyLdZAitDhYOi4 azXKGVLkqMylV5rpLUxIEaRZYAKyOkhVXxyrkP0o4BzeFPtc00Nxo3Xu9pS7lzl+ zNto+NS54uSylX65362RH5G8HRJMptXXKMImhgFxpOBOOYzhOXKK6vG3ISaFEBaO 6ClMoUUqFx2gmt/dCC+MBA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm2; t=1777505844; x=1777592244; bh=a gy/r2nKTNdoLv869ojNSlb8BSq/3DDSxg7vNzhPrQk=; b=TnaoaIqYAqxZTRmgh g075XetVq1TsGVwi57uuLgi943UroL0LpbySAopLDkvidHYZCfAJZ3MGXUu8qJOd DC+zSndGrL+ehv6UQAR0sB39pTrPaSOvhwHP8ZpUwGo/O57UD4T7JXgFbKSqh0O0 aYARJOhNMeIheLlDyBAik7SC3WRl4/7gF7mGRlo4jINt4dSsMlIyDifAhXgJWxkn ahtvAUjhIXnWU1DsdgX+A0Pe3Oz4wVoiL/4ujPCA2lDd78saFSDZGGSSMvHpjJL9 PUKQdFrD3/OAuVfUB84TulDK01+SclCbl76oBhq8bTmhdQvC4Uq0uxa9FbyJKybI kfc2w== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefhedrtddtgdekheejkecutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpuffrtefokffrpgfnqfghnecuuegr ihhlohhuthemuceftddtnecunecujfgurhepkfffgggfuffvfhfhjggtgfesthekredttd dvjeenucfhrhhomheptegurhhirghnucfmlhgrvhgvrhcuoegrughrihgrnhdrkhhlrghv vghrsegrkhhlrghvvghrrdgtohhmqeenucggtffrrghtthgvrhhnpeelgeevkeekkeeuie fgtdevieeluefhfedufeetkeejffekjeeujeehgeehgeektdenucffohhmrghinhepphho shhtghhrvghsqhhlrdhorhhgnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpe hmrghilhhfrhhomheprggurhhirghnrdhklhgrvhgvrhesrghklhgrvhgvrhdrtghomhdp nhgspghrtghpthhtohepvddpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtohepphhosh htghhrvghsqhhlrdhorhhgsehmshhqrhdruhhspdhrtghpthhtohepphhgshhqlhdqghgv nhgvrhgrlheslhhishhtshdrphhoshhtghhrvghsqhhlrdhorhhg X-ME-Proxy: Feedback-ID: i76984098:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 29 Apr 2026 19:37:23 -0400 (EDT) Message-ID: Date: Wed, 29 Apr 2026 16:37:22 -0700 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: Confirmation on concurrent SELECT FOR UPDATE with ON CONFLICT DO NOTHING To: Matt Magoffin , pgsql-general@lists.postgresql.org References: <087DA595-FB65-49F4-89E9-AE9F5CBF6E4C@msqr.us> Content-Language: en-US From: Adrian Klaver In-Reply-To: <087DA595-FB65-49F4-89E9-AE9F5CBF6E4C@msqr.us> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On 4/29/26 4:07 PM, Matt Magoffin wrote: > Hello, I was hoping to confirm some transaction behaviour I am seeing (in Postgres 17) in read-committed isolation mode that caught me off guard is, in fact, expected. First some setup: > > CREATE TABLE txtest (id INTEGER NOT NULL PRIMARY KEY); > INSERT INTO txtest (id) VALUES (1); > > Then in one session, I run: > > BEGIN; SELECT * FROM txtest WHERE id = 1 FOR UPDATE; > > Then, in a different session, I run: > > INSERT INTO txtest > SELECT id > FROM (VALUES > (1), > (2) > ) AS t(id) > ON CONFLICT > DO NOTHING; > > This completes immediately, with > > INSERT 0 1 > > and indeed there are 2 rows now in that session: > > SELECT * FROM txtest; > id > ---- > 1 > 2 > > This is what caught be off guard, as I had been thinking the INSERT would block until the first session’s transaction finished. Now, back in session #1, I run: > > DELETE FROM txtest WHERE ID = 1; COMMIT; > > Now in both sessions there is 1 row, with “2”, where I had been hoping to end up with both “1” and “2” after the INSERT waited for the SELECT … FOR UPDATE to complete first. > > If I change session #1’s query from SELECT … FOR UPDATE to an immediate DELETE, I get what I expected, i.e. > > BEGIN; DELETE FROM txtest WHERE id = 1; > > Then in session #1 the same INSERT … ON CONFLICT DO NOTHING statement blocks until session #1 commits, and it results in > > INSERT 0 2 > > The difference in transaction behaviour between SELECT … FOR UPDATE and DELETE I did not understand from the documentation, so would appreciate any confirmation/clarification/insight on what I’m seeing so I can better understand. From here: https://www.postgresql.org/docs/17/explicit-locking.html#LOCKING-ROWS "FOR UPDATE FOR UPDATE causes the rows retrieved by the SELECT statement to be locked as though for update. This prevents them from being locked, modified or deleted by other transactions until the current transaction ends. That is, other transactions that attempt UPDATE, DELETE, SELECT FOR UPDATE, SELECT FOR NO KEY UPDATE, SELECT FOR SHARE or SELECT FOR KEY SHARE of these rows will be blocked until the current transaction ends; conversely, ..." Nothing about an INSERT. And from here: https://www.postgresql.org/docs/current/sql-insert.html#SQL-ON-CONFLICT ON CONFLICT DO NOTHING simply avoids inserting a row as its alternative action. So in your first case the INSERT is never done and there is no lock for the INSERT in any case. > > Thank you, > Matt Magoffin > > > > -- Adrian Klaver adrian.klaver@aklaver.com