From 30e5b3bbe9ca9f3594816788fe4469798da04f64 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 4 Oct 2019 10:34:21 -0400
Subject: [PATCH] Fix bitshiftright()'s zero-padding some more.

Commit 5ac0d9360 failed to entirely fix bitshiftright's habit of
leaving one-bits in the pad space that should be all zeroes,
because in a moment of sheer brain fade I'd concluded that only
the code path used for not-a-multiple-of-8 shift distances needed
to be fixed.  Of course, a multiple-of-8 shift distance can also
cause the problem, so we need to forcibly zero the extra bits
in both cases.

Per bug #16037 from Alexander Lakhin.  As before, back-patch to all
supported branches.

Discussion: https://postgr.es/m/16037-1d1ebca564db54f4@postgresql.org
---
 src/backend/utils/adt/varbit.c    |  6 ++-
 src/test/regress/expected/bit.out | 66 +++++++++++++++++++++++++++++++
 src/test/regress/sql/bit.sql      |  6 +++
 3 files changed, 76 insertions(+), 2 deletions(-)

diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c
index 3b409d7d3b..ef57fdac78 100644
--- a/src/backend/utils/adt/varbit.c
+++ b/src/backend/utils/adt/varbit.c
@@ -1473,6 +1473,7 @@ bitshiftright(PG_FUNCTION_ARGS)
 		/* Special case: we can do a memcpy */
 		len = VARBITBYTES(arg) - byte_shift;
 		memcpy(r, p, len);
+		r += len;
 	}
 	else
 	{
@@ -1484,10 +1485,11 @@ bitshiftright(PG_FUNCTION_ARGS)
 			if ((++r) < VARBITEND(result))
 				*r = (*p << (BITS_PER_BYTE - ishift)) & BITMASK;
 		}
-		/* We may have shifted 1's into the pad bits, so fix that */
-		VARBIT_PAD_LAST(result, r);
 	}
 
+	/* We may have shifted 1's into the pad bits, so fix that */
+	VARBIT_PAD_LAST(result, r);
+
 	PG_RETURN_VARBIT_P(result);
 }
 
diff --git a/src/test/regress/expected/bit.out b/src/test/regress/expected/bit.out
index 07491bb84c..657b91a89f 100644
--- a/src/test/regress/expected/bit.out
+++ b/src/test/regress/expected/bit.out
@@ -499,6 +499,28 @@ SELECT b, b >> 1 AS bsr, b << 1 AS bsl
  0000000000000001 | 0000000000000000 | 0000000000000010
 (16 rows)
 
+SELECT b, b >> 8 AS bsr8, b << 8 AS bsl8
+       FROM BIT_SHIFT_TABLE ;
+        b         |       bsr8       |       bsl8       
+------------------+------------------+------------------
+ 1101100000000000 | 0000000011011000 | 0000000000000000
+ 0110110000000000 | 0000000001101100 | 0000000000000000
+ 0011011000000000 | 0000000000110110 | 0000000000000000
+ 0001101100000000 | 0000000000011011 | 0000000000000000
+ 0000110110000000 | 0000000000001101 | 1000000000000000
+ 0000011011000000 | 0000000000000110 | 1100000000000000
+ 0000001101100000 | 0000000000000011 | 0110000000000000
+ 0000000110110000 | 0000000000000001 | 1011000000000000
+ 0000000011011000 | 0000000000000000 | 1101100000000000
+ 0000000001101100 | 0000000000000000 | 0110110000000000
+ 0000000000110110 | 0000000000000000 | 0011011000000000
+ 0000000000011011 | 0000000000000000 | 0001101100000000
+ 0000000000001101 | 0000000000000000 | 0000110100000000
+ 0000000000000110 | 0000000000000000 | 0000011000000000
+ 0000000000000011 | 0000000000000000 | 0000001100000000
+ 0000000000000001 | 0000000000000000 | 0000000100000000
+(16 rows)
+
 SELECT b::bit(15), b::bit(15) >> 1 AS bsr, b::bit(15) << 1 AS bsl
        FROM BIT_SHIFT_TABLE ;
         b        |       bsr       |       bsl       
@@ -521,6 +543,28 @@ SELECT b::bit(15), b::bit(15) >> 1 AS bsr, b::bit(15) << 1 AS bsl
  000000000000000 | 000000000000000 | 000000000000000
 (16 rows)
 
