From d9a81022a52c3cc2ab010aa8d2b12a164a4678b7 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Wed, 3 Apr 2024 14:27:23 -0400
Subject: [PATCH v1] Rationalize the behavior of enable_indexscan and
 enable_indexonlyscan.

Previously, setting enable_indexscan=false added disable_cost to the
cost of both index scans and index-only scans. It doesn't make sense
for enable_indexscan to affect the whether index-only scans are chosen
given that we also have a GUC called enable_indexonlyscan.

Also previously, enable_indexonlyscan worked in a completely different
manner than enable_indexscan. Rather than adding disable_cost anywhere,
enable_indexonlyscan=false caused the planner to consider an index-scan
plan in each case where, without that setting, an index-only scan would
have been considered. It doesn't make sense for enable_indexonlyscan to
work in a completely different wany than enable_indexscan.

Accordingly, revise the implementation so that when
enable_indexscan=false or enable_indexscan=false, any paths of the
corresponding types that would have been generated are not generated,
and no new paths are considered that would not have been considered
otherwise. This fixes both of the problems described above.

A surprising number of regression tests depend on the old behavior
in various ways, especially on the fact that enable_indexonlyscan=false
caused additional index scan plans to be considered. Hence, adapt
the tests to the new behavior while preserving, as far as I'm able to
understand it, the intent of the tests.
---
 contrib/btree_gist/expected/interval.out      | 16 ++--
 contrib/btree_gist/sql/interval.sql           |  7 +-
 src/backend/optimizer/path/costsize.c         |  4 -
 src/backend/optimizer/path/indxpath.c         | 20 +++--
 src/test/regress/expected/btree_index.out     |  2 +
 src/test/regress/expected/create_index.out    |  9 ++-
 src/test/regress/expected/mvcc.out            |  7 +-
 src/test/regress/expected/partition_prune.out | 75 +++++++++++++++----
 src/test/regress/expected/select.out          |  2 +
 src/test/regress/expected/select_parallel.out |  2 +
 src/test/regress/expected/stats.out           | 23 ++++--
 src/test/regress/expected/tuplesort.out       |  2 +
 src/test/regress/expected/union.out           | 37 +++++----
 src/test/regress/sql/btree_index.sql          |  4 +
 src/test/regress/sql/create_index.sql         |  8 +-
 src/test/regress/sql/mvcc.sql                 |  7 +-
 src/test/regress/sql/partition_prune.sql      | 17 +++--
 src/test/regress/sql/select.sql               |  2 +
 src/test/regress/sql/select_parallel.sql      |  2 +
 src/test/regress/sql/stats.sql                | 17 +++--
 src/test/regress/sql/tuplesort.sql            |  2 +
 src/test/regress/sql/union.sql                | 18 ++---
 22 files changed, 184 insertions(+), 99 deletions(-)

diff --git a/contrib/btree_gist/expected/interval.out b/contrib/btree_gist/expected/interval.out
index 4c3d494e4a..a62b81e36f 100644
--- a/contrib/btree_gist/expected/interval.out
+++ b/contrib/btree_gist/expected/interval.out
@@ -89,9 +89,9 @@ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21
  @ 220 days 19 hours 5 mins 42 secs  | @ 21 days -2 hours -15 mins -41 secs
 (3 rows)
 
-SET enable_indexonlyscan=off;
+-- prevent index-only scan
 EXPLAIN (COSTS OFF)
-SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
+SELECT a, a <-> '199 days 21:21:23', substr(xmin::text, 0, 1) FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
                                 QUERY PLAN                                 
 ---------------------------------------------------------------------------
  Limit
@@ -99,11 +99,11 @@ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21
          Order By: (a <-> '@ 199 days 21 hours 21 mins 23 secs'::interval)
 (3 rows)
 
-SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
-                  a                  |               ?column?               
--------------------------------------+--------------------------------------
- @ 199 days 21 hours 21 mins 23 secs | @ 0
- @ 183 days 6 hours 52 mins 48 secs  | @ 16 days 14 hours 28 mins 35 secs
- @ 220 days 19 hours 5 mins 42 secs  | @ 21 days -2 hours -15 mins -41 secs
+SELECT a, a <-> '199 days 21:21:23', substr(xmin::text, 0, 1) FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
+                  a                  |               ?column?               | substr 
+-------------------------------------+--------------------------------------+--------
+ @ 199 days 21 hours 21 mins 23 secs | @ 0                                  | 
+ @ 183 days 6 hours 52 mins 48 secs  | @ 16 days 14 hours 28 mins 35 secs   | 
+ @ 220 days 19 hours 5 mins 42 secs  | @ 21 days -2 hours -15 mins -41 secs | 
 (3 rows)
 
diff --git a/contrib/btree_gist/sql/interval.sql b/contrib/btree_gist/sql/interval.sql
index 346d6adcb5..493e751959 100644
--- a/contrib/btree_gist/sql/interval.sql
+++ b/contrib/btree_gist/sql/interval.sql
@@ -36,8 +36,7 @@ EXPLAIN (COSTS OFF)
 SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
 SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
 
