From 0b60432a84d63a7fccaae0fe123a0aa2ae67493b Mon Sep 17 00:00:00 2001
From: Erik Wienhold <ewie@ewie.name>
Date: Sun, 18 Feb 2024 17:33:35 +0100
Subject: [PATCH] Add to_regtypmod() for benchmarking against parse_type()

---
 src/backend/utils/adt/regproc.c       | 18 ++++++++++
 src/include/catalog/pg_proc.dat       |  3 ++
 src/test/regress/expected/regproc.out | 51 +++++++++++++++++++++++++++
 src/test/regress/sql/regproc.sql      | 26 ++++++++++++++
 4 files changed, 98 insertions(+)

diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index 4fee27a139..6285dc7192 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -2067,3 +2067,21 @@ parse_type(PG_FUNCTION_ARGS)
 	return HeapTupleGetDatum(rettuple);
 #undef PARSE_TYPE_STRING_COLS
 }
+
+Datum
+to_regtypmod(PG_FUNCTION_ARGS)
+{
+	const char *type;            /* the type string we want to resolve */
+	Oid         typid;           /* the resolved type oid */
+	int32       typmod;          /* the resolved type modifier */
+
+	type = text_to_cstring(PG_GETARG_TEXT_PP(0));
+
+	/*
+	 * Parse type-name argument to obtain type OID and encoded typmod. We don't
+	 * need to handle parseTypeString failure, just let the error be raised.
+	 */
+	(void) parseTypeString(type, &typid, &typmod, NULL);
+
+	PG_RETURN_INT32(typmod);
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6cad101bca..befeaeec37 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -2190,6 +2190,9 @@
   proargtypes => 'text', proallargtypes => '{text,oid,int4}',
   proargmodes => '{i,o,o}', proargnames => '{typname,typid,typmod}',
   prosrc => 'parse_type' },
+{ oid => '8402', descr => 'covert type name to typmod',
+  proname => 'to_regtypmod', provolatile => 's', prorettype => 'int4',
+  proargtypes => 'text', prosrc => 'to_regtypmod' },
 { oid => '1084', descr => 'I/O',
   proname => 'date_in', provolatile => 's', prorettype => 'date',
   proargtypes => 'cstring', prosrc => 'date_in' },
diff --git a/src/test/regress/expected/regproc.out b/src/test/regress/expected/regproc.out
index 033f6a81a5..c548b320c3 100644
--- a/src/test/regress/expected/regproc.out
+++ b/src/test/regress/expected/regproc.out
@@ -595,3 +595,54 @@ SELECT format_type(typid, typmod) FROM p;
  character varying(128)
 (7 rows)
 
+-- Test to_regtypmod
+SELECT * FROM to_regtypmod('text');
+ to_regtypmod 
+--------------
+           -1
+(1 row)
+
+SELECT * FROM to_regtypmod(NULL);
+ to_regtypmod 
+--------------
+             
+(1 row)
+
+-- Test to_regtypmod errors
+SELECT to_regtypmod('nonesuch'); -- error expected
+ERROR:  type "nonesuch" does not exist
+SELECT to_regtypmod('interval nonesuch'); -- grammar error expected
+ERROR:  syntax error at or near "nonesuch"
+LINE 1: SELECT to_regtypmod('interval nonesuch');
+                 ^
+CONTEXT:  invalid type name "interval nonesuch"
+SELECT to_regtypmod('year(4)'); -- grammar error expected
+ERROR:  type "year" does not exist
+-- Test to_regtypmod with various aliases and grammar-based types
+WITH s(s) AS (
+    SELECT * FROM unnest(ARRAY[
+        'timestamp(4)',
+        'interval(0)',
+        'interval second(0)',
+        'timestamptz',
+        'timestamptz(6)',
+        'varchar',
+        'varchar(128)'
+    ])
+),
+p(typid, typmod) AS (
+    SELECT to_regtype(s), to_regtypmod(s)
+      FROM s
+)
+SELECT format_type(typid, typmod) FROM p;
+          format_type           
+--------------------------------
+ timestamp(4) without time zone
+ interval(0)
+ interval second(0)
+ timestamp with time zone
+ timestamp(6) with time zone
+ character varying
+ character varying(128)
+(7 rows)
+
diff --git a/src/test/regress/sql/regproc.sql b/src/test/regress/sql/regproc.sql
index 489463aa9e..ac19737012 100644
--- a/src/test/regress/sql/regproc.sql
+++ b/src/test/regress/sql/regproc.sql
@@ -173,3 +173,29 @@ p(typid, typmod) AS (
 )
 SELECT format_type(typid, typmod) FROM p;
 
+-- Test to_regtypmod
+SELECT * FROM to_regtypmod('text');
+SELECT * FROM to_regtypmod(NULL);
+
+-- Test to_regtypmod errors
+SELECT to_regtypmod('nonesuch'); -- error expected
+SELECT to_regtypmod('interval nonesuch'); -- grammar error expected
+SELECT to_regtypmod('year(4)'); -- grammar error expected
+
+-- Test to_regtypmod with various aliases and grammar-based types
+WITH s(s) AS (
+    SELECT * FROM unnest(ARRAY[
+        'timestamp(4)',
+        'interval(0)',
+        'interval second(0)',
+        'timestamptz',
+        'timestamptz(6)',
+        'varchar',
+        'varchar(128)'
+    ])
+),
+p(typid, typmod) AS (
+    SELECT to_regtype(s), to_regtypmod(s)
+      FROM s
+)
+SELECT format_type(typid, typmod) FROM p;
-- 
2.43.2

