public inbox for [email protected]  
help / color / mirror / Atom feed
A qsort template
55+ messages / 13 participants
[nested] [flat]

* A qsort template
@ 2021-02-18 03:09  Thomas Munro <[email protected]>
  0 siblings, 2 replies; 55+ messages in thread

From: Thomas Munro @ 2021-02-18 03:09 UTC (permalink / raw)
  To: pgsql-hackers

Hello,

In another thread[1], I proposed $SUBJECT, but then we found a better
solution to that thread's specific problem.  The general idea is still
good though: it's possible to (1) replace several existing copies of
our qsort algorithm with one, and (2) make new specialised versions a
bit more easily than the existing Perl generator allows.  So, I'm back
with a rebased stack of patches.  I'll leave specific cases for new
worthwhile specialisations for separate proposals; I've heard about
several.

[1] https://www.postgresql.org/message-id/flat/CA%2BhUKGKMQFVpjr106gRhwk6R-nXv0qOcTreZuQzxgpHESAL6dw%40m...


Attachments:

  [text/x-patch] 0001-Add-sort_template.h-for-making-fast-sort-functions.patch (13.8K, ../../CA+hUKGJ2-eaDqAum5bxhpMNhvuJmRDZxB_Tow0n-gse+HG0Yig@mail.gmail.com/2-0001-Add-sort_template.h-for-making-fast-sort-functions.patch)
  download | inline diff:
From edfa27a45eca139d683bd0a02136e1da5a489243 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Mon, 17 Aug 2020 21:31:56 +1200
Subject: [PATCH 1/3] Add sort_template.h for making fast sort functions.

Move our qsort implementation into a header that can be used to
define specialized functions for better performance.
---
 src/include/lib/sort_template.h | 431 ++++++++++++++++++++++++++++++++
 1 file changed, 431 insertions(+)
 create mode 100644 src/include/lib/sort_template.h

diff --git a/src/include/lib/sort_template.h b/src/include/lib/sort_template.h
new file mode 100644
index 0000000000..09523a8e4c
--- /dev/null
+++ b/src/include/lib/sort_template.h
@@ -0,0 +1,431 @@
+/*-------------------------------------------------------------------------
+ *
+ * sort_template.h
+ *
+ *	  A template for a sort algorithm that supports varying degrees of
+ *	  specialization.
+ *
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1992-1994, Regents of the University of California
+ *
+ * Usage notes:
+ *
+ *	  To generate functions specialized for a type, the following parameter
+ *	  macros should be #define'd before this file is included.
+ *
+ *	  - ST_SORT - the name of a sort function to be generated
+ *	  - ST_ELEMENT_TYPE - type of the referenced elements
+ *	  - ST_DECLARE - if defined the functions and types are declared
+ *	  - ST_DEFINE - if defined the functions and types are defined
+ *	  - ST_SCOPE - scope (e.g. extern, static inline) for functions
+ *
+ *	  Instead of ST_ELEMENT_TYPE, ST_ELEMENT_TYPE_VOID can be defined.  Then
+ *	  the generated functions will automatically gain an "element_size"
+ *	  parameter.  This allows us to generate a traditional qsort function.
+ *
+ *	  One of the following macros must be defined, to show how to compare
+ *	  elements.  The first two options are arbitrary expressions depending
+ *	  on whether an extra pass-through argument is desired, and the third
+ *	  option should be defined if the sort function should receive a
+ *	  function pointer at runtime.
+ *
+ * 	  - ST_COMPARE(a, b) - a simple comparison expression
+ *	  - ST_COMPARE(a, b, arg) - variant that takes an extra argument
+ *	  - ST_COMPARE_RUNTIME_POINTER - sort function takes a function pointer
+ *
+ *	  To say that the comparator and therefore also sort function should
+ *	  receive an extra pass-through argument, specify the type of the
+ *	  argument.
+ *
+ *	  - ST_COMPARE_ARG_TYPE - type of extra argument
+ *
+ *	  The prototype of the generated sort function is:
+ *
+ *	  void ST_SORT(ST_ELEMENT_TYPE *data, size_t n,
+ *				   [size_t element_size,]
+ *				   [ST_SORT_compare_function compare,]
+ *				   [ST_COMPARE_ARG_TYPE *arg]);
+ *
+ *	  ST_SORT_compare_function is a function pointer of the following type:
+ *
+ *	  int (*)(const ST_ELEMENT_TYPE *a, const ST_ELEMENT_TYPE *b,
+ *			  [ST_COMPARE_ARG_TYPE *arg])
+ *
+ * HISTORY
+ *
+ *	  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
+ *
+ * IDENTIFICATION
+ *		src/include/lib/sort_template.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*	  $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.
+ */
+
+#define ST_MAKE_PREFIX(a) CppConcat(a,_)
+#define ST_MAKE_NAME(a,b) ST_MAKE_NAME_(ST_MAKE_PREFIX(a),b)
+#define ST_MAKE_NAME_(a,b) CppConcat(a,b)
+
+/*
+ * If the element type is void, we'll also need an element_size argument
+ * because we don't know the size.
+ */
+#ifdef ST_ELEMENT_TYPE_VOID
+#define ST_ELEMENT_TYPE void
+#define ST_SORT_PROTO_ELEMENT_SIZE , size_t element_size
+#define ST_SORT_INVOKE_ELEMENT_SIZE , element_size
+#else
+#define ST_SORT_PROTO_ELEMENT_SIZE
+#define ST_SORT_INVOKE_ELEMENT_SIZE
+#endif
+
+/*
+ * If the user wants to be able to pass in compare functions at runtime,
+ * we'll need to make that an argument of the sort and med3 functions.
+ */
+#ifdef ST_COMPARE_RUNTIME_POINTER
+/*
+ * The type of the comparator function pointer that ST_SORT will take, unless
+ * you've already declared a type name manually and want to use that instead of
+ * having a new one defined.
+ */
+#ifndef ST_COMPARATOR_TYPE_NAME
+#define ST_COMPARATOR_TYPE_NAME ST_MAKE_NAME(ST_SORT, compare_function)
+#endif
+#define ST_COMPARE compare
+#ifndef ST_COMPARE_ARG_TYPE
+#define ST_SORT_PROTO_COMPARE , ST_COMPARATOR_TYPE_NAME compare
+#define ST_SORT_INVOKE_COMPARE , compare
+#else
+#define ST_SORT_PROTO_COMPARE , ST_COMPARATOR_TYPE_NAME compare
+#define ST_SORT_INVOKE_COMPARE , compare
+#endif
+#else
+#define ST_SORT_PROTO_COMPARE
+#define ST_SORT_INVOKE_COMPARE
+#endif
+
+/*
+ * If the user wants to use a compare function or expression that takes an
+ * extra argument, we'll need to make that an argument of the sort, compare and
+ * med3 functions.
+ */
+#ifdef ST_COMPARE_ARG_TYPE
+#define ST_SORT_PROTO_ARG , ST_COMPARE_ARG_TYPE *arg
+#define ST_SORT_INVOKE_ARG , arg
+#else
+#define ST_SORT_PROTO_ARG
+#define ST_SORT_INVOKE_ARG
+#endif
+
+#ifdef ST_DECLARE
+
+#ifdef ST_COMPARE_RUNTIME_POINTER
+typedef int (*ST_COMPARATOR_TYPE_NAME)(const ST_ELEMENT_TYPE *,
+									   const ST_ELEMENT_TYPE *
+									   ST_SORT_PROTO_ARG);
+#endif
+
+/* Declare the sort function.  Note optional arguments at end. */
+ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE *first, size_t n
+					  ST_SORT_PROTO_ELEMENT_SIZE
+					  ST_SORT_PROTO_COMPARE
+					  ST_SORT_PROTO_ARG);
+
+#endif
+
+#ifdef ST_DEFINE
+
+/* sort private helper functions */
+#define ST_MED3 ST_MAKE_NAME(ST_SORT, med3)
+#define ST_SWAP ST_MAKE_NAME(ST_SORT, swap)
+#define ST_SWAPN ST_MAKE_NAME(ST_SORT, swapn)
+
+/* Users expecting to run very large sorts may need them to be interruptible. */
+#ifdef ST_CHECK_FOR_INTERRUPTS
+#define DO_CHECK_FOR_INTERRUPTS() CHECK_FOR_INTERRUPTS()
+#else
+#define DO_CHECK_FOR_INTERRUPTS()
+#endif
+
+/*
+ * Create wrapper macros that know how to invoke compare, med3 and sort with
+ * the right arguments.
+ */
+#ifdef ST_COMPARE_RUNTIME_POINTER
+#define DO_COMPARE(a_, b_) ST_COMPARE((a_), (b_) ST_SORT_INVOKE_ARG)
+#elif defined(ST_COMPARE_ARG_TYPE)
+#define DO_COMPARE(a_, b_) ST_COMPARE((a_), (b_), arg)
+#else
+#define DO_COMPARE(a_, b_) ST_COMPARE((a_), (b_))
+#endif
+#define DO_MED3(a_, b_, c_)												\
+	ST_MED3((a_), (b_), (c_)											\
+			ST_SORT_INVOKE_COMPARE										\
+			ST_SORT_INVOKE_ARG)
+#define DO_SORT(a_, n_)													\
+	ST_SORT((a_), (n_)													\
+			ST_SORT_INVOKE_ELEMENT_SIZE									\
+			ST_SORT_INVOKE_COMPARE										\
+			ST_SORT_INVOKE_ARG)
+
+/*
+ * If we're working with void pointers, we'll use pointer arithmetic based on
+ * uint8, and use the runtime element_size to step through the array and swap
+ * elements.  Otherwise we'll work with ST_ELEMENT_TYPE.
+ */
+#ifndef ST_ELEMENT_TYPE_VOID
+#define ST_POINTER_TYPE ST_ELEMENT_TYPE
+#define ST_POINTER_STEP 1
+#define DO_SWAPN(a_, b_, n_) ST_SWAPN((a_), (b_), (n_))
+#define DO_SWAP(a_, b_) ST_SWAP((a_), (b_))
+#else
+#define ST_POINTER_TYPE uint8
+#define ST_POINTER_STEP element_size
+#define DO_SWAPN(a_, b_, n_) ST_SWAPN((a_), (b_), (n_))
+#define DO_SWAP(a_, b_) DO_SWAPN((a_), (b_), element_size)
+#endif
+
+/*
+ * Find the median of three values.  Currently, performance seems to be best
+ * if the 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_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));
+}
+
+static inline void
+ST_SWAP(ST_POINTER_TYPE *a, ST_POINTER_TYPE *b)
+{
+	ST_POINTER_TYPE tmp = *a;
+
+	*a = *b;
+	*b = tmp;
+}
+
+static inline void
+ST_SWAPN(ST_POINTER_TYPE *a, ST_POINTER_TYPE *b, size_t n)
+{
+	for (size_t i = 0; i < n; ++i)
+		ST_SWAP(&a[i], &b[i]);
+}
+
+/*
+ * Sort an array.
+ */
+ST_SCOPE void
+ST_SORT(ST_ELEMENT_TYPE *data, size_t n
+		ST_SORT_PROTO_ELEMENT_SIZE
+		ST_SORT_PROTO_COMPARE
+		ST_SORT_PROTO_ARG)
+{
+	ST_POINTER_TYPE *a = (ST_POINTER_TYPE *) data,
+			   *pa,
+			   *pb,
+			   *pc,
+			   *pd,
+			   *pl,
+			   *pm,
+			   *pn;
+	size_t		d1,
+				d2;
+	int			r,
+				presorted;
+
+loop:
+	DO_CHECK_FOR_INTERRUPTS();
+	if (n < 7)
+	{
+		for (pm = a + ST_POINTER_STEP; pm < a + n * ST_POINTER_STEP;
+			 pm += ST_POINTER_STEP)
+			for (pl = pm; pl > a && DO_COMPARE(pl - ST_POINTER_STEP, pl) > 0;
+				 pl -= ST_POINTER_STEP)
+				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;
+	pm = a + (n / 2) * ST_POINTER_STEP;
+	if (n > 7)
+	{
+		pl = a;
+		pn = a + (n - 1) * ST_POINTER_STEP;
+		if (n > 40)
+		{
+			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);
+		}
+		pm = DO_MED3(pl, pm, pn);
+	}
+	DO_SWAP(a, pm);
+	pa = pb = a + ST_POINTER_STEP;
+	pc = pd = a + (n - 1) * ST_POINTER_STEP;
+	for (;;)
+	{
+		while (pb <= pc && (r = DO_COMPARE(pb, a)) <= 0)
+		{
+			if (r == 0)
+			{
+				DO_SWAP(pa, pb);
+				pa += ST_POINTER_STEP;
+			}
+			pb += ST_POINTER_STEP;
+			DO_CHECK_FOR_INTERRUPTS();
+		}
+		while (pb <= pc && (r = DO_COMPARE(pc, a)) >= 0)
+		{
+			if (r == 0)
+			{
+				DO_SWAP(pc, pd);
+				pd -= ST_POINTER_STEP;
+			}
+			pc -= ST_POINTER_STEP;
+			DO_CHECK_FOR_INTERRUPTS();
+		}
+		if (pb > pc)
+			break;
+		DO_SWAP(pb, pc);
+		pb += ST_POINTER_STEP;
+		pc -= ST_POINTER_STEP;
+	}
+	pn = a + n * ST_POINTER_STEP;
+	d1 = Min(pa - a, pb - pa);
+	DO_SWAPN(a, pb - d1, d1);
+	d1 = Min(pd - pc, pn - pd - ST_POINTER_STEP);
+	DO_SWAPN(pb, pn - d1, d1);
+	d1 = pb - pa;
+	d2 = pd - pc;
+	if (d1 <= d2)
+	{
+		/* Recurse on left partition, then iterate on right partition */
+		if (d1 > ST_POINTER_STEP)
+			DO_SORT(a, d1 / ST_POINTER_STEP);
+		if (d2 > ST_POINTER_STEP)
+		{
+			/* Iterate rather than recurse to save stack space */
+			/* DO_SORT(pn - d2, d2 / ST_POINTER_STEP) */
+			a = pn - d2;
+			n = d2 / ST_POINTER_STEP;
+			goto loop;
+		}
+	}
+	else
+	{
+		/* Recurse on right partition, then iterate on left partition */
+		if (d2 > ST_POINTER_STEP)
+			DO_SORT(pn - d2, d2 / ST_POINTER_STEP);
+		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;
+			goto loop;
+		}
+	}
+}
+#endif
+
+#undef DO_CHECK_FOR_INTERRUPTS
+#undef DO_COMPARE
+#undef DO_MED3
+#undef DO_SORT
+#undef DO_SWAP
+#undef DO_SWAPN
+#undef ST_COMPARATOR_TYPE_NAME
+#undef ST_COMPARE
+#undef ST_COMPARE_ARG_TYPE
+#undef ST_COMPARE_RUNTIME_POINTER
+#undef ST_ELEMENT_TYPE
+#undef ST_ELEMENT_TYPE_VOID
+#undef ST_MAKE_NAME
+#undef ST_MAKE_NAME_
+#undef ST_MAKE_PREFIX
+#undef ST_MED3
+#undef ST_POINTER_STEP
+#undef ST_POINTER_TYPE
+#undef ST_SCOPE
+#undef ST_SORT
+#undef ST_SORT_INVOKE_ARG
+#undef ST_SORT_INVOKE_COMPARE
+#undef ST_SORT_INVOKE_ELEMENT_SIZE
+#undef ST_SORT_PROTO_ARG
+#undef ST_SORT_PROTO_COMPARE
+#undef ST_SORT_PROTO_ELEMENT_SIZE
+#undef ST_SWAP
+#undef ST_SWAPN
-- 
2.30.0



  [text/x-patch] 0002-Use-sort_template.h-for-qsort-and-qsort_arg.patch (13.9K, ../../CA+hUKGJ2-eaDqAum5bxhpMNhvuJmRDZxB_Tow0n-gse+HG0Yig@mail.gmail.com/3-0002-Use-sort_template.h-for-qsort-and-qsort_arg.patch)
  download | inline diff:
From c73967d0ca14e06432949c96e047297c9e9f37c9 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Wed, 19 Aug 2020 19:34:45 +1200
Subject: [PATCH 2/3] Use sort_template.h for qsort() and qsort_arg().

Reduce duplication by using the new template.
---
 src/port/qsort.c     | 227 ++----------------------------------------
 src/port/qsort_arg.c | 228 ++-----------------------------------------
 2 files changed, 15 insertions(+), 440 deletions(-)

diff --git a/src/port/qsort.c b/src/port/qsort.c
index fa992e2081..7879e6cd56 100644
--- a/src/port/qsort.c
+++ b/src/port/qsort.c
@@ -1,229 +1,16 @@
 /*
  *	qsort.c: standard quicksort algorithm
- *
- *	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.
- *
- *	CAUTION: if you change this file, see also qsort_arg.c, gen_qsort_tuple.pl
- *
- *	src/port/qsort.c
- */
-
-/*	$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.
  */
 
 #include "c.h"
 
-
-static char *med3(char *a, char *b, char *c,
-				  int (*cmp) (const void *, const void *));
-static void swapfunc(char *, char *, size_t, int);
-
-/*
- * 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.
- */
-
-#define swapcode(TYPE, parmi, parmj, n) \
-do {		\
-	size_t i = (n) / sizeof (TYPE);			\
-	TYPE *pi = (TYPE *)(void *)(parmi);			\
-	TYPE *pj = (TYPE *)(void *)(parmj);			\
-	do {						\
-		TYPE	t = *pi;			\
-		*pi++ = *pj;				\
-		*pj++ = t;				\
-		} while (--i > 0);				\
-} while (0)
-
-#define SWAPINIT(a, es) swaptype = ((char *)(a) - (char *)0) % sizeof(long) || \
-	(es) % sizeof(long) ? 2 : (es) == sizeof(long)? 0 : 1
-
-static void
-swapfunc(char *a, char *b, size_t n, int swaptype)
-{
-	if (swaptype <= 1)
-		swapcode(long, a, b, n);
-	else
-		swapcode(char, a, b, n);
-}
-
-#define swap(a, b)						\
-	if (swaptype == 0) {					\
-		long t = *(long *)(void *)(a);			\
-		*(long *)(void *)(a) = *(long *)(void *)(b);	\
-		*(long *)(void *)(b) = t;			\
-	} else							\
-		swapfunc(a, b, es, swaptype)
-
-#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
-
-static char *
-med3(char *a, char *b, char *c, int (*cmp) (const void *, const void *))
-{
-	return cmp(a, b) < 0 ?
-		(cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a))
-		: (cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c));
-}
-
-void
-pg_qsort(void *a, size_t n, size_t es, int (*cmp) (const void *, const void *))
-{
-	char	   *pa,
-			   *pb,
-			   *pc,
-			   *pd,
-			   *pl,
-			   *pm,
-			   *pn;
-	size_t		d1,
-				d2;
-	int			r,
-				swaptype,
-				presorted;
-
-loop:SWAPINIT(a, es);
-	if (n < 7)
-	{
-		for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
-			for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
-				 pl -= es)
-				swap(pl, pl - es);
-		return;
-	}
-	presorted = 1;
-	for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
-	{
-		if (cmp(pm - es, pm) > 0)
-		{
-			presorted = 0;
-			break;
-		}
-	}
-	if (presorted)
-		return;
-	pm = (char *) a + (n / 2) * es;
-	if (n > 7)
-	{
-		pl = (char *) a;
-		pn = (char *) a + (n - 1) * es;
-		if (n > 40)
-		{
-			size_t		d = (n / 8) * es;
-
-			pl = med3(pl, pl + d, pl + 2 * d, cmp);
-			pm = med3(pm - d, pm, pm + d, cmp);
-			pn = med3(pn - 2 * d, pn - d, pn, cmp);
-		}
-		pm = med3(pl, pm, pn, cmp);
-	}
-	swap(a, pm);
-	pa = pb = (char *) a + es;
-	pc = pd = (char *) a + (n - 1) * es;
-	for (;;)
-	{
-		while (pb <= pc && (r = cmp(pb, a)) <= 0)
-		{
-			if (r == 0)
-			{
-				swap(pa, pb);
-				pa += es;
-			}
-			pb += es;
-		}
-		while (pb <= pc && (r = cmp(pc, a)) >= 0)
-		{
-			if (r == 0)
-			{
-				swap(pc, pd);
-				pd -= es;
-			}
-			pc -= es;
-		}
-		if (pb > pc)
-			break;
-		swap(pb, pc);
-		pb += es;
-		pc -= es;
-	}
-	pn = (char *) a + n * es;
-	d1 = Min(pa - (char *) a, pb - pa);
-	vecswap(a, pb - d1, d1);
-	d1 = Min(pd - pc, pn - pd - es);
-	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 > es)
-			pg_qsort(a, d1 / es, es, cmp);
-		if (d2 > es)
-		{
-			/* Iterate rather than recurse to save stack space */
-			/* pg_qsort(pn - d2, d2 / es, es, cmp); */
-			a = pn - d2;
-			n = d2 / es;
-			goto loop;
-		}
-	}
-	else
-	{
-		/* Recurse on right partition, then iterate on left partition */
-		if (d2 > es)
-			pg_qsort(pn - d2, d2 / es, es, cmp);
-		if (d1 > es)
-		{
-			/* Iterate rather than recurse to save stack space */
-			/* pg_qsort(a, d1 / es, es, cmp); */
-			n = d1 / es;
-			goto loop;
-		}
-	}
-}
+#define ST_SORT pg_qsort
+#define ST_ELEMENT_TYPE_VOID
+#define ST_COMPARE_RUNTIME_POINTER
+#define ST_SCOPE
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
 
 /*
  * qsort comparator wrapper for strcmp.
diff --git a/src/port/qsort_arg.c b/src/port/qsort_arg.c
index 6d54fbc2b4..fa7e11a3b8 100644
--- a/src/port/qsort_arg.c
+++ b/src/port/qsort_arg.c
@@ -1,226 +1,14 @@
 /*
  *	qsort_arg.c: qsort with a passthrough "void *" argument
- *
- *	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.
- *
- *	CAUTION: if you change this file, see also qsort.c, gen_qsort_tuple.pl
- *
- *	src/port/qsort_arg.c
- */
-
-/*	$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.
  */
 
 #include "c.h"
 
-
-static char *med3(char *a, char *b, char *c,
-				  qsort_arg_comparator cmp, void *arg);
-static void swapfunc(char *, char *, size_t, int);
-
-/*
- * 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.
- */
-
-#define swapcode(TYPE, parmi, parmj, n) \
-do {		\
-	size_t i = (n) / sizeof (TYPE);			\
-	TYPE *pi = (TYPE *)(void *)(parmi);			\
-	TYPE *pj = (TYPE *)(void *)(parmj);			\
-	do {						\
-		TYPE	t = *pi;			\
-		*pi++ = *pj;				\
-		*pj++ = t;				\
-		} while (--i > 0);				\
-} while (0)
-
-#define SWAPINIT(a, es) swaptype = ((char *)(a) - (char *)0) % sizeof(long) || \
-	(es) % sizeof(long) ? 2 : (es) == sizeof(long)? 0 : 1
-
-static void
-swapfunc(char *a, char *b, size_t n, int swaptype)
-{
-	if (swaptype <= 1)
-		swapcode(long, a, b, n);
-	else
-		swapcode(char, a, b, n);
-}
-
-#define swap(a, b)						\
-	if (swaptype == 0) {					\
-		long t = *(long *)(void *)(a);			\
-		*(long *)(void *)(a) = *(long *)(void *)(b);	\
-		*(long *)(void *)(b) = t;			\
-	} else							\
-		swapfunc(a, b, es, swaptype)
-
-#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
-
-static char *
-med3(char *a, char *b, char *c, qsort_arg_comparator cmp, void *arg)
-{
-	return cmp(a, b, arg) < 0 ?
-		(cmp(b, c, arg) < 0 ? b : (cmp(a, c, arg) < 0 ? c : a))
-		: (cmp(b, c, arg) > 0 ? b : (cmp(a, c, arg) < 0 ? a : c));
-}
-
-void
-qsort_arg(void *a, size_t n, size_t es, qsort_arg_comparator cmp, void *arg)
-{
-	char	   *pa,
-			   *pb,
-			   *pc,
-			   *pd,
-			   *pl,
-			   *pm,
-			   *pn;
-	size_t		d1,
-				d2;
-	int			r,
-				swaptype,
-				presorted;
-
-loop:SWAPINIT(a, es);
-	if (n < 7)
-	{
-		for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
-			for (pl = pm; pl > (char *) a && cmp(pl - es, pl, arg) > 0;
-				 pl -= es)
-				swap(pl, pl - es);
-		return;
-	}
-	presorted = 1;
-	for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
-	{
-		if (cmp(pm - es, pm, arg) > 0)
-		{
-			presorted = 0;
-			break;
-		}
-	}
-	if (presorted)
-		return;
-	pm = (char *) a + (n / 2) * es;
-	if (n > 7)
-	{
-		pl = (char *) a;
-		pn = (char *) a + (n - 1) * es;
-		if (n > 40)
-		{
-			size_t		d = (n / 8) * es;
-
-			pl = med3(pl, pl + d, pl + 2 * d, cmp, arg);
-			pm = med3(pm - d, pm, pm + d, cmp, arg);
-			pn = med3(pn - 2 * d, pn - d, pn, cmp, arg);
-		}
-		pm = med3(pl, pm, pn, cmp, arg);
-	}
-	swap(a, pm);
-	pa = pb = (char *) a + es;
-	pc = pd = (char *) a + (n - 1) * es;
-	for (;;)
-	{
-		while (pb <= pc && (r = cmp(pb, a, arg)) <= 0)
-		{
-			if (r == 0)
-			{
-				swap(pa, pb);
-				pa += es;
-			}
-			pb += es;
-		}
-		while (pb <= pc && (r = cmp(pc, a, arg)) >= 0)
-		{
-			if (r == 0)
-			{
-				swap(pc, pd);
-				pd -= es;
-			}
-			pc -= es;
-		}
-		if (pb > pc)
-			break;
-		swap(pb, pc);
-		pb += es;
-		pc -= es;
-	}
-	pn = (char *) a + n * es;
-	d1 = Min(pa - (char *) a, pb - pa);
-	vecswap(a, pb - d1, d1);
-	d1 = Min(pd - pc, pn - pd - es);
-	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 > es)
-			qsort_arg(a, d1 / es, es, cmp, arg);
-		if (d2 > es)
-		{
-			/* Iterate rather than recurse to save stack space */
-			/* qsort_arg(pn - d2, d2 / es, es, cmp, arg); */
-			a = pn - d2;
-			n = d2 / es;
-			goto loop;
-		}
-	}
-	else
-	{
-		/* Recurse on right partition, then iterate on left partition */
-		if (d2 > es)
-			qsort_arg(pn - d2, d2 / es, es, cmp, arg);
-		if (d1 > es)
-		{
-			/* Iterate rather than recurse to save stack space */
-			/* qsort_arg(a, d1 / es, es, cmp, arg); */
-			n = d1 / es;
-			goto loop;
-		}
-	}
-}
+#define ST_SORT qsort_arg
+#define ST_ELEMENT_TYPE_VOID
+#define ST_COMPARATOR_TYPE_NAME qsort_arg_comparator
+#define ST_COMPARE_RUNTIME_POINTER
+#define ST_COMPARE_ARG_TYPE void
+#define ST_SCOPE
+#define ST_DEFINE
+#include "lib/sort_template.h"
-- 
2.30.0



  [text/x-patch] 0003-Use-sort_template.h-for-qsort_tuple-and-qsort_ssup.patch (12.2K, ../../CA+hUKGJ2-eaDqAum5bxhpMNhvuJmRDZxB_Tow0n-gse+HG0Yig@mail.gmail.com/4-0003-Use-sort_template.h-for-qsort_tuple-and-qsort_ssup.patch)
  download | inline diff:
From 53f717b74926bd812acf0985c1ffb42a8e2780ae Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Wed, 19 Aug 2020 20:25:12 +1200
Subject: [PATCH 3/3] Use sort_template.h for qsort_tuple() and qsort_ssup().

Replace the Perl code the previously generated specialized sort
functions with an instantiation of sort_template.h.
---
 src/backend/Makefile                      |   4 +-
 src/backend/utils/sort/.gitignore         |   1 -
 src/backend/utils/sort/Makefile           |   8 -
 src/backend/utils/sort/gen_qsort_tuple.pl | 272 ----------------------
 src/backend/utils/sort/tuplesort.c        |  21 +-
 src/tools/msvc/Solution.pm                |  10 -
 src/tools/msvc/clean.bat                  |   1 -
 7 files changed, 21 insertions(+), 296 deletions(-)
 delete mode 100644 src/backend/utils/sort/.gitignore
 delete mode 100644 src/backend/utils/sort/gen_qsort_tuple.pl

diff --git a/src/backend/Makefile b/src/backend/Makefile
index 9672e2cb43..0da848b1fd 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -190,7 +190,6 @@ distprep:
 	$(MAKE) -C utils	distprep
 	$(MAKE) -C utils/adt	jsonpath_gram.c jsonpath_scan.c
 	$(MAKE) -C utils/misc	guc-file.c
-	$(MAKE) -C utils/sort	qsort_tuple.c
 
 
 ##########################################################################
@@ -312,8 +311,7 @@ maintainer-clean: distclean
 	      storage/lmgr/lwlocknames.h \
 	      utils/adt/jsonpath_gram.c \
 	      utils/adt/jsonpath_scan.c \
-	      utils/misc/guc-file.c \
-	      utils/sort/qsort_tuple.c
+	      utils/misc/guc-file.c
 
 
 ##########################################################################
diff --git a/src/backend/utils/sort/.gitignore b/src/backend/utils/sort/.gitignore
deleted file mode 100644
index f2958633e6..0000000000
--- a/src/backend/utils/sort/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/qsort_tuple.c
diff --git a/src/backend/utils/sort/Makefile b/src/backend/utils/sort/Makefile
index 7ac3659261..26f65fcaf7 100644
--- a/src/backend/utils/sort/Makefile
+++ b/src/backend/utils/sort/Makefile
@@ -21,12 +21,4 @@ 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 4c305806c7..0000000000
--- a/src/backend/utils/sort/gen_qsort_tuple.pl
+++ /dev/null
@@ -1,272 +0,0 @@
-#!/usr/bin/perl
-
-#
-# 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;
-use warnings;
-
-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 7d0f96afb7..22972071ff 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -676,8 +676,27 @@ static void tuplesort_updatemax(Tuplesortstate *state);
  * reduces to ApplySortComparator(), that is single-key MinimalTuple sorts
  * and Datum sorts.
  */
-#include "qsort_tuple.c"
 
+#define ST_SORT qsort_tuple
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE_RUNTIME_POINTER
+#define ST_COMPARE_ARG_TYPE Tuplesortstate
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+#define ST_SORT qsort_ssup
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE(a, b, ssup) \
+	ApplySortComparator((a)->datum1, (a)->isnull1, \
+						(b)->datum1, (b)->isnull1, (ssup))
+#define ST_COMPARE_ARG_TYPE SortSupportData
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
 
 /*
  *		tuplesort_begin_xxx
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 2aa062b2c9..69b08591cc 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -665,16 +665,6 @@ 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/bin/psql/sql_help.h', 'src/bin/psql/create_help.pl'))
 	{
 		print "Generating sql_help.h...\n";
diff --git a/src/tools/msvc/clean.bat b/src/tools/msvc/clean.bat
index d0d79a0932..0cc91e7d6c 100755
--- a/src/tools/msvc/clean.bat
+++ b/src/tools/msvc/clean.bat
@@ -62,7 +62,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.30.0



^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-02-18 06:02  Andres Freund <[email protected]>
  parent: Thomas Munro <[email protected]>
  1 sibling, 0 replies; 55+ messages in thread

From: Andres Freund @ 2021-02-18 06:02 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: pgsql-hackers

Hi,

On 2021-02-18 16:09:49 +1300, Thomas Munro wrote:
> In another thread[1], I proposed $SUBJECT, but then we found a better
> solution to that thread's specific problem.  The general idea is still
> good though: it's possible to (1) replace several existing copies of
> our qsort algorithm with one, and (2) make new specialised versions a
> bit more easily than the existing Perl generator allows.  So, I'm back
> with a rebased stack of patches.  I'll leave specific cases for new
> worthwhile specialisations for separate proposals; I've heard about
> several.

One place that could benefit is the qsort that BufferSync() does at the
start. I tried your patch for that, and it does reduce the sort time
considerably. For 64GB of mostly dirty shared_buffers from ~1.4s to
0.6s.

Now, obviously one can argue that that's not going to be the crucial
spot, and wouldn't be entirely wrong. OTOH, in my AIO branch I see
checkpointer doing ~10GB/s, leading to the sort being a measurable
portion of the overall time.

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-03-02 21:25  Daniel Gustafsson <[email protected]>
  parent: Thomas Munro <[email protected]>
  1 sibling, 1 reply; 55+ messages in thread

From: Daniel Gustafsson @ 2021-03-02 21:25 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: pgsql-hackers

> On 18 Feb 2021, at 04:09, Thomas Munro <[email protected]> wrote:

> In another thread[1], I proposed $SUBJECT, but then we found a better
> solution to that thread's specific problem.  The general idea is still
> good though: it's possible to (1) replace several existing copies of
> our qsort algorithm with one, and (2) make new specialised versions a
> bit more easily than the existing Perl generator allows.  So, I'm back
> with a rebased stack of patches.  I'll leave specific cases for new
> worthwhile specialisations for separate proposals; I've heard about
> several.

Just to play around with this while reviewing I made a qsort_strcmp, like in
the attached, and tested it using a ~9M word [0] randomly shuffled wordlist.
While being too small input to make any meaningful difference in runtime (it
shaved a hair off but it might well be within the error margin) there was no
regression either.  More importantly, it was really simple and quick to make a
tailored qsort which is the intention with the patch.  While still being a bit
of magic, moving from the Perl generator makes this slightly less magic IMO so
+1 on this approach.

A tiny nitpick on the patch itself:

+ *       - ST_COMPARE(a, b) - a simple comparison expression
+ *   - ST_COMPARE(a, b, arg) - variant that takes an extra argument
Indentation.

All tests pass and the documentation in the the sort_template.h is enough to go
on, but I would prefer to see a comment in port/qsort.c referring back to
sort_template.h for documentation.

--
Daniel Gustafsson		https://vmware.com/

[0] https://github.com/dwyl/english-words/ shuffled 20 times over



Attachments:

  [application/octet-stream] qsort_tmpl_strcmp-patch (2.9K, ../../[email protected]/2-qsort_tmpl_strcmp-patch)
  download

^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-03-03 04:17  Thomas Munro <[email protected]>
  parent: Daniel Gustafsson <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Thomas Munro @ 2021-03-03 04:17 UTC (permalink / raw)
  To: Daniel Gustafsson <[email protected]>; +Cc: pgsql-hackers

On Wed, Mar 3, 2021 at 10:25 AM Daniel Gustafsson <[email protected]> wrote:
> > On 18 Feb 2021, at 04:09, Thomas Munro <[email protected]> wrote:
> > In another thread[1], I proposed $SUBJECT, but then we found a better
> > solution to that thread's specific problem.  The general idea is still
> > good though: it's possible to (1) replace several existing copies of
> > our qsort algorithm with one, and (2) make new specialised versions a
> > bit more easily than the existing Perl generator allows.  So, I'm back
> > with a rebased stack of patches.  I'll leave specific cases for new
> > worthwhile specialisations for separate proposals; I've heard about
> > several.
>
> Just to play around with this while reviewing I made a qsort_strcmp, like in
> the attached, and tested it using a ~9M word [0] randomly shuffled wordlist.
> While being too small input to make any meaningful difference in runtime (it
> shaved a hair off but it might well be within the error margin) there was no
> regression either.  More importantly, it was really simple and quick to make a
> tailored qsort which is the intention with the patch.  While still being a bit
> of magic, moving from the Perl generator makes this slightly less magic IMO so
> +1 on this approach.

Thanks for testing and reviewing!

> A tiny nitpick on the patch itself:
>
> + *       - ST_COMPARE(a, b) - a simple comparison expression
> + *   - ST_COMPARE(a, b, arg) - variant that takes an extra argument
> Indentation.

Fixed.  Also ran pgindent.

> All tests pass and the documentation in the the sort_template.h is enough to go
> on, but I would prefer to see a comment in port/qsort.c referring back to
> sort_template.h for documentation.

I tried adding a comment along the lines "see lib/sort_template.h for
details", but it felt pretty redundant, when the file contains very
little other than #include "lib/sort_template.h" which should already
tell you to go and look there to find out what this is about...

I went ahead and pushed these.

I am sure there are plenty of opportunities to experiment with this
code.  Here are some I recall Peter Geoghegan mentioning:

1.  If you know that elements are unique, you could remove some
branches that deal with equal elements (see "r == 0").
2.  Perhaps you might want to be able to disable the "presorted" check
in some cases?
3.  The parameters 7, 7 and 40 were probably tuned for an ancient Vax
or similar[1].  We see higher insertion sort thesholds such as 27 in
more recent sort algorithms[2] used in eg the JVM.  You could perhaps
speculate that the right answer depends in part on the element size; I
dunno, but if so, here we have that at compile time while traditional
qsort() does not.

As for which cases are actually worth specialising, I've attached the
example that Andres mentioned earlier; it seems like a reasonable
candidate to go ahead and commit too, but I realised that I'd
forgotten to attach it earlier.

It's possible that the existing support sorting tuples could be
further specialised for common sort key data types; I haven't tried
that.

[1] http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.8162&rep=rep1&type=pdf
[2] https://codeblab.com/wp-content/uploads/2009/09/DualPivotQuicksort.pdf


Attachments:

  [text/x-patch] 0001-Specialize-checkpointer-sort-functions.patch (4.0K, ../../CA+hUKGJhOtjQH+wjCodtJzHAFCAPYyt6Qm9E1mUoJph42qo1hg@mail.gmail.com/2-0001-Specialize-checkpointer-sort-functions.patch)
  download | inline diff:
From 4cec5cb9a2e0c50726b7337fb8221281e155c4cd Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Thu, 18 Feb 2021 14:47:28 +1300
Subject: [PATCH] Specialize checkpointer sort functions.

When sorting a potentially large number of dirty buffers, the
checkpointer can benefit from a faster sort routine.  One reported
improvement on a large buffer pool system was 1.4s -> 0.6s.

Discussion: https://postgr.es/m/CA%2BhUKGJ2-eaDqAum5bxhpMNhvuJmRDZxB_Tow0n-gse%2BHG0Yig%40mail.gmail.com
---
 src/backend/storage/buffer/bufmgr.c | 37 +++++++++++++++++------------
 1 file changed, 22 insertions(+), 15 deletions(-)

diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 561c212092..7adec2ddc3 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -488,8 +488,8 @@ static void FindAndDropRelFileNodeBuffers(RelFileNode rnode,
 static void AtProcExit_Buffers(int code, Datum arg);
 static void CheckForBufferLeaks(void);
 static int	rnode_comparator(const void *p1, const void *p2);
-static int	buffertag_comparator(const void *p1, const void *p2);
-static int	ckpt_buforder_comparator(const void *pa, const void *pb);
+static inline int buffertag_comparator(const BufferTag *a, const BufferTag *b);
+static inline int ckpt_buforder_comparator(const CkptSortItem *a, const CkptSortItem *b);
 static int	ts_ckpt_progress_comparator(Datum a, Datum b, void *arg);
 
 
@@ -1831,6 +1831,13 @@ UnpinBuffer(BufferDesc *buf, bool fixOwner)
 	}
 }
 
+#define ST_SORT sort_checkpoint_bufferids
+#define ST_ELEMENT_TYPE CkptSortItem
+#define ST_COMPARE(a, b) ckpt_buforder_comparator(a, b)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
 /*
  * BufferSync -- Write out all dirty buffers in the pool.
  *
@@ -1931,8 +1938,7 @@ BufferSync(int flags)
 	 * end up writing to the tablespaces one-by-one; possibly overloading the
 	 * underlying system.
 	 */
-	qsort(CkptBufferIds, num_to_scan, sizeof(CkptSortItem),
-		  ckpt_buforder_comparator);
+	sort_checkpoint_bufferids(CkptBufferIds, num_to_scan);
 
 	num_spaces = 0;
 
@@ -4595,11 +4601,9 @@ WaitBufHdrUnlocked(BufferDesc *buf)
 /*
  * BufferTag comparator.
  */
-static int
-buffertag_comparator(const void *a, const void *b)
+static inline int
+buffertag_comparator(const BufferTag *ba, const BufferTag *bb)
 {
-	const BufferTag *ba = (const BufferTag *) a;
-	const BufferTag *bb = (const BufferTag *) b;
 	int			ret;
 
 	ret = rnode_comparator(&ba->rnode, &bb->rnode);
@@ -4626,12 +4630,9 @@ buffertag_comparator(const void *a, const void *b)
  * It is important that tablespaces are compared first, the logic balancing
  * writes between tablespaces relies on it.
  */
-static int
-ckpt_buforder_comparator(const void *pa, const void *pb)
+static inline int
+ckpt_buforder_comparator(const CkptSortItem *a, const CkptSortItem *b)
 {
-	const CkptSortItem *a = (const CkptSortItem *) pa;
-	const CkptSortItem *b = (const CkptSortItem *) pb;
-
 	/* compare tablespace */
 	if (a->tsId < b->tsId)
 		return -1;
@@ -4722,6 +4723,13 @@ ScheduleBufferTagForWriteback(WritebackContext *context, BufferTag *tag)
 		IssuePendingWritebacks(context);
 }
 
+#define ST_SORT sort_pending_writebacks
+#define ST_ELEMENT_TYPE PendingWriteback
+#define ST_COMPARE(a, b) buffertag_comparator(&a->tag, &b->tag)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
 /*
  * Issue all pending writeback requests, previously scheduled with
  * ScheduleBufferTagForWriteback, to the OS.
@@ -4741,8 +4749,7 @@ IssuePendingWritebacks(WritebackContext *context)
 	 * Executing the writes in-order can make them a lot faster, and allows to
 	 * merge writeback requests to consecutive blocks into larger writebacks.
 	 */
-	qsort(&context->pending_writebacks, context->nr_pending,
-		  sizeof(PendingWriteback), buffertag_comparator);
+	sort_pending_writebacks(context->pending_writebacks, context->nr_pending);
 
 	/*
 	 * Coalesce neighbouring writes, but nothing else. For that we iterate
-- 
2.30.0



^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-03-11 18:58  Andres Freund <[email protected]>
  parent: Thomas Munro <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Andres Freund @ 2021-03-11 18:58 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: Daniel Gustafsson <[email protected]>; pgsql-hackers

Hi,

I wish we had the same for bsearch... :)


On 2021-03-03 17:17:13 +1300, Thomas Munro wrote:
> As for which cases are actually worth specialising, I've attached the
> example that Andres mentioned earlier; it seems like a reasonable
> candidate to go ahead and commit too, but I realised that I'd
> forgotten to attach it earlier.

> From 4cec5cb9a2e0c50726b7337fb8221281e155c4cd Mon Sep 17 00:00:00 2001
> From: Thomas Munro <[email protected]>
> Date: Thu, 18 Feb 2021 14:47:28 +1300
> Subject: [PATCH] Specialize checkpointer sort functions.
> 
> When sorting a potentially large number of dirty buffers, the
> checkpointer can benefit from a faster sort routine.  One reported
> improvement on a large buffer pool system was 1.4s -> 0.6s.
> 
> Discussion: https://postgr.es/m/CA%2BhUKGJ2-eaDqAum5bxhpMNhvuJmRDZxB_Tow0n-gse%2BHG0Yig%40mail.gmail.com

Looks good to me.

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-03-13 02:49  Thomas Munro <[email protected]>
  parent: Andres Freund <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Thomas Munro @ 2021-03-13 02:49 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Daniel Gustafsson <[email protected]>; pgsql-hackers

On Fri, Mar 12, 2021 at 7:58 AM Andres Freund <[email protected]> wrote:
> I wish we had the same for bsearch... :)

Glibc already has the definition of the traditional void-based
function in /usr/include/bits/stdlib-bsearch.h, so the generated code
when the compiler can see the comparator definition is already good in
eg lazy_tid_reaped() and eg some nbtree search routines.  We could
probably expose more trivial comparators in headers to get more of
that, and we could perhaps put our own bsearch definition in a header
for other platforms that didn't think of that...

It might be worth doing type-safe macro templates as well, though (as
I already did in an earlier proposal[1]), just to have nice type safe
code though, not sure, I'm thinking about that...

[1] https://www.postgresql.org/message-id/flat/CA%2BhUKGLY47Cvu62mFDT53Ya0P95cGggcBN6R6aLpx6%3DGm5j%2B1A...





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-03-14 02:35  Thomas Munro <[email protected]>
  parent: Thomas Munro <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Thomas Munro @ 2021-03-14 02:35 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Daniel Gustafsson <[email protected]>; pgsql-hackers

On Sat, Mar 13, 2021 at 3:49 PM Thomas Munro <[email protected]> wrote:
> On Fri, Mar 12, 2021 at 7:58 AM Andres Freund <[email protected]> wrote:
> > I wish we had the same for bsearch... :)
>
> Glibc already has the definition of the traditional void-based
> function in /usr/include/bits/stdlib-bsearch.h, so the generated code
> when the compiler can see the comparator definition is already good in
> eg lazy_tid_reaped() and eg some nbtree search routines.  We could
> probably expose more trivial comparators in headers to get more of
> that, and we could perhaps put our own bsearch definition in a header
> for other platforms that didn't think of that...
>
> It might be worth doing type-safe macro templates as well, though (as
> I already did in an earlier proposal[1]), just to have nice type safe
> code though, not sure, I'm thinking about that...

I remembered a very good reason to do this: the ability to do
branch-free comparators in more places by introducing optional wider
results.  That's good for TIDs (needs 49 bits), and places that want
to "reverse" a traditional comparator (just doing -result on an int
comparator that might theoretically return INT_MIN requires at least
33 bits).  So I rebased the relevant parts of my earlier version, and
went through and wrote a bunch of examples to demonstrate all this
stuff actually working.

There are two categories of change in these patches:

0002-0005: Places that sort/unique/search OIDs, BlockNumbers and TIDs,
which can reuse a small set of typed functions (a few more could be
added, if useful).  See sortitemptr.h and sortscalar.h.  Mostly this
is just a notational improvement, and an excuse to drop a bunch of
duplicated code.  In a few places this might really speed something
important up!  Like VACUUM's lazy_tid_reaped().

0006-0009.  Places where a specialised function is generated for one
special purpose, such as ANALYZE's HeapTuple sort, tidbitmap.c's
pagetable sort,  some places in nbtree code etc.  These may require
some case-by-case research on whether the extra executable size is
worth the speedup, and there are surely more opportunities like that;
I just picked on these arbitrarily.


Attachments:

  [text/x-patch] 0001-Add-bsearch-and-unique-templates-to-sort_template.h.patch (6.6K, ../../CA+hUKGKztHEWm676csTFjYzortziWmOcf8HDss2Zr0muZ2xfEg@mail.gmail.com/2-0001-Add-bsearch-and-unique-templates-to-sort_template.h.patch)
  download | inline diff:
From d0fb306d2720d14be2d46f4ae4198b25a33a95fa Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Sat, 13 Mar 2021 17:29:44 +1300
Subject: [PATCH 1/9] Add bsearch and unique templates to sort_template.h.

Since binary search and uniquify are so closely related to sorting,
let's optionally generate compatible functions at the same time.

Also, optionally support comparators with wider return types.  This will
allow us to do more branchless comparators.

Also, adjust the ST_SORT template to cope with pointer types.
---
 src/include/lib/sort_template.h | 142 ++++++++++++++++++++++++++++----
 1 file changed, 124 insertions(+), 18 deletions(-)

diff --git a/src/include/lib/sort_template.h b/src/include/lib/sort_template.h
index 771c789ced..a6097e1ac5 100644
--- a/src/include/lib/sort_template.h
+++ b/src/include/lib/sort_template.h
@@ -3,7 +3,8 @@
  * sort_template.h
  *
  *	  A template for a sort algorithm that supports varying degrees of
- *	  specialization.
+ *	  specialization.  Also related algorithms for binary search and
+ *	  unique, on sorted arrays.
  *
  * Copyright (c) 2021, PostgreSQL Global Development Group
  * Portions Copyright (c) 1992-1994, Regents of the University of California
@@ -13,7 +14,9 @@
  *	  To generate functions specialized for a type, the following parameter
  *	  macros should be #define'd before this file is included.
  *
- *	  - ST_SORT - the name of a sort function to be generated
+ *	  - ST_SORT - if defined the name of a sort function
+ *	  - ST_UNIQUE - if defined the name of a unique function
+ *	  - ST_SEARCH - if defined the name of a search function
  *	  - ST_ELEMENT_TYPE - type of the referenced elements
  *	  - ST_DECLARE - if defined the functions and types are declared
  *	  - ST_DEFINE - if defined the functions and types are defined
@@ -40,6 +43,10 @@
  *
  *	  - ST_COMPARE_ARG_TYPE - type of extra argument
  *
+ *	  To say that the comparator returns a type other than int, use:
+ *
+ * 	  - ST_COMPARE_TYPE - an integer type
+ *
  *	  The prototype of the generated sort function is:
  *
  *	  void ST_SORT(ST_ELEMENT_TYPE *data, size_t n,
@@ -179,13 +186,35 @@ typedef int (*ST_COMPARATOR_TYPE_NAME) (const ST_ELEMENT_TYPE *,
 										const ST_ELEMENT_TYPE *ST_SORT_PROTO_ARG);
 #endif
 
+#ifdef ST_SORT
 /* Declare the sort function.  Note optional arguments at end. */
-ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE *first, size_t n
+ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE *array,
+					  size_t n
 					  ST_SORT_PROTO_ELEMENT_SIZE
 					  ST_SORT_PROTO_COMPARE
 					  ST_SORT_PROTO_ARG);
+#endif /* ST_SORT */
 
-#endif
+#ifdef ST_SEARCH
+/* Declare the search function. */
+ST_SCOPE ST_ELEMENT_TYPE *ST_SEARCH(ST_ELEMENT_TYPE *value,
+									ST_ELEMENT_TYPE *array,
+									size_t n
+									ST_SORT_PROTO_ELEMENT_SIZE
+									ST_SORT_PROTO_COMPARE
+									ST_SORT_PROTO_ARG);
+#endif /* ST_SEARCH */
+
+#ifdef ST_UNIQUE
+ST_SCOPE size_t
+ST_UNIQUE(ST_ELEMENT_TYPE *array,
+		  size_t n
+		  ST_SORT_PROTO_ELEMENT_SIZE
+		  ST_SORT_PROTO_COMPARE
+		  ST_SORT_PROTO_ARG);
+#endif /* ST_UNIQUE */
+
+#endif /* ST_DECLARE */
 
 #ifdef ST_DEFINE
 
@@ -201,16 +230,20 @@ ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE *first, size_t n
 #define DO_CHECK_FOR_INTERRUPTS()
 #endif
 
+#ifndef ST_COMPARE_TYPE
+#define ST_COMPARE_TYPE int
+#endif
+
 /*
  * Create wrapper macros that know how to invoke compare, med3 and sort with
  * the right arguments.
  */
 #ifdef ST_COMPARE_RUNTIME_POINTER
-#define DO_COMPARE(a_, b_) ST_COMPARE((a_), (b_) ST_SORT_INVOKE_ARG)
+#define DO_COMPARE(a_, b_) ((ST_COMPARE_TYPE) (ST_COMPARE((a_), (b_) ST_SORT_INVOKE_ARG)))
 #elif defined(ST_COMPARE_ARG_TYPE)
-#define DO_COMPARE(a_, b_) ST_COMPARE((a_), (b_), arg)
+#define DO_COMPARE(a_, b_) ((ST_COMPARE_TYPE) (ST_COMPARE((a_), (b_), arg)))
 #else
-#define DO_COMPARE(a_, b_) ST_COMPARE((a_), (b_))
+#define DO_COMPARE(a_, b_) ((ST_COMPARE_TYPE) (ST_COMPARE((a_), (b_))))
 #endif
 #define DO_MED3(a_, b_, c_)												\
 	ST_MED3((a_), (b_), (c_)											\
@@ -239,6 +272,8 @@ ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE *first, size_t n
 #define DO_SWAP(a_, b_) DO_SWAPN((a_), (b_), element_size)
 #endif
 
+#ifdef ST_SORT
+
 /*
  * Find the median of three values.  Currently, performance seems to be best
  * if the the comparator is inlined here, but the med3 function is not inlined
@@ -281,18 +316,18 @@ ST_SORT(ST_ELEMENT_TYPE *data, size_t n
 		ST_SORT_PROTO_COMPARE
 		ST_SORT_PROTO_ARG)
 {
-	ST_POINTER_TYPE *a = (ST_POINTER_TYPE *) data,
-			   *pa,
-			   *pb,
-			   *pc,
-			   *pd,
-			   *pl,
-			   *pm,
-			   *pn;
+	ST_POINTER_TYPE *a = (ST_POINTER_TYPE *) data;
+	ST_POINTER_TYPE *pa;
+	ST_POINTER_TYPE *pb;
+	ST_POINTER_TYPE *pc;
+	ST_POINTER_TYPE *pd;
+	ST_POINTER_TYPE *pl;
+	ST_POINTER_TYPE *pm;
+	ST_POINTER_TYPE *pn;
 	size_t		d1,
 				d2;
-	int			r,
-				presorted;
+	int			presorted;
+	ST_COMPARE_TYPE r;
 
 loop:
 	DO_CHECK_FOR_INTERRUPTS();
@@ -399,7 +434,75 @@ loop:
 		}
 	}
 }
-#endif
+
+#endif /* ST_SORT */
+
+#ifdef ST_SEARCH
+
+/*
+ * Find an element in the array of sorted values that is equal to a given
+ * value, in a sorted array.  Return NULL if there is none.
+ */
+ST_SCOPE ST_ELEMENT_TYPE *
+ST_SEARCH(ST_ELEMENT_TYPE *value,
+		  ST_ELEMENT_TYPE *array,
+		  size_t n
+		  ST_SORT_PROTO_ELEMENT_SIZE
+		  ST_SORT_PROTO_COMPARE
+		  ST_SORT_PROTO_ARG)
+{
+	ssize_t		left = 0,
+				right = n - 1;
+
+	while (left <= right)
+	{
+		size_t		mid = (left + right) / 2;
+		ST_ELEMENT_TYPE *element = &array[mid];
+		ST_COMPARE_TYPE cmp = DO_COMPARE(element, value);
+
+		if (cmp < 0)
+			left = mid + 1;
+		else if (cmp > 0)
+			right = mid - 1;
+		else
+			return element;
+	}
+
+	return NULL;
+}
+
+#endif /* ST_SEARCH */
+
+#ifdef ST_UNIQUE
+
+/*
+ * Remove duplicates from an array.  Return the new size.
+ */
+ST_SCOPE size_t
+ST_UNIQUE(ST_ELEMENT_TYPE *array,
+		  size_t n
+		  ST_SORT_PROTO_ELEMENT_SIZE
+		  ST_SORT_PROTO_COMPARE
+		  ST_SORT_PROTO_ARG)
+{
+	size_t		i,
+				j;
+
+	if (n <= 1)
+		return n;
+
+	for (i = 1, j = 0; i < n; ++i)
+	{
+		if (DO_COMPARE(&array[i], &array[j]) != 0 && ++j != i)
+			array[j] = array[i];
+	}
+
+	return j + 1;
+}
+
+#endif /* ST_UNIQUE */
+
+#endif /* ST_DEFINE */
 
 #undef DO_CHECK_FOR_INTERRUPTS
 #undef DO_COMPARE
@@ -411,6 +514,7 @@ loop:
 #undef ST_COMPARE
 #undef ST_COMPARE_ARG_TYPE
 #undef ST_COMPARE_RUNTIME_POINTER
+#undef ST_COMPARE_TYPE
 #undef ST_ELEMENT_TYPE
 #undef ST_ELEMENT_TYPE_VOID
 #undef ST_MAKE_NAME
@@ -420,6 +524,7 @@ loop:
 #undef ST_POINTER_STEP
 #undef ST_POINTER_TYPE
 #undef ST_SCOPE
+#undef ST_SEARCH
 #undef ST_SORT
 #undef ST_SORT_INVOKE_ARG
 #undef ST_SORT_INVOKE_COMPARE
@@ -429,3 +534,4 @@ loop:
 #undef ST_SORT_PROTO_ELEMENT_SIZE
 #undef ST_SWAP
 #undef ST_SWAPN
+#undef ST_UNIQUE
-- 
2.30.1



  [text/x-patch] 0002-Supply-sort-search-specializations-for-common-scalar.patch (2.4K, ../../CA+hUKGKztHEWm676csTFjYzortziWmOcf8HDss2Zr0muZ2xfEg@mail.gmail.com/3-0002-Supply-sort-search-specializations-for-common-scalar.patch)
  download | inline diff:
From 9a0cb8be6e3146d9cde39c4d9291bf3990604823 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Sun, 14 Mar 2021 10:00:25 +1300
Subject: [PATCH 2/9] Supply sort/search specializations for common scalar
 types.

---
 src/backend/utils/sort/Makefile     |  1 +
 src/backend/utils/sort/sortscalar.c | 11 +++++++++++
 src/include/utils/sortscalar.h      | 26 ++++++++++++++++++++++++++
 3 files changed, 38 insertions(+)
 create mode 100644 src/backend/utils/sort/sortscalar.c
 create mode 100644 src/include/utils/sortscalar.h

diff --git a/src/backend/utils/sort/Makefile b/src/backend/utils/sort/Makefile
index 26f65fcaf7..b4dce08614 100644
--- a/src/backend/utils/sort/Makefile
+++ b/src/backend/utils/sort/Makefile
@@ -17,6 +17,7 @@ override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
 OBJS = \
 	logtape.o \
 	sharedtuplestore.o \
+	sortscalar.o \
 	sortsupport.o \
 	tuplesort.o \
 	tuplestore.o
diff --git a/src/backend/utils/sort/sortscalar.c b/src/backend/utils/sort/sortscalar.c
new file mode 100644
index 0000000000..026e472a1d
--- /dev/null
+++ b/src/backend/utils/sort/sortscalar.c
@@ -0,0 +1,11 @@
+#include "postgres.h"
+
+#include "utils/sortscalar.h"
+
+#define ST_SORT qsort_uint32
+#define ST_ELEMENT_TYPE uint32
+#define ST_COMPARE(a, b) (((int64) *(a)) - ((int64) *(b)))
+#define ST_COMPARE_TYPE int64
+#define ST_SCOPE
+#define ST_DEFINE
+#include "lib/sort_template.h"
diff --git a/src/include/utils/sortscalar.h b/src/include/utils/sortscalar.h
new file mode 100644
index 0000000000..932fe29d42
--- /dev/null
+++ b/src/include/utils/sortscalar.h
@@ -0,0 +1,26 @@
+#ifndef SORTSCALAR_H
+#define SORTSCALAR_H
+
+/* We'll define the sort functions in sortscalar.c (they'd better match). */
+extern void qsort_uint32(uint32 *array, size_t n);
+
+/* The unique and search functions are defined here so they can be inlined. */
+#define ST_UNIQUE unique_uint32
+#define ST_SEARCH bsearch_uint32
+#define ST_ELEMENT_TYPE uint32
+#define ST_COMPARE(a, b) (((int64) *(a)) - ((int64) *(b)))
+#define ST_COMPARE_TYPE int64
+#define ST_SCOPE static inline
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+/* Provide names for other common types that are currently uint32. */
+#define qsort_oid			qsort_uint32
+#define unique_oid			unique_uint32
+#define bsearch_oid			bsearch_uint32
+
+#define qsort_blocknum		qsort_uint32
+#define unique_blocknum		unique_uint32
+#define bsearch_blocknum	bsearch_uint32
+
+#endif
-- 
2.30.1



  [text/x-patch] 0003-Use-qsort_oid-and-friends-in-obvious-places.patch (9.1K, ../../CA+hUKGKztHEWm676csTFjYzortziWmOcf8HDss2Zr0muZ2xfEg@mail.gmail.com/4-0003-Use-qsort_oid-and-friends-in-obvious-places.patch)
  download | inline diff:
From 441563a698a7af06dfb71d0eca1283a1ca7a7cf1 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Sun, 14 Mar 2021 11:40:01 +1300
Subject: [PATCH 3/9] Use qsort_oid() and friends in obvious places.

Done mainly for notational advantage over traditional qsort()/bsearch().
There may be some speedup, from inlined branchless comparators.
---
 src/backend/access/nbtree/nbtinsert.c   | 30 +++++--------------------
 src/backend/catalog/pg_enum.c           |  3 ++-
 src/backend/catalog/pg_inherits.c       |  3 ++-
 src/backend/commands/subscriptioncmds.c | 15 ++++++-------
 src/backend/utils/adt/acl.c             |  5 +++--
 src/backend/utils/cache/syscache.c      | 30 +++++--------------------
 6 files changed, 24 insertions(+), 62 deletions(-)

diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 0bc86943eb..09abf342de 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -19,11 +19,11 @@
 #include "access/nbtxlog.h"
 #include "access/transam.h"
 #include "access/xloginsert.h"
-#include "lib/qunique.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
 #include "storage/predicate.h"
 #include "storage/smgr.h"
+#include "utils/sortscalar.h"
 
 /* Minimum tree height for application of fastpath optimization */
 #define BTREE_FASTPATH_MIN_LEVEL	2
@@ -70,7 +70,6 @@ static void _bt_simpledel_pass(Relation rel, Buffer buffer, Relation heapRel,
 static BlockNumber *_bt_deadblocks(Page page, OffsetNumber *deletable,
 								   int ndeletable, IndexTuple newitem,
 								   int *nblocks);
-static inline int _bt_blk_cmp(const void *arg1, const void *arg2);
 
 /*
  *	_bt_doinsert() -- Handle insertion of a single index tuple in the tree.
@@ -2814,8 +2813,7 @@ _bt_simpledel_pass(Relation rel, Buffer buffer, Relation heapRel,
 		if (!BTreeTupleIsPosting(itup))
 		{
 			tidblock = ItemPointerGetBlockNumber(&itup->t_tid);
-			match = bsearch(&tidblock, deadblocks, ndeadblocks,
-							sizeof(BlockNumber), _bt_blk_cmp);
+			match = bsearch_blocknum(&tidblock, deadblocks, ndeadblocks);
 
 			if (!match)
 			{
@@ -2845,8 +2843,7 @@ _bt_simpledel_pass(Relation rel, Buffer buffer, Relation heapRel,
 				ItemPointer tid = BTreeTupleGetPostingN(itup, p);
 
 				tidblock = ItemPointerGetBlockNumber(tid);
-				match = bsearch(&tidblock, deadblocks, ndeadblocks,
-								sizeof(BlockNumber), _bt_blk_cmp);
+				match = bsearch_blocknum(&tidblock, deadblocks, ndeadblocks);
 
 				if (!match)
 				{
@@ -2966,25 +2963,8 @@ _bt_deadblocks(Page page, OffsetNumber *deletable, int ndeletable,
 		}
 	}
 
-	qsort(tidblocks, ntids, sizeof(BlockNumber), _bt_blk_cmp);
-	*nblocks = qunique(tidblocks, ntids, sizeof(BlockNumber), _bt_blk_cmp);
+	qsort_blocknum(tidblocks, ntids);
+	*nblocks = unique_blocknum(tidblocks, ntids);
 
 	return tidblocks;
 }
-
-/*
- * _bt_blk_cmp() -- qsort comparison function for _bt_simpledel_pass
- */
-static inline int
-_bt_blk_cmp(const void *arg1, const void *arg2)
-{
-	BlockNumber b1 = *((BlockNumber *) arg1);
-	BlockNumber b2 = *((BlockNumber *) arg2);
-
-	if (b1 < b2)
-		return -1;
-	else if (b1 > b2)
-		return 1;
-
-	return 0;
-}
diff --git a/src/backend/catalog/pg_enum.c b/src/backend/catalog/pg_enum.c
index f958f1541d..1d9dacb516 100644
--- a/src/backend/catalog/pg_enum.c
+++ b/src/backend/catalog/pg_enum.c
@@ -30,6 +30,7 @@
 #include "utils/fmgroids.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
+#include "utils/sortscalar.h"
 #include "utils/syscache.h"
 
 /* Potentially set by pg_upgrade_support functions */
@@ -108,7 +109,7 @@ EnumValuesCreate(Oid enumTypeOid, List *vals)
 	}
 
 	/* sort them, just in case OID counter wrapped from high to low */
-	qsort(oids, num_elems, sizeof(Oid), oid_cmp);
+	qsort_oid(oids, num_elems);
 
 	/* and make the entries */
 	memset(nulls, false, sizeof(nulls));
diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c
index 5ab7902827..5cf76b2cad 100644
--- a/src/backend/catalog/pg_inherits.c
+++ b/src/backend/catalog/pg_inherits.c
@@ -29,6 +29,7 @@
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/memutils.h"
+#include "utils/sortscalar.h"
 #include "utils/syscache.h"
 
 /*
@@ -111,7 +112,7 @@ find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
 	 * lock children in the same order to avoid needless deadlocks.
 	 */
 	if (numoids > 1)
-		qsort(oidarr, numoids, sizeof(Oid), oid_cmp);
+		qsort_oid(oidarr, numoids);
 
 	/*
 	 * Acquire locks and build the result list.
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index bfd3514546..a24db5dca8 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -44,6 +44,7 @@
 #include "utils/guc.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
+#include "utils/sortscalar.h"
 #include "utils/syscache.h"
 
 static List *fetch_table_list(WalReceiverConn *wrconn, List *publications);
@@ -608,8 +609,7 @@ AlterSubscription_refresh(Subscription *sub, bool copy_data)
 
 			subrel_local_oids[off++] = relstate->relid;
 		}
-		qsort(subrel_local_oids, list_length(subrel_states),
-			  sizeof(Oid), oid_cmp);
+		qsort_oid(subrel_local_oids, list_length(subrel_states));
 
 		/*
 		 * Rels that we want to remove from subscription and drop any slots
@@ -640,8 +640,8 @@ AlterSubscription_refresh(Subscription *sub, bool copy_data)
 
 			pubrel_local_oids[off++] = relid;
 
-			if (!bsearch(&relid, subrel_local_oids,
-						 list_length(subrel_states), sizeof(Oid), oid_cmp))
+			if (!bsearch_oid(&relid, subrel_local_oids,
+							 list_length(subrel_states)))
 			{
 				AddSubscriptionRelState(sub->oid, relid,
 										copy_data ? SUBREL_STATE_INIT : SUBREL_STATE_READY,
@@ -656,16 +656,15 @@ AlterSubscription_refresh(Subscription *sub, bool copy_data)
 		 * Next remove state for tables we should not care about anymore using
 		 * the data we collected above
 		 */
-		qsort(pubrel_local_oids, list_length(pubrel_names),
-			  sizeof(Oid), oid_cmp);
+		qsort_oid(pubrel_local_oids, list_length(pubrel_names));
 
 		remove_rel_len = 0;
 		for (off = 0; off < list_length(subrel_states); off++)
 		{
 			Oid			relid = subrel_local_oids[off];
 
-			if (!bsearch(&relid, pubrel_local_oids,
-						 list_length(pubrel_names), sizeof(Oid), oid_cmp))
+			if (!bsearch_oid(&relid, pubrel_local_oids,
+							 list_length(pubrel_names)))
 			{
 				char		state;
 				XLogRecPtr	statelsn;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index c7f029e218..2b90ee4c88 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -38,6 +38,7 @@
 #include "utils/inval.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
+#include "utils/sortscalar.h"
 #include "utils/syscache.h"
 #include "utils/varlena.h"
 
@@ -1496,7 +1497,7 @@ aclmembers(const Acl *acl, Oid **roleids)
 	}
 
 	/* Sort the array */
-	qsort(list, j, sizeof(Oid), oid_cmp);
+	qsort_oid(list, j);
 
 	/*
 	 * We could repalloc the array down to minimum size, but it's hardly worth
@@ -1505,7 +1506,7 @@ aclmembers(const Acl *acl, Oid **roleids)
 	*roleids = list;
 
 	/* Remove duplicates from the array */
-	return qunique(list, j, sizeof(Oid), oid_cmp);
+	return unique_oid(list, j);
 }
 
 
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index e4dc4ee34e..26ef27b50d 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -76,6 +76,7 @@
 #include "lib/qunique.h"
 #include "utils/catcache.h"
 #include "utils/rel.h"
+#include "utils/sortscalar.h"
 #include "utils/syscache.h"
 
 /*---------------------------------------------------------------------------
@@ -1006,8 +1007,6 @@ static int	SysCacheRelationOidSize;
 static Oid	SysCacheSupportingRelOid[SysCacheSize * 2];
 static int	SysCacheSupportingRelOidSize;
 
-static int	oid_compare(const void *a, const void *b);
-
 
 /*
  * InitCatalogCache - initialize the caches
@@ -1055,17 +1054,13 @@ InitCatalogCache(void)
 	Assert(SysCacheSupportingRelOidSize <= lengthof(SysCacheSupportingRelOid));
 
 	/* Sort and de-dup OID arrays, so we can use binary search. */
-	pg_qsort(SysCacheRelationOid, SysCacheRelationOidSize,
-			 sizeof(Oid), oid_compare);
+	qsort_oid(SysCacheRelationOid, SysCacheRelationOidSize);
 	SysCacheRelationOidSize =
-		qunique(SysCacheRelationOid, SysCacheRelationOidSize, sizeof(Oid),
-				oid_compare);
+		unique_oid(SysCacheRelationOid, SysCacheRelationOidSize);
 
-	pg_qsort(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
-			 sizeof(Oid), oid_compare);
+	qsort_oid(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize);
 	SysCacheSupportingRelOidSize =
-		qunique(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
-				sizeof(Oid), oid_compare);
+		unique_oid(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize);
 
 	CacheInitialized = true;
 }
@@ -1548,18 +1543,3 @@ RelationSupportsSysCache(Oid relid)
 
 	return false;
 }
-
-
-/*
- * OID comparator for pg_qsort
- */
-static int
-oid_compare(const void *a, const void *b)
-{
-	Oid			oa = *((const Oid *) a);
-	Oid			ob = *((const Oid *) b);
-
-	if (oa == ob)
-		return 0;
-	return (oa > ob) ? 1 : -1;
-}
-- 
2.30.1



  [text/x-patch] 0004-Supply-specialized-sort-search-routines-for-ItemPtrD.patch (2.2K, ../../CA+hUKGKztHEWm676csTFjYzortziWmOcf8HDss2Zr0muZ2xfEg@mail.gmail.com/5-0004-Supply-specialized-sort-search-routines-for-ItemPtrD.patch)
  download | inline diff:
From 7e6091ee0163e0c49abcf99952c493c05baaaab7 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Sun, 14 Mar 2021 15:11:22 +1300
Subject: [PATCH 4/9] Supply specialized sort/search routines for ItemPtrData.

---
 src/backend/utils/sort/Makefile      |  1 +
 src/backend/utils/sort/sortitemptr.c | 12 ++++++++++++
 src/include/utils/sortitemptr.h      | 19 +++++++++++++++++++
 3 files changed, 32 insertions(+)
 create mode 100644 src/backend/utils/sort/sortitemptr.c
 create mode 100644 src/include/utils/sortitemptr.h

diff --git a/src/backend/utils/sort/Makefile b/src/backend/utils/sort/Makefile
index b4dce08614..32acbb7f20 100644
--- a/src/backend/utils/sort/Makefile
+++ b/src/backend/utils/sort/Makefile
@@ -17,6 +17,7 @@ override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
 OBJS = \
 	logtape.o \
 	sharedtuplestore.o \
+	sortitemptr.o \
 	sortscalar.o \
 	sortsupport.o \
 	tuplesort.o \
diff --git a/src/backend/utils/sort/sortitemptr.c b/src/backend/utils/sort/sortitemptr.c
new file mode 100644
index 0000000000..a0db6d6d0d
--- /dev/null
+++ b/src/backend/utils/sort/sortitemptr.c
@@ -0,0 +1,12 @@
+#include "postgres.h"
+
+#include "catalog/index.h"
+#include "utils/sortitemptr.h"
+
+#define ST_SORT qsort_itemptr
+#define ST_SCOPE
+#define ST_ELEMENT_TYPE ItemPointerData
+#define ST_COMPARE(a, b) (itemptr_encode(a) - itemptr_encode(b))
+#define ST_COMPARE_TYPE int64
+#define ST_DEFINE
+#include "lib/sort_template.h"
diff --git a/src/include/utils/sortitemptr.h b/src/include/utils/sortitemptr.h
new file mode 100644
index 0000000000..eb3df25eec
--- /dev/null
+++ b/src/include/utils/sortitemptr.h
@@ -0,0 +1,19 @@
+#ifndef SORTITEMPTR_H
+#define SORTITEMPTR_H
+
+#include "catalog/index.h"
+
+/* Declare sort function, from .c file. */
+extern void qsort_itemptr(ItemPointerData *tids, size_t n);
+
+/* Search and unique functions inline in header. */
+#define ST_SEARCH bsearch_itemptr
+#define ST_UNIQUE unique_itemptr
+#define ST_ELEMENT_TYPE ItemPointerData
+#define ST_COMPARE(a, b) (itemptr_encode(a) - itemptr_encode(b))
+#define ST_COMPARE_TYPE int64
+#define ST_SCOPE static inline
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+#endif
-- 
2.30.1



  [text/x-patch] 0005-Use-qsort_itemptr-and-friends-in-various-places.patch (4.3K, ../../CA+hUKGKztHEWm676csTFjYzortziWmOcf8HDss2Zr0muZ2xfEg@mail.gmail.com/6-0005-Use-qsort_itemptr-and-friends-in-various-places.patch)
  download | inline diff:
From eed4a91d7742166124c26dbdadd41aa4695cc5b8 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Sat, 13 Mar 2021 18:48:10 +1300
Subject: [PATCH 5/9] Use qsort_itemptr() and friends in various places.

---
 src/backend/access/heap/vacuumlazy.c | 40 +++-------------------------
 src/backend/executor/nodeTidscan.c   | 32 +++-------------------
 2 files changed, 7 insertions(+), 65 deletions(-)

diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index a65dcbebfa..ef57e88b43 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -79,6 +79,7 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/pg_rusage.h"
+#include "utils/sortitemptr.h"
 #include "utils/timestamp.h"
 
 
@@ -367,7 +368,6 @@ static void lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks);
 static void lazy_record_dead_tuple(LVDeadTuples *dead_tuples,
 								   ItemPointer itemptr);
 static bool lazy_tid_reaped(ItemPointer itemptr, void *state);
-static int	vac_cmp_itemptr(const void *left, const void *right);
 static bool heap_page_is_all_visible(Relation rel, Buffer buf,
 									 LVRelStats *vacrelstats,
 									 TransactionId *visibility_cutoff_xid, bool *all_frozen);
@@ -2943,45 +2943,13 @@ lazy_tid_reaped(ItemPointer itemptr, void *state)
 	if (item < litem || item > ritem)
 		return false;
 
-	res = (ItemPointer) bsearch((void *) itemptr,
-								(void *) dead_tuples->itemptrs,
-								dead_tuples->num_tuples,
-								sizeof(ItemPointerData),
-								vac_cmp_itemptr);
+	res = bsearch_itemptr(itemptr,
+						  dead_tuples->itemptrs,
+						  dead_tuples->num_tuples);
 
 	return (res != NULL);
 }
 
-/*
- * Comparator routines for use with qsort() and bsearch().
- */
-static int
-vac_cmp_itemptr(const void *left, const void *right)
-{
-	BlockNumber lblk,
-				rblk;
-	OffsetNumber loff,
-				roff;
-
-	lblk = ItemPointerGetBlockNumber((ItemPointer) left);
-	rblk = ItemPointerGetBlockNumber((ItemPointer) right);
-
-	if (lblk < rblk)
-		return -1;
-	if (lblk > rblk)
-		return 1;
-
-	loff = ItemPointerGetOffsetNumber((ItemPointer) left);
-	roff = ItemPointerGetOffsetNumber((ItemPointer) right);
-
-	if (loff < roff)
-		return -1;
-	if (loff > roff)
-		return 1;
-
-	return 0;
-}
-
 /*
  * Check if every tuple in the given page is visible to all current and future
  * transactions. Also return the visibility_cutoff_xid which is the highest
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 48c3737da2..ee6bc4ee05 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -33,6 +33,7 @@
 #include "storage/bufmgr.h"
 #include "utils/array.h"
 #include "utils/rel.h"
+#include "utils/sortitemptr.h"
 
 
 #define IsCTIDVar(node)  \
@@ -51,7 +52,6 @@ typedef struct TidExpr
 
 static void TidExprListCreate(TidScanState *tidstate);
 static void TidListEval(TidScanState *tidstate);
-static int	itemptr_comparator(const void *a, const void *b);
 static TupleTableSlot *TidNext(TidScanState *node);
 
 
@@ -263,10 +263,8 @@ TidListEval(TidScanState *tidstate)
 		/* CurrentOfExpr could never appear OR'd with something else */
 		Assert(!tidstate->tss_isCurrentOf);
 
-		qsort((void *) tidList, numTids, sizeof(ItemPointerData),
-			  itemptr_comparator);
-		numTids = qunique(tidList, numTids, sizeof(ItemPointerData),
-						  itemptr_comparator);
+		qsort_itemptr(tidList, numTids);
+		numTids = unique_itemptr(tidList, numTids);
 	}
 
 	tidstate->tss_TidList = tidList;
@@ -274,30 +272,6 @@ TidListEval(TidScanState *tidstate)
 	tidstate->tss_TidPtr = -1;
 }
 
-/*
- * qsort comparator for ItemPointerData items
- */
-static int
-itemptr_comparator(const void *a, const void *b)
-{
-	const ItemPointerData *ipa = (const ItemPointerData *) a;
-	const ItemPointerData *ipb = (const ItemPointerData *) b;
-	BlockNumber ba = ItemPointerGetBlockNumber(ipa);
-	BlockNumber bb = ItemPointerGetBlockNumber(ipb);
-	OffsetNumber oa = ItemPointerGetOffsetNumber(ipa);
-	OffsetNumber ob = ItemPointerGetOffsetNumber(ipb);
-
-	if (ba < bb)
-		return -1;
-	if (ba > bb)
-		return 1;
-	if (oa < ob)
-		return -1;
-	if (oa > ob)
-		return 1;
-	return 0;
-}
-
 /* ----------------------------------------------------------------
  *		TidNext
  *
-- 
2.30.1



  [text/x-patch] 0006-Specialize-the-HeapTuple-sort-routine-for-ANALYZE.patch (2.6K, ../../CA+hUKGKztHEWm676csTFjYzortziWmOcf8HDss2Zr0muZ2xfEg@mail.gmail.com/7-0006-Specialize-the-HeapTuple-sort-routine-for-ANALYZE.patch)
  download | inline diff:
From 50bb6dfb24fb68559c87ce4e2785c9a65be9a674 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Sat, 13 Mar 2021 17:30:56 +1300
Subject: [PATCH 6/9] Specialize the HeapTuple sort routine for ANALYZE.

Instead of a branching comparator, use "encoded" format, based on 48 bit
integers that can be compared by subtraction.
---
 src/backend/commands/analyze.c | 36 +++++++++-------------------------
 1 file changed, 9 insertions(+), 27 deletions(-)

diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 3a9f358dd4..e760821ef5 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -98,7 +98,6 @@ static VacAttrStats *examine_attribute(Relation onerel, int attnum,
 static int	acquire_sample_rows(Relation onerel, int elevel,
 								HeapTuple *rows, int targrows,
 								double *totalrows, double *totaldeadrows);
-static int	compare_rows(const void *a, const void *b);
 static int	acquire_inherited_sample_rows(Relation onerel, int elevel,
 										  HeapTuple *rows, int targrows,
 										  double *totalrows, double *totaldeadrows);
@@ -998,6 +997,14 @@ examine_attribute(Relation onerel, int attnum, Node *index_expr)
 	return stats;
 }
 
+#define ST_SORT sort_heaptuples_by_tid
+#define ST_ELEMENT_TYPE HeapTuple
+#define ST_COMPARE(a, b) (itemptr_encode(&((*a)->t_self)) - itemptr_encode(&((*b)->t_self)))
+#define ST_COMPARE_TYPE int64
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
 /*
  * acquire_sample_rows -- acquire a random sample of rows from the table
  *
@@ -1141,7 +1148,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 	 * tuples are already sorted.
 	 */
 	if (numrows == targrows)
-		qsort((void *) rows, numrows, sizeof(HeapTuple), compare_rows);
+		sort_heaptuples_by_tid(rows, numrows);
 
 	/*
 	 * Estimate total numbers of live and dead rows in relation, extrapolating
@@ -1176,31 +1183,6 @@ acquire_sample_rows(Relation onerel, int elevel,
 	return numrows;
 }
 
-/*
- * qsort comparator for sorting rows[] array
- */
-static int
-compare_rows(const void *a, const void *b)
-{
-	HeapTuple	ha = *(const HeapTuple *) a;
-	HeapTuple	hb = *(const HeapTuple *) b;
-	BlockNumber ba = ItemPointerGetBlockNumber(&ha->t_self);
-	OffsetNumber oa = ItemPointerGetOffsetNumber(&ha->t_self);
-	BlockNumber bb = ItemPointerGetBlockNumber(&hb->t_self);
-	OffsetNumber ob = ItemPointerGetOffsetNumber(&hb->t_self);
-
-	if (ba < bb)
-		return -1;
-	if (ba > bb)
-		return 1;
-	if (oa < ob)
-		return -1;
-	if (oa > ob)
-		return 1;
-	return 0;
-}
-
-
 /*
  * acquire_inherited_sample_rows -- acquire sample rows from inheritance tree
  *
-- 
2.30.1



  [text/x-patch] 0007-Specialize-pagetable-sort-routines-in-tidbitmap.c.patch (4.1K, ../../CA+hUKGKztHEWm676csTFjYzortziWmOcf8HDss2Zr0muZ2xfEg@mail.gmail.com/8-0007-Specialize-pagetable-sort-routines-in-tidbitmap.c.patch)
  download | inline diff:
From 2352ce2f93fe8b9f9f597ef5ffc6a87468d769ac Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Sun, 14 Mar 2021 13:48:14 +1300
Subject: [PATCH 7/9] Specialize pagetable sort routines in tidbitmap.c.

Provide slightly more efficient routines for sorting the page table.
---
 src/backend/nodes/tidbitmap.c | 73 +++++++++++++----------------------
 1 file changed, 27 insertions(+), 46 deletions(-)

diff --git a/src/backend/nodes/tidbitmap.c b/src/backend/nodes/tidbitmap.c
index c5feacbff4..86d9ff0499 100644
--- a/src/backend/nodes/tidbitmap.c
+++ b/src/backend/nodes/tidbitmap.c
@@ -234,9 +234,6 @@ static PagetableEntry *tbm_get_pageentry(TIDBitmap *tbm, BlockNumber pageno);
 static bool tbm_page_is_lossy(const TIDBitmap *tbm, BlockNumber pageno);
 static void tbm_mark_page_lossy(TIDBitmap *tbm, BlockNumber pageno);
 static void tbm_lossify(TIDBitmap *tbm);
-static int	tbm_comparator(const void *left, const void *right);
-static int	tbm_shared_comparator(const void *left, const void *right,
-								  void *arg);
 
 /* define hashtable mapping block numbers to PagetableEntry's */
 #define SH_USE_NONDEFAULT_ALLOCATOR
@@ -671,6 +668,29 @@ tbm_is_empty(const TIDBitmap *tbm)
 	return (tbm->nentries == 0);
 }
 
+/* An inline sort function to sort PagetableEntry pointers by block. */
+#define ST_SORT qsort_pagetable
+#define ST_ELEMENT_TYPE PagetableEntry *
+#define ST_COMPARE(a, b) (((int64) (*(a))->blockno) - (((int64) (*(b))->blockno)))
+#define ST_COMPARE_TYPE int64
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+/*
+ * As above, but this will get index into PagetableEntry array.  Therefore,
+ * it needs to get actual PagetableEntry using the index before comparing the
+ * blockno.
+ */
+#define ST_SORT qsort_pagetable_shared
+#define ST_ELEMENT_TYPE int
+#define ST_COMPARE(a, b, base) ((int64) (base)[*(a)].blockno - ((int64) (base)[*(b)].blockno))
+#define ST_COMPARE_ARG_TYPE PagetableEntry
+#define ST_COMPARE_TYPE int64
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
 /*
  * tbm_begin_iterate - prepare to iterate through a TIDBitmap
  *
@@ -740,11 +760,9 @@ tbm_begin_iterate(TIDBitmap *tbm)
 		Assert(npages == tbm->npages);
 		Assert(nchunks == tbm->nchunks);
 		if (npages > 1)
-			qsort(tbm->spages, npages, sizeof(PagetableEntry *),
-				  tbm_comparator);
+			qsort_pagetable(tbm->spages, npages);
 		if (nchunks > 1)
-			qsort(tbm->schunks, nchunks, sizeof(PagetableEntry *),
-				  tbm_comparator);
+			qsort_pagetable(tbm->schunks, nchunks);
 	}
 
 	tbm->iterating = TBM_ITERATING_PRIVATE;
@@ -853,11 +871,9 @@ tbm_prepare_shared_iterate(TIDBitmap *tbm)
 		if (ptbase != NULL)
 			pg_atomic_init_u32(&ptbase->refcount, 0);
 		if (npages > 1)
-			qsort_arg((void *) (ptpages->index), npages, sizeof(int),
-					  tbm_shared_comparator, (void *) ptbase->ptentry);
+			qsort_pagetable_shared(ptpages->index, npages, ptbase->ptentry);
 		if (nchunks > 1)
-			qsort_arg((void *) (ptchunks->index), nchunks, sizeof(int),
-					  tbm_shared_comparator, (void *) ptbase->ptentry);
+			qsort_pagetable_shared(ptchunks->index, nchunks, ptbase->ptentry);
 	}
 
 	/*
@@ -1416,41 +1432,6 @@ tbm_lossify(TIDBitmap *tbm)
 		tbm->maxentries = Min(tbm->nentries, (INT_MAX - 1) / 2) * 2;
 }
 
-/*
- * qsort comparator to handle PagetableEntry pointers.
- */
-static int
-tbm_comparator(const void *left, const void *right)
-{
-	BlockNumber l = (*((PagetableEntry *const *) left))->blockno;
-	BlockNumber r = (*((PagetableEntry *const *) right))->blockno;
-
-	if (l < r)
-		return -1;
-	else if (l > r)
-		return 1;
-	return 0;
-}
-
-/*
- * As above, but this will get index into PagetableEntry array.  Therefore,
- * it needs to get actual PagetableEntry using the index before comparing the
- * blockno.
- */
-static int
-tbm_shared_comparator(const void *left, const void *right, void *arg)
-{
-	PagetableEntry *base = (PagetableEntry *) arg;
-	PagetableEntry *lpage = &base[*(int *) left];
-	PagetableEntry *rpage = &base[*(int *) right];
-
-	if (lpage->blockno < rpage->blockno)
-		return -1;
-	else if (lpage->blockno > rpage->blockno)
-		return 1;
-	return 0;
-}
-
 /*
  *	tbm_attach_shared_iterate
  *
-- 
2.30.1



  [text/x-patch] 0008-Specialize-some-sort-search-routines-in-nbtree-code.patch (5.6K, ../../CA+hUKGKztHEWm676csTFjYzortziWmOcf8HDss2Zr0muZ2xfEg@mail.gmail.com/9-0008-Specialize-some-sort-search-routines-in-nbtree-code.patch)
  download | inline diff:
From 2a5676c8b79449bb563d3aa7a489310926e14f25 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Sun, 14 Mar 2021 11:02:40 +1300
Subject: [PATCH 8/9] Specialize some sort/search routines in nbtree code.

---
 src/backend/access/nbtree/nbtpage.c  | 29 ++++--------
 src/backend/access/nbtree/nbtutils.c | 68 ++++++++++++++++------------
 2 files changed, 48 insertions(+), 49 deletions(-)

diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index fc744cf9fd..78e04a3e9a 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -1468,24 +1468,16 @@ _bt_delitems_update(BTVacuumPosting *updatable, int nupdatable,
 }
 
 /*
- * Comparator used by _bt_delitems_delete_check() to restore deltids array
- * back to its original leaf-page-wise sort order
+ * Sort used by _bt_delitems_delete_check() to restore deltids array back to
+ * its original leaf-page-wise sort order.  We can safely use a branchless
+ * int-based comparator expression because id is an int16.
  */
-static int
-_bt_delitems_cmp(const void *a, const void *b)
-{
-	TM_IndexDelete *indexdelete1 = (TM_IndexDelete *) a;
-	TM_IndexDelete *indexdelete2 = (TM_IndexDelete *) b;
-
-	if (indexdelete1->id > indexdelete2->id)
-		return 1;
-	if (indexdelete1->id < indexdelete2->id)
-		return -1;
-
-	Assert(false);
-
-	return 0;
-}
+#define ST_SORT qsort_delitems
+#define ST_ELEMENT_TYPE TM_IndexDelete
+#define ST_COMPARE(a, b) ((int) (a)->id - (int) (b)->id)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
 
 /*
  * Try to delete item(s) from a btree leaf page during single-page cleanup.
@@ -1555,8 +1547,7 @@ _bt_delitems_delete_check(Relation rel, Buffer buf, Relation heapRel,
 	 * no entries at all (with bottom-up deletion caller), in which case there
 	 * is nothing left to do.
 	 */
-	qsort(delstate->deltids, delstate->ndeltids, sizeof(TM_IndexDelete),
-		  _bt_delitems_cmp);
+	qsort_delitems(delstate->deltids, delstate->ndeltids);
 	if (delstate->ndeltids == 0)
 	{
 		Assert(delstate->bottomup);
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index d524310723..3012d9f702 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -22,7 +22,6 @@
 #include "access/relscan.h"
 #include "catalog/catalog.h"
 #include "commands/progress.h"
-#include "lib/qunique.h"
 #include "miscadmin.h"
 #include "utils/array.h"
 #include "utils/datum.h"
@@ -35,7 +34,6 @@ typedef struct BTSortArrayContext
 {
 	FmgrInfo	flinfo;
 	Oid			collation;
-	bool		reverse;
 } BTSortArrayContext;
 
 static Datum _bt_find_extreme_element(IndexScanDesc scan, ScanKey skey,
@@ -44,7 +42,6 @@ static Datum _bt_find_extreme_element(IndexScanDesc scan, ScanKey skey,
 static int	_bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
 									bool reverse,
 									Datum *elems, int nelems);
-static int	_bt_compare_array_elements(const void *a, const void *b, void *arg);
 static bool _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op,
 									 ScanKey leftarg, ScanKey rightarg,
 									 bool *result);
@@ -429,6 +426,33 @@ _bt_find_extreme_element(IndexScanDesc scan, ScanKey skey,
 	return result;
 }
 
+/* Define a specialized sort function for _bt_sort_array_elements. */
+#define ST_SORT qsort_bt_array_elements
+#define ST_UNIQUE unique_bt_array_elements
+#define ST_ELEMENT_TYPE Datum
+#define ST_COMPARE(a, b, cxt) \
+	DatumGetInt32(FunctionCall2Coll(&cxt->flinfo, cxt->collation, *a, *b))
+#define ST_COMPARE_ARG_TYPE BTSortArrayContext
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+/*
+ * Define a reverse sort function for _bt_sort_array_elements.  Note use of
+ * int64 comparator expression, so that we can safely invert even INT_MIN with
+ * simple substraction from zero.
+ */
+#define ST_SORT qsort_bt_array_elements_reverse
+#define ST_UNIQUE unique_bt_array_elements_reverse
+#define ST_ELEMENT_TYPE Datum
+#define ST_COMPARE(a, b, cxt) \
+	(0 - (DatumGetInt32(FunctionCall2Coll(&cxt->flinfo, cxt->collation, *a, *b))))
+#define ST_COMPARE_TYPE int64
+#define ST_COMPARE_ARG_TYPE BTSortArrayContext
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
 /*
  * _bt_sort_array_elements() -- sort and de-dup array elements
  *
@@ -477,35 +501,19 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
 			 BTORDER_PROC, elemtype, elemtype,
 			 rel->rd_opfamily[skey->sk_attno - 1]);
 
-	/* Sort the array elements */
+	/* Sort the array elements and remove duplicates */
 	fmgr_info(cmp_proc, &cxt.flinfo);
 	cxt.collation = skey->sk_collation;
-	cxt.reverse = reverse;
-	qsort_arg((void *) elems, nelems, sizeof(Datum),
-			  _bt_compare_array_elements, (void *) &cxt);
-
-	/* Now scan the sorted elements and remove duplicates */
-	return qunique_arg(elems, nelems, sizeof(Datum),
-					   _bt_compare_array_elements, &cxt);
-}
-
-/*
- * qsort_arg comparator for sorting array elements
- */
-static int
-_bt_compare_array_elements(const void *a, const void *b, void *arg)
-{
-	Datum		da = *((const Datum *) a);
-	Datum		db = *((const Datum *) b);
-	BTSortArrayContext *cxt = (BTSortArrayContext *) arg;
-	int32		compare;
-
-	compare = DatumGetInt32(FunctionCall2Coll(&cxt->flinfo,
-											  cxt->collation,
-											  da, db));
-	if (cxt->reverse)
-		INVERT_COMPARE_RESULT(compare);
-	return compare;
+	if (reverse)
+	{
+		qsort_bt_array_elements_reverse(elems, nelems, &cxt);
+		return unique_bt_array_elements_reverse(elems, nelems, &cxt);
+	}
+	else
+	{
+		qsort_bt_array_elements(elems, nelems, &cxt);
+		return unique_bt_array_elements(elems, nelems, &cxt);
+	}
 }
 
 /*
-- 
2.30.1



  [text/x-patch] 0009-Specialize-sort-routine-used-by-multixact.c.patch (2.9K, ../../CA+hUKGKztHEWm676csTFjYzortziWmOcf8HDss2Zr0muZ2xfEg@mail.gmail.com/10-0009-Specialize-sort-routine-used-by-multixact.c.patch)
  download | inline diff:
From 2308a8556fecdf3388ec4ac4c2a1588619a62b94 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Sun, 14 Mar 2021 14:53:10 +1300
Subject: [PATCH 9/9] Specialize sort routine used by multixact.c.

---
 src/backend/access/transam/multixact.c | 35 +++++++-------------------
 1 file changed, 9 insertions(+), 26 deletions(-)

diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 1f9f1a1fa1..97c5120cdd 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -344,7 +344,6 @@ static void RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 static MultiXactId GetNewMultiXactId(int nmembers, MultiXactOffset *offset);
 
 /* MultiXact cache management */
-static int	mxactMemberComparator(const void *arg1, const void *arg2);
 static MultiXactId mXactCacheGetBySet(int nmembers, MultiXactMember *members);
 static int	mXactCacheGetById(MultiXactId multi, MultiXactMember **members);
 static void mXactCachePut(MultiXactId multi, int nmembers,
@@ -1453,29 +1452,13 @@ retry:
 	return truelength;
 }
 
-/*
- * mxactMemberComparator
- *		qsort comparison function for MultiXactMember
- *
- * We can't use wraparound comparison for XIDs because that does not respect
- * the triangle inequality!  Any old sort order will do.
- */
-static int
-mxactMemberComparator(const void *arg1, const void *arg2)
-{
-	MultiXactMember member1 = *(const MultiXactMember *) arg1;
-	MultiXactMember member2 = *(const MultiXactMember *) arg2;
-
-	if (member1.xid > member2.xid)
-		return 1;
-	if (member1.xid < member2.xid)
-		return -1;
-	if (member1.status > member2.status)
-		return 1;
-	if (member1.status < member2.status)
-		return -1;
-	return 0;
-}
+#define ST_SORT qsort_mxact_members
+#define ST_ELEMENT_TYPE MultiXactMember
+#define ST_COMPARE(a, b) (((((int64) (a)->status) << 8) | (int64) (a)->xid) - ((((int64) (b)->status) << 8) | (int64) (b)->xid))
+#define ST_COMPARE_TYPE int64
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
 
 /*
  * mXactCacheGetBySet
@@ -1499,7 +1482,7 @@ mXactCacheGetBySet(int nmembers, MultiXactMember *members)
 				mxid_to_string(InvalidMultiXactId, nmembers, members));
 
 	/* sort the array so comparison is easy */
-	qsort(members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);
+	qsort_mxact_members(members, nmembers);
 
 	dlist_foreach(iter, &MXactCache)
 	{
@@ -1605,7 +1588,7 @@ mXactCachePut(MultiXactId multi, int nmembers, MultiXactMember *members)
 	memcpy(entry->members, members, nmembers * sizeof(MultiXactMember));
 
 	/* mXactCacheGetBySet assumes the entries are sorted, so sort them */
-	qsort(entry->members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);
+	qsort_mxact_members(entry->members, nmembers);
 
 	dlist_push_head(&MXactCache, &entry->node);
 	if (MXactCacheMembers++ >= MAX_CACHE_ENTRIES)
-- 
2.30.1



^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-03-14 04:06  Zhihong Yu <[email protected]>
  parent: Thomas Munro <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Zhihong Yu @ 2021-03-14 04:06 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: Andres Freund <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

Hi,

For 0001-Add-bsearch-and-unique-templates-to-sort_template.h.patch :

+ * Remove duplicates from an array.  Return the new size.
+ */
+ST_SCOPE size_t
+ST_UNIQUE(ST_ELEMENT_TYPE *array,

The array is supposed to be sorted, right ?
The comment should mention this.

Cheers

On Sat, Mar 13, 2021 at 6:36 PM Thomas Munro <[email protected]> wrote:

> On Sat, Mar 13, 2021 at 3:49 PM Thomas Munro <[email protected]>
> wrote:
> > On Fri, Mar 12, 2021 at 7:58 AM Andres Freund <[email protected]>
> wrote:
> > > I wish we had the same for bsearch... :)
> >
> > Glibc already has the definition of the traditional void-based
> > function in /usr/include/bits/stdlib-bsearch.h, so the generated code
> > when the compiler can see the comparator definition is already good in
> > eg lazy_tid_reaped() and eg some nbtree search routines.  We could
> > probably expose more trivial comparators in headers to get more of
> > that, and we could perhaps put our own bsearch definition in a header
> > for other platforms that didn't think of that...
> >
> > It might be worth doing type-safe macro templates as well, though (as
> > I already did in an earlier proposal[1]), just to have nice type safe
> > code though, not sure, I'm thinking about that...
>
> I remembered a very good reason to do this: the ability to do
> branch-free comparators in more places by introducing optional wider
> results.  That's good for TIDs (needs 49 bits), and places that want
> to "reverse" a traditional comparator (just doing -result on an int
> comparator that might theoretically return INT_MIN requires at least
> 33 bits).  So I rebased the relevant parts of my earlier version, and
> went through and wrote a bunch of examples to demonstrate all this
> stuff actually working.
>
> There are two categories of change in these patches:
>
> 0002-0005: Places that sort/unique/search OIDs, BlockNumbers and TIDs,
> which can reuse a small set of typed functions (a few more could be
> added, if useful).  See sortitemptr.h and sortscalar.h.  Mostly this
> is just a notational improvement, and an excuse to drop a bunch of
> duplicated code.  In a few places this might really speed something
> important up!  Like VACUUM's lazy_tid_reaped().
>
> 0006-0009.  Places where a specialised function is generated for one
> special purpose, such as ANALYZE's HeapTuple sort, tidbitmap.c's
> pagetable sort,  some places in nbtree code etc.  These may require
> some case-by-case research on whether the extra executable size is
> worth the speedup, and there are surely more opportunities like that;
> I just picked on these arbitrarily.
>


^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-03-15 00:09  Thomas Munro <[email protected]>
  parent: Zhihong Yu <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Thomas Munro @ 2021-03-15 00:09 UTC (permalink / raw)
  To: Zhihong Yu <[email protected]>; +Cc: Andres Freund <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

On Sun, Mar 14, 2021 at 5:03 PM Zhihong Yu <[email protected]> wrote:
> For 0001-Add-bsearch-and-unique-templates-to-sort_template.h.patch :
>
> + * Remove duplicates from an array.  Return the new size.
> + */
> +ST_SCOPE size_t
> +ST_UNIQUE(ST_ELEMENT_TYPE *array,
>
> The array is supposed to be sorted, right ?
> The comment should mention this.

Good point, will update.  Thanks!





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-06-16 05:54  Thomas Munro <[email protected]>
  parent: Thomas Munro <[email protected]>
  0 siblings, 2 replies; 55+ messages in thread

From: Thomas Munro @ 2021-06-16 05:54 UTC (permalink / raw)
  To: Zhihong Yu <[email protected]>; +Cc: Andres Freund <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

On Mon, Mar 15, 2021 at 1:09 PM Thomas Munro <[email protected]> wrote:
> On Sun, Mar 14, 2021 at 5:03 PM Zhihong Yu <[email protected]> wrote:
> > + * Remove duplicates from an array.  Return the new size.
> > + */
> > +ST_SCOPE size_t
> > +ST_UNIQUE(ST_ELEMENT_TYPE *array,
> >
> > The array is supposed to be sorted, right ?
> > The comment should mention this.
>
> Good point, will update.  Thanks!

Rebased.  Also fixed some formatting problems and updated
typedefs.list so they don't come back.


Attachments:

  [application/x-compressed-tar] more-sort-search-specializations-v2.tgz (11.7K, ../../CA+hUKGLAfouP-rUpik45Z_KPM5+oxrX_564bLfiZPX4f25MYbA@mail.gmail.com/2-more-sort-search-specializations-v2.tgz)
  download

^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-06-16 20:18  Zhihong Yu <[email protected]>
  parent: Thomas Munro <[email protected]>
  1 sibling, 1 reply; 55+ messages in thread

From: Zhihong Yu @ 2021-06-16 20:18 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: Andres Freund <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

On Tue, Jun 15, 2021 at 10:55 PM Thomas Munro <[email protected]>
wrote:

> On Mon, Mar 15, 2021 at 1:09 PM Thomas Munro <[email protected]>
> wrote:
> > On Sun, Mar 14, 2021 at 5:03 PM Zhihong Yu <[email protected]> wrote:
> > > + * Remove duplicates from an array.  Return the new size.
> > > + */
> > > +ST_SCOPE size_t
> > > +ST_UNIQUE(ST_ELEMENT_TYPE *array,
> > >
> > > The array is supposed to be sorted, right ?
> > > The comment should mention this.
> >
> > Good point, will update.  Thanks!
>
> Rebased.  Also fixed some formatting problems and updated
> typedefs.list so they don't come back.
>

Hi,
In 0001-Add-bsearch-and-unique-templates-to-sort_template.h.patch :

-                                       const ST_ELEMENT_TYPE *
ST_SORT_PROTO_ARG);
+                                       const ST_ELEMENT_TYPE
*ST_SORT_PROTO_ARG);

It seems there is no real change in the line above. Better keep the
original formation.

  *   - ST_COMPARE_ARG_TYPE - type of extra argument
  *
+ *   To say that the comparator returns a type other than int, use:
+ *
+ *       - ST_COMPARE_TYPE - an integer type

Since the ST_COMPARE_TYPE is meant to designate the type of the return
value, maybe ST_COMPARE_RET_TYPE would be better name.
It also goes with ST_COMPARE_ARG_TYPE preceding this.

-   ST_POINTER_TYPE *a = (ST_POINTER_TYPE *) data,
-              *pa,
-              *pb,
-              *pc,
-              *pd,
-              *pl,
-              *pm,
-              *pn;
+   ST_POINTER_TYPE *a = (ST_POINTER_TYPE *) data;
+   ST_POINTER_TYPE *pa;

There doesn't seem to be material change for the above hunk.

+   while (left <= right)
+   {
+       size_t      mid = (left + right) / 2;

The computation for midpoint should be left + (right-left)/2.

Cheers


^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-06-16 21:54  Thomas Munro <[email protected]>
  parent: Zhihong Yu <[email protected]>
  0 siblings, 2 replies; 55+ messages in thread

From: Thomas Munro @ 2021-06-16 21:54 UTC (permalink / raw)
  To: Zhihong Yu <[email protected]>; +Cc: Andres Freund <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

Hi Zhihong,

On Thu, Jun 17, 2021 at 8:13 AM Zhihong Yu <[email protected]> wrote:
> In 0001-Add-bsearch-and-unique-templates-to-sort_template.h.patch :
>
> -                                       const ST_ELEMENT_TYPE * ST_SORT_PROTO_ARG);
> +                                       const ST_ELEMENT_TYPE *ST_SORT_PROTO_ARG);
>
> It seems there is no real change in the line above. Better keep the original formation.

Hmm, well it was only recently damaged by commit def5b065, and that's
because I'd forgotten to put ST_ELEMENT_TYPE into typedefs.list, and I
was correcting that in this patch.  (That file is used by
pg_bsd_indent to decide if an identifier is a type or a variable,
which affects whether '*' is formatted like a unary operator/type
syntax or a binary operator.)

>   *   - ST_COMPARE_ARG_TYPE - type of extra argument
>   *
> + *   To say that the comparator returns a type other than int, use:
> + *
> + *       - ST_COMPARE_TYPE - an integer type
>
> Since the ST_COMPARE_TYPE is meant to designate the type of the return value, maybe ST_COMPARE_RET_TYPE would be better name.
> It also goes with ST_COMPARE_ARG_TYPE preceding this.

Good idea, will do.

> -   ST_POINTER_TYPE *a = (ST_POINTER_TYPE *) data,
> -              *pa,
> -              *pb,
> -              *pc,
> -              *pd,
> -              *pl,
> -              *pm,
> -              *pn;
> +   ST_POINTER_TYPE *a = (ST_POINTER_TYPE *) data;
> +   ST_POINTER_TYPE *pa;
>
> There doesn't seem to be material change for the above hunk.

In master, you can't write #define ST_ELEMENT_TYPE some_type *, which
seems like it would be quite useful.  You can use pointers as element
types, but only with a typedef name due to C parsing rules.  some_type
**a, *pa, ... declares some_type *pa, but we want some_type **pa.  I
don't want to have to introduce extra typedefs.  The change fixes that
problem by not using C's squirrelly variable declaration list syntax.

> +   while (left <= right)
> +   {
> +       size_t      mid = (left + right) / 2;
>
> The computation for midpoint should be left + (right-left)/2.

Right, my way can overflow.  Will fix.  Thanks!





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-06-16 22:05  Zhihong Yu <[email protected]>
  parent: Thomas Munro <[email protected]>
  1 sibling, 0 replies; 55+ messages in thread

From: Zhihong Yu @ 2021-06-16 22:05 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: Andres Freund <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

On Wed, Jun 16, 2021 at 2:54 PM Thomas Munro <[email protected]> wrote:

> Hi Zhihong,
>
> On Thu, Jun 17, 2021 at 8:13 AM Zhihong Yu <[email protected]> wrote:
> > In 0001-Add-bsearch-and-unique-templates-to-sort_template.h.patch :
> >
> > -                                       const ST_ELEMENT_TYPE *
> ST_SORT_PROTO_ARG);
> > +                                       const ST_ELEMENT_TYPE
> *ST_SORT_PROTO_ARG);
> >
> > It seems there is no real change in the line above. Better keep the
> original formation.
>
> Hmm, well it was only recently damaged by commit def5b065, and that's
> because I'd forgotten to put ST_ELEMENT_TYPE into typedefs.list, and I
> was correcting that in this patch.  (That file is used by
> pg_bsd_indent to decide if an identifier is a type or a variable,
> which affects whether '*' is formatted like a unary operator/type
> syntax or a binary operator.)
>
> >   *   - ST_COMPARE_ARG_TYPE - type of extra argument
> >   *
> > + *   To say that the comparator returns a type other than int, use:
> > + *
> > + *       - ST_COMPARE_TYPE - an integer type
> >
> > Since the ST_COMPARE_TYPE is meant to designate the type of the return
> value, maybe ST_COMPARE_RET_TYPE would be better name.
> > It also goes with ST_COMPARE_ARG_TYPE preceding this.
>
> Good idea, will do.
>
> > -   ST_POINTER_TYPE *a = (ST_POINTER_TYPE *) data,
> > -              *pa,
> > -              *pb,
> > -              *pc,
> > -              *pd,
> > -              *pl,
> > -              *pm,
> > -              *pn;
> > +   ST_POINTER_TYPE *a = (ST_POINTER_TYPE *) data;
> > +   ST_POINTER_TYPE *pa;
> >
> > There doesn't seem to be material change for the above hunk.
>
> In master, you can't write #define ST_ELEMENT_TYPE some_type *, which
> seems like it would be quite useful.  You can use pointers as element
> types, but only with a typedef name due to C parsing rules.  some_type
> **a, *pa, ... declares some_type *pa, but we want some_type **pa.  I
> don't want to have to introduce extra typedefs.  The change fixes that
> problem by not using C's squirrelly variable declaration list syntax.
>
> > +   while (left <= right)
> > +   {
> > +       size_t      mid = (left + right) / 2;
> >
> > The computation for midpoint should be left + (right-left)/2.
>
> Right, my way can overflow.  Will fix.  Thanks!
>

Hi,
Thanks for giving me background on typedefs.
The relevant changes look fine to me.

Cheers


^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-06-16 23:40  Tom Lane <[email protected]>
  parent: Thomas Munro <[email protected]>
  1 sibling, 1 reply; 55+ messages in thread

From: Tom Lane @ 2021-06-16 23:40 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: Zhihong Yu <[email protected]>; Andres Freund <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

Thomas Munro <[email protected]> writes:
> Hmm, well it was only recently damaged by commit def5b065, and that's
> because I'd forgotten to put ST_ELEMENT_TYPE into typedefs.list, and I
> was correcting that in this patch.

If ST_ELEMENT_TYPE isn't recognized as a typedef by the buildfarm's
typedef collectors, this sort of manual addition to typedefs.list
is not going to survive the next pgindent run.  No, I will NOT
promise to manually add it back every time.

We do already have special provision for injecting additional typedefs
in the pgindent script, so one possibility is to add it there:

-my @additional = ("bool\n");
+my @additional = ("bool\nST_ELEMENT_TYPE\n");

On the whole I'm not sure that this is a big enough formatting
issue to justify a special hack, though.  Is there any more than
the one line that gets misformatted?

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-06-17 01:07  Thomas Munro <[email protected]>
  parent: Tom Lane <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Thomas Munro @ 2021-06-17 01:07 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Zhihong Yu <[email protected]>; Andres Freund <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

On Thu, Jun 17, 2021 at 11:40 AM Tom Lane <[email protected]> wrote:
> Thomas Munro <[email protected]> writes:
> > Hmm, well it was only recently damaged by commit def5b065, and that's
> > because I'd forgotten to put ST_ELEMENT_TYPE into typedefs.list, and I
> > was correcting that in this patch.
>
> If ST_ELEMENT_TYPE isn't recognized as a typedef by the buildfarm's
> typedef collectors, this sort of manual addition to typedefs.list
> is not going to survive the next pgindent run.  No, I will NOT
> promise to manually add it back every time.
>
> We do already have special provision for injecting additional typedefs
> in the pgindent script, so one possibility is to add it there:
>
> -my @additional = ("bool\n");
> +my @additional = ("bool\nST_ELEMENT_TYPE\n");
>
> On the whole I'm not sure that this is a big enough formatting
> issue to justify a special hack, though.  Is there any more than
> the one line that gets misformatted?

Ohh.  In that case, I won't bother with that hunk and will live with
the extra space.  There are several other lines like this in the tree,
where people use caveman template macrology that is invisible to
whatever analyser is being used for that, and I can see that that's
just going to have to be OK for now.  Perhaps one day we could add a
secondary file, not updated by that mechanism, that holds a manually
maintained list for cases like this.





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-06-17 01:14  Tom Lane <[email protected]>
  parent: Thomas Munro <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Tom Lane @ 2021-06-17 01:14 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: Zhihong Yu <[email protected]>; Andres Freund <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

Thomas Munro <[email protected]> writes:
> Perhaps one day we could add a
> secondary file, not updated by that mechanism, that holds a manually
> maintained list for cases like this.

Yeah, the comments in pgindent already speculate about that.  For
now, those include and exclude lists are short enough that keeping
them inside the script seems a lot easier than building tooling
to get them from somewhere else.

The big problem in my mind, which would not be alleviated in the
slightest by having a separate file, is that it'd be easy to miss
removing entries if they ever become obsolete.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-06-17 01:20  Thomas Munro <[email protected]>
  parent: Tom Lane <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Thomas Munro @ 2021-06-17 01:20 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Zhihong Yu <[email protected]>; Andres Freund <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

On Thu, Jun 17, 2021 at 1:14 PM Tom Lane <[email protected]> wrote:
> The big problem in my mind, which would not be alleviated in the
> slightest by having a separate file, is that it'd be easy to miss
> removing entries if they ever become obsolete.

I suppose you could invent some kind of declaration syntax in a
comment near the use of the pseudo-typename in the source tree that is
mechanically extracted.





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-06-28 19:13  John Naylor <[email protected]>
  parent: Thomas Munro <[email protected]>
  1 sibling, 1 reply; 55+ messages in thread

From: John Naylor @ 2021-06-28 19:13 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: pgsql-hackers

On Wed, Jun 16, 2021 at 1:55 AM Thomas Munro <[email protected]> wrote:
[v2 patch]

Hi Thomas,

I plan to do some performance testing with VACUUM, ANALYZE etc soon, to see
if I can detect any significant differences.

I did a quick check of the MacOS/clang binary size (no debug symbols):

master:    8108408
0001-0009: 8125224

Later, I'll drill down into the individual patches and see if anything
stands out.

There were already some comments for v2 upthread about formatting and an
overflow hazard, but I did find a few more things to ask about:

- For my curiosity, there are a lot of calls to qsort/qunique in the tree
-- without having looked exhaustively, do these patches focus on cases
where there are bespoke comparator functions and/or hot code paths?

- Aside from the qsort{_arg} precedence, is there a practical reason for
keeping the new global functions in their own files?

- 0002 / 0004

+/* Search and unique functions inline in header. */

The functions are pretty small, but is there some advantage for inlining
these?

- 0003

#include "lib/qunique.h" is not needed anymore.

This isn't quite relevant for the current patch perhaps, but I'm wondering
why we don't already call bsearch for RelationHasSysCache() and
RelationSupportsSysCache().

- 0008

+#define ST_COMPARE(a, b, cxt) \
+ DatumGetInt32(FunctionCall2Coll(&cxt->flinfo, cxt->collation, *a, *b))

This seems like a pretty heavyweight comparison, so I'm not sure inlining
buys us much, but it seems also there are fewer branches this way. I'll
come up with a test and see what happens.

--
John Naylor
EDB: http://www.enterprisedb.com


^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-06-29 00:16  Thomas Munro <[email protected]>
  parent: John Naylor <[email protected]>
  0 siblings, 2 replies; 55+ messages in thread

From: Thomas Munro @ 2021-06-29 00:16 UTC (permalink / raw)
  To: John Naylor <[email protected]>; +Cc: pgsql-hackers

Hi John,

On Tue, Jun 29, 2021 at 7:13 AM John Naylor
<[email protected]> wrote:
> I plan to do some performance testing with VACUUM, ANALYZE etc soon, to see if I can detect any significant differences.

Thanks!

> I did a quick check of the MacOS/clang binary size (no debug symbols):
>
> master:    8108408
> 0001-0009: 8125224

Not too bad.

> Later, I'll drill down into the individual patches and see if anything stands out.
>
> There were already some comments for v2 upthread about formatting and an overflow hazard, but I did find a few more things to ask about:

Right, here's an update with fixes discussed earlier with Zhihong and Tom:

* COMPARE_TYPE -> COMPARE_RET_TYPE
* quit fighting with pgindent (I will try to fix this problem generally later)
* fix overflow hazard

> - For my curiosity, there are a lot of calls to qsort/qunique in the tree -- without having looked exhaustively, do these patches focus on cases where there are bespoke comparator functions and/or hot code paths?

Patches 0006-0009 are highly specialised for local usage by a single
module, and require some kind of evidence that they're worth their
bytes, and the onus is on me there of course -- but any ideas and
feedback are welcome.  There are other opportunities like these, maybe
better ones.  That reminds me: I recently had a perf report from
Andres that showed the qsort in compute_scalar_stats() as quite hot.
That's probably a good candidate, and is not yet done in the current
patch set.

The lower numbered patches are all things that are reused in many
places, and in my humble opinion improve the notation and type safety
and code deduplication generally when working with common types
ItemPtr, BlockNumber, Oid, aside from any performance arguments.  At
least the ItemPtr stuff *might* also speed something useful up.

I tried to measure a speedup in vacuum, but so far I have not.  I did
learn some things though:  While doing that with an uncorrelated index
and a lot of deleted tuples, I found that adding more
maintenance_work_mem doesn't help beyond a few MB, because then cache
misses dominate to the point where it's not better than doing multiple
passes (and this is familiar to me from work on hash joins).  If I
turned on huge pages on Linux and set min_dynamic_shared_memory so
that the parallel DSM used by vacuum lives in huge pages, then
parallel vacuum with a large maintenance_work_mem starts to do much
better than non-parallel vacuum by improving the TLB misses (as with
hash joins).  I thought that was quite interesting!  Perhaps
bsearch_itemptr might help with correlated indexes with a lot of
deleted indexes (so not dominated by cache misses), though?

(I wouldn't be suprised if someone comes up with a much better idea
than bsearch for that anyway...  a few ideas have been suggested.)

> - Aside from the qsort{_arg} precedence, is there a practical reason for keeping the new global functions in their own files?

Better idea for layout welcome.  One thing I wondered while trying to
figure out where to put functions that operate on itemptr: why is
itemptr_encode() in src/include/catalog/index.h?!

> - 0002 / 0004
>
> +/* Search and unique functions inline in header. */
>
> The functions are pretty small, but is there some advantage for inlining these?

Glibc's bsearch definition is already in a header for inlining (as is
our qunique), so I thought I should preserve that characteristic on
principle.  I don't have any evidence though.  Other libcs I looked at
didn't have bsearch in a header.  So by doing this we make the
generated code the same across platforms (all other relevant things
being equal).  I don't know if it really makes much difference,
especially since in this case the comparator and size would still be
inlined if we defined it in the .c (unlike standard bsearch)...
Probably only lazy_tid_reaped() calls it enough to potentially show
any difference in a non-microbenchmark workload, if anything does.

> - 0003
>
> #include "lib/qunique.h" is not needed anymore.

Fixed.

> This isn't quite relevant for the current patch perhaps, but I'm wondering why we don't already call bsearch for RelationHasSysCache() and RelationSupportsSysCache().

Right, I missed that.  Done.  Nice to delete some more code.

> - 0008
>
> +#define ST_COMPARE(a, b, cxt) \
> + DatumGetInt32(FunctionCall2Coll(&cxt->flinfo, cxt->collation, *a, *b))
>
> This seems like a pretty heavyweight comparison, so I'm not sure inlining buys us much, but it seems also there are fewer branches this way. I'll come up with a test and see what happens.

I will be very interested to see the results.  Thanks!


Attachments:

  [application/x-compressed-tar] more-sort-search-specializations-v3.tgz (11.6K, ../../CA+hUKG+-Nx4CfutUsgG-x7qfzmV-VHtcdD_kyRksE4t7CWYN3w@mail.gmail.com/2-more-sort-search-specializations-v3.tgz)
  download

^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-06-29 01:11  Thomas Munro <[email protected]>
  parent: Thomas Munro <[email protected]>
  1 sibling, 1 reply; 55+ messages in thread

From: Thomas Munro @ 2021-06-29 01:11 UTC (permalink / raw)
  To: John Naylor <[email protected]>; +Cc: pgsql-hackers

I spotted a mistake in v3: I didn't rename ST_COMPARE_TYPE to
ST_COMPARE_RET_TYPE in the 0009 patch (well, I did, but forgot to
commit before I ran git format-patch).  I won't send another tarball
just for that, but will correct it next time.





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-06-29 06:56  Thomas Munro <[email protected]>
  parent: Thomas Munro <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Thomas Munro @ 2021-06-29 06:56 UTC (permalink / raw)
  To: John Naylor <[email protected]>; +Cc: pgsql-hackers

On Tue, Jun 29, 2021 at 1:11 PM Thomas Munro <[email protected]> wrote:
> I spotted a mistake in v3: I didn't rename ST_COMPARE_TYPE to
> ST_COMPARE_RET_TYPE in the 0009 patch (well, I did, but forgot to
> commit before I ran git format-patch).  I won't send another tarball
> just for that, but will correct it next time.

Here's a version that includes a rather hackish test module that you
might find useful to explore various weird effects.  Testing sorting
routines is really hard, of course... there's a zillion parameters and
things you could do in the data and cache effects etc etc.  One of the
main things that jumps out pretty clearly though with these simple
tests is that sorting 6 byte ItemPointerData objects is *really slow*
compared to more natural object sizes (look at the times and the
MEMORY values in the scripts).  Another is that specialised sort
functions are much faster than traditional qsort (being one of the
goals of this exercise).  Sadly, the 64 bit comparison technique is
not looking too good in the output of this test.


Attachments:

  [application/x-compressed-tar] more-sort-search-specializations-v4.tgz (16.1K, ../../CA+hUKGLPommgNw-SVwUGkw1YmTDwmJ5vSKO0kFnZfbRHtNFW5w@mail.gmail.com/2-more-sort-search-specializations-v4.tgz)
  download

^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-06-29 16:40  John Naylor <[email protected]>
  parent: Thomas Munro <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: John Naylor @ 2021-06-29 16:40 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: pgsql-hackers

On Tue, Jun 29, 2021 at 2:56 AM Thomas Munro <[email protected]> wrote:

> Here's a version that includes a rather hackish test module that you
> might find useful to explore various weird effects.  Testing sorting
> routines is really hard, of course... there's a zillion parameters and
> things you could do in the data and cache effects etc etc.  One of the

That module is incredibly useful!

Yeah, while brushing up on recent findings on sorting, it's clear there's a
huge amount of options with different tradeoffs. I did see your tweet last
year about the "small sort" threshold that was tested on a VAX machine, but
hadn't given it any thought til now. Looking around, I've seen quite a
range, always with the caveat of "it depends". A couple interesting
variations:

Golang uses 12, with an extra tweak:

// Do ShellSort pass with gap 6
// It could be written in this simplified form cause b-a <= 12
for i := a + 6; i < b; i++ {
    if data.Less(i, i-6) {
        data.Swap(i, i-6)
    }
}
insertionSort(data, a, b)

Andrei Alexandrescu gave a couple talks discussing the small-sort part of
quicksort, and demonstrated a ruthlessly-optimized make-heap +
unguarded-insertion-sort, using a threshold of 256. He reported a 6%
speed-up sorting a million doubles, IIRC:

video: https://www.youtube.com/watch?v=FJJTYQYB1JQ
slides:
https://github.com/CppCon/CppCon2019/blob/master/Presentations/speed_is_found_in_the_minds_of_people...

That might not be workable for us, but it's a fun talk.

> main things that jumps out pretty clearly though with these simple
> tests is that sorting 6 byte ItemPointerData objects is *really slow*
> compared to more natural object sizes (look at the times and the
> MEMORY values in the scripts).  Another is that specialised sort
> functions are much faster than traditional qsort (being one of the
> goals of this exercise).  Sadly, the 64 bit comparison technique is
> not looking too good in the output of this test.

One of the points of the talk I linked to is "if doing the sensible thing
makes things worse, try something silly instead".

Anyway, I'll play around with the scripts and see if something useful pops
out.

--
John Naylor
EDB: http://www.enterprisedb.com


^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-07-01 16:39  John Naylor <[email protected]>
  parent: John Naylor <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: John Naylor @ 2021-07-01 16:39 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: pgsql-hackers

I wrote:

> One of the points of the talk I linked to is "if doing the sensible thing
makes things worse, try something silly instead".

For item pointers, it made sense to try doing math to reduce the number of
branches. That made things worse, so let's try the opposite: Increase the
number of branches so we do less math. In the attached patch (applies on
top of your 0012 and a .txt to avoid confusing the CF bot), I test a new
comparator with this approach, and also try a wider range of thresholds.
The thresholds don't seem to make any noticeable difference with this data
type, but the new comparator (cmp=ids below) gives a nice speedup in this
test:

# SELECT test_sort_itemptr();
NOTICE:  [traditional qsort] order=random, threshold=7, cmp=32, test=0,
time=4.964657
NOTICE:  [traditional qsort] order=random, threshold=7, cmp=32, test=1,
time=5.185384
NOTICE:  [traditional qsort] order=random, threshold=7, cmp=32, test=2,
time=5.058179
NOTICE:  order=random, threshold=7, cmp=std, test=0, time=2.810627
NOTICE:  order=random, threshold=7, cmp=std, test=1, time=2.804940
NOTICE:  order=random, threshold=7, cmp=std, test=2, time=2.800677
NOTICE:  order=random, threshold=7, cmp=ids, test=0, time=1.692711
NOTICE:  order=random, threshold=7, cmp=ids, test=1, time=1.694546
NOTICE:  order=random, threshold=7, cmp=ids, test=2, time=1.692839
NOTICE:  order=random, threshold=12, cmp=std, test=0, time=2.687033
NOTICE:  order=random, threshold=12, cmp=std, test=1, time=2.681974
NOTICE:  order=random, threshold=12, cmp=std, test=2, time=2.687833
NOTICE:  order=random, threshold=12, cmp=ids, test=0, time=1.666418
NOTICE:  order=random, threshold=12, cmp=ids, test=1, time=1.666188
NOTICE:  order=random, threshold=12, cmp=ids, test=2, time=1.664176
NOTICE:  order=random, threshold=16, cmp=std, test=0, time=2.574147
NOTICE:  order=random, threshold=16, cmp=std, test=1, time=2.579981
NOTICE:  order=random, threshold=16, cmp=std, test=2, time=2.572861
NOTICE:  order=random, threshold=16, cmp=ids, test=0, time=1.699432
NOTICE:  order=random, threshold=16, cmp=ids, test=1, time=1.703075
NOTICE:  order=random, threshold=16, cmp=ids, test=2, time=1.697173
NOTICE:  order=random, threshold=32, cmp=std, test=0, time=2.750040
NOTICE:  order=random, threshold=32, cmp=std, test=1, time=2.744138
NOTICE:  order=random, threshold=32, cmp=std, test=2, time=2.748026
NOTICE:  order=random, threshold=32, cmp=ids, test=0, time=1.677414
NOTICE:  order=random, threshold=32, cmp=ids, test=1, time=1.683792
NOTICE:  order=random, threshold=32, cmp=ids, test=2, time=1.701309
NOTICE:  [traditional qsort] order=increasing, threshold=7, cmp=32, test=0,
time=2.543837
NOTICE:  [traditional qsort] order=increasing, threshold=7, cmp=32, test=1,
time=2.290497
NOTICE:  [traditional qsort] order=increasing, threshold=7, cmp=32, test=2,
time=2.262956
NOTICE:  order=increasing, threshold=7, cmp=std, test=0, time=1.033052
NOTICE:  order=increasing, threshold=7, cmp=std, test=1, time=1.032079
NOTICE:  order=increasing, threshold=7, cmp=std, test=2, time=1.041836
NOTICE:  order=increasing, threshold=7, cmp=ids, test=0, time=0.367355
NOTICE:  order=increasing, threshold=7, cmp=ids, test=1, time=0.367428
NOTICE:  order=increasing, threshold=7, cmp=ids, test=2, time=0.367384
NOTICE:  order=increasing, threshold=12, cmp=std, test=0, time=1.004991
NOTICE:  order=increasing, threshold=12, cmp=std, test=1, time=1.008045
NOTICE:  order=increasing, threshold=12, cmp=std, test=2, time=1.010778
NOTICE:  order=increasing, threshold=12, cmp=ids, test=0, time=0.370944
NOTICE:  order=increasing, threshold=12, cmp=ids, test=1, time=0.368669
NOTICE:  order=increasing, threshold=12, cmp=ids, test=2, time=0.370100
NOTICE:  order=increasing, threshold=16, cmp=std, test=0, time=1.023682
NOTICE:  order=increasing, threshold=16, cmp=std, test=1, time=1.025805
NOTICE:  order=increasing, threshold=16, cmp=std, test=2, time=1.022005
NOTICE:  order=increasing, threshold=16, cmp=ids, test=0, time=0.365398
NOTICE:  order=increasing, threshold=16, cmp=ids, test=1, time=0.365586
NOTICE:  order=increasing, threshold=16, cmp=ids, test=2, time=0.364807
NOTICE:  order=increasing, threshold=32, cmp=std, test=0, time=0.950780
NOTICE:  order=increasing, threshold=32, cmp=std, test=1, time=0.949920
NOTICE:  order=increasing, threshold=32, cmp=std, test=2, time=0.953239
NOTICE:  order=increasing, threshold=32, cmp=ids, test=0, time=0.367866
NOTICE:  order=increasing, threshold=32, cmp=ids, test=1, time=0.372179
NOTICE:  order=increasing, threshold=32, cmp=ids, test=2, time=0.371115
NOTICE:  [traditional qsort] order=decreasing, threshold=7, cmp=32, test=0,
time=2.317475
NOTICE:  [traditional qsort] order=decreasing, threshold=7, cmp=32, test=1,
time=2.323446
NOTICE:  [traditional qsort] order=decreasing, threshold=7, cmp=32, test=2,
time=2.326714
NOTICE:  order=decreasing, threshold=7, cmp=std, test=0, time=1.022270
NOTICE:  order=decreasing, threshold=7, cmp=std, test=1, time=1.015133
NOTICE:  order=decreasing, threshold=7, cmp=std, test=2, time=1.016367
NOTICE:  order=decreasing, threshold=7, cmp=ids, test=0, time=0.386884
NOTICE:  order=decreasing, threshold=7, cmp=ids, test=1, time=0.388397
NOTICE:  order=decreasing, threshold=7, cmp=ids, test=2, time=0.386328
NOTICE:  order=decreasing, threshold=12, cmp=std, test=0, time=0.993594
NOTICE:  order=decreasing, threshold=12, cmp=std, test=1, time=0.995031
NOTICE:  order=decreasing, threshold=12, cmp=std, test=2, time=0.995320
NOTICE:  order=decreasing, threshold=12, cmp=ids, test=0, time=0.391243
NOTICE:  order=decreasing, threshold=12, cmp=ids, test=1, time=0.391938
NOTICE:  order=decreasing, threshold=12, cmp=ids, test=2, time=0.392478
NOTICE:  order=decreasing, threshold=16, cmp=std, test=0, time=1.006240
NOTICE:  order=decreasing, threshold=16, cmp=std, test=1, time=1.009817
NOTICE:  order=decreasing, threshold=16, cmp=std, test=2, time=1.010281
NOTICE:  order=decreasing, threshold=16, cmp=ids, test=0, time=0.386388
NOTICE:  order=decreasing, threshold=16, cmp=ids, test=1, time=0.385801
NOTICE:  order=decreasing, threshold=16, cmp=ids, test=2, time=0.384484
NOTICE:  order=decreasing, threshold=32, cmp=std, test=0, time=0.959647
NOTICE:  order=decreasing, threshold=32, cmp=std, test=1, time=0.958833
NOTICE:  order=decreasing, threshold=32, cmp=std, test=2, time=0.960234
NOTICE:  order=decreasing, threshold=32, cmp=ids, test=0, time=0.403014
NOTICE:  order=decreasing, threshold=32, cmp=ids, test=1, time=0.393329
NOTICE:  order=decreasing, threshold=32, cmp=ids, test=2, time=0.395659

--
John Naylor
EDB: http://www.enterprisedb.com


Attachments:

  [application/octet-stream] itemptr-cmp-blockids.patch (4.5K, ../../CAFBsxsG_c24CHKA3cWrOP1HynWGLOkLb8hyZfsD9db5g-ZPagA@mail.gmail.com/3-itemptr-cmp-blockids.patch)
  download | inline diff:
diff --git a/src/test/modules/test_sort_perf/make-itemptr-tests.sh b/src/test/modules/test_sort_perf/make-itemptr-tests.sh
index 9151ea703f..fd0048eba3 100755
--- a/src/test/modules/test_sort_perf/make-itemptr-tests.sh
+++ b/src/test/modules/test_sort_perf/make-itemptr-tests.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # different values to test for insertion sorts
-THRESHOLDS="7 8 9 10 11 12 13"
+THRESHOLDS="7 12 16 32"
 
 # amount of data to sort, in megabytes
 MEMORY=128
@@ -18,10 +18,9 @@ for threshold in $THRESHOLDS ; do
   echo "#define ST_DEFINE"
   echo "#include \"lib/sort_template.h\""
   echo
-  echo "#define ST_SORT sort64_${threshold}_itemptr"
+  echo "#define ST_SORT sort_ids_${threshold}_itemptr"
   echo "#define ST_ELEMENT_TYPE ItemPointerData"
-  echo "#define ST_COMPARE(a, b) (itemptr_encode(a) - itemptr_encode(b))"
-  echo "#define ST_COMPARE_RET_TYPE int64"
+  echo "#define ST_COMPARE(a, b) itemptr_comparator_blockids(a, b)"
   echo "#define ST_SCOPE static"
   echo "#define ST_SORT_SMALL_THRESHOLD $threshold"
   echo "#define ST_CHECK_FOR_INTERRUPTS"
@@ -76,7 +75,7 @@ for order in random increasing decreasing ; do
     echo "        sort_${threshold}_itemptr(sorted, nobjects);"
     echo "        INSTR_TIME_SET_CURRENT(end_time);"
     echo "        INSTR_TIME_SUBTRACT(end_time, start_time);"
-    echo "        elog(NOTICE, \"order=$order, threshold=$threshold, cmp=32, test=%d, time=%f\", i, INSTR_TIME_GET_DOUBLE(end_time));"
+    echo "        elog(NOTICE, \"order=$order, threshold=$threshold, cmp=std, test=%d, time=%f\", i, INSTR_TIME_GET_DOUBLE(end_time));"
     echo "    }"
     echo "    for (int i = 0; i < 3; ++i)"
     echo "    {"
@@ -84,10 +83,10 @@ for order in random increasing decreasing ; do
     echo "        memcpy(sorted, unsorted, sizeof(sorted[0]) * nobjects);"
     echo "        INSTR_TIME_SET_CURRENT(start_time);"
     echo "        memcpy(sorted, unsorted, sizeof(sorted[0]) * nobjects);"
-    echo "        sort64_${threshold}_itemptr(sorted, nobjects);"
+    echo "        sort_ids_${threshold}_itemptr(sorted, nobjects);"
     echo "        INSTR_TIME_SET_CURRENT(end_time);"
     echo "        INSTR_TIME_SUBTRACT(end_time, start_time);"
-    echo "        elog(NOTICE, \"order=$order, threshold=$threshold, cmp=64, test=%d, time=%f\", i, INSTR_TIME_GET_DOUBLE(end_time));"
+    echo "        elog(NOTICE, \"order=$order, threshold=$threshold, cmp=ids, test=%d, time=%f\", i, INSTR_TIME_GET_DOUBLE(end_time));"
     echo "    }"
   done
 done
diff --git a/src/test/modules/test_sort_perf/test_sort_perf.c b/src/test/modules/test_sort_perf/test_sort_perf.c
index 05a10924f3..1ebaf41329 100644
--- a/src/test/modules/test_sort_perf/test_sort_perf.c
+++ b/src/test/modules/test_sort_perf/test_sort_perf.c
@@ -8,6 +8,44 @@
 
 #include <stdlib.h>
 
+/*
+ * ItemPointerGetBlockIdHiNoCheck
+ *		Returns the high short of a disk item pointer blockid.
+ */
+#define ItemPointerGetBlockIdHiNoCheck(pointer) \
+( \
+	(pointer)->ip_blkid.bi_hi \
+)
+
+/*
+ * ItemPointerGetBlockIdHi
+ *		As above, but verifies that the item pointer looks valid.
+ */
+#define ItemPointerGetBlockIdHi(pointer) \
+( \
+	AssertMacro(ItemPointerIsValid(pointer)), \
+	ItemPointerGetBlockIdHiNoCheck(pointer) \
+)
+
+/*
+ * ItemPointerGetBlockIdLoNoCheck
+ *		Returns the low short of a disk item pointer blockid.
+ */
+#define ItemPointerGetBlockIdLoNoCheck(pointer) \
+( \
+	(pointer)->ip_blkid.bi_lo \
+)
+
+/*
+ * ItemPointerGetBlockIdLo
+ *		As above, but verifies that the item pointer looks valid.
+ */
+#define ItemPointerGetBlockIdLo(pointer) \
+( \
+	AssertMacro(ItemPointerIsValid(pointer)), \
+	ItemPointerGetBlockIdHiNoCheck(pointer) \
+)
+
 static int
 itemptr_comparator(const void *a, const void *b)
 {
@@ -29,6 +67,38 @@ itemptr_comparator(const void *a, const void *b)
 	return 0;
 }
 
+static int
+itemptr_comparator_blockids(const void *a, const void *b)
+{
+	const ItemPointerData *ipa = (const ItemPointerData *) a;
+	const ItemPointerData *ipb = (const ItemPointerData *) b;
+
+	uint16 ba_hi = ItemPointerGetBlockIdHi(ipa);
+	uint16 bb_hi = ItemPointerGetBlockIdHi(ipb);
+
+	if (ba_hi < bb_hi)
+		return -1;
+	if (ba_hi > bb_hi)
+		return 1;
+
+	uint16 ba_lo = ItemPointerGetBlockIdLo(ipa);
+	uint16 bb_lo = ItemPointerGetBlockIdLo(ipb);
+
+	if (ba_lo < bb_lo)
+		return -1;
+	if (ba_lo > bb_lo)
+		return 1;
+
+	OffsetNumber oa = ItemPointerGetOffsetNumber(ipa);
+	OffsetNumber ob = ItemPointerGetOffsetNumber(ipb);
+
+	if (oa < ob)
+		return -1;
+	if (oa > ob)
+		return 1;
+	return 0;
+}
+
 PG_MODULE_MAGIC;
 
 /* include the generated code */


^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-07-01 22:09  Thomas Munro <[email protected]>
  parent: John Naylor <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Thomas Munro @ 2021-07-01 22:09 UTC (permalink / raw)
  To: John Naylor <[email protected]>; +Cc: pgsql-hackers

On Fri, Jul 2, 2021 at 4:39 AM John Naylor <[email protected]> wrote:
> For item pointers, it made sense to try doing math to reduce the number of branches. That made things worse, so let's try the opposite: Increase the number of branches so we do less math. In the attached patch (applies on top of your 0012 and a .txt to avoid confusing the CF bot), I test a new comparator with this approach, and also try a wider range of thresholds. The thresholds don't seem to make any noticeable difference with this data type, but the new comparator (cmp=ids below) gives a nice speedup in this test:

> NOTICE:  [traditional qsort] order=random, threshold=7, cmp=32, test=0, time=4.964657

> NOTICE:  order=random, threshold=7, cmp=std, test=0, time=2.810627

> NOTICE:  order=random, threshold=7, cmp=ids, test=0, time=1.692711

Oooh.  So, the awkwardness of the 64 maths with unaligned inputs (even
though we obtain all inputs with 16 bit loads) was hurting, and you
realised the same sort of thing might be happening also with the 32
bit version and went the other way.  (It'd be nice to understand
exactly why.)

I tried your 16 bit comparison version on Intel, AMD and Apple CPUs
and the results were all in the same ballpark.  For random input, I
see something like ~1.7x speedup over traditional qsort from
specialising (cmp=std), and ~2.7x from going 16 bit (cmp=ids).  For
increasing and decreasing input, it's ~2x speedup from specialising
and ~4x speedup from going 16 bit.  Beautiful.

One thing I'm wondering about is whether it's worth having stuff to
support future experimentation like ST_SORT_SMALL_THRESHOLD and
ST_COMPARE_RET_TYPE in the tree, or whether we should pare it back to
the minimal changes that definitely produce results.  I think I'd like
to keep those changes: even if it may be some time, possibly an
infinite amount, before we figure out how to tune the thresholds
profitably, giving them names instead of using magic numbers seems
like progress.

The Alexandrescu talk was extremely entertaining, thanks.





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-07-02 02:32  John Naylor <[email protected]>
  parent: Thomas Munro <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: John Naylor @ 2021-07-02 02:32 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: pgsql-hackers

On Thu, Jul 1, 2021 at 6:10 PM Thomas Munro <[email protected]> wrote:

> One thing I'm wondering about is whether it's worth having stuff to
> support future experimentation like ST_SORT_SMALL_THRESHOLD and
> ST_COMPARE_RET_TYPE in the tree, or whether we should pare it back to
> the minimal changes that definitely produce results.  I think I'd like
> to keep those changes: even if it may be some time, possibly an
> infinite amount, before we figure out how to tune the thresholds
> profitably, giving them names instead of using magic numbers seems
> like progress.

I suspect if we experiment on two extremes of type "heaviness" (accessing
and comparing trivial or not), such as uint32 and tuplesort, we'll have a
pretty good idea what the parameters should be, if anything different. I'll
do some testing along those lines.

(BTW, I just realized I lied and sent a .patch file after all, oops)

--
John Naylor
EDB: http://www.enterprisedb.com


^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-07-04 04:27  Thomas Munro <[email protected]>
  parent: John Naylor <[email protected]>
  0 siblings, 2 replies; 55+ messages in thread

From: Thomas Munro @ 2021-07-04 04:27 UTC (permalink / raw)
  To: John Naylor <[email protected]>; +Cc: pgsql-hackers

On Fri, Jul 2, 2021 at 2:32 PM John Naylor <[email protected]> wrote:
> I suspect if we experiment on two extremes of type "heaviness" (accessing and comparing trivial or not), such as uint32 and tuplesort, we'll have a pretty good idea what the parameters should be, if anything different. I'll do some testing along those lines.

Cool.

Since you are experimenting with tuplesort and likely thinking similar
thoughts, here's a patch I've been using to explore that area.  I've
seen it get, for example, ~1.18x speedup for simple index builds in
favourable winds (YMMV, early hacking results only).  Currently, it
kicks in when the leading column is of type int4, int8, timestamp,
timestamptz, date or text + friends (when abbreviatable, currently
that means "C" and ICU collations only), while increasing the
executable by only 8.5kB (Clang, amd64, -O2, no debug).

These types are handled with just three specialisations.  Their custom
"fast" comparators all boiled down to comparisons of datum bits,
varying only in signedness and width, so I tried throwing them away
and using 3 new common routines.  Then I extended
tuplesort_sort_memtuples()'s pre-existing specialisation dispatch to
recognise qualifying users of those and select 3 corresponding sort
specialisations.

It might turn out to be worth burning some more executable size on
extra variants (for example, see XXX notes in the code comments for
opportunities; one could also go nuts trying smaller things like
special cases for not-null, nulls first, reverse sort, ... to kill all
those branches), or not.

From 62a8acbc745f3b4a90c9a14b6b61989a9d83bece Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Sat, 3 Jul 2021 19:02:10 +1200
Subject: [PATCH] WIP: Accelerate tuple sorting for common types.

Several data types have their own fast comparator functions that can be
replaced with common binary or signed comparator functions.  These
functions can then be recognized by tuplesort.c and used to dispatch to
corresponding specialized sort functions, to accelerate sorting.

XXX WIP, experiment grade only, many open questions...
---
 src/backend/access/nbtree/nbtcompare.c |  22 ++--
 src/backend/utils/adt/date.c           |  15 +--
 src/backend/utils/adt/timestamp.c      |  11 ++
 src/backend/utils/adt/varlena.c        |  26 +---
 src/backend/utils/sort/tuplesort.c     | 161 ++++++++++++++++++++++++-
 src/include/utils/sortsupport.h        | 115 ++++++++++++++++++
 6 files changed, 295 insertions(+), 55 deletions(-)

diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c
index 7ac73cb8c2..204cf778fb 100644
--- a/src/backend/access/nbtree/nbtcompare.c
+++ b/src/backend/access/nbtree/nbtcompare.c
@@ -119,26 +119,12 @@ btint4cmp(PG_FUNCTION_ARGS)
 		PG_RETURN_INT32(A_LESS_THAN_B);
 }
 
-static int
-btint4fastcmp(Datum x, Datum y, SortSupport ssup)
-{
-	int32		a = DatumGetInt32(x);
-	int32		b = DatumGetInt32(y);
-
-	if (a > b)
-		return A_GREATER_THAN_B;
-	else if (a == b)
-		return 0;
-	else
-		return A_LESS_THAN_B;
-}
-
 Datum
 btint4sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
-	ssup->comparator = btint4fastcmp;
+	ssup->comparator = ssup_datum_int32_cmp;
 	PG_RETURN_VOID();
 }
 
@@ -156,6 +142,7 @@ btint8cmp(PG_FUNCTION_ARGS)
 		PG_RETURN_INT32(A_LESS_THAN_B);
 }
 
+#ifndef USE_FLOAT8_BYVAL
 static int
 btint8fastcmp(Datum x, Datum y, SortSupport ssup)
 {
@@ -169,13 +156,18 @@ btint8fastcmp(Datum x, Datum y, SortSupport ssup)
 	else
 		return A_LESS_THAN_B;
 }
+#endif
 
 Datum
 btint8sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
+#ifdef USE_FLOAT8_BYVAL
+	ssup->comparator = ssup_datum_signed_cmp;
+#else
 	ssup->comparator = btint8fastcmp;
+#endif
 	PG_RETURN_VOID();
 }
 
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 0bea16cb67..350c662d50 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -438,25 +438,12 @@ date_cmp(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(0);
 }
 
-static int
-date_fastcmp(Datum x, Datum y, SortSupport ssup)
-{
-	DateADT		a = DatumGetDateADT(x);
-	DateADT		b = DatumGetDateADT(y);
-
-	if (a < b)
-		return -1;
-	else if (a > b)
-		return 1;
-	return 0;
-}
-
 Datum
 date_sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
-	ssup->comparator = date_fastcmp;
+	ssup->comparator = ssup_datum_int32_cmp;
 	PG_RETURN_VOID();
 }
 
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 79761f809c..c678517db6 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -37,6 +37,7 @@
 #include "utils/datetime.h"
 #include "utils/float.h"
 #include "utils/numeric.h"
+#include "utils/sortsupport.h"
 
 /*
  * gcc's -ffast-math switch breaks routines that expect exact results from
@@ -2155,6 +2156,7 @@ timestamp_cmp(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
 }
 
+#ifndef USE_FLOAT8_BYVAL
 /* note: this is used for timestamptz also */
 static int
 timestamp_fastcmp(Datum x, Datum y, SortSupport ssup)
@@ -2164,13 +2166,22 @@ timestamp_fastcmp(Datum x, Datum y, SortSupport ssup)
 
 	return timestamp_cmp_internal(a, b);
 }
+#endif
 
 Datum
 timestamp_sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
+#ifdef USE_FLOAT8_BYVAL
+	/*
+	 * If this build has pass-by-value timestamps, then we can use a standard
+	 * comparator function.
+	 */
+	ssup->comparator = ssup_datum_signed_cmp;
+#else
 	ssup->comparator = timestamp_fastcmp;
+#endif
 	PG_RETURN_VOID();
 }
 
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index d2a11b1b5d..4709d8129e 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -127,7 +127,6 @@ static int	namefastcmp_c(Datum x, Datum y, SortSupport ssup);
 static int	varlenafastcmp_locale(Datum x, Datum y, SortSupport ssup);
 static int	namefastcmp_locale(Datum x, Datum y, SortSupport ssup);
 static int	varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup);
-static int	varstrcmp_abbrev(Datum x, Datum y, SortSupport ssup);
 static Datum varstr_abbrev_convert(Datum original, SortSupport ssup);
 static bool varstr_abbrev_abort(int memtupcount, SortSupport ssup);
 static int32 text_length(Datum str);
@@ -2175,7 +2174,7 @@ varstr_sortsupport(SortSupport ssup, Oid typid, Oid collid)
 			initHyperLogLog(&sss->abbr_card, 10);
 			initHyperLogLog(&sss->full_card, 10);
 			ssup->abbrev_full_comparator = ssup->comparator;
-			ssup->comparator = varstrcmp_abbrev;
+			ssup->comparator = ssup_datum_binary_cmp;
 			ssup->abbrev_converter = varstr_abbrev_convert;
 			ssup->abbrev_abort = varstr_abbrev_abort;
 		}
@@ -2461,27 +2460,6 @@ varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup)
 	return result;
 }
 
-/*
- * Abbreviated key comparison func
- */
-static int
-varstrcmp_abbrev(Datum x, Datum y, SortSupport ssup)
-{
-	/*
-	 * When 0 is returned, the core system will call varstrfastcmp_c()
-	 * (bpcharfastcmp_c() in BpChar case) or varlenafastcmp_locale().  Even a
-	 * strcmp() on two non-truncated strxfrm() blobs cannot indicate *equality*
-	 * authoritatively, for the same reason that there is a strcoll()
-	 * tie-breaker call to strcmp() in varstr_cmp().
-	 */
-	if (x > y)
-		return 1;
-	else if (x == y)
-		return 0;
-	else
-		return -1;
-}
-
 /*
  * Conversion routine for sortsupport.  Converts original to abbreviated key
  * representation.  Our encoding strategy is simple -- pack the first 8 bytes
@@ -2710,7 +2688,7 @@ done:
 	/*
 	 * Byteswap on little-endian machines.
 	 *
-	 * This is needed so that varstrcmp_abbrev() (an unsigned integer 3-way
+	 * This is needed so that ssup_datum_binary_cmp() (an unsigned integer 3-way
 	 * comparator) works correctly on all platforms.  If we didn't do this,
 	 * the comparator would have to call memcmp() with a pair of pointers to
 	 * the first byte of each abbreviated key, which is slower.
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 22972071ff..455bb652b8 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -669,14 +669,101 @@ static void free_sort_tuple(Tuplesortstate *state, SortTuple *stup);
 static void tuplesort_free(Tuplesortstate *state);
 static void tuplesort_updatemax(Tuplesortstate *state);
 
+/*
+ * Specialized comparators that we can inline into specialized sorts.  The goal
+ * is to try to sort two tuples without having to follow the pointers to the
+ * comparator or the tuple.
+ *
+ * XXX: For now, these fall back to comparator functions that will compare the
+ * leading datum a second time.
+ *
+ * XXX: For now, there is no specialization for cases where datum1 is
+ * authoritative and we don't even need to fall back to a callback at all (that
+ * would be true for types like int4/int8/timestamp/date, but not true for
+ * abbreviations of text or multi-key sorts.  There could be!  Is it worth it?
+ */
+
+/* Used if first key's comparator is ssup_datum_binary_compare */
+static pg_attribute_always_inline int
+qsort_tuple_binary_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
+{
+	int			compare;
+
+	compare = ApplyBinarySortComparator(a->datum1, a->isnull1,
+										b->datum1, b->isnull1,
+										&state->sortKeys[0]);
+	if (compare != 0)
+		return compare;
+
+	return state->comparetup(a, b, state);
+}
+
+/* Used if first key's comparator is ssup_datum_signed_compare */
+static pg_attribute_always_inline int
+qsort_tuple_signed_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
+{
+	int			compare;
+
+	compare = ApplySignedSortComparator(a->datum1, a->isnull1,
+										b->datum1, b->isnull1,
+										&state->sortKeys[0]);
+	if (compare != 0)
+		return compare;
+
+	return state->comparetup(a, b, state);
+}
+
+/* Used if first key's comparator is ssup_datum_int32_compare */
+static pg_attribute_always_inline int
+qsort_tuple_int32_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
+{
+	int			compare;
+
+	compare = ApplyInt32SortComparator(a->datum1, a->isnull1,
+										b->datum1, b->isnull1,
+										&state->sortKeys[0]);
+	if (compare != 0)
+		return compare;
+
+	return state->comparetup(a, b, state);
+}
+
 /*
  * Special versions of qsort just for SortTuple objects.  qsort_tuple() sorts
  * any variant of SortTuples, using the appropriate comparetup function.
  * qsort_ssup() is specialized for the case where the comparetup function
  * reduces to ApplySortComparator(), that is single-key MinimalTuple sorts
- * and Datum sorts.
+ * and Datum sorts.  qsort_tuple_{binary,signed,int32} are specialized for
+ * common comparison functions on pass-by-value leading datums.
  */
 
+#define ST_SORT qsort_tuple_binary
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE(a, b, state) qsort_tuple_binary_compare(a, b, state)
+#define ST_COMPARE_ARG_TYPE Tuplesortstate
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+#define ST_SORT qsort_tuple_signed
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE(a, b, state) qsort_tuple_signed_compare(a, b, state)
+#define ST_COMPARE_ARG_TYPE Tuplesortstate
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+#define ST_SORT qsort_tuple_int32
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE(a, b, state) qsort_tuple_int32_compare(a, b, state)
+#define ST_COMPARE_ARG_TYPE Tuplesortstate
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
 #define ST_SORT qsort_tuple
 #define ST_ELEMENT_TYPE SortTuple
 #define ST_COMPARE_RUNTIME_POINTER
@@ -698,6 +785,7 @@ static void tuplesort_updatemax(Tuplesortstate *state);
 #define ST_DEFINE
 #include "lib/sort_template.h"
 
+
 /*
  *		tuplesort_begin_xxx
  *
@@ -3558,15 +3646,40 @@ tuplesort_sort_memtuples(Tuplesortstate *state)
 
 	if (state->memtupcount > 1)
 	{
+		/* Do we have a specialization for the leading column's comparator? */
+		if (state->sortKeys &&
+			state->sortKeys[0].comparator == ssup_datum_binary_cmp)
+		{
+			elog(DEBUG1, "qsort_tuple_binary");
+			qsort_tuple_binary(state->memtuples, state->memtupcount, state);
+		}
+		else if (state->sortKeys &&
+				 state->sortKeys[0].comparator == ssup_datum_signed_cmp)
+		{
+			elog(DEBUG1, "qsort_tuple_signed");
+			qsort_tuple_signed(state->memtuples, state->memtupcount, state);
+		}
+		else if (state->sortKeys &&
+				 state->sortKeys[0].comparator == ssup_datum_int32_cmp)
+		{
+			elog(DEBUG1, "qsort_tuple_int32");
+			qsort_tuple_int32(state->memtuples, state->memtupcount, state);
+		}
 		/* Can we use the single-key sort function? */
-		if (state->onlyKey != NULL)
+		else if (state->onlyKey != NULL)
+		{
+			elog(DEBUG1, "qsort_ssup");
 			qsort_ssup(state->memtuples, state->memtupcount,
 					   state->onlyKey);
+		}
 		else
+		{
+			elog(DEBUG1, "qsort_tuple");
 			qsort_tuple(state->memtuples,
 						state->memtupcount,
 						state->comparetup,
 						state);
+		}
 	}
 }
 
@@ -4776,3 +4889,47 @@ free_sort_tuple(Tuplesortstate *state, SortTuple *stup)
 	FREEMEM(state, GetMemoryChunkSpace(stup->tuple));
 	pfree(stup->tuple);
 }
+
+int
+ssup_datum_binary_cmp(Datum x, Datum y, SortSupport ssup)
+{
+	if (x < y)
+		return -1;
+	else if (x > y)
+		return 1;
+	else
+		return 0;
+}
+
+int
+ssup_datum_signed_cmp(Datum x, Datum y, SortSupport ssup)
+{
+#if SIZEOF_DATUM == 8
+	int64		xx = (int64) x;
+	int64		yy = (int64) y;
+#else
+	int32		xx = (int32) x;
+	int32		yy = (int32) y;
+#endif
+
+	if (xx < yy)
+		return -1;
+	else if (xx > yy)
+		return 1;
+	else
+		return 0;
+}
+
+int
+ssup_datum_int32_cmp(Datum x, Datum y, SortSupport ssup)
+{
+	int32		xx = (int32) x;
+	int32		yy = (int32) y;
+
+	if (xx < yy)
+		return -1;
+	else if (xx > yy)
+		return 1;
+	else
+		return 0;
+}
diff --git a/src/include/utils/sortsupport.h b/src/include/utils/sortsupport.h
index 2f12a8b8eb..8b6a0c5e10 100644
--- a/src/include/utils/sortsupport.h
+++ b/src/include/utils/sortsupport.h
@@ -229,6 +229,112 @@ ApplySortComparator(Datum datum1, bool isNull1,
 	return compare;
 }
 
+static inline int
+ApplyBinarySortComparator(Datum datum1, bool isNull1,
+						  Datum datum2, bool isNull2,
+						  SortSupport ssup)
+{
+	int			compare;
+
+	if (isNull1)
+	{
+		if (isNull2)
+			compare = 0;		/* NULL "=" NULL */
+		else if (ssup->ssup_nulls_first)
+			compare = -1;		/* NULL "<" NOT_NULL */
+		else
+			compare = 1;		/* NULL ">" NOT_NULL */
+	}
+	else if (isNull2)
+	{
+		if (ssup->ssup_nulls_first)
+			compare = 1;		/* NOT_NULL ">" NULL */
+		else
+			compare = -1;		/* NOT_NULL "<" NULL */
+	}
+	else
+	{
+		compare = datum1 < datum2 ? -1 : datum1 > datum2 ? 1 : 0;
+		if (ssup->ssup_reverse)
+			INVERT_COMPARE_RESULT(compare);
+	}
+
+	return compare;
+}
+
+static inline int
+ApplySignedSortComparator(Datum datum1, bool isNull1,
+						  Datum datum2, bool isNull2,
+						  SortSupport ssup)
+{
+	int			compare;
+
+	if (isNull1)
+	{
+		if (isNull2)
+			compare = 0;		/* NULL "=" NULL */
+		else if (ssup->ssup_nulls_first)
+			compare = -1;		/* NULL "<" NOT_NULL */
+		else
+			compare = 1;		/* NULL ">" NOT_NULL */
+	}
+	else if (isNull2)
+	{
+		if (ssup->ssup_nulls_first)
+			compare = 1;		/* NOT_NULL ">" NULL */
+		else
+			compare = -1;		/* NOT_NULL "<" NULL */
+	}
+	else
+	{
+#if SIZEOF_DATUM == 8
+		compare = (int64) datum1 < (int64) datum2 ? -1 :
+			(int64) datum1 > (int64) datum2 ? 1 : 0;
+#else
+		compare = (int32) datum1 < (int32) datum2 ? -1 :
+			(int32) datum1 > (int32) datum2 ? 1 : 0;
+#endif
+		if (ssup->ssup_reverse)
+			INVERT_COMPARE_RESULT(compare);
+	}
+
+	return compare;
+}
+
+static inline int
+ApplyInt32SortComparator(Datum datum1, bool isNull1,
+						 Datum datum2, bool isNull2,
+						 SortSupport ssup)
+{
+	int			compare;
+
+	if (isNull1)
+	{
+		if (isNull2)
+			compare = 0;		/* NULL "=" NULL */
+		else if (ssup->ssup_nulls_first)
+			compare = -1;		/* NULL "<" NOT_NULL */
+		else
+			compare = 1;		/* NULL ">" NOT_NULL */
+	}
+	else if (isNull2)
+	{
+		if (ssup->ssup_nulls_first)
+			compare = 1;		/* NOT_NULL ">" NULL */
+		else
+			compare = -1;		/* NOT_NULL "<" NULL */
+	}
+	else
+	{
+		compare = (int32) datum1 < (int32) datum2 ? -1 :
+			(int32) datum1 > (int32) datum2 ? 1 : 0;
+		if (ssup->ssup_reverse)
+			INVERT_COMPARE_RESULT(compare);
+	}
+
+	return compare;
+}
+
 /*
  * Apply a sort comparator function and return a 3-way comparison using full,
  * authoritative comparator.  This takes care of handling reverse-sort and
@@ -267,6 +373,15 @@ ApplySortAbbrevFullComparator(Datum datum1, bool isNull1,
 	return compare;
 }
 
+/*
+ * Datum comparison functions that we have specialized sort routines for.
+ * Datatypes that install these as their comparator or abbrevated comparator
+ * are eligible for faster sorting.
+ */
+extern int ssup_datum_binary_cmp(Datum x, Datum y, SortSupport ssup);
+extern int ssup_datum_signed_cmp(Datum x, Datum y, SortSupport ssup);
+extern int ssup_datum_int32_cmp(Datum x, Datum y, SortSupport ssup);
+
 /* Other functions in utils/sort/sortsupport.c */
 extern void PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup);
 extern void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup);
-- 
2.30.2



Attachments:

  [text/plain] 0001-WIP-Accelerate-tuple-sorting-for-common-types.patch.txt (15.5K, ../../CA+hUKGKKYttZZk-JMRQSVak=CXSJ5fiwtirFf=n=PAbumvn1Ww@mail.gmail.com/2-0001-WIP-Accelerate-tuple-sorting-for-common-types.patch.txt)
  download | inline diff:
From 62a8acbc745f3b4a90c9a14b6b61989a9d83bece Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Sat, 3 Jul 2021 19:02:10 +1200
Subject: [PATCH] WIP: Accelerate tuple sorting for common types.

Several data types have their own fast comparator functions that can be
replaced with common binary or signed comparator functions.  These
functions can then be recognized by tuplesort.c and used to dispatch to
corresponding specialized sort functions, to accelerate sorting.

XXX WIP, experiment grade only, many open questions...
---
 src/backend/access/nbtree/nbtcompare.c |  22 ++--
 src/backend/utils/adt/date.c           |  15 +--
 src/backend/utils/adt/timestamp.c      |  11 ++
 src/backend/utils/adt/varlena.c        |  26 +---
 src/backend/utils/sort/tuplesort.c     | 161 ++++++++++++++++++++++++-
 src/include/utils/sortsupport.h        | 115 ++++++++++++++++++
 6 files changed, 295 insertions(+), 55 deletions(-)

diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c
index 7ac73cb8c2..204cf778fb 100644
--- a/src/backend/access/nbtree/nbtcompare.c
+++ b/src/backend/access/nbtree/nbtcompare.c
@@ -119,26 +119,12 @@ btint4cmp(PG_FUNCTION_ARGS)
 		PG_RETURN_INT32(A_LESS_THAN_B);
 }
 
-static int
-btint4fastcmp(Datum x, Datum y, SortSupport ssup)
-{
-	int32		a = DatumGetInt32(x);
-	int32		b = DatumGetInt32(y);
-
-	if (a > b)
-		return A_GREATER_THAN_B;
-	else if (a == b)
-		return 0;
-	else
-		return A_LESS_THAN_B;
-}
-
 Datum
 btint4sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
-	ssup->comparator = btint4fastcmp;
+	ssup->comparator = ssup_datum_int32_cmp;
 	PG_RETURN_VOID();
 }
 
@@ -156,6 +142,7 @@ btint8cmp(PG_FUNCTION_ARGS)
 		PG_RETURN_INT32(A_LESS_THAN_B);
 }
 
+#ifndef USE_FLOAT8_BYVAL
 static int
 btint8fastcmp(Datum x, Datum y, SortSupport ssup)
 {
@@ -169,13 +156,18 @@ btint8fastcmp(Datum x, Datum y, SortSupport ssup)
 	else
 		return A_LESS_THAN_B;
 }
+#endif
 
 Datum
 btint8sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
+#ifdef USE_FLOAT8_BYVAL
+	ssup->comparator = ssup_datum_signed_cmp;
+#else
 	ssup->comparator = btint8fastcmp;
+#endif
 	PG_RETURN_VOID();
 }
 
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 0bea16cb67..350c662d50 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -438,25 +438,12 @@ date_cmp(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(0);
 }
 
-static int
-date_fastcmp(Datum x, Datum y, SortSupport ssup)
-{
-	DateADT		a = DatumGetDateADT(x);
-	DateADT		b = DatumGetDateADT(y);
-
-	if (a < b)
-		return -1;
-	else if (a > b)
-		return 1;
-	return 0;
-}
-
 Datum
 date_sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
-	ssup->comparator = date_fastcmp;
+	ssup->comparator = ssup_datum_int32_cmp;
 	PG_RETURN_VOID();
 }
 
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 79761f809c..c678517db6 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -37,6 +37,7 @@
 #include "utils/datetime.h"
 #include "utils/float.h"
 #include "utils/numeric.h"
+#include "utils/sortsupport.h"
 
 /*
  * gcc's -ffast-math switch breaks routines that expect exact results from
@@ -2155,6 +2156,7 @@ timestamp_cmp(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
 }
 
+#ifndef USE_FLOAT8_BYVAL
 /* note: this is used for timestamptz also */
 static int
 timestamp_fastcmp(Datum x, Datum y, SortSupport ssup)
@@ -2164,13 +2166,22 @@ timestamp_fastcmp(Datum x, Datum y, SortSupport ssup)
 
 	return timestamp_cmp_internal(a, b);
 }
+#endif
 
 Datum
 timestamp_sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
+#ifdef USE_FLOAT8_BYVAL
+	/*
+	 * If this build has pass-by-value timestamps, then we can use a standard
+	 * comparator function.
+	 */
+	ssup->comparator = ssup_datum_signed_cmp;
+#else
 	ssup->comparator = timestamp_fastcmp;
+#endif
 	PG_RETURN_VOID();
 }
 
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index d2a11b1b5d..4709d8129e 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -127,7 +127,6 @@ static int	namefastcmp_c(Datum x, Datum y, SortSupport ssup);
 static int	varlenafastcmp_locale(Datum x, Datum y, SortSupport ssup);
 static int	namefastcmp_locale(Datum x, Datum y, SortSupport ssup);
 static int	varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup);
-static int	varstrcmp_abbrev(Datum x, Datum y, SortSupport ssup);
 static Datum varstr_abbrev_convert(Datum original, SortSupport ssup);
 static bool varstr_abbrev_abort(int memtupcount, SortSupport ssup);
 static int32 text_length(Datum str);
@@ -2175,7 +2174,7 @@ varstr_sortsupport(SortSupport ssup, Oid typid, Oid collid)
 			initHyperLogLog(&sss->abbr_card, 10);
 			initHyperLogLog(&sss->full_card, 10);
 			ssup->abbrev_full_comparator = ssup->comparator;
-			ssup->comparator = varstrcmp_abbrev;
+			ssup->comparator = ssup_datum_binary_cmp;
 			ssup->abbrev_converter = varstr_abbrev_convert;
 			ssup->abbrev_abort = varstr_abbrev_abort;
 		}
@@ -2461,27 +2460,6 @@ varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup)
 	return result;
 }
 
-/*
- * Abbreviated key comparison func
- */
-static int
-varstrcmp_abbrev(Datum x, Datum y, SortSupport ssup)
-{
-	/*
-	 * When 0 is returned, the core system will call varstrfastcmp_c()
-	 * (bpcharfastcmp_c() in BpChar case) or varlenafastcmp_locale().  Even a
-	 * strcmp() on two non-truncated strxfrm() blobs cannot indicate *equality*
-	 * authoritatively, for the same reason that there is a strcoll()
-	 * tie-breaker call to strcmp() in varstr_cmp().
-	 */
-	if (x > y)
-		return 1;
-	else if (x == y)
-		return 0;
-	else
-		return -1;
-}
-
 /*
  * Conversion routine for sortsupport.  Converts original to abbreviated key
  * representation.  Our encoding strategy is simple -- pack the first 8 bytes
@@ -2710,7 +2688,7 @@ done:
 	/*
 	 * Byteswap on little-endian machines.
 	 *
-	 * This is needed so that varstrcmp_abbrev() (an unsigned integer 3-way
+	 * This is needed so that ssup_datum_binary_cmp() (an unsigned integer 3-way
 	 * comparator) works correctly on all platforms.  If we didn't do this,
 	 * the comparator would have to call memcmp() with a pair of pointers to
 	 * the first byte of each abbreviated key, which is slower.
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 22972071ff..455bb652b8 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -669,14 +669,101 @@ static void free_sort_tuple(Tuplesortstate *state, SortTuple *stup);
 static void tuplesort_free(Tuplesortstate *state);
 static void tuplesort_updatemax(Tuplesortstate *state);
 
+/*
+ * Specialized comparators that we can inline into specialized sorts.  The goal
+ * is to try to sort two tuples without having to follow the pointers to the
+ * comparator or the tuple.
+ *
+ * XXX: For now, these fall back to comparator functions that will compare the
+ * leading datum a second time.
+ *
+ * XXX: For now, there is no specialization for cases where datum1 is
+ * authoritative and we don't even need to fall back to a callback at all (that
+ * would be true for types like int4/int8/timestamp/date, but not true for
+ * abbreviations of text or multi-key sorts.  There could be!  Is it worth it?
+ */
+
+/* Used if first key's comparator is ssup_datum_binary_compare */
+static pg_attribute_always_inline int
+qsort_tuple_binary_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
+{
+	int			compare;
+
+	compare = ApplyBinarySortComparator(a->datum1, a->isnull1,
+										b->datum1, b->isnull1,
+										&state->sortKeys[0]);
+	if (compare != 0)
+		return compare;
+
+	return state->comparetup(a, b, state);
+}
+
+/* Used if first key's comparator is ssup_datum_signed_compare */
+static pg_attribute_always_inline int
+qsort_tuple_signed_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
+{
+	int			compare;
+
+	compare = ApplySignedSortComparator(a->datum1, a->isnull1,
+										b->datum1, b->isnull1,
+										&state->sortKeys[0]);
+	if (compare != 0)
+		return compare;
+
+	return state->comparetup(a, b, state);
+}
+
+/* Used if first key's comparator is ssup_datum_int32_compare */
+static pg_attribute_always_inline int
+qsort_tuple_int32_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
+{
+	int			compare;
+
+	compare = ApplyInt32SortComparator(a->datum1, a->isnull1,
+										b->datum1, b->isnull1,
+										&state->sortKeys[0]);
+	if (compare != 0)
+		return compare;
+
+	return state->comparetup(a, b, state);
+}
+
 /*
  * Special versions of qsort just for SortTuple objects.  qsort_tuple() sorts
  * any variant of SortTuples, using the appropriate comparetup function.
  * qsort_ssup() is specialized for the case where the comparetup function
  * reduces to ApplySortComparator(), that is single-key MinimalTuple sorts
- * and Datum sorts.
+ * and Datum sorts.  qsort_tuple_{binary,signed,int32} are specialized for
+ * common comparison functions on pass-by-value leading datums.
  */
 
+#define ST_SORT qsort_tuple_binary
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE(a, b, state) qsort_tuple_binary_compare(a, b, state)
+#define ST_COMPARE_ARG_TYPE Tuplesortstate
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+#define ST_SORT qsort_tuple_signed
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE(a, b, state) qsort_tuple_signed_compare(a, b, state)
+#define ST_COMPARE_ARG_TYPE Tuplesortstate
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+#define ST_SORT qsort_tuple_int32
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE(a, b, state) qsort_tuple_int32_compare(a, b, state)
+#define ST_COMPARE_ARG_TYPE Tuplesortstate
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
 #define ST_SORT qsort_tuple
 #define ST_ELEMENT_TYPE SortTuple
 #define ST_COMPARE_RUNTIME_POINTER
@@ -698,6 +785,7 @@ static void tuplesort_updatemax(Tuplesortstate *state);
 #define ST_DEFINE
 #include "lib/sort_template.h"
 
+
 /*
  *		tuplesort_begin_xxx
  *
@@ -3558,15 +3646,40 @@ tuplesort_sort_memtuples(Tuplesortstate *state)
 
 	if (state->memtupcount > 1)
 	{
+		/* Do we have a specialization for the leading column's comparator? */
+		if (state->sortKeys &&
+			state->sortKeys[0].comparator == ssup_datum_binary_cmp)
+		{
+			elog(DEBUG1, "qsort_tuple_binary");
+			qsort_tuple_binary(state->memtuples, state->memtupcount, state);
+		}
+		else if (state->sortKeys &&
+				 state->sortKeys[0].comparator == ssup_datum_signed_cmp)
+		{
+			elog(DEBUG1, "qsort_tuple_signed");
+			qsort_tuple_signed(state->memtuples, state->memtupcount, state);
+		}
+		else if (state->sortKeys &&
+				 state->sortKeys[0].comparator == ssup_datum_int32_cmp)
+		{
+			elog(DEBUG1, "qsort_tuple_int32");
+			qsort_tuple_int32(state->memtuples, state->memtupcount, state);
+		}
 		/* Can we use the single-key sort function? */
-		if (state->onlyKey != NULL)
+		else if (state->onlyKey != NULL)
+		{
+			elog(DEBUG1, "qsort_ssup");
 			qsort_ssup(state->memtuples, state->memtupcount,
 					   state->onlyKey);
+		}
 		else
+		{
+			elog(DEBUG1, "qsort_tuple");
 			qsort_tuple(state->memtuples,
 						state->memtupcount,
 						state->comparetup,
 						state);
+		}
 	}
 }
 
@@ -4776,3 +4889,47 @@ free_sort_tuple(Tuplesortstate *state, SortTuple *stup)
 	FREEMEM(state, GetMemoryChunkSpace(stup->tuple));
 	pfree(stup->tuple);
 }
+
+int
+ssup_datum_binary_cmp(Datum x, Datum y, SortSupport ssup)
+{
+	if (x < y)
+		return -1;
+	else if (x > y)
+		return 1;
+	else
+		return 0;
+}
+
+int
+ssup_datum_signed_cmp(Datum x, Datum y, SortSupport ssup)
+{
+#if SIZEOF_DATUM == 8
+	int64		xx = (int64) x;
+	int64		yy = (int64) y;
+#else
+	int32		xx = (int32) x;
+	int32		yy = (int32) y;
+#endif
+
+	if (xx < yy)
+		return -1;
+	else if (xx > yy)
+		return 1;
+	else
+		return 0;
+}
+
+int
+ssup_datum_int32_cmp(Datum x, Datum y, SortSupport ssup)
+{
+	int32		xx = (int32) x;
+	int32		yy = (int32) y;
+
+	if (xx < yy)
+		return -1;
+	else if (xx > yy)
+		return 1;
+	else
+		return 0;
+}
diff --git a/src/include/utils/sortsupport.h b/src/include/utils/sortsupport.h
index 2f12a8b8eb..8b6a0c5e10 100644
--- a/src/include/utils/sortsupport.h
+++ b/src/include/utils/sortsupport.h
@@ -229,6 +229,112 @@ ApplySortComparator(Datum datum1, bool isNull1,
 	return compare;
 }
 
+static inline int
+ApplyBinarySortComparator(Datum datum1, bool isNull1,
+						  Datum datum2, bool isNull2,
+						  SortSupport ssup)
+{
+	int			compare;
+
+	if (isNull1)
+	{
+		if (isNull2)
+			compare = 0;		/* NULL "=" NULL */
+		else if (ssup->ssup_nulls_first)
+			compare = -1;		/* NULL "<" NOT_NULL */
+		else
+			compare = 1;		/* NULL ">" NOT_NULL */
+	}
+	else if (isNull2)
+	{
+		if (ssup->ssup_nulls_first)
+			compare = 1;		/* NOT_NULL ">" NULL */
+		else
+			compare = -1;		/* NOT_NULL "<" NULL */
+	}
+	else
+	{
+		compare = datum1 < datum2 ? -1 : datum1 > datum2 ? 1 : 0;
+		if (ssup->ssup_reverse)
+			INVERT_COMPARE_RESULT(compare);
+	}
+
+	return compare;
+}
+
+static inline int
+ApplySignedSortComparator(Datum datum1, bool isNull1,
+						  Datum datum2, bool isNull2,
+						  SortSupport ssup)
+{
+	int			compare;
+
+	if (isNull1)
+	{
+		if (isNull2)
+			compare = 0;		/* NULL "=" NULL */
+		else if (ssup->ssup_nulls_first)
+			compare = -1;		/* NULL "<" NOT_NULL */
+		else
+			compare = 1;		/* NULL ">" NOT_NULL */
+	}
+	else if (isNull2)
+	{
+		if (ssup->ssup_nulls_first)
+			compare = 1;		/* NOT_NULL ">" NULL */
+		else
+			compare = -1;		/* NOT_NULL "<" NULL */
+	}
+	else
+	{
+#if SIZEOF_DATUM == 8
+		compare = (int64) datum1 < (int64) datum2 ? -1 :
+			(int64) datum1 > (int64) datum2 ? 1 : 0;
+#else
+		compare = (int32) datum1 < (int32) datum2 ? -1 :
+			(int32) datum1 > (int32) datum2 ? 1 : 0;
+#endif
+		if (ssup->ssup_reverse)
+			INVERT_COMPARE_RESULT(compare);
+	}
+
+	return compare;
+}
+
+static inline int
+ApplyInt32SortComparator(Datum datum1, bool isNull1,
+						 Datum datum2, bool isNull2,
+						 SortSupport ssup)
+{
+	int			compare;
+
+	if (isNull1)
+	{
+		if (isNull2)
+			compare = 0;		/* NULL "=" NULL */
+		else if (ssup->ssup_nulls_first)
+			compare = -1;		/* NULL "<" NOT_NULL */
+		else
+			compare = 1;		/* NULL ">" NOT_NULL */
+	}
+	else if (isNull2)
+	{
+		if (ssup->ssup_nulls_first)
+			compare = 1;		/* NOT_NULL ">" NULL */
+		else
+			compare = -1;		/* NOT_NULL "<" NULL */
+	}
+	else
+	{
+		compare = (int32) datum1 < (int32) datum2 ? -1 :
+			(int32) datum1 > (int32) datum2 ? 1 : 0;
+		if (ssup->ssup_reverse)
+			INVERT_COMPARE_RESULT(compare);
+	}
+
+	return compare;
+}
+
 /*
  * Apply a sort comparator function and return a 3-way comparison using full,
  * authoritative comparator.  This takes care of handling reverse-sort and
@@ -267,6 +373,15 @@ ApplySortAbbrevFullComparator(Datum datum1, bool isNull1,
 	return compare;
 }
 
+/*
+ * Datum comparison functions that we have specialized sort routines for.
+ * Datatypes that install these as their comparator or abbrevated comparator
+ * are eligible for faster sorting.
+ */
+extern int ssup_datum_binary_cmp(Datum x, Datum y, SortSupport ssup);
+extern int ssup_datum_signed_cmp(Datum x, Datum y, SortSupport ssup);
+extern int ssup_datum_int32_cmp(Datum x, Datum y, SortSupport ssup);
+
 /* Other functions in utils/sort/sortsupport.c */
 extern void PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup);
 extern void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup);
-- 
2.30.2



^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-07-15 11:49  vignesh C <[email protected]>
  parent: Thomas Munro <[email protected]>
  1 sibling, 1 reply; 55+ messages in thread

From: vignesh C @ 2021-07-15 11:49 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: John Naylor <[email protected]>; pgsql-hackers

On Sun, Jul 4, 2021 at 9:58 AM Thomas Munro <[email protected]> wrote:
>
> On Fri, Jul 2, 2021 at 2:32 PM John Naylor <[email protected]> wrote:
> > I suspect if we experiment on two extremes of type "heaviness" (accessing and comparing trivial or not), such as uint32 and tuplesort, we'll have a pretty good idea what the parameters should be, if anything different. I'll do some testing along those lines.
>
> Cool.
>
> Since you are experimenting with tuplesort and likely thinking similar
> thoughts, here's a patch I've been using to explore that area.  I've
> seen it get, for example, ~1.18x speedup for simple index builds in
> favourable winds (YMMV, early hacking results only).  Currently, it
> kicks in when the leading column is of type int4, int8, timestamp,
> timestamptz, date or text + friends (when abbreviatable, currently
> that means "C" and ICU collations only), while increasing the
> executable by only 8.5kB (Clang, amd64, -O2, no debug).
>
> These types are handled with just three specialisations.  Their custom
> "fast" comparators all boiled down to comparisons of datum bits,
> varying only in signedness and width, so I tried throwing them away
> and using 3 new common routines.  Then I extended
> tuplesort_sort_memtuples()'s pre-existing specialisation dispatch to
> recognise qualifying users of those and select 3 corresponding sort
> specialisations.
>
> It might turn out to be worth burning some more executable size on
> extra variants (for example, see XXX notes in the code comments for
> opportunities; one could also go nuts trying smaller things like
> special cases for not-null, nulls first, reverse sort, ... to kill all
> those branches), or not.

The patch does not apply on Head anymore, could you rebase and post a
patch. I'm changing the status to "Waiting for Author".

Regards,
Vignesh





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-07-15 11:57  John Naylor <[email protected]>
  parent: vignesh C <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: John Naylor @ 2021-07-15 11:57 UTC (permalink / raw)
  To: vignesh C <[email protected]>; +Cc: Thomas Munro <[email protected]>; pgsql-hackers

On Thu, Jul 15, 2021 at 7:50 AM vignesh C <[email protected]> wrote:
> The patch does not apply on Head anymore, could you rebase and post a
> patch. I'm changing the status to "Waiting for Author".

The patch set is fine. The error is my fault since I attached an
experimental addendum and neglected to name it as .txt. I've set it back to
"needs review" and will resume testing shortly.

--
John Naylor
EDB: http://www.enterprisedb.com


^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-07-22 07:30  Thomas Munro <[email protected]>
  parent: Thomas Munro <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: Thomas Munro @ 2021-07-22 07:30 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Zhihong Yu <[email protected]>; Andres Freund <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

On Thu, Jun 17, 2021 at 1:20 PM Thomas Munro <[email protected]> wrote:
> On Thu, Jun 17, 2021 at 1:14 PM Tom Lane <[email protected]> wrote:
> > The big problem in my mind, which would not be alleviated in the
> > slightest by having a separate file, is that it'd be easy to miss
> > removing entries if they ever become obsolete.
>
> I suppose you could invent some kind of declaration syntax in a
> comment near the use of the pseudo-typename in the source tree that is
> mechanically extracted.

What do you think about something like this?

From 8252eb18c19c8a78fa6326ae5de8261d7d793dda Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Thu, 22 Jul 2021 19:05:15 +1200
Subject: [PATCH] Teach pgindent about special file-local typenames.

Allow source files to declare an extra typename with a special
annotation of the form:

    @pgindent typename XXX@

Use these to fix some whitespace problems in simplehash.h and
sort_template.h.
---
 src/include/lib/simplehash.h    | 110 +++++++++++++++++---------------
 src/include/lib/sort_template.h |  21 +++---
 src/tools/pgindent/pgindent     |  11 +++-
 3 files changed, 79 insertions(+), 63 deletions(-)

diff --git a/src/include/lib/simplehash.h b/src/include/lib/simplehash.h
index da51781e98..3e44c7cc03 100644
--- a/src/include/lib/simplehash.h
+++ b/src/include/lib/simplehash.h
@@ -86,6 +86,12 @@
  *	  presence is relevant to determine whether a lookup needs to continue
  *	  looking or is done - buckets following a deleted element are shifted
  *	  backwards, unless they're empty or already at their optimal position.
+ *
+ * Help pgindent understand our pseudo-typenames:
+ *
+ *	  @pgindent typename SH_ELEMENT_TYPE@
+ *	  @pgindent typename SH_TYPE@
+ *	  @pgindent typename SH_ITERATOR@
  */
 
 #include "port/pg_bitutils.h"
@@ -164,7 +170,7 @@ typedef struct SH_TYPE
 
 	/* user defined data, useful for callbacks */
 	void	   *private_data;
-}			SH_TYPE;
+} SH_TYPE;
 
 typedef enum SH_STATUS
 {
@@ -177,67 +183,67 @@ typedef struct SH_ITERATOR
 	uint32		cur;			/* current element */
 	uint32		end;
 	bool		done;			/* iterator exhausted? */
-}			SH_ITERATOR;
+} SH_ITERATOR;
 
 /* externally visible function prototypes */
 #ifdef SH_RAW_ALLOCATOR
 /* <prefix>_hash <prefix>_create(uint32 nelements, void *private_data) */
-SH_SCOPE	SH_TYPE *SH_CREATE(uint32 nelements, void *private_data);
+SH_SCOPE SH_TYPE *SH_CREATE(uint32 nelements, void *private_data);
 #else
 /*
  * <prefix>_hash <prefix>_create(MemoryContext ctx, uint32 nelements,
  *								 void *private_data)
  */
-SH_SCOPE	SH_TYPE *SH_CREATE(MemoryContext ctx, uint32 nelements,
-							   void *private_data);
+SH_SCOPE SH_TYPE *SH_CREATE(MemoryContext ctx, uint32 nelements,
+							void *private_data);
 #endif
 
 /* void <prefix>_destroy(<prefix>_hash *tb) */
-SH_SCOPE void SH_DESTROY(SH_TYPE * tb);
+SH_SCOPE void SH_DESTROY(SH_TYPE *tb);
 
 /* void <prefix>_reset(<prefix>_hash *tb) */
-SH_SCOPE void SH_RESET(SH_TYPE * tb);
+SH_SCOPE void SH_RESET(SH_TYPE *tb);
 
 /* void <prefix>_grow(<prefix>_hash *tb) */
-SH_SCOPE void SH_GROW(SH_TYPE * tb, uint32 newsize);
+SH_SCOPE void SH_GROW(SH_TYPE *tb, uint32 newsize);
 
 /* <element> *<prefix>_insert(<prefix>_hash *tb, <key> key, bool *found) */
-SH_SCOPE	SH_ELEMENT_TYPE *SH_INSERT(SH_TYPE * tb, SH_KEY_TYPE key, bool *found);
+SH_SCOPE SH_ELEMENT_TYPE *SH_INSERT(SH_TYPE *tb, SH_KEY_TYPE key, bool *found);
 
 /*
  * <element> *<prefix>_insert_hash(<prefix>_hash *tb, <key> key, uint32 hash,
  * 								  bool *found)
  */
-SH_SCOPE	SH_ELEMENT_TYPE *SH_INSERT_HASH(SH_TYPE * tb, SH_KEY_TYPE key,
-											uint32 hash, bool *found);
+SH_SCOPE SH_ELEMENT_TYPE *SH_INSERT_HASH(SH_TYPE *tb, SH_KEY_TYPE key,
+										 uint32 hash, bool *found);
 
 /* <element> *<prefix>_lookup(<prefix>_hash *tb, <key> key) */
-SH_SCOPE	SH_ELEMENT_TYPE *SH_LOOKUP(SH_TYPE * tb, SH_KEY_TYPE key);
+SH_SCOPE SH_ELEMENT_TYPE *SH_LOOKUP(SH_TYPE *tb, SH_KEY_TYPE key);
 
 /* <element> *<prefix>_lookup_hash(<prefix>_hash *tb, <key> key, uint32 hash) */
-SH_SCOPE	SH_ELEMENT_TYPE *SH_LOOKUP_HASH(SH_TYPE * tb, SH_KEY_TYPE key,
-											uint32 hash);
+SH_SCOPE SH_ELEMENT_TYPE *SH_LOOKUP_HASH(SH_TYPE *tb, SH_KEY_TYPE key,
+										 uint32 hash);
 
 /* void <prefix>_delete_item(<prefix>_hash *tb, <element> *entry) */
-SH_SCOPE void SH_DELETE_ITEM(SH_TYPE * tb, SH_ELEMENT_TYPE * entry);
+SH_SCOPE void SH_DELETE_ITEM(SH_TYPE *tb, SH_ELEMENT_TYPE *entry);
 
 /* bool <prefix>_delete(<prefix>_hash *tb, <key> key) */
-SH_SCOPE bool SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key);
+SH_SCOPE bool SH_DELETE(SH_TYPE *tb, SH_KEY_TYPE key);
 
 /* void <prefix>_start_iterate(<prefix>_hash *tb, <prefix>_iterator *iter) */
-SH_SCOPE void SH_START_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter);
+SH_SCOPE void SH_START_ITERATE(SH_TYPE *tb, SH_ITERATOR *iter);
 
 /*
  * void <prefix>_start_iterate_at(<prefix>_hash *tb, <prefix>_iterator *iter,
  *								  uint32 at)
  */
-SH_SCOPE void SH_START_ITERATE_AT(SH_TYPE * tb, SH_ITERATOR * iter, uint32 at);
+SH_SCOPE void SH_START_ITERATE_AT(SH_TYPE *tb, SH_ITERATOR *iter, uint32 at);
 
 /* <element> *<prefix>_iterate(<prefix>_hash *tb, <prefix>_iterator *iter) */
-SH_SCOPE	SH_ELEMENT_TYPE *SH_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter);
+SH_SCOPE SH_ELEMENT_TYPE *SH_ITERATE(SH_TYPE *tb, SH_ITERATOR *iter);
 
 /* void <prefix>_stat(<prefix>_hash *tb */
-SH_SCOPE void SH_STAT(SH_TYPE * tb);
+SH_SCOPE void SH_STAT(SH_TYPE *tb);
 
 #endif							/* SH_DECLARE */
 
@@ -302,7 +308,7 @@ SH_SCOPE void SH_STAT(SH_TYPE * tb);
  * the hashtable.
  */
 static inline void
-SH_COMPUTE_PARAMETERS(SH_TYPE * tb, uint32 newsize)
+SH_COMPUTE_PARAMETERS(SH_TYPE *tb, uint32 newsize)
 {
 	uint64		size;
 
@@ -340,14 +346,14 @@ SH_COMPUTE_PARAMETERS(SH_TYPE * tb, uint32 newsize)
 
 /* return the optimal bucket for the hash */
 static inline uint32
-SH_INITIAL_BUCKET(SH_TYPE * tb, uint32 hash)
+SH_INITIAL_BUCKET(SH_TYPE *tb, uint32 hash)
 {
 	return hash & tb->sizemask;
 }
 
 /* return next bucket after the current, handling wraparound */
 static inline uint32
-SH_NEXT(SH_TYPE * tb, uint32 curelem, uint32 startelem)
+SH_NEXT(SH_TYPE *tb, uint32 curelem, uint32 startelem)
 {
 	curelem = (curelem + 1) & tb->sizemask;
 
@@ -358,7 +364,7 @@ SH_NEXT(SH_TYPE * tb, uint32 curelem, uint32 startelem)
 
 /* return bucket before the current, handling wraparound */
 static inline uint32
-SH_PREV(SH_TYPE * tb, uint32 curelem, uint32 startelem)
+SH_PREV(SH_TYPE *tb, uint32 curelem, uint32 startelem)
 {
 	curelem = (curelem - 1) & tb->sizemask;
 
@@ -369,7 +375,7 @@ SH_PREV(SH_TYPE * tb, uint32 curelem, uint32 startelem)
 
 /* return distance between bucket and its optimal position */
 static inline uint32
-SH_DISTANCE_FROM_OPTIMAL(SH_TYPE * tb, uint32 optimal, uint32 bucket)
+SH_DISTANCE_FROM_OPTIMAL(SH_TYPE *tb, uint32 optimal, uint32 bucket)
 {
 	if (optimal <= bucket)
 		return bucket - optimal;
@@ -378,7 +384,7 @@ SH_DISTANCE_FROM_OPTIMAL(SH_TYPE * tb, uint32 optimal, uint32 bucket)
 }
 
 static inline uint32
-SH_ENTRY_HASH(SH_TYPE * tb, SH_ELEMENT_TYPE * entry)
+SH_ENTRY_HASH(SH_TYPE *tb, SH_ELEMENT_TYPE *entry)
 {
 #ifdef SH_STORE_HASH
 	return SH_GET_HASH(tb, entry);
@@ -388,14 +394,14 @@ SH_ENTRY_HASH(SH_TYPE * tb, SH_ELEMENT_TYPE * entry)
 }
 
 /* default memory allocator function */
-static inline void *SH_ALLOCATE(SH_TYPE * type, Size size);
-static inline void SH_FREE(SH_TYPE * type, void *pointer);
+static inline void *SH_ALLOCATE(SH_TYPE *type, Size size);
+static inline void SH_FREE(SH_TYPE *type, void *pointer);
 
 #ifndef SH_USE_NONDEFAULT_ALLOCATOR
 
 /* default memory allocator function */
 static inline void *
-SH_ALLOCATE(SH_TYPE * type, Size size)
+SH_ALLOCATE(SH_TYPE *type, Size size)
 {
 #ifdef SH_RAW_ALLOCATOR
 	return SH_RAW_ALLOCATOR(size);
@@ -407,7 +413,7 @@ SH_ALLOCATE(SH_TYPE * type, Size size)
 
 /* default memory free function */
 static inline void
-SH_FREE(SH_TYPE * type, void *pointer)
+SH_FREE(SH_TYPE *type, void *pointer)
 {
 	pfree(pointer);
 }
@@ -424,10 +430,10 @@ SH_FREE(SH_TYPE * type, void *pointer)
  * the passed-in context.
  */
 #ifdef SH_RAW_ALLOCATOR
-SH_SCOPE	SH_TYPE *
+SH_SCOPE SH_TYPE *
 SH_CREATE(uint32 nelements, void *private_data)
 #else
-SH_SCOPE	SH_TYPE *
+SH_SCOPE SH_TYPE *
 SH_CREATE(MemoryContext ctx, uint32 nelements, void *private_data)
 #endif
 {
@@ -454,7 +460,7 @@ SH_CREATE(MemoryContext ctx, uint32 nelements, void *private_data)
 
 /* destroy a previously created hash table */
 SH_SCOPE void
-SH_DESTROY(SH_TYPE * tb)
+SH_DESTROY(SH_TYPE *tb)
 {
 	SH_FREE(tb, tb->data);
 	pfree(tb);
@@ -462,7 +468,7 @@ SH_DESTROY(SH_TYPE * tb)
 
 /* reset the contents of a previously created hash table */
 SH_SCOPE void
-SH_RESET(SH_TYPE * tb)
+SH_RESET(SH_TYPE *tb)
 {
 	memset(tb->data, 0, sizeof(SH_ELEMENT_TYPE) * tb->size);
 	tb->members = 0;
@@ -476,7 +482,7 @@ SH_RESET(SH_TYPE * tb)
  * performance-wise, when known at some point.
  */
 SH_SCOPE void
-SH_GROW(SH_TYPE * tb, uint32 newsize)
+SH_GROW(SH_TYPE *tb, uint32 newsize)
 {
 	uint64		oldsize = tb->size;
 	SH_ELEMENT_TYPE *olddata = tb->data;
@@ -586,7 +592,7 @@ SH_GROW(SH_TYPE * tb, uint32 newsize)
  * into its wrapper functions even if SH_SCOPE is extern.
  */
 static inline SH_ELEMENT_TYPE *
-SH_INSERT_HASH_INTERNAL(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash, bool *found)
+SH_INSERT_HASH_INTERNAL(SH_TYPE *tb, SH_KEY_TYPE key, uint32 hash, bool *found)
 {
 	uint32		startelem;
 	uint32		curelem;
@@ -756,8 +762,8 @@ restart:
  * already exists, false otherwise. Returns the hash-table entry in either
  * case.
  */
-SH_SCOPE	SH_ELEMENT_TYPE *
-SH_INSERT(SH_TYPE * tb, SH_KEY_TYPE key, bool *found)
+SH_SCOPE SH_ELEMENT_TYPE *
+SH_INSERT(SH_TYPE *tb, SH_KEY_TYPE key, bool *found)
 {
 	uint32		hash = SH_HASH_KEY(tb, key);
 
@@ -769,8 +775,8 @@ SH_INSERT(SH_TYPE * tb, SH_KEY_TYPE key, bool *found)
  * hash. Set *found to true if the key already exists, false
  * otherwise. Returns the hash-table entry in either case.
  */
-SH_SCOPE	SH_ELEMENT_TYPE *
-SH_INSERT_HASH(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash, bool *found)
+SH_SCOPE SH_ELEMENT_TYPE *
+SH_INSERT_HASH(SH_TYPE *tb, SH_KEY_TYPE key, uint32 hash, bool *found)
 {
 	return SH_INSERT_HASH_INTERNAL(tb, key, hash, found);
 }
@@ -780,7 +786,7 @@ SH_INSERT_HASH(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash, bool *found)
  * into its wrapper functions even if SH_SCOPE is extern.
  */
 static inline SH_ELEMENT_TYPE *
-SH_LOOKUP_HASH_INTERNAL(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash)
+SH_LOOKUP_HASH_INTERNAL(SH_TYPE *tb, SH_KEY_TYPE key, uint32 hash)
 {
 	const uint32 startelem = SH_INITIAL_BUCKET(tb, hash);
 	uint32		curelem = startelem;
@@ -813,8 +819,8 @@ SH_LOOKUP_HASH_INTERNAL(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash)
 /*
  * Lookup up entry in hash table.  Returns NULL if key not present.
  */
-SH_SCOPE	SH_ELEMENT_TYPE *
-SH_LOOKUP(SH_TYPE * tb, SH_KEY_TYPE key)
+SH_SCOPE SH_ELEMENT_TYPE *
+SH_LOOKUP(SH_TYPE *tb, SH_KEY_TYPE key)
 {
 	uint32		hash = SH_HASH_KEY(tb, key);
 
@@ -826,8 +832,8 @@ SH_LOOKUP(SH_TYPE * tb, SH_KEY_TYPE key)
  *
  * Returns NULL if key not present.
  */
-SH_SCOPE	SH_ELEMENT_TYPE *
-SH_LOOKUP_HASH(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash)
+SH_SCOPE SH_ELEMENT_TYPE *
+SH_LOOKUP_HASH(SH_TYPE *tb, SH_KEY_TYPE key, uint32 hash)
 {
 	return SH_LOOKUP_HASH_INTERNAL(tb, key, hash);
 }
@@ -837,7 +843,7 @@ SH_LOOKUP_HASH(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash)
  * present.
  */
 SH_SCOPE bool
-SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key)
+SH_DELETE(SH_TYPE *tb, SH_KEY_TYPE key)
 {
 	uint32		hash = SH_HASH_KEY(tb, key);
 	uint32		startelem = SH_INITIAL_BUCKET(tb, hash);
@@ -908,7 +914,7 @@ SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key)
  * Delete entry from hash table by entry pointer
  */
 SH_SCOPE void
-SH_DELETE_ITEM(SH_TYPE * tb, SH_ELEMENT_TYPE * entry)
+SH_DELETE_ITEM(SH_TYPE *tb, SH_ELEMENT_TYPE *entry)
 {
 	SH_ELEMENT_TYPE *lastentry = entry;
 	uint32		hash = SH_ENTRY_HASH(tb, entry);
@@ -963,7 +969,7 @@ SH_DELETE_ITEM(SH_TYPE * tb, SH_ELEMENT_TYPE * entry)
  * Initialize iterator.
  */
 SH_SCOPE void
-SH_START_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter)
+SH_START_ITERATE(SH_TYPE *tb, SH_ITERATOR *iter)
 {
 	int			i;
 	uint64		startelem = PG_UINT64_MAX;
@@ -1003,7 +1009,7 @@ SH_START_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter)
  * same position.
  */
 SH_SCOPE void
-SH_START_ITERATE_AT(SH_TYPE * tb, SH_ITERATOR * iter, uint32 at)
+SH_START_ITERATE_AT(SH_TYPE *tb, SH_ITERATOR *iter, uint32 at)
 {
 	/*
 	 * Iterate backwards, that allows the current element to be deleted, even
@@ -1024,8 +1030,8 @@ SH_START_ITERATE_AT(SH_TYPE * tb, SH_ITERATOR * iter, uint32 at)
  * deletions), but if so, there's neither a guarantee that all nodes are
  * visited at least once, nor a guarantee that a node is visited at most once.
  */
-SH_SCOPE	SH_ELEMENT_TYPE *
-SH_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter)
+SH_SCOPE SH_ELEMENT_TYPE *
+SH_ITERATE(SH_TYPE *tb, SH_ITERATOR *iter)
 {
 	while (!iter->done)
 	{
@@ -1052,7 +1058,7 @@ SH_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter)
  * debugging/profiling purposes only.
  */
 SH_SCOPE void
-SH_STAT(SH_TYPE * tb)
+SH_STAT(SH_TYPE *tb)
 {
 	uint32		max_chain_length = 0;
 	uint32		total_chain_length = 0;
diff --git a/src/include/lib/sort_template.h b/src/include/lib/sort_template.h
index f52627d8ce..cb9d9bf449 100644
--- a/src/include/lib/sort_template.h
+++ b/src/include/lib/sort_template.h
@@ -52,6 +52,11 @@
  *	  int (*)(const ST_ELEMENT_TYPE *a, const ST_ELEMENT_TYPE *b,
  *			  [ST_COMPARE_ARG_TYPE *arg])
  *
+ * 	  Help pgindent understand our pseudo-typenames:
+ *
+ *		@pgindent typename ST_ELEMENT_TYPE@
+ *		@pgindent typename ST_POINTER_TYPE@
+ *
  * HISTORY
  *
  *	  Modifications from vanilla NetBSD source:
@@ -176,11 +181,11 @@
 
 #ifdef ST_COMPARE_RUNTIME_POINTER
 typedef int (*ST_COMPARATOR_TYPE_NAME) (const ST_ELEMENT_TYPE *,
-										const ST_ELEMENT_TYPE * ST_SORT_PROTO_ARG);
+										const ST_ELEMENT_TYPE *ST_SORT_PROTO_ARG);
 #endif
 
 /* Declare the sort function.  Note optional arguments at end. */
-ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE * first, size_t n
+ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE *first, size_t n
 					  ST_SORT_PROTO_ELEMENT_SIZE
 					  ST_SORT_PROTO_COMPARE
 					  ST_SORT_PROTO_ARG);
@@ -245,9 +250,9 @@ ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE * first, size_t n
  * 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_MED3(ST_ELEMENT_TYPE *a,
+		ST_ELEMENT_TYPE *b,
+		ST_ELEMENT_TYPE *c
 		ST_SORT_PROTO_COMPARE
 		ST_SORT_PROTO_ARG)
 {
@@ -257,7 +262,7 @@ ST_MED3(ST_ELEMENT_TYPE * a,
 }
 
 static inline void
-ST_SWAP(ST_POINTER_TYPE * a, ST_POINTER_TYPE * b)
+ST_SWAP(ST_POINTER_TYPE *a, ST_POINTER_TYPE *b)
 {
 	ST_POINTER_TYPE tmp = *a;
 
@@ -266,7 +271,7 @@ ST_SWAP(ST_POINTER_TYPE * a, ST_POINTER_TYPE * b)
 }
 
 static inline void
-ST_SWAPN(ST_POINTER_TYPE * a, ST_POINTER_TYPE * b, size_t n)
+ST_SWAPN(ST_POINTER_TYPE *a, ST_POINTER_TYPE *b, size_t n)
 {
 	for (size_t i = 0; i < n; ++i)
 		ST_SWAP(&a[i], &b[i]);
@@ -276,7 +281,7 @@ ST_SWAPN(ST_POINTER_TYPE * a, ST_POINTER_TYPE * b, size_t n)
  * Sort an array.
  */
 ST_SCOPE void
-ST_SORT(ST_ELEMENT_TYPE * data, size_t n
+ST_SORT(ST_ELEMENT_TYPE *data, size_t n
 		ST_SORT_PROTO_ELEMENT_SIZE
 		ST_SORT_PROTO_COMPARE
 		ST_SORT_PROTO_ARG)
diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent
index f8190b6c35..2d62245648 100755
--- a/src/tools/pgindent/pgindent
+++ b/src/tools/pgindent/pgindent
@@ -56,9 +56,8 @@ $excludes ||= "$code_base/src/tools/pgindent/exclude_file_patterns"
 # some names we want to treat like typedefs, e.g. "bool" (which is a macro
 # according to <stdbool.h>), and may include some names we don't want
 # treated as typedefs, although various headers that some builds include
-# might make them so.  For the moment we just hardwire a list of names
-# to add and a list of names to exclude; eventually this may need to be
-# easier to configure.  Note that the typedefs need trailing newlines.
+# might make them so.  Extra items can be added within individual source files
+# with special annotation comments; see run_indent.
 my @additional = ("bool\n");
 
 my %excluded = map { +"$_\n" => 1 } qw(
@@ -264,6 +263,12 @@ sub run_indent
 
 	my $cmd = "$indent $indent_opts -U" . $filtered_typedefs_fh->filename;
 
+	# add file-local typenames in inline annotations
+	foreach ($source =~ /\@pgindent typename (\w+)\@/g)
+	{
+		$cmd .= " -T'$_'";
+	}
+
 	my $tmp_fh = new File::Temp(TEMPLATE => "pgsrcXXXXX");
 	my $filename = $tmp_fh->filename;
 	print $tmp_fh $source;
-- 
2.30.2



Attachments:

  [text/plain] 0001-Teach-pgindent-about-special-file-local-typenames.txt (15.8K, ../../CA+hUKGK0w6sKNRPx4bgS_+DSY5LxKTZnDqxCL5PmEo3tKGySgw@mail.gmail.com/2-0001-Teach-pgindent-about-special-file-local-typenames.txt)
  download | inline diff:
From 8252eb18c19c8a78fa6326ae5de8261d7d793dda Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Thu, 22 Jul 2021 19:05:15 +1200
Subject: [PATCH] Teach pgindent about special file-local typenames.

Allow source files to declare an extra typename with a special
annotation of the form:

    @pgindent typename XXX@

Use these to fix some whitespace problems in simplehash.h and
sort_template.h.
---
 src/include/lib/simplehash.h    | 110 +++++++++++++++++---------------
 src/include/lib/sort_template.h |  21 +++---
 src/tools/pgindent/pgindent     |  11 +++-
 3 files changed, 79 insertions(+), 63 deletions(-)

diff --git a/src/include/lib/simplehash.h b/src/include/lib/simplehash.h
index da51781e98..3e44c7cc03 100644
--- a/src/include/lib/simplehash.h
+++ b/src/include/lib/simplehash.h
@@ -86,6 +86,12 @@
  *	  presence is relevant to determine whether a lookup needs to continue
  *	  looking or is done - buckets following a deleted element are shifted
  *	  backwards, unless they're empty or already at their optimal position.
+ *
+ * Help pgindent understand our pseudo-typenames:
+ *
+ *	  @pgindent typename SH_ELEMENT_TYPE@
+ *	  @pgindent typename SH_TYPE@
+ *	  @pgindent typename SH_ITERATOR@
  */
 
 #include "port/pg_bitutils.h"
@@ -164,7 +170,7 @@ typedef struct SH_TYPE
 
 	/* user defined data, useful for callbacks */
 	void	   *private_data;
-}			SH_TYPE;
+} SH_TYPE;
 
 typedef enum SH_STATUS
 {
@@ -177,67 +183,67 @@ typedef struct SH_ITERATOR
 	uint32		cur;			/* current element */
 	uint32		end;
 	bool		done;			/* iterator exhausted? */
-}			SH_ITERATOR;
+} SH_ITERATOR;
 
 /* externally visible function prototypes */
 #ifdef SH_RAW_ALLOCATOR
 /* <prefix>_hash <prefix>_create(uint32 nelements, void *private_data) */
-SH_SCOPE	SH_TYPE *SH_CREATE(uint32 nelements, void *private_data);
+SH_SCOPE SH_TYPE *SH_CREATE(uint32 nelements, void *private_data);
 #else
 /*
  * <prefix>_hash <prefix>_create(MemoryContext ctx, uint32 nelements,
  *								 void *private_data)
  */
-SH_SCOPE	SH_TYPE *SH_CREATE(MemoryContext ctx, uint32 nelements,
-							   void *private_data);
+SH_SCOPE SH_TYPE *SH_CREATE(MemoryContext ctx, uint32 nelements,
+							void *private_data);
 #endif
 
 /* void <prefix>_destroy(<prefix>_hash *tb) */
-SH_SCOPE void SH_DESTROY(SH_TYPE * tb);
+SH_SCOPE void SH_DESTROY(SH_TYPE *tb);
 
 /* void <prefix>_reset(<prefix>_hash *tb) */
-SH_SCOPE void SH_RESET(SH_TYPE * tb);
+SH_SCOPE void SH_RESET(SH_TYPE *tb);
 
 /* void <prefix>_grow(<prefix>_hash *tb) */
-SH_SCOPE void SH_GROW(SH_TYPE * tb, uint32 newsize);
+SH_SCOPE void SH_GROW(SH_TYPE *tb, uint32 newsize);
 
 /* <element> *<prefix>_insert(<prefix>_hash *tb, <key> key, bool *found) */
-SH_SCOPE	SH_ELEMENT_TYPE *SH_INSERT(SH_TYPE * tb, SH_KEY_TYPE key, bool *found);
+SH_SCOPE SH_ELEMENT_TYPE *SH_INSERT(SH_TYPE *tb, SH_KEY_TYPE key, bool *found);
 
 /*
  * <element> *<prefix>_insert_hash(<prefix>_hash *tb, <key> key, uint32 hash,
  * 								  bool *found)
  */
-SH_SCOPE	SH_ELEMENT_TYPE *SH_INSERT_HASH(SH_TYPE * tb, SH_KEY_TYPE key,
-											uint32 hash, bool *found);
+SH_SCOPE SH_ELEMENT_TYPE *SH_INSERT_HASH(SH_TYPE *tb, SH_KEY_TYPE key,
+										 uint32 hash, bool *found);
 
 /* <element> *<prefix>_lookup(<prefix>_hash *tb, <key> key) */
-SH_SCOPE	SH_ELEMENT_TYPE *SH_LOOKUP(SH_TYPE * tb, SH_KEY_TYPE key);
+SH_SCOPE SH_ELEMENT_TYPE *SH_LOOKUP(SH_TYPE *tb, SH_KEY_TYPE key);
 
 /* <element> *<prefix>_lookup_hash(<prefix>_hash *tb, <key> key, uint32 hash) */
-SH_SCOPE	SH_ELEMENT_TYPE *SH_LOOKUP_HASH(SH_TYPE * tb, SH_KEY_TYPE key,
-											uint32 hash);
+SH_SCOPE SH_ELEMENT_TYPE *SH_LOOKUP_HASH(SH_TYPE *tb, SH_KEY_TYPE key,
+										 uint32 hash);
 
 /* void <prefix>_delete_item(<prefix>_hash *tb, <element> *entry) */
-SH_SCOPE void SH_DELETE_ITEM(SH_TYPE * tb, SH_ELEMENT_TYPE * entry);
+SH_SCOPE void SH_DELETE_ITEM(SH_TYPE *tb, SH_ELEMENT_TYPE *entry);
 
 /* bool <prefix>_delete(<prefix>_hash *tb, <key> key) */
-SH_SCOPE bool SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key);
+SH_SCOPE bool SH_DELETE(SH_TYPE *tb, SH_KEY_TYPE key);
 
 /* void <prefix>_start_iterate(<prefix>_hash *tb, <prefix>_iterator *iter) */
-SH_SCOPE void SH_START_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter);
+SH_SCOPE void SH_START_ITERATE(SH_TYPE *tb, SH_ITERATOR *iter);
 
 /*
  * void <prefix>_start_iterate_at(<prefix>_hash *tb, <prefix>_iterator *iter,
  *								  uint32 at)
  */
-SH_SCOPE void SH_START_ITERATE_AT(SH_TYPE * tb, SH_ITERATOR * iter, uint32 at);
+SH_SCOPE void SH_START_ITERATE_AT(SH_TYPE *tb, SH_ITERATOR *iter, uint32 at);
 
 /* <element> *<prefix>_iterate(<prefix>_hash *tb, <prefix>_iterator *iter) */
-SH_SCOPE	SH_ELEMENT_TYPE *SH_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter);
+SH_SCOPE SH_ELEMENT_TYPE *SH_ITERATE(SH_TYPE *tb, SH_ITERATOR *iter);
 
 /* void <prefix>_stat(<prefix>_hash *tb */
-SH_SCOPE void SH_STAT(SH_TYPE * tb);
+SH_SCOPE void SH_STAT(SH_TYPE *tb);
 
 #endif							/* SH_DECLARE */
 
@@ -302,7 +308,7 @@ SH_SCOPE void SH_STAT(SH_TYPE * tb);
  * the hashtable.
  */
 static inline void
-SH_COMPUTE_PARAMETERS(SH_TYPE * tb, uint32 newsize)
+SH_COMPUTE_PARAMETERS(SH_TYPE *tb, uint32 newsize)
 {
 	uint64		size;
 
@@ -340,14 +346,14 @@ SH_COMPUTE_PARAMETERS(SH_TYPE * tb, uint32 newsize)
 
 /* return the optimal bucket for the hash */
 static inline uint32
-SH_INITIAL_BUCKET(SH_TYPE * tb, uint32 hash)
+SH_INITIAL_BUCKET(SH_TYPE *tb, uint32 hash)
 {
 	return hash & tb->sizemask;
 }
 
 /* return next bucket after the current, handling wraparound */
 static inline uint32
-SH_NEXT(SH_TYPE * tb, uint32 curelem, uint32 startelem)
+SH_NEXT(SH_TYPE *tb, uint32 curelem, uint32 startelem)
 {
 	curelem = (curelem + 1) & tb->sizemask;
 
@@ -358,7 +364,7 @@ SH_NEXT(SH_TYPE * tb, uint32 curelem, uint32 startelem)
 
 /* return bucket before the current, handling wraparound */
 static inline uint32
-SH_PREV(SH_TYPE * tb, uint32 curelem, uint32 startelem)
+SH_PREV(SH_TYPE *tb, uint32 curelem, uint32 startelem)
 {
 	curelem = (curelem - 1) & tb->sizemask;
 
@@ -369,7 +375,7 @@ SH_PREV(SH_TYPE * tb, uint32 curelem, uint32 startelem)
 
 /* return distance between bucket and its optimal position */
 static inline uint32
-SH_DISTANCE_FROM_OPTIMAL(SH_TYPE * tb, uint32 optimal, uint32 bucket)
+SH_DISTANCE_FROM_OPTIMAL(SH_TYPE *tb, uint32 optimal, uint32 bucket)
 {
 	if (optimal <= bucket)
 		return bucket - optimal;
@@ -378,7 +384,7 @@ SH_DISTANCE_FROM_OPTIMAL(SH_TYPE * tb, uint32 optimal, uint32 bucket)
 }
 
 static inline uint32
-SH_ENTRY_HASH(SH_TYPE * tb, SH_ELEMENT_TYPE * entry)
+SH_ENTRY_HASH(SH_TYPE *tb, SH_ELEMENT_TYPE *entry)
 {
 #ifdef SH_STORE_HASH
 	return SH_GET_HASH(tb, entry);
@@ -388,14 +394,14 @@ SH_ENTRY_HASH(SH_TYPE * tb, SH_ELEMENT_TYPE * entry)
 }
 
 /* default memory allocator function */
-static inline void *SH_ALLOCATE(SH_TYPE * type, Size size);
-static inline void SH_FREE(SH_TYPE * type, void *pointer);
+static inline void *SH_ALLOCATE(SH_TYPE *type, Size size);
+static inline void SH_FREE(SH_TYPE *type, void *pointer);
 
 #ifndef SH_USE_NONDEFAULT_ALLOCATOR
 
 /* default memory allocator function */
 static inline void *
-SH_ALLOCATE(SH_TYPE * type, Size size)
+SH_ALLOCATE(SH_TYPE *type, Size size)
 {
 #ifdef SH_RAW_ALLOCATOR
 	return SH_RAW_ALLOCATOR(size);
@@ -407,7 +413,7 @@ SH_ALLOCATE(SH_TYPE * type, Size size)
 
 /* default memory free function */
 static inline void
-SH_FREE(SH_TYPE * type, void *pointer)
+SH_FREE(SH_TYPE *type, void *pointer)
 {
 	pfree(pointer);
 }
@@ -424,10 +430,10 @@ SH_FREE(SH_TYPE * type, void *pointer)
  * the passed-in context.
  */
 #ifdef SH_RAW_ALLOCATOR
-SH_SCOPE	SH_TYPE *
+SH_SCOPE SH_TYPE *
 SH_CREATE(uint32 nelements, void *private_data)
 #else
-SH_SCOPE	SH_TYPE *
+SH_SCOPE SH_TYPE *
 SH_CREATE(MemoryContext ctx, uint32 nelements, void *private_data)
 #endif
 {
@@ -454,7 +460,7 @@ SH_CREATE(MemoryContext ctx, uint32 nelements, void *private_data)
 
 /* destroy a previously created hash table */
 SH_SCOPE void
-SH_DESTROY(SH_TYPE * tb)
+SH_DESTROY(SH_TYPE *tb)
 {
 	SH_FREE(tb, tb->data);
 	pfree(tb);
@@ -462,7 +468,7 @@ SH_DESTROY(SH_TYPE * tb)
 
 /* reset the contents of a previously created hash table */
 SH_SCOPE void
-SH_RESET(SH_TYPE * tb)
+SH_RESET(SH_TYPE *tb)
 {
 	memset(tb->data, 0, sizeof(SH_ELEMENT_TYPE) * tb->size);
 	tb->members = 0;
@@ -476,7 +482,7 @@ SH_RESET(SH_TYPE * tb)
  * performance-wise, when known at some point.
  */
 SH_SCOPE void
-SH_GROW(SH_TYPE * tb, uint32 newsize)
+SH_GROW(SH_TYPE *tb, uint32 newsize)
 {
 	uint64		oldsize = tb->size;
 	SH_ELEMENT_TYPE *olddata = tb->data;
@@ -586,7 +592,7 @@ SH_GROW(SH_TYPE * tb, uint32 newsize)
  * into its wrapper functions even if SH_SCOPE is extern.
  */
 static inline SH_ELEMENT_TYPE *
-SH_INSERT_HASH_INTERNAL(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash, bool *found)
+SH_INSERT_HASH_INTERNAL(SH_TYPE *tb, SH_KEY_TYPE key, uint32 hash, bool *found)
 {
 	uint32		startelem;
 	uint32		curelem;
@@ -756,8 +762,8 @@ restart:
  * already exists, false otherwise. Returns the hash-table entry in either
  * case.
  */
-SH_SCOPE	SH_ELEMENT_TYPE *
-SH_INSERT(SH_TYPE * tb, SH_KEY_TYPE key, bool *found)
+SH_SCOPE SH_ELEMENT_TYPE *
+SH_INSERT(SH_TYPE *tb, SH_KEY_TYPE key, bool *found)
 {
 	uint32		hash = SH_HASH_KEY(tb, key);
 
@@ -769,8 +775,8 @@ SH_INSERT(SH_TYPE * tb, SH_KEY_TYPE key, bool *found)
  * hash. Set *found to true if the key already exists, false
  * otherwise. Returns the hash-table entry in either case.
  */
-SH_SCOPE	SH_ELEMENT_TYPE *
-SH_INSERT_HASH(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash, bool *found)
+SH_SCOPE SH_ELEMENT_TYPE *
+SH_INSERT_HASH(SH_TYPE *tb, SH_KEY_TYPE key, uint32 hash, bool *found)
 {
 	return SH_INSERT_HASH_INTERNAL(tb, key, hash, found);
 }
@@ -780,7 +786,7 @@ SH_INSERT_HASH(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash, bool *found)
  * into its wrapper functions even if SH_SCOPE is extern.
  */
 static inline SH_ELEMENT_TYPE *
-SH_LOOKUP_HASH_INTERNAL(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash)
+SH_LOOKUP_HASH_INTERNAL(SH_TYPE *tb, SH_KEY_TYPE key, uint32 hash)
 {
 	const uint32 startelem = SH_INITIAL_BUCKET(tb, hash);
 	uint32		curelem = startelem;
@@ -813,8 +819,8 @@ SH_LOOKUP_HASH_INTERNAL(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash)
 /*
  * Lookup up entry in hash table.  Returns NULL if key not present.
  */
-SH_SCOPE	SH_ELEMENT_TYPE *
-SH_LOOKUP(SH_TYPE * tb, SH_KEY_TYPE key)
+SH_SCOPE SH_ELEMENT_TYPE *
+SH_LOOKUP(SH_TYPE *tb, SH_KEY_TYPE key)
 {
 	uint32		hash = SH_HASH_KEY(tb, key);
 
@@ -826,8 +832,8 @@ SH_LOOKUP(SH_TYPE * tb, SH_KEY_TYPE key)
  *
  * Returns NULL if key not present.
  */
-SH_SCOPE	SH_ELEMENT_TYPE *
-SH_LOOKUP_HASH(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash)
+SH_SCOPE SH_ELEMENT_TYPE *
+SH_LOOKUP_HASH(SH_TYPE *tb, SH_KEY_TYPE key, uint32 hash)
 {
 	return SH_LOOKUP_HASH_INTERNAL(tb, key, hash);
 }
@@ -837,7 +843,7 @@ SH_LOOKUP_HASH(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash)
  * present.
  */
 SH_SCOPE bool
-SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key)
+SH_DELETE(SH_TYPE *tb, SH_KEY_TYPE key)
 {
 	uint32		hash = SH_HASH_KEY(tb, key);
 	uint32		startelem = SH_INITIAL_BUCKET(tb, hash);
@@ -908,7 +914,7 @@ SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key)
  * Delete entry from hash table by entry pointer
  */
 SH_SCOPE void
-SH_DELETE_ITEM(SH_TYPE * tb, SH_ELEMENT_TYPE * entry)
+SH_DELETE_ITEM(SH_TYPE *tb, SH_ELEMENT_TYPE *entry)
 {
 	SH_ELEMENT_TYPE *lastentry = entry;
 	uint32		hash = SH_ENTRY_HASH(tb, entry);
@@ -963,7 +969,7 @@ SH_DELETE_ITEM(SH_TYPE * tb, SH_ELEMENT_TYPE * entry)
  * Initialize iterator.
  */
 SH_SCOPE void
-SH_START_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter)
+SH_START_ITERATE(SH_TYPE *tb, SH_ITERATOR *iter)
 {
 	int			i;
 	uint64		startelem = PG_UINT64_MAX;
@@ -1003,7 +1009,7 @@ SH_START_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter)
  * same position.
  */
 SH_SCOPE void
-SH_START_ITERATE_AT(SH_TYPE * tb, SH_ITERATOR * iter, uint32 at)
+SH_START_ITERATE_AT(SH_TYPE *tb, SH_ITERATOR *iter, uint32 at)
 {
 	/*
 	 * Iterate backwards, that allows the current element to be deleted, even
@@ -1024,8 +1030,8 @@ SH_START_ITERATE_AT(SH_TYPE * tb, SH_ITERATOR * iter, uint32 at)
  * deletions), but if so, there's neither a guarantee that all nodes are
  * visited at least once, nor a guarantee that a node is visited at most once.
  */
-SH_SCOPE	SH_ELEMENT_TYPE *
-SH_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter)
+SH_SCOPE SH_ELEMENT_TYPE *
+SH_ITERATE(SH_TYPE *tb, SH_ITERATOR *iter)
 {
 	while (!iter->done)
 	{
@@ -1052,7 +1058,7 @@ SH_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter)
  * debugging/profiling purposes only.
  */
 SH_SCOPE void
-SH_STAT(SH_TYPE * tb)
+SH_STAT(SH_TYPE *tb)
 {
 	uint32		max_chain_length = 0;
 	uint32		total_chain_length = 0;
diff --git a/src/include/lib/sort_template.h b/src/include/lib/sort_template.h
index f52627d8ce..cb9d9bf449 100644
--- a/src/include/lib/sort_template.h
+++ b/src/include/lib/sort_template.h
@@ -52,6 +52,11 @@
  *	  int (*)(const ST_ELEMENT_TYPE *a, const ST_ELEMENT_TYPE *b,
  *			  [ST_COMPARE_ARG_TYPE *arg])
  *
+ * 	  Help pgindent understand our pseudo-typenames:
+ *
+ *		@pgindent typename ST_ELEMENT_TYPE@
+ *		@pgindent typename ST_POINTER_TYPE@
+ *
  * HISTORY
  *
  *	  Modifications from vanilla NetBSD source:
@@ -176,11 +181,11 @@
 
 #ifdef ST_COMPARE_RUNTIME_POINTER
 typedef int (*ST_COMPARATOR_TYPE_NAME) (const ST_ELEMENT_TYPE *,
-										const ST_ELEMENT_TYPE * ST_SORT_PROTO_ARG);
+										const ST_ELEMENT_TYPE *ST_SORT_PROTO_ARG);
 #endif
 
 /* Declare the sort function.  Note optional arguments at end. */
-ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE * first, size_t n
+ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE *first, size_t n
 					  ST_SORT_PROTO_ELEMENT_SIZE
 					  ST_SORT_PROTO_COMPARE
 					  ST_SORT_PROTO_ARG);
@@ -245,9 +250,9 @@ ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE * first, size_t n
  * 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_MED3(ST_ELEMENT_TYPE *a,
+		ST_ELEMENT_TYPE *b,
+		ST_ELEMENT_TYPE *c
 		ST_SORT_PROTO_COMPARE
 		ST_SORT_PROTO_ARG)
 {
@@ -257,7 +262,7 @@ ST_MED3(ST_ELEMENT_TYPE * a,
 }
 
 static inline void
-ST_SWAP(ST_POINTER_TYPE * a, ST_POINTER_TYPE * b)
+ST_SWAP(ST_POINTER_TYPE *a, ST_POINTER_TYPE *b)
 {
 	ST_POINTER_TYPE tmp = *a;
 
@@ -266,7 +271,7 @@ ST_SWAP(ST_POINTER_TYPE * a, ST_POINTER_TYPE * b)
 }
 
 static inline void
-ST_SWAPN(ST_POINTER_TYPE * a, ST_POINTER_TYPE * b, size_t n)
+ST_SWAPN(ST_POINTER_TYPE *a, ST_POINTER_TYPE *b, size_t n)
 {
 	for (size_t i = 0; i < n; ++i)
 		ST_SWAP(&a[i], &b[i]);
@@ -276,7 +281,7 @@ ST_SWAPN(ST_POINTER_TYPE * a, ST_POINTER_TYPE * b, size_t n)
  * Sort an array.
  */
 ST_SCOPE void
-ST_SORT(ST_ELEMENT_TYPE * data, size_t n
+ST_SORT(ST_ELEMENT_TYPE *data, size_t n
 		ST_SORT_PROTO_ELEMENT_SIZE
 		ST_SORT_PROTO_COMPARE
 		ST_SORT_PROTO_ARG)
diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent
index f8190b6c35..2d62245648 100755
--- a/src/tools/pgindent/pgindent
+++ b/src/tools/pgindent/pgindent
@@ -56,9 +56,8 @@ $excludes ||= "$code_base/src/tools/pgindent/exclude_file_patterns"
 # some names we want to treat like typedefs, e.g. "bool" (which is a macro
 # according to <stdbool.h>), and may include some names we don't want
 # treated as typedefs, although various headers that some builds include
-# might make them so.  For the moment we just hardwire a list of names
-# to add and a list of names to exclude; eventually this may need to be
-# easier to configure.  Note that the typedefs need trailing newlines.
+# might make them so.  Extra items can be added within individual source files
+# with special annotation comments; see run_indent.
 my @additional = ("bool\n");
 
 my %excluded = map { +"$_\n" => 1 } qw(
@@ -264,6 +263,12 @@ sub run_indent
 
 	my $cmd = "$indent $indent_opts -U" . $filtered_typedefs_fh->filename;
 
+	# add file-local typenames in inline annotations
+	foreach ($source =~ /\@pgindent typename (\w+)\@/g)
+	{
+		$cmd .= " -T'$_'";
+	}
+
 	my $tmp_fh = new File::Temp(TEMPLATE => "pgsrcXXXXX");
 	my $filename = $tmp_fh->filename;
 	print $tmp_fh $source;
-- 
2.30.2



^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-07-30 00:34  John Naylor <[email protected]>
  parent: Thomas Munro <[email protected]>
  1 sibling, 3 replies; 55+ messages in thread

From: John Naylor @ 2021-07-30 00:34 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: pgsql-hackers

On Sun, Jul 4, 2021 at 12:27 AM Thomas Munro <[email protected]> wrote:
>
> Since you are experimenting with tuplesort and likely thinking similar
> thoughts, here's a patch I've been using to explore that area.  I've
> seen it get, for example, ~1.18x speedup for simple index builds in
> favourable winds (YMMV, early hacking results only).  Currently, it
> kicks in when the leading column is of type int4, int8, timestamp,
> timestamptz, date or text + friends (when abbreviatable, currently
> that means "C" and ICU collations only), while increasing the
> executable by only 8.5kB (Clang, amd64, -O2, no debug).
>
> These types are handled with just three specialisations.  Their custom
> "fast" comparators all boiled down to comparisons of datum bits,
> varying only in signedness and width, so I tried throwing them away
> and using 3 new common routines.  Then I extended
> tuplesort_sort_memtuples()'s pre-existing specialisation dispatch to
> recognise qualifying users of those and select 3 corresponding sort
> specialisations.

I got around to getting a benchmark together to serve as a starting point.
I based it off something I got from the archives, but don't remember where
(I seem to remember Tomas Vondra wrote the original, but not sure). To
start I just used types that were there already -- int, text, numeric. The
latter two won't be helped by this patch, but I wanted to keep something
like that so we can see what kind of noise variation there is. I'll
probably cut text out in the future and just keep numeric for that purpose.

I've attached both the script and a crude spreadsheet. I'll try to figure
out something nicer for future tests, and maybe some graphs. The
"comparison" sheet has the results side by side (min of five). There are 6
distributions of values:
- random
- sorted
- "almost sorted"
- reversed
- organ pipe (first half ascending, second half descending)
- rotated (sorted but then put the smallest at the end)
- random 0s/1s

I included both "select a" and "select *" to make sure we have the recent
datum sort optimization represented. The results look pretty good for ints
-- about the same speed up master gets going from tuple sorts to datum
sorts, and those got faster in turn also.

Next I think I'll run microbenchmarks on int64s with the test harness you
attached earlier, and experiment with the qsort parameters a bit.

I'm also attaching your tuplesort patch so others can see what exactly I'm
comparing.

--
John Naylor
EDB: http://www.enterprisedb.com


Attachments:

  [text/x-sh] sort-bench-jcn1.sh (7.5K, ../../CAFBsxsGzW5zTEGP6HPPh9vxkxJd_6kYN1-mRi6bKy+nM+oNpdA@mail.gmail.com/3-sort-bench-jcn1.sh)
  download | inline:
set -e

ROWS=$1
WORK_MEM=$2


function log {
	echo `date +%s` [`date +'%Y-%m-%d %H:%M:%S'`] $1
}

function create_tables {

	./inst/bin/psql > /dev/null  <<EOF
-- tables for source data
DROP TABLE IF EXISTS data_int;
CREATE TABLE data_int (a INT);
INSERT INTO  data_int SELECT i FROM generate_series(1, $ROWS) s(i);

DROP TABLE IF EXISTS data_flt;
CREATE TABLE data_flt (a FLOAT);
INSERT INTO  data_flt SELECT 100000 * random() FROM generate_series(1, $ROWS) s(i);

-- tables used for the actual testing
CREATE TABLE IF NOT EXISTS int_test         (a INT, b TEXT);
CREATE TABLE IF NOT EXISTS txt_test         (a TEXT, b TEXT);
CREATE TABLE IF NOT EXISTS num_test         (a NUMERIC, b TEXT);
EOF

}

function truncate_tables {

	log "truncating tables"

	./inst/bin/psql > /dev/null  <<EOF
TRUNCATE TABLE int_test;
TRUNCATE TABLE txt_test;
TRUNCATE TABLE num_test;
EOF

}

function vacuum_analyze {

	log "analyzing"

	./inst/bin/psql > /dev/null  <<EOF
VACUUM ANALYZE;
CHECKPOINT;
EOF

}

function prewarm {
	log "prewarming buffers"

	./inst/bin/psql > /dev/null  <<EOF
SELECT pg_prewarm(oid) FROM pg_class WHERE oid > 16384 AND relkind = 'r';
EOF

}

################# load test tables

function load_random {

	truncate_tables

	log "loading random"

	./inst/bin/psql > /dev/null  <<EOF
SET work_mem = '$WORK_MEM';

-- this needs randomization
INSERT INTO int_test SELECT a, md5(a::text) FROM data_int ORDER BY random();

-- these already are random
INSERT INTO txt_test SELECT md5(a::text), md5((a+1)::text) FROM data_int;
INSERT INTO num_test SELECT a, md5(a::text) FROM data_flt;
EOF

	prewarm

	vacuum_analyze

}

function load_sorted {

	truncate_tables

	log "loading sorted"

	./inst/bin/psql > /dev/null  <<EOF
SET work_mem = '$WORK_MEM';

INSERT INTO int_test SELECT a, md5(a::text) FROM data_int ORDER BY 1;
INSERT INTO txt_test SELECT md5(a::text), md5((a+1)::text) FROM data_int ORDER BY 1;
INSERT INTO num_test SELECT a, md5(a::text) FROM data_flt ORDER BY 1;
EOF

	prewarm

	vacuum_analyze

}

function load_almost_sorted {

	truncate_tables

	log "loading almost sorted"

	./inst/bin/psql > /dev/null  <<EOF
SET work_mem = '$WORK_MEM';

INSERT INTO int_test SELECT a, b FROM (
    SELECT a, md5(a::text) AS b, rank() OVER (ORDER BY a) AS r FROM data_int
) foo ORDER BY r + (100 * random());

INSERT INTO txt_test SELECT a, b FROM (
    SELECT md5(a::text) AS a, md5(((a+1))::text) AS b, rank() OVER (ORDER BY md5(a::text)) AS r FROM data_int
) foo ORDER BY r + (100 * random());

INSERT INTO num_test SELECT a, b FROM (
    SELECT a, md5(a::text) AS b, rank() OVER (ORDER BY a) AS r FROM data_flt
) foo ORDER BY r + (100 * random());
EOF

	prewarm

	vacuum_analyze

}

function load_reversed {

	truncate_tables

	log "loading reversed"

	./inst/bin/psql > /dev/null  <<EOF
SET work_mem = '$WORK_MEM';

INSERT INTO int_test SELECT a, md5(a::text) FROM data_int ORDER BY 1 DESC;
INSERT INTO txt_test SELECT md5(a::text), md5((a+1)::text) FROM data_int ORDER BY 1 DESC;
INSERT INTO num_test SELECT a, md5(a::text) FROM data_flt ORDER BY 1 DESC;
EOF

	prewarm

	vacuum_analyze

}

function load_organ_pipe {

	truncate_tables

	log "loading organ pipe"

	./inst/bin/psql > /dev/null  <<EOF
SET work_mem = '$WORK_MEM';

WITH c AS (SELECT count(*)/2 AS half from data_int)
INSERT INTO int_test
(SELECT a, md5(a::text) FROM data_int ORDER BY 1 ASC  LIMIT (SELECT half from c))
UNION ALL
(SELECT a, md5(a::text) FROM data_int ORDER BY 1 DESC LIMIT (SELECT half from c));

WITH c AS (SELECT count(*)/2 AS half from data_int)
INSERT INTO txt_test
(SELECT md5(a::text), md5((a+1)::text) FROM data_int ORDER BY 1 ASC  LIMIT (SELECT half from c))
UNION ALL
(SELECT md5(a::text), md5((a+1)::text) FROM data_int ORDER BY 1 DESC LIMIT (SELECT half from c));

WITH c AS (SELECT count(*)/2 AS half from num_test)
INSERT INTO num_test
(SELECT a, md5(a::text) FROM data_int ORDER BY 1 ASC  LIMIT (SELECT half from c))
UNION ALL
(SELECT a, md5(a::text) FROM data_int ORDER BY 1 DESC LIMIT (SELECT half from c));
EOF

	prewarm

	vacuum_analyze

}

function load_rotated {

	truncate_tables

	log "loading rotated"

	./inst/bin/psql > /dev/null  <<EOF
SET work_mem = '$WORK_MEM';

INSERT INTO int_test SELECT a, md5(a::text) FROM data_int;
INSERT INTO int_test VALUES (0, 'b');

INSERT INTO txt_test SELECT md5(a::text), md5((a+1)::text) FROM data_int ORDER BY 1;
INSERT INTO txt_test VALUES ('a', 'b');

INSERT INTO num_test SELECT a + 1.0 FROM data_flt ORDER BY 1;
INSERT INTO num_test VALUES (0.0, 'b');
EOF

	prewarm

	vacuum_analyze

}

function load_0_1 {

	truncate_tables

	log "loading 0 1"

	./inst/bin/psql > /dev/null  <<EOF
SET work_mem = '$WORK_MEM';

INSERT INTO int_test SELECT round(a), md5(a::text) FROM data_int;
INSERT INTO txt_test SELECT 
	case when a > 0.5 
	then 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
	else 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
	end
FROM data_int;
INSERT INTO num_test SELECT round(a), md5(a::text) FROM data_int;
EOF

	prewarm

	vacuum_analyze

}

################# run

function run_query {

	times=""
	group=$1
	wmem=$2
	workers=$3
	query=$4

	echo "--" `date` [`date +%s`] >> explains.log
	echo "-- $group rows=$ROWS work_mem=$wmem workers=$workers" >> explains.log

	./inst/bin/psql >> explains.log 2>&1 <<EOF
SET trace_sort=on;
SET work_mem='$wmem';
SET max_parallel_workers_per_gather=$workers;
explain $query
EOF

	s=`date +%s`

	for i in `seq 1 5`; do

		/usr/bin/time -f '%e' -o 'query.time' ./inst/bin/psql > /dev/null <<EOF
\pset pager off
\o /dev/null
SET trace_sort=on;
SET work_mem='$wmem';
SET max_parallel_workers_per_gather=$workers;
COPY ($query) TO '/dev/null'
EOF

		x=`cat query.time`
		times="$times\t$x"

	done

	e=`date +%s`

	echo -e "$group\t$ROWS\t$wmem\t$workers\t$s\t$e\t$query\t$times" >> results.csv
}

function run_index {

        times=""
        group=$1
        wmem=$2
        workers=$3
        query=$4

        times=""

	s=`date +%s`

    for i in `seq 1 5`; do

        /usr/bin/time -f '%e' -o 'query.time' ./inst/bin/psql > /dev/null <<EOF
\pset pager off
\o /dev/null
SET maintenance_work_mem='$wmem';
$query
EOF

                x=`cat query.time`
                times="$times\t$x"

        done

	e=`date +%s`

        echo -e "$group\t$ROWS\t$wmem\t$workers\t$s\t$e\t$query\t$times" >> results.csv
}

function run_queries {

#	for wm in '1MB' '8MB' '32MB' '128MB' '512MB' '1GB'; do
	for wm in $WORK_MEM; do

		log "running queries work_mem=$wm noparallel"

		run_query $1 $wm 0 'SELECT a FROM int_test ORDER BY 1 OFFSET '$ROWS''
		run_query $1 $wm 0 'SELECT * FROM int_test ORDER BY 1 OFFSET '$ROWS''
#		run_query $1 $wm 0 'SELECT a FROM int_test ORDER BY 1 DESC OFFSET '$ROWS''
#		run_query $1 $wm 0 'SELECT * FROM int_test ORDER BY 1 DESC OFFSET '$ROWS''
		run_index $1 $wm 0 'CREATE INDEX x ON int_test (a); DROP INDEX x'

		run_query $1 $wm 0 'SELECT a FROM txt_test ORDER BY 1 OFFSET '$ROWS''
		run_query $1 $wm 0 'SELECT * FROM txt_test ORDER BY 1 OFFSET '$ROWS''
#		run_query $1 $wm 0 'SELECT a FROM txt_test ORDER BY 1 DESC OFFSET '$ROWS''
#		run_query $1 $wm 0 'SELECT * FROM txt_test ORDER BY 1 DESC OFFSET '$ROWS''
		run_index $1 $wm 0 'CREATE INDEX x ON txt_test (a); DROP INDEX x'

		run_query $1 $wm 0 'SELECT * FROM num_test ORDER BY 1 OFFSET '$ROWS''
#		run_query $1 $wm 0 'SELECT * FROM num_test ORDER BY 1 DESC OFFSET '$ROWS''
		run_index $1 $wm 0 'CREATE INDEX x ON num_test (a); DROP INDEX x'

	done

}

create_tables

log "sort benchmark $ROWS"

load_random
run_queries "random"

load_sorted
run_queries "sorted"

load_almost_sorted
run_queries "almost_sorted"

load_reversed
run_queries "reversed"

load_organ_pipe
run_queries "organ_pipe"

load_rotated
run_queries "rotated"

load_0_1
run_queries "0_1"

  [application/vnd.oasis.opendocument.spreadsheet] test-acc-tuplesort-20210729.ods (17.9K, ../../CAFBsxsGzW5zTEGP6HPPh9vxkxJd_6kYN1-mRi6bKy+nM+oNpdA@mail.gmail.com/4-test-acc-tuplesort-20210729.ods)
  download

  [application/octet-stream] 0001-WIP-Accelerate-tuple-sorting-for-common-types.patch (15.5K, ../../CAFBsxsGzW5zTEGP6HPPh9vxkxJd_6kYN1-mRi6bKy+nM+oNpdA@mail.gmail.com/5-0001-WIP-Accelerate-tuple-sorting-for-common-types.patch)
  download | inline diff:
From 62a8acbc745f3b4a90c9a14b6b61989a9d83bece Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Sat, 3 Jul 2021 19:02:10 +1200
Subject: [PATCH] WIP: Accelerate tuple sorting for common types.

Several data types have their own fast comparator functions that can be
replaced with common binary or signed comparator functions.  These
functions can then be recognized by tuplesort.c and used to dispatch to
corresponding specialized sort functions, to accelerate sorting.

XXX WIP, experiment grade only, many open questions...
---
 src/backend/access/nbtree/nbtcompare.c |  22 ++--
 src/backend/utils/adt/date.c           |  15 +--
 src/backend/utils/adt/timestamp.c      |  11 ++
 src/backend/utils/adt/varlena.c        |  26 +---
 src/backend/utils/sort/tuplesort.c     | 161 ++++++++++++++++++++++++-
 src/include/utils/sortsupport.h        | 115 ++++++++++++++++++
 6 files changed, 295 insertions(+), 55 deletions(-)

diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c
index 7ac73cb8c2..204cf778fb 100644
--- a/src/backend/access/nbtree/nbtcompare.c
+++ b/src/backend/access/nbtree/nbtcompare.c
@@ -119,26 +119,12 @@ btint4cmp(PG_FUNCTION_ARGS)
 		PG_RETURN_INT32(A_LESS_THAN_B);
 }
 
-static int
-btint4fastcmp(Datum x, Datum y, SortSupport ssup)
-{
-	int32		a = DatumGetInt32(x);
-	int32		b = DatumGetInt32(y);
-
-	if (a > b)
-		return A_GREATER_THAN_B;
-	else if (a == b)
-		return 0;
-	else
-		return A_LESS_THAN_B;
-}
-
 Datum
 btint4sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
-	ssup->comparator = btint4fastcmp;
+	ssup->comparator = ssup_datum_int32_cmp;
 	PG_RETURN_VOID();
 }
 
@@ -156,6 +142,7 @@ btint8cmp(PG_FUNCTION_ARGS)
 		PG_RETURN_INT32(A_LESS_THAN_B);
 }
 
+#ifndef USE_FLOAT8_BYVAL
 static int
 btint8fastcmp(Datum x, Datum y, SortSupport ssup)
 {
@@ -169,13 +156,18 @@ btint8fastcmp(Datum x, Datum y, SortSupport ssup)
 	else
 		return A_LESS_THAN_B;
 }
+#endif
 
 Datum
 btint8sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
+#ifdef USE_FLOAT8_BYVAL
+	ssup->comparator = ssup_datum_signed_cmp;
+#else
 	ssup->comparator = btint8fastcmp;
+#endif
 	PG_RETURN_VOID();
 }
 
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 0bea16cb67..350c662d50 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -438,25 +438,12 @@ date_cmp(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(0);
 }
 
-static int
-date_fastcmp(Datum x, Datum y, SortSupport ssup)
-{
-	DateADT		a = DatumGetDateADT(x);
-	DateADT		b = DatumGetDateADT(y);
-
-	if (a < b)
-		return -1;
-	else if (a > b)
-		return 1;
-	return 0;
-}
-
 Datum
 date_sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
-	ssup->comparator = date_fastcmp;
+	ssup->comparator = ssup_datum_int32_cmp;
 	PG_RETURN_VOID();
 }
 
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 79761f809c..c678517db6 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -37,6 +37,7 @@
 #include "utils/datetime.h"
 #include "utils/float.h"
 #include "utils/numeric.h"
+#include "utils/sortsupport.h"
 
 /*
  * gcc's -ffast-math switch breaks routines that expect exact results from
@@ -2155,6 +2156,7 @@ timestamp_cmp(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
 }
 
+#ifndef USE_FLOAT8_BYVAL
 /* note: this is used for timestamptz also */
 static int
 timestamp_fastcmp(Datum x, Datum y, SortSupport ssup)
@@ -2164,13 +2166,22 @@ timestamp_fastcmp(Datum x, Datum y, SortSupport ssup)
 
 	return timestamp_cmp_internal(a, b);
 }
+#endif
 
 Datum
 timestamp_sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
+#ifdef USE_FLOAT8_BYVAL
+	/*
+	 * If this build has pass-by-value timestamps, then we can use a standard
+	 * comparator function.
+	 */
+	ssup->comparator = ssup_datum_signed_cmp;
+#else
 	ssup->comparator = timestamp_fastcmp;
+#endif
 	PG_RETURN_VOID();
 }
 
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index d2a11b1b5d..4709d8129e 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -127,7 +127,6 @@ static int	namefastcmp_c(Datum x, Datum y, SortSupport ssup);
 static int	varlenafastcmp_locale(Datum x, Datum y, SortSupport ssup);
 static int	namefastcmp_locale(Datum x, Datum y, SortSupport ssup);
 static int	varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup);
-static int	varstrcmp_abbrev(Datum x, Datum y, SortSupport ssup);
 static Datum varstr_abbrev_convert(Datum original, SortSupport ssup);
 static bool varstr_abbrev_abort(int memtupcount, SortSupport ssup);
 static int32 text_length(Datum str);
@@ -2175,7 +2174,7 @@ varstr_sortsupport(SortSupport ssup, Oid typid, Oid collid)
 			initHyperLogLog(&sss->abbr_card, 10);
 			initHyperLogLog(&sss->full_card, 10);
 			ssup->abbrev_full_comparator = ssup->comparator;
-			ssup->comparator = varstrcmp_abbrev;
+			ssup->comparator = ssup_datum_binary_cmp;
 			ssup->abbrev_converter = varstr_abbrev_convert;
 			ssup->abbrev_abort = varstr_abbrev_abort;
 		}
@@ -2461,27 +2460,6 @@ varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup)
 	return result;
 }
 
-/*
- * Abbreviated key comparison func
- */
-static int
-varstrcmp_abbrev(Datum x, Datum y, SortSupport ssup)
-{
-	/*
-	 * When 0 is returned, the core system will call varstrfastcmp_c()
-	 * (bpcharfastcmp_c() in BpChar case) or varlenafastcmp_locale().  Even a
-	 * strcmp() on two non-truncated strxfrm() blobs cannot indicate *equality*
-	 * authoritatively, for the same reason that there is a strcoll()
-	 * tie-breaker call to strcmp() in varstr_cmp().
-	 */
-	if (x > y)
-		return 1;
-	else if (x == y)
-		return 0;
-	else
-		return -1;
-}
-
 /*
  * Conversion routine for sortsupport.  Converts original to abbreviated key
  * representation.  Our encoding strategy is simple -- pack the first 8 bytes
@@ -2710,7 +2688,7 @@ done:
 	/*
 	 * Byteswap on little-endian machines.
 	 *
-	 * This is needed so that varstrcmp_abbrev() (an unsigned integer 3-way
+	 * This is needed so that ssup_datum_binary_cmp() (an unsigned integer 3-way
 	 * comparator) works correctly on all platforms.  If we didn't do this,
 	 * the comparator would have to call memcmp() with a pair of pointers to
 	 * the first byte of each abbreviated key, which is slower.
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 22972071ff..455bb652b8 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -669,14 +669,101 @@ static void free_sort_tuple(Tuplesortstate *state, SortTuple *stup);
 static void tuplesort_free(Tuplesortstate *state);
 static void tuplesort_updatemax(Tuplesortstate *state);
 
+/*
+ * Specialized comparators that we can inline into specialized sorts.  The goal
+ * is to try to sort two tuples without having to follow the pointers to the
+ * comparator or the tuple.
+ *
+ * XXX: For now, these fall back to comparator functions that will compare the
+ * leading datum a second time.
+ *
+ * XXX: For now, there is no specialization for cases where datum1 is
+ * authoritative and we don't even need to fall back to a callback at all (that
+ * would be true for types like int4/int8/timestamp/date, but not true for
+ * abbreviations of text or multi-key sorts.  There could be!  Is it worth it?
+ */
+
+/* Used if first key's comparator is ssup_datum_binary_compare */
+static pg_attribute_always_inline int
+qsort_tuple_binary_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
+{
+	int			compare;
+
+	compare = ApplyBinarySortComparator(a->datum1, a->isnull1,
+										b->datum1, b->isnull1,
+										&state->sortKeys[0]);
+	if (compare != 0)
+		return compare;
+
+	return state->comparetup(a, b, state);
+}
+
+/* Used if first key's comparator is ssup_datum_signed_compare */
+static pg_attribute_always_inline int
+qsort_tuple_signed_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
+{
+	int			compare;
+
+	compare = ApplySignedSortComparator(a->datum1, a->isnull1,
+										b->datum1, b->isnull1,
+										&state->sortKeys[0]);
+	if (compare != 0)
+		return compare;
+
+	return state->comparetup(a, b, state);
+}
+
+/* Used if first key's comparator is ssup_datum_int32_compare */
+static pg_attribute_always_inline int
+qsort_tuple_int32_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
+{
+	int			compare;
+
+	compare = ApplyInt32SortComparator(a->datum1, a->isnull1,
+										b->datum1, b->isnull1,
+										&state->sortKeys[0]);
+	if (compare != 0)
+		return compare;
+
+	return state->comparetup(a, b, state);
+}
+
 /*
  * Special versions of qsort just for SortTuple objects.  qsort_tuple() sorts
  * any variant of SortTuples, using the appropriate comparetup function.
  * qsort_ssup() is specialized for the case where the comparetup function
  * reduces to ApplySortComparator(), that is single-key MinimalTuple sorts
- * and Datum sorts.
+ * and Datum sorts.  qsort_tuple_{binary,signed,int32} are specialized for
+ * common comparison functions on pass-by-value leading datums.
  */
 
+#define ST_SORT qsort_tuple_binary
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE(a, b, state) qsort_tuple_binary_compare(a, b, state)
+#define ST_COMPARE_ARG_TYPE Tuplesortstate
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+#define ST_SORT qsort_tuple_signed
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE(a, b, state) qsort_tuple_signed_compare(a, b, state)
+#define ST_COMPARE_ARG_TYPE Tuplesortstate
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+#define ST_SORT qsort_tuple_int32
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE(a, b, state) qsort_tuple_int32_compare(a, b, state)
+#define ST_COMPARE_ARG_TYPE Tuplesortstate
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
 #define ST_SORT qsort_tuple
 #define ST_ELEMENT_TYPE SortTuple
 #define ST_COMPARE_RUNTIME_POINTER
@@ -698,6 +785,7 @@ static void tuplesort_updatemax(Tuplesortstate *state);
 #define ST_DEFINE
 #include "lib/sort_template.h"
 
+
 /*
  *		tuplesort_begin_xxx
  *
@@ -3558,15 +3646,40 @@ tuplesort_sort_memtuples(Tuplesortstate *state)
 
 	if (state->memtupcount > 1)
 	{
+		/* Do we have a specialization for the leading column's comparator? */
+		if (state->sortKeys &&
+			state->sortKeys[0].comparator == ssup_datum_binary_cmp)
+		{
+			elog(DEBUG1, "qsort_tuple_binary");
+			qsort_tuple_binary(state->memtuples, state->memtupcount, state);
+		}
+		else if (state->sortKeys &&
+				 state->sortKeys[0].comparator == ssup_datum_signed_cmp)
+		{
+			elog(DEBUG1, "qsort_tuple_signed");
+			qsort_tuple_signed(state->memtuples, state->memtupcount, state);
+		}
+		else if (state->sortKeys &&
+				 state->sortKeys[0].comparator == ssup_datum_int32_cmp)
+		{
+			elog(DEBUG1, "qsort_tuple_int32");
+			qsort_tuple_int32(state->memtuples, state->memtupcount, state);
+		}
 		/* Can we use the single-key sort function? */
-		if (state->onlyKey != NULL)
+		else if (state->onlyKey != NULL)
+		{
+			elog(DEBUG1, "qsort_ssup");
 			qsort_ssup(state->memtuples, state->memtupcount,
 					   state->onlyKey);
+		}
 		else
+		{
+			elog(DEBUG1, "qsort_tuple");
 			qsort_tuple(state->memtuples,
 						state->memtupcount,
 						state->comparetup,
 						state);
+		}
 	}
 }
 
@@ -4776,3 +4889,47 @@ free_sort_tuple(Tuplesortstate *state, SortTuple *stup)
 	FREEMEM(state, GetMemoryChunkSpace(stup->tuple));
 	pfree(stup->tuple);
 }
+
+int
+ssup_datum_binary_cmp(Datum x, Datum y, SortSupport ssup)
+{
+	if (x < y)
+		return -1;
+	else if (x > y)
+		return 1;
+	else
+		return 0;
+}
+
+int
+ssup_datum_signed_cmp(Datum x, Datum y, SortSupport ssup)
+{
+#if SIZEOF_DATUM == 8
+	int64		xx = (int64) x;
+	int64		yy = (int64) y;
+#else
+	int32		xx = (int32) x;
+	int32		yy = (int32) y;
+#endif
+
+	if (xx < yy)
+		return -1;
+	else if (xx > yy)
+		return 1;
+	else
+		return 0;
+}
+
+int
+ssup_datum_int32_cmp(Datum x, Datum y, SortSupport ssup)
+{
+	int32		xx = (int32) x;
+	int32		yy = (int32) y;
+
+	if (xx < yy)
+		return -1;
+	else if (xx > yy)
+		return 1;
+	else
+		return 0;
+}
diff --git a/src/include/utils/sortsupport.h b/src/include/utils/sortsupport.h
index 2f12a8b8eb..8b6a0c5e10 100644
--- a/src/include/utils/sortsupport.h
+++ b/src/include/utils/sortsupport.h
@@ -229,6 +229,112 @@ ApplySortComparator(Datum datum1, bool isNull1,
 	return compare;
 }
 
+static inline int
+ApplyBinarySortComparator(Datum datum1, bool isNull1,
+						  Datum datum2, bool isNull2,
+						  SortSupport ssup)
+{
+	int			compare;
+
+	if (isNull1)
+	{
+		if (isNull2)
+			compare = 0;		/* NULL "=" NULL */
+		else if (ssup->ssup_nulls_first)
+			compare = -1;		/* NULL "<" NOT_NULL */
+		else
+			compare = 1;		/* NULL ">" NOT_NULL */
+	}
+	else if (isNull2)
+	{
+		if (ssup->ssup_nulls_first)
+			compare = 1;		/* NOT_NULL ">" NULL */
+		else
+			compare = -1;		/* NOT_NULL "<" NULL */
+	}
+	else
+	{
+		compare = datum1 < datum2 ? -1 : datum1 > datum2 ? 1 : 0;
+		if (ssup->ssup_reverse)
+			INVERT_COMPARE_RESULT(compare);
+	}
+
+	return compare;
+}
+
+static inline int
+ApplySignedSortComparator(Datum datum1, bool isNull1,
+						  Datum datum2, bool isNull2,
+						  SortSupport ssup)
+{
+	int			compare;
+
+	if (isNull1)
+	{
+		if (isNull2)
+			compare = 0;		/* NULL "=" NULL */
+		else if (ssup->ssup_nulls_first)
+			compare = -1;		/* NULL "<" NOT_NULL */
+		else
+			compare = 1;		/* NULL ">" NOT_NULL */
+	}
+	else if (isNull2)
+	{
+		if (ssup->ssup_nulls_first)
+			compare = 1;		/* NOT_NULL ">" NULL */
+		else
+			compare = -1;		/* NOT_NULL "<" NULL */
+	}
+	else
+	{
+#if SIZEOF_DATUM == 8
+		compare = (int64) datum1 < (int64) datum2 ? -1 :
+			(int64) datum1 > (int64) datum2 ? 1 : 0;
+#else
+		compare = (int32) datum1 < (int32) datum2 ? -1 :
+			(int32) datum1 > (int32) datum2 ? 1 : 0;
+#endif
+		if (ssup->ssup_reverse)
+			INVERT_COMPARE_RESULT(compare);
+	}
+
+	return compare;
+}
+
+static inline int
+ApplyInt32SortComparator(Datum datum1, bool isNull1,
+						 Datum datum2, bool isNull2,
+						 SortSupport ssup)
+{
+	int			compare;
+
+	if (isNull1)
+	{
+		if (isNull2)
+			compare = 0;		/* NULL "=" NULL */
+		else if (ssup->ssup_nulls_first)
+			compare = -1;		/* NULL "<" NOT_NULL */
+		else
+			compare = 1;		/* NULL ">" NOT_NULL */
+	}
+	else if (isNull2)
+	{
+		if (ssup->ssup_nulls_first)
+			compare = 1;		/* NOT_NULL ">" NULL */
+		else
+			compare = -1;		/* NOT_NULL "<" NULL */
+	}
+	else
+	{
+		compare = (int32) datum1 < (int32) datum2 ? -1 :
+			(int32) datum1 > (int32) datum2 ? 1 : 0;
+		if (ssup->ssup_reverse)
+			INVERT_COMPARE_RESULT(compare);
+	}
+
+	return compare;
+}
+
 /*
  * Apply a sort comparator function and return a 3-way comparison using full,
  * authoritative comparator.  This takes care of handling reverse-sort and
@@ -267,6 +373,15 @@ ApplySortAbbrevFullComparator(Datum datum1, bool isNull1,
 	return compare;
 }
 
+/*
+ * Datum comparison functions that we have specialized sort routines for.
+ * Datatypes that install these as their comparator or abbrevated comparator
+ * are eligible for faster sorting.
+ */
+extern int ssup_datum_binary_cmp(Datum x, Datum y, SortSupport ssup);
+extern int ssup_datum_signed_cmp(Datum x, Datum y, SortSupport ssup);
+extern int ssup_datum_int32_cmp(Datum x, Datum y, SortSupport ssup);
+
 /* Other functions in utils/sort/sortsupport.c */
 extern void PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup);
 extern void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup);
-- 
2.30.2



^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-07-30 07:10  Peter Geoghegan <[email protected]>
  parent: John Naylor <[email protected]>
  2 siblings, 1 reply; 55+ messages in thread

From: Peter Geoghegan @ 2021-07-30 07:10 UTC (permalink / raw)
  To: John Naylor <[email protected]>; +Cc: Thomas Munro <[email protected]>; pgsql-hackers

On Fri, Jul 30, 2021 at 3:34 AM John Naylor
<[email protected]> wrote:
> I'm also attaching your tuplesort patch so others can see what exactly I'm comparing.

If you're going to specialize the sort routine for unsigned integer
style abbreviated keys then you might as well cover all relevant
opclasses/types. Almost all abbreviated key schemes produce
conditioned datums that are designed to use simple 3-way unsigned int
comparator. It's not just text. (Actually, the only abbreviated key
scheme that doesn't do it that way is numeric.)

Offhand I know that UUID, macaddr, and inet all have abbreviated keys
that can use your new ssup_datum_binary_cmp() comparator instead of
their own duplicated comparator (which will make them use the
corresponding specialized sort routine inside tuplesort.c).

-- 
Peter Geoghegan





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-07-30 11:47  Ranier Vilela <[email protected]>
  parent: John Naylor <[email protected]>
  2 siblings, 1 reply; 55+ messages in thread

From: Ranier Vilela @ 2021-07-30 11:47 UTC (permalink / raw)
  To: John Naylor <[email protected]>; +Cc: Thomas Munro <[email protected]>; pgsql-hackers

Em qui., 29 de jul. de 2021 às 21:34, John Naylor <
[email protected]> escreveu:

>
> On Sun, Jul 4, 2021 at 12:27 AM Thomas Munro <[email protected]>
> wrote:
> >
> > Since you are experimenting with tuplesort and likely thinking similar
> > thoughts, here's a patch I've been using to explore that area.  I've
> > seen it get, for example, ~1.18x speedup for simple index builds in
> > favourable winds (YMMV, early hacking results only).  Currently, it
> > kicks in when the leading column is of type int4, int8, timestamp,
> > timestamptz, date or text + friends (when abbreviatable, currently
> > that means "C" and ICU collations only), while increasing the
> > executable by only 8.5kB (Clang, amd64, -O2, no debug).
> >
> > These types are handled with just three specialisations.  Their custom
> > "fast" comparators all boiled down to comparisons of datum bits,
> > varying only in signedness and width, so I tried throwing them away
> > and using 3 new common routines.  Then I extended
> > tuplesort_sort_memtuples()'s pre-existing specialisation dispatch to
> > recognise qualifying users of those and select 3 corresponding sort
> > specialisations.
>
> I got around to getting a benchmark together to serve as a starting point.
> I based it off something I got from the archives, but don't remember where
> (I seem to remember Tomas Vondra wrote the original, but not sure). To
> start I just used types that were there already -- int, text, numeric. The
> latter two won't be helped by this patch, but I wanted to keep something
> like that so we can see what kind of noise variation there is. I'll
> probably cut text out in the future and just keep numeric for that purpose.
>
> I've attached both the script and a crude spreadsheet. I'll try to figure
> out something nicer for future tests, and maybe some graphs. The
> "comparison" sheet has the results side by side (min of five). There are 6
> distributions of values:
> - random
> - sorted
> - "almost sorted"
> - reversed
> - organ pipe (first half ascending, second half descending)
> - rotated (sorted but then put the smallest at the end)
> - random 0s/1s
>
> I included both "select a" and "select *" to make sure we have the recent
> datum sort optimization represented. The results look pretty good for ints
> -- about the same speed up master gets going from tuple sorts to datum
> sorts, and those got faster in turn also.
>
> Next I think I'll run microbenchmarks on int64s with the test harness you
> attached earlier, and experiment with the qsort parameters a bit.
>
> I'm also attaching your tuplesort patch so others can see what exactly I'm
> comparing.
>
The patch attached does not apply cleanly,
please can fix it?

error: patch failed: src/backend/utils/sort/tuplesort.c:4776
error: src/backend/utils/sort/tuplesort.c: patch does not apply

regards,
Ranier Vilela


^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-07-30 14:53  John Naylor <[email protected]>
  parent: Ranier Vilela <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: John Naylor @ 2021-07-30 14:53 UTC (permalink / raw)
  To: Ranier Vilela <[email protected]>; +Cc: Thomas Munro <[email protected]>; pgsql-hackers

On Fri, Jul 30, 2021 at 7:47 AM Ranier Vilela <[email protected]> wrote:

> The patch attached does not apply cleanly,
> please can fix it?

It applies just fine with "patch", for those wondering.

--
John Naylor
EDB: http://www.enterprisedb.com


^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-08-02 00:01  Thomas Munro <[email protected]>
  parent: Peter Geoghegan <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: Thomas Munro @ 2021-08-02 00:01 UTC (permalink / raw)
  To: Peter Geoghegan <[email protected]>; +Cc: John Naylor <[email protected]>; pgsql-hackers

On Fri, Jul 30, 2021 at 7:11 PM Peter Geoghegan <[email protected]> wrote:
> If you're going to specialize the sort routine for unsigned integer
> style abbreviated keys then you might as well cover all relevant
> opclasses/types. Almost all abbreviated key schemes produce
> conditioned datums that are designed to use simple 3-way unsigned int
> comparator. It's not just text. (Actually, the only abbreviated key
> scheme that doesn't do it that way is numeric.)

Right, that was the plan, but this was just experimenting with an
idea.  Looks like John's also seeing evidence that it may be worth
pursuing.

(Re numeric, I guess it must be possible to rearrange things so it can
use ssup_datum_signed_cmp; maybe something like NaN -> INT64_MAX, +inf
-> INT64_MAX - 1, -inf -> INT64_MIN, and then -1 - (whatever we're
doing now for normal values).)

> Offhand I know that UUID, macaddr, and inet all have abbreviated keys
> that can use your new ssup_datum_binary_cmp() comparator instead of
> their own duplicated comparator (which will make them use the
> corresponding specialized sort routine inside tuplesort.c).

Thanks, I've added these ones, and also gist_bbox_zorder_cmp_abbrev.

I also renamed that function to ssup_datum_unsigned_cmp(), because
"binary" was misleading.


Attachments:

  [text/x-patch] v3-0001-WIP-Accelerate-tuple-sorting-for-common-types.patch (23.7K, ../../CA+hUKGJgbsMZbqm5t0e8dUEKk6uTz4ztQ=A_wbD0QCu7aNWhYA@mail.gmail.com/2-v3-0001-WIP-Accelerate-tuple-sorting-for-common-types.patch)
  download | inline diff:
From a1ca8e9ca989de5f61f1b8f067cb9785034ce9cc Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Sat, 3 Jul 2021 19:02:10 +1200
Subject: [PATCH v3] WIP: Accelerate tuple sorting for common types.

Several data types have their own fast comparator functions that can be
replaced with common binary or signed comparator functions.  These
functions can then be recognized by tuplesort.c and used to dispatch to
corresponding specialized sort functions, to accelerate sorting.

XXX WIP, experiment grade only, many open questions... such as:

XXX Can we avoid repeating the null-handling logic?
XXX Is it worth specializing for reverse sort?
XXX Is it worth specializing for nulls first, nulls last, not null?
XXX Should we have separate cases for "result is authoritative", "need
XXX tiebreaker for atts 1..n (= abbrev case)", "need tie breaker for
XXX atts 2..n"?
XXX If we did all of the above, that'd give:
XXX  {nulls_first, nulls_last, not_null} x
XXX  {asc, desc} x
XXX  {tiebreak_none, tiebreak_first, tiebreak_rest} x
XXX  {unsigned, signed, int32}
XXX  = 54 specializations!  Seems a little over the top.
XXX You could use a smaller SortTuple for some cases.
XXX How should we handle numeric?
---
 src/backend/access/gist/gistproc.c     |  18 +--
 src/backend/access/nbtree/nbtcompare.c |  22 ++--
 src/backend/utils/adt/date.c           |  15 +--
 src/backend/utils/adt/mac.c            |  23 +---
 src/backend/utils/adt/network.c        |  17 +--
 src/backend/utils/adt/timestamp.c      |  11 ++
 src/backend/utils/adt/uuid.c           |  25 +---
 src/backend/utils/adt/varlena.c        |  34 +-----
 src/backend/utils/sort/tuplesort.c     | 160 ++++++++++++++++++++++++-
 src/include/utils/sortsupport.h        | 115 ++++++++++++++++++
 10 files changed, 308 insertions(+), 132 deletions(-)

diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c
index d474612b77..20135ea0ec 100644
--- a/src/backend/access/gist/gistproc.c
+++ b/src/backend/access/gist/gistproc.c
@@ -37,7 +37,6 @@ static uint64 part_bits32_by2(uint32 x);
 static uint32 ieee_float32_to_uint32(float f);
 static int	gist_bbox_zorder_cmp(Datum a, Datum b, SortSupport ssup);
 static Datum gist_bbox_zorder_abbrev_convert(Datum original, SortSupport ssup);
-static int	gist_bbox_zorder_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup);
 static bool gist_bbox_zorder_abbrev_abort(int memtupcount, SortSupport ssup);
 
 
@@ -1726,21 +1725,6 @@ gist_bbox_zorder_abbrev_convert(Datum original, SortSupport ssup)
 #endif
 }
 
-static int
-gist_bbox_zorder_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup)
-{
-	/*
-	 * Compare the pre-computed Z-orders as unsigned integers. Datum is a
-	 * typedef for 'uintptr_t', so no casting is required.
-	 */
-	if (z1 > z2)
-		return 1;
-	else if (z1 < z2)
-		return -1;
-	else
-		return 0;
-}
-
 /*
  * We never consider aborting the abbreviation.
  *
@@ -1764,7 +1748,7 @@ gist_point_sortsupport(PG_FUNCTION_ARGS)
 
 	if (ssup->abbreviate)
 	{
-		ssup->comparator = gist_bbox_zorder_cmp_abbrev;
+		ssup->comparator = ssup_datum_unsigned_cmp;
 		ssup->abbrev_converter = gist_bbox_zorder_abbrev_convert;
 		ssup->abbrev_abort = gist_bbox_zorder_abbrev_abort;
 		ssup->abbrev_full_comparator = gist_bbox_zorder_cmp;
diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c
index 7ac73cb8c2..204cf778fb 100644
--- a/src/backend/access/nbtree/nbtcompare.c
+++ b/src/backend/access/nbtree/nbtcompare.c
@@ -119,26 +119,12 @@ btint4cmp(PG_FUNCTION_ARGS)
 		PG_RETURN_INT32(A_LESS_THAN_B);
 }
 
-static int
-btint4fastcmp(Datum x, Datum y, SortSupport ssup)
-{
-	int32		a = DatumGetInt32(x);
-	int32		b = DatumGetInt32(y);
-
-	if (a > b)
-		return A_GREATER_THAN_B;
-	else if (a == b)
-		return 0;
-	else
-		return A_LESS_THAN_B;
-}
-
 Datum
 btint4sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
-	ssup->comparator = btint4fastcmp;
+	ssup->comparator = ssup_datum_int32_cmp;
 	PG_RETURN_VOID();
 }
 
@@ -156,6 +142,7 @@ btint8cmp(PG_FUNCTION_ARGS)
 		PG_RETURN_INT32(A_LESS_THAN_B);
 }
 
+#ifndef USE_FLOAT8_BYVAL
 static int
 btint8fastcmp(Datum x, Datum y, SortSupport ssup)
 {
@@ -169,13 +156,18 @@ btint8fastcmp(Datum x, Datum y, SortSupport ssup)
 	else
 		return A_LESS_THAN_B;
 }
+#endif
 
 Datum
 btint8sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
+#ifdef USE_FLOAT8_BYVAL
+	ssup->comparator = ssup_datum_signed_cmp;
+#else
 	ssup->comparator = btint8fastcmp;
+#endif
 	PG_RETURN_VOID();
 }
 
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 0bea16cb67..350c662d50 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -438,25 +438,12 @@ date_cmp(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(0);
 }
 
-static int
-date_fastcmp(Datum x, Datum y, SortSupport ssup)
-{
-	DateADT		a = DatumGetDateADT(x);
-	DateADT		b = DatumGetDateADT(y);
-
-	if (a < b)
-		return -1;
-	else if (a > b)
-		return 1;
-	return 0;
-}
-
 Datum
 date_sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
-	ssup->comparator = date_fastcmp;
+	ssup->comparator = ssup_datum_int32_cmp;
 	PG_RETURN_VOID();
 }
 
diff --git a/src/backend/utils/adt/mac.c b/src/backend/utils/adt/mac.c
index 844d8814e6..9016c6bf94 100644
--- a/src/backend/utils/adt/mac.c
+++ b/src/backend/utils/adt/mac.c
@@ -44,7 +44,6 @@ typedef struct
 
 static int	macaddr_cmp_internal(macaddr *a1, macaddr *a2);
 static int	macaddr_fast_cmp(Datum x, Datum y, SortSupport ssup);
-static int	macaddr_cmp_abbrev(Datum x, Datum y, SortSupport ssup);
 static bool macaddr_abbrev_abort(int memtupcount, SortSupport ssup);
 static Datum macaddr_abbrev_convert(Datum original, SortSupport ssup);
 
@@ -381,7 +380,7 @@ macaddr_sortsupport(PG_FUNCTION_ARGS)
 
 		ssup->ssup_extra = uss;
 
-		ssup->comparator = macaddr_cmp_abbrev;
+		ssup->comparator = ssup_datum_unsigned_cmp;
 		ssup->abbrev_converter = macaddr_abbrev_convert;
 		ssup->abbrev_abort = macaddr_abbrev_abort;
 		ssup->abbrev_full_comparator = macaddr_fast_cmp;
@@ -405,22 +404,6 @@ macaddr_fast_cmp(Datum x, Datum y, SortSupport ssup)
 	return macaddr_cmp_internal(arg1, arg2);
 }
 
-/*
- * SortSupport abbreviated key comparison function. Compares two MAC addresses
- * quickly by treating them like integers, and without having to go to the
- * heap.
- */
-static int
-macaddr_cmp_abbrev(Datum x, Datum y, SortSupport ssup)
-{
-	if (x > y)
-		return 1;
-	else if (x == y)
-		return 0;
-	else
-		return -1;
-}
-
 /*
  * Callback for estimating effectiveness of abbreviated key optimization.
  *
@@ -537,8 +520,8 @@ macaddr_abbrev_convert(Datum original, SortSupport ssup)
 	/*
 	 * Byteswap on little-endian machines.
 	 *
-	 * This is needed so that macaddr_cmp_abbrev() (an unsigned integer 3-way
-	 * comparator) works correctly on all platforms. Without this, the
+	 * This is needed so that ssup_datum_unsigned_cmp() (an unsigned integer
+	 * 3-way comparator) works correctly on all platforms. Without this, the
 	 * comparator would have to call memcmp() with a pair of pointers to the
 	 * first byte of each abbreviated key, which is slower.
 	 */
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
index 0ab54316f8..ea1c7390d0 100644
--- a/src/backend/utils/adt/network.c
+++ b/src/backend/utils/adt/network.c
@@ -53,7 +53,6 @@ typedef struct
 
 static int32 network_cmp_internal(inet *a1, inet *a2);
 static int	network_fast_cmp(Datum x, Datum y, SortSupport ssup);
-static int	network_cmp_abbrev(Datum x, Datum y, SortSupport ssup);
 static bool network_abbrev_abort(int memtupcount, SortSupport ssup);
 static Datum network_abbrev_convert(Datum original, SortSupport ssup);
 static List *match_network_function(Node *leftop,
@@ -456,7 +455,7 @@ network_sortsupport(PG_FUNCTION_ARGS)
 
 		ssup->ssup_extra = uss;
 
-		ssup->comparator = network_cmp_abbrev;
+		ssup->comparator = ssup_datum_unsigned_cmp;
 		ssup->abbrev_converter = network_abbrev_convert;
 		ssup->abbrev_abort = network_abbrev_abort;
 		ssup->abbrev_full_comparator = network_fast_cmp;
@@ -479,20 +478,6 @@ network_fast_cmp(Datum x, Datum y, SortSupport ssup)
 	return network_cmp_internal(arg1, arg2);
 }
 
-/*
- * Abbreviated key comparison func
- */
-static int
-network_cmp_abbrev(Datum x, Datum y, SortSupport ssup)
-{
-	if (x > y)
-		return 1;
-	else if (x == y)
-		return 0;
-	else
-		return -1;
-}
-
 /*
  * Callback for estimating effectiveness of abbreviated key optimization.
  *
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 1c0bf0aa5c..410b39b44e 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -37,6 +37,7 @@
 #include "utils/datetime.h"
 #include "utils/float.h"
 #include "utils/numeric.h"
+#include "utils/sortsupport.h"
 
 /*
  * gcc's -ffast-math switch breaks routines that expect exact results from
@@ -2155,6 +2156,7 @@ timestamp_cmp(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
 }
 
+#ifndef USE_FLOAT8_BYVAL
 /* note: this is used for timestamptz also */
 static int
 timestamp_fastcmp(Datum x, Datum y, SortSupport ssup)
@@ -2164,13 +2166,22 @@ timestamp_fastcmp(Datum x, Datum y, SortSupport ssup)
 
 	return timestamp_cmp_internal(a, b);
 }
+#endif
 
 Datum
 timestamp_sortsupport(PG_FUNCTION_ARGS)
 {
 	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
 
+#ifdef USE_FLOAT8_BYVAL
+	/*
+	 * If this build has pass-by-value timestamps, then we can use a standard
+	 * comparator function.
+	 */
+	ssup->comparator = ssup_datum_signed_cmp;
+#else
 	ssup->comparator = timestamp_fastcmp;
+#endif
 	PG_RETURN_VOID();
 }
 
diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c
index b02c9fcf98..eaf0a6bbdf 100644
--- a/src/backend/utils/adt/uuid.c
+++ b/src/backend/utils/adt/uuid.c
@@ -34,7 +34,6 @@ typedef struct
 static void string_to_uuid(const char *source, pg_uuid_t *uuid);
 static int	uuid_internal_cmp(const pg_uuid_t *arg1, const pg_uuid_t *arg2);
 static int	uuid_fast_cmp(Datum x, Datum y, SortSupport ssup);
-static int	uuid_cmp_abbrev(Datum x, Datum y, SortSupport ssup);
 static bool uuid_abbrev_abort(int memtupcount, SortSupport ssup);
 static Datum uuid_abbrev_convert(Datum original, SortSupport ssup);
 
@@ -255,7 +254,7 @@ uuid_sortsupport(PG_FUNCTION_ARGS)
 
 		ssup->ssup_extra = uss;
 
-		ssup->comparator = uuid_cmp_abbrev;
+		ssup->comparator = ssup_datum_unsigned_cmp;
 		ssup->abbrev_converter = uuid_abbrev_convert;
 		ssup->abbrev_abort = uuid_abbrev_abort;
 		ssup->abbrev_full_comparator = uuid_fast_cmp;
@@ -278,20 +277,6 @@ uuid_fast_cmp(Datum x, Datum y, SortSupport ssup)
 	return uuid_internal_cmp(arg1, arg2);
 }
 
-/*
- * Abbreviated key comparison func
- */
-static int
-uuid_cmp_abbrev(Datum x, Datum y, SortSupport ssup)
-{
-	if (x > y)
-		return 1;
-	else if (x == y)
-		return 0;
-	else
-		return -1;
-}
-
 /*
  * Callback for estimating effectiveness of abbreviated key optimization.
  *
@@ -390,10 +375,10 @@ uuid_abbrev_convert(Datum original, SortSupport ssup)
 	/*
 	 * Byteswap on little-endian machines.
 	 *
-	 * This is needed so that uuid_cmp_abbrev() (an unsigned integer 3-way
-	 * comparator) works correctly on all platforms.  If we didn't do this,
-	 * the comparator would have to call memcmp() with a pair of pointers to
-	 * the first byte of each abbreviated key, which is slower.
+	 * This is needed so that ssup_datum_unsigned_cmp() (an unsigned integer
+	 * 3-way comparator) works correctly on all platforms.  If we didn't do
+	 * this, the comparator would have to call memcmp() with a pair of pointers
+	 * to the first byte of each abbreviated key, which is slower.
 	 */
 	res = DatumBigEndianToNative(res);
 
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index d2a11b1b5d..0a91de6992 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -127,7 +127,6 @@ static int	namefastcmp_c(Datum x, Datum y, SortSupport ssup);
 static int	varlenafastcmp_locale(Datum x, Datum y, SortSupport ssup);
 static int	namefastcmp_locale(Datum x, Datum y, SortSupport ssup);
 static int	varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup);
-static int	varstrcmp_abbrev(Datum x, Datum y, SortSupport ssup);
 static Datum varstr_abbrev_convert(Datum original, SortSupport ssup);
 static bool varstr_abbrev_abort(int memtupcount, SortSupport ssup);
 static int32 text_length(Datum str);
@@ -2175,7 +2174,7 @@ varstr_sortsupport(SortSupport ssup, Oid typid, Oid collid)
 			initHyperLogLog(&sss->abbr_card, 10);
 			initHyperLogLog(&sss->full_card, 10);
 			ssup->abbrev_full_comparator = ssup->comparator;
-			ssup->comparator = varstrcmp_abbrev;
+			ssup->comparator = ssup_datum_unsigned_cmp;
 			ssup->abbrev_converter = varstr_abbrev_convert;
 			ssup->abbrev_abort = varstr_abbrev_abort;
 		}
@@ -2461,27 +2460,6 @@ varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup)
 	return result;
 }
 
-/*
- * Abbreviated key comparison func
- */
-static int
-varstrcmp_abbrev(Datum x, Datum y, SortSupport ssup)
-{
-	/*
-	 * When 0 is returned, the core system will call varstrfastcmp_c()
-	 * (bpcharfastcmp_c() in BpChar case) or varlenafastcmp_locale().  Even a
-	 * strcmp() on two non-truncated strxfrm() blobs cannot indicate *equality*
-	 * authoritatively, for the same reason that there is a strcoll()
-	 * tie-breaker call to strcmp() in varstr_cmp().
-	 */
-	if (x > y)
-		return 1;
-	else if (x == y)
-		return 0;
-	else
-		return -1;
-}
-
 /*
  * Conversion routine for sortsupport.  Converts original to abbreviated key
  * representation.  Our encoding strategy is simple -- pack the first 8 bytes
@@ -2520,7 +2498,7 @@ varstr_abbrev_convert(Datum original, SortSupport ssup)
 	 * strings may contain NUL bytes.  Besides, this should be faster, too.
 	 *
 	 * More generally, it's okay that bytea callers can have NUL bytes in
-	 * strings because varstrcmp_abbrev() need not make a distinction between
+	 * strings because abbreviated cmp need not make a distinction between
 	 * terminating NUL bytes, and NUL bytes representing actual NULs in the
 	 * authoritative representation.  Hopefully a comparison at or past one
 	 * abbreviated key's terminating NUL byte will resolve the comparison
@@ -2710,10 +2688,10 @@ done:
 	/*
 	 * Byteswap on little-endian machines.
 	 *
-	 * This is needed so that varstrcmp_abbrev() (an unsigned integer 3-way
-	 * comparator) works correctly on all platforms.  If we didn't do this,
-	 * the comparator would have to call memcmp() with a pair of pointers to
-	 * the first byte of each abbreviated key, which is slower.
+	 * This is needed so that ssup_datum_unsigned_cmp() (an unsigned integer
+	 * 3-way comparator) works correctly on all platforms.  If we didn't do
+	 * this, the comparator would have to call memcmp() with a pair of pointers
+	 * to the first byte of each abbreviated key, which is slower.
 	 */
 	res = DatumBigEndianToNative(res);
 
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index b17347b214..32cd072c82 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -669,14 +669,101 @@ static void free_sort_tuple(Tuplesortstate *state, SortTuple *stup);
 static void tuplesort_free(Tuplesortstate *state);
 static void tuplesort_updatemax(Tuplesortstate *state);
 
+/*
+ * Specialized comparators that we can inline into specialized sorts.  The goal
+ * is to try to sort two tuples without having to follow the pointers to the
+ * comparator or the tuple.
+ *
+ * XXX: For now, these fall back to comparator functions that will compare the
+ * leading datum a second time.
+ *
+ * XXX: For now, there is no specialization for cases where datum1 is
+ * authoritative and we don't even need to fall back to a callback at all (that
+ * would be true for types like int4/int8/timestamp/date, but not true for
+ * abbreviations of text or multi-key sorts.  There could be!  Is it worth it?
+ */
+
+/* Used if first key's comparator is ssup_datum_unsigned_compare */
+static pg_attribute_always_inline int
+qsort_tuple_unsigned_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
+{
+	int			compare;
+
+	compare = ApplyUnsignedSortComparator(a->datum1, a->isnull1,
+										  b->datum1, b->isnull1,
+										  &state->sortKeys[0]);
+	if (compare != 0)
+		return compare;
+
+	return state->comparetup(a, b, state);
+}
+
+/* Used if first key's comparator is ssup_datum_signed_compare */
+static pg_attribute_always_inline int
+qsort_tuple_signed_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
+{
+	int			compare;
+
+	compare = ApplySignedSortComparator(a->datum1, a->isnull1,
+										b->datum1, b->isnull1,
+										&state->sortKeys[0]);
+	if (compare != 0)
+		return compare;
+
+	return state->comparetup(a, b, state);
+}
+
+/* Used if first key's comparator is ssup_datum_int32_compare */
+static pg_attribute_always_inline int
+qsort_tuple_int32_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
+{
+	int			compare;
+
+	compare = ApplyInt32SortComparator(a->datum1, a->isnull1,
+										b->datum1, b->isnull1,
+										&state->sortKeys[0]);
+	if (compare != 0)
+		return compare;
+
+	return state->comparetup(a, b, state);
+}
+
 /*
  * Special versions of qsort just for SortTuple objects.  qsort_tuple() sorts
  * any variant of SortTuples, using the appropriate comparetup function.
  * qsort_ssup() is specialized for the case where the comparetup function
  * reduces to ApplySortComparator(), that is single-key MinimalTuple sorts
- * and Datum sorts.
+ * and Datum sorts.  qsort_tuple_{unsigned,signed,int32} are specialized for
+ * common comparison functions on pass-by-value leading datums.
  */
 
+#define ST_SORT qsort_tuple_unsigned
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE(a, b, state) qsort_tuple_unsigned_compare(a, b, state)
+#define ST_COMPARE_ARG_TYPE Tuplesortstate
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+#define ST_SORT qsort_tuple_signed
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE(a, b, state) qsort_tuple_signed_compare(a, b, state)
+#define ST_COMPARE_ARG_TYPE Tuplesortstate
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+#define ST_SORT qsort_tuple_int32
+#define ST_ELEMENT_TYPE SortTuple
+#define ST_COMPARE(a, b, state) qsort_tuple_int32_compare(a, b, state)
+#define ST_COMPARE_ARG_TYPE Tuplesortstate
+#define ST_CHECK_FOR_INTERRUPTS
+#define ST_SCOPE static
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
 #define ST_SORT qsort_tuple
 #define ST_ELEMENT_TYPE SortTuple
 #define ST_COMPARE_RUNTIME_POINTER
@@ -3558,15 +3645,40 @@ tuplesort_sort_memtuples(Tuplesortstate *state)
 
 	if (state->memtupcount > 1)
 	{
+		/* Do we have a specialization for the leading column's comparator? */
+		if (state->sortKeys &&
+			state->sortKeys[0].comparator == ssup_datum_unsigned_cmp)
+		{
+			elog(DEBUG1, "qsort_tuple_unsigned");
+			qsort_tuple_unsigned(state->memtuples, state->memtupcount, state);
+		}
+		else if (state->sortKeys &&
+				 state->sortKeys[0].comparator == ssup_datum_signed_cmp)
+		{
+			elog(DEBUG1, "qsort_tuple_signed");
+			qsort_tuple_signed(state->memtuples, state->memtupcount, state);
+		}
+		else if (state->sortKeys &&
+				 state->sortKeys[0].comparator == ssup_datum_int32_cmp)
+		{
+			elog(DEBUG1, "qsort_tuple_int32");
+			qsort_tuple_int32(state->memtuples, state->memtupcount, state);
+		}
 		/* Can we use the single-key sort function? */
-		if (state->onlyKey != NULL)
+		else if (state->onlyKey != NULL)
+		{
+			elog(DEBUG1, "qsort_ssup");
 			qsort_ssup(state->memtuples, state->memtupcount,
 					   state->onlyKey);
+		}
 		else
+		{
+			elog(DEBUG1, "qsort_tuple");
 			qsort_tuple(state->memtuples,
 						state->memtupcount,
 						state->comparetup,
 						state);
+		}
 	}
 }
 
@@ -4780,3 +4892,47 @@ free_sort_tuple(Tuplesortstate *state, SortTuple *stup)
 		stup->tuple = NULL;
 	}
 }
+
+int
+ssup_datum_unsigned_cmp(Datum x, Datum y, SortSupport ssup)
+{
+	if (x < y)
+		return -1;
+	else if (x > y)
+		return 1;
+	else
+		return 0;
+}
+
+int
+ssup_datum_signed_cmp(Datum x, Datum y, SortSupport ssup)
+{
+#if SIZEOF_DATUM == 8
+	int64		xx = (int64) x;
+	int64		yy = (int64) y;
+#else
+	int32		xx = (int32) x;
+	int32		yy = (int32) y;
+#endif
+
+	if (xx < yy)
+		return -1;
+	else if (xx > yy)
+		return 1;
+	else
+		return 0;
+}
+
+int
+ssup_datum_int32_cmp(Datum x, Datum y, SortSupport ssup)
+{
+	int32		xx = (int32) x;
+	int32		yy = (int32) y;
+
+	if (xx < yy)
+		return -1;
+	else if (xx > yy)
+		return 1;
+	else
+		return 0;
+}
diff --git a/src/include/utils/sortsupport.h b/src/include/utils/sortsupport.h
index 2f12a8b8eb..99eac089b3 100644
--- a/src/include/utils/sortsupport.h
+++ b/src/include/utils/sortsupport.h
@@ -229,6 +229,112 @@ ApplySortComparator(Datum datum1, bool isNull1,
 	return compare;
 }
 
+static inline int
+ApplyUnsignedSortComparator(Datum datum1, bool isNull1,
+							Datum datum2, bool isNull2,
+							SortSupport ssup)
+{
+	int			compare;
+
+	if (isNull1)
+	{
+		if (isNull2)
+			compare = 0;		/* NULL "=" NULL */
+		else if (ssup->ssup_nulls_first)
+			compare = -1;		/* NULL "<" NOT_NULL */
+		else
+			compare = 1;		/* NULL ">" NOT_NULL */
+	}
+	else if (isNull2)
+	{
+		if (ssup->ssup_nulls_first)
+			compare = 1;		/* NOT_NULL ">" NULL */
+		else
+			compare = -1;		/* NOT_NULL "<" NULL */
+	}
+	else
+	{
+		compare = datum1 < datum2 ? -1 : datum1 > datum2 ? 1 : 0;
+		if (ssup->ssup_reverse)
+			INVERT_COMPARE_RESULT(compare);
+	}
+
+	return compare;
+}
+
+static inline int
+ApplySignedSortComparator(Datum datum1, bool isNull1,
+						  Datum datum2, bool isNull2,
+						  SortSupport ssup)
+{
+	int			compare;
+
+	if (isNull1)
+	{
+		if (isNull2)
+			compare = 0;		/* NULL "=" NULL */
+		else if (ssup->ssup_nulls_first)
+			compare = -1;		/* NULL "<" NOT_NULL */
+		else
+			compare = 1;		/* NULL ">" NOT_NULL */
+	}
+	else if (isNull2)
+	{
+		if (ssup->ssup_nulls_first)
+			compare = 1;		/* NOT_NULL ">" NULL */
+		else
+			compare = -1;		/* NOT_NULL "<" NULL */
+	}
+	else
+	{
+#if SIZEOF_DATUM == 8
+		compare = (int64) datum1 < (int64) datum2 ? -1 :
+			(int64) datum1 > (int64) datum2 ? 1 : 0;
+#else
+		compare = (int32) datum1 < (int32) datum2 ? -1 :
+			(int32) datum1 > (int32) datum2 ? 1 : 0;
+#endif
+		if (ssup->ssup_reverse)
+			INVERT_COMPARE_RESULT(compare);
+	}
+
+	return compare;
+}
+
+static inline int
+ApplyInt32SortComparator(Datum datum1, bool isNull1,
+						 Datum datum2, bool isNull2,
+						 SortSupport ssup)
+{
+	int			compare;
+
+	if (isNull1)
+	{
+		if (isNull2)
+			compare = 0;		/* NULL "=" NULL */
+		else if (ssup->ssup_nulls_first)
+			compare = -1;		/* NULL "<" NOT_NULL */
+		else
+			compare = 1;		/* NULL ">" NOT_NULL */
+	}
+	else if (isNull2)
+	{
+		if (ssup->ssup_nulls_first)
+			compare = 1;		/* NOT_NULL ">" NULL */
+		else
+			compare = -1;		/* NOT_NULL "<" NULL */
+	}
+	else
+	{
+		compare = (int32) datum1 < (int32) datum2 ? -1 :
+			(int32) datum1 > (int32) datum2 ? 1 : 0;
+		if (ssup->ssup_reverse)
+			INVERT_COMPARE_RESULT(compare);
+	}
+
+	return compare;
+}
+
 /*
  * Apply a sort comparator function and return a 3-way comparison using full,
  * authoritative comparator.  This takes care of handling reverse-sort and
@@ -267,6 +373,15 @@ ApplySortAbbrevFullComparator(Datum datum1, bool isNull1,
 	return compare;
 }
 
+/*
+ * Datum comparison functions that we have specialized sort routines for.
+ * Datatypes that install these as their comparator or abbrevated comparator
+ * are eligible for faster sorting.
+ */
+extern int ssup_datum_unsigned_cmp(Datum x, Datum y, SortSupport ssup);
+extern int ssup_datum_signed_cmp(Datum x, Datum y, SortSupport ssup);
+extern int ssup_datum_int32_cmp(Datum x, Datum y, SortSupport ssup);
+
 /* Other functions in utils/sort/sortsupport.c */
 extern void PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup);
 extern void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup);
-- 
2.30.2



^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-08-02 00:40  Thomas Munro <[email protected]>
  parent: John Naylor <[email protected]>
  2 siblings, 2 replies; 55+ messages in thread

From: Thomas Munro @ 2021-08-02 00:40 UTC (permalink / raw)
  To: John Naylor <[email protected]>; +Cc: pgsql-hackers

On Fri, Jul 30, 2021 at 12:34 PM John Naylor
<[email protected]> wrote:
> I got around to getting a benchmark together to serve as a starting point. I based it off something I got from the archives, but don't remember where (I seem to remember Tomas Vondra wrote the original, but not sure). To start I just used types that were there already -- int, text, numeric. The latter two won't be helped by this patch, but I wanted to keep something like that so we can see what kind of noise variation there is. I'll probably cut text out in the future and just keep numeric for that purpose.

Thanks, that's very useful.

> I've attached both the script and a crude spreadsheet. I'll try to figure out something nicer for future tests, and maybe some graphs. The "comparison" sheet has the results side by side (min of five). There are 6 distributions of values:
> - random
> - sorted
> - "almost sorted"
> - reversed
> - organ pipe (first half ascending, second half descending)
> - rotated (sorted but then put the smallest at the end)
> - random 0s/1s
>
> I included both "select a" and "select *" to make sure we have the recent datum sort optimization represented. The results look pretty good for ints -- about the same speed up master gets going from tuple sorts to datum sorts, and those got faster in turn also.

Great!  I saw similar sorts of numbers.  It's really just a few
crumbs, nothing compared to the gains David just found over in the
thread "Use generation context to speed up tuplesorts", but on the
bright side, these crumbs will be magnified by that work.

> Next I think I'll run microbenchmarks on int64s with the test harness you attached earlier, and experiment with the qsort parameters a bit.

Cool.  I haven't had much luck experimenting with that yet, though I
consider the promotion from magic numbers to names as an improvement
in any case.

> I'm also attaching your tuplesort patch so others can see what exactly I'm comparing.

We've been bouncing around quite a few different ideas and patches in
this thread; soon I'll try to bring it back to one patch set with the
ideas that are looking good so far in a more tidied up form.  For the
tupesort.c part, I added some TODO notes in
v3-0001-WIP-Accelerate-tuple-sorting-for-common-types.patch's commit
message (see reply to Peter).





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-08-02 00:42  Thomas Munro <[email protected]>
  parent: Thomas Munro <[email protected]>
  1 sibling, 0 replies; 55+ messages in thread

From: Thomas Munro @ 2021-08-02 00:42 UTC (permalink / raw)
  To: John Naylor <[email protected]>; +Cc: pgsql-hackers

On Mon, Aug 2, 2021 at 12:40 PM Thomas Munro <[email protected]> wrote:
> Great!  I saw similar sorts of numbers.  It's really just a few
> crumbs, nothing compared to the gains David just found over in the
> thread "Use generation context to speed up tuplesorts", but on the
> bright side, these crumbs will be magnified by that work.

(Hmm, that also makes me wonder about using a smaller SortTuple when
possible...)





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2021-08-05 23:18  Peter Geoghegan <[email protected]>
  parent: Thomas Munro <[email protected]>
  1 sibling, 0 replies; 55+ messages in thread

From: Peter Geoghegan @ 2021-08-05 23:18 UTC (permalink / raw)
  To: Thomas Munro <[email protected]>; +Cc: John Naylor <[email protected]>; pgsql-hackers

On Sun, Aug 1, 2021 at 5:41 PM Thomas Munro <[email protected]> wrote:
> On Fri, Jul 30, 2021 at 12:34 PM John Naylor
> <[email protected]> wrote:
> > I got around to getting a benchmark together to serve as a starting point. I based it off something I got from the archives, but don't remember where (I seem to remember Tomas Vondra wrote the original, but not sure). To start I just used types that were there already -- int, text, numeric. The latter two won't be helped by this patch, but I wanted to keep something like that so we can see what kind of noise variation there is. I'll probably cut text out in the future and just keep numeric for that purpose.
>
> Thanks, that's very useful.

If somebody wants to get a sense of what the size hit is from all of
these specializations, I can recommend the diff feature of bloaty:

https://github.com/google/bloaty/blob/master/doc/using.md#size-diffs

Obviously you'd approach this by building postgres without the patch,
and diffing that baseline to postgres with the patch. And possibly
variations of the patch, with less or more sort specializations.

-- 
Peter Geoghegan





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2022-03-31 10:09  John Naylor <[email protected]>
  parent: Thomas Munro <[email protected]>
  1 sibling, 1 reply; 55+ messages in thread

From: John Naylor @ 2022-03-31 10:09 UTC (permalink / raw)
  To: Peter Geoghegan <[email protected]>; +Cc: Thomas Munro <[email protected]>; pgsql-hackers

In a couple days I'm going to commit the v3 patch "accelerate tuple
sorting for common types" as-is after giving it one more look, barring
objections.

I started towards incorporating the change in insertion sort threshold
(part of 0010), but that caused regression test failures, so that will
have to wait for a bit of analysis and retesting. (My earlier tests
were done in a separate module.)

The rest in this series that I looked at closely were either
refactoring or could use some minor tweaks so likely v16 material.

-- 
John Naylor
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2022-03-31 21:42  Thomas Munro <[email protected]>
  parent: John Naylor <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: Thomas Munro @ 2022-03-31 21:42 UTC (permalink / raw)
  To: John Naylor <[email protected]>; +Cc: Peter Geoghegan <[email protected]>; pgsql-hackers

On Thu, Mar 31, 2022 at 11:09 PM John Naylor
<[email protected]> wrote:
> In a couple days I'm going to commit the v3 patch "accelerate tuple
> sorting for common types" as-is after giving it one more look, barring
> objections.

Hi John,

Thanks so much for all the work you've done here!  I feel bad that I
lobbed so many experimental patches in here and then ran away due to
lack of cycles.  That particular patch (the one cfbot has been chewing
on all this time) does indeed seem committable, despite the
deficiencies/opportunities listed in comments.  It's nice to reduce
code duplication, it gives the right answers, and it goes faster.

> I started towards incorporating the change in insertion sort threshold
> (part of 0010), but that caused regression test failures, so that will
> have to wait for a bit of analysis and retesting. (My earlier tests
> were done in a separate module.)
>
> The rest in this series that I looked at closely were either
> refactoring or could use some minor tweaks so likely v16 material.

Looking forward to it.





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2022-05-19 20:12  Justin Pryzby <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Justin Pryzby @ 2022-05-19 20:12 UTC (permalink / raw)
  To: John Naylor <[email protected]>; +Cc: David Rowley <[email protected]>; Thomas Munro <[email protected]>; Andres Freund <[email protected]>; Peter Geoghegan <[email protected]>; pgsql-hackers; Robert Haas <[email protected]>

On Fri, Apr 22, 2022 at 11:37:29AM +0700, John Naylor wrote:
> On Fri, Apr 22, 2022 at 11:13 AM David Rowley <[email protected]> wrote:
> >
> > On Thu, 21 Apr 2022 at 19:09, John Naylor <[email protected]> wrote:
> > > I intend to commit David's v2 fix next week, unless there are
> > > objections, or unless he beats me to it.
> >
> > I wasn't sure if you wanted to handle it or not, but I don't mind
> > doing it, so I just pushed it after a small adjustment to a comment.
> 
> Thank you!

Should these debug lines be removed ?

elog(DEBUG1, "qsort_tuple");

Perhaps if I ask for debug output, I shouldn't be surprised if it changes
between major releases - but I still found this surprising.

I'm sure it's useful during development and maybe during beta.  It could even
make sense if it were shown during regression tests (preferably at DEBUG2).
But right now it's not.  is that

ts=# \dt
DEBUG:  qsort_tuple
List of relations

-- 
Justin





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2022-05-19 22:24  Peter Geoghegan <[email protected]>
  parent: Justin Pryzby <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Peter Geoghegan @ 2022-05-19 22:24 UTC (permalink / raw)
  To: Justin Pryzby <[email protected]>; +Cc: John Naylor <[email protected]>; David Rowley <[email protected]>; Thomas Munro <[email protected]>; Andres Freund <[email protected]>; pgsql-hackers; Robert Haas <[email protected]>

On Thu, May 19, 2022 at 1:12 PM Justin Pryzby <[email protected]> wrote:
> Should these debug lines be removed ?
>
> elog(DEBUG1, "qsort_tuple");

I agree -- DEBUG1 seems too chatty for something like this. DEBUG2
would be more appropriate IMV. Though I don't feel very strongly about
it.

--
Peter Geoghegan





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2022-05-19 22:43  Tom Lane <[email protected]>
  parent: Peter Geoghegan <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Tom Lane @ 2022-05-19 22:43 UTC (permalink / raw)
  To: Peter Geoghegan <[email protected]>; +Cc: Justin Pryzby <[email protected]>; John Naylor <[email protected]>; David Rowley <[email protected]>; Thomas Munro <[email protected]>; Andres Freund <[email protected]>; pgsql-hackers; Robert Haas <[email protected]>

Peter Geoghegan <[email protected]> writes:
> On Thu, May 19, 2022 at 1:12 PM Justin Pryzby <[email protected]> wrote:
>> Should these debug lines be removed ?
>> 
>> elog(DEBUG1, "qsort_tuple");

> I agree -- DEBUG1 seems too chatty for something like this. DEBUG2
> would be more appropriate IMV. Though I don't feel very strongly about
> it.

Given the lack of context identification, I'd put the usefulness of
these in production at close to zero.  +1 for removing them
altogether, or failing that, downgrade to DEBUG5 or so.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2022-05-20 06:40  John Naylor <[email protected]>
  parent: Tom Lane <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: John Naylor @ 2022-05-20 06:40 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Peter Geoghegan <[email protected]>; Justin Pryzby <[email protected]>; David Rowley <[email protected]>; Thomas Munro <[email protected]>; Andres Freund <[email protected]>; pgsql-hackers; Robert Haas <[email protected]>

On Fri, May 20, 2022 at 5:43 AM Tom Lane <[email protected]> wrote:
>
> Peter Geoghegan <[email protected]> writes:
> > On Thu, May 19, 2022 at 1:12 PM Justin Pryzby <[email protected]> wrote:
> >> Should these debug lines be removed ?
> >>
> >> elog(DEBUG1, "qsort_tuple");
>
> > I agree -- DEBUG1 seems too chatty for something like this. DEBUG2
> > would be more appropriate IMV. Though I don't feel very strongly about
> > it.
>
> Given the lack of context identification, I'd put the usefulness of
> these in production at close to zero.  +1 for removing them
> altogether, or failing that, downgrade to DEBUG5 or so.

I agree this is only useful in development. Removal sounds fine to me,
so I'll do that soon.

-- 
John Naylor
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: A qsort template
@ 2022-05-23 06:17  John Naylor <[email protected]>
  parent: John Naylor <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: John Naylor @ 2022-05-23 06:17 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Peter Geoghegan <[email protected]>; Justin Pryzby <[email protected]>; David Rowley <[email protected]>; Thomas Munro <[email protected]>; Andres Freund <[email protected]>; pgsql-hackers; Robert Haas <[email protected]>

I wrote:
> I agree this is only useful in development. Removal sounds fine to me,
> so I'll do that soon.

This is done.

-- 
John Naylor
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: partitioning and identity column
@ 2023-12-19 10:47  Ashutosh Bapat <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Ashutosh Bapat @ 2023-12-19 10:47 UTC (permalink / raw)
  To: Peter Eisentraut <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>

On Mon, Nov 13, 2023 at 3:51 PM Peter Eisentraut <[email protected]> wrote:
>
> On 27.10.23 13:32, Ashutosh Bapat wrote:
> > I think we should fix these anomalies as follows
> > 1. Allow identity columns to be added to the partitioned table
> > irrespective of whether they have partitions of not.
> > 2. Propagate identity property to partitions.
> > 3. Use the same underlying sequence for getting default value of an
> > identity column when INSERTing directly in a partition.
> > 4. Disallow attaching a partition with identity column.
> >
> > 1 will fix inconsistencies in Behaviour 3 and 4. 2 and 3 will fix
> > anomalies in Behaviour 1. 4 will fix Behaviour 2.
>
> This makes sense to me.

PFA WIP patches implementing/fixing identity support for partitioned
tables as outlined above.

A partitioned table is a single relation and thus an identity column
of a partitioned table should use the same identity space across all
the partitions. This means that the sequence underlying the identity
column will be shared by all the partitions of a partitioned table and
the column will have the same identity properties across all the
partitions. Thus
1. When a new partition is added or a table is attached as a
partition, it inherits the identity column along with the underlying
sequence from the partitioned table. It can not have an identity
column of its own.
2. Since a partition never had its own identity column, when detaching
a partition, it will loose identity property of any column that had
it. If it were to retain the identity property it can not use
underlying sequence. That's not possible anyway.

This is different from the way we treat identity in inheritance.
Children in inheritance hierarchy are independent enough to have
separate identity columns and sequences of their own. So the above
discussion applies only to partitioned table. The patches too deal
with only partitioned tables.

At this point I am looking for opinions on the above rules and whether
the implementation is on the right track.

The work consists of many small code changes. In order to know which
code change is associated with which SQL each patch has test changes
and associated code change. Each patch has a commit message explaining
the changes in detail (and some times repeating the above rules again,
sorry for the repetition). These patches will be merged into a single
patch or a couple patches at most. Here's what each patch does

0001 - change to get_partition_ancestors() prologue. Can be reviewed
and committed independent of other patches.

0002 - A new partition inherits identity column and uses the
underlying sequence for direct INSERTs

0004 - An attached partition inherits identity property and uses the
underlying sequence for direct INSERTs. When inheriting the identity
property it should also inherit the NOT NULL constraint, but that's a
TODO in this patch. We expect matching NOT NULL constraints to be
present in the partition being attached. I am not sure whether we want
to add NOT NULL constraints automatically for an identity column. We
require a NOT NULL constraint to be present when adding identity
property to a column. The behavior in the patch seems to be consistent
with this.

0006 - supports ADD COLUMN ... GENERATED AS IDENTITY on a partitioned
table. identity property is propagated down the partition hierarchy.

0008 - A TODO: that I need verify/address before finalizing these
patches. Any hints are welcome.

0009 - supports ALTER COLUMN ... ADD GENERATED AS IDENTITY on a
partitioned table. Propagates the identity property down the partition
hierarchy. It requires adding NOT NULL constraint before adding
identity property just like regular table.

0011 - dropping identity property of a column of a partitioned table
drops it from the corresponding columns of all of its partitions. But
the NOT NULL constraint is retained just like in case of regular
table.

0013 - detaching a partition, drops identity property from all the
columns of partition.

patches 0003, 0005, 0007, 0010, 0012, 0014 have detailed white box
tests testing the catalog changes for each SQL. But they are not meant
to be part of the final patch-set.


--
Best Wishes,
Ashutosh Bapat


Attachments:

  [application/x-patch] 0001-Fix-prologue-of-get_partition_ancestors-20231219.patch (1.4K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/2-0001-Fix-prologue-of-get_partition_ancestors-20231219.patch)
  download | inline diff:
From a0fe61af663de60bcba8a1b527508e93f12d8d3a Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Wed, 6 Dec 2023 10:51:56 +0530
Subject: [PATCH 01/14] Fix prologue of get_partition_ancestors()

The callers of this function assume that the first Oid in the list returned by
this function corresponds to the immediate parent and the last on corresponds
to the topmost parent. Make that explicit in the function prologue.

Ashutosh Bapat
---
 src/backend/catalog/partition.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index f8780ce57d..b6e837d70b 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -123,7 +123,9 @@ get_partition_parent_worker(Relation inhRel, Oid relid, bool *detach_pending)
  * get_partition_ancestors
  *		Obtain ancestors of given relation
  *
- * Returns a list of ancestors of the given relation.
+ * Returns a list of ancestors of the given relation. The first ancestor in the
+ * list is the immediate parent and the last one is the topmost parent in the
+ * partition hierarchy. When there is a single ancestor both of them are same.
  *
  * Note: Because this function assumes that the relation whose OID is passed
  * as an argument and each ancestor will have precisely one parent, it should
-- 
2.25.1



  [application/x-patch] 0005-Extra-ATTACH-PARTITION-tests-for-identity-c-20231219.patch (21.4K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/3-0005-Extra-ATTACH-PARTITION-tests-for-identity-c-20231219.patch)
  download | inline diff:
From 8f1eb264173d3dc9f76ca59caf4d8fe1671d098e Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Wed, 6 Dec 2023 15:54:01 +0530
Subject: [PATCH 05/14] Extra ATTACH PARTITION tests for identity column

Not for final commit.
---
 .../regress/expected/identity_part_extra.out  | 148 ++++++++++++++----
 src/test/regress/sql/identity_part_extra.sql  |  82 +++++++---
 2 files changed, 179 insertions(+), 51 deletions(-)

diff --git a/src/test/regress/expected/identity_part_extra.out b/src/test/regress/expected/identity_part_extra.out
index 0c8af1c658..81dfca2acc 100644
--- a/src/test/regress/expected/identity_part_extra.out
+++ b/src/test/regress/expected/identity_part_extra.out
@@ -51,8 +51,10 @@ CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 bigint generated always
 CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
 SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
     FROM pg_attribute
-    WHERE attrelid IN ('itest_parted'::regclass, 'itest_p1'::regclass)
-        AND attnum > 0;
+    WHERE attrelid IN (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+        AND attnum > 0
+    ORDER BY attrelid, attnum;
    attrelid   | attname | attidentity | atthasdef | attgenerated | attnotnull 
 --------------+---------+-------------+-----------+--------------+------------
  itest_parted | f1      |             | f         |              | t
@@ -65,7 +67,9 @@ SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnot
 
 SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
     FROM pg_constraint
-    WHERE conrelid in ('itest_parted'::regclass, 'itest_p1'::regclass);
+    WHERE conrelid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+    ORDER BY conrelid, conname;
    conrelid   |         conname          | connoinherit | conislocal | coninhcount 
 --------------+--------------------------+--------------+------------+-------------
  itest_parted | itest_parted_f1_not_null | f            | t          |           0
@@ -74,25 +78,28 @@ SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
  itest_p1     | itest_parted_f3_not_null | f            | f          |           1
 (4 rows)
 
-SELECT *
+SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype
     FROM v_pg_depend
-    WHERE refobjid in ('itest_parted'::regclass, 'itest_p1'::regclass)
-            OR objid in ('itest_parted'::regclass, 'itest_p1'::regclass);
-    classid    |           objid_name            | objid | objsubid |  refclassid  | refobjid_name | refobjid | refobjsubid |   deptype    
----------------+---------------------------------+-------+----------+--------------+---------------+----------+-------------+--------------
- pg_type       | itest_parted                    | 16619 |        0 | pg_class     | itest_parted  |    16617 |           0 | internal (i)
- pg_class      | itest_parted                    | 16617 |        0 | pg_namespace | 2200          |     2200 |           0 | normal (n)
- pg_class      | itest_parted                    | 16617 |        1 | pg_class     | itest_parted  |    16617 |           0 | internal (i)
- pg_constraint | public.itest_parted_f1_not_null | 16620 |        0 | pg_class     | itest_parted  |    16617 |           1 | auto (a)
- pg_constraint | public.itest_parted_f3_not_null | 16621 |        0 | pg_class     | itest_parted  |    16617 |           3 | auto (a)
- pg_class      | itest_parted_f3_seq             | 16616 |        0 | pg_class     | itest_parted  |    16617 |           3 | internal (i)
- pg_type       | itest_p1                        | 16624 |        0 | pg_class     | itest_p1      |    16622 |           0 | internal (i)
- pg_class      | itest_p1                        | 16622 |        0 | pg_namespace | 2200          |     2200 |           0 | normal (n)
- pg_class      | itest_p1                        | 16622 |        0 | pg_class     | itest_parted  |    16617 |           0 | auto (a)
- pg_constraint | public.itest_parted_f1_not_null | 16625 |        0 | pg_class     | itest_p1      |    16622 |           1 | auto (a)
- pg_constraint | public.itest_parted_f3_not_null | 16626 |        0 | pg_class     | itest_p1      |    16622 |           3 | auto (a)
- pg_class      | pg_toast.pg_toast_16622         | 16627 |        0 | pg_class     | itest_p1      |    16622 |           0 | internal (i)
-(12 rows)
+    WHERE (refobjid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+            OR objid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p')))
+            AND objid_name NOT LIKE 'pg_toast%'
+    ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
+    classid    |           objid_name            | objsubid |  refclassid  | refobjid_name | refobjsubid |   deptype    
+---------------+---------------------------------+----------+--------------+---------------+-------------+--------------
+ pg_type       | itest_p1                        |        0 | pg_class     | itest_p1      |           0 | internal (i)
+ pg_type       | itest_parted                    |        0 | pg_class     | itest_parted  |           0 | internal (i)
+ pg_class      | itest_p1                        |        0 | pg_class     | itest_parted  |           0 | auto (a)
+ pg_class      | itest_p1                        |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_parted                    |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_parted                    |        1 | pg_class     | itest_parted  |           0 | internal (i)
+ pg_class      | itest_parted_f3_seq             |        0 | pg_class     | itest_parted  |           3 | internal (i)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_p1      |           1 | auto (a)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_parted  |           1 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_p1      |           3 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_parted  |           3 | auto (a)
+(11 rows)
 
 INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
 INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
@@ -103,26 +110,107 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
  itest_p1 | 07-03-2016 | from itest_p1     |  2
 (2 rows)
 
+-- attached partition inherits identity and shares the sequence.
+-- existing values in the table should not be affected
+CREATE TABLE itest_p2 (f1 date NOT NULL, f2 text, f3 bigint);
+-- TODO: ideally setting an identity column should automatically spawn the NOT NULL constraint. But it doesn't do that right now.
+ALTER TABLE itest_p2 ALTER COLUMN f3 SET NOT NULL;
+INSERT INTO itest_p2 VALUES ('2016-08-2', 'before attaching', 100);
+ALTER TABLE itest_parted ATTACH PARTITION itest_p2 FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
+    FROM pg_attribute
+    WHERE attrelid IN (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+        AND attnum > 0
+    ORDER BY attrelid, attnum;
+   attrelid   | attname | attidentity | atthasdef | attgenerated | attnotnull 
+--------------+---------+-------------+-----------+--------------+------------
+ itest_parted | f1      |             | f         |              | t
+ itest_parted | f2      |             | f         |              | f
+ itest_parted | f3      | a           | f         |              | t
+ itest_p1     | f1      |             | f         |              | t
+ itest_p1     | f2      |             | f         |              | f
+ itest_p1     | f3      | a           | f         |              | t
+ itest_p2     | f1      |             | f         |              | t
+ itest_p2     | f2      |             | f         |              | f
+ itest_p2     | f3      | a           | f         |              | t
+(9 rows)
+
+SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
+    FROM pg_constraint
+    WHERE conrelid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+    ORDER BY conrelid, conname;
+   conrelid   |         conname          | connoinherit | conislocal | coninhcount 
+--------------+--------------------------+--------------+------------+-------------
+ itest_parted | itest_parted_f1_not_null | f            | t          |           0
+ itest_parted | itest_parted_f3_not_null | f            | t          |           0
+ itest_p1     | itest_parted_f1_not_null | f            | f          |           1
+ itest_p1     | itest_parted_f3_not_null | f            | f          |           1
+ itest_p2     | itest_p2_f1_not_null     | f            | f          |           1
+ itest_p2     | itest_p2_f3_not_null     | f            | f          |           1
+(6 rows)
+
+SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype
+    FROM v_pg_depend
+    WHERE (refobjid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+            OR objid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p')))
+            AND objid_name NOT LIKE 'pg_toast%'
+    ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
+    classid    |           objid_name            | objsubid |  refclassid  | refobjid_name | refobjsubid |   deptype    
+---------------+---------------------------------+----------+--------------+---------------+-------------+--------------
+ pg_type       | itest_p1                        |        0 | pg_class     | itest_p1      |           0 | internal (i)
+ pg_type       | itest_p2                        |        0 | pg_class     | itest_p2      |           0 | internal (i)
+ pg_type       | itest_parted                    |        0 | pg_class     | itest_parted  |           0 | internal (i)
+ pg_class      | itest_p1                        |        0 | pg_class     | itest_parted  |           0 | auto (a)
+ pg_class      | itest_p1                        |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_p2                        |        0 | pg_class     | itest_parted  |           0 | auto (a)
+ pg_class      | itest_p2                        |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_parted                    |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_parted                    |        1 | pg_class     | itest_parted  |           0 | internal (i)
+ pg_class      | itest_parted_f3_seq             |        0 | pg_class     | itest_parted  |           3 | internal (i)
+ pg_constraint | public.itest_p2_f1_not_null     |        0 | pg_class     | itest_p2      |           1 | auto (a)
+ pg_constraint | public.itest_p2_f3_not_null     |        0 | pg_class     | itest_p2      |           3 | auto (a)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_p1      |           1 | auto (a)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_parted  |           1 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_p1      |           3 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_parted  |           3 | auto (a)
+(16 rows)
+
+INSERT INTO itest_p2 (f1, f2) VALUES ('2016-08-3', 'from itest_p2');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-4', 'from itest_parted');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3  
+----------+------------+-------------------+-----
+ itest_p1 | 07-02-2016 | from itest_parted |   1
+ itest_p1 | 07-03-2016 | from itest_p1     |   2
+ itest_p2 | 08-02-2016 | before attaching  | 100
+ itest_p2 | 08-03-2016 | from itest_p2     |   3
+ itest_p2 | 08-04-2016 | from itest_parted |   4
+(5 rows)
+
 DROP TABLE itest_parted;
 -- scenarios to test
+-- adding an identity column to a partitioned table adds it to all the partitions, including the ones added after this operation - 7th Dec
+-- changing a normal column to identity column is reflected in all the partitions - 8th Dec
+-- detaching a partition removes identity property - 11th Dec
+-- attaching table with identity column is not allowed (even when the parent does not have an identity column) - 12th Dec
+-- trying to drop inherited identity of column of partition is not allowed - 13th Dec
+-- trying to change the identity properties of partition? Is that allowed? - 13th Dec
 -- changing NOT NULL attribute of inherited identity column of partition is not allowed
 -- trying to change default value of inherited identity column of partition is not allowed
 -- trying to add identity to an inherited identity column of partition is not allowed
--- trying to drop inherited identity column of partition is not allowed
--- trying to change the identity properties of partition? Is that allowed?
 -- TODO: test OVERRIDING SYSTEM VALUE OR OVERRIDING USER VALUE
--- attaching table with identity column is not allowed
--- attaching a table as partition inherits identity property
--- detaching a partition removes identity property
--- attaching a table with identity column is not allowed even when the parent table does not have an identity column
 -- dropping an identity column of parent drops it from all the partitions
 -- TODO: do we need a test for this?
--- adding an identity column to a partitioned table adds it to all the partitions, including the ones added after this operation
--- changing a normal column to identity column is reflected in all the partitions
 -- TODO: can we change an identity column to a normal column?
 -- Dump and restore tests - that would be a separate file
 -- pg_upgrade tests
--- multi-level partition test
+-- multi-level partition tests
+-- tests with other flavours of identity column
+-- tests when the identity column is partition key
 -- \d tests
 -- creating a child table with identity column is not allowed when the parent does not have it
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
diff --git a/src/test/regress/sql/identity_part_extra.sql b/src/test/regress/sql/identity_part_extra.sql
index 1ab883e5e1..7925ce6205 100644
--- a/src/test/regress/sql/identity_part_extra.sql
+++ b/src/test/regress/sql/identity_part_extra.sql
@@ -5,60 +5,100 @@ SELECT attrelid, attname, attidentity FROM pg_attribute WHERE attidentity NOT IN
 
 -- table partitions
 CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+
 -- partition inherits identity and shares the sequence
 CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
 SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
     FROM pg_attribute
-    WHERE attrelid IN ('itest_parted'::regclass, 'itest_p1'::regclass)
-        AND attnum > 0;
+    WHERE attrelid IN (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+        AND attnum > 0
+    ORDER BY attrelid, attnum;
 SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
     FROM pg_constraint
-    WHERE conrelid in ('itest_parted'::regclass, 'itest_p1'::regclass);
-SELECT *
+    WHERE conrelid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+    ORDER BY conrelid, conname;
+SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype
     FROM v_pg_depend
-    WHERE refobjid in ('itest_parted'::regclass, 'itest_p1'::regclass)
-            OR objid in ('itest_parted'::regclass, 'itest_p1'::regclass);
+    WHERE (refobjid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+            OR objid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p')))
+            AND objid_name NOT LIKE 'pg_toast%'
+    ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
 INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
 INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
 SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+
+-- attached partition inherits identity and shares the sequence.
+-- existing values in the table should not be affected
+CREATE TABLE itest_p2 (f1 date NOT NULL, f2 text, f3 bigint);
+-- TODO: ideally setting an identity column should automatically spawn the NOT NULL constraint. But it doesn't do that right now.
+ALTER TABLE itest_p2 ALTER COLUMN f3 SET NOT NULL;
+INSERT INTO itest_p2 VALUES ('2016-08-2', 'before attaching', 100);
+ALTER TABLE itest_parted ATTACH PARTITION itest_p2 FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
+    FROM pg_attribute
+    WHERE attrelid IN (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+        AND attnum > 0
+    ORDER BY attrelid, attnum;
+SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
+    FROM pg_constraint
+    WHERE conrelid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+    ORDER BY conrelid, conname;
+SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype
+    FROM v_pg_depend
+    WHERE (refobjid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+            OR objid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p')))
+            AND objid_name NOT LIKE 'pg_toast%'
+    ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
+INSERT INTO itest_p2 (f1, f2) VALUES ('2016-08-3', 'from itest_p2');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-4', 'from itest_parted');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+
 DROP TABLE itest_parted;
 
 -- scenarios to test
 
--- changing NOT NULL attribute of inherited identity column of partition is not allowed
+-- adding an identity column to a partitioned table adds it to all the partitions, including the ones added after this operation - 7th Dec
 
--- trying to change default value of inherited identity column of partition is not allowed
+-- changing a normal column to identity column is reflected in all the partitions - 8th Dec
 
--- trying to add identity to an inherited identity column of partition is not allowed
+-- detaching a partition removes identity property - 11th Dec
 
--- trying to drop inherited identity column of partition is not allowed
+-- attaching table with identity column is not allowed (even when the parent does not have an identity column) - 12th Dec
 
--- trying to change the identity properties of partition? Is that allowed?
+-- trying to drop inherited identity of column of partition is not allowed - 13th Dec
+-- trying to change the identity properties of partition? Is that allowed? - 13th Dec
 
--- TODO: test OVERRIDING SYSTEM VALUE OR OVERRIDING USER VALUE
+-- changing NOT NULL attribute of inherited identity column of partition is not allowed
 
--- attaching table with identity column is not allowed
+-- trying to change default value of inherited identity column of partition is not allowed
 
--- attaching a table as partition inherits identity property
+-- trying to add identity to an inherited identity column of partition is not allowed
 
--- detaching a partition removes identity property
+-- TODO: test OVERRIDING SYSTEM VALUE OR OVERRIDING USER VALUE
 
--- attaching a table with identity column is not allowed even when the parent table does not have an identity column
 
 -- dropping an identity column of parent drops it from all the partitions
 -- TODO: do we need a test for this?
 
--- adding an identity column to a partitioned table adds it to all the partitions, including the ones added after this operation
-
--- changing a normal column to identity column is reflected in all the partitions
-
 -- TODO: can we change an identity column to a normal column?
 
 -- Dump and restore tests - that would be a separate file
 
 -- pg_upgrade tests
 
--- multi-level partition test
+-- multi-level partition tests
+
+-- tests with other flavours of identity column
+
+-- tests when the identity column is partition key
 
 -- \d tests
 
-- 
2.25.1



  [application/x-patch] 0002-A-newly-created-partition-inherits-indentit-20231219.patch (5.7K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/4-0002-A-newly-created-partition-inherits-indentit-20231219.patch)
  download | inline diff:
From e40489264b2f083808833a56c706e1762523ec6f Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Tue, 5 Dec 2023 17:08:11 +0530
Subject: [PATCH 02/14] A newly created partition inherits indentity property

The partitions of a partitioned table are integral part of the
partitioned table. A partition inherits identity columns from the
partitioned table. An indentity column of a partition shares the
identity space with the corresponding column of the partitioned table.
This is effected by sharing the same underlying sequence.  When
INSERTing directly into a partition, sequence associated with the
topmost partitioned table is used to calculate the value of the
corresponding identity column.

Children participating in a regular inheritance, however, may enjoy
identity properties and spaces different from their parent since they
are treated as independent objects.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       |  8 ++++++++
 src/backend/rewrite/rewriteHandler.c   | 19 ++++++++++++++++++-
 src/test/regress/expected/identity.out | 16 +++++++++++++++-
 src/test/regress/sql/identity.sql      | 11 ++++++++++-
 4 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7206da7c53..3e3cd2ed75 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -2852,6 +2852,14 @@ MergeAttributes(List *columns, const List *supers, char relpersistence,
 					def->is_not_null = true;
 				def->storage = attribute->attstorage;
 				def->generated = attribute->attgenerated;
+
+				/*
+				 * partitions inherit identity property from the parent but
+				 * inheritance child does not.
+				 */
+				if (is_partition)
+					def->identity = attribute->attidentity;
+
 				if (CompressionMethodIsValid(attribute->attcompression))
 					def->compression =
 						pstrdup(GetCompressionMethodName(attribute->attcompression));
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 41a362310a..d5f1993665 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -25,6 +25,7 @@
 #include "access/table.h"
 #include "catalog/dependency.h"
 #include "catalog/pg_type.h"
+#include "catalog/partition.h"
 #include "commands/trigger.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
@@ -1234,8 +1235,24 @@ build_column_default(Relation rel, int attrno)
 	if (att_tup->attidentity)
 	{
 		NextValueExpr *nve = makeNode(NextValueExpr);
+		Oid			reloid;
 
-		nve->seqid = getIdentitySequence(RelationGetRelid(rel), attrno, false);
+		/*
+		 * The identity sequence is associated with the topmost partitioned
+		 * table.
+		 */
+		if (rel->rd_rel->relispartition)
+		{
+			List	   *ancestors =
+				get_partition_ancestors(RelationGetRelid(rel));
+
+			reloid = llast_oid(ancestors);
+			list_free(ancestors);
+		}
+		else
+			reloid = RelationGetRelid(rel);
+
+		nve->seqid = getIdentitySequence(reloid, attrno, false);
 		nve->typeId = att_tup->atttypid;
 
 		return (Node *) nve;
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 7c6e87e8a5..eff73add59 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -539,7 +539,21 @@ CREATE TYPE itest_type AS (f1 integer, f2 text, f3 bigint);
 CREATE TABLE itest12 OF itest_type (f1 WITH OPTIONS GENERATED ALWAYS AS IDENTITY); -- error
 ERROR:  identity columns are not supported on typed tables
 DROP TYPE itest_type CASCADE;
--- table partitions (currently not supported)
+-- table partitions
+-- a newly created partition inherits identity and shares the sequence
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3 
+----------+------------+-------------------+----
+ itest_p1 | 07-02-2016 | from itest_parted |  1
+ itest_p1 | 07-03-2016 | from itest_p1     |  2
+(2 rows)
+
+DROP TABLE itest_parted;
+-- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
     f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index 9b8db2e4a3..ad2a4249b9 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -331,8 +331,17 @@ CREATE TABLE itest12 OF itest_type (f1 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
 DROP TYPE itest_type CASCADE;
 
 
--- table partitions (currently not supported)
+-- table partitions
 
+-- a newly created partition inherits identity and shares the sequence
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+DROP TABLE itest_parted;
+
+-- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
     f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
-- 
2.25.1



  [application/x-patch] 0003-Identity-column-support-in-partitioned-tabl-20231219.patch (11.4K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/5-0003-Identity-column-support-in-partitioned-tabl-20231219.patch)
  download | inline diff:
From f909361f703dc9ad12644f601633243d835a9993 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Mon, 4 Dec 2023 15:43:58 +0530
Subject: [PATCH 03/14] Identity column support in partitioned tables extra
 tests

These tests are more like white box tests. Not to be included in the final set of patches.

Ashutosh Bapat
---
 .../regress/expected/identity_part_extra.out  | 133 ++++++++++++++++++
 src/test/regress/sql/identity_part_extra.sql  |  70 +++++++++
 2 files changed, 203 insertions(+)
 create mode 100644 src/test/regress/expected/identity_part_extra.out
 create mode 100644 src/test/regress/sql/identity_part_extra.sql

diff --git a/src/test/regress/expected/identity_part_extra.out b/src/test/regress/expected/identity_part_extra.out
new file mode 100644
index 0000000000..0c8af1c658
--- /dev/null
+++ b/src/test/regress/expected/identity_part_extra.out
@@ -0,0 +1,133 @@
+-- sanity check of system catalog
+SELECT attrelid, attname, attidentity FROM pg_attribute WHERE attidentity NOT IN ('', 'a', 'd');
+ attrelid | attname | attidentity 
+----------+---------+-------------
+(0 rows)
+
+\i /home/ashutosh/scripts/postgresql/readable_pg_catalog.sql
+create function object_name(classid oid, objid oid)
+returns name
+language plpgsql
+as $$
+declare
+	object_name name;
+begin
+	case classid
+		when 'pg_catalog.pg_class'::regclass then
+			object_name := objid::regclass;
+		when 'pg_catalog.pg_constraint'::regclass then
+			select connamespace::regnamespace || '.' || conname
+				into object_name
+				from pg_constraint where oid = objid;
+		when 'pg_catalog.pg_type'::regclass then
+			object_name := objid::regtype;
+		else
+			object_name := objid::text::name;
+	end case;
+	return object_name;
+end;
+$$;
+create view v_pg_depend as
+	select classid::regclass,
+			object_name(classid, objid) objid_name,
+			objid,
+			objsubid,
+			refclassid::regclass,
+			object_name(refclassid, refobjid) refobjid_name,
+			refobjid,
+			refobjsubid,
+			(case deptype when 'i' then 'internal'
+							when 'a' then 'auto'
+							when 'n' then 'normal'
+							when 'P' then 'partition primary'
+							when 'S' then 'partition secondary'
+							when 'e' then 'extension'
+							when 'x' then 'auto extension'
+			 	end) || ' (' || deptype::text || ')' as deptype
+		from pg_depend;
+-- table partitions
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+-- partition inherits identity and shares the sequence
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
+    FROM pg_attribute
+    WHERE attrelid IN ('itest_parted'::regclass, 'itest_p1'::regclass)
+        AND attnum > 0;
+   attrelid   | attname | attidentity | atthasdef | attgenerated | attnotnull 
+--------------+---------+-------------+-----------+--------------+------------
+ itest_parted | f1      |             | f         |              | t
+ itest_parted | f2      |             | f         |              | f
+ itest_parted | f3      | a           | f         |              | t
+ itest_p1     | f1      |             | f         |              | t
+ itest_p1     | f2      |             | f         |              | f
+ itest_p1     | f3      | a           | f         |              | t
+(6 rows)
+
+SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
+    FROM pg_constraint
+    WHERE conrelid in ('itest_parted'::regclass, 'itest_p1'::regclass);
+   conrelid   |         conname          | connoinherit | conislocal | coninhcount 
+--------------+--------------------------+--------------+------------+-------------
+ itest_parted | itest_parted_f1_not_null | f            | t          |           0
+ itest_parted | itest_parted_f3_not_null | f            | t          |           0
+ itest_p1     | itest_parted_f1_not_null | f            | f          |           1
+ itest_p1     | itest_parted_f3_not_null | f            | f          |           1
+(4 rows)
+
+SELECT *
+    FROM v_pg_depend
+    WHERE refobjid in ('itest_parted'::regclass, 'itest_p1'::regclass)
+            OR objid in ('itest_parted'::regclass, 'itest_p1'::regclass);
+    classid    |           objid_name            | objid | objsubid |  refclassid  | refobjid_name | refobjid | refobjsubid |   deptype    
+---------------+---------------------------------+-------+----------+--------------+---------------+----------+-------------+--------------
+ pg_type       | itest_parted                    | 16619 |        0 | pg_class     | itest_parted  |    16617 |           0 | internal (i)
+ pg_class      | itest_parted                    | 16617 |        0 | pg_namespace | 2200          |     2200 |           0 | normal (n)
+ pg_class      | itest_parted                    | 16617 |        1 | pg_class     | itest_parted  |    16617 |           0 | internal (i)
+ pg_constraint | public.itest_parted_f1_not_null | 16620 |        0 | pg_class     | itest_parted  |    16617 |           1 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null | 16621 |        0 | pg_class     | itest_parted  |    16617 |           3 | auto (a)
+ pg_class      | itest_parted_f3_seq             | 16616 |        0 | pg_class     | itest_parted  |    16617 |           3 | internal (i)
+ pg_type       | itest_p1                        | 16624 |        0 | pg_class     | itest_p1      |    16622 |           0 | internal (i)
+ pg_class      | itest_p1                        | 16622 |        0 | pg_namespace | 2200          |     2200 |           0 | normal (n)
+ pg_class      | itest_p1                        | 16622 |        0 | pg_class     | itest_parted  |    16617 |           0 | auto (a)
+ pg_constraint | public.itest_parted_f1_not_null | 16625 |        0 | pg_class     | itest_p1      |    16622 |           1 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null | 16626 |        0 | pg_class     | itest_p1      |    16622 |           3 | auto (a)
+ pg_class      | pg_toast.pg_toast_16622         | 16627 |        0 | pg_class     | itest_p1      |    16622 |           0 | internal (i)
+(12 rows)
+
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3 
+----------+------------+-------------------+----
+ itest_p1 | 07-02-2016 | from itest_parted |  1
+ itest_p1 | 07-03-2016 | from itest_p1     |  2
+(2 rows)
+
+DROP TABLE itest_parted;
+-- scenarios to test
+-- changing NOT NULL attribute of inherited identity column of partition is not allowed
+-- trying to change default value of inherited identity column of partition is not allowed
+-- trying to add identity to an inherited identity column of partition is not allowed
+-- trying to drop inherited identity column of partition is not allowed
+-- trying to change the identity properties of partition? Is that allowed?
+-- TODO: test OVERRIDING SYSTEM VALUE OR OVERRIDING USER VALUE
+-- attaching table with identity column is not allowed
+-- attaching a table as partition inherits identity property
+-- detaching a partition removes identity property
+-- attaching a table with identity column is not allowed even when the parent table does not have an identity column
+-- dropping an identity column of parent drops it from all the partitions
+-- TODO: do we need a test for this?
+-- adding an identity column to a partitioned table adds it to all the partitions, including the ones added after this operation
+-- changing a normal column to identity column is reflected in all the partitions
+-- TODO: can we change an identity column to a normal column?
+-- Dump and restore tests - that would be a separate file
+-- pg_upgrade tests
+-- multi-level partition test
+-- \d tests
+-- creating a child table with identity column is not allowed when the parent does not have it
+CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
+CREATE TABLE itest_child PARTITION OF itest_parent(
+    f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
+) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
+ERROR:  identity columns are not supported on partitions
+DROP TABLE itest_parent;
diff --git a/src/test/regress/sql/identity_part_extra.sql b/src/test/regress/sql/identity_part_extra.sql
new file mode 100644
index 0000000000..1ab883e5e1
--- /dev/null
+++ b/src/test/regress/sql/identity_part_extra.sql
@@ -0,0 +1,70 @@
+-- sanity check of system catalog
+SELECT attrelid, attname, attidentity FROM pg_attribute WHERE attidentity NOT IN ('', 'a', 'd');
+
+\i /home/ashutosh/scripts/postgresql/readable_pg_catalog.sql
+
+-- table partitions
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+-- partition inherits identity and shares the sequence
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
+    FROM pg_attribute
+    WHERE attrelid IN ('itest_parted'::regclass, 'itest_p1'::regclass)
+        AND attnum > 0;
+SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
+    FROM pg_constraint
+    WHERE conrelid in ('itest_parted'::regclass, 'itest_p1'::regclass);
+SELECT *
+    FROM v_pg_depend
+    WHERE refobjid in ('itest_parted'::regclass, 'itest_p1'::regclass)
+            OR objid in ('itest_parted'::regclass, 'itest_p1'::regclass);
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+DROP TABLE itest_parted;
+
+-- scenarios to test
+
+-- changing NOT NULL attribute of inherited identity column of partition is not allowed
+
+-- trying to change default value of inherited identity column of partition is not allowed
+
+-- trying to add identity to an inherited identity column of partition is not allowed
+
+-- trying to drop inherited identity column of partition is not allowed
+
+-- trying to change the identity properties of partition? Is that allowed?
+
+-- TODO: test OVERRIDING SYSTEM VALUE OR OVERRIDING USER VALUE
+
+-- attaching table with identity column is not allowed
+
+-- attaching a table as partition inherits identity property
+
+-- detaching a partition removes identity property
+
+-- attaching a table with identity column is not allowed even when the parent table does not have an identity column
+
+-- dropping an identity column of parent drops it from all the partitions
+-- TODO: do we need a test for this?
+
+-- adding an identity column to a partitioned table adds it to all the partitions, including the ones added after this operation
+
+-- changing a normal column to identity column is reflected in all the partitions
+
+-- TODO: can we change an identity column to a normal column?
+
+-- Dump and restore tests - that would be a separate file
+
+-- pg_upgrade tests
+
+-- multi-level partition test
+
+-- \d tests
+
+-- creating a child table with identity column is not allowed when the parent does not have it
+CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
+CREATE TABLE itest_child PARTITION OF itest_parent(
+    f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
+) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
+DROP TABLE itest_parent;
\ No newline at end of file
-- 
2.25.1



  [application/x-patch] 0004-Attached-partition-inherits-identity-column-20231219.patch (8.1K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/6-0004-Attached-partition-inherits-identity-column-20231219.patch)
  download | inline diff:
From 83bdda1151055d943cc6318d9b792f2203df23dd Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Wed, 6 Dec 2023 16:08:59 +0530
Subject: [PATCH 04/14] Attached partition inherits identity column

Identity column of a partitioned table should use the same value space.
Hence column which is identity column in partitioned table must be
identity column in all the partitions and share the same value space. A
table being attached as a partition inherits identity column. This
should be fine since we expect that the partition table's column has the
same type as the partitioned table's corresponding column.

TODO:

However this commit doesn't address one problem. An Identity column in
the partitioned table is also marked as NOT NULL. If that column in the
partition is not marked as NOT NULL, it should automatically get marked
as such. But this commit does not do so. The test has an ALTER TABLE
statement to mark the column in partition table as NOT NULL.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       | 24 +++++++++++++++++-------
 src/test/regress/expected/identity.out | 19 +++++++++++++++++++
 src/test/regress/sql/identity.sql      | 12 ++++++++++++
 3 files changed, 48 insertions(+), 7 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3e3cd2ed75..b298747e13 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -354,7 +354,8 @@ static List *MergeAttributes(List *columns, const List *supers, char relpersiste
 							 bool is_partition, List **supconstr,
 							 List **supnotnulls);
 static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
-static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
+static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel,
+										bool ispartition);
 static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
 static void StoreCatalogInheritance(Oid relationId, List *supers,
 									bool child_is_partition);
@@ -618,7 +619,8 @@ static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partsp
 static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
 								  List **partexprs, Oid *partopclass, Oid *partcollation,
 								  PartitionStrategy strategy);
-static void CreateInheritance(Relation child_rel, Relation parent_rel);
+static void CreateInheritance(Relation child_rel, Relation parent_rel,
+							  bool ispartition);
 static void RemoveInheritance(Relation child_rel, Relation parent_rel,
 							  bool expect_detached);
 static void ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel,
@@ -15643,7 +15645,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
 				 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
 
 	/* OK to create inheritance */
-	CreateInheritance(child_rel, parent_rel);
+	CreateInheritance(child_rel, parent_rel, false);
 
 	/*
 	 * If parent_rel has a primary key, then child_rel has not-null
@@ -15669,7 +15671,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
  * Common to ATExecAddInherit() and ATExecAttachPartition().
  */
 static void
-CreateInheritance(Relation child_rel, Relation parent_rel)
+CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
 {
 	Relation	catalogRelation;
 	SysScanDesc scan;
@@ -15714,7 +15716,7 @@ CreateInheritance(Relation child_rel, Relation parent_rel)
 	systable_endscan(scan);
 
 	/* Match up the columns and bump attinhcount as needed */
-	MergeAttributesIntoExisting(child_rel, parent_rel);
+	MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
 
 	/* Match up the constraints and bump coninhcount as needed */
 	MergeConstraintsIntoExisting(child_rel, parent_rel);
@@ -15792,7 +15794,8 @@ constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
  * the child must be as well. Defaults are not compared, however.
  */
 static void
-MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
+MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel,
+							bool ispartition)
 {
 	Relation	attrrel;
 	TupleDesc	parent_desc;
@@ -15861,6 +15864,13 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
 						(errcode(ERRCODE_DATATYPE_MISMATCH),
 						 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
 
+			/*
+			 * A partition inherits identity column from the parent but not a
+			 * regular inheritance child.
+			 */
+			if (ispartition)
+				child_att->attidentity = parent_att->attidentity;
+
 			/*
 			 * OK, bump the child column's inheritance count.  (If we fail
 			 * later on, this change will just roll back.)
@@ -18692,7 +18702,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
 							  cmd->bound, pstate);
 
 	/* OK to create inheritance.  Rest of the checks performed there */
-	CreateInheritance(attachrel, rel);
+	CreateInheritance(attachrel, rel, true);
 
 	/* Update the pg_class entry. */
 	StorePartitionBound(attachrel, rel, cmd->bound);
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index eff73add59..6b9edb22c1 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -552,6 +552,25 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
  itest_p1 | 07-03-2016 | from itest_p1     |  2
 (2 rows)
 
+-- attached partition inherits identity and shares the sequence.
+-- existing values in the table should not be affected
+CREATE TABLE itest_p2 (f1 date NOT NULL, f2 text, f3 bigint);
+-- TODO: ideally setting an identity column should automatically spawn the NOT NULL constraint. But it doesn't do that right now.
+ALTER TABLE itest_p2 ALTER COLUMN f3 SET NOT NULL;
+INSERT INTO itest_p2 VALUES ('2016-08-2', 'before attaching', 100);
+ALTER TABLE itest_parted ATTACH PARTITION itest_p2 FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT INTO itest_p2 (f1, f2) VALUES ('2016-08-3', 'from itest_p2');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-4', 'from itest_parted');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3  
+----------+------------+-------------------+-----
+ itest_p1 | 07-02-2016 | from itest_parted |   1
+ itest_p1 | 07-03-2016 | from itest_p1     |   2
+ itest_p2 | 08-02-2016 | before attaching  | 100
+ itest_p2 | 08-03-2016 | from itest_p2     |   3
+ itest_p2 | 08-04-2016 | from itest_parted |   4
+(5 rows)
+
 DROP TABLE itest_parted;
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index ad2a4249b9..a9babd1baa 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -339,6 +339,18 @@ CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') T
 INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
 INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
 SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+
+-- attached partition inherits identity and shares the sequence.
+-- existing values in the table should not be affected
+CREATE TABLE itest_p2 (f1 date NOT NULL, f2 text, f3 bigint);
+-- TODO: ideally setting an identity column should automatically spawn the NOT NULL constraint. But it doesn't do that right now.
+ALTER TABLE itest_p2 ALTER COLUMN f3 SET NOT NULL;
+INSERT INTO itest_p2 VALUES ('2016-08-2', 'before attaching', 100);
+ALTER TABLE itest_parted ATTACH PARTITION itest_p2 FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT INTO itest_p2 (f1, f2) VALUES ('2016-08-3', 'from itest_p2');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-4', 'from itest_parted');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+
 DROP TABLE itest_parted;
 
 -- partition with identity column of its own is not allowed
-- 
2.25.1



  [application/x-patch] 0008-TODO-Assess-whether-the-TODO-needs-to-be-ad-20231219.patch (1.1K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/7-0008-TODO-Assess-whether-the-TODO-needs-to-be-ad-20231219.patch)
  download | inline diff:
From b19f8d83cd04132706869c0d99140a1eb62ed4a5 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Thu, 7 Dec 2023 11:38:07 +0530
Subject: [PATCH 08/14] TODO: Assess whether the TODO needs to be addressed.

The commit adds a TODO in MergeAttributes() in code which should not be
reachable for a partitioned table. But the code seems to use
is_partition variable. Assess whether it's reachable and if reachable
fix it for indentity column in partitioned table.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index b471cb4d6b..7b94d7f434 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -2826,6 +2826,8 @@ MergeAttributes(List *columns, const List *supers, char relpersistence,
 							 errmsg("inherited column \"%s\" has a generation conflict",
 									attributeName)));
 
+				/* TODO: We shouldn't enter this branch for a partition? */
+
 				/*
 				 * Default and other constraints are handled below
 				 */
-- 
2.25.1



  [application/x-patch] 0007-Extra-tests-for-adding-column-to-a-partitio-20231219.patch (15.0K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/8-0007-Extra-tests-for-adding-column-to-a-partitio-20231219.patch)
  download | inline diff:
From a203c08d54947925541af1ea448f9984c7d3344c Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Thu, 7 Dec 2023 11:08:17 +0530
Subject: [PATCH 07/14] Extra tests for adding column to a partitioned table

NOT FOR FINAL COMMIT
---
 .../regress/expected/identity_part_extra.out  | 130 +++++++++++++++++-
 src/test/regress/sql/identity_part_extra.sql  |  51 ++++++-
 2 files changed, 178 insertions(+), 3 deletions(-)

diff --git a/src/test/regress/expected/identity_part_extra.out b/src/test/regress/expected/identity_part_extra.out
index 81dfca2acc..644395f89a 100644
--- a/src/test/regress/expected/identity_part_extra.out
+++ b/src/test/regress/expected/identity_part_extra.out
@@ -101,6 +101,12 @@ SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, de
  pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_parted  |           3 | auto (a)
 (11 rows)
 
+SELECT seqrelid::regclass, seqtypid::regtype FROM pg_sequence;
+      seqrelid       | seqtypid 
+---------------------+----------
+ itest_parted_f3_seq | bigint
+(1 row)
+
 INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
 INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
 SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
@@ -179,6 +185,12 @@ SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, de
  pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_parted  |           3 | auto (a)
 (16 rows)
 
+SELECT seqrelid::regclass, seqtypid::regtype FROM pg_sequence;
+      seqrelid       | seqtypid 
+---------------------+----------
+ itest_parted_f3_seq | bigint
+(1 row)
+
 INSERT INTO itest_p2 (f1, f2) VALUES ('2016-08-3', 'from itest_p2');
 INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-4', 'from itest_parted');
 SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
@@ -191,9 +203,125 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
  itest_p2 | 08-04-2016 | from itest_parted |   4
 (5 rows)
 
+DROP TABLE itest_parted;
+-- adding an identity column to a partitioned table adds it to all the
+-- partitions, including the ones added after this operation
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+CREATE TABLE itest_p2 PARTITION OF itest_parted FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-2', 'from itest_parted');
+ALTER TABLE itest_parted ADD COLUMN f3 int generated always as identity;
+SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
+    FROM pg_attribute
+    WHERE attrelid IN (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+        AND attnum > 0
+    ORDER BY attrelid, attnum;
+   attrelid   | attname | attidentity | atthasdef | attgenerated | attnotnull 
+--------------+---------+-------------+-----------+--------------+------------
+ itest_parted | f1      |             | f         |              | t
+ itest_parted | f2      |             | f         |              | f
+ itest_parted | f3      | a           | f         |              | t
+ itest_p1     | f1      |             | f         |              | t
+ itest_p1     | f2      |             | f         |              | f
+ itest_p1     | f3      | a           | f         |              | t
+ itest_p2     | f1      |             | f         |              | t
+ itest_p2     | f2      |             | f         |              | f
+ itest_p2     | f3      | a           | f         |              | t
+(9 rows)
+
+SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
+    FROM pg_constraint
+    WHERE conrelid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+    ORDER BY conrelid, conname;
+   conrelid   |         conname          | connoinherit | conislocal | coninhcount 
+--------------+--------------------------+--------------+------------+-------------
+ itest_parted | itest_parted_f1_not_null | f            | t          |           0
+ itest_parted | itest_parted_f3_not_null | f            | t          |           0
+ itest_p1     | itest_parted_f1_not_null | f            | f          |           1
+ itest_p1     | itest_parted_f3_not_null | f            | f          |           1
+ itest_p2     | itest_parted_f1_not_null | f            | f          |           1
+ itest_p2     | itest_parted_f3_not_null | f            | f          |           1
+(6 rows)
+
+SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype
+    FROM v_pg_depend
+    WHERE (refobjid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+            OR objid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p')))
+            AND objid_name NOT LIKE 'pg_toast%'
+    ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
+    classid    |           objid_name            | objsubid |  refclassid  | refobjid_name | refobjsubid |   deptype    
+---------------+---------------------------------+----------+--------------+---------------+-------------+--------------
+ pg_type       | itest_p1                        |        0 | pg_class     | itest_p1      |           0 | internal (i)
+ pg_type       | itest_p2                        |        0 | pg_class     | itest_p2      |           0 | internal (i)
+ pg_type       | itest_parted                    |        0 | pg_class     | itest_parted  |           0 | internal (i)
+ pg_class      | itest_p1                        |        0 | pg_class     | itest_parted  |           0 | auto (a)
+ pg_class      | itest_p1                        |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_p2                        |        0 | pg_class     | itest_parted  |           0 | auto (a)
+ pg_class      | itest_p2                        |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_parted                    |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_parted                    |        1 | pg_class     | itest_parted  |           0 | internal (i)
+ pg_class      | itest_parted_f3_seq             |        0 | pg_class     | itest_parted  |           3 | internal (i)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_p1      |           1 | auto (a)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_p2      |           1 | auto (a)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_parted  |           1 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_p1      |           3 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_p2      |           3 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_parted  |           3 | auto (a)
+(16 rows)
+
+SELECT seqrelid::regclass, seqtypid::regtype FROM pg_sequence;
+      seqrelid       | seqtypid 
+---------------------+----------
+ itest_parted_f3_seq | integer
+(1 row)
+
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
+INSERT INTO itest_p2 (f1, f2) VALUES ('2016-08-3', 'from itest_p2');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-4', 'from itest_parted');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-4', 'from itest_parted');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3 
+----------+------------+-------------------+----
+ itest_p1 | 07-02-2016 | from itest_parted |  1
+ itest_p1 | 07-03-2016 | from itest_p1     |  3
+ itest_p1 | 07-04-2016 | from itest_parted |  5
+ itest_p2 | 08-02-2016 | from itest_parted |  2
+ itest_p2 | 08-03-2016 | from itest_p2     |  4
+ itest_p2 | 08-04-2016 | from itest_parted |  6
+(6 rows)
+
+-- extra tests - consider if we want to add it to the main test file
+CREATE TABLE itest_p3 PARTITION OF itest_parted FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
+CREATE TABLE itest_p4 (f1 date NOT NULL, f2 text, f3 int);
+-- TODO: ideally setting an identity column should automatically spawn the NOT NULL constraint. But it doesn't do that right now.
+ALTER TABLE itest_p4 ALTER COLUMN f3 SET NOT NULL;
+ALTER TABLE itest_parted ATTACH PARTITION itest_p4 FOR VALUES FROM ('2016-10-01') TO ('2016-11-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-09-2', 'from itest_parted');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-10-2', 'from itest_parted');
+INSERT into itest_p3 (f1, f2) VALUES ('2016-09-3', 'from itest_p3');
+INSERT INTO itest_p4 (f1, f2) VALUES ('2016-10-3', 'from itest_p4');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3 
+----------+------------+-------------------+----
+ itest_p1 | 07-02-2016 | from itest_parted |  1
+ itest_p1 | 07-03-2016 | from itest_p1     |  3
+ itest_p1 | 07-04-2016 | from itest_parted |  5
+ itest_p2 | 08-02-2016 | from itest_parted |  2
+ itest_p2 | 08-03-2016 | from itest_p2     |  4
+ itest_p2 | 08-04-2016 | from itest_parted |  6
+ itest_p3 | 09-02-2016 | from itest_parted |  7
+ itest_p3 | 09-03-2016 | from itest_p3     |  9
+ itest_p4 | 10-02-2016 | from itest_parted |  8
+ itest_p4 | 10-03-2016 | from itest_p4     | 10
+(10 rows)
+
 DROP TABLE itest_parted;
 -- scenarios to test
--- adding an identity column to a partitioned table adds it to all the partitions, including the ones added after this operation - 7th Dec
 -- changing a normal column to identity column is reflected in all the partitions - 8th Dec
 -- detaching a partition removes identity property - 11th Dec
 -- attaching table with identity column is not allowed (even when the parent does not have an identity column) - 12th Dec
diff --git a/src/test/regress/sql/identity_part_extra.sql b/src/test/regress/sql/identity_part_extra.sql
index 7925ce6205..33c5a30a94 100644
--- a/src/test/regress/sql/identity_part_extra.sql
+++ b/src/test/regress/sql/identity_part_extra.sql
@@ -27,6 +27,7 @@ SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, de
                                                     and relkind in ('r', 'p')))
             AND objid_name NOT LIKE 'pg_toast%'
     ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
+SELECT seqrelid::regclass, seqtypid::regtype FROM pg_sequence;
 INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
 INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
 SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
@@ -57,15 +58,61 @@ SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, de
                                                     and relkind in ('r', 'p')))
             AND objid_name NOT LIKE 'pg_toast%'
     ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
+SELECT seqrelid::regclass, seqtypid::regtype FROM pg_sequence;
 INSERT INTO itest_p2 (f1, f2) VALUES ('2016-08-3', 'from itest_p2');
 INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-4', 'from itest_parted');
 SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
 
 DROP TABLE itest_parted;
 
--- scenarios to test
+-- adding an identity column to a partitioned table adds it to all the
+-- partitions, including the ones added after this operation
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+CREATE TABLE itest_p2 PARTITION OF itest_parted FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-2', 'from itest_parted');
+ALTER TABLE itest_parted ADD COLUMN f3 int generated always as identity;
+SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
+    FROM pg_attribute
+    WHERE attrelid IN (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+        AND attnum > 0
+    ORDER BY attrelid, attnum;
+SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
+    FROM pg_constraint
+    WHERE conrelid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+    ORDER BY conrelid, conname;
+SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype
+    FROM v_pg_depend
+    WHERE (refobjid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+            OR objid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p')))
+            AND objid_name NOT LIKE 'pg_toast%'
+    ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
+SELECT seqrelid::regclass, seqtypid::regtype FROM pg_sequence;
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
+INSERT INTO itest_p2 (f1, f2) VALUES ('2016-08-3', 'from itest_p2');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-4', 'from itest_parted');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-4', 'from itest_parted');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+-- extra tests - consider if we want to add it to the main test file
+CREATE TABLE itest_p3 PARTITION OF itest_parted FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
+CREATE TABLE itest_p4 (f1 date NOT NULL, f2 text, f3 int);
+-- TODO: ideally setting an identity column should automatically spawn the NOT NULL constraint. But it doesn't do that right now.
+ALTER TABLE itest_p4 ALTER COLUMN f3 SET NOT NULL;
+ALTER TABLE itest_parted ATTACH PARTITION itest_p4 FOR VALUES FROM ('2016-10-01') TO ('2016-11-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-09-2', 'from itest_parted');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-10-2', 'from itest_parted');
+INSERT into itest_p3 (f1, f2) VALUES ('2016-09-3', 'from itest_p3');
+INSERT INTO itest_p4 (f1, f2) VALUES ('2016-10-3', 'from itest_p4');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
 
--- adding an identity column to a partitioned table adds it to all the partitions, including the ones added after this operation - 7th Dec
+DROP TABLE itest_parted;
+
+-- scenarios to test
 
 -- changing a normal column to identity column is reflected in all the partitions - 8th Dec
 
-- 
2.25.1



  [application/x-patch] 0009-Adding-identity-to-partitioned-table-adds-i-20231219.patch (7.1K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/9-0009-Adding-identity-to-partitioned-table-adds-i-20231219.patch)
  download | inline diff:
From 546b7a154db10edae4d63fbd3935be6b1342c1e5 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Mon, 11 Dec 2023 16:41:09 +0530
Subject: [PATCH 09/14] Adding identity to partitioned table adds it to all
 partitions

... additionally do not allow adding identity to only partitioned table.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       | 42 +++++++++++++++++++++++---
 src/test/regress/expected/identity.out | 25 +++++++++++++++
 src/test/regress/sql/identity.sql      | 17 +++++++++++
 3 files changed, 80 insertions(+), 4 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7b94d7f434..cf521a65df 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -452,7 +452,8 @@ static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
 static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
 											   Node *newDefault);
 static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
-									   Node *def, LOCKMODE lockmode);
+									   Node *def, LOCKMODE lockmode,
+									   bool recurse, bool recursing);
 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
 									   Node *def, LOCKMODE lockmode);
 static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
@@ -4823,7 +4824,9 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			break;
 		case AT_AddIdentity:
 			ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
-			/* This command never recurses */
+			/* Set up recursion for phase 2; no other prep needed */
+			if (recurse)
+				cmd->recurse = true;
 			pass = AT_PASS_ADD_OTHERCONSTR;
 			break;
 		case AT_SetIdentity:
@@ -5222,7 +5225,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
 									  cur_pass, context);
 			Assert(cmd != NULL);
-			address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
+			address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode,
+										cmd->recurse, false);
 			break;
 		case AT_SetIdentity:
 			cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
@@ -8100,7 +8104,7 @@ ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
  */
 static ObjectAddress
 ATExecAddIdentity(Relation rel, const char *colName,
-				  Node *def, LOCKMODE lockmode)
+				  Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
 {
 	Relation	attrelation;
 	HeapTuple	tuple;
@@ -8108,6 +8112,14 @@ ATExecAddIdentity(Relation rel, const char *colName,
 	AttrNumber	attnum;
 	ObjectAddress address;
 	ColumnDef  *cdef = castNode(ColumnDef, def);
+	bool		ispartitioned;
+
+	ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
+	if (ispartitioned && !recurse)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+				 errmsg("identity can only be added to column \"%s\" across all the partitions of the relation \"%s\"",
+						colName, RelationGetRelationName(rel))));
 
 	attrelation = table_open(AttributeRelationId, RowExclusiveLock);
 
@@ -8162,6 +8174,28 @@ ATExecAddIdentity(Relation rel, const char *colName,
 
 	table_close(attrelation, RowExclusiveLock);
 
+	/*
+	 * Recurse to propagate the identity column to partitions. Identity is not
+	 * inherited in regular inheritance children.
+	 */
+	if (recurse && ispartitioned)
+	{
+		List	   *children;
+		ListCell   *lc;
+
+		children = find_inheritance_children(RelationGetRelid(rel),
+											 lockmode);
+
+		foreach(lc, children)
+		{
+			Relation	childrel;
+
+			childrel = table_open(lfirst_oid(lc), NoLock);
+			ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
+			table_close(childrel, NoLock);
+		}
+	}
+
 	return address;
 }
 
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 9984f7ed19..96659d4399 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -595,6 +595,31 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
  itest_p2 | 08-04-2016 | from itest_parted |  6
 (6 rows)
 
+DROP TABLE itest_parted;
+-- changing a regular column to identity column in a partitioned table
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 int) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into itest_parted VALUES ('2016-07-2', 'from itest_parted', 1);
+INSERT into itest_p1 VALUES ('2016-07-3', 'from itest_p1', 2);
+ALTER TABLE ONLY itest_parted -- ONLY causes the command to fail
+            ALTER COLUMN f3 SET NOT NULL,
+            ALTER COLUMN f3 ADD generated always as identity (start with 3);
+ERROR:  constraint must be added to child tables too
+HINT:  Do not specify the ONLY keyword.
+ALTER TABLE itest_parted
+            ALTER COLUMN f3 SET NOT NULL, -- avoids error
+            ALTER COLUMN f3 ADD generated always as identity (start with 3);
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-4', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-5', 'from itest_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3 
+----------+------------+-------------------+----
+ itest_p1 | 07-02-2016 | from itest_parted |  1
+ itest_p1 | 07-03-2016 | from itest_p1     |  2
+ itest_p1 | 07-04-2016 | from itest_parted |  3
+ itest_p1 | 07-05-2016 | from itest_p1     |  4
+(4 rows)
+
 DROP TABLE itest_parted;
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index f23ccd2a86..31615914f5 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -369,6 +369,23 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
 
 DROP TABLE itest_parted;
 
+-- changing a regular column to identity column in a partitioned table
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 int) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into itest_parted VALUES ('2016-07-2', 'from itest_parted', 1);
+INSERT into itest_p1 VALUES ('2016-07-3', 'from itest_p1', 2);
+ALTER TABLE ONLY itest_parted -- ONLY causes the command to fail
+            ALTER COLUMN f3 SET NOT NULL,
+            ALTER COLUMN f3 ADD generated always as identity (start with 3);
+ALTER TABLE itest_parted
+            ALTER COLUMN f3 SET NOT NULL, -- avoids error
+            ALTER COLUMN f3 ADD generated always as identity (start with 3);
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-4', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-5', 'from itest_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+
+DROP TABLE itest_parted;
+
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
-- 
2.25.1



  [application/x-patch] 0010-Extra-tests-for-adding-identity-to-a-column-20231219.patch (12.3K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/10-0010-Extra-tests-for-adding-identity-to-a-column-20231219.patch)
  download | inline diff:
From 50950964baa11eb451abda954d4893952c95f9f0 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Mon, 11 Dec 2023 16:20:15 +0530
Subject: [PATCH 10/14] Extra tests for adding identity to a column

NOT FOR FINAL COMMIT
---
 .../regress/expected/identity_part_extra.out  | 111 +++++++++++++++++-
 src/test/regress/sql/identity_part_extra.sql  |  53 ++++++++-
 2 files changed, 158 insertions(+), 6 deletions(-)

diff --git a/src/test/regress/expected/identity_part_extra.out b/src/test/regress/expected/identity_part_extra.out
index 644395f89a..05de0cabab 100644
--- a/src/test/regress/expected/identity_part_extra.out
+++ b/src/test/regress/expected/identity_part_extra.out
@@ -320,11 +320,116 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
  itest_p4 | 10-03-2016 | from itest_p4     | 10
 (10 rows)
 
+DROP TABLE itest_parted;
+-- changing a regular column to identity column in a partitioned table
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 int) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into itest_parted VALUES ('2016-07-2', 'from itest_parted', 1);
+INSERT into itest_p1 VALUES ('2016-07-3', 'from itest_p1', 2);
+ALTER TABLE ONLY itest_parted -- ONLY causes the command to fail
+            ALTER COLUMN f3 SET NOT NULL, -- avoids error
+            ALTER COLUMN f3 ADD generated always as identity (start with 3);
+ERROR:  constraint must be added to child tables too
+HINT:  Do not specify the ONLY keyword.
+ALTER TABLE itest_parted
+            ALTER COLUMN f3 SET NOT NULL, -- avoids error
+            ALTER COLUMN f3 ADD generated always as identity (start with 3);
+SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
+    FROM pg_attribute
+    WHERE attrelid IN (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+        AND attnum > 0
+    ORDER BY attrelid, attnum;
+   attrelid   | attname | attidentity | atthasdef | attgenerated | attnotnull 
+--------------+---------+-------------+-----------+--------------+------------
+ itest_parted | f1      |             | f         |              | t
+ itest_parted | f2      |             | f         |              | f
+ itest_parted | f3      | a           | f         |              | t
+ itest_p1     | f1      |             | f         |              | t
+ itest_p1     | f2      |             | f         |              | f
+ itest_p1     | f3      | a           | f         |              | t
+(6 rows)
+
+SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
+    FROM pg_constraint
+    WHERE conrelid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+    ORDER BY conrelid, conname;
+   conrelid   |         conname          | connoinherit | conislocal | coninhcount 
+--------------+--------------------------+--------------+------------+-------------
+ itest_parted | itest_parted_f1_not_null | f            | t          |           0
+ itest_parted | itest_parted_f3_not_null | f            | t          |           0
+ itest_p1     | itest_parted_f1_not_null | f            | f          |           1
+ itest_p1     | itest_parted_f3_not_null | f            | f          |           1
+(4 rows)
+
+SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype
+    FROM v_pg_depend
+    WHERE (refobjid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+            OR objid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p')))
+            AND objid_name NOT LIKE 'pg_toast%'
+    ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
+    classid    |           objid_name            | objsubid |  refclassid  | refobjid_name | refobjsubid |   deptype    
+---------------+---------------------------------+----------+--------------+---------------+-------------+--------------
+ pg_type       | itest_p1                        |        0 | pg_class     | itest_p1      |           0 | internal (i)
+ pg_type       | itest_parted                    |        0 | pg_class     | itest_parted  |           0 | internal (i)
+ pg_class      | itest_p1                        |        0 | pg_class     | itest_parted  |           0 | auto (a)
+ pg_class      | itest_p1                        |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_parted                    |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_parted                    |        1 | pg_class     | itest_parted  |           0 | internal (i)
+ pg_class      | itest_parted_f3_seq             |        0 | pg_class     | itest_parted  |           3 | internal (i)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_p1      |           1 | auto (a)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_parted  |           1 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_p1      |           3 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_parted  |           3 | auto (a)
+(11 rows)
+
+SELECT seqrelid::regclass, seqtypid::regtype FROM pg_sequence;
+      seqrelid       | seqtypid 
+---------------------+----------
+ itest_parted_f3_seq | integer
+(1 row)
+
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-4', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-5', 'from itest_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3 
+----------+------------+-------------------+----
+ itest_p1 | 07-02-2016 | from itest_parted |  1
+ itest_p1 | 07-03-2016 | from itest_p1     |  2
+ itest_p1 | 07-04-2016 | from itest_parted |  3
+ itest_p1 | 07-05-2016 | from itest_p1     |  4
+(4 rows)
+
+--extra tests - see if those need to be included in the main regression test
+CREATE TABLE itest_p3 PARTITION OF itest_parted FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
+CREATE TABLE itest_p4 (f1 date NOT NULL, f2 text, f3 int);
+-- TODO: ideally setting an identity column should automatically spawn the NOT NULL constraint. But it doesn't do that right now.
+ALTER TABLE itest_p4 ALTER COLUMN f3 SET NOT NULL;
+ALTER TABLE itest_parted ATTACH PARTITION itest_p4 FOR VALUES FROM ('2016-10-01') TO ('2016-11-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-09-2', 'from itest_parted');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-10-2', 'from itest_parted');
+INSERT into itest_p3 (f1, f2) VALUES ('2016-09-3', 'from itest_p3');
+INSERT INTO itest_p4 (f1, f2) VALUES ('2016-10-3', 'from itest_p4');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3 
+----------+------------+-------------------+----
+ itest_p1 | 07-02-2016 | from itest_parted |  1
+ itest_p1 | 07-03-2016 | from itest_p1     |  2
+ itest_p1 | 07-04-2016 | from itest_parted |  3
+ itest_p1 | 07-05-2016 | from itest_p1     |  4
+ itest_p3 | 09-02-2016 | from itest_parted |  5
+ itest_p3 | 09-03-2016 | from itest_p3     |  7
+ itest_p4 | 10-02-2016 | from itest_parted |  6
+ itest_p4 | 10-03-2016 | from itest_p4     |  8
+(8 rows)
+
 DROP TABLE itest_parted;
 -- scenarios to test
--- changing a normal column to identity column is reflected in all the partitions - 8th Dec
--- detaching a partition removes identity property - 11th Dec
--- attaching table with identity column is not allowed (even when the parent does not have an identity column) - 12th Dec
+-- detaching a partition removes identity property - 12th Dec
+-- attaching table with identity column is not allowed (even when the parent does not have an identity column) - 13th Dec
 -- trying to drop inherited identity of column of partition is not allowed - 13th Dec
 -- trying to change the identity properties of partition? Is that allowed? - 13th Dec
 -- changing NOT NULL attribute of inherited identity column of partition is not allowed
diff --git a/src/test/regress/sql/identity_part_extra.sql b/src/test/regress/sql/identity_part_extra.sql
index 33c5a30a94..dc8ba47617 100644
--- a/src/test/regress/sql/identity_part_extra.sql
+++ b/src/test/regress/sql/identity_part_extra.sql
@@ -112,13 +112,60 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
 
 DROP TABLE itest_parted;
 
+-- changing a regular column to identity column in a partitioned table
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 int) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into itest_parted VALUES ('2016-07-2', 'from itest_parted', 1);
+INSERT into itest_p1 VALUES ('2016-07-3', 'from itest_p1', 2);
+ALTER TABLE ONLY itest_parted -- ONLY causes the command to fail
+            ALTER COLUMN f3 SET NOT NULL, -- avoids error
+            ALTER COLUMN f3 ADD generated always as identity (start with 3);
+ALTER TABLE itest_parted
+            ALTER COLUMN f3 SET NOT NULL, -- avoids error
+            ALTER COLUMN f3 ADD generated always as identity (start with 3);
+SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
+    FROM pg_attribute
+    WHERE attrelid IN (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+        AND attnum > 0
+    ORDER BY attrelid, attnum;
+SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
+    FROM pg_constraint
+    WHERE conrelid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+    ORDER BY conrelid, conname;
+SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype
+    FROM v_pg_depend
+    WHERE (refobjid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+            OR objid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p')))
+            AND objid_name NOT LIKE 'pg_toast%'
+    ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
+SELECT seqrelid::regclass, seqtypid::regtype FROM pg_sequence;
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-4', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-5', 'from itest_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+--extra tests - see if those need to be included in the main regression test
+CREATE TABLE itest_p3 PARTITION OF itest_parted FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
+CREATE TABLE itest_p4 (f1 date NOT NULL, f2 text, f3 int);
+-- TODO: ideally setting an identity column should automatically spawn the NOT NULL constraint. But it doesn't do that right now.
+ALTER TABLE itest_p4 ALTER COLUMN f3 SET NOT NULL;
+ALTER TABLE itest_parted ATTACH PARTITION itest_p4 FOR VALUES FROM ('2016-10-01') TO ('2016-11-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-09-2', 'from itest_parted');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-10-2', 'from itest_parted');
+INSERT into itest_p3 (f1, f2) VALUES ('2016-09-3', 'from itest_p3');
+INSERT INTO itest_p4 (f1, f2) VALUES ('2016-10-3', 'from itest_p4');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+
+DROP TABLE itest_parted;
+
 -- scenarios to test
 
--- changing a normal column to identity column is reflected in all the partitions - 8th Dec
 
--- detaching a partition removes identity property - 11th Dec
+-- detaching a partition removes identity property - 12th Dec
 
--- attaching table with identity column is not allowed (even when the parent does not have an identity column) - 12th Dec
+-- attaching table with identity column is not allowed (even when the parent does not have an identity column) - 13th Dec
 
 -- trying to drop inherited identity of column of partition is not allowed - 13th Dec
 -- trying to change the identity properties of partition? Is that allowed? - 13th Dec
-- 
2.25.1



  [application/x-patch] 0006-Support-adding-indentity-column-to-a-partit-20231219.patch (5.6K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/11-0006-Support-adding-indentity-column-to-a-partit-20231219.patch)
  download | inline diff:
From c8256786b5db282f5ad0cb20b8b75dbf399da35f Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Thu, 7 Dec 2023 11:20:07 +0530
Subject: [PATCH 06/14] Support adding indentity column to a partitioned table

All partitions in a partitioned table share the same identity space for an
identity column. Adding an identity column to a partitioned table just means
propagating the identity property along with the column down the partitioning
hierarchy. All the partitions share the same underlying sequence.

As for the implementation, we need to just disable the code prohibiting this
case for partitioned tables. The column definition is transformed only once.
The statements related to identity sequence are executed only once before
executing any subcommands. Thus only one sequence, associated with the identity
column, is created. The transformed column definition and related commands are
copied as they are for all the partitions. Hence default expressions of all the
partition use the same sequence when rewriting the table. Also the NOT NULL
constraints required by the identity column are propagated.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       |  7 +++++--
 src/test/regress/expected/identity.out | 24 ++++++++++++++++++++++++
 src/test/regress/sql/identity.sql      | 16 ++++++++++++++++
 3 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index b298747e13..b471cb4d6b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7087,11 +7087,14 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	}
 
 	/*
-	 * Cannot add identity column if table has children, because identity does
-	 * not inherit.  (Adding column and identity separately will work.)
+	 * Regular inheritance children do not inherit the identity hence prohibit
+	 * adding identity column if the table has inheritance children. But all
+	 * the partitions in a partition hierarchy share identity space so
+	 * propagate it down the partitioning hierarchy.
 	 */
 	if (colDef->identity &&
 		recurse &&
+		rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
 		find_inheritance_children(myrelid, NoLock) != NIL)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 6b9edb22c1..9984f7ed19 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -571,6 +571,30 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
  itest_p2 | 08-04-2016 | from itest_parted |   4
 (5 rows)
 
+DROP TABLE itest_parted;
+-- adding an identity column to a partitioned table adds it to all the
+-- partitions
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+CREATE TABLE itest_p2 PARTITION OF itest_parted FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-2', 'from itest_parted');
+ALTER TABLE itest_parted ADD COLUMN f3 int generated always as identity;
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
+INSERT INTO itest_p2 (f1, f2) VALUES ('2016-08-3', 'from itest_p2');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-4', 'from itest_parted');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-4', 'from itest_parted');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3 
+----------+------------+-------------------+----
+ itest_p1 | 07-02-2016 | from itest_parted |  1
+ itest_p1 | 07-03-2016 | from itest_p1     |  3
+ itest_p1 | 07-04-2016 | from itest_parted |  5
+ itest_p2 | 08-02-2016 | from itest_parted |  2
+ itest_p2 | 08-03-2016 | from itest_p2     |  4
+ itest_p2 | 08-04-2016 | from itest_parted |  6
+(6 rows)
+
 DROP TABLE itest_parted;
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index a9babd1baa..f23ccd2a86 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -353,6 +353,22 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
 
 DROP TABLE itest_parted;
 
+-- adding an identity column to a partitioned table adds it to all the
+-- partitions
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+CREATE TABLE itest_p2 PARTITION OF itest_parted FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-2', 'from itest_parted');
+ALTER TABLE itest_parted ADD COLUMN f3 int generated always as identity;
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
+INSERT INTO itest_p2 (f1, f2) VALUES ('2016-08-3', 'from itest_p2');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-4', 'from itest_parted');
+INSERT INTO itest_parted (f1, f2) VALUES ('2016-08-4', 'from itest_parted');
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+
+DROP TABLE itest_parted;
+
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
-- 
2.25.1



  [application/x-patch] 0013-Drop-identity-property-when-detaching-parti-20231219.patch (5.7K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/12-0013-Drop-identity-property-when-detaching-parti-20231219.patch)
  download | inline diff:
From 75df6d97a950859285cff2438fde882dfee43470 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Tue, 19 Dec 2023 11:38:48 +0530
Subject: [PATCH 13/14] Drop identity property when detaching partition

A partition's identity columns share the identity space and have same
identity property as the corresponding columns of the partitioned table.
If a partition is detached it can longer share the identity space as
before. Hence the identity columns of the partition being detached loose
their identity property. When identity of a column of a regular table is
dropped it retains the NOT NULL constarint that came with the identity
property. Similarly the columns of the partition being detached retain
the NOT NULL constraints that came with identity property, even though
the identity property itself is lost. Also note that the sequence
associated with the identity property is linked to the partitioned table
(and not the partition being detached). That sequence is not dropped as
part of detach operation.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       | 13 ++++++++++++
 src/test/regress/expected/identity.out | 28 ++++++++++++++++++++++++++
 src/test/regress/sql/identity.sql      | 16 +++++++++++++++
 3 files changed, 57 insertions(+)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 79af2f4e85..fc90db9c82 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19442,6 +19442,7 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
 	HeapTuple	tuple,
 				newtuple;
 	Relation	trigrel = NULL;
+	int			attno;
 
 	if (concurrent)
 	{
@@ -19607,6 +19608,18 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
 	heap_freetuple(newtuple);
 	table_close(classRel, RowExclusiveLock);
 
+	/*
+	 * Drop identity property from all identity columns of partition.
+	 */
+	for (attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
+	{
+		Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
+
+		if (!attr->attisdropped && attr->attidentity)
+			ATExecDropIdentity(partRel, NameStr(attr->attname), false,
+							   AccessExclusiveLock, true, true);
+	}
+
 	if (OidIsValid(defaultPartOid))
 	{
 		/*
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index cb783a3f6c..4f91af0ca0 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -647,6 +647,34 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
 (4 rows)
 
 DROP TABLE itest_parted;
+-- detaching a partition removes identity property
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 int generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+CREATE TABLE itest_p2 PARTITION OF itest_parted FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT into itest_parted VALUES ('2016-07-2', 'from itest_parted');
+INSERT into itest_parted VALUES ('2016-08-2', 'from itest_parted');
+ALTER TABLE itest_parted DETACH PARTITION itest_p1;
+INSERT into itest_parted(f1, f2) VALUES ('2016-08-4', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-5', 'from itest_p1'); -- error
+ERROR:  null value in column "f3" of relation "itest_p1" violates not-null constraint
+DETAIL:  Failing row contains (07-05-2016, from itest_p1, null).
+INSERT into itest_p1 (f1, f2, f3) VALUES ('2016-07-5', 'from itest_p1', 100);
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3 
+----------+------------+-------------------+----
+ itest_p2 | 08-02-2016 | from itest_parted |  2
+ itest_p2 | 08-04-2016 | from itest_parted |  3
+(2 rows)
+
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_p1;
+ tableoid |     f1     |        f2         | f3  
+----------+------------+-------------------+-----
+ itest_p1 | 07-02-2016 | from itest_parted |   1
+ itest_p1 | 07-05-2016 | from itest_p1     | 100
+(2 rows)
+
+DROP TABLE itest_parted;
+DROP TABLE itest_p1;
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index fa823c2a30..bcbba24032 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -401,6 +401,22 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
 
 DROP TABLE itest_parted;
 
+-- detaching a partition removes identity property
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 int generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+CREATE TABLE itest_p2 PARTITION OF itest_parted FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT into itest_parted VALUES ('2016-07-2', 'from itest_parted');
+INSERT into itest_parted VALUES ('2016-08-2', 'from itest_parted');
+ALTER TABLE itest_parted DETACH PARTITION itest_p1;
+INSERT into itest_parted(f1, f2) VALUES ('2016-08-4', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-5', 'from itest_p1'); -- error
+INSERT into itest_p1 (f1, f2, f3) VALUES ('2016-07-5', 'from itest_p1', 100);
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_p1;
+
+DROP TABLE itest_parted;
+DROP TABLE itest_p1;
+
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
-- 
2.25.1



  [application/x-patch] 0014-Extra-tests-for-Detach-partition-20231219.patch (10.7K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/13-0014-Extra-tests-for-Detach-partition-20231219.patch)
  download | inline diff:
From 5a291187eec6ec066dc080ae979dd916d8395c73 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Tue, 19 Dec 2023 11:33:21 +0530
Subject: [PATCH 14/14] Extra tests for Detach partition

Not for final commit
---
 .../regress/expected/identity_part_extra.out  | 96 ++++++++++++++++++-
 src/test/regress/sql/identity_part_extra.sql  | 44 ++++++++-
 2 files changed, 134 insertions(+), 6 deletions(-)

diff --git a/src/test/regress/expected/identity_part_extra.out b/src/test/regress/expected/identity_part_extra.out
index d4174fd8c0..cbff2cf137 100644
--- a/src/test/regress/expected/identity_part_extra.out
+++ b/src/test/regress/expected/identity_part_extra.out
@@ -509,8 +509,102 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
 (4 rows)
 
 DROP TABLE itest_parted;
+-- detaching a partition removes identity property
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 int generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+CREATE TABLE itest_p2 PARTITION OF itest_parted FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT into itest_parted VALUES ('2016-07-2', 'from itest_parted');
+INSERT into itest_parted VALUES ('2016-08-2', 'from itest_parted');
+ALTER TABLE itest_parted DETACH PARTITION itest_p1;
+SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
+    FROM pg_attribute
+    WHERE attrelid IN (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+        AND attnum > 0
+    ORDER BY attrelid, attnum;
+   attrelid   | attname | attidentity | atthasdef | attgenerated | attnotnull 
+--------------+---------+-------------+-----------+--------------+------------
+ itest_parted | f1      |             | f         |              | t
+ itest_parted | f2      |             | f         |              | f
+ itest_parted | f3      | a           | f         |              | t
+ itest_p1     | f1      |             | f         |              | t
+ itest_p1     | f2      |             | f         |              | f
+ itest_p1     | f3      |             | f         |              | t
+ itest_p2     | f1      |             | f         |              | t
+ itest_p2     | f2      |             | f         |              | f
+ itest_p2     | f3      | a           | f         |              | t
+(9 rows)
+
+SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
+    FROM pg_constraint
+    WHERE conrelid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+    ORDER BY conrelid, conname;
+   conrelid   |         conname          | connoinherit | conislocal | coninhcount 
+--------------+--------------------------+--------------+------------+-------------
+ itest_parted | itest_parted_f1_not_null | f            | t          |           0
+ itest_parted | itest_parted_f3_not_null | f            | t          |           0
+ itest_p1     | itest_parted_f1_not_null | f            | t          |           0
+ itest_p1     | itest_parted_f3_not_null | f            | t          |           0
+ itest_p2     | itest_parted_f1_not_null | f            | f          |           1
+ itest_p2     | itest_parted_f3_not_null | f            | f          |           1
+(6 rows)
+
+SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype
+    FROM v_pg_depend
+    WHERE (refobjid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+            OR objid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p')))
+            AND objid_name NOT LIKE 'pg_toast%'
+    ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
+    classid    |           objid_name            | objsubid |  refclassid  | refobjid_name | refobjsubid |   deptype    
+---------------+---------------------------------+----------+--------------+---------------+-------------+--------------
+ pg_type       | itest_p1                        |        0 | pg_class     | itest_p1      |           0 | internal (i)
+ pg_type       | itest_p2                        |        0 | pg_class     | itest_p2      |           0 | internal (i)
+ pg_type       | itest_parted                    |        0 | pg_class     | itest_parted  |           0 | internal (i)
+ pg_class      | itest_p1                        |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_p2                        |        0 | pg_class     | itest_parted  |           0 | auto (a)
+ pg_class      | itest_p2                        |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_parted                    |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_parted                    |        1 | pg_class     | itest_parted  |           0 | internal (i)
+ pg_class      | itest_parted_f3_seq             |        0 | pg_class     | itest_parted  |           3 | internal (i)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_p1      |           1 | auto (a)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_p2      |           1 | auto (a)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_parted  |           1 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_p1      |           3 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_p2      |           3 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_parted  |           3 | auto (a)
+(15 rows)
+
+SELECT seqrelid::regclass, seqtypid::regtype FROM pg_sequence;
+      seqrelid       | seqtypid 
+---------------------+----------
+ itest_parted_f3_seq | integer
+(1 row)
+
+INSERT into itest_parted(f1, f2) VALUES ('2016-08-4', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-5', 'from itest_p1'); -- error
+ERROR:  null value in column "f3" of relation "itest_p1" violates not-null constraint
+DETAIL:  Failing row contains (07-05-2016, from itest_p1, null).
+INSERT into itest_p1 (f1, f2, f3) VALUES ('2016-07-5', 'from itest_p1', 100);
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3 
+----------+------------+-------------------+----
+ itest_p2 | 08-02-2016 | from itest_parted |  2
+ itest_p2 | 08-04-2016 | from itest_parted |  3
+(2 rows)
+
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_p1;
+ tableoid |     f1     |        f2         | f3  
+----------+------------+-------------------+-----
+ itest_p1 | 07-02-2016 | from itest_parted |   1
+ itest_p1 | 07-05-2016 | from itest_p1     | 100
+(2 rows)
+
+DROP TABLE itest_parted;
+DROP TABLE itest_p1;
 -- scenarios to test
--- detaching a partition removes identity property - 12th Dec
 -- attaching table with identity column is not allowed (even when the parent does not have an identity column) - 13th Dec
 -- trying to drop inherited identity of column of partition is not allowed - 13th Dec
 -- trying to change the identity properties of partition? Is that allowed? - 13th Dec
diff --git a/src/test/regress/sql/identity_part_extra.sql b/src/test/regress/sql/identity_part_extra.sql
index 7e7b1f10ca..b151bfb9ab 100644
--- a/src/test/regress/sql/identity_part_extra.sql
+++ b/src/test/regress/sql/identity_part_extra.sql
@@ -195,15 +195,49 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
 
 DROP TABLE itest_parted;
 
--- scenarios to test
+-- detaching a partition removes identity property
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 int generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+CREATE TABLE itest_p2 PARTITION OF itest_parted FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT into itest_parted VALUES ('2016-07-2', 'from itest_parted');
+INSERT into itest_parted VALUES ('2016-08-2', 'from itest_parted');
+ALTER TABLE itest_parted DETACH PARTITION itest_p1;
+SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
+    FROM pg_attribute
+    WHERE attrelid IN (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+        AND attnum > 0
+    ORDER BY attrelid, attnum;
+SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
+    FROM pg_constraint
+    WHERE conrelid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+    ORDER BY conrelid, conname;
+SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype
+    FROM v_pg_depend
+    WHERE (refobjid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+            OR objid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p')))
+            AND objid_name NOT LIKE 'pg_toast%'
+    ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
+SELECT seqrelid::regclass, seqtypid::regtype FROM pg_sequence;
+INSERT into itest_parted(f1, f2) VALUES ('2016-08-4', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-5', 'from itest_p1'); -- error
+INSERT into itest_p1 (f1, f2, f3) VALUES ('2016-07-5', 'from itest_p1', 100);
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_p1;
 
+DROP TABLE itest_parted;
+DROP TABLE itest_p1;
+
+-- scenarios to test
 
--- detaching a partition removes identity property - 12th Dec
+-- attaching table with identity column is not allowed (even when the parent does not have an identity column)
 
--- attaching table with identity column is not allowed (even when the parent does not have an identity column) - 13th Dec
+-- trying to drop inherited identity of column of partition is not allowed
 
--- trying to drop inherited identity of column of partition is not allowed - 13th Dec
--- trying to change the identity properties of partition? Is that allowed? - 13th Dec
+-- trying to change the identity properties of partition? Is that allowed?
 
 -- changing NOT NULL attribute of inherited identity column of partition is not allowed
 
-- 
2.25.1



  [application/x-patch] 0011-DROP-IDENTITY-on-partitioned-table-recurses-20231219.patch (9.3K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/14-0011-DROP-IDENTITY-on-partitioned-table-recurses-20231219.patch)
  download | inline diff:
From d9f7d81cf7137cb978e07d161ca0d01ebbfafca4 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Mon, 18 Dec 2023 16:39:30 +0530
Subject: [PATCH 11/14] DROP IDENTITY on partitioned table recurses to its
 partitions

Since identity property is inherited by partition tables, dropping
identity of a column in partitioned table should result in dropping it
from all the partitions.

I tried to implement the DROP SEQUENCE as a separate step to be executed
at the end of ALTER TABLE command. But that does not work since the
sequence has a dependency on the column (whose identity is being
dropped) and not on the identity property itself. There is no step/ALTER
TABLE command to drop that dependecy.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       | 68 +++++++++++++++++++++-----
 src/test/regress/expected/identity.out | 26 ++++++++++
 src/test/regress/sql/identity.sql      | 15 ++++++
 3 files changed, 96 insertions(+), 13 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cf521a65df..79af2f4e85 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -456,7 +456,9 @@ static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
 									   bool recurse, bool recursing);
 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
 									   Node *def, LOCKMODE lockmode);
-static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
+static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName,
+										bool missing_ok, LOCKMODE lockmode,
+										bool recurse, bool recursing);
 static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
 static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
 static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
@@ -4837,7 +4839,9 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			break;
 		case AT_DropIdentity:
 			ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
-			/* This command never recurses */
+			/* Set up recursion for phase 2; no other prep needed */
+			if (recurse)
+				cmd->recurse = true;
 			pass = AT_PASS_DROP;
 			break;
 		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
@@ -5235,7 +5239,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
 			break;
 		case AT_DropIdentity:
-			address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
+			address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok,
+										 lockmode, cmd->recurse, false);
 			break;
 		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
 			address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
@@ -8287,7 +8292,9 @@ ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmod
  * Return the address of the affected column.
  */
 static ObjectAddress
-ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
+ATExecDropIdentity(Relation rel, const char *colName,
+				   bool missing_ok, LOCKMODE lockmode,
+				   bool recurse, bool recursing)
 {
 	HeapTuple	tuple;
 	Form_pg_attribute attTup;
@@ -8296,6 +8303,14 @@ ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE
 	ObjectAddress address;
 	Oid			seqid;
 	ObjectAddress seqaddress;
+	bool		ispartitioned;
+
+	ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
+	if (ispartitioned && !recurse)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+				 errmsg("identity can only be dropped from column \"%s\" across all the partitions of the relation \"%s\"",
+						colName, RelationGetRelationName(rel))));
 
 	attrelation = table_open(AttributeRelationId, RowExclusiveLock);
 	tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
@@ -8344,15 +8359,42 @@ ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE
 
 	table_close(attrelation, RowExclusiveLock);
 
-	/* drop the internal sequence */
-	seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
-	deleteDependencyRecordsForClass(RelationRelationId, seqid,
-									RelationRelationId, DEPENDENCY_INTERNAL);
-	CommandCounterIncrement();
-	seqaddress.classId = RelationRelationId;
-	seqaddress.objectId = seqid;
-	seqaddress.objectSubId = 0;
-	performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+	/*
+	 * Recurse to drop the identity from column in partitions. Identity is not
+	 * inherited in regular inheritance children so ignore them.
+	 */
+	if (recurse && ispartitioned)
+	{
+		List	   *children;
+		ListCell   *lc;
+
+		children = find_inheritance_children(RelationGetRelid(rel),
+											 lockmode);
+
+		foreach(lc, children)
+		{
+			Relation	childrel;
+
+			childrel = table_open(lfirst_oid(lc), NoLock);
+			ATExecDropIdentity(childrel, colName, false, lockmode, recurse,
+							   true);
+			table_close(childrel, NoLock);
+		}
+	}
+
+	if (!recursing)
+	{
+		/* drop the internal sequence */
+		seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
+		deleteDependencyRecordsForClass(RelationRelationId, seqid,
+										RelationRelationId,
+										DEPENDENCY_INTERNAL);
+		CommandCounterIncrement();
+		seqaddress.classId = RelationRelationId;
+		seqaddress.objectId = seqid;
+		seqaddress.objectSubId = 0;
+		performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+	}
 
 	return address;
 }
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 96659d4399..cb783a3f6c 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -620,6 +620,32 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
  itest_p1 | 07-05-2016 | from itest_p1     |  4
 (4 rows)
 
+DROP TABLE itest_parted;
+-- changing an identity column to a non-identity column in a partitioned table
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
+ALTER TABLE ONLY itest_parted ALTER COLUMN f3 DROP IDENTITY; -- fails
+ERROR:  identity can only be dropped from column "f3" across all the partitions of the relation "itest_parted"
+ALTER TABLE itest_parted ALTER COLUMN f3 DROP IDENTITY;
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-4', 'from itest_parted'); -- fails
+ERROR:  null value in column "f3" of relation "itest_p1" violates not-null constraint
+DETAIL:  Failing row contains (07-04-2016, from itest_parted, null).
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-5', 'from itest_p1'); -- fails
+ERROR:  null value in column "f3" of relation "itest_p1" violates not-null constraint
+DETAIL:  Failing row contains (07-05-2016, from itest_p1, null).
+INSERT into itest_parted(f1, f2, f3) VALUES ('2016-07-4', 'from itest_parted', 3);
+INSERT into itest_p1 (f1, f2, f3) VALUES ('2016-07-5', 'from itest_p1', 4);
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3 
+----------+------------+-------------------+----
+ itest_p1 | 07-02-2016 | from itest_parted |  1
+ itest_p1 | 07-03-2016 | from itest_p1     |  2
+ itest_p1 | 07-04-2016 | from itest_parted |  3
+ itest_p1 | 07-05-2016 | from itest_p1     |  4
+(4 rows)
+
 DROP TABLE itest_parted;
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index 31615914f5..fa823c2a30 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -386,6 +386,21 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
 
 DROP TABLE itest_parted;
 
+-- changing an identity column to a non-identity column in a partitioned table
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
+ALTER TABLE ONLY itest_parted ALTER COLUMN f3 DROP IDENTITY; -- fails
+ALTER TABLE itest_parted ALTER COLUMN f3 DROP IDENTITY;
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-4', 'from itest_parted'); -- fails
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-5', 'from itest_p1'); -- fails
+INSERT into itest_parted(f1, f2, f3) VALUES ('2016-07-4', 'from itest_parted', 3);
+INSERT into itest_p1 (f1, f2, f3) VALUES ('2016-07-5', 'from itest_p1', 4);
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+
+DROP TABLE itest_parted;
+
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
-- 
2.25.1



  [application/x-patch] 0012-Extra-tests-for-drop-idenity-20231219.patch (9.6K, ../../CAExHW5vCbATEmht861=G-BFPHNwLUqyeGa_=8-xibJ6Q1UxAeA@mail.gmail.com/15-0012-Extra-tests-for-drop-idenity-20231219.patch)
  download | inline diff:
From 71be0358d120288cdcb19592d664c959c18fdbd3 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Mon, 18 Dec 2023 16:27:52 +0530
Subject: [PATCH 12/14] Extra tests for drop idenity

NOT FOR FINAL COMMIT
---
 .../regress/expected/identity_part_extra.out  | 83 ++++++++++++++++++-
 src/test/regress/sql/identity_part_extra.sql  | 37 ++++++++-
 2 files changed, 117 insertions(+), 3 deletions(-)

diff --git a/src/test/regress/expected/identity_part_extra.out b/src/test/regress/expected/identity_part_extra.out
index 05de0cabab..d4174fd8c0 100644
--- a/src/test/regress/expected/identity_part_extra.out
+++ b/src/test/regress/expected/identity_part_extra.out
@@ -426,6 +426,88 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
  itest_p4 | 10-03-2016 | from itest_p4     |  8
 (8 rows)
 
+DROP TABLE itest_parted;
+-- changing an identity column to a non-identity column in a partitioned table
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
+ALTER TABLE ONLY itest_parted ALTER COLUMN f3 DROP IDENTITY; -- fails
+ERROR:  identity can only be dropped from column "f3" across all the partitions of the relation "itest_parted"
+ALTER TABLE itest_parted ALTER COLUMN f3 DROP IDENTITY;
+SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
+    FROM pg_attribute
+    WHERE attrelid IN (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+        AND attnum > 0
+    ORDER BY attrelid, attnum;
+   attrelid   | attname | attidentity | atthasdef | attgenerated | attnotnull 
+--------------+---------+-------------+-----------+--------------+------------
+ itest_parted | f1      |             | f         |              | t
+ itest_parted | f2      |             | f         |              | f
+ itest_parted | f3      |             | f         |              | t
+ itest_p1     | f1      |             | f         |              | t
+ itest_p1     | f2      |             | f         |              | f
+ itest_p1     | f3      |             | f         |              | t
+(6 rows)
+
+SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
+    FROM pg_constraint
+    WHERE conrelid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+    ORDER BY conrelid, conname;
+   conrelid   |         conname          | connoinherit | conislocal | coninhcount 
+--------------+--------------------------+--------------+------------+-------------
+ itest_parted | itest_parted_f1_not_null | f            | t          |           0
+ itest_parted | itest_parted_f3_not_null | f            | t          |           0
+ itest_p1     | itest_parted_f1_not_null | f            | f          |           1
+ itest_p1     | itest_parted_f3_not_null | f            | f          |           1
+(4 rows)
+
+SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype
+    FROM v_pg_depend
+    WHERE (refobjid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+            OR objid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p')))
+            AND objid_name NOT LIKE 'pg_toast%'
+    ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
+    classid    |           objid_name            | objsubid |  refclassid  | refobjid_name | refobjsubid |   deptype    
+---------------+---------------------------------+----------+--------------+---------------+-------------+--------------
+ pg_type       | itest_p1                        |        0 | pg_class     | itest_p1      |           0 | internal (i)
+ pg_type       | itest_parted                    |        0 | pg_class     | itest_parted  |           0 | internal (i)
+ pg_class      | itest_p1                        |        0 | pg_class     | itest_parted  |           0 | auto (a)
+ pg_class      | itest_p1                        |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_parted                    |        0 | pg_namespace | 2200          |           0 | normal (n)
+ pg_class      | itest_parted                    |        1 | pg_class     | itest_parted  |           0 | internal (i)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_p1      |           1 | auto (a)
+ pg_constraint | public.itest_parted_f1_not_null |        0 | pg_class     | itest_parted  |           1 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_p1      |           3 | auto (a)
+ pg_constraint | public.itest_parted_f3_not_null |        0 | pg_class     | itest_parted  |           3 | auto (a)
+(10 rows)
+
+SELECT seqrelid::regclass, seqtypid::regtype FROM pg_sequence;
+ seqrelid | seqtypid 
+----------+----------
+(0 rows)
+
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-4', 'from itest_parted'); -- fails
+ERROR:  null value in column "f3" of relation "itest_p1" violates not-null constraint
+DETAIL:  Failing row contains (07-04-2016, from itest_parted, null).
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-5', 'from itest_p1'); -- fails
+ERROR:  null value in column "f3" of relation "itest_p1" violates not-null constraint
+DETAIL:  Failing row contains (07-05-2016, from itest_p1, null).
+INSERT into itest_parted(f1, f2, f3) VALUES ('2016-07-4', 'from itest_parted', 3);
+INSERT into itest_p1 (f1, f2, f3) VALUES ('2016-07-5', 'from itest_p1', 4);
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+ tableoid |     f1     |        f2         | f3 
+----------+------------+-------------------+----
+ itest_p1 | 07-02-2016 | from itest_parted |  1
+ itest_p1 | 07-03-2016 | from itest_p1     |  2
+ itest_p1 | 07-04-2016 | from itest_parted |  3
+ itest_p1 | 07-05-2016 | from itest_p1     |  4
+(4 rows)
+
 DROP TABLE itest_parted;
 -- scenarios to test
 -- detaching a partition removes identity property - 12th Dec
@@ -438,7 +520,6 @@ DROP TABLE itest_parted;
 -- TODO: test OVERRIDING SYSTEM VALUE OR OVERRIDING USER VALUE
 -- dropping an identity column of parent drops it from all the partitions
 -- TODO: do we need a test for this?
--- TODO: can we change an identity column to a normal column?
 -- Dump and restore tests - that would be a separate file
 -- pg_upgrade tests
 -- multi-level partition tests
diff --git a/src/test/regress/sql/identity_part_extra.sql b/src/test/regress/sql/identity_part_extra.sql
index dc8ba47617..7e7b1f10ca 100644
--- a/src/test/regress/sql/identity_part_extra.sql
+++ b/src/test/regress/sql/identity_part_extra.sql
@@ -160,6 +160,41 @@ SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
 
 DROP TABLE itest_parted;
 
+-- changing an identity column to a non-identity column in a partitioned table
+CREATE TABLE itest_parted (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE itest_p1 PARTITION OF itest_parted FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-2', 'from itest_parted');
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-3', 'from itest_p1');
+ALTER TABLE ONLY itest_parted ALTER COLUMN f3 DROP IDENTITY; -- fails
+ALTER TABLE itest_parted ALTER COLUMN f3 DROP IDENTITY;
+SELECT attrelid::regclass, attname, attidentity, atthasdef, attgenerated, attnotnull
+    FROM pg_attribute
+    WHERE attrelid IN (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+        AND attnum > 0
+    ORDER BY attrelid, attnum;
+SELECT conrelid::regclass, conname, connoinherit, conislocal, coninhcount
+    FROM pg_constraint
+    WHERE conrelid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+    ORDER BY conrelid, conname;
+SELECT classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype
+    FROM v_pg_depend
+    WHERE (refobjid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p'))
+            OR objid in (SELECT oid FROM pg_class WHERE relname like 'itest_p%'
+                                                    and relkind in ('r', 'p')))
+            AND objid_name NOT LIKE 'pg_toast%'
+    ORDER BY classid, objid_name, objsubid, refclassid, refobjid_name, refobjsubid, deptype;
+SELECT seqrelid::regclass, seqtypid::regtype FROM pg_sequence;
+INSERT into itest_parted(f1, f2) VALUES ('2016-07-4', 'from itest_parted'); -- fails
+INSERT into itest_p1 (f1, f2) VALUES ('2016-07-5', 'from itest_p1'); -- fails
+INSERT into itest_parted(f1, f2, f3) VALUES ('2016-07-4', 'from itest_parted', 3);
+INSERT into itest_p1 (f1, f2, f3) VALUES ('2016-07-5', 'from itest_p1', 4);
+SELECT tableoid::regclass, f1, f2, f3 FROM itest_parted;
+
+DROP TABLE itest_parted;
+
 -- scenarios to test
 
 
@@ -182,8 +217,6 @@ DROP TABLE itest_parted;
 -- dropping an identity column of parent drops it from all the partitions
 -- TODO: do we need a test for this?
 
--- TODO: can we change an identity column to a normal column?
-
 -- Dump and restore tests - that would be a separate file
 
 -- pg_upgrade tests
-- 
2.25.1



^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: partitioning and identity column
@ 2023-12-21 11:02  Peter Eisentraut <[email protected]>
  parent: Ashutosh Bapat <[email protected]>
  0 siblings, 1 reply; 55+ messages in thread

From: Peter Eisentraut @ 2023-12-21 11:02 UTC (permalink / raw)
  To: Ashutosh Bapat <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>

On 19.12.23 11:47, Ashutosh Bapat wrote:
> At this point I am looking for opinions on the above rules and whether
> the implementation is on the right track.

This looks on the right track to me.

> 0001 - change to get_partition_ancestors() prologue. Can be reviewed
> and committed independent of other patches.

I committed that.

> 0004 - An attached partition inherits identity property and uses the
> underlying sequence for direct INSERTs. When inheriting the identity
> property it should also inherit the NOT NULL constraint, but that's a
> TODO in this patch. We expect matching NOT NULL constraints to be
> present in the partition being attached. I am not sure whether we want
> to add NOT NULL constraints automatically for an identity column. We
> require a NOT NULL constraint to be present when adding identity
> property to a column. The behavior in the patch seems to be consistent
> with this.

I think it makes sense that the NOT NULL constraint must be added 
manually before attaching  is allowed.







^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* Re: partitioning and identity column
@ 2024-01-09 14:10  Ashutosh Bapat <[email protected]>
  parent: Peter Eisentraut <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: Ashutosh Bapat @ 2024-01-09 14:10 UTC (permalink / raw)
  To: Peter Eisentraut <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>

On Thu, Dec 21, 2023 at 4:32 PM Peter Eisentraut <[email protected]> wrote:
>
> On 19.12.23 11:47, Ashutosh Bapat wrote:
> > At this point I am looking for opinions on the above rules and whether
> > the implementation is on the right track.
>
> This looks on the right track to me.

Thanks.

>
> > 0001 - change to get_partition_ancestors() prologue. Can be reviewed
> > and committed independent of other patches.
>
> I committed that.

Thanks.

>
> > 0004 - An attached partition inherits identity property and uses the
> > underlying sequence for direct INSERTs. When inheriting the identity
> > property it should also inherit the NOT NULL constraint, but that's a
> > TODO in this patch. We expect matching NOT NULL constraints to be
> > present in the partition being attached. I am not sure whether we want
> > to add NOT NULL constraints automatically for an identity column. We
> > require a NOT NULL constraint to be present when adding identity
> > property to a column. The behavior in the patch seems to be consistent
> > with this.
>
> I think it makes sense that the NOT NULL constraint must be added
> manually before attaching  is allowed.
>
Ok. I have modified the test case to add NOT NULL constraint.

Here's complete patch-set.
0001 - fixes unrelated documentation style - can be committed
independently OR ignored
0002 - adds an Assert in related code - can be independently committed

On Mon, Nov 13, 2023 at 3:51 PM Peter Eisentraut <[email protected]> wrote:
> Note, here is a writeup about the behavior of generated columns with
> partitioning:
> https://www.postgresql.org/docs/devel/ddl-generated-columns.html.  It
> would be useful if we documented the behavior of identity columns
> similarly.  (I'm not saying the behavior has to match.)
0003 - addresses this request

0004 - 0011 - each patch contains code changes and SQL testing those
changes for ease of review. Each patch has commit message that
describes the changes and rationale, if any, behind those changes.
0012 - test changes
0013 - expected output change because of code changes
All these patches should be committed as a single commit finally.
Please let me know when I can squash those all together. We may commit
0003 separately or along with 0004-0013.

0014 and 0015 - pg_dump/restore and pg_upgrade tests. But these
patches are not expected to be committed for the reasons explained in
the commit message. Since identity columns of a partitioned table are
not marked as such in partitions in the older version, I tested their
upgrade from PG 14 through the changes in 0015. pg_dumpall_14.out
contains the dump file from PG 14 I used for this testing.

-- 
Best Wishes,
Ashutosh Bapat


Attachments:

  [text/x-patch] 0003-Add-Identity-Column-section-under-Data-Defi-20240109.patch (3.0K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/2-0003-Add-Identity-Column-section-under-Data-Defi-20240109.patch)
  download | inline diff:
From fee416ae2141019b7370a2ae31fc3d927d639977 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Tue, 9 Jan 2024 15:42:03 +0530
Subject: [PATCH 03/27] Add Identity Column section under Data Definition
 chapter

This seems to be missing since identity column support was added. Add
the same.

Ashutosh Bapat
---
 doc/src/sgml/ddl.sgml | 56 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index bb6ce9291e..b33c35e141 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -404,6 +404,62 @@ CREATE TABLE people (
   </para>
  </sect1>
 
+ <sect1 id="ddl-identity-columns">
+  <title>Identity Columns</title>
+
+  <indexterm zone="ddl-identity-columns">
+   <primary>identity column</primary>
+  </indexterm>
+
+  <para>
+  An identity column is a special column that is generated automatically. It
+  can be used to generate key values. In <productname>PostgreSQL</productname>
+  each identity column has an associated sequence from which it derives its
+  values. Such a column is implicitly NOT NULL. An identity column, however,
+  does not guarantee uniqueness. Uniqueness must be enforced using a
+  <literal>PRIMARY KEY</literal> or <literal>UNIQUE</literal> constraint or a
+  <literal>UNIQUE</literal> index.
+  </para>
+
+  <para>
+   To create an identity column, use the <literal>GENERATED ...
+   AS IDENTITY</literal> clause in <command>CREATE TABLE</command>, for example:
+<programlisting>
+CREATE TABLE people (
+    id bigint <emphasis>GENERATED ALWAYS AS IDENTITY</emphasis>,
+    ...,
+);
+</programlisting>
+   See <xref linkend="sql-createtable"/> for more details. The data type of an
+   identity column must be one of the data types supported by sequences. See
+   <xref linkend="sql-createsequence"/>. The properties of associated sequence
+   may be specified when creating an identity column (See <xref
+   linkend="sql-createtable"/>) or changed afterwards (See <xref
+   linkend="sql-altertable"/>).
+  </para>
+
+  <para>
+   In <command>INSERT</command> or <command>UPDATE</command> commands, the
+   keyword <literal>DEFAULT</literal> may be used, if necessary, to specify
+   system generated value.
+  </para>
+
+  <para>
+  Identity columns and their properties in a child table are independent of
+  those in its parent tables. A child table does not inherit identity columns
+  or their properties automatically from the parent. During insert or update, a
+  column is treated as an identity column if that column is an identity column
+  in the table named in the query and corresponding identity properties are
+  applied.
+  </para>
+
+  <para>
+  Partitions inherit indentity columns from the partitioned table. They cannot
+  have their own identity columns. The properties of a given identity column
+  are consistent across all the partitions in the partition hierarchy.
+  </para>
+ </sect1>
+
  <sect1 id="ddl-constraints">
   <title>Constraints</title>
 
-- 
2.25.1



  [text/x-patch] 0002-Assert-that-partition-inherits-from-only-on-20240109.patch (1.8K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/3-0002-Assert-that-partition-inherits-from-only-on-20240109.patch)
  download | inline diff:
From 428d997b9f0172d1cdea02a8946843dcf474c759 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Thu, 7 Dec 2023 11:38:07 +0530
Subject: [PATCH 02/27] Assert that partition inherits from only one parent in
 MergeAttributes()

A partition inherits only from one partitioned table and thus inherits
column definition only once. Assert the same in MergeAttributes() and
simplify a condition accordingly.

Similar definition exists about line 3068 in the same function.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6b0a20010e..18046a0d69 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -2712,6 +2712,12 @@ MergeAttributes(List *columns, const List *supers, char relpersistence,
 				int32		deftypmod;
 				Oid			defCollId;
 
+				/*
+				 * Partitions have only one parent and have no column
+				 * definitions of their own, so conflict should never occur.
+				 */
+				Assert(!is_partition);
+
 				/*
 				 * Yes, try to merge the two column definitions.
 				 */
@@ -2783,12 +2789,9 @@ MergeAttributes(List *columns, const List *supers, char relpersistence,
 
 				/*
 				 * In regular inheritance, columns in the parent's primary key
-				 * get an extra not-null constraint.  Partitioning doesn't
-				 * need this, because the PK itself is going to be cloned to
-				 * the partition.
+				 * get an extra not-null constraint.
 				 */
-				if (!is_partition &&
-					bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
+				if (bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
 								  pkattrs))
 				{
 					CookedConstraint *nn;
-- 
2.25.1



  [text/x-patch] 0005-Attached-partition-inherits-identity-column-20240109.patch (8.5K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/4-0005-Attached-partition-inherits-identity-column-20240109.patch)
  download | inline diff:
From 4d2dbe95f3c5267d7ddf7144329c5a98d95842f9 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Wed, 6 Dec 2023 16:08:59 +0530
Subject: [PATCH 05/27] Attached partition inherits identity column

A table being attached as a partition inherits identity property from
the partitioned table. This should be fine since we expect that the
partition table's column has the same type as the partitioned table's
corresponding column. If the table being attached is a partitioned
table, the indentity properties are propagated down its partition
hierarchy.

An Identity column in the partitioned table is also marked as NOT NULL.
The corresponding column in the partition needs to be marked as NOT NULL
for the attach to succeed.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       | 25 ++++++++++++++++++-------
 src/test/regress/expected/identity.out | 23 ++++++++++++++++++-----
 src/test/regress/sql/identity.sql      |  9 +++++++++
 3 files changed, 45 insertions(+), 12 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 06f6301a57..5719df2b76 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -354,7 +354,8 @@ static List *MergeAttributes(List *columns, const List *supers, char relpersiste
 							 bool is_partition, List **supconstr,
 							 List **supnotnulls);
 static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
-static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
+static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel,
+										bool ispartition);
 static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
 static void StoreCatalogInheritance(Oid relationId, List *supers,
 									bool child_is_partition);
@@ -618,7 +619,8 @@ static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partsp
 static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
 								  List **partexprs, Oid *partopclass, Oid *partcollation,
 								  PartitionStrategy strategy);
-static void CreateInheritance(Relation child_rel, Relation parent_rel);
+static void CreateInheritance(Relation child_rel, Relation parent_rel,
+							  bool ispartition);
 static void RemoveInheritance(Relation child_rel, Relation parent_rel,
 							  bool expect_detached);
 static void ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel,
@@ -15647,7 +15649,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
 				 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
 
 	/* OK to create inheritance */
-	CreateInheritance(child_rel, parent_rel);
+	CreateInheritance(child_rel, parent_rel, false);
 
 	/*
 	 * If parent_rel has a primary key, then child_rel has not-null
@@ -15673,7 +15675,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
  * Common to ATExecAddInherit() and ATExecAttachPartition().
  */
 static void
-CreateInheritance(Relation child_rel, Relation parent_rel)
+CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
 {
 	Relation	catalogRelation;
 	SysScanDesc scan;
@@ -15718,7 +15720,7 @@ CreateInheritance(Relation child_rel, Relation parent_rel)
 	systable_endscan(scan);
 
 	/* Match up the columns and bump attinhcount as needed */
-	MergeAttributesIntoExisting(child_rel, parent_rel);
+	MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
 
 	/* Match up the constraints and bump coninhcount as needed */
 	MergeConstraintsIntoExisting(child_rel, parent_rel);
@@ -15796,7 +15798,8 @@ constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
  * the child must be as well. Defaults are not compared, however.
  */
 static void
-MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
+MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel,
+							bool ispartition)
 {
 	Relation	attrrel;
 	TupleDesc	parent_desc;
@@ -15865,6 +15868,14 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
 						(errcode(ERRCODE_DATATYPE_MISMATCH),
 						 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
 
+			/*
+			 * Regular inheritance children are independent enough not to
+			 * inherit identity columns. But partitions are integral part of a
+			 * partitioned table and inherit identity column.
+			 */
+			if (ispartition)
+				child_att->attidentity = parent_att->attidentity;
+
 			/*
 			 * OK, bump the child column's inheritance count.  (If we fail
 			 * later on, this change will just roll back.)
@@ -18696,7 +18707,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
 							  cmd->bound, pstate);
 
 	/* OK to create inheritance.  Rest of the checks performed there */
-	CreateInheritance(attachrel, rel);
+	CreateInheritance(attachrel, rel, true);
 
 	/* Update the pg_class entry. */
 	StorePartitionBound(attachrel, rel, cmd->bound);
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 20803a1d24..222b3684da 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -542,15 +542,28 @@ DROP TYPE itest_type CASCADE;
 -- table partitions
 -- partitions inherit identity column and share sequence
 CREATE TABLE pitest1 (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+-- new partition
 CREATE TABLE pitest1_p1 PARTITION OF pitest1 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
 INSERT into pitest1(f1, f2) VALUES ('2016-07-2', 'from pitest1');
 INSERT into pitest1_p1 (f1, f2) VALUES ('2016-07-3', 'from pitest1_p1');
+-- attached partition
+CREATE TABLE pitest1_p2 (f1 date NOT NULL, f2 text, f3 bigint);
+INSERT INTO pitest1_p2 VALUES ('2016-08-2', 'before attaching', 100);
+ALTER TABLE pitest1 ATTACH PARTITION pitest1_p2 FOR VALUES FROM ('2016-08-01') TO ('2016-09-01'); -- requires NOT NULL constraint
+ERROR:  column "f3" in child table must be marked NOT NULL
+ALTER TABLE pitest1_p2 ALTER COLUMN f3 SET NOT NULL;
+ALTER TABLE pitest1 ATTACH PARTITION pitest1_p2 FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT INTO pitest1_p2 (f1, f2) VALUES ('2016-08-3', 'from pitest1_p2');
+INSERT INTO pitest1 (f1, f2) VALUES ('2016-08-4', 'from pitest1');
 SELECT tableoid::regclass, f1, f2, f3 FROM pitest1;
-  tableoid  |     f1     |       f2        | f3 
-------------+------------+-----------------+----
- pitest1_p1 | 07-02-2016 | from pitest1    |  1
- pitest1_p1 | 07-03-2016 | from pitest1_p1 |  2
-(2 rows)
+  tableoid  |     f1     |        f2        | f3  
+------------+------------+------------------+-----
+ pitest1_p1 | 07-02-2016 | from pitest1     |   1
+ pitest1_p1 | 07-03-2016 | from pitest1_p1  |   2
+ pitest1_p2 | 08-02-2016 | before attaching | 100
+ pitest1_p2 | 08-03-2016 | from pitest1_p2  |   3
+ pitest1_p2 | 08-04-2016 | from pitest1     |   4
+(5 rows)
 
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index 3660fa6a28..be29cbf119 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -335,9 +335,18 @@ DROP TYPE itest_type CASCADE;
 
 -- partitions inherit identity column and share sequence
 CREATE TABLE pitest1 (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+-- new partition
 CREATE TABLE pitest1_p1 PARTITION OF pitest1 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
 INSERT into pitest1(f1, f2) VALUES ('2016-07-2', 'from pitest1');
 INSERT into pitest1_p1 (f1, f2) VALUES ('2016-07-3', 'from pitest1_p1');
+-- attached partition
+CREATE TABLE pitest1_p2 (f1 date NOT NULL, f2 text, f3 bigint);
+INSERT INTO pitest1_p2 VALUES ('2016-08-2', 'before attaching', 100);
+ALTER TABLE pitest1 ATTACH PARTITION pitest1_p2 FOR VALUES FROM ('2016-08-01') TO ('2016-09-01'); -- requires NOT NULL constraint
+ALTER TABLE pitest1_p2 ALTER COLUMN f3 SET NOT NULL;
+ALTER TABLE pitest1 ATTACH PARTITION pitest1_p2 FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT INTO pitest1_p2 (f1, f2) VALUES ('2016-08-3', 'from pitest1_p2');
+INSERT INTO pitest1 (f1, f2) VALUES ('2016-08-4', 'from pitest1');
 SELECT tableoid::regclass, f1, f2, f3 FROM pitest1;
 
 -- partition with identity column of its own is not allowed
-- 
2.25.1



  [text/x-patch] 0004-A-newly-created-partition-inherits-indentit-20240109.patch (5.9K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/5-0004-A-newly-created-partition-inherits-indentit-20240109.patch)
  download | inline diff:
From 18ac85e9467038ec4cce03162f2d91e2efaa6bc4 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Tue, 5 Dec 2023 17:08:11 +0530
Subject: [PATCH 04/27] A newly created partition inherits indentity property

The partitions of a partitioned table are integral part of the
partitioned table. A partition inherits identity columns from the
partitioned table. An indentity column of a partition shares the
identity space with the corresponding column of the partitioned table.
In other words, the same identity column across all partitions of a
partitioned table share the same identity space.  This is effected by
sharing the same underlying sequence.

When INSERTing directly into a partition, sequence associated with the
topmost partitioned table is used to calculate the value of the
corresponding identity column.

In regular inheritance, Identity columns and their properties in a child
table are independent of those in its parent tables. A child table does
not inherit identity columns or their properties automatically from the
parent.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       |  9 +++++++++
 src/backend/rewrite/rewriteHandler.c   | 19 ++++++++++++++++++-
 src/test/regress/expected/identity.out | 15 ++++++++++++++-
 src/test/regress/sql/identity.sql      | 10 +++++++++-
 4 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 18046a0d69..06f6301a57 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -2855,6 +2855,15 @@ MergeAttributes(List *columns, const List *supers, char relpersistence,
 					def->is_not_null = true;
 				def->storage = attribute->attstorage;
 				def->generated = attribute->attgenerated;
+
+				/*
+				 * Regular inheritance children are independent enough not to
+				 * inherit identity columns. But partitions are integral part
+				 * of a partitioned table and inherit identity column.
+				 */
+				if (is_partition)
+					def->identity = attribute->attidentity;
+
 				if (CompressionMethodIsValid(attribute->attcompression))
 					def->compression =
 						pstrdup(GetCompressionMethodName(attribute->attcompression));
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 41a362310a..d5f1993665 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -25,6 +25,7 @@
 #include "access/table.h"
 #include "catalog/dependency.h"
 #include "catalog/pg_type.h"
+#include "catalog/partition.h"
 #include "commands/trigger.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
@@ -1234,8 +1235,24 @@ build_column_default(Relation rel, int attrno)
 	if (att_tup->attidentity)
 	{
 		NextValueExpr *nve = makeNode(NextValueExpr);
+		Oid			reloid;
 
-		nve->seqid = getIdentitySequence(RelationGetRelid(rel), attrno, false);
+		/*
+		 * The identity sequence is associated with the topmost partitioned
+		 * table.
+		 */
+		if (rel->rd_rel->relispartition)
+		{
+			List	   *ancestors =
+				get_partition_ancestors(RelationGetRelid(rel));
+
+			reloid = llast_oid(ancestors);
+			list_free(ancestors);
+		}
+		else
+			reloid = RelationGetRelid(rel);
+
+		nve->seqid = getIdentitySequence(reloid, attrno, false);
 		nve->typeId = att_tup->atttypid;
 
 		return (Node *) nve;
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 7c6e87e8a5..20803a1d24 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -539,7 +539,20 @@ CREATE TYPE itest_type AS (f1 integer, f2 text, f3 bigint);
 CREATE TABLE itest12 OF itest_type (f1 WITH OPTIONS GENERATED ALWAYS AS IDENTITY); -- error
 ERROR:  identity columns are not supported on typed tables
 DROP TYPE itest_type CASCADE;
--- table partitions (currently not supported)
+-- table partitions
+-- partitions inherit identity column and share sequence
+CREATE TABLE pitest1 (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE pitest1_p1 PARTITION OF pitest1 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into pitest1(f1, f2) VALUES ('2016-07-2', 'from pitest1');
+INSERT into pitest1_p1 (f1, f2) VALUES ('2016-07-3', 'from pitest1_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest1;
+  tableoid  |     f1     |       f2        | f3 
+------------+------------+-----------------+----
+ pitest1_p1 | 07-02-2016 | from pitest1    |  1
+ pitest1_p1 | 07-03-2016 | from pitest1_p1 |  2
+(2 rows)
+
+-- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
     f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index 9b8db2e4a3..3660fa6a28 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -331,8 +331,16 @@ CREATE TABLE itest12 OF itest_type (f1 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
 DROP TYPE itest_type CASCADE;
 
 
--- table partitions (currently not supported)
+-- table partitions
 
+-- partitions inherit identity column and share sequence
+CREATE TABLE pitest1 (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE pitest1_p1 PARTITION OF pitest1 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into pitest1(f1, f2) VALUES ('2016-07-2', 'from pitest1');
+INSERT into pitest1_p1 (f1, f2) VALUES ('2016-07-3', 'from pitest1_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest1;
+
+-- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
     f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
-- 
2.25.1



  [text/x-patch] 0001-Decorate-PostgreSQL-with-productname-tag-20240109.patch (983B, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/6-0001-Decorate-PostgreSQL-with-productname-tag-20240109.patch)
  download | inline diff:
From 2fa0310839eae93a1bbf8c9ab3a4cea3dd9d635b Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Tue, 9 Jan 2024 15:40:27 +0530
Subject: [PATCH 01/27] Decorate PostgreSQL with productname tag

... in the section about Generated Columns.

Ashutosh Bapat
---
 doc/src/sgml/ddl.sgml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 22d04006ad..bb6ce9291e 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -249,7 +249,7 @@ CREATE TABLE products (
    storage and is computed when it is read.  Thus, a virtual generated column
    is similar to a view and a stored generated column is similar to a
    materialized view (except that it is always updated automatically).
-   PostgreSQL currently implements only stored generated columns.
+   <productname>PostgreSQL</productname> currently implements only stored generated columns.
   </para>
 
   <para>
-- 
2.25.1



  [text/x-patch] 0006-Support-adding-indentity-column-to-a-partit-20240109.patch (5.4K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/7-0006-Support-adding-indentity-column-to-a-partit-20240109.patch)
  download | inline diff:
From d2b9eaf168d7a168671e4f164230eaa8d4b4965d Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Thu, 7 Dec 2023 11:20:07 +0530
Subject: [PATCH 06/27] Support adding indentity column to a partitioned table

Adding an identity column to a partitioned table just means propagating
it down the partitioning hierarchy. All the partitions share the same
underlying sequence.

As for the implementation, we need to just disable the code prohibiting this
case for partitioned tables. The column definition is transformed only once.
The statements related to identity sequence are executed only once before
executing any subcommands. Thus only one sequence, associated with the identity
column, is created. The transformed column definition and related commands are
copied as they are for all the partitions. Hence default expressions of all the
partition use the same sequence when rewriting the table. Also the NOT NULL
constraints required by the identity column are propagated.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       | 10 ++++++++--
 src/test/regress/expected/identity.out | 22 ++++++++++++++++++++++
 src/test/regress/sql/identity.sql      | 13 +++++++++++++
 3 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 5719df2b76..527a106c48 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7091,11 +7091,17 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	}
 
 	/*
-	 * Cannot add identity column if table has children, because identity does
-	 * not inherit.  (Adding column and identity separately will work.)
+	 * Regular inheritance children are independent enough not to inherit the
+	 * identity column from parent hence can not recursively add  identity
+	 * column if the table has inheritance children.
+	 *
+	 * Partitions, on the other hand, are integral part of a partitioned table
+	 * and inherit indetity column. Hence propagate identity column down the
+	 * partition hierarchy.
 	 */
 	if (colDef->identity &&
 		recurse &&
+		rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
 		find_inheritance_children(myrelid, NoLock) != NIL)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 222b3684da..93d9a60b03 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -565,6 +565,28 @@ SELECT tableoid::regclass, f1, f2, f3 FROM pitest1;
  pitest1_p2 | 08-04-2016 | from pitest1     |   4
 (5 rows)
 
+-- add identity column
+CREATE TABLE pitest2 (f1 date NOT NULL, f2 text) PARTITION BY RANGE (f1);
+CREATE TABLE pitest2_p1 PARTITION OF pitest2 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+CREATE TABLE pitest2_p2 PARTITION OF pitest2 FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT into pitest2(f1, f2) VALUES ('2016-07-2', 'from pitest2');
+INSERT INTO pitest2 (f1, f2) VALUES ('2016-08-2', 'from pitest2');
+ALTER TABLE pitest2 ADD COLUMN f3 int GENERATED ALWAYS AS IDENTITY;
+INSERT into pitest2_p1 (f1, f2) VALUES ('2016-07-3', 'from pitest2_p1');
+INSERT INTO pitest2_p2 (f1, f2) VALUES ('2016-08-3', 'from pitest2_p2');
+INSERT into pitest2(f1, f2) VALUES ('2016-07-4', 'from pitest2');
+INSERT INTO pitest2 (f1, f2) VALUES ('2016-08-4', 'from pitest2');
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;
+  tableoid  |     f1     |       f2        | f3 
+------------+------------+-----------------+----
+ pitest2_p1 | 07-02-2016 | from pitest2    |  1
+ pitest2_p1 | 07-03-2016 | from pitest2_p1 |  3
+ pitest2_p1 | 07-04-2016 | from pitest2    |  5
+ pitest2_p2 | 08-02-2016 | from pitest2    |  2
+ pitest2_p2 | 08-03-2016 | from pitest2_p2 |  4
+ pitest2_p2 | 08-04-2016 | from pitest2    |  6
+(6 rows)
+
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index be29cbf119..ba655002c0 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -349,6 +349,19 @@ INSERT INTO pitest1_p2 (f1, f2) VALUES ('2016-08-3', 'from pitest1_p2');
 INSERT INTO pitest1 (f1, f2) VALUES ('2016-08-4', 'from pitest1');
 SELECT tableoid::regclass, f1, f2, f3 FROM pitest1;
 
+-- add identity column
+CREATE TABLE pitest2 (f1 date NOT NULL, f2 text) PARTITION BY RANGE (f1);
+CREATE TABLE pitest2_p1 PARTITION OF pitest2 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+CREATE TABLE pitest2_p2 PARTITION OF pitest2 FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+INSERT into pitest2(f1, f2) VALUES ('2016-07-2', 'from pitest2');
+INSERT INTO pitest2 (f1, f2) VALUES ('2016-08-2', 'from pitest2');
+ALTER TABLE pitest2 ADD COLUMN f3 int GENERATED ALWAYS AS IDENTITY;
+INSERT into pitest2_p1 (f1, f2) VALUES ('2016-07-3', 'from pitest2_p1');
+INSERT INTO pitest2_p2 (f1, f2) VALUES ('2016-08-3', 'from pitest2_p2');
+INSERT into pitest2(f1, f2) VALUES ('2016-07-4', 'from pitest2');
+INSERT INTO pitest2 (f1, f2) VALUES ('2016-08-4', 'from pitest2');
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;
+
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
-- 
2.25.1



  [text/x-patch] 0007-Adding-identity-to-partitioned-table-adds-i-20240109.patch (7.8K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/8-0007-Adding-identity-to-partitioned-table-adds-i-20240109.patch)
  download | inline diff:
From 106fee38ca410855098ce3acb2d8d9092da13b64 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Mon, 11 Dec 2023 16:41:09 +0530
Subject: [PATCH 07/27] Adding identity to partitioned table adds it to all
 partitions

... recursively. Additionally do not allow adding identity to only
partitioned table or a partition

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       | 47 +++++++++++++++++++++++---
 src/test/regress/expected/identity.out | 30 ++++++++++++++++
 src/test/regress/sql/identity.sql      | 20 +++++++++++
 3 files changed, 93 insertions(+), 4 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 527a106c48..5ae0739377 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -452,7 +452,8 @@ static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
 static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
 											   Node *newDefault);
 static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
-									   Node *def, LOCKMODE lockmode);
+									   Node *def, LOCKMODE lockmode,
+									   bool recurse, bool recursing);
 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
 									   Node *def, LOCKMODE lockmode);
 static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
@@ -4825,7 +4826,9 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			break;
 		case AT_AddIdentity:
 			ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
-			/* This command never recurses */
+			/* Set up recursion for phase 2; no other prep needed */
+			if (recurse)
+				cmd->recurse = true;
 			pass = AT_PASS_ADD_OTHERCONSTR;
 			break;
 		case AT_SetIdentity:
@@ -5224,7 +5227,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
 									  cur_pass, context);
 			Assert(cmd != NULL);
-			address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
+			address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode,
+										cmd->recurse, false);
 			break;
 		case AT_SetIdentity:
 			cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
@@ -8105,7 +8109,7 @@ ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
  */
 static ObjectAddress
 ATExecAddIdentity(Relation rel, const char *colName,
-				  Node *def, LOCKMODE lockmode)
+				  Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
 {
 	Relation	attrelation;
 	HeapTuple	tuple;
@@ -8113,6 +8117,19 @@ ATExecAddIdentity(Relation rel, const char *colName,
 	AttrNumber	attnum;
 	ObjectAddress address;
 	ColumnDef  *cdef = castNode(ColumnDef, def);
+	bool		ispartitioned;
+
+	ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
+	if (ispartitioned && !recurse)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+				 errmsg("cannot add identity to a column of only the partitioned table"),
+				 errhint("Do not specify the ONLY keyword.")));
+
+	if (rel->rd_rel->relispartition && !recursing)
+		ereport(ERROR,
+				errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+				errmsg("cannot add identity to a column of a partition"));
 
 	attrelation = table_open(AttributeRelationId, RowExclusiveLock);
 
@@ -8167,6 +8184,28 @@ ATExecAddIdentity(Relation rel, const char *colName,
 
 	table_close(attrelation, RowExclusiveLock);
 
+	/*
+	 * Recurse to propagate the identity column to partitions. Identity is not
+	 * inherited in regular inheritance children.
+	 */
+	if (recurse && ispartitioned)
+	{
+		List	   *children;
+		ListCell   *lc;
+
+		children = find_inheritance_children(RelationGetRelid(rel),
+											 lockmode);
+
+		foreach(lc, children)
+		{
+			Relation	childrel;
+
+			childrel = table_open(lfirst_oid(lc), NoLock);
+			ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
+			table_close(childrel, NoLock);
+		}
+	}
+
 	return address;
 }
 
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 93d9a60b03..9de35470af 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -587,6 +587,36 @@ SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;
  pitest2_p2 | 08-04-2016 | from pitest2    |  6
 (6 rows)
 
+-- changing a regular column to identity column in a partitioned table
+CREATE TABLE pitest3 (f1 date NOT NULL, f2 text, f3 int) PARTITION BY RANGE (f1);
+CREATE TABLE pitest3_p1 PARTITION OF pitest3 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into pitest3 VALUES ('2016-07-2', 'from pitest3', 1);
+INSERT into pitest3_p1 VALUES ('2016-07-3', 'from pitest3_p1', 2);
+-- fails, changing only a partition not allowed
+ALTER TABLE pitest3_p1
+            ALTER COLUMN f3 SET NOT NULL,
+            ALTER COLUMN f3 ADD GENERATED ALWAYS AS IDENTITY (START WITH 3);
+ERROR:  cannot add identity to a column of a partition
+-- fails, changing only the partitioned table not allowed
+ALTER TABLE ONLY pitest3
+            ALTER COLUMN f3 SET NOT NULL,
+            ALTER COLUMN f3 ADD GENERATED ALWAYS AS IDENTITY (START WITH 3);
+ERROR:  constraint must be added to child tables too
+HINT:  Do not specify the ONLY keyword.
+ALTER TABLE pitest3
+            ALTER COLUMN f3 SET NOT NULL,
+            ALTER COLUMN f3 ADD GENERATED ALWAYS AS IDENTITY (START WITH 3);
+INSERT into pitest3(f1, f2) VALUES ('2016-07-4', 'from pitest3');
+INSERT into pitest3_p1 (f1, f2) VALUES ('2016-07-5', 'from pitest3_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest3;
+  tableoid  |     f1     |       f2        | f3 
+------------+------------+-----------------+----
+ pitest3_p1 | 07-02-2016 | from pitest3    |  1
+ pitest3_p1 | 07-03-2016 | from pitest3_p1 |  2
+ pitest3_p1 | 07-04-2016 | from pitest3    |  3
+ pitest3_p1 | 07-05-2016 | from pitest3_p1 |  4
+(4 rows)
+
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index ba655002c0..9d1effa059 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -362,6 +362,26 @@ INSERT into pitest2(f1, f2) VALUES ('2016-07-4', 'from pitest2');
 INSERT INTO pitest2 (f1, f2) VALUES ('2016-08-4', 'from pitest2');
 SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;
 
+-- changing a regular column to identity column in a partitioned table
+CREATE TABLE pitest3 (f1 date NOT NULL, f2 text, f3 int) PARTITION BY RANGE (f1);
+CREATE TABLE pitest3_p1 PARTITION OF pitest3 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into pitest3 VALUES ('2016-07-2', 'from pitest3', 1);
+INSERT into pitest3_p1 VALUES ('2016-07-3', 'from pitest3_p1', 2);
+-- fails, changing only a partition not allowed
+ALTER TABLE pitest3_p1
+            ALTER COLUMN f3 SET NOT NULL,
+            ALTER COLUMN f3 ADD GENERATED ALWAYS AS IDENTITY (START WITH 3);
+-- fails, changing only the partitioned table not allowed
+ALTER TABLE ONLY pitest3
+            ALTER COLUMN f3 SET NOT NULL,
+            ALTER COLUMN f3 ADD GENERATED ALWAYS AS IDENTITY (START WITH 3);
+ALTER TABLE pitest3
+            ALTER COLUMN f3 SET NOT NULL,
+            ALTER COLUMN f3 ADD GENERATED ALWAYS AS IDENTITY (START WITH 3);
+INSERT into pitest3(f1, f2) VALUES ('2016-07-4', 'from pitest3');
+INSERT into pitest3_p1 (f1, f2) VALUES ('2016-07-5', 'from pitest3_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest3;
+
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
-- 
2.25.1



  [text/x-patch] 0009-Changing-Identity-column-of-a-partitioned-t-20240109.patch (7.7K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/9-0009-Changing-Identity-column-of-a-partitioned-t-20240109.patch)
  download | inline diff:
From 06a04b4ce767e2baaebaf112fe82b18d1be5d9a2 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Wed, 3 Jan 2024 10:00:22 +0530
Subject: [PATCH 09/27] Changing Identity column of a partitioned table

The change is propagated to all the partitions. Changing the column only
in a partitioned table or a partition is not allowed; the change needs
to be applied to the whole partition hierarchy.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       | 48 +++++++++++++++++++++++---
 src/test/regress/expected/identity.out | 28 +++++++++++++++
 src/test/regress/sql/identity.sql      | 11 ++++++
 3 files changed, 83 insertions(+), 4 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f0c1a85031..67b38c5101 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -455,7 +455,8 @@ static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
 									   Node *def, LOCKMODE lockmode,
 									   bool recurse, bool recursing);
 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
-									   Node *def, LOCKMODE lockmode);
+									   Node *def, LOCKMODE lockmode,
+									   bool recurse, bool recursing);
 static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName,
 										bool missing_ok, LOCKMODE lockmode,
 										bool recurse, bool recursing);
@@ -4835,7 +4836,9 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			break;
 		case AT_SetIdentity:
 			ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
-			/* This command never recurses */
+			/* Set up recursion for phase 2; no other prep needed */
+			if (recurse)
+				cmd->recurse = true;
 			/* This should run after AddIdentity, so do it in MISC pass */
 			pass = AT_PASS_MISC;
 			break;
@@ -5238,7 +5241,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
 									  cur_pass, context);
 			Assert(cmd != NULL);
-			address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
+			address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode,
+										cmd->recurse, false);
 			break;
 		case AT_DropIdentity:
 			address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok,
@@ -8220,7 +8224,8 @@ ATExecAddIdentity(Relation rel, const char *colName,
  * Return the address of the affected column.
  */
 static ObjectAddress
-ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode)
+ATExecSetIdentity(Relation rel, const char *colName, Node *def,
+				  LOCKMODE lockmode, bool recurse, bool recursing)
 {
 	ListCell   *option;
 	DefElem    *generatedEl = NULL;
@@ -8229,6 +8234,19 @@ ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmod
 	AttrNumber	attnum;
 	Relation	attrelation;
 	ObjectAddress address;
+	bool		ispartitioned;
+
+	ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
+	if (ispartitioned && !recurse)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+				 errmsg("cannot change identity column of only the partitioned table"),
+				 errhint("Do not specify the ONLY keyword.")));
+
+	if (rel->rd_rel->relispartition && !recursing)
+		ereport(ERROR,
+				errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+				errmsg("cannot change identity column of a partition"));
 
 	foreach(option, castNode(List, def))
 	{
@@ -8293,6 +8311,28 @@ ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmod
 	heap_freetuple(tuple);
 	table_close(attrelation, RowExclusiveLock);
 
+	/*
+	 * Recurse to propagate the identity change to partitions. Identity is not
+	 * inherited in regular inheritance children.
+	 */
+	if (generatedEl && recurse && ispartitioned)
+	{
+		List	   *children;
+		ListCell   *lc;
+
+		children = find_inheritance_children(RelationGetRelid(rel),
+											 lockmode);
+
+		foreach(lc, children)
+		{
+			Relation	childrel;
+
+			childrel = table_open(lfirst_oid(lc), NoLock);
+			ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
+			table_close(childrel, NoLock);
+		}
+	}
+
 	return address;
 }
 
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index d2860b4347..e185e0d4a1 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -587,6 +587,34 @@ SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;
  pitest2_p2 | 08-04-2016 | from pitest2    |  6
 (6 rows)
 
+-- SET identity column
+ALTER TABLE pitest2_p1 ALTER COLUMN f3 SET GENERATED BY DEFAULT; -- fails
+ERROR:  cannot change identity column of a partition
+ALTER TABLE pitest2_p1 ALTER COLUMN f3 SET INCREMENT BY 2; -- fails
+ERROR:  cannot change identity column of a partition
+ALTER TABLE ONLY pitest2 ALTER COLUMN f3 SET GENERATED BY DEFAULT SET INCREMENT BY 2 SET START WITH 1000 RESTART; -- fails
+ERROR:  cannot change identity column of only the partitioned table
+HINT:  Do not specify the ONLY keyword.
+ALTER TABLE pitest2 ALTER COLUMN f3 SET GENERATED BY DEFAULT SET INCREMENT BY 2 SET START WITH 1000 RESTART;
+INSERT into pitest2(f1, f2, f3) VALUES ('2016-07-5', 'from pitest2', 200);
+INSERT INTO pitest2(f1, f2) VALUES ('2016-08-5', 'from pitest2');
+INSERT into pitest2_p1 (f1, f2) VALUES ('2016-07-6', 'from pitest2_p1');
+INSERT INTO pitest2_p2 (f1, f2, f3) VALUES ('2016-08-6', 'from pitest2_p2', 300);
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;
+  tableoid  |     f1     |       f2        |  f3  
+------------+------------+-----------------+------
+ pitest2_p1 | 07-02-2016 | from pitest2    |    1
+ pitest2_p1 | 07-03-2016 | from pitest2_p1 |    3
+ pitest2_p1 | 07-04-2016 | from pitest2    |    5
+ pitest2_p1 | 07-05-2016 | from pitest2    |  200
+ pitest2_p1 | 07-06-2016 | from pitest2_p1 | 1002
+ pitest2_p2 | 08-02-2016 | from pitest2    |    2
+ pitest2_p2 | 08-03-2016 | from pitest2_p2 |    4
+ pitest2_p2 | 08-04-2016 | from pitest2    |    6
+ pitest2_p2 | 08-05-2016 | from pitest2    | 1000
+ pitest2_p2 | 08-06-2016 | from pitest2_p2 |  300
+(10 rows)
+
 -- changing a regular column to identity column in a partitioned table
 CREATE TABLE pitest3 (f1 date NOT NULL, f2 text, f3 int) PARTITION BY RANGE (f1);
 CREATE TABLE pitest3_p1 PARTITION OF pitest3 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index 62323f9cbd..a1862df1d8 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -362,6 +362,17 @@ INSERT into pitest2(f1, f2) VALUES ('2016-07-4', 'from pitest2');
 INSERT INTO pitest2 (f1, f2) VALUES ('2016-08-4', 'from pitest2');
 SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;
 
+-- SET identity column
+ALTER TABLE pitest2_p1 ALTER COLUMN f3 SET GENERATED BY DEFAULT; -- fails
+ALTER TABLE pitest2_p1 ALTER COLUMN f3 SET INCREMENT BY 2; -- fails
+ALTER TABLE ONLY pitest2 ALTER COLUMN f3 SET GENERATED BY DEFAULT SET INCREMENT BY 2 SET START WITH 1000 RESTART; -- fails
+ALTER TABLE pitest2 ALTER COLUMN f3 SET GENERATED BY DEFAULT SET INCREMENT BY 2 SET START WITH 1000 RESTART;
+INSERT into pitest2(f1, f2, f3) VALUES ('2016-07-5', 'from pitest2', 200);
+INSERT INTO pitest2(f1, f2) VALUES ('2016-08-5', 'from pitest2');
+INSERT into pitest2_p1 (f1, f2) VALUES ('2016-07-6', 'from pitest2_p1');
+INSERT INTO pitest2_p2 (f1, f2, f3) VALUES ('2016-08-6', 'from pitest2_p2', 300);
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;
+
 -- changing a regular column to identity column in a partitioned table
 CREATE TABLE pitest3 (f1 date NOT NULL, f2 text, f3 int) PARTITION BY RANGE (f1);
 CREATE TABLE pitest3_p1 PARTITION OF pitest3 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
-- 
2.25.1



  [text/x-patch] 0008-DROP-IDENTITY-on-partitioned-table-recurses-20240109.patch (9.1K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/10-0008-DROP-IDENTITY-on-partitioned-table-recurses-20240109.patch)
  download | inline diff:
From 6f5276cef8814300057a370778bb9b8ce6ebde23 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Mon, 18 Dec 2023 16:39:30 +0530
Subject: [PATCH 08/27] DROP IDENTITY on partitioned table recurses to its
 partitions

Since identity property is inherited by partition tables, dropping
identity of a column in partitioned table should result in dropping it
from all the partitions.

I tried to implement the DROP SEQUENCE as a separate step to be executed
at the end of ALTER TABLE command. But that does not work since the
sequence has a dependency on the column (whose identity is being
dropped) and not on the identity property itself. There is no step/ALTER
TABLE command to drop that dependecy.

Dropping identity property from a column of a partition table is not
allwoed.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       | 73 +++++++++++++++++++++-----
 src/test/regress/expected/identity.out | 26 +++++++++
 src/test/regress/sql/identity.sql      | 10 ++++
 3 files changed, 96 insertions(+), 13 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 5ae0739377..f0c1a85031 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -456,7 +456,9 @@ static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
 									   bool recurse, bool recursing);
 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
 									   Node *def, LOCKMODE lockmode);
-static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
+static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName,
+										bool missing_ok, LOCKMODE lockmode,
+										bool recurse, bool recursing);
 static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
 static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
 static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
@@ -4839,7 +4841,9 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			break;
 		case AT_DropIdentity:
 			ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
-			/* This command never recurses */
+			/* Set up recursion for phase 2; no other prep needed */
+			if (recurse)
+				cmd->recurse = true;
 			pass = AT_PASS_DROP;
 			break;
 		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
@@ -5237,7 +5241,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
 			break;
 		case AT_DropIdentity:
-			address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
+			address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok,
+										 lockmode, cmd->recurse, false);
 			break;
 		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
 			address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
@@ -8297,7 +8302,9 @@ ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmod
  * Return the address of the affected column.
  */
 static ObjectAddress
-ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
+ATExecDropIdentity(Relation rel, const char *colName,
+				   bool missing_ok, LOCKMODE lockmode,
+				   bool recurse, bool recursing)
 {
 	HeapTuple	tuple;
 	Form_pg_attribute attTup;
@@ -8306,6 +8313,19 @@ ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE
 	ObjectAddress address;
 	Oid			seqid;
 	ObjectAddress seqaddress;
+	bool		ispartitioned;
+
+	ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
+	if (ispartitioned && !recurse)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+				 errmsg("cannot drop identity from a column of only the partitioned table"),
+				 errhint("Do not specify the ONLY keyword.")));
+
+	if (rel->rd_rel->relispartition && !recursing)
+		ereport(ERROR,
+				errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+				errmsg("cannot drop identity from a column of a partition"));
 
 	attrelation = table_open(AttributeRelationId, RowExclusiveLock);
 	tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
@@ -8354,15 +8374,42 @@ ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE
 
 	table_close(attrelation, RowExclusiveLock);
 
-	/* drop the internal sequence */
-	seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
-	deleteDependencyRecordsForClass(RelationRelationId, seqid,
-									RelationRelationId, DEPENDENCY_INTERNAL);
-	CommandCounterIncrement();
-	seqaddress.classId = RelationRelationId;
-	seqaddress.objectId = seqid;
-	seqaddress.objectSubId = 0;
-	performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+	/*
+	 * Recurse to drop the identity from column in partitions. Identity is not
+	 * inherited in regular inheritance children so ignore them.
+	 */
+	if (recurse && ispartitioned)
+	{
+		List	   *children;
+		ListCell   *lc;
+
+		children = find_inheritance_children(RelationGetRelid(rel),
+											 lockmode);
+
+		foreach(lc, children)
+		{
+			Relation	childrel;
+
+			childrel = table_open(lfirst_oid(lc), NoLock);
+			ATExecDropIdentity(childrel, colName, false, lockmode, recurse,
+							   true);
+			table_close(childrel, NoLock);
+		}
+	}
+
+	if (!recursing)
+	{
+		/* drop the internal sequence */
+		seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
+		deleteDependencyRecordsForClass(RelationRelationId, seqid,
+										RelationRelationId,
+										DEPENDENCY_INTERNAL);
+		CommandCounterIncrement();
+		seqaddress.classId = RelationRelationId;
+		seqaddress.objectId = seqid;
+		seqaddress.objectSubId = 0;
+		performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+	}
 
 	return address;
 }
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 9de35470af..d2860b4347 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -617,6 +617,32 @@ SELECT tableoid::regclass, f1, f2, f3 FROM pitest3;
  pitest3_p1 | 07-05-2016 | from pitest3_p1 |  4
 (4 rows)
 
+-- changing an identity column to a non-identity column in a partitioned table
+ALTER TABLE pitest3_p1 ALTER COLUMN f3 DROP IDENTITY; -- fails
+ERROR:  cannot drop identity from a column of a partition
+ALTER TABLE ONLY pitest3 ALTER COLUMN f3 DROP IDENTITY; -- fails
+ERROR:  cannot drop identity from a column of only the partitioned table
+HINT:  Do not specify the ONLY keyword.
+ALTER TABLE pitest3 ALTER COLUMN f3 DROP IDENTITY;
+INSERT into pitest3(f1, f2) VALUES ('2016-07-4', 'from pitest3'); -- fails
+ERROR:  null value in column "f3" of relation "pitest3_p1" violates not-null constraint
+DETAIL:  Failing row contains (07-04-2016, from pitest3, null).
+INSERT into pitest3_p1 (f1, f2) VALUES ('2016-07-5', 'from pitest3_p1'); -- fails
+ERROR:  null value in column "f3" of relation "pitest3_p1" violates not-null constraint
+DETAIL:  Failing row contains (07-05-2016, from pitest3_p1, null).
+INSERT into pitest3(f1, f2, f3) VALUES ('2016-07-6', 'from pitest3', 5);
+INSERT into pitest3_p1 (f1, f2, f3) VALUES ('2016-07-7', 'from pitest3_p1', 6);
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest3;
+  tableoid  |     f1     |       f2        | f3 
+------------+------------+-----------------+----
+ pitest3_p1 | 07-02-2016 | from pitest3    |  1
+ pitest3_p1 | 07-03-2016 | from pitest3_p1 |  2
+ pitest3_p1 | 07-04-2016 | from pitest3    |  3
+ pitest3_p1 | 07-05-2016 | from pitest3_p1 |  4
+ pitest3_p1 | 07-06-2016 | from pitest3    |  5
+ pitest3_p1 | 07-07-2016 | from pitest3_p1 |  6
+(6 rows)
+
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index 9d1effa059..62323f9cbd 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -382,6 +382,16 @@ INSERT into pitest3(f1, f2) VALUES ('2016-07-4', 'from pitest3');
 INSERT into pitest3_p1 (f1, f2) VALUES ('2016-07-5', 'from pitest3_p1');
 SELECT tableoid::regclass, f1, f2, f3 FROM pitest3;
 
+-- changing an identity column to a non-identity column in a partitioned table
+ALTER TABLE pitest3_p1 ALTER COLUMN f3 DROP IDENTITY; -- fails
+ALTER TABLE ONLY pitest3 ALTER COLUMN f3 DROP IDENTITY; -- fails
+ALTER TABLE pitest3 ALTER COLUMN f3 DROP IDENTITY;
+INSERT into pitest3(f1, f2) VALUES ('2016-07-4', 'from pitest3'); -- fails
+INSERT into pitest3_p1 (f1, f2) VALUES ('2016-07-5', 'from pitest3_p1'); -- fails
+INSERT into pitest3(f1, f2, f3) VALUES ('2016-07-6', 'from pitest3', 5);
+INSERT into pitest3_p1 (f1, f2, f3) VALUES ('2016-07-7', 'from pitest3_p1', 6);
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest3;
+
 -- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
-- 
2.25.1



  [text/x-patch] 0010-Drop-identity-property-when-detaching-parti-20240109.patch (5.4K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/11-0010-Drop-identity-property-when-detaching-parti-20240109.patch)
  download | inline diff:
From 1a5517a57ac5f3dd083b4814d403e35f26a542f5 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Tue, 19 Dec 2023 11:38:48 +0530
Subject: [PATCH 10/27] Drop identity property when detaching partition

A partition's identity column shares the identity space (i.e. underlying
sequence) as the corresponding column of the partitioned table.  If a
partition is detached it can longer share the identity space as before.
Hence the identity columns of the partition being detached loose their
identity property.

When identity of a column of a regular table is dropped it retains the
NOT NULL constarint that came with the identity property. Similarly the
columns of the partition being detached retain the NOT NULL constraints
that came with identity property, even though the identity property
itself is lost.

Also note that the sequence associated with the identity property is
linked to the partitioned table (and not the partition being detached).
That sequence is not dropped as part of detach operation.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       | 13 +++++++++++
 src/test/regress/expected/identity.out | 30 ++++++++++++++++++++++++++
 src/test/regress/sql/identity.sql      | 10 +++++++++
 3 files changed, 53 insertions(+)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 67b38c5101..19badb3fba 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19498,6 +19498,7 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
 	HeapTuple	tuple,
 				newtuple;
 	Relation	trigrel = NULL;
+	int			attno;
 
 	if (concurrent)
 	{
@@ -19663,6 +19664,18 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
 	heap_freetuple(newtuple);
 	table_close(classRel, RowExclusiveLock);
 
+	/*
+	 * Drop identity property from all identity columns of partition.
+	 */
+	for (attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
+	{
+		Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
+
+		if (!attr->attisdropped && attr->attidentity)
+			ATExecDropIdentity(partRel, NameStr(attr->attname), false,
+							   AccessExclusiveLock, true, true);
+	}
+
 	if (OidIsValid(defaultPartOid))
 	{
 		/*
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index e185e0d4a1..501fcc22c6 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -615,6 +615,36 @@ SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;
  pitest2_p2 | 08-06-2016 | from pitest2_p2 |  300
 (10 rows)
 
+-- detaching a partition removes identity property
+ALTER TABLE pitest2 DETACH PARTITION pitest2_p1;
+INSERT into pitest2(f1, f2) VALUES ('2016-08-7', 'from pitest2');
+INSERT into pitest2_p1 (f1, f2) VALUES ('2016-07-7', 'from pitest2_p1'); -- error
+ERROR:  null value in column "f3" of relation "pitest2_p1" violates not-null constraint
+DETAIL:  Failing row contains (07-07-2016, from pitest2_p1, null).
+INSERT into pitest2_p1 (f1, f2, f3) VALUES ('2016-07-7', 'from pitest2_p1', 2000);
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;
+  tableoid  |     f1     |       f2        |  f3  
+------------+------------+-----------------+------
+ pitest2_p2 | 08-02-2016 | from pitest2    |    2
+ pitest2_p2 | 08-03-2016 | from pitest2_p2 |    4
+ pitest2_p2 | 08-04-2016 | from pitest2    |    6
+ pitest2_p2 | 08-05-2016 | from pitest2    | 1000
+ pitest2_p2 | 08-06-2016 | from pitest2_p2 |  300
+ pitest2_p2 | 08-07-2016 | from pitest2    | 1004
+(6 rows)
+
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest2_p1;
+  tableoid  |     f1     |       f2        |  f3  
+------------+------------+-----------------+------
+ pitest2_p1 | 07-02-2016 | from pitest2    |    1
+ pitest2_p1 | 07-03-2016 | from pitest2_p1 |    3
+ pitest2_p1 | 07-04-2016 | from pitest2    |    5
+ pitest2_p1 | 07-05-2016 | from pitest2    |  200
+ pitest2_p1 | 07-06-2016 | from pitest2_p1 | 1002
+ pitest2_p1 | 07-07-2016 | from pitest2_p1 | 2000
+(6 rows)
+
+DROP TABLE pitest2_p1;
 -- changing a regular column to identity column in a partitioned table
 CREATE TABLE pitest3 (f1 date NOT NULL, f2 text, f3 int) PARTITION BY RANGE (f1);
 CREATE TABLE pitest3_p1 PARTITION OF pitest3 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index a1862df1d8..0d7ecfb3e0 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -373,6 +373,16 @@ INSERT into pitest2_p1 (f1, f2) VALUES ('2016-07-6', 'from pitest2_p1');
 INSERT INTO pitest2_p2 (f1, f2, f3) VALUES ('2016-08-6', 'from pitest2_p2', 300);
 SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;
 
+-- detaching a partition removes identity property
+ALTER TABLE pitest2 DETACH PARTITION pitest2_p1;
+INSERT into pitest2(f1, f2) VALUES ('2016-08-7', 'from pitest2');
+INSERT into pitest2_p1 (f1, f2) VALUES ('2016-07-7', 'from pitest2_p1'); -- error
+INSERT into pitest2_p1 (f1, f2, f3) VALUES ('2016-07-7', 'from pitest2_p1', 2000);
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest2_p1;
+
+DROP TABLE pitest2_p1;
+
 -- changing a regular column to identity column in a partitioned table
 CREATE TABLE pitest3 (f1 date NOT NULL, f2 text, f3 int) PARTITION BY RANGE (f1);
 CREATE TABLE pitest3_p1 PARTITION OF pitest3 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
-- 
2.25.1



  [text/x-patch] 0011-Partitions-with-their-own-identity-columns--20240109.patch (6.2K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/12-0011-Partitions-with-their-own-identity-columns--20240109.patch)
  download | inline diff:
From 3ace7b78b04395e01a973e49af1e7b8f29bbafae Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Wed, 20 Dec 2023 14:55:24 +0530
Subject: [PATCH 11/27] Partitions with their own identity columns are not
 allowed

... for the reasons specified in earlier commit messages.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c        | 12 +++++++++++-
 src/test/regress/expected/generated.out |  2 +-
 src/test/regress/expected/identity.out  | 20 +++++++++++++++-----
 src/test/regress/sql/identity.sql       | 19 ++++++++++++++-----
 4 files changed, 41 insertions(+), 12 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 19badb3fba..f6db45522f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -18793,7 +18793,10 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 				 errmsg("cannot attach temporary relation of another session as partition")));
 
-	/* Check if there are any columns in attachrel that aren't in the parent */
+	/*
+	 * Check if attachrel has any identity columns or any columns that aren't
+	 * in the parent.
+	 */
 	tupleDesc = RelationGetDescr(attachrel);
 	natts = tupleDesc->natts;
 	for (attno = 1; attno <= natts; attno++)
@@ -18805,6 +18808,13 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
 		if (attribute->attisdropped)
 			continue;
 
+		if (attribute->attidentity)
+			ereport(ERROR,
+					errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					errmsg("table \"%s\" being attached can not contain identity column \"%s\"",
+						   RelationGetRelationName(attachrel),
+						   attributeName));
+
 		/* Try to find the column in parent (matching on column name) */
 		if (!SearchSysCacheExists2(ATTNAME,
 								   ObjectIdGetDatum(RelationGetRelid(rel)),
diff --git a/src/test/regress/expected/generated.out b/src/test/regress/expected/generated.out
index a2f38d0f50..c9b0856fa2 100644
--- a/src/test/regress/expected/generated.out
+++ b/src/test/regress/expected/generated.out
@@ -753,7 +753,7 @@ ERROR:  column "f3" in child table must be a generated column
 DROP TABLE gtest_child3;
 CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS IDENTITY);
 ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01'); -- error
-ERROR:  column "f3" in child table must be a generated column
+ERROR:  table "gtest_child3" being attached can not contain identity column "f3"
 DROP TABLE gtest_child3;
 CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 33) STORED);
 ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 501fcc22c6..0b6a4b160e 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -701,13 +701,23 @@ SELECT tableoid::regclass, f1, f2, f3 FROM pitest3;
  pitest3_p1 | 07-07-2016 | from pitest3_p1 |  6
 (6 rows)
 
--- partition with identity column of its own is not allowed
-CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
-CREATE TABLE itest_child PARTITION OF itest_parent (
+-- partitions with their own identity columns are not allowed, even if the
+-- partitioned table does not have an identity column.
+CREATE TABLE pitest1_pfail PARTITION OF pitest1 (
     f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
-) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
+) FOR VALUES FROM ('2016-11-01') TO ('2016-12-01');
 ERROR:  identity columns are not supported on partitions
-DROP TABLE itest_parent;
+CREATE TABLE pitest_pfail PARTITION OF pitest3 (
+    f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
+) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+ERROR:  identity columns are not supported on partitions
+CREATE TABLE pitest1_pfail (f1 date NOT NULL, f2 text, f3 bigint GENERATED ALWAYS AS IDENTITY);
+ALTER TABLE pitest1 ATTACH PARTITION pitest1_pfail FOR VALUES FROM ('2016-11-01') TO ('2016-12-01');
+ERROR:  table "pitest1_pfail" being attached can not contain identity column "f3"
+ALTER TABLE pitest3 ATTACH PARTITION pitest1_pfail FOR VALUES FROM ('2016-11-01') TO ('2016-12-01');
+ERROR:  table "pitest1_pfail" being attached can not contain identity column "f3"
+DROP TABLE pitest1_pfail;
+DROP TABLE pitest3;
 -- test that sequence of half-dropped serial column is properly ignored
 CREATE TABLE itest14 (id serial);
 ALTER TABLE itest14 ALTER id DROP DEFAULT;
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index 0d7ecfb3e0..e0ce2c799b 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -413,13 +413,22 @@ INSERT into pitest3(f1, f2, f3) VALUES ('2016-07-6', 'from pitest3', 5);
 INSERT into pitest3_p1 (f1, f2, f3) VALUES ('2016-07-7', 'from pitest3_p1', 6);
 SELECT tableoid::regclass, f1, f2, f3 FROM pitest3;
 
--- partition with identity column of its own is not allowed
-CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
-CREATE TABLE itest_child PARTITION OF itest_parent (
+-- partitions with their own identity columns are not allowed, even if the
+-- partitioned table does not have an identity column.
+CREATE TABLE pitest1_pfail PARTITION OF pitest1 (
     f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
-) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
-DROP TABLE itest_parent;
+) FOR VALUES FROM ('2016-11-01') TO ('2016-12-01');
 
+CREATE TABLE pitest_pfail PARTITION OF pitest3 (
+    f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
+) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+
+CREATE TABLE pitest1_pfail (f1 date NOT NULL, f2 text, f3 bigint GENERATED ALWAYS AS IDENTITY);
+ALTER TABLE pitest1 ATTACH PARTITION pitest1_pfail FOR VALUES FROM ('2016-11-01') TO ('2016-12-01');
+ALTER TABLE pitest3 ATTACH PARTITION pitest1_pfail FOR VALUES FROM ('2016-11-01') TO ('2016-12-01');
+
+DROP TABLE pitest1_pfail;
+DROP TABLE pitest3;
 
 -- test that sequence of half-dropped serial column is properly ignored
 
-- 
2.25.1



  [text/x-patch] 0012-Test-changing-some-properties-of-identity-c-20240109.patch (3.0K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/13-0012-Test-changing-some-properties-of-identity-c-20240109.patch)
  download | inline diff:
From ab37ba8f8a517c8aea8c6cb05eb3712b5da15955 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Wed, 3 Jan 2024 11:54:22 +0530
Subject: [PATCH 12/27] Test changing some properties of identity column in
 partitioned table

NULL constraint, default and identity property

Ashutosh Bapat
---
 src/test/regress/expected/identity.out | 15 +++++++++++++++
 src/test/regress/sql/identity.sql      | 10 ++++++++++
 2 files changed, 25 insertions(+)

diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 0b6a4b160e..f03d88035f 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -701,6 +701,21 @@ SELECT tableoid::regclass, f1, f2, f3 FROM pitest3;
  pitest3_p1 | 07-07-2016 | from pitest3_p1 |  6
 (6 rows)
 
+-- Changing NOT NULL constraint of identity columns is not allowed
+ALTER TABLE pitest1_p1 ALTER COLUMN f3 DROP NOT NULL;
+ERROR:  column "f3" of relation "pitest1_p1" is an identity column
+ALTER TABLE pitest1 ALTER COLUMN f3 DROP NOT NULL;
+ERROR:  column "f3" of relation "pitest1" is an identity column
+-- Identity columns have their own default
+ALTER TABLE pitest1_p2 ALTER COLUMN f3 SET DEFAULT 10000;
+ERROR:  column "f3" of relation "pitest1_p2" is an identity column
+ALTER TABLE pitest1 ALTER COLUMN f3 SET DEFAULT 10000;
+ERROR:  column "f3" of relation "pitest1" is an identity column
+-- Adding identity to an identity column is not allowed
+ALTER TABLE pitest1_p2 ALTER COLUMN f3 ADD GENERATED BY DEFAULT AS IDENTITY;
+ERROR:  cannot add identity to a column of a partition
+ALTER TABLE pitest1 ALTER COLUMN f3 ADD GENERATED BY DEFAULT AS IDENTITY;
+ERROR:  column "f3" of relation "pitest1" is already an identity column
 -- partitions with their own identity columns are not allowed, even if the
 -- partitioned table does not have an identity column.
 CREATE TABLE pitest1_pfail PARTITION OF pitest1 (
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index e0ce2c799b..9f35214751 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -413,6 +413,16 @@ INSERT into pitest3(f1, f2, f3) VALUES ('2016-07-6', 'from pitest3', 5);
 INSERT into pitest3_p1 (f1, f2, f3) VALUES ('2016-07-7', 'from pitest3_p1', 6);
 SELECT tableoid::regclass, f1, f2, f3 FROM pitest3;
 
+-- Changing NOT NULL constraint of identity columns is not allowed
+ALTER TABLE pitest1_p1 ALTER COLUMN f3 DROP NOT NULL;
+ALTER TABLE pitest1 ALTER COLUMN f3 DROP NOT NULL;
+-- Identity columns have their own default
+ALTER TABLE pitest1_p2 ALTER COLUMN f3 SET DEFAULT 10000;
+ALTER TABLE pitest1 ALTER COLUMN f3 SET DEFAULT 10000;
+-- Adding identity to an identity column is not allowed
+ALTER TABLE pitest1_p2 ALTER COLUMN f3 ADD GENERATED BY DEFAULT AS IDENTITY;
+ALTER TABLE pitest1 ALTER COLUMN f3 ADD GENERATED BY DEFAULT AS IDENTITY;
+
 -- partitions with their own identity columns are not allowed, even if the
 -- partitioned table does not have an identity column.
 CREATE TABLE pitest1_pfail PARTITION OF pitest1 (
-- 
2.25.1



  [text/x-patch] 0013-Fix-output-in-modules-test_ddl_deparse-20240109.patch (2.2K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/14-0013-Fix-output-in-modules-test_ddl_deparse-20240109.patch)
  download | inline diff:
From d5f226c0542c1524ef62fa80ae9fef9d5513e685 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Mon, 8 Jan 2024 16:22:23 +0530
Subject: [PATCH 13/27] Fix output in modules/test_ddl_deparse/

ALTER TABLE ... ALTER COLUMN ... ADD/SET/DROP IDENTITY commands need to
recurse into partition hierarchy. Hence they are marked to recurse when
ONLY is not specified. Change the expected output for the same.

Please note that in case of regular inheritance the commands won't
recurse down the inheritance tree whether or not ONLY is specified. This
is the same as the earlier behaviour.

Ashutosh Bapat
---
 src/test/modules/test_ddl_deparse/expected/alter_table.out | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/test/modules/test_ddl_deparse/expected/alter_table.out b/src/test/modules/test_ddl_deparse/expected/alter_table.out
index ecde9d7422..b5e71af9aa 100644
--- a/src/test/modules/test_ddl_deparse/expected/alter_table.out
+++ b/src/test/modules/test_ddl_deparse/expected/alter_table.out
@@ -74,14 +74,14 @@ ALTER TABLE parent ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;
 NOTICE:  DDL test: type simple, tag CREATE SEQUENCE
 NOTICE:  DDL test: type simple, tag ALTER SEQUENCE
 NOTICE:  DDL test: type alter table, tag ALTER TABLE
-NOTICE:    subcommand: type ADD IDENTITY desc column a of table parent
+NOTICE:    subcommand: type ADD IDENTITY (and recurse) desc column a of table parent
 ALTER TABLE parent ALTER COLUMN a SET GENERATED BY DEFAULT;
 NOTICE:  DDL test: type simple, tag ALTER SEQUENCE
 NOTICE:  DDL test: type alter table, tag ALTER TABLE
-NOTICE:    subcommand: type SET IDENTITY desc column a of table parent
+NOTICE:    subcommand: type SET IDENTITY (and recurse) desc column a of table parent
 ALTER TABLE parent ALTER COLUMN a DROP IDENTITY;
 NOTICE:  DDL test: type alter table, tag ALTER TABLE
-NOTICE:    subcommand: type DROP IDENTITY desc column a of table parent
+NOTICE:    subcommand: type DROP IDENTITY (and recurse) desc column a of table parent
 ALTER TABLE parent ALTER COLUMN a SET STATISTICS 100;
 NOTICE:  DDL test: type alter table, tag ALTER TABLE
 NOTICE:    subcommand: type SET STATS desc column a of table parent
-- 
2.25.1



  [text/x-patch] 0014-Test-dump-and-restore-NOT-FOR-FINAL-COMMIT-20240109.patch (3.1K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/15-0014-Test-dump-and-restore-NOT-FOR-FINAL-COMMIT-20240109.patch)
  download | inline diff:
From e18135cc29c074c23113f6cb907cbe83fe22e248 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Fri, 5 Jan 2024 19:28:16 +0530
Subject: [PATCH 14/27] Test dump and restore: NOT FOR FINAL COMMIT

... through the changed testcase since it dumps and restores the objects
left behind by sql/identity.sql.

The additional SQL statements here test the sanity of the restored
partitioned tables with identity column in it. I have not seen these
kind of tests for checking sanity of other objects so it's quite
possible that dump comparison performed by the test is enough. Hence not
planning to propose this for final commit.

The earlier patches do not change anything in dump restore and yet it
works. This is because the identity columns are dumped using ALTER TABLE
command, which is constructed only for the table owning the underlying
sequence. In case of partitioned tables, only the topmost partitioned
table owns the sequence and thus ALTER TABLE command adding identity to
the column is run only on the topmost table. The partitions are attached
afterwards and inherit the identity column as part of that operation.

Ashutosh Bapat
---
 src/bin/pg_upgrade/t/002_pg_upgrade.pl | 33 ++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/src/bin/pg_upgrade/t/002_pg_upgrade.pl b/src/bin/pg_upgrade/t/002_pg_upgrade.pl
index b0470844de..26ee1fb2af 100644
--- a/src/bin/pg_upgrade/t/002_pg_upgrade.pl
+++ b/src/bin/pg_upgrade/t/002_pg_upgrade.pl
@@ -458,4 +458,37 @@ if ($compare_res != 0)
 	print "=== EOF ===\n";
 }
 
+# Test restored partitioned table with identity
+$newnode->safe_psql('regression',
+					"INSERT INTO pitest1 (f1, f2) VALUES ('2016-08-5', 'from pitest1'), ('2016-07-5', 'from pitest1');");
+$newnode->safe_psql('regression',
+					"INSERT INTO pitest1_p1 (f1, f2) VALUES ('2016-07-6', 'from pitest1_p1');");
+$newnode->safe_psql('regression',
+					"INSERT INTO pitest1_p2 (f1, f2) VALUES ('2016-08-6', 'from pitest1_p2');");
+$result = $newnode->safe_psql('regression',
+					"SELECT tableoid::regclass, f1, f2, f3 FROM pitest1");
+is($result,"pitest1_p1|2016-07-02|from pitest1|1
+pitest1_p1|2016-07-03|from pitest1_p1|2
+pitest1_p1|2016-07-05|from pitest1|6
+pitest1_p1|2016-07-06|from pitest1_p1|7
+pitest1_p2|2016-08-02|before attaching|100
+pitest1_p2|2016-08-03|from pitest1_p2|3
+pitest1_p2|2016-08-04|from pitest1|4
+pitest1_p2|2016-08-05|from pitest1|5
+pitest1_p2|2016-08-06|from pitest1_p2|8");
+$newnode->safe_psql('regression',
+					"INSERT INTO pitest2 (f1, f2) VALUES ('2016-08-8', 'from pitest2');");
+$newnode->safe_psql('regression',
+					"INSERT INTO pitest2_p2 (f1, f2) VALUES ('2016-08-9', 'from pitest2_p2');");
+$result = $newnode->safe_psql('regression',
+					"SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;");
+is($result, "pitest2_p2|2016-08-02|from pitest2|2
+pitest2_p2|2016-08-03|from pitest2_p2|4
+pitest2_p2|2016-08-04|from pitest2|6
+pitest2_p2|2016-08-05|from pitest2|1000
+pitest2_p2|2016-08-06|from pitest2_p2|300
+pitest2_p2|2016-08-07|from pitest2|1004
+pitest2_p2|2016-08-08|from pitest2|1006
+pitest2_p2|2016-08-09|from pitest2_p2|1008");
+
 done_testing();
-- 
2.25.1



  [text/x-patch] 0015-Test-pg_upgrade-20240109.patch (3.3K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/16-0015-Test-pg_upgrade-20240109.patch)
  download | inline diff:
From 304969e542940240e21698b290cc647a38f066fb Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Mon, 8 Jan 2024 15:01:06 +0530
Subject: [PATCH 15/27] Test pg_upgrade

NOT FOR FINAL COMMIT

Old clusters, with versions 10 and above, may have partitioned tables
with identity columns. The corresponding columns in partitions of such
tables will not be marked as identity columns. Test that such
partitioned tables are upgraded correctly. The columns in partitions are
also marked as identity columns and use the same underlying sequence as
the partitioned table.

Test this using following steps
1. On an old version cluster run following commands to create required
partitioned tables in "regression" database
--- sql
CREATE TABLE pitest1 (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
CREATE TABLE pitest1_p1 PARTITION OF pitest1 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
INSERT into pitest1(f1, f2) VALUES ('2016-07-2', 'from pitest1');
INSERT into pitest1_p1 (f1, f2, f3) VALUES ('2016-07-3', 'from pitest1_p1', nextval('pitest1_f3_seq'));
CREATE TABLE pitest1_p2 (f1 date NOT NULL, f2 text, f3 bigint);
INSERT INTO pitest1_p2 VALUES ('2016-08-2', 'before attaching', 100);
ALTER TABLE pitest1_p2 ALTER COLUMN f3 SET NOT NULL;
ALTER TABLE pitest1 ATTACH PARTITION pitest1_p2 FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
INSERT INTO pitest1_p2 (f1, f2, f3) VALUES ('2016-08-3', 'from pitest1_p2', nextval('pitest1_f3_seq'));
INSERT INTO pitest1 (f1, f2) VALUES ('2016-08-4', 'from pitest1');
--- sql

2. Take dump using pg_dumpall and save it in a file
3. Set environment variables olddump to the path of this file and oldinstall to
the binaries of old version.

4. Run modified 002_pg_upgrade.
---
 src/bin/pg_upgrade/t/002_pg_upgrade.pl | 19 +++----------------
 1 file changed, 3 insertions(+), 16 deletions(-)

diff --git a/src/bin/pg_upgrade/t/002_pg_upgrade.pl b/src/bin/pg_upgrade/t/002_pg_upgrade.pl
index 26ee1fb2af..9b63a7b9dd 100644
--- a/src/bin/pg_upgrade/t/002_pg_upgrade.pl
+++ b/src/bin/pg_upgrade/t/002_pg_upgrade.pl
@@ -241,8 +241,9 @@ if (defined($ENV{oldinstall}))
 	my %dbnames;
 	do { $dbnames{$_} = 1; }
 	  foreach split /\s+/s, $dbnames;
-	my $adjust_cmds =
-	  adjust_database_contents($oldnode->pg_version, %dbnames);
+	# Testing a custom dump that doesn't have regression objects
+	my $adjust_cmds = ();
+	#  adjust_database_contents($oldnode->pg_version, %dbnames);
 
 	foreach my $updb (keys %$adjust_cmds)
 	{
@@ -476,19 +477,5 @@ pitest1_p2|2016-08-03|from pitest1_p2|3
 pitest1_p2|2016-08-04|from pitest1|4
 pitest1_p2|2016-08-05|from pitest1|5
 pitest1_p2|2016-08-06|from pitest1_p2|8");
-$newnode->safe_psql('regression',
-					"INSERT INTO pitest2 (f1, f2) VALUES ('2016-08-8', 'from pitest2');");
-$newnode->safe_psql('regression',
-					"INSERT INTO pitest2_p2 (f1, f2) VALUES ('2016-08-9', 'from pitest2_p2');");
-$result = $newnode->safe_psql('regression',
-					"SELECT tableoid::regclass, f1, f2, f3 FROM pitest2;");
-is($result, "pitest2_p2|2016-08-02|from pitest2|2
-pitest2_p2|2016-08-03|from pitest2_p2|4
-pitest2_p2|2016-08-04|from pitest2|6
-pitest2_p2|2016-08-05|from pitest2|1000
-pitest2_p2|2016-08-06|from pitest2_p2|300
-pitest2_p2|2016-08-07|from pitest2|1004
-pitest2_p2|2016-08-08|from pitest2|1006
-pitest2_p2|2016-08-09|from pitest2_p2|1008");
 
 done_testing();
-- 
2.25.1



  [application/octet-stream] pg_dumpall_14.out (4.6K, ../../CAExHW5s+552sq=s2igOaFBuWFcRwL2uoL-O1gz2d0fDRf4bpvg@mail.gmail.com/17-pg_dumpall_14.out)
  download

^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* [PATCH v2 4/4] run pgindent
@ 2026-05-01 19:38  Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: Nathan Bossart @ 2026-05-01 19:38 UTC (permalink / raw)

---
 src/bin/pg_dump/pg_dump.c              | 457 ++++++++++++-----------
 src/bin/pg_dump/pg_dumpall.c           |  30 +-
 src/bin/pg_upgrade/check.c             |  16 +-
 src/bin/pg_upgrade/exec.c              |   8 +-
 src/bin/pg_upgrade/multixact_rewrite.c |  80 ++--
 src/bin/pg_upgrade/pg_upgrade.c        |   2 +-
 src/bin/pg_upgrade/relfilenumber.c     |  54 +--
 src/bin/psql/command.c                 |  29 +-
 src/bin/psql/describe.c                | 483 ++++++++++++-------------
 9 files changed, 578 insertions(+), 581 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index eed9aaeb7c1..c05623b1889 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1491,8 +1491,8 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	 * Disable timeouts if supported.
 	 */
 	ExecuteSqlStatement(AH, "SET statement_timeout = 0");
-		ExecuteSqlStatement(AH, "SET lock_timeout = 0");
-		ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
+	ExecuteSqlStatement(AH, "SET lock_timeout = 0");
+	ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
 	if (AH->remoteVersion >= 170000)
 		ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
 
@@ -1505,10 +1505,10 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	/*
 	 * Adjust row-security mode, if supported.
 	 */
-		if (dopt->enable_row_security)
-			ExecuteSqlStatement(AH, "SET row_security = on");
-		else
-			ExecuteSqlStatement(AH, "SET row_security = off");
+	if (dopt->enable_row_security)
+		ExecuteSqlStatement(AH, "SET row_security = on");
+	else
+		ExecuteSqlStatement(AH, "SET row_security = off");
 
 	/*
 	 * For security reasons, we restrict the expansion of non-system views and
@@ -1955,7 +1955,7 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
 	if (fout->dopt->binary_upgrade)
 		dobj->dump = ext->dobj.dump;
 	else
-			dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
+		dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
 
 	return true;
 }
@@ -1989,9 +1989,9 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
 	else if (strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
 	{
 		/*
-		 * We dump out any ACLs defined in pg_catalog, if
-		 * they are interesting (and not the original ACLs which were set at
-		 * initdb time, see pg_init_privs).
+		 * We dump out any ACLs defined in pg_catalog, if they are interesting
+		 * (and not the original ACLs which were set at initdb time, see
+		 * pg_init_privs).
 		 */
 		nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
 	}
@@ -3298,7 +3298,7 @@ dumpDatabase(Archive *fout)
 						 "datcollate, datctype, datfrozenxid, "
 						 "datacl, acldefault('d', datdba) AS acldefault, "
 						 "datistemplate, datconnlimit, ");
-		appendPQExpBufferStr(dbQry, "datminmxid, ");
+	appendPQExpBufferStr(dbQry, "datminmxid, ");
 	if (fout->remoteVersion >= 170000)
 		appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
 	else if (fout->remoteVersion >= 150000)
@@ -3640,11 +3640,11 @@ dumpDatabase(Archive *fout)
 					ii_oid,
 					ii_relminmxid;
 
-			appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
-							  "FROM pg_catalog.pg_class\n"
-							  "WHERE oid IN (%u, %u, %u, %u);\n",
-							  LargeObjectRelationId, LargeObjectLOidPNIndexId,
-							  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
+		appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
+						  "FROM pg_catalog.pg_class\n"
+						  "WHERE oid IN (%u, %u, %u, %u);\n",
+						  LargeObjectRelationId, LargeObjectLOidPNIndexId,
+						  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
 
 		lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
 
@@ -4276,7 +4276,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 
 	printfPQExpBuffer(query,
 					  "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
-		appendPQExpBufferStr(query, "pol.polpermissive, ");
+	appendPQExpBufferStr(query, "pol.polpermissive, ");
 	appendPQExpBuffer(query,
 					  "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
 					  "   pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
@@ -6635,9 +6635,9 @@ getAccessMethods(Archive *fout)
 	 * Select all access methods from pg_am table.
 	 */
 	appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
-		appendPQExpBufferStr(query,
-							 "amtype, "
-							 "amhandler::pg_catalog.regproc AS amhandler ");
+	appendPQExpBufferStr(query,
+						 "amtype, "
+						 "amhandler::pg_catalog.regproc AS amhandler ");
 	appendPQExpBufferStr(query, "FROM pg_am");
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -6829,35 +6829,35 @@ getAggregates(Archive *fout)
 	 * Find all interesting aggregates.  See comment in getFuncs() for the
 	 * rationale behind the filtering logic.
 	 */
-		agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
-					 : "p.proisagg");
+	agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
+				 : "p.proisagg");
 
-		appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
-						  "p.proname AS aggname, "
-						  "p.pronamespace AS aggnamespace, "
-						  "p.pronargs, p.proargtypes, "
-						  "p.proowner, "
-						  "p.proacl AS aggacl, "
-						  "acldefault('f', p.proowner) AS acldefault "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s AND ("
-						  "p.pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog') OR "
-						  "p.proacl IS DISTINCT FROM pip.initprivs",
-						  agg_check);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
-		appendPQExpBufferChar(query, ')');
+	appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
+					  "p.proname AS aggname, "
+					  "p.pronamespace AS aggnamespace, "
+					  "p.pronargs, p.proargtypes, "
+					  "p.proowner, "
+					  "p.proacl AS aggacl, "
+					  "acldefault('f', p.proowner) AS acldefault "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s AND ("
+					  "p.pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog') OR "
+					  "p.proacl IS DISTINCT FROM pip.initprivs",
+					  agg_check);
+	if (dopt->binary_upgrade)
+		appendPQExpBufferStr(query,
+							 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -6959,53 +6959,53 @@ getFuncs(Archive *fout)
 	 * include them, since we want to dump extension members individually in
 	 * that mode.  Also, if they are used by casts or transforms then we need
 	 * to gather the information about them, though they won't be dumped if
-	 * they are built-in.  Also, include functions in
-	 * pg_catalog if they have an ACL different from what's shown in
-	 * pg_init_privs (so we have to join to pg_init_privs; annoying).
+	 * they are built-in.  Also, include functions in pg_catalog if they have
+	 * an ACL different from what's shown in pg_init_privs (so we have to join
+	 * to pg_init_privs; annoying).
 	 */
-		not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
-						 : "NOT p.proisagg");
+	not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
+					 : "NOT p.proisagg");
 
-		appendPQExpBuffer(query,
-						  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
-						  "p.pronargs, p.proargtypes, p.prorettype, "
-						  "p.proacl, "
-						  "acldefault('f', p.proowner) AS acldefault, "
-						  "p.pronamespace, "
-						  "p.proowner "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s"
-						  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
-						  "WHERE classid = 'pg_proc'::regclass AND "
-						  "objid = p.oid AND deptype = 'i')"
-						  "\n  AND ("
-						  "\n  pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog')"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
-						  "\n  WHERE pg_cast.oid > %u "
-						  "\n  AND p.oid = pg_cast.castfunc)"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
-						  "\n  WHERE pg_transform.oid > %u AND "
-						  "\n  (p.oid = pg_transform.trffromsql"
-						  "\n  OR p.oid = pg_transform.trftosql))",
-						  not_agg_check,
-						  g_last_builtin_oid,
-						  g_last_builtin_oid);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
+	appendPQExpBuffer(query,
+					  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
+					  "p.pronargs, p.proargtypes, p.prorettype, "
+					  "p.proacl, "
+					  "acldefault('f', p.proowner) AS acldefault, "
+					  "p.pronamespace, "
+					  "p.proowner "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s"
+					  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
+					  "WHERE classid = 'pg_proc'::regclass AND "
+					  "objid = p.oid AND deptype = 'i')"
+					  "\n  AND ("
+					  "\n  pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog')"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
+					  "\n  WHERE pg_cast.oid > %u "
+					  "\n  AND p.oid = pg_cast.castfunc)"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
+					  "\n  WHERE pg_transform.oid > %u AND "
+					  "\n  (p.oid = pg_transform.trffromsql"
+					  "\n  OR p.oid = pg_transform.trftosql))",
+					  not_agg_check,
+					  g_last_builtin_oid,
+					  g_last_builtin_oid);
+	if (dopt->binary_upgrade)
 		appendPQExpBufferStr(query,
-							 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
-		appendPQExpBufferChar(query, ')');
+							 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferStr(query,
+						 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -7254,31 +7254,31 @@ getTables(Archive *fout, int *numTables)
 		appendPQExpBufferStr(query,
 							 "c.relhasoids, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispopulated, ");
+	appendPQExpBufferStr(query,
+						 "c.relispopulated, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relreplident, ");
+	appendPQExpBufferStr(query,
+						 "c.relreplident, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relrowsecurity, c.relforcerowsecurity, ");
+	appendPQExpBufferStr(query,
+						 "c.relrowsecurity, c.relforcerowsecurity, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relminmxid, tc.relminmxid AS tminmxid, ");
+	appendPQExpBufferStr(query,
+						 "c.relminmxid, tc.relminmxid AS tminmxid, ");
 
-		appendPQExpBufferStr(query,
-							 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-							 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-							 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
+	appendPQExpBufferStr(query,
+						 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+						 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+						 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
 
-		appendPQExpBufferStr(query,
-							 "am.amname, ");
+	appendPQExpBufferStr(query,
+						 "am.amname, ");
 
-		appendPQExpBufferStr(query,
-							 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
+	appendPQExpBufferStr(query,
+						 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispartition AS ispartition ");
+	appendPQExpBufferStr(query,
+						 "c.relispartition AS ispartition ");
 
 	/*
 	 * Left join to pg_depend to pick up dependency info linking sequences to
@@ -7298,8 +7298,8 @@ getTables(Archive *fout, int *numTables)
 	/*
 	 * Left join to pg_am to pick up the amname.
 	 */
-		appendPQExpBufferStr(query,
-							 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
+	appendPQExpBufferStr(query,
+						 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
 
 	/*
 	 * We purposefully ignore toast OIDs for partitioned tables; the reason is
@@ -7870,8 +7870,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 						 "t.reloptions AS indreloptions, ");
 
 
-		appendPQExpBufferStr(query,
-							 "i.indisreplident, ");
+	appendPQExpBufferStr(query,
+						 "i.indisreplident, ");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(query,
@@ -9315,8 +9315,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 		appendPQExpBufferStr(q,
 							 "'' AS attcompression,\n");
 
-		appendPQExpBufferStr(q,
-							 "a.attidentity,\n");
+	appendPQExpBufferStr(q,
+						 "a.attidentity,\n");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(q,
@@ -10719,73 +10719,73 @@ getAdditionalACLs(Archive *fout)
 	PQclear(res);
 
 	/* Fetch initial-privileges data */
-		printfPQExpBuffer(query,
-						  "SELECT objoid, classoid, objsubid, privtype, initprivs "
-						  "FROM pg_init_privs");
+	printfPQExpBuffer(query,
+					  "SELECT objoid, classoid, objsubid, privtype, initprivs "
+					  "FROM pg_init_privs");
 
-		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
-		ntups = PQntuples(res);
-		for (i = 0; i < ntups; i++)
-		{
-			Oid			objoid = atooid(PQgetvalue(res, i, 0));
-			Oid			classoid = atooid(PQgetvalue(res, i, 1));
-			int			objsubid = atoi(PQgetvalue(res, i, 2));
-			char		privtype = *(PQgetvalue(res, i, 3));
-			char	   *initprivs = PQgetvalue(res, i, 4);
-			CatalogId	objId;
-			DumpableObject *dobj;
+	ntups = PQntuples(res);
+	for (i = 0; i < ntups; i++)
+	{
+		Oid			objoid = atooid(PQgetvalue(res, i, 0));
+		Oid			classoid = atooid(PQgetvalue(res, i, 1));
+		int			objsubid = atoi(PQgetvalue(res, i, 2));
+		char		privtype = *(PQgetvalue(res, i, 3));
+		char	   *initprivs = PQgetvalue(res, i, 4);
+		CatalogId	objId;
+		DumpableObject *dobj;
 
-			objId.tableoid = classoid;
-			objId.oid = objoid;
-			dobj = findObjectByCatalogId(objId);
-			/* OK to ignore entries we haven't got a DumpableObject for */
-			if (dobj)
+		objId.tableoid = classoid;
+		objId.oid = objoid;
+		dobj = findObjectByCatalogId(objId);
+		/* OK to ignore entries we haven't got a DumpableObject for */
+		if (dobj)
+		{
+			/* Cope with sub-object initprivs */
+			if (objsubid != 0)
 			{
-				/* Cope with sub-object initprivs */
-				if (objsubid != 0)
-				{
-					if (dobj->objType == DO_TABLE)
-					{
-						/* For a column initprivs, set the table's ACL flags */
-						dobj->components |= DUMP_COMPONENT_ACL;
-						((TableInfo *) dobj)->hascolumnACLs = true;
-					}
-					else
-						pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
-									   classoid, objoid, objsubid);
-					continue;
-				}
-
-				/*
-				 * We ignore any pg_init_privs.initprivs entry for the public
-				 * schema, as explained in getNamespaces().
-				 */
-				if (dobj->objType == DO_NAMESPACE &&
-					strcmp(dobj->name, "public") == 0)
-					continue;
-
-				/* Else it had better be of a type we think has ACLs */
-				if (dobj->objType == DO_NAMESPACE ||
-					dobj->objType == DO_TYPE ||
-					dobj->objType == DO_FUNC ||
-					dobj->objType == DO_AGG ||
-					dobj->objType == DO_TABLE ||
-					dobj->objType == DO_PROCLANG ||
-					dobj->objType == DO_FDW ||
-					dobj->objType == DO_FOREIGN_SERVER)
+				if (dobj->objType == DO_TABLE)
 				{
-					DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
-
-					daobj->dacl.privtype = privtype;
-					daobj->dacl.initprivs = pstrdup(initprivs);
+					/* For a column initprivs, set the table's ACL flags */
+					dobj->components |= DUMP_COMPONENT_ACL;
+					((TableInfo *) dobj)->hascolumnACLs = true;
 				}
 				else
 					pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
 								   classoid, objoid, objsubid);
+				continue;
+			}
+
+			/*
+			 * We ignore any pg_init_privs.initprivs entry for the public
+			 * schema, as explained in getNamespaces().
+			 */
+			if (dobj->objType == DO_NAMESPACE &&
+				strcmp(dobj->name, "public") == 0)
+				continue;
+
+			/* Else it had better be of a type we think has ACLs */
+			if (dobj->objType == DO_NAMESPACE ||
+				dobj->objType == DO_TYPE ||
+				dobj->objType == DO_FUNC ||
+				dobj->objType == DO_AGG ||
+				dobj->objType == DO_TABLE ||
+				dobj->objType == DO_PROCLANG ||
+				dobj->objType == DO_FDW ||
+				dobj->objType == DO_FOREIGN_SERVER)
+			{
+				DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
+
+				daobj->dacl.privtype = privtype;
+				daobj->dacl.initprivs = pstrdup(initprivs);
 			}
+			else
+				pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
+							   classoid, objoid, objsubid);
 		}
-		PQclear(res);
+	}
+	PQclear(res);
 
 	destroyPQExpBuffer(query);
 }
@@ -11130,8 +11130,8 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 		 * The results must be in the order of the relations supplied in the
 		 * parameters to ensure we remain in sync as we walk through the TOC.
 		 *
-		 * For versions before 19, the redundant filter clause on s.tablename =
-		 * ANY(...) seems sufficient to convince the planner to use
+		 * For versions before 19, the redundant filter clause on s.tablename
+		 * = ANY(...) seems sufficient to convince the planner to use
 		 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
 		 * In newer versions, pg_stats returns the table OIDs, eliminating the
 		 * need for that hack.
@@ -13471,11 +13471,11 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
 							 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
 							 "proleakproof,\n");
 
-			appendPQExpBufferStr(query,
-								 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
+		appendPQExpBufferStr(query,
+							 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
 
-			appendPQExpBufferStr(query,
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -14965,9 +14965,9 @@ dumpCollation(Archive *fout, const CollInfo *collinfo)
 	/* Get collation-specific details */
 	appendPQExpBufferStr(query, "SELECT ");
 
-		appendPQExpBufferStr(query,
-							 "collprovider, "
-							 "collversion, ");
+	appendPQExpBufferStr(query,
+						 "collprovider, "
+						 "collversion, ");
 
 	if (fout->remoteVersion >= 120000)
 		appendPQExpBufferStr(query,
@@ -15374,23 +15374,23 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 							 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
 							 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggkind,\n"
-								 "aggmtransfn,\n"
-								 "aggminvtransfn,\n"
-								 "aggmfinalfn,\n"
-								 "aggmtranstype::pg_catalog.regtype,\n"
-								 "aggfinalextra,\n"
-								 "aggmfinalextra,\n"
-								 "aggtransspace,\n"
-								 "aggmtransspace,\n"
-								 "aggminitval,\n");
+		appendPQExpBufferStr(query,
+							 "aggkind,\n"
+							 "aggmtransfn,\n"
+							 "aggminvtransfn,\n"
+							 "aggmfinalfn,\n"
+							 "aggmtranstype::pg_catalog.regtype,\n"
+							 "aggfinalextra,\n"
+							 "aggmfinalextra,\n"
+							 "aggtransspace,\n"
+							 "aggmtransspace,\n"
+							 "aggminitval,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggcombinefn,\n"
-								 "aggserialfn,\n"
-								 "aggdeserialfn,\n"
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "aggcombinefn,\n"
+							 "aggserialfn,\n"
+							 "aggdeserialfn,\n"
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -16850,30 +16850,30 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
 			appendPQExpBufferStr(query,
 								 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
 
-				/*
-				 * In principle we should call acldefault('c', relowner) to
-				 * get the default ACL for a column.  However, we don't
-				 * currently store the numeric OID of the relowner in
-				 * TableInfo.  We could convert the owner name using regrole,
-				 * but that creates a risk of failure due to concurrent role
-				 * renames.  Given that the default ACL for columns is empty
-				 * and is likely to stay that way, it's not worth extra cycles
-				 * and risk to avoid hard-wiring that knowledge here.
-				 */
-				appendPQExpBufferStr(query,
-									 "SELECT at.attname, "
-									 "at.attacl, "
-									 "'{}' AS acldefault, "
-									 "pip.privtype, pip.initprivs "
-									 "FROM pg_catalog.pg_attribute at "
-									 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
-									 "(at.attrelid = pip.objoid "
-									 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
-									 "AND at.attnum = pip.objsubid) "
-									 "WHERE at.attrelid = $1 AND "
-									 "NOT at.attisdropped "
-									 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
-									 "ORDER BY at.attnum");
+			/*
+			 * In principle we should call acldefault('c', relowner) to get
+			 * the default ACL for a column.  However, we don't currently
+			 * store the numeric OID of the relowner in TableInfo.  We could
+			 * convert the owner name using regrole, but that creates a risk
+			 * of failure due to concurrent role renames.  Given that the
+			 * default ACL for columns is empty and is likely to stay that
+			 * way, it's not worth extra cycles and risk to avoid hard-wiring
+			 * that knowledge here.
+			 */
+			appendPQExpBufferStr(query,
+								 "SELECT at.attname, "
+								 "at.attacl, "
+								 "'{}' AS acldefault, "
+								 "pip.privtype, pip.initprivs "
+								 "FROM pg_catalog.pg_attribute at "
+								 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
+								 "(at.attrelid = pip.objoid "
+								 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
+								 "AND at.attnum = pip.objsubid) "
+								 "WHERE at.attrelid = $1 AND "
+								 "NOT at.attisdropped "
+								 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
+								 "ORDER BY at.attnum");
 
 			ExecuteSqlStatement(fout, query->data);
 
@@ -19167,7 +19167,7 @@ collectSequences(Archive *fout)
 	 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
 	 */
 	if (fout->remoteVersion < 180000 ||
-			 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
+		(!fout->dopt->dumpData && !fout->dopt->sequence_data))
 		query = "SELECT seqrelid, format_type(seqtypid, NULL), "
 			"seqstart, seqincrement, "
 			"seqmax, seqmin, "
@@ -19229,15 +19229,14 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
 
 	/*
-	 * The sequence information is gathered in a sorted
-	 * table before any calls to dumpSequence().  See collectSequences() for
-	 * more information.
+	 * The sequence information is gathered in a sorted table before any calls
+	 * to dumpSequence().  See collectSequences() for more information.
 	 */
-		Assert(sequences);
+	Assert(sequences);
 
-		key.oid = tbinfo->dobj.catId.oid;
-		seq = bsearch(&key, sequences, nsequences,
-					  sizeof(SequenceItem), SequenceItemCmp);
+	key.oid = tbinfo->dobj.catId.oid;
+	seq = bsearch(&key, sequences, nsequences,
+				  sizeof(SequenceItem), SequenceItemCmp);
 
 	/* Calculate default limits for a sequence of this type */
 	is_ascending = (seq->incby >= 0);
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 5b10f7122b7..3f61196671c 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -954,11 +954,11 @@ dropRoles(PGconn *conn)
 	int			i_rolname;
 	int			i;
 
-		printfPQExpBuffer(buf,
-						  "SELECT rolname "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 1", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT rolname "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 1", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
@@ -1035,16 +1035,16 @@ dumpRoles(PGconn *conn)
 	 * Notes: rolconfig is dumped later, and pg_authid must be used for
 	 * extracting rolcomment regardless of role_catalog.
 	 */
-		printfPQExpBuffer(buf,
-						  "SELECT oid, rolname, rolsuper, rolinherit, "
-						  "rolcreaterole, rolcreatedb, "
-						  "rolcanlogin, rolconnlimit, rolpassword, "
-						  "rolvaliduntil, rolreplication, rolbypassrls, "
-						  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
-						  "rolname = current_user AS is_current_user "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 2", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT oid, rolname, rolsuper, rolinherit, "
+					  "rolcreaterole, rolcreatedb, "
+					  "rolcanlogin, rolconnlimit, rolpassword, "
+					  "rolvaliduntil, rolreplication, rolbypassrls, "
+					  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
+					  "rolname = current_user AS is_current_user "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 2", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 0813cef2729..5f63e2114c8 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -1475,15 +1475,15 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_remove(anyarray,anyelement)'"
-							 ", 'array_replace(anyarray,anyelement,anyelement)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_remove(anyarray,anyelement)'"
+						 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_position(anyarray,anyelement)'"
-							 ", 'array_position(anyarray,anyelement,integer)'"
-							 ", 'array_positions(anyarray,anyelement)'"
-							 ", 'width_bucket(anyelement,anyarray)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_position(anyarray,anyelement)'"
+						 ", 'array_position(anyarray,anyelement,integer)'"
+						 ", 'array_positions(anyarray,anyelement)'"
+						 ", 'width_bucket(anyelement,anyarray)'");
 
 	/*
 	 * The query below hardcodes FirstNormalObjectId as 16384 rather than
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 479557abdcc..9a675929e17 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,7 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-		cluster->bin_version = v1 * 10000;
+	cluster->bin_version = v1 * 10000;
 }
 
 
@@ -344,8 +344,8 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-		check_single_dir(pg_data, "pg_wal");
-		check_single_dir(pg_data, "pg_xact");
+	check_single_dir(pg_data, "pg_wal");
+	check_single_dir(pg_data, "pg_xact");
 }
 
 
@@ -385,7 +385,7 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-		check_exec(cluster->bindir, "pg_resetwal", check_versions);
+	check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
 	{
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index c45b3183684..c7a1416494d 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -61,52 +61,52 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
-										   old_cluster.controldata.chkpnt_nxtmulti,
-										   old_cluster.controldata.chkpnt_nxtmxoff);
+	old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
+									   old_cluster.controldata.chkpnt_nxtmulti,
+									   old_cluster.controldata.chkpnt_nxtmxoff);
 
-		for (MultiXactId multi = from_multi; multi != to_multi;)
-		{
-			MultiXactMember member;
-			bool		multixid_valid;
-
-			/*
-			 * Read this multixid's members.
-			 *
-			 * Locking-only XIDs that may be part of multi-xids don't matter
-			 * after upgrade, as there can be no transactions running across
-			 * upgrade.  So as a small optimization, we only read one member
-			 * from each multixid: the one updating one, or if there was no
-			 * update, arbitrarily the first locking xid.
-			 */
-			multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
+	for (MultiXactId multi = from_multi; multi != to_multi;)
+	{
+		MultiXactMember member;
+		bool		multixid_valid;
 
-			/*
-			 * Write the new offset to pg_multixact/offsets.
-			 *
-			 * Even if this multixid is invalid, we still need to write its
-			 * offset if the *previous* multixid was valid.  That's because
-			 * when reading a multixid, the number of members is calculated
-			 * from the difference between the two offsets.
-			 */
-			RecordMultiXactOffset(offsets_writer, multi,
-								  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
+		/*
+		 * Read this multixid's members.
+		 *
+		 * Locking-only XIDs that may be part of multi-xids don't matter after
+		 * upgrade, as there can be no transactions running across upgrade. So
+		 * as a small optimization, we only read one member from each
+		 * multixid: the one updating one, or if there was no update,
+		 * arbitrarily the first locking xid.
+		 */
+		multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
 
-			/* Write the members */
-			if (multixid_valid)
-			{
-				RecordMultiXactMembers(members_writer, next_offset, 1, &member);
-				next_offset += 1;
-			}
+		/*
+		 * Write the new offset to pg_multixact/offsets.
+		 *
+		 * Even if this multixid is invalid, we still need to write its offset
+		 * if the *previous* multixid was valid.  That's because when reading
+		 * a multixid, the number of members is calculated from the difference
+		 * between the two offsets.
+		 */
+		RecordMultiXactOffset(offsets_writer, multi,
+							  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
 
-			/* Advance to next multixid, handling wraparound */
-			multi++;
-			if (multi < FirstMultiXactId)
-				multi = FirstMultiXactId;
-			prev_multixid_valid = multixid_valid;
+		/* Write the members */
+		if (multixid_valid)
+		{
+			RecordMultiXactMembers(members_writer, next_offset, 1, &member);
+			next_offset += 1;
 		}
 
-		FreeOldMultiXactReader(old_reader);
+		/* Advance to next multixid, handling wraparound */
+		multi++;
+		if (multi < FirstMultiXactId)
+			multi = FirstMultiXactId;
+		prev_multixid_valid = multixid_valid;
+	}
+
+	FreeOldMultiXactReader(old_reader);
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index e5d7920c1b1..d8e1b680f5a 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -833,7 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
+		oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index ec2ff7acb21..6c467bdc8a5 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -587,32 +587,32 @@ transfer_relfile(FileNameMap *map, const char *type_suffix)
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-			switch (user_opts.transfer_mode)
-			{
-				case TRANSFER_MODE_CLONE:
-					pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
-						   old_file, new_file);
-					cloneFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
-						   old_file, new_file);
-					copyFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY_FILE_RANGE:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
-						   old_file, new_file);
-					copyFileByRange(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_LINK:
-					pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
-						   old_file, new_file);
-					linkFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_SWAP:
-					/* swap mode is handled in its own code path */
-					pg_fatal("should never happen");
-					break;
-			}
+		switch (user_opts.transfer_mode)
+		{
+			case TRANSFER_MODE_CLONE:
+				pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
+					   old_file, new_file);
+				cloneFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
+					   old_file, new_file);
+				copyFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY_FILE_RANGE:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
+					   old_file, new_file);
+				copyFileByRange(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_LINK:
+				pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
+					   old_file, new_file);
+				linkFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_SWAP:
+				/* swap mode is handled in its own code path */
+				pg_fatal("should never happen");
+				break;
+		}
 	}
 }
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c9573d4b765..e5fb3595598 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -6272,23 +6272,22 @@ get_create_object_cmd(EditableObjectType obj_type, Oid oid,
 			 * ensure the right view gets replaced.  Also, check relation kind
 			 * to be sure it's a view.
 			 *
-			 * Views may have WITH [LOCAL|CASCADED]
-			 * CHECK OPTION.  These are not part of the view definition
-			 * returned by pg_get_viewdef() and so need to be retrieved
-			 * separately.  Materialized views may have
-			 * arbitrary storage parameter reloptions.
+			 * Views may have WITH [LOCAL|CASCADED] CHECK OPTION.  These are
+			 * not part of the view definition returned by pg_get_viewdef()
+			 * and so need to be retrieved separately.  Materialized views may
+			 * have arbitrary storage parameter reloptions.
 			 */
 			printfPQExpBuffer(query, "/* %s */\n", _("Get view's definition and details"));
-				appendPQExpBuffer(query,
-								  "SELECT nspname, relname, relkind, "
-								  "pg_catalog.pg_get_viewdef(c.oid, true), "
-								  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-								  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-								  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
-								  "FROM pg_catalog.pg_class c "
-								  "LEFT JOIN pg_catalog.pg_namespace n "
-								  "ON c.relnamespace = n.oid WHERE c.oid = %u",
-								  oid);
+			appendPQExpBuffer(query,
+							  "SELECT nspname, relname, relkind, "
+							  "pg_catalog.pg_get_viewdef(c.oid, true), "
+							  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+							  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+							  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
+							  "FROM pg_catalog.pg_class c "
+							  "LEFT JOIN pg_catalog.pg_namespace n "
+							  "ON c.relnamespace = n.oid WHERE c.oid = %u",
+							  oid);
 			break;
 	}
 
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 76d299fb55c..389c4dce367 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -98,12 +98,12 @@ describeAggregates(const char *pattern, bool verbose, bool showSystem)
 					  gettext_noop("Result data type"),
 					  gettext_noop("Argument data types"));
 
-		appendPQExpBuffer(&buf,
-						  "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
-						  "FROM pg_catalog.pg_proc p\n"
-						  "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
-						  "WHERE p.prokind = " CppAsString2(PROKIND_AGGREGATE) "\n",
-						  gettext_noop("Description"));
+	appendPQExpBuffer(&buf,
+					  "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
+					  "FROM pg_catalog.pg_proc p\n"
+					  "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
+					  "WHERE p.prokind = " CppAsString2(PROKIND_AGGREGATE) "\n",
+					  gettext_noop("Description"));
 
 	if (!showSystem && !pattern)
 		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
@@ -379,19 +379,19 @@ describeFunctions(const char *functypes, const char *func_pattern,
 						  gettext_noop("stable"),
 						  gettext_noop("volatile"),
 						  gettext_noop("Volatility"));
-			appendPQExpBuffer(&buf,
-							  ",\n CASE\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
-							  " END as \"%s\"",
-							  gettext_noop("restricted"),
-							  gettext_noop("safe"),
-							  gettext_noop("unsafe"),
-							  gettext_noop("Parallel"));
+		appendPQExpBuffer(&buf,
+						  ",\n CASE\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
+						  " END as \"%s\"",
+						  gettext_noop("restricted"),
+						  gettext_noop("safe"),
+						  gettext_noop("unsafe"),
+						  gettext_noop("Parallel"));
 		appendPQExpBuffer(&buf,
 						  ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\""
 						  ",\n CASE WHEN prosecdef THEN '%s' ELSE '%s' END AS \"%s\""
@@ -591,8 +591,8 @@ describeFunctions(const char *functypes, const char *func_pattern,
 
 	myopt.title = _("List of functions");
 	myopt.translate_header = true;
-		myopt.translate_columns = translate_columns;
-		myopt.n_translate_columns = lengthof(translate_columns);
+	myopt.translate_columns = translate_columns;
+	myopt.n_translate_columns = lengthof(translate_columns);
 
 	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
 
@@ -1078,38 +1078,38 @@ permissionsList(const char *pattern, bool showSystem)
 					  "  ), E'\\n') AS \"%s\"",
 					  gettext_noop("Column privileges"));
 
-		appendPQExpBuffer(&buf,
-						  ",\n  pg_catalog.array_to_string(ARRAY(\n"
-						  "    SELECT polname\n"
-						  "    || CASE WHEN NOT polpermissive THEN\n"
-						  "       E' (RESTRICTIVE)'\n"
-						  "       ELSE '' END\n"
-						  "    || CASE WHEN polcmd != '*' THEN\n"
-						  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
-						  "       ELSE E':'\n"
-						  "       END\n"
-						  "    || CASE WHEN polqual IS NOT NULL THEN\n"
-						  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
-						  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END"
-						  "    || CASE WHEN polroles <> '{0}' THEN\n"
-						  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
-						  "               ARRAY(\n"
-						  "                   SELECT rolname\n"
-						  "                   FROM pg_catalog.pg_roles\n"
-						  "                   WHERE oid = ANY (polroles)\n"
-						  "                   ORDER BY 1\n"
-						  "               ), E', ')\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    FROM pg_catalog.pg_policy pol\n"
-						  "    WHERE polrelid = c.oid), E'\\n')\n"
-						  "    AS \"%s\"",
-						  gettext_noop("Policies"));
+	appendPQExpBuffer(&buf,
+					  ",\n  pg_catalog.array_to_string(ARRAY(\n"
+					  "    SELECT polname\n"
+					  "    || CASE WHEN NOT polpermissive THEN\n"
+					  "       E' (RESTRICTIVE)'\n"
+					  "       ELSE '' END\n"
+					  "    || CASE WHEN polcmd != '*' THEN\n"
+					  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
+					  "       ELSE E':'\n"
+					  "       END\n"
+					  "    || CASE WHEN polqual IS NOT NULL THEN\n"
+					  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
+					  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END"
+					  "    || CASE WHEN polroles <> '{0}' THEN\n"
+					  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
+					  "               ARRAY(\n"
+					  "                   SELECT rolname\n"
+					  "                   FROM pg_catalog.pg_roles\n"
+					  "                   WHERE oid = ANY (polroles)\n"
+					  "                   ORDER BY 1\n"
+					  "               ), E', ')\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    FROM pg_catalog.pg_policy pol\n"
+					  "    WHERE polrelid = c.oid), E'\\n')\n"
+					  "    AS \"%s\"",
+					  gettext_noop("Policies"));
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n"
 						 "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
@@ -1667,27 +1667,27 @@ describeOneTableDetails(const char *schemaname,
 		char	   *footers[3] = {NULL, NULL, NULL};
 
 		printfPQExpBuffer(&buf, "/* %s */\n", _("Get sequence information"));
-			appendPQExpBuffer(&buf,
-							  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
-							  "       seqstart AS \"%s\",\n"
-							  "       seqmin AS \"%s\",\n"
-							  "       seqmax AS \"%s\",\n"
-							  "       seqincrement AS \"%s\",\n"
-							  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
-							  "       seqcache AS \"%s\"\n",
-							  gettext_noop("Type"),
-							  gettext_noop("Start"),
-							  gettext_noop("Minimum"),
-							  gettext_noop("Maximum"),
-							  gettext_noop("Increment"),
-							  gettext_noop("yes"),
-							  gettext_noop("no"),
-							  gettext_noop("Cycles?"),
-							  gettext_noop("Cache"));
-			appendPQExpBuffer(&buf,
-							  "FROM pg_catalog.pg_sequence\n"
-							  "WHERE seqrelid = '%s';",
-							  oid);
+		appendPQExpBuffer(&buf,
+						  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
+						  "       seqstart AS \"%s\",\n"
+						  "       seqmin AS \"%s\",\n"
+						  "       seqmax AS \"%s\",\n"
+						  "       seqincrement AS \"%s\",\n"
+						  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
+						  "       seqcache AS \"%s\"\n",
+						  gettext_noop("Type"),
+						  gettext_noop("Start"),
+						  gettext_noop("Minimum"),
+						  gettext_noop("Maximum"),
+						  gettext_noop("Increment"),
+						  gettext_noop("yes"),
+						  gettext_noop("no"),
+						  gettext_noop("Cycles?"),
+						  gettext_noop("Cache"));
+		appendPQExpBuffer(&buf,
+						  "FROM pg_catalog.pg_sequence\n"
+						  "WHERE seqrelid = '%s';",
+						  oid);
 
 		res = PSQLexec(buf.data);
 		if (!res)
@@ -1905,7 +1905,7 @@ describeOneTableDetails(const char *schemaname,
 		appendPQExpBufferStr(&buf, ",\n  (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
 							 "   WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
 		attcoll_col = cols++;
-			appendPQExpBufferStr(&buf, ",\n  a.attidentity");
+		appendPQExpBufferStr(&buf, ",\n  a.attidentity");
 		attidentity_col = cols++;
 		if (pset.sversion >= 120000)
 			appendPQExpBufferStr(&buf, ",\n  a.attgenerated");
@@ -1916,11 +1916,11 @@ describeOneTableDetails(const char *schemaname,
 	if (tableinfo.relkind == RELKIND_INDEX ||
 		tableinfo.relkind == RELKIND_PARTITIONED_INDEX)
 	{
-			appendPQExpBuffer(&buf, ",\n  CASE WHEN a.attnum <= (SELECT i.indnkeyatts FROM pg_catalog.pg_index i WHERE i.indexrelid = '%s') THEN '%s' ELSE '%s' END AS is_key",
-							  oid,
-							  gettext_noop("yes"),
-							  gettext_noop("no"));
-			isindexkey_col = cols++;
+		appendPQExpBuffer(&buf, ",\n  CASE WHEN a.attnum <= (SELECT i.indnkeyatts FROM pg_catalog.pg_index i WHERE i.indexrelid = '%s') THEN '%s' ELSE '%s' END AS is_key",
+						  oid,
+						  gettext_noop("yes"),
+						  gettext_noop("no"));
+		isindexkey_col = cols++;
 		appendPQExpBufferStr(&buf, ",\n  pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
 		indexdef_col = cols++;
 	}
@@ -2315,7 +2315,7 @@ describeOneTableDetails(const char *schemaname,
 							 CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
 							 "condeferred) AS condeferred,\n");
 
-			appendPQExpBufferStr(&buf, "i.indisreplident,\n");
+		appendPQExpBufferStr(&buf, "i.indisreplident,\n");
 
 		if (pset.sversion >= 150000)
 			appendPQExpBufferStr(&buf, "i.indnullsnotdistinct,\n");
@@ -2420,7 +2420,7 @@ describeOneTableDetails(const char *schemaname,
 								 "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n  "
 								 "pg_catalog.pg_get_constraintdef(con.oid, true), "
 								 "contype, condeferrable, condeferred");
-				appendPQExpBufferStr(&buf, ", i.indisreplident");
+			appendPQExpBufferStr(&buf, ", i.indisreplident");
 			appendPQExpBufferStr(&buf, ", c2.reltablespace");
 			if (pset.sversion >= 180000)
 				appendPQExpBufferStr(&buf, ", con.conperiod");
@@ -2671,81 +2671,80 @@ describeOneTableDetails(const char *schemaname,
 		PQclear(result);
 
 		/* print any row-level policies */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get row-level policies for this table"));
-			appendPQExpBufferStr(&buf, "SELECT pol.polname,");
-				appendPQExpBufferStr(&buf,
-									 " pol.polpermissive,\n");
-			appendPQExpBuffer(&buf,
-							  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
-							  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
-							  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
-							  "  CASE pol.polcmd\n"
-							  "    WHEN 'r' THEN 'SELECT'\n"
-							  "    WHEN 'a' THEN 'INSERT'\n"
-							  "    WHEN 'w' THEN 'UPDATE'\n"
-							  "    WHEN 'd' THEN 'DELETE'\n"
-							  "    END AS cmd\n"
-							  "FROM pg_catalog.pg_policy pol\n"
-							  "WHERE pol.polrelid = '%s' ORDER BY 1;",
-							  oid);
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get row-level policies for this table"));
+		appendPQExpBufferStr(&buf, "SELECT pol.polname,");
+		appendPQExpBufferStr(&buf,
+							 " pol.polpermissive,\n");
+		appendPQExpBuffer(&buf,
+						  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
+						  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
+						  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
+						  "  CASE pol.polcmd\n"
+						  "    WHEN 'r' THEN 'SELECT'\n"
+						  "    WHEN 'a' THEN 'INSERT'\n"
+						  "    WHEN 'w' THEN 'UPDATE'\n"
+						  "    WHEN 'd' THEN 'DELETE'\n"
+						  "    END AS cmd\n"
+						  "FROM pg_catalog.pg_policy pol\n"
+						  "WHERE pol.polrelid = '%s' ORDER BY 1;",
+						  oid);
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			/*
-			 * Handle cases where RLS is enabled and there are policies, or
-			 * there aren't policies, or RLS isn't enabled but there are
-			 * policies
-			 */
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies:"));
+		/*
+		 * Handle cases where RLS is enabled and there are policies, or there
+		 * aren't policies, or RLS isn't enabled but there are policies
+		 */
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies:"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
 
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
 
-			if (!tableinfo.rowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (row security disabled):"));
+		if (!tableinfo.rowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (row security disabled):"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    POLICY \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    POLICY \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				if (*(PQgetvalue(result, i, 1)) == 'f')
-					appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
+			if (*(PQgetvalue(result, i, 1)) == 'f')
+				appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
 
-				if (!PQgetisnull(result, i, 5))
-					appendPQExpBuffer(&buf, " FOR %s",
-									  PQgetvalue(result, i, 5));
+			if (!PQgetisnull(result, i, 5))
+				appendPQExpBuffer(&buf, " FOR %s",
+								  PQgetvalue(result, i, 5));
 
-				if (!PQgetisnull(result, i, 2))
-				{
-					appendPQExpBuffer(&buf, "\n      TO %s",
-									  PQgetvalue(result, i, 2));
-				}
+			if (!PQgetisnull(result, i, 2))
+			{
+				appendPQExpBuffer(&buf, "\n      TO %s",
+								  PQgetvalue(result, i, 2));
+			}
 
-				if (!PQgetisnull(result, i, 3))
-					appendPQExpBuffer(&buf, "\n      USING (%s)",
-									  PQgetvalue(result, i, 3));
+			if (!PQgetisnull(result, i, 3))
+				appendPQExpBuffer(&buf, "\n      USING (%s)",
+								  PQgetvalue(result, i, 3));
 
-				if (!PQgetisnull(result, i, 4))
-					appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
-									  PQgetvalue(result, i, 4));
+			if (!PQgetisnull(result, i, 4))
+				appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
+								  PQgetvalue(result, i, 4));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* print any extended statistics */
 		if (pset.sversion >= 140000)
@@ -3014,115 +3013,115 @@ describeOneTableDetails(const char *schemaname,
 		}
 
 		/* print any publications */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get publications that publish this table"));
-			if (pset.sversion >= 150000)
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get publications that publish this table"));
+		if (pset.sversion >= 150000)
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
+							  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
+							  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "UNION\n"
+							  "SELECT pubname\n"
+							  "     , pg_get_expr(pr.prqual, c.oid)\n"
+							  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
+							  "         (SELECT string_agg(attname, ', ')\n"
+							  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
+							  "                pg_catalog.pg_attribute\n"
+							  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
+							  "        ELSE NULL END) "
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
+							  "WHERE pr.prrelid = '%s'\n",
+							  oid, oid, oid);
+
+			if (pset.sversion >= 190000)
 			{
+				/*
+				 * Skip entries where this relation appears in the
+				 * publication's EXCEPT list.
+				 */
 				appendPQExpBuffer(&buf,
+								  " AND NOT pr.prexcept\n"
+								  "UNION\n"
 								  "SELECT pubname\n"
 								  "     , NULL\n"
 								  "     , NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
-								  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
-								  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
-								  "UNION\n"
-								  "SELECT pubname\n"
-								  "     , pg_get_expr(pr.prqual, c.oid)\n"
-								  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
-								  "         (SELECT string_agg(attname, ', ')\n"
-								  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
-								  "                pg_catalog.pg_attribute\n"
-								  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
-								  "        ELSE NULL END) "
-								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
-								  "WHERE pr.prrelid = '%s'\n",
+								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+								  "     AND NOT EXISTS (\n"
+								  "     SELECT 1\n"
+								  "     FROM pg_catalog.pg_publication_rel pr\n"
+								  "     WHERE pr.prpubid = p.oid AND\n"
+								  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
+								  "ORDER BY 1;",
 								  oid, oid, oid);
-
-				if (pset.sversion >= 190000)
-				{
-					/*
-					 * Skip entries where this relation appears in the
-					 * publication's EXCEPT list.
-					 */
-					appendPQExpBuffer(&buf,
-									  " AND NOT pr.prexcept\n"
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "     , NULL\n"
-									  "     , NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "     AND NOT EXISTS (\n"
-									  "     SELECT 1\n"
-									  "     FROM pg_catalog.pg_publication_rel pr\n"
-									  "     WHERE pr.prpubid = p.oid AND\n"
-									  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
-									  "ORDER BY 1;",
-									  oid, oid, oid);
-				}
-				else
-				{
-					appendPQExpBuffer(&buf,
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "		, NULL\n"
-									  "		, NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "ORDER BY 1;",
-									  oid);
-				}
 			}
 			else
 			{
 				appendPQExpBuffer(&buf,
+								  "UNION\n"
 								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
-								  "FROM pg_catalog.pg_publication p\n"
-								  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "WHERE pr.prrelid = '%s'\n"
-								  "UNION ALL\n"
-								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
+								  "		, NULL\n"
+								  "		, NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
 								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
 								  "ORDER BY 1;",
-								  oid, oid);
+								  oid);
 			}
+		}
+		else
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "WHERE pr.prrelid = '%s'\n"
+							  "UNION ALL\n"
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "ORDER BY 1;",
+							  oid, oid);
+		}
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			if (tuples > 0)
-				printTableAddFooter(&cont, _("Included in publications:"));
+		if (tuples > 0)
+			printTableAddFooter(&cont, _("Included in publications:"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				/* column list (if any) */
-				if (!PQgetisnull(result, i, 2))
-					appendPQExpBuffer(&buf, " (%s)",
-									  PQgetvalue(result, i, 2));
+			/* column list (if any) */
+			if (!PQgetisnull(result, i, 2))
+				appendPQExpBuffer(&buf, " (%s)",
+								  PQgetvalue(result, i, 2));
 
-				/* row filter (if any) */
-				if (!PQgetisnull(result, i, 1))
-					appendPQExpBuffer(&buf, " WHERE %s",
-									  PQgetvalue(result, i, 1));
+			/* row filter (if any) */
+			if (!PQgetisnull(result, i, 1))
+				appendPQExpBuffer(&buf, " WHERE %s",
+								  PQgetvalue(result, i, 1));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* Print publications where the table is in the EXCEPT clause */
 		if (pset.sversion >= 190000)
@@ -3794,7 +3793,7 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		ncols++;
 	}
 	appendPQExpBufferStr(&buf, "\n, r.rolreplication");
-		appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
+	appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
@@ -3849,8 +3848,8 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
 			add_role_attribute(&buf, _("Replication"));
 
-			if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
-				add_role_attribute(&buf, _("Bypass RLS"));
+		if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
+			add_role_attribute(&buf, _("Bypass RLS"));
 
 		conns = atoi(PQgetvalue(res, i, 6));
 		if (conns >= 0)
@@ -5144,14 +5143,14 @@ listCollations(const char *pattern, bool verbose, bool showSystem)
 					  gettext_noop("Schema"),
 					  gettext_noop("Name"));
 
-		appendPQExpBuffer(&buf,
-						  "  CASE c.collprovider "
-						  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
-						  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
-						  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
-						  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
-						  "END AS \"%s\",\n",
-						  gettext_noop("Provider"));
+	appendPQExpBuffer(&buf,
+					  "  CASE c.collprovider "
+					  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
+					  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
+					  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
+					  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
+					  "END AS \"%s\",\n",
+					  gettext_noop("Provider"));
 
 	appendPQExpBuffer(&buf,
 					  "  c.collcollate AS \"%s\",\n"
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH--





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* [PATCH v2 4/4] run pgindent
@ 2026-05-01 19:38  Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: Nathan Bossart @ 2026-05-01 19:38 UTC (permalink / raw)

---
 src/bin/pg_dump/pg_dump.c              | 457 ++++++++++++-----------
 src/bin/pg_dump/pg_dumpall.c           |  30 +-
 src/bin/pg_upgrade/check.c             |  16 +-
 src/bin/pg_upgrade/exec.c              |   8 +-
 src/bin/pg_upgrade/multixact_rewrite.c |  80 ++--
 src/bin/pg_upgrade/pg_upgrade.c        |   2 +-
 src/bin/pg_upgrade/relfilenumber.c     |  54 +--
 src/bin/psql/command.c                 |  29 +-
 src/bin/psql/describe.c                | 483 ++++++++++++-------------
 9 files changed, 578 insertions(+), 581 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index eed9aaeb7c1..c05623b1889 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1491,8 +1491,8 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	 * Disable timeouts if supported.
 	 */
 	ExecuteSqlStatement(AH, "SET statement_timeout = 0");
-		ExecuteSqlStatement(AH, "SET lock_timeout = 0");
-		ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
+	ExecuteSqlStatement(AH, "SET lock_timeout = 0");
+	ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
 	if (AH->remoteVersion >= 170000)
 		ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
 
@@ -1505,10 +1505,10 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	/*
 	 * Adjust row-security mode, if supported.
 	 */
-		if (dopt->enable_row_security)
-			ExecuteSqlStatement(AH, "SET row_security = on");
-		else
-			ExecuteSqlStatement(AH, "SET row_security = off");
+	if (dopt->enable_row_security)
+		ExecuteSqlStatement(AH, "SET row_security = on");
+	else
+		ExecuteSqlStatement(AH, "SET row_security = off");
 
 	/*
 	 * For security reasons, we restrict the expansion of non-system views and
@@ -1955,7 +1955,7 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
 	if (fout->dopt->binary_upgrade)
 		dobj->dump = ext->dobj.dump;
 	else
-			dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
+		dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
 
 	return true;
 }
@@ -1989,9 +1989,9 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
 	else if (strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
 	{
 		/*
-		 * We dump out any ACLs defined in pg_catalog, if
-		 * they are interesting (and not the original ACLs which were set at
-		 * initdb time, see pg_init_privs).
+		 * We dump out any ACLs defined in pg_catalog, if they are interesting
+		 * (and not the original ACLs which were set at initdb time, see
+		 * pg_init_privs).
 		 */
 		nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
 	}
@@ -3298,7 +3298,7 @@ dumpDatabase(Archive *fout)
 						 "datcollate, datctype, datfrozenxid, "
 						 "datacl, acldefault('d', datdba) AS acldefault, "
 						 "datistemplate, datconnlimit, ");
-		appendPQExpBufferStr(dbQry, "datminmxid, ");
+	appendPQExpBufferStr(dbQry, "datminmxid, ");
 	if (fout->remoteVersion >= 170000)
 		appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
 	else if (fout->remoteVersion >= 150000)
@@ -3640,11 +3640,11 @@ dumpDatabase(Archive *fout)
 					ii_oid,
 					ii_relminmxid;
 
-			appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
-							  "FROM pg_catalog.pg_class\n"
-							  "WHERE oid IN (%u, %u, %u, %u);\n",
-							  LargeObjectRelationId, LargeObjectLOidPNIndexId,
-							  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
+		appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
+						  "FROM pg_catalog.pg_class\n"
+						  "WHERE oid IN (%u, %u, %u, %u);\n",
+						  LargeObjectRelationId, LargeObjectLOidPNIndexId,
+						  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
 
 		lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
 
@@ -4276,7 +4276,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 
 	printfPQExpBuffer(query,
 					  "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
-		appendPQExpBufferStr(query, "pol.polpermissive, ");
+	appendPQExpBufferStr(query, "pol.polpermissive, ");
 	appendPQExpBuffer(query,
 					  "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
 					  "   pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
@@ -6635,9 +6635,9 @@ getAccessMethods(Archive *fout)
 	 * Select all access methods from pg_am table.
 	 */
 	appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
-		appendPQExpBufferStr(query,
-							 "amtype, "
-							 "amhandler::pg_catalog.regproc AS amhandler ");
+	appendPQExpBufferStr(query,
+						 "amtype, "
+						 "amhandler::pg_catalog.regproc AS amhandler ");
 	appendPQExpBufferStr(query, "FROM pg_am");
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -6829,35 +6829,35 @@ getAggregates(Archive *fout)
 	 * Find all interesting aggregates.  See comment in getFuncs() for the
 	 * rationale behind the filtering logic.
 	 */
-		agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
-					 : "p.proisagg");
+	agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
+				 : "p.proisagg");
 
-		appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
-						  "p.proname AS aggname, "
-						  "p.pronamespace AS aggnamespace, "
-						  "p.pronargs, p.proargtypes, "
-						  "p.proowner, "
-						  "p.proacl AS aggacl, "
-						  "acldefault('f', p.proowner) AS acldefault "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s AND ("
-						  "p.pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog') OR "
-						  "p.proacl IS DISTINCT FROM pip.initprivs",
-						  agg_check);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
-		appendPQExpBufferChar(query, ')');
+	appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
+					  "p.proname AS aggname, "
+					  "p.pronamespace AS aggnamespace, "
+					  "p.pronargs, p.proargtypes, "
+					  "p.proowner, "
+					  "p.proacl AS aggacl, "
+					  "acldefault('f', p.proowner) AS acldefault "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s AND ("
+					  "p.pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog') OR "
+					  "p.proacl IS DISTINCT FROM pip.initprivs",
+					  agg_check);
+	if (dopt->binary_upgrade)
+		appendPQExpBufferStr(query,
+							 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -6959,53 +6959,53 @@ getFuncs(Archive *fout)
 	 * include them, since we want to dump extension members individually in
 	 * that mode.  Also, if they are used by casts or transforms then we need
 	 * to gather the information about them, though they won't be dumped if
-	 * they are built-in.  Also, include functions in
-	 * pg_catalog if they have an ACL different from what's shown in
-	 * pg_init_privs (so we have to join to pg_init_privs; annoying).
+	 * they are built-in.  Also, include functions in pg_catalog if they have
+	 * an ACL different from what's shown in pg_init_privs (so we have to join
+	 * to pg_init_privs; annoying).
 	 */
-		not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
-						 : "NOT p.proisagg");
+	not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
+					 : "NOT p.proisagg");
 
-		appendPQExpBuffer(query,
-						  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
-						  "p.pronargs, p.proargtypes, p.prorettype, "
-						  "p.proacl, "
-						  "acldefault('f', p.proowner) AS acldefault, "
-						  "p.pronamespace, "
-						  "p.proowner "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s"
-						  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
-						  "WHERE classid = 'pg_proc'::regclass AND "
-						  "objid = p.oid AND deptype = 'i')"
-						  "\n  AND ("
-						  "\n  pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog')"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
-						  "\n  WHERE pg_cast.oid > %u "
-						  "\n  AND p.oid = pg_cast.castfunc)"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
-						  "\n  WHERE pg_transform.oid > %u AND "
-						  "\n  (p.oid = pg_transform.trffromsql"
-						  "\n  OR p.oid = pg_transform.trftosql))",
-						  not_agg_check,
-						  g_last_builtin_oid,
-						  g_last_builtin_oid);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
+	appendPQExpBuffer(query,
+					  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
+					  "p.pronargs, p.proargtypes, p.prorettype, "
+					  "p.proacl, "
+					  "acldefault('f', p.proowner) AS acldefault, "
+					  "p.pronamespace, "
+					  "p.proowner "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s"
+					  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
+					  "WHERE classid = 'pg_proc'::regclass AND "
+					  "objid = p.oid AND deptype = 'i')"
+					  "\n  AND ("
+					  "\n  pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog')"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
+					  "\n  WHERE pg_cast.oid > %u "
+					  "\n  AND p.oid = pg_cast.castfunc)"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
+					  "\n  WHERE pg_transform.oid > %u AND "
+					  "\n  (p.oid = pg_transform.trffromsql"
+					  "\n  OR p.oid = pg_transform.trftosql))",
+					  not_agg_check,
+					  g_last_builtin_oid,
+					  g_last_builtin_oid);
+	if (dopt->binary_upgrade)
 		appendPQExpBufferStr(query,
-							 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
-		appendPQExpBufferChar(query, ')');
+							 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferStr(query,
+						 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -7254,31 +7254,31 @@ getTables(Archive *fout, int *numTables)
 		appendPQExpBufferStr(query,
 							 "c.relhasoids, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispopulated, ");
+	appendPQExpBufferStr(query,
+						 "c.relispopulated, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relreplident, ");
+	appendPQExpBufferStr(query,
+						 "c.relreplident, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relrowsecurity, c.relforcerowsecurity, ");
+	appendPQExpBufferStr(query,
+						 "c.relrowsecurity, c.relforcerowsecurity, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relminmxid, tc.relminmxid AS tminmxid, ");
+	appendPQExpBufferStr(query,
+						 "c.relminmxid, tc.relminmxid AS tminmxid, ");
 
-		appendPQExpBufferStr(query,
-							 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-							 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-							 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
+	appendPQExpBufferStr(query,
+						 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+						 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+						 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
 
-		appendPQExpBufferStr(query,
-							 "am.amname, ");
+	appendPQExpBufferStr(query,
+						 "am.amname, ");
 
-		appendPQExpBufferStr(query,
-							 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
+	appendPQExpBufferStr(query,
+						 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispartition AS ispartition ");
+	appendPQExpBufferStr(query,
+						 "c.relispartition AS ispartition ");
 
 	/*
 	 * Left join to pg_depend to pick up dependency info linking sequences to
@@ -7298,8 +7298,8 @@ getTables(Archive *fout, int *numTables)
 	/*
 	 * Left join to pg_am to pick up the amname.
 	 */
-		appendPQExpBufferStr(query,
-							 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
+	appendPQExpBufferStr(query,
+						 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
 
 	/*
 	 * We purposefully ignore toast OIDs for partitioned tables; the reason is
@@ -7870,8 +7870,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 						 "t.reloptions AS indreloptions, ");
 
 
-		appendPQExpBufferStr(query,
-							 "i.indisreplident, ");
+	appendPQExpBufferStr(query,
+						 "i.indisreplident, ");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(query,
@@ -9315,8 +9315,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 		appendPQExpBufferStr(q,
 							 "'' AS attcompression,\n");
 
-		appendPQExpBufferStr(q,
-							 "a.attidentity,\n");
+	appendPQExpBufferStr(q,
+						 "a.attidentity,\n");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(q,
@@ -10719,73 +10719,73 @@ getAdditionalACLs(Archive *fout)
 	PQclear(res);
 
 	/* Fetch initial-privileges data */
-		printfPQExpBuffer(query,
-						  "SELECT objoid, classoid, objsubid, privtype, initprivs "
-						  "FROM pg_init_privs");
+	printfPQExpBuffer(query,
+					  "SELECT objoid, classoid, objsubid, privtype, initprivs "
+					  "FROM pg_init_privs");
 
-		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
-		ntups = PQntuples(res);
-		for (i = 0; i < ntups; i++)
-		{
-			Oid			objoid = atooid(PQgetvalue(res, i, 0));
-			Oid			classoid = atooid(PQgetvalue(res, i, 1));
-			int			objsubid = atoi(PQgetvalue(res, i, 2));
-			char		privtype = *(PQgetvalue(res, i, 3));
-			char	   *initprivs = PQgetvalue(res, i, 4);
-			CatalogId	objId;
-			DumpableObject *dobj;
+	ntups = PQntuples(res);
+	for (i = 0; i < ntups; i++)
+	{
+		Oid			objoid = atooid(PQgetvalue(res, i, 0));
+		Oid			classoid = atooid(PQgetvalue(res, i, 1));
+		int			objsubid = atoi(PQgetvalue(res, i, 2));
+		char		privtype = *(PQgetvalue(res, i, 3));
+		char	   *initprivs = PQgetvalue(res, i, 4);
+		CatalogId	objId;
+		DumpableObject *dobj;
 
-			objId.tableoid = classoid;
-			objId.oid = objoid;
-			dobj = findObjectByCatalogId(objId);
-			/* OK to ignore entries we haven't got a DumpableObject for */
-			if (dobj)
+		objId.tableoid = classoid;
+		objId.oid = objoid;
+		dobj = findObjectByCatalogId(objId);
+		/* OK to ignore entries we haven't got a DumpableObject for */
+		if (dobj)
+		{
+			/* Cope with sub-object initprivs */
+			if (objsubid != 0)
 			{
-				/* Cope with sub-object initprivs */
-				if (objsubid != 0)
-				{
-					if (dobj->objType == DO_TABLE)
-					{
-						/* For a column initprivs, set the table's ACL flags */
-						dobj->components |= DUMP_COMPONENT_ACL;
-						((TableInfo *) dobj)->hascolumnACLs = true;
-					}
-					else
-						pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
-									   classoid, objoid, objsubid);
-					continue;
-				}
-
-				/*
-				 * We ignore any pg_init_privs.initprivs entry for the public
-				 * schema, as explained in getNamespaces().
-				 */
-				if (dobj->objType == DO_NAMESPACE &&
-					strcmp(dobj->name, "public") == 0)
-					continue;
-
-				/* Else it had better be of a type we think has ACLs */
-				if (dobj->objType == DO_NAMESPACE ||
-					dobj->objType == DO_TYPE ||
-					dobj->objType == DO_FUNC ||
-					dobj->objType == DO_AGG ||
-					dobj->objType == DO_TABLE ||
-					dobj->objType == DO_PROCLANG ||
-					dobj->objType == DO_FDW ||
-					dobj->objType == DO_FOREIGN_SERVER)
+				if (dobj->objType == DO_TABLE)
 				{
-					DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
-
-					daobj->dacl.privtype = privtype;
-					daobj->dacl.initprivs = pstrdup(initprivs);
+					/* For a column initprivs, set the table's ACL flags */
+					dobj->components |= DUMP_COMPONENT_ACL;
+					((TableInfo *) dobj)->hascolumnACLs = true;
 				}
 				else
 					pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
 								   classoid, objoid, objsubid);
+				continue;
+			}
+
+			/*
+			 * We ignore any pg_init_privs.initprivs entry for the public
+			 * schema, as explained in getNamespaces().
+			 */
+			if (dobj->objType == DO_NAMESPACE &&
+				strcmp(dobj->name, "public") == 0)
+				continue;
+
+			/* Else it had better be of a type we think has ACLs */
+			if (dobj->objType == DO_NAMESPACE ||
+				dobj->objType == DO_TYPE ||
+				dobj->objType == DO_FUNC ||
+				dobj->objType == DO_AGG ||
+				dobj->objType == DO_TABLE ||
+				dobj->objType == DO_PROCLANG ||
+				dobj->objType == DO_FDW ||
+				dobj->objType == DO_FOREIGN_SERVER)
+			{
+				DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
+
+				daobj->dacl.privtype = privtype;
+				daobj->dacl.initprivs = pstrdup(initprivs);
 			}
+			else
+				pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
+							   classoid, objoid, objsubid);
 		}
-		PQclear(res);
+	}
+	PQclear(res);
 
 	destroyPQExpBuffer(query);
 }
@@ -11130,8 +11130,8 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 		 * The results must be in the order of the relations supplied in the
 		 * parameters to ensure we remain in sync as we walk through the TOC.
 		 *
-		 * For versions before 19, the redundant filter clause on s.tablename =
-		 * ANY(...) seems sufficient to convince the planner to use
+		 * For versions before 19, the redundant filter clause on s.tablename
+		 * = ANY(...) seems sufficient to convince the planner to use
 		 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
 		 * In newer versions, pg_stats returns the table OIDs, eliminating the
 		 * need for that hack.
@@ -13471,11 +13471,11 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
 							 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
 							 "proleakproof,\n");
 
-			appendPQExpBufferStr(query,
-								 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
+		appendPQExpBufferStr(query,
+							 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
 
-			appendPQExpBufferStr(query,
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -14965,9 +14965,9 @@ dumpCollation(Archive *fout, const CollInfo *collinfo)
 	/* Get collation-specific details */
 	appendPQExpBufferStr(query, "SELECT ");
 
-		appendPQExpBufferStr(query,
-							 "collprovider, "
-							 "collversion, ");
+	appendPQExpBufferStr(query,
+						 "collprovider, "
+						 "collversion, ");
 
 	if (fout->remoteVersion >= 120000)
 		appendPQExpBufferStr(query,
@@ -15374,23 +15374,23 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 							 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
 							 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggkind,\n"
-								 "aggmtransfn,\n"
-								 "aggminvtransfn,\n"
-								 "aggmfinalfn,\n"
-								 "aggmtranstype::pg_catalog.regtype,\n"
-								 "aggfinalextra,\n"
-								 "aggmfinalextra,\n"
-								 "aggtransspace,\n"
-								 "aggmtransspace,\n"
-								 "aggminitval,\n");
+		appendPQExpBufferStr(query,
+							 "aggkind,\n"
+							 "aggmtransfn,\n"
+							 "aggminvtransfn,\n"
+							 "aggmfinalfn,\n"
+							 "aggmtranstype::pg_catalog.regtype,\n"
+							 "aggfinalextra,\n"
+							 "aggmfinalextra,\n"
+							 "aggtransspace,\n"
+							 "aggmtransspace,\n"
+							 "aggminitval,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggcombinefn,\n"
-								 "aggserialfn,\n"
-								 "aggdeserialfn,\n"
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "aggcombinefn,\n"
+							 "aggserialfn,\n"
+							 "aggdeserialfn,\n"
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -16850,30 +16850,30 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
 			appendPQExpBufferStr(query,
 								 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
 
-				/*
-				 * In principle we should call acldefault('c', relowner) to
-				 * get the default ACL for a column.  However, we don't
-				 * currently store the numeric OID of the relowner in
-				 * TableInfo.  We could convert the owner name using regrole,
-				 * but that creates a risk of failure due to concurrent role
-				 * renames.  Given that the default ACL for columns is empty
-				 * and is likely to stay that way, it's not worth extra cycles
-				 * and risk to avoid hard-wiring that knowledge here.
-				 */
-				appendPQExpBufferStr(query,
-									 "SELECT at.attname, "
-									 "at.attacl, "
-									 "'{}' AS acldefault, "
-									 "pip.privtype, pip.initprivs "
-									 "FROM pg_catalog.pg_attribute at "
-									 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
-									 "(at.attrelid = pip.objoid "
-									 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
-									 "AND at.attnum = pip.objsubid) "
-									 "WHERE at.attrelid = $1 AND "
-									 "NOT at.attisdropped "
-									 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
-									 "ORDER BY at.attnum");
+			/*
+			 * In principle we should call acldefault('c', relowner) to get
+			 * the default ACL for a column.  However, we don't currently
+			 * store the numeric OID of the relowner in TableInfo.  We could
+			 * convert the owner name using regrole, but that creates a risk
+			 * of failure due to concurrent role renames.  Given that the
+			 * default ACL for columns is empty and is likely to stay that
+			 * way, it's not worth extra cycles and risk to avoid hard-wiring
+			 * that knowledge here.
+			 */
+			appendPQExpBufferStr(query,
+								 "SELECT at.attname, "
+								 "at.attacl, "
+								 "'{}' AS acldefault, "
+								 "pip.privtype, pip.initprivs "
+								 "FROM pg_catalog.pg_attribute at "
+								 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
+								 "(at.attrelid = pip.objoid "
+								 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
+								 "AND at.attnum = pip.objsubid) "
+								 "WHERE at.attrelid = $1 AND "
+								 "NOT at.attisdropped "
+								 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
+								 "ORDER BY at.attnum");
 
 			ExecuteSqlStatement(fout, query->data);
 
@@ -19167,7 +19167,7 @@ collectSequences(Archive *fout)
 	 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
 	 */
 	if (fout->remoteVersion < 180000 ||
-			 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
+		(!fout->dopt->dumpData && !fout->dopt->sequence_data))
 		query = "SELECT seqrelid, format_type(seqtypid, NULL), "
 			"seqstart, seqincrement, "
 			"seqmax, seqmin, "
@@ -19229,15 +19229,14 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
 
 	/*
-	 * The sequence information is gathered in a sorted
-	 * table before any calls to dumpSequence().  See collectSequences() for
-	 * more information.
+	 * The sequence information is gathered in a sorted table before any calls
+	 * to dumpSequence().  See collectSequences() for more information.
 	 */
-		Assert(sequences);
+	Assert(sequences);
 
-		key.oid = tbinfo->dobj.catId.oid;
-		seq = bsearch(&key, sequences, nsequences,
-					  sizeof(SequenceItem), SequenceItemCmp);
+	key.oid = tbinfo->dobj.catId.oid;
+	seq = bsearch(&key, sequences, nsequences,
+				  sizeof(SequenceItem), SequenceItemCmp);
 
 	/* Calculate default limits for a sequence of this type */
 	is_ascending = (seq->incby >= 0);
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 5b10f7122b7..3f61196671c 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -954,11 +954,11 @@ dropRoles(PGconn *conn)
 	int			i_rolname;
 	int			i;
 
-		printfPQExpBuffer(buf,
-						  "SELECT rolname "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 1", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT rolname "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 1", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
@@ -1035,16 +1035,16 @@ dumpRoles(PGconn *conn)
 	 * Notes: rolconfig is dumped later, and pg_authid must be used for
 	 * extracting rolcomment regardless of role_catalog.
 	 */
-		printfPQExpBuffer(buf,
-						  "SELECT oid, rolname, rolsuper, rolinherit, "
-						  "rolcreaterole, rolcreatedb, "
-						  "rolcanlogin, rolconnlimit, rolpassword, "
-						  "rolvaliduntil, rolreplication, rolbypassrls, "
-						  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
-						  "rolname = current_user AS is_current_user "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 2", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT oid, rolname, rolsuper, rolinherit, "
+					  "rolcreaterole, rolcreatedb, "
+					  "rolcanlogin, rolconnlimit, rolpassword, "
+					  "rolvaliduntil, rolreplication, rolbypassrls, "
+					  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
+					  "rolname = current_user AS is_current_user "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 2", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 0813cef2729..5f63e2114c8 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -1475,15 +1475,15 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_remove(anyarray,anyelement)'"
-							 ", 'array_replace(anyarray,anyelement,anyelement)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_remove(anyarray,anyelement)'"
+						 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_position(anyarray,anyelement)'"
-							 ", 'array_position(anyarray,anyelement,integer)'"
-							 ", 'array_positions(anyarray,anyelement)'"
-							 ", 'width_bucket(anyelement,anyarray)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_position(anyarray,anyelement)'"
+						 ", 'array_position(anyarray,anyelement,integer)'"
+						 ", 'array_positions(anyarray,anyelement)'"
+						 ", 'width_bucket(anyelement,anyarray)'");
 
 	/*
 	 * The query below hardcodes FirstNormalObjectId as 16384 rather than
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 479557abdcc..9a675929e17 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,7 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-		cluster->bin_version = v1 * 10000;
+	cluster->bin_version = v1 * 10000;
 }
 
 
@@ -344,8 +344,8 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-		check_single_dir(pg_data, "pg_wal");
-		check_single_dir(pg_data, "pg_xact");
+	check_single_dir(pg_data, "pg_wal");
+	check_single_dir(pg_data, "pg_xact");
 }
 
 
@@ -385,7 +385,7 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-		check_exec(cluster->bindir, "pg_resetwal", check_versions);
+	check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
 	{
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index c45b3183684..c7a1416494d 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -61,52 +61,52 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
-										   old_cluster.controldata.chkpnt_nxtmulti,
-										   old_cluster.controldata.chkpnt_nxtmxoff);
+	old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
+									   old_cluster.controldata.chkpnt_nxtmulti,
+									   old_cluster.controldata.chkpnt_nxtmxoff);
 
-		for (MultiXactId multi = from_multi; multi != to_multi;)
-		{
-			MultiXactMember member;
-			bool		multixid_valid;
-
-			/*
-			 * Read this multixid's members.
-			 *
-			 * Locking-only XIDs that may be part of multi-xids don't matter
-			 * after upgrade, as there can be no transactions running across
-			 * upgrade.  So as a small optimization, we only read one member
-			 * from each multixid: the one updating one, or if there was no
-			 * update, arbitrarily the first locking xid.
-			 */
-			multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
+	for (MultiXactId multi = from_multi; multi != to_multi;)
+	{
+		MultiXactMember member;
+		bool		multixid_valid;
 
-			/*
-			 * Write the new offset to pg_multixact/offsets.
-			 *
-			 * Even if this multixid is invalid, we still need to write its
-			 * offset if the *previous* multixid was valid.  That's because
-			 * when reading a multixid, the number of members is calculated
-			 * from the difference between the two offsets.
-			 */
-			RecordMultiXactOffset(offsets_writer, multi,
-								  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
+		/*
+		 * Read this multixid's members.
+		 *
+		 * Locking-only XIDs that may be part of multi-xids don't matter after
+		 * upgrade, as there can be no transactions running across upgrade. So
+		 * as a small optimization, we only read one member from each
+		 * multixid: the one updating one, or if there was no update,
+		 * arbitrarily the first locking xid.
+		 */
+		multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
 
-			/* Write the members */
-			if (multixid_valid)
-			{
-				RecordMultiXactMembers(members_writer, next_offset, 1, &member);
-				next_offset += 1;
-			}
+		/*
+		 * Write the new offset to pg_multixact/offsets.
+		 *
+		 * Even if this multixid is invalid, we still need to write its offset
+		 * if the *previous* multixid was valid.  That's because when reading
+		 * a multixid, the number of members is calculated from the difference
+		 * between the two offsets.
+		 */
+		RecordMultiXactOffset(offsets_writer, multi,
+							  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
 
-			/* Advance to next multixid, handling wraparound */
-			multi++;
-			if (multi < FirstMultiXactId)
-				multi = FirstMultiXactId;
-			prev_multixid_valid = multixid_valid;
+		/* Write the members */
+		if (multixid_valid)
+		{
+			RecordMultiXactMembers(members_writer, next_offset, 1, &member);
+			next_offset += 1;
 		}
 
-		FreeOldMultiXactReader(old_reader);
+		/* Advance to next multixid, handling wraparound */
+		multi++;
+		if (multi < FirstMultiXactId)
+			multi = FirstMultiXactId;
+		prev_multixid_valid = multixid_valid;
+	}
+
+	FreeOldMultiXactReader(old_reader);
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index e5d7920c1b1..d8e1b680f5a 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -833,7 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
+		oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index ec2ff7acb21..6c467bdc8a5 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -587,32 +587,32 @@ transfer_relfile(FileNameMap *map, const char *type_suffix)
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-			switch (user_opts.transfer_mode)
-			{
-				case TRANSFER_MODE_CLONE:
-					pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
-						   old_file, new_file);
-					cloneFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
-						   old_file, new_file);
-					copyFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY_FILE_RANGE:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
-						   old_file, new_file);
-					copyFileByRange(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_LINK:
-					pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
-						   old_file, new_file);
-					linkFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_SWAP:
-					/* swap mode is handled in its own code path */
-					pg_fatal("should never happen");
-					break;
-			}
+		switch (user_opts.transfer_mode)
+		{
+			case TRANSFER_MODE_CLONE:
+				pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
+					   old_file, new_file);
+				cloneFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
+					   old_file, new_file);
+				copyFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY_FILE_RANGE:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
+					   old_file, new_file);
+				copyFileByRange(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_LINK:
+				pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
+					   old_file, new_file);
+				linkFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_SWAP:
+				/* swap mode is handled in its own code path */
+				pg_fatal("should never happen");
+				break;
+		}
 	}
 }
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c9573d4b765..e5fb3595598 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -6272,23 +6272,22 @@ get_create_object_cmd(EditableObjectType obj_type, Oid oid,
 			 * ensure the right view gets replaced.  Also, check relation kind
 			 * to be sure it's a view.
 			 *
-			 * Views may have WITH [LOCAL|CASCADED]
-			 * CHECK OPTION.  These are not part of the view definition
-			 * returned by pg_get_viewdef() and so need to be retrieved
-			 * separately.  Materialized views may have
-			 * arbitrary storage parameter reloptions.
+			 * Views may have WITH [LOCAL|CASCADED] CHECK OPTION.  These are
+			 * not part of the view definition returned by pg_get_viewdef()
+			 * and so need to be retrieved separately.  Materialized views may
+			 * have arbitrary storage parameter reloptions.
 			 */
 			printfPQExpBuffer(query, "/* %s */\n", _("Get view's definition and details"));
-				appendPQExpBuffer(query,
-								  "SELECT nspname, relname, relkind, "
-								  "pg_catalog.pg_get_viewdef(c.oid, true), "
-								  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-								  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-								  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
-								  "FROM pg_catalog.pg_class c "
-								  "LEFT JOIN pg_catalog.pg_namespace n "
-								  "ON c.relnamespace = n.oid WHERE c.oid = %u",
-								  oid);
+			appendPQExpBuffer(query,
+							  "SELECT nspname, relname, relkind, "
+							  "pg_catalog.pg_get_viewdef(c.oid, true), "
+							  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+							  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+							  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
+							  "FROM pg_catalog.pg_class c "
+							  "LEFT JOIN pg_catalog.pg_namespace n "
+							  "ON c.relnamespace = n.oid WHERE c.oid = %u",
+							  oid);
 			break;
 	}
 
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 76d299fb55c..389c4dce367 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -98,12 +98,12 @@ describeAggregates(const char *pattern, bool verbose, bool showSystem)
 					  gettext_noop("Result data type"),
 					  gettext_noop("Argument data types"));
 
-		appendPQExpBuffer(&buf,
-						  "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
-						  "FROM pg_catalog.pg_proc p\n"
-						  "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
-						  "WHERE p.prokind = " CppAsString2(PROKIND_AGGREGATE) "\n",
-						  gettext_noop("Description"));
+	appendPQExpBuffer(&buf,
+					  "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
+					  "FROM pg_catalog.pg_proc p\n"
+					  "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
+					  "WHERE p.prokind = " CppAsString2(PROKIND_AGGREGATE) "\n",
+					  gettext_noop("Description"));
 
 	if (!showSystem && !pattern)
 		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
@@ -379,19 +379,19 @@ describeFunctions(const char *functypes, const char *func_pattern,
 						  gettext_noop("stable"),
 						  gettext_noop("volatile"),
 						  gettext_noop("Volatility"));
-			appendPQExpBuffer(&buf,
-							  ",\n CASE\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
-							  " END as \"%s\"",
-							  gettext_noop("restricted"),
-							  gettext_noop("safe"),
-							  gettext_noop("unsafe"),
-							  gettext_noop("Parallel"));
+		appendPQExpBuffer(&buf,
+						  ",\n CASE\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
+						  " END as \"%s\"",
+						  gettext_noop("restricted"),
+						  gettext_noop("safe"),
+						  gettext_noop("unsafe"),
+						  gettext_noop("Parallel"));
 		appendPQExpBuffer(&buf,
 						  ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\""
 						  ",\n CASE WHEN prosecdef THEN '%s' ELSE '%s' END AS \"%s\""
@@ -591,8 +591,8 @@ describeFunctions(const char *functypes, const char *func_pattern,
 
 	myopt.title = _("List of functions");
 	myopt.translate_header = true;
-		myopt.translate_columns = translate_columns;
-		myopt.n_translate_columns = lengthof(translate_columns);
+	myopt.translate_columns = translate_columns;
+	myopt.n_translate_columns = lengthof(translate_columns);
 
 	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
 
@@ -1078,38 +1078,38 @@ permissionsList(const char *pattern, bool showSystem)
 					  "  ), E'\\n') AS \"%s\"",
 					  gettext_noop("Column privileges"));
 
-		appendPQExpBuffer(&buf,
-						  ",\n  pg_catalog.array_to_string(ARRAY(\n"
-						  "    SELECT polname\n"
-						  "    || CASE WHEN NOT polpermissive THEN\n"
-						  "       E' (RESTRICTIVE)'\n"
-						  "       ELSE '' END\n"
-						  "    || CASE WHEN polcmd != '*' THEN\n"
-						  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
-						  "       ELSE E':'\n"
-						  "       END\n"
-						  "    || CASE WHEN polqual IS NOT NULL THEN\n"
-						  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
-						  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END"
-						  "    || CASE WHEN polroles <> '{0}' THEN\n"
-						  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
-						  "               ARRAY(\n"
-						  "                   SELECT rolname\n"
-						  "                   FROM pg_catalog.pg_roles\n"
-						  "                   WHERE oid = ANY (polroles)\n"
-						  "                   ORDER BY 1\n"
-						  "               ), E', ')\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    FROM pg_catalog.pg_policy pol\n"
-						  "    WHERE polrelid = c.oid), E'\\n')\n"
-						  "    AS \"%s\"",
-						  gettext_noop("Policies"));
+	appendPQExpBuffer(&buf,
+					  ",\n  pg_catalog.array_to_string(ARRAY(\n"
+					  "    SELECT polname\n"
+					  "    || CASE WHEN NOT polpermissive THEN\n"
+					  "       E' (RESTRICTIVE)'\n"
+					  "       ELSE '' END\n"
+					  "    || CASE WHEN polcmd != '*' THEN\n"
+					  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
+					  "       ELSE E':'\n"
+					  "       END\n"
+					  "    || CASE WHEN polqual IS NOT NULL THEN\n"
+					  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
+					  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END"
+					  "    || CASE WHEN polroles <> '{0}' THEN\n"
+					  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
+					  "               ARRAY(\n"
+					  "                   SELECT rolname\n"
+					  "                   FROM pg_catalog.pg_roles\n"
+					  "                   WHERE oid = ANY (polroles)\n"
+					  "                   ORDER BY 1\n"
+					  "               ), E', ')\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    FROM pg_catalog.pg_policy pol\n"
+					  "    WHERE polrelid = c.oid), E'\\n')\n"
+					  "    AS \"%s\"",
+					  gettext_noop("Policies"));
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n"
 						 "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
@@ -1667,27 +1667,27 @@ describeOneTableDetails(const char *schemaname,
 		char	   *footers[3] = {NULL, NULL, NULL};
 
 		printfPQExpBuffer(&buf, "/* %s */\n", _("Get sequence information"));
-			appendPQExpBuffer(&buf,
-							  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
-							  "       seqstart AS \"%s\",\n"
-							  "       seqmin AS \"%s\",\n"
-							  "       seqmax AS \"%s\",\n"
-							  "       seqincrement AS \"%s\",\n"
-							  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
-							  "       seqcache AS \"%s\"\n",
-							  gettext_noop("Type"),
-							  gettext_noop("Start"),
-							  gettext_noop("Minimum"),
-							  gettext_noop("Maximum"),
-							  gettext_noop("Increment"),
-							  gettext_noop("yes"),
-							  gettext_noop("no"),
-							  gettext_noop("Cycles?"),
-							  gettext_noop("Cache"));
-			appendPQExpBuffer(&buf,
-							  "FROM pg_catalog.pg_sequence\n"
-							  "WHERE seqrelid = '%s';",
-							  oid);
+		appendPQExpBuffer(&buf,
+						  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
+						  "       seqstart AS \"%s\",\n"
+						  "       seqmin AS \"%s\",\n"
+						  "       seqmax AS \"%s\",\n"
+						  "       seqincrement AS \"%s\",\n"
+						  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
+						  "       seqcache AS \"%s\"\n",
+						  gettext_noop("Type"),
+						  gettext_noop("Start"),
+						  gettext_noop("Minimum"),
+						  gettext_noop("Maximum"),
+						  gettext_noop("Increment"),
+						  gettext_noop("yes"),
+						  gettext_noop("no"),
+						  gettext_noop("Cycles?"),
+						  gettext_noop("Cache"));
+		appendPQExpBuffer(&buf,
+						  "FROM pg_catalog.pg_sequence\n"
+						  "WHERE seqrelid = '%s';",
+						  oid);
 
 		res = PSQLexec(buf.data);
 		if (!res)
@@ -1905,7 +1905,7 @@ describeOneTableDetails(const char *schemaname,
 		appendPQExpBufferStr(&buf, ",\n  (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
 							 "   WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
 		attcoll_col = cols++;
-			appendPQExpBufferStr(&buf, ",\n  a.attidentity");
+		appendPQExpBufferStr(&buf, ",\n  a.attidentity");
 		attidentity_col = cols++;
 		if (pset.sversion >= 120000)
 			appendPQExpBufferStr(&buf, ",\n  a.attgenerated");
@@ -1916,11 +1916,11 @@ describeOneTableDetails(const char *schemaname,
 	if (tableinfo.relkind == RELKIND_INDEX ||
 		tableinfo.relkind == RELKIND_PARTITIONED_INDEX)
 	{
-			appendPQExpBuffer(&buf, ",\n  CASE WHEN a.attnum <= (SELECT i.indnkeyatts FROM pg_catalog.pg_index i WHERE i.indexrelid = '%s') THEN '%s' ELSE '%s' END AS is_key",
-							  oid,
-							  gettext_noop("yes"),
-							  gettext_noop("no"));
-			isindexkey_col = cols++;
+		appendPQExpBuffer(&buf, ",\n  CASE WHEN a.attnum <= (SELECT i.indnkeyatts FROM pg_catalog.pg_index i WHERE i.indexrelid = '%s') THEN '%s' ELSE '%s' END AS is_key",
+						  oid,
+						  gettext_noop("yes"),
+						  gettext_noop("no"));
+		isindexkey_col = cols++;
 		appendPQExpBufferStr(&buf, ",\n  pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
 		indexdef_col = cols++;
 	}
@@ -2315,7 +2315,7 @@ describeOneTableDetails(const char *schemaname,
 							 CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
 							 "condeferred) AS condeferred,\n");
 
-			appendPQExpBufferStr(&buf, "i.indisreplident,\n");
+		appendPQExpBufferStr(&buf, "i.indisreplident,\n");
 
 		if (pset.sversion >= 150000)
 			appendPQExpBufferStr(&buf, "i.indnullsnotdistinct,\n");
@@ -2420,7 +2420,7 @@ describeOneTableDetails(const char *schemaname,
 								 "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n  "
 								 "pg_catalog.pg_get_constraintdef(con.oid, true), "
 								 "contype, condeferrable, condeferred");
-				appendPQExpBufferStr(&buf, ", i.indisreplident");
+			appendPQExpBufferStr(&buf, ", i.indisreplident");
 			appendPQExpBufferStr(&buf, ", c2.reltablespace");
 			if (pset.sversion >= 180000)
 				appendPQExpBufferStr(&buf, ", con.conperiod");
@@ -2671,81 +2671,80 @@ describeOneTableDetails(const char *schemaname,
 		PQclear(result);
 
 		/* print any row-level policies */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get row-level policies for this table"));
-			appendPQExpBufferStr(&buf, "SELECT pol.polname,");
-				appendPQExpBufferStr(&buf,
-									 " pol.polpermissive,\n");
-			appendPQExpBuffer(&buf,
-							  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
-							  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
-							  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
-							  "  CASE pol.polcmd\n"
-							  "    WHEN 'r' THEN 'SELECT'\n"
-							  "    WHEN 'a' THEN 'INSERT'\n"
-							  "    WHEN 'w' THEN 'UPDATE'\n"
-							  "    WHEN 'd' THEN 'DELETE'\n"
-							  "    END AS cmd\n"
-							  "FROM pg_catalog.pg_policy pol\n"
-							  "WHERE pol.polrelid = '%s' ORDER BY 1;",
-							  oid);
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get row-level policies for this table"));
+		appendPQExpBufferStr(&buf, "SELECT pol.polname,");
+		appendPQExpBufferStr(&buf,
+							 " pol.polpermissive,\n");
+		appendPQExpBuffer(&buf,
+						  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
+						  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
+						  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
+						  "  CASE pol.polcmd\n"
+						  "    WHEN 'r' THEN 'SELECT'\n"
+						  "    WHEN 'a' THEN 'INSERT'\n"
+						  "    WHEN 'w' THEN 'UPDATE'\n"
+						  "    WHEN 'd' THEN 'DELETE'\n"
+						  "    END AS cmd\n"
+						  "FROM pg_catalog.pg_policy pol\n"
+						  "WHERE pol.polrelid = '%s' ORDER BY 1;",
+						  oid);
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			/*
-			 * Handle cases where RLS is enabled and there are policies, or
-			 * there aren't policies, or RLS isn't enabled but there are
-			 * policies
-			 */
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies:"));
+		/*
+		 * Handle cases where RLS is enabled and there are policies, or there
+		 * aren't policies, or RLS isn't enabled but there are policies
+		 */
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies:"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
 
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
 
-			if (!tableinfo.rowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (row security disabled):"));
+		if (!tableinfo.rowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (row security disabled):"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    POLICY \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    POLICY \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				if (*(PQgetvalue(result, i, 1)) == 'f')
-					appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
+			if (*(PQgetvalue(result, i, 1)) == 'f')
+				appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
 
-				if (!PQgetisnull(result, i, 5))
-					appendPQExpBuffer(&buf, " FOR %s",
-									  PQgetvalue(result, i, 5));
+			if (!PQgetisnull(result, i, 5))
+				appendPQExpBuffer(&buf, " FOR %s",
+								  PQgetvalue(result, i, 5));
 
-				if (!PQgetisnull(result, i, 2))
-				{
-					appendPQExpBuffer(&buf, "\n      TO %s",
-									  PQgetvalue(result, i, 2));
-				}
+			if (!PQgetisnull(result, i, 2))
+			{
+				appendPQExpBuffer(&buf, "\n      TO %s",
+								  PQgetvalue(result, i, 2));
+			}
 
-				if (!PQgetisnull(result, i, 3))
-					appendPQExpBuffer(&buf, "\n      USING (%s)",
-									  PQgetvalue(result, i, 3));
+			if (!PQgetisnull(result, i, 3))
+				appendPQExpBuffer(&buf, "\n      USING (%s)",
+								  PQgetvalue(result, i, 3));
 
-				if (!PQgetisnull(result, i, 4))
-					appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
-									  PQgetvalue(result, i, 4));
+			if (!PQgetisnull(result, i, 4))
+				appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
+								  PQgetvalue(result, i, 4));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* print any extended statistics */
 		if (pset.sversion >= 140000)
@@ -3014,115 +3013,115 @@ describeOneTableDetails(const char *schemaname,
 		}
 
 		/* print any publications */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get publications that publish this table"));
-			if (pset.sversion >= 150000)
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get publications that publish this table"));
+		if (pset.sversion >= 150000)
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
+							  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
+							  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "UNION\n"
+							  "SELECT pubname\n"
+							  "     , pg_get_expr(pr.prqual, c.oid)\n"
+							  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
+							  "         (SELECT string_agg(attname, ', ')\n"
+							  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
+							  "                pg_catalog.pg_attribute\n"
+							  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
+							  "        ELSE NULL END) "
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
+							  "WHERE pr.prrelid = '%s'\n",
+							  oid, oid, oid);
+
+			if (pset.sversion >= 190000)
 			{
+				/*
+				 * Skip entries where this relation appears in the
+				 * publication's EXCEPT list.
+				 */
 				appendPQExpBuffer(&buf,
+								  " AND NOT pr.prexcept\n"
+								  "UNION\n"
 								  "SELECT pubname\n"
 								  "     , NULL\n"
 								  "     , NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
-								  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
-								  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
-								  "UNION\n"
-								  "SELECT pubname\n"
-								  "     , pg_get_expr(pr.prqual, c.oid)\n"
-								  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
-								  "         (SELECT string_agg(attname, ', ')\n"
-								  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
-								  "                pg_catalog.pg_attribute\n"
-								  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
-								  "        ELSE NULL END) "
-								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
-								  "WHERE pr.prrelid = '%s'\n",
+								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+								  "     AND NOT EXISTS (\n"
+								  "     SELECT 1\n"
+								  "     FROM pg_catalog.pg_publication_rel pr\n"
+								  "     WHERE pr.prpubid = p.oid AND\n"
+								  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
+								  "ORDER BY 1;",
 								  oid, oid, oid);
-
-				if (pset.sversion >= 190000)
-				{
-					/*
-					 * Skip entries where this relation appears in the
-					 * publication's EXCEPT list.
-					 */
-					appendPQExpBuffer(&buf,
-									  " AND NOT pr.prexcept\n"
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "     , NULL\n"
-									  "     , NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "     AND NOT EXISTS (\n"
-									  "     SELECT 1\n"
-									  "     FROM pg_catalog.pg_publication_rel pr\n"
-									  "     WHERE pr.prpubid = p.oid AND\n"
-									  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
-									  "ORDER BY 1;",
-									  oid, oid, oid);
-				}
-				else
-				{
-					appendPQExpBuffer(&buf,
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "		, NULL\n"
-									  "		, NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "ORDER BY 1;",
-									  oid);
-				}
 			}
 			else
 			{
 				appendPQExpBuffer(&buf,
+								  "UNION\n"
 								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
-								  "FROM pg_catalog.pg_publication p\n"
-								  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "WHERE pr.prrelid = '%s'\n"
-								  "UNION ALL\n"
-								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
+								  "		, NULL\n"
+								  "		, NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
 								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
 								  "ORDER BY 1;",
-								  oid, oid);
+								  oid);
 			}
+		}
+		else
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "WHERE pr.prrelid = '%s'\n"
+							  "UNION ALL\n"
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "ORDER BY 1;",
+							  oid, oid);
+		}
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			if (tuples > 0)
-				printTableAddFooter(&cont, _("Included in publications:"));
+		if (tuples > 0)
+			printTableAddFooter(&cont, _("Included in publications:"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				/* column list (if any) */
-				if (!PQgetisnull(result, i, 2))
-					appendPQExpBuffer(&buf, " (%s)",
-									  PQgetvalue(result, i, 2));
+			/* column list (if any) */
+			if (!PQgetisnull(result, i, 2))
+				appendPQExpBuffer(&buf, " (%s)",
+								  PQgetvalue(result, i, 2));
 
-				/* row filter (if any) */
-				if (!PQgetisnull(result, i, 1))
-					appendPQExpBuffer(&buf, " WHERE %s",
-									  PQgetvalue(result, i, 1));
+			/* row filter (if any) */
+			if (!PQgetisnull(result, i, 1))
+				appendPQExpBuffer(&buf, " WHERE %s",
+								  PQgetvalue(result, i, 1));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* Print publications where the table is in the EXCEPT clause */
 		if (pset.sversion >= 190000)
@@ -3794,7 +3793,7 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		ncols++;
 	}
 	appendPQExpBufferStr(&buf, "\n, r.rolreplication");
-		appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
+	appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
@@ -3849,8 +3848,8 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
 			add_role_attribute(&buf, _("Replication"));
 
-			if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
-				add_role_attribute(&buf, _("Bypass RLS"));
+		if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
+			add_role_attribute(&buf, _("Bypass RLS"));
 
 		conns = atoi(PQgetvalue(res, i, 6));
 		if (conns >= 0)
@@ -5144,14 +5143,14 @@ listCollations(const char *pattern, bool verbose, bool showSystem)
 					  gettext_noop("Schema"),
 					  gettext_noop("Name"));
 
-		appendPQExpBuffer(&buf,
-						  "  CASE c.collprovider "
-						  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
-						  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
-						  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
-						  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
-						  "END AS \"%s\",\n",
-						  gettext_noop("Provider"));
+	appendPQExpBuffer(&buf,
+					  "  CASE c.collprovider "
+					  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
+					  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
+					  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
+					  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
+					  "END AS \"%s\",\n",
+					  gettext_noop("Provider"));
 
 	appendPQExpBuffer(&buf,
 					  "  c.collcollate AS \"%s\",\n"
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH--





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* [PATCH v3 4/4] run pgindent
@ 2026-05-06 21:43  Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: Nathan Bossart @ 2026-05-06 21:43 UTC (permalink / raw)

---
 src/bin/pg_dump/pg_dump.c              | 457 ++++++++++++------------
 src/bin/pg_dump/pg_dumpall.c           |  30 +-
 src/bin/pg_upgrade/check.c             |  16 +-
 src/bin/pg_upgrade/exec.c              |   8 +-
 src/bin/pg_upgrade/multixact_rewrite.c |  80 ++---
 src/bin/pg_upgrade/pg_upgrade.c        |   2 +-
 src/bin/pg_upgrade/relfilenumber.c     |  54 +--
 src/bin/psql/command.c                 |  29 +-
 src/bin/psql/describe.c                | 461 ++++++++++++-------------
 9 files changed, 567 insertions(+), 570 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index eed9aaeb7c1..c05623b1889 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1491,8 +1491,8 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	 * Disable timeouts if supported.
 	 */
 	ExecuteSqlStatement(AH, "SET statement_timeout = 0");
-		ExecuteSqlStatement(AH, "SET lock_timeout = 0");
-		ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
+	ExecuteSqlStatement(AH, "SET lock_timeout = 0");
+	ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
 	if (AH->remoteVersion >= 170000)
 		ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
 
@@ -1505,10 +1505,10 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	/*
 	 * Adjust row-security mode, if supported.
 	 */
-		if (dopt->enable_row_security)
-			ExecuteSqlStatement(AH, "SET row_security = on");
-		else
-			ExecuteSqlStatement(AH, "SET row_security = off");
+	if (dopt->enable_row_security)
+		ExecuteSqlStatement(AH, "SET row_security = on");
+	else
+		ExecuteSqlStatement(AH, "SET row_security = off");
 
 	/*
 	 * For security reasons, we restrict the expansion of non-system views and
@@ -1955,7 +1955,7 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
 	if (fout->dopt->binary_upgrade)
 		dobj->dump = ext->dobj.dump;
 	else
-			dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
+		dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
 
 	return true;
 }
@@ -1989,9 +1989,9 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
 	else if (strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
 	{
 		/*
-		 * We dump out any ACLs defined in pg_catalog, if
-		 * they are interesting (and not the original ACLs which were set at
-		 * initdb time, see pg_init_privs).
+		 * We dump out any ACLs defined in pg_catalog, if they are interesting
+		 * (and not the original ACLs which were set at initdb time, see
+		 * pg_init_privs).
 		 */
 		nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
 	}
@@ -3298,7 +3298,7 @@ dumpDatabase(Archive *fout)
 						 "datcollate, datctype, datfrozenxid, "
 						 "datacl, acldefault('d', datdba) AS acldefault, "
 						 "datistemplate, datconnlimit, ");
-		appendPQExpBufferStr(dbQry, "datminmxid, ");
+	appendPQExpBufferStr(dbQry, "datminmxid, ");
 	if (fout->remoteVersion >= 170000)
 		appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
 	else if (fout->remoteVersion >= 150000)
@@ -3640,11 +3640,11 @@ dumpDatabase(Archive *fout)
 					ii_oid,
 					ii_relminmxid;
 
-			appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
-							  "FROM pg_catalog.pg_class\n"
-							  "WHERE oid IN (%u, %u, %u, %u);\n",
-							  LargeObjectRelationId, LargeObjectLOidPNIndexId,
-							  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
+		appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
+						  "FROM pg_catalog.pg_class\n"
+						  "WHERE oid IN (%u, %u, %u, %u);\n",
+						  LargeObjectRelationId, LargeObjectLOidPNIndexId,
+						  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
 
 		lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
 
@@ -4276,7 +4276,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 
 	printfPQExpBuffer(query,
 					  "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
-		appendPQExpBufferStr(query, "pol.polpermissive, ");
+	appendPQExpBufferStr(query, "pol.polpermissive, ");
 	appendPQExpBuffer(query,
 					  "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
 					  "   pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
@@ -6635,9 +6635,9 @@ getAccessMethods(Archive *fout)
 	 * Select all access methods from pg_am table.
 	 */
 	appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
-		appendPQExpBufferStr(query,
-							 "amtype, "
-							 "amhandler::pg_catalog.regproc AS amhandler ");
+	appendPQExpBufferStr(query,
+						 "amtype, "
+						 "amhandler::pg_catalog.regproc AS amhandler ");
 	appendPQExpBufferStr(query, "FROM pg_am");
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -6829,35 +6829,35 @@ getAggregates(Archive *fout)
 	 * Find all interesting aggregates.  See comment in getFuncs() for the
 	 * rationale behind the filtering logic.
 	 */
-		agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
-					 : "p.proisagg");
+	agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
+				 : "p.proisagg");
 
-		appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
-						  "p.proname AS aggname, "
-						  "p.pronamespace AS aggnamespace, "
-						  "p.pronargs, p.proargtypes, "
-						  "p.proowner, "
-						  "p.proacl AS aggacl, "
-						  "acldefault('f', p.proowner) AS acldefault "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s AND ("
-						  "p.pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog') OR "
-						  "p.proacl IS DISTINCT FROM pip.initprivs",
-						  agg_check);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
-		appendPQExpBufferChar(query, ')');
+	appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
+					  "p.proname AS aggname, "
+					  "p.pronamespace AS aggnamespace, "
+					  "p.pronargs, p.proargtypes, "
+					  "p.proowner, "
+					  "p.proacl AS aggacl, "
+					  "acldefault('f', p.proowner) AS acldefault "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s AND ("
+					  "p.pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog') OR "
+					  "p.proacl IS DISTINCT FROM pip.initprivs",
+					  agg_check);
+	if (dopt->binary_upgrade)
+		appendPQExpBufferStr(query,
+							 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -6959,53 +6959,53 @@ getFuncs(Archive *fout)
 	 * include them, since we want to dump extension members individually in
 	 * that mode.  Also, if they are used by casts or transforms then we need
 	 * to gather the information about them, though they won't be dumped if
-	 * they are built-in.  Also, include functions in
-	 * pg_catalog if they have an ACL different from what's shown in
-	 * pg_init_privs (so we have to join to pg_init_privs; annoying).
+	 * they are built-in.  Also, include functions in pg_catalog if they have
+	 * an ACL different from what's shown in pg_init_privs (so we have to join
+	 * to pg_init_privs; annoying).
 	 */
-		not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
-						 : "NOT p.proisagg");
+	not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
+					 : "NOT p.proisagg");
 
-		appendPQExpBuffer(query,
-						  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
-						  "p.pronargs, p.proargtypes, p.prorettype, "
-						  "p.proacl, "
-						  "acldefault('f', p.proowner) AS acldefault, "
-						  "p.pronamespace, "
-						  "p.proowner "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s"
-						  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
-						  "WHERE classid = 'pg_proc'::regclass AND "
-						  "objid = p.oid AND deptype = 'i')"
-						  "\n  AND ("
-						  "\n  pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog')"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
-						  "\n  WHERE pg_cast.oid > %u "
-						  "\n  AND p.oid = pg_cast.castfunc)"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
-						  "\n  WHERE pg_transform.oid > %u AND "
-						  "\n  (p.oid = pg_transform.trffromsql"
-						  "\n  OR p.oid = pg_transform.trftosql))",
-						  not_agg_check,
-						  g_last_builtin_oid,
-						  g_last_builtin_oid);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
+	appendPQExpBuffer(query,
+					  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
+					  "p.pronargs, p.proargtypes, p.prorettype, "
+					  "p.proacl, "
+					  "acldefault('f', p.proowner) AS acldefault, "
+					  "p.pronamespace, "
+					  "p.proowner "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s"
+					  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
+					  "WHERE classid = 'pg_proc'::regclass AND "
+					  "objid = p.oid AND deptype = 'i')"
+					  "\n  AND ("
+					  "\n  pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog')"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
+					  "\n  WHERE pg_cast.oid > %u "
+					  "\n  AND p.oid = pg_cast.castfunc)"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
+					  "\n  WHERE pg_transform.oid > %u AND "
+					  "\n  (p.oid = pg_transform.trffromsql"
+					  "\n  OR p.oid = pg_transform.trftosql))",
+					  not_agg_check,
+					  g_last_builtin_oid,
+					  g_last_builtin_oid);
+	if (dopt->binary_upgrade)
 		appendPQExpBufferStr(query,
-							 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
-		appendPQExpBufferChar(query, ')');
+							 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferStr(query,
+						 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -7254,31 +7254,31 @@ getTables(Archive *fout, int *numTables)
 		appendPQExpBufferStr(query,
 							 "c.relhasoids, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispopulated, ");
+	appendPQExpBufferStr(query,
+						 "c.relispopulated, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relreplident, ");
+	appendPQExpBufferStr(query,
+						 "c.relreplident, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relrowsecurity, c.relforcerowsecurity, ");
+	appendPQExpBufferStr(query,
+						 "c.relrowsecurity, c.relforcerowsecurity, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relminmxid, tc.relminmxid AS tminmxid, ");
+	appendPQExpBufferStr(query,
+						 "c.relminmxid, tc.relminmxid AS tminmxid, ");
 
-		appendPQExpBufferStr(query,
-							 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-							 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-							 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
+	appendPQExpBufferStr(query,
+						 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+						 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+						 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
 
-		appendPQExpBufferStr(query,
-							 "am.amname, ");
+	appendPQExpBufferStr(query,
+						 "am.amname, ");
 
-		appendPQExpBufferStr(query,
-							 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
+	appendPQExpBufferStr(query,
+						 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispartition AS ispartition ");
+	appendPQExpBufferStr(query,
+						 "c.relispartition AS ispartition ");
 
 	/*
 	 * Left join to pg_depend to pick up dependency info linking sequences to
@@ -7298,8 +7298,8 @@ getTables(Archive *fout, int *numTables)
 	/*
 	 * Left join to pg_am to pick up the amname.
 	 */
-		appendPQExpBufferStr(query,
-							 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
+	appendPQExpBufferStr(query,
+						 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
 
 	/*
 	 * We purposefully ignore toast OIDs for partitioned tables; the reason is
@@ -7870,8 +7870,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 						 "t.reloptions AS indreloptions, ");
 
 
-		appendPQExpBufferStr(query,
-							 "i.indisreplident, ");
+	appendPQExpBufferStr(query,
+						 "i.indisreplident, ");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(query,
@@ -9315,8 +9315,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 		appendPQExpBufferStr(q,
 							 "'' AS attcompression,\n");
 
-		appendPQExpBufferStr(q,
-							 "a.attidentity,\n");
+	appendPQExpBufferStr(q,
+						 "a.attidentity,\n");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(q,
@@ -10719,73 +10719,73 @@ getAdditionalACLs(Archive *fout)
 	PQclear(res);
 
 	/* Fetch initial-privileges data */
-		printfPQExpBuffer(query,
-						  "SELECT objoid, classoid, objsubid, privtype, initprivs "
-						  "FROM pg_init_privs");
+	printfPQExpBuffer(query,
+					  "SELECT objoid, classoid, objsubid, privtype, initprivs "
+					  "FROM pg_init_privs");
 
-		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
-		ntups = PQntuples(res);
-		for (i = 0; i < ntups; i++)
-		{
-			Oid			objoid = atooid(PQgetvalue(res, i, 0));
-			Oid			classoid = atooid(PQgetvalue(res, i, 1));
-			int			objsubid = atoi(PQgetvalue(res, i, 2));
-			char		privtype = *(PQgetvalue(res, i, 3));
-			char	   *initprivs = PQgetvalue(res, i, 4);
-			CatalogId	objId;
-			DumpableObject *dobj;
+	ntups = PQntuples(res);
+	for (i = 0; i < ntups; i++)
+	{
+		Oid			objoid = atooid(PQgetvalue(res, i, 0));
+		Oid			classoid = atooid(PQgetvalue(res, i, 1));
+		int			objsubid = atoi(PQgetvalue(res, i, 2));
+		char		privtype = *(PQgetvalue(res, i, 3));
+		char	   *initprivs = PQgetvalue(res, i, 4);
+		CatalogId	objId;
+		DumpableObject *dobj;
 
-			objId.tableoid = classoid;
-			objId.oid = objoid;
-			dobj = findObjectByCatalogId(objId);
-			/* OK to ignore entries we haven't got a DumpableObject for */
-			if (dobj)
+		objId.tableoid = classoid;
+		objId.oid = objoid;
+		dobj = findObjectByCatalogId(objId);
+		/* OK to ignore entries we haven't got a DumpableObject for */
+		if (dobj)
+		{
+			/* Cope with sub-object initprivs */
+			if (objsubid != 0)
 			{
-				/* Cope with sub-object initprivs */
-				if (objsubid != 0)
-				{
-					if (dobj->objType == DO_TABLE)
-					{
-						/* For a column initprivs, set the table's ACL flags */
-						dobj->components |= DUMP_COMPONENT_ACL;
-						((TableInfo *) dobj)->hascolumnACLs = true;
-					}
-					else
-						pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
-									   classoid, objoid, objsubid);
-					continue;
-				}
-
-				/*
-				 * We ignore any pg_init_privs.initprivs entry for the public
-				 * schema, as explained in getNamespaces().
-				 */
-				if (dobj->objType == DO_NAMESPACE &&
-					strcmp(dobj->name, "public") == 0)
-					continue;
-
-				/* Else it had better be of a type we think has ACLs */
-				if (dobj->objType == DO_NAMESPACE ||
-					dobj->objType == DO_TYPE ||
-					dobj->objType == DO_FUNC ||
-					dobj->objType == DO_AGG ||
-					dobj->objType == DO_TABLE ||
-					dobj->objType == DO_PROCLANG ||
-					dobj->objType == DO_FDW ||
-					dobj->objType == DO_FOREIGN_SERVER)
+				if (dobj->objType == DO_TABLE)
 				{
-					DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
-
-					daobj->dacl.privtype = privtype;
-					daobj->dacl.initprivs = pstrdup(initprivs);
+					/* For a column initprivs, set the table's ACL flags */
+					dobj->components |= DUMP_COMPONENT_ACL;
+					((TableInfo *) dobj)->hascolumnACLs = true;
 				}
 				else
 					pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
 								   classoid, objoid, objsubid);
+				continue;
+			}
+
+			/*
+			 * We ignore any pg_init_privs.initprivs entry for the public
+			 * schema, as explained in getNamespaces().
+			 */
+			if (dobj->objType == DO_NAMESPACE &&
+				strcmp(dobj->name, "public") == 0)
+				continue;
+
+			/* Else it had better be of a type we think has ACLs */
+			if (dobj->objType == DO_NAMESPACE ||
+				dobj->objType == DO_TYPE ||
+				dobj->objType == DO_FUNC ||
+				dobj->objType == DO_AGG ||
+				dobj->objType == DO_TABLE ||
+				dobj->objType == DO_PROCLANG ||
+				dobj->objType == DO_FDW ||
+				dobj->objType == DO_FOREIGN_SERVER)
+			{
+				DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
+
+				daobj->dacl.privtype = privtype;
+				daobj->dacl.initprivs = pstrdup(initprivs);
 			}
+			else
+				pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
+							   classoid, objoid, objsubid);
 		}
-		PQclear(res);
+	}
+	PQclear(res);
 
 	destroyPQExpBuffer(query);
 }
@@ -11130,8 +11130,8 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 		 * The results must be in the order of the relations supplied in the
 		 * parameters to ensure we remain in sync as we walk through the TOC.
 		 *
-		 * For versions before 19, the redundant filter clause on s.tablename =
-		 * ANY(...) seems sufficient to convince the planner to use
+		 * For versions before 19, the redundant filter clause on s.tablename
+		 * = ANY(...) seems sufficient to convince the planner to use
 		 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
 		 * In newer versions, pg_stats returns the table OIDs, eliminating the
 		 * need for that hack.
@@ -13471,11 +13471,11 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
 							 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
 							 "proleakproof,\n");
 
-			appendPQExpBufferStr(query,
-								 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
+		appendPQExpBufferStr(query,
+							 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
 
-			appendPQExpBufferStr(query,
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -14965,9 +14965,9 @@ dumpCollation(Archive *fout, const CollInfo *collinfo)
 	/* Get collation-specific details */
 	appendPQExpBufferStr(query, "SELECT ");
 
-		appendPQExpBufferStr(query,
-							 "collprovider, "
-							 "collversion, ");
+	appendPQExpBufferStr(query,
+						 "collprovider, "
+						 "collversion, ");
 
 	if (fout->remoteVersion >= 120000)
 		appendPQExpBufferStr(query,
@@ -15374,23 +15374,23 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 							 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
 							 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggkind,\n"
-								 "aggmtransfn,\n"
-								 "aggminvtransfn,\n"
-								 "aggmfinalfn,\n"
-								 "aggmtranstype::pg_catalog.regtype,\n"
-								 "aggfinalextra,\n"
-								 "aggmfinalextra,\n"
-								 "aggtransspace,\n"
-								 "aggmtransspace,\n"
-								 "aggminitval,\n");
+		appendPQExpBufferStr(query,
+							 "aggkind,\n"
+							 "aggmtransfn,\n"
+							 "aggminvtransfn,\n"
+							 "aggmfinalfn,\n"
+							 "aggmtranstype::pg_catalog.regtype,\n"
+							 "aggfinalextra,\n"
+							 "aggmfinalextra,\n"
+							 "aggtransspace,\n"
+							 "aggmtransspace,\n"
+							 "aggminitval,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggcombinefn,\n"
-								 "aggserialfn,\n"
-								 "aggdeserialfn,\n"
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "aggcombinefn,\n"
+							 "aggserialfn,\n"
+							 "aggdeserialfn,\n"
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -16850,30 +16850,30 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
 			appendPQExpBufferStr(query,
 								 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
 
-				/*
-				 * In principle we should call acldefault('c', relowner) to
-				 * get the default ACL for a column.  However, we don't
-				 * currently store the numeric OID of the relowner in
-				 * TableInfo.  We could convert the owner name using regrole,
-				 * but that creates a risk of failure due to concurrent role
-				 * renames.  Given that the default ACL for columns is empty
-				 * and is likely to stay that way, it's not worth extra cycles
-				 * and risk to avoid hard-wiring that knowledge here.
-				 */
-				appendPQExpBufferStr(query,
-									 "SELECT at.attname, "
-									 "at.attacl, "
-									 "'{}' AS acldefault, "
-									 "pip.privtype, pip.initprivs "
-									 "FROM pg_catalog.pg_attribute at "
-									 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
-									 "(at.attrelid = pip.objoid "
-									 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
-									 "AND at.attnum = pip.objsubid) "
-									 "WHERE at.attrelid = $1 AND "
-									 "NOT at.attisdropped "
-									 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
-									 "ORDER BY at.attnum");
+			/*
+			 * In principle we should call acldefault('c', relowner) to get
+			 * the default ACL for a column.  However, we don't currently
+			 * store the numeric OID of the relowner in TableInfo.  We could
+			 * convert the owner name using regrole, but that creates a risk
+			 * of failure due to concurrent role renames.  Given that the
+			 * default ACL for columns is empty and is likely to stay that
+			 * way, it's not worth extra cycles and risk to avoid hard-wiring
+			 * that knowledge here.
+			 */
+			appendPQExpBufferStr(query,
+								 "SELECT at.attname, "
+								 "at.attacl, "
+								 "'{}' AS acldefault, "
+								 "pip.privtype, pip.initprivs "
+								 "FROM pg_catalog.pg_attribute at "
+								 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
+								 "(at.attrelid = pip.objoid "
+								 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
+								 "AND at.attnum = pip.objsubid) "
+								 "WHERE at.attrelid = $1 AND "
+								 "NOT at.attisdropped "
+								 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
+								 "ORDER BY at.attnum");
 
 			ExecuteSqlStatement(fout, query->data);
 
@@ -19167,7 +19167,7 @@ collectSequences(Archive *fout)
 	 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
 	 */
 	if (fout->remoteVersion < 180000 ||
-			 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
+		(!fout->dopt->dumpData && !fout->dopt->sequence_data))
 		query = "SELECT seqrelid, format_type(seqtypid, NULL), "
 			"seqstart, seqincrement, "
 			"seqmax, seqmin, "
@@ -19229,15 +19229,14 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
 
 	/*
-	 * The sequence information is gathered in a sorted
-	 * table before any calls to dumpSequence().  See collectSequences() for
-	 * more information.
+	 * The sequence information is gathered in a sorted table before any calls
+	 * to dumpSequence().  See collectSequences() for more information.
 	 */
-		Assert(sequences);
+	Assert(sequences);
 
-		key.oid = tbinfo->dobj.catId.oid;
-		seq = bsearch(&key, sequences, nsequences,
-					  sizeof(SequenceItem), SequenceItemCmp);
+	key.oid = tbinfo->dobj.catId.oid;
+	seq = bsearch(&key, sequences, nsequences,
+				  sizeof(SequenceItem), SequenceItemCmp);
 
 	/* Calculate default limits for a sequence of this type */
 	is_ascending = (seq->incby >= 0);
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 5b10f7122b7..3f61196671c 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -954,11 +954,11 @@ dropRoles(PGconn *conn)
 	int			i_rolname;
 	int			i;
 
-		printfPQExpBuffer(buf,
-						  "SELECT rolname "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 1", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT rolname "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 1", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
@@ -1035,16 +1035,16 @@ dumpRoles(PGconn *conn)
 	 * Notes: rolconfig is dumped later, and pg_authid must be used for
 	 * extracting rolcomment regardless of role_catalog.
 	 */
-		printfPQExpBuffer(buf,
-						  "SELECT oid, rolname, rolsuper, rolinherit, "
-						  "rolcreaterole, rolcreatedb, "
-						  "rolcanlogin, rolconnlimit, rolpassword, "
-						  "rolvaliduntil, rolreplication, rolbypassrls, "
-						  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
-						  "rolname = current_user AS is_current_user "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 2", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT oid, rolname, rolsuper, rolinherit, "
+					  "rolcreaterole, rolcreatedb, "
+					  "rolcanlogin, rolconnlimit, rolpassword, "
+					  "rolvaliduntil, rolreplication, rolbypassrls, "
+					  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
+					  "rolname = current_user AS is_current_user "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 2", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 0813cef2729..5f63e2114c8 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -1475,15 +1475,15 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_remove(anyarray,anyelement)'"
-							 ", 'array_replace(anyarray,anyelement,anyelement)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_remove(anyarray,anyelement)'"
+						 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_position(anyarray,anyelement)'"
-							 ", 'array_position(anyarray,anyelement,integer)'"
-							 ", 'array_positions(anyarray,anyelement)'"
-							 ", 'width_bucket(anyelement,anyarray)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_position(anyarray,anyelement)'"
+						 ", 'array_position(anyarray,anyelement,integer)'"
+						 ", 'array_positions(anyarray,anyelement)'"
+						 ", 'width_bucket(anyelement,anyarray)'");
 
 	/*
 	 * The query below hardcodes FirstNormalObjectId as 16384 rather than
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 479557abdcc..9a675929e17 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,7 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-		cluster->bin_version = v1 * 10000;
+	cluster->bin_version = v1 * 10000;
 }
 
 
@@ -344,8 +344,8 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-		check_single_dir(pg_data, "pg_wal");
-		check_single_dir(pg_data, "pg_xact");
+	check_single_dir(pg_data, "pg_wal");
+	check_single_dir(pg_data, "pg_xact");
 }
 
 
@@ -385,7 +385,7 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-		check_exec(cluster->bindir, "pg_resetwal", check_versions);
+	check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
 	{
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index c45b3183684..c7a1416494d 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -61,52 +61,52 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
-										   old_cluster.controldata.chkpnt_nxtmulti,
-										   old_cluster.controldata.chkpnt_nxtmxoff);
+	old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
+									   old_cluster.controldata.chkpnt_nxtmulti,
+									   old_cluster.controldata.chkpnt_nxtmxoff);
 
-		for (MultiXactId multi = from_multi; multi != to_multi;)
-		{
-			MultiXactMember member;
-			bool		multixid_valid;
-
-			/*
-			 * Read this multixid's members.
-			 *
-			 * Locking-only XIDs that may be part of multi-xids don't matter
-			 * after upgrade, as there can be no transactions running across
-			 * upgrade.  So as a small optimization, we only read one member
-			 * from each multixid: the one updating one, or if there was no
-			 * update, arbitrarily the first locking xid.
-			 */
-			multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
+	for (MultiXactId multi = from_multi; multi != to_multi;)
+	{
+		MultiXactMember member;
+		bool		multixid_valid;
 
-			/*
-			 * Write the new offset to pg_multixact/offsets.
-			 *
-			 * Even if this multixid is invalid, we still need to write its
-			 * offset if the *previous* multixid was valid.  That's because
-			 * when reading a multixid, the number of members is calculated
-			 * from the difference between the two offsets.
-			 */
-			RecordMultiXactOffset(offsets_writer, multi,
-								  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
+		/*
+		 * Read this multixid's members.
+		 *
+		 * Locking-only XIDs that may be part of multi-xids don't matter after
+		 * upgrade, as there can be no transactions running across upgrade. So
+		 * as a small optimization, we only read one member from each
+		 * multixid: the one updating one, or if there was no update,
+		 * arbitrarily the first locking xid.
+		 */
+		multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
 
-			/* Write the members */
-			if (multixid_valid)
-			{
-				RecordMultiXactMembers(members_writer, next_offset, 1, &member);
-				next_offset += 1;
-			}
+		/*
+		 * Write the new offset to pg_multixact/offsets.
+		 *
+		 * Even if this multixid is invalid, we still need to write its offset
+		 * if the *previous* multixid was valid.  That's because when reading
+		 * a multixid, the number of members is calculated from the difference
+		 * between the two offsets.
+		 */
+		RecordMultiXactOffset(offsets_writer, multi,
+							  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
 
-			/* Advance to next multixid, handling wraparound */
-			multi++;
-			if (multi < FirstMultiXactId)
-				multi = FirstMultiXactId;
-			prev_multixid_valid = multixid_valid;
+		/* Write the members */
+		if (multixid_valid)
+		{
+			RecordMultiXactMembers(members_writer, next_offset, 1, &member);
+			next_offset += 1;
 		}
 
-		FreeOldMultiXactReader(old_reader);
+		/* Advance to next multixid, handling wraparound */
+		multi++;
+		if (multi < FirstMultiXactId)
+			multi = FirstMultiXactId;
+		prev_multixid_valid = multixid_valid;
+	}
+
+	FreeOldMultiXactReader(old_reader);
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index e5d7920c1b1..d8e1b680f5a 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -833,7 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
+		oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index ec2ff7acb21..6c467bdc8a5 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -587,32 +587,32 @@ transfer_relfile(FileNameMap *map, const char *type_suffix)
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-			switch (user_opts.transfer_mode)
-			{
-				case TRANSFER_MODE_CLONE:
-					pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
-						   old_file, new_file);
-					cloneFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
-						   old_file, new_file);
-					copyFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY_FILE_RANGE:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
-						   old_file, new_file);
-					copyFileByRange(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_LINK:
-					pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
-						   old_file, new_file);
-					linkFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_SWAP:
-					/* swap mode is handled in its own code path */
-					pg_fatal("should never happen");
-					break;
-			}
+		switch (user_opts.transfer_mode)
+		{
+			case TRANSFER_MODE_CLONE:
+				pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
+					   old_file, new_file);
+				cloneFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
+					   old_file, new_file);
+				copyFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY_FILE_RANGE:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
+					   old_file, new_file);
+				copyFileByRange(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_LINK:
+				pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
+					   old_file, new_file);
+				linkFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_SWAP:
+				/* swap mode is handled in its own code path */
+				pg_fatal("should never happen");
+				break;
+		}
 	}
 }
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c9573d4b765..e5fb3595598 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -6272,23 +6272,22 @@ get_create_object_cmd(EditableObjectType obj_type, Oid oid,
 			 * ensure the right view gets replaced.  Also, check relation kind
 			 * to be sure it's a view.
 			 *
-			 * Views may have WITH [LOCAL|CASCADED]
-			 * CHECK OPTION.  These are not part of the view definition
-			 * returned by pg_get_viewdef() and so need to be retrieved
-			 * separately.  Materialized views may have
-			 * arbitrary storage parameter reloptions.
+			 * Views may have WITH [LOCAL|CASCADED] CHECK OPTION.  These are
+			 * not part of the view definition returned by pg_get_viewdef()
+			 * and so need to be retrieved separately.  Materialized views may
+			 * have arbitrary storage parameter reloptions.
 			 */
 			printfPQExpBuffer(query, "/* %s */\n", _("Get view's definition and details"));
-				appendPQExpBuffer(query,
-								  "SELECT nspname, relname, relkind, "
-								  "pg_catalog.pg_get_viewdef(c.oid, true), "
-								  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-								  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-								  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
-								  "FROM pg_catalog.pg_class c "
-								  "LEFT JOIN pg_catalog.pg_namespace n "
-								  "ON c.relnamespace = n.oid WHERE c.oid = %u",
-								  oid);
+			appendPQExpBuffer(query,
+							  "SELECT nspname, relname, relkind, "
+							  "pg_catalog.pg_get_viewdef(c.oid, true), "
+							  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+							  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+							  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
+							  "FROM pg_catalog.pg_class c "
+							  "LEFT JOIN pg_catalog.pg_namespace n "
+							  "ON c.relnamespace = n.oid WHERE c.oid = %u",
+							  oid);
 			break;
 	}
 
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 9f26ed928cb..9731c0079c8 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -387,19 +387,19 @@ describeFunctions(const char *functypes, const char *func_pattern,
 						  gettext_noop("stable"),
 						  gettext_noop("volatile"),
 						  gettext_noop("Volatility"));
-			appendPQExpBuffer(&buf,
-							  ",\n CASE\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
-							  " END as \"%s\"",
-							  gettext_noop("restricted"),
-							  gettext_noop("safe"),
-							  gettext_noop("unsafe"),
-							  gettext_noop("Parallel"));
+		appendPQExpBuffer(&buf,
+						  ",\n CASE\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
+						  " END as \"%s\"",
+						  gettext_noop("restricted"),
+						  gettext_noop("safe"),
+						  gettext_noop("unsafe"),
+						  gettext_noop("Parallel"));
 		appendPQExpBuffer(&buf,
 						  ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\""
 						  ",\n CASE WHEN prosecdef THEN '%s' ELSE '%s' END AS \"%s\""
@@ -599,8 +599,8 @@ describeFunctions(const char *functypes, const char *func_pattern,
 
 	myopt.title = _("List of functions");
 	myopt.translate_header = true;
-		myopt.translate_columns = translate_columns;
-		myopt.n_translate_columns = lengthof(translate_columns);
+	myopt.translate_columns = translate_columns;
+	myopt.n_translate_columns = lengthof(translate_columns);
 
 	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
 
@@ -1086,38 +1086,38 @@ permissionsList(const char *pattern, bool showSystem)
 					  "  ), E'\\n') AS \"%s\"",
 					  gettext_noop("Column privileges"));
 
-		appendPQExpBuffer(&buf,
-						  ",\n  pg_catalog.array_to_string(ARRAY(\n"
-						  "    SELECT polname\n"
-						  "    || CASE WHEN NOT polpermissive THEN\n"
-						  "       E' (RESTRICTIVE)'\n"
-						  "       ELSE '' END\n"
-						  "    || CASE WHEN polcmd != '*' THEN\n"
-						  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
-						  "       ELSE E':'\n"
-						  "       END\n"
-						  "    || CASE WHEN polqual IS NOT NULL THEN\n"
-						  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
-						  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END"
-						  "    || CASE WHEN polroles <> '{0}' THEN\n"
-						  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
-						  "               ARRAY(\n"
-						  "                   SELECT rolname\n"
-						  "                   FROM pg_catalog.pg_roles\n"
-						  "                   WHERE oid = ANY (polroles)\n"
-						  "                   ORDER BY 1\n"
-						  "               ), E', ')\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    FROM pg_catalog.pg_policy pol\n"
-						  "    WHERE polrelid = c.oid), E'\\n')\n"
-						  "    AS \"%s\"",
-						  gettext_noop("Policies"));
+	appendPQExpBuffer(&buf,
+					  ",\n  pg_catalog.array_to_string(ARRAY(\n"
+					  "    SELECT polname\n"
+					  "    || CASE WHEN NOT polpermissive THEN\n"
+					  "       E' (RESTRICTIVE)'\n"
+					  "       ELSE '' END\n"
+					  "    || CASE WHEN polcmd != '*' THEN\n"
+					  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
+					  "       ELSE E':'\n"
+					  "       END\n"
+					  "    || CASE WHEN polqual IS NOT NULL THEN\n"
+					  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
+					  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END"
+					  "    || CASE WHEN polroles <> '{0}' THEN\n"
+					  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
+					  "               ARRAY(\n"
+					  "                   SELECT rolname\n"
+					  "                   FROM pg_catalog.pg_roles\n"
+					  "                   WHERE oid = ANY (polroles)\n"
+					  "                   ORDER BY 1\n"
+					  "               ), E', ')\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    FROM pg_catalog.pg_policy pol\n"
+					  "    WHERE polrelid = c.oid), E'\\n')\n"
+					  "    AS \"%s\"",
+					  gettext_noop("Policies"));
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n"
 						 "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
@@ -1675,27 +1675,27 @@ describeOneTableDetails(const char *schemaname,
 		char	   *footers[3] = {NULL, NULL, NULL};
 
 		printfPQExpBuffer(&buf, "/* %s */\n", _("Get sequence information"));
-			appendPQExpBuffer(&buf,
-							  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
-							  "       seqstart AS \"%s\",\n"
-							  "       seqmin AS \"%s\",\n"
-							  "       seqmax AS \"%s\",\n"
-							  "       seqincrement AS \"%s\",\n"
-							  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
-							  "       seqcache AS \"%s\"\n",
-							  gettext_noop("Type"),
-							  gettext_noop("Start"),
-							  gettext_noop("Minimum"),
-							  gettext_noop("Maximum"),
-							  gettext_noop("Increment"),
-							  gettext_noop("yes"),
-							  gettext_noop("no"),
-							  gettext_noop("Cycles?"),
-							  gettext_noop("Cache"));
-			appendPQExpBuffer(&buf,
-							  "FROM pg_catalog.pg_sequence\n"
-							  "WHERE seqrelid = '%s';",
-							  oid);
+		appendPQExpBuffer(&buf,
+						  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
+						  "       seqstart AS \"%s\",\n"
+						  "       seqmin AS \"%s\",\n"
+						  "       seqmax AS \"%s\",\n"
+						  "       seqincrement AS \"%s\",\n"
+						  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
+						  "       seqcache AS \"%s\"\n",
+						  gettext_noop("Type"),
+						  gettext_noop("Start"),
+						  gettext_noop("Minimum"),
+						  gettext_noop("Maximum"),
+						  gettext_noop("Increment"),
+						  gettext_noop("yes"),
+						  gettext_noop("no"),
+						  gettext_noop("Cycles?"),
+						  gettext_noop("Cache"));
+		appendPQExpBuffer(&buf,
+						  "FROM pg_catalog.pg_sequence\n"
+						  "WHERE seqrelid = '%s';",
+						  oid);
 
 		res = PSQLexec(buf.data);
 		if (!res)
@@ -1913,7 +1913,7 @@ describeOneTableDetails(const char *schemaname,
 		appendPQExpBufferStr(&buf, ",\n  (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
 							 "   WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
 		attcoll_col = cols++;
-			appendPQExpBufferStr(&buf, ",\n  a.attidentity");
+		appendPQExpBufferStr(&buf, ",\n  a.attidentity");
 		attidentity_col = cols++;
 		if (pset.sversion >= 120000)
 			appendPQExpBufferStr(&buf, ",\n  a.attgenerated");
@@ -2326,7 +2326,7 @@ describeOneTableDetails(const char *schemaname,
 							 CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
 							 "condeferred) AS condeferred,\n");
 
-			appendPQExpBufferStr(&buf, "i.indisreplident,\n");
+		appendPQExpBufferStr(&buf, "i.indisreplident,\n");
 
 		if (pset.sversion >= 150000)
 			appendPQExpBufferStr(&buf, "i.indnullsnotdistinct,\n");
@@ -2431,7 +2431,7 @@ describeOneTableDetails(const char *schemaname,
 								 "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n  "
 								 "pg_catalog.pg_get_constraintdef(con.oid, true), "
 								 "contype, condeferrable, condeferred");
-				appendPQExpBufferStr(&buf, ", i.indisreplident");
+			appendPQExpBufferStr(&buf, ", i.indisreplident");
 			appendPQExpBufferStr(&buf, ", c2.reltablespace");
 			if (pset.sversion >= 180000)
 				appendPQExpBufferStr(&buf, ", con.conperiod");
@@ -2682,81 +2682,80 @@ describeOneTableDetails(const char *schemaname,
 		PQclear(result);
 
 		/* print any row-level policies */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get row-level policies for this table"));
-			appendPQExpBufferStr(&buf, "SELECT pol.polname,");
-				appendPQExpBufferStr(&buf,
-									 " pol.polpermissive,\n");
-			appendPQExpBuffer(&buf,
-							  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
-							  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
-							  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
-							  "  CASE pol.polcmd\n"
-							  "    WHEN 'r' THEN 'SELECT'\n"
-							  "    WHEN 'a' THEN 'INSERT'\n"
-							  "    WHEN 'w' THEN 'UPDATE'\n"
-							  "    WHEN 'd' THEN 'DELETE'\n"
-							  "    END AS cmd\n"
-							  "FROM pg_catalog.pg_policy pol\n"
-							  "WHERE pol.polrelid = '%s' ORDER BY 1;",
-							  oid);
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get row-level policies for this table"));
+		appendPQExpBufferStr(&buf, "SELECT pol.polname,");
+		appendPQExpBufferStr(&buf,
+							 " pol.polpermissive,\n");
+		appendPQExpBuffer(&buf,
+						  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
+						  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
+						  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
+						  "  CASE pol.polcmd\n"
+						  "    WHEN 'r' THEN 'SELECT'\n"
+						  "    WHEN 'a' THEN 'INSERT'\n"
+						  "    WHEN 'w' THEN 'UPDATE'\n"
+						  "    WHEN 'd' THEN 'DELETE'\n"
+						  "    END AS cmd\n"
+						  "FROM pg_catalog.pg_policy pol\n"
+						  "WHERE pol.polrelid = '%s' ORDER BY 1;",
+						  oid);
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			/*
-			 * Handle cases where RLS is enabled and there are policies, or
-			 * there aren't policies, or RLS isn't enabled but there are
-			 * policies
-			 */
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies:"));
+		/*
+		 * Handle cases where RLS is enabled and there are policies, or there
+		 * aren't policies, or RLS isn't enabled but there are policies
+		 */
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies:"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
 
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
 
-			if (!tableinfo.rowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (row security disabled):"));
+		if (!tableinfo.rowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (row security disabled):"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    POLICY \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    POLICY \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				if (*(PQgetvalue(result, i, 1)) == 'f')
-					appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
+			if (*(PQgetvalue(result, i, 1)) == 'f')
+				appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
 
-				if (!PQgetisnull(result, i, 5))
-					appendPQExpBuffer(&buf, " FOR %s",
-									  PQgetvalue(result, i, 5));
+			if (!PQgetisnull(result, i, 5))
+				appendPQExpBuffer(&buf, " FOR %s",
+								  PQgetvalue(result, i, 5));
 
-				if (!PQgetisnull(result, i, 2))
-				{
-					appendPQExpBuffer(&buf, "\n      TO %s",
-									  PQgetvalue(result, i, 2));
-				}
+			if (!PQgetisnull(result, i, 2))
+			{
+				appendPQExpBuffer(&buf, "\n      TO %s",
+								  PQgetvalue(result, i, 2));
+			}
 
-				if (!PQgetisnull(result, i, 3))
-					appendPQExpBuffer(&buf, "\n      USING (%s)",
-									  PQgetvalue(result, i, 3));
+			if (!PQgetisnull(result, i, 3))
+				appendPQExpBuffer(&buf, "\n      USING (%s)",
+								  PQgetvalue(result, i, 3));
 
-				if (!PQgetisnull(result, i, 4))
-					appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
-									  PQgetvalue(result, i, 4));
+			if (!PQgetisnull(result, i, 4))
+				appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
+								  PQgetvalue(result, i, 4));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* print any extended statistics */
 		if (pset.sversion >= 140000)
@@ -3025,115 +3024,115 @@ describeOneTableDetails(const char *schemaname,
 		}
 
 		/* print any publications */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get publications that publish this table"));
-			if (pset.sversion >= 150000)
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get publications that publish this table"));
+		if (pset.sversion >= 150000)
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
+							  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
+							  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "UNION\n"
+							  "SELECT pubname\n"
+							  "     , pg_get_expr(pr.prqual, c.oid)\n"
+							  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
+							  "         (SELECT string_agg(attname, ', ')\n"
+							  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
+							  "                pg_catalog.pg_attribute\n"
+							  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
+							  "        ELSE NULL END) "
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
+							  "WHERE pr.prrelid = '%s'\n",
+							  oid, oid, oid);
+
+			if (pset.sversion >= 190000)
 			{
+				/*
+				 * Skip entries where this relation appears in the
+				 * publication's EXCEPT list.
+				 */
 				appendPQExpBuffer(&buf,
+								  " AND NOT pr.prexcept\n"
+								  "UNION\n"
 								  "SELECT pubname\n"
 								  "     , NULL\n"
 								  "     , NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
-								  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
-								  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
-								  "UNION\n"
-								  "SELECT pubname\n"
-								  "     , pg_get_expr(pr.prqual, c.oid)\n"
-								  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
-								  "         (SELECT string_agg(attname, ', ')\n"
-								  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
-								  "                pg_catalog.pg_attribute\n"
-								  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
-								  "        ELSE NULL END) "
-								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
-								  "WHERE pr.prrelid = '%s'\n",
+								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+								  "     AND NOT EXISTS (\n"
+								  "     SELECT 1\n"
+								  "     FROM pg_catalog.pg_publication_rel pr\n"
+								  "     WHERE pr.prpubid = p.oid AND\n"
+								  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
+								  "ORDER BY 1;",
 								  oid, oid, oid);
-
-				if (pset.sversion >= 190000)
-				{
-					/*
-					 * Skip entries where this relation appears in the
-					 * publication's EXCEPT list.
-					 */
-					appendPQExpBuffer(&buf,
-									  " AND NOT pr.prexcept\n"
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "     , NULL\n"
-									  "     , NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "     AND NOT EXISTS (\n"
-									  "     SELECT 1\n"
-									  "     FROM pg_catalog.pg_publication_rel pr\n"
-									  "     WHERE pr.prpubid = p.oid AND\n"
-									  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
-									  "ORDER BY 1;",
-									  oid, oid, oid);
-				}
-				else
-				{
-					appendPQExpBuffer(&buf,
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "		, NULL\n"
-									  "		, NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "ORDER BY 1;",
-									  oid);
-				}
 			}
 			else
 			{
 				appendPQExpBuffer(&buf,
+								  "UNION\n"
 								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
-								  "FROM pg_catalog.pg_publication p\n"
-								  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "WHERE pr.prrelid = '%s'\n"
-								  "UNION ALL\n"
-								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
+								  "		, NULL\n"
+								  "		, NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
 								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
 								  "ORDER BY 1;",
-								  oid, oid);
+								  oid);
 			}
+		}
+		else
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "WHERE pr.prrelid = '%s'\n"
+							  "UNION ALL\n"
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "ORDER BY 1;",
+							  oid, oid);
+		}
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			if (tuples > 0)
-				printTableAddFooter(&cont, _("Included in publications:"));
+		if (tuples > 0)
+			printTableAddFooter(&cont, _("Included in publications:"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				/* column list (if any) */
-				if (!PQgetisnull(result, i, 2))
-					appendPQExpBuffer(&buf, " (%s)",
-									  PQgetvalue(result, i, 2));
+			/* column list (if any) */
+			if (!PQgetisnull(result, i, 2))
+				appendPQExpBuffer(&buf, " (%s)",
+								  PQgetvalue(result, i, 2));
 
-				/* row filter (if any) */
-				if (!PQgetisnull(result, i, 1))
-					appendPQExpBuffer(&buf, " WHERE %s",
-									  PQgetvalue(result, i, 1));
+			/* row filter (if any) */
+			if (!PQgetisnull(result, i, 1))
+				appendPQExpBuffer(&buf, " WHERE %s",
+								  PQgetvalue(result, i, 1));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* Print publications where the table is in the EXCEPT clause */
 		if (pset.sversion >= 190000)
@@ -3805,7 +3804,7 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		ncols++;
 	}
 	appendPQExpBufferStr(&buf, "\n, r.rolreplication");
-		appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
+	appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
@@ -3860,8 +3859,8 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
 			add_role_attribute(&buf, _("Replication"));
 
-			if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
-				add_role_attribute(&buf, _("Bypass RLS"));
+		if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
+			add_role_attribute(&buf, _("Bypass RLS"));
 
 		conns = atoi(PQgetvalue(res, i, 6));
 		if (conns >= 0)
@@ -5155,14 +5154,14 @@ listCollations(const char *pattern, bool verbose, bool showSystem)
 					  gettext_noop("Schema"),
 					  gettext_noop("Name"));
 
-		appendPQExpBuffer(&buf,
-						  "  CASE c.collprovider "
-						  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
-						  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
-						  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
-						  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
-						  "END AS \"%s\",\n",
-						  gettext_noop("Provider"));
+	appendPQExpBuffer(&buf,
+					  "  CASE c.collprovider "
+					  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
+					  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
+					  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
+					  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
+					  "END AS \"%s\",\n",
+					  gettext_noop("Provider"));
 
 	appendPQExpBuffer(&buf,
 					  "  c.collcollate AS \"%s\",\n"
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6--





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* [PATCH v3 4/4] run pgindent
@ 2026-05-06 21:43  Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: Nathan Bossart @ 2026-05-06 21:43 UTC (permalink / raw)

---
 src/bin/pg_dump/pg_dump.c              | 457 ++++++++++++------------
 src/bin/pg_dump/pg_dumpall.c           |  30 +-
 src/bin/pg_upgrade/check.c             |  16 +-
 src/bin/pg_upgrade/exec.c              |   8 +-
 src/bin/pg_upgrade/multixact_rewrite.c |  80 ++---
 src/bin/pg_upgrade/pg_upgrade.c        |   2 +-
 src/bin/pg_upgrade/relfilenumber.c     |  54 +--
 src/bin/psql/command.c                 |  29 +-
 src/bin/psql/describe.c                | 461 ++++++++++++-------------
 9 files changed, 567 insertions(+), 570 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index eed9aaeb7c1..c05623b1889 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1491,8 +1491,8 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	 * Disable timeouts if supported.
 	 */
 	ExecuteSqlStatement(AH, "SET statement_timeout = 0");
-		ExecuteSqlStatement(AH, "SET lock_timeout = 0");
-		ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
+	ExecuteSqlStatement(AH, "SET lock_timeout = 0");
+	ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
 	if (AH->remoteVersion >= 170000)
 		ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
 
@@ -1505,10 +1505,10 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	/*
 	 * Adjust row-security mode, if supported.
 	 */
-		if (dopt->enable_row_security)
-			ExecuteSqlStatement(AH, "SET row_security = on");
-		else
-			ExecuteSqlStatement(AH, "SET row_security = off");
+	if (dopt->enable_row_security)
+		ExecuteSqlStatement(AH, "SET row_security = on");
+	else
+		ExecuteSqlStatement(AH, "SET row_security = off");
 
 	/*
 	 * For security reasons, we restrict the expansion of non-system views and
@@ -1955,7 +1955,7 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
 	if (fout->dopt->binary_upgrade)
 		dobj->dump = ext->dobj.dump;
 	else
-			dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
+		dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
 
 	return true;
 }
@@ -1989,9 +1989,9 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
 	else if (strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
 	{
 		/*
-		 * We dump out any ACLs defined in pg_catalog, if
-		 * they are interesting (and not the original ACLs which were set at
-		 * initdb time, see pg_init_privs).
+		 * We dump out any ACLs defined in pg_catalog, if they are interesting
+		 * (and not the original ACLs which were set at initdb time, see
+		 * pg_init_privs).
 		 */
 		nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
 	}
@@ -3298,7 +3298,7 @@ dumpDatabase(Archive *fout)
 						 "datcollate, datctype, datfrozenxid, "
 						 "datacl, acldefault('d', datdba) AS acldefault, "
 						 "datistemplate, datconnlimit, ");
-		appendPQExpBufferStr(dbQry, "datminmxid, ");
+	appendPQExpBufferStr(dbQry, "datminmxid, ");
 	if (fout->remoteVersion >= 170000)
 		appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
 	else if (fout->remoteVersion >= 150000)
@@ -3640,11 +3640,11 @@ dumpDatabase(Archive *fout)
 					ii_oid,
 					ii_relminmxid;
 
-			appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
-							  "FROM pg_catalog.pg_class\n"
-							  "WHERE oid IN (%u, %u, %u, %u);\n",
-							  LargeObjectRelationId, LargeObjectLOidPNIndexId,
-							  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
+		appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
+						  "FROM pg_catalog.pg_class\n"
+						  "WHERE oid IN (%u, %u, %u, %u);\n",
+						  LargeObjectRelationId, LargeObjectLOidPNIndexId,
+						  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
 
 		lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
 
@@ -4276,7 +4276,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 
 	printfPQExpBuffer(query,
 					  "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
-		appendPQExpBufferStr(query, "pol.polpermissive, ");
+	appendPQExpBufferStr(query, "pol.polpermissive, ");
 	appendPQExpBuffer(query,
 					  "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
 					  "   pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
@@ -6635,9 +6635,9 @@ getAccessMethods(Archive *fout)
 	 * Select all access methods from pg_am table.
 	 */
 	appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
-		appendPQExpBufferStr(query,
-							 "amtype, "
-							 "amhandler::pg_catalog.regproc AS amhandler ");
+	appendPQExpBufferStr(query,
+						 "amtype, "
+						 "amhandler::pg_catalog.regproc AS amhandler ");
 	appendPQExpBufferStr(query, "FROM pg_am");
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -6829,35 +6829,35 @@ getAggregates(Archive *fout)
 	 * Find all interesting aggregates.  See comment in getFuncs() for the
 	 * rationale behind the filtering logic.
 	 */
-		agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
-					 : "p.proisagg");
+	agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
+				 : "p.proisagg");
 
-		appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
-						  "p.proname AS aggname, "
-						  "p.pronamespace AS aggnamespace, "
-						  "p.pronargs, p.proargtypes, "
-						  "p.proowner, "
-						  "p.proacl AS aggacl, "
-						  "acldefault('f', p.proowner) AS acldefault "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s AND ("
-						  "p.pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog') OR "
-						  "p.proacl IS DISTINCT FROM pip.initprivs",
-						  agg_check);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
-		appendPQExpBufferChar(query, ')');
+	appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
+					  "p.proname AS aggname, "
+					  "p.pronamespace AS aggnamespace, "
+					  "p.pronargs, p.proargtypes, "
+					  "p.proowner, "
+					  "p.proacl AS aggacl, "
+					  "acldefault('f', p.proowner) AS acldefault "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s AND ("
+					  "p.pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog') OR "
+					  "p.proacl IS DISTINCT FROM pip.initprivs",
+					  agg_check);
+	if (dopt->binary_upgrade)
+		appendPQExpBufferStr(query,
+							 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -6959,53 +6959,53 @@ getFuncs(Archive *fout)
 	 * include them, since we want to dump extension members individually in
 	 * that mode.  Also, if they are used by casts or transforms then we need
 	 * to gather the information about them, though they won't be dumped if
-	 * they are built-in.  Also, include functions in
-	 * pg_catalog if they have an ACL different from what's shown in
-	 * pg_init_privs (so we have to join to pg_init_privs; annoying).
+	 * they are built-in.  Also, include functions in pg_catalog if they have
+	 * an ACL different from what's shown in pg_init_privs (so we have to join
+	 * to pg_init_privs; annoying).
 	 */
-		not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
-						 : "NOT p.proisagg");
+	not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
+					 : "NOT p.proisagg");
 
-		appendPQExpBuffer(query,
-						  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
-						  "p.pronargs, p.proargtypes, p.prorettype, "
-						  "p.proacl, "
-						  "acldefault('f', p.proowner) AS acldefault, "
-						  "p.pronamespace, "
-						  "p.proowner "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s"
-						  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
-						  "WHERE classid = 'pg_proc'::regclass AND "
-						  "objid = p.oid AND deptype = 'i')"
-						  "\n  AND ("
-						  "\n  pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog')"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
-						  "\n  WHERE pg_cast.oid > %u "
-						  "\n  AND p.oid = pg_cast.castfunc)"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
-						  "\n  WHERE pg_transform.oid > %u AND "
-						  "\n  (p.oid = pg_transform.trffromsql"
-						  "\n  OR p.oid = pg_transform.trftosql))",
-						  not_agg_check,
-						  g_last_builtin_oid,
-						  g_last_builtin_oid);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
+	appendPQExpBuffer(query,
+					  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
+					  "p.pronargs, p.proargtypes, p.prorettype, "
+					  "p.proacl, "
+					  "acldefault('f', p.proowner) AS acldefault, "
+					  "p.pronamespace, "
+					  "p.proowner "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s"
+					  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
+					  "WHERE classid = 'pg_proc'::regclass AND "
+					  "objid = p.oid AND deptype = 'i')"
+					  "\n  AND ("
+					  "\n  pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog')"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
+					  "\n  WHERE pg_cast.oid > %u "
+					  "\n  AND p.oid = pg_cast.castfunc)"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
+					  "\n  WHERE pg_transform.oid > %u AND "
+					  "\n  (p.oid = pg_transform.trffromsql"
+					  "\n  OR p.oid = pg_transform.trftosql))",
+					  not_agg_check,
+					  g_last_builtin_oid,
+					  g_last_builtin_oid);
+	if (dopt->binary_upgrade)
 		appendPQExpBufferStr(query,
-							 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
-		appendPQExpBufferChar(query, ')');
+							 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferStr(query,
+						 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -7254,31 +7254,31 @@ getTables(Archive *fout, int *numTables)
 		appendPQExpBufferStr(query,
 							 "c.relhasoids, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispopulated, ");
+	appendPQExpBufferStr(query,
+						 "c.relispopulated, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relreplident, ");
+	appendPQExpBufferStr(query,
+						 "c.relreplident, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relrowsecurity, c.relforcerowsecurity, ");
+	appendPQExpBufferStr(query,
+						 "c.relrowsecurity, c.relforcerowsecurity, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relminmxid, tc.relminmxid AS tminmxid, ");
+	appendPQExpBufferStr(query,
+						 "c.relminmxid, tc.relminmxid AS tminmxid, ");
 
-		appendPQExpBufferStr(query,
-							 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-							 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-							 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
+	appendPQExpBufferStr(query,
+						 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+						 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+						 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
 
-		appendPQExpBufferStr(query,
-							 "am.amname, ");
+	appendPQExpBufferStr(query,
+						 "am.amname, ");
 
-		appendPQExpBufferStr(query,
-							 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
+	appendPQExpBufferStr(query,
+						 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispartition AS ispartition ");
+	appendPQExpBufferStr(query,
+						 "c.relispartition AS ispartition ");
 
 	/*
 	 * Left join to pg_depend to pick up dependency info linking sequences to
@@ -7298,8 +7298,8 @@ getTables(Archive *fout, int *numTables)
 	/*
 	 * Left join to pg_am to pick up the amname.
 	 */
-		appendPQExpBufferStr(query,
-							 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
+	appendPQExpBufferStr(query,
+						 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
 
 	/*
 	 * We purposefully ignore toast OIDs for partitioned tables; the reason is
@@ -7870,8 +7870,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 						 "t.reloptions AS indreloptions, ");
 
 
-		appendPQExpBufferStr(query,
-							 "i.indisreplident, ");
+	appendPQExpBufferStr(query,
+						 "i.indisreplident, ");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(query,
@@ -9315,8 +9315,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 		appendPQExpBufferStr(q,
 							 "'' AS attcompression,\n");
 
-		appendPQExpBufferStr(q,
-							 "a.attidentity,\n");
+	appendPQExpBufferStr(q,
+						 "a.attidentity,\n");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(q,
@@ -10719,73 +10719,73 @@ getAdditionalACLs(Archive *fout)
 	PQclear(res);
 
 	/* Fetch initial-privileges data */
-		printfPQExpBuffer(query,
-						  "SELECT objoid, classoid, objsubid, privtype, initprivs "
-						  "FROM pg_init_privs");
+	printfPQExpBuffer(query,
+					  "SELECT objoid, classoid, objsubid, privtype, initprivs "
+					  "FROM pg_init_privs");
 
-		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
-		ntups = PQntuples(res);
-		for (i = 0; i < ntups; i++)
-		{
-			Oid			objoid = atooid(PQgetvalue(res, i, 0));
-			Oid			classoid = atooid(PQgetvalue(res, i, 1));
-			int			objsubid = atoi(PQgetvalue(res, i, 2));
-			char		privtype = *(PQgetvalue(res, i, 3));
-			char	   *initprivs = PQgetvalue(res, i, 4);
-			CatalogId	objId;
-			DumpableObject *dobj;
+	ntups = PQntuples(res);
+	for (i = 0; i < ntups; i++)
+	{
+		Oid			objoid = atooid(PQgetvalue(res, i, 0));
+		Oid			classoid = atooid(PQgetvalue(res, i, 1));
+		int			objsubid = atoi(PQgetvalue(res, i, 2));
+		char		privtype = *(PQgetvalue(res, i, 3));
+		char	   *initprivs = PQgetvalue(res, i, 4);
+		CatalogId	objId;
+		DumpableObject *dobj;
 
-			objId.tableoid = classoid;
-			objId.oid = objoid;
-			dobj = findObjectByCatalogId(objId);
-			/* OK to ignore entries we haven't got a DumpableObject for */
-			if (dobj)
+		objId.tableoid = classoid;
+		objId.oid = objoid;
+		dobj = findObjectByCatalogId(objId);
+		/* OK to ignore entries we haven't got a DumpableObject for */
+		if (dobj)
+		{
+			/* Cope with sub-object initprivs */
+			if (objsubid != 0)
 			{
-				/* Cope with sub-object initprivs */
-				if (objsubid != 0)
-				{
-					if (dobj->objType == DO_TABLE)
-					{
-						/* For a column initprivs, set the table's ACL flags */
-						dobj->components |= DUMP_COMPONENT_ACL;
-						((TableInfo *) dobj)->hascolumnACLs = true;
-					}
-					else
-						pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
-									   classoid, objoid, objsubid);
-					continue;
-				}
-
-				/*
-				 * We ignore any pg_init_privs.initprivs entry for the public
-				 * schema, as explained in getNamespaces().
-				 */
-				if (dobj->objType == DO_NAMESPACE &&
-					strcmp(dobj->name, "public") == 0)
-					continue;
-
-				/* Else it had better be of a type we think has ACLs */
-				if (dobj->objType == DO_NAMESPACE ||
-					dobj->objType == DO_TYPE ||
-					dobj->objType == DO_FUNC ||
-					dobj->objType == DO_AGG ||
-					dobj->objType == DO_TABLE ||
-					dobj->objType == DO_PROCLANG ||
-					dobj->objType == DO_FDW ||
-					dobj->objType == DO_FOREIGN_SERVER)
+				if (dobj->objType == DO_TABLE)
 				{
-					DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
-
-					daobj->dacl.privtype = privtype;
-					daobj->dacl.initprivs = pstrdup(initprivs);
+					/* For a column initprivs, set the table's ACL flags */
+					dobj->components |= DUMP_COMPONENT_ACL;
+					((TableInfo *) dobj)->hascolumnACLs = true;
 				}
 				else
 					pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
 								   classoid, objoid, objsubid);
+				continue;
+			}
+
+			/*
+			 * We ignore any pg_init_privs.initprivs entry for the public
+			 * schema, as explained in getNamespaces().
+			 */
+			if (dobj->objType == DO_NAMESPACE &&
+				strcmp(dobj->name, "public") == 0)
+				continue;
+
+			/* Else it had better be of a type we think has ACLs */
+			if (dobj->objType == DO_NAMESPACE ||
+				dobj->objType == DO_TYPE ||
+				dobj->objType == DO_FUNC ||
+				dobj->objType == DO_AGG ||
+				dobj->objType == DO_TABLE ||
+				dobj->objType == DO_PROCLANG ||
+				dobj->objType == DO_FDW ||
+				dobj->objType == DO_FOREIGN_SERVER)
+			{
+				DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
+
+				daobj->dacl.privtype = privtype;
+				daobj->dacl.initprivs = pstrdup(initprivs);
 			}
+			else
+				pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
+							   classoid, objoid, objsubid);
 		}
-		PQclear(res);
+	}
+	PQclear(res);
 
 	destroyPQExpBuffer(query);
 }
@@ -11130,8 +11130,8 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 		 * The results must be in the order of the relations supplied in the
 		 * parameters to ensure we remain in sync as we walk through the TOC.
 		 *
-		 * For versions before 19, the redundant filter clause on s.tablename =
-		 * ANY(...) seems sufficient to convince the planner to use
+		 * For versions before 19, the redundant filter clause on s.tablename
+		 * = ANY(...) seems sufficient to convince the planner to use
 		 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
 		 * In newer versions, pg_stats returns the table OIDs, eliminating the
 		 * need for that hack.
@@ -13471,11 +13471,11 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
 							 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
 							 "proleakproof,\n");
 
-			appendPQExpBufferStr(query,
-								 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
+		appendPQExpBufferStr(query,
+							 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
 
-			appendPQExpBufferStr(query,
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -14965,9 +14965,9 @@ dumpCollation(Archive *fout, const CollInfo *collinfo)
 	/* Get collation-specific details */
 	appendPQExpBufferStr(query, "SELECT ");
 
-		appendPQExpBufferStr(query,
-							 "collprovider, "
-							 "collversion, ");
+	appendPQExpBufferStr(query,
+						 "collprovider, "
+						 "collversion, ");
 
 	if (fout->remoteVersion >= 120000)
 		appendPQExpBufferStr(query,
@@ -15374,23 +15374,23 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 							 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
 							 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggkind,\n"
-								 "aggmtransfn,\n"
-								 "aggminvtransfn,\n"
-								 "aggmfinalfn,\n"
-								 "aggmtranstype::pg_catalog.regtype,\n"
-								 "aggfinalextra,\n"
-								 "aggmfinalextra,\n"
-								 "aggtransspace,\n"
-								 "aggmtransspace,\n"
-								 "aggminitval,\n");
+		appendPQExpBufferStr(query,
+							 "aggkind,\n"
+							 "aggmtransfn,\n"
+							 "aggminvtransfn,\n"
+							 "aggmfinalfn,\n"
+							 "aggmtranstype::pg_catalog.regtype,\n"
+							 "aggfinalextra,\n"
+							 "aggmfinalextra,\n"
+							 "aggtransspace,\n"
+							 "aggmtransspace,\n"
+							 "aggminitval,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggcombinefn,\n"
-								 "aggserialfn,\n"
-								 "aggdeserialfn,\n"
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "aggcombinefn,\n"
+							 "aggserialfn,\n"
+							 "aggdeserialfn,\n"
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -16850,30 +16850,30 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
 			appendPQExpBufferStr(query,
 								 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
 
-				/*
-				 * In principle we should call acldefault('c', relowner) to
-				 * get the default ACL for a column.  However, we don't
-				 * currently store the numeric OID of the relowner in
-				 * TableInfo.  We could convert the owner name using regrole,
-				 * but that creates a risk of failure due to concurrent role
-				 * renames.  Given that the default ACL for columns is empty
-				 * and is likely to stay that way, it's not worth extra cycles
-				 * and risk to avoid hard-wiring that knowledge here.
-				 */
-				appendPQExpBufferStr(query,
-									 "SELECT at.attname, "
-									 "at.attacl, "
-									 "'{}' AS acldefault, "
-									 "pip.privtype, pip.initprivs "
-									 "FROM pg_catalog.pg_attribute at "
-									 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
-									 "(at.attrelid = pip.objoid "
-									 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
-									 "AND at.attnum = pip.objsubid) "
-									 "WHERE at.attrelid = $1 AND "
-									 "NOT at.attisdropped "
-									 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
-									 "ORDER BY at.attnum");
+			/*
+			 * In principle we should call acldefault('c', relowner) to get
+			 * the default ACL for a column.  However, we don't currently
+			 * store the numeric OID of the relowner in TableInfo.  We could
+			 * convert the owner name using regrole, but that creates a risk
+			 * of failure due to concurrent role renames.  Given that the
+			 * default ACL for columns is empty and is likely to stay that
+			 * way, it's not worth extra cycles and risk to avoid hard-wiring
+			 * that knowledge here.
+			 */
+			appendPQExpBufferStr(query,
+								 "SELECT at.attname, "
+								 "at.attacl, "
+								 "'{}' AS acldefault, "
+								 "pip.privtype, pip.initprivs "
+								 "FROM pg_catalog.pg_attribute at "
+								 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
+								 "(at.attrelid = pip.objoid "
+								 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
+								 "AND at.attnum = pip.objsubid) "
+								 "WHERE at.attrelid = $1 AND "
+								 "NOT at.attisdropped "
+								 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
+								 "ORDER BY at.attnum");
 
 			ExecuteSqlStatement(fout, query->data);
 
@@ -19167,7 +19167,7 @@ collectSequences(Archive *fout)
 	 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
 	 */
 	if (fout->remoteVersion < 180000 ||
-			 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
+		(!fout->dopt->dumpData && !fout->dopt->sequence_data))
 		query = "SELECT seqrelid, format_type(seqtypid, NULL), "
 			"seqstart, seqincrement, "
 			"seqmax, seqmin, "
@@ -19229,15 +19229,14 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
 
 	/*
-	 * The sequence information is gathered in a sorted
-	 * table before any calls to dumpSequence().  See collectSequences() for
-	 * more information.
+	 * The sequence information is gathered in a sorted table before any calls
+	 * to dumpSequence().  See collectSequences() for more information.
 	 */
-		Assert(sequences);
+	Assert(sequences);
 
-		key.oid = tbinfo->dobj.catId.oid;
-		seq = bsearch(&key, sequences, nsequences,
-					  sizeof(SequenceItem), SequenceItemCmp);
+	key.oid = tbinfo->dobj.catId.oid;
+	seq = bsearch(&key, sequences, nsequences,
+				  sizeof(SequenceItem), SequenceItemCmp);
 
 	/* Calculate default limits for a sequence of this type */
 	is_ascending = (seq->incby >= 0);
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 5b10f7122b7..3f61196671c 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -954,11 +954,11 @@ dropRoles(PGconn *conn)
 	int			i_rolname;
 	int			i;
 
-		printfPQExpBuffer(buf,
-						  "SELECT rolname "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 1", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT rolname "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 1", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
@@ -1035,16 +1035,16 @@ dumpRoles(PGconn *conn)
 	 * Notes: rolconfig is dumped later, and pg_authid must be used for
 	 * extracting rolcomment regardless of role_catalog.
 	 */
-		printfPQExpBuffer(buf,
-						  "SELECT oid, rolname, rolsuper, rolinherit, "
-						  "rolcreaterole, rolcreatedb, "
-						  "rolcanlogin, rolconnlimit, rolpassword, "
-						  "rolvaliduntil, rolreplication, rolbypassrls, "
-						  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
-						  "rolname = current_user AS is_current_user "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 2", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT oid, rolname, rolsuper, rolinherit, "
+					  "rolcreaterole, rolcreatedb, "
+					  "rolcanlogin, rolconnlimit, rolpassword, "
+					  "rolvaliduntil, rolreplication, rolbypassrls, "
+					  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
+					  "rolname = current_user AS is_current_user "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 2", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 0813cef2729..5f63e2114c8 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -1475,15 +1475,15 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_remove(anyarray,anyelement)'"
-							 ", 'array_replace(anyarray,anyelement,anyelement)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_remove(anyarray,anyelement)'"
+						 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_position(anyarray,anyelement)'"
-							 ", 'array_position(anyarray,anyelement,integer)'"
-							 ", 'array_positions(anyarray,anyelement)'"
-							 ", 'width_bucket(anyelement,anyarray)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_position(anyarray,anyelement)'"
+						 ", 'array_position(anyarray,anyelement,integer)'"
+						 ", 'array_positions(anyarray,anyelement)'"
+						 ", 'width_bucket(anyelement,anyarray)'");
 
 	/*
 	 * The query below hardcodes FirstNormalObjectId as 16384 rather than
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 479557abdcc..9a675929e17 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,7 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-		cluster->bin_version = v1 * 10000;
+	cluster->bin_version = v1 * 10000;
 }
 
 
@@ -344,8 +344,8 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-		check_single_dir(pg_data, "pg_wal");
-		check_single_dir(pg_data, "pg_xact");
+	check_single_dir(pg_data, "pg_wal");
+	check_single_dir(pg_data, "pg_xact");
 }
 
 
@@ -385,7 +385,7 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-		check_exec(cluster->bindir, "pg_resetwal", check_versions);
+	check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
 	{
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index c45b3183684..c7a1416494d 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -61,52 +61,52 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
-										   old_cluster.controldata.chkpnt_nxtmulti,
-										   old_cluster.controldata.chkpnt_nxtmxoff);
+	old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
+									   old_cluster.controldata.chkpnt_nxtmulti,
+									   old_cluster.controldata.chkpnt_nxtmxoff);
 
-		for (MultiXactId multi = from_multi; multi != to_multi;)
-		{
-			MultiXactMember member;
-			bool		multixid_valid;
-
-			/*
-			 * Read this multixid's members.
-			 *
-			 * Locking-only XIDs that may be part of multi-xids don't matter
-			 * after upgrade, as there can be no transactions running across
-			 * upgrade.  So as a small optimization, we only read one member
-			 * from each multixid: the one updating one, or if there was no
-			 * update, arbitrarily the first locking xid.
-			 */
-			multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
+	for (MultiXactId multi = from_multi; multi != to_multi;)
+	{
+		MultiXactMember member;
+		bool		multixid_valid;
 
-			/*
-			 * Write the new offset to pg_multixact/offsets.
-			 *
-			 * Even if this multixid is invalid, we still need to write its
-			 * offset if the *previous* multixid was valid.  That's because
-			 * when reading a multixid, the number of members is calculated
-			 * from the difference between the two offsets.
-			 */
-			RecordMultiXactOffset(offsets_writer, multi,
-								  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
+		/*
+		 * Read this multixid's members.
+		 *
+		 * Locking-only XIDs that may be part of multi-xids don't matter after
+		 * upgrade, as there can be no transactions running across upgrade. So
+		 * as a small optimization, we only read one member from each
+		 * multixid: the one updating one, or if there was no update,
+		 * arbitrarily the first locking xid.
+		 */
+		multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
 
-			/* Write the members */
-			if (multixid_valid)
-			{
-				RecordMultiXactMembers(members_writer, next_offset, 1, &member);
-				next_offset += 1;
-			}
+		/*
+		 * Write the new offset to pg_multixact/offsets.
+		 *
+		 * Even if this multixid is invalid, we still need to write its offset
+		 * if the *previous* multixid was valid.  That's because when reading
+		 * a multixid, the number of members is calculated from the difference
+		 * between the two offsets.
+		 */
+		RecordMultiXactOffset(offsets_writer, multi,
+							  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
 
-			/* Advance to next multixid, handling wraparound */
-			multi++;
-			if (multi < FirstMultiXactId)
-				multi = FirstMultiXactId;
-			prev_multixid_valid = multixid_valid;
+		/* Write the members */
+		if (multixid_valid)
+		{
+			RecordMultiXactMembers(members_writer, next_offset, 1, &member);
+			next_offset += 1;
 		}
 
-		FreeOldMultiXactReader(old_reader);
+		/* Advance to next multixid, handling wraparound */
+		multi++;
+		if (multi < FirstMultiXactId)
+			multi = FirstMultiXactId;
+		prev_multixid_valid = multixid_valid;
+	}
+
+	FreeOldMultiXactReader(old_reader);
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index e5d7920c1b1..d8e1b680f5a 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -833,7 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
+		oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index ec2ff7acb21..6c467bdc8a5 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -587,32 +587,32 @@ transfer_relfile(FileNameMap *map, const char *type_suffix)
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-			switch (user_opts.transfer_mode)
-			{
-				case TRANSFER_MODE_CLONE:
-					pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
-						   old_file, new_file);
-					cloneFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
-						   old_file, new_file);
-					copyFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY_FILE_RANGE:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
-						   old_file, new_file);
-					copyFileByRange(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_LINK:
-					pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
-						   old_file, new_file);
-					linkFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_SWAP:
-					/* swap mode is handled in its own code path */
-					pg_fatal("should never happen");
-					break;
-			}
+		switch (user_opts.transfer_mode)
+		{
+			case TRANSFER_MODE_CLONE:
+				pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
+					   old_file, new_file);
+				cloneFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
+					   old_file, new_file);
+				copyFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY_FILE_RANGE:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
+					   old_file, new_file);
+				copyFileByRange(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_LINK:
+				pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
+					   old_file, new_file);
+				linkFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_SWAP:
+				/* swap mode is handled in its own code path */
+				pg_fatal("should never happen");
+				break;
+		}
 	}
 }
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c9573d4b765..e5fb3595598 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -6272,23 +6272,22 @@ get_create_object_cmd(EditableObjectType obj_type, Oid oid,
 			 * ensure the right view gets replaced.  Also, check relation kind
 			 * to be sure it's a view.
 			 *
-			 * Views may have WITH [LOCAL|CASCADED]
-			 * CHECK OPTION.  These are not part of the view definition
-			 * returned by pg_get_viewdef() and so need to be retrieved
-			 * separately.  Materialized views may have
-			 * arbitrary storage parameter reloptions.
+			 * Views may have WITH [LOCAL|CASCADED] CHECK OPTION.  These are
+			 * not part of the view definition returned by pg_get_viewdef()
+			 * and so need to be retrieved separately.  Materialized views may
+			 * have arbitrary storage parameter reloptions.
 			 */
 			printfPQExpBuffer(query, "/* %s */\n", _("Get view's definition and details"));
-				appendPQExpBuffer(query,
-								  "SELECT nspname, relname, relkind, "
-								  "pg_catalog.pg_get_viewdef(c.oid, true), "
-								  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-								  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-								  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
-								  "FROM pg_catalog.pg_class c "
-								  "LEFT JOIN pg_catalog.pg_namespace n "
-								  "ON c.relnamespace = n.oid WHERE c.oid = %u",
-								  oid);
+			appendPQExpBuffer(query,
+							  "SELECT nspname, relname, relkind, "
+							  "pg_catalog.pg_get_viewdef(c.oid, true), "
+							  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+							  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+							  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
+							  "FROM pg_catalog.pg_class c "
+							  "LEFT JOIN pg_catalog.pg_namespace n "
+							  "ON c.relnamespace = n.oid WHERE c.oid = %u",
+							  oid);
 			break;
 	}
 
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 9f26ed928cb..9731c0079c8 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -387,19 +387,19 @@ describeFunctions(const char *functypes, const char *func_pattern,
 						  gettext_noop("stable"),
 						  gettext_noop("volatile"),
 						  gettext_noop("Volatility"));
-			appendPQExpBuffer(&buf,
-							  ",\n CASE\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
-							  " END as \"%s\"",
-							  gettext_noop("restricted"),
-							  gettext_noop("safe"),
-							  gettext_noop("unsafe"),
-							  gettext_noop("Parallel"));
+		appendPQExpBuffer(&buf,
+						  ",\n CASE\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
+						  " END as \"%s\"",
+						  gettext_noop("restricted"),
+						  gettext_noop("safe"),
+						  gettext_noop("unsafe"),
+						  gettext_noop("Parallel"));
 		appendPQExpBuffer(&buf,
 						  ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\""
 						  ",\n CASE WHEN prosecdef THEN '%s' ELSE '%s' END AS \"%s\""
@@ -599,8 +599,8 @@ describeFunctions(const char *functypes, const char *func_pattern,
 
 	myopt.title = _("List of functions");
 	myopt.translate_header = true;
-		myopt.translate_columns = translate_columns;
-		myopt.n_translate_columns = lengthof(translate_columns);
+	myopt.translate_columns = translate_columns;
+	myopt.n_translate_columns = lengthof(translate_columns);
 
 	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
 
@@ -1086,38 +1086,38 @@ permissionsList(const char *pattern, bool showSystem)
 					  "  ), E'\\n') AS \"%s\"",
 					  gettext_noop("Column privileges"));
 
-		appendPQExpBuffer(&buf,
-						  ",\n  pg_catalog.array_to_string(ARRAY(\n"
-						  "    SELECT polname\n"
-						  "    || CASE WHEN NOT polpermissive THEN\n"
-						  "       E' (RESTRICTIVE)'\n"
-						  "       ELSE '' END\n"
-						  "    || CASE WHEN polcmd != '*' THEN\n"
-						  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
-						  "       ELSE E':'\n"
-						  "       END\n"
-						  "    || CASE WHEN polqual IS NOT NULL THEN\n"
-						  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
-						  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END"
-						  "    || CASE WHEN polroles <> '{0}' THEN\n"
-						  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
-						  "               ARRAY(\n"
-						  "                   SELECT rolname\n"
-						  "                   FROM pg_catalog.pg_roles\n"
-						  "                   WHERE oid = ANY (polroles)\n"
-						  "                   ORDER BY 1\n"
-						  "               ), E', ')\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    FROM pg_catalog.pg_policy pol\n"
-						  "    WHERE polrelid = c.oid), E'\\n')\n"
-						  "    AS \"%s\"",
-						  gettext_noop("Policies"));
+	appendPQExpBuffer(&buf,
+					  ",\n  pg_catalog.array_to_string(ARRAY(\n"
+					  "    SELECT polname\n"
+					  "    || CASE WHEN NOT polpermissive THEN\n"
+					  "       E' (RESTRICTIVE)'\n"
+					  "       ELSE '' END\n"
+					  "    || CASE WHEN polcmd != '*' THEN\n"
+					  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
+					  "       ELSE E':'\n"
+					  "       END\n"
+					  "    || CASE WHEN polqual IS NOT NULL THEN\n"
+					  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
+					  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END"
+					  "    || CASE WHEN polroles <> '{0}' THEN\n"
+					  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
+					  "               ARRAY(\n"
+					  "                   SELECT rolname\n"
+					  "                   FROM pg_catalog.pg_roles\n"
+					  "                   WHERE oid = ANY (polroles)\n"
+					  "                   ORDER BY 1\n"
+					  "               ), E', ')\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    FROM pg_catalog.pg_policy pol\n"
+					  "    WHERE polrelid = c.oid), E'\\n')\n"
+					  "    AS \"%s\"",
+					  gettext_noop("Policies"));
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n"
 						 "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
@@ -1675,27 +1675,27 @@ describeOneTableDetails(const char *schemaname,
 		char	   *footers[3] = {NULL, NULL, NULL};
 
 		printfPQExpBuffer(&buf, "/* %s */\n", _("Get sequence information"));
-			appendPQExpBuffer(&buf,
-							  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
-							  "       seqstart AS \"%s\",\n"
-							  "       seqmin AS \"%s\",\n"
-							  "       seqmax AS \"%s\",\n"
-							  "       seqincrement AS \"%s\",\n"
-							  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
-							  "       seqcache AS \"%s\"\n",
-							  gettext_noop("Type"),
-							  gettext_noop("Start"),
-							  gettext_noop("Minimum"),
-							  gettext_noop("Maximum"),
-							  gettext_noop("Increment"),
-							  gettext_noop("yes"),
-							  gettext_noop("no"),
-							  gettext_noop("Cycles?"),
-							  gettext_noop("Cache"));
-			appendPQExpBuffer(&buf,
-							  "FROM pg_catalog.pg_sequence\n"
-							  "WHERE seqrelid = '%s';",
-							  oid);
+		appendPQExpBuffer(&buf,
+						  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
+						  "       seqstart AS \"%s\",\n"
+						  "       seqmin AS \"%s\",\n"
+						  "       seqmax AS \"%s\",\n"
+						  "       seqincrement AS \"%s\",\n"
+						  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
+						  "       seqcache AS \"%s\"\n",
+						  gettext_noop("Type"),
+						  gettext_noop("Start"),
+						  gettext_noop("Minimum"),
+						  gettext_noop("Maximum"),
+						  gettext_noop("Increment"),
+						  gettext_noop("yes"),
+						  gettext_noop("no"),
+						  gettext_noop("Cycles?"),
+						  gettext_noop("Cache"));
+		appendPQExpBuffer(&buf,
+						  "FROM pg_catalog.pg_sequence\n"
+						  "WHERE seqrelid = '%s';",
+						  oid);
 
 		res = PSQLexec(buf.data);
 		if (!res)
@@ -1913,7 +1913,7 @@ describeOneTableDetails(const char *schemaname,
 		appendPQExpBufferStr(&buf, ",\n  (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
 							 "   WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
 		attcoll_col = cols++;
-			appendPQExpBufferStr(&buf, ",\n  a.attidentity");
+		appendPQExpBufferStr(&buf, ",\n  a.attidentity");
 		attidentity_col = cols++;
 		if (pset.sversion >= 120000)
 			appendPQExpBufferStr(&buf, ",\n  a.attgenerated");
@@ -2326,7 +2326,7 @@ describeOneTableDetails(const char *schemaname,
 							 CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
 							 "condeferred) AS condeferred,\n");
 
-			appendPQExpBufferStr(&buf, "i.indisreplident,\n");
+		appendPQExpBufferStr(&buf, "i.indisreplident,\n");
 
 		if (pset.sversion >= 150000)
 			appendPQExpBufferStr(&buf, "i.indnullsnotdistinct,\n");
@@ -2431,7 +2431,7 @@ describeOneTableDetails(const char *schemaname,
 								 "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n  "
 								 "pg_catalog.pg_get_constraintdef(con.oid, true), "
 								 "contype, condeferrable, condeferred");
-				appendPQExpBufferStr(&buf, ", i.indisreplident");
+			appendPQExpBufferStr(&buf, ", i.indisreplident");
 			appendPQExpBufferStr(&buf, ", c2.reltablespace");
 			if (pset.sversion >= 180000)
 				appendPQExpBufferStr(&buf, ", con.conperiod");
@@ -2682,81 +2682,80 @@ describeOneTableDetails(const char *schemaname,
 		PQclear(result);
 
 		/* print any row-level policies */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get row-level policies for this table"));
-			appendPQExpBufferStr(&buf, "SELECT pol.polname,");
-				appendPQExpBufferStr(&buf,
-									 " pol.polpermissive,\n");
-			appendPQExpBuffer(&buf,
-							  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
-							  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
-							  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
-							  "  CASE pol.polcmd\n"
-							  "    WHEN 'r' THEN 'SELECT'\n"
-							  "    WHEN 'a' THEN 'INSERT'\n"
-							  "    WHEN 'w' THEN 'UPDATE'\n"
-							  "    WHEN 'd' THEN 'DELETE'\n"
-							  "    END AS cmd\n"
-							  "FROM pg_catalog.pg_policy pol\n"
-							  "WHERE pol.polrelid = '%s' ORDER BY 1;",
-							  oid);
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get row-level policies for this table"));
+		appendPQExpBufferStr(&buf, "SELECT pol.polname,");
+		appendPQExpBufferStr(&buf,
+							 " pol.polpermissive,\n");
+		appendPQExpBuffer(&buf,
+						  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
+						  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
+						  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
+						  "  CASE pol.polcmd\n"
+						  "    WHEN 'r' THEN 'SELECT'\n"
+						  "    WHEN 'a' THEN 'INSERT'\n"
+						  "    WHEN 'w' THEN 'UPDATE'\n"
+						  "    WHEN 'd' THEN 'DELETE'\n"
+						  "    END AS cmd\n"
+						  "FROM pg_catalog.pg_policy pol\n"
+						  "WHERE pol.polrelid = '%s' ORDER BY 1;",
+						  oid);
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			/*
-			 * Handle cases where RLS is enabled and there are policies, or
-			 * there aren't policies, or RLS isn't enabled but there are
-			 * policies
-			 */
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies:"));
+		/*
+		 * Handle cases where RLS is enabled and there are policies, or there
+		 * aren't policies, or RLS isn't enabled but there are policies
+		 */
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies:"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
 
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
 
-			if (!tableinfo.rowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (row security disabled):"));
+		if (!tableinfo.rowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (row security disabled):"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    POLICY \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    POLICY \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				if (*(PQgetvalue(result, i, 1)) == 'f')
-					appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
+			if (*(PQgetvalue(result, i, 1)) == 'f')
+				appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
 
-				if (!PQgetisnull(result, i, 5))
-					appendPQExpBuffer(&buf, " FOR %s",
-									  PQgetvalue(result, i, 5));
+			if (!PQgetisnull(result, i, 5))
+				appendPQExpBuffer(&buf, " FOR %s",
+								  PQgetvalue(result, i, 5));
 
-				if (!PQgetisnull(result, i, 2))
-				{
-					appendPQExpBuffer(&buf, "\n      TO %s",
-									  PQgetvalue(result, i, 2));
-				}
+			if (!PQgetisnull(result, i, 2))
+			{
+				appendPQExpBuffer(&buf, "\n      TO %s",
+								  PQgetvalue(result, i, 2));
+			}
 
-				if (!PQgetisnull(result, i, 3))
-					appendPQExpBuffer(&buf, "\n      USING (%s)",
-									  PQgetvalue(result, i, 3));
+			if (!PQgetisnull(result, i, 3))
+				appendPQExpBuffer(&buf, "\n      USING (%s)",
+								  PQgetvalue(result, i, 3));
 
-				if (!PQgetisnull(result, i, 4))
-					appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
-									  PQgetvalue(result, i, 4));
+			if (!PQgetisnull(result, i, 4))
+				appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
+								  PQgetvalue(result, i, 4));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* print any extended statistics */
 		if (pset.sversion >= 140000)
@@ -3025,115 +3024,115 @@ describeOneTableDetails(const char *schemaname,
 		}
 
 		/* print any publications */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get publications that publish this table"));
-			if (pset.sversion >= 150000)
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get publications that publish this table"));
+		if (pset.sversion >= 150000)
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
+							  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
+							  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "UNION\n"
+							  "SELECT pubname\n"
+							  "     , pg_get_expr(pr.prqual, c.oid)\n"
+							  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
+							  "         (SELECT string_agg(attname, ', ')\n"
+							  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
+							  "                pg_catalog.pg_attribute\n"
+							  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
+							  "        ELSE NULL END) "
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
+							  "WHERE pr.prrelid = '%s'\n",
+							  oid, oid, oid);
+
+			if (pset.sversion >= 190000)
 			{
+				/*
+				 * Skip entries where this relation appears in the
+				 * publication's EXCEPT list.
+				 */
 				appendPQExpBuffer(&buf,
+								  " AND NOT pr.prexcept\n"
+								  "UNION\n"
 								  "SELECT pubname\n"
 								  "     , NULL\n"
 								  "     , NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
-								  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
-								  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
-								  "UNION\n"
-								  "SELECT pubname\n"
-								  "     , pg_get_expr(pr.prqual, c.oid)\n"
-								  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
-								  "         (SELECT string_agg(attname, ', ')\n"
-								  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
-								  "                pg_catalog.pg_attribute\n"
-								  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
-								  "        ELSE NULL END) "
-								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
-								  "WHERE pr.prrelid = '%s'\n",
+								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+								  "     AND NOT EXISTS (\n"
+								  "     SELECT 1\n"
+								  "     FROM pg_catalog.pg_publication_rel pr\n"
+								  "     WHERE pr.prpubid = p.oid AND\n"
+								  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
+								  "ORDER BY 1;",
 								  oid, oid, oid);
-
-				if (pset.sversion >= 190000)
-				{
-					/*
-					 * Skip entries where this relation appears in the
-					 * publication's EXCEPT list.
-					 */
-					appendPQExpBuffer(&buf,
-									  " AND NOT pr.prexcept\n"
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "     , NULL\n"
-									  "     , NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "     AND NOT EXISTS (\n"
-									  "     SELECT 1\n"
-									  "     FROM pg_catalog.pg_publication_rel pr\n"
-									  "     WHERE pr.prpubid = p.oid AND\n"
-									  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
-									  "ORDER BY 1;",
-									  oid, oid, oid);
-				}
-				else
-				{
-					appendPQExpBuffer(&buf,
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "		, NULL\n"
-									  "		, NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "ORDER BY 1;",
-									  oid);
-				}
 			}
 			else
 			{
 				appendPQExpBuffer(&buf,
+								  "UNION\n"
 								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
-								  "FROM pg_catalog.pg_publication p\n"
-								  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "WHERE pr.prrelid = '%s'\n"
-								  "UNION ALL\n"
-								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
+								  "		, NULL\n"
+								  "		, NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
 								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
 								  "ORDER BY 1;",
-								  oid, oid);
+								  oid);
 			}
+		}
+		else
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "WHERE pr.prrelid = '%s'\n"
+							  "UNION ALL\n"
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "ORDER BY 1;",
+							  oid, oid);
+		}
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			if (tuples > 0)
-				printTableAddFooter(&cont, _("Included in publications:"));
+		if (tuples > 0)
+			printTableAddFooter(&cont, _("Included in publications:"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				/* column list (if any) */
-				if (!PQgetisnull(result, i, 2))
-					appendPQExpBuffer(&buf, " (%s)",
-									  PQgetvalue(result, i, 2));
+			/* column list (if any) */
+			if (!PQgetisnull(result, i, 2))
+				appendPQExpBuffer(&buf, " (%s)",
+								  PQgetvalue(result, i, 2));
 
-				/* row filter (if any) */
-				if (!PQgetisnull(result, i, 1))
-					appendPQExpBuffer(&buf, " WHERE %s",
-									  PQgetvalue(result, i, 1));
+			/* row filter (if any) */
+			if (!PQgetisnull(result, i, 1))
+				appendPQExpBuffer(&buf, " WHERE %s",
+								  PQgetvalue(result, i, 1));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* Print publications where the table is in the EXCEPT clause */
 		if (pset.sversion >= 190000)
@@ -3805,7 +3804,7 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		ncols++;
 	}
 	appendPQExpBufferStr(&buf, "\n, r.rolreplication");
-		appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
+	appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
@@ -3860,8 +3859,8 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
 			add_role_attribute(&buf, _("Replication"));
 
-			if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
-				add_role_attribute(&buf, _("Bypass RLS"));
+		if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
+			add_role_attribute(&buf, _("Bypass RLS"));
 
 		conns = atoi(PQgetvalue(res, i, 6));
 		if (conns >= 0)
@@ -5155,14 +5154,14 @@ listCollations(const char *pattern, bool verbose, bool showSystem)
 					  gettext_noop("Schema"),
 					  gettext_noop("Name"));
 
-		appendPQExpBuffer(&buf,
-						  "  CASE c.collprovider "
-						  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
-						  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
-						  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
-						  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
-						  "END AS \"%s\",\n",
-						  gettext_noop("Provider"));
+	appendPQExpBuffer(&buf,
+					  "  CASE c.collprovider "
+					  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
+					  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
+					  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
+					  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
+					  "END AS \"%s\",\n",
+					  gettext_noop("Provider"));
 
 	appendPQExpBuffer(&buf,
 					  "  c.collcollate AS \"%s\",\n"
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6--





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* [PATCH v4 4/4] run pgindent
@ 2026-06-11 14:15  Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: Nathan Bossart @ 2026-06-11 14:15 UTC (permalink / raw)

---
 src/bin/pg_dump/pg_dump.c              | 457 ++++++++++++------------
 src/bin/pg_dump/pg_dumpall.c           |  30 +-
 src/bin/pg_upgrade/check.c             |  16 +-
 src/bin/pg_upgrade/exec.c              |   8 +-
 src/bin/pg_upgrade/multixact_rewrite.c |  80 ++---
 src/bin/pg_upgrade/pg_upgrade.c        |   2 +-
 src/bin/pg_upgrade/relfilenumber.c     |  54 +--
 src/bin/psql/command.c                 |  29 +-
 src/bin/psql/describe.c                | 461 ++++++++++++-------------
 9 files changed, 567 insertions(+), 570 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 75c27a08540..67a8ebdd3f5 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1491,8 +1491,8 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	 * Disable timeouts if supported.
 	 */
 	ExecuteSqlStatement(AH, "SET statement_timeout = 0");
-		ExecuteSqlStatement(AH, "SET lock_timeout = 0");
-		ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
+	ExecuteSqlStatement(AH, "SET lock_timeout = 0");
+	ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
 	if (AH->remoteVersion >= 170000)
 		ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
 
@@ -1505,10 +1505,10 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	/*
 	 * Adjust row-security mode, if supported.
 	 */
-		if (dopt->enable_row_security)
-			ExecuteSqlStatement(AH, "SET row_security = on");
-		else
-			ExecuteSqlStatement(AH, "SET row_security = off");
+	if (dopt->enable_row_security)
+		ExecuteSqlStatement(AH, "SET row_security = on");
+	else
+		ExecuteSqlStatement(AH, "SET row_security = off");
 
 	/*
 	 * For security reasons, we restrict the expansion of non-system views and
@@ -1955,7 +1955,7 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
 	if (fout->dopt->binary_upgrade)
 		dobj->dump = ext->dobj.dump;
 	else
-			dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
+		dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
 
 	return true;
 }
@@ -1989,9 +1989,9 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
 	else if (strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
 	{
 		/*
-		 * We dump out any ACLs defined in pg_catalog, if
-		 * they are interesting (and not the original ACLs which were set at
-		 * initdb time, see pg_init_privs).
+		 * We dump out any ACLs defined in pg_catalog, if they are interesting
+		 * (and not the original ACLs which were set at initdb time, see
+		 * pg_init_privs).
 		 */
 		nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
 	}
@@ -3298,7 +3298,7 @@ dumpDatabase(Archive *fout)
 						 "datcollate, datctype, datfrozenxid, "
 						 "datacl, acldefault('d', datdba) AS acldefault, "
 						 "datistemplate, datconnlimit, ");
-		appendPQExpBufferStr(dbQry, "datminmxid, ");
+	appendPQExpBufferStr(dbQry, "datminmxid, ");
 	if (fout->remoteVersion >= 170000)
 		appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
 	else if (fout->remoteVersion >= 150000)
@@ -3640,11 +3640,11 @@ dumpDatabase(Archive *fout)
 					ii_oid,
 					ii_relminmxid;
 
-			appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
-							  "FROM pg_catalog.pg_class\n"
-							  "WHERE oid IN (%u, %u, %u, %u);\n",
-							  LargeObjectRelationId, LargeObjectLOidPNIndexId,
-							  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
+		appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
+						  "FROM pg_catalog.pg_class\n"
+						  "WHERE oid IN (%u, %u, %u, %u);\n",
+						  LargeObjectRelationId, LargeObjectLOidPNIndexId,
+						  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
 
 		lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
 
@@ -4276,7 +4276,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 
 	printfPQExpBuffer(query,
 					  "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
-		appendPQExpBufferStr(query, "pol.polpermissive, ");
+	appendPQExpBufferStr(query, "pol.polpermissive, ");
 	appendPQExpBuffer(query,
 					  "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
 					  "   pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
@@ -6635,9 +6635,9 @@ getAccessMethods(Archive *fout)
 	 * Select all access methods from pg_am table.
 	 */
 	appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
-		appendPQExpBufferStr(query,
-							 "amtype, "
-							 "amhandler::pg_catalog.regproc AS amhandler ");
+	appendPQExpBufferStr(query,
+						 "amtype, "
+						 "amhandler::pg_catalog.regproc AS amhandler ");
 	appendPQExpBufferStr(query, "FROM pg_am");
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -6829,35 +6829,35 @@ getAggregates(Archive *fout)
 	 * Find all interesting aggregates.  See comment in getFuncs() for the
 	 * rationale behind the filtering logic.
 	 */
-		agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
-					 : "p.proisagg");
+	agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
+				 : "p.proisagg");
 
-		appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
-						  "p.proname AS aggname, "
-						  "p.pronamespace AS aggnamespace, "
-						  "p.pronargs, p.proargtypes, "
-						  "p.proowner, "
-						  "p.proacl AS aggacl, "
-						  "acldefault('f', p.proowner) AS acldefault "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s AND ("
-						  "p.pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog') OR "
-						  "p.proacl IS DISTINCT FROM pip.initprivs",
-						  agg_check);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
-		appendPQExpBufferChar(query, ')');
+	appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
+					  "p.proname AS aggname, "
+					  "p.pronamespace AS aggnamespace, "
+					  "p.pronargs, p.proargtypes, "
+					  "p.proowner, "
+					  "p.proacl AS aggacl, "
+					  "acldefault('f', p.proowner) AS acldefault "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s AND ("
+					  "p.pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog') OR "
+					  "p.proacl IS DISTINCT FROM pip.initprivs",
+					  agg_check);
+	if (dopt->binary_upgrade)
+		appendPQExpBufferStr(query,
+							 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -6959,53 +6959,53 @@ getFuncs(Archive *fout)
 	 * include them, since we want to dump extension members individually in
 	 * that mode.  Also, if they are used by casts or transforms then we need
 	 * to gather the information about them, though they won't be dumped if
-	 * they are built-in.  Also, include functions in
-	 * pg_catalog if they have an ACL different from what's shown in
-	 * pg_init_privs (so we have to join to pg_init_privs; annoying).
+	 * they are built-in.  Also, include functions in pg_catalog if they have
+	 * an ACL different from what's shown in pg_init_privs (so we have to join
+	 * to pg_init_privs; annoying).
 	 */
-		not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
-						 : "NOT p.proisagg");
+	not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
+					 : "NOT p.proisagg");
 
-		appendPQExpBuffer(query,
-						  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
-						  "p.pronargs, p.proargtypes, p.prorettype, "
-						  "p.proacl, "
-						  "acldefault('f', p.proowner) AS acldefault, "
-						  "p.pronamespace, "
-						  "p.proowner "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s"
-						  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
-						  "WHERE classid = 'pg_proc'::regclass AND "
-						  "objid = p.oid AND deptype = 'i')"
-						  "\n  AND ("
-						  "\n  pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog')"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
-						  "\n  WHERE pg_cast.oid > %u "
-						  "\n  AND p.oid = pg_cast.castfunc)"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
-						  "\n  WHERE pg_transform.oid > %u AND "
-						  "\n  (p.oid = pg_transform.trffromsql"
-						  "\n  OR p.oid = pg_transform.trftosql))",
-						  not_agg_check,
-						  g_last_builtin_oid,
-						  g_last_builtin_oid);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
+	appendPQExpBuffer(query,
+					  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
+					  "p.pronargs, p.proargtypes, p.prorettype, "
+					  "p.proacl, "
+					  "acldefault('f', p.proowner) AS acldefault, "
+					  "p.pronamespace, "
+					  "p.proowner "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s"
+					  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
+					  "WHERE classid = 'pg_proc'::regclass AND "
+					  "objid = p.oid AND deptype = 'i')"
+					  "\n  AND ("
+					  "\n  pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog')"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
+					  "\n  WHERE pg_cast.oid > %u "
+					  "\n  AND p.oid = pg_cast.castfunc)"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
+					  "\n  WHERE pg_transform.oid > %u AND "
+					  "\n  (p.oid = pg_transform.trffromsql"
+					  "\n  OR p.oid = pg_transform.trftosql))",
+					  not_agg_check,
+					  g_last_builtin_oid,
+					  g_last_builtin_oid);
+	if (dopt->binary_upgrade)
 		appendPQExpBufferStr(query,
-							 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
-		appendPQExpBufferChar(query, ')');
+							 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferStr(query,
+						 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -7254,31 +7254,31 @@ getTables(Archive *fout, int *numTables)
 		appendPQExpBufferStr(query,
 							 "c.relhasoids, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispopulated, ");
+	appendPQExpBufferStr(query,
+						 "c.relispopulated, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relreplident, ");
+	appendPQExpBufferStr(query,
+						 "c.relreplident, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relrowsecurity, c.relforcerowsecurity, ");
+	appendPQExpBufferStr(query,
+						 "c.relrowsecurity, c.relforcerowsecurity, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relminmxid, tc.relminmxid AS tminmxid, ");
+	appendPQExpBufferStr(query,
+						 "c.relminmxid, tc.relminmxid AS tminmxid, ");
 
-		appendPQExpBufferStr(query,
-							 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-							 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-							 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
+	appendPQExpBufferStr(query,
+						 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+						 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+						 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
 
-		appendPQExpBufferStr(query,
-							 "am.amname, ");
+	appendPQExpBufferStr(query,
+						 "am.amname, ");
 
-		appendPQExpBufferStr(query,
-							 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
+	appendPQExpBufferStr(query,
+						 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispartition AS ispartition ");
+	appendPQExpBufferStr(query,
+						 "c.relispartition AS ispartition ");
 
 	/*
 	 * Left join to pg_depend to pick up dependency info linking sequences to
@@ -7298,8 +7298,8 @@ getTables(Archive *fout, int *numTables)
 	/*
 	 * Left join to pg_am to pick up the amname.
 	 */
-		appendPQExpBufferStr(query,
-							 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
+	appendPQExpBufferStr(query,
+						 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
 
 	/*
 	 * We purposefully ignore toast OIDs for partitioned tables; the reason is
@@ -7870,8 +7870,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 						 "t.reloptions AS indreloptions, ");
 
 
-		appendPQExpBufferStr(query,
-							 "i.indisreplident, ");
+	appendPQExpBufferStr(query,
+						 "i.indisreplident, ");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(query,
@@ -9315,8 +9315,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 		appendPQExpBufferStr(q,
 							 "'' AS attcompression,\n");
 
-		appendPQExpBufferStr(q,
-							 "a.attidentity,\n");
+	appendPQExpBufferStr(q,
+						 "a.attidentity,\n");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(q,
@@ -10719,73 +10719,73 @@ getAdditionalACLs(Archive *fout)
 	PQclear(res);
 
 	/* Fetch initial-privileges data */
-		printfPQExpBuffer(query,
-						  "SELECT objoid, classoid, objsubid, privtype, initprivs "
-						  "FROM pg_init_privs");
+	printfPQExpBuffer(query,
+					  "SELECT objoid, classoid, objsubid, privtype, initprivs "
+					  "FROM pg_init_privs");
 
-		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
-		ntups = PQntuples(res);
-		for (i = 0; i < ntups; i++)
-		{
-			Oid			objoid = atooid(PQgetvalue(res, i, 0));
-			Oid			classoid = atooid(PQgetvalue(res, i, 1));
-			int			objsubid = atoi(PQgetvalue(res, i, 2));
-			char		privtype = *(PQgetvalue(res, i, 3));
-			char	   *initprivs = PQgetvalue(res, i, 4);
-			CatalogId	objId;
-			DumpableObject *dobj;
+	ntups = PQntuples(res);
+	for (i = 0; i < ntups; i++)
+	{
+		Oid			objoid = atooid(PQgetvalue(res, i, 0));
+		Oid			classoid = atooid(PQgetvalue(res, i, 1));
+		int			objsubid = atoi(PQgetvalue(res, i, 2));
+		char		privtype = *(PQgetvalue(res, i, 3));
+		char	   *initprivs = PQgetvalue(res, i, 4);
+		CatalogId	objId;
+		DumpableObject *dobj;
 
-			objId.tableoid = classoid;
-			objId.oid = objoid;
-			dobj = findObjectByCatalogId(objId);
-			/* OK to ignore entries we haven't got a DumpableObject for */
-			if (dobj)
+		objId.tableoid = classoid;
+		objId.oid = objoid;
+		dobj = findObjectByCatalogId(objId);
+		/* OK to ignore entries we haven't got a DumpableObject for */
+		if (dobj)
+		{
+			/* Cope with sub-object initprivs */
+			if (objsubid != 0)
 			{
-				/* Cope with sub-object initprivs */
-				if (objsubid != 0)
-				{
-					if (dobj->objType == DO_TABLE)
-					{
-						/* For a column initprivs, set the table's ACL flags */
-						dobj->components |= DUMP_COMPONENT_ACL;
-						((TableInfo *) dobj)->hascolumnACLs = true;
-					}
-					else
-						pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
-									   classoid, objoid, objsubid);
-					continue;
-				}
-
-				/*
-				 * We ignore any pg_init_privs.initprivs entry for the public
-				 * schema, as explained in getNamespaces().
-				 */
-				if (dobj->objType == DO_NAMESPACE &&
-					strcmp(dobj->name, "public") == 0)
-					continue;
-
-				/* Else it had better be of a type we think has ACLs */
-				if (dobj->objType == DO_NAMESPACE ||
-					dobj->objType == DO_TYPE ||
-					dobj->objType == DO_FUNC ||
-					dobj->objType == DO_AGG ||
-					dobj->objType == DO_TABLE ||
-					dobj->objType == DO_PROCLANG ||
-					dobj->objType == DO_FDW ||
-					dobj->objType == DO_FOREIGN_SERVER)
+				if (dobj->objType == DO_TABLE)
 				{
-					DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
-
-					daobj->dacl.privtype = privtype;
-					daobj->dacl.initprivs = pstrdup(initprivs);
+					/* For a column initprivs, set the table's ACL flags */
+					dobj->components |= DUMP_COMPONENT_ACL;
+					((TableInfo *) dobj)->hascolumnACLs = true;
 				}
 				else
 					pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
 								   classoid, objoid, objsubid);
+				continue;
+			}
+
+			/*
+			 * We ignore any pg_init_privs.initprivs entry for the public
+			 * schema, as explained in getNamespaces().
+			 */
+			if (dobj->objType == DO_NAMESPACE &&
+				strcmp(dobj->name, "public") == 0)
+				continue;
+
+			/* Else it had better be of a type we think has ACLs */
+			if (dobj->objType == DO_NAMESPACE ||
+				dobj->objType == DO_TYPE ||
+				dobj->objType == DO_FUNC ||
+				dobj->objType == DO_AGG ||
+				dobj->objType == DO_TABLE ||
+				dobj->objType == DO_PROCLANG ||
+				dobj->objType == DO_FDW ||
+				dobj->objType == DO_FOREIGN_SERVER)
+			{
+				DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
+
+				daobj->dacl.privtype = privtype;
+				daobj->dacl.initprivs = pstrdup(initprivs);
 			}
+			else
+				pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
+							   classoid, objoid, objsubid);
 		}
-		PQclear(res);
+	}
+	PQclear(res);
 
 	destroyPQExpBuffer(query);
 }
@@ -11130,8 +11130,8 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 		 * The results must be in the order of the relations supplied in the
 		 * parameters to ensure we remain in sync as we walk through the TOC.
 		 *
-		 * For versions before 19, the redundant filter clause on s.tablename =
-		 * ANY(...) seems sufficient to convince the planner to use
+		 * For versions before 19, the redundant filter clause on s.tablename
+		 * = ANY(...) seems sufficient to convince the planner to use
 		 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
 		 * In newer versions, pg_stats returns the table OIDs, eliminating the
 		 * need for that hack.
@@ -13471,11 +13471,11 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
 							 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
 							 "proleakproof,\n");
 
-			appendPQExpBufferStr(query,
-								 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
+		appendPQExpBufferStr(query,
+							 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
 
-			appendPQExpBufferStr(query,
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -14965,9 +14965,9 @@ dumpCollation(Archive *fout, const CollInfo *collinfo)
 	/* Get collation-specific details */
 	appendPQExpBufferStr(query, "SELECT ");
 
-		appendPQExpBufferStr(query,
-							 "collprovider, "
-							 "collversion, ");
+	appendPQExpBufferStr(query,
+						 "collprovider, "
+						 "collversion, ");
 
 	if (fout->remoteVersion >= 120000)
 		appendPQExpBufferStr(query,
@@ -15374,23 +15374,23 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 							 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
 							 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggkind,\n"
-								 "aggmtransfn,\n"
-								 "aggminvtransfn,\n"
-								 "aggmfinalfn,\n"
-								 "aggmtranstype::pg_catalog.regtype,\n"
-								 "aggfinalextra,\n"
-								 "aggmfinalextra,\n"
-								 "aggtransspace,\n"
-								 "aggmtransspace,\n"
-								 "aggminitval,\n");
+		appendPQExpBufferStr(query,
+							 "aggkind,\n"
+							 "aggmtransfn,\n"
+							 "aggminvtransfn,\n"
+							 "aggmfinalfn,\n"
+							 "aggmtranstype::pg_catalog.regtype,\n"
+							 "aggfinalextra,\n"
+							 "aggmfinalextra,\n"
+							 "aggtransspace,\n"
+							 "aggmtransspace,\n"
+							 "aggminitval,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggcombinefn,\n"
-								 "aggserialfn,\n"
-								 "aggdeserialfn,\n"
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "aggcombinefn,\n"
+							 "aggserialfn,\n"
+							 "aggdeserialfn,\n"
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -16850,30 +16850,30 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
 			appendPQExpBufferStr(query,
 								 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
 
-				/*
-				 * In principle we should call acldefault('c', relowner) to
-				 * get the default ACL for a column.  However, we don't
-				 * currently store the numeric OID of the relowner in
-				 * TableInfo.  We could convert the owner name using regrole,
-				 * but that creates a risk of failure due to concurrent role
-				 * renames.  Given that the default ACL for columns is empty
-				 * and is likely to stay that way, it's not worth extra cycles
-				 * and risk to avoid hard-wiring that knowledge here.
-				 */
-				appendPQExpBufferStr(query,
-									 "SELECT at.attname, "
-									 "at.attacl, "
-									 "'{}' AS acldefault, "
-									 "pip.privtype, pip.initprivs "
-									 "FROM pg_catalog.pg_attribute at "
-									 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
-									 "(at.attrelid = pip.objoid "
-									 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
-									 "AND at.attnum = pip.objsubid) "
-									 "WHERE at.attrelid = $1 AND "
-									 "NOT at.attisdropped "
-									 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
-									 "ORDER BY at.attnum");
+			/*
+			 * In principle we should call acldefault('c', relowner) to get
+			 * the default ACL for a column.  However, we don't currently
+			 * store the numeric OID of the relowner in TableInfo.  We could
+			 * convert the owner name using regrole, but that creates a risk
+			 * of failure due to concurrent role renames.  Given that the
+			 * default ACL for columns is empty and is likely to stay that
+			 * way, it's not worth extra cycles and risk to avoid hard-wiring
+			 * that knowledge here.
+			 */
+			appendPQExpBufferStr(query,
+								 "SELECT at.attname, "
+								 "at.attacl, "
+								 "'{}' AS acldefault, "
+								 "pip.privtype, pip.initprivs "
+								 "FROM pg_catalog.pg_attribute at "
+								 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
+								 "(at.attrelid = pip.objoid "
+								 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
+								 "AND at.attnum = pip.objsubid) "
+								 "WHERE at.attrelid = $1 AND "
+								 "NOT at.attisdropped "
+								 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
+								 "ORDER BY at.attnum");
 
 			ExecuteSqlStatement(fout, query->data);
 
@@ -19167,7 +19167,7 @@ collectSequences(Archive *fout)
 	 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
 	 */
 	if (fout->remoteVersion < 180000 ||
-			 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
+		(!fout->dopt->dumpData && !fout->dopt->sequence_data))
 		query = "SELECT seqrelid, format_type(seqtypid, NULL), "
 			"seqstart, seqincrement, "
 			"seqmax, seqmin, "
@@ -19229,15 +19229,14 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
 
 	/*
-	 * The sequence information is gathered in a sorted
-	 * table before any calls to dumpSequence().  See collectSequences() for
-	 * more information.
+	 * The sequence information is gathered in a sorted table before any calls
+	 * to dumpSequence().  See collectSequences() for more information.
 	 */
-		Assert(sequences);
+	Assert(sequences);
 
-		key.oid = tbinfo->dobj.catId.oid;
-		seq = bsearch(&key, sequences, nsequences,
-					  sizeof(SequenceItem), SequenceItemCmp);
+	key.oid = tbinfo->dobj.catId.oid;
+	seq = bsearch(&key, sequences, nsequences,
+				  sizeof(SequenceItem), SequenceItemCmp);
 
 	/* Calculate default limits for a sequence of this type */
 	is_ascending = (seq->incby >= 0);
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 5b10f7122b7..3f61196671c 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -954,11 +954,11 @@ dropRoles(PGconn *conn)
 	int			i_rolname;
 	int			i;
 
-		printfPQExpBuffer(buf,
-						  "SELECT rolname "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 1", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT rolname "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 1", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
@@ -1035,16 +1035,16 @@ dumpRoles(PGconn *conn)
 	 * Notes: rolconfig is dumped later, and pg_authid must be used for
 	 * extracting rolcomment regardless of role_catalog.
 	 */
-		printfPQExpBuffer(buf,
-						  "SELECT oid, rolname, rolsuper, rolinherit, "
-						  "rolcreaterole, rolcreatedb, "
-						  "rolcanlogin, rolconnlimit, rolpassword, "
-						  "rolvaliduntil, rolreplication, rolbypassrls, "
-						  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
-						  "rolname = current_user AS is_current_user "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 2", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT oid, rolname, rolsuper, rolinherit, "
+					  "rolcreaterole, rolcreatedb, "
+					  "rolcanlogin, rolconnlimit, rolpassword, "
+					  "rolvaliduntil, rolreplication, rolbypassrls, "
+					  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
+					  "rolname = current_user AS is_current_user "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 2", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f1e35a6af47..a2f9b683c59 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -1475,15 +1475,15 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_remove(anyarray,anyelement)'"
-							 ", 'array_replace(anyarray,anyelement,anyelement)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_remove(anyarray,anyelement)'"
+						 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_position(anyarray,anyelement)'"
-							 ", 'array_position(anyarray,anyelement,integer)'"
-							 ", 'array_positions(anyarray,anyelement)'"
-							 ", 'width_bucket(anyelement,anyarray)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_position(anyarray,anyelement)'"
+						 ", 'array_position(anyarray,anyelement,integer)'"
+						 ", 'array_positions(anyarray,anyelement)'"
+						 ", 'width_bucket(anyelement,anyarray)'");
 
 	/*
 	 * The query below hardcodes FirstNormalObjectId as 16384 rather than
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 44355feea30..a1bdbf373e3 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,7 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-		cluster->bin_version = v1 * 10000;
+	cluster->bin_version = v1 * 10000;
 }
 
 
@@ -344,8 +344,8 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-		check_single_dir(pg_data, "pg_wal");
-		check_single_dir(pg_data, "pg_xact");
+	check_single_dir(pg_data, "pg_wal");
+	check_single_dir(pg_data, "pg_xact");
 }
 
 
@@ -385,7 +385,7 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-		check_exec(cluster->bindir, "pg_resetwal", check_versions);
+	check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
 	{
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index c45b3183684..c7a1416494d 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -61,52 +61,52 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
-										   old_cluster.controldata.chkpnt_nxtmulti,
-										   old_cluster.controldata.chkpnt_nxtmxoff);
+	old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
+									   old_cluster.controldata.chkpnt_nxtmulti,
+									   old_cluster.controldata.chkpnt_nxtmxoff);
 
-		for (MultiXactId multi = from_multi; multi != to_multi;)
-		{
-			MultiXactMember member;
-			bool		multixid_valid;
-
-			/*
-			 * Read this multixid's members.
-			 *
-			 * Locking-only XIDs that may be part of multi-xids don't matter
-			 * after upgrade, as there can be no transactions running across
-			 * upgrade.  So as a small optimization, we only read one member
-			 * from each multixid: the one updating one, or if there was no
-			 * update, arbitrarily the first locking xid.
-			 */
-			multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
+	for (MultiXactId multi = from_multi; multi != to_multi;)
+	{
+		MultiXactMember member;
+		bool		multixid_valid;
 
-			/*
-			 * Write the new offset to pg_multixact/offsets.
-			 *
-			 * Even if this multixid is invalid, we still need to write its
-			 * offset if the *previous* multixid was valid.  That's because
-			 * when reading a multixid, the number of members is calculated
-			 * from the difference between the two offsets.
-			 */
-			RecordMultiXactOffset(offsets_writer, multi,
-								  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
+		/*
+		 * Read this multixid's members.
+		 *
+		 * Locking-only XIDs that may be part of multi-xids don't matter after
+		 * upgrade, as there can be no transactions running across upgrade. So
+		 * as a small optimization, we only read one member from each
+		 * multixid: the one updating one, or if there was no update,
+		 * arbitrarily the first locking xid.
+		 */
+		multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
 
-			/* Write the members */
-			if (multixid_valid)
-			{
-				RecordMultiXactMembers(members_writer, next_offset, 1, &member);
-				next_offset += 1;
-			}
+		/*
+		 * Write the new offset to pg_multixact/offsets.
+		 *
+		 * Even if this multixid is invalid, we still need to write its offset
+		 * if the *previous* multixid was valid.  That's because when reading
+		 * a multixid, the number of members is calculated from the difference
+		 * between the two offsets.
+		 */
+		RecordMultiXactOffset(offsets_writer, multi,
+							  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
 
-			/* Advance to next multixid, handling wraparound */
-			multi++;
-			if (multi < FirstMultiXactId)
-				multi = FirstMultiXactId;
-			prev_multixid_valid = multixid_valid;
+		/* Write the members */
+		if (multixid_valid)
+		{
+			RecordMultiXactMembers(members_writer, next_offset, 1, &member);
+			next_offset += 1;
 		}
 
-		FreeOldMultiXactReader(old_reader);
+		/* Advance to next multixid, handling wraparound */
+		multi++;
+		if (multi < FirstMultiXactId)
+			multi = FirstMultiXactId;
+		prev_multixid_valid = multixid_valid;
+	}
+
+	FreeOldMultiXactReader(old_reader);
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index e5d7920c1b1..d8e1b680f5a 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -833,7 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
+		oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index ec2ff7acb21..6c467bdc8a5 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -587,32 +587,32 @@ transfer_relfile(FileNameMap *map, const char *type_suffix)
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-			switch (user_opts.transfer_mode)
-			{
-				case TRANSFER_MODE_CLONE:
-					pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
-						   old_file, new_file);
-					cloneFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
-						   old_file, new_file);
-					copyFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY_FILE_RANGE:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
-						   old_file, new_file);
-					copyFileByRange(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_LINK:
-					pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
-						   old_file, new_file);
-					linkFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_SWAP:
-					/* swap mode is handled in its own code path */
-					pg_fatal("should never happen");
-					break;
-			}
+		switch (user_opts.transfer_mode)
+		{
+			case TRANSFER_MODE_CLONE:
+				pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
+					   old_file, new_file);
+				cloneFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
+					   old_file, new_file);
+				copyFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY_FILE_RANGE:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
+					   old_file, new_file);
+				copyFileByRange(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_LINK:
+				pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
+					   old_file, new_file);
+				linkFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_SWAP:
+				/* swap mode is handled in its own code path */
+				pg_fatal("should never happen");
+				break;
+		}
 	}
 }
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 44ef11d980e..c90bf8bbde2 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -6278,23 +6278,22 @@ get_create_object_cmd(EditableObjectType obj_type, Oid oid,
 			 * ensure the right view gets replaced.  Also, check relation kind
 			 * to be sure it's a view.
 			 *
-			 * Views may have WITH [LOCAL|CASCADED]
-			 * CHECK OPTION.  These are not part of the view definition
-			 * returned by pg_get_viewdef() and so need to be retrieved
-			 * separately.  Materialized views may have
-			 * arbitrary storage parameter reloptions.
+			 * Views may have WITH [LOCAL|CASCADED] CHECK OPTION.  These are
+			 * not part of the view definition returned by pg_get_viewdef()
+			 * and so need to be retrieved separately.  Materialized views may
+			 * have arbitrary storage parameter reloptions.
 			 */
 			printfPQExpBuffer(query, "/* %s */\n", _("Get view's definition and details"));
-				appendPQExpBuffer(query,
-								  "SELECT nspname, relname, relkind, "
-								  "pg_catalog.pg_get_viewdef(c.oid, true), "
-								  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-								  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-								  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
-								  "FROM pg_catalog.pg_class c "
-								  "LEFT JOIN pg_catalog.pg_namespace n "
-								  "ON c.relnamespace = n.oid WHERE c.oid = %u",
-								  oid);
+			appendPQExpBuffer(query,
+							  "SELECT nspname, relname, relkind, "
+							  "pg_catalog.pg_get_viewdef(c.oid, true), "
+							  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+							  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+							  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
+							  "FROM pg_catalog.pg_class c "
+							  "LEFT JOIN pg_catalog.pg_namespace n "
+							  "ON c.relnamespace = n.oid WHERE c.oid = %u",
+							  oid);
 			break;
 	}
 
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index ec9c61ee924..87dfe702050 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -387,19 +387,19 @@ describeFunctions(const char *functypes, const char *func_pattern,
 						  gettext_noop("stable"),
 						  gettext_noop("volatile"),
 						  gettext_noop("Volatility"));
-			appendPQExpBuffer(&buf,
-							  ",\n CASE\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
-							  " END as \"%s\"",
-							  gettext_noop("restricted"),
-							  gettext_noop("safe"),
-							  gettext_noop("unsafe"),
-							  gettext_noop("Parallel"));
+		appendPQExpBuffer(&buf,
+						  ",\n CASE\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
+						  " END as \"%s\"",
+						  gettext_noop("restricted"),
+						  gettext_noop("safe"),
+						  gettext_noop("unsafe"),
+						  gettext_noop("Parallel"));
 		appendPQExpBuffer(&buf,
 						  ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\""
 						  ",\n CASE WHEN prosecdef THEN '%s' ELSE '%s' END AS \"%s\""
@@ -599,8 +599,8 @@ describeFunctions(const char *functypes, const char *func_pattern,
 
 	myopt.title = _("List of functions");
 	myopt.translate_header = true;
-		myopt.translate_columns = translate_columns;
-		myopt.n_translate_columns = lengthof(translate_columns);
+	myopt.translate_columns = translate_columns;
+	myopt.n_translate_columns = lengthof(translate_columns);
 
 	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
 
@@ -1086,38 +1086,38 @@ permissionsList(const char *pattern, bool showSystem)
 					  "  ), E'\\n') AS \"%s\"",
 					  gettext_noop("Column privileges"));
 
-		appendPQExpBuffer(&buf,
-						  ",\n  pg_catalog.array_to_string(ARRAY(\n"
-						  "    SELECT polname\n"
-						  "    || CASE WHEN NOT polpermissive THEN\n"
-						  "       E' (RESTRICTIVE)'\n"
-						  "       ELSE '' END\n"
-						  "    || CASE WHEN polcmd != '*' THEN\n"
-						  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
-						  "       ELSE E':'\n"
-						  "       END\n"
-						  "    || CASE WHEN polqual IS NOT NULL THEN\n"
-						  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
-						  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END"
-						  "    || CASE WHEN polroles <> '{0}' THEN\n"
-						  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
-						  "               ARRAY(\n"
-						  "                   SELECT rolname\n"
-						  "                   FROM pg_catalog.pg_roles\n"
-						  "                   WHERE oid = ANY (polroles)\n"
-						  "                   ORDER BY 1\n"
-						  "               ), E', ')\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    FROM pg_catalog.pg_policy pol\n"
-						  "    WHERE polrelid = c.oid), E'\\n')\n"
-						  "    AS \"%s\"",
-						  gettext_noop("Policies"));
+	appendPQExpBuffer(&buf,
+					  ",\n  pg_catalog.array_to_string(ARRAY(\n"
+					  "    SELECT polname\n"
+					  "    || CASE WHEN NOT polpermissive THEN\n"
+					  "       E' (RESTRICTIVE)'\n"
+					  "       ELSE '' END\n"
+					  "    || CASE WHEN polcmd != '*' THEN\n"
+					  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
+					  "       ELSE E':'\n"
+					  "       END\n"
+					  "    || CASE WHEN polqual IS NOT NULL THEN\n"
+					  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
+					  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END"
+					  "    || CASE WHEN polroles <> '{0}' THEN\n"
+					  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
+					  "               ARRAY(\n"
+					  "                   SELECT rolname\n"
+					  "                   FROM pg_catalog.pg_roles\n"
+					  "                   WHERE oid = ANY (polroles)\n"
+					  "                   ORDER BY 1\n"
+					  "               ), E', ')\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    FROM pg_catalog.pg_policy pol\n"
+					  "    WHERE polrelid = c.oid), E'\\n')\n"
+					  "    AS \"%s\"",
+					  gettext_noop("Policies"));
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n"
 						 "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
@@ -1675,27 +1675,27 @@ describeOneTableDetails(const char *schemaname,
 		char	   *footers[3] = {NULL, NULL, NULL};
 
 		printfPQExpBuffer(&buf, "/* %s */\n", _("Get sequence information"));
-			appendPQExpBuffer(&buf,
-							  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
-							  "       seqstart AS \"%s\",\n"
-							  "       seqmin AS \"%s\",\n"
-							  "       seqmax AS \"%s\",\n"
-							  "       seqincrement AS \"%s\",\n"
-							  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
-							  "       seqcache AS \"%s\"\n",
-							  gettext_noop("Type"),
-							  gettext_noop("Start"),
-							  gettext_noop("Minimum"),
-							  gettext_noop("Maximum"),
-							  gettext_noop("Increment"),
-							  gettext_noop("yes"),
-							  gettext_noop("no"),
-							  gettext_noop("Cycles?"),
-							  gettext_noop("Cache"));
-			appendPQExpBuffer(&buf,
-							  "FROM pg_catalog.pg_sequence\n"
-							  "WHERE seqrelid = '%s';",
-							  oid);
+		appendPQExpBuffer(&buf,
+						  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
+						  "       seqstart AS \"%s\",\n"
+						  "       seqmin AS \"%s\",\n"
+						  "       seqmax AS \"%s\",\n"
+						  "       seqincrement AS \"%s\",\n"
+						  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
+						  "       seqcache AS \"%s\"\n",
+						  gettext_noop("Type"),
+						  gettext_noop("Start"),
+						  gettext_noop("Minimum"),
+						  gettext_noop("Maximum"),
+						  gettext_noop("Increment"),
+						  gettext_noop("yes"),
+						  gettext_noop("no"),
+						  gettext_noop("Cycles?"),
+						  gettext_noop("Cache"));
+		appendPQExpBuffer(&buf,
+						  "FROM pg_catalog.pg_sequence\n"
+						  "WHERE seqrelid = '%s';",
+						  oid);
 
 		res = PSQLexec(buf.data);
 		if (!res)
@@ -1913,7 +1913,7 @@ describeOneTableDetails(const char *schemaname,
 		appendPQExpBufferStr(&buf, ",\n  (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
 							 "   WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
 		attcoll_col = cols++;
-			appendPQExpBufferStr(&buf, ",\n  a.attidentity");
+		appendPQExpBufferStr(&buf, ",\n  a.attidentity");
 		attidentity_col = cols++;
 		if (pset.sversion >= 120000)
 			appendPQExpBufferStr(&buf, ",\n  a.attgenerated");
@@ -2326,7 +2326,7 @@ describeOneTableDetails(const char *schemaname,
 							 CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
 							 "condeferred) AS condeferred,\n");
 
-			appendPQExpBufferStr(&buf, "i.indisreplident,\n");
+		appendPQExpBufferStr(&buf, "i.indisreplident,\n");
 
 		if (pset.sversion >= 150000)
 			appendPQExpBufferStr(&buf, "i.indnullsnotdistinct,\n");
@@ -2431,7 +2431,7 @@ describeOneTableDetails(const char *schemaname,
 								 "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n  "
 								 "pg_catalog.pg_get_constraintdef(con.oid, true), "
 								 "contype, condeferrable, condeferred");
-				appendPQExpBufferStr(&buf, ", i.indisreplident");
+			appendPQExpBufferStr(&buf, ", i.indisreplident");
 			appendPQExpBufferStr(&buf, ", c2.reltablespace");
 			if (pset.sversion >= 180000)
 				appendPQExpBufferStr(&buf, ", con.conperiod");
@@ -2682,81 +2682,80 @@ describeOneTableDetails(const char *schemaname,
 		PQclear(result);
 
 		/* print any row-level policies */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get row-level policies for this table"));
-			appendPQExpBufferStr(&buf, "SELECT pol.polname,");
-				appendPQExpBufferStr(&buf,
-									 " pol.polpermissive,\n");
-			appendPQExpBuffer(&buf,
-							  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
-							  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
-							  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
-							  "  CASE pol.polcmd\n"
-							  "    WHEN 'r' THEN 'SELECT'\n"
-							  "    WHEN 'a' THEN 'INSERT'\n"
-							  "    WHEN 'w' THEN 'UPDATE'\n"
-							  "    WHEN 'd' THEN 'DELETE'\n"
-							  "    END AS cmd\n"
-							  "FROM pg_catalog.pg_policy pol\n"
-							  "WHERE pol.polrelid = '%s' ORDER BY 1;",
-							  oid);
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get row-level policies for this table"));
+		appendPQExpBufferStr(&buf, "SELECT pol.polname,");
+		appendPQExpBufferStr(&buf,
+							 " pol.polpermissive,\n");
+		appendPQExpBuffer(&buf,
+						  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
+						  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
+						  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
+						  "  CASE pol.polcmd\n"
+						  "    WHEN 'r' THEN 'SELECT'\n"
+						  "    WHEN 'a' THEN 'INSERT'\n"
+						  "    WHEN 'w' THEN 'UPDATE'\n"
+						  "    WHEN 'd' THEN 'DELETE'\n"
+						  "    END AS cmd\n"
+						  "FROM pg_catalog.pg_policy pol\n"
+						  "WHERE pol.polrelid = '%s' ORDER BY 1;",
+						  oid);
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			/*
-			 * Handle cases where RLS is enabled and there are policies, or
-			 * there aren't policies, or RLS isn't enabled but there are
-			 * policies
-			 */
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies:"));
+		/*
+		 * Handle cases where RLS is enabled and there are policies, or there
+		 * aren't policies, or RLS isn't enabled but there are policies
+		 */
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies:"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
 
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
 
-			if (!tableinfo.rowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (row security disabled):"));
+		if (!tableinfo.rowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (row security disabled):"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    POLICY \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    POLICY \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				if (*(PQgetvalue(result, i, 1)) == 'f')
-					appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
+			if (*(PQgetvalue(result, i, 1)) == 'f')
+				appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
 
-				if (!PQgetisnull(result, i, 5))
-					appendPQExpBuffer(&buf, " FOR %s",
-									  PQgetvalue(result, i, 5));
+			if (!PQgetisnull(result, i, 5))
+				appendPQExpBuffer(&buf, " FOR %s",
+								  PQgetvalue(result, i, 5));
 
-				if (!PQgetisnull(result, i, 2))
-				{
-					appendPQExpBuffer(&buf, "\n      TO %s",
-									  PQgetvalue(result, i, 2));
-				}
+			if (!PQgetisnull(result, i, 2))
+			{
+				appendPQExpBuffer(&buf, "\n      TO %s",
+								  PQgetvalue(result, i, 2));
+			}
 
-				if (!PQgetisnull(result, i, 3))
-					appendPQExpBuffer(&buf, "\n      USING (%s)",
-									  PQgetvalue(result, i, 3));
+			if (!PQgetisnull(result, i, 3))
+				appendPQExpBuffer(&buf, "\n      USING (%s)",
+								  PQgetvalue(result, i, 3));
 
-				if (!PQgetisnull(result, i, 4))
-					appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
-									  PQgetvalue(result, i, 4));
+			if (!PQgetisnull(result, i, 4))
+				appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
+								  PQgetvalue(result, i, 4));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* print any extended statistics */
 		if (pset.sversion >= 140000)
@@ -3025,115 +3024,115 @@ describeOneTableDetails(const char *schemaname,
 		}
 
 		/* print any publications */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get publications that publish this table"));
-			if (pset.sversion >= 150000)
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get publications that publish this table"));
+		if (pset.sversion >= 150000)
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
+							  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
+							  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "UNION\n"
+							  "SELECT pubname\n"
+							  "     , pg_catalog.pg_get_expr(pr.prqual, c.oid)\n"
+							  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
+							  "         (SELECT pg_catalog.string_agg(attname, ', ')\n"
+							  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
+							  "                pg_catalog.pg_attribute\n"
+							  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
+							  "        ELSE NULL END) "
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
+							  "WHERE pr.prrelid = '%s'\n",
+							  oid, oid, oid);
+
+			if (pset.sversion >= 190000)
 			{
+				/*
+				 * Skip entries where this relation appears in the
+				 * publication's EXCEPT list.
+				 */
 				appendPQExpBuffer(&buf,
+								  " AND NOT pr.prexcept\n"
+								  "UNION\n"
 								  "SELECT pubname\n"
 								  "     , NULL\n"
 								  "     , NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
-								  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
-								  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
-								  "UNION\n"
-								  "SELECT pubname\n"
-								  "     , pg_catalog.pg_get_expr(pr.prqual, c.oid)\n"
-								  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
-								  "         (SELECT pg_catalog.string_agg(attname, ', ')\n"
-								  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
-								  "                pg_catalog.pg_attribute\n"
-								  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
-								  "        ELSE NULL END) "
-								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
-								  "WHERE pr.prrelid = '%s'\n",
+								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+								  "     AND NOT EXISTS (\n"
+								  "     SELECT 1\n"
+								  "     FROM pg_catalog.pg_publication_rel pr\n"
+								  "     WHERE pr.prpubid = p.oid AND\n"
+								  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
+								  "ORDER BY 1;",
 								  oid, oid, oid);
-
-				if (pset.sversion >= 190000)
-				{
-					/*
-					 * Skip entries where this relation appears in the
-					 * publication's EXCEPT list.
-					 */
-					appendPQExpBuffer(&buf,
-									  " AND NOT pr.prexcept\n"
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "     , NULL\n"
-									  "     , NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "     AND NOT EXISTS (\n"
-									  "     SELECT 1\n"
-									  "     FROM pg_catalog.pg_publication_rel pr\n"
-									  "     WHERE pr.prpubid = p.oid AND\n"
-									  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
-									  "ORDER BY 1;",
-									  oid, oid, oid);
-				}
-				else
-				{
-					appendPQExpBuffer(&buf,
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "		, NULL\n"
-									  "		, NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "ORDER BY 1;",
-									  oid);
-				}
 			}
 			else
 			{
 				appendPQExpBuffer(&buf,
+								  "UNION\n"
 								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
-								  "FROM pg_catalog.pg_publication p\n"
-								  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "WHERE pr.prrelid = '%s'\n"
-								  "UNION ALL\n"
-								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
+								  "		, NULL\n"
+								  "		, NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
 								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
 								  "ORDER BY 1;",
-								  oid, oid);
+								  oid);
 			}
+		}
+		else
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "WHERE pr.prrelid = '%s'\n"
+							  "UNION ALL\n"
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "ORDER BY 1;",
+							  oid, oid);
+		}
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			if (tuples > 0)
-				printTableAddFooter(&cont, _("Included in publications:"));
+		if (tuples > 0)
+			printTableAddFooter(&cont, _("Included in publications:"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				/* column list (if any) */
-				if (!PQgetisnull(result, i, 2))
-					appendPQExpBuffer(&buf, " (%s)",
-									  PQgetvalue(result, i, 2));
+			/* column list (if any) */
+			if (!PQgetisnull(result, i, 2))
+				appendPQExpBuffer(&buf, " (%s)",
+								  PQgetvalue(result, i, 2));
 
-				/* row filter (if any) */
-				if (!PQgetisnull(result, i, 1))
-					appendPQExpBuffer(&buf, " WHERE %s",
-									  PQgetvalue(result, i, 1));
+			/* row filter (if any) */
+			if (!PQgetisnull(result, i, 1))
+				appendPQExpBuffer(&buf, " WHERE %s",
+								  PQgetvalue(result, i, 1));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* Print publications where the table is in the EXCEPT clause */
 		if (pset.sversion >= 190000)
@@ -3805,7 +3804,7 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		ncols++;
 	}
 	appendPQExpBufferStr(&buf, "\n, r.rolreplication");
-		appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
+	appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
@@ -3860,8 +3859,8 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
 			add_role_attribute(&buf, _("Replication"));
 
-			if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
-				add_role_attribute(&buf, _("Bypass RLS"));
+		if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
+			add_role_attribute(&buf, _("Bypass RLS"));
 
 		conns = atoi(PQgetvalue(res, i, 6));
 		if (conns >= 0)
@@ -5155,14 +5154,14 @@ listCollations(const char *pattern, bool verbose, bool showSystem)
 					  gettext_noop("Schema"),
 					  gettext_noop("Name"));
 
-		appendPQExpBuffer(&buf,
-						  "  CASE c.collprovider "
-						  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
-						  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
-						  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
-						  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
-						  "END AS \"%s\",\n",
-						  gettext_noop("Provider"));
+	appendPQExpBuffer(&buf,
+					  "  CASE c.collprovider "
+					  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
+					  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
+					  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
+					  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
+					  "END AS \"%s\",\n",
+					  gettext_noop("Provider"));
 
 	appendPQExpBuffer(&buf,
 					  "  c.collcollate AS \"%s\",\n"
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg--





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* [PATCH v4 4/4] run pgindent
@ 2026-06-11 14:15  Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: Nathan Bossart @ 2026-06-11 14:15 UTC (permalink / raw)

---
 src/bin/pg_dump/pg_dump.c              | 457 ++++++++++++------------
 src/bin/pg_dump/pg_dumpall.c           |  30 +-
 src/bin/pg_upgrade/check.c             |  16 +-
 src/bin/pg_upgrade/exec.c              |   8 +-
 src/bin/pg_upgrade/multixact_rewrite.c |  80 ++---
 src/bin/pg_upgrade/pg_upgrade.c        |   2 +-
 src/bin/pg_upgrade/relfilenumber.c     |  54 +--
 src/bin/psql/command.c                 |  29 +-
 src/bin/psql/describe.c                | 461 ++++++++++++-------------
 9 files changed, 567 insertions(+), 570 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 75c27a08540..67a8ebdd3f5 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1491,8 +1491,8 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	 * Disable timeouts if supported.
 	 */
 	ExecuteSqlStatement(AH, "SET statement_timeout = 0");
-		ExecuteSqlStatement(AH, "SET lock_timeout = 0");
-		ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
+	ExecuteSqlStatement(AH, "SET lock_timeout = 0");
+	ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
 	if (AH->remoteVersion >= 170000)
 		ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
 
@@ -1505,10 +1505,10 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	/*
 	 * Adjust row-security mode, if supported.
 	 */
-		if (dopt->enable_row_security)
-			ExecuteSqlStatement(AH, "SET row_security = on");
-		else
-			ExecuteSqlStatement(AH, "SET row_security = off");
+	if (dopt->enable_row_security)
+		ExecuteSqlStatement(AH, "SET row_security = on");
+	else
+		ExecuteSqlStatement(AH, "SET row_security = off");
 
 	/*
 	 * For security reasons, we restrict the expansion of non-system views and
@@ -1955,7 +1955,7 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
 	if (fout->dopt->binary_upgrade)
 		dobj->dump = ext->dobj.dump;
 	else
-			dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
+		dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
 
 	return true;
 }
@@ -1989,9 +1989,9 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
 	else if (strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
 	{
 		/*
-		 * We dump out any ACLs defined in pg_catalog, if
-		 * they are interesting (and not the original ACLs which were set at
-		 * initdb time, see pg_init_privs).
+		 * We dump out any ACLs defined in pg_catalog, if they are interesting
+		 * (and not the original ACLs which were set at initdb time, see
+		 * pg_init_privs).
 		 */
 		nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
 	}
@@ -3298,7 +3298,7 @@ dumpDatabase(Archive *fout)
 						 "datcollate, datctype, datfrozenxid, "
 						 "datacl, acldefault('d', datdba) AS acldefault, "
 						 "datistemplate, datconnlimit, ");
-		appendPQExpBufferStr(dbQry, "datminmxid, ");
+	appendPQExpBufferStr(dbQry, "datminmxid, ");
 	if (fout->remoteVersion >= 170000)
 		appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
 	else if (fout->remoteVersion >= 150000)
@@ -3640,11 +3640,11 @@ dumpDatabase(Archive *fout)
 					ii_oid,
 					ii_relminmxid;
 
-			appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
-							  "FROM pg_catalog.pg_class\n"
-							  "WHERE oid IN (%u, %u, %u, %u);\n",
-							  LargeObjectRelationId, LargeObjectLOidPNIndexId,
-							  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
+		appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
+						  "FROM pg_catalog.pg_class\n"
+						  "WHERE oid IN (%u, %u, %u, %u);\n",
+						  LargeObjectRelationId, LargeObjectLOidPNIndexId,
+						  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
 
 		lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
 
@@ -4276,7 +4276,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 
 	printfPQExpBuffer(query,
 					  "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
-		appendPQExpBufferStr(query, "pol.polpermissive, ");
+	appendPQExpBufferStr(query, "pol.polpermissive, ");
 	appendPQExpBuffer(query,
 					  "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
 					  "   pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
@@ -6635,9 +6635,9 @@ getAccessMethods(Archive *fout)
 	 * Select all access methods from pg_am table.
 	 */
 	appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
-		appendPQExpBufferStr(query,
-							 "amtype, "
-							 "amhandler::pg_catalog.regproc AS amhandler ");
+	appendPQExpBufferStr(query,
+						 "amtype, "
+						 "amhandler::pg_catalog.regproc AS amhandler ");
 	appendPQExpBufferStr(query, "FROM pg_am");
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -6829,35 +6829,35 @@ getAggregates(Archive *fout)
 	 * Find all interesting aggregates.  See comment in getFuncs() for the
 	 * rationale behind the filtering logic.
 	 */
-		agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
-					 : "p.proisagg");
+	agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
+				 : "p.proisagg");
 
-		appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
-						  "p.proname AS aggname, "
-						  "p.pronamespace AS aggnamespace, "
-						  "p.pronargs, p.proargtypes, "
-						  "p.proowner, "
-						  "p.proacl AS aggacl, "
-						  "acldefault('f', p.proowner) AS acldefault "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s AND ("
-						  "p.pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog') OR "
-						  "p.proacl IS DISTINCT FROM pip.initprivs",
-						  agg_check);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
-		appendPQExpBufferChar(query, ')');
+	appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
+					  "p.proname AS aggname, "
+					  "p.pronamespace AS aggnamespace, "
+					  "p.pronargs, p.proargtypes, "
+					  "p.proowner, "
+					  "p.proacl AS aggacl, "
+					  "acldefault('f', p.proowner) AS acldefault "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s AND ("
+					  "p.pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog') OR "
+					  "p.proacl IS DISTINCT FROM pip.initprivs",
+					  agg_check);
+	if (dopt->binary_upgrade)
+		appendPQExpBufferStr(query,
+							 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -6959,53 +6959,53 @@ getFuncs(Archive *fout)
 	 * include them, since we want to dump extension members individually in
 	 * that mode.  Also, if they are used by casts or transforms then we need
 	 * to gather the information about them, though they won't be dumped if
-	 * they are built-in.  Also, include functions in
-	 * pg_catalog if they have an ACL different from what's shown in
-	 * pg_init_privs (so we have to join to pg_init_privs; annoying).
+	 * they are built-in.  Also, include functions in pg_catalog if they have
+	 * an ACL different from what's shown in pg_init_privs (so we have to join
+	 * to pg_init_privs; annoying).
 	 */
-		not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
-						 : "NOT p.proisagg");
+	not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
+					 : "NOT p.proisagg");
 
-		appendPQExpBuffer(query,
-						  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
-						  "p.pronargs, p.proargtypes, p.prorettype, "
-						  "p.proacl, "
-						  "acldefault('f', p.proowner) AS acldefault, "
-						  "p.pronamespace, "
-						  "p.proowner "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s"
-						  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
-						  "WHERE classid = 'pg_proc'::regclass AND "
-						  "objid = p.oid AND deptype = 'i')"
-						  "\n  AND ("
-						  "\n  pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog')"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
-						  "\n  WHERE pg_cast.oid > %u "
-						  "\n  AND p.oid = pg_cast.castfunc)"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
-						  "\n  WHERE pg_transform.oid > %u AND "
-						  "\n  (p.oid = pg_transform.trffromsql"
-						  "\n  OR p.oid = pg_transform.trftosql))",
-						  not_agg_check,
-						  g_last_builtin_oid,
-						  g_last_builtin_oid);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
+	appendPQExpBuffer(query,
+					  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
+					  "p.pronargs, p.proargtypes, p.prorettype, "
+					  "p.proacl, "
+					  "acldefault('f', p.proowner) AS acldefault, "
+					  "p.pronamespace, "
+					  "p.proowner "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s"
+					  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
+					  "WHERE classid = 'pg_proc'::regclass AND "
+					  "objid = p.oid AND deptype = 'i')"
+					  "\n  AND ("
+					  "\n  pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog')"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
+					  "\n  WHERE pg_cast.oid > %u "
+					  "\n  AND p.oid = pg_cast.castfunc)"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
+					  "\n  WHERE pg_transform.oid > %u AND "
+					  "\n  (p.oid = pg_transform.trffromsql"
+					  "\n  OR p.oid = pg_transform.trftosql))",
+					  not_agg_check,
+					  g_last_builtin_oid,
+					  g_last_builtin_oid);
+	if (dopt->binary_upgrade)
 		appendPQExpBufferStr(query,
-							 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
-		appendPQExpBufferChar(query, ')');
+							 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferStr(query,
+						 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -7254,31 +7254,31 @@ getTables(Archive *fout, int *numTables)
 		appendPQExpBufferStr(query,
 							 "c.relhasoids, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispopulated, ");
+	appendPQExpBufferStr(query,
+						 "c.relispopulated, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relreplident, ");
+	appendPQExpBufferStr(query,
+						 "c.relreplident, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relrowsecurity, c.relforcerowsecurity, ");
+	appendPQExpBufferStr(query,
+						 "c.relrowsecurity, c.relforcerowsecurity, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relminmxid, tc.relminmxid AS tminmxid, ");
+	appendPQExpBufferStr(query,
+						 "c.relminmxid, tc.relminmxid AS tminmxid, ");
 
-		appendPQExpBufferStr(query,
-							 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-							 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-							 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
+	appendPQExpBufferStr(query,
+						 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+						 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+						 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
 
-		appendPQExpBufferStr(query,
-							 "am.amname, ");
+	appendPQExpBufferStr(query,
+						 "am.amname, ");
 
-		appendPQExpBufferStr(query,
-							 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
+	appendPQExpBufferStr(query,
+						 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispartition AS ispartition ");
+	appendPQExpBufferStr(query,
+						 "c.relispartition AS ispartition ");
 
 	/*
 	 * Left join to pg_depend to pick up dependency info linking sequences to
@@ -7298,8 +7298,8 @@ getTables(Archive *fout, int *numTables)
 	/*
 	 * Left join to pg_am to pick up the amname.
 	 */
-		appendPQExpBufferStr(query,
-							 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
+	appendPQExpBufferStr(query,
+						 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
 
 	/*
 	 * We purposefully ignore toast OIDs for partitioned tables; the reason is
@@ -7870,8 +7870,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 						 "t.reloptions AS indreloptions, ");
 
 
-		appendPQExpBufferStr(query,
-							 "i.indisreplident, ");
+	appendPQExpBufferStr(query,
+						 "i.indisreplident, ");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(query,
@@ -9315,8 +9315,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 		appendPQExpBufferStr(q,
 							 "'' AS attcompression,\n");
 
-		appendPQExpBufferStr(q,
-							 "a.attidentity,\n");
+	appendPQExpBufferStr(q,
+						 "a.attidentity,\n");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(q,
@@ -10719,73 +10719,73 @@ getAdditionalACLs(Archive *fout)
 	PQclear(res);
 
 	/* Fetch initial-privileges data */
-		printfPQExpBuffer(query,
-						  "SELECT objoid, classoid, objsubid, privtype, initprivs "
-						  "FROM pg_init_privs");
+	printfPQExpBuffer(query,
+					  "SELECT objoid, classoid, objsubid, privtype, initprivs "
+					  "FROM pg_init_privs");
 
-		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
-		ntups = PQntuples(res);
-		for (i = 0; i < ntups; i++)
-		{
-			Oid			objoid = atooid(PQgetvalue(res, i, 0));
-			Oid			classoid = atooid(PQgetvalue(res, i, 1));
-			int			objsubid = atoi(PQgetvalue(res, i, 2));
-			char		privtype = *(PQgetvalue(res, i, 3));
-			char	   *initprivs = PQgetvalue(res, i, 4);
-			CatalogId	objId;
-			DumpableObject *dobj;
+	ntups = PQntuples(res);
+	for (i = 0; i < ntups; i++)
+	{
+		Oid			objoid = atooid(PQgetvalue(res, i, 0));
+		Oid			classoid = atooid(PQgetvalue(res, i, 1));
+		int			objsubid = atoi(PQgetvalue(res, i, 2));
+		char		privtype = *(PQgetvalue(res, i, 3));
+		char	   *initprivs = PQgetvalue(res, i, 4);
+		CatalogId	objId;
+		DumpableObject *dobj;
 
-			objId.tableoid = classoid;
-			objId.oid = objoid;
-			dobj = findObjectByCatalogId(objId);
-			/* OK to ignore entries we haven't got a DumpableObject for */
-			if (dobj)
+		objId.tableoid = classoid;
+		objId.oid = objoid;
+		dobj = findObjectByCatalogId(objId);
+		/* OK to ignore entries we haven't got a DumpableObject for */
+		if (dobj)
+		{
+			/* Cope with sub-object initprivs */
+			if (objsubid != 0)
 			{
-				/* Cope with sub-object initprivs */
-				if (objsubid != 0)
-				{
-					if (dobj->objType == DO_TABLE)
-					{
-						/* For a column initprivs, set the table's ACL flags */
-						dobj->components |= DUMP_COMPONENT_ACL;
-						((TableInfo *) dobj)->hascolumnACLs = true;
-					}
-					else
-						pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
-									   classoid, objoid, objsubid);
-					continue;
-				}
-
-				/*
-				 * We ignore any pg_init_privs.initprivs entry for the public
-				 * schema, as explained in getNamespaces().
-				 */
-				if (dobj->objType == DO_NAMESPACE &&
-					strcmp(dobj->name, "public") == 0)
-					continue;
-
-				/* Else it had better be of a type we think has ACLs */
-				if (dobj->objType == DO_NAMESPACE ||
-					dobj->objType == DO_TYPE ||
-					dobj->objType == DO_FUNC ||
-					dobj->objType == DO_AGG ||
-					dobj->objType == DO_TABLE ||
-					dobj->objType == DO_PROCLANG ||
-					dobj->objType == DO_FDW ||
-					dobj->objType == DO_FOREIGN_SERVER)
+				if (dobj->objType == DO_TABLE)
 				{
-					DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
-
-					daobj->dacl.privtype = privtype;
-					daobj->dacl.initprivs = pstrdup(initprivs);
+					/* For a column initprivs, set the table's ACL flags */
+					dobj->components |= DUMP_COMPONENT_ACL;
+					((TableInfo *) dobj)->hascolumnACLs = true;
 				}
 				else
 					pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
 								   classoid, objoid, objsubid);
+				continue;
+			}
+
+			/*
+			 * We ignore any pg_init_privs.initprivs entry for the public
+			 * schema, as explained in getNamespaces().
+			 */
+			if (dobj->objType == DO_NAMESPACE &&
+				strcmp(dobj->name, "public") == 0)
+				continue;
+
+			/* Else it had better be of a type we think has ACLs */
+			if (dobj->objType == DO_NAMESPACE ||
+				dobj->objType == DO_TYPE ||
+				dobj->objType == DO_FUNC ||
+				dobj->objType == DO_AGG ||
+				dobj->objType == DO_TABLE ||
+				dobj->objType == DO_PROCLANG ||
+				dobj->objType == DO_FDW ||
+				dobj->objType == DO_FOREIGN_SERVER)
+			{
+				DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
+
+				daobj->dacl.privtype = privtype;
+				daobj->dacl.initprivs = pstrdup(initprivs);
 			}
+			else
+				pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
+							   classoid, objoid, objsubid);
 		}
-		PQclear(res);
+	}
+	PQclear(res);
 
 	destroyPQExpBuffer(query);
 }
@@ -11130,8 +11130,8 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 		 * The results must be in the order of the relations supplied in the
 		 * parameters to ensure we remain in sync as we walk through the TOC.
 		 *
-		 * For versions before 19, the redundant filter clause on s.tablename =
-		 * ANY(...) seems sufficient to convince the planner to use
+		 * For versions before 19, the redundant filter clause on s.tablename
+		 * = ANY(...) seems sufficient to convince the planner to use
 		 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
 		 * In newer versions, pg_stats returns the table OIDs, eliminating the
 		 * need for that hack.
@@ -13471,11 +13471,11 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
 							 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
 							 "proleakproof,\n");
 
-			appendPQExpBufferStr(query,
-								 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
+		appendPQExpBufferStr(query,
+							 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
 
-			appendPQExpBufferStr(query,
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -14965,9 +14965,9 @@ dumpCollation(Archive *fout, const CollInfo *collinfo)
 	/* Get collation-specific details */
 	appendPQExpBufferStr(query, "SELECT ");
 
-		appendPQExpBufferStr(query,
-							 "collprovider, "
-							 "collversion, ");
+	appendPQExpBufferStr(query,
+						 "collprovider, "
+						 "collversion, ");
 
 	if (fout->remoteVersion >= 120000)
 		appendPQExpBufferStr(query,
@@ -15374,23 +15374,23 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 							 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
 							 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggkind,\n"
-								 "aggmtransfn,\n"
-								 "aggminvtransfn,\n"
-								 "aggmfinalfn,\n"
-								 "aggmtranstype::pg_catalog.regtype,\n"
-								 "aggfinalextra,\n"
-								 "aggmfinalextra,\n"
-								 "aggtransspace,\n"
-								 "aggmtransspace,\n"
-								 "aggminitval,\n");
+		appendPQExpBufferStr(query,
+							 "aggkind,\n"
+							 "aggmtransfn,\n"
+							 "aggminvtransfn,\n"
+							 "aggmfinalfn,\n"
+							 "aggmtranstype::pg_catalog.regtype,\n"
+							 "aggfinalextra,\n"
+							 "aggmfinalextra,\n"
+							 "aggtransspace,\n"
+							 "aggmtransspace,\n"
+							 "aggminitval,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggcombinefn,\n"
-								 "aggserialfn,\n"
-								 "aggdeserialfn,\n"
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "aggcombinefn,\n"
+							 "aggserialfn,\n"
+							 "aggdeserialfn,\n"
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -16850,30 +16850,30 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
 			appendPQExpBufferStr(query,
 								 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
 
-				/*
-				 * In principle we should call acldefault('c', relowner) to
-				 * get the default ACL for a column.  However, we don't
-				 * currently store the numeric OID of the relowner in
-				 * TableInfo.  We could convert the owner name using regrole,
-				 * but that creates a risk of failure due to concurrent role
-				 * renames.  Given that the default ACL for columns is empty
-				 * and is likely to stay that way, it's not worth extra cycles
-				 * and risk to avoid hard-wiring that knowledge here.
-				 */
-				appendPQExpBufferStr(query,
-									 "SELECT at.attname, "
-									 "at.attacl, "
-									 "'{}' AS acldefault, "
-									 "pip.privtype, pip.initprivs "
-									 "FROM pg_catalog.pg_attribute at "
-									 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
-									 "(at.attrelid = pip.objoid "
-									 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
-									 "AND at.attnum = pip.objsubid) "
-									 "WHERE at.attrelid = $1 AND "
-									 "NOT at.attisdropped "
-									 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
-									 "ORDER BY at.attnum");
+			/*
+			 * In principle we should call acldefault('c', relowner) to get
+			 * the default ACL for a column.  However, we don't currently
+			 * store the numeric OID of the relowner in TableInfo.  We could
+			 * convert the owner name using regrole, but that creates a risk
+			 * of failure due to concurrent role renames.  Given that the
+			 * default ACL for columns is empty and is likely to stay that
+			 * way, it's not worth extra cycles and risk to avoid hard-wiring
+			 * that knowledge here.
+			 */
+			appendPQExpBufferStr(query,
+								 "SELECT at.attname, "
+								 "at.attacl, "
+								 "'{}' AS acldefault, "
+								 "pip.privtype, pip.initprivs "
+								 "FROM pg_catalog.pg_attribute at "
+								 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
+								 "(at.attrelid = pip.objoid "
+								 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
+								 "AND at.attnum = pip.objsubid) "
+								 "WHERE at.attrelid = $1 AND "
+								 "NOT at.attisdropped "
+								 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
+								 "ORDER BY at.attnum");
 
 			ExecuteSqlStatement(fout, query->data);
 
@@ -19167,7 +19167,7 @@ collectSequences(Archive *fout)
 	 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
 	 */
 	if (fout->remoteVersion < 180000 ||
-			 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
+		(!fout->dopt->dumpData && !fout->dopt->sequence_data))
 		query = "SELECT seqrelid, format_type(seqtypid, NULL), "
 			"seqstart, seqincrement, "
 			"seqmax, seqmin, "
@@ -19229,15 +19229,14 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
 
 	/*
-	 * The sequence information is gathered in a sorted
-	 * table before any calls to dumpSequence().  See collectSequences() for
-	 * more information.
+	 * The sequence information is gathered in a sorted table before any calls
+	 * to dumpSequence().  See collectSequences() for more information.
 	 */
-		Assert(sequences);
+	Assert(sequences);
 
-		key.oid = tbinfo->dobj.catId.oid;
-		seq = bsearch(&key, sequences, nsequences,
-					  sizeof(SequenceItem), SequenceItemCmp);
+	key.oid = tbinfo->dobj.catId.oid;
+	seq = bsearch(&key, sequences, nsequences,
+				  sizeof(SequenceItem), SequenceItemCmp);
 
 	/* Calculate default limits for a sequence of this type */
 	is_ascending = (seq->incby >= 0);
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 5b10f7122b7..3f61196671c 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -954,11 +954,11 @@ dropRoles(PGconn *conn)
 	int			i_rolname;
 	int			i;
 
-		printfPQExpBuffer(buf,
-						  "SELECT rolname "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 1", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT rolname "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 1", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
@@ -1035,16 +1035,16 @@ dumpRoles(PGconn *conn)
 	 * Notes: rolconfig is dumped later, and pg_authid must be used for
 	 * extracting rolcomment regardless of role_catalog.
 	 */
-		printfPQExpBuffer(buf,
-						  "SELECT oid, rolname, rolsuper, rolinherit, "
-						  "rolcreaterole, rolcreatedb, "
-						  "rolcanlogin, rolconnlimit, rolpassword, "
-						  "rolvaliduntil, rolreplication, rolbypassrls, "
-						  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
-						  "rolname = current_user AS is_current_user "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 2", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT oid, rolname, rolsuper, rolinherit, "
+					  "rolcreaterole, rolcreatedb, "
+					  "rolcanlogin, rolconnlimit, rolpassword, "
+					  "rolvaliduntil, rolreplication, rolbypassrls, "
+					  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
+					  "rolname = current_user AS is_current_user "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 2", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f1e35a6af47..a2f9b683c59 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -1475,15 +1475,15 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_remove(anyarray,anyelement)'"
-							 ", 'array_replace(anyarray,anyelement,anyelement)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_remove(anyarray,anyelement)'"
+						 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_position(anyarray,anyelement)'"
-							 ", 'array_position(anyarray,anyelement,integer)'"
-							 ", 'array_positions(anyarray,anyelement)'"
-							 ", 'width_bucket(anyelement,anyarray)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_position(anyarray,anyelement)'"
+						 ", 'array_position(anyarray,anyelement,integer)'"
+						 ", 'array_positions(anyarray,anyelement)'"
+						 ", 'width_bucket(anyelement,anyarray)'");
 
 	/*
 	 * The query below hardcodes FirstNormalObjectId as 16384 rather than
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 44355feea30..a1bdbf373e3 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,7 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-		cluster->bin_version = v1 * 10000;
+	cluster->bin_version = v1 * 10000;
 }
 
 
@@ -344,8 +344,8 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-		check_single_dir(pg_data, "pg_wal");
-		check_single_dir(pg_data, "pg_xact");
+	check_single_dir(pg_data, "pg_wal");
+	check_single_dir(pg_data, "pg_xact");
 }
 
 
@@ -385,7 +385,7 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-		check_exec(cluster->bindir, "pg_resetwal", check_versions);
+	check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
 	{
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index c45b3183684..c7a1416494d 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -61,52 +61,52 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
-										   old_cluster.controldata.chkpnt_nxtmulti,
-										   old_cluster.controldata.chkpnt_nxtmxoff);
+	old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
+									   old_cluster.controldata.chkpnt_nxtmulti,
+									   old_cluster.controldata.chkpnt_nxtmxoff);
 
-		for (MultiXactId multi = from_multi; multi != to_multi;)
-		{
-			MultiXactMember member;
-			bool		multixid_valid;
-
-			/*
-			 * Read this multixid's members.
-			 *
-			 * Locking-only XIDs that may be part of multi-xids don't matter
-			 * after upgrade, as there can be no transactions running across
-			 * upgrade.  So as a small optimization, we only read one member
-			 * from each multixid: the one updating one, or if there was no
-			 * update, arbitrarily the first locking xid.
-			 */
-			multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
+	for (MultiXactId multi = from_multi; multi != to_multi;)
+	{
+		MultiXactMember member;
+		bool		multixid_valid;
 
-			/*
-			 * Write the new offset to pg_multixact/offsets.
-			 *
-			 * Even if this multixid is invalid, we still need to write its
-			 * offset if the *previous* multixid was valid.  That's because
-			 * when reading a multixid, the number of members is calculated
-			 * from the difference between the two offsets.
-			 */
-			RecordMultiXactOffset(offsets_writer, multi,
-								  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
+		/*
+		 * Read this multixid's members.
+		 *
+		 * Locking-only XIDs that may be part of multi-xids don't matter after
+		 * upgrade, as there can be no transactions running across upgrade. So
+		 * as a small optimization, we only read one member from each
+		 * multixid: the one updating one, or if there was no update,
+		 * arbitrarily the first locking xid.
+		 */
+		multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
 
-			/* Write the members */
-			if (multixid_valid)
-			{
-				RecordMultiXactMembers(members_writer, next_offset, 1, &member);
-				next_offset += 1;
-			}
+		/*
+		 * Write the new offset to pg_multixact/offsets.
+		 *
+		 * Even if this multixid is invalid, we still need to write its offset
+		 * if the *previous* multixid was valid.  That's because when reading
+		 * a multixid, the number of members is calculated from the difference
+		 * between the two offsets.
+		 */
+		RecordMultiXactOffset(offsets_writer, multi,
+							  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
 
-			/* Advance to next multixid, handling wraparound */
-			multi++;
-			if (multi < FirstMultiXactId)
-				multi = FirstMultiXactId;
-			prev_multixid_valid = multixid_valid;
+		/* Write the members */
+		if (multixid_valid)
+		{
+			RecordMultiXactMembers(members_writer, next_offset, 1, &member);
+			next_offset += 1;
 		}
 
-		FreeOldMultiXactReader(old_reader);
+		/* Advance to next multixid, handling wraparound */
+		multi++;
+		if (multi < FirstMultiXactId)
+			multi = FirstMultiXactId;
+		prev_multixid_valid = multixid_valid;
+	}
+
+	FreeOldMultiXactReader(old_reader);
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index e5d7920c1b1..d8e1b680f5a 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -833,7 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
+		oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index ec2ff7acb21..6c467bdc8a5 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -587,32 +587,32 @@ transfer_relfile(FileNameMap *map, const char *type_suffix)
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-			switch (user_opts.transfer_mode)
-			{
-				case TRANSFER_MODE_CLONE:
-					pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
-						   old_file, new_file);
-					cloneFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
-						   old_file, new_file);
-					copyFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY_FILE_RANGE:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
-						   old_file, new_file);
-					copyFileByRange(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_LINK:
-					pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
-						   old_file, new_file);
-					linkFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_SWAP:
-					/* swap mode is handled in its own code path */
-					pg_fatal("should never happen");
-					break;
-			}
+		switch (user_opts.transfer_mode)
+		{
+			case TRANSFER_MODE_CLONE:
+				pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
+					   old_file, new_file);
+				cloneFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
+					   old_file, new_file);
+				copyFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY_FILE_RANGE:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
+					   old_file, new_file);
+				copyFileByRange(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_LINK:
+				pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
+					   old_file, new_file);
+				linkFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_SWAP:
+				/* swap mode is handled in its own code path */
+				pg_fatal("should never happen");
+				break;
+		}
 	}
 }
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 44ef11d980e..c90bf8bbde2 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -6278,23 +6278,22 @@ get_create_object_cmd(EditableObjectType obj_type, Oid oid,
 			 * ensure the right view gets replaced.  Also, check relation kind
 			 * to be sure it's a view.
 			 *
-			 * Views may have WITH [LOCAL|CASCADED]
-			 * CHECK OPTION.  These are not part of the view definition
-			 * returned by pg_get_viewdef() and so need to be retrieved
-			 * separately.  Materialized views may have
-			 * arbitrary storage parameter reloptions.
+			 * Views may have WITH [LOCAL|CASCADED] CHECK OPTION.  These are
+			 * not part of the view definition returned by pg_get_viewdef()
+			 * and so need to be retrieved separately.  Materialized views may
+			 * have arbitrary storage parameter reloptions.
 			 */
 			printfPQExpBuffer(query, "/* %s */\n", _("Get view's definition and details"));
-				appendPQExpBuffer(query,
-								  "SELECT nspname, relname, relkind, "
-								  "pg_catalog.pg_get_viewdef(c.oid, true), "
-								  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-								  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-								  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
-								  "FROM pg_catalog.pg_class c "
-								  "LEFT JOIN pg_catalog.pg_namespace n "
-								  "ON c.relnamespace = n.oid WHERE c.oid = %u",
-								  oid);
+			appendPQExpBuffer(query,
+							  "SELECT nspname, relname, relkind, "
+							  "pg_catalog.pg_get_viewdef(c.oid, true), "
+							  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+							  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+							  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
+							  "FROM pg_catalog.pg_class c "
+							  "LEFT JOIN pg_catalog.pg_namespace n "
+							  "ON c.relnamespace = n.oid WHERE c.oid = %u",
+							  oid);
 			break;
 	}
 
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index ec9c61ee924..87dfe702050 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -387,19 +387,19 @@ describeFunctions(const char *functypes, const char *func_pattern,
 						  gettext_noop("stable"),
 						  gettext_noop("volatile"),
 						  gettext_noop("Volatility"));
-			appendPQExpBuffer(&buf,
-							  ",\n CASE\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
-							  " END as \"%s\"",
-							  gettext_noop("restricted"),
-							  gettext_noop("safe"),
-							  gettext_noop("unsafe"),
-							  gettext_noop("Parallel"));
+		appendPQExpBuffer(&buf,
+						  ",\n CASE\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
+						  " END as \"%s\"",
+						  gettext_noop("restricted"),
+						  gettext_noop("safe"),
+						  gettext_noop("unsafe"),
+						  gettext_noop("Parallel"));
 		appendPQExpBuffer(&buf,
 						  ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\""
 						  ",\n CASE WHEN prosecdef THEN '%s' ELSE '%s' END AS \"%s\""
@@ -599,8 +599,8 @@ describeFunctions(const char *functypes, const char *func_pattern,
 
 	myopt.title = _("List of functions");
 	myopt.translate_header = true;
-		myopt.translate_columns = translate_columns;
-		myopt.n_translate_columns = lengthof(translate_columns);
+	myopt.translate_columns = translate_columns;
+	myopt.n_translate_columns = lengthof(translate_columns);
 
 	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
 
@@ -1086,38 +1086,38 @@ permissionsList(const char *pattern, bool showSystem)
 					  "  ), E'\\n') AS \"%s\"",
 					  gettext_noop("Column privileges"));
 
-		appendPQExpBuffer(&buf,
-						  ",\n  pg_catalog.array_to_string(ARRAY(\n"
-						  "    SELECT polname\n"
-						  "    || CASE WHEN NOT polpermissive THEN\n"
-						  "       E' (RESTRICTIVE)'\n"
-						  "       ELSE '' END\n"
-						  "    || CASE WHEN polcmd != '*' THEN\n"
-						  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
-						  "       ELSE E':'\n"
-						  "       END\n"
-						  "    || CASE WHEN polqual IS NOT NULL THEN\n"
-						  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
-						  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END"
-						  "    || CASE WHEN polroles <> '{0}' THEN\n"
-						  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
-						  "               ARRAY(\n"
-						  "                   SELECT rolname\n"
-						  "                   FROM pg_catalog.pg_roles\n"
-						  "                   WHERE oid = ANY (polroles)\n"
-						  "                   ORDER BY 1\n"
-						  "               ), E', ')\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    FROM pg_catalog.pg_policy pol\n"
-						  "    WHERE polrelid = c.oid), E'\\n')\n"
-						  "    AS \"%s\"",
-						  gettext_noop("Policies"));
+	appendPQExpBuffer(&buf,
+					  ",\n  pg_catalog.array_to_string(ARRAY(\n"
+					  "    SELECT polname\n"
+					  "    || CASE WHEN NOT polpermissive THEN\n"
+					  "       E' (RESTRICTIVE)'\n"
+					  "       ELSE '' END\n"
+					  "    || CASE WHEN polcmd != '*' THEN\n"
+					  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
+					  "       ELSE E':'\n"
+					  "       END\n"
+					  "    || CASE WHEN polqual IS NOT NULL THEN\n"
+					  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
+					  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END"
+					  "    || CASE WHEN polroles <> '{0}' THEN\n"
+					  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
+					  "               ARRAY(\n"
+					  "                   SELECT rolname\n"
+					  "                   FROM pg_catalog.pg_roles\n"
+					  "                   WHERE oid = ANY (polroles)\n"
+					  "                   ORDER BY 1\n"
+					  "               ), E', ')\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    FROM pg_catalog.pg_policy pol\n"
+					  "    WHERE polrelid = c.oid), E'\\n')\n"
+					  "    AS \"%s\"",
+					  gettext_noop("Policies"));
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n"
 						 "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
@@ -1675,27 +1675,27 @@ describeOneTableDetails(const char *schemaname,
 		char	   *footers[3] = {NULL, NULL, NULL};
 
 		printfPQExpBuffer(&buf, "/* %s */\n", _("Get sequence information"));
-			appendPQExpBuffer(&buf,
-							  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
-							  "       seqstart AS \"%s\",\n"
-							  "       seqmin AS \"%s\",\n"
-							  "       seqmax AS \"%s\",\n"
-							  "       seqincrement AS \"%s\",\n"
-							  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
-							  "       seqcache AS \"%s\"\n",
-							  gettext_noop("Type"),
-							  gettext_noop("Start"),
-							  gettext_noop("Minimum"),
-							  gettext_noop("Maximum"),
-							  gettext_noop("Increment"),
-							  gettext_noop("yes"),
-							  gettext_noop("no"),
-							  gettext_noop("Cycles?"),
-							  gettext_noop("Cache"));
-			appendPQExpBuffer(&buf,
-							  "FROM pg_catalog.pg_sequence\n"
-							  "WHERE seqrelid = '%s';",
-							  oid);
+		appendPQExpBuffer(&buf,
+						  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
+						  "       seqstart AS \"%s\",\n"
+						  "       seqmin AS \"%s\",\n"
+						  "       seqmax AS \"%s\",\n"
+						  "       seqincrement AS \"%s\",\n"
+						  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
+						  "       seqcache AS \"%s\"\n",
+						  gettext_noop("Type"),
+						  gettext_noop("Start"),
+						  gettext_noop("Minimum"),
+						  gettext_noop("Maximum"),
+						  gettext_noop("Increment"),
+						  gettext_noop("yes"),
+						  gettext_noop("no"),
+						  gettext_noop("Cycles?"),
+						  gettext_noop("Cache"));
+		appendPQExpBuffer(&buf,
+						  "FROM pg_catalog.pg_sequence\n"
+						  "WHERE seqrelid = '%s';",
+						  oid);
 
 		res = PSQLexec(buf.data);
 		if (!res)
@@ -1913,7 +1913,7 @@ describeOneTableDetails(const char *schemaname,
 		appendPQExpBufferStr(&buf, ",\n  (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
 							 "   WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
 		attcoll_col = cols++;
-			appendPQExpBufferStr(&buf, ",\n  a.attidentity");
+		appendPQExpBufferStr(&buf, ",\n  a.attidentity");
 		attidentity_col = cols++;
 		if (pset.sversion >= 120000)
 			appendPQExpBufferStr(&buf, ",\n  a.attgenerated");
@@ -2326,7 +2326,7 @@ describeOneTableDetails(const char *schemaname,
 							 CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
 							 "condeferred) AS condeferred,\n");
 
-			appendPQExpBufferStr(&buf, "i.indisreplident,\n");
+		appendPQExpBufferStr(&buf, "i.indisreplident,\n");
 
 		if (pset.sversion >= 150000)
 			appendPQExpBufferStr(&buf, "i.indnullsnotdistinct,\n");
@@ -2431,7 +2431,7 @@ describeOneTableDetails(const char *schemaname,
 								 "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n  "
 								 "pg_catalog.pg_get_constraintdef(con.oid, true), "
 								 "contype, condeferrable, condeferred");
-				appendPQExpBufferStr(&buf, ", i.indisreplident");
+			appendPQExpBufferStr(&buf, ", i.indisreplident");
 			appendPQExpBufferStr(&buf, ", c2.reltablespace");
 			if (pset.sversion >= 180000)
 				appendPQExpBufferStr(&buf, ", con.conperiod");
@@ -2682,81 +2682,80 @@ describeOneTableDetails(const char *schemaname,
 		PQclear(result);
 
 		/* print any row-level policies */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get row-level policies for this table"));
-			appendPQExpBufferStr(&buf, "SELECT pol.polname,");
-				appendPQExpBufferStr(&buf,
-									 " pol.polpermissive,\n");
-			appendPQExpBuffer(&buf,
-							  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
-							  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
-							  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
-							  "  CASE pol.polcmd\n"
-							  "    WHEN 'r' THEN 'SELECT'\n"
-							  "    WHEN 'a' THEN 'INSERT'\n"
-							  "    WHEN 'w' THEN 'UPDATE'\n"
-							  "    WHEN 'd' THEN 'DELETE'\n"
-							  "    END AS cmd\n"
-							  "FROM pg_catalog.pg_policy pol\n"
-							  "WHERE pol.polrelid = '%s' ORDER BY 1;",
-							  oid);
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get row-level policies for this table"));
+		appendPQExpBufferStr(&buf, "SELECT pol.polname,");
+		appendPQExpBufferStr(&buf,
+							 " pol.polpermissive,\n");
+		appendPQExpBuffer(&buf,
+						  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
+						  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
+						  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
+						  "  CASE pol.polcmd\n"
+						  "    WHEN 'r' THEN 'SELECT'\n"
+						  "    WHEN 'a' THEN 'INSERT'\n"
+						  "    WHEN 'w' THEN 'UPDATE'\n"
+						  "    WHEN 'd' THEN 'DELETE'\n"
+						  "    END AS cmd\n"
+						  "FROM pg_catalog.pg_policy pol\n"
+						  "WHERE pol.polrelid = '%s' ORDER BY 1;",
+						  oid);
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			/*
-			 * Handle cases where RLS is enabled and there are policies, or
-			 * there aren't policies, or RLS isn't enabled but there are
-			 * policies
-			 */
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies:"));
+		/*
+		 * Handle cases where RLS is enabled and there are policies, or there
+		 * aren't policies, or RLS isn't enabled but there are policies
+		 */
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies:"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
 
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
 
-			if (!tableinfo.rowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (row security disabled):"));
+		if (!tableinfo.rowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (row security disabled):"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    POLICY \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    POLICY \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				if (*(PQgetvalue(result, i, 1)) == 'f')
-					appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
+			if (*(PQgetvalue(result, i, 1)) == 'f')
+				appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
 
-				if (!PQgetisnull(result, i, 5))
-					appendPQExpBuffer(&buf, " FOR %s",
-									  PQgetvalue(result, i, 5));
+			if (!PQgetisnull(result, i, 5))
+				appendPQExpBuffer(&buf, " FOR %s",
+								  PQgetvalue(result, i, 5));
 
-				if (!PQgetisnull(result, i, 2))
-				{
-					appendPQExpBuffer(&buf, "\n      TO %s",
-									  PQgetvalue(result, i, 2));
-				}
+			if (!PQgetisnull(result, i, 2))
+			{
+				appendPQExpBuffer(&buf, "\n      TO %s",
+								  PQgetvalue(result, i, 2));
+			}
 
-				if (!PQgetisnull(result, i, 3))
-					appendPQExpBuffer(&buf, "\n      USING (%s)",
-									  PQgetvalue(result, i, 3));
+			if (!PQgetisnull(result, i, 3))
+				appendPQExpBuffer(&buf, "\n      USING (%s)",
+								  PQgetvalue(result, i, 3));
 
-				if (!PQgetisnull(result, i, 4))
-					appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
-									  PQgetvalue(result, i, 4));
+			if (!PQgetisnull(result, i, 4))
+				appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
+								  PQgetvalue(result, i, 4));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* print any extended statistics */
 		if (pset.sversion >= 140000)
@@ -3025,115 +3024,115 @@ describeOneTableDetails(const char *schemaname,
 		}
 
 		/* print any publications */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get publications that publish this table"));
-			if (pset.sversion >= 150000)
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get publications that publish this table"));
+		if (pset.sversion >= 150000)
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
+							  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
+							  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "UNION\n"
+							  "SELECT pubname\n"
+							  "     , pg_catalog.pg_get_expr(pr.prqual, c.oid)\n"
+							  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
+							  "         (SELECT pg_catalog.string_agg(attname, ', ')\n"
+							  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
+							  "                pg_catalog.pg_attribute\n"
+							  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
+							  "        ELSE NULL END) "
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
+							  "WHERE pr.prrelid = '%s'\n",
+							  oid, oid, oid);
+
+			if (pset.sversion >= 190000)
 			{
+				/*
+				 * Skip entries where this relation appears in the
+				 * publication's EXCEPT list.
+				 */
 				appendPQExpBuffer(&buf,
+								  " AND NOT pr.prexcept\n"
+								  "UNION\n"
 								  "SELECT pubname\n"
 								  "     , NULL\n"
 								  "     , NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
-								  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
-								  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
-								  "UNION\n"
-								  "SELECT pubname\n"
-								  "     , pg_catalog.pg_get_expr(pr.prqual, c.oid)\n"
-								  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
-								  "         (SELECT pg_catalog.string_agg(attname, ', ')\n"
-								  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
-								  "                pg_catalog.pg_attribute\n"
-								  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
-								  "        ELSE NULL END) "
-								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
-								  "WHERE pr.prrelid = '%s'\n",
+								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+								  "     AND NOT EXISTS (\n"
+								  "     SELECT 1\n"
+								  "     FROM pg_catalog.pg_publication_rel pr\n"
+								  "     WHERE pr.prpubid = p.oid AND\n"
+								  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
+								  "ORDER BY 1;",
 								  oid, oid, oid);
-
-				if (pset.sversion >= 190000)
-				{
-					/*
-					 * Skip entries where this relation appears in the
-					 * publication's EXCEPT list.
-					 */
-					appendPQExpBuffer(&buf,
-									  " AND NOT pr.prexcept\n"
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "     , NULL\n"
-									  "     , NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "     AND NOT EXISTS (\n"
-									  "     SELECT 1\n"
-									  "     FROM pg_catalog.pg_publication_rel pr\n"
-									  "     WHERE pr.prpubid = p.oid AND\n"
-									  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
-									  "ORDER BY 1;",
-									  oid, oid, oid);
-				}
-				else
-				{
-					appendPQExpBuffer(&buf,
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "		, NULL\n"
-									  "		, NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "ORDER BY 1;",
-									  oid);
-				}
 			}
 			else
 			{
 				appendPQExpBuffer(&buf,
+								  "UNION\n"
 								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
-								  "FROM pg_catalog.pg_publication p\n"
-								  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "WHERE pr.prrelid = '%s'\n"
-								  "UNION ALL\n"
-								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
+								  "		, NULL\n"
+								  "		, NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
 								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
 								  "ORDER BY 1;",
-								  oid, oid);
+								  oid);
 			}
+		}
+		else
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "WHERE pr.prrelid = '%s'\n"
+							  "UNION ALL\n"
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "ORDER BY 1;",
+							  oid, oid);
+		}
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			if (tuples > 0)
-				printTableAddFooter(&cont, _("Included in publications:"));
+		if (tuples > 0)
+			printTableAddFooter(&cont, _("Included in publications:"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				/* column list (if any) */
-				if (!PQgetisnull(result, i, 2))
-					appendPQExpBuffer(&buf, " (%s)",
-									  PQgetvalue(result, i, 2));
+			/* column list (if any) */
+			if (!PQgetisnull(result, i, 2))
+				appendPQExpBuffer(&buf, " (%s)",
+								  PQgetvalue(result, i, 2));
 
-				/* row filter (if any) */
-				if (!PQgetisnull(result, i, 1))
-					appendPQExpBuffer(&buf, " WHERE %s",
-									  PQgetvalue(result, i, 1));
+			/* row filter (if any) */
+			if (!PQgetisnull(result, i, 1))
+				appendPQExpBuffer(&buf, " WHERE %s",
+								  PQgetvalue(result, i, 1));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* Print publications where the table is in the EXCEPT clause */
 		if (pset.sversion >= 190000)
@@ -3805,7 +3804,7 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		ncols++;
 	}
 	appendPQExpBufferStr(&buf, "\n, r.rolreplication");
-		appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
+	appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
@@ -3860,8 +3859,8 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
 			add_role_attribute(&buf, _("Replication"));
 
-			if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
-				add_role_attribute(&buf, _("Bypass RLS"));
+		if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
+			add_role_attribute(&buf, _("Bypass RLS"));
 
 		conns = atoi(PQgetvalue(res, i, 6));
 		if (conns >= 0)
@@ -5155,14 +5154,14 @@ listCollations(const char *pattern, bool verbose, bool showSystem)
 					  gettext_noop("Schema"),
 					  gettext_noop("Name"));
 
-		appendPQExpBuffer(&buf,
-						  "  CASE c.collprovider "
-						  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
-						  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
-						  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
-						  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
-						  "END AS \"%s\",\n",
-						  gettext_noop("Provider"));
+	appendPQExpBuffer(&buf,
+					  "  CASE c.collprovider "
+					  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
+					  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
+					  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
+					  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
+					  "END AS \"%s\",\n",
+					  gettext_noop("Provider"));
 
 	appendPQExpBuffer(&buf,
 					  "  c.collcollate AS \"%s\",\n"
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg--





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* [PATCH v5 4/4] run pgindent
@ 2026-06-29 14:56  Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: Nathan Bossart @ 2026-06-29 14:56 UTC (permalink / raw)

---
 src/bin/pg_dump/pg_dump.c              | 457 ++++++++++++------------
 src/bin/pg_dump/pg_dumpall.c           |  30 +-
 src/bin/pg_upgrade/check.c             |  16 +-
 src/bin/pg_upgrade/exec.c              |   8 +-
 src/bin/pg_upgrade/multixact_rewrite.c |  80 ++---
 src/bin/pg_upgrade/pg_upgrade.c        |   2 +-
 src/bin/pg_upgrade/relfilenumber.c     |  54 +--
 src/bin/psql/command.c                 |  29 +-
 src/bin/psql/describe.c                | 461 ++++++++++++-------------
 9 files changed, 567 insertions(+), 570 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 17ad8eeb838..33786f976e3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1491,8 +1491,8 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	 * Disable timeouts if supported.
 	 */
 	ExecuteSqlStatement(AH, "SET statement_timeout = 0");
-		ExecuteSqlStatement(AH, "SET lock_timeout = 0");
-		ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
+	ExecuteSqlStatement(AH, "SET lock_timeout = 0");
+	ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
 	if (AH->remoteVersion >= 170000)
 		ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
 
@@ -1505,10 +1505,10 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	/*
 	 * Adjust row-security mode, if supported.
 	 */
-		if (dopt->enable_row_security)
-			ExecuteSqlStatement(AH, "SET row_security = on");
-		else
-			ExecuteSqlStatement(AH, "SET row_security = off");
+	if (dopt->enable_row_security)
+		ExecuteSqlStatement(AH, "SET row_security = on");
+	else
+		ExecuteSqlStatement(AH, "SET row_security = off");
 
 	/*
 	 * For security reasons, we restrict the expansion of non-system views and
@@ -1955,7 +1955,7 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
 	if (fout->dopt->binary_upgrade)
 		dobj->dump = ext->dobj.dump;
 	else
-			dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
+		dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
 
 	return true;
 }
@@ -1989,9 +1989,9 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
 	else if (strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
 	{
 		/*
-		 * We dump out any ACLs defined in pg_catalog, if
-		 * they are interesting (and not the original ACLs which were set at
-		 * initdb time, see pg_init_privs).
+		 * We dump out any ACLs defined in pg_catalog, if they are interesting
+		 * (and not the original ACLs which were set at initdb time, see
+		 * pg_init_privs).
 		 */
 		nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
 	}
@@ -3298,7 +3298,7 @@ dumpDatabase(Archive *fout)
 						 "datcollate, datctype, datfrozenxid, "
 						 "datacl, acldefault('d', datdba) AS acldefault, "
 						 "datistemplate, datconnlimit, ");
-		appendPQExpBufferStr(dbQry, "datminmxid, ");
+	appendPQExpBufferStr(dbQry, "datminmxid, ");
 	if (fout->remoteVersion >= 170000)
 		appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
 	else if (fout->remoteVersion >= 150000)
@@ -3640,11 +3640,11 @@ dumpDatabase(Archive *fout)
 					ii_oid,
 					ii_relminmxid;
 
-			appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
-							  "FROM pg_catalog.pg_class\n"
-							  "WHERE oid IN (%u, %u, %u, %u);\n",
-							  LargeObjectRelationId, LargeObjectLOidPNIndexId,
-							  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
+		appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
+						  "FROM pg_catalog.pg_class\n"
+						  "WHERE oid IN (%u, %u, %u, %u);\n",
+						  LargeObjectRelationId, LargeObjectLOidPNIndexId,
+						  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
 
 		lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
 
@@ -4276,7 +4276,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 
 	printfPQExpBuffer(query,
 					  "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
-		appendPQExpBufferStr(query, "pol.polpermissive, ");
+	appendPQExpBufferStr(query, "pol.polpermissive, ");
 	appendPQExpBuffer(query,
 					  "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
 					  "   pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
@@ -6635,9 +6635,9 @@ getAccessMethods(Archive *fout)
 	 * Select all access methods from pg_am table.
 	 */
 	appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
-		appendPQExpBufferStr(query,
-							 "amtype, "
-							 "amhandler::pg_catalog.regproc AS amhandler ");
+	appendPQExpBufferStr(query,
+						 "amtype, "
+						 "amhandler::pg_catalog.regproc AS amhandler ");
 	appendPQExpBufferStr(query, "FROM pg_am");
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -6829,35 +6829,35 @@ getAggregates(Archive *fout)
 	 * Find all interesting aggregates.  See comment in getFuncs() for the
 	 * rationale behind the filtering logic.
 	 */
-		agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
-					 : "p.proisagg");
+	agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
+				 : "p.proisagg");
 
-		appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
-						  "p.proname AS aggname, "
-						  "p.pronamespace AS aggnamespace, "
-						  "p.pronargs, p.proargtypes, "
-						  "p.proowner, "
-						  "p.proacl AS aggacl, "
-						  "acldefault('f', p.proowner) AS acldefault "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s AND ("
-						  "p.pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog') OR "
-						  "p.proacl IS DISTINCT FROM pip.initprivs",
-						  agg_check);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
-		appendPQExpBufferChar(query, ')');
+	appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
+					  "p.proname AS aggname, "
+					  "p.pronamespace AS aggnamespace, "
+					  "p.pronargs, p.proargtypes, "
+					  "p.proowner, "
+					  "p.proacl AS aggacl, "
+					  "acldefault('f', p.proowner) AS acldefault "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s AND ("
+					  "p.pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog') OR "
+					  "p.proacl IS DISTINCT FROM pip.initprivs",
+					  agg_check);
+	if (dopt->binary_upgrade)
+		appendPQExpBufferStr(query,
+							 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -6959,53 +6959,53 @@ getFuncs(Archive *fout)
 	 * include them, since we want to dump extension members individually in
 	 * that mode.  Also, if they are used by casts or transforms then we need
 	 * to gather the information about them, though they won't be dumped if
-	 * they are built-in.  Also, include functions in
-	 * pg_catalog if they have an ACL different from what's shown in
-	 * pg_init_privs (so we have to join to pg_init_privs; annoying).
+	 * they are built-in.  Also, include functions in pg_catalog if they have
+	 * an ACL different from what's shown in pg_init_privs (so we have to join
+	 * to pg_init_privs; annoying).
 	 */
-		not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
-						 : "NOT p.proisagg");
+	not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
+					 : "NOT p.proisagg");
 
-		appendPQExpBuffer(query,
-						  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
-						  "p.pronargs, p.proargtypes, p.prorettype, "
-						  "p.proacl, "
-						  "acldefault('f', p.proowner) AS acldefault, "
-						  "p.pronamespace, "
-						  "p.proowner "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s"
-						  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
-						  "WHERE classid = 'pg_proc'::regclass AND "
-						  "objid = p.oid AND deptype = 'i')"
-						  "\n  AND ("
-						  "\n  pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog')"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
-						  "\n  WHERE pg_cast.oid > %u "
-						  "\n  AND p.oid = pg_cast.castfunc)"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
-						  "\n  WHERE pg_transform.oid > %u AND "
-						  "\n  (p.oid = pg_transform.trffromsql"
-						  "\n  OR p.oid = pg_transform.trftosql))",
-						  not_agg_check,
-						  g_last_builtin_oid,
-						  g_last_builtin_oid);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
+	appendPQExpBuffer(query,
+					  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
+					  "p.pronargs, p.proargtypes, p.prorettype, "
+					  "p.proacl, "
+					  "acldefault('f', p.proowner) AS acldefault, "
+					  "p.pronamespace, "
+					  "p.proowner "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s"
+					  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
+					  "WHERE classid = 'pg_proc'::regclass AND "
+					  "objid = p.oid AND deptype = 'i')"
+					  "\n  AND ("
+					  "\n  pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog')"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
+					  "\n  WHERE pg_cast.oid > %u "
+					  "\n  AND p.oid = pg_cast.castfunc)"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
+					  "\n  WHERE pg_transform.oid > %u AND "
+					  "\n  (p.oid = pg_transform.trffromsql"
+					  "\n  OR p.oid = pg_transform.trftosql))",
+					  not_agg_check,
+					  g_last_builtin_oid,
+					  g_last_builtin_oid);
+	if (dopt->binary_upgrade)
 		appendPQExpBufferStr(query,
-							 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
-		appendPQExpBufferChar(query, ')');
+							 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferStr(query,
+						 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -7254,31 +7254,31 @@ getTables(Archive *fout, int *numTables)
 		appendPQExpBufferStr(query,
 							 "c.relhasoids, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispopulated, ");
+	appendPQExpBufferStr(query,
+						 "c.relispopulated, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relreplident, ");
+	appendPQExpBufferStr(query,
+						 "c.relreplident, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relrowsecurity, c.relforcerowsecurity, ");
+	appendPQExpBufferStr(query,
+						 "c.relrowsecurity, c.relforcerowsecurity, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relminmxid, tc.relminmxid AS tminmxid, ");
+	appendPQExpBufferStr(query,
+						 "c.relminmxid, tc.relminmxid AS tminmxid, ");
 
-		appendPQExpBufferStr(query,
-							 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-							 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-							 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
+	appendPQExpBufferStr(query,
+						 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+						 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+						 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
 
-		appendPQExpBufferStr(query,
-							 "am.amname, ");
+	appendPQExpBufferStr(query,
+						 "am.amname, ");
 
-		appendPQExpBufferStr(query,
-							 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
+	appendPQExpBufferStr(query,
+						 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispartition AS ispartition ");
+	appendPQExpBufferStr(query,
+						 "c.relispartition AS ispartition ");
 
 	/*
 	 * Left join to pg_depend to pick up dependency info linking sequences to
@@ -7298,8 +7298,8 @@ getTables(Archive *fout, int *numTables)
 	/*
 	 * Left join to pg_am to pick up the amname.
 	 */
-		appendPQExpBufferStr(query,
-							 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
+	appendPQExpBufferStr(query,
+						 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
 
 	/*
 	 * We purposefully ignore toast OIDs for partitioned tables; the reason is
@@ -7870,8 +7870,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 						 "t.reloptions AS indreloptions, ");
 
 
-		appendPQExpBufferStr(query,
-							 "i.indisreplident, ");
+	appendPQExpBufferStr(query,
+						 "i.indisreplident, ");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(query,
@@ -9315,8 +9315,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 		appendPQExpBufferStr(q,
 							 "'' AS attcompression,\n");
 
-		appendPQExpBufferStr(q,
-							 "a.attidentity,\n");
+	appendPQExpBufferStr(q,
+						 "a.attidentity,\n");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(q,
@@ -10719,73 +10719,73 @@ getAdditionalACLs(Archive *fout)
 	PQclear(res);
 
 	/* Fetch initial-privileges data */
-		printfPQExpBuffer(query,
-						  "SELECT objoid, classoid, objsubid, privtype, initprivs "
-						  "FROM pg_init_privs");
+	printfPQExpBuffer(query,
+					  "SELECT objoid, classoid, objsubid, privtype, initprivs "
+					  "FROM pg_init_privs");
 
-		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
-		ntups = PQntuples(res);
-		for (i = 0; i < ntups; i++)
-		{
-			Oid			objoid = atooid(PQgetvalue(res, i, 0));
-			Oid			classoid = atooid(PQgetvalue(res, i, 1));
-			int			objsubid = atoi(PQgetvalue(res, i, 2));
-			char		privtype = *(PQgetvalue(res, i, 3));
-			char	   *initprivs = PQgetvalue(res, i, 4);
-			CatalogId	objId;
-			DumpableObject *dobj;
+	ntups = PQntuples(res);
+	for (i = 0; i < ntups; i++)
+	{
+		Oid			objoid = atooid(PQgetvalue(res, i, 0));
+		Oid			classoid = atooid(PQgetvalue(res, i, 1));
+		int			objsubid = atoi(PQgetvalue(res, i, 2));
+		char		privtype = *(PQgetvalue(res, i, 3));
+		char	   *initprivs = PQgetvalue(res, i, 4);
+		CatalogId	objId;
+		DumpableObject *dobj;
 
-			objId.tableoid = classoid;
-			objId.oid = objoid;
-			dobj = findObjectByCatalogId(objId);
-			/* OK to ignore entries we haven't got a DumpableObject for */
-			if (dobj)
+		objId.tableoid = classoid;
+		objId.oid = objoid;
+		dobj = findObjectByCatalogId(objId);
+		/* OK to ignore entries we haven't got a DumpableObject for */
+		if (dobj)
+		{
+			/* Cope with sub-object initprivs */
+			if (objsubid != 0)
 			{
-				/* Cope with sub-object initprivs */
-				if (objsubid != 0)
-				{
-					if (dobj->objType == DO_TABLE)
-					{
-						/* For a column initprivs, set the table's ACL flags */
-						dobj->components |= DUMP_COMPONENT_ACL;
-						((TableInfo *) dobj)->hascolumnACLs = true;
-					}
-					else
-						pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
-									   classoid, objoid, objsubid);
-					continue;
-				}
-
-				/*
-				 * We ignore any pg_init_privs.initprivs entry for the public
-				 * schema, as explained in getNamespaces().
-				 */
-				if (dobj->objType == DO_NAMESPACE &&
-					strcmp(dobj->name, "public") == 0)
-					continue;
-
-				/* Else it had better be of a type we think has ACLs */
-				if (dobj->objType == DO_NAMESPACE ||
-					dobj->objType == DO_TYPE ||
-					dobj->objType == DO_FUNC ||
-					dobj->objType == DO_AGG ||
-					dobj->objType == DO_TABLE ||
-					dobj->objType == DO_PROCLANG ||
-					dobj->objType == DO_FDW ||
-					dobj->objType == DO_FOREIGN_SERVER)
+				if (dobj->objType == DO_TABLE)
 				{
-					DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
-
-					daobj->dacl.privtype = privtype;
-					daobj->dacl.initprivs = pstrdup(initprivs);
+					/* For a column initprivs, set the table's ACL flags */
+					dobj->components |= DUMP_COMPONENT_ACL;
+					((TableInfo *) dobj)->hascolumnACLs = true;
 				}
 				else
 					pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
 								   classoid, objoid, objsubid);
+				continue;
+			}
+
+			/*
+			 * We ignore any pg_init_privs.initprivs entry for the public
+			 * schema, as explained in getNamespaces().
+			 */
+			if (dobj->objType == DO_NAMESPACE &&
+				strcmp(dobj->name, "public") == 0)
+				continue;
+
+			/* Else it had better be of a type we think has ACLs */
+			if (dobj->objType == DO_NAMESPACE ||
+				dobj->objType == DO_TYPE ||
+				dobj->objType == DO_FUNC ||
+				dobj->objType == DO_AGG ||
+				dobj->objType == DO_TABLE ||
+				dobj->objType == DO_PROCLANG ||
+				dobj->objType == DO_FDW ||
+				dobj->objType == DO_FOREIGN_SERVER)
+			{
+				DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
+
+				daobj->dacl.privtype = privtype;
+				daobj->dacl.initprivs = pstrdup(initprivs);
 			}
+			else
+				pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
+							   classoid, objoid, objsubid);
 		}
-		PQclear(res);
+	}
+	PQclear(res);
 
 	destroyPQExpBuffer(query);
 }
@@ -11130,8 +11130,8 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 		 * The results must be in the order of the relations supplied in the
 		 * parameters to ensure we remain in sync as we walk through the TOC.
 		 *
-		 * For versions before 19, the redundant filter clause on s.tablename =
-		 * ANY(...) seems sufficient to convince the planner to use
+		 * For versions before 19, the redundant filter clause on s.tablename
+		 * = ANY(...) seems sufficient to convince the planner to use
 		 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
 		 * In newer versions, pg_stats returns the table OIDs, eliminating the
 		 * need for that hack.
@@ -13471,11 +13471,11 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
 							 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
 							 "proleakproof,\n");
 
-			appendPQExpBufferStr(query,
-								 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
+		appendPQExpBufferStr(query,
+							 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
 
-			appendPQExpBufferStr(query,
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -14965,9 +14965,9 @@ dumpCollation(Archive *fout, const CollInfo *collinfo)
 	/* Get collation-specific details */
 	appendPQExpBufferStr(query, "SELECT ");
 
-		appendPQExpBufferStr(query,
-							 "collprovider, "
-							 "collversion, ");
+	appendPQExpBufferStr(query,
+						 "collprovider, "
+						 "collversion, ");
 
 	if (fout->remoteVersion >= 120000)
 		appendPQExpBufferStr(query,
@@ -15374,23 +15374,23 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 							 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
 							 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggkind,\n"
-								 "aggmtransfn,\n"
-								 "aggminvtransfn,\n"
-								 "aggmfinalfn,\n"
-								 "aggmtranstype::pg_catalog.regtype,\n"
-								 "aggfinalextra,\n"
-								 "aggmfinalextra,\n"
-								 "aggtransspace,\n"
-								 "aggmtransspace,\n"
-								 "aggminitval,\n");
+		appendPQExpBufferStr(query,
+							 "aggkind,\n"
+							 "aggmtransfn,\n"
+							 "aggminvtransfn,\n"
+							 "aggmfinalfn,\n"
+							 "aggmtranstype::pg_catalog.regtype,\n"
+							 "aggfinalextra,\n"
+							 "aggmfinalextra,\n"
+							 "aggtransspace,\n"
+							 "aggmtransspace,\n"
+							 "aggminitval,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggcombinefn,\n"
-								 "aggserialfn,\n"
-								 "aggdeserialfn,\n"
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "aggcombinefn,\n"
+							 "aggserialfn,\n"
+							 "aggdeserialfn,\n"
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -16850,30 +16850,30 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
 			appendPQExpBufferStr(query,
 								 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
 
-				/*
-				 * In principle we should call acldefault('c', relowner) to
-				 * get the default ACL for a column.  However, we don't
-				 * currently store the numeric OID of the relowner in
-				 * TableInfo.  We could convert the owner name using regrole,
-				 * but that creates a risk of failure due to concurrent role
-				 * renames.  Given that the default ACL for columns is empty
-				 * and is likely to stay that way, it's not worth extra cycles
-				 * and risk to avoid hard-wiring that knowledge here.
-				 */
-				appendPQExpBufferStr(query,
-									 "SELECT at.attname, "
-									 "at.attacl, "
-									 "'{}' AS acldefault, "
-									 "pip.privtype, pip.initprivs "
-									 "FROM pg_catalog.pg_attribute at "
-									 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
-									 "(at.attrelid = pip.objoid "
-									 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
-									 "AND at.attnum = pip.objsubid) "
-									 "WHERE at.attrelid = $1 AND "
-									 "NOT at.attisdropped "
-									 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
-									 "ORDER BY at.attnum");
+			/*
+			 * In principle we should call acldefault('c', relowner) to get
+			 * the default ACL for a column.  However, we don't currently
+			 * store the numeric OID of the relowner in TableInfo.  We could
+			 * convert the owner name using regrole, but that creates a risk
+			 * of failure due to concurrent role renames.  Given that the
+			 * default ACL for columns is empty and is likely to stay that
+			 * way, it's not worth extra cycles and risk to avoid hard-wiring
+			 * that knowledge here.
+			 */
+			appendPQExpBufferStr(query,
+								 "SELECT at.attname, "
+								 "at.attacl, "
+								 "'{}' AS acldefault, "
+								 "pip.privtype, pip.initprivs "
+								 "FROM pg_catalog.pg_attribute at "
+								 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
+								 "(at.attrelid = pip.objoid "
+								 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
+								 "AND at.attnum = pip.objsubid) "
+								 "WHERE at.attrelid = $1 AND "
+								 "NOT at.attisdropped "
+								 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
+								 "ORDER BY at.attnum");
 
 			ExecuteSqlStatement(fout, query->data);
 
@@ -19167,7 +19167,7 @@ collectSequences(Archive *fout)
 	 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
 	 */
 	if (fout->remoteVersion < 180000 ||
-			 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
+		(!fout->dopt->dumpData && !fout->dopt->sequence_data))
 		query = "SELECT seqrelid, format_type(seqtypid, NULL), "
 			"seqstart, seqincrement, "
 			"seqmax, seqmin, "
@@ -19229,15 +19229,14 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
 
 	/*
-	 * The sequence information is gathered in a sorted
-	 * table before any calls to dumpSequence().  See collectSequences() for
-	 * more information.
+	 * The sequence information is gathered in a sorted table before any calls
+	 * to dumpSequence().  See collectSequences() for more information.
 	 */
-		Assert(sequences);
+	Assert(sequences);
 
-		key.oid = tbinfo->dobj.catId.oid;
-		seq = bsearch(&key, sequences, nsequences,
-					  sizeof(SequenceItem), SequenceItemCmp);
+	key.oid = tbinfo->dobj.catId.oid;
+	seq = bsearch(&key, sequences, nsequences,
+				  sizeof(SequenceItem), SequenceItemCmp);
 
 	/* Calculate default limits for a sequence of this type */
 	is_ascending = (seq->incby >= 0);
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index e68937c9934..afcd9a218d8 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -785,11 +785,11 @@ dropRoles(PGconn *conn)
 	int			i_rolname;
 	int			i;
 
-		printfPQExpBuffer(buf,
-						  "SELECT rolname "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 1", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT rolname "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 1", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
@@ -843,16 +843,16 @@ dumpRoles(PGconn *conn)
 	 * Notes: rolconfig is dumped later, and pg_authid must be used for
 	 * extracting rolcomment regardless of role_catalog.
 	 */
-		printfPQExpBuffer(buf,
-						  "SELECT oid, rolname, rolsuper, rolinherit, "
-						  "rolcreaterole, rolcreatedb, "
-						  "rolcanlogin, rolconnlimit, rolpassword, "
-						  "rolvaliduntil, rolreplication, rolbypassrls, "
-						  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
-						  "rolname = current_user AS is_current_user "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 2", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT oid, rolname, rolsuper, rolinherit, "
+					  "rolcreaterole, rolcreatedb, "
+					  "rolcanlogin, rolconnlimit, rolpassword, "
+					  "rolvaliduntil, rolreplication, rolbypassrls, "
+					  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
+					  "rolname = current_user AS is_current_user "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 2", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 01b354e3c44..7629f8cab02 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -1475,15 +1475,15 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_remove(anyarray,anyelement)'"
-							 ", 'array_replace(anyarray,anyelement,anyelement)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_remove(anyarray,anyelement)'"
+						 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_position(anyarray,anyelement)'"
-							 ", 'array_position(anyarray,anyelement,integer)'"
-							 ", 'array_positions(anyarray,anyelement)'"
-							 ", 'width_bucket(anyelement,anyarray)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_position(anyarray,anyelement)'"
+						 ", 'array_position(anyarray,anyelement,integer)'"
+						 ", 'array_positions(anyarray,anyelement)'"
+						 ", 'width_bucket(anyelement,anyarray)'");
 
 	/*
 	 * The query below hardcodes FirstNormalObjectId as 16384 rather than
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 44355feea30..a1bdbf373e3 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,7 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-		cluster->bin_version = v1 * 10000;
+	cluster->bin_version = v1 * 10000;
 }
 
 
@@ -344,8 +344,8 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-		check_single_dir(pg_data, "pg_wal");
-		check_single_dir(pg_data, "pg_xact");
+	check_single_dir(pg_data, "pg_wal");
+	check_single_dir(pg_data, "pg_xact");
 }
 
 
@@ -385,7 +385,7 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-		check_exec(cluster->bindir, "pg_resetwal", check_versions);
+	check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
 	{
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index c45b3183684..c7a1416494d 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -61,52 +61,52 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
-										   old_cluster.controldata.chkpnt_nxtmulti,
-										   old_cluster.controldata.chkpnt_nxtmxoff);
+	old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
+									   old_cluster.controldata.chkpnt_nxtmulti,
+									   old_cluster.controldata.chkpnt_nxtmxoff);
 
-		for (MultiXactId multi = from_multi; multi != to_multi;)
-		{
-			MultiXactMember member;
-			bool		multixid_valid;
-
-			/*
-			 * Read this multixid's members.
-			 *
-			 * Locking-only XIDs that may be part of multi-xids don't matter
-			 * after upgrade, as there can be no transactions running across
-			 * upgrade.  So as a small optimization, we only read one member
-			 * from each multixid: the one updating one, or if there was no
-			 * update, arbitrarily the first locking xid.
-			 */
-			multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
+	for (MultiXactId multi = from_multi; multi != to_multi;)
+	{
+		MultiXactMember member;
+		bool		multixid_valid;
 
-			/*
-			 * Write the new offset to pg_multixact/offsets.
-			 *
-			 * Even if this multixid is invalid, we still need to write its
-			 * offset if the *previous* multixid was valid.  That's because
-			 * when reading a multixid, the number of members is calculated
-			 * from the difference between the two offsets.
-			 */
-			RecordMultiXactOffset(offsets_writer, multi,
-								  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
+		/*
+		 * Read this multixid's members.
+		 *
+		 * Locking-only XIDs that may be part of multi-xids don't matter after
+		 * upgrade, as there can be no transactions running across upgrade. So
+		 * as a small optimization, we only read one member from each
+		 * multixid: the one updating one, or if there was no update,
+		 * arbitrarily the first locking xid.
+		 */
+		multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
 
-			/* Write the members */
-			if (multixid_valid)
-			{
-				RecordMultiXactMembers(members_writer, next_offset, 1, &member);
-				next_offset += 1;
-			}
+		/*
+		 * Write the new offset to pg_multixact/offsets.
+		 *
+		 * Even if this multixid is invalid, we still need to write its offset
+		 * if the *previous* multixid was valid.  That's because when reading
+		 * a multixid, the number of members is calculated from the difference
+		 * between the two offsets.
+		 */
+		RecordMultiXactOffset(offsets_writer, multi,
+							  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
 
-			/* Advance to next multixid, handling wraparound */
-			multi++;
-			if (multi < FirstMultiXactId)
-				multi = FirstMultiXactId;
-			prev_multixid_valid = multixid_valid;
+		/* Write the members */
+		if (multixid_valid)
+		{
+			RecordMultiXactMembers(members_writer, next_offset, 1, &member);
+			next_offset += 1;
 		}
 
-		FreeOldMultiXactReader(old_reader);
+		/* Advance to next multixid, handling wraparound */
+		multi++;
+		if (multi < FirstMultiXactId)
+			multi = FirstMultiXactId;
+		prev_multixid_valid = multixid_valid;
+	}
+
+	FreeOldMultiXactReader(old_reader);
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index e5d7920c1b1..d8e1b680f5a 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -833,7 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
+		oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index ec2ff7acb21..6c467bdc8a5 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -587,32 +587,32 @@ transfer_relfile(FileNameMap *map, const char *type_suffix)
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-			switch (user_opts.transfer_mode)
-			{
-				case TRANSFER_MODE_CLONE:
-					pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
-						   old_file, new_file);
-					cloneFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
-						   old_file, new_file);
-					copyFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY_FILE_RANGE:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
-						   old_file, new_file);
-					copyFileByRange(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_LINK:
-					pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
-						   old_file, new_file);
-					linkFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_SWAP:
-					/* swap mode is handled in its own code path */
-					pg_fatal("should never happen");
-					break;
-			}
+		switch (user_opts.transfer_mode)
+		{
+			case TRANSFER_MODE_CLONE:
+				pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
+					   old_file, new_file);
+				cloneFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
+					   old_file, new_file);
+				copyFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY_FILE_RANGE:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
+					   old_file, new_file);
+				copyFileByRange(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_LINK:
+				pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
+					   old_file, new_file);
+				linkFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_SWAP:
+				/* swap mode is handled in its own code path */
+				pg_fatal("should never happen");
+				break;
+		}
 	}
 }
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 44ef11d980e..c90bf8bbde2 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -6278,23 +6278,22 @@ get_create_object_cmd(EditableObjectType obj_type, Oid oid,
 			 * ensure the right view gets replaced.  Also, check relation kind
 			 * to be sure it's a view.
 			 *
-			 * Views may have WITH [LOCAL|CASCADED]
-			 * CHECK OPTION.  These are not part of the view definition
-			 * returned by pg_get_viewdef() and so need to be retrieved
-			 * separately.  Materialized views may have
-			 * arbitrary storage parameter reloptions.
+			 * Views may have WITH [LOCAL|CASCADED] CHECK OPTION.  These are
+			 * not part of the view definition returned by pg_get_viewdef()
+			 * and so need to be retrieved separately.  Materialized views may
+			 * have arbitrary storage parameter reloptions.
 			 */
 			printfPQExpBuffer(query, "/* %s */\n", _("Get view's definition and details"));
-				appendPQExpBuffer(query,
-								  "SELECT nspname, relname, relkind, "
-								  "pg_catalog.pg_get_viewdef(c.oid, true), "
-								  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-								  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-								  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
-								  "FROM pg_catalog.pg_class c "
-								  "LEFT JOIN pg_catalog.pg_namespace n "
-								  "ON c.relnamespace = n.oid WHERE c.oid = %u",
-								  oid);
+			appendPQExpBuffer(query,
+							  "SELECT nspname, relname, relkind, "
+							  "pg_catalog.pg_get_viewdef(c.oid, true), "
+							  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+							  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+							  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
+							  "FROM pg_catalog.pg_class c "
+							  "LEFT JOIN pg_catalog.pg_namespace n "
+							  "ON c.relnamespace = n.oid WHERE c.oid = %u",
+							  oid);
 			break;
 	}
 
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index ce99137c613..ac0c55e7aa5 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -387,19 +387,19 @@ describeFunctions(const char *functypes, const char *func_pattern,
 						  gettext_noop("stable"),
 						  gettext_noop("volatile"),
 						  gettext_noop("Volatility"));
-			appendPQExpBuffer(&buf,
-							  ",\n CASE\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
-							  " END as \"%s\"",
-							  gettext_noop("restricted"),
-							  gettext_noop("safe"),
-							  gettext_noop("unsafe"),
-							  gettext_noop("Parallel"));
+		appendPQExpBuffer(&buf,
+						  ",\n CASE\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
+						  " END as \"%s\"",
+						  gettext_noop("restricted"),
+						  gettext_noop("safe"),
+						  gettext_noop("unsafe"),
+						  gettext_noop("Parallel"));
 		appendPQExpBuffer(&buf,
 						  ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\""
 						  ",\n CASE WHEN prosecdef THEN '%s' ELSE '%s' END AS \"%s\""
@@ -599,8 +599,8 @@ describeFunctions(const char *functypes, const char *func_pattern,
 
 	myopt.title = _("List of functions");
 	myopt.translate_header = true;
-		myopt.translate_columns = translate_columns;
-		myopt.n_translate_columns = lengthof(translate_columns);
+	myopt.translate_columns = translate_columns;
+	myopt.n_translate_columns = lengthof(translate_columns);
 
 	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
 
@@ -1086,38 +1086,38 @@ permissionsList(const char *pattern, bool showSystem)
 					  "  ), E'\\n') AS \"%s\"",
 					  gettext_noop("Column privileges"));
 
-		appendPQExpBuffer(&buf,
-						  ",\n  pg_catalog.array_to_string(ARRAY(\n"
-						  "    SELECT polname\n"
-						  "    || CASE WHEN NOT polpermissive THEN\n"
-						  "       E' (RESTRICTIVE)'\n"
-						  "       ELSE '' END\n"
-						  "    || CASE WHEN polcmd != '*' THEN\n"
-						  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
-						  "       ELSE E':'\n"
-						  "       END\n"
-						  "    || CASE WHEN polqual IS NOT NULL THEN\n"
-						  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
-						  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END"
-						  "    || CASE WHEN polroles <> '{0}' THEN\n"
-						  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
-						  "               ARRAY(\n"
-						  "                   SELECT rolname\n"
-						  "                   FROM pg_catalog.pg_roles\n"
-						  "                   WHERE oid = ANY (polroles)\n"
-						  "                   ORDER BY 1\n"
-						  "               ), E', ')\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    FROM pg_catalog.pg_policy pol\n"
-						  "    WHERE polrelid = c.oid), E'\\n')\n"
-						  "    AS \"%s\"",
-						  gettext_noop("Policies"));
+	appendPQExpBuffer(&buf,
+					  ",\n  pg_catalog.array_to_string(ARRAY(\n"
+					  "    SELECT polname\n"
+					  "    || CASE WHEN NOT polpermissive THEN\n"
+					  "       E' (RESTRICTIVE)'\n"
+					  "       ELSE '' END\n"
+					  "    || CASE WHEN polcmd != '*' THEN\n"
+					  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
+					  "       ELSE E':'\n"
+					  "       END\n"
+					  "    || CASE WHEN polqual IS NOT NULL THEN\n"
+					  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
+					  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END"
+					  "    || CASE WHEN polroles <> '{0}' THEN\n"
+					  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
+					  "               ARRAY(\n"
+					  "                   SELECT rolname\n"
+					  "                   FROM pg_catalog.pg_roles\n"
+					  "                   WHERE oid = ANY (polroles)\n"
+					  "                   ORDER BY 1\n"
+					  "               ), E', ')\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    FROM pg_catalog.pg_policy pol\n"
+					  "    WHERE polrelid = c.oid), E'\\n')\n"
+					  "    AS \"%s\"",
+					  gettext_noop("Policies"));
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n"
 						 "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
@@ -1675,27 +1675,27 @@ describeOneTableDetails(const char *schemaname,
 		char	   *footers[3] = {NULL, NULL, NULL};
 
 		printfPQExpBuffer(&buf, "/* %s */\n", _("Get sequence information"));
-			appendPQExpBuffer(&buf,
-							  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
-							  "       seqstart AS \"%s\",\n"
-							  "       seqmin AS \"%s\",\n"
-							  "       seqmax AS \"%s\",\n"
-							  "       seqincrement AS \"%s\",\n"
-							  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
-							  "       seqcache AS \"%s\"\n",
-							  gettext_noop("Type"),
-							  gettext_noop("Start"),
-							  gettext_noop("Minimum"),
-							  gettext_noop("Maximum"),
-							  gettext_noop("Increment"),
-							  gettext_noop("yes"),
-							  gettext_noop("no"),
-							  gettext_noop("Cycles?"),
-							  gettext_noop("Cache"));
-			appendPQExpBuffer(&buf,
-							  "FROM pg_catalog.pg_sequence\n"
-							  "WHERE seqrelid = '%s';",
-							  oid);
+		appendPQExpBuffer(&buf,
+						  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
+						  "       seqstart AS \"%s\",\n"
+						  "       seqmin AS \"%s\",\n"
+						  "       seqmax AS \"%s\",\n"
+						  "       seqincrement AS \"%s\",\n"
+						  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
+						  "       seqcache AS \"%s\"\n",
+						  gettext_noop("Type"),
+						  gettext_noop("Start"),
+						  gettext_noop("Minimum"),
+						  gettext_noop("Maximum"),
+						  gettext_noop("Increment"),
+						  gettext_noop("yes"),
+						  gettext_noop("no"),
+						  gettext_noop("Cycles?"),
+						  gettext_noop("Cache"));
+		appendPQExpBuffer(&buf,
+						  "FROM pg_catalog.pg_sequence\n"
+						  "WHERE seqrelid = '%s';",
+						  oid);
 
 		res = PSQLexec(buf.data);
 		if (!res)
@@ -1913,7 +1913,7 @@ describeOneTableDetails(const char *schemaname,
 		appendPQExpBufferStr(&buf, ",\n  (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
 							 "   WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
 		attcoll_col = cols++;
-			appendPQExpBufferStr(&buf, ",\n  a.attidentity");
+		appendPQExpBufferStr(&buf, ",\n  a.attidentity");
 		attidentity_col = cols++;
 		if (pset.sversion >= 120000)
 			appendPQExpBufferStr(&buf, ",\n  a.attgenerated");
@@ -2326,7 +2326,7 @@ describeOneTableDetails(const char *schemaname,
 							 CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
 							 "condeferred) AS condeferred,\n");
 
-			appendPQExpBufferStr(&buf, "i.indisreplident,\n");
+		appendPQExpBufferStr(&buf, "i.indisreplident,\n");
 
 		if (pset.sversion >= 150000)
 			appendPQExpBufferStr(&buf, "i.indnullsnotdistinct,\n");
@@ -2431,7 +2431,7 @@ describeOneTableDetails(const char *schemaname,
 								 "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n  "
 								 "pg_catalog.pg_get_constraintdef(con.oid, true), "
 								 "contype, condeferrable, condeferred");
-				appendPQExpBufferStr(&buf, ", i.indisreplident");
+			appendPQExpBufferStr(&buf, ", i.indisreplident");
 			appendPQExpBufferStr(&buf, ", c2.reltablespace");
 			if (pset.sversion >= 180000)
 				appendPQExpBufferStr(&buf, ", con.conperiod");
@@ -2682,81 +2682,80 @@ describeOneTableDetails(const char *schemaname,
 		PQclear(result);
 
 		/* print any row-level policies */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get row-level policies for this table"));
-			appendPQExpBufferStr(&buf, "SELECT pol.polname,");
-				appendPQExpBufferStr(&buf,
-									 " pol.polpermissive,\n");
-			appendPQExpBuffer(&buf,
-							  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
-							  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
-							  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
-							  "  CASE pol.polcmd\n"
-							  "    WHEN 'r' THEN 'SELECT'\n"
-							  "    WHEN 'a' THEN 'INSERT'\n"
-							  "    WHEN 'w' THEN 'UPDATE'\n"
-							  "    WHEN 'd' THEN 'DELETE'\n"
-							  "    END AS cmd\n"
-							  "FROM pg_catalog.pg_policy pol\n"
-							  "WHERE pol.polrelid = '%s' ORDER BY 1;",
-							  oid);
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get row-level policies for this table"));
+		appendPQExpBufferStr(&buf, "SELECT pol.polname,");
+		appendPQExpBufferStr(&buf,
+							 " pol.polpermissive,\n");
+		appendPQExpBuffer(&buf,
+						  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
+						  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
+						  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
+						  "  CASE pol.polcmd\n"
+						  "    WHEN 'r' THEN 'SELECT'\n"
+						  "    WHEN 'a' THEN 'INSERT'\n"
+						  "    WHEN 'w' THEN 'UPDATE'\n"
+						  "    WHEN 'd' THEN 'DELETE'\n"
+						  "    END AS cmd\n"
+						  "FROM pg_catalog.pg_policy pol\n"
+						  "WHERE pol.polrelid = '%s' ORDER BY 1;",
+						  oid);
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			/*
-			 * Handle cases where RLS is enabled and there are policies, or
-			 * there aren't policies, or RLS isn't enabled but there are
-			 * policies
-			 */
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies:"));
+		/*
+		 * Handle cases where RLS is enabled and there are policies, or there
+		 * aren't policies, or RLS isn't enabled but there are policies
+		 */
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies:"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
 
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
 
-			if (!tableinfo.rowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (row security disabled):"));
+		if (!tableinfo.rowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (row security disabled):"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    POLICY \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    POLICY \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				if (*(PQgetvalue(result, i, 1)) == 'f')
-					appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
+			if (*(PQgetvalue(result, i, 1)) == 'f')
+				appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
 
-				if (!PQgetisnull(result, i, 5))
-					appendPQExpBuffer(&buf, " FOR %s",
-									  PQgetvalue(result, i, 5));
+			if (!PQgetisnull(result, i, 5))
+				appendPQExpBuffer(&buf, " FOR %s",
+								  PQgetvalue(result, i, 5));
 
-				if (!PQgetisnull(result, i, 2))
-				{
-					appendPQExpBuffer(&buf, "\n      TO %s",
-									  PQgetvalue(result, i, 2));
-				}
+			if (!PQgetisnull(result, i, 2))
+			{
+				appendPQExpBuffer(&buf, "\n      TO %s",
+								  PQgetvalue(result, i, 2));
+			}
 
-				if (!PQgetisnull(result, i, 3))
-					appendPQExpBuffer(&buf, "\n      USING (%s)",
-									  PQgetvalue(result, i, 3));
+			if (!PQgetisnull(result, i, 3))
+				appendPQExpBuffer(&buf, "\n      USING (%s)",
+								  PQgetvalue(result, i, 3));
 
-				if (!PQgetisnull(result, i, 4))
-					appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
-									  PQgetvalue(result, i, 4));
+			if (!PQgetisnull(result, i, 4))
+				appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
+								  PQgetvalue(result, i, 4));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* print any extended statistics */
 		if (pset.sversion >= 140000)
@@ -3025,115 +3024,115 @@ describeOneTableDetails(const char *schemaname,
 		}
 
 		/* print any publications */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get publications that publish this table"));
-			if (pset.sversion >= 150000)
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get publications that publish this table"));
+		if (pset.sversion >= 150000)
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
+							  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
+							  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "UNION\n"
+							  "SELECT pubname\n"
+							  "     , pg_catalog.pg_get_expr(pr.prqual, c.oid)\n"
+							  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
+							  "         (SELECT pg_catalog.string_agg(attname, ', ')\n"
+							  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
+							  "                pg_catalog.pg_attribute\n"
+							  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
+							  "        ELSE NULL END) "
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
+							  "WHERE pr.prrelid = '%s'\n",
+							  oid, oid, oid);
+
+			if (pset.sversion >= 190000)
 			{
+				/*
+				 * Skip entries where this relation appears in the
+				 * publication's EXCEPT list.
+				 */
 				appendPQExpBuffer(&buf,
+								  " AND NOT pr.prexcept\n"
+								  "UNION\n"
 								  "SELECT pubname\n"
 								  "     , NULL\n"
 								  "     , NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
-								  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
-								  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
-								  "UNION\n"
-								  "SELECT pubname\n"
-								  "     , pg_catalog.pg_get_expr(pr.prqual, c.oid)\n"
-								  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
-								  "         (SELECT pg_catalog.string_agg(attname, ', ')\n"
-								  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
-								  "                pg_catalog.pg_attribute\n"
-								  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
-								  "        ELSE NULL END) "
-								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
-								  "WHERE pr.prrelid = '%s'\n",
+								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+								  "     AND NOT EXISTS (\n"
+								  "     SELECT 1\n"
+								  "     FROM pg_catalog.pg_publication_rel pr\n"
+								  "     WHERE pr.prpubid = p.oid AND\n"
+								  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
+								  "ORDER BY 1;",
 								  oid, oid, oid);
-
-				if (pset.sversion >= 190000)
-				{
-					/*
-					 * Skip entries where this relation appears in the
-					 * publication's EXCEPT list.
-					 */
-					appendPQExpBuffer(&buf,
-									  " AND NOT pr.prexcept\n"
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "     , NULL\n"
-									  "     , NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "     AND NOT EXISTS (\n"
-									  "     SELECT 1\n"
-									  "     FROM pg_catalog.pg_publication_rel pr\n"
-									  "     WHERE pr.prpubid = p.oid AND\n"
-									  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
-									  "ORDER BY 1;",
-									  oid, oid, oid);
-				}
-				else
-				{
-					appendPQExpBuffer(&buf,
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "		, NULL\n"
-									  "		, NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "ORDER BY 1;",
-									  oid);
-				}
 			}
 			else
 			{
 				appendPQExpBuffer(&buf,
+								  "UNION\n"
 								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
-								  "FROM pg_catalog.pg_publication p\n"
-								  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "WHERE pr.prrelid = '%s'\n"
-								  "UNION ALL\n"
-								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
+								  "		, NULL\n"
+								  "		, NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
 								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
 								  "ORDER BY 1;",
-								  oid, oid);
+								  oid);
 			}
+		}
+		else
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "WHERE pr.prrelid = '%s'\n"
+							  "UNION ALL\n"
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "ORDER BY 1;",
+							  oid, oid);
+		}
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			if (tuples > 0)
-				printTableAddFooter(&cont, _("Included in publications:"));
+		if (tuples > 0)
+			printTableAddFooter(&cont, _("Included in publications:"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				/* column list (if any) */
-				if (!PQgetisnull(result, i, 2))
-					appendPQExpBuffer(&buf, " (%s)",
-									  PQgetvalue(result, i, 2));
+			/* column list (if any) */
+			if (!PQgetisnull(result, i, 2))
+				appendPQExpBuffer(&buf, " (%s)",
+								  PQgetvalue(result, i, 2));
 
-				/* row filter (if any) */
-				if (!PQgetisnull(result, i, 1))
-					appendPQExpBuffer(&buf, " WHERE %s",
-									  PQgetvalue(result, i, 1));
+			/* row filter (if any) */
+			if (!PQgetisnull(result, i, 1))
+				appendPQExpBuffer(&buf, " WHERE %s",
+								  PQgetvalue(result, i, 1));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* Print publications where the table is in the EXCEPT clause */
 		if (pset.sversion >= 190000)
@@ -3805,7 +3804,7 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		ncols++;
 	}
 	appendPQExpBufferStr(&buf, "\n, r.rolreplication");
-		appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
+	appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
@@ -3860,8 +3859,8 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
 			add_role_attribute(&buf, _("Replication"));
 
-			if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
-				add_role_attribute(&buf, _("Bypass RLS"));
+		if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
+			add_role_attribute(&buf, _("Bypass RLS"));
 
 		conns = atoi(PQgetvalue(res, i, 6));
 		if (conns >= 0)
@@ -5155,14 +5154,14 @@ listCollations(const char *pattern, bool verbose, bool showSystem)
 					  gettext_noop("Schema"),
 					  gettext_noop("Name"));
 
-		appendPQExpBuffer(&buf,
-						  "  CASE c.collprovider "
-						  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
-						  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
-						  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
-						  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
-						  "END AS \"%s\",\n",
-						  gettext_noop("Provider"));
+	appendPQExpBuffer(&buf,
+					  "  CASE c.collprovider "
+					  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
+					  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
+					  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
+					  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
+					  "END AS \"%s\",\n",
+					  gettext_noop("Provider"));
 
 	appendPQExpBuffer(&buf,
 					  "  c.collcollate AS \"%s\",\n"
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ--





^ permalink  raw  reply  [nested|flat] 55+ messages in thread

* [PATCH v5 4/4] run pgindent
@ 2026-06-29 14:56  Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 55+ messages in thread

From: Nathan Bossart @ 2026-06-29 14:56 UTC (permalink / raw)

---
 src/bin/pg_dump/pg_dump.c              | 457 ++++++++++++------------
 src/bin/pg_dump/pg_dumpall.c           |  30 +-
 src/bin/pg_upgrade/check.c             |  16 +-
 src/bin/pg_upgrade/exec.c              |   8 +-
 src/bin/pg_upgrade/multixact_rewrite.c |  80 ++---
 src/bin/pg_upgrade/pg_upgrade.c        |   2 +-
 src/bin/pg_upgrade/relfilenumber.c     |  54 +--
 src/bin/psql/command.c                 |  29 +-
 src/bin/psql/describe.c                | 461 ++++++++++++-------------
 9 files changed, 567 insertions(+), 570 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 17ad8eeb838..33786f976e3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1491,8 +1491,8 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	 * Disable timeouts if supported.
 	 */
 	ExecuteSqlStatement(AH, "SET statement_timeout = 0");
-		ExecuteSqlStatement(AH, "SET lock_timeout = 0");
-		ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
+	ExecuteSqlStatement(AH, "SET lock_timeout = 0");
+	ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
 	if (AH->remoteVersion >= 170000)
 		ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
 
@@ -1505,10 +1505,10 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	/*
 	 * Adjust row-security mode, if supported.
 	 */
-		if (dopt->enable_row_security)
-			ExecuteSqlStatement(AH, "SET row_security = on");
-		else
-			ExecuteSqlStatement(AH, "SET row_security = off");
+	if (dopt->enable_row_security)
+		ExecuteSqlStatement(AH, "SET row_security = on");
+	else
+		ExecuteSqlStatement(AH, "SET row_security = off");
 
 	/*
 	 * For security reasons, we restrict the expansion of non-system views and
@@ -1955,7 +1955,7 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
 	if (fout->dopt->binary_upgrade)
 		dobj->dump = ext->dobj.dump;
 	else
-			dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
+		dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
 
 	return true;
 }
@@ -1989,9 +1989,9 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
 	else if (strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
 	{
 		/*
-		 * We dump out any ACLs defined in pg_catalog, if
-		 * they are interesting (and not the original ACLs which were set at
-		 * initdb time, see pg_init_privs).
+		 * We dump out any ACLs defined in pg_catalog, if they are interesting
+		 * (and not the original ACLs which were set at initdb time, see
+		 * pg_init_privs).
 		 */
 		nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
 	}
@@ -3298,7 +3298,7 @@ dumpDatabase(Archive *fout)
 						 "datcollate, datctype, datfrozenxid, "
 						 "datacl, acldefault('d', datdba) AS acldefault, "
 						 "datistemplate, datconnlimit, ");
-		appendPQExpBufferStr(dbQry, "datminmxid, ");
+	appendPQExpBufferStr(dbQry, "datminmxid, ");
 	if (fout->remoteVersion >= 170000)
 		appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
 	else if (fout->remoteVersion >= 150000)
@@ -3640,11 +3640,11 @@ dumpDatabase(Archive *fout)
 					ii_oid,
 					ii_relminmxid;
 
-			appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
-							  "FROM pg_catalog.pg_class\n"
-							  "WHERE oid IN (%u, %u, %u, %u);\n",
-							  LargeObjectRelationId, LargeObjectLOidPNIndexId,
-							  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
+		appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
+						  "FROM pg_catalog.pg_class\n"
+						  "WHERE oid IN (%u, %u, %u, %u);\n",
+						  LargeObjectRelationId, LargeObjectLOidPNIndexId,
+						  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
 
 		lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
 
@@ -4276,7 +4276,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 
 	printfPQExpBuffer(query,
 					  "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
-		appendPQExpBufferStr(query, "pol.polpermissive, ");
+	appendPQExpBufferStr(query, "pol.polpermissive, ");
 	appendPQExpBuffer(query,
 					  "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
 					  "   pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
@@ -6635,9 +6635,9 @@ getAccessMethods(Archive *fout)
 	 * Select all access methods from pg_am table.
 	 */
 	appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
-		appendPQExpBufferStr(query,
-							 "amtype, "
-							 "amhandler::pg_catalog.regproc AS amhandler ");
+	appendPQExpBufferStr(query,
+						 "amtype, "
+						 "amhandler::pg_catalog.regproc AS amhandler ");
 	appendPQExpBufferStr(query, "FROM pg_am");
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -6829,35 +6829,35 @@ getAggregates(Archive *fout)
 	 * Find all interesting aggregates.  See comment in getFuncs() for the
 	 * rationale behind the filtering logic.
 	 */
-		agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
-					 : "p.proisagg");
+	agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
+				 : "p.proisagg");
 
-		appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
-						  "p.proname AS aggname, "
-						  "p.pronamespace AS aggnamespace, "
-						  "p.pronargs, p.proargtypes, "
-						  "p.proowner, "
-						  "p.proacl AS aggacl, "
-						  "acldefault('f', p.proowner) AS acldefault "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s AND ("
-						  "p.pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog') OR "
-						  "p.proacl IS DISTINCT FROM pip.initprivs",
-						  agg_check);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
-		appendPQExpBufferChar(query, ')');
+	appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
+					  "p.proname AS aggname, "
+					  "p.pronamespace AS aggnamespace, "
+					  "p.pronargs, p.proargtypes, "
+					  "p.proowner, "
+					  "p.proacl AS aggacl, "
+					  "acldefault('f', p.proowner) AS acldefault "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s AND ("
+					  "p.pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog') OR "
+					  "p.proacl IS DISTINCT FROM pip.initprivs",
+					  agg_check);
+	if (dopt->binary_upgrade)
+		appendPQExpBufferStr(query,
+							 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -6959,53 +6959,53 @@ getFuncs(Archive *fout)
 	 * include them, since we want to dump extension members individually in
 	 * that mode.  Also, if they are used by casts or transforms then we need
 	 * to gather the information about them, though they won't be dumped if
-	 * they are built-in.  Also, include functions in
-	 * pg_catalog if they have an ACL different from what's shown in
-	 * pg_init_privs (so we have to join to pg_init_privs; annoying).
+	 * they are built-in.  Also, include functions in pg_catalog if they have
+	 * an ACL different from what's shown in pg_init_privs (so we have to join
+	 * to pg_init_privs; annoying).
 	 */
-		not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
-						 : "NOT p.proisagg");
+	not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
+					 : "NOT p.proisagg");
 
-		appendPQExpBuffer(query,
-						  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
-						  "p.pronargs, p.proargtypes, p.prorettype, "
-						  "p.proacl, "
-						  "acldefault('f', p.proowner) AS acldefault, "
-						  "p.pronamespace, "
-						  "p.proowner "
-						  "FROM pg_proc p "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(p.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_proc'::regclass "
-						  "AND pip.objsubid = 0) "
-						  "WHERE %s"
-						  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
-						  "WHERE classid = 'pg_proc'::regclass AND "
-						  "objid = p.oid AND deptype = 'i')"
-						  "\n  AND ("
-						  "\n  pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog')"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
-						  "\n  WHERE pg_cast.oid > %u "
-						  "\n  AND p.oid = pg_cast.castfunc)"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
-						  "\n  WHERE pg_transform.oid > %u AND "
-						  "\n  (p.oid = pg_transform.trffromsql"
-						  "\n  OR p.oid = pg_transform.trftosql))",
-						  not_agg_check,
-						  g_last_builtin_oid,
-						  g_last_builtin_oid);
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
+	appendPQExpBuffer(query,
+					  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
+					  "p.pronargs, p.proargtypes, p.prorettype, "
+					  "p.proacl, "
+					  "acldefault('f', p.proowner) AS acldefault, "
+					  "p.pronamespace, "
+					  "p.proowner "
+					  "FROM pg_proc p "
+					  "LEFT JOIN pg_init_privs pip ON "
+					  "(p.oid = pip.objoid "
+					  "AND pip.classoid = 'pg_proc'::regclass "
+					  "AND pip.objsubid = 0) "
+					  "WHERE %s"
+					  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
+					  "WHERE classid = 'pg_proc'::regclass AND "
+					  "objid = p.oid AND deptype = 'i')"
+					  "\n  AND ("
+					  "\n  pronamespace != "
+					  "(SELECT oid FROM pg_namespace "
+					  "WHERE nspname = 'pg_catalog')"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
+					  "\n  WHERE pg_cast.oid > %u "
+					  "\n  AND p.oid = pg_cast.castfunc)"
+					  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
+					  "\n  WHERE pg_transform.oid > %u AND "
+					  "\n  (p.oid = pg_transform.trffromsql"
+					  "\n  OR p.oid = pg_transform.trftosql))",
+					  not_agg_check,
+					  g_last_builtin_oid,
+					  g_last_builtin_oid);
+	if (dopt->binary_upgrade)
 		appendPQExpBufferStr(query,
-							 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
-		appendPQExpBufferChar(query, ')');
+							 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							 "classid = 'pg_proc'::regclass AND "
+							 "objid = p.oid AND "
+							 "refclassid = 'pg_extension'::regclass AND "
+							 "deptype = 'e')");
+	appendPQExpBufferStr(query,
+						 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
+	appendPQExpBufferChar(query, ')');
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -7254,31 +7254,31 @@ getTables(Archive *fout, int *numTables)
 		appendPQExpBufferStr(query,
 							 "c.relhasoids, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispopulated, ");
+	appendPQExpBufferStr(query,
+						 "c.relispopulated, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relreplident, ");
+	appendPQExpBufferStr(query,
+						 "c.relreplident, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relrowsecurity, c.relforcerowsecurity, ");
+	appendPQExpBufferStr(query,
+						 "c.relrowsecurity, c.relforcerowsecurity, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relminmxid, tc.relminmxid AS tminmxid, ");
+	appendPQExpBufferStr(query,
+						 "c.relminmxid, tc.relminmxid AS tminmxid, ");
 
-		appendPQExpBufferStr(query,
-							 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-							 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-							 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
+	appendPQExpBufferStr(query,
+						 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+						 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+						 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
 
-		appendPQExpBufferStr(query,
-							 "am.amname, ");
+	appendPQExpBufferStr(query,
+						 "am.amname, ");
 
-		appendPQExpBufferStr(query,
-							 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
+	appendPQExpBufferStr(query,
+						 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
 
-		appendPQExpBufferStr(query,
-							 "c.relispartition AS ispartition ");
+	appendPQExpBufferStr(query,
+						 "c.relispartition AS ispartition ");
 
 	/*
 	 * Left join to pg_depend to pick up dependency info linking sequences to
@@ -7298,8 +7298,8 @@ getTables(Archive *fout, int *numTables)
 	/*
 	 * Left join to pg_am to pick up the amname.
 	 */
-		appendPQExpBufferStr(query,
-							 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
+	appendPQExpBufferStr(query,
+						 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
 
 	/*
 	 * We purposefully ignore toast OIDs for partitioned tables; the reason is
@@ -7870,8 +7870,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 						 "t.reloptions AS indreloptions, ");
 
 
-		appendPQExpBufferStr(query,
-							 "i.indisreplident, ");
+	appendPQExpBufferStr(query,
+						 "i.indisreplident, ");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(query,
@@ -9315,8 +9315,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 		appendPQExpBufferStr(q,
 							 "'' AS attcompression,\n");
 
-		appendPQExpBufferStr(q,
-							 "a.attidentity,\n");
+	appendPQExpBufferStr(q,
+						 "a.attidentity,\n");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(q,
@@ -10719,73 +10719,73 @@ getAdditionalACLs(Archive *fout)
 	PQclear(res);
 
 	/* Fetch initial-privileges data */
-		printfPQExpBuffer(query,
-						  "SELECT objoid, classoid, objsubid, privtype, initprivs "
-						  "FROM pg_init_privs");
+	printfPQExpBuffer(query,
+					  "SELECT objoid, classoid, objsubid, privtype, initprivs "
+					  "FROM pg_init_privs");
 
-		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
-		ntups = PQntuples(res);
-		for (i = 0; i < ntups; i++)
-		{
-			Oid			objoid = atooid(PQgetvalue(res, i, 0));
-			Oid			classoid = atooid(PQgetvalue(res, i, 1));
-			int			objsubid = atoi(PQgetvalue(res, i, 2));
-			char		privtype = *(PQgetvalue(res, i, 3));
-			char	   *initprivs = PQgetvalue(res, i, 4);
-			CatalogId	objId;
-			DumpableObject *dobj;
+	ntups = PQntuples(res);
+	for (i = 0; i < ntups; i++)
+	{
+		Oid			objoid = atooid(PQgetvalue(res, i, 0));
+		Oid			classoid = atooid(PQgetvalue(res, i, 1));
+		int			objsubid = atoi(PQgetvalue(res, i, 2));
+		char		privtype = *(PQgetvalue(res, i, 3));
+		char	   *initprivs = PQgetvalue(res, i, 4);
+		CatalogId	objId;
+		DumpableObject *dobj;
 
-			objId.tableoid = classoid;
-			objId.oid = objoid;
-			dobj = findObjectByCatalogId(objId);
-			/* OK to ignore entries we haven't got a DumpableObject for */
-			if (dobj)
+		objId.tableoid = classoid;
+		objId.oid = objoid;
+		dobj = findObjectByCatalogId(objId);
+		/* OK to ignore entries we haven't got a DumpableObject for */
+		if (dobj)
+		{
+			/* Cope with sub-object initprivs */
+			if (objsubid != 0)
 			{
-				/* Cope with sub-object initprivs */
-				if (objsubid != 0)
-				{
-					if (dobj->objType == DO_TABLE)
-					{
-						/* For a column initprivs, set the table's ACL flags */
-						dobj->components |= DUMP_COMPONENT_ACL;
-						((TableInfo *) dobj)->hascolumnACLs = true;
-					}
-					else
-						pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
-									   classoid, objoid, objsubid);
-					continue;
-				}
-
-				/*
-				 * We ignore any pg_init_privs.initprivs entry for the public
-				 * schema, as explained in getNamespaces().
-				 */
-				if (dobj->objType == DO_NAMESPACE &&
-					strcmp(dobj->name, "public") == 0)
-					continue;
-
-				/* Else it had better be of a type we think has ACLs */
-				if (dobj->objType == DO_NAMESPACE ||
-					dobj->objType == DO_TYPE ||
-					dobj->objType == DO_FUNC ||
-					dobj->objType == DO_AGG ||
-					dobj->objType == DO_TABLE ||
-					dobj->objType == DO_PROCLANG ||
-					dobj->objType == DO_FDW ||
-					dobj->objType == DO_FOREIGN_SERVER)
+				if (dobj->objType == DO_TABLE)
 				{
-					DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
-
-					daobj->dacl.privtype = privtype;
-					daobj->dacl.initprivs = pstrdup(initprivs);
+					/* For a column initprivs, set the table's ACL flags */
+					dobj->components |= DUMP_COMPONENT_ACL;
+					((TableInfo *) dobj)->hascolumnACLs = true;
 				}
 				else
 					pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
 								   classoid, objoid, objsubid);
+				continue;
+			}
+
+			/*
+			 * We ignore any pg_init_privs.initprivs entry for the public
+			 * schema, as explained in getNamespaces().
+			 */
+			if (dobj->objType == DO_NAMESPACE &&
+				strcmp(dobj->name, "public") == 0)
+				continue;
+
+			/* Else it had better be of a type we think has ACLs */
+			if (dobj->objType == DO_NAMESPACE ||
+				dobj->objType == DO_TYPE ||
+				dobj->objType == DO_FUNC ||
+				dobj->objType == DO_AGG ||
+				dobj->objType == DO_TABLE ||
+				dobj->objType == DO_PROCLANG ||
+				dobj->objType == DO_FDW ||
+				dobj->objType == DO_FOREIGN_SERVER)
+			{
+				DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
+
+				daobj->dacl.privtype = privtype;
+				daobj->dacl.initprivs = pstrdup(initprivs);
 			}
+			else
+				pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
+							   classoid, objoid, objsubid);
 		}
-		PQclear(res);
+	}
+	PQclear(res);
 
 	destroyPQExpBuffer(query);
 }
@@ -11130,8 +11130,8 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 		 * The results must be in the order of the relations supplied in the
 		 * parameters to ensure we remain in sync as we walk through the TOC.
 		 *
-		 * For versions before 19, the redundant filter clause on s.tablename =
-		 * ANY(...) seems sufficient to convince the planner to use
+		 * For versions before 19, the redundant filter clause on s.tablename
+		 * = ANY(...) seems sufficient to convince the planner to use
 		 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
 		 * In newer versions, pg_stats returns the table OIDs, eliminating the
 		 * need for that hack.
@@ -13471,11 +13471,11 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
 							 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
 							 "proleakproof,\n");
 
-			appendPQExpBufferStr(query,
-								 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
+		appendPQExpBufferStr(query,
+							 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
 
-			appendPQExpBufferStr(query,
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -14965,9 +14965,9 @@ dumpCollation(Archive *fout, const CollInfo *collinfo)
 	/* Get collation-specific details */
 	appendPQExpBufferStr(query, "SELECT ");
 
-		appendPQExpBufferStr(query,
-							 "collprovider, "
-							 "collversion, ");
+	appendPQExpBufferStr(query,
+						 "collprovider, "
+						 "collversion, ");
 
 	if (fout->remoteVersion >= 120000)
 		appendPQExpBufferStr(query,
@@ -15374,23 +15374,23 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 							 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
 							 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggkind,\n"
-								 "aggmtransfn,\n"
-								 "aggminvtransfn,\n"
-								 "aggmfinalfn,\n"
-								 "aggmtranstype::pg_catalog.regtype,\n"
-								 "aggfinalextra,\n"
-								 "aggmfinalextra,\n"
-								 "aggtransspace,\n"
-								 "aggmtransspace,\n"
-								 "aggminitval,\n");
+		appendPQExpBufferStr(query,
+							 "aggkind,\n"
+							 "aggmtransfn,\n"
+							 "aggminvtransfn,\n"
+							 "aggmfinalfn,\n"
+							 "aggmtranstype::pg_catalog.regtype,\n"
+							 "aggfinalextra,\n"
+							 "aggmfinalextra,\n"
+							 "aggtransspace,\n"
+							 "aggmtransspace,\n"
+							 "aggminitval,\n");
 
-			appendPQExpBufferStr(query,
-								 "aggcombinefn,\n"
-								 "aggserialfn,\n"
-								 "aggdeserialfn,\n"
-								 "proparallel,\n");
+		appendPQExpBufferStr(query,
+							 "aggcombinefn,\n"
+							 "aggserialfn,\n"
+							 "aggdeserialfn,\n"
+							 "proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -16850,30 +16850,30 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
 			appendPQExpBufferStr(query,
 								 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
 
-				/*
-				 * In principle we should call acldefault('c', relowner) to
-				 * get the default ACL for a column.  However, we don't
-				 * currently store the numeric OID of the relowner in
-				 * TableInfo.  We could convert the owner name using regrole,
-				 * but that creates a risk of failure due to concurrent role
-				 * renames.  Given that the default ACL for columns is empty
-				 * and is likely to stay that way, it's not worth extra cycles
-				 * and risk to avoid hard-wiring that knowledge here.
-				 */
-				appendPQExpBufferStr(query,
-									 "SELECT at.attname, "
-									 "at.attacl, "
-									 "'{}' AS acldefault, "
-									 "pip.privtype, pip.initprivs "
-									 "FROM pg_catalog.pg_attribute at "
-									 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
-									 "(at.attrelid = pip.objoid "
-									 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
-									 "AND at.attnum = pip.objsubid) "
-									 "WHERE at.attrelid = $1 AND "
-									 "NOT at.attisdropped "
-									 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
-									 "ORDER BY at.attnum");
+			/*
+			 * In principle we should call acldefault('c', relowner) to get
+			 * the default ACL for a column.  However, we don't currently
+			 * store the numeric OID of the relowner in TableInfo.  We could
+			 * convert the owner name using regrole, but that creates a risk
+			 * of failure due to concurrent role renames.  Given that the
+			 * default ACL for columns is empty and is likely to stay that
+			 * way, it's not worth extra cycles and risk to avoid hard-wiring
+			 * that knowledge here.
+			 */
+			appendPQExpBufferStr(query,
+								 "SELECT at.attname, "
+								 "at.attacl, "
+								 "'{}' AS acldefault, "
+								 "pip.privtype, pip.initprivs "
+								 "FROM pg_catalog.pg_attribute at "
+								 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
+								 "(at.attrelid = pip.objoid "
+								 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
+								 "AND at.attnum = pip.objsubid) "
+								 "WHERE at.attrelid = $1 AND "
+								 "NOT at.attisdropped "
+								 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
+								 "ORDER BY at.attnum");
 
 			ExecuteSqlStatement(fout, query->data);
 
@@ -19167,7 +19167,7 @@ collectSequences(Archive *fout)
 	 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
 	 */
 	if (fout->remoteVersion < 180000 ||
-			 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
+		(!fout->dopt->dumpData && !fout->dopt->sequence_data))
 		query = "SELECT seqrelid, format_type(seqtypid, NULL), "
 			"seqstart, seqincrement, "
 			"seqmax, seqmin, "
@@ -19229,15 +19229,14 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
 
 	/*
-	 * The sequence information is gathered in a sorted
-	 * table before any calls to dumpSequence().  See collectSequences() for
-	 * more information.
+	 * The sequence information is gathered in a sorted table before any calls
+	 * to dumpSequence().  See collectSequences() for more information.
 	 */
-		Assert(sequences);
+	Assert(sequences);
 
-		key.oid = tbinfo->dobj.catId.oid;
-		seq = bsearch(&key, sequences, nsequences,
-					  sizeof(SequenceItem), SequenceItemCmp);
+	key.oid = tbinfo->dobj.catId.oid;
+	seq = bsearch(&key, sequences, nsequences,
+				  sizeof(SequenceItem), SequenceItemCmp);
 
 	/* Calculate default limits for a sequence of this type */
 	is_ascending = (seq->incby >= 0);
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index e68937c9934..afcd9a218d8 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -785,11 +785,11 @@ dropRoles(PGconn *conn)
 	int			i_rolname;
 	int			i;
 
-		printfPQExpBuffer(buf,
-						  "SELECT rolname "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 1", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT rolname "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 1", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
@@ -843,16 +843,16 @@ dumpRoles(PGconn *conn)
 	 * Notes: rolconfig is dumped later, and pg_authid must be used for
 	 * extracting rolcomment regardless of role_catalog.
 	 */
-		printfPQExpBuffer(buf,
-						  "SELECT oid, rolname, rolsuper, rolinherit, "
-						  "rolcreaterole, rolcreatedb, "
-						  "rolcanlogin, rolconnlimit, rolpassword, "
-						  "rolvaliduntil, rolreplication, rolbypassrls, "
-						  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
-						  "rolname = current_user AS is_current_user "
-						  "FROM %s "
-						  "WHERE rolname !~ '^pg_' "
-						  "ORDER BY 2", role_catalog);
+	printfPQExpBuffer(buf,
+					  "SELECT oid, rolname, rolsuper, rolinherit, "
+					  "rolcreaterole, rolcreatedb, "
+					  "rolcanlogin, rolconnlimit, rolpassword, "
+					  "rolvaliduntil, rolreplication, rolbypassrls, "
+					  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
+					  "rolname = current_user AS is_current_user "
+					  "FROM %s "
+					  "WHERE rolname !~ '^pg_' "
+					  "ORDER BY 2", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 01b354e3c44..7629f8cab02 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -1475,15 +1475,15 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_remove(anyarray,anyelement)'"
-							 ", 'array_replace(anyarray,anyelement,anyelement)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_remove(anyarray,anyelement)'"
+						 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-		appendPQExpBufferStr(&old_polymorphics,
-							 ", 'array_position(anyarray,anyelement)'"
-							 ", 'array_position(anyarray,anyelement,integer)'"
-							 ", 'array_positions(anyarray,anyelement)'"
-							 ", 'width_bucket(anyelement,anyarray)'");
+	appendPQExpBufferStr(&old_polymorphics,
+						 ", 'array_position(anyarray,anyelement)'"
+						 ", 'array_position(anyarray,anyelement,integer)'"
+						 ", 'array_positions(anyarray,anyelement)'"
+						 ", 'width_bucket(anyelement,anyarray)'");
 
 	/*
 	 * The query below hardcodes FirstNormalObjectId as 16384 rather than
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 44355feea30..a1bdbf373e3 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,7 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-		cluster->bin_version = v1 * 10000;
+	cluster->bin_version = v1 * 10000;
 }
 
 
@@ -344,8 +344,8 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-		check_single_dir(pg_data, "pg_wal");
-		check_single_dir(pg_data, "pg_xact");
+	check_single_dir(pg_data, "pg_wal");
+	check_single_dir(pg_data, "pg_xact");
 }
 
 
@@ -385,7 +385,7 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-		check_exec(cluster->bindir, "pg_resetwal", check_versions);
+	check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
 	{
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index c45b3183684..c7a1416494d 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -61,52 +61,52 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
-										   old_cluster.controldata.chkpnt_nxtmulti,
-										   old_cluster.controldata.chkpnt_nxtmxoff);
+	old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
+									   old_cluster.controldata.chkpnt_nxtmulti,
+									   old_cluster.controldata.chkpnt_nxtmxoff);
 
-		for (MultiXactId multi = from_multi; multi != to_multi;)
-		{
-			MultiXactMember member;
-			bool		multixid_valid;
-
-			/*
-			 * Read this multixid's members.
-			 *
-			 * Locking-only XIDs that may be part of multi-xids don't matter
-			 * after upgrade, as there can be no transactions running across
-			 * upgrade.  So as a small optimization, we only read one member
-			 * from each multixid: the one updating one, or if there was no
-			 * update, arbitrarily the first locking xid.
-			 */
-			multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
+	for (MultiXactId multi = from_multi; multi != to_multi;)
+	{
+		MultiXactMember member;
+		bool		multixid_valid;
 
-			/*
-			 * Write the new offset to pg_multixact/offsets.
-			 *
-			 * Even if this multixid is invalid, we still need to write its
-			 * offset if the *previous* multixid was valid.  That's because
-			 * when reading a multixid, the number of members is calculated
-			 * from the difference between the two offsets.
-			 */
-			RecordMultiXactOffset(offsets_writer, multi,
-								  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
+		/*
+		 * Read this multixid's members.
+		 *
+		 * Locking-only XIDs that may be part of multi-xids don't matter after
+		 * upgrade, as there can be no transactions running across upgrade. So
+		 * as a small optimization, we only read one member from each
+		 * multixid: the one updating one, or if there was no update,
+		 * arbitrarily the first locking xid.
+		 */
+		multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
 
-			/* Write the members */
-			if (multixid_valid)
-			{
-				RecordMultiXactMembers(members_writer, next_offset, 1, &member);
-				next_offset += 1;
-			}
+		/*
+		 * Write the new offset to pg_multixact/offsets.
+		 *
+		 * Even if this multixid is invalid, we still need to write its offset
+		 * if the *previous* multixid was valid.  That's because when reading
+		 * a multixid, the number of members is calculated from the difference
+		 * between the two offsets.
+		 */
+		RecordMultiXactOffset(offsets_writer, multi,
+							  (multixid_valid || prev_multixid_valid) ? next_offset : 0);
 
-			/* Advance to next multixid, handling wraparound */
-			multi++;
-			if (multi < FirstMultiXactId)
-				multi = FirstMultiXactId;
-			prev_multixid_valid = multixid_valid;
+		/* Write the members */
+		if (multixid_valid)
+		{
+			RecordMultiXactMembers(members_writer, next_offset, 1, &member);
+			next_offset += 1;
 		}
 
-		FreeOldMultiXactReader(old_reader);
+		/* Advance to next multixid, handling wraparound */
+		multi++;
+		if (multi < FirstMultiXactId)
+			multi = FirstMultiXactId;
+		prev_multixid_valid = multixid_valid;
+	}
+
+	FreeOldMultiXactReader(old_reader);
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index e5d7920c1b1..d8e1b680f5a 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -833,7 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
+		oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index ec2ff7acb21..6c467bdc8a5 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -587,32 +587,32 @@ transfer_relfile(FileNameMap *map, const char *type_suffix)
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-			switch (user_opts.transfer_mode)
-			{
-				case TRANSFER_MODE_CLONE:
-					pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
-						   old_file, new_file);
-					cloneFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
-						   old_file, new_file);
-					copyFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_COPY_FILE_RANGE:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
-						   old_file, new_file);
-					copyFileByRange(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_LINK:
-					pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
-						   old_file, new_file);
-					linkFile(old_file, new_file, map->nspname, map->relname);
-					break;
-				case TRANSFER_MODE_SWAP:
-					/* swap mode is handled in its own code path */
-					pg_fatal("should never happen");
-					break;
-			}
+		switch (user_opts.transfer_mode)
+		{
+			case TRANSFER_MODE_CLONE:
+				pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
+					   old_file, new_file);
+				cloneFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
+					   old_file, new_file);
+				copyFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_COPY_FILE_RANGE:
+				pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
+					   old_file, new_file);
+				copyFileByRange(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_LINK:
+				pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
+					   old_file, new_file);
+				linkFile(old_file, new_file, map->nspname, map->relname);
+				break;
+			case TRANSFER_MODE_SWAP:
+				/* swap mode is handled in its own code path */
+				pg_fatal("should never happen");
+				break;
+		}
 	}
 }
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 44ef11d980e..c90bf8bbde2 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -6278,23 +6278,22 @@ get_create_object_cmd(EditableObjectType obj_type, Oid oid,
 			 * ensure the right view gets replaced.  Also, check relation kind
 			 * to be sure it's a view.
 			 *
-			 * Views may have WITH [LOCAL|CASCADED]
-			 * CHECK OPTION.  These are not part of the view definition
-			 * returned by pg_get_viewdef() and so need to be retrieved
-			 * separately.  Materialized views may have
-			 * arbitrary storage parameter reloptions.
+			 * Views may have WITH [LOCAL|CASCADED] CHECK OPTION.  These are
+			 * not part of the view definition returned by pg_get_viewdef()
+			 * and so need to be retrieved separately.  Materialized views may
+			 * have arbitrary storage parameter reloptions.
 			 */
 			printfPQExpBuffer(query, "/* %s */\n", _("Get view's definition and details"));
-				appendPQExpBuffer(query,
-								  "SELECT nspname, relname, relkind, "
-								  "pg_catalog.pg_get_viewdef(c.oid, true), "
-								  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
-								  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
-								  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
-								  "FROM pg_catalog.pg_class c "
-								  "LEFT JOIN pg_catalog.pg_namespace n "
-								  "ON c.relnamespace = n.oid WHERE c.oid = %u",
-								  oid);
+			appendPQExpBuffer(query,
+							  "SELECT nspname, relname, relkind, "
+							  "pg_catalog.pg_get_viewdef(c.oid, true), "
+							  "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
+							  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+							  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
+							  "FROM pg_catalog.pg_class c "
+							  "LEFT JOIN pg_catalog.pg_namespace n "
+							  "ON c.relnamespace = n.oid WHERE c.oid = %u",
+							  oid);
 			break;
 	}
 
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index ce99137c613..ac0c55e7aa5 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -387,19 +387,19 @@ describeFunctions(const char *functypes, const char *func_pattern,
 						  gettext_noop("stable"),
 						  gettext_noop("volatile"),
 						  gettext_noop("Volatility"));
-			appendPQExpBuffer(&buf,
-							  ",\n CASE\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
-							  "  WHEN p.proparallel = "
-							  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
-							  " END as \"%s\"",
-							  gettext_noop("restricted"),
-							  gettext_noop("safe"),
-							  gettext_noop("unsafe"),
-							  gettext_noop("Parallel"));
+		appendPQExpBuffer(&buf,
+						  ",\n CASE\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
+						  "  WHEN p.proparallel = "
+						  CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
+						  " END as \"%s\"",
+						  gettext_noop("restricted"),
+						  gettext_noop("safe"),
+						  gettext_noop("unsafe"),
+						  gettext_noop("Parallel"));
 		appendPQExpBuffer(&buf,
 						  ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\""
 						  ",\n CASE WHEN prosecdef THEN '%s' ELSE '%s' END AS \"%s\""
@@ -599,8 +599,8 @@ describeFunctions(const char *functypes, const char *func_pattern,
 
 	myopt.title = _("List of functions");
 	myopt.translate_header = true;
-		myopt.translate_columns = translate_columns;
-		myopt.n_translate_columns = lengthof(translate_columns);
+	myopt.translate_columns = translate_columns;
+	myopt.n_translate_columns = lengthof(translate_columns);
 
 	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
 
@@ -1086,38 +1086,38 @@ permissionsList(const char *pattern, bool showSystem)
 					  "  ), E'\\n') AS \"%s\"",
 					  gettext_noop("Column privileges"));
 
-		appendPQExpBuffer(&buf,
-						  ",\n  pg_catalog.array_to_string(ARRAY(\n"
-						  "    SELECT polname\n"
-						  "    || CASE WHEN NOT polpermissive THEN\n"
-						  "       E' (RESTRICTIVE)'\n"
-						  "       ELSE '' END\n"
-						  "    || CASE WHEN polcmd != '*' THEN\n"
-						  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
-						  "       ELSE E':'\n"
-						  "       END\n"
-						  "    || CASE WHEN polqual IS NOT NULL THEN\n"
-						  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
-						  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
-						  "       ELSE E''\n"
-						  "       END"
-						  "    || CASE WHEN polroles <> '{0}' THEN\n"
-						  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
-						  "               ARRAY(\n"
-						  "                   SELECT rolname\n"
-						  "                   FROM pg_catalog.pg_roles\n"
-						  "                   WHERE oid = ANY (polroles)\n"
-						  "                   ORDER BY 1\n"
-						  "               ), E', ')\n"
-						  "       ELSE E''\n"
-						  "       END\n"
-						  "    FROM pg_catalog.pg_policy pol\n"
-						  "    WHERE polrelid = c.oid), E'\\n')\n"
-						  "    AS \"%s\"",
-						  gettext_noop("Policies"));
+	appendPQExpBuffer(&buf,
+					  ",\n  pg_catalog.array_to_string(ARRAY(\n"
+					  "    SELECT polname\n"
+					  "    || CASE WHEN NOT polpermissive THEN\n"
+					  "       E' (RESTRICTIVE)'\n"
+					  "       ELSE '' END\n"
+					  "    || CASE WHEN polcmd != '*' THEN\n"
+					  "           E' (' || polcmd::pg_catalog.text || E'):'\n"
+					  "       ELSE E':'\n"
+					  "       END\n"
+					  "    || CASE WHEN polqual IS NOT NULL THEN\n"
+					  "           E'\\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    || CASE WHEN polwithcheck IS NOT NULL THEN\n"
+					  "           E'\\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
+					  "       ELSE E''\n"
+					  "       END"
+					  "    || CASE WHEN polroles <> '{0}' THEN\n"
+					  "           E'\\n  to: ' || pg_catalog.array_to_string(\n"
+					  "               ARRAY(\n"
+					  "                   SELECT rolname\n"
+					  "                   FROM pg_catalog.pg_roles\n"
+					  "                   WHERE oid = ANY (polroles)\n"
+					  "                   ORDER BY 1\n"
+					  "               ), E', ')\n"
+					  "       ELSE E''\n"
+					  "       END\n"
+					  "    FROM pg_catalog.pg_policy pol\n"
+					  "    WHERE polrelid = c.oid), E'\\n')\n"
+					  "    AS \"%s\"",
+					  gettext_noop("Policies"));
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n"
 						 "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
@@ -1675,27 +1675,27 @@ describeOneTableDetails(const char *schemaname,
 		char	   *footers[3] = {NULL, NULL, NULL};
 
 		printfPQExpBuffer(&buf, "/* %s */\n", _("Get sequence information"));
-			appendPQExpBuffer(&buf,
-							  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
-							  "       seqstart AS \"%s\",\n"
-							  "       seqmin AS \"%s\",\n"
-							  "       seqmax AS \"%s\",\n"
-							  "       seqincrement AS \"%s\",\n"
-							  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
-							  "       seqcache AS \"%s\"\n",
-							  gettext_noop("Type"),
-							  gettext_noop("Start"),
-							  gettext_noop("Minimum"),
-							  gettext_noop("Maximum"),
-							  gettext_noop("Increment"),
-							  gettext_noop("yes"),
-							  gettext_noop("no"),
-							  gettext_noop("Cycles?"),
-							  gettext_noop("Cache"));
-			appendPQExpBuffer(&buf,
-							  "FROM pg_catalog.pg_sequence\n"
-							  "WHERE seqrelid = '%s';",
-							  oid);
+		appendPQExpBuffer(&buf,
+						  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
+						  "       seqstart AS \"%s\",\n"
+						  "       seqmin AS \"%s\",\n"
+						  "       seqmax AS \"%s\",\n"
+						  "       seqincrement AS \"%s\",\n"
+						  "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
+						  "       seqcache AS \"%s\"\n",
+						  gettext_noop("Type"),
+						  gettext_noop("Start"),
+						  gettext_noop("Minimum"),
+						  gettext_noop("Maximum"),
+						  gettext_noop("Increment"),
+						  gettext_noop("yes"),
+						  gettext_noop("no"),
+						  gettext_noop("Cycles?"),
+						  gettext_noop("Cache"));
+		appendPQExpBuffer(&buf,
+						  "FROM pg_catalog.pg_sequence\n"
+						  "WHERE seqrelid = '%s';",
+						  oid);
 
 		res = PSQLexec(buf.data);
 		if (!res)
@@ -1913,7 +1913,7 @@ describeOneTableDetails(const char *schemaname,
 		appendPQExpBufferStr(&buf, ",\n  (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
 							 "   WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
 		attcoll_col = cols++;
-			appendPQExpBufferStr(&buf, ",\n  a.attidentity");
+		appendPQExpBufferStr(&buf, ",\n  a.attidentity");
 		attidentity_col = cols++;
 		if (pset.sversion >= 120000)
 			appendPQExpBufferStr(&buf, ",\n  a.attgenerated");
@@ -2326,7 +2326,7 @@ describeOneTableDetails(const char *schemaname,
 							 CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
 							 "condeferred) AS condeferred,\n");
 
-			appendPQExpBufferStr(&buf, "i.indisreplident,\n");
+		appendPQExpBufferStr(&buf, "i.indisreplident,\n");
 
 		if (pset.sversion >= 150000)
 			appendPQExpBufferStr(&buf, "i.indnullsnotdistinct,\n");
@@ -2431,7 +2431,7 @@ describeOneTableDetails(const char *schemaname,
 								 "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n  "
 								 "pg_catalog.pg_get_constraintdef(con.oid, true), "
 								 "contype, condeferrable, condeferred");
-				appendPQExpBufferStr(&buf, ", i.indisreplident");
+			appendPQExpBufferStr(&buf, ", i.indisreplident");
 			appendPQExpBufferStr(&buf, ", c2.reltablespace");
 			if (pset.sversion >= 180000)
 				appendPQExpBufferStr(&buf, ", con.conperiod");
@@ -2682,81 +2682,80 @@ describeOneTableDetails(const char *schemaname,
 		PQclear(result);
 
 		/* print any row-level policies */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get row-level policies for this table"));
-			appendPQExpBufferStr(&buf, "SELECT pol.polname,");
-				appendPQExpBufferStr(&buf,
-									 " pol.polpermissive,\n");
-			appendPQExpBuffer(&buf,
-							  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
-							  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
-							  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
-							  "  CASE pol.polcmd\n"
-							  "    WHEN 'r' THEN 'SELECT'\n"
-							  "    WHEN 'a' THEN 'INSERT'\n"
-							  "    WHEN 'w' THEN 'UPDATE'\n"
-							  "    WHEN 'd' THEN 'DELETE'\n"
-							  "    END AS cmd\n"
-							  "FROM pg_catalog.pg_policy pol\n"
-							  "WHERE pol.polrelid = '%s' ORDER BY 1;",
-							  oid);
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get row-level policies for this table"));
+		appendPQExpBufferStr(&buf, "SELECT pol.polname,");
+		appendPQExpBufferStr(&buf,
+							 " pol.polpermissive,\n");
+		appendPQExpBuffer(&buf,
+						  "  CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
+						  "  pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
+						  "  pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
+						  "  CASE pol.polcmd\n"
+						  "    WHEN 'r' THEN 'SELECT'\n"
+						  "    WHEN 'a' THEN 'INSERT'\n"
+						  "    WHEN 'w' THEN 'UPDATE'\n"
+						  "    WHEN 'd' THEN 'DELETE'\n"
+						  "    END AS cmd\n"
+						  "FROM pg_catalog.pg_policy pol\n"
+						  "WHERE pol.polrelid = '%s' ORDER BY 1;",
+						  oid);
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			/*
-			 * Handle cases where RLS is enabled and there are policies, or
-			 * there aren't policies, or RLS isn't enabled but there are
-			 * policies
-			 */
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies:"));
+		/*
+		 * Handle cases where RLS is enabled and there are policies, or there
+		 * aren't policies, or RLS isn't enabled but there are policies
+		 */
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies:"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
 
-			if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
+		if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
 
-			if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
-				printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
+		if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
+			printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
 
-			if (!tableinfo.rowsecurity && tuples > 0)
-				printTableAddFooter(&cont, _("Policies (row security disabled):"));
+		if (!tableinfo.rowsecurity && tuples > 0)
+			printTableAddFooter(&cont, _("Policies (row security disabled):"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    POLICY \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    POLICY \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				if (*(PQgetvalue(result, i, 1)) == 'f')
-					appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
+			if (*(PQgetvalue(result, i, 1)) == 'f')
+				appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
 
-				if (!PQgetisnull(result, i, 5))
-					appendPQExpBuffer(&buf, " FOR %s",
-									  PQgetvalue(result, i, 5));
+			if (!PQgetisnull(result, i, 5))
+				appendPQExpBuffer(&buf, " FOR %s",
+								  PQgetvalue(result, i, 5));
 
-				if (!PQgetisnull(result, i, 2))
-				{
-					appendPQExpBuffer(&buf, "\n      TO %s",
-									  PQgetvalue(result, i, 2));
-				}
+			if (!PQgetisnull(result, i, 2))
+			{
+				appendPQExpBuffer(&buf, "\n      TO %s",
+								  PQgetvalue(result, i, 2));
+			}
 
-				if (!PQgetisnull(result, i, 3))
-					appendPQExpBuffer(&buf, "\n      USING (%s)",
-									  PQgetvalue(result, i, 3));
+			if (!PQgetisnull(result, i, 3))
+				appendPQExpBuffer(&buf, "\n      USING (%s)",
+								  PQgetvalue(result, i, 3));
 
-				if (!PQgetisnull(result, i, 4))
-					appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
-									  PQgetvalue(result, i, 4));
+			if (!PQgetisnull(result, i, 4))
+				appendPQExpBuffer(&buf, "\n      WITH CHECK (%s)",
+								  PQgetvalue(result, i, 4));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* print any extended statistics */
 		if (pset.sversion >= 140000)
@@ -3025,115 +3024,115 @@ describeOneTableDetails(const char *schemaname,
 		}
 
 		/* print any publications */
-			printfPQExpBuffer(&buf, "/* %s */\n",
-							  _("Get publications that publish this table"));
-			if (pset.sversion >= 150000)
+		printfPQExpBuffer(&buf, "/* %s */\n",
+						  _("Get publications that publish this table"));
+		if (pset.sversion >= 150000)
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
+							  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
+							  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "UNION\n"
+							  "SELECT pubname\n"
+							  "     , pg_catalog.pg_get_expr(pr.prqual, c.oid)\n"
+							  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
+							  "         (SELECT pg_catalog.string_agg(attname, ', ')\n"
+							  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
+							  "                pg_catalog.pg_attribute\n"
+							  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
+							  "        ELSE NULL END) "
+							  "FROM pg_catalog.pg_publication p\n"
+							  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
+							  "WHERE pr.prrelid = '%s'\n",
+							  oid, oid, oid);
+
+			if (pset.sversion >= 190000)
 			{
+				/*
+				 * Skip entries where this relation appears in the
+				 * publication's EXCEPT list.
+				 */
 				appendPQExpBuffer(&buf,
+								  " AND NOT pr.prexcept\n"
+								  "UNION\n"
 								  "SELECT pubname\n"
 								  "     , NULL\n"
 								  "     , NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
-								  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
-								  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
-								  "UNION\n"
-								  "SELECT pubname\n"
-								  "     , pg_catalog.pg_get_expr(pr.prqual, c.oid)\n"
-								  "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
-								  "         (SELECT pg_catalog.string_agg(attname, ', ')\n"
-								  "           FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
-								  "                pg_catalog.pg_attribute\n"
-								  "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
-								  "        ELSE NULL END) "
-								  "FROM pg_catalog.pg_publication p\n"
-								  "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
-								  "WHERE pr.prrelid = '%s'\n",
+								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+								  "     AND NOT EXISTS (\n"
+								  "     SELECT 1\n"
+								  "     FROM pg_catalog.pg_publication_rel pr\n"
+								  "     WHERE pr.prpubid = p.oid AND\n"
+								  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
+								  "ORDER BY 1;",
 								  oid, oid, oid);
-
-				if (pset.sversion >= 190000)
-				{
-					/*
-					 * Skip entries where this relation appears in the
-					 * publication's EXCEPT list.
-					 */
-					appendPQExpBuffer(&buf,
-									  " AND NOT pr.prexcept\n"
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "     , NULL\n"
-									  "     , NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "     AND NOT EXISTS (\n"
-									  "     SELECT 1\n"
-									  "     FROM pg_catalog.pg_publication_rel pr\n"
-									  "     WHERE pr.prpubid = p.oid AND\n"
-									  "     (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
-									  "ORDER BY 1;",
-									  oid, oid, oid);
-				}
-				else
-				{
-					appendPQExpBuffer(&buf,
-									  "UNION\n"
-									  "SELECT pubname\n"
-									  "		, NULL\n"
-									  "		, NULL\n"
-									  "FROM pg_catalog.pg_publication p\n"
-									  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
-									  "ORDER BY 1;",
-									  oid);
-				}
 			}
 			else
 			{
 				appendPQExpBuffer(&buf,
+								  "UNION\n"
 								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
-								  "FROM pg_catalog.pg_publication p\n"
-								  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
-								  "WHERE pr.prrelid = '%s'\n"
-								  "UNION ALL\n"
-								  "SELECT pubname\n"
-								  "     , NULL\n"
-								  "     , NULL\n"
+								  "		, NULL\n"
+								  "		, NULL\n"
 								  "FROM pg_catalog.pg_publication p\n"
 								  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
 								  "ORDER BY 1;",
-								  oid, oid);
+								  oid);
 			}
+		}
+		else
+		{
+			appendPQExpBuffer(&buf,
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+							  "WHERE pr.prrelid = '%s'\n"
+							  "UNION ALL\n"
+							  "SELECT pubname\n"
+							  "     , NULL\n"
+							  "     , NULL\n"
+							  "FROM pg_catalog.pg_publication p\n"
+							  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
+							  "ORDER BY 1;",
+							  oid, oid);
+		}
 
-			result = PSQLexec(buf.data);
-			if (!result)
-				goto error_return;
-			else
-				tuples = PQntuples(result);
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+		else
+			tuples = PQntuples(result);
 
-			if (tuples > 0)
-				printTableAddFooter(&cont, _("Included in publications:"));
+		if (tuples > 0)
+			printTableAddFooter(&cont, _("Included in publications:"));
 
-			/* Might be an empty set - that's ok */
-			for (i = 0; i < tuples; i++)
-			{
-				printfPQExpBuffer(&buf, "    \"%s\"",
-								  PQgetvalue(result, i, 0));
+		/* Might be an empty set - that's ok */
+		for (i = 0; i < tuples; i++)
+		{
+			printfPQExpBuffer(&buf, "    \"%s\"",
+							  PQgetvalue(result, i, 0));
 
-				/* column list (if any) */
-				if (!PQgetisnull(result, i, 2))
-					appendPQExpBuffer(&buf, " (%s)",
-									  PQgetvalue(result, i, 2));
+			/* column list (if any) */
+			if (!PQgetisnull(result, i, 2))
+				appendPQExpBuffer(&buf, " (%s)",
+								  PQgetvalue(result, i, 2));
 
-				/* row filter (if any) */
-				if (!PQgetisnull(result, i, 1))
-					appendPQExpBuffer(&buf, " WHERE %s",
-									  PQgetvalue(result, i, 1));
+			/* row filter (if any) */
+			if (!PQgetisnull(result, i, 1))
+				appendPQExpBuffer(&buf, " WHERE %s",
+								  PQgetvalue(result, i, 1));
 
-				printTableAddFooter(&cont, buf.data);
-			}
-			PQclear(result);
+			printTableAddFooter(&cont, buf.data);
+		}
+		PQclear(result);
 
 		/* Print publications where the table is in the EXCEPT clause */
 		if (pset.sversion >= 190000)
@@ -3805,7 +3804,7 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		ncols++;
 	}
 	appendPQExpBufferStr(&buf, "\n, r.rolreplication");
-		appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
+	appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
@@ -3860,8 +3859,8 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
 		if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
 			add_role_attribute(&buf, _("Replication"));
 
-			if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
-				add_role_attribute(&buf, _("Bypass RLS"));
+		if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
+			add_role_attribute(&buf, _("Bypass RLS"));
 
 		conns = atoi(PQgetvalue(res, i, 6));
 		if (conns >= 0)
@@ -5155,14 +5154,14 @@ listCollations(const char *pattern, bool verbose, bool showSystem)
 					  gettext_noop("Schema"),
 					  gettext_noop("Name"));
 
-		appendPQExpBuffer(&buf,
-						  "  CASE c.collprovider "
-						  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
-						  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
-						  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
-						  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
-						  "END AS \"%s\",\n",
-						  gettext_noop("Provider"));
+	appendPQExpBuffer(&buf,
+					  "  CASE c.collprovider "
+					  "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
+					  "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
+					  "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
+					  "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
+					  "END AS \"%s\",\n",
+					  gettext_noop("Provider"));
 
 	appendPQExpBuffer(&buf,
 					  "  c.collcollate AS \"%s\",\n"
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ--





^ permalink  raw  reply  [nested|flat] 55+ messages in thread


end of thread, other threads:[~2026-06-29 14:56 UTC | newest]

Thread overview: 55+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2021-02-18 03:09 A qsort template Thomas Munro <[email protected]>
2021-02-18 06:02 ` Andres Freund <[email protected]>
2021-03-02 21:25 ` Daniel Gustafsson <[email protected]>
2021-03-03 04:17   ` Thomas Munro <[email protected]>
2021-03-11 18:58     ` Andres Freund <[email protected]>
2021-03-13 02:49       ` Thomas Munro <[email protected]>
2021-03-14 02:35         ` Thomas Munro <[email protected]>
2021-03-14 04:06           ` Zhihong Yu <[email protected]>
2021-03-15 00:09             ` Thomas Munro <[email protected]>
2021-06-16 05:54               ` Thomas Munro <[email protected]>
2021-06-16 20:18                 ` Zhihong Yu <[email protected]>
2021-06-16 21:54                   ` Thomas Munro <[email protected]>
2021-06-16 22:05                     ` Zhihong Yu <[email protected]>
2021-06-16 23:40                     ` Tom Lane <[email protected]>
2021-06-17 01:07                       ` Thomas Munro <[email protected]>
2021-06-17 01:14                         ` Tom Lane <[email protected]>
2021-06-17 01:20                           ` Thomas Munro <[email protected]>
2021-07-22 07:30                             ` Thomas Munro <[email protected]>
2021-06-28 19:13                 ` John Naylor <[email protected]>
2021-06-29 00:16                   ` Thomas Munro <[email protected]>
2021-06-29 01:11                     ` Thomas Munro <[email protected]>
2021-06-29 06:56                       ` Thomas Munro <[email protected]>
2021-06-29 16:40                         ` John Naylor <[email protected]>
2021-07-01 16:39                           ` John Naylor <[email protected]>
2021-07-01 22:09                             ` Thomas Munro <[email protected]>
2021-07-02 02:32                               ` John Naylor <[email protected]>
2021-07-04 04:27                                 ` Thomas Munro <[email protected]>
2021-07-15 11:49                                   ` vignesh C <[email protected]>
2021-07-15 11:57                                     ` John Naylor <[email protected]>
2021-07-30 00:34                                   ` John Naylor <[email protected]>
2021-07-30 07:10                                     ` Peter Geoghegan <[email protected]>
2021-08-02 00:01                                       ` Thomas Munro <[email protected]>
2021-07-30 11:47                                     ` Ranier Vilela <[email protected]>
2021-07-30 14:53                                       ` John Naylor <[email protected]>
2021-08-02 00:40                                     ` Thomas Munro <[email protected]>
2021-08-02 00:42                                       ` Thomas Munro <[email protected]>
2021-08-05 23:18                                       ` Peter Geoghegan <[email protected]>
2022-03-31 10:09                     ` John Naylor <[email protected]>
2022-03-31 21:42                       ` Thomas Munro <[email protected]>
2022-05-19 20:12 Re: A qsort template Justin Pryzby <[email protected]>
2022-05-19 22:24 ` Peter Geoghegan <[email protected]>
2022-05-19 22:43   ` Tom Lane <[email protected]>
2022-05-20 06:40     ` John Naylor <[email protected]>
2022-05-23 06:17       ` John Naylor <[email protected]>
2023-12-19 10:47 Re: partitioning and identity column Ashutosh Bapat <[email protected]>
2023-12-21 11:02 ` Re: partitioning and identity column Peter Eisentraut <[email protected]>
2024-01-09 14:10   ` Re: partitioning and identity column Ashutosh Bapat <[email protected]>
2026-05-01 19:38 [PATCH v2 4/4] run pgindent Nathan Bossart <[email protected]>
2026-05-01 19:38 [PATCH v2 4/4] run pgindent Nathan Bossart <[email protected]>
2026-05-06 21:43 [PATCH v3 4/4] run pgindent Nathan Bossart <[email protected]>
2026-05-06 21:43 [PATCH v3 4/4] run pgindent Nathan Bossart <[email protected]>
2026-06-11 14:15 [PATCH v4 4/4] run pgindent Nathan Bossart <[email protected]>
2026-06-11 14:15 [PATCH v4 4/4] run pgindent Nathan Bossart <[email protected]>
2026-06-29 14:56 [PATCH v5 4/4] run pgindent Nathan Bossart <[email protected]>
2026-06-29 14:56 [PATCH v5 4/4] run pgindent Nathan Bossart <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox