diff --git a/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml b/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml index 59cf9c65e77e331ce568794ab3a726829747d35b..7376804403db2024348235f966c020ecd362a23d 100644 --- a/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml +++ b/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml @@ -26,6 +26,7 @@ ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> [ VALIDATOR <replaceable class="parameter">validator_function</replaceable> | NO VALIDATOR ] [ OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ]) ] ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> OWNER TO <replaceable>new_owner</replaceable> +ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> RENAME TO <replaceable>new_name</replaceable> </synopsis> </refsynopsisdiv> @@ -122,6 +123,24 @@ ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> OWN </para> </listitem> </varlistentry> + + <varlistentry> + <term><replaceable class="PARAMETER">new_owner</replaceable></term> + <listitem> + <para> + The user name of the new owner of the foreign-data wrapper. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">new_name</replaceable></term> + <listitem> + <para> + The new name for the foreign-data wrapper. + </para> + </listitem> + </varlistentry> </variablelist> </refsect1> @@ -150,7 +169,8 @@ ALTER FOREIGN DATA WRAPPER dbi VALIDATOR bob.myvalidator; <para> <command>ALTER FOREIGN DATA WRAPPER</command> conforms to ISO/IEC 9075-9 (SQL/MED), except that the <literal>HANDLER</literal>, - <literal>VALIDATOR</> and <literal>OWNER TO</> clauses are extensions. + <literal>VALIDATOR</>, <literal>OWNER TO</>, and <literal>RENAME</literal> + clauses are extensions. </para> </refsect1> diff --git a/doc/src/sgml/ref/alter_server.sgml b/doc/src/sgml/ref/alter_server.sgml index c27b4c06a1cc1f8a128601ac8877846db20a32ab..248efd91630584c5e605aae14f1ad4bd6431ee22 100644 --- a/doc/src/sgml/ref/alter_server.sgml +++ b/doc/src/sgml/ref/alter_server.sgml @@ -24,6 +24,7 @@ PostgreSQL documentation ALTER SERVER <replaceable class="parameter">server_name</replaceable> [ VERSION '<replaceable class="parameter">new_version</replaceable>' ] [ OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] ) ] ALTER SERVER <replaceable class="PARAMETER">server_name</replaceable> OWNER TO <replaceable>new_owner</replaceable> +ALTER SERVER <replaceable class="PARAMETER">server_name</replaceable> RENAME TO <replaceable>new_name</replaceable> </synopsis> </refsynopsisdiv> @@ -82,6 +83,24 @@ ALTER SERVER <replaceable class="PARAMETER">server_name</replaceable> OWNER TO < </para> </listitem> </varlistentry> + + <varlistentry> + <term><replaceable class="PARAMETER">new_owner</replaceable></term> + <listitem> + <para> + The user name of the new owner of the foreign server. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">new_name</replaceable></term> + <listitem> + <para> + The new name for the foreign server. + </para> + </listitem> + </varlistentry> </variablelist> </refsect1> @@ -108,6 +127,8 @@ ALTER SERVER foo VERSION '8.4' OPTIONS (SET host 'baz'); <para> <command>ALTER SERVER</command> conforms to ISO/IEC 9075-9 (SQL/MED). + The <literal>OWNER TO</literal> and <literal>RENAME</literal> forms are + PostgreSQL extensions. </para> </refsect1> diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index eb0584e99105f69433cd6dcfdd6f4f6701273c74..f9be3a9a4ec185e0c186d0984ed7e6055d5053ef 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -65,6 +65,14 @@ ExecRenameStmt(RenameStmt *stmt) RenameDatabase(stmt->subname, stmt->newname); break; + case OBJECT_FDW: + RenameForeignDataWrapper(stmt->subname, stmt->newname); + break; + + case OBJECT_FOREIGN_SERVER: + RenameForeignServer(stmt->subname, stmt->newname); + break; + case OBJECT_FUNCTION: RenameFunction(stmt->object, stmt->objarg, stmt->newname); break; diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index b30ff4092356317ae1bef6847414602d346f87b0..5e58713da707b9f871adbae3cbaf6feafe431e84 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -200,6 +200,82 @@ GetUserOidFromMapping(const char *username, bool missing_ok) } +/* + * Rename foreign-data wrapper + */ +void +RenameForeignDataWrapper(const char *oldname, const char *newname) +{ + HeapTuple tup; + Relation rel; + + rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock); + + tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(oldname)); + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("foreign-data wrapper \"%s\" does not exist", oldname))); + + /* make sure the new name doesn't exist */ + if (SearchSysCacheExists1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(newname))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("foreign-data wrapper \"%s\" already exists", newname))); + + /* must be owner of FDW */ + if (!pg_foreign_data_wrapper_ownercheck(HeapTupleGetOid(tup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FDW, + oldname); + + /* rename */ + namestrcpy(&(((Form_pg_foreign_data_wrapper) GETSTRUCT(tup))->fdwname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); + + heap_close(rel, NoLock); + heap_freetuple(tup); +} + + +/* + * Rename foreign server + */ +void +RenameForeignServer(const char *oldname, const char *newname) +{ + HeapTuple tup; + Relation rel; + + rel = heap_open(ForeignServerRelationId, RowExclusiveLock); + + tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(oldname)); + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("server \"%s\" does not exist", oldname))); + + /* make sure the new name doesn't exist */ + if (SearchSysCacheExists1(FOREIGNSERVERNAME, CStringGetDatum(newname))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("server \"%s\" already exists", newname))); + + /* must be owner of server */ + if (!pg_foreign_server_ownercheck(HeapTupleGetOid(tup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, + oldname); + + /* rename */ + namestrcpy(&(((Form_pg_foreign_server) GETSTRUCT(tup))->srvname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); + + heap_close(rel, NoLock); + heap_freetuple(tup); +} + + /* * Change foreign-data wrapper owner. * diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 2a497d1b79dd57d92b2f78655baa3edbddc6a5ac..c3e0ee1877d099c673c84d7ce2c88b7aa3fb2c26 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -6434,6 +6434,14 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->newname = $6; $$ = (Node *)n; } + | ALTER FOREIGN DATA_P WRAPPER name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_FDW; + n->subname = $5; + n->newname = $8; + $$ = (Node *)n; + } | ALTER FUNCTION function_with_argtypes RENAME TO name { RenameStmt *n = makeNode(RenameStmt); @@ -6485,6 +6493,14 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->newname = $6; $$ = (Node *)n; } + | ALTER SERVER name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_FOREIGN_SERVER; + n->subname = $3; + n->newname = $6; + $$ = (Node *)n; + } | ALTER TABLE relation_expr RENAME TO name { RenameStmt *n = makeNode(RenameStmt); diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 70d3a8fe021cef6e84627ef24c0466617e537231..1390351697ef14f5fa302581dadf432b179d3fee 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -146,6 +146,8 @@ extern text *serialize_deflist(List *deflist); extern List *deserialize_deflist(Datum txt); /* commands/foreigncmds.c */ +extern void RenameForeignServer(const char *oldname, const char *newname); +extern void RenameForeignDataWrapper(const char *oldname, const char *newname); extern void AlterForeignServerOwner(const char *name, Oid newOwnerId); extern void AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId); extern void CreateForeignDataWrapper(CreateFdwStmt *stmt); diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out index 314748c6d4478d9ceb76775841edbe3237f34232..122e28532afe59140861165c33b5f50402f43ab1 100644 --- a/src/test/regress/expected/foreign_data.out +++ b/src/test/regress/expected/foreign_data.out @@ -177,6 +177,17 @@ RESET ROLE; postgresql | foreign_data_user | - | postgresql_fdw_validator | | | (3 rows) +ALTER FOREIGN DATA WRAPPER foo RENAME TO foo1; +\dew+ + List of foreign-data wrappers + Name | Owner | Handler | Validator | Access privileges | FDW Options | Description +------------+-------------------------+---------+--------------------------+-------------------+------------------------------+------------- + dummy | foreign_data_user | - | - | | | useless + foo1 | regress_test_role_super | - | - | | (b '3', c '4', a '2', d '5') | + postgresql | foreign_data_user | - | postgresql_fdw_validator | | | +(3 rows) + +ALTER FOREIGN DATA WRAPPER foo1 RENAME TO foo; -- DROP FOREIGN DATA WRAPPER DROP FOREIGN DATA WRAPPER nonexistent; -- ERROR ERROR: foreign-data wrapper "nonexistent" does not exist @@ -427,6 +438,26 @@ privileges for foreign-data wrapper foo t2 | regress_test_role | foo | | | | | (10 rows) +ALTER SERVER s8 RENAME to s8new; +\des+ + List of foreign servers + Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | FDW Options | Description +-------+-----------------------+----------------------+-----------------------------------------+--------+---------+--------------------------------------+------------- + s1 | regress_test_indirect | foo | foreign_data_user=U/foreign_data_user +| | 1.1 | (servername 's1') | + | | | regress_test_role=U/foreign_data_user | | | | + s2 | foreign_data_user | foo | | | 1.1 | (host 'a', dbname 'b') | + s3 | foreign_data_user | foo | | oracle | | ("tns name" 'orcl', port '1521') | + s4 | foreign_data_user | foo | | oracle | | (host 'a', dbname 'b') | + s5 | foreign_data_user | foo | | | 15.0 | | + s6 | foreign_data_user | foo | foreign_data_user=U/foreign_data_user +| | 16.0 | (host 'a', dbname 'b') | + | | | regress_test_role2=U*/foreign_data_user | | | | + s7 | foreign_data_user | foo | | oracle | 17.0 | (host 'a', dbname 'b') | + s8new | foreign_data_user | postgresql | | | | (dbname 'db1', connect_timeout '30') | + t1 | regress_test_role | foo | | | | | + t2 | regress_test_role | foo | | | | | +(10 rows) + +ALTER SERVER s8new RENAME to s8; -- DROP SERVER DROP SERVER nonexistent; -- ERROR ERROR: server "nonexistent" does not exist diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql index bfdb32afdc0942b88bc9259a67bc70ac62f18f22..e99e7079b1ef9b5855fbddc8a559621d777f922f 100644 --- a/src/test/regress/sql/foreign_data.sql +++ b/src/test/regress/sql/foreign_data.sql @@ -84,6 +84,10 @@ ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD e '6'); -- ERROR RESET ROLE; \dew+ +ALTER FOREIGN DATA WRAPPER foo RENAME TO foo1; +\dew+ +ALTER FOREIGN DATA WRAPPER foo1 RENAME TO foo; + -- DROP FOREIGN DATA WRAPPER DROP FOREIGN DATA WRAPPER nonexistent; -- ERROR DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent; @@ -181,6 +185,10 @@ RESET ROLE; DROP ROLE regress_test_indirect; -- ERROR \des+ +ALTER SERVER s8 RENAME to s8new; +\des+ +ALTER SERVER s8new RENAME to s8; + -- DROP SERVER DROP SERVER nonexistent; -- ERROR DROP SERVER IF EXISTS nonexistent;