diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml index df32811..58623f0 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_name | DOMAIN object_name | EXTENSION object_name | EVENT TRIGGER object_name | @@ -149,6 +149,16 @@ COMMENT ON + database_name + + + The name of a database or keyword current_database. When using + current_database,it means using the name of the connecting database. + + + + + source_type @@ -311,6 +321,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 aa8be47..e10d58e 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_name | DOMAIN object_name | EVENT TRIGGER object_name | FOREIGN TABLE object_name @@ -103,6 +103,16 @@ SECURITY LABEL [ FOR provider ] ON + database_name + + + The name of a database or keyword current_database. When using + current_database,it means using the name of the connecting database. + + + + + provider diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index c2ad7c6..f1424b8 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -721,7 +721,8 @@ const ObjectAddress InvalidObjectAddress = InvalidOid, 0 }; - +static ObjectAddress get_object_address_database(ObjectType objtype, + DbSpec * object, bool missing_ok); static ObjectAddress get_object_address_unqualified(ObjectType objtype, Value *strval, bool missing_ok); static ObjectAddress get_relation_by_qualified_name(ObjectType objtype, @@ -865,6 +866,8 @@ get_object_address(ObjectType objtype, Node *object, } break; case OBJECT_DATABASE: + address = get_object_address_database(objtype, (DbSpec*)object, missing_ok); + break; case OBJECT_EXTENSION: case OBJECT_TABLESPACE: case OBJECT_ROLE: @@ -1108,6 +1111,25 @@ get_object_address_rv(ObjectType objtype, RangeVar *rel, List *object, /* * Find an ObjectAddress for a type of object that is identified by an + * database name + */ +static ObjectAddress +get_object_address_database(ObjectType objtype, DbSpec *object, bool missing_ok) +{ + char *dbname; + ObjectAddress address; + + dbname = get_dbspec_name(object); + + address.classId = DatabaseRelationId; + address.objectId = get_database_oid(dbname, missing_ok); + address.objectSubId = 0; + + return address; +} + +/* + * Find an ObjectAddress for a type of object that is identified by an * unqualified name. */ static ObjectAddress @@ -2242,7 +2264,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..e1de191 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 1c17927..ac7de1f 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 e138539..e0dfa0c 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 c5e97ef..33665bc 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2016,6 +2016,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 c1a83ca..46a503e 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2920,6 +2920,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) { @@ -3732,7 +3744,7 @@ _copyAlterDatabaseStmt(const AlterDatabaseStmt *from) { AlterDatabaseStmt *newnode = makeNode(AlterDatabaseStmt); - COPY_STRING_FIELD(dbname); + COPY_NODE_FIELD(dbspec); COPY_NODE_FIELD(options); return newnode; @@ -3743,7 +3755,7 @@ _copyAlterDatabaseSetStmt(const AlterDatabaseSetStmt *from) { AlterDatabaseSetStmt *newnode = makeNode(AlterDatabaseSetStmt); - COPY_STRING_FIELD(dbname); + COPY_NODE_FIELD(dbspec); COPY_NODE_FIELD(setstmt); return newnode; @@ -5544,7 +5556,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 7a70001..c0e2dc3 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; @@ -2875,6 +2875,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 */ @@ -3688,6 +3698,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 4c83a63..7176fec 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 @@ -616,7 +617,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 @@ -6295,6 +6296,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); @@ -6443,7 +6452,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; } @@ -6492,6 +6500,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 { @@ -6560,8 +6578,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; } @@ -8990,11 +9007,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; } @@ -9800,24 +9817,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; @@ -9825,10 +9842,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; } @@ -13357,6 +13374,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); @@ -14327,7 +14348,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; }; @@ -14353,6 +14396,8 @@ func_name: type_function_name $$ = check_func_name(lcons(makeString($1), $2), yyscanner); } + | CURRENT_DATABASE + { $$ = list_make1(makeString("current_database")); } ; @@ -15004,6 +15049,7 @@ reserved_keyword: | CONSTRAINT | CREATE | CURRENT_CATALOG + | CURRENT_DATABASE | CURRENT_DATE | CURRENT_ROLE | CURRENT_TIME @@ -15915,6 +15961,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 1aaa524..57b908f 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 2547524..7a55e3f 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1873,6 +1873,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 0c26e44..fced2c9 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" @@ -5268,3 +5269,68 @@ 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; + + switch (db->dbtype) + { + case DBSPEC_CSTRING: + dbname = db->dbname; + break; + + case DBSPEC_CURRENT_DATABASE: + { + HeapTuple tuple; + Form_pg_database dbForm; + + 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 84759b6..d3c67db 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -8344,6 +8344,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 50eec73..5672b92 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3016,6 +3016,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 * ---------------------- @@ -3034,14 +3052,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 ccb5123..064e3ed 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1095,7 +1095,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 860e8ab..b062cc0 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -122,3 +122,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 ef275d0..404de67 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -180,3 +180,4 @@ test: with test: xml 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;