From 250279b86de5e56d02afba2f004717d42790cc6b Mon Sep 17 00:00:00 2001
From: Aleksander Alekseev <aleksander@timescale.com>
Date: Thu, 18 Jul 2024 12:59:40 +0300
Subject: [PATCH v4] Add crc32(bytea) & crc32c(bytea)

Per user requests.

Aleksander Alekseev, reviewed by Nathan Bossart
Discussion: https://postgr.es/m/CAJ7c6TNMTGnqnG=yXXUQh9E88JDckmR45H2Q+=ucaCLMOW1QQw@mail.gmail.com
---
 doc/src/sgml/func.sgml                   | 34 ++++++++++++++
 src/backend/utils/hash/pg_crc.c          | 56 ++++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |  8 ++++
 src/test/regress/expected/opr_sanity.out |  2 +
 src/test/regress/expected/strings.out    | 27 ++++++++++++
 src/test/regress/sql/strings.sql         |  9 ++++
 6 files changed, 136 insertions(+)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 0f7154b76a..a0541d897f 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -4490,6 +4490,40 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>crc32</primary>
+        </indexterm>
+        <function>crc32</function> ( <type>bytea</type> )
+        <returnvalue>text</returnvalue>
+       </para>
+       <para>
+        Computes the CRC32 value of the binary string.
+       </para>
+       <para>
+        <literal>crc32('PostgreSQL'::bytea)</literal>
+        <returnvalue>cb97b83b</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>crc32c</primary>
+        </indexterm>
+        <function>crc32c</function> ( <type>bytea</type> )
+        <returnvalue>text</returnvalue>
+       </para>
+       <para>
+        Computes the CRC32C value of the binary string.
+       </para>
+       <para>
+        <literal>crc32c('PostgreSQL'::bytea)</literal>
+        <returnvalue>a7c16abd</returnvalue>
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
diff --git a/src/backend/utils/hash/pg_crc.c b/src/backend/utils/hash/pg_crc.c
index 3595938dc4..6965a7f8d5 100644
--- a/src/backend/utils/hash/pg_crc.c
+++ b/src/backend/utils/hash/pg_crc.c
@@ -18,8 +18,12 @@
  */
 
 #include "c.h"
+#include "postgres.h"
 
+#include "port/pg_crc32c.h"
+#include "utils/builtins.h"
 #include "utils/pg_crc.h"
+#include "varatt.h"
 
 /*
  * Lookup table for calculating CRC-32 using Sarwate's algorithm.
@@ -95,3 +99,55 @@ const uint32 pg_crc32_table[256] = {
 	0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
 	0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
 };
+
+/*
+ * Calculate CRC32 of a bytea value and return it as text.
+ */
+Datum
+crc32_bytea(PG_FUNCTION_ARGS)
+{
+	bytea	   *in = PG_GETARG_BYTEA_PP(0);
+	size_t		len;
+	pg_crc32	crc;
+	char 		result[16];
+
+	/* calculate the length of the buffer using varlena metadata */
+	len = VARSIZE_ANY_EXHDR(in);
+
+	/* get the crc value */
+	INIT_TRADITIONAL_CRC32(crc);
+	COMP_TRADITIONAL_CRC32(crc, VARDATA_ANY(in), len);
+	FIN_TRADITIONAL_CRC32(crc);
+
+	/* format the hex string */
+	snprintf(result, sizeof(result), "%08x", (uint32)crc);
+
+	/* convert to text and return it */
+	PG_RETURN_TEXT_P(cstring_to_text(result));
+}
+
+/*
+ * Calculate CRC32C of a bytea value and return it as text.
+ */
+Datum
+crc32c_bytea(PG_FUNCTION_ARGS)
+{
+	bytea	   *in = PG_GETARG_BYTEA_PP(0);
+	size_t		len;
+	pg_crc32c	crc;
+	char 		result[16];
+
+	/* calculate the length of the buffer using varlena metadata */
+	len = VARSIZE_ANY_EXHDR(in);
+
+	/* get the crc value */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc, VARDATA_ANY(in), len);
+	FIN_CRC32C(crc);
+
+	/* format the hex string */
+	snprintf(result, sizeof(result), "%08x", (uint32)crc);
+
+	/* convert to text and return it */
+	PG_RETURN_TEXT_P(cstring_to_text(result));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d36f6001bb..c31b1c72ec 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7743,6 +7743,14 @@
   proname => 'system', provolatile => 'v', prorettype => 'tsm_handler',
   proargtypes => 'internal', prosrc => 'tsm_system_handler' },
 
+# non-cryptographic
+{ oid => '8571', descr => 'CRC32 value',
+  proname => 'crc32', proleakproof => 't', prorettype => 'text',
+  proargtypes => 'bytea', prosrc => 'crc32_bytea' },
+{ oid => '8572', descr => 'CRC32C value',
+  proname => 'crc32c', proleakproof => 't', prorettype => 'text',
+  proargtypes => 'bytea', prosrc => 'crc32c_bytea' },
+
 # cryptographic
 { oid => '2311', descr => 'MD5 hash',
   proname => 'md5', proleakproof => 't', prorettype => 'text',
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 9d047b21b8..0d734169f1 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -874,6 +874,8 @@ xid8ne(xid8,xid8)
 xid8cmp(xid8,xid8)
 uuid_extract_timestamp(uuid)
 uuid_extract_version(uuid)
+crc32(bytea)
+crc32c(bytea)
 -- restore normal output mode
 \a\t
 -- List of functions used by libpq's fe-lobj.c
diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out
index 52b69a107f..c795df3056 100644
--- a/src/test/regress/expected/strings.out
+++ b/src/test/regress/expected/strings.out
@@ -2255,6 +2255,33 @@ SELECT sha512('The quick brown fox jumps over the lazy dog.');
  \x91ea1245f20d46ae9a037a989f54f1f790f0a47607eeb8a14d12890cea77a1bbc6c7ed9cf205e67b7f2b8fd4c7dfd3a7a8617e45f3c463d481c7e586c39ac1ed
 (1 row)
 
+--
+-- CRC32
+--
+SELECT crc32(''::bytea);
+  crc32   
+----------
+ 00000000
+(1 row)
+
+SELECT crc32('The quick brown fox jumps over the lazy dog.'::bytea);
+  crc32   
+----------
+ 519025e9
+(1 row)
+
+SELECT crc32c(''::bytea);
+  crc32c  
+----------
+ 00000000
+(1 row)
+
+SELECT crc32c('The quick brown fox jumps over the lazy dog.'::bytea);
+  crc32c  
+----------
+ 190097b3
+(1 row)
+
 --
 -- encode/decode
 --
diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql
index 3959678992..f8cd621d9a 100644
--- a/src/test/regress/sql/strings.sql
+++ b/src/test/regress/sql/strings.sql
@@ -719,6 +719,15 @@ SELECT sha384('The quick brown fox jumps over the lazy dog.');
 SELECT sha512('');
 SELECT sha512('The quick brown fox jumps over the lazy dog.');
 
+--
+-- CRC32
+--
+SELECT crc32(''::bytea);
+SELECT crc32('The quick brown fox jumps over the lazy dog.'::bytea);
+
+SELECT crc32c(''::bytea);
+SELECT crc32c('The quick brown fox jumps over the lazy dog.'::bytea);
+
 --
 -- encode/decode
 --
-- 
2.45.2