+SELECT b::bit(15), b::bit(15) >> 8 AS bsr8, b::bit(15) << 8 AS bsl8
+       FROM BIT_SHIFT_TABLE ;
+        b        |      bsr8       |      bsl8       
+-----------------+-----------------+-----------------
+ 110110000000000 | 000000001101100 | 000000000000000
+ 011011000000000 | 000000000110110 | 000000000000000
+ 001101100000000 | 000000000011011 | 000000000000000
+ 000110110000000 | 000000000001101 | 000000000000000
+ 000011011000000 | 000000000000110 | 100000000000000
+ 000001101100000 | 000000000000011 | 110000000000000
+ 000000110110000 | 000000000000001 | 011000000000000
+ 000000011011000 | 000000000000000 | 101100000000000
+ 000000001101100 | 000000000000000 | 110110000000000
+ 000000000110110 | 000000000000000 | 011011000000000
+ 000000000011011 | 000000000000000 | 001101100000000
+ 000000000001101 | 000000000000000 | 000110100000000
+ 000000000000110 | 000000000000000 | 000011000000000
+ 000000000000011 | 000000000000000 | 000001100000000
+ 000000000000001 | 000000000000000 | 000000100000000
+ 000000000000000 | 000000000000000 | 000000000000000
+(16 rows)
+
 CREATE TABLE VARBIT_SHIFT_TABLE(v BIT VARYING(20));
 INSERT INTO VARBIT_SHIFT_TABLE VALUES (B'11011');
 INSERT INTO VARBIT_SHIFT_TABLE SELECT CAST(v || B'0' AS BIT VARYING(6)) >>1 FROM VARBIT_SHIFT_TABLE;
@@ -573,6 +617,28 @@ SELECT v, v >> 1 AS vsr, v << 1 AS vsl
  00000000000000011011 | 00000000000000001101 | 00000000000000110110
 (16 rows)
 
+SELECT v, v >> 8 AS vsr8, v << 8 AS vsl8
+       FROM VARBIT_SHIFT_TABLE ;
+          v           |         vsr8         |         vsl8         
+----------------------+----------------------+----------------------
+ 11011                | 00000                | 00000
+ 011011               | 000000               | 000000
+ 0011011              | 0000000              | 0000000
+ 00011011             | 00000000             | 00000000
+ 000011011            | 000000000            | 100000000
+ 0000011011           | 0000000000           | 1100000000
+ 00000011011          | 00000000000          | 01100000000
+ 000000011011         | 000000000000         | 101100000000
+ 0000000011011        | 0000000000000        | 1101100000000
+ 00000000011011       | 00000000000000       | 01101100000000
+ 000000000011011      | 000000000000000      | 001101100000000
+ 0000000000011011     | 0000000000000000     | 0001101100000000
+ 00000000000011011    | 00000000000000000    | 00001101100000000
+ 000000000000011011   | 000000000000000000   | 000001101100000000
+ 0000000000000011011  | 0000000000000000000  | 0000001101100000000
+ 00000000000000011011 | 00000000000000000000 | 00000001101100000000
+(16 rows)
+
 DROP TABLE BIT_SHIFT_TABLE;
 DROP TABLE VARBIT_SHIFT_TABLE;
 -- Get/Set bit
diff --git a/src/test/regress/sql/bit.sql b/src/test/regress/sql/bit.sql
index cf2460b212..a740c28e2d 100644
--- a/src/test/regress/sql/bit.sql
+++ b/src/test/regress/sql/bit.sql
@@ -170,8 +170,12 @@ SELECT POSITION(B'1101' IN b),
        FROM BIT_SHIFT_TABLE ;
 SELECT b, b >> 1 AS bsr, b << 1 AS bsl
        FROM BIT_SHIFT_TABLE ;
+SELECT b, b >> 8 AS bsr8, b << 8 AS bsl8
+       FROM BIT_SHIFT_TABLE ;
 SELECT b::bit(15), b::bit(15) >> 1 AS bsr, b::bit(15) << 1 AS bsl
        FROM BIT_SHIFT_TABLE ;
+SELECT b::bit(15), b::bit(15) >> 8 AS bsr8, b::bit(15) << 8 AS bsl8
+       FROM BIT_SHIFT_TABLE ;
 
 
 CREATE TABLE VARBIT_SHIFT_TABLE(v BIT VARYING(20));
@@ -186,6 +190,8 @@ SELECT POSITION(B'1101' IN v),
        FROM VARBIT_SHIFT_TABLE ;
 SELECT v, v >> 1 AS vsr, v << 1 AS vsl
        FROM VARBIT_SHIFT_TABLE ;
+SELECT v, v >> 8 AS vsr8, v << 8 AS vsl8
+       FROM VARBIT_SHIFT_TABLE ;
 
 DROP TABLE BIT_SHIFT_TABLE;
 DROP TABLE VARBIT_SHIFT_TABLE;
-- 
2.40.0