*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 134,139 ****
--- 134,154 ----
+ pg_foreign_data_wrapper
+ foreign data wrapper definitions
+
+
+
+ pg_foreign_server
+ foreign server definitions
+
+
+
+ pg_foreign_user_mapping
+ foreign user mappings
+
+
+ pg_indexadditional index information
***************
*** 2552,2557 ****
--- 2567,2816 ----
+
+ pg_foreign_data_wrapper
+
+
+ pg_foreign_data_wrapper
+
+
+
+ The catalog pg_foreign_data_wrapper catalog
+ stores foreign data wrapper definitions. A foreign data wrapper is
+ the mechanism by which external data, residing on foreign servers is
+ accessed.
+
+
+
+ pg_foreign_data_wrapper> Columns
+
+
+
+
+ Name
+ Type
+ References
+ Description
+
+
+
+
+
+ fdwname
+ name
+
+ Name of the foreign data wrapper
+
+
+
+ fdwnamespace
+ oid
+ pg_namespace.oid
+
+ The OID of the namespace that contains this foreign data wrapper
+
+
+
+
+ fdwowner
+ oid
+ pg_authid.oid
+ Owner of the foreign data wrapper
+
+
+
+ fdwlibrary
+ text
+
+ The library, implementing this foreign data wrapper
+
+
+
+ fdwacl
+ aclitem[]
+
+
+ Access privileges; see
+ and
+
+ for details
+
+
+
+
+ fdwoptions
+ text[]
+
+
+ Foreign data wrapper specific options, as keyword=value> strings
+
+
+
+
+
+
+
+
+
+
+ pg_foreign_server
+
+
+ pg_foreign_server
+
+
+
+ The catalog pg_foreign_server catalog
+ stores foreign server definitions. A foreign server describes the
+ connection to a remote server, managing external data. Foreign
+ servers are accessed via foreign data wrappers.
+
+
+
+ pg_foreign_server> Columns
+
+
+
+
+ Name
+ Type
+ References
+ Description
+
+
+
+
+
+ srvname
+ name
+
+ Name of the foreign server
+
+
+
+ srvnamespace
+ oid
+ pg_namespace.oid
+
+ The OID of the namespace that contains this foreign server
+
+
+
+
+ srvowner
+ oid
+ pg_authid.oid
+ Owner of the foreign server
+
+
+
+ srvfdw
+ oid
+ pg_foreign_data_wrapper.oid
+ The OID of the foreign data wrapper of this foreign server
+
+
+
+ srvtype
+ text
+
+ Type of the server (optional)
+
+
+
+ srvversion
+ text
+
+ Version of the server (optional)
+
+
+
+ srvacl
+ aclitem[]
+
+
+ Access privileges; see
+ and
+
+ for details
+
+
+
+
+ srvoptions
+ text[]
+
+
+ Foreign server specific options, as keyword=value> strings.
+
+
+
+
+
+
+
+
+
+
+ pg_foreign_user_mapping
+
+
+ pg_foreign_user_mapping
+
+
+
+ The catalog pg_foreign_user_mapping catalog
+ stores the mappings from local user to remote. Access to this catalog
+ is restricted from normal users, use the view
+ pg_user_mapping
+ instead.
+
+
+
+ pg_foreign_user_mapping> Columns
+
+
+
+
+ Name
+ Type
+ References
+ Description
+
+
+
+
+
+ usesysid
+ oid
+ pg_authid.oid
+ OID of the local role to be mapped, 0 if the user mapping is public
+
+
+
+ usesrv
+ oid
+ pg_foreign_server.oid
+
+ The OID of the foreign server that contains this mapping
+
+
+
+
+ useoptions
+ text[]
+
+
+ Foreign user mapping specific options, as keyword=value> strings.
+
+
+
+
+
+
+
+
+
pg_index
***************
*** 6998,7003 ****
--- 7257,7336 ----
+
+ pg_user_mapping
+
+
+ pg_user_mapping
+
+
+
+ The view pg_user_mapping provides access to
+ information about foreign user mappings. This is essentially a publicly
+ readable view of
+ pg_foreign_user_mapping
+ that leaves out the options field.
+
+
+
+ pg_user_mapping> Columns
+
+
+
+
+ Name
+ Type
+ References
+ Description
+
+
+
+
+
+ umid
+ oid
+ pg_foreign_user_mapping.oid
+ OID of the foreign user mapping
+
+
+
+ srvoid
+ oid
+ pg_foreign_server.oid
+
+ The OID of the foreign server that contains this mapping
+
+
+
+
+ srvname
+ text
+
+
+ Name of the foreign server
+
+
+
+
+ usesysid
+ oid
+ pg_authid.oid
+ OID of the local role to be mapped, 0 if the user mapping is public
+
+
+
+ usename
+ name
+
+ Name of the local user to be mapped
+
+
+
+
+
+
+
+
pg_views
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 12974,12979 **** SELECT (pg_stat_file('filename')).modification;
--- 12974,13073 ----
at session end, even if the client disconnects ungracefully.)
+
+ The functions shown in provide
+ information about connections to foreign databases.
+
+
+
+ Remote Connection Information Functions
+
+
+ NameReturn TypeDescription
+
+
+
+
+
+
+ pg_get_remote_connection_info(server_name> text>)
+
+ setof record
+ Fetch information about connection to remote database
+
+
+
+ pg_get_remote_connection_info(server_name> text>, usernametext>)
+
+ setof record
+ Fetch information about connection to remote database, use the mapping for specified username
+
+
+
+ pg_get_foreign_data_wrapper_options(fdwid> oid>)
+
+ setof record
+ Fetch options for foreign data wrapper
+
+
+
+ pg_get_foreign_server_options(srvid> oid>)
+
+ setof record
+ Fetch options for foreign server
+
+
+
+ pg_get_user_mapping_options(umid> oid>)
+
+ setof record
+ Fetch options for foreign user mapping
+
+
+
+
+
+
+ pg_get_remote_connection_info
+
+
+ pg_get_remote_connection_info> fetches the connection
+ information for the specified foreign server. The options are processed
+ through server's foreign data wrapper to form a valid connection string.
+ User mapping information is used to complement the server connection options.
+ In the two parameter version the mapping of the specified user name is used,
+ otherwise the mapping for current user is used. As the output may contain
+ information about remote user names and passwords, users must explicitly be
+ granted access to this function.
+
+
+
+ pg_get_foreign_data_wrapper_options
+
+
+ pg_get_foreign_data_wrapper_options> fetches the generic options
+ associated with the foreign data wrapper.
+
+
+
+ pg_get_foreign_server_options
+
+
+ pg_get_foreign_server_options> fetches the generic options
+ associated with the foreign server. Typically this includes the connection
+ information to the remote host and database.
+
+
+
+ pg_get_user_mapping_options
+
+
+ pg_get_user_mapping_options> fetches the generic options
+ associated with the user mapping. Typically this includes the username
+ and password of the remote user. The calling user must be the owner of
+ the server of the mapping, otherwise an empty set is returned.
+
+
*** a/doc/src/sgml/keywords.sgml
--- b/doc/src/sgml/keywords.sgml
***************
*** 2688,2693 ****
--- 2688,2700 ----
reserved
+ LIBRARY
+ non-reserved
+
+
+
+
+ LIKEreserved (can be function or type)reserved
***************
*** 2817,2823 ****
MAPPING
! non-reserved
--- 2824,2830 ----
MAPPING
! reserved
***************
*** 3441,3447 ****
OPTIONS
! non-reservednon-reservednon-reserved
--- 3448,3454 ----
OPTIONS
! non-reservednon-reservednon-reservednon-reserved
***************
*** 4456,4461 ****
--- 4463,4475 ----
non-reserved
+ SERVER
+
+
+
+
+
+ SERVER_NAMEnon-reserved
***************
*** 5584,5589 ****
--- 5598,5610 ----
reserved
+ WRAPPER
+ non-reserved
+
+
+
+
+ WRITEnon-reservednon-reserved
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
***************
*** 11,16 **** Complete list of usable sgml source files in this directory.
--- 11,17 ----
+
***************
*** 19,24 **** Complete list of usable sgml source files in this directory.
--- 20,26 ----
+
***************
*** 29,34 **** Complete list of usable sgml source files in this directory.
--- 31,37 ----
+
***************
*** 45,50 **** Complete list of usable sgml source files in this directory.
--- 48,54 ----
+
***************
*** 56,61 **** Complete list of usable sgml source files in this directory.
--- 60,66 ----
+
***************
*** 66,71 **** Complete list of usable sgml source files in this directory.
--- 71,77 ----
+
***************
*** 77,82 **** Complete list of usable sgml source files in this directory.
--- 83,89 ----
+
***************
*** 88,93 **** Complete list of usable sgml source files in this directory.
--- 95,101 ----
+
***************
*** 97,102 **** Complete list of usable sgml source files in this directory.
--- 105,111 ----
+
*** /dev/null
--- b/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml
***************
*** 0 ****
--- 1,130 ----
+
+
+
+
+ ALTER FOREIGN DATA WRAPPER
+ SQL - Language Statements
+
+
+
+ ALTER FOREIGN DATA WRAPPER
+ change the definition of a foreign data wrapper
+
+
+
+ ALTER FOREIGN DATA WRAPPER
+
+
+
+
+ ALTER FOREIGN DATA WRAPPER name
+ LIBRARY 'libraryname'
+ [ OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ]) ]
+ ALTER FOREIGN DATA WRAPPER name OWNER TO new_owner
+
+
+
+
+ Description
+
+
+ ALTER FOREIGN DATA WRAPPER changes the definition of a foreign data wrapper.
+
+
+
+ The first form of the command changes the library or the generic options of the foreign data wrapper.
+ The second form changes the owner of the data wrapper.
+
+
+
+ Only superusers can alter foreign data wrappers. Additionally only superusers can own foreign
+ data wrappers.
+
+
+
+
+ Parameters
+
+
+
+ name
+
+
+ The name (optionally schema-qualified) of an existing foreign data wrapper.
+
+
+
+
+
+ libraryname
+
+
+ New name of the foreign data wrapper library.
+
+
+
+ It is possible that after changing the library, the options to
+ the foreign data wrapper, servers and user mappings have become invalid.
+ It is up to the user to make sure that these options are correct, before
+ using the foreign data wrapper.
+
+
+
+
+
+
+
+ OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] )
+
+
+ Change options for the foreign data wrapper. ADD, SET or DROP specify the
+ action to be performed. ADD is assumed if no operation is explicitly specified.
+ Option names must be unique, names and values are also validated using
+ the foreign data wrapper library.
+
+
+
+
+
+
+
+
+ Examples
+
+
+ Change a foreign data wrapper dbi>, add option foo>, drop bar>:
+
+ ALTER FOREIGN DATA WRAPPER dbi OPTIONS (ADD foo '1', DROP 'bar');
+
+
+
+
+ Change the foreign data wrapper test.mywrapper> library to /home/bob/mylibrary.so>:
+
+ ALTER FOREIGN DATA WRAPPER test.mywrapper LIBRARY '/home/bob/mylibrary.so';
+
+
+
+
+
+ Compatibility
+
+
+ ALTER FOREIGN DATA WRAPPER conforms to ISO/IEC 9075-9 (SQL/MED). The standard
+ does not specify the OWNER TO> variant of the command.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/alter_server.sgml
***************
*** 0 ****
--- 1,117 ----
+
+
+
+
+ ALTER SERVER
+ SQL - Language Statements
+
+
+
+ ALTER SERVER
+ change the definition of a foreign server
+
+
+
+ ALTER SERVER
+
+
+
+
+ ALTER SERVER servername [ VERSION 'newversion' ]
+ [ OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) ]
+ ALTER SERVER servername OWNER TO new_owner
+
+
+
+
+ Description
+
+
+ ALTER SERVER changes the definition of a foreign server.
+
+
+
+ The first form changes the server version string or the generic options of the server.
+ The second form changes the owner of the server. To alter the server you must be the
+ owner of the server. Additionally to alter the owner, you must own the
+ server and also be a direct or indirect member of the new owning role, and you
+ must have USAGE privilege on the server's foreign data wrapper. (Note that
+ superusers have all these privileges automatically.)
+
+
+
+
+ Parameters
+
+
+
+ servername
+
+
+ The name (optionally schema-qualified) of an existing server.
+
+
+
+
+
+ serverversion
+
+
+ New server version.
+
+
+
+
+
+ OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] )
+
+
+ Change options for the server. ADD, SET or DROP specify the action to be
+ performed. ADD is assumed if no operation is explicitly specified.
+ Option names must be unique, names and values are also validated using
+ the server's foreign data wrapper library.
+
+
+
+
+
+
+
+ Examples
+
+
+ Alter server foo>, add connection options:
+
+ ALTER SERVER foo OPTIONS (host 'foo', dbname 'foodb');
+
+
+
+
+ Alter server test.myserver>, change version, change host> option:
+
+ ALTER SERVER test.myserver VERSION '8.4' OPTIONS (SET host 'baz');
+
+
+
+
+
+ Compatibility
+
+
+ ALTER SERVER conforms to ISO/IEC 9075-9 (SQL/MED).
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/alter_user_mapping.sgml
***************
*** 0 ****
--- 1,106 ----
+
+
+
+
+ ALTER USER MAPPING
+ SQL - Language Statements
+
+
+
+ ALTER USER MAPPING
+ change the definition of a user mapping
+
+
+
+ ALTER USER MAPPING
+
+
+
+
+ ALTER USER MAPPING { username | USER | CURRENT_USER | PUBLIC }
+ SERVER servername
+ OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] )
+
+
+
+
+ Description
+
+
+ ALTER USER MAPPING changes the definition of a user mapping.
+ Only the owner of the server can change the user mappings of that server.
+
+
+
+
+
+ Parameters
+
+
+
+ username
+
+
+ User name of the mapping. CURRENT_USER> and USER> match
+ the name of the current user. PUBLIC> is used to match all present
+ and future user names in the system.
+
+
+
+
+
+ servername
+
+
+ Server name (optionally schema qualified) of the user mapping.
+
+
+
+
+
+ OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] )
+
+
+ Change options for the user mapping. The new options override any
+ previously specified options. Option names must be unique, options are
+ also validated by the server's foreign data wrapper.
+
+
+
+
+
+
+
+
+ Examples
+
+
+ Change the password for user mapping bob>, server foo>:
+
+ ALTER USER MAPPING bob SERVER foo OPTIONS (user 'bob', password 'public');
+
+
+
+
+
+
+ Compatibility
+
+
+ ALTER USER MAPPING conforms to ISO/IEC 9075-9 (SQL/MED).
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/create_foreign_data_wrapper.sgml
***************
*** 0 ****
--- 1,126 ----
+
+
+
+
+ CREATE FOREIGN DATA WRAPPER
+ SQL - Language Statements
+
+
+
+ CREATE FOREIGN DATA WRAPPER
+ define a new foreign data wrapper
+
+
+
+ CREATE FOREIGN DATA WRAPPER
+
+
+
+
+ CREATE FOREIGN DATA WRAPPER name
+ LIBRARY 'libraryname'
+ [ OPTIONS ( option 'value' [, ... ] ) ]
+
+
+
+
+ Description
+
+
+ CREATE FOREIGN DATA WRAPPER creates a new foreign data wrapper. The
+ user who defines a foreign data wrapper becomes its owner.
+
+
+
+ If a schema name is given then the foreign data wrapper is created in the
+ specified schema. Otherwise it is created in the current schema. The foreign
+ data wrapper name must be unique among the foreign data wrappers existing in
+ its schema.
+
+
+
+ Only superusers can create foreign data wrappers.
+
+
+
+
+ Parameters
+
+
+
+ name
+
+
+ The name (optionally schema qualified) of the foreign data wrapper to be created.
+
+
+
+
+
+ libraryname
+
+
+ The name of the shared library implementing the foreign data wrapper.
+
+
+
+
+
+ OPTIONS ( option ['value'] [, ... ] )
+
+
+ This clause specifies options for the new foreign data wrapper. The allowed option
+ names and values are specific to each foreign data wrapper and are validated using
+ the foreign data wrapper library. Option names must be unique.
+
+
+
+
+
+
+
+
+ Examples
+
+
+ Create a foreign data wrapper dbi> with library default_fdw>:
+
+ CREATE FOREIGN DATA WRAPPER dbi LIBRARY 'default_fdw';
+
+
+
+
+ Create a foreign data wrapper mywrapper> in schema test> with library
+ /home/bob/mywrapper.so> and some options:
+
+ CREATE FOREIGN DATA WRAPPER test.mywrapper LIBRARY '/home/bob/mywrapper.so'
+ OPTIONS (debug 'true');
+
+
+
+
+
+
+ Compatibility
+
+
+ CREATE FOREIGN DATA WRAPPER conforms to ISO/IEC 9075-9 (SQL/MED), with
+ the exception that language clause has been omitted.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/create_server.sgml
***************
*** 0 ****
--- 1,142 ----
+
+
+
+
+ CREATE SERVER
+ SQL - Language Statements
+
+
+
+ CREATE SERVER
+ define a new foreign server
+
+
+
+ CREATE SERVER
+
+
+
+
+ CREATE SERVER servername [ TYPE 'servertype' ] [ VERSION 'serverversion' ]
+ FOREIGN DATA WRAPPER fdwname
+ [ OPTIONS ( option 'value' [, ... ] ) ]
+
+
+
+
+ Description
+
+
+ CREATE SERVER defines a new foreign server. The user who defines
+ the server becomes its owner.
+
+
+
+ If a schema name is given then the foreign server is created in the
+ specified schema. Otherwise it is created in the current schema. The server
+ name must be unique among the servers existing in its schema.
+
+
+
+ USAGE> privilege is required on the FOREIGN DATA WRAPPER> to
+ define servers using it.
+
+
+
+
+
+ Parameters
+
+
+
+ servername
+
+
+ The name (optionally schema qualified) of the foreign server to be created.
+
+
+
+
+
+ servertype
+
+
+ Optional server type.
+
+
+
+
+
+ serverversion
+
+
+ Optional server version.
+
+
+
+
+
+ fdwname
+
+
+ The name (optionally schema qualified) of the foreign data wrapper that manages the server.
+
+
+
+
+
+ OPTIONS ( option ['value'] [, ... ] )
+
+
+ This clause specifies the options for the server. The options typically
+ define the connection details of the server, but the actual names and
+ values are dependent on the server's foreign data wrapper.
+
+
+
+
+
+
+
+
+ Examples
+
+
+ Create a server foo> that uses the builtin foreign data wrapperdefault>:
+
+ CREATE SERVER foo FOREIGN DATA WRAPPER "default";
+
+
+
+
+ Create a server test.myserver> that uses the foreign data wrapperpgsql>:
+
+ CREATE SERVER test.myserver FOREIGN DATA WRAPPER pgsql OPTIONS (host 'foo', dbname 'foodb', port '5432');
+
+
+
+
+
+
+ Compatibility
+
+
+ CREATE SERVER conforms to ISO/IEC 9075-9 (SQL/MED).
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/create_user_mapping.sgml
***************
*** 0 ****
--- 1,108 ----
+
+
+
+
+ CREATE USER MAPPING
+ SQL - Language Statements
+
+
+
+ CREATE USER MAPPING
+ define a new user mapping to foreign server
+
+
+
+ CREATE USER MAPPING
+
+
+
+
+ CREATE USER MAPPING FOR { username | USER | CURRENT_USER | PUBLIC }
+ SERVER servername
+ [ OPTIONS ( option 'value' [ , ... ] ) ]
+
+
+
+
+ Description
+
+
+ CREATE USER MAPPING defines a mapping of a user to a foreign server.
+ You must be the owner of the server to define user mappings for it.
+
+
+
+
+
+ Parameters
+
+
+
+ username
+
+
+ The name of an existing user that is mapped to foreign server.
+
+
+
+
+
+ username
+
+
+ The name of an existing user that is mapped to foreign server.
+ CURRENT_USER> and USER> match the name of the current user.
+ PUBLIC> is used to match all present and future user names in the system.
+
+
+
+
+
+ OPTIONS ( option ['value'] [, ... ] )
+
+
+ This clause specifies the options of the user mapping. The options typically define the
+ actual username and password of the mapping. Option names must be unque, the allowed option
+ names and values are specific to servers foreign data wrapper.
+
+
+
+
+
+
+
+
+ Examples
+
+
+ Create a user mapping for user bob>, server foo>:
+
+ CREATE USER MAPPING FOR bob SERVER foo OPTIONS (user 'bob', password 'secret');
+
+
+
+
+
+
+ Compatibility
+
+
+ CREATE USER MAPPING conforms to ISO/IEC 9075-9 (SQL/MED).
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/drop_foreign_data_wrapper.sgml
***************
*** 0 ****
--- 1,112 ----
+
+
+
+
+ DROP FOREIGN DATA WRAPPER
+ SQL - Language Statements
+
+
+
+ DROP FOREIGN DATA WRAPPER
+ remove a foreign data wrapper
+
+
+
+ DROP FOREIGN DATA WRAPPER
+
+
+
+
+ DROP FOREIGN DATA WRAPPER [ IF EXISTS ] name [ CASCADE | RESTRICT ]
+
+
+
+
+ Description
+
+
+ DROP FOREIGN DATA WRAPPER removes an existing foreign data wrapper.
+ To execute this command the current user must be the owner of the foreign data wrapper.
+
+
+
+
+
+ Parameters
+
+
+
+
+ IF EXISTS
+
+
+ Do not throw an error if the foreign data wrapper does not exist. A notice is issued
+ in this case.
+
+
+
+
+
+ name
+
+
+ The name (optionally schema qualified) of an existing foreign data wrapper.
+
+
+
+
+
+ CASCADE
+
+
+ Automatically drop objects that depend on the foreign data wrapper (such as servers).
+
+
+
+
+
+ RESTRICT
+
+
+ Refuse to drop the foreign data wrappers if any objects depend on
+ it. This is the default.
+
+
+
+
+
+
+
+ Examples
+
+
+ Drop the foreign data wrapper dbi>:
+
+ DROP FOREIGN DATA WRAPPER dbi;
+
+
+
+
+
+
+ Compatibility
+
+
+ DROP FOREIGN DATA WRAPPER conforms to ISO/IEC 9075-9 (SQL/MED).
+ The IF EXISTS> clause is a PostgreSQL> extension.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/drop_server.sgml
***************
*** 0 ****
--- 1,114 ----
+
+
+
+
+ DROP SERVER
+ SQL - Language Statements
+
+
+
+ DROP SERVER
+ remove a foreign server
+
+
+
+ DROP SERVER
+
+
+
+
+ DROP SERVER [ IF EXISTS ] servername [ CASCADE | RESTRICT ]
+
+
+
+
+ Description
+
+
+ DROP SERVER removes an existing foreign server.
+ To execute this command the current user must be the owner of the server.
+
+
+
+
+
+
+ Parameters
+
+
+
+
+ IF EXISTS
+
+
+ Do not throw an error if the server does not exist. A notice is issued
+ in this case.
+
+
+
+
+
+ servername
+
+
+ The name (optionally schema qualified) of an existing server.
+
+
+
+
+
+ CASCADE
+
+
+ Automatically drop objects that depend on the server (such as user mappings).
+
+
+
+
+
+ RESTRICT
+
+
+ Refuse to drop the server if any objects depend on
+ it. This is the default.
+
+
+
+
+
+
+
+
+ Examples
+
+
+ Drop a server foo> if it exists:
+
+ DROP SERVER IF EXISTS foo;
+
+
+
+
+
+
+ Compatibility
+
+
+ DROP SERVER conforms to ISO/IEC 9075-9 (SQL/MED).
+ The IF EXISTS> clause is a PostgreSQL> extension.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/drop_user_mapping.sgml
***************
*** 0 ****
--- 1,106 ----
+
+
+
+
+ DROP USER MAPPING
+ SQL - Language Statements
+
+
+
+ DROP USER MAPPING
+ remove a user mapping from foreign server
+
+
+
+ DROP USER MAPPING
+
+
+
+
+ DROP USER MAPPING [ IF EXISTS ] FOR { username | USER | CURRENT_USER | PUBLIC } SERVER servername
+
+
+
+
+ Description
+
+
+ DROP USER MAPPING removes an existing user mapping from foreign server.
+ To execute this command the current user must be the owner of the server containing
+ the mapping.
+
+
+
+
+
+ Parameters
+
+
+
+
+ IF EXISTS
+
+
+ Do not throw an error if the user mapping does not exist. A notice is issued
+ in this case.
+
+
+
+
+
+ username
+
+
+ User name of the mapping. CURRENT_USER> and USER> match
+ the name of the current user. PUBLIC> is used to match all present
+ and future user names in the system.
+
+
+
+
+
+ servername
+
+
+ Server name (optionally schema qualified) of the user mapping.
+
+
+
+
+
+
+
+
+ Examples
+
+
+ Drop a user mapping bob>, server foo> if it exists:
+
+ DROP USER MAPPING IF EXISTS FOR bob SERVER foo;
+
+
+
+
+
+
+ Compatibility
+
+
+ DROP USER MAPPING conforms to ISO/IEC 9075-9 (SQL/MED).
+ The IF EXISTS> clause is a PostgreSQL> extension.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
*** a/doc/src/sgml/ref/grant.sgml
--- b/doc/src/sgml/ref/grant.sgml
***************
*** 51,56 **** GRANT { CREATE | ALL [ PRIVILEGES ] }
--- 51,64 ----
ON TABLESPACE tablespacename> [, ...]
TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+ GRANT { USAGE | ALL [ PRIVILEGES ] }
+ ON FOREIGN DATA WRAPPER fdwname> [, ...]
+ TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
+ GRANT { USAGE | ALL [ PRIVILEGES ] }
+ ON SERVER servername> [, ...]
+ TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
GRANT role [, ...] TO rolename [, ...] [ WITH ADMIN OPTION ]
***************
*** 61,70 **** GRANT role [, ...] TO
The GRANT command has two basic variants: one
that grants privileges on a database object (table, view, sequence,
! database, function, procedural language, schema, or tablespace),
! and one that grants membership in a role. These variants are
! similar in many ways, but they are different enough to be described
! separately.
--- 69,78 ----
The GRANT command has two basic variants: one
that grants privileges on a database object (table, view, sequence,
! database, function, procedural language, schema, foreign data wrapper,
! server or tablespace), and one that grants membership in a role. These
! variants are similar in many ways, but they are different enough to be
! described separately.
***************
*** 299,304 **** GRANT role [, ...] TO currval and nextval functions.
+
+ For foreign data wrappers, this privilege enables the grantee to create
+ new servers using that foreign data wrapper.
+
+
+ For servers, this privilege enables the grantee look up connection information
+ for the server.
+
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
***************
*** 1089,1094 **** testdb=>
--- 1089,1116 ----
+ \dm [ pattern ]
+ \dm+ [ pattern ]
+
+
+ Lists all user mappings. If pattern
+ is specified, only those mappings whose user names match the pattern are listed.
+ If the form \dm+ is used, additional information about each
+ mapping is shown.
+
+
+
+
+ \dm+ might also display the username and password of the
+ remote user, so precautions should be taken not to disclose them.
+
+
+
+
+
+
+
+ \dn [ pattern ]\dn+ [ pattern ]
***************
*** 1137,1142 **** testdb=>
--- 1159,1178 ----
+ \dr [ pattern ]
+ \dr+ [ pattern ]
+
+
+ Lists all foreign servers (remote). If pattern
+ is specified, only those servers whose name matches the pattern are listed.
+ If the form \dr+ is used, a full desription of each server is shown,
+ including the server's acl, type, version and connection options.
+
+
+
+
+
+ \dT [ pattern ]\dT+ [ pattern ]
***************
*** 1162,1167 **** testdb=>
--- 1198,1216 ----
+ \dw [ pattern ]
+
+
+ Lists all foreign data wrappers. If pattern
+ is specified, only those foreign data wrappers whose name matches the pattern are listed.
+ If the form \dw+ is used, also the acl and options to the foreign data
+ wrapper are shown.
+
+
+
+
+
+ \edit (or \e) filename
*** a/doc/src/sgml/ref/revoke.sgml
--- b/doc/src/sgml/ref/revoke.sgml
***************
*** 65,70 **** REVOKE [ GRANT OPTION FOR ]
--- 65,82 ----
FROM { [ GROUP ] rolename | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
+ REVOKE [ GRANT OPTION FOR ]
+ { USAGE | ALL [ PRIVILEGES ] }
+ ON FOREIGN DATA WRAPPER fdwname [, ...]
+ FROM { [ GROUP ] rolename | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
+
+ REVOKE [ GRANT OPTION FOR ]
+ { USAGE | ALL [ PRIVILEGES ] }
+ ON SERVER servername [, ...]
+ FROM { [ GROUP ] rolename | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
+
REVOKE [ ADMIN OPTION FOR ]
role [, ...] FROM rolename [, ...]
[ CASCADE | RESTRICT ]
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 38,43 ****
--- 38,44 ----
&alterConversion;
&alterDatabase;
&alterDomain;
+ &alterForeignDataWrapper;
&alterFunction;
&alterGroup;
&alterIndex;
***************
*** 48,53 ****
--- 49,55 ----
&alterRole;
&alterSchema;
&alterSequence;
+ &alterServer;
&alterTable;
&alterTableSpace;
&alterTSConfig;
***************
*** 57,62 ****
--- 59,65 ----
&alterTrigger;
&alterType;
&alterUser;
+ &alterUserMapping;
&alterView;
&analyze;
&begin;
***************
*** 73,78 ****
--- 76,82 ----
&createConversion;
&createDatabase;
&createDomain;
+ &createForeignDataWrapper;
&createFunction;
&createGroup;
&createIndex;
***************
*** 84,89 ****
--- 88,94 ----
&createRule;
&createSchema;
&createSequence;
+ &createServer;
&createTable;
&createTableAs;
&createTableSpace;
***************
*** 94,99 ****
--- 99,105 ----
&createTrigger;
&createType;
&createUser;
+ &createUserMapping;
&createView;
&deallocate;
&declare;
***************
*** 104,109 ****
--- 110,116 ----
&dropConversion;
&dropDatabase;
&dropDomain;
+ &dropForeignDataWrapper;
&dropFunction;
&dropGroup;
&dropIndex;
***************
*** 116,121 ****
--- 123,129 ----
&dropRule;
&dropSchema;
&dropSequence;
+ &dropServer;
&dropTable;
&dropTableSpace;
&dropTSConfig;
***************
*** 125,130 ****
--- 133,139 ----
&dropTrigger;
&dropType;
&dropUser;
+ &dropUserMapping;
&dropView;
&end;
&execute;
*** a/src/Makefile
--- b/src/Makefile
***************
*** 19,24 **** all install installdirs uninstall distprep:
--- 19,26 ----
$(MAKE) -C backend $@
$(MAKE) -C backend/utils/mb/conversion_procs $@
$(MAKE) -C backend/snowball $@
+ $(MAKE) -C backend/foreign/default $@
+ $(MAKE) -C backend/foreign/pgsql $@
$(MAKE) -C include $@
$(MAKE) -C interfaces $@
$(MAKE) -C bin $@
*** a/src/backend/Makefile
--- b/src/backend/Makefile
***************
*** 16,22 **** include $(top_builddir)/src/Makefile.global
SUBDIRS = access bootstrap catalog parser commands executor lib libpq \
main nodes optimizer port postmaster regex rewrite \
! storage tcop tsearch utils $(top_builddir)/src/timezone
include $(srcdir)/common.mk
--- 16,22 ----
SUBDIRS = access bootstrap catalog parser commands executor lib libpq \
main nodes optimizer port postmaster regex rewrite \
! storage tcop tsearch utils foreign $(top_builddir)/src/timezone
include $(srcdir)/common.mk
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
***************
*** 36,41 **** POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
--- 36,42 ----
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
pg_ts_parser.h pg_ts_template.h \
+ pg_fdw.h pg_foreign_server.h pg_user_mapping.h \
toasting.h indexing.h \
)
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
***************
*** 37,46 ****
--- 37,49 ----
#include "catalog/pg_type.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
+ #include "catalog/pg_fdw.h"
+ #include "catalog/pg_foreign_server.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
#include "utils/acl.h"
+ #include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
***************
*** 54,59 **** static void ExecGrant_Function(InternalGrant *grantStmt);
--- 57,64 ----
static void ExecGrant_Language(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
+ static void ExecGrant_Fdw(InternalGrant *grantStmt);
+ static void ExecGrant_ForeignServer(InternalGrant *grantStmt);
static List *objectNamesToOids(GrantObjectType objtype, List *objnames);
static AclMode string_to_privilege(const char *privname);
***************
*** 188,193 **** restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
--- 193,204 ----
case ACL_KIND_TABLESPACE:
whole_mask = ACL_ALL_RIGHTS_TABLESPACE;
break;
+ case ACL_KIND_FDW:
+ whole_mask = ACL_ALL_RIGHTS_FDW;
+ break;
+ case ACL_KIND_FOREIGN_SERVER:
+ whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
+ break;
default:
elog(ERROR, "unrecognized object kind: %d", objkind);
/* not reached, but keep compiler quiet */
***************
*** 323,328 **** ExecuteGrantStmt(GrantStmt *stmt)
--- 334,347 ----
all_privileges = ACL_ALL_RIGHTS_TABLESPACE;
errormsg = gettext_noop("invalid privilege type %s for tablespace");
break;
+ case ACL_OBJECT_FDW:
+ all_privileges = ACL_ALL_RIGHTS_FDW;
+ errormsg = gettext_noop("invalid privilege type %s for foreign data wrapper");
+ break;
+ case ACL_OBJECT_FOREIGN_SERVER:
+ all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
+ errormsg = gettext_noop("invalid privilege type %s for foreign server");
+ break;
default:
/* keep compiler quiet */
all_privileges = ACL_NO_RIGHTS;
***************
*** 392,397 **** ExecGrantStmt_oids(InternalGrant *istmt)
--- 411,422 ----
case ACL_OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
+ case ACL_OBJECT_FDW:
+ ExecGrant_Fdw(istmt);
+ break;
+ case ACL_OBJECT_FOREIGN_SERVER:
+ ExecGrant_ForeignServer(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
***************
*** 520,525 **** objectNamesToOids(GrantObjectType objtype, List *objnames)
--- 545,566 ----
heap_close(relation, AccessShareLock);
}
break;
+ case ACL_OBJECT_FDW:
+ foreach(cell, objnames)
+ {
+ Oid fdwid = ForeignDataWrapperNameGetId(lfirst(cell), false);
+
+ objects = lappend_oid(objects, fdwid);
+ }
+ break;
+ case ACL_OBJECT_FOREIGN_SERVER:
+ foreach(cell, objnames)
+ {
+ Oid srvid = ForeignServerNameGetServerid(lfirst(cell), false);
+
+ objects = lappend_oid(objects, srvid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
***************
*** 1319,1324 **** ExecGrant_Tablespace(InternalGrant *istmt)
--- 1360,1597 ----
heap_close(relation, RowExclusiveLock);
}
+ static void
+ ExecGrant_Fdw(InternalGrant *istmt)
+ {
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_FDW;
+
+ relation = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid fdwid = lfirst_oid(cell);
+ Form_pg_foreign_data_wrapper pg_fdw_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_fdw];
+ char nulls[Natts_pg_fdw];
+ char replaces[Natts_pg_fdw];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdwid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for foreign data wrapper %u", fdwid);
+
+ pg_fdw_tuple = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_fdw_tuple->fdwowner;
+ aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple,
+ Anum_pg_fdw_fdwacl,
+ &isNull);
+ if (isNull)
+ old_acl = acldefault(ACL_OBJECT_FDW, ownerId);
+ else
+ old_acl = DatumGetAclPCopy(aclDatum);
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ fdwid, grantorId, ACL_KIND_FDW,
+ NameStr(pg_fdw_tuple->fdwname));
+
+ /*
+ * Generate new ACL.
+ *
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, ' ', sizeof(nulls));
+ MemSet(replaces, ' ', sizeof(replaces));
+
+ replaces[Anum_pg_fdw_fdwacl - 1] = 'r';
+ values[Anum_pg_fdw_fdwacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(relation, newtuple);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(ForeignDataWrapperRelationId, HeapTupleGetOid(tuple),
+ ownerId, istmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+ }
+
+ static void ExecGrant_ForeignServer(InternalGrant *istmt)
+ {
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
+
+ relation = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid srvid = lfirst_oid(cell);
+ Form_pg_foreign_server pg_server_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_foreign_server];
+ char nulls[Natts_pg_foreign_server];
+ char replaces[Natts_pg_foreign_server];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache(FOREIGNSERVEROID,
+ ObjectIdGetDatum(srvid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for foreign server %u", srvid);
+
+ pg_server_tuple = (Form_pg_foreign_server) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_server_tuple->srvowner;
+ aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple,
+ Anum_pg_foreign_server_srvacl,
+ &isNull);
+ if (isNull)
+ old_acl = acldefault(ACL_OBJECT_FOREIGN_SERVER, ownerId);
+ else
+ old_acl = DatumGetAclPCopy(aclDatum);
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ srvid, grantorId, ACL_KIND_FOREIGN_SERVER,
+ NameStr(pg_server_tuple->srvname));
+
+ /*
+ * Generate new ACL.
+ *
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, ' ', sizeof(nulls));
+ MemSet(replaces, ' ', sizeof(replaces));
+
+ replaces[Anum_pg_foreign_server_srvacl - 1] = 'r';
+ values[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(relation, newtuple);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(ForeignServerRelationId, HeapTupleGetOid(tuple),
+ ownerId, istmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+ }
static AclMode
string_to_privilege(const char *privname)
***************
*** 1428,1434 **** static const char *const no_priv_msg[MAX_ACL_KIND] =
/* ACL_KIND_TSDICTIONARY */
gettext_noop("permission denied for text search dictionary %s"),
/* ACL_KIND_TSCONFIGURATION */
! gettext_noop("permission denied for text search configuration %s")
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
--- 1701,1711 ----
/* ACL_KIND_TSDICTIONARY */
gettext_noop("permission denied for text search dictionary %s"),
/* ACL_KIND_TSCONFIGURATION */
! gettext_noop("permission denied for text search configuration %s"),
! /* ACL_KIND_FDW */
! gettext_noop("permission denied for foreign data wrapper %s"),
! /* ACL_KIND_FOREIGN_SERVER */
! gettext_noop("permission denied for foreign server %s")
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
***************
*** 1460,1466 **** static const char *const not_owner_msg[MAX_ACL_KIND] =
/* ACL_KIND_TSDICTIONARY */
gettext_noop("must be owner of text search dictionary %s"),
/* ACL_KIND_TSCONFIGURATION */
! gettext_noop("must be owner of text search configuration %s")
};
--- 1737,1747 ----
/* ACL_KIND_TSDICTIONARY */
gettext_noop("must be owner of text search dictionary %s"),
/* ACL_KIND_TSCONFIGURATION */
! gettext_noop("must be owner of text search configuration %s"),
! /* ACL_KIND_FDW */
! gettext_noop("must be owner of foreign data wrapper %s"),
! /* ACL_KIND_FOREIGN_SERVER */
! gettext_noop("must be owner of foreign server %s")
};
***************
*** 1534,1539 **** pg_aclmask(AclObjectKind objkind, Oid table_oid, Oid roleid,
--- 1815,1824 ----
return pg_namespace_aclmask(table_oid, roleid, mask, how);
case ACL_KIND_TABLESPACE:
return pg_tablespace_aclmask(table_oid, roleid, mask, how);
+ case ACL_KIND_FDW:
+ return pg_fdw_aclmask(table_oid, roleid, mask, how);
+ case ACL_KIND_FOREIGN_SERVER:
+ return pg_foreign_server_aclmask(table_oid, roleid, mask, how);
default:
elog(ERROR, "unrecognized objkind: %d",
(int) objkind);
***************
*** 1962,1967 **** pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
--- 2247,2377 ----
return result;
}
+ /*
+ * Exported routine for examining a user's privileges for a foreign
+ * data wrapper
+ */
+ AclMode
+ pg_fdw_aclmask(Oid fdw_oid, Oid roleid,
+ AclMode mask, AclMaskHow how)
+ {
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ Form_pg_foreign_data_wrapper fdwForm;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Must get the FDW's tuple from pg_foreign_data_wrapper
+ */
+ tuple = SearchSysCache(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdw_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errmsg("foreign data wrapper with OID %u does not exist",
+ fdw_oid)));
+ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
+
+ /*
+ * Normal case: get the FDW's ACL from pg_foreign_data_wrapper
+ */
+ ownerId = fdwForm->fdwowner;
+
+ aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple,
+ Anum_pg_fdw_fdwacl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(ACL_OBJECT_FDW, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+ }
+
+ /*
+ * Exported routine for examining a user's privileges for a foreign
+ * server.
+ */
+ AclMode
+ pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
+ AclMode mask, AclMaskHow how)
+ {
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ Form_pg_foreign_server srvForm;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Must get the FDW's tuple from pg_foreign_data_wrapper
+ */
+ tuple = SearchSysCache(FOREIGNSERVEROID,
+ ObjectIdGetDatum(srv_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errmsg("foreign server with OID %u does not exist",
+ srv_oid)));
+ srvForm = (Form_pg_foreign_server) GETSTRUCT(tuple);
+
+ /*
+ * Normal case: get the foreign server's ACL from pg_foreign_server
+ */
+ ownerId = srvForm->srvowner;
+
+ aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple,
+ Anum_pg_foreign_server_srvacl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(ACL_OBJECT_FOREIGN_SERVER, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+ }
/*
* Exported routine for checking a user's access privileges to a table
***************
*** 2039,2044 **** pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode)
--- 2449,2479 ----
return ACLCHECK_NO_PRIV;
}
+ /*
+ * Exported routine for checking a user's access privileges to a foreign
+ * data wrapper
+ */
+ AclResult
+ pg_fdw_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode)
+ {
+ if (pg_fdw_aclmask(fdw_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+ }
+
+ /*
+ * Exported routine for checking a user's access privileges to a foreign
+ * server
+ */
+ AclResult
+ pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode)
+ {
+ if (pg_foreign_server_aclmask(srv_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+ }
/*
* Ownership check for a relation (specified by OID).
***************
*** 2364,2369 **** pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid)
--- 2799,2832 ----
return has_privs_of_role(roleid, ownerId);
}
+ /*
+ * Ownership check for a foreign server (specified by OID).
+ */
+ bool
+ pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid)
+ {
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache(FOREIGNSERVEROID,
+ ObjectIdGetDatum(srv_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign server with OID %u does not exist",
+ srv_oid)));
+
+ ownerId = ((Form_pg_foreign_server) GETSTRUCT(tuple))->srvowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+ }
/*
* Ownership check for a database (specified by OID).
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 33,38 ****
--- 33,41 ----
#include "catalog/pg_conversion_fn.h"
#include "catalog/pg_database.h"
#include "catalog/pg_depend.h"
+ #include "catalog/pg_fdw.h"
+ #include "catalog/pg_foreign_server.h"
+ #include "catalog/pg_user_mapping.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
***************
*** 66,71 ****
--- 69,75 ----
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "foreign/foreign.h"
/*
***************
*** 1105,1110 **** doDeletion(const ObjectAddress *object)
--- 1109,1126 ----
RemoveTSConfigurationById(object->objectId);
break;
+ case OCLASS_USER_MAPPING:
+ RemoveUserMappingById(object->objectId);
+ break;
+
+ case OCLASS_FOREIGN_SERVER:
+ RemoveForeignServerById(object->objectId);
+ break;
+
+ case OCLASS_FDW:
+ RemoveForeignDataWrapperById(object->objectId);
+ break;
+
/* OCLASS_ROLE, OCLASS_DATABASE, OCLASS_TBLSPACE not handled */
default:
***************
*** 2005,2010 **** getObjectClass(const ObjectAddress *object)
--- 2021,2038 ----
case TableSpaceRelationId:
Assert(object->objectSubId == 0);
return OCLASS_TBLSPACE;
+
+ case ForeignDataWrapperRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_FDW;
+
+ case ForeignServerRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_FOREIGN_SERVER;
+
+ case ForeignUserMappingRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_USER_MAPPING;
}
/* shouldn't get here */
***************
*** 2501,2506 **** getObjectDescription(const ObjectAddress *object)
--- 2529,2578 ----
break;
}
+ case OCLASS_FDW:
+ {
+ ForeignDataWrapper *fdw;
+
+ fdw = GetForeignDataWrapper(object->objectId);
+ appendStringInfo(&buffer, _("foreign data wrapper %s"), fdw->fdwname);
+ break;
+ }
+
+ case OCLASS_FOREIGN_SERVER:
+ {
+ ForeignServer *srv;
+
+ srv = GetForeignServer(object->objectId);
+ appendStringInfo(&buffer, _("server %s"), srv->servername);
+ break;
+ }
+
+ case OCLASS_USER_MAPPING:
+ {
+ HeapTuple tup;
+ Oid useid;
+ char *usename;
+
+ tup = SearchSysCache(FOREIGNUSERMAPPINGOID,
+ ObjectIdGetDatum(object->objectId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for user mapping %u",
+ object->objectId);
+
+ useid = ((Form_pg_foreign_user_mapping) GETSTRUCT(tup))->usesysid;
+
+ ReleaseSysCache(tup);
+
+ if (OidIsValid(useid))
+ usename = GetUserNameFromId(useid);
+ else
+ usename = "public";
+
+ appendStringInfo(&buffer, _("user mapping for %s"), usename);
+ break;
+ }
+
default:
appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId,
*** a/src/backend/catalog/information_schema.sql
--- b/src/backend/catalog/information_schema.sql
***************
*** 2313,2315 **** CREATE VIEW element_types AS
--- 2313,2447 ----
FROM data_type_privileges );
GRANT SELECT ON element_types TO PUBLIC;
+
+ -- SQL/MED specific views, these use chapter numbers from part 5 of the standard.
+
+ /* Base view for foreign data wrappers */
+ CREATE VIEW _pg_foreign_data_wrappers AS
+ SELECT w.oid,
+ CAST(current_database() AS sql_identifier) AS foreign_data_wrapper_catalog,
+ CASE WHEN pg_fdw_is_visible(w.oid) THEN
+ CAST(fdwname AS sql_identifier)
+ ELSE
+ CAST(nspname || '.' || fdwname AS sql_identifier)
+ END AS foreign_data_wrapper_name,
+ CAST(u.rolname AS sql_identifier) AS authorization_identifier,
+ CAST(fdwlibrary AS character_data) AS library_name,
+ CAST(null AS character_data) AS foreign_data_wrapper_language
+ FROM pg_foreign_data_wrapper w, pg_namespace ns, pg_authid u
+ WHERE ns.oid = w.fdwnamespace
+ AND u.oid = w.fdwowner
+ AND pg_has_role(w.fdwowner, 'USAGE');
+
+ /*
+ * 24.4
+ * FOREIGN_DATA_WRAPPER_OPTIONS view
+ */
+ CREATE VIEW foreign_data_wrapper_options AS
+ SELECT foreign_data_wrapper_catalog,
+ foreign_data_wrapper_name,
+ CAST((pg_get_foreign_data_wrapper_options(w.oid)).option_name AS sql_identifier) AS option_name,
+ CAST((pg_get_foreign_data_wrapper_options(w.oid)).option_value AS sql_identifier) AS option_value
+ FROM _pg_foreign_data_wrappers w;
+
+ GRANT SELECT ON foreign_data_wrapper_options TO PUBLIC;
+
+ /*
+ * 24.5
+ * FOREIGN_DATA_WRAPPERS
+ */
+ CREATE VIEW foreign_data_wrappers AS
+ SELECT foreign_data_wrapper_catalog,
+ foreign_data_wrapper_name,
+ authorization_identifier,
+ library_name,
+ foreign_data_wrapper_language
+ FROM _pg_foreign_data_wrappers;
+
+ GRANT SELECT ON foreign_data_wrappers TO PUBLIC;
+
+ /* Base view for foreign servers */
+ CREATE VIEW _pg_foreign_servers AS
+ SELECT s.oid,
+ CAST(current_database() AS sql_identifier) AS foreign_server_catalog,
+ CASE WHEN pg_server_is_visible(s.oid) THEN
+ CAST(srvname AS sql_identifier)
+ ELSE
+ CAST(ns.nspname || '.' || srvname AS sql_identifier)
+ END AS foreign_server_name,
+ w.foreign_data_wrapper_catalog,
+ w.foreign_data_wrapper_name,
+ CAST(srvtype AS character_data) AS foreign_server_type,
+ CAST(srvversion AS character_data) AS foreign_server_version,
+ CAST(u.rolname AS sql_identifier) AS authorization_identifier
+ FROM pg_foreign_server s, _pg_foreign_data_wrappers w, pg_namespace ns, pg_authid u
+ WHERE w.oid = s.srvfdw
+ AND ns.oid = s.srvnamespace
+ AND u.oid = s.srvowner
+ AND pg_has_role(s.srvowner, 'USAGE');
+
+ /*
+ * 24.6
+ * FOREIGN_SERVER_OPTIONS view
+ */
+ CREATE VIEW foreign_server_options AS
+ SELECT foreign_server_catalog,
+ foreign_server_name,
+ CAST((pg_get_foreign_server_options(s.oid)).option_name AS character_data) AS option_name,
+ CAST((pg_get_foreign_server_options(s.oid)).option_value AS character_data) AS option_value
+ FROM _pg_foreign_servers s;
+
+ GRANT SELECT ON TABLE foreign_server_options TO PUBLIC;
+
+ /*
+ * 24.7
+ * FOREIGN_SERVERS view
+ */
+ CREATE VIEW foreign_servers AS
+ SELECT foreign_server_catalog,
+ foreign_server_name,
+ foreign_data_wrapper_catalog,
+ foreign_data_wrapper_name,
+ foreign_server_type,
+ foreign_server_version,
+ authorization_identifier
+ FROM _pg_foreign_servers;
+
+ GRANT SELECT ON foreign_servers TO PUBLIC;
+
+ /* Base view for user mappings */
+ CREATE VIEW _pg_user_mappings AS
+ SELECT fu.oid,
+ CAST(COALESCE(u.rolname,'public') AS sql_identifier ) AS authorization_identifier,
+ s.foreign_server_catalog,
+ s.foreign_server_name
+ FROM pg_foreign_user_mapping fu LEFT JOIN pg_authid u ON (u.oid = fu.usesysid),
+ _pg_foreign_servers s
+ WHERE s.oid = fu.usesrv;
+
+ /*
+ * 24.12
+ * USER_MAPPING_OPTIONS view
+ */
+ CREATE VIEW user_mapping_options AS
+ SELECT authorization_identifier,
+ foreign_server_catalog,
+ foreign_server_name,
+ CAST((pg_get_user_mapping_options(um.oid)).option_name AS character_data) AS option_name,
+ CAST((pg_get_user_mapping_options(um.oid)).option_value AS character_data) AS option_value
+ FROM _pg_user_mappings um;
+
+ GRANT SELECT ON user_mapping_options TO PUBLIC;
+
+ /*
+ * 24.13
+ * USER_MAPPINGS view
+ */
+ CREATE VIEW user_mappings AS
+ SELECT authorization_identifier,
+ foreign_server_catalog,
+ foreign_server_name
+ FROM _pg_user_mappings;
+
+ GRANT SELECT ON user_mappings TO PUBLIC;
+
*** a/src/backend/catalog/namespace.c
--- b/src/backend/catalog/namespace.c
***************
*** 25,30 ****
--- 25,32 ----
#include "catalog/pg_authid.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_conversion_fn.h"
+ #include "catalog/pg_fdw.h"
+ #include "catalog/pg_foreign_server.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
***************
*** 200,205 **** Datum pg_ts_parser_is_visible(PG_FUNCTION_ARGS);
--- 202,209 ----
Datum pg_ts_dict_is_visible(PG_FUNCTION_ARGS);
Datum pg_ts_template_is_visible(PG_FUNCTION_ARGS);
Datum pg_ts_config_is_visible(PG_FUNCTION_ARGS);
+ Datum pg_fdw_is_visible(PG_FUNCTION_ARGS);
+ Datum pg_server_is_visible(PG_FUNCTION_ARGS);
Datum pg_my_temp_schema(PG_FUNCTION_ARGS);
Datum pg_is_other_temp_schema(PG_FUNCTION_ARGS);
***************
*** 456,461 **** RelationIsVisible(Oid relid)
--- 460,688 ----
/*
+ * ForeingDataWrapperNameGetId
+ * Try to resolve an unqualified foreign data wrapper name.
+ * Returns OID if found, otherwise InvalidOid if failOK or
+ * raises an ERROR if not.
+ */
+ Oid
+ ForeignDataWrapperNameGetId(List *names, bool failOK)
+ {
+ char *schemaname;
+ char *fdwname;
+ Oid fdwId = InvalidOid;
+ Oid namespaceId;
+
+ DeconstructQualifiedName(names, &schemaname, &fdwname);
+
+ if (schemaname)
+ {
+ /* Use the provided schema name */
+ namespaceId = GetSysCacheOid(NAMESPACENAME,
+ CStringGetDatum(schemaname),
+ 0, 0, 0);
+ if (!OidIsValid(namespaceId) && failOK)
+ return InvalidOid;
+
+ LookupExplicitNamespace(schemaname);
+ fdwId = GetSysCacheOid(FOREIGNDATAWRAPPERNAMENSP,
+ CStringGetDatum(fdwname),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0);
+ }
+ else
+ {
+ ListCell *l;
+
+ /* Determine schema from search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+ fdwId = GetSysCacheOid(FOREIGNDATAWRAPPERNAMENSP,
+ CStringGetDatum(fdwname),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0);
+
+ if (OidIsValid(fdwId))
+ break;
+ }
+ }
+
+ if (!OidIsValid(fdwId) && !failOK)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign data wrapper \"%s\" does not exist",
+ NameListToString(names))));
+
+ return fdwId;
+ }
+
+ /*
+ * ForeignDataWrapperIsVisible
+ * Determine whether a foreign data wrapper (identified by OID)
+ * is visible in the current search path. Visible means "would be found
+ * by searching for the unqualified foreign data wrapper name".
+ */
+ bool
+ ForeignDataWrapperIsVisible(Oid fdwid)
+ {
+ HeapTuple tup;
+ Oid namespace;
+ bool visible;
+
+ Form_pg_foreign_data_wrapper form;
+
+ tup = SearchSysCache(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdwid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for foreign data wrapper %u",
+ fdwid);
+ form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ namespace = form->fdwnamespace;
+ if (namespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, namespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another configuration of the same name earlier in the
+ * path. So we must do a slow check for conflicting configurations.
+ */
+ char *name = NameStr(form->fdwname);
+
+ visible = (ForeignDataWrapperNameGetId(list_make1(makeString(name)),
+ true) == fdwid);
+ }
+
+ ReleaseSysCache(tup);
+
+ return visible;
+ }
+
+ /*
+ * ForeignServerNameGetServerid
+ * Try to resolve an unqualified foreign server name.
+ * Returns OID if FDW found in search path, otherwise raise ERROR.
+ */
+ Oid
+ ForeignServerNameGetServerid(List *names, bool failOK)
+ {
+ char *schemaname;
+ char *servername;
+ Oid srvId = InvalidOid;
+ Oid namespaceId;
+
+ DeconstructQualifiedName(names, &schemaname, &servername);
+
+ if (schemaname)
+ {
+ /* Use the provided schema name */
+ namespaceId = GetSysCacheOid(NAMESPACENAME,
+ CStringGetDatum(schemaname),
+ 0, 0, 0);
+ if (!OidIsValid(namespaceId) && failOK)
+ return InvalidOid;
+
+ srvId = GetSysCacheOid(FOREIGNSERVERNAMENSP,
+ CStringGetDatum(servername),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0);
+ }
+ else
+ {
+ ListCell *l;
+
+ /* Determine schema from search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+ srvId = GetSysCacheOid(FOREIGNSERVERNAMENSP,
+ CStringGetDatum(servername),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0);
+
+ if (OidIsValid(srvId))
+ break;
+ }
+ }
+
+ if (!OidIsValid(srvId) && !failOK)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign server \"%s\" does not exist",
+ NameListToString(names))));
+
+ return srvId;
+ }
+
+ /*
+ * ForeignServerIsVisible
+ * Determine whether a foreign server (identified by OID) is visible in
+ * the current search path. Visible means "would be found by searching
+ * for the unqualified server name".
+ */
+ bool
+ ForeignServerIsVisible(Oid srvid)
+ {
+ HeapTuple tup;
+ Oid namespace;
+ bool visible;
+
+ Form_pg_foreign_server form;
+
+ tup = SearchSysCache(FOREIGNSERVEROID,
+ ObjectIdGetDatum(srvid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for foreign server %u",
+ srvid);
+ form = (Form_pg_foreign_server) GETSTRUCT(tup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ namespace = form->srvnamespace;
+ if (namespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, namespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another configuration of the same name earlier in the
+ * path. So we must do a slow check for conflicting configurations.
+ */
+ char *name = NameStr(form->srvname);
+
+ visible = (ForeignServerNameGetServerid(list_make1(makeString(name)),
+ true) == srvid);
+ }
+
+ ReleaseSysCache(tup);
+
+ return visible;
+ }
+
+
+ /*
* TypenameGetTypid
* Try to resolve an unqualified datatype name.
* Returns OID if type found in search path, else InvalidOid.
***************
*** 3333,3338 **** pg_ts_config_is_visible(PG_FUNCTION_ARGS)
--- 3560,3581 ----
}
Datum
+ pg_fdw_is_visible(PG_FUNCTION_ARGS)
+ {
+ Oid oid = PG_GETARG_OID(0);
+
+ PG_RETURN_BOOL(ForeignDataWrapperIsVisible(oid));
+ }
+
+ Datum
+ pg_server_is_visible(PG_FUNCTION_ARGS)
+ {
+ Oid oid = PG_GETARG_OID(0);
+
+ PG_RETURN_BOOL(ForeignServerIsVisible(oid));
+ }
+
+ Datum
pg_my_temp_schema(PG_FUNCTION_ARGS)
{
PG_RETURN_OID(myTempNamespace);
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 381,386 **** CREATE VIEW pg_stat_bgwriter AS
--- 381,405 ----
pg_stat_get_buf_written_backend() AS buffers_backend,
pg_stat_get_buf_alloc() AS buffers_alloc;
+ CREATE VIEW pg_user_mapping AS
+ SELECT
+ U.oid AS umid,
+ S.oid AS srvoid,
+ S.srvname AS srvname,
+ U.usesysid AS usesysid,
+ CASE WHEN U.usesysid = 0 THEN
+ 'public'
+ ELSE
+ A.rolname
+ END AS usename
+ FROM pg_foreign_user_mapping U
+ LEFT JOIN pg_authid A ON (A.oid = U.usesysid) JOIN
+ pg_foreign_server S ON (U.usesrv=S.oid);
+
+ REVOKE ALL on pg_foreign_user_mapping FROM public;
+ REVOKE ALL on FUNCTION pg_get_remote_connection_info(name) FROM public;
+ REVOKE ALL on FUNCTION pg_get_remote_connection_info(name,name) FROM public;
+
-- Tsearch debug function. Defined here because it'd be pretty unwieldy
-- to put it into pg_proc.h
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
***************
*** 15,21 **** include $(top_builddir)/src/Makefile.global
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
conversioncmds.o copy.o \
dbcommands.o define.o discard.o explain.o functioncmds.o \
! indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
portalcmds.o prepare.o proclang.o \
schemacmds.o sequence.o tablecmds.o tablespace.o trigger.o \
tsearchcmds.o typecmds.o user.o vacuum.o vacuumlazy.o \
--- 15,21 ----
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
conversioncmds.o copy.o \
dbcommands.o define.o discard.o explain.o functioncmds.o \
! foreigncmds.o indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
portalcmds.o prepare.o proclang.o \
schemacmds.o sequence.o tablecmds.o tablespace.o trigger.o \
tsearchcmds.o typecmds.o user.o vacuum.o vacuumlazy.o \
*** a/src/backend/commands/alter.c
--- b/src/backend/commands/alter.c
***************
*** 270,275 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
--- 270,283 ----
AlterTSConfigurationOwner(stmt->object, newowner);
break;
+ case OBJECT_FDW:
+ AlterForeignDataWrapperOwner(stmt->object, newowner);
+ break;
+
+ case OBJECT_FOREIGN_SERVER:
+ AlterForeignServerOwner(stmt->object, newowner);
+ break;
+
default:
elog(ERROR, "unrecognized AlterOwnerStmt type: %d",
(int) stmt->objectType);
*** /dev/null
--- b/src/backend/commands/foreigncmds.c
***************
*** 0 ****
--- 1,1136 ----
+ /*-------------------------------------------------------------------------
+ *
+ * foreigncmds.c
+ * foreign data wrapper/server creation/manipulation commands
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include "access/heapam.h"
+ #include "access/reloptions.h"
+ #include "catalog/catalog.h"
+ #include "catalog/dependency.h"
+ #include "catalog/indexing.h"
+ #include "catalog/namespace.h"
+ #include "catalog/pg_fdw.h"
+ #include "catalog/pg_foreign_server.h"
+ #include "catalog/pg_namespace.h"
+ #include "catalog/pg_user_mapping.h"
+ #include "catalog/pg_type.h"
+ #include "commands/defrem.h"
+ #include "miscadmin.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
+ #include "utils/rel.h"
+ #include "utils/syscache.h"
+ #include "foreign/foreign.h"
+
+
+ /*
+ * Convert a DefElem list to the text array format that is used in
+ * pg_foreign_data_wrapper, pg_foreign_server and pg_foreign_user_mapping.
+ *
+ * Note: the array is usually stored to database without further processing,
+ * hence any validation should be done before this conversion.
+ */
+ static Datum
+ optionListToArray(List *options)
+ {
+ ArrayBuildState *astate = NULL;
+ ListCell *cell;
+ text *t;
+
+ foreach (cell, options)
+ {
+ DefElem *def = lfirst(cell);
+ const char *value = "";
+ Size len;
+
+ value = defGetString(def);
+ len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
+ t = palloc(len + 1);
+ SET_VARSIZE(t, len);
+ sprintf(VARDATA(t), "%s=%s", def->defname, value);
+
+ astate = accumArrayResult(astate, PointerGetDatum(t),
+ false, TEXTOID,
+ CurrentMemoryContext);
+ }
+
+ if (astate)
+ return makeArrayResult(astate, CurrentMemoryContext);
+
+ return PointerGetDatum(NULL);
+ }
+
+ /*
+ * Transform the list of OptionDefElem into list of generic options. The
+ * result is converted to array of text suitable for storing in options.
+ *
+ * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING.
+ * In the ALTER cases, oldOptions is the current text array of options.
+ */
+ static Datum
+ transformGenericOptions(Datum oldOptions, List *optionDefList,
+ GenericOptionFlags flags, ForeignDataWrapper *fdw,
+ OptionListValidatorFunc validateOptionList)
+ {
+ List *resultOptions = untransformRelOptions(oldOptions);
+ ListCell *optcell;
+
+ foreach (optcell, optionDefList)
+ {
+ ListCell *cell;
+ ListCell *prev = NULL;
+ OptionDefElem *od = lfirst(optcell);
+
+ /*
+ * Find the element in resultOptions -- we need this for validation in
+ * all cases.
+ */
+ foreach (cell, resultOptions)
+ {
+ DefElem *def = lfirst(cell);
+
+ if (strcmp(def->defname, od->def->defname) == 0)
+ break;
+ else
+ prev = cell;
+ }
+
+ /*
+ * At the moment it possible to perform multiple SET/DROP actions
+ * on the same option -- the standard permits this, as long as the
+ * options to be added are unique.
+ */
+
+ switch (od->alter_op)
+ {
+ case ALTER_OPT_DROP:
+ if (!cell)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("option \"%s\" not found",
+ od->def->defname)));
+ resultOptions = list_delete_cell(resultOptions, cell, prev);
+ break;
+ case ALTER_OPT_SET:
+ if (!cell)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("option \"%s\" not found",
+ od->def->defname)));
+ lfirst(cell) = od->def;
+ break;
+ case ALTER_OPT_ADD:
+ if (cell)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("duplicate option \"%s\"",
+ od->def->defname)));
+ resultOptions = lappend(resultOptions, od->def);
+ break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("unrecognized action %d on option \"%s\"",
+ od->alter_op, od->def->defname)));
+ break;
+ }
+ }
+
+ if (validateOptionList)
+ validateOptionList(fdw, flags, resultOptions);
+
+ return optionListToArray(resultOptions);
+ }
+
+ /*
+ * Convert the user mapping username to Oid
+ */
+ static Oid
+ GetUserOidFromMapping(const char *username, bool missing_ok)
+ {
+ if (!username)
+ /* PUBLIC user mapping */
+ return InvalidOid;
+
+ if (strcmp(username, "current_user") == 0)
+ /* Map to the owner */
+ return GetUserId();
+
+ /* Map to provided user */
+ return missing_ok ? get_roleid(username) : get_roleid_checked(username);
+ }
+
+ /*
+ * Change foreign data wrapper owner.
+ *
+ * Allow this only for superusers, also the new owner must be a
+ * superuser.
+ */
+ void
+ AlterForeignDataWrapperOwner(List *names, Oid newOwnerId)
+ {
+ HeapTuple tup;
+ Relation rel;
+ Oid fdwId;
+
+ Form_pg_foreign_data_wrapper form;
+
+ /* Must be a superuser to change a FDW owner */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to change owner of foreign data wrapper \"%s\"",
+ NameListToString(names)),
+ errhint("must be superuser to change owner of a foreign data wrapper.")));
+
+ /* New owner must also be a superuser */
+ if (!superuser_arg(newOwnerId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to change owner of foreign data wrapper \"%s\"",
+ NameListToString(names)),
+ errhint("the owner of a foreign data wrapper must be a superuser.")));
+
+ rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+ fdwId = ForeignDataWrapperNameGetId(names, false);
+
+ tup = SearchSysCacheCopy(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdwId),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for foreign data wrapper %u", fdwId);
+
+ form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
+
+ if (form->fdwowner != newOwnerId)
+ {
+ form->fdwowner = newOwnerId;
+
+ simple_heap_update(rel, &tup->t_self, tup);
+ CatalogUpdateIndexes(rel, tup);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(ForeignDataWrapperRelationId,
+ HeapTupleGetOid(tup),
+ newOwnerId);
+ }
+
+ heap_close(rel, NoLock);
+ heap_freetuple(tup);
+ }
+
+ /*
+ * Change foreign server owner
+ */
+ void
+ AlterForeignServerOwner(List *names, Oid newOwnerId)
+ {
+ HeapTuple tup;
+ Relation rel;
+ Oid srvId;
+ AclResult aclresult;
+ Oid namespaceOid;
+ Form_pg_foreign_server form;
+
+ rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+ srvId = ForeignServerNameGetServerid(names, false);
+
+ tup = SearchSysCacheCopy(FOREIGNSERVEROID,
+ ObjectIdGetDatum(srvId),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for foreign server %u", srvId);
+
+ form = (Form_pg_foreign_server) GETSTRUCT(tup);
+ namespaceOid = form->srvnamespace;
+
+ if (form->srvowner != newOwnerId)
+ {
+ /* Superusers can always do it */
+ if (!superuser())
+ {
+ /* Must be owner */
+ if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+ NameListToString(names));
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), newOwnerId);
+
+ /* New owner must have CREATE privilege on namespace */
+ aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId, ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(namespaceOid));
+
+ /* New owner must have USAGE privilege on foreign data wrapper */
+ aclresult = pg_fdw_aclcheck(form->srvfdw, newOwnerId, ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ {
+ ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
+
+ aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
+ }
+ }
+
+ form->srvowner = newOwnerId;
+
+ simple_heap_update(rel, &tup->t_self, tup);
+ CatalogUpdateIndexes(rel, tup);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(ForeignServerRelationId, HeapTupleGetOid(tup),
+ newOwnerId);
+ }
+
+ heap_close(rel, NoLock);
+ heap_freetuple(tup);
+ }
+
+ /*
+ * Create a foreign data wrapper
+ */
+ void
+ CreateForeignDataWrapper(CreateFdwStmt *stmt)
+ {
+ Relation rel;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ Datum values[Natts_pg_fdw];
+ char nulls[Natts_pg_fdw];
+ HeapTuple tuple;
+ Oid fdwId;
+ Datum fdwoptions = InvalidOid;
+ Oid namespaceOid = InvalidOid;
+ Oid ownerId;
+ char *fdwname;
+ ForeignDataWrapperLibrary *fdwlib;
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to create foreign data wrapper \"%s\"",
+ NameListToString(stmt->fdwname)),
+ errhint("must be superuser to create a foreign data wrapper.")));
+
+ /* For now the owner cannot be specified on create. Use effective user id */
+ ownerId = GetUserId();
+
+ /*
+ * Check that there is no other foreign data wrapper by this name.
+ */
+ if (OidIsValid(ForeignDataWrapperNameGetId(stmt->fdwname, true)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("foreign data wrapper \"%s\" already exists",
+ NameListToString(stmt->fdwname))));
+
+ /*
+ * Determine the schema for the new FDW. We need also CREATE on the
+ * schema -- check this if we're not running as superuser.
+ */
+ namespaceOid = QualifiedNameGetCreationNamespace(stmt->fdwname, &fdwname);
+
+ /*
+ * Insert tuple into pg_foreign_data_wrapper.
+ */
+ rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+ MemSet(nulls, ' ', Natts_pg_fdw);
+
+ values[Anum_pg_fdw_fdwname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(fdwname));
+ values[Anum_pg_fdw_fdwnamespace - 1] = ObjectIdGetDatum(namespaceOid);
+ values[Anum_pg_fdw_fdwowner - 1] = ObjectIdGetDatum(ownerId);
+ values[Anum_pg_fdw_fdwlibrary - 1] = CStringGetTextDatum(stmt->library);
+ nulls[Anum_pg_fdw_fdwacl - 1] = 'n';
+
+ /*
+ * See if the FDW library loads at all. We also might want to use it
+ * later for validating the options.
+ */
+ fdwlib = GetForeignDataWrapperLibrary(stmt->library);
+
+ fdwoptions = transformGenericOptions(0, stmt->options, FdwOpt, NULL,
+ fdwlib->validateOptionList);
+
+ if (OidIsValid(fdwoptions))
+ values[Anum_pg_fdw_fdwoptions - 1] = fdwoptions;
+ else
+ nulls[Anum_pg_fdw_fdwoptions - 1] = 'n';
+
+ tuple = heap_formtuple(rel->rd_att, values, nulls);
+
+ fdwId = simple_heap_insert(rel, tuple);
+ CatalogUpdateIndexes(rel, tuple);
+
+ heap_freetuple(tuple);
+
+ /* Record dependencies on owner and schema */
+ myself.classId = ForeignDataWrapperRelationId;
+ myself.objectId = fdwId;
+ myself.objectSubId = 0;
+
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = namespaceOid;
+ referenced.objectSubId = 0;
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
+
+ heap_close(rel, NoLock);
+ }
+
+ /*
+ * Alter foreign data wrapper
+ */
+ void
+ AlterForeignDataWrapper(AlterFdwStmt *stmt)
+ {
+ Relation rel;
+ HeapTuple tp;
+ Datum repl_val[Natts_pg_fdw];
+ char repl_null[Natts_pg_fdw];
+ char repl_repl[Natts_pg_fdw];
+ Oid fdwId;
+ bool isnull;
+ Datum datum;
+ ForeignDataWrapperLibrary *fdwlib;
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to alter foreign data wrapper \"%s\"",
+ NameListToString(stmt->fdwname)),
+ errhint("must be superuser to alter a foreign data wrapper.")));
+
+ fdwId = ForeignDataWrapperNameGetId(stmt->fdwname, false);
+
+ tp = SearchSysCacheCopy(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdwId),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for foreign data wrapper %u", fdwId);
+
+ memset(repl_val, 0, sizeof(repl_val));
+ memset(repl_null, ' ', sizeof(repl_null));
+ memset(repl_repl, ' ', sizeof(repl_repl));
+
+ if (stmt->library)
+ {
+ /*
+ * New library specified -- load to see if valid.
+ */
+ fdwlib = GetForeignDataWrapperLibrary(stmt->library);
+
+ repl_val[Anum_pg_fdw_fdwlibrary - 1] = CStringGetTextDatum(stmt->library);
+ repl_repl[Anum_pg_fdw_fdwlibrary - 1] = 'r';
+
+ /*
+ * It could be that the options for the FDW, SERVER and USER MAPPING
+ * are no longer valid with the new library. Notice this.
+ */
+ ereport(NOTICE,
+ (errmsg("changing the foreign data wrapper library can cause "
+ "the options for dependent object to become invalid")));
+ }
+ else
+ {
+ /*
+ * No LIBRARY clause specified, but we need to load it for validating
+ * options.
+ */
+ datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
+ tp,
+ Anum_pg_fdw_fdwlibrary,
+ &isnull);
+ fdwlib = GetForeignDataWrapperLibrary(TextDatumGetCString(datum));
+ }
+
+ /*
+ * Options specified, validate and update.
+ */
+ if (stmt->options)
+ {
+ /* Extract the current options */
+ datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
+ tp,
+ Anum_pg_fdw_fdwoptions,
+ &isnull);
+
+ /* Transform the options */
+ datum = transformGenericOptions(datum, stmt->options, FdwOpt,
+ NULL, fdwlib->validateOptionList);
+
+ if (OidIsValid(datum))
+ repl_val[Anum_pg_fdw_fdwoptions - 1] = ObjectIdGetDatum(datum);
+ else
+ repl_null[Anum_pg_fdw_fdwoptions - 1] = 'n';
+
+ repl_repl[Anum_pg_fdw_fdwoptions - 1] = 'r';
+ }
+
+ /* Everything looks good - update the tuple */
+
+ rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+ tp = heap_modifytuple(tp, RelationGetDescr(rel),
+ repl_val, repl_null, repl_repl);
+
+ simple_heap_update(rel, &tp->t_self, tp);
+ CatalogUpdateIndexes(rel, tp);
+
+ heap_close(rel, RowExclusiveLock);
+ heap_freetuple(tp);
+ }
+
+ /*
+ * Drop foreign data wrapper
+ */
+ void
+ RemoveForeignDataWrapper(DropFdwStmt *stmt)
+ {
+ ObjectAddress object;
+ Oid fdwId;
+
+ fdwId = ForeignDataWrapperNameGetId(stmt->fdwname, true);
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to drop foreign data wrapper \"%s\"",
+ NameListToString(stmt->fdwname)),
+ errhint("must be superuser to drop a foreign data wrapper.")));
+
+ if (!OidIsValid(fdwId))
+ {
+ if (!stmt->missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign data wrapper \"%s\" does not exist",
+ NameListToString(stmt->fdwname))));
+
+ /* IF EXISTS specified, just note it */
+ ereport(NOTICE,
+ (errmsg("foreign data wrapper \"%s\" does not exist, skipping",
+ NameListToString(stmt->fdwname))));
+ return;
+ }
+
+ /*
+ * Do the deletion
+ */
+ object.classId = ForeignDataWrapperRelationId;
+ object.objectId = fdwId;
+ object.objectSubId = 0;
+
+ performDeletion(&object, stmt->behavior);
+ }
+
+ /*
+ * Drop foreign data wrapper by Oid.
+ */
+ void
+ RemoveForeignDataWrapperById(Oid fdwId)
+ {
+ HeapTuple tp;
+ Relation rel;
+
+ rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+ tp = SearchSysCache(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdwId),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for foreign data wrapper %u", fdwId);
+
+ simple_heap_delete(rel, &tp->t_self);
+
+ ReleaseSysCache(tp);
+
+ heap_close(rel, RowExclusiveLock);
+ }
+
+ /*
+ * Create a foreign server
+ */
+ void
+ CreateForeignServer(CreateForeignServerStmt *stmt)
+ {
+ Relation rel;
+ Datum srvoptions = InvalidOid;
+ Datum values[Natts_pg_foreign_server];
+ char nulls[Natts_pg_foreign_server];
+ HeapTuple tuple;
+ Oid srvId;
+ Oid fdwoid;
+ Oid namespaceOid = InvalidOid;
+ Oid ownerId;
+ AclResult aclresult;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ char *servername;
+ ForeignDataWrapper *fdw;
+
+ /* For now the owner cannot be specified on create. Use effective user id */
+ ownerId = GetUserId();
+
+ /*
+ * Check that there is no other foreign server by this name.
+ */
+ if (OidIsValid(ForeignServerNameGetServerid(stmt->servername, true)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("server \"%s\" already exists",
+ NameListToString(stmt->servername))));
+
+ /*
+ * Check that the FDW exists and that we have USAGE on it.
+ * Also get the actual FDW for option validation etc.
+ */
+ fdwoid = ForeignDataWrapperNameGetId(stmt->fdwname, false);
+ fdw = GetForeignDataWrapper(fdwoid);
+
+ aclresult = pg_fdw_aclcheck(fdwoid, ownerId, ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
+
+ /* Determine the namespace for the new object. */
+ namespaceOid = QualifiedNameGetCreationNamespace(stmt->servername,
+ &servername);
+
+ /* Need CREATE on the schema */
+ aclresult = pg_namespace_aclcheck(namespaceOid, ownerId, ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(namespaceOid));
+
+ /*
+ * Insert tuple into pg_foreign_server.
+ */
+ rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+ MemSet(nulls, ' ', Natts_pg_foreign_server);
+
+ values[Anum_pg_foreign_server_srvname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(servername));
+ values[Anum_pg_foreign_server_srvnamespace - 1] =
+ ObjectIdGetDatum(namespaceOid);
+ values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
+ values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdwoid);
+
+ /* Add server type if supplied */
+ if (stmt->servertype)
+ values[Anum_pg_foreign_server_srvtype - 1] =
+ CStringGetTextDatum(stmt->servertype);
+ else
+ nulls[Anum_pg_foreign_server_srvtype - 1] = 'n';
+
+ /* Add server version if supplied */
+ if (stmt->version)
+ values[Anum_pg_foreign_server_srvversion - 1] =
+ CStringGetTextDatum(stmt->version);
+ else
+ nulls[Anum_pg_foreign_server_srvversion - 1] = 'n';
+
+ /* Start with a blank acl */
+ nulls[Anum_pg_foreign_server_srvacl - 1] = 'n';
+
+ /* Add server options */
+ srvoptions = transformGenericOptions(0, stmt->options, ServerOpt, fdw,
+ fdw->lib->validateOptionList);
+
+ if (OidIsValid(srvoptions))
+ values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
+ else
+ nulls[Anum_pg_foreign_server_srvoptions - 1] = 'n';
+
+ tuple = heap_formtuple(rel->rd_att, values, nulls);
+
+ srvId = simple_heap_insert(rel, tuple);
+
+ CatalogUpdateIndexes(rel, tuple);
+
+ heap_freetuple(tuple);
+
+ /* Add dependency on FDW, owner and schema */
+ myself.classId = ForeignServerRelationId;
+ myself.objectId = srvId;
+ myself.objectSubId = 0;
+
+ referenced.classId = ForeignDataWrapperRelationId;
+ referenced.objectId = fdwoid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = namespaceOid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
+
+ heap_close(rel, NoLock);
+ }
+
+ /*
+ * Alter foreign server
+ */
+ void
+ AlterForeignServer(AlterForeignServerStmt *stmt)
+ {
+ Relation rel;
+ HeapTuple tp;
+ Datum repl_val[Natts_pg_foreign_server];
+ char repl_null[Natts_pg_foreign_server];
+ char repl_repl[Natts_pg_foreign_server];
+ Oid srvId;
+ Form_pg_foreign_server srvForm;
+
+ srvId = ForeignServerNameGetServerid(stmt->servername, false);
+
+ /*
+ * Only owner or a superuser can ALTER a SERVER.
+ */
+ if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+ NameListToString(stmt->servername));
+
+ tp = SearchSysCacheCopy(FOREIGNSERVEROID,
+ ObjectIdGetDatum(srvId),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for foreign server %u", srvId);
+
+ srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
+
+ memset(repl_val, 0, sizeof(repl_val));
+ memset(repl_null, ' ', sizeof(repl_null));
+ memset(repl_repl, ' ', sizeof(repl_repl));
+
+ if (stmt->has_version)
+ {
+ /*
+ * Change the server VERSION string.
+ */
+ if (stmt->version)
+ repl_val[Anum_pg_foreign_server_srvversion - 1] =
+ CStringGetTextDatum(stmt->version);
+ else
+ repl_null[Anum_pg_foreign_server_srvversion - 1] = 'n';
+
+ repl_repl[Anum_pg_foreign_server_srvversion - 1] = 'r';
+ }
+
+ if (stmt->options)
+ {
+ ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
+ Datum datum;
+ bool isnull;
+
+ /* Extract the current srvoptions */
+ datum = SysCacheGetAttr(FOREIGNSERVEROID,
+ tp,
+ Anum_pg_foreign_server_srvoptions,
+ &isnull);
+
+ /* Prepare the options array */
+ datum = transformGenericOptions(datum, stmt->options, ServerOpt,
+ fdw, fdw->lib->validateOptionList);
+
+ if (OidIsValid(datum))
+ repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
+ else
+ repl_null[Anum_pg_foreign_server_srvoptions - 1] = 'n';
+
+ repl_repl[Anum_pg_foreign_server_srvoptions - 1] = 'r';
+ }
+
+ /* Everything looks good - update the tuple */
+
+ rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+ tp = heap_modifytuple(tp, RelationGetDescr(rel),
+ repl_val, repl_null, repl_repl);
+
+ simple_heap_update(rel, &tp->t_self, tp);
+ CatalogUpdateIndexes(rel, tp);
+
+ heap_close(rel, RowExclusiveLock);
+ heap_freetuple(tp);
+ }
+
+ /*
+ * Drop foreign server
+ */
+ void
+ RemoveForeignServer(DropForeignServerStmt *stmt)
+ {
+ ObjectAddress object;
+ Oid srvId;
+
+ srvId = ForeignServerNameGetServerid(stmt->servername, true);
+
+ if (!OidIsValid(srvId))
+ {
+ /* Server not found, complain or notice */
+ if (!stmt->missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("server \"%s\" does not exist",
+ NameListToString(stmt->servername))));
+
+ /* IF EXISTS specified, just note it */
+ ereport(NOTICE,
+ (errmsg("server \"%s\" does not exist, skipping",
+ NameListToString(stmt->servername))));
+ return;
+ }
+
+ /* Only allow DROP if the server is owned by the user. */
+ if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+ NameListToString(stmt->servername));
+
+ object.classId = ForeignServerRelationId;
+ object.objectId = srvId;
+ object.objectSubId = 0;
+
+ performDeletion(&object, stmt->behavior);
+ }
+
+ /*
+ * Drop foreign server by Oid
+ */
+ void
+ RemoveForeignServerById(Oid srvId)
+ {
+ HeapTuple tp;
+ Relation rel;
+
+ rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+ tp = SearchSysCache(FOREIGNSERVEROID,
+ ObjectIdGetDatum(srvId),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for foreign server %u", srvId);
+
+ simple_heap_delete(rel, &tp->t_self);
+
+ ReleaseSysCache(tp);
+
+ heap_close(rel, RowExclusiveLock);
+ }
+
+ /*
+ * Create user mapping
+ */
+ void
+ CreateUserMapping(CreateUserMappingStmt *stmt)
+ {
+ Relation rel;
+ Datum useoptions = InvalidOid;
+ Datum values[Natts_pg_user_mapping];
+ char nulls[Natts_pg_user_mapping];
+ HeapTuple tuple;
+ Oid srvId;
+ Oid useId;
+ Oid umId;
+ Oid ownerId;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ ForeignServer *server;
+ ForeignDataWrapper *fdw;
+
+ ownerId = GetUserId();
+
+ useId = GetUserOidFromMapping(stmt->username, false);
+
+ /*
+ * Check that the server exists and that the we own it.
+ */
+ srvId = ForeignServerNameGetServerid(stmt->servername, false);
+
+ if (!pg_foreign_server_ownercheck(srvId, ownerId))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+ NameListToString(stmt->servername));
+
+ /*
+ * Check that the user mapping is unique within server.
+ */
+ umId = GetSysCacheOid(FOREIGNUSERMAPPINGUSEIDSRV,
+ ObjectIdGetDatum(useId),
+ ObjectIdGetDatum(srvId),
+ 0, 0);
+ if (OidIsValid(umId))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("user mapping \"%s\" already exists for server %s",
+ MappingUserName(useId),
+ NameListToString(stmt->servername))));
+
+ server = GetForeignServer(srvId);
+ fdw = GetForeignDataWrapper(server->fdwid);
+
+ /*
+ * Insert tuple into pg_foreign_user_mapping.
+ */
+ rel = heap_open(ForeignUserMappingRelationId, RowExclusiveLock);
+
+ MemSet(nulls, ' ', Natts_pg_user_mapping);
+
+ values[Anum_pg_user_mapping_usesysid - 1] = ObjectIdGetDatum(useId);
+ values[Anum_pg_user_mapping_usesrv - 1] = ObjectIdGetDatum(srvId);
+
+ /* Add user options */
+ useoptions = transformGenericOptions(0, stmt->options, UserMappingOpt,
+ fdw, fdw->lib->validateOptionList);
+
+ if (OidIsValid(useoptions))
+ values[Anum_pg_user_mapping_useoptions - 1] = useoptions;
+ else
+ nulls[Anum_pg_user_mapping_useoptions - 1] = 'n';
+
+ tuple = heap_formtuple(rel->rd_att, values, nulls);
+
+ umId = simple_heap_insert(rel, tuple);
+
+ CatalogUpdateIndexes(rel, tuple);
+
+ heap_freetuple(tuple);
+
+ /* Add dependency on the server */
+ myself.classId = ForeignUserMappingRelationId;
+ myself.objectId = umId;
+ myself.objectSubId = 0;
+
+ referenced.classId = ForeignServerRelationId;
+ referenced.objectId = srvId;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ if (OidIsValid(useId))
+ /* Record the mapped user dependency */
+ recordDependencyOnOwner(ForeignUserMappingRelationId, umId, useId);
+
+ heap_close(rel, NoLock);
+ }
+
+ /*
+ * Alter user mapping
+ */
+ void
+ AlterUserMapping(AlterUserMappingStmt *stmt)
+ {
+ Relation rel;
+ HeapTuple tp;
+ Datum repl_val[Natts_pg_user_mapping];
+ char repl_null[Natts_pg_user_mapping];
+ char repl_repl[Natts_pg_user_mapping];
+ Oid useId;
+ Oid srvId;
+ Oid umId;
+
+ useId = GetUserOidFromMapping(stmt->username, false);
+ srvId = ForeignServerNameGetServerid(stmt->servername, false);
+
+ umId = GetSysCacheOid(FOREIGNUSERMAPPINGUSEIDSRV,
+ ObjectIdGetDatum(useId),
+ ObjectIdGetDatum(srvId),
+ 0, 0);
+ if (!OidIsValid(umId))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("user mapping \"%s\" does not exist for the server",
+ MappingUserName(useId))));
+ }
+
+ /*
+ * Must be owner of the server to alter user mapping.
+ */
+ if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+ NameListToString(stmt->servername));
+
+ tp = SearchSysCacheCopy(FOREIGNUSERMAPPINGOID,
+ ObjectIdGetDatum(umId),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for user mapping %u", umId);
+
+ memset(repl_val, 0, sizeof(repl_val));
+ memset(repl_null, ' ', sizeof(repl_null));
+ memset(repl_repl, ' ', sizeof(repl_repl));
+
+ if (stmt->options)
+ {
+ ForeignDataWrapper *fdw;
+ ForeignServer *srv;
+ Datum datum;
+ bool isnull;
+
+ /*
+ * Process the options.
+ */
+
+ srv = GetForeignServer(srvId);
+ fdw = GetForeignDataWrapper(srv->fdwid);
+
+ datum = SysCacheGetAttr(FOREIGNUSERMAPPINGUSEIDSRV,
+ tp,
+ Anum_pg_user_mapping_useoptions,
+ &isnull);
+
+ /* Prepare the options array */
+ datum = transformGenericOptions(datum, stmt->options, UserMappingOpt,
+ fdw, fdw->lib->validateOptionList);
+
+ if (OidIsValid(datum))
+ repl_val[Anum_pg_user_mapping_useoptions - 1] = datum;
+ else
+ repl_null[Anum_pg_user_mapping_useoptions - 1] = 'n';
+
+ repl_repl[Anum_pg_user_mapping_useoptions - 1] = 'r';
+ }
+
+ /* Everything looks good - update the tuple */
+
+ rel = heap_open(ForeignUserMappingRelationId, RowExclusiveLock);
+
+ tp = heap_modifytuple(tp, RelationGetDescr(rel),
+ repl_val, repl_null, repl_repl);
+
+ simple_heap_update(rel, &tp->t_self, tp);
+ CatalogUpdateIndexes(rel, tp);
+
+ heap_close(rel, RowExclusiveLock);
+ heap_freetuple(tp);
+ }
+
+ /*
+ * Drop user mapping
+ */
+ void
+ RemoveUserMapping(DropUserMappingStmt *stmt)
+ {
+ ObjectAddress object;
+ Oid useId;
+ Oid srvId;
+ Oid umId;
+
+ useId = GetUserOidFromMapping(stmt->username, stmt->missing_ok);
+ srvId = ForeignServerNameGetServerid(stmt->servername, true);
+
+ if (stmt->username && !OidIsValid(useId))
+ {
+ /*
+ * IF EXISTS specified, role not found and not public.
+ * Notice this and leave.
+ */
+ elog(NOTICE, "role \"%s\" does not exist, skipping", stmt->username);
+ return;
+ }
+
+ if (!OidIsValid(srvId))
+ {
+ if (!stmt->missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("server \"%s\" does not exist",
+ NameListToString(stmt->servername))));
+ /* IF EXISTS, just note it */
+ ereport(NOTICE, (errmsg("server does not exist, skipping")));
+ return;
+ }
+
+ umId = GetSysCacheOid(FOREIGNUSERMAPPINGUSEIDSRV,
+ ObjectIdGetDatum(useId),
+ ObjectIdGetDatum(srvId),
+ 0, 0);
+
+ if (!OidIsValid(umId))
+ {
+ if (!stmt->missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("user mapping \"%s\" does not exist for the server",
+ MappingUserName(useId))));
+
+ /* IF EXISTS specified, just note it */
+ ereport(NOTICE,
+ (errmsg("user mapping \"%s\" does not exist for the server, skipping",
+ MappingUserName(useId))));
+ return;
+ }
+
+ /*
+ * Only allow DROP if we own the server.
+ */
+ if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
+ {
+ ForeignServer *server = GetForeignServer(srvId);
+
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+ server->servername);
+ }
+
+ /*
+ * Do the deletion
+ */
+ object.classId = ForeignUserMappingRelationId;
+ object.objectId = umId;
+ object.objectSubId = 0;
+
+ performDeletion(&object, DROP_CASCADE);
+ }
+
+ /*
+ * Drop user mapping by oid - this is called to clean up
+ * dependencies.
+ */
+ void
+ RemoveUserMappingById(Oid umId)
+ {
+ HeapTuple tp;
+ Relation rel;
+
+ rel = heap_open(ForeignUserMappingRelationId, RowExclusiveLock);
+
+ tp = SearchSysCache(FOREIGNUSERMAPPINGOID,
+ ObjectIdGetDatum(umId),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for user mapping %u", umId);
+
+ simple_heap_delete(rel, &tp->t_self);
+
+ ReleaseSysCache(tp);
+
+ heap_close(rel, RowExclusiveLock);
+ }
+
*** /dev/null
--- b/src/backend/foreign/Makefile
***************
*** 0 ****
--- 1,21 ----
+ #-------------------------------------------------------------------------
+ #
+ # Makefile--
+ # Makefile for foreign
+ #
+ # IDENTIFICATION
+ # $PostgreSQL$
+ #
+ #-------------------------------------------------------------------------
+
+ subdir = src/backend/foreign
+ top_builddir = ../../..
+ include $(top_builddir)/src/Makefile.global
+
+ OBJS= foreign.o
+
+ ifneq (,$(filter $(PORTNAME),cygwin win32))
+ override CPPFLAGS += -DWIN32_STACK_RLIMIT=$(WIN32_STACK_RLIMIT)
+ endif
+
+ include $(top_srcdir)/src/backend/common.mk
*** /dev/null
--- b/src/backend/foreign/default/Makefile
***************
*** 0 ****
--- 1,28 ----
+ #-------------------------------------------------------------------------
+ #
+ # Makefile--
+ # Makefile for default foreign data wrapper
+ #
+ # IDENTIFICATION
+ # $PostgreSQL$
+ #
+ #-------------------------------------------------------------------------
+
+ subdir = src/backend/foreign/default
+ top_builddir = ../../../..
+ include $(top_builddir)/src/Makefile.global
+
+ OBJS= default_fdw.o
+ NAME= default_fdw
+ rpath =
+
+ ifneq (,$(filter $(PORTNAME),cygwin win32))
+ override CPPFLAGS += -DWIN32_STACK_RLIMIT=$(WIN32_STACK_RLIMIT)
+ endif
+
+ all: all-shared-lib
+ clean distclean maintainer-clean: clean-lib
+ install: all install-lib
+
+ # Shared library stuff
+ include $(top_srcdir)/src/Makefile.shlib
*** /dev/null
--- b/src/backend/foreign/default/default_fdw.c
***************
*** 0 ****
--- 1,86 ----
+ /*-------------------------------------------------------------------------
+ *
+ * default_fdw.c
+ * Default "dummy" foreign data wrapper.
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include "fmgr.h"
+ #include "foreign/foreign.h"
+
+ PG_MODULE_MAGIC;
+
+ /* Pointer to the FDW library that loaded us */
+ static ForeignDataWrapperLibrary *cachedLibrary = NULL;
+
+ void _PG_fini(void);
+
+
+ /*
+ * Validate the DefElem list given as FDW, SERVER or USER MAPPING generic
+ * options. "flag" indicates which kind of options are we interested in.
+ * Raise an ERROR if the option or its value is considered.
+ *
+ * Note: When validating new FDW the fdw parameter is NULL.
+ *
+ * No validation in the dummy wrapper.
+ */
+ static void
+ validateOptionList(ForeignDataWrapper *fdw, GenericOptionFlags flags,
+ List *options)
+ {
+ }
+
+ /*
+ * Provide the connection details as DefElem list. Here, in the dummy
+ * FDW we just merge all of the server and user mapping options and
+ * hope that the client can cope with that.
+ */
+ static List *
+ GetConnectionInfo(ForeignDataWrapper *fdw, ForeignServer *server,
+ ForeignUserMapping *um)
+ {
+ List *result = NIL;
+ ListCell *cell;
+
+ /* Add server options */
+ foreach (cell, server->options)
+ result = lappend(result, lfirst(cell));
+ /* Add user mapping options */
+ foreach (cell, um->options)
+ result = lappend(result, lfirst(cell));
+
+ /* And we're done */
+ return result;
+ }
+
+ /*
+ * Register the ForeignDataWrapperLibrary that loaded us. Initialize the
+ * function pointers and keep the fdwl reference for unload notifications.
+ */
+ void
+ InitializeFdw(ForeignDataWrapperLibrary *fdwl)
+ {
+ fdwl->GetConnectionInfo = GetConnectionInfo;
+ fdwl->validateOptionList = validateOptionList;
+ cachedLibrary = fdwl;
+ }
+
+ /*
+ * Module unload callback.
+ */
+ void
+ _PG_fini(void)
+ {
+ /* Notify our master that the library has been unloaded. */
+ if (cachedLibrary)
+ cachedLibrary->need_reload = true;
+ }
+
*** /dev/null
--- b/src/backend/foreign/foreign.c
***************
*** 0 ****
--- 1,483 ----
+ /*-------------------------------------------------------------------------
+ *
+ * foreign.c
+ * support for foreign data wrappers, servers and user mappings.
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include "utils/array.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/memutils.h"
+ #include "utils/syscache.h"
+ #include "utils/lsyscache.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/pg_fdw.h"
+ #include "catalog/pg_foreign_server.h"
+ #include "catalog/pg_user_mapping.h"
+ #include "catalog/namespace.h"
+ #include "nodes/parsenodes.h"
+ #include "foreign/foreign.h"
+ #include "access/reloptions.h"
+ #include "funcapi.h"
+ #include "miscadmin.h"
+
+
+ /*
+ * List of currently loaded foreign data wrapper interfaces.
+ */
+ static List *loaded_fdw_interfaces = NIL;
+
+ extern Datum pg_get_remote_connection_info(PG_FUNCTION_ARGS);
+ extern Datum pg_get_user_mapping_options(PG_FUNCTION_ARGS);
+ extern Datum pg_get_foreign_data_wrapper_options(PG_FUNCTION_ARGS);
+ extern Datum pg_get_foreign_server_options(PG_FUNCTION_ARGS);
+
+
+ /*
+ * GetForeignDataWrapperLibrary - return the name FDW library.
+ * If it is already loaded - use that. Otherwise allocate, initialize
+ * and store in cache.
+ *
+ * Note that currently we don't care about library timestamp changes etc.
+ * Once the library is loaded it stays there. Eventually we might have to
+ * handle that.
+ *
+ */
+ ForeignDataWrapperLibrary *
+ GetForeignDataWrapperLibrary(const char *libname)
+ {
+ ForeignDataWrapperLibrary *fdwl = NULL;
+ ListCell *cell;
+ MemoryContext oldcontext;
+
+ /*
+ * See if we have the FDW library is already loaded, use it
+ * if possible.
+ */
+ foreach (cell, loaded_fdw_interfaces)
+ {
+ fdwl = lfirst(cell);
+
+ if (strcmp(fdwl->libname, libname) == 0)
+ break;
+ }
+
+ /*
+ * Not found, allocate a new copy and add to list.
+ */
+ if (!cell)
+ {
+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+
+ fdwl = palloc(sizeof(*fdwl));
+ fdwl->libname = pstrdup(libname);
+ fdwl->need_reload = true;
+ loaded_fdw_interfaces = lappend(loaded_fdw_interfaces, fdwl);
+
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ /*
+ * Reload the library, either it has been unloaded or it has just been
+ * initialized. All the required function pointers are populated by
+ * InitializeFdw()
+ *
+ * Note that there is a possibility that we have added an invalid library
+ * to our list. For sake of simplicity we just ignore it.
+ */
+ if (fdwl->need_reload)
+ {
+ void (*fdwinit)(ForeignDataWrapperLibrary *);
+
+ fdwinit = (void *)load_external_function(fdwl->libname,
+ "InitializeFdw",
+ false,
+ NULL);
+ if (!fdwinit)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("\"%s\" is not a valid foreign data wrapper library.",
+ fdwl->libname)));
+ fdwinit(fdwl);
+ fdwl->need_reload = false;
+ }
+
+ return fdwl;
+ }
+
+ /*
+ * GetForeignDataWrapper - look up the foreign data wrapper.
+ *
+ * Here we also deal with loading the FDW library and looking up
+ * the actual functions.
+ */
+ ForeignDataWrapper *
+ GetForeignDataWrapper(Oid fdwid)
+ {
+ Form_pg_foreign_data_wrapper fdwform;
+ ForeignDataWrapper *fdw;
+ Datum datum;
+ HeapTuple tp;
+ bool isnull;
+
+ tp = SearchSysCache(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdwid),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for foreign data wrapper %u", fdwid);
+
+ fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
+
+ fdw = palloc(sizeof(ForeignDataWrapper));
+ fdw->fdwid = fdwid;
+ fdw->namespace = fdwform->fdwnamespace;
+ fdw->owner = fdwform->fdwowner;
+ fdw->fdwname = pstrdup(NameStr(fdwform->fdwname));
+
+ /* Extract library name */
+ datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
+ tp,
+ Anum_pg_fdw_fdwlibrary,
+ &isnull);
+ fdw->fdwlibrary = pstrdup(TextDatumGetCString(datum));
+
+ fdw->lib = GetForeignDataWrapperLibrary(fdw->fdwlibrary);
+
+ /* Extract the useoptions */
+ datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
+ tp,
+ Anum_pg_fdw_fdwoptions,
+ &isnull);
+ fdw->options = untransformRelOptions(datum);
+
+ ReleaseSysCache(tp);
+
+ return fdw;
+ }
+
+ /*
+ * GetForeignServer - look up the foreign server definition.
+ */
+ ForeignServer *
+ GetForeignServer(Oid serverid)
+ {
+ Form_pg_foreign_server serverform;
+ ForeignServer *server;
+ HeapTuple tp;
+ Datum datum;
+ bool isnull;
+
+ tp = SearchSysCache(FOREIGNSERVEROID,
+ ObjectIdGetDatum(serverid),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for foreign server %u", serverid);
+
+ serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
+
+ server = palloc(sizeof(ForeignServer));
+ server->serverid = serverid;
+ server->servername = pstrdup(NameStr(serverform->srvname));
+ server->namespace = serverform->srvnamespace;
+ server->owner = serverform->srvowner;
+ server->fdwid = serverform->srvfdw;
+
+ /* Extract server type */
+ datum = SysCacheGetAttr(FOREIGNSERVEROID,
+ tp,
+ Anum_pg_foreign_server_srvtype,
+ &isnull);
+ server->servertype = isnull ? NULL : pstrdup(TextDatumGetCString(datum));
+
+ /* Extract server version */
+ datum = SysCacheGetAttr(FOREIGNSERVEROID,
+ tp,
+ Anum_pg_foreign_server_srvversion,
+ &isnull);
+ server->serverversion = isnull ? NULL : pstrdup(TextDatumGetCString(datum));
+
+ /* Extract the srvoptions */
+ datum = SysCacheGetAttr(FOREIGNSERVEROID,
+ tp,
+ Anum_pg_foreign_server_srvoptions,
+ &isnull);
+
+ /* untransformRelOptions does exactly what we want - avoid duplication */
+ server->options = untransformRelOptions(datum);
+
+ ReleaseSysCache(tp);
+
+ return server;
+ }
+
+ /*
+ * ForeignUserMapping - look up the foreign user mapping.
+ *
+ * If no mapping is found for the supplied user we also look for PUBLIC
+ * mappings (usesysid == InvalidOid).
+ */
+ ForeignUserMapping *
+ GetForeignUserMapping(Oid userid, Oid serverid)
+ {
+ Form_pg_foreign_user_mapping umform;
+ Datum datum;
+ HeapTuple tp;
+ bool isnull;
+ ForeignUserMapping *um;
+
+ tp = SearchSysCache(FOREIGNUSERMAPPINGUSEIDSRV,
+ ObjectIdGetDatum(userid),
+ ObjectIdGetDatum(serverid),
+ 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ {
+ /* Not found for the specific user -- try PUBLIC */
+ tp = SearchSysCache(FOREIGNUSERMAPPINGUSEIDSRV,
+ ObjectIdGetDatum(InvalidOid),
+ ObjectIdGetDatum(serverid),
+ 0, 0);
+ }
+
+ if (!HeapTupleIsValid(tp))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("user mapping not found for \"%s\"",
+ MappingUserName(userid))));
+
+ umform = (Form_pg_foreign_user_mapping) GETSTRUCT(tp);
+
+ /* Extract the useoptions */
+ datum = SysCacheGetAttr(FOREIGNUSERMAPPINGUSEIDSRV,
+ tp,
+ Anum_pg_user_mapping_useoptions,
+ &isnull);
+
+ um = palloc(sizeof(ForeignUserMapping));
+ um->userid = userid;
+ um->serverid = serverid;
+ um->options = untransformRelOptions(datum);
+
+ ReleaseSysCache(tp);
+
+ return um;
+ }
+
+ /*
+ * GetRemoteConnectionInfo
+ *
+ * Look up the server, FDW and user mapping. For non-public mappings
+ * (userid != InvalidOid) first validate that the supplied user has USAGE on
+ * the server. Then call FDW to provide the connection details.
+ */
+ List *
+ GetRemoteConnectionInfo(Oid serverid, Oid userid)
+ {
+ ForeignServer *server;
+ ForeignUserMapping *um;
+ ForeignDataWrapper *fdw;
+ AclResult aclresult;
+
+ server = GetForeignServer(serverid);
+
+ /* check permissions */
+ if (OidIsValid(userid))
+ {
+ aclresult = pg_foreign_server_aclcheck(serverid, userid, ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER,
+ server->servername);
+ }
+
+ um = GetForeignUserMapping(userid, serverid);
+ fdw = GetForeignDataWrapper(server->fdwid);
+
+ if (fdw->lib->GetConnectionInfo == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("foreign data wrapper does not provide connection lookup")));
+
+ return fdw->lib->GetConnectionInfo(fdw, server, um);
+ }
+
+ /*
+ * deflist_to_tuplestore - Helper function to convert DefElem list to
+ * tuplestore usable in SRF.
+ */
+ static void
+ deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options)
+ {
+ ListCell *cell;
+ TupleDesc tupdesc;
+ Tuplestorestate *tupstore;
+ Datum values[2];
+ bool nulls[2] = { 0 };
+ MemoryContext per_query_ctx;
+ MemoryContext oldcontext;
+
+ /* check to see if caller supports us returning a tuplestore */
+ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+ if (!(rsinfo->allowedModes & SFRM_Materialize))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialize mode required, but it is not " \
+ "allowed in this context")));
+
+ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+ /*
+ * Now prepare the result set.
+ */
+ tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
+ tupstore = tuplestore_begin_heap(true, false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = tupdesc;
+
+ foreach (cell, options)
+ {
+ DefElem *def = lfirst(cell);
+
+ values[0] = CStringGetTextDatum(def->defname);
+ values[1] = CStringGetTextDatum(((Value *)def->arg)->val.str);
+ tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ }
+
+ /* clean up and return the tuplestore */
+ tuplestore_donestoring(tupstore);
+
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ /*
+ * pg_get_remote_connection_info - provide connection details for the
+ * server/user pair.
+ *
+ * By default we use current effective user id for privilege check and user
+ * mapping lookup. Alternatively another username (or public) can be specified
+ * by second parameter.
+ *
+ */
+ Datum
+ pg_get_remote_connection_info(PG_FUNCTION_ARGS)
+ {
+ Name servername = PG_GETARG_NAME(0);
+ Oid serverid;
+ Oid userid;
+
+ if (PG_NARGS() == 2)
+ {
+ /* Use the provided username for mapping and privilege check */
+ Name username = PG_GETARG_NAME(1);
+
+ if (pg_strcasecmp(NameStr(*username), "public") == 0)
+ userid = InvalidOid;
+ else
+ userid = get_roleid_checked(NameStr(*username));
+ }
+ else
+ {
+ /* Otherwise use the effective user id */
+ userid = GetUserId();
+ }
+
+ /* Find the server */
+ serverid = ForeignServerNameGetServerid(
+ stringToQualifiedNameList(NameStr(*servername)), false);
+
+ deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo,
+ GetRemoteConnectionInfo(serverid, userid));
+
+ return (Datum) 0;
+ }
+
+ /*
+ * pg_get_foreign_data_wrapper_options - Return the options for a foreign
+ * data wrapper.
+ *
+ * pg_get_foreign_data_wrapper_options(srvId IN text, option OUT text, value OUT text)
+ */
+ Datum
+ pg_get_foreign_data_wrapper_options(PG_FUNCTION_ARGS)
+ {
+ Oid fdwId = PG_GETARG_OID(0);
+ ForeignDataWrapper *fdw = GetForeignDataWrapper(fdwId);
+
+ deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, fdw->options);
+
+ return (Datum) 0;
+ }
+
+ /*
+ * pg_get_foreign_server_options - Return the options for a foreign
+ * server.
+ *
+ * pg_get_foreign_server_options(srvId IN text, option OUT text, value OUT text)
+ */
+ Datum
+ pg_get_foreign_server_options(PG_FUNCTION_ARGS)
+ {
+ Oid srvId = PG_GETARG_OID(0);
+ ForeignServer *srv = GetForeignServer(srvId);
+
+ deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, srv->options);
+
+ return (Datum) 0;
+ }
+
+ /*
+ * pg_get_user_mapping_options - Return the user mapping options for
+ * the specified mapping. The options are only visible to server owner
+ * or superuser -- return empty result set for others.
+ */
+ Datum
+ pg_get_user_mapping_options(PG_FUNCTION_ARGS)
+ {
+ Oid umId = PG_GETARG_OID(0);
+ Oid srvId;
+ Oid useId;
+ HeapTuple tp;
+ ForeignUserMapping *um;
+ List *umoptions = NIL;
+
+ tp = SearchSysCache(FOREIGNUSERMAPPINGOID,
+ ObjectIdGetDatum(umId),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("user mapping %u not found", umId)));
+
+ srvId = ((Form_pg_foreign_user_mapping) GETSTRUCT(tp))->usesrv;
+ useId = ((Form_pg_foreign_user_mapping) GETSTRUCT(tp))->usesysid;
+
+ ReleaseSysCache(tp);
+
+ um = GetForeignUserMapping(useId, srvId);
+
+ if (pg_foreign_server_ownercheck(srvId, GetUserId()))
+ {
+ /* Server owner -- OK to show user mapping options */
+ umoptions = um->options;
+ }
+
+ deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, umoptions);
+
+ return (Datum) 0;
+ }
+
*** /dev/null
--- b/src/backend/foreign/pgsql/Makefile
***************
*** 0 ****
--- 1,28 ----
+ #-------------------------------------------------------------------------
+ #
+ # Makefile--
+ # Makefile for pgsql foreign data wrapper
+ #
+ # IDENTIFICATION
+ # $PostgreSQL$
+ #
+ #-------------------------------------------------------------------------
+
+ subdir = src/backend/foreign/pgsql
+ top_builddir = ../../../..
+ include $(top_builddir)/src/Makefile.global
+
+ OBJS= pgsql_fdw.o
+ NAME= pgsql_fdw
+ rpath =
+
+ ifneq (,$(filter $(PORTNAME),cygwin win32))
+ override CPPFLAGS += -DWIN32_STACK_RLIMIT=$(WIN32_STACK_RLIMIT)
+ endif
+
+ all: all-shared-lib
+ clean distclean maintainer-clean: clean-lib
+ install: all install-lib
+
+ # Shared library stuff
+ include $(top_srcdir)/src/Makefile.shlib
*** /dev/null
--- b/src/backend/foreign/pgsql/pgsql_fdw.c
***************
*** 0 ****
--- 1,198 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pgsql_fdw.c
+ * foreign data wrapper for pgsql (libpq) connections.
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include "fmgr.h"
+ #include "lib/stringinfo.h"
+ #include "nodes/value.h"
+ #include "nodes/parsenodes.h"
+ #include "nodes/makefuncs.h"
+ #include "foreign/foreign.h"
+
+ PG_MODULE_MAGIC;
+
+ /* Pointer to the FDW library that loaded us */
+ static ForeignDataWrapperLibrary *cachedLibrary = NULL;
+
+ /*
+ * Describes the valid options for pgsql FDW, server and user mapping.
+ */
+ typedef struct ConnectionOptions {
+ const char *optname; /* Option name */
+ GenericOptionFlags optflags; /* Option usage bitmap */
+ } ConnectionOptions;
+
+ /*
+ * Copied from fe-connect.c PQconninfoOptions.
+ *
+ * The list is small - don't bother with bsearch if it stays so.
+ */
+ static ConnectionOptions libpq_conninfo_options[] = {
+ { "authtype", ServerOpt },
+ { "service", ServerOpt },
+ { "user", UserMappingOpt },
+ { "password", UserMappingOpt },
+ { "connect_timeout", ServerOpt },
+ { "dbname", ServerOpt },
+ { "host", ServerOpt },
+ { "hostaddr", ServerOpt },
+ { "port", ServerOpt },
+ { "tty", ServerOpt },
+ { "options", ServerOpt },
+ { "requiressl", ServerOpt },
+ { "sslmode", ServerOpt },
+ { "gsslib", ServerOpt },
+ { NULL, InvalidOpt }
+ };
+
+ void _PG_fini(void);
+
+
+ /*
+ * Check if the provided option is one of libpq conninfo options.
+ * We look at only options with matching flags.
+ */
+ static bool
+ is_conninfo_option(const char *option, GenericOptionFlags flags)
+ {
+ ConnectionOptions *opt;
+
+ for (opt = libpq_conninfo_options; opt->optname != NULL; opt++)
+ if (flags & opt->optflags && strcmp(opt->optname, option) == 0)
+ return true;
+ return false;
+ }
+
+ /*
+ * Validate the generic option given to SERVER or USER MAPPING.
+ * Raise an ERROR if the option or its value is considered
+ * invalid.
+ *
+ * Valid server options are all libpq conninfo options except
+ * user and password -- these may only appear in USER MAPPING options.
+ */
+ static void
+ validateOptionList(ForeignDataWrapper *fdw, GenericOptionFlags flags,
+ List *options)
+ {
+ ListCell *cell;
+
+ foreach (cell, options)
+ {
+ DefElem *def = lfirst(cell);
+
+ if (!is_conninfo_option(def->defname, flags))
+ {
+ ConnectionOptions *opt;
+ StringInfoData buf;
+ const char *objtype;
+
+ /*
+ * Unknown option specified, complain about it. Provide a hint
+ * with list of valid options for the object.
+ */
+ initStringInfo(&buf);
+ for (opt = libpq_conninfo_options; opt->optname != NULL; opt++)
+ if (flags & opt->optflags)
+ appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
+ opt->optname);
+
+ if (flags & ServerOpt)
+ objtype = "server";
+ else if (flags & UserMappingOpt)
+ objtype = "user mapping";
+ else if (flags & FdwOpt)
+ objtype = "foreign data wrapper";
+ else
+ objtype = "???";
+
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid option \"%s\" to %s", def->defname, objtype),
+ errhint("valid %s options are: %s", objtype, buf.data)));
+ }
+ }
+ }
+
+ /*
+ * Provide the connection details as DefElem list.
+ *
+ * The known libpq conninfo parameters are merged into a single connect
+ * string and returned under the option name of "datasource". Unrecognized
+ * options are returned unmodified.
+ */
+ static List *
+ GetConnectionInfo(ForeignDataWrapper *fdw, ForeignServer *server,
+ ForeignUserMapping *um)
+ {
+ List *result = NIL;
+ ListCell *cell;
+ StringInfo stringptr;
+ DefElem *datasource;
+
+ stringptr = makeStringInfo();
+
+ /* Add server options */
+ foreach (cell, server->options)
+ {
+ DefElem *def = lfirst(cell);
+
+ if (is_conninfo_option(def->defname, ServerOpt))
+ appendStringInfo(stringptr, "%s%s=%s",
+ (stringptr->len > 0) ? " " : "", def->defname, strVal(def->arg));
+ else
+ result = lappend(result, lfirst(cell));
+ }
+
+ /* And the user mapping options */
+ foreach (cell, um->options)
+ {
+ DefElem *def = lfirst(cell);
+
+ if (is_conninfo_option(def->defname, UserMappingOpt))
+ appendStringInfo(stringptr, "%s%s=%s",
+ (stringptr->len > 0) ? " " : "", def->defname, strVal(def->arg));
+ else
+ result = lappend(result, lfirst(cell));
+ }
+
+ /* Finally the compiled conninfo string */
+ datasource = makeDefElem("datasource", (Node *)makeString(stringptr->data));
+ result = lappend(result, datasource);
+
+ return result;
+ }
+
+ /*
+ * Register the ForeignDataWrapperLibrary that loaded us. Initialize the
+ * function pointers and keep the fdwl reference for unload notifications.
+ */
+ void
+ InitializeFdw(ForeignDataWrapperLibrary *fdwl)
+ {
+ fdwl->GetConnectionInfo = GetConnectionInfo;
+ fdwl->validateOptionList = validateOptionList;
+ cachedLibrary = fdwl;
+ }
+
+ /*
+ * Module unload callback.
+ */
+ void
+ _PG_fini(void)
+ {
+ /* Notify our master that the library has been unloaded. */
+ if (cachedLibrary)
+ cachedLibrary->need_reload = true;
+ }
+
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 2033,2038 **** _copyDefElem(DefElem *from)
--- 2033,2049 ----
return newnode;
}
+ static OptionDefElem *
+ _copyOptionDefElem(OptionDefElem *from)
+ {
+ OptionDefElem *newnode = makeNode(OptionDefElem);
+
+ COPY_SCALAR_FIELD(alter_op);
+ COPY_NODE_FIELD(def);
+
+ return newnode;
+ }
+
static LockingClause *
_copyLockingClause(LockingClause *from)
{
***************
*** 2869,2874 **** _copyDropTableSpaceStmt(DropTableSpaceStmt *from)
--- 2880,2996 ----
return newnode;
}
+ static CreateFdwStmt *
+ _copyCreateFdwStmt(CreateFdwStmt *from)
+ {
+ CreateFdwStmt *newnode = makeNode(CreateFdwStmt);
+
+ COPY_NODE_FIELD(fdwname);
+ COPY_STRING_FIELD(library);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+ }
+
+ static AlterFdwStmt *
+ _copyAlterFdwStmt(AlterFdwStmt *from)
+ {
+ AlterFdwStmt *newnode = makeNode(AlterFdwStmt);
+
+ COPY_NODE_FIELD(fdwname);
+ COPY_STRING_FIELD(library);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+ }
+
+ static DropFdwStmt *
+ _copyDropFdwStmt(DropFdwStmt *from)
+ {
+ DropFdwStmt *newnode = makeNode(DropFdwStmt);
+
+ COPY_NODE_FIELD(fdwname);
+ COPY_SCALAR_FIELD(missing_ok);
+ COPY_SCALAR_FIELD(behavior);
+
+ return newnode;
+ }
+
+ static CreateForeignServerStmt *
+ _copyCreateForeignServerStmt(CreateForeignServerStmt *from)
+ {
+ CreateForeignServerStmt *newnode = makeNode(CreateForeignServerStmt);
+
+ COPY_NODE_FIELD(servername);
+ COPY_STRING_FIELD(servertype);
+ COPY_STRING_FIELD(version);
+ COPY_NODE_FIELD(fdwname);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+ }
+
+ static AlterForeignServerStmt *
+ _copyAlterForeignServerStmt(AlterForeignServerStmt *from)
+ {
+ AlterForeignServerStmt *newnode = makeNode(AlterForeignServerStmt);
+
+ COPY_NODE_FIELD(servername);
+ COPY_STRING_FIELD(version);
+ COPY_NODE_FIELD(options);
+ COPY_SCALAR_FIELD(has_version);
+
+ return newnode;
+ }
+
+ static DropForeignServerStmt *
+ _copyDropForeignServerStmt(DropForeignServerStmt *from)
+ {
+ DropForeignServerStmt *newnode = makeNode(DropForeignServerStmt);
+
+ COPY_NODE_FIELD(servername);
+ COPY_SCALAR_FIELD(missing_ok);
+ COPY_SCALAR_FIELD(behavior);
+
+ return newnode;
+ }
+
+ static CreateUserMappingStmt *
+ _copyCreateUserMappingStmt(CreateUserMappingStmt *from)
+ {
+ CreateUserMappingStmt *newnode = makeNode(CreateUserMappingStmt);
+
+ COPY_STRING_FIELD(username);
+ COPY_NODE_FIELD(servername);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+ }
+
+ static AlterUserMappingStmt *
+ _copyAlterUserMappingStmt(AlterUserMappingStmt *from)
+ {
+ AlterUserMappingStmt *newnode = makeNode(AlterUserMappingStmt);
+
+ COPY_STRING_FIELD(username);
+ COPY_NODE_FIELD(servername);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+ }
+
+ static DropUserMappingStmt *
+ _copyDropUserMappingStmt(DropUserMappingStmt *from)
+ {
+ DropUserMappingStmt *newnode = makeNode(DropUserMappingStmt);
+
+ COPY_STRING_FIELD(username);
+ COPY_NODE_FIELD(servername);
+ COPY_SCALAR_FIELD(missing_ok);
+
+ return newnode;
+ }
+
static CreateTrigStmt *
_copyCreateTrigStmt(CreateTrigStmt *from)
{
***************
*** 3696,3701 **** copyObject(void *from)
--- 3818,3850 ----
case T_DropTableSpaceStmt:
retval = _copyDropTableSpaceStmt(from);
break;
+ case T_CreateFdwStmt:
+ retval = _copyCreateFdwStmt(from);
+ break;
+ case T_AlterFdwStmt:
+ retval = _copyAlterFdwStmt(from);
+ break;
+ case T_DropFdwStmt:
+ retval = _copyDropFdwStmt(from);
+ break;
+ case T_CreateForeignServerStmt:
+ retval = _copyCreateForeignServerStmt(from);
+ break;
+ case T_AlterForeignServerStmt:
+ retval = _copyAlterForeignServerStmt(from);
+ break;
+ case T_DropForeignServerStmt:
+ retval = _copyDropForeignServerStmt(from);
+ break;
+ case T_CreateUserMappingStmt:
+ retval = _copyCreateUserMappingStmt(from);
+ break;
+ case T_AlterUserMappingStmt:
+ retval = _copyAlterUserMappingStmt(from);
+ break;
+ case T_DropUserMappingStmt:
+ retval = _copyDropUserMappingStmt(from);
+ break;
case T_CreateTrigStmt:
retval = _copyCreateTrigStmt(from);
break;
***************
*** 3823,3828 **** copyObject(void *from)
--- 3972,3980 ----
case T_DefElem:
retval = _copyDefElem(from);
break;
+ case T_OptionDefElem:
+ retval = _copyOptionDefElem(from);
+ break;
case T_LockingClause:
retval = _copyLockingClause(from);
break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1519,1524 **** _equalDropTableSpaceStmt(DropTableSpaceStmt *a, DropTableSpaceStmt *b)
--- 1519,1617 ----
}
static bool
+ _equalCreateFdwStmt(CreateFdwStmt *a, CreateFdwStmt *b)
+ {
+ COMPARE_NODE_FIELD(fdwname);
+ COMPARE_STRING_FIELD(library);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+ }
+
+ static bool
+ _equalAlterFdwStmt(AlterFdwStmt *a, AlterFdwStmt *b)
+ {
+ COMPARE_NODE_FIELD(fdwname);
+ COMPARE_STRING_FIELD(library);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+ }
+
+ static bool
+ _equalDropFdwStmt(DropFdwStmt *a, DropFdwStmt *b)
+ {
+ COMPARE_NODE_FIELD(fdwname);
+ COMPARE_SCALAR_FIELD(missing_ok);
+ COMPARE_SCALAR_FIELD(behavior);
+
+ return true;
+ }
+
+ static bool
+ _equalCreateForeignServerStmt(CreateForeignServerStmt *a, CreateForeignServerStmt *b)
+ {
+ COMPARE_NODE_FIELD(servername);
+ COMPARE_STRING_FIELD(servertype);
+ COMPARE_STRING_FIELD(version);
+ COMPARE_NODE_FIELD(fdwname);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+ }
+
+ static bool
+ _equalAlterForeignServerStmt(AlterForeignServerStmt *a, AlterForeignServerStmt *b)
+ {
+ COMPARE_NODE_FIELD(servername);
+ COMPARE_STRING_FIELD(version);
+ COMPARE_NODE_FIELD(options);
+ COMPARE_SCALAR_FIELD(has_version);
+
+ return true;
+ }
+
+ static bool
+ _equalDropForeignServerStmt(DropForeignServerStmt *a, DropForeignServerStmt *b)
+ {
+ COMPARE_NODE_FIELD(servername);
+ COMPARE_SCALAR_FIELD(missing_ok);
+ COMPARE_SCALAR_FIELD(behavior);
+
+ return true;
+ }
+
+ static bool
+ _equalCreateUserMappingStmt(CreateUserMappingStmt *a, CreateUserMappingStmt *b)
+ {
+ COMPARE_STRING_FIELD(username);
+ COMPARE_NODE_FIELD(servername);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+ }
+
+ static bool
+ _equalAlterUserMappingStmt(AlterUserMappingStmt *a, AlterUserMappingStmt *b)
+ {
+ COMPARE_STRING_FIELD(username);
+ COMPARE_NODE_FIELD(servername);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+ }
+
+ static bool
+ _equalDropUserMappingStmt(DropUserMappingStmt *a, DropUserMappingStmt *b)
+ {
+ COMPARE_STRING_FIELD(username);
+ COMPARE_NODE_FIELD(servername);
+ COMPARE_SCALAR_FIELD(missing_ok);
+
+ return true;
+ }
+
+ static bool
_equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b)
{
COMPARE_STRING_FIELD(trigname);
***************
*** 1957,1962 **** _equalDefElem(DefElem *a, DefElem *b)
--- 2050,2064 ----
}
static bool
+ _equalOptionDefElem(OptionDefElem *a, OptionDefElem *b)
+ {
+ COMPARE_SCALAR_FIELD(alter_op);
+ COMPARE_NODE_FIELD(def);
+
+ return true;
+ }
+
+ static bool
_equalLockingClause(LockingClause *a, LockingClause *b)
{
COMPARE_NODE_FIELD(lockedRels);
***************
*** 2534,2539 **** equal(void *a, void *b)
--- 2636,2668 ----
case T_DropTableSpaceStmt:
retval = _equalDropTableSpaceStmt(a, b);
break;
+ case T_CreateFdwStmt:
+ retval = _equalCreateFdwStmt(a, b);
+ break;
+ case T_AlterFdwStmt:
+ retval = _equalAlterFdwStmt(a, b);
+ break;
+ case T_DropFdwStmt:
+ retval = _equalDropFdwStmt(a, b);
+ break;
+ case T_CreateForeignServerStmt:
+ retval = _equalCreateForeignServerStmt(a, b);
+ break;
+ case T_AlterForeignServerStmt:
+ retval = _equalAlterForeignServerStmt(a, b);
+ break;
+ case T_DropForeignServerStmt:
+ retval = _equalDropForeignServerStmt(a, b);
+ break;
+ case T_CreateUserMappingStmt:
+ retval = _equalCreateUserMappingStmt(a, b);
+ break;
+ case T_AlterUserMappingStmt:
+ retval = _equalAlterUserMappingStmt(a, b);
+ break;
+ case T_DropUserMappingStmt:
+ retval = _equalDropUserMappingStmt(a, b);
+ break;
case T_CreateTrigStmt:
retval = _equalCreateTrigStmt(a, b);
break;
***************
*** 2661,2666 **** equal(void *a, void *b)
--- 2790,2798 ----
case T_DefElem:
retval = _equalDefElem(a, b);
break;
+ case T_OptionDefElem:
+ retval = _equalOptionDefElem(a, b);
+ break;
case T_LockingClause:
retval = _equalLockingClause(a, b);
break;
*** a/src/backend/nodes/makefuncs.c
--- b/src/backend/nodes/makefuncs.c
***************
*** 361,363 **** makeDefElem(char *name, Node *arg)
--- 361,376 ----
res->arg = arg;
return res;
}
+
+ /*
+ * makeDefElem -
+ * build a DefElem node
+ */
+ OptionDefElem *
+ makeOptionDefElem(int op, DefElem *def)
+ {
+ OptionDefElem *res = makeNode(OptionDefElem);
+ res->alter_op = op;
+ res->def = def;
+ return res;
+ }
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 156,161 **** static TypeName *TableFuncTypeName(List *columns);
--- 156,162 ----
FunctionParameterMode fun_param_mode;
FuncWithArgs *funwithargs;
DefElem *defelt;
+ OptionDefElem *optdef;
SortBy *sortby;
JoinExpr *jexpr;
IndexElem *ielem;
***************
*** 172,190 **** static TypeName *TableFuncTypeName(List *columns);
}
%type stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
! AlterUserStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
! CreateAssertStmt CreateTrigStmt CreateUserStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
! DropUserStmt DropdbStmt DropTableSpaceStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
--- 173,194 ----
}
%type stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
! AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
! AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
! CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
! CreateUserStmt CreateUserMappingStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
! DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
! DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
***************
*** 222,227 **** static TypeName *TableFuncTypeName(List *columns);
--- 226,236 ----
%type OptRoleList
%type OptRoleElem
+ %type OptType
+ %type OptVersion
+ %type AuthIdent
+ %type ForeignServerVersion
+
%type OptSchemaName
%type OptSchemaEltList
***************
*** 253,259 **** static TypeName *TableFuncTypeName(List *columns);
%type TriggerOneEvent
%type stmtblock stmtmulti
! OptTableElementList TableElementList OptInherit definition
OptWith opt_distinct opt_definition func_args func_args_list
func_args_with_defaults func_args_with_defaults_list
func_as createfunc_opt_list alterfunc_opt_list
--- 262,268 ----
%type TriggerOneEvent
%type stmtblock stmtmulti
! OptTableElementList TableElementList OptInherit definition
OptWith opt_distinct opt_definition func_args func_args_list
func_args_with_defaults func_args_with_defaults_list
func_as createfunc_opt_list alterfunc_opt_list
***************
*** 274,279 **** static TypeName *TableFuncTypeName(List *columns);
--- 283,289 ----
prep_type_clause
execute_param_clause using_clause returning_clause
enum_val_list table_func_column_list
+ CreateGenericOptions AlterGenericOptions
%type OptTempTableName
%type into_clause create_as_target
***************
*** 342,347 **** static TypeName *TableFuncTypeName(List *columns);
--- 352,363 ----
%type relation_expr_opt_alias
%type target_el single_set_clause set_target insert_column_item
+ %type gopt_name
+ %type gopt_arg
+ %type gopt_elem
+ %type alter_gopt_elem
+ %type gopt_list alter_gopt_list
+
%type Typename SimpleTypename ConstTypename
GenericType Numeric opt_float
Character ConstCharacter
***************
*** 436,442 **** static TypeName *TableFuncTypeName(List *columns);
KEY
LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEAST LEFT LEVEL
! LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
LOCK_P LOGIN_P
MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
--- 452,458 ----
KEY
LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEAST LEFT LEVEL
! LIBRARY LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
LOCK_P LOGIN_P
MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
***************
*** 445,451 **** static TypeName *TableFuncTypeName(List *columns);
NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER
NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC
! OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR
ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNED OWNER
PARSER PARTIAL PASSWORD PLACING PLANS POSITION
--- 461,467 ----
NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER
NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC
! OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OPTIONS OR
ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNED OWNER
PARSER PARTIAL PASSWORD PLACING PLANS POSITION
***************
*** 459,465 **** static TypeName *TableFuncTypeName(List *columns);
REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE
! SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE_P START STATEMENT
STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P
SYMMETRIC SYSID SYSTEM_P
--- 475,481 ----
REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE
! SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE
SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE_P START STATEMENT
STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P
SYMMETRIC SYSID SYSTEM_P
***************
*** 474,480 **** static TypeName *TableFuncTypeName(List *columns);
VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
VERBOSE VERSION_P VIEW VOLATILE
! WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE
XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLFOREST XMLPARSE
XMLPI XMLROOT XMLSERIALIZE
--- 490,496 ----
VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
VERBOSE VERSION_P VIEW VOLATILE
! WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRAPPER WRITE
XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLFOREST XMLPARSE
XMLPI XMLROOT XMLSERIALIZE
***************
*** 563,568 **** stmt :
--- 579,586 ----
| AlterDatabaseSetStmt
| AlterDomainStmt
| AlterFunctionStmt
+ | AlterFdwStmt
+ | AlterForeignServerStmt
| AlterGroupStmt
| AlterObjectSchemaStmt
| AlterOwnerStmt
***************
*** 572,577 **** stmt :
--- 590,596 ----
| AlterRoleStmt
| AlterTSConfigurationStmt
| AlterTSDictionaryStmt
+ | AlterUserMappingStmt
| AlterUserSetStmt
| AlterUserStmt
| AnalyzeStmt
***************
*** 587,592 **** stmt :
--- 606,612 ----
| CreateConversionStmt
| CreateDomainStmt
| CreateFunctionStmt
+ | CreateForeignServerStmt
| CreateGroupStmt
| CreateOpClassStmt
| CreateOpFamilyStmt
***************
*** 596,604 **** stmt :
--- 616,626 ----
| CreateSeqStmt
| CreateStmt
| CreateTableSpaceStmt
+ | CreateFdwStmt
| CreateTrigStmt
| CreateRoleStmt
| CreateUserStmt
+ | CreateUserMappingStmt
| CreatedbStmt
| DeallocateStmt
| DeclareCursorStmt
***************
*** 615,623 **** stmt :
--- 637,648 ----
| DropRuleStmt
| DropStmt
| DropTableSpaceStmt
+ | DropFdwStmt
+ | DropForeignServerStmt
| DropTrigStmt
| DropRoleStmt
| DropUserStmt
+ | DropUserMappingStmt
| DropdbStmt
| ExecuteStmt
| ExplainStmt
***************
*** 2717,2722 **** DropTableSpaceStmt: DROP TABLESPACE name
--- 2742,3048 ----
/*****************************************************************************
*
+ * QUERY:
+ * CREATE FOREIGN DATA WRAPPER any_name LIBRARY 'library_name
+ *
+ *****************************************************************************/
+
+ CreateFdwStmt: CREATE FOREIGN DATA_P WRAPPER any_name LIBRARY Sconst CreateGenericOptions
+ {
+ CreateFdwStmt *n = makeNode(CreateFdwStmt);
+ n->fdwname = $5;
+ n->library = $7;
+ n->options = $8;
+ $$ = (Node *) n;
+ }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERY :
+ * DROP FOREIGN DATA WRAPPER
+ *
+ ****************************************************************************/
+
+ DropFdwStmt: DROP FOREIGN DATA_P WRAPPER any_name opt_drop_behavior
+ {
+ DropFdwStmt *n = makeNode(DropFdwStmt);
+ n->fdwname = $5;
+ n->missing_ok = false;
+ n->behavior = $6;
+ $$ = (Node *) n;
+ }
+ | DROP FOREIGN DATA_P WRAPPER IF_P EXISTS any_name opt_drop_behavior
+ {
+ DropFdwStmt *n = makeNode(DropFdwStmt);
+ n->fdwname = $7;
+ n->missing_ok = true;
+ n->behavior = $8;
+ $$ = (Node *) n;
+ }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERY :
+ * ALTER FOREIGN DATA WRAPPER
+ *
+ ****************************************************************************/
+
+ AlterFdwStmt: ALTER FOREIGN DATA_P WRAPPER any_name LIBRARY Sconst AlterGenericOptions
+ {
+ AlterFdwStmt *n = makeNode(AlterFdwStmt);
+ n->fdwname = $5;
+ n->library = $7;
+ n->options = $8;
+ $$ = (Node *) n;
+ }
+ | ALTER FOREIGN DATA_P WRAPPER any_name LIBRARY Sconst
+ {
+ AlterFdwStmt *n = makeNode(AlterFdwStmt);
+ n->fdwname = $5;
+ n->library = $7;
+ $$ = (Node *) n;
+ }
+ | ALTER FOREIGN DATA_P WRAPPER any_name AlterGenericOptions
+ {
+ AlterFdwStmt *n = makeNode(AlterFdwStmt);
+ n->fdwname = $5;
+ n->options = $6;
+ $$ = (Node *) n;
+ }
+ ;
+
+ /* Options definition for CREATE FDW, SERVER and USER MAPPING */
+ CreateGenericOptions:
+ OPTIONS '(' gopt_list ')' { $$ = $3; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+ gopt_list: gopt_elem
+ {
+ $$ = list_make1(makeOptionDefElem(ALTER_OPT_ADD, $1));
+ }
+ | gopt_list ',' gopt_elem
+ {
+ $$ = lappend($1, makeOptionDefElem(ALTER_OPT_ADD, $3));
+ }
+ ;
+
+ /* Options definition for ALTER FDW, SERVER and USER MAPPING */
+ AlterGenericOptions:
+ OPTIONS '(' alter_gopt_list ')' { $$ = $3; }
+ ;
+
+ alter_gopt_list:
+ alter_gopt_elem
+ {
+ $$ = list_make1($1);
+ }
+ | gopt_elem
+ {
+ $$ = list_make1(makeOptionDefElem(ALTER_OPT_ADD, $1));
+ }
+ | alter_gopt_list ',' alter_gopt_elem
+ {
+ $$ = lappend($1, $3);
+ }
+ | alter_gopt_list ',' gopt_elem
+ {
+ $$ = lappend($1, makeOptionDefElem(ALTER_OPT_ADD, $3));
+ }
+ ;
+
+ alter_gopt_elem:
+ ADD_P gopt_elem
+ {
+ $$ = makeOptionDefElem(ALTER_OPT_ADD, $2);
+ }
+ | SET gopt_elem
+ {
+ $$ = makeOptionDefElem(ALTER_OPT_SET, $2);
+ }
+ | DROP gopt_name
+ {
+ $$ = makeOptionDefElem(ALTER_OPT_DROP,
+ makeDefElem($2, NULL));
+ }
+ ;
+
+ gopt_elem: gopt_name gopt_arg { $$ = makeDefElem($1, $2); }
+ ;
+
+ gopt_name: attr_name { $$ = $1; }
+ ;
+
+ gopt_arg: Sconst { $$ = (Node *)makeString($1); }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERY:
+ * CREATE SERVER any_name [TYPE] [VERSION] [OPTIONS]
+ *
+ *****************************************************************************/
+
+ CreateForeignServerStmt: CREATE SERVER any_name OptType OptVersion
+ FOREIGN DATA_P WRAPPER any_name CreateGenericOptions
+ {
+ CreateForeignServerStmt *n = makeNode(CreateForeignServerStmt);
+ n->servername = $3;
+ n->servertype = $4;
+ n->version = $5;
+ n->fdwname = $9;
+ n->options = $10;
+ $$ = (Node *) n;
+ }
+ ;
+
+ /* Optional server type specification */
+ OptType:
+ TYPE_P Sconst { $$ = $2; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+
+ /* Optional server version */
+ OptVersion:
+ VERSION_P Sconst { $$ = $2; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERY :
+ * DROP SERVER
+ *
+ ****************************************************************************/
+
+ DropForeignServerStmt: DROP SERVER any_name opt_drop_behavior
+ {
+ DropForeignServerStmt *n = makeNode(DropForeignServerStmt);
+ n->servername = $3;
+ n->missing_ok = false;
+ n->behavior = $4;
+ $$ = (Node *) n;
+ }
+ | DROP SERVER IF_P EXISTS any_name opt_drop_behavior
+ {
+ DropForeignServerStmt *n = makeNode(DropForeignServerStmt);
+ n->servername = $5;
+ n->missing_ok = true;
+ n->behavior = $6;
+ $$ = (Node *) n;
+ }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERY :
+ * ALTER SERVER [VERSION] [OPTIONS]
+ *
+ ****************************************************************************/
+
+ AlterForeignServerStmt: ALTER SERVER any_name VERSION_P ForeignServerVersion AlterGenericOptions
+ {
+ AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt);
+ n->servername = $3;
+ n->version = $5;
+ n->options = $6;
+ n->has_version = true;
+ $$ = (Node *) n;
+ }
+ | ALTER SERVER any_name VERSION_P ForeignServerVersion
+ {
+ AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt);
+ n->servername = $3;
+ n->version = $5;
+ n->has_version = true;
+ $$ = (Node *) n;
+ }
+ | ALTER SERVER any_name AlterGenericOptions
+ {
+ AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt);
+ n->servername = $3;
+ n->options = $4;
+ $$ = (Node *) n;
+ }
+ ;
+
+ /* User mapping authorization identifier */
+ ForeignServerVersion:
+ Sconst { $$ = $1; }
+ | NULL_P { $$ = NULL; }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERY:
+ * CREATE USER MAPPING FOR SERVER [OPTIONS]
+ *
+ *****************************************************************************/
+
+ CreateUserMappingStmt: CREATE USER MAPPING FOR AuthIdent SERVER any_name CreateGenericOptions
+ {
+ CreateUserMappingStmt *n = makeNode(CreateUserMappingStmt);
+ n->username = $5;
+ n->servername = $7;
+ n->options = $8;
+ $$ = (Node *) n;
+ }
+ ;
+
+ /* User mapping authorization identifier */
+ AuthIdent:
+ CURRENT_USER { $$ = "current_user"; }
+ | USER { $$ = "current_user"; }
+ | RoleId { $$ = (strcmp($1, "public") == 0) ? NULL : $1 }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERY :
+ * DROP USER MAPPING FOR SERVER
+ *
+ ****************************************************************************/
+
+ DropUserMappingStmt: DROP USER MAPPING FOR AuthIdent SERVER any_name
+ {
+ DropUserMappingStmt *n = makeNode(DropUserMappingStmt);
+ n->username = $5;
+ n->servername = $7;
+ n->missing_ok = false;
+ $$ = (Node *) n;
+ }
+ | DROP USER MAPPING IF_P EXISTS FOR AuthIdent SERVER any_name
+ {
+ DropUserMappingStmt *n = makeNode(DropUserMappingStmt);
+ n->username = $7;
+ n->servername = $9;
+ n->missing_ok = true;
+ $$ = (Node *) n;
+ }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERY :
+ * ALTER USER MAPPING SERVER OPTIONS
+ *
+ ****************************************************************************/
+
+ AlterUserMappingStmt: ALTER USER MAPPING FOR AuthIdent SERVER any_name AlterGenericOptions
+ {
+ AlterUserMappingStmt *n = makeNode(AlterUserMappingStmt);
+ n->username = $5;
+ n->servername = $7;
+ n->options = $8;
+ $$ = (Node *) n;
+ }
+ ;
+
+ /*****************************************************************************
+ *
* QUERIES :
* CREATE TRIGGER ...
* DROP TRIGGER ...
***************
*** 3947,3952 **** privilege_target:
--- 4273,4292 ----
n->objs = $2;
$$ = n;
}
+ | SERVER any_name_list
+ {
+ PrivTarget *n = makeNode(PrivTarget);
+ n->objtype = ACL_OBJECT_FOREIGN_SERVER;
+ n->objs = $2;
+ $$ = n;
+ }
+ | FOREIGN DATA_P WRAPPER any_name_list
+ {
+ PrivTarget *n = makeNode(PrivTarget);
+ n->objtype = ACL_OBJECT_FDW;
+ n->objs = $4;
+ $$ = n;
+ }
;
***************
*** 5122,5127 **** AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId
--- 5462,5483 ----
n->newowner = $8;
$$ = (Node *)n;
}
+ | ALTER FOREIGN DATA_P WRAPPER any_name OWNER TO RoleId
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_FDW;
+ n->object = $5;
+ n->newowner = $8;
+ $$ = (Node *)n;
+ }
+ | ALTER SERVER any_name OWNER TO RoleId
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_FOREIGN_SERVER;
+ n->object = $3;
+ n->newowner = $6;
+ $$ = (Node *)n;
+ }
;
***************
*** 9555,9560 **** unreserved_keyword:
--- 9911,9917 ----
| INVOKER
| ISOLATION
| KEY
+ | LIBRARY
| LANCOMPILER
| LANGUAGE
| LARGE_P
***************
*** 9593,9598 **** unreserved_keyword:
--- 9950,9956 ----
| OIDS
| OPERATOR
| OPTION
+ | OPTIONS
| OWNED
| OWNER
| PARSER
***************
*** 9628,9633 **** unreserved_keyword:
--- 9986,9992 ----
| ROWS
| RULE
| SAVEPOINT
+ | SERVER
| SCHEMA
| SCROLL
| SEARCH
***************
*** 9680,9685 **** unreserved_keyword:
--- 10039,10045 ----
| WHITESPACE_P
| WITHOUT
| WORK
+ | WRAPPER
| WRITE
| XML_P
| YEAR_P
*** a/src/backend/parser/keywords.c
--- b/src/backend/parser/keywords.c
***************
*** 229,234 **** const ScanKeyword ScanKeywords[] = {
--- 229,235 ----
{"least", LEAST, COL_NAME_KEYWORD},
{"left", LEFT, TYPE_FUNC_NAME_KEYWORD},
{"level", LEVEL, UNRESERVED_KEYWORD},
+ {"library", LIBRARY, UNRESERVED_KEYWORD},
{"like", LIKE, TYPE_FUNC_NAME_KEYWORD},
{"limit", LIMIT, RESERVED_KEYWORD},
{"listen", LISTEN, UNRESERVED_KEYWORD},
***************
*** 281,286 **** const ScanKeyword ScanKeywords[] = {
--- 282,288 ----
{"only", ONLY, RESERVED_KEYWORD},
{"operator", OPERATOR, UNRESERVED_KEYWORD},
{"option", OPTION, UNRESERVED_KEYWORD},
+ {"options", OPTIONS, UNRESERVED_KEYWORD},
{"or", OR, RESERVED_KEYWORD},
{"order", ORDER, RESERVED_KEYWORD},
{"out", OUT_P, COL_NAME_KEYWORD},
***************
*** 339,344 **** const ScanKeyword ScanKeywords[] = {
--- 341,347 ----
{"select", SELECT, RESERVED_KEYWORD},
{"sequence", SEQUENCE, UNRESERVED_KEYWORD},
{"serializable", SERIALIZABLE, UNRESERVED_KEYWORD},
+ {"server", SERVER, UNRESERVED_KEYWORD},
{"session", SESSION, UNRESERVED_KEYWORD},
{"session_user", SESSION_USER, RESERVED_KEYWORD},
{"set", SET, UNRESERVED_KEYWORD},
***************
*** 411,416 **** const ScanKeyword ScanKeywords[] = {
--- 414,420 ----
{"with", WITH, RESERVED_KEYWORD},
{"without", WITHOUT, UNRESERVED_KEYWORD},
{"work", WORK, UNRESERVED_KEYWORD},
+ {"wrapper", WRAPPER, UNRESERVED_KEYWORD},
{"write", WRITE, UNRESERVED_KEYWORD},
{"xml", XML_P, UNRESERVED_KEYWORD},
{"xmlattributes", XMLATTRIBUTES, COL_NAME_KEYWORD},
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 203,208 **** check_xact_readonly(Node *parsetree)
--- 203,217 ----
case T_ReassignOwnedStmt:
case T_AlterTSDictionaryStmt:
case T_AlterTSConfigurationStmt:
+ case T_CreateFdwStmt:
+ case T_AlterFdwStmt:
+ case T_DropFdwStmt:
+ case T_CreateForeignServerStmt:
+ case T_AlterForeignServerStmt:
+ case T_DropForeignServerStmt:
+ case T_CreateUserMappingStmt:
+ case T_AlterUserMappingStmt:
+ case T_DropUserMappingStmt:
ereport(ERROR,
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
errmsg("transaction is read-only")));
***************
*** 452,457 **** ProcessUtility(Node *parsetree,
--- 461,502 ----
DropTableSpace((DropTableSpaceStmt *) parsetree);
break;
+ case T_CreateFdwStmt:
+ CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
+ break;
+
+ case T_AlterFdwStmt:
+ AlterForeignDataWrapper((AlterFdwStmt *) parsetree);
+ break;
+
+ case T_DropFdwStmt:
+ RemoveForeignDataWrapper((DropFdwStmt *) parsetree);
+ break;
+
+ case T_CreateForeignServerStmt:
+ CreateForeignServer((CreateForeignServerStmt *) parsetree);
+ break;
+
+ case T_AlterForeignServerStmt:
+ AlterForeignServer((AlterForeignServerStmt *) parsetree);
+ break;
+
+ case T_DropForeignServerStmt:
+ RemoveForeignServer((DropForeignServerStmt *) parsetree);
+ break;
+
+ case T_CreateUserMappingStmt:
+ CreateUserMapping((CreateUserMappingStmt *) parsetree);
+ break;
+
+ case T_AlterUserMappingStmt:
+ AlterUserMapping((AlterUserMappingStmt *) parsetree);
+ break;
+
+ case T_DropUserMappingStmt:
+ RemoveUserMapping((DropUserMappingStmt *) parsetree);
+ break;
+
case T_DropStmt:
{
DropStmt *stmt = (DropStmt *) parsetree;
***************
*** 1310,1315 **** CreateCommandTag(Node *parsetree)
--- 1355,1396 ----
tag = "DROP TABLESPACE";
break;
+ case T_CreateFdwStmt:
+ tag = "CREATE FOREIGN DATA WRAPPER";
+ break;
+
+ case T_AlterFdwStmt:
+ tag = "ALTER FOREIGN DATA WRAPPER";
+ break;
+
+ case T_DropFdwStmt:
+ tag = "DROP FOREIGN DATA WRAPPER";
+ break;
+
+ case T_CreateForeignServerStmt:
+ tag = "CREATE SERVER";
+ break;
+
+ case T_AlterForeignServerStmt:
+ tag = "ALTER SERVER";
+ break;
+
+ case T_DropForeignServerStmt:
+ tag = "DROP SERVER";
+ break;
+
+ case T_CreateUserMappingStmt:
+ tag = "CREATE USER MAPPING";
+ break;
+
+ case T_AlterUserMappingStmt:
+ tag = "ALTER USER MAPPING";
+ break;
+
+ case T_DropUserMappingStmt:
+ tag = "DROP USER MAPPING";
+ break;
+
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
***************
*** 1523,1528 **** CreateCommandTag(Node *parsetree)
--- 1604,1615 ----
case OBJECT_TSDICTIONARY:
tag = "ALTER TEXT SEARCH DICTIONARY";
break;
+ case OBJECT_FDW:
+ tag = "ALTER FOREIGN DATA WRAPPER";
+ break;
+ case OBJECT_FOREIGN_SERVER:
+ tag = "ALTER SERVER";
+ break;
default:
tag = "???";
break;
***************
*** 2037,2042 **** GetCommandLogLevel(Node *parsetree)
--- 2124,2141 ----
lev = LOGSTMT_DDL;
break;
+ case T_CreateFdwStmt:
+ case T_AlterFdwStmt:
+ case T_DropFdwStmt:
+ case T_CreateForeignServerStmt:
+ case T_AlterForeignServerStmt:
+ case T_DropForeignServerStmt:
+ case T_CreateUserMappingStmt:
+ case T_AlterUserMappingStmt:
+ case T_DropUserMappingStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_DropStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/acl.c
--- b/src/backend/utils/adt/acl.c
***************
*** 577,582 **** acldefault(GrantObjectType objtype, Oid ownerId)
--- 577,590 ----
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_TABLESPACE;
break;
+ case ACL_OBJECT_FDW:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_FDW;
+ break;
+ case ACL_OBJECT_FOREIGN_SERVER:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
*** a/src/backend/utils/cache/syscache.c
--- b/src/backend/utils/cache/syscache.c
***************
*** 46,51 ****
--- 46,54 ----
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+ #include "catalog/pg_fdw.h"
+ #include "catalog/pg_foreign_server.h"
+ #include "catalog/pg_user_mapping.h"
#include "utils/rel.h"
#include "utils/syscache.h"
***************
*** 365,370 **** static const struct cachedesc cacheinfo[] = {
--- 368,445 ----
},
256
},
+ {ForeignDataWrapperRelationId, /* FOREIGNDATAWRAPPEROID */
+ ForeignDataWrapperOidIndexId,
+ 0,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {ForeignDataWrapperRelationId, /* FOREIGNDATAWRAPPERNAMENSP */
+ ForeignDataWrapperNameNspIndexId,
+ 0,
+ 2,
+ {
+ Anum_pg_fdw_fdwname,
+ Anum_pg_fdw_fdwnamespace,
+ 0,
+ 0
+ },
+ 8
+ },
+ {ForeignServerRelationId, /* FOREIGNSERVEROID */
+ ForeignServerOidIndexId,
+ 0,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 32
+ },
+ {ForeignServerRelationId, /* FOREIGNSERVERNAMENSP */
+ ForeignServerNameNspIndexId,
+ 0,
+ 2,
+ {
+ Anum_pg_foreign_server_srvname,
+ Anum_pg_foreign_server_srvnamespace,
+ 0,
+ 0
+ },
+ 32
+ },
+ {ForeignUserMappingRelationId, /* FOREIGNUSERMAPPINGOID */
+ ForeignUserMappingOidIndexId,
+ 0,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 128
+ },
+ {ForeignUserMappingRelationId, /* FOREIGNUSERMAPPINGUSEIDSRV */
+ ForeignUserMappingUseIdSrvIndexId,
+ 0,
+ 2,
+ {
+ Anum_pg_user_mapping_usesysid,
+ Anum_pg_user_mapping_usesrv,
+ 0,
+ 0
+ },
+ 128
+ },
{IndexRelationId, /* INDEXRELID */
IndexRelidIndexId,
Anum_pg_index_indrelid,
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
***************
*** 91,96 **** getSchemaData(int *numTablesPtr)
--- 91,98 ----
TSTemplateInfo *tmplinfo;
TSDictInfo *dictinfo;
TSConfigInfo *cfginfo;
+ FdwInfo *fdwinfo;
+ ForeignServerInfo *srvinfo;
int numNamespaces;
int numAggregates;
int numInherits;
***************
*** 104,109 **** getSchemaData(int *numTablesPtr)
--- 106,113 ----
int numTSTemplates;
int numTSDicts;
int numTSConfigs;
+ int numForeignDataWrappers;
+ int numForeignServers;
if (g_verbose)
write_msg(NULL, "reading schemas\n");
***************
*** 155,160 **** getSchemaData(int *numTablesPtr)
--- 159,172 ----
cfginfo = getTSConfigurations(&numTSConfigs);
if (g_verbose)
+ write_msg(NULL, "reading user-defined foreign data wrappers\n");
+ fdwinfo = getForeignDataWrappers(&numForeignDataWrappers);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined foreign servers\n");
+ srvinfo = getForeignServers(&numForeignServers);
+
+ if (g_verbose)
write_msg(NULL, "reading user-defined operator families\n");
opfinfo = getOpfamilies(&numOpfamilies);
*** a/src/bin/pg_dump/dumputils.c
--- b/src/bin/pg_dump/dumputils.c
***************
*** 687,692 **** do { \
--- 687,696 ----
}
else if (strcmp(type, "TABLESPACE") == 0)
CONVERT_PRIV('C', "CREATE");
+ else if (strcmp(type, "FOREIGN DATA WRAPPER") == 0)
+ CONVERT_PRIV('U', "USAGE");
+ else if (strcmp(type, "SERVER") == 0)
+ CONVERT_PRIV('U', "USAGE");
else
abort();
*** a/src/bin/pg_dump/pg_backup_archiver.c
--- b/src/bin/pg_dump/pg_backup_archiver.c
***************
*** 2455,2461 **** _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
strcmp(type, "TABLE") == 0 ||
strcmp(type, "TYPE") == 0 ||
strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
! strcmp(type, "TEXT SEARCH CONFIGURATION") == 0)
{
appendPQExpBuffer(buf, "%s ", type);
if (te->namespace && te->namespace[0]) /* is null pre-7.3 */
--- 2455,2463 ----
strcmp(type, "TABLE") == 0 ||
strcmp(type, "TYPE") == 0 ||
strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
! strcmp(type, "TEXT SEARCH CONFIGURATION") == 0 ||
! strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
! strcmp(type, "SERVER") == 0)
{
appendPQExpBuffer(buf, "%s ", type);
if (te->namespace && te->namespace[0]) /* is null pre-7.3 */
***************
*** 2636,2642 **** _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
strcmp(te->desc, "VIEW") == 0 ||
strcmp(te->desc, "SEQUENCE") == 0 ||
strcmp(te->desc, "TEXT SEARCH DICTIONARY") == 0 ||
! strcmp(te->desc, "TEXT SEARCH CONFIGURATION") == 0)
{
PQExpBuffer temp = createPQExpBuffer();
--- 2638,2646 ----
strcmp(te->desc, "VIEW") == 0 ||
strcmp(te->desc, "SEQUENCE") == 0 ||
strcmp(te->desc, "TEXT SEARCH DICTIONARY") == 0 ||
! strcmp(te->desc, "TEXT SEARCH CONFIGURATION") == 0 ||
! strcmp(te->desc, "FOREIGN DATA WRAPPER") == 0 ||
! strcmp(te->desc, "SERVER") == 0)
{
PQExpBuffer temp = createPQExpBuffer();
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 158,163 **** static void dumpTSParser(Archive *fout, TSParserInfo *prsinfo);
--- 158,168 ----
static void dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo);
static void dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo);
static void dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo);
+ static void dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo);
+ static void dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo);
+ static void dumpUserMappings(Archive *fout, const char *target,
+ const char *servername, const char *namespace,
+ const char *owner, CatalogId catalogId, DumpId dumpId);
static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
const char *type, const char *name,
***************
*** 5269,5274 **** getTSConfigurations(int *numTSConfigs)
--- 5274,5448 ----
return cfginfo;
}
+ /*
+ * getForeignDataWrappers:
+ * read all foreign data wrappers in the system catalogs and return
+ * them in the FdwInfo* structure
+ *
+ * numForeignDataWrappers is set to the number of fdws read in
+ */
+ FdwInfo *
+ getForeignDataWrappers(int *numForeignDataWrappers)
+ {
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ FdwInfo *fdwinfo;
+ int i_oid;
+ int i_fdwname;
+ int i_fdwnamespace;
+ int i_rolname;
+ int i_fdwlibrary;
+ int i_fdwacl;
+ int i_fdwoptions;
+
+ /* Before 8.4, there are no foreign data wrappers */
+ if (g_fout->remoteVersion < 80400)
+ {
+ *numForeignDataWrappers = 0;
+ return NULL;
+ }
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ appendPQExpBuffer(query, "SELECT oid, fdwname, "
+ "fdwnamespace, (%s fdwowner) as rolname, fdwlibrary, fdwacl,"
+ "array_to_string(ARRAY("
+ " select option_name || ' ' || quote_literal(option_value)"
+ " from pg_get_foreign_data_wrapper_options(oid)), ', ') as fdwoptions "
+ "FROM pg_foreign_data_wrapper",
+ username_subquery);
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+ *numForeignDataWrappers = ntups;
+
+ fdwinfo = (FdwInfo *) malloc(ntups * sizeof(FdwInfo));
+
+ i_oid = PQfnumber(res, "oid");
+ i_fdwname = PQfnumber(res, "fdwname");
+ i_fdwnamespace = PQfnumber(res, "fdwnamespace");
+ i_rolname = PQfnumber(res, "rolname");
+ i_fdwlibrary = PQfnumber(res, "fdwlibrary");
+ i_fdwacl = PQfnumber(res, "fdwacl");
+ i_fdwoptions = PQfnumber(res, "fdwoptions");
+
+ for (i = 0; i < ntups; i++)
+ {
+ fdwinfo[i].dobj.objType = DO_FDW;
+ fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&fdwinfo[i].dobj);
+ fdwinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_fdwname));
+ fdwinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_fdwnamespace)),
+ fdwinfo[i].dobj.catId.oid);
+ fdwinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
+ fdwinfo[i].fdwlibrary = strdup(PQgetvalue(res, i, i_fdwlibrary));
+ fdwinfo[i].fdwoptions = strdup(PQgetvalue(res, i, i_fdwoptions));
+ fdwinfo[i].fdwacl = strdup(PQgetvalue(res, i, i_fdwacl));
+
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(fdwinfo[i].dobj));
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return fdwinfo;
+ }
+
+ /*
+ * getForeignServers:
+ * read all foreign servers in the system catalogs and return
+ * them in the ForeignServerInfo * structure
+ *
+ * numForeignServers is set to the number of servers read in
+ */
+ ForeignServerInfo *
+ getForeignServers(int *numForeignServers)
+ {
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ ForeignServerInfo *srvinfo;
+ int i_oid;
+ int i_srvname;
+ int i_srvnamespace;
+ int i_rolname;
+ int i_srvfdw;
+ int i_srvtype;
+ int i_srvversion;
+ int i_srvacl;
+ int i_srvoptions;
+
+ /* Before 8.4, there are no foreign servers */
+ if (g_fout->remoteVersion < 80400)
+ {
+ *numForeignServers = 0;
+ return NULL;
+ }
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ appendPQExpBuffer(query, "SELECT oid, srvname, "
+ "srvnamespace, (%s srvowner) as rolname, "
+ "srvfdw, srvtype, srvversion, srvacl,"
+ "array_to_string(ARRAY("
+ " select option_name || ' ' || quote_literal(option_value)"
+ " from pg_get_foreign_server_options(oid)), ', ') as srvoptions "
+ "FROM pg_foreign_server",
+ username_subquery);
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+ *numForeignServers = ntups;
+
+ srvinfo = (ForeignServerInfo *) malloc(ntups * sizeof(ForeignServerInfo));
+
+ i_oid = PQfnumber(res, "oid");
+ i_srvname = PQfnumber(res, "srvname");
+ i_srvnamespace = PQfnumber(res, "srvnamespace");
+ i_rolname = PQfnumber(res, "rolname");
+ i_srvfdw = PQfnumber(res, "srvfdw");
+ i_srvtype = PQfnumber(res, "srvtype");
+ i_srvversion = PQfnumber(res, "srvversion");
+ i_srvacl = PQfnumber(res, "srvacl");
+ i_srvoptions = PQfnumber(res, "srvoptions");
+
+ for (i = 0; i < ntups; i++)
+ {
+ srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
+ srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&srvinfo[i].dobj);
+ srvinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_srvname));
+ srvinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_srvnamespace)),
+ srvinfo[i].dobj.catId.oid);
+ srvinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
+ srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
+ srvinfo[i].srvtype = strdup(PQgetvalue(res, i, i_srvtype));
+ srvinfo[i].srvversion = strdup(PQgetvalue(res, i, i_srvversion));
+ srvinfo[i].srvoptions = strdup(PQgetvalue(res, i, i_srvoptions));
+ srvinfo[i].srvacl = strdup(PQgetvalue(res, i, i_srvacl));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(srvinfo[i].dobj));
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return srvinfo;
+ }
/*
* dumpComment --
***************
*** 5671,5676 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
--- 5845,5856 ----
case DO_TSCONFIG:
dumpTSConfig(fout, (TSConfigInfo *) dobj);
break;
+ case DO_FDW:
+ dumpForeignDataWrapper(fout, (FdwInfo *) dobj);
+ break;
+ case DO_FOREIGN_SERVER:
+ dumpForeignServer(fout, (ForeignServerInfo *) dobj);
+ break;
case DO_BLOBS:
ArchiveEntry(fout, dobj->catId, dobj->dumpId,
dobj->name, NULL, NULL, "",
***************
*** 8976,8981 **** dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo)
--- 9156,9398 ----
destroyPQExpBuffer(query);
}
+ /*
+ * dumpForeignDataWrapper
+ * write out a single foreign data wrapper definition
+ */
+ static void
+ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo)
+ {
+ PQExpBuffer q;
+ PQExpBuffer delq;
+ char *namecopy;
+
+ /* Skip if not to be dumped */
+ if (!fdwinfo->dobj.dump || dataOnly)
+ return;
+
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fdwinfo->dobj.namespace->dobj.name);
+
+ appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s LIBRARY '%s'",
+ fmtId(fdwinfo->dobj.name), fdwinfo->fdwlibrary);
+ if (fdwinfo->fdwoptions && strlen(fdwinfo->fdwoptions) > 0)
+ appendPQExpBuffer(q, " OPTIONS (%s)", fdwinfo->fdwoptions);
+
+ appendPQExpBuffer(q, ";\n");
+
+ /*
+ * DROP must be fully qualified in case same name appears in pg_catalog
+ */
+ appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s",
+ fmtId(fdwinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, ".%s;\n",
+ fmtId(fdwinfo->dobj.name));
+
+ ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
+ fdwinfo->dobj.name,
+ fdwinfo->dobj.namespace->dobj.name,
+ NULL,
+ fdwinfo->rolname,
+ false, "FOREIGN DATA WRAPPER", q->data, delq->data, NULL,
+ fdwinfo->dobj.dependencies, fdwinfo->dobj.nDeps,
+ NULL, NULL);
+
+ /* Handle the ACL */
+ namecopy = strdup(fmtId(fdwinfo->dobj.name));
+ dumpACL(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
+ "FOREIGN DATA WRAPPER",
+ namecopy, fdwinfo->dobj.name,
+ fdwinfo->dobj.namespace->dobj.name, fdwinfo->rolname,
+ fdwinfo->fdwacl);
+ free(namecopy);
+
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+ }
+
+ /*
+ * dumpForeignServer
+ * write out a foreign server definition
+ */
+ static void
+ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo)
+ {
+ PQExpBuffer q;
+ PQExpBuffer delq;
+ PQExpBuffer query;
+ PGresult *res;
+ int ntups;
+ char *namecopy;
+ char *nspname;
+ char *fdwname;
+
+ /* Skip if not to be dumped */
+ if (!srvinfo->dobj.dump || dataOnly)
+ return;
+
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ /* look up the foreign data wrapper */
+ appendPQExpBuffer(query, "SELECT nspname, fdwname "
+ "FROM pg_foreign_data_wrapper w, pg_namespace n "
+ "WHERE w.oid = '%u' AND n.oid = w.fdwnamespace",
+ srvinfo->srvfdw);
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+ ntups = PQntuples(res);
+ if (ntups != 1)
+ {
+ write_msg(NULL, "query returned %d rows instead of one: %s\n",
+ ntups, query->data);
+ exit_nicely();
+ }
+ nspname = PQgetvalue(res, 0, 0);
+ fdwname = PQgetvalue(res, 0, 1);
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(srvinfo->dobj.namespace->dobj.name);
+
+ appendPQExpBuffer(q, "CREATE SERVER %s", fmtId(srvinfo->dobj.name));
+ if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
+ appendPQExpBuffer(q, " TYPE '%s'", srvinfo->srvtype);
+ if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
+ appendPQExpBuffer(q, " VERSION '%s'", srvinfo->srvversion);
+
+ appendPQExpBuffer(q, " FOREIGN DATA WRAPPER ");
+ if (strcmp(nspname, srvinfo->dobj.namespace->dobj.name) != 0)
+ appendPQExpBuffer(q, "%s.", fmtId(nspname));
+ appendPQExpBuffer(q, "%s", fmtId(fdwname));
+
+ if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
+ appendPQExpBuffer(q, " OPTIONS (%s)", srvinfo->srvoptions);
+
+ appendPQExpBuffer(q, ";\n");
+
+ /*
+ * DROP must be fully qualified in case same name appears in pg_catalog.
+ * We include CASCADE here to get rid of any user mappings.
+ */
+ appendPQExpBuffer(delq, "DROP SERVER %s",
+ fmtId(srvinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, ".%s CASCADE;\n",
+ fmtId(srvinfo->dobj.name));
+
+ ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
+ srvinfo->dobj.name,
+ srvinfo->dobj.namespace->dobj.name,
+ NULL,
+ srvinfo->rolname,
+ false, "SERVER", q->data, delq->data, NULL,
+ srvinfo->dobj.dependencies, srvinfo->dobj.nDeps,
+ NULL, NULL);
+
+ /* Handle the ACL */
+ namecopy = strdup(fmtId(srvinfo->dobj.name));
+ dumpACL(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
+ "SERVER",
+ namecopy, srvinfo->dobj.name,
+ srvinfo->dobj.namespace->dobj.name, srvinfo->rolname,
+ srvinfo->srvacl);
+ free(namecopy);
+
+ /* Dump user mappings */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "SERVER %s", fmtId(srvinfo->dobj.name));
+ dumpUserMappings(fout, q->data,
+ srvinfo->dobj.name, srvinfo->dobj.namespace->dobj.name,
+ srvinfo->rolname,
+ srvinfo->dobj.catId, srvinfo->dobj.dumpId);
+
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+ }
+
+ /*
+ * dumpUserMappings
+ *
+ * This routine is used to dump any user mappings associated with the
+ * server handed to this routine. Should be called after ArchiveEntry()
+ * for the server.
+ */
+ static void
+ dumpUserMappings(Archive *fout, const char *target,
+ const char *servername, const char *namespace,
+ const char *owner,
+ CatalogId catalogId, DumpId dumpId)
+ {
+ PQExpBuffer q;
+ PQExpBuffer query;
+ PQExpBuffer um;
+ PGresult *res;
+ int ntups;
+ int i_usename;
+ int i_umoptions;
+ int i;
+
+ q = createPQExpBuffer();
+ um = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ appendPQExpBuffer(query, "SELECT usename, "
+ "array_to_string(ARRAY(\n"
+ " SELECT\n"
+ " option_name || ' ' || quote_literal(option_value)\n"
+ " FROM\n"
+ " pg_get_user_mapping_options(umid)\n"
+ "), ', ') as umoptions\n"
+ "FROM pg_user_mapping WHERE srvoid=%u",
+ catalogId.oid);
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+ i_usename = PQfnumber(res, "usename");
+ i_umoptions = PQfnumber(res, "umoptions");
+
+ for (i = 0; i < ntups; i++)
+ {
+ char *usename;
+ char *umoptions;
+
+ usename = PQgetvalue(res, i, i_usename);
+ umoptions = PQgetvalue(res, i, i_umoptions);
+
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
+ appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
+
+ if (umoptions && strlen(umoptions) > 0)
+ appendPQExpBuffer(q, " OPTIONS (%s)", umoptions);
+
+ appendPQExpBuffer(q, ";\n");
+
+ resetPQExpBuffer(um);
+ appendPQExpBuffer(um, "USER MAPPING FOR %s", fmtId(usename));
+
+ ArchiveEntry(fout, nilCatalogId, createDumpId(),
+ target,
+ namespace,
+ NULL,
+ owner, false,
+ um->data, q->data,
+ "", NULL,
+ &dumpId, 1,
+ NULL, NULL);
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+ destroyPQExpBuffer(um);
+ destroyPQExpBuffer(q);
+ }
/*----------
* Write out grant/revoke information
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
***************
*** 131,136 **** typedef enum
--- 131,138 ----
DO_TSDICT,
DO_TSTEMPLATE,
DO_TSCONFIG,
+ DO_FDW,
+ DO_FOREIGN_SERVER,
DO_BLOBS,
DO_BLOB_COMMENTS
} DumpableObjectType;
***************
*** 418,423 **** typedef struct _cfgInfo
--- 420,445 ----
Oid cfgparser;
} TSConfigInfo;
+ typedef struct _fdwInfo
+ {
+ DumpableObject dobj;
+ char *rolname;
+ char *fdwlibrary;
+ char *fdwoptions;
+ char *fdwacl;
+ } FdwInfo;
+
+ typedef struct _foreignServerInfo
+ {
+ DumpableObject dobj;
+ char *rolname;
+ Oid srvfdw;
+ char *srvtype;
+ char *srvversion;
+ char *srvacl;
+ char *srvoptions;
+ } ForeignServerInfo;
+
/* global decls */
extern bool force_quotes; /* double-quotes for identifiers flag */
extern bool g_verbose; /* verbose flag */
***************
*** 500,504 **** extern TSParserInfo *getTSParsers(int *numTSParsers);
--- 522,528 ----
extern TSDictInfo *getTSDictionaries(int *numTSDicts);
extern TSTemplateInfo *getTSTemplates(int *numTSTemplates);
extern TSConfigInfo *getTSConfigurations(int *numTSConfigs);
+ extern FdwInfo *getForeignDataWrappers(int *numForeignDataWrappers);
+ extern ForeignServerInfo *getForeignServers(int *numForeignServers);
#endif /* PG_DUMP_H */
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
***************
*** 50,55 **** static const int oldObjectTypePriority[] =
--- 50,57 ----
4, /* DO_TSDICT */
3, /* DO_TSTEMPLATE */
5, /* DO_TSCONFIG */
+ 3, /* DO_FDW */
+ 4, /* DO_FOREIGN_SERVER */
10, /* DO_BLOBS */
11 /* DO_BLOB_COMMENTS */
};
***************
*** 84,89 **** static const int newObjectTypePriority[] =
--- 86,93 ----
6, /* DO_TSDICT */
5, /* DO_TSTEMPLATE */
7, /* DO_TSCONFIG */
+ 3, /* DO_FDW */
+ 4, /* DO_FOREIGN_SERVER */
14, /* DO_BLOBS */
15 /* DO_BLOB_COMMENTS */
};
***************
*** 1123,1128 **** describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
--- 1127,1142 ----
"TEXT SEARCH CONFIGURATION %s (ID %d OID %u)",
obj->name, obj->dumpId, obj->catId.oid);
return;
+ case DO_FDW:
+ snprintf(buf, bufsize,
+ "FOREIGN DATA WRAPPER %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
+ case DO_FOREIGN_SERVER:
+ snprintf(buf, bufsize,
+ "FOREIGN SERVER %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
case DO_BLOBS:
snprintf(buf, bufsize,
"BLOBS (ID %d)",
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
***************
*** 416,421 **** exec_command(const char *cmd,
--- 416,430 ----
break;
}
break;
+ case 'w':
+ success = listForeignDataWrappers(pattern, show_verbose);
+ break;
+ case 'r':
+ success = listForeignServers(pattern, show_verbose);
+ break;
+ case 'm':
+ success = listUserMappings(pattern, show_verbose);
+ break;
default:
status = PSQL_CMD_UNKNOWN;
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 2766,2768 **** describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname,
--- 2766,2943 ----
PQclear(res);
return true;
}
+
+
+ /*
+ * \dw
+ *
+ * Describes foreign data wrappers
+ */
+ bool
+ listForeignDataWrappers(const char *pattern, bool verbose)
+ {
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname AS \"%s\",\n"
+ " fdwname AS \"%s\",\n"
+ " pg_catalog.pg_get_userbyid(fdwowner) AS \"%s\",\n"
+ " fdwlibrary AS \"%s\"\n",
+ gettext_noop("Schema"),
+ gettext_noop("Name"),
+ gettext_noop("Owner"),
+ gettext_noop("Library"));
+
+ if (verbose)
+ appendPQExpBuffer(&buf,
+ ",\n fdwacl as \"%s\","
+ " fdwoptions as \"%s\"",
+ gettext_noop("Access privileges"),
+ gettext_noop("Options"));
+
+ appendPQExpBuffer(&buf,
+ "\nFROM pg_catalog.pg_namespace n\n"
+ "JOIN pg_catalog.pg_foreign_data_wrapper f ON fdwnamespace=n.oid\n"
+ "WHERE (n.nspname !~ '^pg_temp_' OR\n"
+ " n.nspname = (pg_catalog.current_schemas(true))[1])\n"); /* temp schema is first */
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ "n.nspname", "fdwname", NULL,
+ "pg_catalog.pg_fdw_is_visible(f.oid)");
+
+ appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of foreign data wrappers");
+ myopt.translate_header = true;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+ }
+
+ /*
+ * \dr
+ *
+ * Describes servers.
+ */
+ bool
+ listForeignServers(const char *pattern, bool verbose)
+ {
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname AS \"%s\",\n"
+ " s.srvname AS \"%s\",\n"
+ " pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
+ " f.fdwname AS \"%s\"\n",
+ gettext_noop("Schema"),
+ gettext_noop("Name"),
+ gettext_noop("Owner"),
+ gettext_noop("Foreign Data Wrapper"));
+
+ if (verbose)
+ appendPQExpBuffer(&buf,
+ ",\n s.srvacl as \"%s\","
+ " s.srvtype as \"%s\","
+ " s.srvversion as \"%s\","
+ " s.srvoptions as \"%s\"",
+ gettext_noop("Access privileges"),
+ gettext_noop("Type"),
+ gettext_noop("Version"),
+ gettext_noop("Options"));
+
+ appendPQExpBuffer(&buf,
+ "\nFROM pg_catalog.pg_namespace n\n"
+ "JOIN pg_foreign_server s ON s.srvnamespace=n.oid\n"
+ "JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n"
+ "WHERE (n.nspname !~ '^pg_temp_' OR\n"
+ "n.nspname = (pg_catalog.current_schemas(true))[1])\n"); /* temp schema is first */
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ "n.nspname", "s.srvname", NULL,
+ "pg_catalog.pg_server_is_visible(s.oid)");
+
+ appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of foreign servers");
+ myopt.translate_header = true;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+ }
+
+ /*
+ * \dm
+ *
+ * Describes user mappings.
+ */
+ bool
+ listUserMappings(const char *pattern, bool verbose)
+ {
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname AS \"%s\",\n"
+ " um.srvname AS \"%s\",\n"
+ " um.usename AS \"%s\"",
+ gettext_noop("Schema"),
+ gettext_noop("Server"),
+ gettext_noop("Username"));
+
+ if (verbose)
+ appendPQExpBuffer(&buf,
+ ",\n nullif(array(select option_name||'='||option_value"
+ " from pg_get_user_mapping_options(um.umid)), '{}') \"%s\"",
+ gettext_noop("Options"));
+
+ appendPQExpBuffer(&buf,
+ "\nFROM pg_catalog.pg_namespace n\n"
+ "JOIN pg_foreign_server s ON s.srvnamespace=n.oid\n"
+ "JOIN pg_catalog.pg_user_mapping um ON s.oid=um.srvoid\n"
+ "WHERE (n.nspname !~ '^pg_temp_' OR\n"
+ "n.nspname = (pg_catalog.current_schemas(true))[1])\n"); /* temp schema is first */
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ "n.nspname", "um.srvname", "um.usename",
+ "pg_catalog.pg_server_is_visible(s.oid)");
+
+ appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of user mappings");
+ myopt.translate_header = true;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+ }
*** a/src/bin/psql/describe.h
--- b/src/bin/psql/describe.h
***************
*** 66,70 **** extern bool listCasts(const char *pattern);
--- 66,79 ----
/* \dn */
extern bool listSchemas(const char *pattern, bool verbose);
+ /* \dw */
+ extern bool listForeignDataWrappers(const char *pattern, bool verbose);
+
+ /* \dr */
+ extern bool listForeignServers(const char *pattern, bool verbose);
+
+ /* \dm */
+ extern bool listUserMappings(const char *pattern, bool verbose);
+
#endif /* DESCRIBE_H */
*** a/src/bin/psql/help.c
--- b/src/bin/psql/help.c
***************
*** 209,220 **** slashUsage(unsigned short int pager)
--- 209,223 ----
fprintf(output, _(" \\dFt [PATTERN] list text search templates\n"));
fprintf(output, _(" \\dFp [PATTERN] list text search parsers (add \"+\" for more detail)\n"));
fprintf(output, _(" \\dg [PATTERN] list roles (groups)\n"));
+ fprintf(output, _(" \\dm [PATTERN] list user mappings (add \"+\" for more detail)\n"));
fprintf(output, _(" \\dn [PATTERN] list schemas (add \"+\" for more detail)\n"));
fprintf(output, _(" \\do [NAME] list operators\n"));
fprintf(output, _(" \\dl list large objects, same as \\lo_list\n"));
fprintf(output, _(" \\dp [PATTERN] list table, view, and sequence access privileges\n"));
+ fprintf(output, _(" \\dr [PATTERN] list remote servers (add \"+\" for more detail)\n"));
fprintf(output, _(" \\dT [PATTERN] list data types (add \"+\" for more detail)\n"));
fprintf(output, _(" \\du [PATTERN] list roles (users)\n"));
+ fprintf(output, _(" \\dw [PATTERN] list foreign data wrappers (add \"+\" for more detail)\n"));
fprintf(output, _(" \\l list all databases (add \"+\" for more detail)\n"));
fprintf(output, _(" \\z [PATTERN] list table, view, and sequence access privileges (same as \\dp)\n"));
fprintf(output, "\n");
*** a/src/bin/psql/tab-complete.c
--- b/src/bin/psql/tab-complete.c
***************
*** 346,351 **** static const SchemaQuery Query_for_list_of_views = {
--- 346,381 ----
NULL
};
+ static const SchemaQuery Query_for_list_of_fdws = {
+ /* catname */
+ "pg_catalog.pg_foreign_data_wrapper f",
+ /* selcondition */
+ NULL,
+ /* viscondition */
+ "pg_catalog.pg_fdw_is_visible(f.oid)",
+ /* namespace */
+ "f.fdwnamespace",
+ /* result */
+ "pg_catalog.quote_ident(f.fdwname)",
+ /* qualresult */
+ NULL
+ };
+
+ static const SchemaQuery Query_for_list_of_servers = {
+ /* catname */
+ "pg_catalog.pg_foreign_server s",
+ /* selcondition */
+ NULL,
+ /* viscondition */
+ "pg_catalog.pg_server_is_visible(s.oid)",
+ /* namespace */
+ "s.srvnamespace",
+ /* result */
+ "pg_catalog.quote_ident(s.srvname)",
+ /* qualresult */
+ NULL
+ };
+
/*
* Queries to get lists of names of various kinds of things, possibly
***************
*** 525,530 **** static const pgsql_thing_t words_after_create[] = {
--- 555,561 ----
{"DATABASE", Query_for_list_of_databases},
{"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, true},
{"DOMAIN", NULL, &Query_for_list_of_domains},
+ {"FOREIGN DATA WRAPPER", NULL, NULL},
{"FUNCTION", NULL, &Query_for_list_of_functions},
{"GROUP", Query_for_list_of_roles},
{"LANGUAGE", Query_for_list_of_languages},
***************
*** 536,541 **** static const pgsql_thing_t words_after_create[] = {
--- 567,573 ----
{"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"},
{"SCHEMA", Query_for_list_of_schemas},
{"SEQUENCE", NULL, &Query_for_list_of_sequences},
+ {"SERVER", NULL, &Query_for_list_of_servers},
{"TABLE", NULL, &Query_for_list_of_tables},
{"TABLESPACE", Query_for_list_of_tablespaces},
{"TEMP", NULL, NULL}, /* for CREATE TEMP TABLE ... */
***************
*** 545,550 **** static const pgsql_thing_t words_after_create[] = {
--- 577,583 ----
{"TYPE", NULL, &Query_for_list_of_datatypes},
{"UNIQUE", NULL, NULL}, /* for CREATE UNIQUE INDEX ... */
{"USER", Query_for_list_of_roles},
+ {"USER MAPPING", NULL, NULL},
{"VIEW", NULL, &Query_for_list_of_views},
{NULL, NULL, NULL, false} /* end of list */
};
***************
*** 624,629 **** psql_completion(char *text, int start, int end)
--- 657,663 ----
"\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\df",
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl",
"\\dn", "\\do", "\\dp", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
+ "\\dw", "\\dm", "\\dr",
"\\e", "\\echo", "\\encoding",
"\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
***************
*** 686,694 **** psql_completion(char *text, int start, int end)
pg_strcasecmp(prev3_wd, "TABLE") != 0)
{
static const char *const list_ALTER[] =
! {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FUNCTION",
! "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", "SCHEMA", "SEQUENCE", "TABLE",
! "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "VIEW", NULL};
COMPLETE_WITH_LIST(list_ALTER);
}
--- 720,728 ----
pg_strcasecmp(prev3_wd, "TABLE") != 0)
{
static const char *const list_ALTER[] =
! {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION",
! "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
! "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING", "VIEW", NULL};
COMPLETE_WITH_LIST(list_ALTER);
}
***************
*** 2075,2080 **** psql_completion(char *text, int start, int end)
--- 2109,2122 ----
pg_strcasecmp(prev3_wd, "\\copy") != 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
+ /* FOREIGN DATA WRAPPER */
+ else if ((pg_strcasecmp(prev4_wd, "DROP") == 0 ||
+ pg_strcasecmp(prev4_wd, "ALTER") == 0 ||
+ pg_strcasecmp(prev4_wd, "CREATE") == 0) &&
+ pg_strcasecmp(prev3_wd, "FOREIGN") == 0 &&
+ pg_strcasecmp(prev2_wd, "DATA") == 0 &&
+ pg_strcasecmp(prev_wd, "WRAPPER") == 0)
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_fdws, NULL);
/* Backslash commands */
/* TODO: \dc \dd \dl */
***************
*** 2104,2109 **** psql_completion(char *text, int start, int end)
--- 2146,2153 ----
COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
else if (strcmp(prev_wd, "\\dp") == 0 || strcmp(prev_wd, "\\z") == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
+ else if (strcmp(prev_wd, "\\dr") == 0 || strcmp(prev_wd, "\\dr+") == 0)
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_servers, NULL);
else if (strcmp(prev_wd, "\\ds") == 0 || strcmp(prev_wd, "\\ds+") == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
else if (strcmp(prev_wd, "\\dS") == 0 || strcmp(prev_wd, "\\dS+") == 0)
***************
*** 2116,2121 **** psql_completion(char *text, int start, int end)
--- 2160,2167 ----
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
else if (strcmp(prev_wd, "\\dv") == 0 || strcmp(prev_wd, "\\dv+") == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
+ else if (strcmp(prev_wd, "\\dw") == 0 || strcmp(prev_wd, "\\dw+") == 0)
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_fdws, NULL);
else if (strcmp(prev_wd, "\\encoding") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0)
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
***************
*** 143,148 **** typedef enum ObjectClass
--- 143,151 ----
OCLASS_ROLE, /* pg_authid */
OCLASS_DATABASE, /* pg_database */
OCLASS_TBLSPACE, /* pg_tablespace */
+ OCLASS_FDW, /* pg_foreign_data_wrapper */
+ OCLASS_FOREIGN_SERVER, /* pg_foreign_server */
+ OCLASS_USER_MAPPING, /* pg_foreign_user_mapping */
MAX_OCLASS /* MUST BE LAST */
} ObjectClass;
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 252,257 **** DECLARE_UNIQUE_INDEX(pg_type_oid_index, 2703, on pg_type using btree(oid oid_ops
--- 252,275 ----
DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops));
#define TypeNameNspIndexId 2704
+ DECLARE_UNIQUE_INDEX(pg_foreign_data_wrapper_oid_index, 112, on pg_foreign_data_wrapper using btree(oid oid_ops));
+ #define ForeignDataWrapperOidIndexId 112
+
+ DECLARE_UNIQUE_INDEX(pg_foreign_data_wrapper_name_nsp_index, 548, on pg_foreign_data_wrapper using btree(fdwname name_ops, fdwnamespace oid_ops));
+ #define ForeignDataWrapperNameNspIndexId 548
+
+ DECLARE_UNIQUE_INDEX(pg_foreign_server_oid_index, 113, on pg_foreign_server using btree(oid oid_ops));
+ #define ForeignServerOidIndexId 113
+
+ DECLARE_UNIQUE_INDEX(pg_foreign_server_name_nsp_index, 549, on pg_foreign_server using btree(srvname name_ops, srvnamespace oid_ops));
+ #define ForeignServerNameNspIndexId 549
+
+ DECLARE_UNIQUE_INDEX(pg_foreign_user_mapping_oid_index, 174, on pg_foreign_user_mapping using btree(oid oid_ops));
+ #define ForeignUserMappingOidIndexId 174
+
+ DECLARE_UNIQUE_INDEX(pg_foreign_user_mapping_useidsrv_index, 175, on pg_foreign_user_mapping using btree(usesysid oid_ops, usesrv oid_ops));
+ #define ForeignUserMappingUseIdSrvIndexId 175
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
*** a/src/include/catalog/namespace.h
--- b/src/include/catalog/namespace.h
***************
*** 50,55 **** extern Oid RangeVarGetCreationNamespace(const RangeVar *newRelation);
--- 50,60 ----
extern Oid RelnameGetRelid(const char *relname);
extern bool RelationIsVisible(Oid relid);
+ extern Oid ForeignDataWrapperNameGetId(List *names, bool failOK);
+ extern bool ForeignDataWrapperIsVisible(Oid fdwid);
+ extern Oid ForeignServerNameGetServerid(List *names, bool failOK);
+ extern bool ForeignServerIsVisible(Oid srvid);
+
extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid);
*** /dev/null
--- b/src/include/catalog/pg_fdw.h
***************
*** 0 ****
--- 1,74 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pg_fdw.h
+ * definition of the system "foreign data wrapper" relation (pg_fdw)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef PG_FDW_H
+ #define PG_FDW_H
+
+ #include "catalog/genbki.h"
+
+ /* ----------------
+ * pg_fdw definition. cpp turns this into
+ * typedef struct FormData_pg_foreign_data_wrapper
+ * ----------------
+ */
+ #define ForeignDataWrapperRelationId 2328
+
+ CATALOG(pg_foreign_data_wrapper,2328)
+ {
+ NameData fdwname; /* foreign data wrapper name */
+ Oid fdwnamespace; /* OID of namespace containing this FDW */
+ Oid fdwowner; /* FDW owner */
+
+ /* VARIABLE LENGTH FIELDS start here. */
+
+ text fdwlibrary; /* FDW shared library location */
+ aclitem fdwacl[1]; /* access permissions */
+ text fdwoptions[1]; /* access-method-specific options */
+ } FormData_pg_foreign_data_wrapper;
+
+ /* ----------------
+ * Form_pg_fdw corresponds to a pointer to a tuple with
+ * the format of pg_fdw relation.
+ * ----------------
+ */
+ typedef FormData_pg_foreign_data_wrapper *Form_pg_foreign_data_wrapper;
+
+ /* ----------------
+ * compiler constants for pg_fdw
+ * ----------------
+ */
+
+ #define Natts_pg_fdw 6
+ #define Anum_pg_fdw_fdwname 1
+ #define Anum_pg_fdw_fdwnamespace 2
+ #define Anum_pg_fdw_fdwowner 3
+ #define Anum_pg_fdw_fdwlibrary 4
+ #define Anum_pg_fdw_fdwacl 5
+ #define Anum_pg_fdw_fdwoptions 6
+
+ /* ----------------
+ * initial contents of pg_fdw
+ * ----------------
+ */
+
+ DATA(insert OID = 626 ( default PGNSP PGUID default_fdw _null_ _null_ ));
+ DESCR("Pass-through foreign data wrapper");
+ DATA(insert OID = 627 ( pgsql PGNSP PGUID pgsql_fdw _null_ _null_ ));
+ DESCR("pgsql foreign data wrapper");
+
+ #endif /* PG_FDW_H */
*** /dev/null
--- b/src/include/catalog/pg_foreign_server.h
***************
*** 0 ****
--- 1,67 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pg_foreign_server.h
+ * definition of the system "foreign server" relation (pg_foreign_server)
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef PG_FOREIGN_SERVER_H
+ #define PG_FOREIGN_SERVER_H
+
+ #include "catalog/genbki.h"
+
+ /* ----------------
+ * pg_foreign_server definition. cpp turns this into
+ * typedef struct FormData_pg_foreign_server
+ * ----------------
+ */
+ #define ForeignServerRelationId 1417
+
+ CATALOG(pg_foreign_server,1417)
+ {
+ NameData srvname; /* foreign server name */
+ Oid srvnamespace; /* OID of namespace containing this server */
+ Oid srvowner; /* server owner */
+ Oid srvfdw; /* server fdw */
+
+ /*
+ * VARIABLE LENGTH FIELDS start here. These fields may be NULL, too.
+ */
+ text srvtype;
+ text srvversion;
+ aclitem srvacl[1]; /* access permissions */
+ text srvoptions[1]; /* access-method-specific options */
+ } FormData_pg_foreign_server;
+
+ /* ----------------
+ * Form_pg_foreign_server corresponds to a pointer to a tuple with
+ * the format of pg_foreign_server relation.
+ * ----------------
+ */
+ typedef FormData_pg_foreign_server *Form_pg_foreign_server;
+
+ /* ----------------
+ * compiler constants for pg_foreign_server
+ * ----------------
+ */
+
+ #define Natts_pg_foreign_server 8
+ #define Anum_pg_foreign_server_srvname 1
+ #define Anum_pg_foreign_server_srvnamespace 2
+ #define Anum_pg_foreign_server_srvowner 3
+ #define Anum_pg_foreign_server_srvfdw 4
+ #define Anum_pg_foreign_server_srvtype 5
+ #define Anum_pg_foreign_server_srvversion 6
+ #define Anum_pg_foreign_server_srvacl 7
+ #define Anum_pg_foreign_server_srvoptions 8
+
+ #endif /* PG_FOREIGN_SERVER_H */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3198,3203 **** DATA(insert OID = 3768 ( pg_ts_template_is_visible PGNSP PGUID 12 1 0 0 f f t f
--- 3198,3207 ----
DESCR("is text search template visible in search path?");
DATA(insert OID = 3758 ( pg_ts_config_is_visible PGNSP PGUID 12 1 0 0 f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_ts_config_is_visible _null_ _null_ _null_ ));
DESCR("is text search configuration visible in search path?");
+ DATA(insert OID = 3775 ( pg_fdw_is_visible PGNSP PGUID 12 1 0 0 f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_fdw_is_visible _null_ _null_ _null_ ));
+ DESCR("is foreign data wrapper visible in search path?");
+ DATA(insert OID = 3776 ( pg_server_is_visible PGNSP PGUID 12 1 0 0 f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_server_is_visible _null_ _null_ _null_ ));
+ DESCR("is foreign server visible in search path?");
DATA(insert OID = 2854 ( pg_my_temp_schema PGNSP PGUID 12 1 0 0 f f t f s 0 0 26 "" _null_ _null_ _null_ _null_ pg_my_temp_schema _null_ _null_ _null_ ));
DESCR("get OID of current session's temp schema, if any");
***************
*** 4602,4607 **** DESCR("record greater than or equal");
--- 4606,4622 ----
DATA(insert OID = 2987 ( btrecordcmp PGNSP PGUID 12 1 0 0 f f t f i 2 0 23 "2249 2249" _null_ _null_ _null_ _null_ btrecordcmp _null_ _null_ _null_ ));
DESCR("btree less-equal-greater");
+ /* foreign connections */
+ DATA(insert OID = 2995 ( pg_get_remote_connection_info PGNSP PGUID 12 1 3 0 f f t t s 2 0 2249 "19 19" "{19,19,25,25}" "{i,i,o,o}" "{server,username,option,value}" _null_ pg_get_remote_connection_info _null_ _null_ _null_ ));
+ DESCR("remote connection info");
+ DATA(insert OID = 2996 ( pg_get_remote_connection_info PGNSP PGUID 12 1 3 0 f f t t s 1 0 2249 "19" "{19,25,25}" "{i,o,o}" "{server,option_name,option_value}" _null_ pg_get_remote_connection_info _null_ _null_ _null_ ));
+ DESCR("remote connection info");
+ DATA(insert OID = 2997 ( pg_get_foreign_data_wrapper_options PGNSP PGUID 12 1 3 0 f f t t s 1 0 2249 "26" "{26,25,25}" "{i,o,o}" "{fdwid,option_name,option_value}" _null_ pg_get_foreign_data_wrapper_options _null_ _null_ _null_ ));
+ DESCR("user mapping options");
+ DATA(insert OID = 2998 ( pg_get_foreign_server_options PGNSP PGUID 12 1 3 0 f f t t s 1 0 2249 "26" "{26,25,25}" "{i,o,o}" "{srvid,option_name,option_value}" _null_ pg_get_foreign_server_options _null_ _null_ _null_ ));
+ DESCR("user mapping options");
+ DATA(insert OID = 2999 ( pg_get_user_mapping_options PGNSP PGUID 12 1 3 0 f f t t s 1 0 2249 "26" "{26,25,25}" "{i,o,o}" "{umid,option_name,option_value}" _null_ pg_get_user_mapping_options _null_ _null_ _null_ ));
+ DESCR("user mapping options");
/*
* Symbolic values for provolatile column: these indicate whether the result
*** /dev/null
--- b/src/include/catalog/pg_user_mapping.h
***************
*** 0 ****
--- 1,58 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pg_user_mapping.h
+ * definition of the system "foreign user mapping" relation (pg_foreign_user_mapping)
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef PG_USER_MAPPING_H
+ #define PG_USER_MAPPING_H
+
+ #include "catalog/genbki.h"
+
+ /* ----------------
+ * pg_user_mapping definition. cpp turns this into
+ * typedef struct FormData_pg_user_mapping
+ * ----------------
+ */
+ #define ForeignUserMappingRelationId 1418
+
+ CATALOG(pg_foreign_user_mapping,1418)
+ {
+ Oid usesysid; /* Id of the user, InvalidOid if PUBLIC is wanted */
+ Oid usesrv; /* server of this mapping */
+
+ /*
+ * VARIABLE LENGTH FIELDS start here. These fields may be NULL, too.
+ */
+
+ text useoptions[1]; /* access-method-specific options */
+ } FormData_pg_foreign_user_mapping;
+
+ /* ----------------
+ * Form_pg_user_mapping corresponds to a pointer to a tuple with
+ * the format of pg_user_mapping relation.
+ * ----------------
+ */
+ typedef FormData_pg_foreign_user_mapping *Form_pg_foreign_user_mapping;
+
+ /* ----------------
+ * compiler constants for pg_user_mapping
+ * ----------------
+ */
+
+ #define Natts_pg_user_mapping 3
+ #define Anum_pg_user_mapping_usesysid 1
+ #define Anum_pg_user_mapping_usesrv 2
+ #define Anum_pg_user_mapping_useoptions 3
+
+ #endif /* PG_USER_MAPPING_H */
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
***************
*** 118,123 **** extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
--- 118,139 ----
extern text *serialize_deflist(List *deflist);
extern List *deserialize_deflist(Datum txt);
+ /* commands/foreigncmds.c */
+ extern void AlterForeignServerOwner(List *names, Oid newOwnerId);
+ extern void AlterForeignDataWrapperOwner(List *names, Oid newOwnerId);
+ extern void CreateForeignDataWrapper(CreateFdwStmt *stmt);
+ extern void AlterForeignDataWrapper(AlterFdwStmt *stmt);
+ extern void RemoveForeignDataWrapper(DropFdwStmt *stmt);
+ extern void RemoveForeignDataWrapperById(Oid fdwId);
+ extern void CreateForeignServer(CreateForeignServerStmt *stmt);
+ extern void AlterForeignServer(AlterForeignServerStmt *stmt);
+ extern void RemoveForeignServer(DropForeignServerStmt *stmt);
+ extern void RemoveForeignServerById(Oid srvId);
+ extern void CreateUserMapping(CreateUserMappingStmt *stmt);
+ extern void AlterUserMapping(AlterUserMappingStmt *stmt);
+ extern void RemoveUserMapping(DropUserMappingStmt *stmt);
+ extern void RemoveUserMappingById(Oid umId);
+
/* support routines in commands/define.c */
extern char *case_translate_language_name(const char *input);
*** /dev/null
--- b/src/include/foreign/foreign.h
***************
*** 0 ****
--- 1,100 ----
+ /*-------------------------------------------------------------------------
+ *
+ * foreign.h
+ * support for foreign data wrappers, servers and user mappings.
+ *
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef FOREIGN_H
+ #define FOREIGN_H
+
+ #include "nodes/pg_list.h"
+ #include "nodes/parsenodes.h"
+
+ /* Helper for obtaining username for user mapping */
+ #define MappingUserName(userid) \
+ (OidIsValid(userid) ? GetUserNameFromId(userid) : "public")
+
+ /*
+ * Generic option types for validation.
+ * NB! Thes are treated as flags, so use only powers of two here.
+ */
+ typedef enum {
+ InvalidOpt = 0,
+ ServerOpt = 1, /* options applicable to SERVER */
+ UserMappingOpt = 2, /* options for USER MAPPING */
+ FdwOpt = 4, /* options for FOREIGN DATA WRAPPER */
+ } GenericOptionFlags;
+
+ typedef struct ForeignDataWrapperLibrary ForeignDataWrapperLibrary;
+
+ typedef struct ForeignDataWrapper
+ {
+ Oid fdwid; /* FDW Oid */
+ Oid namespace; /* FDW namespace */
+ Oid owner; /* FDW owner user Oid */
+ char *fdwname; /* Name of the FDW */
+ char *fdwlibrary; /* Library name */
+ List *options; /* fdwoptions as DefElem list */
+
+ ForeignDataWrapperLibrary *lib; /* interface to the FDW functions */
+ } ForeignDataWrapper;
+
+ typedef struct ForeignServer
+ {
+ Oid serverid; /* server Oid */
+ Oid fdwid; /* foreign data wrapper */
+ Oid namespace; /* server namespace */
+ Oid owner; /* server owner user Oid */
+ char *servername; /* name of the server */
+ char *servertype; /* server type, optional */
+ char *serverversion; /* server version, optional */
+ List *options; /* srvoptions as DefElem list */
+ } ForeignServer;
+
+ typedef struct ForeignUserMapping
+ {
+ Oid userid; /* local user Oid */
+ Oid serverid; /* server Oid */
+ List *options; /* useoptions as DefElem list */
+ } ForeignUserMapping;
+
+
+ /*
+ * Foreign data wrapper library function types.
+ */
+ typedef void (*OptionListValidatorFunc)(ForeignDataWrapper *,
+ GenericOptionFlags, List *);
+ typedef List * (*GetConnectionInfoFunc)(ForeignDataWrapper *, ForeignServer *,
+ ForeignUserMapping *);
+
+ /*
+ * Interface functions to the foreign data wrapper. This is decoupled
+ * from the FDW as there maybe several FDW-s accessing the same library.
+ */
+ struct ForeignDataWrapperLibrary
+ {
+ char *libname; /* name of the library file */
+ bool need_reload; /* true if the library has been unloaded */
+
+ GetConnectionInfoFunc GetConnectionInfo;
+ OptionListValidatorFunc validateOptionList;
+ };
+
+
+ extern ForeignServer *GetForeignServer(Oid serverid);
+ extern ForeignUserMapping *GetForeignUserMapping(Oid userid, Oid serverid);
+ extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid);
+ extern ForeignDataWrapperLibrary *GetForeignDataWrapperLibrary(
+ const char *libname);
+ extern List *GetRemoteConnectionInfo(Oid serverid, Oid userid);
+
+ /* FDW interface prototypes */
+ extern void InitializeFdw(ForeignDataWrapperLibrary *fdwl);
+
+ #endif /* FOREIGN_H */
*** a/src/include/nodes/makefuncs.h
--- b/src/include/nodes/makefuncs.h
***************
*** 67,70 **** extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype,
--- 67,72 ----
extern DefElem *makeDefElem(char *name, Node *arg);
+ extern OptionDefElem *makeOptionDefElem(int op, DefElem *def);
+
#endif /* MAKEFUNC_H */
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 325,330 **** typedef enum NodeTag
--- 325,339 ----
T_CreateEnumStmt,
T_AlterTSDictionaryStmt,
T_AlterTSConfigurationStmt,
+ T_CreateFdwStmt,
+ T_AlterFdwStmt,
+ T_DropFdwStmt,
+ T_CreateForeignServerStmt,
+ T_AlterForeignServerStmt,
+ T_DropForeignServerStmt,
+ T_CreateUserMappingStmt,
+ T_AlterUserMappingStmt,
+ T_DropUserMappingStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
***************
*** 348,353 **** typedef enum NodeTag
--- 357,363 ----
T_IndexElem,
T_Constraint,
T_DefElem,
+ T_OptionDefElem,
T_RangeTblEntry,
T_SortGroupClause,
T_FkConstraint,
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 49,54 **** typedef enum SortByNulls
--- 49,61 ----
SORTBY_NULLS_LAST
} SortByNulls;
+ /* Alter operations for generic options */
+ typedef enum AlterOptionOp
+ {
+ ALTER_OPT_DROP = -1,
+ ALTER_OPT_SET,
+ ALTER_OPT_ADD
+ } AlterOptionOp;
/*
* Grantable rights are encoded so that we can OR them together in a bitmask.
***************
*** 67,73 **** typedef uint32 AclMode; /* a bitmask of privilege bits */
#define ACL_REFERENCES (1<<5)
#define ACL_TRIGGER (1<<6)
#define ACL_EXECUTE (1<<7) /* for functions */
! #define ACL_USAGE (1<<8) /* for languages and namespaces */
#define ACL_CREATE (1<<9) /* for namespaces and databases */
#define ACL_CREATE_TEMP (1<<10) /* for databases */
#define ACL_CONNECT (1<<11) /* for databases */
--- 74,80 ----
#define ACL_REFERENCES (1<<5)
#define ACL_TRIGGER (1<<6)
#define ACL_EXECUTE (1<<7) /* for functions */
! #define ACL_USAGE (1<<8) /* for languages,namespaces,fdw's and servers */
#define ACL_CREATE (1<<9) /* for namespaces and databases */
#define ACL_CREATE_TEMP (1<<10) /* for databases */
#define ACL_CONNECT (1<<11) /* for databases */
***************
*** 467,472 **** typedef struct DefElem
--- 474,490 ----
} DefElem;
/*
+ * Option definition. Used in options definition lists, with optional alter
+ * operation.
+ */
+ typedef struct OptionDefElem
+ {
+ NodeTag type;
+ AlterOptionOp alter_op; /* Alter operation: ADD/SET/DROP */
+ DefElem *def; /* The actual definition */
+ } OptionDefElem;
+
+ /*
* LockingClause - raw representation of FOR UPDATE/SHARE options
*
* Note: lockedRels == NIL means "all relations in query". Otherwise it
***************
*** 949,955 **** typedef enum ObjectType
OBJECT_TSPARSER,
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
! OBJECT_VIEW
} ObjectType;
/* ----------------------
--- 967,975 ----
OBJECT_TSPARSER,
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
! OBJECT_VIEW,
! OBJECT_FDW,
! OBJECT_FOREIGN_SERVER
} ObjectType;
/* ----------------------
***************
*** 1078,1084 **** typedef enum GrantObjectType
ACL_OBJECT_FUNCTION, /* function */
ACL_OBJECT_LANGUAGE, /* procedural language */
ACL_OBJECT_NAMESPACE, /* namespace */
! ACL_OBJECT_TABLESPACE /* tablespace */
} GrantObjectType;
typedef struct GrantStmt
--- 1098,1106 ----
ACL_OBJECT_FUNCTION, /* function */
ACL_OBJECT_LANGUAGE, /* procedural language */
ACL_OBJECT_NAMESPACE, /* namespace */
! ACL_OBJECT_TABLESPACE, /* tablespace */
! ACL_OBJECT_FDW, /* foreign data wrapper */
! ACL_OBJECT_FOREIGN_SERVER /* foreign server */
} GrantObjectType;
typedef struct GrantStmt
***************
*** 1328,1333 **** typedef struct DropTableSpaceStmt
--- 1350,1445 ----
} DropTableSpaceStmt;
/* ----------------------
+ * Create/Drop FOREIGN DATA WRAPPER Statements
+ * ----------------------
+ */
+
+ typedef struct CreateFdwStmt
+ {
+ NodeTag type;
+ List *fdwname; /* qualified foreign data wrapper name */
+ char *library; /* libray name */
+ List *options; /* generic options to FDW */
+ } CreateFdwStmt;
+
+ typedef struct AlterFdwStmt
+ {
+ NodeTag type;
+ List *fdwname; /* qualified foreign data wrapper name */
+ char *library; /* libray name */
+ List *options; /* generic options to FDW */
+ } AlterFdwStmt;
+
+ typedef struct DropFdwStmt
+ {
+ NodeTag type;
+ List *fdwname; /* qualified foreign data wrapper name */
+ bool missing_ok; /* don't complain if missing */
+ DropBehavior behavior; /* drop behavior - cascade/restrict */
+ } DropFdwStmt;
+
+ /* ----------------------
+ * Create/Drop FOREIGN SERVER Statements
+ * ----------------------
+ */
+
+ typedef struct CreateForeignServerStmt
+ {
+ NodeTag type;
+ List *servername; /* qualified server name */
+ char *servertype; /* optional server type */
+ char *version; /* optional server version */
+ List *fdwname; /* qualified FDW name */
+ List *options; /* generic options to server */
+ } CreateForeignServerStmt;
+
+ typedef struct AlterForeignServerStmt
+ {
+ NodeTag type;
+ List *servername; /* qualified server name */
+ char *version; /* optional server version */
+ List *options; /* generic options to server */
+ bool has_version; /* version specified */
+ } AlterForeignServerStmt;
+
+ typedef struct DropForeignServerStmt
+ {
+ NodeTag type;
+ List *servername; /* qualified server name */
+ bool missing_ok; /* ignore missing servers */
+ DropBehavior behavior; /* drop behavior - cascade/restrict */
+ } DropForeignServerStmt;
+
+ /* ----------------------
+ * Create/Drop USER MAPPING Statements
+ * ----------------------
+ */
+
+ typedef struct CreateUserMappingStmt
+ {
+ NodeTag type;
+ char *username; /* username or PUBLIC/CURRENT_USER */
+ List *servername; /* server name */
+ List *options; /* generic options to server */
+ } CreateUserMappingStmt;
+
+ typedef struct AlterUserMappingStmt
+ {
+ NodeTag type;
+ char *username; /* username or PUBLIC/CURRENT_USER */
+ List *servername; /* server name */
+ List *options; /* generic options to server */
+ } AlterUserMappingStmt;
+
+ typedef struct DropUserMappingStmt
+ {
+ NodeTag type;
+ char *username; /* username or PUBLIC/CURRENT_USER */
+ List *servername; /* server name */
+ bool missing_ok; /* ignore missing mappings */
+ } DropUserMappingStmt;
+
+ /* ----------------------
* Create/Drop TRIGGER Statements
* ----------------------
*/
*** a/src/include/utils/acl.h
--- b/src/include/utils/acl.h
***************
*** 150,155 **** typedef ArrayType Acl;
--- 150,157 ----
#define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE)
#define ACL_ALL_RIGHTS_NAMESPACE (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
+ #define ACL_ALL_RIGHTS_FDW (ACL_USAGE)
+ #define ACL_ALL_RIGHTS_FOREIGN_SERVER (ACL_USAGE)
/* operation codes for pg_*_aclmask */
typedef enum
***************
*** 184,189 **** typedef enum AclObjectKind
--- 186,193 ----
ACL_KIND_TABLESPACE, /* pg_tablespace */
ACL_KIND_TSDICTIONARY, /* pg_ts_dict */
ACL_KIND_TSCONFIGURATION, /* pg_ts_config */
+ ACL_KIND_FDW, /* pg_foreign_data_wrapper */
+ ACL_KIND_FOREIGN_SERVER, /* pg_foreign_server */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
***************
*** 261,266 **** extern AclMode pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
--- 265,274 ----
AclMode mask, AclMaskHow how);
extern AclMode pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+ extern AclMode pg_fdw_aclmask(Oid fdw_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
+ extern AclMode pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
extern AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode);
extern AclResult pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode);
***************
*** 268,273 **** extern AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode);
--- 276,283 ----
extern AclResult pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode);
extern AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode);
extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode);
+ extern AclResult pg_fdw_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
+ extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode);
extern void aclcheck_error(AclResult aclerr, AclObjectKind objectkind,
const char *objectname);
***************
*** 286,290 **** extern bool pg_database_ownercheck(Oid db_oid, Oid roleid);
--- 296,301 ----
extern bool pg_conversion_ownercheck(Oid conv_oid, Oid roleid);
extern bool pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid);
extern bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid);
+ extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid);
#endif /* ACL_H */
*** a/src/include/utils/syscache.h
--- b/src/include/utils/syscache.h
***************
*** 51,56 **** enum SysCacheIdentifier
--- 51,62 ----
DATABASEOID,
ENUMOID,
ENUMTYPOIDNAME,
+ FOREIGNDATAWRAPPEROID,
+ FOREIGNDATAWRAPPERNAMENSP,
+ FOREIGNSERVEROID,
+ FOREIGNSERVERNAMENSP,
+ FOREIGNUSERMAPPINGOID,
+ FOREIGNUSERMAPPINGUSEIDSRV,
INDEXRELID,
LANGNAME,
LANGOID,
*** /dev/null
--- b/src/test/regress/expected/connection_manager.out
***************
*** 0 ****
--- 1,1071 ----
+ --
+ -- Test remote connection management.
+ --
+ -- At this point we should have 2 builtin wrappers and no servers
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+---------+-------------+-------------------+---------
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ (2 rows)
+
+ \dr+ *.*
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper | Access privileges | Type | Version | Options
+ --------+------+-------+----------------------+-------------------+------+---------+---------
+ (0 rows)
+
+ \dm+ *.*
+ List of user mappings
+ Schema | Server | Username | Options
+ --------+--------+----------+---------
+ (0 rows)
+
+ -- CREATE FOREIGN DATA WRAPPER
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY ''; -- ERROR
+ ERROR: could not access file "": No such file or directory
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'plpgsql'; -- ERROR
+ ERROR: "plpgsql" is not a valid foreign data wrapper library.
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw';
+ \dw+
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+---------+-------------+-------------------+---------
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | martinp | default_fdw | |
+ (3 rows)
+
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw'; -- duplicate
+ ERROR: foreign data wrapper "foo" already exists
+ CREATE FOREIGN DATA WRAPPER "Foo" LIBRARY 'default_fdw';
+ DROP FOREIGN DATA WRAPPER "Foo";
+ DROP FOREIGN DATA WRAPPER foo;
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' OPTIONS (testing '1');
+ \dw+
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+---------+-------------+-------------------+-------------
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | martinp | default_fdw | | {testing=1}
+ (3 rows)
+
+ DROP FOREIGN DATA WRAPPER foo;
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' OPTIONS (testing '1', testing '2'); -- ERROR
+ ERROR: duplicate option "testing"
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' OPTIONS (testing '1', another '2');
+ \dw+
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+---------+-------------+-------------------+-----------------------
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | martinp | default_fdw | | {testing=1,another=2}
+ (3 rows)
+
+ CREATE SCHEMA fdwtest;
+ CREATE FOREIGN DATA WRAPPER fdwtest.foo LIBRARY 'default_fdw';
+ \dw *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library
+ ------------+---------+---------+-------------
+ fdwtest | foo | martinp | default_fdw
+ pg_catalog | default | martinp | default_fdw
+ pg_catalog | pgsql | martinp | pgsql_fdw
+ public | foo | martinp | default_fdw
+ (4 rows)
+
+ SET search_path = fdwtest;
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw'; -- duplicate
+ ERROR: foreign data wrapper "foo" already exists
+ DROP FOREIGN DATA WRAPPER foo;
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw';
+ RESET search_path;
+ \dw *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library
+ ------------+---------+---------+-------------
+ fdwtest | foo | martinp | default_fdw
+ pg_catalog | default | martinp | default_fdw
+ pg_catalog | pgsql | martinp | pgsql_fdw
+ public | foo | martinp | default_fdw
+ (4 rows)
+
+ CREATE ROLE fdwtest_role;
+ CREATE ROLE fdwtest_role_super SUPERUSER;
+ \dw
+ List of foreign data wrappers
+ Schema | Name | Owner | Library
+ ------------+---------+---------+-------------
+ pg_catalog | default | martinp | default_fdw
+ pg_catalog | pgsql | martinp | pgsql_fdw
+ public | foo | martinp | default_fdw
+ (3 rows)
+
+ DROP FOREIGN DATA WRAPPER foo;
+ SET ROLE fdwtest_role;
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw'; -- ERROR
+ ERROR: permission denied to create foreign data wrapper "foo"
+ HINT: must be superuser to create a foreign data wrapper.
+ SET ROLE fdwtest_role_super;
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'pgsql_fdw';
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+--------------------+-------------+-------------------+---------
+ fdwtest | foo | martinp | default_fdw | |
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | fdwtest_role_super | pgsql_fdw | |
+ (4 rows)
+
+ RESET ROLE;
+ -- ALTER FOREIGN DATA WRAPPER
+ ALTER FOREIGN DATA WRAPPER foo LIBRARY ''; -- ERROR
+ ERROR: could not access file "": No such file or directory
+ ALTER FOREIGN DATA WRAPPER foo LIBRARY 'plpgsql'; -- ERROR
+ ERROR: "plpgsql" is not a valid foreign data wrapper library.
+ ALTER FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw';
+ NOTICE: changing the foreign data wrapper library can cause the options for dependent object to become invalid
+ \dw
+ List of foreign data wrappers
+ Schema | Name | Owner | Library
+ ------------+---------+--------------------+-------------
+ pg_catalog | default | martinp | default_fdw
+ pg_catalog | pgsql | martinp | pgsql_fdw
+ public | foo | fdwtest_role_super | default_fdw
+ (3 rows)
+
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (SET c '4'); -- ERROR
+ ERROR: option "c" not found
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP c); -- ERROR
+ ERROR: option "c" not found
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+--------------------+-------------+-------------------+-----------
+ fdwtest | foo | martinp | default_fdw | |
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | fdwtest_role_super | default_fdw | | {a=1,b=2}
+ (4 rows)
+
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+--------------------+-------------+-------------------+-----------
+ fdwtest | foo | martinp | default_fdw | |
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | fdwtest_role_super | default_fdw | | {b=3,c=4}
+ (4 rows)
+
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4'); -- ERROR
+ ERROR: duplicate option "b"
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+--------------------+-------------+-------------------+---------------
+ fdwtest | foo | martinp | default_fdw | |
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | fdwtest_role_super | default_fdw | | {b=3,c=4,a=2}
+ (4 rows)
+
+ SET ROLE fdwtest_role;
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5'); -- ERROR
+ ERROR: permission denied to alter foreign data wrapper "foo"
+ HINT: must be superuser to alter a foreign data wrapper.
+ SET ROLE fdwtest_role_super;
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+--------------------+-------------+-------------------+-------------------
+ fdwtest | foo | martinp | default_fdw | |
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | fdwtest_role_super | default_fdw | | {b=3,c=4,a=2,d=5}
+ (4 rows)
+
+ ALTER FOREIGN DATA WRAPPER foo OWNER to fdwtest_role; -- ERROR
+ ERROR: permission denied to change owner of foreign data wrapper "foo"
+ HINT: the owner of a foreign data wrapper must be a superuser.
+ ALTER FOREIGN DATA WRAPPER foo OWNER to fdwtest_role_super;
+ ALTER ROLE fdwtest_role_super NOSUPERUSER;
+ SET ROLE fdwtest_role_super;
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD e '6'); -- ERROR
+ ERROR: permission denied to alter foreign data wrapper "foo"
+ HINT: must be superuser to alter a foreign data wrapper.
+ RESET ROLE;
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+--------------------+-------------+-------------------+-------------------
+ fdwtest | foo | martinp | default_fdw | |
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | fdwtest_role_super | default_fdw | | {b=3,c=4,a=2,d=5}
+ (4 rows)
+
+ SET search_path=fdwtest;
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (another 'fdw');
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+--------------------+-------------+-------------------+-------------------
+ fdwtest | foo | martinp | default_fdw | | {another=fdw}
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | fdwtest_role_super | default_fdw | | {b=3,c=4,a=2,d=5}
+ (4 rows)
+
+ ALTER FOREIGN DATA WRAPPER fdwtest.foo OPTIONS (with 'qualified name');
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+--------------------+-------------+-------------------+-------------------------------------
+ fdwtest | foo | martinp | default_fdw | | {another=fdw,"with=qualified name"}
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | fdwtest_role_super | default_fdw | | {b=3,c=4,a=2,d=5}
+ (4 rows)
+
+ RESET search_path;
+ -- DROP FOREIGN DATA WRAPPER
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+--------------------+-------------+-------------------+-------------------------------------
+ fdwtest | foo | martinp | default_fdw | | {another=fdw,"with=qualified name"}
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | fdwtest_role_super | default_fdw | | {b=3,c=4,a=2,d=5}
+ (4 rows)
+
+ DROP FOREIGN DATA WRAPPER nonexistent; -- ERROR
+ ERROR: foreign data wrapper "nonexistent" does not exist
+ DROP FOREIGN DATA WRAPPER noschema.nonexistent; -- ERROR
+ ERROR: foreign data wrapper "noschema.nonexistent" does not exist
+ DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
+ NOTICE: foreign data wrapper "nonexistent" does not exist, skipping
+ DROP FOREIGN DATA WRAPPER IF EXISTS noschema.nonexistent;
+ NOTICE: foreign data wrapper "noschema.nonexistent" does not exist, skipping
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+--------------------+-------------+-------------------+-------------------------------------
+ fdwtest | foo | martinp | default_fdw | | {another=fdw,"with=qualified name"}
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | fdwtest_role_super | default_fdw | | {b=3,c=4,a=2,d=5}
+ (4 rows)
+
+ DROP ROLE fdwtest_role_super; -- ERROR
+ ERROR: role "fdwtest_role_super" cannot be dropped because some objects depend on it
+ DETAIL: owner of foreign data wrapper foo
+ SET ROLE fdwtest_role_super;
+ DROP FOREIGN DATA WRAPPER foo; -- ERROR
+ ERROR: permission denied to drop foreign data wrapper "foo"
+ HINT: must be superuser to drop a foreign data wrapper.
+ RESET ROLE;
+ DROP FOREIGN DATA WRAPPER foo;
+ DROP ROLE fdwtest_role_super;
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+---------+-------------+-------------------+-------------------------------------
+ fdwtest | foo | martinp | default_fdw | | {another=fdw,"with=qualified name"}
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ (3 rows)
+
+ DROP SCHEMA fdwtest; -- ERROR
+ ERROR: cannot drop schema fdwtest because other objects depend on it
+ DETAIL: foreign data wrapper foo depends on schema fdwtest
+ HINT: Use DROP ... CASCADE to drop the dependent objects too.
+ DROP SCHEMA fdwtest CASCADE;
+ NOTICE: drop cascades to foreign data wrapper foo
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+---------+-------------+-------------------+---------
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ (2 rows)
+
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw';
+ CREATE SERVER s1 FOREIGN DATA WRAPPER foo;
+ CREATE USER MAPPING FOR current_user SERVER s1;
+ \dw+
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+---------+-------------+-------------------+---------
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ public | foo | martinp | default_fdw | |
+ (3 rows)
+
+ \dr+
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper | Access privileges | Type | Version | Options
+ --------+------+---------+----------------------+-------------------+------+---------+---------
+ public | s1 | martinp | foo | | | |
+ (1 row)
+
+ \dm+
+ List of user mappings
+ Schema | Server | Username | Options
+ --------+--------+----------+---------
+ public | s1 | martinp |
+ (1 row)
+
+ DROP FOREIGN DATA WRAPPER foo; -- ERROR
+ ERROR: cannot drop foreign data wrapper foo because other objects depend on it
+ DETAIL: server s1 depends on foreign data wrapper foo
+ user mapping for martinp depends on server s1
+ HINT: Use DROP ... CASCADE to drop the dependent objects too.
+ SET ROLE fdwtest_role;
+ DROP FOREIGN DATA WRAPPER foo CASCADE; -- ERROR
+ ERROR: permission denied to drop foreign data wrapper "foo"
+ HINT: must be superuser to drop a foreign data wrapper.
+ RESET ROLE;
+ DROP FOREIGN DATA WRAPPER foo CASCADE; -- ERROR
+ NOTICE: drop cascades to 2 other objects
+ DETAIL: drop cascades to server s1
+ drop cascades to user mapping for martinp
+ \dw+
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+---------+-------------+-------------------+---------
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | |
+ (2 rows)
+
+ \dr+
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper | Access privileges | Type | Version | Options
+ --------+------+-------+----------------------+-------------------+------+---------+---------
+ (0 rows)
+
+ \dm+
+ List of user mappings
+ Schema | Server | Username | Options
+ --------+--------+----------+---------
+ (0 rows)
+
+ -- exercise CREATE SERVER
+ CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR
+ ERROR: foreign data wrapper "foo" does not exist
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' OPTIONS (test_wrapper 'true');
+ CREATE SERVER s1 FOREIGN DATA WRAPPER bar; -- ERROR
+ ERROR: foreign data wrapper "bar" does not exist
+ CREATE SERVER s1 FOREIGN DATA WRAPPER foo;
+ CREATE SERVER s2 FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b');
+ CREATE SERVER s3 TYPE 'oracle' FOREIGN DATA WRAPPER foo;
+ CREATE SERVER s4 TYPE 'oracle' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b');
+ CREATE SERVER s5 VERSION '15.0' FOREIGN DATA WRAPPER foo;
+ CREATE SERVER s6 VERSION '16.0' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b');
+ CREATE SERVER s7 TYPE 'oracle' VERSION '17.0' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b');
+ CREATE SERVER s8 FOREIGN DATA WRAPPER pgsql OPTIONS (foo '1'); -- ERROR
+ ERROR: invalid option "foo" to server
+ HINT: valid server options are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib
+ CREATE SERVER s8 FOREIGN DATA WRAPPER pgsql OPTIONS (host 'localhost', dbname 's8db');
+ CREATE SERVER nonexistent.s8 FOREIGN DATA WRAPPER foo; -- ERROR
+ ERROR: schema "nonexistent" does not exist
+ \dr+
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper | Access privileges | Type | Version | Options
+ --------+------+---------+----------------------+-------------------+--------+---------+------------------------------
+ public | s1 | martinp | foo | | | |
+ public | s2 | martinp | foo | | | | {host=a,dbname=b}
+ public | s3 | martinp | foo | | oracle | |
+ public | s4 | martinp | foo | | oracle | | {host=a,dbname=b}
+ public | s5 | martinp | foo | | | 15.0 |
+ public | s6 | martinp | foo | | | 16.0 | {host=a,dbname=b}
+ public | s7 | martinp | foo | | oracle | 17.0 | {host=a,dbname=b}
+ public | s8 | martinp | pgsql | | | | {host=localhost,dbname=s8db}
+ (8 rows)
+
+ CREATE ROLE server_test_role;
+ SET ROLE server_test_role;
+ CREATE SERVER st1 FOREIGN DATA WRAPPER foo; -- ERROR: no usage on FDW
+ ERROR: permission denied for foreign data wrapper foo
+ RESET ROLE;
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO server_test_role;
+ SET ROLE server_test_role;
+ CREATE SERVER st1 FOREIGN DATA WRAPPER foo;
+ RESET ROLE;
+ \dr+
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper | Access privileges | Type | Version | Options
+ --------+------+------------------+----------------------+-------------------+--------+---------+------------------------------
+ public | s1 | martinp | foo | | | |
+ public | s2 | martinp | foo | | | | {host=a,dbname=b}
+ public | s3 | martinp | foo | | oracle | |
+ public | s4 | martinp | foo | | oracle | | {host=a,dbname=b}
+ public | s5 | martinp | foo | | | 15.0 |
+ public | s6 | martinp | foo | | | 16.0 | {host=a,dbname=b}
+ public | s7 | martinp | foo | | oracle | 17.0 | {host=a,dbname=b}
+ public | s8 | martinp | pgsql | | | | {host=localhost,dbname=s8db}
+ public | st1 | server_test_role | foo | | | |
+ (9 rows)
+
+ CREATE SCHEMA server_test;
+ CREATE SERVER server_test.s1 FOREIGN DATA WRAPPER foo;
+ SET search_path=server_test;
+ \dr
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper
+ -------------+------+---------+----------------------
+ server_test | s1 | martinp | foo
+ (1 row)
+
+ CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR: duplicate
+ ERROR: server "s1" already exists
+ CREATE SERVER s2 FOREIGN DATA WRAPPER foo; -- ERROR: FDW not visible
+ ERROR: foreign data wrapper "foo" does not exist
+ CREATE SERVER s2 FOREIGN DATA WRAPPER public.foo;
+ \dr
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper
+ -------------+------+---------+----------------------
+ server_test | s1 | martinp | foo
+ server_test | s2 | martinp | foo
+ (2 rows)
+
+ SET ROLE server_test_role;
+ CREATE SERVER s3 FOREIGN DATA WRAPPER public.foo; -- ERROR: no schema rights
+ ERROR: no schema has been selected to create in
+ RESET search_path;
+ RESET ROLE;
+ CREATE ROLE server_test_indirect;
+ REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM server_test_role;
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO server_test_indirect;
+ SET ROLE server_test_role;
+ CREATE SERVER st2 FOREIGN DATA WRAPPER foo; -- ERROR
+ ERROR: permission denied for foreign data wrapper foo
+ RESET ROLE;
+ GRANT server_test_indirect TO server_test_role;
+ SET ROLE server_test_role;
+ CREATE SERVER st2 FOREIGN DATA WRAPPER foo;
+ \dr+ *.*
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper | Access privileges | Type | Version | Options
+ -------------+------+------------------+----------------------+-------------------+--------+---------+------------------------------
+ public | s1 | martinp | foo | | | |
+ public | s2 | martinp | foo | | | | {host=a,dbname=b}
+ public | s3 | martinp | foo | | oracle | |
+ public | s4 | martinp | foo | | oracle | | {host=a,dbname=b}
+ public | s5 | martinp | foo | | | 15.0 |
+ public | s6 | martinp | foo | | | 16.0 | {host=a,dbname=b}
+ public | s7 | martinp | foo | | oracle | 17.0 | {host=a,dbname=b}
+ public | s8 | martinp | pgsql | | | | {host=localhost,dbname=s8db}
+ public | st1 | server_test_role | foo | | | |
+ public | st2 | server_test_role | foo | | | |
+ server_test | s1 | martinp | foo | | | |
+ server_test | s2 | martinp | foo | | | |
+ (12 rows)
+
+ RESET ROLE;
+ REVOKE server_test_indirect FROM server_test_role;
+ -- ALTER SERVER
+ ALTER SERVER s0; -- ERROR
+ ERROR: syntax error at or near ";"
+ LINE 1: ALTER SERVER s0;
+ ^
+ ALTER SERVER s0 OPTIONS (a '1'); -- ERROR
+ ERROR: foreign server "s0" does not exist
+ ALTER SERVER test.server VERSION '1'; -- ERROR
+ ERROR: foreign server "test.server" does not exist
+ ALTER SERVER s1 VERSION '1.0' OPTIONS (servername 's1');
+ ALTER SERVER s2 VERSION '1.1';
+ ALTER SERVER s3 OPTIONS (tnsname 'orcl', port '1521');
+ GRANT USAGE ON SERVER s1 TO server_test_role;
+ \dr+ *.*
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper | Access privileges | Type | Version | Options
+ -------------+------+------------------+----------------------+------------------------------------------------+--------+---------+------------------------------
+ public | s1 | martinp | foo | {martinp=U/martinp,server_test_role=U/martinp} | | 1.0 | {servername=s1}
+ public | s2 | martinp | foo | | | 1.1 | {host=a,dbname=b}
+ public | s3 | martinp | foo | | oracle | | {tnsname=orcl,port=1521}
+ public | s4 | martinp | foo | | oracle | | {host=a,dbname=b}
+ public | s5 | martinp | foo | | | 15.0 |
+ public | s6 | martinp | foo | | | 16.0 | {host=a,dbname=b}
+ public | s7 | martinp | foo | | oracle | 17.0 | {host=a,dbname=b}
+ public | s8 | martinp | pgsql | | | | {host=localhost,dbname=s8db}
+ public | st1 | server_test_role | foo | | | |
+ public | st2 | server_test_role | foo | | | |
+ server_test | s1 | martinp | foo | | | |
+ server_test | s2 | martinp | foo | | | |
+ (12 rows)
+
+ SET ROLE server_test_role;
+ ALTER SERVER s1 VERSION '1.1'; -- ERROR
+ ERROR: must be owner of foreign server s1
+ ALTER SERVER s1 OWNER TO server_test_role; -- ERROR
+ ERROR: must be owner of foreign server s1
+ RESET ROLE;
+ ALTER SERVER s1 OWNER TO server_test_role;
+ SET ROLE server_test_role;
+ ALTER SERVER s1 VERSION '1.1';
+ RESET ROLE;
+ ALTER SERVER s8 OPTIONS (foo '1'); -- ERROR option validation
+ ERROR: invalid option "foo" to server
+ HINT: valid server options are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib
+ ALTER SERVER s8 OPTIONS (connect_timeout '30', set dbname 'db1', drop host);
+ SET ROLE server_test_role;
+ ALTER SERVER s1 OWNER TO server_test_indirect; -- ERROR
+ ERROR: must be member of role "server_test_indirect"
+ RESET ROLE;
+ GRANT server_test_indirect TO server_test_role;
+ SET ROLE server_test_role;
+ ALTER SERVER s1 OWNER TO server_test_indirect;
+ RESET ROLE;
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO server_test_indirect;
+ SET ROLE server_test_role;
+ ALTER SERVER s1 OWNER TO server_test_indirect;
+ RESET ROLE;
+ DROP ROLE server_test_indirect; -- ERROR
+ ERROR: role "server_test_indirect" cannot be dropped because some objects depend on it
+ DETAIL: owner of server s1
+ access to foreign data wrapper foo
+ \dr+ *.*
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper | Access privileges | Type | Version | Options
+ -------------+------+----------------------+----------------------+------------------------------------------------+--------+---------+---------------------------------
+ public | s1 | server_test_indirect | foo | {martinp=U/martinp,server_test_role=U/martinp} | | 1.1 | {servername=s1}
+ public | s2 | martinp | foo | | | 1.1 | {host=a,dbname=b}
+ public | s3 | martinp | foo | | oracle | | {tnsname=orcl,port=1521}
+ public | s4 | martinp | foo | | oracle | | {host=a,dbname=b}
+ public | s5 | martinp | foo | | | 15.0 |
+ public | s6 | martinp | foo | | | 16.0 | {host=a,dbname=b}
+ public | s7 | martinp | foo | | oracle | 17.0 | {host=a,dbname=b}
+ public | s8 | martinp | pgsql | | | | {dbname=db1,connect_timeout=30}
+ public | st1 | server_test_role | foo | | | |
+ public | st2 | server_test_role | foo | | | |
+ server_test | s1 | martinp | foo | | | |
+ server_test | s2 | martinp | foo | | | |
+ (12 rows)
+
+ -- DROP SERVER
+ DROP SERVER nonexistent; -- ERROR
+ ERROR: server "nonexistent" does not exist
+ DROP SERVER noschema.nonexistent; -- ERROR
+ ERROR: server "noschema.nonexistent" does not exist
+ DROP SERVER IF EXISTS nonexistent;
+ NOTICE: server "nonexistent" does not exist, skipping
+ DROP SERVER IF EXISTS noschema.nonexistent;
+ NOTICE: server "noschema.nonexistent" does not exist, skipping
+ \dr *.*
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper
+ -------------+------+----------------------+----------------------
+ public | s1 | server_test_indirect | foo
+ public | s2 | martinp | foo
+ public | s3 | martinp | foo
+ public | s4 | martinp | foo
+ public | s5 | martinp | foo
+ public | s6 | martinp | foo
+ public | s7 | martinp | foo
+ public | s8 | martinp | pgsql
+ public | st1 | server_test_role | foo
+ public | st2 | server_test_role | foo
+ server_test | s1 | martinp | foo
+ server_test | s2 | martinp | foo
+ (12 rows)
+
+ SET ROLE server_test_role;
+ DROP SERVER s2; -- ERROR
+ ERROR: must be owner of foreign server s2
+ DROP SERVER s1;
+ RESET ROLE;
+ \dr *.*
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper
+ -------------+------+------------------+----------------------
+ public | s2 | martinp | foo
+ public | s3 | martinp | foo
+ public | s4 | martinp | foo
+ public | s5 | martinp | foo
+ public | s6 | martinp | foo
+ public | s7 | martinp | foo
+ public | s8 | martinp | pgsql
+ public | st1 | server_test_role | foo
+ public | st2 | server_test_role | foo
+ server_test | s1 | martinp | foo
+ server_test | s2 | martinp | foo
+ (11 rows)
+
+ ALTER SERVER s2 OWNER TO server_test_role;
+ SET ROLE server_test_role;
+ DROP SERVER s2;
+ RESET ROLE;
+ \dr *.*
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper
+ -------------+------+------------------+----------------------
+ public | s3 | martinp | foo
+ public | s4 | martinp | foo
+ public | s5 | martinp | foo
+ public | s6 | martinp | foo
+ public | s7 | martinp | foo
+ public | s8 | martinp | pgsql
+ public | st1 | server_test_role | foo
+ public | st2 | server_test_role | foo
+ server_test | s1 | martinp | foo
+ server_test | s2 | martinp | foo
+ (10 rows)
+
+ CREATE USER MAPPING FOR current_user SERVER s3;
+ \dm *.*
+ List of user mappings
+ Schema | Server | Username
+ --------+--------+----------
+ public | s3 | martinp
+ (1 row)
+
+ DROP SERVER s3; -- ERROR
+ ERROR: cannot drop server s3 because other objects depend on it
+ DETAIL: user mapping for martinp depends on server s3
+ HINT: Use DROP ... CASCADE to drop the dependent objects too.
+ DROP SERVER s3 CASCADE;
+ NOTICE: drop cascades to user mapping for martinp
+ \dr *.*
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper
+ -------------+------+------------------+----------------------
+ public | s4 | martinp | foo
+ public | s5 | martinp | foo
+ public | s6 | martinp | foo
+ public | s7 | martinp | foo
+ public | s8 | martinp | pgsql
+ public | st1 | server_test_role | foo
+ public | st2 | server_test_role | foo
+ server_test | s1 | martinp | foo
+ server_test | s2 | martinp | foo
+ (9 rows)
+
+ \dm *.*
+ List of user mappings
+ Schema | Server | Username
+ --------+--------+----------
+ (0 rows)
+
+ -- CREATE USER MAPPING
+ CREATE USER MAPPING FOR baz SERVER s1; -- ERROR
+ ERROR: role "baz" does not exist
+ CREATE USER MAPPING FOR current_user SERVER s1; -- ERROR
+ ERROR: foreign server "s1" does not exist
+ CREATE USER MAPPING FOR current_user SERVER s4;
+ CREATE USER MAPPING FOR user SERVER s4; -- ERROR duplicate
+ ERROR: user mapping "martinp" already exists for server s4
+ CREATE USER MAPPING FOR public SERVER s4 OPTIONS (mapping 'is public');
+ CREATE USER MAPPING FOR user SERVER s8 OPTIONS (username 'test', password 'secret'); -- ERROR
+ ERROR: invalid option "username" to user mapping
+ HINT: valid user mapping options are: user, password
+ CREATE USER MAPPING FOR user SERVER s8 OPTIONS (user 'test', password 'secret');
+ ALTER SERVER s5 OWNER TO server_test_role;
+ ALTER SERVER s6 OWNER TO server_test_indirect;
+ SET ROLE server_test_role;
+ CREATE USER MAPPING FOR current_user SERVER s5;
+ CREATE USER MAPPING FOR current_user SERVER s6 OPTIONS (username 'test');
+ CREATE USER MAPPING FOR current_user SERVER s7; -- ERROR
+ ERROR: must be owner of foreign server s7
+ CREATE USER MAPPING FOR public SERVER s8; -- ERROR
+ ERROR: must be owner of foreign server s8
+ RESET ROLE;
+ ALTER SERVER server_test.s1 OWNER TO server_test_indirect;
+ SET ROLE server_test_role;
+ CREATE USER MAPPING FOR current_user SERVER server_test.s1;
+ SET search_path = server_test;
+ CREATE USER MAPPING FOR public SERVER s1; -- ERROR no usage on server schema
+ ERROR: foreign server "s1" does not exist
+ RESET ROLE;
+ GRANT USAGE ON SCHEMA server_test TO server_test_role;
+ SET ROLE server_test_role;
+ CREATE USER MAPPING FOR public SERVER s1;
+ RESET search_path;
+ RESET ROLE;
+ \dm *.*
+ List of user mappings
+ Schema | Server | Username
+ -------------+--------+------------------
+ public | s4 | martinp
+ public | s4 | public
+ public | s5 | server_test_role
+ public | s6 | server_test_role
+ public | s8 | martinp
+ server_test | s1 | server_test_role
+ server_test | s1 | public
+ (7 rows)
+
+ -- ALTER USER MAPPING
+ ALTER USER MAPPING FOR bob SERVER s4 OPTIONS (gotcha 'true'); -- ERROR
+ ERROR: role "bob" does not exist
+ ALTER USER MAPPING FOR user SERVER ss4 OPTIONS (gotcha 'true'); -- ERROR
+ ERROR: foreign server "ss4" does not exist
+ ALTER USER MAPPING FOR public SERVER s5 OPTIONS (gotcha 'true'); -- ERROR
+ ERROR: user mapping "public" does not exist for the server
+ ALTER USER MAPPING FOR current_user SERVER s8 OPTIONS (username 'test'); -- ERROR
+ ERROR: invalid option "username" to user mapping
+ HINT: valid user mapping options are: user, password
+ ALTER USER MAPPING FOR current_user SERVER s8 OPTIONS (DROP user, SET password 'public');
+ SET ROLE server_test_role;
+ ALTER USER MAPPING FOR current_user SERVER s5 OPTIONS (add modified '1');
+ ALTER USER MAPPING FOR public SERVER s4 OPTIONS (add modified '1'); -- ERROR
+ ERROR: must be owner of foreign server s4
+ ALTER USER MAPPING FOR public SERVER server_test.s1 OPTIONS (add modified '1');
+ RESET ROLE;
+ SET search_path = server_test;
+ ALTER USER MAPPING FOR public SERVER s1 OPTIONS (add modified '2'); -- ERROR
+ ERROR: duplicate option "modified"
+ ALTER USER MAPPING FOR public SERVER s1 OPTIONS (set modified '2');
+ RESET ROLE;
+ RESET search_path;
+ \dm+ *.*
+ List of user mappings
+ Schema | Server | Username | Options
+ -------------+--------+------------------+-----------------------
+ public | s4 | public | {"mapping=is public"}
+ public | s4 | martinp |
+ public | s5 | server_test_role | {modified=1}
+ public | s6 | server_test_role | {username=test}
+ public | s8 | martinp | {password=public}
+ server_test | s1 | server_test_role |
+ server_test | s1 | public | {modified=2}
+ (7 rows)
+
+ -- DROP USER MAPPING
+ DROP USER MAPPING FOR bob SERVER s4; -- ERROR
+ ERROR: role "bob" does not exist
+ DROP USER MAPPING FOR user SERVER ss4; -- ERROR
+ ERROR: server "ss4" does not exist
+ DROP USER MAPPING FOR user SERVER test.ss4; -- ERROR
+ ERROR: server "test.ss4" does not exist
+ DROP USER MAPPING IF EXISTS for bob SERVER s4; -- ERROR
+ NOTICE: role "bob" does not exist, skipping
+ DROP USER MAPPING IF EXISTS for public SERVER s7;
+ NOTICE: user mapping "public" does not exist for the server, skipping
+ CREATE USER MAPPING FOR public SERVER s8;
+ SET ROLE server_test_role;
+ DROP USER MAPPING FOR public SERVER s8; -- ERROR
+ ERROR: must be owner of foreign server s8
+ DROP USER MAPPING FOR current_user SERVER server_test.s1;
+ RESET ROLE;
+ SET search_path = server_test;
+ DROP USER MAPPING FOR public SERVER s1;
+ RESET search_path;
+ DROP SERVER s7;
+ \dm *.*
+ List of user mappings
+ Schema | Server | Username
+ --------+--------+------------------
+ public | s4 | martinp
+ public | s4 | public
+ public | s5 | server_test_role
+ public | s6 | server_test_role
+ public | s8 | martinp
+ public | s8 | public
+ (6 rows)
+
+ -- Information schema
+ SELECT * FROM information_schema.foreign_data_wrappers;
+ foreign_data_wrapper_catalog | foreign_data_wrapper_name | authorization_identifier | library_name | foreign_data_wrapper_language
+ ------------------------------+---------------------------+--------------------------+--------------+-------------------------------
+ regression | pgsql | martinp | pgsql_fdw |
+ regression | default | martinp | default_fdw |
+ regression | foo | martinp | default_fdw |
+ (3 rows)
+
+ SELECT * FROM information_schema.foreign_data_wrapper_options;
+ foreign_data_wrapper_catalog | foreign_data_wrapper_name | option_name | option_value
+ ------------------------------+---------------------------+--------------+--------------
+ regression | foo | test_wrapper | true
+ (1 row)
+
+ SELECT * FROM information_schema.foreign_servers;
+ foreign_server_catalog | foreign_server_name | foreign_data_wrapper_catalog | foreign_data_wrapper_name | foreign_server_type | foreign_server_version | authorization_identifier
+ ------------------------+---------------------+------------------------------+---------------------------+---------------------+------------------------+--------------------------
+ regression | s4 | regression | foo | oracle | | martinp
+ regression | st1 | regression | foo | | | server_test_role
+ regression | server_test.s2 | regression | foo | | | martinp
+ regression | st2 | regression | foo | | | server_test_role
+ regression | s8 | regression | pgsql | | | martinp
+ regression | s5 | regression | foo | | 15.0 | server_test_role
+ regression | s6 | regression | foo | | 16.0 | server_test_indirect
+ regression | server_test.s1 | regression | foo | | | server_test_indirect
+ (8 rows)
+
+ SELECT * FROM information_schema.foreign_server_options;
+ foreign_server_catalog | foreign_server_name | option_name | option_value
+ ------------------------+---------------------+-----------------+--------------
+ regression | s4 | host | a
+ regression | s4 | dbname | b
+ regression | s8 | dbname | db1
+ regression | s8 | connect_timeout | 30
+ regression | s6 | host | a
+ regression | s6 | dbname | b
+ (6 rows)
+
+ SELECT * FROM information_schema.user_mappings;
+ authorization_identifier | foreign_server_catalog | foreign_server_name
+ --------------------------+------------------------+---------------------
+ public | regression | s4
+ martinp | regression | s4
+ public | regression | s8
+ martinp | regression | s8
+ server_test_role | regression | s5
+ server_test_role | regression | s6
+ (6 rows)
+
+ SELECT * FROM information_schema.user_mapping_options;
+ authorization_identifier | foreign_server_catalog | foreign_server_name | option_name | option_value
+ --------------------------+------------------------+---------------------+-------------+--------------
+ public | regression | s4 | mapping | is public
+ martinp | regression | s8 | password | public
+ server_test_role | regression | s5 | modified | 1
+ server_test_role | regression | s6 | username | test
+ (4 rows)
+
+ SET ROLE server_test_role;
+ SELECT * FROM information_schema.user_mapping_options;
+ authorization_identifier | foreign_server_catalog | foreign_server_name | option_name | option_value
+ --------------------------+------------------------+---------------------+-------------+--------------
+ (0 rows)
+
+ RESET ROLE;
+ -- pg_get_remote_connection_info
+ SELECT * FROM pg_get_remote_connection_info(NULL);
+ option_name | option_value
+ -------------+--------------
+ (0 rows)
+
+ SELECT * FROM pg_get_remote_connection_info(''); -- ERROR
+ ERROR: invalid name syntax
+ SELECT * FROM pg_get_remote_connection_info('foo'); -- ERROR
+ ERROR: foreign server "foo" does not exist
+ SELECT * FROM pg_get_remote_connection_info('foo', 'bob'); -- ERROR
+ ERROR: role "bob" does not exist
+ SELECT * FROM pg_get_remote_connection_info('foo', 'server_test_role'); -- ERROR
+ ERROR: foreign server "foo" does not exist
+ SELECT * FROM pg_get_remote_connection_info('foo.bar'); -- ERROR
+ ERROR: foreign server "foo.bar" does not exist
+ SELECT * FROM pg_get_remote_connection_info('foo.bar.baz'); -- ERROR
+ ERROR: cross-database references are not implemented: foo.bar.baz
+ SELECT * FROM pg_get_remote_connection_info('foo.bar.baz.foobar'); -- ERROR
+ ERROR: improper qualified name (too many dotted names): foo.bar.baz.foobar
+ SELECT * FROM pg_get_remote_connection_info('foo', current_user); -- ERROR
+ ERROR: foreign server "foo" does not exist
+ SELECT * FROM pg_get_remote_connection_info('s4');
+ option_name | option_value
+ -------------+--------------
+ host | a
+ dbname | b
+ (2 rows)
+
+ SELECT * FROM pg_get_remote_connection_info('s4', 'public');
+ option | value
+ ---------+-----------
+ host | a
+ dbname | b
+ mapping | is public
+ (3 rows)
+
+ SELECT * FROM pg_get_remote_connection_info('s6', 'server_test_role');
+ option | value
+ ----------+-------
+ host | a
+ dbname | b
+ username | test
+ (3 rows)
+
+ ALTER SERVER s6 OPTIONS (DROP host, DROP dbname);
+ SELECT * FROM pg_get_remote_connection_info('s6', 'server_test_role');
+ option | value
+ ----------+-------
+ username | test
+ (1 row)
+
+ ALTER USER MAPPING FOR server_test_role SERVER s6 OPTIONS (DROP username);
+ SELECT * FROM pg_get_remote_connection_info('s6', 'server_test_role');
+ option | value
+ --------+-------
+ (0 rows)
+
+ SELECT * FROM pg_get_remote_connection_info('s8');
+ option_name | option_value
+ -------------+-----------------------------------------------
+ datasource | dbname=db1 connect_timeout=30 password=public
+ (1 row)
+
+ SELECT * FROM pg_get_remote_connection_info('s8', 'public');
+ option | value
+ ------------+-------------------------------
+ datasource | dbname=db1 connect_timeout=30
+ (1 row)
+
+ SET ROLE server_test_role;
+ SELECT * FROM pg_get_remote_connection_info('s6'); -- ERROR
+ ERROR: permission denied for function pg_get_remote_connection_info
+ RESET ROLE;
+ -- Privileges
+ CREATE ROLE unprivileged_role;
+ SET ROLE unprivileged_role;
+ CREATE FOREIGN DATA WRAPPER foobar LIBRARY 'default_fdw'; -- ERROR
+ ERROR: permission denied to create foreign data wrapper "foobar"
+ HINT: must be superuser to create a foreign data wrapper.
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (gotcha 'true'); -- ERROR
+ ERROR: permission denied to alter foreign data wrapper "foo"
+ HINT: must be superuser to alter a foreign data wrapper.
+ DROP FOREIGN DATA WRAPPER foo; -- ERROR
+ ERROR: permission denied to drop foreign data wrapper "foo"
+ HINT: must be superuser to drop a foreign data wrapper.
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO server_test_role; -- ERROR
+ ERROR: permission denied for foreign data wrapper foo
+ CREATE SERVER s9 FOREIGN DATA WRAPPER foo; -- ERROR
+ ERROR: permission denied for foreign data wrapper foo
+ ALTER SERVER s4 VERSION '0.5'; -- ERROR
+ ERROR: must be owner of foreign server s4
+ DROP SERVER s4; -- ERROR
+ ERROR: must be owner of foreign server s4
+ GRANT USAGE ON SERVER s4 TO server_test_role; -- ERROR
+ ERROR: permission denied for foreign server s4
+ CREATE USER MAPPING FOR public SERVER s4; -- ERROR
+ ERROR: must be owner of foreign server s4
+ ALTER USER MAPPING FOR server_test_role SERVER s6 OPTIONS (gotcha 'true'); -- ERROR
+ ERROR: must be owner of foreign server s6
+ DROP USER MAPPING FOR server_test_role SERVER s6; -- ERROR
+ ERROR: must be owner of foreign server s6
+ RESET ROLE;
+ GRANT USAGE ON FOREIGN DATA WRAPPER pgsql TO unprivileged_role;
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO unprivileged_role WITH GRANT OPTION;
+ SET ROLE unprivileged_role;
+ CREATE FOREIGN DATA WRAPPER foobar LIBRARY 'default_fdw'; -- ERROR
+ ERROR: permission denied to create foreign data wrapper "foobar"
+ HINT: must be superuser to create a foreign data wrapper.
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (gotcha 'true'); -- ERROR
+ ERROR: permission denied to alter foreign data wrapper "foo"
+ HINT: must be superuser to alter a foreign data wrapper.
+ DROP FOREIGN DATA WRAPPER foo; -- ERROR
+ ERROR: permission denied to drop foreign data wrapper "foo"
+ HINT: must be superuser to drop a foreign data wrapper.
+ GRANT USAGE ON FOREIGN DATA WRAPPER pgsql TO server_test_role; -- WARNING
+ WARNING: no privileges were granted for "pgsql"
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO server_test_role;
+ CREATE SERVER s9 FOREIGN DATA WRAPPER pgsql;
+ ALTER SERVER s6 VERSION '0.5'; -- ERROR
+ ERROR: must be owner of foreign server s6
+ DROP SERVER s6; -- ERROR
+ ERROR: must be owner of foreign server s6
+ GRANT USAGE ON SERVER s6 TO server_test_role; -- ERROR
+ ERROR: permission denied for foreign server s6
+ GRANT USAGE ON SERVER s9 TO server_test_role;
+ CREATE USER MAPPING FOR public SERVER s6; -- ERROR
+ ERROR: must be owner of foreign server s6
+ CREATE USER MAPPING FOR public SERVER s9;
+ ALTER USER MAPPING FOR server_test_role SERVER s6 OPTIONS (gotcha 'true'); -- ERROR
+ ERROR: must be owner of foreign server s6
+ DROP USER MAPPING FOR server_test_role SERVER s6; -- ERROR
+ ERROR: must be owner of foreign server s6
+ RESET ROLE;
+ REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM unprivileged_role; -- ERROR
+ ERROR: dependent privileges exist
+ HINT: Use CASCADE to revoke them too.
+ REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM unprivileged_role CASCADE;
+ SET ROLE unprivileged_role;
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO server_test_role; -- ERROR
+ ERROR: permission denied for foreign data wrapper foo
+ CREATE SERVER s10 FOREIGN DATA WRAPPER foo; -- ERROR
+ ERROR: permission denied for foreign data wrapper foo
+ ALTER SERVER s9 VERSION '1.1';
+ GRANT USAGE ON SERVER s9 TO server_test_role;
+ CREATE USER MAPPING FOR current_user SERVER s9;
+ DROP SERVER s9 CASCADE;
+ NOTICE: drop cascades to 2 other objects
+ DETAIL: drop cascades to user mapping for public
+ drop cascades to user mapping for unprivileged_role
+ RESET ROLE;
+ CREATE SERVER s9 FOREIGN DATA WRAPPER foo;
+ GRANT USAGE ON SERVER s9 TO unprivileged_role;
+ SET ROLE unprivileged_role;
+ ALTER SERVER s9 VERSION '1.2'; -- ERROR
+ ERROR: must be owner of foreign server s9
+ GRANT USAGE ON SERVER s9 TO server_test_role; -- WARNING
+ WARNING: no privileges were granted for "s9"
+ CREATE USER MAPPING FOR current_user SERVER s9; -- ERROR
+ ERROR: must be owner of foreign server s9
+ DROP SERVER s9 CASCADE; -- ERROR
+ ERROR: must be owner of foreign server s9
+ RESET ROLE;
+ -- Cleanup
+ DROP ROLE server_test_role; -- ERROR
+ ERROR: role "server_test_role" cannot be dropped because some objects depend on it
+ DETAIL: access to schema server_test
+ owner of user mapping for server_test_role
+ owner of user mapping for server_test_role
+ owner of server s5
+ owner of server st2
+ owner of server st1
+ DROP SCHEMA server_test; -- ERROR
+ ERROR: cannot drop schema server_test because other objects depend on it
+ DETAIL: server s1 depends on schema server_test
+ server s2 depends on schema server_test
+ HINT: Use DROP ... CASCADE to drop the dependent objects too.
+ DROP SCHEMA server_test CASCADE;
+ NOTICE: drop cascades to 2 other objects
+ DETAIL: drop cascades to server s1
+ drop cascades to server s2
+ DROP SERVER s5 CASCADE;
+ NOTICE: drop cascades to user mapping for server_test_role
+ DROP SERVER st1;
+ DROP SERVER st2;
+ DROP ROLE server_test_role; -- ERROR
+ ERROR: role "server_test_role" cannot be dropped because some objects depend on it
+ DETAIL: owner of user mapping for server_test_role
+ DROP USER MAPPING FOR server_test_role SERVER s6;
+ DROP ROLE server_test_role;
+ DROP FOREIGN DATA WRAPPER foo CASCADE;
+ NOTICE: drop cascades to 5 other objects
+ DETAIL: drop cascades to server s4
+ drop cascades to user mapping for martinp
+ drop cascades to user mapping for public
+ drop cascades to server s6
+ drop cascades to server s9
+ DROP SERVER s8 CASCADE;
+ NOTICE: drop cascades to 2 other objects
+ DETAIL: drop cascades to user mapping for martinp
+ drop cascades to user mapping for public
+ DROP ROLE server_test_indirect;
+ DROP ROLE fdwtest_role;
+ DROP ROLE unprivileged_role; -- ERROR
+ ERROR: role "unprivileged_role" cannot be dropped because some objects depend on it
+ DETAIL: access to foreign data wrapper pgsql
+ REVOKE ALL ON foreign data wrapper pgsql FROM unprivileged_role;
+ DROP ROLE unprivileged_role;
+ -- At this point we should have 2 builtin wrappers and no servers or mappings
+ \dw+ *.*
+ List of foreign data wrappers
+ Schema | Name | Owner | Library | Access privileges | Options
+ ------------+---------+---------+-------------+---------------------+---------
+ pg_catalog | default | martinp | default_fdw | |
+ pg_catalog | pgsql | martinp | pgsql_fdw | {martinp=U/martinp} |
+ (2 rows)
+
+ \dr+ *.*
+ List of foreign servers
+ Schema | Name | Owner | Foreign Data Wrapper | Access privileges | Type | Version | Options
+ --------+------+-------+----------------------+-------------------+------+---------+---------
+ (0 rows)
+
+ \dm+ *.*
+ List of user mappings
+ Schema | Server | Username | Options
+ --------+--------+----------+---------
+ (0 rows)
+
*** a/src/test/regress/expected/opr_sanity.out
--- b/src/test/regress/expected/opr_sanity.out
***************
*** 117,125 **** WHERE p1.oid < p2.oid AND
p1.proretset != p2.proretset OR
p1.provolatile != p2.provolatile OR
p1.pronargs != p2.pronargs);
! oid | proname | oid | proname
! -----+---------+-----+---------
! (0 rows)
-- Look for uses of different type OIDs in the argument/result type fields
-- for different aliases of the same built-in function.
--- 117,126 ----
p1.proretset != p2.proretset OR
p1.provolatile != p2.provolatile OR
p1.pronargs != p2.pronargs);
! oid | proname | oid | proname
! ------+-------------------------------+------+-------------------------------
! 2995 | pg_get_remote_connection_info | 2996 | pg_get_remote_connection_info
! (1 row)
-- Look for uses of different type OIDs in the argument/result type fields
-- for different aliases of the same built-in function.
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1313,1318 **** SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
--- 1313,1319 ----
pg_timezone_abbrevs | SELECT pg_timezone_abbrevs.abbrev, pg_timezone_abbrevs.utc_offset, pg_timezone_abbrevs.is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst);
pg_timezone_names | SELECT pg_timezone_names.name, pg_timezone_names.abbrev, pg_timezone_names.utc_offset, pg_timezone_names.is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst);
pg_user | SELECT pg_shadow.usename, pg_shadow.usesysid, pg_shadow.usecreatedb, pg_shadow.usesuper, pg_shadow.usecatupd, '********'::text AS passwd, pg_shadow.valuntil, pg_shadow.useconfig FROM pg_shadow;
+ pg_user_mapping | SELECT u.oid AS umid, s.oid AS srvoid, s.srvname, u.usesysid, CASE WHEN (u.usesysid = (0)::oid) THEN 'public'::name ELSE a.rolname END AS usename FROM ((pg_foreign_user_mapping u LEFT JOIN pg_authid a ON ((a.oid = u.usesysid))) JOIN pg_foreign_server s ON ((u.usesrv = s.oid)));
pg_views | SELECT n.nspname AS schemaname, c.relname AS viewname, pg_get_userbyid(c.relowner) AS viewowner, pg_get_viewdef(c.oid) AS definition FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'v'::"char");
rtest_v1 | SELECT rtest_t1.a, rtest_t1.b FROM rtest_t1;
rtest_vcomp | SELECT x.part, (x.size * y.factor) AS size_in_cm FROM rtest_comp x, rtest_unitfact y WHERE (x.unit = y.unit);
***************
*** 1328,1334 **** SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color))));
street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath);
toyemp | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp;
! (50 rows)
SELECT tablename, rulename, definition FROM pg_rules
ORDER BY tablename, rulename;
--- 1329,1335 ----
shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color))));
street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath);
toyemp | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp;
! (51 rows)
SELECT tablename, rulename, definition FROM pg_rules
ORDER BY tablename, rulename;
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
***************
*** 99,104 **** SELECT relname, relhasindex
--- 99,107 ----
pg_depend | t
pg_description | t
pg_enum | t
+ pg_foreign_data_wrapper | t
+ pg_foreign_server | t
+ pg_foreign_user_mapping | t
pg_index | t
pg_inherits | t
pg_language | t
***************
*** 149,155 **** SELECT relname, relhasindex
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
! (138 rows)
--
-- another sanity check: every system catalog that has OIDs should have
--- 152,158 ----
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
! (141 rows)
--
-- another sanity check: every system catalog that has OIDs should have
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 77,83 **** test: misc
# ----------
# Another group of parallel tests
# ----------
! test: select_views portals_p2 rules foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts
# ----------
# Another group of parallel tests
--- 77,83 ----
# ----------
# Another group of parallel tests
# ----------
! test: select_views portals_p2 rules foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts connection_manager
# ----------
# Another group of parallel tests
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 108,113 **** test: prepare
--- 108,114 ----
test: without_oid
test: conversion
test: tsdicts
+ test: connection_manager
test: truncate
test: alter_table
test: sequence
*** /dev/null
--- b/src/test/regress/sql/connection_manager.sql
***************
*** 0 ****
--- 1,389 ----
+ --
+ -- Test remote connection management.
+ --
+
+ -- At this point we should have 2 builtin wrappers and no servers
+ \dw+ *.*
+ \dr+ *.*
+ \dm+ *.*
+
+ -- CREATE FOREIGN DATA WRAPPER
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY ''; -- ERROR
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'plpgsql'; -- ERROR
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw';
+ \dw+
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw'; -- duplicate
+ CREATE FOREIGN DATA WRAPPER "Foo" LIBRARY 'default_fdw';
+ DROP FOREIGN DATA WRAPPER "Foo";
+ DROP FOREIGN DATA WRAPPER foo;
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' OPTIONS (testing '1');
+ \dw+
+ DROP FOREIGN DATA WRAPPER foo;
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' OPTIONS (testing '1', testing '2'); -- ERROR
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' OPTIONS (testing '1', another '2');
+ \dw+
+ CREATE SCHEMA fdwtest;
+ CREATE FOREIGN DATA WRAPPER fdwtest.foo LIBRARY 'default_fdw';
+ \dw *.*
+ SET search_path = fdwtest;
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw'; -- duplicate
+ DROP FOREIGN DATA WRAPPER foo;
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw';
+ RESET search_path;
+ \dw *.*
+ CREATE ROLE fdwtest_role;
+ CREATE ROLE fdwtest_role_super SUPERUSER;
+ \dw
+ DROP FOREIGN DATA WRAPPER foo;
+ SET ROLE fdwtest_role;
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw'; -- ERROR
+ SET ROLE fdwtest_role_super;
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'pgsql_fdw';
+ \dw+ *.*
+ RESET ROLE;
+
+ -- ALTER FOREIGN DATA WRAPPER
+ ALTER FOREIGN DATA WRAPPER foo LIBRARY ''; -- ERROR
+ ALTER FOREIGN DATA WRAPPER foo LIBRARY 'plpgsql'; -- ERROR
+ ALTER FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw';
+ \dw
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (SET c '4'); -- ERROR
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP c); -- ERROR
+ \dw+ *.*
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
+ \dw+ *.*
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4'); -- ERROR
+ \dw+ *.*
+ SET ROLE fdwtest_role;
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5'); -- ERROR
+ SET ROLE fdwtest_role_super;
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
+ \dw+ *.*
+ ALTER FOREIGN DATA WRAPPER foo OWNER to fdwtest_role; -- ERROR
+ ALTER FOREIGN DATA WRAPPER foo OWNER to fdwtest_role_super;
+ ALTER ROLE fdwtest_role_super NOSUPERUSER;
+ SET ROLE fdwtest_role_super;
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD e '6'); -- ERROR
+ RESET ROLE;
+ \dw+ *.*
+ SET search_path=fdwtest;
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (another 'fdw');
+ \dw+ *.*
+ ALTER FOREIGN DATA WRAPPER fdwtest.foo OPTIONS (with 'qualified name');
+ \dw+ *.*
+ RESET search_path;
+
+ -- DROP FOREIGN DATA WRAPPER
+ \dw+ *.*
+ DROP FOREIGN DATA WRAPPER nonexistent; -- ERROR
+ DROP FOREIGN DATA WRAPPER noschema.nonexistent; -- ERROR
+ DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
+ DROP FOREIGN DATA WRAPPER IF EXISTS noschema.nonexistent;
+ \dw+ *.*
+ DROP ROLE fdwtest_role_super; -- ERROR
+ SET ROLE fdwtest_role_super;
+ DROP FOREIGN DATA WRAPPER foo; -- ERROR
+ RESET ROLE;
+ DROP FOREIGN DATA WRAPPER foo;
+ DROP ROLE fdwtest_role_super;
+ \dw+ *.*
+ DROP SCHEMA fdwtest; -- ERROR
+ DROP SCHEMA fdwtest CASCADE;
+ \dw+ *.*
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw';
+ CREATE SERVER s1 FOREIGN DATA WRAPPER foo;
+ CREATE USER MAPPING FOR current_user SERVER s1;
+ \dw+
+ \dr+
+ \dm+
+ DROP FOREIGN DATA WRAPPER foo; -- ERROR
+ SET ROLE fdwtest_role;
+ DROP FOREIGN DATA WRAPPER foo CASCADE; -- ERROR
+ RESET ROLE;
+ DROP FOREIGN DATA WRAPPER foo CASCADE; -- ERROR
+ \dw+
+ \dr+
+ \dm+
+
+ -- exercise CREATE SERVER
+ CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR
+ CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' OPTIONS (test_wrapper 'true');
+ CREATE SERVER s1 FOREIGN DATA WRAPPER bar; -- ERROR
+ CREATE SERVER s1 FOREIGN DATA WRAPPER foo;
+ CREATE SERVER s2 FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b');
+ CREATE SERVER s3 TYPE 'oracle' FOREIGN DATA WRAPPER foo;
+ CREATE SERVER s4 TYPE 'oracle' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b');
+ CREATE SERVER s5 VERSION '15.0' FOREIGN DATA WRAPPER foo;
+ CREATE SERVER s6 VERSION '16.0' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b');
+ CREATE SERVER s7 TYPE 'oracle' VERSION '17.0' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b');
+ CREATE SERVER s8 FOREIGN DATA WRAPPER pgsql OPTIONS (foo '1'); -- ERROR
+ CREATE SERVER s8 FOREIGN DATA WRAPPER pgsql OPTIONS (host 'localhost', dbname 's8db');
+ CREATE SERVER nonexistent.s8 FOREIGN DATA WRAPPER foo; -- ERROR
+ \dr+
+ CREATE ROLE server_test_role;
+ SET ROLE server_test_role;
+ CREATE SERVER st1 FOREIGN DATA WRAPPER foo; -- ERROR: no usage on FDW
+ RESET ROLE;
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO server_test_role;
+ SET ROLE server_test_role;
+ CREATE SERVER st1 FOREIGN DATA WRAPPER foo;
+ RESET ROLE;
+ \dr+
+ CREATE SCHEMA server_test;
+ CREATE SERVER server_test.s1 FOREIGN DATA WRAPPER foo;
+ SET search_path=server_test;
+ \dr
+ CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR: duplicate
+ CREATE SERVER s2 FOREIGN DATA WRAPPER foo; -- ERROR: FDW not visible
+ CREATE SERVER s2 FOREIGN DATA WRAPPER public.foo;
+ \dr
+ SET ROLE server_test_role;
+ CREATE SERVER s3 FOREIGN DATA WRAPPER public.foo; -- ERROR: no schema rights
+ RESET search_path;
+ RESET ROLE;
+ CREATE ROLE server_test_indirect;
+ REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM server_test_role;
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO server_test_indirect;
+ SET ROLE server_test_role;
+ CREATE SERVER st2 FOREIGN DATA WRAPPER foo; -- ERROR
+ RESET ROLE;
+ GRANT server_test_indirect TO server_test_role;
+ SET ROLE server_test_role;
+ CREATE SERVER st2 FOREIGN DATA WRAPPER foo;
+ \dr+ *.*
+ RESET ROLE;
+ REVOKE server_test_indirect FROM server_test_role;
+
+ -- ALTER SERVER
+ ALTER SERVER s0; -- ERROR
+ ALTER SERVER s0 OPTIONS (a '1'); -- ERROR
+ ALTER SERVER test.server VERSION '1'; -- ERROR
+ ALTER SERVER s1 VERSION '1.0' OPTIONS (servername 's1');
+ ALTER SERVER s2 VERSION '1.1';
+ ALTER SERVER s3 OPTIONS (tnsname 'orcl', port '1521');
+ GRANT USAGE ON SERVER s1 TO server_test_role;
+ \dr+ *.*
+ SET ROLE server_test_role;
+ ALTER SERVER s1 VERSION '1.1'; -- ERROR
+ ALTER SERVER s1 OWNER TO server_test_role; -- ERROR
+ RESET ROLE;
+ ALTER SERVER s1 OWNER TO server_test_role;
+ SET ROLE server_test_role;
+ ALTER SERVER s1 VERSION '1.1';
+ RESET ROLE;
+ ALTER SERVER s8 OPTIONS (foo '1'); -- ERROR option validation
+ ALTER SERVER s8 OPTIONS (connect_timeout '30', set dbname 'db1', drop host);
+ SET ROLE server_test_role;
+ ALTER SERVER s1 OWNER TO server_test_indirect; -- ERROR
+ RESET ROLE;
+ GRANT server_test_indirect TO server_test_role;
+ SET ROLE server_test_role;
+ ALTER SERVER s1 OWNER TO server_test_indirect;
+ RESET ROLE;
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO server_test_indirect;
+ SET ROLE server_test_role;
+ ALTER SERVER s1 OWNER TO server_test_indirect;
+ RESET ROLE;
+ DROP ROLE server_test_indirect; -- ERROR
+ \dr+ *.*
+
+ -- DROP SERVER
+ DROP SERVER nonexistent; -- ERROR
+ DROP SERVER noschema.nonexistent; -- ERROR
+ DROP SERVER IF EXISTS nonexistent;
+ DROP SERVER IF EXISTS noschema.nonexistent;
+ \dr *.*
+ SET ROLE server_test_role;
+ DROP SERVER s2; -- ERROR
+ DROP SERVER s1;
+ RESET ROLE;
+ \dr *.*
+ ALTER SERVER s2 OWNER TO server_test_role;
+ SET ROLE server_test_role;
+ DROP SERVER s2;
+ RESET ROLE;
+ \dr *.*
+ CREATE USER MAPPING FOR current_user SERVER s3;
+ \dm *.*
+ DROP SERVER s3; -- ERROR
+ DROP SERVER s3 CASCADE;
+ \dr *.*
+ \dm *.*
+
+ -- CREATE USER MAPPING
+ CREATE USER MAPPING FOR baz SERVER s1; -- ERROR
+ CREATE USER MAPPING FOR current_user SERVER s1; -- ERROR
+ CREATE USER MAPPING FOR current_user SERVER s4;
+ CREATE USER MAPPING FOR user SERVER s4; -- ERROR duplicate
+ CREATE USER MAPPING FOR public SERVER s4 OPTIONS (mapping 'is public');
+ CREATE USER MAPPING FOR user SERVER s8 OPTIONS (username 'test', password 'secret'); -- ERROR
+ CREATE USER MAPPING FOR user SERVER s8 OPTIONS (user 'test', password 'secret');
+ ALTER SERVER s5 OWNER TO server_test_role;
+ ALTER SERVER s6 OWNER TO server_test_indirect;
+ SET ROLE server_test_role;
+ CREATE USER MAPPING FOR current_user SERVER s5;
+ CREATE USER MAPPING FOR current_user SERVER s6 OPTIONS (username 'test');
+ CREATE USER MAPPING FOR current_user SERVER s7; -- ERROR
+ CREATE USER MAPPING FOR public SERVER s8; -- ERROR
+ RESET ROLE;
+ ALTER SERVER server_test.s1 OWNER TO server_test_indirect;
+ SET ROLE server_test_role;
+ CREATE USER MAPPING FOR current_user SERVER server_test.s1;
+ SET search_path = server_test;
+ CREATE USER MAPPING FOR public SERVER s1; -- ERROR no usage on server schema
+ RESET ROLE;
+ GRANT USAGE ON SCHEMA server_test TO server_test_role;
+ SET ROLE server_test_role;
+ CREATE USER MAPPING FOR public SERVER s1;
+ RESET search_path;
+ RESET ROLE;
+ \dm *.*
+
+ -- ALTER USER MAPPING
+ ALTER USER MAPPING FOR bob SERVER s4 OPTIONS (gotcha 'true'); -- ERROR
+ ALTER USER MAPPING FOR user SERVER ss4 OPTIONS (gotcha 'true'); -- ERROR
+ ALTER USER MAPPING FOR public SERVER s5 OPTIONS (gotcha 'true'); -- ERROR
+ ALTER USER MAPPING FOR current_user SERVER s8 OPTIONS (username 'test'); -- ERROR
+ ALTER USER MAPPING FOR current_user SERVER s8 OPTIONS (DROP user, SET password 'public');
+ SET ROLE server_test_role;
+ ALTER USER MAPPING FOR current_user SERVER s5 OPTIONS (add modified '1');
+ ALTER USER MAPPING FOR public SERVER s4 OPTIONS (add modified '1'); -- ERROR
+ ALTER USER MAPPING FOR public SERVER server_test.s1 OPTIONS (add modified '1');
+ RESET ROLE;
+ SET search_path = server_test;
+ ALTER USER MAPPING FOR public SERVER s1 OPTIONS (add modified '2'); -- ERROR
+ ALTER USER MAPPING FOR public SERVER s1 OPTIONS (set modified '2');
+ RESET ROLE;
+ RESET search_path;
+ \dm+ *.*
+
+ -- DROP USER MAPPING
+ DROP USER MAPPING FOR bob SERVER s4; -- ERROR
+ DROP USER MAPPING FOR user SERVER ss4; -- ERROR
+ DROP USER MAPPING FOR user SERVER test.ss4; -- ERROR
+ DROP USER MAPPING IF EXISTS for bob SERVER s4; -- ERROR
+ DROP USER MAPPING IF EXISTS for public SERVER s7;
+ CREATE USER MAPPING FOR public SERVER s8;
+ SET ROLE server_test_role;
+ DROP USER MAPPING FOR public SERVER s8; -- ERROR
+ DROP USER MAPPING FOR current_user SERVER server_test.s1;
+ RESET ROLE;
+ SET search_path = server_test;
+ DROP USER MAPPING FOR public SERVER s1;
+ RESET search_path;
+ DROP SERVER s7;
+ \dm *.*
+
+ -- Information schema
+ SELECT * FROM information_schema.foreign_data_wrappers;
+ SELECT * FROM information_schema.foreign_data_wrapper_options;
+ SELECT * FROM information_schema.foreign_servers;
+ SELECT * FROM information_schema.foreign_server_options;
+ SELECT * FROM information_schema.user_mappings;
+ SELECT * FROM information_schema.user_mapping_options;
+ SET ROLE server_test_role;
+ SELECT * FROM information_schema.user_mapping_options;
+ RESET ROLE;
+
+ -- pg_get_remote_connection_info
+ SELECT * FROM pg_get_remote_connection_info(NULL);
+ SELECT * FROM pg_get_remote_connection_info(''); -- ERROR
+ SELECT * FROM pg_get_remote_connection_info('foo'); -- ERROR
+ SELECT * FROM pg_get_remote_connection_info('foo', 'bob'); -- ERROR
+ SELECT * FROM pg_get_remote_connection_info('foo', 'server_test_role'); -- ERROR
+ SELECT * FROM pg_get_remote_connection_info('foo.bar'); -- ERROR
+ SELECT * FROM pg_get_remote_connection_info('foo.bar.baz'); -- ERROR
+ SELECT * FROM pg_get_remote_connection_info('foo.bar.baz.foobar'); -- ERROR
+ SELECT * FROM pg_get_remote_connection_info('foo', current_user); -- ERROR
+ SELECT * FROM pg_get_remote_connection_info('s4');
+ SELECT * FROM pg_get_remote_connection_info('s4', 'public');
+ SELECT * FROM pg_get_remote_connection_info('s6', 'server_test_role');
+ ALTER SERVER s6 OPTIONS (DROP host, DROP dbname);
+ SELECT * FROM pg_get_remote_connection_info('s6', 'server_test_role');
+ ALTER USER MAPPING FOR server_test_role SERVER s6 OPTIONS (DROP username);
+ SELECT * FROM pg_get_remote_connection_info('s6', 'server_test_role');
+ SELECT * FROM pg_get_remote_connection_info('s8');
+ SELECT * FROM pg_get_remote_connection_info('s8', 'public');
+ SET ROLE server_test_role;
+ SELECT * FROM pg_get_remote_connection_info('s6'); -- ERROR
+ RESET ROLE;
+
+ -- Privileges
+ CREATE ROLE unprivileged_role;
+ SET ROLE unprivileged_role;
+ CREATE FOREIGN DATA WRAPPER foobar LIBRARY 'default_fdw'; -- ERROR
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (gotcha 'true'); -- ERROR
+ DROP FOREIGN DATA WRAPPER foo; -- ERROR
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO server_test_role; -- ERROR
+ CREATE SERVER s9 FOREIGN DATA WRAPPER foo; -- ERROR
+ ALTER SERVER s4 VERSION '0.5'; -- ERROR
+ DROP SERVER s4; -- ERROR
+ GRANT USAGE ON SERVER s4 TO server_test_role; -- ERROR
+ CREATE USER MAPPING FOR public SERVER s4; -- ERROR
+ ALTER USER MAPPING FOR server_test_role SERVER s6 OPTIONS (gotcha 'true'); -- ERROR
+ DROP USER MAPPING FOR server_test_role SERVER s6; -- ERROR
+ RESET ROLE;
+
+ GRANT USAGE ON FOREIGN DATA WRAPPER pgsql TO unprivileged_role;
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO unprivileged_role WITH GRANT OPTION;
+ SET ROLE unprivileged_role;
+ CREATE FOREIGN DATA WRAPPER foobar LIBRARY 'default_fdw'; -- ERROR
+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (gotcha 'true'); -- ERROR
+ DROP FOREIGN DATA WRAPPER foo; -- ERROR
+ GRANT USAGE ON FOREIGN DATA WRAPPER pgsql TO server_test_role; -- WARNING
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO server_test_role;
+ CREATE SERVER s9 FOREIGN DATA WRAPPER pgsql;
+ ALTER SERVER s6 VERSION '0.5'; -- ERROR
+ DROP SERVER s6; -- ERROR
+ GRANT USAGE ON SERVER s6 TO server_test_role; -- ERROR
+ GRANT USAGE ON SERVER s9 TO server_test_role;
+ CREATE USER MAPPING FOR public SERVER s6; -- ERROR
+ CREATE USER MAPPING FOR public SERVER s9;
+ ALTER USER MAPPING FOR server_test_role SERVER s6 OPTIONS (gotcha 'true'); -- ERROR
+ DROP USER MAPPING FOR server_test_role SERVER s6; -- ERROR
+ RESET ROLE;
+
+ REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM unprivileged_role; -- ERROR
+ REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM unprivileged_role CASCADE;
+ SET ROLE unprivileged_role;
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO server_test_role; -- ERROR
+ CREATE SERVER s10 FOREIGN DATA WRAPPER foo; -- ERROR
+ ALTER SERVER s9 VERSION '1.1';
+ GRANT USAGE ON SERVER s9 TO server_test_role;
+ CREATE USER MAPPING FOR current_user SERVER s9;
+ DROP SERVER s9 CASCADE;
+ RESET ROLE;
+ CREATE SERVER s9 FOREIGN DATA WRAPPER foo;
+ GRANT USAGE ON SERVER s9 TO unprivileged_role;
+ SET ROLE unprivileged_role;
+ ALTER SERVER s9 VERSION '1.2'; -- ERROR
+ GRANT USAGE ON SERVER s9 TO server_test_role; -- WARNING
+ CREATE USER MAPPING FOR current_user SERVER s9; -- ERROR
+ DROP SERVER s9 CASCADE; -- ERROR
+ RESET ROLE;
+
+ -- Cleanup
+ DROP ROLE server_test_role; -- ERROR
+ DROP SCHEMA server_test; -- ERROR
+ DROP SCHEMA server_test CASCADE;
+ DROP SERVER s5 CASCADE;
+ DROP SERVER st1;
+ DROP SERVER st2;
+ DROP ROLE server_test_role; -- ERROR
+ DROP USER MAPPING FOR server_test_role SERVER s6;
+ DROP ROLE server_test_role;
+ DROP FOREIGN DATA WRAPPER foo CASCADE;
+ DROP SERVER s8 CASCADE;
+ DROP ROLE server_test_indirect;
+ DROP ROLE fdwtest_role;
+ DROP ROLE unprivileged_role; -- ERROR
+ REVOKE ALL ON foreign data wrapper pgsql FROM unprivileged_role;
+ DROP ROLE unprivileged_role;
+
+ -- At this point we should have 2 builtin wrappers and no servers or mappings
+ \dw+ *.*
+ \dr+ *.*
+ \dm+ *.*
+