Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1o0lo1-00025f-J0 for pgsql-bugs@arkaria.postgresql.org; Mon, 13 Jun 2022 15:16:41 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1o0lo0-0006pi-37 for pgsql-bugs@arkaria.postgresql.org; Mon, 13 Jun 2022 15:16:40 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1o0lnz-0006pZ-RB for pgsql-bugs@lists.postgresql.org; Mon, 13 Jun 2022 15:16:39 +0000 Received: from sss.pgh.pa.us ([66.207.139.130]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1o0lnx-0003NM-D2 for pgsql-bugs@lists.postgresql.org; Mon, 13 Jun 2022 15:16:38 +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 25DFGaXO3798787; Mon, 13 Jun 2022 11:16:36 -0400 From: Tom Lane To: Onur Tirtir cc: "pgsql-bugs@lists.postgresql.org" Subject: Re: DELETE deletes more than one rows when LIMIT is used in the USING clause In-reply-to: References: Comments: In-reply-to Onur Tirtir message dated "Mon, 13 Jun 2022 14:13:16 -0000" MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-ID: <3798785.1655133396.1@sss.pgh.pa.us> Content-Transfer-Encoding: quoted-printable Date: Mon, 13 Jun 2022 11:16:36 -0400 Message-ID: <3798786.1655133396@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Onur Tirtir writes: > With the following table schema, DELETE deletes more than one rows unexp= ectedly when LIMIT is specified in the USING clause of the DELETE statemen= t. I do not see a bug here. Your query is fundamentally unstable: > DELETE FROM my_table d > USING ( > SELECT int_col_3 > FROM my_table > WHERE my_table.int_col_1 =3D 1 > LIMIT 1 FOR UPDATE OF my_table > ) s > WHERE d.int_col_3 =3D s.int_col_3 AND d.int_col_1 =3D 1; The SELECT ... LIMIT query is underspecified, in that there are multiple rows it could choose to return. Worse, because it's FOR UPDATE, its results are affected by whatever the DELETE may have already done: it won't return an already-deleted row. Thus, the query's behavior changes depending on whether the planner happens to put the sub-select on the inside or the outside of the nestloop join with "d". I think the CREATE INDEX command is just incidentally causing a statistics update that switches the preferred plan shape. Perhaps there's an argument that the planner should understand that the sub-select's results are volatile and avoid executing it more than once. But we haven't felt a need for such a restriction in the past, and this example does nothing to convince me that one is needed now. It looks like a mighty ugly, brute-force hack; surely there's a simpler and better solution to whatever your actual need is. If you really do want to build a query that works this way, the approved solution for ensuring the sub-select executes just once is to put it in WITH ... AS MATERIALIZED. regards, tom lane