From 45664e5282fa217ac773743ac75090d8b52bbf3a Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Tue, 14 Jan 2020 14:01:55 -0800
Subject: [PATCH v30 2/5] Header for customized qsort.

This is a dependency of the "Bucket sort for compactify_tuples" commit
that follows.  (We probably won't need to inline qsort, but leave it for
now.)

This patch was retrieved from:
https://postgr.es/m/CAL-rCA2n7UfVu1Ui0f%2B7cVN4vAKVM0%2B-cZKb_ka6-mGQBAF92w%40mail.gmail.com
---
 src/include/lib/qsort_template.h          | 325 ++++++++++++++++++++++
 src/port/qsort.c                          |   2 +-
 src/port/qsort_arg.c                      |   2 +-
 src/backend/utils/sort/Makefile           |   7 -
 src/backend/utils/sort/gen_qsort_tuple.pl | 270 ------------------
 src/backend/utils/sort/tuplesort.c        |  27 +-
 src/tools/msvc/Solution.pm                |  10 +
 src/tools/msvc/clean.bat                  |   1 -
 8 files changed, 363 insertions(+), 281 deletions(-)
 create mode 100644 src/include/lib/qsort_template.h
 delete mode 100644 src/backend/utils/sort/gen_qsort_tuple.pl

diff --git a/src/include/lib/qsort_template.h b/src/include/lib/qsort_template.h
new file mode 100644
index 0000000000..3d9ffbd81b
--- /dev/null
+++ b/src/include/lib/qsort_template.h
@@ -0,0 +1,325 @@
+/*	$NetBSD: qsort.c,v 1.13 2003/08/07 16:43:42 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1992, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	  notice, this list of conditions and the following disclaimer in the
+ *	  documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *	  may be used to endorse or promote products derived from this software
+ *	  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Qsort routine based on J. L. Bentley and M. D. McIlroy,
+ * "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,
+ * 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
+ * worth the effort", but we have seen crashes in the field due to stack
+ * overrun, so that judgment seems wrong.
+ */
+
+
+/*
+ * Template parameters are:
+ * QS_SUFFIX - name suffix.
+ * QS_TYPE - array element type.
+ * QS_CMP  - Function used to compare elements, should be defined
+ *           before inclusion, or passed using QS_EXTRAPARAMS/QS_EXTRAARGS.
+ *           Default is `cmp_##QS_SUFFIX`
+ * QS_EXTRAPARAMS - extra parameters consumed by qsort and insertion sort.
+ * QS_EXTRAARGS - extra arguments passed to qsort and insertion sort.
+ * QS_CMPARGS - extra arguments passed to cmp function.
+ *
+ * if QS_EXTRAPARAMS, QS_EXTRAARGS and QS_CMPARGS are all undefined, then
+ * QS_CMP assummed to be predefined and accepting only elements to compare
+ * (ie no extra parameters).
+ *
+ * QS_DECLARE - if defined function prototypes and type declarations are
+ *		        generated
+ * QS_DEFINE - if defined function definitions are generated
+ * QS_SCOPE - in which scope (e.g. extern, static) do function declarations reside
+ *
+ * QS_CHECK_FOR_INTERRUPTS - if defined, then CHECK_FOR_INTERRUPTS is called
+ *			periodically.
+ * QS_CHECK_PRESORTED - if defined, check for presorted array is included.
+ */
+
+#ifndef QS_SUFFIX
+#error "QS_SUFFIX should be defined"
+#endif
+#ifndef QS_TYPE
+#error "QS_TYPE should be defined"
+#endif
+
+/* helpers */
+#define QS_MAKE_NAME_(a, b) CppConcat(a, b)
+#define QS_MAKE_NAME(a) QS_MAKE_NAME_(a, QS_SUFFIX)
+
+#define QS_SWAPMANY QS_MAKE_NAME(qsswapmany_)
+#define QS_SWAPONE QS_MAKE_NAME(qsswapone_)
+#define QS_MED3 QS_MAKE_NAME(qsmed3_)
+#define QS_INSERTION_SORT QS_MAKE_NAME(insertion_sort_)
+#define QS_QSORT QS_MAKE_NAME(qsort_)
+#define QS_QSORT_IMPL QS_MAKE_NAME(qsort_impl_)
+#ifndef QS_CMP
+#define QS_CMP QS_MAKE_NAME(cmp_)
+#endif
+
+#if !defined(QS_EXTRAPARAMS) && !defined(QS_EXTRAARGS) && !defined(QS_CMPARGS)
+#define QS_EXTRAPARAMS
+#define QS_EXTRAARGS
+#define QS_CMPARGS
+#else
+#ifndef QS_EXTRAPARAMS
+#error "QS_EXTRAPARAMS should be defined"
+#endif
+#ifndef QS_EXTRAARGS
+#error "QS_EXTRAARGS should be defined"
+#endif
+#ifndef QS_CMPARGS
+#error "QS_CMPARGS should be defined"
+#endif
+#endif
+
+/* generate forward declarations necessary to use the hash table */
+#ifdef QS_DECLARE
+QS_SCOPE void QS_INSERTION_SORT(QS_TYPE * a, QS_TYPE * b QS_EXTRAPARAMS);
+QS_SCOPE void QS_QSORT(QS_TYPE * a, QS_TYPE * b QS_EXTRAPARAMS);
+#endif
+
+#ifdef QS_DEFINE
+static inline void
+QS_SWAPONE(QS_TYPE * a, QS_TYPE * b)
+{
+	QS_TYPE		t = *a;
+
+	*a = *b;
+	*b = t;
+}
+
+static void
+QS_SWAPMANY(QS_TYPE * a, QS_TYPE * b, size_t n)
+{
+	for (; n > 0; n--, a++, b++)
+		QS_SWAPONE(a, b);
+}
+
+#ifndef QS_SKIP_MED3
+static QS_TYPE *
+QS_MED3(QS_TYPE * a, QS_TYPE * b, QS_TYPE * c QS_EXTRAPARAMS)
+{
+	return QS_CMP(a, b QS_CMPARGS) < 0 ?
+		(QS_CMP(b, c QS_CMPARGS) < 0 ? b :
+		 (QS_CMP(a, c QS_CMPARGS) < 0 ? c : a))
+		: (QS_CMP(b, c QS_CMPARGS) > 0 ? b :
+		   (QS_CMP(a, c QS_CMPARGS) < 0 ? a : c));
+}
+#endif
+
+QS_SCOPE void
+QS_INSERTION_SORT(QS_TYPE * a, size_t n QS_EXTRAPARAMS)
+{
+	QS_TYPE    *pm,
+			   *pl;
+
+	for (pm = a + 1; pm < a + n; pm++)
+		for (pl = pm; pl > a && QS_CMP(pl - 1, pl QS_CMPARGS) > 0; pl--)
+			QS_SWAPONE(pl, pl - 1);
+}
+
+#ifdef QS_CHECK_FOR_INTERRUPTS
+#define DO_CHECK_FOR_INTERRUPTS() CHECK_FOR_INTERRUPTS()
+#else
+#define DO_CHECK_FOR_INTERRUPTS()
+#endif
+
+QS_SCOPE void
+QS_QSORT(QS_TYPE * a, size_t n QS_EXTRAPARAMS)
+{
+	QS_TYPE    *pa,
+			   *pb,
+			   *pc,
+			   *pd,
+			   *pm,
+			   *pn;
+	size_t		d1,
+				d2;
+	int			r;
+
+loop:
+	DO_CHECK_FOR_INTERRUPTS();
+	if (n < 7)
+	{
+		QS_INSERTION_SORT(a, n QS_EXTRAARGS);
+		return;
+	}
+
+#ifdef QS_CHECK_PRESORTED
+	{
+		int			presorted = 1;
+
+		for (pm = a + 1; pm < a + n; pm++)
+		{
+			DO_CHECK_FOR_INTERRUPTS();
+			if (QS_CMP(pm - 1, pm QS_CMPARGS) > 0)
+			{
+				presorted = 0;
+				break;
+			}
+		}
+		if (presorted)
+			return;
+	}
+#endif
+	pm = a + (n / 2);
+#ifndef QS_SKIP_MED3
+	if (n > 7)
+	{
+		QS_TYPE    *pl = a;
+
+		pn = a + (n - 1);
+#ifndef QS_SKIP_MED_OF_MED
+		if (n > 40)
+		{
+			size_t		d = (n / 8);
+
+			pl = QS_MED3(pl, pl + d, pl + 2 * d QS_EXTRAARGS);
+			pm = QS_MED3(pm - d, pm, pm + d QS_EXTRAARGS);
+			pn = QS_MED3(pn - 2 * d, pn - d, pn QS_EXTRAARGS);
+		}
+#endif
+		pm = QS_MED3(pl, pm, pn QS_EXTRAARGS);
+	}
+#endif
+	QS_SWAPONE(a, pm);
+	pa = pb = a + 1;
+	pc = pd = a + (n - 1);
+	for (;;)
+	{
+		while (pb <= pc && (r = QS_CMP(pb, a QS_CMPARGS)) <= 0)
+		{
+			if (r == 0)
+			{
+				QS_SWAPONE(pa, pb);
+				pa++;
+			}
+			pb++;
+			DO_CHECK_FOR_INTERRUPTS();
+		}
+		while (pb <= pc && (r = QS_CMP(pc, a QS_CMPARGS)) >= 0)
+		{
+			if (r == 0)
+			{
+				QS_SWAPONE(pc, pd);
+				pd--;
+			}
+			pc--;
+			DO_CHECK_FOR_INTERRUPTS();
+		}
+		if (pb > pc)
+			break;
+		QS_SWAPONE(pb, pc);
+		pb++;
+		pc--;
+	}
+	pn = a + n;
+	d1 = Min(pa - a, pb - pa);
+	if (d1 > 0)
+		QS_SWAPMANY(a, pb - d1, d1);
+	d1 = Min(pd - pc, pn - pd - 1);
+	if (d1 > 0)
+		QS_SWAPMANY(pb, pn - d1, d1);
+	d1 = pb - pa;
+	d2 = pd - pc;
+	if (d1 <= d2)
+	{
+		/* Recurse on left partition, then iterate on right partition */
+		if (d1 > 1)
+			QS_QSORT(a, d1 QS_EXTRAARGS);
+		if (d2 > 1)
+		{
+			/* Iterate rather than recurse to save stack space */
+			/* QS_QSORT(pn - d2, d2 EXTRAARGS); */
+			a = pn - d2;
+			n = d2;
+			goto loop;
+		}
+	}
+	else
+	{
+		/* Recurse on right partition, then iterate on left partition */
+		if (d2 > 1)
+			QS_QSORT(pn - d2, d2 QS_EXTRAARGS);
+		if (d1 > 1)
+		{
+			/* Iterate rather than recurse to save stack space */
+			/* QS_QSORT(a, d1 EXTRAARGS); */
+			n = d1;
+			goto loop;
+		}
+	}
+}
+#endif
+
+#undef QS_SUFFIX
+#undef QS_TYPE
+#ifdef QS_DECLARE
+#undef QS_DECLARE
+#endif
+#ifdef QS_DEFINE
+#undef QS_DEFINE
+#endif
+#ifdef QS_SCOPE
+#undef QS_SCOPE
+#endif
+#ifdef QS_CHECK_FOR_INTERRUPTS
+#undef QS_CHECK_FOR_INTERRUPTS
+#endif
+#ifdef QS_CHECK_PRESORTED
+#undef QS_CHECK_PRESORTED
+#endif
+#ifdef QS_SKIP_MED3
+#undef QS_SKIP_MED3
+#endif
+#ifdef QS_SKIP_MED_OF_MED
+#undef QS_SKIP_MED_OF_MED
+#endif
+#undef DO_CHECK_FOR_INTERRUPTS
+#undef QS_MAKE_NAME
+#undef QS_SWAPMANY
+#undef QS_SWAPONE
+#undef QS_MED3
+#undef QS_INSERTION_SORT
+#undef QS_QSORT
+#undef QS_QSORT_IMPL
+#undef QS_CMP
+#undef QS_EXTRAPARAMS
+#undef QS_EXTRAARGS
+#undef QS_CMPARGS
diff --git a/src/port/qsort.c b/src/port/qsort.c
index 409f69a128..b38b33a63a 100644
--- a/src/port/qsort.c
+++ b/src/port/qsort.c
@@ -8,7 +8,7 @@
  *	  in favor of a simple check for presorted input.
  *	  Take care to recurse on the smaller partition, to bound stack usage.
  *
- *	CAUTION: if you change this file, see also qsort_arg.c, gen_qsort_tuple.pl
+ *	CAUTION: if you change this file, see also qsort_arg.c, qsort_template.h
  *
  *	src/port/qsort.c
  */
diff --git a/src/port/qsort_arg.c b/src/port/qsort_arg.c
index a2194b0fb1..9bc7e6edca 100644
--- a/src/port/qsort_arg.c
+++ b/src/port/qsort_arg.c
@@ -8,7 +8,7 @@
  *	  in favor of a simple check for presorted input.
  *	  Take care to recurse on the smaller partition, to bound stack usage.
  *
- *	CAUTION: if you change this file, see also qsort.c, gen_qsort_tuple.pl
+ *	CAUTION: if you change this file, see also qsort_arg.c, qsort_template.h
  *
  *	src/port/qsort_arg.c
  */
diff --git a/src/backend/utils/sort/Makefile b/src/backend/utils/sort/Makefile
index 7ac3659261..fbd00046f4 100644
--- a/src/backend/utils/sort/Makefile
+++ b/src/backend/utils/sort/Makefile
@@ -21,12 +21,5 @@ OBJS = \
 	tuplesort.o \
 	tuplestore.o
 
-tuplesort.o: qsort_tuple.c
-
-qsort_tuple.c: gen_qsort_tuple.pl
-	$(PERL) $(srcdir)/gen_qsort_tuple.pl $< > $@
-
 include $(top_srcdir)/src/backend/common.mk
 
-maintainer-clean:
-	rm -f qsort_tuple.c
diff --git a/src/backend/utils/sort/gen_qsort_tuple.pl b/src/backend/utils/sort/gen_qsort_tuple.pl
deleted file mode 100644
index b6b2ffa7d0..0000000000
--- a/src/backend/utils/sort/gen_qsort_tuple.pl
+++ /dev/null
@@ -1,270 +0,0 @@
-#!/usr/bin/perl -w
-
-#
-# gen_qsort_tuple.pl
-#
-# This script generates specialized versions of the quicksort algorithm for
-# tuple sorting.  The quicksort code is derived from the NetBSD code.  The
-# code generated by this script runs significantly faster than vanilla qsort
-# when used to sort tuples.  This speedup comes from a number of places.
-# The major effects are (1) inlining simple tuple comparators is much faster
-# than jumping through a function pointer and (2) swap and vecswap operations
-# specialized to the particular data type of interest (in this case, SortTuple)
-# are faster than the generic routines.
-#
-#	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.
-#
-#     Instead of sorting arbitrary objects, we're always sorting SortTuples.
-#     Add CHECK_FOR_INTERRUPTS().
-#
-# CAUTION: if you change this file, see also qsort.c and qsort_arg.c
-#
-
-use strict;
-
-my $SUFFIX;
-my $EXTRAARGS;
-my $EXTRAPARAMS;
-my $CMPPARAMS;
-
-emit_qsort_boilerplate();
-
-$SUFFIX      = 'tuple';
-$EXTRAARGS   = ', SortTupleComparator cmp_tuple, Tuplesortstate *state';
-$EXTRAPARAMS = ', cmp_tuple, state';
-$CMPPARAMS   = ', state';
-emit_qsort_implementation();
-
-$SUFFIX      = 'ssup';
-$EXTRAARGS   = ', SortSupport ssup';
-$EXTRAPARAMS = ', ssup';
-$CMPPARAMS   = ', ssup';
-print <<'EOM';
-
-#define cmp_ssup(a, b, ssup) \
-	ApplySortComparator((a)->datum1, (a)->isnull1, \
-						(b)->datum1, (b)->isnull1, ssup)
-
-EOM
-emit_qsort_implementation();
-
-sub emit_qsort_boilerplate
-{
-	print <<'EOM';
-/*
- * autogenerated by src/backend/utils/sort/gen_qsort_tuple.pl, do not edit!
- *
- * This file is included by tuplesort.c, rather than compiled separately.
- */
-
-/*	$NetBSD: qsort.c,v 1.13 2003/08/07 16:43:42 agc Exp $	*/
-
-/*-
- * Copyright (c) 1992, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *	  notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *	  notice, this list of conditions and the following disclaimer in the
- *	  documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *	  may be used to endorse or promote products derived from this software
- *	  without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * Qsort routine based on J. L. Bentley and M. D. McIlroy,
- * "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,
- * 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
- * worth the effort", but we have seen crashes in the field due to stack
- * overrun, so that judgment seems wrong.
- */
-
-static void
-swapfunc(SortTuple *a, SortTuple *b, size_t n)
-{
-	do
-	{
-		SortTuple 	t = *a;
-		*a++ = *b;
-		*b++ = t;
-	} while (--n > 0);
-}
-
-#define swap(a, b)						\
-	do { 								\
-		SortTuple t = *(a);				\
-		*(a) = *(b);					\
-		*(b) = t;						\
-	} while (0);
-
-#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n)
-
-EOM
-
-	return;
-}
-
-sub emit_qsort_implementation
-{
-	print <<EOM;
-static SortTuple *
-med3_$SUFFIX(SortTuple *a, SortTuple *b, SortTuple *c$EXTRAARGS)
-{
-	return cmp_$SUFFIX(a, b$CMPPARAMS) < 0 ?
-		(cmp_$SUFFIX(b, c$CMPPARAMS) < 0 ? b :
-			(cmp_$SUFFIX(a, c$CMPPARAMS) < 0 ? c : a))
-		: (cmp_$SUFFIX(b, c$CMPPARAMS) > 0 ? b :
-			(cmp_$SUFFIX(a, c$CMPPARAMS) < 0 ? a : c));
-}
-
-static void
-qsort_$SUFFIX(SortTuple *a, size_t n$EXTRAARGS)
-{
-	SortTuple  *pa,
-			   *pb,
-			   *pc,
-			   *pd,
-			   *pl,
-			   *pm,
-			   *pn;
-	size_t		d1,
-				d2;
-	int			r,
-				presorted;
-
-loop:
-	CHECK_FOR_INTERRUPTS();
-	if (n < 7)
-	{
-		for (pm = a + 1; pm < a + n; pm++)
-			for (pl = pm; pl > a && cmp_$SUFFIX(pl - 1, pl$CMPPARAMS) > 0; pl--)
-				swap(pl, pl - 1);
-		return;
-	}
-	presorted = 1;
-	for (pm = a + 1; pm < a + n; pm++)
-	{
-		CHECK_FOR_INTERRUPTS();
-		if (cmp_$SUFFIX(pm - 1, pm$CMPPARAMS) > 0)
-		{
-			presorted = 0;
-			break;
-		}
-	}
-	if (presorted)
-		return;
-	pm = a + (n / 2);
-	if (n > 7)
-	{
-		pl = a;
-		pn = a + (n - 1);
-		if (n > 40)
-		{
-			size_t		d = (n / 8);
-
-			pl = med3_$SUFFIX(pl, pl + d, pl + 2 * d$EXTRAPARAMS);
-			pm = med3_$SUFFIX(pm - d, pm, pm + d$EXTRAPARAMS);
-			pn = med3_$SUFFIX(pn - 2 * d, pn - d, pn$EXTRAPARAMS);
-		}
-		pm = med3_$SUFFIX(pl, pm, pn$EXTRAPARAMS);
-	}
-	swap(a, pm);
-	pa = pb = a + 1;
-	pc = pd = a + (n - 1);
-	for (;;)
-	{
-		while (pb <= pc && (r = cmp_$SUFFIX(pb, a$CMPPARAMS)) <= 0)
-		{
-			if (r == 0)
-			{
-				swap(pa, pb);
-				pa++;
-			}
-			pb++;
-			CHECK_FOR_INTERRUPTS();
-		}
-		while (pb <= pc && (r = cmp_$SUFFIX(pc, a$CMPPARAMS)) >= 0)
-		{
-			if (r == 0)
-			{
-				swap(pc, pd);
-				pd--;
-			}
-			pc--;
-			CHECK_FOR_INTERRUPTS();
-		}
-		if (pb > pc)
-			break;
-		swap(pb, pc);
-		pb++;
-		pc--;
-	}
-	pn = a + n;
-	d1 = Min(pa - a, pb - pa);
-	vecswap(a, pb - d1, d1);
-	d1 = Min(pd - pc, pn - pd - 1);
-	vecswap(pb, pn - d1, d1);
-	d1 = pb - pa;
-	d2 = pd - pc;
-	if (d1 <= d2)
-	{
-		/* Recurse on left partition, then iterate on right partition */
-		if (d1 > 1)
-			qsort_$SUFFIX(a, d1$EXTRAPARAMS);
-		if (d2 > 1)
-		{
-			/* Iterate rather than recurse to save stack space */
-			/* qsort_$SUFFIX(pn - d2, d2$EXTRAPARAMS); */
-			a = pn - d2;
-			n = d2;
-			goto loop;
-		}
-	}
-	else
-	{
-		/* Recurse on right partition, then iterate on left partition */
-		if (d2 > 1)
-			qsort_$SUFFIX(pn - d2, d2$EXTRAPARAMS);
-		if (d1 > 1)
-		{
-			/* Iterate rather than recurse to save stack space */
-			/* qsort_$SUFFIX(a, d1$EXTRAPARAMS); */
-			n = d1;
-			goto loop;
-		}
-	}
-}
-EOM
-
-	return;
-}
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index d02e676aa3..ecb9241fad 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -655,8 +655,33 @@ static void free_sort_tuple(Tuplesortstate *state, SortTuple *stup);
  * reduces to ApplySortComparator(), that is single-key MinimalTuple sorts
  * and Datum sorts.
  */
