From cec55013943d160538334ee19ef5db429a085969 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 13 Jul 2016 15:23:56 -0400
Subject: [PATCH] Add a regression test case to improve code coverage for
 tuplesort.

Test the external-sort code path in CLUSTER for two different scenarios:
multiple-pass external sorting, and the best case for replacement
selection, where only one run is produced, so that no merge is required.
This test would have caught the bug fixed in commit 1b0fc8507, at
least when run with valgrind enabled.

In passing, add a short-circuit test in plan_cluster_use_sort() to make
dead certain that it selects sorting when enable_indexscan is off.  As
things stand, that would happen anyway, but it seems like good future
proofing for this test.

Peter Geoghegan

Discussion: <CAM3SWZSgxehDkDMq1FdiW2A0Dxc79wH0hz1x-TnGy=1BXEL+nw@mail.gmail.com>
---
 src/backend/optimizer/plan/planner.c  |  4 +++
 src/test/regress/expected/cluster.out | 36 +++++++++++++++++++++++++++
 src/test/regress/sql/cluster.sql      | 34 +++++++++++++++++++++++++
 3 files changed, 74 insertions(+)

diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 796f564e4f8..f484fb91c11 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -5193,6 +5193,10 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
 	IndexPath  *indexScanPath;
 	ListCell   *lc;
 
+	/* We can short-circuit the cost comparison if indexscans are disabled */
+	if (!enable_indexscan)
+		return true;			/* use sort */
+
 	/* Set up mostly-dummy planner state */
 	query = makeNode(Query);
 	query->commandType = CMD_SELECT;
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index bc26846d6a4..4c8d834d7ab 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -438,10 +438,46 @@ select * from clstr_temp;
 (2 rows)
 
 drop table clstr_temp;
+RESET SESSION AUTHORIZATION;
+-- Test CLUSTER with external tuplesorting
+create table clstr_4 as select * from tenk1;
+create index cluster_sort on clstr_4 (hundred, thousand, tenthous);
+-- ensure we don't use the index in CLUSTER nor the checking SELECTs
+set enable_indexscan = off;
+-- Use external sort that only ever uses quicksort to sort runs:
+set maintenance_work_mem = '1MB';
+set replacement_sort_tuples = 0;
+cluster clstr_4 using cluster_sort;
+select * from
+(select hundred, lag(hundred) over () as lhundred,
+        thousand, lag(thousand) over () as lthousand,
+        tenthous, lag(tenthous) over () as ltenthous from clstr_4) ss
+where row(hundred, thousand, tenthous) <= row(lhundred, lthousand, ltenthous);
+ hundred | lhundred | thousand | lthousand | tenthous | ltenthous 
+---------+----------+----------+-----------+----------+-----------
+(0 rows)
+
+-- Replacement selection will now be forced.  It should only produce a single
+-- run, due to the fact that input is found to be presorted:
+set replacement_sort_tuples = 150000;
+cluster clstr_4 using cluster_sort;
+select * from
+(select hundred, lag(hundred) over () as lhundred,
+        thousand, lag(thousand) over () as lthousand,
+        tenthous, lag(tenthous) over () as ltenthous from clstr_4) ss
+where row(hundred, thousand, tenthous) <= row(lhundred, lthousand, ltenthous);
+ hundred | lhundred | thousand | lthousand | tenthous | ltenthous 
+---------+----------+----------+-----------+----------+-----------
+(0 rows)
+
+reset enable_indexscan;
+reset maintenance_work_mem;
+reset replacement_sort_tuples;
 -- clean up
 \c -
 DROP TABLE clustertest;
 DROP TABLE clstr_1;
 DROP TABLE clstr_2;
 DROP TABLE clstr_3;
+DROP TABLE clstr_4;
 DROP USER clstr_user;
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index 8d536c8c363..6e619fdcc7d 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -194,10 +194,44 @@ cluster clstr_temp using clstr_temp_pkey;
 select * from clstr_temp;
 drop table clstr_temp;
 
+RESET SESSION AUTHORIZATION;
+
+-- Test CLUSTER with external tuplesorting
+
+create table clstr_4 as select * from tenk1;
+create index cluster_sort on clstr_4 (hundred, thousand, tenthous);
+-- ensure we don't use the index in CLUSTER nor the checking SELECTs
+set enable_indexscan = off;
+
+-- Use external sort that only ever uses quicksort to sort runs:
+set maintenance_work_mem = '1MB';
+set replacement_sort_tuples = 0;
+cluster clstr_4 using cluster_sort;
+select * from
+(select hundred, lag(hundred) over () as lhundred,
+        thousand, lag(thousand) over () as lthousand,
+        tenthous, lag(tenthous) over () as ltenthous from clstr_4) ss
+where row(hundred, thousand, tenthous) <= row(lhundred, lthousand, ltenthous);
+
+-- Replacement selection will now be forced.  It should only produce a single
+-- run, due to the fact that input is found to be presorted:
+set replacement_sort_tuples = 150000;
+cluster clstr_4 using cluster_sort;
+select * from
+(select hundred, lag(hundred) over () as lhundred,
+        thousand, lag(thousand) over () as lthousand,
+        tenthous, lag(tenthous) over () as ltenthous from clstr_4) ss
+where row(hundred, thousand, tenthous) <= row(lhundred, lthousand, ltenthous);
+
+reset enable_indexscan;
+reset maintenance_work_mem;
+reset replacement_sort_tuples;
+
 -- clean up
 \c -
 DROP TABLE clustertest;
 DROP TABLE clstr_1;
 DROP TABLE clstr_2;
 DROP TABLE clstr_3;
+DROP TABLE clstr_4;
 DROP USER clstr_user;
-- 
GitLab