2025年5月7日(水) 4:29 Nathan Bossart <nathandboss...@gmail.com>: > > Every once in a while, I find myself wanting to use regdatabase for > something like current_database()::regdatabase, and I'm always surprised > when I inevitably rediscover that it doesn't exist. I only found one > reference to the idea in the archives [0]. So, I have two questions: > > * Is there an easier way to get the current database's (or any database's) > OID that I am overlooking (besides "SELECT oid FROM pg_database...")? > > * Would anyone object if I put together some patches to add regdatabase? > > [0] https://postgr.es/m/20191109220939.jz55zcc33d3g7h7b%40alap3.anarazel.de
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. Regards Ian Barwick
commit aea262f0fe6917fbad7edb7db9c0e33a5e3c7941 Author: Ian Barwick <barw...@gmail.com> Date: Mon May 25 23:44:17 2020 +0900 Implement regdatabase datatype diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index 7027758d28..cc54c3ffa7 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -4514,6 +4514,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> @@ -4654,6 +4658,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> @@ -4738,8 +4749,8 @@ SELECT * FROM pg_attribute understands that the default expression depends on the sequence <literal>my_seq</literal>; the system will not let the sequence be dropped without first removing the default expression. - <type>regrole</type> is the only exception for the property. Constants of this - type are not allowed in such 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> <note> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index f065856535..fb8d685486 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -22517,6 +22517,24 @@ SELECT collation for ('foo' COLLATE "de_DE"); </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 6779a5bddc..9781ab3933 100644 --- a/doc/src/sgml/ref/pgupgrade.sgml +++ b/doc/src/sgml/ref/pgupgrade.sgml @@ -777,6 +777,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 5480a024e0..7953b116e9 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -125,6 +125,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 b33a2f94af..41e28a49f8 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -1858,9 +1858,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 c800d797ac..447286ae6b 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -41,6 +41,8 @@ #include "utils/syscache.h" #include "utils/varlena.h" +#include "commands/dbcommands.h" + static char *format_operator_internal(Oid operator_oid, bool force_qualify); static char *format_procedure_internal(Oid procedure_oid, bool force_qualify); static void parseNameAndArgTypes(const char *string, bool allowNone, @@ -1810,6 +1812,132 @@ 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); + 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); + + 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)); + Oid result; + List *names; + + names = stringToQualifiedNameList(dat_name); + + 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 be08eb4814..01050f7708 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -4074,6 +4074,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 3613ae5f44..a533728469 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -243,6 +243,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 00aef855dc..1c754bbeb5 100644 --- a/src/bin/pg_upgrade/check.c +++ b/src/bin/pg_upgrade/check.c @@ -1030,6 +1030,7 @@ check_for_reg_data_type_usage(ClusterInfo *cluster) /* regclass.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 5a58f50fbb..d74e89f1e2 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 38295aca48..def8908252 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6767,6 +6767,17 @@ prorettype => 'regnamespace', proargtypes => 'text', prosrc => 'to_regnamespace' }, +{ oid => '8802', descr => 'I/O', + proname => 'regdatabasein', provolatile => 's', prorettype => 'regdatabase', + proargtypes => 'cstring', prosrc => 'regdatabasein' }, +{ oid => '8803', descr => 'I/O', + proname => 'regdatabaseout', provolatile => 's', prorettype => 'cstring', + proargtypes => 'regdatabase', prosrc => 'regdatabaseout' }, +{ oid => '8804', 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', @@ -7561,7 +7572,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' }, @@ -7574,6 +7584,12 @@ { oid => '4088', descr => 'I/O', proname => 'regnamespacesend', prorettype => 'bytea', proargtypes => 'regnamespace', prosrc => 'regnamespacesend' }, +{ oid => '8805', descr => 'I/O', + proname => 'regdatabaserecv', prorettype => 'regdatabase', + proargtypes => 'internal', prosrc => 'regdatabaserecv' }, +{ oid => '8806', 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 e8be000835..5e104a740c 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -401,6 +401,11 @@ typinput => 'regnamespacein', typoutput => 'regnamespaceout', typreceive => 'regnamespacerecv', typsend => 'regnamespacesend', typalign => 'i' }, +{ oid => '8800', array_type_oid => '8801', 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 datatype', diff --git a/src/test/regress/expected/regproc.out b/src/test/regress/expected/regproc.out index e45ff5483f..a7d75187fd 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: schema "ng_catalog" does not exist @@ -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 @@ -435,3 +457,10 @@ SELECT to_regnamespace('"Nonexistent"'); SELECT to_regnamespace('foo.bar'); ERROR: invalid name syntax +-- constant cannot be used here +CREATE ROLE regress_regrole_test; +CREATE TABLE regrole_test (rolid OID DEFAULT 'regress_regrole_test'::regrole); +ERROR: constant of the type regrole cannot be used here +DROP ROLE regress_regrole_test; +CREATE TABLE regdatabase_test (datid OID DEFAULT 'template1'::regdatabase); +ERROR: constant of the type regdatabase cannot be used here diff --git a/src/test/regress/sql/regproc.sql b/src/test/regress/sql/regproc.sql index faab0c15ce..c684886a15 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 @@ -98,6 +101,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 @@ -120,3 +124,11 @@ SELECT to_regrole('foo.bar'); SELECT to_regnamespace('Nonexistent'); SELECT to_regnamespace('"Nonexistent"'); SELECT to_regnamespace('foo.bar'); + +-- constant cannot be used here + +CREATE ROLE regress_regrole_test; +CREATE TABLE regrole_test (rolid OID DEFAULT 'regress_regrole_test'::regrole); +DROP ROLE regress_regrole_test; + +CREATE TABLE regdatabase_test (datid OID DEFAULT 'template1'::regdatabase);