From f7d7dade8afe19847510efe44be191c35e1ce26c Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Tue, 7 Feb 2012 12:41:42 -0500
Subject: [PATCH] Add a transform function for varbit typmod coercisions.

This enables ALTER TABLE to skip table and index rebuilds when the
new type is unconstraint varbit, or when the allowable number of bits
is not decreasing.

Noah Misch, with review and a fix for an OID collision by me.
---
 src/backend/utils/adt/varbit.c   | 35 ++++++++++++++++++++++++++++++++
 src/include/catalog/catversion.h |  2 +-
 src/include/catalog/pg_proc.h    |  4 +++-
 src/include/utils/varbit.h       |  1 +
 4 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c
index adb08369ed2..1227e5a5082 100644
--- a/src/backend/utils/adt/varbit.c
+++ b/src/backend/utils/adt/varbit.c
@@ -18,6 +18,8 @@
 
 #include "access/htup.h"
 #include "libpq/pqformat.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_clause.h"
 #include "utils/array.h"
 #include "utils/varbit.h"
 
@@ -645,6 +647,39 @@ varbit_send(PG_FUNCTION_ARGS)
 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
+/*
+ * varbit_transform()
+ * Flatten calls to our length coercion function that leave the new maximum
+ * length >= the previous maximum length.  We ignore the isExplicit argument,
+ * which only affects truncation.
+ */
+Datum
+varbit_transform(PG_FUNCTION_ARGS)
+{
+	FuncExpr   *expr = (FuncExpr *) PG_GETARG_POINTER(0);
+	Node	   *typmod;
+	Node	   *ret = NULL;
+
+	if (!IsA(expr, FuncExpr))
+		PG_RETURN_POINTER(ret);
+
+	Assert(list_length(expr->args) == 3);
+	typmod = lsecond(expr->args);
+
+	if (IsA(typmod, Const))
+	{
+		Node	   *source = linitial(expr->args);
+		int32		new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
+		int32		old_max = exprTypmod(source);
+		int32		new_max = new_typmod;
+
+		if (new_max <= 0 || (old_max >= 0 && old_max <= new_max))
+			ret = relabel_to_typmod(source, new_typmod);
+	}
+
+	PG_RETURN_POINTER(ret);
+}
+
 /*
  * varbit()
  * Converts a varbit() type to a specific internal length.
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index a748c5e623e..ae4e5f5ae6b 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201202071
+#define CATALOG_VERSION_NO	201202072
 
 #endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 006ed171824..f8d01fbba49 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2009,7 +2009,9 @@ DESCR("convert bitstring to int4");
 
 DATA(insert OID = 1685 (  bit			   PGNSP PGUID 12 1 0 0 0 f f f t f i 3 0 1560 "1560 23 16" _null_ _null_ _null_ _null_ bit _null_ _null_ _null_ ));
 DESCR("adjust bit() to typmod length");
-DATA(insert OID = 1687 (  varbit		   PGNSP PGUID 12 1 0 0 0 f f f t f i 3 0 1562 "1562 23 16" _null_ _null_ _null_ _null_ varbit _null_ _null_ _null_ ));
+DATA(insert OID = 3158 ( varbit_transform  PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ varbit_transform _null_ _null_ _null_ ));
+DESCR("transform a varbit length coercion");
+DATA(insert OID = 1687 (  varbit		   PGNSP PGUID 12 1 0 0 3158 f f f t f i 3 0 1562 "1562 23 16" _null_ _null_ _null_ _null_ varbit _null_ _null_ _null_ ));
 DESCR("adjust varbit() to typmod length");
 
 DATA(insert OID = 1698 (  position		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "1560 1560" _null_ _null_ _null_ _null_ bitposition _null_ _null_ _null_ ));
diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h
index f268ca5853d..52dca8b5d29 100644
--- a/src/include/utils/varbit.h
+++ b/src/include/utils/varbit.h
@@ -72,6 +72,7 @@ extern Datum varbit_send(PG_FUNCTION_ARGS);
 extern Datum varbittypmodin(PG_FUNCTION_ARGS);
 extern Datum varbittypmodout(PG_FUNCTION_ARGS);
 extern Datum bit(PG_FUNCTION_ARGS);
+extern Datum varbit_transform(PG_FUNCTION_ARGS);
 extern Datum varbit(PG_FUNCTION_ARGS);
 extern Datum biteq(PG_FUNCTION_ARGS);
 extern Datum bitne(PG_FUNCTION_ARGS);
-- 
GitLab