-#include "qsort_tuple.c"
+#define QS_SUFFIX tuple
+#define QS_TYPE SortTuple
+#define QS_EXTRAPARAMS , SortTupleComparator cmp_tuple, Tuplesortstate *state
+#define QS_EXTRAARGS , cmp_tuple, state
+#define QS_CMPARGS , state
+#define QS_CHECK_FOR_INTERRUPTS
+#define QS_CHECK_PRESORTED
+#define QS_SCOPE static
+#define QS_DEFINE
+#include "lib/qsort_template.h"
 
+#define QS_SUFFIX ssup
+#define QS_TYPE SortTuple
+#define QS_EXTRAPARAMS , SortSupport ssup
+#define QS_EXTRAARGS , ssup
+#define QS_CMPARGS , ssup
+#define QS_CHECK_FOR_INTERRUPTS
+#define QS_CHECK_PRESORTED
+#define QS_SCOPE static
+static inline int
+cmp_ssup(SortTuple *a, SortTuple *b, SortSupport ssup)
+{
+	return ApplySortComparator(a->datum1, a->isnull1,
+							   b->datum1, b->isnull1, ssup);
+}
+#define QS_DEFINE
+#include "lib/qsort_template.h"
 
 /*
  *		tuplesort_begin_xxx
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 909bded592..7cfd0fe863 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -668,6 +668,16 @@ sub GenerateFiles
 		);
 	}
 
+	if (IsNewer(
+			'src/backend/utils/sort/qsort_tuple.c',
+			'src/backend/utils/sort/gen_qsort_tuple.pl'))
+	{
+		print "Generating qsort_tuple.c...\n";
+		system(
+'perl src/backend/utils/sort/gen_qsort_tuple.pl > src/backend/utils/sort/qsort_tuple.c'
+		);
+	}
+
 	if (IsNewer(
 			'src/interfaces/libpq/libpq.rc',
 			'src/interfaces/libpq/libpq.rc.in'))
diff --git a/src/tools/msvc/clean.bat b/src/tools/msvc/clean.bat
index d034ec5765..b1eb759f52 100755
--- a/src/tools/msvc/clean.bat
+++ b/src/tools/msvc/clean.bat
@@ -61,7 +61,6 @@ if %DIST%==1 if exist src\backend\storage\lmgr\lwlocknames.h del /q src\backend\
 if %DIST%==1 if exist src\pl\plpython\spiexceptions.h del /q src\pl\plpython\spiexceptions.h
 if %DIST%==1 if exist src\pl\plpgsql\src\plerrcodes.h del /q src\pl\plpgsql\src\plerrcodes.h
 if %DIST%==1 if exist src\pl\tcl\pltclerrcodes.h del /q src\pl\tcl\pltclerrcodes.h
-if %DIST%==1 if exist src\backend\utils\sort\qsort_tuple.c del /q src\backend\utils\sort\qsort_tuple.c
 if %DIST%==1 if exist src\bin\psql\sql_help.c del /q src\bin\psql\sql_help.c
 if %DIST%==1 if exist src\bin\psql\sql_help.h del /q src\bin\psql\sql_help.h
 if %DIST%==1 if exist src\common\kwlist_d.h del /q src\common\kwlist_d.h
-- 
2.17.1

