diff --git a/doc/src/sgml/ref/alter_database.sgml b/doc/src/sgml/ref/alter_database.sgml index 1e09b5d..c9070f9 100644 --- a/doc/src/sgml/ref/alter_database.sgml +++ b/doc/src/sgml/ref/alter_database.sgml @@ -31,14 +31,14 @@ ALTER DATABASE name [ [ WITH ] name RENAME TO new_name -ALTER DATABASE name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER DATABASE {name | CURRENT_DATABASE} OWNER TO { new_owner | CURRENT_USER | SESSION_USER } ALTER DATABASE name SET TABLESPACE new_tablespace -ALTER DATABASE name SET configuration_parameter { TO | = } { value | DEFAULT } -ALTER DATABASE name SET configuration_parameter FROM CURRENT -ALTER DATABASE name RESET configuration_parameter -ALTER DATABASE name RESET ALL +ALTER DATABASE {name | CURRENT_DATABASE} SET configuration_parameter { TO | = } { value | DEFAULT } +ALTER DATABASE {name | CURRENT_DATABASE} SET configuration_parameter FROM CURRENT +ALTER DATABASE {name | CURRENT_DATABASE} RESET configuration_parameter +ALTER DATABASE {name | CURRENT_DATABASE} RESET ALL diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml index d705792..d02179f 100644 --- a/doc/src/sgml/ref/comment.sgml +++ b/doc/src/sgml/ref/comment.sgml @@ -31,7 +31,7 @@ COMMENT ON CONSTRAINT constraint_name ON table_name | CONSTRAINT constraint_name ON DOMAIN domain_name | CONVERSION object_name | - DATABASE object_name | + DATABASE database_specification | DOMAIN object_name | EXTENSION object_name | EVENT TRIGGER object_name | @@ -71,6 +71,11 @@ COMMENT ON * | [ argmode ] [ argname ] argtype [ , ... ] | [ [ argmode ] [ argname ] argtype [ , ... ] ] ORDER BY [ argmode ] [ argname ] argtype [ , ... ] + +where database_specification is: + + object_name + | CURRENT_DATABASE @@ -311,6 +316,7 @@ COMMENT ON CONVERSION my_conv IS 'Conversion to UTF8'; COMMENT ON CONSTRAINT bar_col_cons ON bar IS 'Constrains column col'; COMMENT ON CONSTRAINT dom_col_constr ON DOMAIN dom IS 'Constrains col of domain'; COMMENT ON DATABASE my_database IS 'Development Database'; +COMMENT ON DATABASE current_database IS 'Comment on current Database'; COMMENT ON DOMAIN my_domain IS 'Email Address Domain'; COMMENT ON EXTENSION hstore IS 'implements the hstore data type'; COMMENT ON FOREIGN DATA WRAPPER mywrapper IS 'my foreign data wrapper'; diff --git a/doc/src/sgml/ref/security_label.sgml b/doc/src/sgml/ref/security_label.sgml index ce5a1c1..3e2391b 100644 --- a/doc/src/sgml/ref/security_label.sgml +++ b/doc/src/sgml/ref/security_label.sgml @@ -26,7 +26,7 @@ SECURITY LABEL [ FOR provider ] ON TABLE object_name | COLUMN table_name.column_name | AGGREGATE aggregate_name ( aggregate_signature ) | - DATABASE object_name | + DATABASE database_specification | DOMAIN object_name | EVENT TRIGGER object_name | FOREIGN TABLE object_name @@ -49,6 +49,11 @@ SECURITY LABEL [ FOR provider ] ON * | [ argmode ] [ argname ] argtype [ , ... ] | [ [ argmode ] [ argname ] argtype [ , ... ] ] ORDER BY [ argmode ] [ argname ] argtype [ , ... ] + +where database_specification is: + + object_name + | CURRENT_DATABASE diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 8d55c76..519ab55 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -721,7 +721,6 @@ const ObjectAddress InvalidObjectAddress = InvalidOid, 0 }; - static ObjectAddress get_object_address_unqualified(ObjectType objtype, Value *strval, bool missing_ok); static ObjectAddress get_relation_by_qualified_name(ObjectType objtype, @@ -865,6 +864,10 @@ get_object_address(ObjectType objtype, Node *object, } break; case OBJECT_DATABASE: + address.classId = DatabaseRelationId; + address.objectId = get_dbspec_oid((DbSpec *)object, missing_ok); + address.objectSubId = 0; + break; case OBJECT_EXTENSION: case OBJECT_TABLESPACE: case OBJECT_ROLE: @@ -2242,7 +2245,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, case OBJECT_DATABASE: if (!pg_database_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - strVal((Value *) object)); + get_dbspec_name((DbSpec *)object)); break; case OBJECT_TYPE: case OBJECT_DOMAIN: diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 4f81479..573eefc 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -802,7 +802,7 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) switch (stmt->objectType) { case OBJECT_DATABASE: - return AlterDatabaseOwner(strVal((Value *) stmt->object), newowner); + return AlterDatabaseOwner((DbSpec *) stmt->object, newowner); case OBJECT_SCHEMA: return AlterSchemaOwner(strVal((Value *) stmt->object), newowner); diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index 2dc9371..047d283 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -52,13 +52,15 @@ CommentObject(CommentStmt *stmt) */ if (stmt->objtype == OBJECT_DATABASE) { - char *database = strVal((Value *) stmt->object); - - if (!OidIsValid(get_database_oid(database, true))) + if (!OidIsValid(get_dbspec_oid((DbSpec *)stmt->object, true))) { + char *dbname = NULL; + + dbname = get_dbspec_name((DbSpec *)stmt->object); + ereport(WARNING, (errcode(ERRCODE_UNDEFINED_DATABASE), - errmsg("database \"%s\" does not exist", database))); + errmsg("database \"%s\" does not exist", dbname))); return address; } } diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index eb1a469..600b592 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -1413,6 +1413,9 @@ AlterDatabase(ParseState *pstate, AlterDatabaseStmt *stmt, bool isTopLevel) Datum new_record[Natts_pg_database]; bool new_record_nulls[Natts_pg_database]; bool new_record_repl[Natts_pg_database]; + char *dbname = NULL; + + dbname = get_dbspec_name((DbSpec *)stmt->dbspec); /* Extract options from the statement node tree */ foreach(option, stmt->options) @@ -1477,7 +1480,7 @@ AlterDatabase(ParseState *pstate, AlterDatabaseStmt *stmt, bool isTopLevel) parser_errposition(pstate, dtablespace->location))); /* this case isn't allowed within a transaction block */ PreventTransactionChain(isTopLevel, "ALTER DATABASE SET TABLESPACE"); - movedb(stmt->dbname, defGetString(dtablespace)); + movedb(dbname, defGetString(dtablespace)); return InvalidOid; } @@ -1503,20 +1506,20 @@ AlterDatabase(ParseState *pstate, AlterDatabaseStmt *stmt, bool isTopLevel) ScanKeyInit(&scankey, Anum_pg_database_datname, BTEqualStrategyNumber, F_NAMEEQ, - CStringGetDatum(stmt->dbname)); + CStringGetDatum(dbname)); scan = systable_beginscan(rel, DatabaseNameIndexId, true, NULL, 1, &scankey); tuple = systable_getnext(scan); if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), - errmsg("database \"%s\" does not exist", stmt->dbname))); + errmsg("database \"%s\" does not exist", dbname))); dboid = HeapTupleGetOid(tuple); if (!pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - stmt->dbname); + dbname); /* * In order to avoid getting locked out and having to go through @@ -1574,7 +1577,9 @@ AlterDatabase(ParseState *pstate, AlterDatabaseStmt *stmt, bool isTopLevel) Oid AlterDatabaseSet(AlterDatabaseSetStmt *stmt) { - Oid datid = get_database_oid(stmt->dbname, false); + Oid datid; + + datid = get_dbspec_oid((DbSpec *)stmt->dbspec, false); /* * Obtain a lock on the database and make sure it didn't go away in the @@ -1584,7 +1589,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) if (!pg_database_ownercheck(datid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - stmt->dbname); + get_dbspec_name((DbSpec *)stmt->dbspec)); AlterSetting(datid, InvalidOid, stmt->setstmt); @@ -1598,7 +1603,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) * ALTER DATABASE name OWNER TO newowner */ ObjectAddress -AlterDatabaseOwner(const char *dbname, Oid newOwnerId) +AlterDatabaseOwner(const DbSpec *dbspec, Oid newOwnerId) { Oid db_id; HeapTuple tuple; @@ -1607,6 +1612,9 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) SysScanDesc scan; Form_pg_database datForm; ObjectAddress address; + char *dbname; + + dbname = get_dbspec_name(dbspec); /* * Get the old tuple. We don't need a lock on the database per se, diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 6c4612d..7172125 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2043,6 +2043,7 @@ ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op) *op->resnull = fcinfo.isnull; break; case SVFOP_CURRENT_CATALOG: + case SVFOP_CURRENT_DATABASE: InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL); *op->resvalue = current_database(&fcinfo); *op->resnull = fcinfo.isnull; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index d9ff8a7..933378a 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2922,6 +2922,18 @@ _copyTriggerTransition(const TriggerTransition *from) return newnode; } +static DbSpec * +_copyDatabaseSpec(const DbSpec *from) +{ + DbSpec *newnode = makeNode(DbSpec); + + COPY_SCALAR_FIELD(dbtype); + COPY_STRING_FIELD(dbname); + COPY_LOCATION_FIELD(location); + + return newnode; +} + static Query * _copyQuery(const Query *from) { @@ -3734,7 +3746,7 @@ _copyAlterDatabaseStmt(const AlterDatabaseStmt *from) { AlterDatabaseStmt *newnode = makeNode(AlterDatabaseStmt); - COPY_STRING_FIELD(dbname); + COPY_NODE_FIELD(dbspec); COPY_NODE_FIELD(options); return newnode; @@ -3745,7 +3757,7 @@ _copyAlterDatabaseSetStmt(const AlterDatabaseSetStmt *from) { AlterDatabaseSetStmt *newnode = makeNode(AlterDatabaseSetStmt); - COPY_STRING_FIELD(dbname); + COPY_NODE_FIELD(dbspec); COPY_NODE_FIELD(setstmt); return newnode; @@ -5548,7 +5560,9 @@ copyObjectImpl(const void *from) case T_PartitionCmd: retval = _copyPartitionCmd(from); break; - + case T_DbSpec: + retval = _copyDatabaseSpec(from); + break; /* * MISCELLANEOUS NODES */ diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 2866fd7..3570514 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1635,7 +1635,7 @@ _equalCreatedbStmt(const CreatedbStmt *a, const CreatedbStmt *b) static bool _equalAlterDatabaseStmt(const AlterDatabaseStmt *a, const AlterDatabaseStmt *b) { - COMPARE_STRING_FIELD(dbname); + COMPARE_NODE_FIELD(dbspec); COMPARE_NODE_FIELD(options); return true; @@ -1644,7 +1644,7 @@ _equalAlterDatabaseStmt(const AlterDatabaseStmt *a, const AlterDatabaseStmt *b) static bool _equalAlterDatabaseSetStmt(const AlterDatabaseSetStmt *a, const AlterDatabaseSetStmt *b) { - COMPARE_STRING_FIELD(dbname); + COMPARE_NODE_FIELD(dbspec); COMPARE_NODE_FIELD(setstmt); return true; @@ -2877,6 +2877,16 @@ _equalPartitionCmd(const PartitionCmd *a, const PartitionCmd *b) return true; } +static bool +_equalDatabaseSpec(const DbSpec *a, const DbSpec *b) +{ + COMPARE_SCALAR_FIELD(dbtype); + COMPARE_STRING_FIELD(dbname); + COMPARE_LOCATION_FIELD(location); + + return true; +} + /* * Stuff from pg_list.h */ @@ -3690,6 +3700,9 @@ equal(const void *a, const void *b) case T_PartitionCmd: retval = _equalPartitionCmd(a, b); break; + case T_DbSpec: + retval = _equalDatabaseSpec(a, b); + break; default: elog(ERROR, "unrecognized node type: %d", diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c301ca4..431a287 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -187,7 +187,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, bool *deferrable, bool *initdeferred, bool *not_valid, bool *no_inherit, core_yyscan_t yyscanner); static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); - +static Node *makeDbSpec(DbSpecType type, int location); %} %pure-parser @@ -572,6 +572,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); opt_frame_clause frame_extent frame_bound %type opt_existing_window_name %type opt_if_not_exists +%type db_spec_name %type generated_when override_kind %type PartitionSpec OptPartitionSpec %type part_strategy @@ -617,7 +618,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CROSS CSV CUBE CURRENT_P - CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA + CURRENT_CATALOG CURRENT_DATABASE CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS @@ -6367,6 +6368,14 @@ CommentStmt: n->comment = $6; $$ = (Node *) n; } + | COMMENT ON DATABASE db_spec_name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_DATABASE; + n->object = (Node *) $4; + n->comment = $6; + $$ = (Node *) n; + } | COMMENT ON TYPE_P Typename IS comment_text { CommentStmt *n = makeNode(CommentStmt); @@ -6515,7 +6524,6 @@ comment_type_any_name: /* object types taking name */ comment_type_name: ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; } - | DATABASE { $$ = OBJECT_DATABASE; } | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } | EXTENSION { $$ = OBJECT_EXTENSION; } | FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; } @@ -6564,6 +6572,16 @@ SecLabelStmt: n->label = $8; $$ = (Node *) n; } + | SECURITY LABEL opt_provider ON DATABASE db_spec_name + IS security_label + { + SecLabelStmt *n = makeNode(SecLabelStmt); + n->provider = $3; + n->objtype = OBJECT_DATABASE; + n->object = (Node *) $6; + n->label = $8; + $$ = (Node *) n; + } | SECURITY LABEL opt_provider ON TYPE_P Typename IS security_label { @@ -6632,8 +6650,7 @@ security_label_type_any_name: /* object types taking name */ security_label_type_name: - DATABASE { $$ = OBJECT_DATABASE; } - | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } + EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } | opt_procedural LANGUAGE { $$ = OBJECT_LANGUAGE; } | PUBLICATION { $$ = OBJECT_PUBLICATION; } | ROLE { $$ = OBJECT_ROLE; } @@ -9062,11 +9079,11 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec n->newowner = $6; $$ = (Node *)n; } - | ALTER DATABASE database_name OWNER TO RoleSpec + | ALTER DATABASE db_spec_name OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_DATABASE; - n->object = (Node *) makeString($3); + n->object = (Node *) $3; n->newowner = $6; $$ = (Node *)n; } @@ -9872,24 +9889,24 @@ opt_equal: '=' {} *****************************************************************************/ AlterDatabaseStmt: - ALTER DATABASE database_name WITH createdb_opt_list + ALTER DATABASE db_spec_name WITH createdb_opt_list { AlterDatabaseStmt *n = makeNode(AlterDatabaseStmt); - n->dbname = $3; + n->dbspec = $3; n->options = $5; $$ = (Node *)n; } - | ALTER DATABASE database_name createdb_opt_list + | ALTER DATABASE db_spec_name createdb_opt_list { AlterDatabaseStmt *n = makeNode(AlterDatabaseStmt); - n->dbname = $3; + n->dbspec = $3; n->options = $4; $$ = (Node *)n; } - | ALTER DATABASE database_name SET TABLESPACE name + | ALTER DATABASE db_spec_name SET TABLESPACE name { AlterDatabaseStmt *n = makeNode(AlterDatabaseStmt); - n->dbname = $3; + n->dbspec = $3; n->options = list_make1(makeDefElem("tablespace", (Node *)makeString($6), @6)); $$ = (Node *)n; @@ -9897,10 +9914,10 @@ AlterDatabaseStmt: ; AlterDatabaseSetStmt: - ALTER DATABASE database_name SetResetClause + ALTER DATABASE db_spec_name SetResetClause { AlterDatabaseSetStmt *n = makeNode(AlterDatabaseSetStmt); - n->dbname = $3; + n->dbspec = $3; n->setstmt = $4; $$ = (Node *)n; } @@ -13429,6 +13446,10 @@ func_expr_common_subexpr: { $$ = makeSQLValueFunction(SVFOP_SESSION_USER, -1, @1); } + | CURRENT_DATABASE + { + $$ = makeSQLValueFunction(SVFOP_CURRENT_DATABASE, -1, @1); + } | USER { $$ = makeSQLValueFunction(SVFOP_USER, -1, @1); @@ -14399,7 +14420,29 @@ name_list: name name: ColId { $$ = $1; }; database_name: - ColId { $$ = $1; }; + ColId { $$ = $1; } + | CURRENT_DATABASE + { + ereport(ERROR, + (errcode(ERRCODE_RESERVED_NAME), + errmsg("%s cannot be used as a database name here", + "CURRENT_DATABASE"), + parser_errposition(@1))); + } + ; + +db_spec_name: + ColId + { + DbSpec *n = (DbSpec *) makeDbSpec(DBSPEC_CSTRING, @1); + n->dbname = pstrdup($1); + $$ = (Node *)n; + } + | CURRENT_DATABASE + { + $$ = (Node *) makeDbSpec(DBSPEC_CURRENT_DATABASE, @1); + } + ; access_method: ColId { $$ = $1; }; @@ -14425,6 +14468,8 @@ func_name: type_function_name $$ = check_func_name(lcons(makeString($1), $2), yyscanner); } + | CURRENT_DATABASE + { $$ = list_make1(makeString("current_database")); } ; @@ -15076,6 +15121,7 @@ reserved_keyword: | CONSTRAINT | CREATE | CURRENT_CATALOG + | CURRENT_DATABASE | CURRENT_DATE | CURRENT_ROLE | CURRENT_TIME @@ -15987,6 +16033,20 @@ makeRecursiveViewSelect(char *relname, List *aliases, Node *query) return (Node *) s; } +/* makeDbSpec + * Create a DbSpec with the given type + */ +static Node * +makeDbSpec(DbSpecType type, int location) +{ + DbSpec *spec = makeNode(DbSpec); + + spec->dbtype = type; + spec->location = location; + + return (Node *) spec; +} + /* parser_init() * Initialize to parse one query string */ diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 86d1da0..82a6c5f 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -2328,6 +2328,7 @@ transformSQLValueFunction(ParseState *pstate, SQLValueFunction *svf) case SVFOP_SESSION_USER: case SVFOP_CURRENT_CATALOG: case SVFOP_CURRENT_SCHEMA: + case SVFOP_CURRENT_DATABASE: svf->type = NAMEOID; break; } diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 21593b2..508fd41 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1882,6 +1882,9 @@ FigureColnameInternal(Node *node, char **name) case SVFOP_CURRENT_SCHEMA: *name = "current_schema"; return 2; + case SVFOP_CURRENT_DATABASE: + *name = "current_database"; + return 2; } break; case T_XmlExpr: diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index fa6b792..a9f468a 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -24,6 +24,7 @@ #include "catalog/pg_auth_members.h" #include "catalog/pg_type.h" #include "catalog/pg_class.h" +#include "catalog/pg_database.h" #include "commands/dbcommands.h" #include "commands/proclang.h" #include "commands/tablespace.h" @@ -5270,3 +5271,66 @@ check_rolespec_name(const RoleSpec *role, const char *detail_msg) role->rolename))); } } + + +/* + * Given a DbSpec, returns a palloc'ed copy of the corresponding role's name. + */ +char * +get_dbspec_name(const DbSpec *db) +{ + char *dbname; + HeapTuple tuple; + Form_pg_database dbForm; + + switch (db->dbtype) + { + case DBSPEC_CSTRING: + Assert(db->dbname); + dbname = db->dbname; + break; + + case DBSPEC_CURRENT_DATABASE: + tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for database %u", MyDatabaseId); + + dbForm = (Form_pg_database) GETSTRUCT(tuple); + dbname = pstrdup(NameStr(dbForm->datname)); + ReleaseSysCache(tuple); + break; + + default: + elog(ERROR, "unexpected database type %d", db->dbtype); + } + + + return dbname; +} + +/* + * Given a DbSpec node, return the OID it corresponds to. If missing_ok is + * true, return InvalidOid if the database does not exist. + */ +Oid +get_dbspec_oid(const DbSpec *db, bool missing_ok) +{ + Oid oid; + + switch (db->dbtype) + { + case DBSPEC_CSTRING: + Assert(db->dbname); + oid = get_database_oid(db->dbname, missing_ok); + break; + + case DBSPEC_CURRENT_DATABASE: + oid = MyDatabaseId; + break; + + default: + elog(ERROR, "unexpected database type %d", db->dbtype); + } + + return oid; +} diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 06cf32f..8478c60 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -8351,6 +8351,10 @@ get_rule_expr(Node *node, deparse_context *context, case SVFOP_CURRENT_SCHEMA: appendStringInfoString(buf, "CURRENT_SCHEMA"); break; + case SVFOP_CURRENT_DATABASE: + appendStringInfoString(buf, "CURRENT_DATABASE"); + break; + } } break; diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h index f42c8cd..9221b54 100644 --- a/src/include/commands/dbcommands.h +++ b/src/include/commands/dbcommands.h @@ -24,7 +24,8 @@ extern void dropdb(const char *dbname, bool missing_ok); extern ObjectAddress RenameDatabase(const char *oldname, const char *newname); extern Oid AlterDatabase(ParseState *pstate, AlterDatabaseStmt *stmt, bool isTopLevel); extern Oid AlterDatabaseSet(AlterDatabaseSetStmt *stmt); -extern ObjectAddress AlterDatabaseOwner(const char *dbname, Oid newOwnerId); +extern ObjectAddress AlterDatabaseOwner(const DbSpec *dbspec, Oid newOwnerId); + extern Oid get_database_oid(const char *dbname, bool missingok); extern char *get_database_name(Oid dbid); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index ffeeb49..7847af5 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -462,6 +462,7 @@ typedef enum NodeTag T_OnConflictClause, T_CommonTableExpr, T_RoleSpec, + T_DbSpec, T_TriggerTransition, T_PartitionElem, T_PartitionSpec, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 34d6afc..4852c7d 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3023,6 +3023,24 @@ typedef struct LoadStmt char *filename; /* file to load */ } LoadStmt; + +/* + * DbSpecType - The type of a database name. + */ +typedef enum DbSpecType +{ + DBSPEC_CSTRING, /* database name is stored as a C string */ + DBSPEC_CURRENT_DATABASE /* database name is CURRENT_DATABASE */ +} DbSpecType; + +typedef struct DbSpec +{ + NodeTag type; + DbSpecType dbtype; /* Type of the database */ + char *dbname; /* filled only for DBSPEC_CSTRING */ + int location; /* token location, or -1 if unknown */ +} DbSpec; + /* ---------------------- * Createdb Statement * ---------------------- @@ -3041,14 +3059,14 @@ typedef struct CreatedbStmt typedef struct AlterDatabaseStmt { NodeTag type; - char *dbname; /* name of database to alter */ - List *options; /* List of DefElem nodes */ + Node *dbspec; /* name of database to alter, DbSpec */ + List *options; /* List of DefElem nodes */ } AlterDatabaseStmt; typedef struct AlterDatabaseSetStmt { NodeTag type; - char *dbname; /* database name */ + Node *dbspec; /* DbSpec */ VariableSetStmt *setstmt; /* SET or RESET subcommand */ } AlterDatabaseSetStmt; diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 074ae0a..c65a05c 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1102,7 +1102,8 @@ typedef enum SQLValueFunctionOp SVFOP_USER, SVFOP_SESSION_USER, SVFOP_CURRENT_CATALOG, - SVFOP_CURRENT_SCHEMA + SVFOP_CURRENT_SCHEMA, + SVFOP_CURRENT_DATABASE } SQLValueFunctionOp; typedef struct SQLValueFunction diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index f50e45e..a022826 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -104,6 +104,7 @@ PG_KEYWORD("csv", CSV, UNRESERVED_KEYWORD) PG_KEYWORD("cube", CUBE, UNRESERVED_KEYWORD) PG_KEYWORD("current", CURRENT_P, UNRESERVED_KEYWORD) PG_KEYWORD("current_catalog", CURRENT_CATALOG, RESERVED_KEYWORD) +PG_KEYWORD("current_database", CURRENT_DATABASE, RESERVED_KEYWORD) PG_KEYWORD("current_date", CURRENT_DATE, RESERVED_KEYWORD) PG_KEYWORD("current_role", CURRENT_ROLE, RESERVED_KEYWORD) PG_KEYWORD("current_schema", CURRENT_SCHEMA, TYPE_FUNC_NAME_KEYWORD) diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 254a811..c9cfa85 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -246,6 +246,8 @@ extern Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok); extern void check_rolespec_name(const RoleSpec *role, const char *detail_msg); extern HeapTuple get_rolespec_tuple(const RoleSpec *role); extern char *get_rolespec_name(const RoleSpec *role); +extern char *get_dbspec_name(const DbSpec *db); +extern Oid get_dbspec_oid(const DbSpec *db, bool missing_ok); extern void select_best_grantor(Oid roleId, AclMode privileges, const Acl *acl, Oid ownerId, diff --git a/src/test/regress/expected/dbname.out b/src/test/regress/expected/dbname.out new file mode 100644 index 0000000..cac34de --- /dev/null +++ b/src/test/regress/expected/dbname.out @@ -0,0 +1,113 @@ +CREATE ROLE dbuser1 with LOGIN; +CREATE ROLE dbuser2 with SUPERUSER LOGIN; +CREATE ROLE dbuser3 with SUPERUSER LOGIN; +DROP DATABASE IF EXISTS db1; +NOTICE: database "db1" does not exist, skipping +CREATE DATABASE db1 with owner=dbuser1; +CREATE DATABASE "current_database" with owner=dbuser1; +CREATE DATABASE current_database with owner=dbuser1; +ERROR: CURRENT_DATABASE cannot be used as a database name here +LINE 1: CREATE DATABASE current_database with owner=dbuser1; + ^ +SELECT d.datname as "Name", + pg_catalog.shobj_description(d.oid, 'pg_database') as "Description" +FROM pg_catalog.pg_database d + JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid +WHERE d.datname='current_database' or d.datname='db1' +ORDER BY 1; + Name | Description +------------------+------------- + current_database | + db1 | +(2 rows) + +\c db1; +SELECT CURRENT_DATABASE; + current_database +------------------ + db1 +(1 row) + +COMMENT ON DATABASE current_database IS 'db1'; +COMMENT ON DATABASE "current_database" IS 'db2'; +SELECT d.datname as "Name", + pg_catalog.shobj_description(d.oid, 'pg_database') as "Description" +FROM pg_catalog.pg_database d + JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid +WHERE d.datname='current_database' or d.datname='db1' +ORDER BY 1; + Name | Description +------------------+------------- + current_database | db2 + db1 | db1 +(2 rows) + +-- test alter owner +ALTER DATABASE current_database OWNER to dbuser2; +ALTER DATABASE "current_database" OWNER to dbuser2; +SELECT d.datname as "Name", + pg_catalog.pg_get_userbyid(d.datdba) as "Owner", + pg_catalog.shobj_description(d.oid, 'pg_database') as "Description" +FROM pg_catalog.pg_database d + JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid +WHERE d.datname='current_database' or d.datname='db1' +ORDER BY 1; + Name | Owner | Description +------------------+---------+------------- + current_database | dbuser2 | db2 + db1 | dbuser2 | db1 +(2 rows) + +-- test alter database tablespace +ALTER DATABASE current_database SET TABLESPACE pg_default; +ERROR: cannot change the tablespace of the currently open database +ALTER DATABASE "current_database" SET TABLESPACE pg_default; +-- test alter database rename +ALTER DATABASE current_database rename to db2; +ERROR: CURRENT_DATABASE cannot be used as a database name here +LINE 1: ALTER DATABASE current_database rename to db2; + ^ +COMMENT ON DATABASE "current_database" IS 'changed from current_database'; +ALTER DATABASE "current_database" rename to db2; +ALTER DATABASE db2 rename to current_database; +ERROR: CURRENT_DATABASE cannot be used as a database name here +LINE 1: ALTER DATABASE db2 rename to current_database; + ^ +SELECT d.datname as "Name", + pg_catalog.shobj_description(d.oid, 'pg_database') as "Description" +FROM pg_catalog.pg_database d + JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid +WHERE d.datname='current_database' or d.datname='db1' or d.datname='db2' +ORDER BY 1; + Name | Description +------+------------------------------- + db1 | db1 + db2 | changed from current_database +(2 rows) + +-- test alter database set parameter +ALTER DATABASE current_database SET parallel_tuple_cost=0.3; +\c db1 +show parallel_tuple_cost; + parallel_tuple_cost +--------------------- + 0.3 +(1 row) + +ALTER DATABASE current_database RESET parallel_tuple_cost; +\c db1 +show parallel_tuple_cost; + parallel_tuple_cost +--------------------- + 0.1 +(1 row) + +-- clean up +\c postgres +DROP DATABASE IF EXISTS "current_database"; +NOTICE: database "current_database" does not exist, skipping +DROP DATABASE IF EXISTS db1; +DROP DATABASE IF EXISTS db2; +DROP ROLE dbuser1; +DROP ROLE dbuser2; +DROP ROLE dbuser3; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index aa5e6af..6a6382c 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -123,3 +123,4 @@ test: event_trigger # run stats by itself because its delay may be insufficient under heavy load test: stats +test: dbname \ No newline at end of file diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 3866314..96e6202 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -183,3 +183,4 @@ test: partition_join test: reloptions test: event_trigger test: stats +test: dbname diff --git a/src/test/regress/sql/dbname.sql b/src/test/regress/sql/dbname.sql new file mode 100644 index 0000000..5c8b36e --- /dev/null +++ b/src/test/regress/sql/dbname.sql @@ -0,0 +1,77 @@ +CREATE ROLE dbuser1 with LOGIN; +CREATE ROLE dbuser2 with SUPERUSER LOGIN; +CREATE ROLE dbuser3 with SUPERUSER LOGIN; + +DROP DATABASE IF EXISTS db1; +CREATE DATABASE db1 with owner=dbuser1; +CREATE DATABASE "current_database" with owner=dbuser1; +CREATE DATABASE current_database with owner=dbuser1; + +SELECT d.datname as "Name", + pg_catalog.shobj_description(d.oid, 'pg_database') as "Description" +FROM pg_catalog.pg_database d + JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid +WHERE d.datname='current_database' or d.datname='db1' +ORDER BY 1; + + +\c db1; +SELECT CURRENT_DATABASE; + + +COMMENT ON DATABASE current_database IS 'db1'; +COMMENT ON DATABASE "current_database" IS 'db2'; + +SELECT d.datname as "Name", + pg_catalog.shobj_description(d.oid, 'pg_database') as "Description" +FROM pg_catalog.pg_database d + JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid +WHERE d.datname='current_database' or d.datname='db1' +ORDER BY 1; + +-- test alter owner +ALTER DATABASE current_database OWNER to dbuser2; +ALTER DATABASE "current_database" OWNER to dbuser2; + +SELECT d.datname as "Name", + pg_catalog.pg_get_userbyid(d.datdba) as "Owner", + pg_catalog.shobj_description(d.oid, 'pg_database') as "Description" +FROM pg_catalog.pg_database d + JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid +WHERE d.datname='current_database' or d.datname='db1' +ORDER BY 1; + +-- test alter database tablespace +ALTER DATABASE current_database SET TABLESPACE pg_default; +ALTER DATABASE "current_database" SET TABLESPACE pg_default; + +-- test alter database rename +ALTER DATABASE current_database rename to db2; +COMMENT ON DATABASE "current_database" IS 'changed from current_database'; +ALTER DATABASE "current_database" rename to db2; +ALTER DATABASE db2 rename to current_database; + +SELECT d.datname as "Name", + pg_catalog.shobj_description(d.oid, 'pg_database') as "Description" +FROM pg_catalog.pg_database d + JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid +WHERE d.datname='current_database' or d.datname='db1' or d.datname='db2' +ORDER BY 1; + +-- test alter database set parameter +ALTER DATABASE current_database SET parallel_tuple_cost=0.3; +\c db1 +show parallel_tuple_cost; +ALTER DATABASE current_database RESET parallel_tuple_cost; +\c db1 +show parallel_tuple_cost; + +-- clean up +\c postgres + +DROP DATABASE IF EXISTS "current_database"; +DROP DATABASE IF EXISTS db1; +DROP DATABASE IF EXISTS db2; +DROP ROLE dbuser1; +DROP ROLE dbuser2; +DROP ROLE dbuser3;