v1-0001-Don-t-assume-that-cheapest_startup_path-exists.patch

application/octet-stream

Filename: v1-0001-Don-t-assume-that-cheapest_startup_path-exists.patch
Type: application/octet-stream
Part: 0
Message: Crash in add_paths_to_append_rel

Patch

Format: format-patch
Series: patch v1-0001
Subject: Don't assume that cheapest_startup_path exists
File+
src/backend/optimizer/path/allpaths.c 10 2
src/test/regress/expected/union.out 16 0
src/test/regress/sql/union.sql 8 1
From 5c555fb67037d34f3a90be6052cb2fa26f34769a Mon Sep 17 00:00:00 2001
From: Richard Guo <guofenglinux@gmail.com>
Date: Mon, 9 Oct 2023 17:10:59 +0800
Subject: [PATCH v1] Don't assume that cheapest_startup_path exists

Normally only unparameterized paths are considered candidates for
cheapest_startup_path, because startup cost does not have too much value
for parameterized paths since they are on the inside of a nestloop.  So
if there are no unparameterized paths, the cheapest_startup_path will be
NULL.  In add_paths_to_append_rel() we should not assume that
cheapest_startup_path always exists.
---
 src/backend/optimizer/path/allpaths.c | 12 ++++++++++--
 src/test/regress/expected/union.out   | 16 ++++++++++++++++
 src/test/regress/sql/union.sql        |  9 ++++++++-
 3 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 7af001feaa..5c4e3695e5 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -1352,12 +1352,20 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 		 * When the planner is considering cheap startup plans, we'll also
 		 * collect all the cheapest_startup_paths and build an AppendPath
 		 * containing those as subpaths.
+		 *
+		 * Only unparameterized paths are considered candidates for
+		 * cheapest_startup_path, so that will be NULL if there are no
+		 * unparameterized paths.  We have to explicitly check that we have a
+		 * valid cheapest_startup_path.
 		 */
-		if (rel->consider_startup && childrel->pathlist != NIL &&
-			childrel->cheapest_startup_path->param_info == NULL)
+		if (rel->consider_startup && childrel->cheapest_startup_path != NULL)
+		{
+			/* cheapest_startup_path must be unparameterized path. */
+			Assert(childrel->cheapest_startup_path->param_info == NULL);
 			accumulate_append_subpath(childrel->cheapest_startup_path,
 									  &startup_subpaths,
 									  NULL);
+		}
 		else
 			startup_subpaths_valid = false;
 
diff --git a/src/test/regress/expected/union.out b/src/test/regress/expected/union.out
index f046e522de..a4619cb014 100644
--- a/src/test/regress/expected/union.out
+++ b/src/test/regress/expected/union.out
@@ -1453,3 +1453,19 @@ inner join tenk2 t2 on t1.tenthous = t2.tenthous
          ->  Result
 (8 rows)
 
+-- Ensure there is no problem if cheapest_startup_path is NULL
+explain (costs off)
+select * from tenk1 t1
+left join lateral
+	(select t1.tenthous from tenk2 t2 union all (values(1)))
+on true limit 1;
+                            QUERY PLAN                             
+-------------------------------------------------------------------
+ Limit
+   ->  Nested Loop Left Join
+         ->  Seq Scan on tenk1 t1
+         ->  Append
+               ->  Index Only Scan using tenk2_hundred on tenk2 t2
+               ->  Result
+(6 rows)
+
diff --git a/src/test/regress/sql/union.sql b/src/test/regress/sql/union.sql
index d65ca9f86d..213309e226 100644
--- a/src/test/regress/sql/union.sql
+++ b/src/test/regress/sql/union.sql
@@ -550,4 +550,11 @@ explain (costs off)
 select t1.unique1 from tenk1 t1
 inner join tenk2 t2 on t1.tenthous = t2.tenthous
    union all
-(values(1)) limit 1;
\ No newline at end of file
+(values(1)) limit 1;
+
+-- Ensure there is no problem if cheapest_startup_path is NULL
+explain (costs off)
+select * from tenk1 t1
+left join lateral
+	(select t1.tenthous from tenk2 t2 union all (values(1)))
+on true limit 1;
-- 
2.31.0