From 4ec846adcc228c8bc63a50df314dfea0805b7b1a Mon Sep 17 00:00:00 2001
From: John Naylor <john.naylor@postgresql.org>
Date: Mon, 30 May 2022 11:48:51 +0700
Subject: [PATCH v1 3/5] Optimistically attempt insertion sort on
 pre-partitioned input

During the partitioning phase, when no swaps happened, and the partitions
were not highly unbalanced, inform the next recursion level. If all the
pivot candidates on the next level down are presorted, we will attempt
a "partial insertion sort". If that partition happens to be presorted,
it can return. Otherwise, if the sequence is large enough such that
we checked the ordering of nine pivot candidates, it will try a few
iterations of insertion sort in the hopes this will be enough to sort
the sequence.

For the root level, we only check the pivot candidates and assume
the other criteria are fine.

The changes to the regression tests are due to duplicates in sort
keys, since qsort is not a stable sort.
---
 src/include/lib/sort_template.h            | 194 ++++++++++++++++-----
 src/test/regress/expected/create_index.out |   4 +-
 src/test/regress/expected/tsrf.out         |  42 ++---
 src/test/regress/expected/tuplesort.out    |   6 +-
 src/test/regress/expected/window.out       | 104 +++++------
 5 files changed, 227 insertions(+), 123 deletions(-)

diff --git a/src/include/lib/sort_template.h b/src/include/lib/sort_template.h
index 54921de568..492ec2823b 100644
--- a/src/include/lib/sort_template.h
+++ b/src/include/lib/sort_template.h
@@ -57,8 +57,6 @@
  *	  Modifications from vanilla NetBSD source:
  *	  - Add do ... while() macro fix
  *	  - Remove __inline, _DIAGASSERTs, __P
- *	  - Remove ill-considered "swap_cnt" switch to insertion sort, in favor
- *		of a simple check for presorted input.
  *	  - Take care to recurse on the smaller partition, to bound stack usage
  *	  - Convert into a header that can generate specialized functions
  *
@@ -104,11 +102,7 @@
  * "Engineering a sort function",
  * Software--Practice and Experience 23 (1993) 1249-1265.
  *
- * We have modified their original by adding a check for already-sorted
- * input, which seems to be a win per discussions on pgsql-hackers around
- * 2006-03-21.
- *
- * Also, we recurse on the smaller partition and iterate on the larger one,
+ * We have modified their original by recursing on the smaller partition and iterating on the larger one,
  * which ensures we cannot recurse more than log(N) levels (since the
  * partition recursed to is surely no more than half of the input).  Bentley
  * and McIlroy explicitly rejected doing this on the grounds that it's "not
@@ -172,6 +166,9 @@
 #define ST_SORT_INVOKE_ARG
 #endif
 
+/* threshold for choosing pivot from nine candidates */
+#define ST_THRESHOLD_MED9 40
+
 #ifdef ST_DECLARE
 
 #ifdef ST_COMPARE_RUNTIME_POINTER
@@ -191,6 +188,7 @@ ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE * first, size_t n
 
 /* sort private helper functions */
 #define ST_MED3 ST_MAKE_NAME(ST_SORT, med3)
+#define ST_PARTIAL_INSERTION_SORT ST_MAKE_NAME(ST_SORT, insertion_sort_partial)
 #define ST_SORT_INTERNAL ST_MAKE_NAME(ST_SORT, internal)
 #define ST_SWAP ST_MAKE_NAME(ST_SORT, swap)
 #define ST_SWAPN ST_MAKE_NAME(ST_SORT, swapn)
@@ -203,7 +201,7 @@ ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE * first, size_t n
 #endif
 
 /*
- * Create wrapper macros that know how to invoke compare, med3 and sort with
+ * Create wrapper macros that know how to invoke functions with
  * the right arguments.
  */
 #ifdef ST_COMPARE_RUNTIME_POINTER
@@ -213,12 +211,17 @@ ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE * first, size_t n
 #else
 #define DO_COMPARE(a_, b_) ST_COMPARE((a_), (b_))
 #endif