-SET enable_indexonlyscan=off;
-
+-- prevent index-only scan
 EXPLAIN (COSTS OFF)
-SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
-SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
+SELECT a, a <-> '199 days 21:21:23', substr(xmin::text, 0, 1) FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
+SELECT a, a <-> '199 days 21:21:23', substr(xmin::text, 0, 1) FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index ee23ed7835..b71f720b06 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -603,10 +603,6 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
 											  path->indexclauses);
 	}
 
-	if (!enable_indexscan)
-		startup_cost += disable_cost;
-	/* we don't need to check enable_indexonlyscan; indxpath.c does that */
-
 	/*
 	 * Call index-access-method-specific code to estimate the processing cost
 	 * for scanning the index, as well as the selectivity of the index (ie,
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 32c6a8bbdc..5ec5b69c51 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -765,7 +765,13 @@ get_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		IndexPath  *ipath = (IndexPath *) lfirst(lc);
 
 		if (index->amhasgettuple)
-			add_path(rel, (Path *) ipath);
+		{
+			if (ipath->path.pathtype == T_IndexScan && enable_indexscan)
+				add_path(rel, (Path *) ipath);
+			else if (ipath->path.pathtype == T_IndexOnlyScan &&
+				enable_indexonlyscan)
+				add_path(rel, (Path *) ipath);
+		}
 
 		if (index->amhasgetbitmap &&
 			(ipath->path.pathkeys == NIL ||
@@ -862,6 +868,8 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		case ST_INDEXSCAN:
 			if (!index->amhasgettuple)
 				return NIL;
+			if (!enable_indexscan && !enable_indexonlyscan)
+				return NIL;
 			break;
 		case ST_BITMAPSCAN:
 			if (!index->amhasgetbitmap)
@@ -1031,7 +1039,8 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 */
 		if (index->amcanparallel &&
 			rel->consider_parallel && outer_relids == NULL &&
-			scantype != ST_BITMAPSCAN)
+			scantype != ST_BITMAPSCAN &&
+			(index_only_scan ? enable_indexonlyscan : enable_indexscan))
 		{
 			ipath = create_index_path(root, index,
 									  index_clauses,
@@ -1081,7 +1090,8 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 			/* If appropriate, consider parallel index scan */
 			if (index->amcanparallel &&
 				rel->consider_parallel && outer_relids == NULL &&
-				scantype != ST_BITMAPSCAN)
+				scantype != ST_BITMAPSCAN &&
+				(index_only_scan ? enable_indexonlyscan : enable_indexscan))
 			{
 				ipath = create_index_path(root, index,
 										  index_clauses,
@@ -1789,10 +1799,6 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
 	ListCell   *lc;
 	int			i;
 
-	/* Index-only scans must be enabled */
-	if (!enable_indexonlyscan)
-		return false;
-
 	/*
 	 * Check that all needed attributes of the relation are available from the
 	 * index.
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 8311a03c3d..115f7c46f8 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -238,6 +238,7 @@ select proname from pg_proc where proname ilike 'ri%foo' order by 1;
 (2 rows)
 
 set enable_indexscan to false;
+set enable_indexonlyscan to false;
 set enable_bitmapscan to true;
 explain (costs off)
 select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
@@ -278,6 +279,7 @@ select proname from pg_proc where proname ilike '00%foo' order by 1;
 ---------
 (0 rows)
 
+reset enable_indexonlyscan;
 explain (costs off)
 select proname from pg_proc where proname ilike 'ri%foo' order by 1;
                            QUERY PLAN                            
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 70ab47a92f..551a8b0331 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -618,6 +618,7 @@ SELECT point(x,x), (SELECT f1 FROM gpolygon_tbl ORDER BY f1 <-> point(x,x) LIMIT
 -- Now check the results from bitmap indexscan
 SET enable_seqscan = OFF;
 SET enable_indexscan = OFF;
+SET enable_indexonlyscan = OFF;
 SET enable_bitmapscan = ON;
 EXPLAIN (COSTS OFF)
 SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
@@ -643,6 +644,7 @@ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0
 
 RESET enable_seqscan;
 RESET enable_indexscan;
+RESET enable_indexonlyscan;
 RESET enable_bitmapscan;
 --
 -- GIN over int[] and text[]
@@ -1952,10 +1954,10 @@ ORDER BY thousand;
         1 |     1001
 (2 rows)
 
-SET enable_indexonlyscan = OFF;
 explain (costs off)
 SELECT thousand, tenthous FROM tenk1
 WHERE thousand < 2 AND tenthous IN (1001,3000)
+AND two IS NOT DISTINCT FROM two
 ORDER BY thousand;
                                       QUERY PLAN                                      
 --------------------------------------------------------------------------------------
@@ -1963,10 +1965,12 @@ ORDER BY thousand;
    Sort Key: thousand
    ->  Index Scan using tenk1_thous_tenthous on tenk1
          Index Cond: ((thousand < 2) AND (tenthous = ANY ('{1001,3000}'::integer[])))
-(4 rows)
+         Filter: (NOT (two IS DISTINCT FROM two))
+(5 rows)
 
 SELECT thousand, tenthous FROM tenk1
 WHERE thousand < 2 AND tenthous IN (1001,3000)
+AND two IS NOT DISTINCT FROM two
 ORDER BY thousand;
  thousand | tenthous 
 ----------+----------
@@ -1974,7 +1978,6 @@ ORDER BY thousand;
         1 |     1001
 (2 rows)
 
-RESET enable_indexonlyscan;
 --
 -- Check elimination of constant-NULL subexpressions
 --
diff --git a/src/test/regress/expected/mvcc.out b/src/test/regress/expected/mvcc.out
index 225c39f64f..38802f89f3 100644
--- a/src/test/regress/expected/mvcc.out
+++ b/src/test/regress/expected/mvcc.out
@@ -8,7 +8,6 @@
 -- this.
 BEGIN;
 SET LOCAL enable_seqscan = false;
-SET LOCAL enable_indexonlyscan = false;
 SET LOCAL enable_bitmapscan = false;
 -- Can't easily use a unique index, since dead tuples can be found
 -- independent of the kill_prior_tuples optimization.
@@ -22,8 +21,10 @@ BEGIN
     -- iterate often enough to see index growth even on larger-than-default page sizes
     FOR i IN 1..100 LOOP
         BEGIN
-	    -- perform index scan over all the inserted keys to get them to be seen as dead
-            IF EXISTS(SELECT * FROM clean_aborted_self WHERE key > 0 AND key < 100) THEN
+	    -- perform index scan over all the inserted keys to get them to be seen
+	    -- as dead; we mention an unindexed column here so that the planner
+	    -- cannot believe that an index-only scan is possible
+            IF EXISTS(SELECT * FROM clean_aborted_self WHERE key > 0 AND key < 100 AND data IS NOT DISTINCT FROM data) THEN
 	        RAISE data_corrupted USING MESSAGE = 'these rows should not exist';
             END IF;
             INSERT INTO clean_aborted_self SELECT g.i, 'rolling back in a sec' FROM generate_series(1, 100) g(i);
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index 46b78ba3c4..eeaba2ebd3 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -2480,7 +2480,7 @@ where c.relname like 'ab\_%' order by c.relname;
  ab_a3_b3_a_idx |        1 |         0 | t          |                  |                  
 (21 rows)
 
-select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(0, 0, 1)');
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(0, 0, 1) and ab.b in (1, 2, 3)');
                                         explain_parallel_append                                         
 --------------------------------------------------------------------------------------------------------
  Finalize Aggregate (actual rows=N loops=N)
@@ -2494,27 +2494,36 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on
                      ->  Append (actual rows=N loops=N)
                            ->  Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (actual rows=N loops=N)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (actual rows=N loops=N)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (actual rows=N loops=N)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (never executed)
                                  Index Cond: (a = a.a)
-(27 rows)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
+(36 rows)
 
 -- Ensure the same partitions are pruned when we make the nested loop
 -- parameter an Expr rather than a plain Param.
-select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a + 0 where a.a in(0, 0, 1)');
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a + 0 where a.a in(0, 0, 1) and ab.b in (1, 2, 3)');
                                         explain_parallel_append                                         
 --------------------------------------------------------------------------------------------------------
  Finalize Aggregate (actual rows=N loops=N)
@@ -2528,26 +2537,35 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on
                      ->  Append (actual rows=N loops=N)
                            ->  Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (actual rows=N loops=N)
                                  Index Cond: (a = (a.a + 0))
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (actual rows=N loops=N)
                                  Index Cond: (a = (a.a + 0))
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (actual rows=N loops=N)
                                  Index Cond: (a = (a.a + 0))
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed)
                                  Index Cond: (a = (a.a + 0))
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed)
                                  Index Cond: (a = (a.a + 0))
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed)
                                  Index Cond: (a = (a.a + 0))
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (never executed)
                                  Index Cond: (a = (a.a + 0))
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (never executed)
                                  Index Cond: (a = (a.a + 0))
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (never executed)
                                  Index Cond: (a = (a.a + 0))
-(27 rows)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
+(36 rows)
 
 insert into lprt_a values(3),(3);
-select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 3)');
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 3) and ab.b in (1, 2, 3)');
                                         explain_parallel_append                                         
 --------------------------------------------------------------------------------------------------------
  Finalize Aggregate (actual rows=N loops=N)
@@ -2561,25 +2579,34 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on
                      ->  Append (actual rows=N loops=N)
                            ->  Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (actual rows=N loops=N)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (actual rows=N loops=N)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (actual rows=N loops=N)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (actual rows=N loops=N)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (actual rows=N loops=N)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (actual rows=N loops=N)
                                  Index Cond: (a = a.a)
-(27 rows)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
+(36 rows)
 
-select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0)');
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0) and ab.b in (1, 2, 3)');
                                         explain_parallel_append                                         
 --------------------------------------------------------------------------------------------------------
  Finalize Aggregate (actual rows=N loops=N)
@@ -2594,26 +2621,35 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on
                      ->  Append (actual rows=N loops=N)
                            ->  Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (actual rows=N loops=N)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (actual rows=N loops=N)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (actual rows=N loops=N)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (never executed)
                                  Index Cond: (a = a.a)
-(28 rows)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
+(37 rows)
 
 delete from lprt_a where a = 1;
-select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0)');
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0) and ab.b in (1, 2, 3)');
                                      explain_parallel_append                                     
 -------------------------------------------------------------------------------------------------
  Finalize Aggregate (actual rows=N loops=N)
@@ -2628,23 +2664,32 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on
                      ->  Append (actual rows=N loops=N)
                            ->  Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (never executed)
                                  Index Cond: (a = a.a)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
                            ->  Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (never executed)
                                  Index Cond: (a = a.a)
-(28 rows)
+                                 Filter: (b = ANY ('{1,2,3}'::integer[]))
+(37 rows)
 
 reset enable_hashjoin;
 reset enable_mergejoin;
@@ -2978,7 +3023,7 @@ drop table ab, lprt_a;
 create table tbl1(col1 int);
 insert into tbl1 values (501), (505);
 -- Basic table
-create table tprt (col1 int) partition by range (col1);
+create table tprt (col1 int, col2 int) partition by range (col1);
 create table tprt_1 partition of tprt for values from (1) to (501);
 create table tprt_2 partition of tprt for values from (501) to (1001);
 create table tprt_3 partition of tprt for values from (1001) to (2001);
@@ -2991,7 +3036,8 @@ create index tprt3_idx on tprt_3 (col1);
 create index tprt4_idx on tprt_4 (col1);
 create index tprt5_idx on tprt_5 (col1);
 create index tprt6_idx on tprt_6 (col1);
-insert into tprt values (10), (20), (501), (502), (505), (1001), (4500);
+insert into tprt values (10, 0), (20, 0), (501, 0), (502, 0), (505, 0),
+	(1001, 0), (4500, 0);
 set enable_hashjoin = off;
 set enable_mergejoin = off;
 explain (analyze, costs off, summary off, timing off)
@@ -3584,7 +3630,7 @@ explain (analyze, verbose, costs off, summary off, timing off) execute mt_q2 (35
 
 deallocate mt_q2;
 -- ensure initplan params properly prune partitions
-explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(b) from ma_test_p2) order by b;
+explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(b) from ma_test_p2 where a >= 0) order by b;
                                           QUERY PLAN                                           
 -----------------------------------------------------------------------------------------------
  Merge Append (actual rows=20 loops=1)
@@ -3595,13 +3641,14 @@ explain (analyze, costs off, summary off, timing off) select * from ma_test wher
              ->  Limit (actual rows=1 loops=1)
                    ->  Index Scan using ma_test_p2_b_idx on ma_test_p2 (actual rows=1 loops=1)
                          Index Cond: (b IS NOT NULL)
+                         Filter: (a >= 0)
    ->  Index Scan using ma_test_p1_b_idx on ma_test_p1 ma_test_1 (never executed)
          Filter: (a >= (InitPlan 2).col1)
    ->  Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_2 (actual rows=10 loops=1)
          Filter: (a >= (InitPlan 2).col1)
    ->  Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_3 (actual rows=10 loops=1)
          Filter: (a >= (InitPlan 2).col1)
-(14 rows)
+(15 rows)
 
 reset enable_seqscan;
 reset enable_sort;
diff --git a/src/test/regress/expected/select.out b/src/test/regress/expected/select.out
index 33a6dceb0e..5be4ac94a2 100644
--- a/src/test/regress/expected/select.out
+++ b/src/test/regress/expected/select.out
@@ -844,6 +844,7 @@ select unique2 from onek2 where unique2 = 11 and stringu1 < 'C';
 
 -- partial index implies clause, but bitmap scan must recheck predicate anyway
 SET enable_indexscan TO off;
+SET enable_indexonlyscan TO off;
 explain (costs off)
 select unique2 from onek2 where unique2 = 11 and stringu1 < 'B';
                          QUERY PLAN                          
@@ -861,6 +862,7 @@ select unique2 from onek2 where unique2 = 11 and stringu1 < 'B';
 (1 row)
 
 RESET enable_indexscan;
+RESET enable_indexonlyscan;
 -- check multi-index cases too
 explain (costs off)
 select unique1, unique2 from onek2
diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out
index 4ffc5b4c56..8a5235358b 100644
--- a/src/test/regress/expected/select_parallel.out
+++ b/src/test/regress/expected/select_parallel.out
@@ -497,6 +497,7 @@ reset enable_indexscan;
 -- test parallel bitmap heap scan.
 set enable_seqscan to off;
 set enable_indexscan to off;
+set enable_indexonlyscan to off;
 set enable_hashjoin to off;
 set enable_mergejoin to off;
 set enable_material to off;
@@ -597,6 +598,7 @@ select * from explain_parallel_sort_stats();
 (14 rows)
 
 reset enable_indexscan;
+reset enable_indexonlyscan;
 reset enable_hashjoin;
 reset enable_mergejoin;
 reset enable_material;
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 6e08898b18..f864878869 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -92,8 +92,9 @@ SELECT count(*) FROM tenk2;
 
 -- do an indexscan
 -- make sure it is not a bitmap scan, which might skip fetching heap tuples
+-- mention an unrelated column to forestall an index-only scan
 SET enable_bitmapscan TO off;
-SELECT count(*) FROM tenk2 WHERE unique1 = 1;
+SELECT count(*) FROM tenk2 WHERE unique1 = 1 and two is not distinct from two;
  count 
 -------
      1
@@ -593,6 +594,7 @@ SELECT seq_scan, idx_scan FROM pg_stat_all_tables WHERE relid = 'test_last_scan'
 (1 row)
 
 -- ensure we start out with exactly one index and sequential scan
+-- as above, mention another column to forestall an index-only scan
 BEGIN;
 SET LOCAL enable_seqscan TO on;
 SET LOCAL enable_indexscan TO on;
@@ -612,15 +614,18 @@ SELECT count(*) FROM test_last_scan WHERE noidx_col = 1;
 (1 row)
 
 SET LOCAL enable_seqscan TO off;
-EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
+EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1
+   AND noidx_col IS NOT DISTINCT FROM noidx_col;
                           QUERY PLAN                          
 --------------------------------------------------------------
  Aggregate
    ->  Index Scan using test_last_scan_pkey on test_last_scan
          Index Cond: (idx_col = 1)
-(3 rows)
+         Filter: (NOT (noidx_col IS DISTINCT FROM noidx_col))
+(4 rows)
 
-SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
+SELECT count(*) FROM test_last_scan WHERE idx_col = 1
+   AND noidx_col IS NOT DISTINCT FROM noidx_col;
  count 
 -------
      1
@@ -686,19 +691,23 @@ SELECT pg_sleep(0.1);
 (1 row)
 
 -- cause one index scan
+-- as above, mention another column to forestall an index-only scan
 BEGIN;
 SET LOCAL enable_seqscan TO off;
 SET LOCAL enable_indexscan TO on;
 SET LOCAL enable_bitmapscan TO off;
-EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
+EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1
+   AND noidx_col IS NOT DISTINCT FROM noidx_col;
                           QUERY PLAN                          
 --------------------------------------------------------------
  Aggregate
    ->  Index Scan using test_last_scan_pkey on test_last_scan
          Index Cond: (idx_col = 1)
-(3 rows)
+         Filter: (NOT (noidx_col IS DISTINCT FROM noidx_col))
+(4 rows)
 
-SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
+SELECT count(*) FROM test_last_scan WHERE idx_col = 1
+   AND noidx_col IS NOT DISTINCT FROM noidx_col;
  count 
 -------
      1
diff --git a/src/test/regress/expected/tuplesort.out b/src/test/regress/expected/tuplesort.out
index 0e8b5bf4a3..eeea0f7926 100644
--- a/src/test/regress/expected/tuplesort.out
+++ b/src/test/regress/expected/tuplesort.out
@@ -349,6 +349,7 @@ ROLLBACK;
 -- in-memory
 BEGIN;
 SET LOCAL enable_indexscan = false;
+SET LOCAL enable_indexonlyscan = false;
 -- unfortunately can't show analyze output confirming sort method,
 -- the memory used output wouldn't be stable
 EXPLAIN (COSTS OFF) DECLARE c SCROLL CURSOR FOR SELECT noabort_decreasing FROM abbrev_abort_uuids ORDER BY noabort_decreasing;
@@ -445,6 +446,7 @@ COMMIT;
 -- disk based
 BEGIN;
 SET LOCAL enable_indexscan = false;
+SET LOCAL enable_indexonlyscan = false;
 SET LOCAL work_mem = '100kB';
 -- unfortunately can't show analyze output confirming sort method,
 -- the memory used output wouldn't be stable
diff --git a/src/test/regress/expected/union.out b/src/test/regress/expected/union.out
index 26b718e903..05ff08582e 100644
--- a/src/test/regress/expected/union.out
+++ b/src/test/regress/expected/union.out
@@ -1129,12 +1129,12 @@ INSERT INTO t1c VALUES ('v', 'w'), ('c', 'd'), ('m', 'n'), ('e', 'f');
 INSERT INTO t2c VALUES ('vw'), ('cd'), ('mn'), ('ef');
 CREATE INDEX t1c_ab_idx on t1c ((a || b));
 set enable_seqscan = on;
-set enable_indexonlyscan = off;
+-- force xmin to be fetched to avoid an index-only scan
 explain (costs off)
   SELECT * FROM
-  (SELECT a || b AS ab FROM t1
+  (SELECT a || b AS ab, substr(xmin::text, 1, 0) AS c FROM t1
    UNION ALL
-   SELECT ab FROM t2) t
+   SELECT ab, substr(xmin::text, 1, 0) FROM t2) t
   ORDER BY 1 LIMIT 8;
                      QUERY PLAN                      
 -----------------------------------------------------
@@ -1148,20 +1148,20 @@ explain (costs off)
 (7 rows)
 
   SELECT * FROM
-  (SELECT a || b AS ab FROM t1
+  (SELECT a || b AS ab, substr(xmin::text, 1, 0) AS c FROM t1
    UNION ALL
-   SELECT ab FROM t2) t
+   SELECT ab, substr(xmin::text, 1, 0) FROM t2) t
   ORDER BY 1 LIMIT 8;
- ab 
-----
- ab
- ab
- cd
- dc
- ef
- fe
- mn
- nm
+ ab | c 
+----+---
+ ab | 
+ ab | 
+ cd | 
+ dc | 
+ ef | 
+ fe | 
+ mn | 
+ nm | 
 (8 rows)
 
 reset enable_seqscan;
@@ -1173,10 +1173,10 @@ create table events (event_id int primary key);
 create table other_events (event_id int primary key);
 create table events_child () inherits (events);
 explain (costs off)
-select event_id
- from (select event_id from events
+select event_id, xmin
+ from (select event_id, xmin from events
        union all
-       select event_id from other_events) ss
+       select event_id, xmin from other_events) ss
  order by event_id;
                         QUERY PLAN                        
 ----------------------------------------------------------
@@ -1190,7 +1190,6 @@ select event_id
 (7 rows)
 
 drop table events_child, events, other_events;
-reset enable_indexonlyscan;
 -- Test constraint exclusion of UNION ALL subqueries
 explain (costs off)
  SELECT * FROM
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index ef84354234..0dfb3e6312 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -153,6 +153,7 @@ explain (costs off)
 select proname from pg_proc where proname ilike 'ri%foo' order by 1;
 
 set enable_indexscan to false;
+set enable_indexonlyscan to false;
 set enable_bitmapscan to true;
 explain (costs off)
 select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
@@ -160,6 +161,9 @@ select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
 explain (costs off)
 select proname from pg_proc where proname ilike '00%foo' order by 1;
 select proname from pg_proc where proname ilike '00%foo' order by 1;
+
+reset enable_indexonlyscan;
+
 explain (costs off)
 select proname from pg_proc where proname ilike 'ri%foo' order by 1;
 
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index d49ce9f300..490e402f4d 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -246,6 +246,7 @@ SELECT point(x,x), (SELECT f1 FROM gpolygon_tbl ORDER BY f1 <-> point(x,x) LIMIT
 -- Now check the results from bitmap indexscan
 SET enable_seqscan = OFF;
 SET enable_indexscan = OFF;
+SET enable_indexonlyscan = OFF;
 SET enable_bitmapscan = ON;
 
 EXPLAIN (COSTS OFF)
@@ -254,6 +255,7 @@ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0
 
 RESET enable_seqscan;
 RESET enable_indexscan;
+RESET enable_indexonlyscan;
 RESET enable_bitmapscan;
 
 --
@@ -774,19 +776,17 @@ SELECT thousand, tenthous FROM tenk1
 WHERE thousand < 2 AND tenthous IN (1001,3000)
 ORDER BY thousand;
 
-SET enable_indexonlyscan = OFF;
-
 explain (costs off)
 SELECT thousand, tenthous FROM tenk1
 WHERE thousand < 2 AND tenthous IN (1001,3000)
+AND two IS NOT DISTINCT FROM two
 ORDER BY thousand;
 
 SELECT thousand, tenthous FROM tenk1
 WHERE thousand < 2 AND tenthous IN (1001,3000)
+AND two IS NOT DISTINCT FROM two
 ORDER BY thousand;
 
-RESET enable_indexonlyscan;
-
 --
 -- Check elimination of constant-NULL subexpressions
 --
diff --git a/src/test/regress/sql/mvcc.sql b/src/test/regress/sql/mvcc.sql
index 0a3ebc88f3..df4b197966 100644
--- a/src/test/regress/sql/mvcc.sql
+++ b/src/test/regress/sql/mvcc.sql
@@ -9,7 +9,6 @@
 BEGIN;
 
 SET LOCAL enable_seqscan = false;
-SET LOCAL enable_indexonlyscan = false;
 SET LOCAL enable_bitmapscan = false;
 
 -- Can't easily use a unique index, since dead tuples can be found
@@ -26,8 +25,10 @@ BEGIN
     -- iterate often enough to see index growth even on larger-than-default page sizes
     FOR i IN 1..100 LOOP
         BEGIN
-	    -- perform index scan over all the inserted keys to get them to be seen as dead
-            IF EXISTS(SELECT * FROM clean_aborted_self WHERE key > 0 AND key < 100) THEN
+	    -- perform index scan over all the inserted keys to get them to be seen
+	    -- as dead; we mention an unindexed column here so that the planner
+	    -- cannot believe that an index-only scan is possible
+            IF EXISTS(SELECT * FROM clean_aborted_self WHERE key > 0 AND key < 100 AND data IS NOT DISTINCT FROM data) THEN
 	        RAISE data_corrupted USING MESSAGE = 'these rows should not exist';
             END IF;
             INSERT INTO clean_aborted_self SELECT g.i, 'rolling back in a sec' FROM generate_series(1, 100) g(i);
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index dc71693861..25f5142b84 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -614,20 +614,20 @@ left join pg_stat_all_tables s on c.oid = s.relid
 left join pg_index i on c.oid = i.indexrelid
 where c.relname like 'ab\_%' order by c.relname;
 
-select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(0, 0, 1)');
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(0, 0, 1) and ab.b in (1, 2, 3)');
 
 -- Ensure the same partitions are pruned when we make the nested loop
 -- parameter an Expr rather than a plain Param.
-select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a + 0 where a.a in(0, 0, 1)');
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a + 0 where a.a in(0, 0, 1) and ab.b in (1, 2, 3)');
 
 insert into lprt_a values(3),(3);
 
-select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 3)');
-select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0)');
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 3) and ab.b in (1, 2, 3)');
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0) and ab.b in (1, 2, 3)');
 
 delete from lprt_a where a = 1;
 
-select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0)');
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0) and ab.b in (1, 2, 3)');
 
 reset enable_hashjoin;
 reset enable_mergejoin;
@@ -708,7 +708,7 @@ create table tbl1(col1 int);
 insert into tbl1 values (501), (505);
 
 -- Basic table
-create table tprt (col1 int) partition by range (col1);
+create table tprt (col1 int, col2 int) partition by range (col1);
 create table tprt_1 partition of tprt for values from (1) to (501);
 create table tprt_2 partition of tprt for values from (501) to (1001);
 create table tprt_3 partition of tprt for values from (1001) to (2001);
@@ -723,7 +723,8 @@ create index tprt4_idx on tprt_4 (col1);
 create index tprt5_idx on tprt_5 (col1);
 create index tprt6_idx on tprt_6 (col1);
 
-insert into tprt values (10), (20), (501), (502), (505), (1001), (4500);
+insert into tprt values (10, 0), (20, 0), (501, 0), (502, 0), (505, 0),
+	(1001, 0), (4500, 0);
 
 set enable_hashjoin = off;
 set enable_mergejoin = off;
@@ -963,7 +964,7 @@ explain (analyze, verbose, costs off, summary off, timing off) execute mt_q2 (35
 deallocate mt_q2;
 
 -- ensure initplan params properly prune partitions
-explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(b) from ma_test_p2) order by b;
+explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(b) from ma_test_p2 where a >= 0) order by b;
 
 reset enable_seqscan;
 reset enable_sort;
diff --git a/src/test/regress/sql/select.sql b/src/test/regress/sql/select.sql
index 019f1e7673..e5f41b649a 100644
--- a/src/test/regress/sql/select.sql
+++ b/src/test/regress/sql/select.sql
@@ -218,10 +218,12 @@ select unique2 from onek2 where unique2 = 11 and stringu1 < 'C';
 select unique2 from onek2 where unique2 = 11 and stringu1 < 'C';
 -- partial index implies clause, but bitmap scan must recheck predicate anyway
 SET enable_indexscan TO off;
+SET enable_indexonlyscan TO off;
 explain (costs off)
 select unique2 from onek2 where unique2 = 11 and stringu1 < 'B';
 select unique2 from onek2 where unique2 = 11 and stringu1 < 'B';
 RESET enable_indexscan;
+RESET enable_indexonlyscan;
 -- check multi-index cases too
 explain (costs off)
 select unique1, unique2 from onek2
diff --git a/src/test/regress/sql/select_parallel.sql b/src/test/regress/sql/select_parallel.sql
index c43a5b2119..ad5c734279 100644
--- a/src/test/regress/sql/select_parallel.sql
+++ b/src/test/regress/sql/select_parallel.sql
@@ -193,6 +193,7 @@ reset enable_indexscan;
 -- test parallel bitmap heap scan.
 set enable_seqscan to off;
 set enable_indexscan to off;
+set enable_indexonlyscan to off;
 set enable_hashjoin to off;
 set enable_mergejoin to off;
 set enable_material to off;
@@ -240,6 +241,7 @@ $$;
 select * from explain_parallel_sort_stats();
 
 reset enable_indexscan;
+reset enable_indexonlyscan;
 reset enable_hashjoin;
 reset enable_mergejoin;
 reset enable_material;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index d8ac0d06f4..c5a8b18db5 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -94,8 +94,9 @@ ROLLBACK;
 SELECT count(*) FROM tenk2;
 -- do an indexscan
 -- make sure it is not a bitmap scan, which might skip fetching heap tuples
+-- mention an unrelated column to forestall an index-only scan
 SET enable_bitmapscan TO off;
-SELECT count(*) FROM tenk2 WHERE unique1 = 1;
+SELECT count(*) FROM tenk2 WHERE unique1 = 1 and two is not distinct from two;
 RESET enable_bitmapscan;
 
 -- ensure pending stats are flushed
@@ -310,6 +311,7 @@ SELECT pg_stat_reset_single_table_counters('test_last_scan'::regclass);
 SELECT seq_scan, idx_scan FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
 
 -- ensure we start out with exactly one index and sequential scan
+-- as above, mention another column to forestall an index-only scan
 BEGIN;
 SET LOCAL enable_seqscan TO on;
 SET LOCAL enable_indexscan TO on;
@@ -317,8 +319,10 @@ SET LOCAL enable_bitmapscan TO off;
 EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE noidx_col = 1;
 SELECT count(*) FROM test_last_scan WHERE noidx_col = 1;
 SET LOCAL enable_seqscan TO off;
-EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
-SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
+EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1
+   AND noidx_col IS NOT DISTINCT FROM noidx_col;
+SELECT count(*) FROM test_last_scan WHERE idx_col = 1
+   AND noidx_col IS NOT DISTINCT FROM noidx_col;
 SELECT pg_stat_force_next_flush();
 COMMIT;
 
@@ -346,12 +350,15 @@ FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass \gset
 SELECT pg_sleep(0.1);
 
 -- cause one index scan
+-- as above, mention another column to forestall an index-only scan
 BEGIN;
 SET LOCAL enable_seqscan TO off;
 SET LOCAL enable_indexscan TO on;
 SET LOCAL enable_bitmapscan TO off;
-EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
-SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
+EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1
+   AND noidx_col IS NOT DISTINCT FROM noidx_col;
+SELECT count(*) FROM test_last_scan WHERE idx_col = 1
+   AND noidx_col IS NOT DISTINCT FROM noidx_col;
 SELECT pg_stat_force_next_flush();
 COMMIT;
 -- check that just index scan stats were incremented
diff --git a/src/test/regress/sql/tuplesort.sql b/src/test/regress/sql/tuplesort.sql
index 658fe98dc5..ae1a5b9d0a 100644
--- a/src/test/regress/sql/tuplesort.sql
+++ b/src/test/regress/sql/tuplesort.sql
@@ -153,6 +153,7 @@ ROLLBACK;
 -- in-memory
 BEGIN;
 SET LOCAL enable_indexscan = false;
+SET LOCAL enable_indexonlyscan = false;
 -- unfortunately can't show analyze output confirming sort method,
 -- the memory used output wouldn't be stable
 EXPLAIN (COSTS OFF) DECLARE c SCROLL CURSOR FOR SELECT noabort_decreasing FROM abbrev_abort_uuids ORDER BY noabort_decreasing;
@@ -183,6 +184,7 @@ COMMIT;
 -- disk based
 BEGIN;
 SET LOCAL enable_indexscan = false;
+SET LOCAL enable_indexonlyscan = false;
 SET LOCAL work_mem = '100kB';
 -- unfortunately can't show analyze output confirming sort method,
 -- the memory used output wouldn't be stable
diff --git a/src/test/regress/sql/union.sql b/src/test/regress/sql/union.sql
index 8afc580c63..30ab3bf941 100644
--- a/src/test/regress/sql/union.sql
+++ b/src/test/regress/sql/union.sql
@@ -401,19 +401,19 @@ INSERT INTO t2c VALUES ('vw'), ('cd'), ('mn'), ('ef');
 CREATE INDEX t1c_ab_idx on t1c ((a || b));
 
 set enable_seqscan = on;
-set enable_indexonlyscan = off;
 
+-- force xmin to be fetched to avoid an index-only scan
 explain (costs off)
   SELECT * FROM
-  (SELECT a || b AS ab FROM t1
+  (SELECT a || b AS ab, substr(xmin::text, 1, 0) AS c FROM t1
    UNION ALL
-   SELECT ab FROM t2) t
+   SELECT ab, substr(xmin::text, 1, 0) FROM t2) t
   ORDER BY 1 LIMIT 8;
 
   SELECT * FROM
-  (SELECT a || b AS ab FROM t1
+  (SELECT a || b AS ab, substr(xmin::text, 1, 0) AS c FROM t1
    UNION ALL
-   SELECT ab FROM t2) t
+   SELECT ab, substr(xmin::text, 1, 0) FROM t2) t
   ORDER BY 1 LIMIT 8;
 
 reset enable_seqscan;
@@ -428,16 +428,14 @@ create table other_events (event_id int primary key);
 create table events_child () inherits (events);
 
 explain (costs off)
-select event_id
- from (select event_id from events
+select event_id, xmin
+ from (select event_id, xmin from events
        union all
-       select event_id from other_events) ss
+       select event_id, xmin from other_events) ss
  order by event_id;
 
 drop table events_child, events, other_events;
 
-reset enable_indexonlyscan;
-
 -- Test constraint exclusion of UNION ALL subqueries
 explain (costs off)
  SELECT * FROM
-- 
2.39.3 (Apple Git-145)

