2025年5月7日(水) 10:47 Nathan Bossart <nathandboss...@gmail.com>:
>
> On Wed, May 07, 2025 at 09:18:28AM +0900, Ian Lawrence Barwick wrote:
> > Hah, I put together a patch to implement just that a while back, but
> > then concluded
> > for some reason that it would likely be rejected so saved myself the
> > humiliation of
> > submitting it...
> >
> > Attaching patch for reference - it's from mid 2020 so no longer applies. 
> > I'll
> > have a crack at cleaning it up if I get a chance.
>
> Oh, thanks!

Version which applies/builds against current HEAD attached. I haven't
yet had a chance to look at the code beyond fixing it, however.

Regards

Ian Barwick
commit 2e20fea416ae6b107ffbe952a951efb145f13ec7
Author: Ian Barwick <barw...@gmail.com>
Date:   Wed May 7 17:50:21 2025 +0900

    Rebase to HEAD

diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 09309ba0390..cab8f0fda93 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4737,6 +4737,10 @@ INSERT INTO mytable VALUES(-1);  -- fails
     <primary>regconfig</primary>
    </indexterm>
 
+   <indexterm zone="datatype-oid">
+    <primary>regdatabase</primary>
+   </indexterm>
+
    <indexterm zone="datatype-oid">
     <primary>regdictionary</primary>
    </indexterm>
@@ -4878,6 +4882,13 @@ SELECT * FROM pg_attribute
         <entry><literal>english</literal></entry>
        </row>
 
+       <row>
+        <entry><type>regdatabase</type></entry>
+        <entry><structname>pg_database</structname></entry>
+        <entry>database name</entry>
+        <entry><literal>template1</literal></entry>
+       </row>
+
        <row>
         <entry><type>regdictionary</type></entry>
         <entry><structname>pg_ts_dict</structname></entry>
@@ -5049,8 +5060,8 @@ WHERE ...
     be dropped without first removing the default expression.  The
     alternative of <literal>nextval('my_seq'::text)</literal> does not
     create a dependency.
-    (<type>regrole</type> is an exception to this property. Constants of this
-    type are not allowed in stored expressions.)
+    (<type>regrole</type> and <type>regdatabase</type> are the only exceptions for the property.
+    Constants of this type are not allowed in such expressions.)
    </para>
 
    <para>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index af3d056b992..0323667485e 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -21940,6 +21940,24 @@ SELECT NULLIF(value, '(none)') ...
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>to_regdatabase</primary>
+        </indexterm>
+        <function>to_regdatabase</function> ( <type>text</type> )
+        <returnvalue>regdatabase</returnvalue>
+       </para>
+       <para>
+        Translates a textual database name to its OID.  A similar result is
+        obtained by casting the string to type <type>regdatabase</type> (see
+        <xref linkend="datatype-oid"/>); however, this function will return
+        <literal>NULL</literal> rather than throwing an error if the name is
+        not found.  Also unlike the cast, this does not accept
+        a numeric OID as input.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cb8e4f5c48a..34689dd8845 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -1103,6 +1103,7 @@ psql --username=postgres --file=script.sql postgres
    <simplelist>
     <member><type>regcollation</type></member>
     <member><type>regconfig</type></member>
+    <member><type>regdatabase</type></member>
     <member><type>regdictionary</type></member>
     <member><type>regnamespace</type></member>
     <member><type>regoper</type></member>
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 6db864892d0..fc8638c1b61 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -109,6 +109,8 @@ static const struct typinfo TypInfo[] = {
 	F_REGROLEIN, F_REGROLEOUT},
 	{"regnamespace", REGNAMESPACEOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
 	F_REGNAMESPACEIN, F_REGNAMESPACEOUT},
+	{"regdatabase", REGDATABASEOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
+	F_REGDATABASEIN, F_REGDATABASEOUT},
 	{"text", TEXTOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, DEFAULT_COLLATION_OID,
 	F_TEXTIN, F_TEXTOUT},
 	{"oid", OIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 18316a3968b..ca9ac41b92c 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1841,9 +1841,15 @@ find_expr_references_walker(Node *node,
 					break;
 
 					/*
-					 * Dependencies for regrole should be shared among all
-					 * databases, so explicitly inhibit to have dependencies.
+					 * Explicitly prohibit dependencies for cluster-wide shared objects
 					 */
+				case REGDATABASEOID:
+					ereport(ERROR,
+							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							 errmsg("constant of the type %s cannot be used here",
+									"regdatabase")));
+					break;
+
 				case REGROLEOID:
 					ereport(ERROR,
 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index 5ee608a2b39..7c010e82008 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -30,6 +30,7 @@
 #include "catalog/pg_ts_config.h"
 #include "catalog/pg_ts_dict.h"
 #include "catalog/pg_type.h"
+#include "commands/dbcommands.h"
 #include "lib/stringinfo.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
@@ -1763,6 +1764,134 @@ regnamespacesend(PG_FUNCTION_ARGS)
 	return oidsend(fcinfo);
 }
 
+
+/*
+ * regdatabasein		- converts "datname" to database OID
+ *
+ * We also accept a numeric OID, for symmetry with the output routine.
+ *
+ * '-' signifies unknown (OID 0).  In all other cases, the input must
+ * match an existing pg_database entry.
+ */
+Datum
+regdatabasein(PG_FUNCTION_ARGS)
+{
+	char	   *dat_name_or_oid = PG_GETARG_CSTRING(0);
+	Node	   *escontext = fcinfo->context;
+	Oid			result;
+	List	   *names;
+
+	/* '-' ? */
+	if (strcmp(dat_name_or_oid, "-") == 0)
+		PG_RETURN_OID(InvalidOid);
+
+	/* Numeric OID? */
+	if (dat_name_or_oid[0] >= '0' &&
+		dat_name_or_oid[0] <= '9' &&
+		strspn(dat_name_or_oid, "0123456789") == strlen(dat_name_or_oid))
+	{
+		result = DatumGetObjectId(DirectFunctionCall1(oidin,
+													  CStringGetDatum(dat_name_or_oid)));
+		PG_RETURN_OID(result);
+	}
+
+	/* The rest of this wouldn't work in bootstrap mode */
+	if (IsBootstrapProcessingMode())
+		elog(ERROR, "regdatabase values must be OIDs in bootstrap mode");
+
+	/* Normal case: see if the name matches any pg_database entry. */
+	names = stringToQualifiedNameList(dat_name_or_oid, escontext);
+
+	if (list_length(names) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_NAME),
+				 errmsg("invalid name syntax")));
+
+	result = get_database_oid(strVal(linitial(names)), false);
+
+	PG_RETURN_OID(result);
+}
+
+/*
+ * to_regdatabase		- converts "datname" to database OID
+ *
+ * If the name is not found, we return NULL.
+ */
+Datum
+to_regdatabase(PG_FUNCTION_ARGS)
+{
+	char	   *dat_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
+	Node	   *escontext = fcinfo->context;
+	Oid			result;
+	List	   *names;
+
+	names = stringToQualifiedNameList(dat_name, escontext);
+
+	if (list_length(names) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_NAME),
+				 errmsg("invalid name syntax")));
+
+	result = get_database_oid(strVal(linitial(names)), true);
+
+	if (OidIsValid(result))
+		PG_RETURN_OID(result);
+	else
+		PG_RETURN_NULL();
+}
+
+/*
+ * regdatabaseout		- converts database OID to "dat_name"
+ */
+Datum
+regdatabaseout(PG_FUNCTION_ARGS)
+{
+	Oid			datid = PG_GETARG_OID(0);
+	char	   *result;
+
+	if (datid == InvalidOid)
+	{
+		result = pstrdup("-");
+		PG_RETURN_CSTRING(result);
+	}
+
+	result = get_database_name(datid);
+
+	if (result)
+	{
+		/* pstrdup is not really necessary, but it avoids a compiler warning */
+		result = pstrdup(quote_identifier(result));
+	}
+	else
+	{
+		/* If OID doesn't match any database, return it numerically */
+		result = (char *) palloc(NAMEDATALEN);
+		snprintf(result, NAMEDATALEN, "%u", datid);
+	}
+
+	PG_RETURN_CSTRING(result);
+}
+
+/*
+ *		regdatabaserecv	- converts external binary format to regdatabase
+ */
+Datum
+regdatabaserecv(PG_FUNCTION_ARGS)
+{
+	/* Exactly the same as oidrecv, so share code */
+	return oidrecv(fcinfo);
+}
+
+/*
+ *		regdatabasesend		- converts regdatabase to binary format
+ */
+Datum
+regdatabasesend(PG_FUNCTION_ARGS)
+{
+	/* Exactly the same as oidsend, so share code */
+	return oidsend(fcinfo);
+}
+
 /*
  * text_regclass: convert text to regclass
  *
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index a96b1b9c0bc..7e439af619e 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -4620,6 +4620,7 @@ convert_to_scalar(Datum value, Oid valuetypid, Oid collid, double *scaledvalue,
 		case REGDICTIONARYOID:
 		case REGROLEOID:
 		case REGNAMESPACEOID:
+		case REGDATABASEOID:
 			*scaledvalue = convert_numeric_to_scalar(value, valuetypid,
 													 &failure);
 			*scaledlobound = convert_numeric_to_scalar(lobound, boundstypid,
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 6e3cad454c0..0c3459f730e 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -317,6 +317,7 @@ GetCCHashEqFuncs(Oid keytype, CCHashFN *hashfunc, RegProcedure *eqfunc, CCFastEq
 		case REGDICTIONARYOID:
 		case REGROLEOID:
 		case REGNAMESPACEOID:
+		case REGDATABASEOID:
 			*hashfunc = int4hashfast;
 			*fasteqfunc = int4eqfast;
 			*eqfunc = F_OIDEQ;
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 940fc77fc2e..79a5e66f35e 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -168,6 +168,7 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		/* pg_class.oid is preserved, so 'regclass' is OK */
 		"           'regcollation', "
 		"           'regconfig', "
+		"           'regdatabase', "
 		"           'regdictionary', "
 		"           'regnamespace', "
 		"           'regoper', "
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index ab46be606f0..fbfd669587f 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -281,6 +281,20 @@
   castcontext => 'a', castmethod => 'f' },
 { castsource => 'regnamespace', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
+{ castsource => 'oid', casttarget => 'regdatabase', castfunc => '0',
+  castcontext => 'i', castmethod => 'b' },
+{ castsource => 'regdatabase', casttarget => 'oid', castfunc => '0',
+  castcontext => 'i', castmethod => 'b' },
+{ castsource => 'int8', casttarget => 'regdatabase', castfunc => 'oid',
+  castcontext => 'i', castmethod => 'f' },
+{ castsource => 'int2', casttarget => 'regdatabase', castfunc => 'int4(int2)',
+  castcontext => 'i', castmethod => 'f' },
+{ castsource => 'int4', casttarget => 'regdatabase', castfunc => '0',
+  castcontext => 'i', castmethod => 'b' },
+{ castsource => 'regdatabase', casttarget => 'int8', castfunc => 'int8(oid)',
+  castcontext => 'a', castmethod => 'f' },
+{ castsource => 'regdatabase', casttarget => 'int4', castfunc => '0',
+  castcontext => 'a', castmethod => 'b' },
 
 # String category
 { castsource => 'text', casttarget => 'bpchar', castfunc => '0',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 62beb71da28..9564a804627 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7466,6 +7466,17 @@
   proargnames => '{value,type_name,message,detail,hint,sql_error_code}',
   prosrc => 'pg_input_error_info' },
 
+{ oid => '8602', descr => 'I/O',
+  proname => 'regdatabasein', provolatile => 's', prorettype => 'regdatabase',
+  proargtypes => 'cstring', prosrc => 'regdatabasein' },
+{ oid => '8603', descr => 'I/O',
+  proname => 'regdatabaseout', provolatile => 's', prorettype => 'cstring',
+  proargtypes => 'regdatabase', prosrc => 'regdatabaseout' },
+{ oid => '8604', descr => 'convert database name to regdatabase',
+  proname => 'to_regdatabase', provolatile => 's',
+  prorettype => 'regdatabase', proargtypes => 'text',
+  prosrc => 'to_regdatabase' },
+
 { oid => '1268',
   descr => 'parse qualified identifier to array of identifiers',
   proname => 'parse_ident', prorettype => '_text', proargtypes => 'text bool',
@@ -8299,7 +8310,6 @@
 { oid => '2455', descr => 'I/O',
   proname => 'regtypesend', prorettype => 'bytea', proargtypes => 'regtype',
   prosrc => 'regtypesend' },
-
 { oid => '4094', descr => 'I/O',
   proname => 'regrolerecv', prorettype => 'regrole', proargtypes => 'internal',
   prosrc => 'regrolerecv' },
@@ -8312,6 +8322,12 @@
 { oid => '4088', descr => 'I/O',
   proname => 'regnamespacesend', prorettype => 'bytea',
   proargtypes => 'regnamespace', prosrc => 'regnamespacesend' },
+{ oid => '8605', descr => 'I/O',
+  proname => 'regdatabaserecv', prorettype => 'regdatabase',
+  proargtypes => 'internal', prosrc => 'regdatabaserecv' },
+{ oid => '8606', descr => 'I/O',
+  proname => 'regdatabasesend', prorettype => 'bytea',
+  proargtypes => 'regdatabase', prosrc => 'regdatabasesend' },
 { oid => '2456', descr => 'I/O',
   proname => 'bit_recv', prorettype => 'bit',
   proargtypes => 'internal oid int4', prosrc => 'bit_recv' },
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 6dca77e0a22..2e75c9895bf 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -399,6 +399,11 @@
   typinput => 'regnamespacein', typoutput => 'regnamespaceout',
   typreceive => 'regnamespacerecv', typsend => 'regnamespacesend',
   typalign => 'i' },
+{ oid => '8600', array_type_oid => '8601', descr => 'registered database',
+  typname => 'regdatabase', typlen => '4', typbyval => 't', typcategory => 'N',
+  typinput => 'regdatabasein', typoutput => 'regdatabaseout',
+  typreceive => 'regdatabaserecv', typsend => 'regdatabasesend',
+  typalign => 'i' },
 
 # uuid
 { oid => '2950', array_type_oid => '2951', descr => 'UUID',
diff --git a/src/test/regress/expected/regproc.out b/src/test/regress/expected/regproc.out
index 97b917502ca..5c1bb1c7354 100644
--- a/src/test/regress/expected/regproc.out
+++ b/src/test/regress/expected/regproc.out
@@ -46,6 +46,12 @@ SELECT regcollation('"POSIX"');
  "POSIX"
 (1 row)
 
+SELECT regdatabase('template1');
+ regdatabase 
+-------------
+ template1
+(1 row)
+
 SELECT to_regoper('||/');
  to_regoper 
 ------------
@@ -88,6 +94,12 @@ SELECT to_regcollation('"POSIX"');
  "POSIX"
 (1 row)
 
+SELECT to_regdatabase('template1');
+ to_regdatabase 
+----------------
+ template1
+(1 row)
+
 -- with schemaname
 SELECT regoper('pg_catalog.||/');
  regoper 
@@ -243,6 +255,10 @@ SELECT regtype('int3');
 ERROR:  type "int3" does not exist
 LINE 1: SELECT regtype('int3');
                        ^
+SELECT regdatabase('notadatabase');
+ERROR:  database "notadatabase" does not exist
+LINE 1: SELECT regdatabase('notadatabase');
+                           ^
 -- with schemaname
 SELECT regoper('ng_catalog.||/');
 ERROR:  operator does not exist: ng_catalog.||/
@@ -349,6 +365,12 @@ SELECT to_regcollation('notacollation');
  
 (1 row)
 
+SELECT to_regdatabase('notadatabase');
+ to_regdatabase 
+----------------
+ 
+(1 row)
+
 -- with schemaname
 SELECT to_regoper('ng_catalog.||/');
  to_regoper 
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index dd0c52ab08b..1e4860547ed 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -738,7 +738,7 @@ SELECT oid, typname, typtype, typelem, typarray
     typtype NOT IN ('p', 'c') AND
     -- These reg* types cannot be pg_upgraded, so discard them.
     oid != ALL(ARRAY['regproc', 'regprocedure', 'regoper',
-                     'regoperator', 'regconfig', 'regdictionary',
+                     'regoperator', 'regconfig', 'regdatabase', 'regdictionary',
                      'regnamespace', 'regcollation']::regtype[]) AND
     -- Discard types that do not accept input values as these cannot be
     -- tested easily.
diff --git a/src/test/regress/sql/regproc.sql b/src/test/regress/sql/regproc.sql
index 232289ac398..a62e84890d8 100644
--- a/src/test/regress/sql/regproc.sql
+++ b/src/test/regress/sql/regproc.sql
@@ -15,6 +15,7 @@ SELECT regprocedure('abs(numeric)');
 SELECT regclass('pg_class');
 SELECT regtype('int4');
 SELECT regcollation('"POSIX"');
+SELECT regdatabase('template1');
 
 SELECT to_regoper('||/');
 SELECT to_regoperator('+(int4,int4)');
@@ -23,6 +24,7 @@ SELECT to_regprocedure('abs(numeric)');
 SELECT to_regclass('pg_class');
 SELECT to_regtype('int4');
 SELECT to_regcollation('"POSIX"');
+SELECT to_regdatabase('template1');
 
 -- with schemaname
 
@@ -65,6 +67,7 @@ SELECT regproc('know');
 SELECT regprocedure('absinthe(numeric)');
 SELECT regclass('pg_classes');
 SELECT regtype('int3');
+SELECT regdatabase('notadatabase');
 
 -- with schemaname
 
@@ -100,6 +103,7 @@ SELECT to_regprocedure('absinthe(numeric)');
 SELECT to_regclass('pg_classes');
 SELECT to_regtype('int3');
 SELECT to_regcollation('notacollation');
+SELECT to_regdatabase('notadatabase');
 
 -- with schemaname
 
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index c94dd83d306..914613fe8a0 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -567,7 +567,7 @@ SELECT oid, typname, typtype, typelem, typarray
     typtype NOT IN ('p', 'c') AND
     -- These reg* types cannot be pg_upgraded, so discard them.
     oid != ALL(ARRAY['regproc', 'regprocedure', 'regoper',
-                     'regoperator', 'regconfig', 'regdictionary',
+                     'regoperator', 'regconfig', 'regdatabase', 'regdictionary',
                      'regnamespace', 'regcollation']::regtype[]) AND
     -- Discard types that do not accept input values as these cannot be
     -- tested easily.

Reply via email to