BUG #17703: Recursive query early terminate when subquery result of the recursive term is NULL - Mailing list pgsql-bugs
From | PG Bug reporting form |
---|---|
Subject | BUG #17703: Recursive query early terminate when subquery result of the recursive term is NULL |
Date | |
Msg-id | 17703-c6c2e68257425996@postgresql.org Whole thread Raw |
Responses |
Re: BUG #17703: Recursive query early terminate when subquery result of the recursive term is NULL
|
List | pgsql-bugs |
The following bug has been logged on the website: Bug reference: 17703 Logged by: Jiangshan Liu Email address: jiangshan.liu@tju.edu.cn PostgreSQL version: 15.1 Operating system: Ubuntu 18.04.6 LTS Description: Hello! I actually found the problem stems from a more complex code, this is the example after minimization. -- create table and insert data DROP TABLE IF EXISTS "public"."table1"; CREATE TABLE "public"."table1" ( "id" int4 NOT NULL ); INSERT INTO "public"."table1" VALUES (1); INSERT INTO "public"."table1" VALUES (2); INSERT INTO "public"."table1" VALUES (3); -- execute recursive query with potential early terminate WITH RECURSIVE run(__AUX_CTRL_COL_REC__,__AUX_CTRL_COL_RES__,i,r,id_table1) AS ( (SELECT True, NULL::int, i_0, r_0, id_table1 FROM LATERAL (SELECT NULL::int) AS let_id_table1(id_table1), LATERAL (SELECT ( 0)::int) AS let_r_0(r_0), LATERAL (SELECT ( 1)::int) AS let_i_0(i_0)) UNION ALL SELECT result.* FROM run, LATERAL ( (SELECT if_p_0.* FROM LATERAL (SELECT ( i <= 10)::boolean) AS let_p_0(p_0), LATERAL ( SELECT True, NULL::int, i_1, r_1, id_table1_0 FROM LATERAL ( SELECT id FROM table1 WHERE id = i ) AS let_id_table1_0(id_table1_0), LATERAL (SELECT ( r + 1)::int) AS let_r_1(r_1), LATERAL (SELECT ( i + 1)::int) AS let_i_1(i_1) WHERE p_0 UNION ALL SELECT False, r, NULL::int, NULL::int, NULL::int WHERE NOT p_0 OR p_0 IS NULL )AS if_p_0 ) )AS result WHERE run.__AUX_CTRL_COL_REC__ ) SELECT run.__AUX_CTRL_COL_RES__ AS test FROM run WHERE NOT run.__AUX_CTRL_COL_REC__; -- end of example The result table of recursive query has 1 expected column named "test", but it is an unexpected empty table with 0 row. test ------ (0 row) Actually, if recursive query does not early terminate, it will return result with 1 row. The value of result will be 10. I remove the SELECT subquery in LATERAL and the result performs normally. -- execute normal recursive query WITH RECURSIVE run(__AUX_CTRL_COL_REC__,__AUX_CTRL_COL_RES__,i,r,id_table1) AS ( (SELECT True, NULL::int, i_0, r_0, id_table1 FROM LATERAL (SELECT NULL::int) AS let_id_table1(id_table1), LATERAL (SELECT ( 0)::int) AS let_r_0(r_0), LATERAL (SELECT ( 1)::int) AS let_i_0(i_0)) UNION ALL SELECT result.* FROM run, LATERAL ( (SELECT if_p_0.* FROM LATERAL (SELECT ( i <= 10)::boolean) AS let_p_0(p_0), LATERAL ( SELECT True, NULL::int, i_1, r_1, id_table1 FROM LATERAL (SELECT ( r + 1)::int) AS let_r_1(r_1), LATERAL (SELECT ( i + 1)::int) AS let_i_1(i_1) WHERE p_0 UNION ALL SELECT False, r, NULL::int, NULL::int, NULL::int WHERE NOT p_0 OR p_0 IS NULL )AS if_p_0 ) )AS result WHERE run.__AUX_CTRL_COL_REC__ ) SELECT run.__AUX_CTRL_COL_RES__ AS test FROM run WHERE NOT run.__AUX_CTRL_COL_REC__; -- end of example The result of this query is expected. test ------ 10 (1 row) This is an unexpected case where the recursive query seems to have terminated early. I wanted to figure out the details of the problem, so I replaced the final SELECT query of the recursive query from SELECT run.__AUX_CTRL_COL_RES__ AS test FROM run WHERE NOT run.__AUX_CTRL_COL_REC__; to SELECT * FROM run; and I got the intermediate results of recursive query. -- intermediate results of recursive query with potential early terminate __aux_ctrl_col_rec__ | __aux_ctrl_col_res__ | i | r | id_table1 ----------------------+----------------------+---+---+----------- t | | 1 | 0 | t | | 2 | 1 | 1 t | | 3 | 2 | 2 t | | 4 | 3 | 3 (4 rows) -- intermediate results of normal recursive query __aux_ctrl_col_rec__ | __aux_ctrl_col_res__ | i | r | id_table1 ----------------------+----------------------+----+----+----------- t | | 1 | 0 | t | | 2 | 1 | t | | 3 | 2 | t | | 4 | 3 | t | | 5 | 4 | t | | 6 | 5 | t | | 7 | 6 | t | | 8 | 7 | t | | 9 | 8 | t | | 10 | 9 | t | | 11 | 10 | f | 10 | | | (12 rows) This is all the objective phenomenon I have observed. It looks like during the recursive query, the subquery result of SELECT id FROM table1 WHERE id = i is NULL when i=4 is encountered, leading to an unexpected early termination of the recursive query. Is this a bug of recursive query, and how to avoid this early termination? By the way, I would like to elaborate on the importance of this query structure for me, even though it is a subjective motivation and may mean nothing to you. Please think of it as a nag. I want to use recursive queries in SQL to replace LOOP statements in PL/pgSQL. The methodology used draws on the paper [1]. The initial problem stems from the equivalence of the recursive query with potential early terminate above and this PL/SQL function. -- drop function DROP FUNCTION test(); -- create function CREATE OR REPLACE FUNCTION test() RETURNS int AS $$ DECLARE id_table1 int; r int := 0; BEGIN FOR i IN 1 .. 10 LOOP id_table1 := (SELECT id FROM table1 WHERE id = i); r := r+1; END LOOP; return r; END; $$ LANGUAGE PLPGSQL; -- call function SELECT test(); -- end of example The result of function call is expected. test ------ 10 (1 row) And the normal recursive query is equivalent to removing the id_table1 := (SELECT id FROM table1 WHERE id = i); statement from this PL/SQL function. It also returns an expected result table. test ------ 10 (1 row) ---- [1] Hirn, Denis, and Torsten Grust. "One with recursive is worth many GOTOs." Proceedings of the 2021 International Conference on Management of Data. 2021. Thank you and look forward to your reply! Regards, Jiangshan Liu
pgsql-bugs by date: