commit 2e20fea416ae6b107ffbe952a951efb145f13ec7 Author: Ian Barwick 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 regconfig + + regdatabase + + regdictionary @@ -4878,6 +4882,13 @@ SELECT * FROM pg_attribute english + + regdatabase + pg_database + database name + template1 + + regdictionary pg_ts_dict @@ -5049,8 +5060,8 @@ WHERE ... be dropped without first removing the default expression. The alternative of nextval('my_seq'::text) does not create a dependency. - (regrole is an exception to this property. Constants of this - type are not allowed in stored expressions.) + (regrole and regdatabase are the only exceptions for the property. + Constants of this type are not allowed in such expressions.) 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)') ... + + + + to_regdatabase + + to_regdatabase ( text ) + regdatabase + + + Translates a textual database name to its OID. A similar result is + obtained by casting the string to type regdatabase (see + ); however, this function will return + NULL rather than throwing an error if the name is + not found. Also unlike the cast, this does not accept + a numeric OID as input. + + + 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 regcollation regconfig + regdatabase regdictionary regnamespace regoper 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.