-#define DO_MED3(a_, b_, c_)												\
-	ST_MED3((a_), (b_), (c_)											\
+#define DO_MED3(a_, b_, c_, nr_)										\
+	ST_MED3((a_), (b_), (c_), (nr_)										\
+			ST_SORT_INVOKE_COMPARE										\
+			ST_SORT_INVOKE_ARG)
+#define DO_PARTIAL_INSERTION_SORT(a_, n_)								\
+	ST_PARTIAL_INSERTION_SORT((a_), (n_)								\
+			ST_SORT_INVOKE_ELEMENT_SIZE									\
 			ST_SORT_INVOKE_COMPARE										\
 			ST_SORT_INVOKE_ARG)
-#define DO_SORT(a_, n_)													\
-	ST_SORT_INTERNAL((a_), (n_)													\
+#define DO_SORT(a_, n_, bal_, ap_)										\
+	ST_SORT_INTERNAL((a_), (n_), (bal_), (ap_)							\
 			ST_SORT_INVOKE_ELEMENT_SIZE									\
 			ST_SORT_INVOKE_COMPARE										\
 			ST_SORT_INVOKE_ARG)
@@ -241,20 +244,55 @@ ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE * first, size_t n
 #endif
 
 /*
- * Find the median of three values.  Currently, performance seems to be best
+ * Find the median of three values, and count the number of out-of-order
+ * elements. We don't care to keep an accurate count, only that we count
+ * report 0 only if the elements are presorted.
+ *
+ * Currently, performance seems to be best
  * if the comparator is inlined here, but the med3 function is not inlined
  * in the qsort function.
  */
 static pg_noinline ST_ELEMENT_TYPE *
 ST_MED3(ST_ELEMENT_TYPE * a,
 		ST_ELEMENT_TYPE * b,
-		ST_ELEMENT_TYPE * c
+		ST_ELEMENT_TYPE * c,
+		int * num_reversed
 		ST_SORT_PROTO_COMPARE
 		ST_SORT_PROTO_ARG)
 {
-	return DO_COMPARE(a, b) < 0 ?
-		(DO_COMPARE(b, c) < 0 ? b : (DO_COMPARE(a, c) < 0 ? c : a))
-		: (DO_COMPARE(b, c) > 0 ? b : (DO_COMPARE(a, c) < 0 ? a : c));
+	if (DO_COMPARE(b, a) < 0)
+	{
+		(*num_reversed)++;
+		if (DO_COMPARE(c, a) < 0)
+		{
+			/* (*num_reversed)++; WIP */
+			if (DO_COMPARE(c, b) < 0)
+			{
+				/* (*num_reversed)++; WIP */
+				return b;
+			}
+			else
+				return c;
+		}
+		else
+			return a;
+	}
+	else
+	{
+		if (DO_COMPARE(c, b) < 0)
+		{
+			(*num_reversed)++;
+			if (DO_COMPARE(c, a) < 0)
+			{
+				/* out-of-order, but we already know the values are neither sorted nor reversed */
+				return a;
+			}
+			else
+				return c;
+		}
+		else
+			return b;
+	}
 }
 
 static inline void
@@ -273,11 +311,66 @@ ST_SWAPN(ST_POINTER_TYPE * a, ST_POINTER_TYPE * b, size_t n)
 		ST_SWAP(&a[i], &b[i]);
 }
 
+static inline bool
+ST_PARTIAL_INSERTION_SORT(ST_POINTER_TYPE * begin, size_t n
+				  ST_SORT_PROTO_ELEMENT_SIZE
+				  ST_SORT_PROTO_COMPARE
+				  ST_SORT_PROTO_ARG)
+{
+	ST_POINTER_TYPE *cur = begin + ST_POINTER_STEP;
+	ST_POINTER_TYPE *end = begin + n * ST_POINTER_STEP;
+	/* WIP: This number was adopted from the Rust stdlib and hasn't been tested within PG */
+	const int max_iters = 5;
+
+	for (int i = 0; i < max_iters; i++)
+	{
+		while (cur < end && !(DO_COMPARE(cur, cur - ST_POINTER_STEP) < 0))
+			cur += ST_POINTER_STEP;
+
+		/* are we done? */
+		if (cur == end)
+			return true;
+
+		/* only proceed with sorting if we checked the ordering of 9 pivot candidates */
+		if (n <= ST_THRESHOLD_MED9)
+			return false;
+
+		DO_SWAP(cur, cur - ST_POINTER_STEP);
+
+		// Shift the smaller one to the left.
+		if (cur - begin >= 2 * ST_POINTER_STEP)
+			for (ST_POINTER_TYPE * j = cur - ST_POINTER_STEP;
+				 j > begin;
+				 j -= ST_POINTER_STEP)
+				{
+					if (!(DO_COMPARE(j, j - ST_POINTER_STEP) < 0))
+						break;
+					else
+						DO_SWAP(j, j - ST_POINTER_STEP);
+				}
+
+		// Shift the larger one to the right.
+		if (end - cur >= 2 * ST_POINTER_STEP)
+			for (ST_POINTER_TYPE * j = cur + ST_POINTER_STEP;
+				 j < end;
+				 j += ST_POINTER_STEP)
+				{
+					if (!(DO_COMPARE(j, j - ST_POINTER_STEP) < 0))
+						break;
+					else
+						DO_SWAP(j, j - ST_POINTER_STEP);
+				}
+	}
+	return false;
+}
+
 /*
  * Workhorse for ST_SORT
  */
 static void
-ST_SORT_INTERNAL(ST_POINTER_TYPE * data, size_t n
+ST_SORT_INTERNAL(ST_POINTER_TYPE * data, size_t n,
+				 bool balanced,
+				 bool already_partitioned
 		ST_SORT_PROTO_ELEMENT_SIZE
 		ST_SORT_PROTO_COMPARE
 		ST_SORT_PROTO_ARG)
@@ -291,9 +384,11 @@ ST_SORT_INTERNAL(ST_POINTER_TYPE * data, size_t n
 			   *pm,
 			   *pn;
 	size_t		d1,
-				d2;
+				d2,
+				n1,
+				n2;
 	int			r,
-				presorted;
+				num_reversed;
 
 loop:
 	DO_CHECK_FOR_INTERRUPTS();
@@ -306,34 +401,33 @@ loop:
 				DO_SWAP(pl, pl - ST_POINTER_STEP);
 		return;
 	}
-	presorted = 1;
-	for (pm = a + ST_POINTER_STEP; pm < a + n * ST_POINTER_STEP;
-		 pm += ST_POINTER_STEP)
-	{
-		DO_CHECK_FOR_INTERRUPTS();
-		if (DO_COMPARE(pm - ST_POINTER_STEP, pm) > 0)
-		{
-			presorted = 0;
-			break;
-		}
-	}
-	if (presorted)
-		return;
+
+	num_reversed = 0; // we must reset this on every iteration
 	pm = a + (n / 2) * ST_POINTER_STEP;
 	if (n > 7)
 	{
 		pl = a;
 		pn = a + (n - 1) * ST_POINTER_STEP;
-		if (n > 40)
+		if (n > ST_THRESHOLD_MED9)
 		{
 			size_t		d = (n / 8) * ST_POINTER_STEP;
 
-			pl = DO_MED3(pl, pl + d, pl + 2 * d);
-			pm = DO_MED3(pm - d, pm, pm + d);
-			pn = DO_MED3(pn - 2 * d, pn - d, pn);
+			pl = DO_MED3(pl, pl + d, pl + 2 * d, &num_reversed);
+			pm = DO_MED3(pm - d, pm, pm + d, &num_reversed);
+			pn = DO_MED3(pn - 2 * d, pn - d, pn, &num_reversed);
 		}
-		pm = DO_MED3(pl, pm, pn);
+		pm = DO_MED3(pl, pm, pn, &num_reversed);
 	}
+
+	// If the sequence was decently balanced and already partitioned,
+	// and the pivot candidates were presorted, try to use insertion sort.
+	if (already_partitioned && balanced && num_reversed == 0)
+	{
+		if (DO_PARTIAL_INSERTION_SORT(a, n))
+			return;
+	}
+
+	/* put the pivot at the beginning */
 	DO_SWAP(a, pm);
 	pa = pb = a + ST_POINTER_STEP;
 	pc = pd = a + (n - 1) * ST_POINTER_STEP;
@@ -344,6 +438,7 @@ loop:
 			if (r == 0)
 			{
 				DO_SWAP(pa, pb);
+				already_partitioned = false;
 				pa += ST_POINTER_STEP;
 			}
 			pb += ST_POINTER_STEP;
@@ -354,6 +449,7 @@ loop:
 			if (r == 0)
 			{
 				DO_SWAP(pc, pd);
+				already_partitioned = false;
 				pd -= ST_POINTER_STEP;
 			}
 			pc -= ST_POINTER_STEP;
@@ -362,6 +458,7 @@ loop:
 		if (pb > pc)
 			break;
 		DO_SWAP(pb, pc);
+		already_partitioned = false;
 		pb += ST_POINTER_STEP;
 		pc -= ST_POINTER_STEP;
 	}
@@ -372,30 +469,34 @@ loop:
 	DO_SWAPN(pb, pn - d1, d1);
 	d1 = pb - pa;
 	d2 = pd - pc;
+	n1 = d1 / ST_POINTER_STEP;
+	n2 = d2 / ST_POINTER_STEP;
 	if (d1 <= d2)
 	{
 		/* Recurse on left partition, then iterate on right partition */
+		balanced = n1 >= n / 8;
 		if (d1 > ST_POINTER_STEP)
-			DO_SORT(a, d1 / ST_POINTER_STEP);
+			DO_SORT(a, n1, balanced, already_partitioned);
 		if (d2 > ST_POINTER_STEP)
 		{
 			/* Iterate rather than recurse to save stack space */
-			/* DO_SORT(pn - d2, d2 / ST_POINTER_STEP) */
+			/* DO_SORT(pn - d2, n2, ...) */
 			a = pn - d2;
-			n = d2 / ST_POINTER_STEP;
+			n = n2;
 			goto loop;
 		}
 	}
 	else
 	{
 		/* Recurse on right partition, then iterate on left partition */
+		balanced = n2 >= n / 8;
 		if (d2 > ST_POINTER_STEP)
-			DO_SORT(pn - d2, d2 / ST_POINTER_STEP);
+			DO_SORT(pn - d2, n2, balanced, already_partitioned);
 		if (d1 > ST_POINTER_STEP)
 		{
 			/* Iterate rather than recurse to save stack space */
-			/* DO_SORT(a, d1 / ST_POINTER_STEP) */
-			n = d1 / ST_POINTER_STEP;
+			/* DO_SORT(a, n1, ...) */
+			n = n1;
 			goto loop;
 		}
 	}
@@ -412,7 +513,7 @@ ST_SORT(ST_ELEMENT_TYPE * data, size_t n
 {
 	ST_POINTER_TYPE *begin = (ST_POINTER_TYPE *) data;
 
-	DO_SORT(begin, n);
+	DO_SORT(begin, n, true, true);
 
 #ifdef USE_ASSERT_CHECKING
 	/* WIP: verify the sorting worked */
@@ -429,6 +530,7 @@ ST_SORT(ST_ELEMENT_TYPE * data, size_t n
 #undef DO_CHECK_FOR_INTERRUPTS
 #undef DO_COMPARE
 #undef DO_MED3
+#undef DO_PARTIAL_INSERTION_SORT
 #undef DO_SORT
 #undef DO_SWAP
 #undef DO_SWAPN
@@ -443,6 +545,7 @@ ST_SORT(ST_ELEMENT_TYPE * data, size_t n
 #undef ST_MAKE_NAME_
 #undef ST_MAKE_PREFIX
 #undef ST_MED3
+#undef ST_PARTIAL_INSERTION_SORT
 #undef ST_POINTER_STEP
 #undef ST_POINTER_TYPE
 #undef ST_SCOPE
@@ -456,3 +559,4 @@ ST_SORT(ST_ELEMENT_TYPE * data, size_t n
 #undef ST_SORT_PROTO_ELEMENT_SIZE
 #undef ST_SWAP
 #undef ST_SWAPN
+#undef ST_THRESHOLD_MED9
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index d55aec3a1d..0a544e7167 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -165,8 +165,8 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
 SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
         f1         
 -------------------
- (0,0)
  (1e-300,-1e-300)
+ (0,0)
  (-3,4)
  (-10,0)
  (10,10)
@@ -187,8 +187,8 @@ SELECT * FROM point_tbl WHERE f1 IS NULL;
 SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
         f1         
 -------------------
- (0,0)
  (1e-300,-1e-300)
+ (0,0)
  (-3,4)
  (-10,0)
  (10,10)
diff --git a/src/test/regress/expected/tsrf.out b/src/test/regress/expected/tsrf.out
index d47b5f6ec5..ef3bd4f539 100644
--- a/src/test/regress/expected/tsrf.out
+++ b/src/test/regress/expected/tsrf.out
@@ -348,19 +348,19 @@ SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(d
 SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab) ORDER BY g;
  dataa |  b  | g | count 
 -------+-----+---+-------
- a     | bar | 1 |     1
+ b     |     | 1 |     1
  a     | foo | 1 |     1
  a     |     | 1 |     2
  b     | bar | 1 |     1
- b     |     | 1 |     1
+ a     | bar | 1 |     1
        |     | 1 |     3
        | bar | 1 |     2
        | foo | 1 |     1
-       | foo | 2 |     1
+       | bar | 2 |     2
  a     | bar | 2 |     1
  b     |     | 2 |     1
  a     | foo | 2 |     1
-       | bar | 2 |     2
+       | foo | 2 |     1
  a     |     | 2 |     2
        |     | 2 |     3
  b     | bar | 2 |     1
@@ -398,56 +398,56 @@ SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(d
 SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab, g) ORDER BY dataa;
  dataa |  b  | g | count 
 -------+-----+---+-------
+ a     | foo | 1 |     1
  a     | foo |   |     2
  a     |     |   |     4
+ a     |     | 1 |     2
  a     |     | 2 |     2
- a     | bar | 1 |     1
  a     | bar | 2 |     1
+ a     | bar | 1 |     1
  a     | bar |   |     2
- a     | foo | 1 |     1
  a     | foo | 2 |     1
- a     |     | 1 |     2
+ b     | bar |   |     2
+ b     |     | 1 |     1
  b     | bar | 1 |     1
  b     |     |   |     2
- b     |     | 1 |     1
- b     | bar | 2 |     1
- b     | bar |   |     2
  b     |     | 2 |     1
-       |     | 2 |     3
-       |     |   |     6
+ b     | bar | 2 |     1
        | bar | 1 |     2
+       |     |   |     6
        | bar | 2 |     2
        | bar |   |     4
        | foo | 1 |     1
        | foo | 2 |     1
        | foo |   |     2
        |     | 1 |     3
+       |     | 2 |     3
 (24 rows)
 
 SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab, g) ORDER BY g;
  dataa |  b  | g | count 
 -------+-----+---+-------
- a     | bar | 1 |     1
+       | bar | 1 |     2
  a     | foo | 1 |     1
  b     | bar | 1 |     1
-       | bar | 1 |     2
+ a     | bar | 1 |     1
        | foo | 1 |     1
  a     |     | 1 |     2
  b     |     | 1 |     1
        |     | 1 |     3
- a     |     | 2 |     2
+       | foo | 2 |     1
  b     |     | 2 |     1
        | bar | 2 |     2
        |     | 2 |     3
-       | foo | 2 |     1
+ b     | bar | 2 |     1
  a     | bar | 2 |     1
  a     | foo | 2 |     1
- b     | bar | 2 |     1
- a     |     |   |     4
+ a     |     | 2 |     2
+ a     | foo |   |     2
  b     | bar |   |     2
  b     |     |   |     2
        |     |   |     6
- a     | foo |   |     2
+ a     |     |   |     4
  a     | bar |   |     2
        | bar |   |     4
        | foo |   |     2
@@ -597,9 +597,9 @@ SELECT DISTINCT ON (g) a, b, generate_series(1,3) g
 FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b);
  a | b | g 
 ---+---+---
- 3 | 2 | 1
+ 1 | 4 | 1
  5 | 1 | 2
- 3 | 1 | 3
+ 5 | 3 | 3
 (3 rows)
 
 -- LIMIT / OFFSET is evaluated after SRF evaluation
diff --git a/src/test/regress/expected/tuplesort.out b/src/test/regress/expected/tuplesort.out
index 418f296a3f..a1a5889756 100644
--- a/src/test/regress/expected/tuplesort.out
+++ b/src/test/regress/expected/tuplesort.out
@@ -242,11 +242,11 @@ FROM abbrev_abort_uuids
 ORDER BY ctid DESC LIMIT 5;
   id   |           abort_increasing           |           abort_decreasing           |          noabort_increasing          |          noabort_decreasing          
 -------+--------------------------------------+--------------------------------------+--------------------------------------+--------------------------------------
-     0 |                                      |                                      |                                      | 
- 20002 |                                      |                                      |                                      | 
  20003 |                                      |                                      |                                      | 
- 20001 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000
+ 20002 |                                      |                                      |                                      | 
+     0 |                                      |                                      |                                      | 
  20010 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000
+ 20001 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000
 (5 rows)
 
 ROLLBACK;
diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out
index 433a0bb025..c4fc72df93 100644
--- a/src/test/regress/expected/window.out
+++ b/src/test/regress/expected/window.out
@@ -23,8 +23,8 @@ SELECT depname, empno, salary, sum(salary) OVER (PARTITION BY depname) FROM emps
 -----------+-------+--------+-------
  develop   |     7 |   4200 | 25100
  develop   |     9 |   4500 | 25100
- develop   |    11 |   5200 | 25100
  develop   |    10 |   5200 | 25100
+ develop   |    11 |   5200 | 25100
  develop   |     8 |   6000 | 25100
  personnel |     5 |   3500 |  7400
  personnel |     2 |   3900 |  7400
@@ -38,8 +38,8 @@ SELECT depname, empno, salary, rank() OVER (PARTITION BY depname ORDER BY salary
 -----------+-------+--------+------
  develop   |     7 |   4200 |    1
  develop   |     9 |   4500 |    2
- develop   |    11 |   5200 |    3
  develop   |    10 |   5200 |    3
+ develop   |    11 |   5200 |    3
  develop   |     8 |   6000 |    5
  personnel |     5 |   3500 |    1
  personnel |     2 |   3900 |    2
@@ -78,11 +78,11 @@ GROUP BY four, ten ORDER BY four, ten;
 SELECT depname, empno, salary, sum(salary) OVER w FROM empsalary WINDOW w AS (PARTITION BY depname);
   depname  | empno | salary |  sum  
 -----------+-------+--------+-------
- develop   |    11 |   5200 | 25100
  develop   |     7 |   4200 | 25100
+ develop   |    10 |   5200 | 25100
  develop   |     9 |   4500 | 25100
  develop   |     8 |   6000 | 25100
- develop   |    10 |   5200 | 25100
+ develop   |    11 |   5200 | 25100
  personnel |     5 |   3500 |  7400
  personnel |     2 |   3900 |  7400
  sales     |     3 |   4800 | 14600
@@ -93,15 +93,15 @@ SELECT depname, empno, salary, sum(salary) OVER w FROM empsalary WINDOW w AS (PA
 SELECT depname, empno, salary, rank() OVER w FROM empsalary WINDOW w AS (PARTITION BY depname ORDER BY salary) ORDER BY rank() OVER w;
   depname  | empno | salary | rank 
 -----------+-------+--------+------
- develop   |     7 |   4200 |    1
  personnel |     5 |   3500 |    1
+ develop   |     7 |   4200 |    1
  sales     |     3 |   4800 |    1
  sales     |     4 |   4800 |    1
  personnel |     2 |   3900 |    2
  develop   |     9 |   4500 |    2
  sales     |     1 |   5000 |    3
- develop   |    11 |   5200 |    3
  develop   |    10 |   5200 |    3
+ develop   |    11 |   5200 |    3
  develop   |     8 |   6000 |    5
 (10 rows)
 
@@ -803,9 +803,9 @@ SELECT sum(unique1) over (order by four range between current row and unbounded
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  45 |       0 |    0
   45 |       8 |    0
   45 |       4 |    0
+  45 |       0 |    0
   33 |       5 |    1
   33 |       9 |    1
   33 |       1 |    1
@@ -922,9 +922,9 @@ SELECT first_value(unique1) over (ORDER BY four rows between current row and 2 f
 FROM tenk1 WHERE unique1 < 10;
  first_value | unique1 | four 
 -------------+---------+------
-           8 |       0 |    0
            4 |       8 |    0
-           5 |       4 |    0
+           0 |       4 |    0
+           5 |       0 |    0
            9 |       5 |    1
            1 |       9 |    1
            6 |       1 |    1
@@ -939,9 +939,9 @@ SELECT first_value(unique1) over (ORDER BY four rows between current row and 2 f
 FROM tenk1 WHERE unique1 < 10;
  first_value | unique1 | four 
 -------------+---------+------
-             |       0 |    0
-           5 |       8 |    0
+             |       8 |    0
            5 |       4 |    0
+           5 |       0 |    0
              |       5 |    1
            6 |       9 |    1
            6 |       1 |    1
@@ -956,9 +956,9 @@ SELECT first_value(unique1) over (ORDER BY four rows between current row and 2 f
 FROM tenk1 WHERE unique1 < 10;
  first_value | unique1 | four 
 -------------+---------+------
-           0 |       0 |    0
            8 |       8 |    0
            4 |       4 |    0
+           0 |       0 |    0
            5 |       5 |    1
            9 |       9 |    1
            1 |       1 |    1
@@ -973,9 +973,9 @@ SELECT last_value(unique1) over (ORDER BY four rows between current row and 2 fo
 FROM tenk1 WHERE unique1 < 10;
  last_value | unique1 | four 
 ------------+---------+------
-          4 |       0 |    0
-          5 |       8 |    0
-          9 |       4 |    0
+          0 |       8 |    0
+          5 |       4 |    0
+          9 |       0 |    0
           1 |       5 |    1
           6 |       9 |    1
           2 |       1 |    1
@@ -990,9 +990,9 @@ SELECT last_value(unique1) over (ORDER BY four rows between current row and 2 fo
 FROM tenk1 WHERE unique1 < 10;
  last_value | unique1 | four 
 ------------+---------+------
-            |       0 |    0
-          5 |       8 |    0
-          9 |       4 |    0
+            |       8 |    0
+          5 |       4 |    0
+          9 |       0 |    0
             |       5 |    1
           6 |       9 |    1
           2 |       1 |    1
@@ -1007,9 +1007,9 @@ SELECT last_value(unique1) over (ORDER BY four rows between current row and 2 fo
 FROM tenk1 WHERE unique1 < 10;
  last_value | unique1 | four 
 ------------+---------+------
-          0 |       0 |    0
-          5 |       8 |    0
-          9 |       4 |    0
+          8 |       8 |    0
+          5 |       4 |    0
+          9 |       0 |    0
           5 |       5 |    1
           6 |       9 |    1
           2 |       1 |    1
@@ -1075,9 +1075,9 @@ SELECT sum(unique1) over (w range between current row and unbounded following),
 FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
  sum | unique1 | four 
 -----+---------+------
-  45 |       0 |    0
   45 |       8 |    0
   45 |       4 |    0
+  45 |       0 |    0
   33 |       5 |    1
   33 |       9 |    1
   33 |       1 |    1
@@ -1092,9 +1092,9 @@ SELECT sum(unique1) over (w range between unbounded preceding and current row ex
 FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
  sum | unique1 | four 
 -----+---------+------
-  12 |       0 |    0
    4 |       8 |    0
    8 |       4 |    0
+  12 |       0 |    0
   22 |       5 |    1
   18 |       9 |    1
   26 |       1 |    1
@@ -1109,9 +1109,9 @@ SELECT sum(unique1) over (w range between unbounded preceding and current row ex
 FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
  sum | unique1 | four 
 -----+---------+------
-     |       0 |    0
      |       8 |    0
      |       4 |    0
+     |       0 |    0
   12 |       5 |    1
   12 |       9 |    1
   12 |       1 |    1
@@ -1126,9 +1126,9 @@ SELECT sum(unique1) over (w range between unbounded preceding and current row ex
 FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
  sum | unique1 | four 
 -----+---------+------
-   0 |       0 |    0
    8 |       8 |    0
    4 |       4 |    0
+   0 |       0 |    0
   17 |       5 |    1
   21 |       9 |    1
   13 |       1 |    1
@@ -1145,9 +1145,9 @@ FROM tenk1 WHERE unique1 < 10
 WINDOW w AS (order by four range between current row and unbounded following);
  first_value | nth_2 | last_value | unique1 | four 
 -------------+-------+------------+---------+------
-           0 |     8 |          7 |       0 |    0
-           0 |     8 |          7 |       8 |    0
-           0 |     8 |          7 |       4 |    0
+           8 |     4 |          7 |       8 |    0
+           8 |     4 |          7 |       4 |    0
+           8 |     4 |          7 |       0 |    0
            5 |     9 |          7 |       5 |    1
            5 |     9 |          7 |       9 |    1
            5 |     9 |          7 |       1 |    1
@@ -1349,9 +1349,9 @@ SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::i
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-     |       0 |    0
      |       8 |    0
      |       4 |    0
+     |       0 |    0
   12 |       5 |    1
   12 |       9 |    1
   12 |       1 |    1
@@ -1373,9 +1373,9 @@ FROM tenk1 WHERE unique1 < 10;
   18 |       9 |    1
   18 |       5 |    1
   18 |       1 |    1
-  23 |       0 |    0
   23 |       8 |    0
   23 |       4 |    0
+  23 |       0 |    0
 (10 rows)
 
 SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding exclude no others),
@@ -1383,9 +1383,9 @@ SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::i
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-     |       0 |    0
      |       8 |    0
      |       4 |    0
+     |       0 |    0
   12 |       5 |    1
   12 |       9 |    1
   12 |       1 |    1
@@ -1400,9 +1400,9 @@ SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::i
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-     |       0 |    0
      |       8 |    0
      |       4 |    0
+     |       0 |    0
   12 |       5 |    1
   12 |       9 |    1
   12 |       1 |    1
@@ -1417,9 +1417,9 @@ SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::i
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-     |       0 |    0
      |       8 |    0
      |       4 |    0
+     |       0 |    0
   12 |       5 |    1
   12 |       9 |    1
   12 |       1 |    1
@@ -1434,9 +1434,9 @@ SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::i
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-     |       0 |    0
      |       8 |    0
      |       4 |    0
+     |       0 |    0
   12 |       5 |    1
   12 |       9 |    1
   12 |       1 |    1
@@ -1451,9 +1451,9 @@ SELECT sum(unique1) over (order by four range between 2::int8 preceding and 6::i
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  33 |       0 |    0
   41 |       8 |    0
   37 |       4 |    0
+  33 |       0 |    0
   35 |       5 |    1
   39 |       9 |    1
   31 |       1 |    1
@@ -1468,9 +1468,9 @@ SELECT sum(unique1) over (order by four range between 2::int8 preceding and 6::i
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  33 |       0 |    0
   33 |       8 |    0
   33 |       4 |    0
+  33 |       0 |    0
   30 |       5 |    1
   30 |       9 |    1
   30 |       1 |    1
@@ -2588,9 +2588,9 @@ SELECT sum(unique1) over (order by four groups between unbounded preceding and c
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  12 |       0 |    0
   12 |       8 |    0
   12 |       4 |    0
+  12 |       0 |    0
   27 |       5 |    1
   27 |       9 |    1
   27 |       1 |    1
@@ -2605,9 +2605,9 @@ SELECT sum(unique1) over (order by four groups between unbounded preceding and u
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  45 |       0 |    0
   45 |       8 |    0
   45 |       4 |    0
+  45 |       0 |    0
   45 |       5 |    1
   45 |       9 |    1
   45 |       1 |    1
@@ -2622,9 +2622,9 @@ SELECT sum(unique1) over (order by four groups between current row and unbounded
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  45 |       0 |    0
   45 |       8 |    0
   45 |       4 |    0
+  45 |       0 |    0
   33 |       5 |    1
   33 |       9 |    1
   33 |       1 |    1
@@ -2639,9 +2639,9 @@ SELECT sum(unique1) over (order by four groups between 1 preceding and unbounded
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  45 |       0 |    0
   45 |       8 |    0
   45 |       4 |    0
+  45 |       0 |    0
   45 |       5 |    1
   45 |       9 |    1
   45 |       1 |    1
@@ -2656,9 +2656,9 @@ SELECT sum(unique1) over (order by four groups between 1 following and unbounded
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  33 |       0 |    0
   33 |       8 |    0
   33 |       4 |    0
+  33 |       0 |    0
   18 |       5 |    1
   18 |       9 |    1
   18 |       1 |    1
@@ -2673,9 +2673,9 @@ SELECT sum(unique1) over (order by four groups between unbounded preceding and 2
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  35 |       0 |    0
   35 |       8 |    0
   35 |       4 |    0
+  35 |       0 |    0
   45 |       5 |    1
   45 |       9 |    1
   45 |       1 |    1
@@ -2690,9 +2690,9 @@ SELECT sum(unique1) over (order by four groups between 2 preceding and 1 precedi
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-     |       0 |    0
      |       8 |    0
      |       4 |    0
+     |       0 |    0
   12 |       5 |    1
   12 |       9 |    1
   12 |       1 |    1
@@ -2707,9 +2707,9 @@ SELECT sum(unique1) over (order by four groups between 2 preceding and 1 followi
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  27 |       0 |    0
   27 |       8 |    0
   27 |       4 |    0
+  27 |       0 |    0
   35 |       5 |    1
   35 |       9 |    1
   35 |       1 |    1
@@ -2724,9 +2724,9 @@ SELECT sum(unique1) over (order by four groups between 0 preceding and 0 followi
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  12 |       0 |    0
   12 |       8 |    0
   12 |       4 |    0
+  12 |       0 |    0
   15 |       5 |    1
   15 |       9 |    1
   15 |       1 |    1
@@ -2741,9 +2741,9 @@ SELECT sum(unique1) over (order by four groups between 2 preceding and 1 followi
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  27 |       0 |    0
   19 |       8 |    0
   23 |       4 |    0
+  27 |       0 |    0
   30 |       5 |    1
   26 |       9 |    1
   34 |       1 |    1
@@ -2758,9 +2758,9 @@ SELECT sum(unique1) over (order by four groups between 2 preceding and 1 followi
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  15 |       0 |    0
   15 |       8 |    0
   15 |       4 |    0
+  15 |       0 |    0
   20 |       5 |    1
   20 |       9 |    1
   20 |       1 |    1
@@ -2775,9 +2775,9 @@ SELECT sum(unique1) over (order by four groups between 2 preceding and 1 followi
 FROM tenk1 WHERE unique1 < 10;
  sum | unique1 | four 
 -----+---------+------
-  15 |       0 |    0
   23 |       8 |    0
   19 |       4 |    0
+  15 |       0 |    0
   25 |       5 |    1
   29 |       9 |    1
   21 |       1 |    1
@@ -3413,8 +3413,8 @@ WHERE r <= 3;
  empno | salary | r 
 -------+--------+---
      8 |   6000 | 1
-    10 |   5200 | 2
     11 |   5200 | 2
+    10 |   5200 | 2
 (3 rows)
 
 -- Ensure dr = 1 is converted to dr <= 1 to get all rows leading up to dr = 1
@@ -3473,8 +3473,8 @@ WHERE c <= 3;
  empno | salary | c 
 -------+--------+---
      8 |   6000 | 1
-    10 |   5200 | 3
     11 |   5200 | 3
+    10 |   5200 | 3
 (3 rows)
 
 EXPLAIN (COSTS OFF)
@@ -3502,8 +3502,8 @@ WHERE c <= 3;
  empno | salary | c 
 -------+--------+---
      8 |   6000 | 1
-    10 |   5200 | 3
     11 |   5200 | 3
+    10 |   5200 | 3
 (3 rows)
 
 EXPLAIN (COSTS OFF)
@@ -3637,8 +3637,8 @@ WHERE c <= 3;
  empno |  depname  | salary | c 
 -------+-----------+--------+---
      8 | develop   |   6000 | 1
-    10 | develop   |   5200 | 3
     11 | develop   |   5200 | 3
+    10 | develop   |   5200 | 3
      2 | personnel |   3900 | 1
      5 | personnel |   3500 | 2
      1 | sales     |   5000 | 1
-- 
2.36.1

