diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/catalogs.sgml ./doc/src/sgml/catalogs.sgml *** ../cvs-pgsql/doc/src/sgml/catalogs.sgml 2008-11-24 09:49:51.000000000 +0200 --- ./doc/src/sgml/catalogs.sgml 2008-12-09 11:24:31.000000000 +0200 *************** *** 134,139 **** --- 134,149 ---- + pg_foreign_data_wrapper + foreign-data wrapper definitions + + + + pg_foreign_server + foreign server definitions + + + pg_index additional index information *************** *** 247,252 **** --- 257,267 ---- pg_type data types + + + pg_user_mapping + mappings of users to foreign servers + *************** *** 2552,2557 **** --- 2567,2813 ---- + + <structname>pg_foreign_data_wrapper</structname> + + + pg_foreign_data_wrapper + + + + The catalog pg_foreign_data_wrapper stores + foreign-data wrapper definitions. A foreign-data wrapper is the + mechanism by which external data, residing on foreign servers, is + accessed. + + + + <structname>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 + + + + +
+
+ + + + <structname>pg_foreign_server</structname> + + + pg_foreign_server + + + + The catalog pg_foreign_server 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. + + + + <structname>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. + + + + +
+
+ + + + <structname>pg_user_mapping</structname> + + + pg_user_mapping + + + + The catalog pg_user_mapping stores + the mappings from local user to remote. Access to this catalog is + restricted from normal users, use the view + pg_user_mappings + instead. + + + + <structname>pg_user_mapping</> Columns + + + + + Name + Type + References + Description + + + + + + umuser + oid + pg_authid.oid + OID of the local role to be mapped, 0 if the user mapping is public + + + + umserver + oid + pg_foreign_server.oid + + The OID of the foreign server that contains this mapping + + + + + umoptions + text[] + + + User mapping specific options, as keyword=value strings. + + + + +
+
+ + <structname>pg_index</structname> *************** *** 6998,7003 **** --- 7254,7332 ---- + + <structname>pg_user_mappings</structname> + + + pg_user_mappings + + + + The view pg_user_mappings provides access + to information about user mappings. This is essentially a publicly + readable view of + pg_user_mapping + that leaves out the options field. + + + + <structname>pg_user_mappings</> Columns + + + + + Name + Type + References + Description + + + + + + umid + oid + pg_user_mapping.oid + OID of the user mapping + + + + srvid + oid + pg_foreign_server.oid + + The OID of the foreign server that contains this mapping + + + + + srvname + text + + + Name of the foreign server + + + + + umuser + 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 + + + +
+
+ + <structname>pg_views</structname> diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/features.sgml ./doc/src/sgml/features.sgml *** ../cvs-pgsql/doc/src/sgml/features.sgml 2008-12-01 10:24:54.000000000 +0200 --- ./doc/src/sgml/features.sgml 2008-12-08 17:12:14.000000000 +0200 *************** *** 71,81 **** ! The PostgreSQL core covers parts 1, 2, 11, and 14. Part 3 is covered by the ODBC driver, and part 13 is covered by the PL/Java plug-in, but exact conformance is currently not being verified for these components. There are currently no ! implementations of parts 4, 9, and 10 for PostgreSQL. --- 71,81 ---- ! The PostgreSQL core covers parts 1, 2, 9, 11, and 14. Part 3 is covered by the ODBC driver, and part 13 is covered by the PL/Java plug-in, but exact conformance is currently not being verified for these components. There are currently no ! implementations of parts 4 and 10 for PostgreSQL. diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/func.sgml ./doc/src/sgml/func.sgml *** ../cvs-pgsql/doc/src/sgml/func.sgml 2008-12-05 15:06:21.000000000 +0200 --- ./doc/src/sgml/func.sgml 2008-12-09 12:07:51.000000000 +0200 *************** *** 11574,11579 **** --- 11574,11585 ---- is conversion visible in search path + pg_foreign_data_wrapper_is_visible(fdw_oid) + + boolean + is foreign-data wrapper visible in search path + + pg_function_is_visible(function_oid) boolean *************** *** 11592,11597 **** --- 11598,11609 ---- is operator class visible in search path + pg_server_is_visible(server_oid) + + boolean + is foreign server visible in search path + + pg_table_is_visible(table_oid) boolean *************** *** 12974,12979 **** --- 12986,13085 ---- at session end, even if the client disconnects ungracefully.) + + The functions shown + in provide + information about connections to foreign databases. + + + + Remote Connection Information Functions + + + + Name + Return Type + Description + + + + + + + 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, username text) + + 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 user mapping + + + +
+ + + pg_get_remote_connection_infopg_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_optionspg_get_foreign_data_wrapper_options + fetches the generic options associated with the foreign-data + wrapper. + + + + pg_get_foreign_server_optionspg_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_optionspg_get_user_mapping_options + fetches the generic options associated with the user mapping. + Typically this includes the user name 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. +
diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/keywords.sgml ./doc/src/sgml/keywords.sgml *** ../cvs-pgsql/doc/src/sgml/keywords.sgml 2008-12-05 15:06:21.000000000 +0200 --- ./doc/src/sgml/keywords.sgml 2008-12-08 14:41:59.000000000 +0200 *************** *** 2688,2693 **** --- 2688,2701 ---- reserved + LIBRARY + non-reserved + non-reserved + non-reserved + + + + LIKE reserved (can be function or type) reserved *************** *** 2818,2825 **** MAPPING non-reserved ! ! --- 2826,2833 ---- MAPPING non-reserved ! non-reserved ! non-reserved *************** *** 3441,3447 **** OPTIONS ! non-reserved non-reserved non-reserved --- 3449,3455 ---- OPTIONS ! non-reserved non-reserved non-reserved non-reserved *************** *** 4456,4461 **** --- 4464,4477 ---- non-reserved + SERVER + non-reserved + non-reserved + non-reserved + + + + SERVER_NAME non-reserved *************** *** 5584,5589 **** --- 5600,5613 ---- reserved + WRAPPER + non-reserved + non-reserved + non-reserved + + + + WRITE non-reserved non-reserved diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/ref/allfiles.sgml ./doc/src/sgml/ref/allfiles.sgml *** ../cvs-pgsql/doc/src/sgml/ref/allfiles.sgml 2008-03-27 19:24:16.000000000 +0200 --- ./doc/src/sgml/ref/allfiles.sgml 2008-12-08 10:04:29.000000000 +0200 *************** *** 10,15 **** --- 10,16 ---- + *************** *** 19,24 **** --- 20,26 ---- + *************** *** 29,34 **** --- 31,37 ---- + *************** *** 45,50 **** --- 48,54 ---- + *************** *** 56,61 **** --- 60,66 ---- + *************** *** 66,71 **** --- 71,77 ---- + *************** *** 76,81 **** --- 82,88 ---- + *************** *** 88,93 **** --- 95,101 ---- + *************** *** 97,102 **** --- 105,111 ---- + diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml ./doc/src/sgml/ref/alter_foreign_data_wrapper.sgml *** ../cvs-pgsql/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml 1970-01-01 02:00:00.000000000 +0200 --- ./doc/src/sgml/ref/alter_foreign_data_wrapper.sgml 2008-12-09 11:32:08.000000000 +0200 *************** *** 0 **** --- 1,132 ---- + + + + + 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 + foreign-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. + + + + Note that 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, and 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 + + + + + + + + diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/ref/alter_server.sgml ./doc/src/sgml/ref/alter_server.sgml *** ../cvs-pgsql/doc/src/sgml/ref/alter_server.sgml 1970-01-01 02:00:00.000000000 +0200 --- ./doc/src/sgml/ref/alter_server.sgml 2008-12-09 11:45:21.000000000 +0200 *************** *** 0 **** --- 1,123 ---- + + + + + 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 satisfy all these criteria + 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, and 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 + + + + + + + + diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/ref/alter_user_mapping.sgml ./doc/src/sgml/ref/alter_user_mapping.sgml *** ../cvs-pgsql/doc/src/sgml/ref/alter_user_mapping.sgml 1970-01-01 02:00:00.000000000 +0200 --- ./doc/src/sgml/ref/alter_user_mapping.sgml 2008-12-09 11:47:26.000000000 +0200 *************** *** 0 **** --- 1,111 ---- + + + + + 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. ADD, SET, and DROP + specify the action to be performed. ADD is assumed + if no operation is explicitly specified. 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 + + + + + + + + diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/ref/create_foreign_data_wrapper.sgml ./doc/src/sgml/ref/create_foreign_data_wrapper.sgml *** ../cvs-pgsql/doc/src/sgml/ref/create_foreign_data_wrapper.sgml 1970-01-01 02:00:00.000000000 +0200 --- ./doc/src/sgml/ref/create_foreign_data_wrapper.sgml 2008-12-09 12:54:57.000000000 +0200 *************** *** 0 **** --- 1,144 ---- + + + + + 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' + LANGUAGE C + [ 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. + + + + + + LANGUAGE C + + + Currently, only the C programming language is supported for + implementing foreign-data wrappers. + + + + + + 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' LANGUAGE C; + + + + + 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' + LANGUAGE C + OPTIONS (debug 'true'); + + + + + + + Compatibility + + + CREATE FOREIGN DATA WRAPPER conforms to ISO/IEC + 9075-9 (SQL/MED), with the exception that + the LIBRARY clause is not optional in + PostgreSQL. + + + + + See Also + + + + + + + + + + diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/ref/create_server.sgml ./doc/src/sgml/ref/create_server.sgml *** ../cvs-pgsql/doc/src/sgml/ref/create_server.sgml 1970-01-01 02:00:00.000000000 +0200 --- ./doc/src/sgml/ref/create_server.sgml 2008-12-09 11:46:37.000000000 +0200 *************** *** 0 **** --- 1,143 ---- + + + + + 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. + + + + Creating a server requires USAGE privilege on the + foreign-data wrapper being used. + + + + + 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 built-in foreign-data + wrapper default: + + CREATE SERVER foo FOREIGN DATA WRAPPER "default"; + + + + + Create a server test.myserver that uses the + foreign-data wrapper pgsql: + + 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 + + + + + + + + + + diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/ref/create_user_mapping.sgml ./doc/src/sgml/ref/create_user_mapping.sgml *** ../cvs-pgsql/doc/src/sgml/ref/create_user_mapping.sgml 1970-01-01 02:00:00.000000000 +0200 --- ./doc/src/sgml/ref/create_user_mapping.sgml 2008-12-09 11:48:05.000000000 +0200 *************** *** 0 **** --- 1,110 ---- + + + + + CREATE USER MAPPING + SQL - Language Statements + + + + CREATE USER MAPPING + define a new mapping of a user to a 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 user name and password of + the mapping. Option names must be unque. The allowed option + names and values are specific to the server's 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 + + + + + + + + + + diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/ref/drop_foreign_data_wrapper.sgml ./doc/src/sgml/ref/drop_foreign_data_wrapper.sgml *** ../cvs-pgsql/doc/src/sgml/ref/drop_foreign_data_wrapper.sgml 1970-01-01 02:00:00.000000000 +0200 --- ./doc/src/sgml/ref/drop_foreign_data_wrapper.sgml 2008-12-09 11:33:26.000000000 +0200 *************** *** 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 + + + + + + + + diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/ref/drop_server.sgml ./doc/src/sgml/ref/drop_server.sgml *** ../cvs-pgsql/doc/src/sgml/ref/drop_server.sgml 1970-01-01 02:00:00.000000000 +0200 --- ./doc/src/sgml/ref/drop_server.sgml 2008-12-08 14:30:50.000000000 +0200 *************** *** 0 **** --- 1,112 ---- + + + + + DROP SERVER + SQL - Language Statements + + + + DROP SERVER + remove a foreign server descriptor + + + + DROP SERVER + + + + + DROP SERVER [ IF EXISTS ] servername [ CASCADE | RESTRICT ] + + + + + Description + + + DROP SERVER removes an existing foreign server + descriptor. 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 + + + + + + + + diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/ref/drop_user_mapping.sgml ./doc/src/sgml/ref/drop_user_mapping.sgml *** ../cvs-pgsql/doc/src/sgml/ref/drop_user_mapping.sgml 1970-01-01 02:00:00.000000000 +0200 --- ./doc/src/sgml/ref/drop_user_mapping.sgml 2008-12-08 11:18:14.000000000 +0200 *************** *** 0 **** --- 1,104 ---- + + + + + DROP USER MAPPING + SQL - Language Statements + + + + DROP USER MAPPING + remove a user mapping for a 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 + + + + + + + + diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/ref/grant.sgml ./doc/src/sgml/ref/grant.sgml *** ../cvs-pgsql/doc/src/sgml/ref/grant.sgml 2008-11-17 16:34:26.000000000 +0200 --- ./doc/src/sgml/ref/grant.sgml 2008-12-09 11:48:43.000000000 +0200 *************** *** 35,40 **** --- 35,48 ---- ON DATABASE dbname [, ...] 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 FOREIGN SERVER servername [, ...] + TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ] + GRANT { EXECUTE | ALL [ PRIVILEGES ] } ON FUNCTION funcname ( [ [ argmode ] [ argname ] argtype [, ...] ] ) [, ...] TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ] *************** *** 61,70 **** 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, foreign-data wrapper, foreign server, 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. *************** *** 299,304 **** --- 307,320 ---- For sequences, this privilege allows the use of the 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. + diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/ref/psql-ref.sgml ./doc/src/sgml/ref/psql-ref.sgml *** ../cvs-pgsql/doc/src/sgml/ref/psql-ref.sgml 2008-12-05 15:06:21.000000000 +0200 --- ./doc/src/sgml/ref/psql-ref.sgml 2008-12-09 11:34:18.000000000 +0200 *************** *** 1089,1094 **** --- 1089,1117 ---- + \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 user name and + password of the remote user, so precautions should be taken + not to disclose them. + + + + + + + \dn [ pattern ] \dn+ [ pattern ] *************** *** 1137,1142 **** --- 1160,1181 ---- + \dr [ pattern ] + \dr+ [ pattern ] + + + Lists all foreign (remote) servers. + 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 **** --- 1201,1221 ---- + \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 diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/ref/revoke.sgml ./doc/src/sgml/ref/revoke.sgml *** ../cvs-pgsql/doc/src/sgml/ref/revoke.sgml 2008-11-17 16:34:26.000000000 +0200 --- ./doc/src/sgml/ref/revoke.sgml 2008-12-08 14:25:44.000000000 +0200 *************** *** 42,47 **** --- 42,59 ---- [ 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 FOREIGN SERVER servername [, ...] + FROM { [ GROUP ] rolename | PUBLIC } [, ...] + [ CASCADE | RESTRICT ] + + REVOKE [ GRANT OPTION FOR ] { EXECUTE | ALL [ PRIVILEGES ] } ON FUNCTION funcname ( [ [ argmode ] [ argname ] argtype [, ...] ] ) [, ...] FROM { [ GROUP ] rolename | PUBLIC } [, ...] diff -x CVS -cdNr ../cvs-pgsql/doc/src/sgml/reference.sgml ./doc/src/sgml/reference.sgml *** ../cvs-pgsql/doc/src/sgml/reference.sgml 2008-03-27 19:24:16.000000000 +0200 --- ./doc/src/sgml/reference.sgml 2008-12-08 09:40:17.000000000 +0200 *************** *** 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; diff -x CVS -cdNr ../cvs-pgsql/src/Makefile ./src/Makefile *** ../cvs-pgsql/src/Makefile 2008-03-18 18:24:50.000000000 +0200 --- ./src/Makefile 2008-12-08 09:40:17.000000000 +0200 *************** *** 19,24 **** --- 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 $@ diff -x CVS -cdNr ../cvs-pgsql/src/backend/Makefile ./src/backend/Makefile *** ../cvs-pgsql/src/backend/Makefile 2008-08-29 20:35:25.000000000 +0300 --- ./src/backend/Makefile 2008-12-08 11:39:09.000000000 +0200 *************** *** 14,20 **** top_builddir = ../.. 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 --- 14,20 ---- top_builddir = ../.. include $(top_builddir)/src/Makefile.global ! SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ main nodes optimizer port postmaster regex rewrite \ storage tcop tsearch utils $(top_builddir)/src/timezone diff -x CVS -cdNr ../cvs-pgsql/src/backend/catalog/Makefile ./src/backend/catalog/Makefile *** ../cvs-pgsql/src/backend/catalog/Makefile 2008-11-20 10:21:55.000000000 +0200 --- ./src/backend/catalog/Makefile 2008-12-09 10:28:52.000000000 +0200 *************** *** 36,41 **** --- 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_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ toasting.h indexing.h \ ) diff -x CVS -cdNr ../cvs-pgsql/src/backend/catalog/aclchk.c ./src/backend/catalog/aclchk.c *** ../cvs-pgsql/src/backend/catalog/aclchk.c 2008-11-03 16:31:20.000000000 +0200 --- ./src/backend/catalog/aclchk.c 2008-12-09 17:40:57.000000000 +0200 *************** *** 27,32 **** --- 27,34 ---- #include "catalog/pg_authid.h" #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" + #include "catalog/pg_foreign_data_wrapper.h" + #include "catalog/pg_foreign_server.h" #include "catalog/pg_language.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" *************** *** 50,55 **** --- 52,59 ---- static void ExecGrant_Relation(InternalGrant *grantStmt); static void ExecGrant_Database(InternalGrant *grantStmt); + static void ExecGrant_Fdw(InternalGrant *grantStmt); + static void ExecGrant_ForeignServer(InternalGrant *grantStmt); static void ExecGrant_Function(InternalGrant *grantStmt); static void ExecGrant_Language(InternalGrant *grantStmt); static void ExecGrant_Namespace(InternalGrant *grantStmt); *************** *** 188,193 **** --- 192,203 ---- 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 **** --- 333,346 ---- 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; *************** *** 380,385 **** --- 398,409 ---- case ACL_OBJECT_DATABASE: ExecGrant_Database(istmt); break; + case ACL_OBJECT_FDW: + ExecGrant_Fdw(istmt); + break; + case ACL_OBJECT_FOREIGN_SERVER: + ExecGrant_ForeignServer(istmt); + break; case ACL_OBJECT_FUNCTION: ExecGrant_Function(istmt); break; *************** *** 520,525 **** --- 544,565 ---- 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); *************** *** 841,846 **** --- 881,1119 ---- } 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_foreign_data_wrapper]; + bool nulls[Natts_pg_foreign_data_wrapper]; + bool replaces[Natts_pg_foreign_data_wrapper]; + 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_foreign_data_wrapper_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, false, sizeof(nulls)); + MemSet(replaces, false, sizeof(replaces)); + + replaces[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true; + values[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(new_acl); + + newtuple = heap_modify_tuple(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]; + bool nulls[Natts_pg_foreign_server]; + bool 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, false, sizeof(nulls)); + MemSet(replaces, false, sizeof(replaces)); + + replaces[Anum_pg_foreign_server_srvacl - 1] = true; + values[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(new_acl); + + newtuple = heap_modify_tuple(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 void ExecGrant_Function(InternalGrant *istmt) { Relation relation; *************** *** 1428,1434 **** /* 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 **** /* 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 **** --- 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_foreign_data_wrapper_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 **** --- 2247,2377 ---- return result; } + /* + * Exported routine for examining a user's privileges for a foreign + * data wrapper + */ + AclMode + pg_foreign_data_wrapper_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_foreign_data_wrapper_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 **** --- 2449,2479 ---- return ACLCHECK_NO_PRIV; } + /* + * Exported routine for checking a user's access privileges to a foreign + * data wrapper + */ + AclResult + pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode) + { + if (pg_foreign_data_wrapper_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 **** --- 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). diff -x CVS -cdNr ../cvs-pgsql/src/backend/catalog/dependency.c ./src/backend/catalog/dependency.c *** ../cvs-pgsql/src/backend/catalog/dependency.c 2008-11-13 10:44:31.000000000 +0200 --- ./src/backend/catalog/dependency.c 2008-12-09 11:51:43.000000000 +0200 *************** *** 33,38 **** --- 33,40 ---- #include "catalog/pg_conversion_fn.h" #include "catalog/pg_database.h" #include "catalog/pg_depend.h" + #include "catalog/pg_foreign_data_wrapper.h" + #include "catalog/pg_foreign_server.h" #include "catalog/pg_language.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" *************** *** 47,52 **** --- 49,55 ---- #include "catalog/pg_ts_parser.h" #include "catalog/pg_ts_template.h" #include "catalog/pg_type.h" + #include "catalog/pg_user_mapping.h" #include "commands/comment.h" #include "commands/dbcommands.h" #include "commands/defrem.h" *************** *** 55,60 **** --- 58,64 ---- #include "commands/tablespace.h" #include "commands/trigger.h" #include "commands/typecmds.h" + #include "foreign/foreign.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" *************** *** 1105,1110 **** --- 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 **** --- 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 UserMappingRelationId: + Assert(object->objectSubId == 0); + return OCLASS_USER_MAPPING; } /* shouldn't get here */ *************** *** 2501,2506 **** --- 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(USERMAPPINGOID, + ObjectIdGetDatum(object->objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for user mapping %u", + object->objectId); + + useid = ((Form_pg_user_mapping) GETSTRUCT(tup))->umuser; + + 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, diff -x CVS -cdNr ../cvs-pgsql/src/backend/catalog/information_schema.sql ./src/backend/catalog/information_schema.sql *** ../cvs-pgsql/src/backend/catalog/information_schema.sql 2008-09-11 15:29:57.000000000 +0300 --- ./src/backend/catalog/information_schema.sql 2008-12-09 17:42:59.000000000 +0200 *************** *** 2313,2315 **** --- 2313,2455 ---- FROM data_type_privileges ); GRANT SELECT ON element_types TO PUBLIC; + + + -- SQL/MED views; these use section numbers from part 9 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_foreign_data_wrapper_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 um.oid, + CAST(COALESCE(u.rolname,'public') AS sql_identifier ) AS authorization_identifier, + s.foreign_server_catalog, + s.foreign_server_name + FROM pg_user_mapping um LEFT JOIN pg_authid u ON (u.oid = um.umuser), + _pg_foreign_servers s + WHERE s.oid = um.umserver; + + + /* + * 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; diff -x CVS -cdNr ../cvs-pgsql/src/backend/catalog/namespace.c ./src/backend/catalog/namespace.c *** ../cvs-pgsql/src/backend/catalog/namespace.c 2008-12-05 15:06:23.000000000 +0200 --- ./src/backend/catalog/namespace.c 2008-12-09 12:04:11.000000000 +0200 *************** *** 25,30 **** --- 25,32 ---- #include "catalog/pg_authid.h" #include "catalog/pg_conversion.h" #include "catalog/pg_conversion_fn.h" + #include "catalog/pg_foreign_data_wrapper.h" + #include "catalog/pg_foreign_server.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" *************** *** 200,205 **** --- 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_foreign_data_wrapper_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 **** --- 460,688 ---- /* + * ForeignDataWrapperNameGetId + * 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 **** --- 3560,3581 ---- } Datum + pg_foreign_data_wrapper_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); diff -x CVS -cdNr ../cvs-pgsql/src/backend/catalog/sql_features.txt ./src/backend/catalog/sql_features.txt *** ../cvs-pgsql/src/backend/catalog/sql_features.txt 2008-12-01 10:24:54.000000000 +0200 --- ./src/backend/catalog/sql_features.txt 2008-12-08 17:10:52.000000000 +0200 *************** *** 487,492 **** --- 487,517 ---- T653 SQL-schema statements in external routines NO T654 SQL-dynamic statements in external routines NO T655 Cyclically dependent routines NO + M001 Datalinks NO + M002 Datalinks via SQL/CLI NO + M003 Datalinks via Embedded SQL NO + M004 Foreign data support NO + M005 Foreign schema support NO + M006 GetSQLString routine NO + M007 TransmitRequest NO + M009 GetOpts and GetStatistics routines NO + M010 Foreign data wrapper support NO + M011 Datalinks via Ada NO + M012 Datalinks via C NO + M013 Datalinks via COBOL NO + M014 Datalinks via Fortran NO + M015 Datalinks via M NO + M016 Datalinks via Pascal NO + M017 Datalinks via PL/I NO + M018 Foreign data wrapper interface routines in Ada NO + M019 Foreign data wrapper interface routines in C NO + M020 Foreign data wrapper interface routines in COBOL NO + M021 Foreign data wrapper interface routines in Fortran NO + M022 Foreign data wrapper interface routines in MUMPS NO + M023 Foreign data wrapper interface routines in Pascal NO + M024 Foreign data wrapper interface routines in PL/I NO + M030 SQL-server foreign data support NO + M031 Foreign data wrapper general routines NO X010 XML type YES X011 Arrays of XML type YES X012 Multisets of XML type NO diff -x CVS -cdNr ../cvs-pgsql/src/backend/catalog/system_views.sql ./src/backend/catalog/system_views.sql *** ../cvs-pgsql/src/backend/catalog/system_views.sql 2008-11-13 10:44:31.000000000 +0200 --- ./src/backend/catalog/system_views.sql 2008-12-08 18:13:51.000000000 +0200 *************** *** 381,386 **** --- 381,405 ---- pg_stat_get_buf_written_backend() AS buffers_backend, pg_stat_get_buf_alloc() AS buffers_alloc; + CREATE VIEW pg_user_mappings AS + SELECT + U.oid AS umid, + S.oid AS srvid, + S.srvname AS srvname, + U.umuser AS umuser, + CASE WHEN U.umuser = 0 THEN + 'public' + ELSE + A.rolname + END AS usename + FROM pg_user_mapping U + LEFT JOIN pg_authid A ON (A.oid = U.umuser) JOIN + pg_foreign_server S ON (U.umserver = S.oid); + + REVOKE ALL on pg_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 diff -x CVS -cdNr ../cvs-pgsql/src/backend/commands/Makefile ./src/backend/commands/Makefile *** ../cvs-pgsql/src/backend/commands/Makefile 2008-02-19 12:30:07.000000000 +0200 --- ./src/backend/commands/Makefile 2008-12-08 14:03:34.000000000 +0200 *************** *** 14,20 **** 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 \ --- 14,20 ---- 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 foreigncmds.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 \ diff -x CVS -cdNr ../cvs-pgsql/src/backend/commands/alter.c ./src/backend/commands/alter.c *** ../cvs-pgsql/src/backend/commands/alter.c 2008-06-15 04:25:53.000000000 +0300 --- ./src/backend/commands/alter.c 2008-12-08 09:40:17.000000000 +0200 *************** *** 270,275 **** --- 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); diff -x CVS -cdNr ../cvs-pgsql/src/backend/commands/foreigncmds.c ./src/backend/commands/foreigncmds.c *** ../cvs-pgsql/src/backend/commands/foreigncmds.c 1970-01-01 02:00:00.000000000 +0200 --- ./src/backend/commands/foreigncmds.c 2008-12-09 17:57:16.000000000 +0200 *************** *** 0 **** --- 1,1157 ---- + /*------------------------------------------------------------------------- + * + * 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_foreign_data_wrapper.h" + #include "catalog/pg_foreign_server.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_type.h" + #include "catalog/pg_user_mapping.h" + #include "commands/defrem.h" + #include "foreign/foreign.h" + #include "miscadmin.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" + #include "utils/rel.h" + #include "utils/syscache.h" + + + /* + * Convert a DefElem list to the text array format that is used in + * pg_foreign_data_wrapper, pg_foreign_server, and pg_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("option \"%s\" provided more than once", + od->def->defname))); + resultOptions = lappend(resultOptions, od->def); + break; + + default: + elog(ERROR, "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 user name 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_foreign_data_wrapper_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_foreign_data_wrapper]; + char nulls[Natts_pg_foreign_data_wrapper]; + 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_foreign_data_wrapper); + + values[Anum_pg_foreign_data_wrapper_fdwname - 1] = + DirectFunctionCall1(namein, CStringGetDatum(fdwname)); + values[Anum_pg_foreign_data_wrapper_fdwnamespace - 1] = ObjectIdGetDatum(namespaceOid); + values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId); + values[Anum_pg_foreign_data_wrapper_fdwlibrary - 1] = CStringGetTextDatum(stmt->library); + nulls[Anum_pg_foreign_data_wrapper_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_foreign_data_wrapper_fdwoptions - 1] = fdwoptions; + else + nulls[Anum_pg_foreign_data_wrapper_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_foreign_data_wrapper]; + bool repl_null[Natts_pg_foreign_data_wrapper]; + bool repl_repl[Natts_pg_foreign_data_wrapper]; + 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, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + + if (stmt->library) + { + /* + * New library specified -- load to see if valid. + */ + fdwlib = GetForeignDataWrapperLibrary(stmt->library); + + repl_val[Anum_pg_foreign_data_wrapper_fdwlibrary - 1] = CStringGetTextDatum(stmt->library); + repl_repl[Anum_pg_foreign_data_wrapper_fdwlibrary - 1] = true; + + /* + * It could be that the options for the FDW, SERVER and USER MAPPING + * are no longer valid with the new library. Warn about this. + */ + ereport(WARNING, + (errmsg("changing the foreign-data wrapper library can cause " + "the options for dependent objects to become invalid"))); + } + else + { + /* + * No LIBRARY clause specified, but we need to load it for validating + * options. + */ + datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, + tp, + Anum_pg_foreign_data_wrapper_fdwlibrary, + &isnull); + fdwlib = GetForeignDataWrapperLibrary(TextDatumGetCString(datum)); + } + + /* + * Options specified, validate and update. + */ + if (stmt->options) + { + /* Extract the current options */ + datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, + tp, + Anum_pg_foreign_data_wrapper_fdwoptions, + &isnull); + + /* Transform the options */ + datum = transformGenericOptions(datum, stmt->options, FdwOpt, + NULL, fdwlib->validateOptionList); + + if (OidIsValid(datum)) + repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = ObjectIdGetDatum(datum); + else + repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; + + repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; + } + + /* Everything looks good - update the tuple */ + + rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock); + + tp = heap_modify_tuple(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_foreign_data_wrapper_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]; + bool repl_null[Natts_pg_foreign_server]; + bool 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, false, sizeof(repl_null)); + memset(repl_repl, false, 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] = true; + + repl_repl[Anum_pg_foreign_server_srvversion - 1] = true; + } + + 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] = true; + + repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true; + } + + /* Everything looks good - update the tuple */ + + rel = heap_open(ForeignServerRelationId, RowExclusiveLock); + + tp = heap_modify_tuple(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(USERMAPPINGUSERSERVER, + 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_user_mapping. + */ + rel = heap_open(UserMappingRelationId, RowExclusiveLock); + + MemSet(nulls, ' ', Natts_pg_user_mapping); + + values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId); + values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srvId); + + /* Add user options */ + useoptions = transformGenericOptions(0, stmt->options, UserMappingOpt, + fdw, fdw->lib->validateOptionList); + + if (OidIsValid(useoptions)) + values[Anum_pg_user_mapping_umoptions - 1] = useoptions; + else + nulls[Anum_pg_user_mapping_umoptions - 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 = UserMappingRelationId; + 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(UserMappingRelationId, umId, useId); + + heap_close(rel, NoLock); + } + + + /* + * Alter user mapping + */ + void + AlterUserMapping(AlterUserMappingStmt *stmt) + { + Relation rel; + HeapTuple tp; + Datum repl_val[Natts_pg_user_mapping]; + bool repl_null[Natts_pg_user_mapping]; + bool repl_repl[Natts_pg_user_mapping]; + Oid useId; + Oid srvId; + Oid umId; + + useId = GetUserOidFromMapping(stmt->username, false); + srvId = ForeignServerNameGetServerid(stmt->servername, false); + + umId = GetSysCacheOid(USERMAPPINGUSERSERVER, + 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(USERMAPPINGOID, + 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, false, sizeof(repl_null)); + memset(repl_repl, false, 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(USERMAPPINGUSERSERVER, + tp, + Anum_pg_user_mapping_umoptions, + &isnull); + + /* Prepare the options array */ + datum = transformGenericOptions(datum, stmt->options, UserMappingOpt, + fdw, fdw->lib->validateOptionList); + + if (OidIsValid(datum)) + repl_val[Anum_pg_user_mapping_umoptions - 1] = datum; + else + repl_null[Anum_pg_user_mapping_umoptions - 1] = true; + + repl_repl[Anum_pg_user_mapping_umoptions - 1] = true; + } + + /* Everything looks good - update the tuple */ + + rel = heap_open(UserMappingRelationId, RowExclusiveLock); + + tp = heap_modify_tuple(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(USERMAPPINGUSERSERVER, + 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 = UserMappingRelationId; + 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(UserMappingRelationId, RowExclusiveLock); + + tp = SearchSysCache(USERMAPPINGOID, + 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); + } diff -x CVS -cdNr ../cvs-pgsql/src/backend/foreign/Makefile ./src/backend/foreign/Makefile *** ../cvs-pgsql/src/backend/foreign/Makefile 1970-01-01 02:00:00.000000000 +0200 --- ./src/backend/foreign/Makefile 2008-12-09 17:45:38.000000000 +0200 *************** *** 0 **** --- 1,21 ---- + #------------------------------------------------------------------------- + # + # Makefile-- + # Makefile for foreign + # + # IDENTIFICATION + # $PostgreSQL$ + # + #------------------------------------------------------------------------- + + subdir = src/backend/foreign + top_builddir = ../../.. + include $(top_builddir)/src/Makefile.global + + OBJS= foreign.o + + include $(top_srcdir)/src/backend/common.mk + + clean distclean maintainer-clean: + $(MAKE) -C default $@ + $(MAKE) -C pgsql $@ diff -x CVS -cdNr ../cvs-pgsql/src/backend/foreign/default/Makefile ./src/backend/foreign/default/Makefile *** ../cvs-pgsql/src/backend/foreign/default/Makefile 1970-01-01 02:00:00.000000000 +0200 --- ./src/backend/foreign/default/Makefile 2008-12-09 17:50:49.000000000 +0200 *************** *** 0 **** --- 1,27 ---- + #------------------------------------------------------------------------- + # + # Makefile-- + # Makefile for default foreign-data wrapper + # + # IDENTIFICATION + # $PostgreSQL$ + # + #------------------------------------------------------------------------- + + subdir = src/backend/foreign/default + top_builddir = ../../../.. + include $(top_builddir)/src/Makefile.global + + NAME = default_fdw + OBJS = default_fdw.o + + include $(top_srcdir)/src/Makefile.shlib + + all: all-shared-lib + + install: all install-lib + + installdirs: installdirs-lib + + clean distclean maintainer-clean: clean-lib + rm -f $(OBJS) diff -x CVS -cdNr ../cvs-pgsql/src/backend/foreign/default/default_fdw.c ./src/backend/foreign/default/default_fdw.c *** ../cvs-pgsql/src/backend/foreign/default/default_fdw.c 1970-01-01 02:00:00.000000000 +0200 --- ./src/backend/foreign/default/default_fdw.c 2008-12-09 17:58:51.000000000 +0200 *************** *** 0 **** --- 1,85 ---- + /*------------------------------------------------------------------------- + * + * 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, + UserMapping *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; + } diff -x CVS -cdNr ../cvs-pgsql/src/backend/foreign/foreign.c ./src/backend/foreign/foreign.c *** ../cvs-pgsql/src/backend/foreign/foreign.c 1970-01-01 02:00:00.000000000 +0200 --- ./src/backend/foreign/foreign.c 2008-12-09 17:58:35.000000000 +0200 *************** *** 0 **** --- 1,480 ---- + /*------------------------------------------------------------------------- + * + * 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_foreign_data_wrapper.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_foreign_data_wrapper_fdwlibrary, + &isnull); + fdw->fdwlibrary = pstrdup(TextDatumGetCString(datum)); + + fdw->lib = GetForeignDataWrapperLibrary(fdw->fdwlibrary); + + /* Extract the useoptions */ + datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, + tp, + Anum_pg_foreign_data_wrapper_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; + } + + /* + * GetUserMapping - look up the user mapping. + * + * If no mapping is found for the supplied user we also look for PUBLIC + * mappings (usesysid == InvalidOid). + */ + UserMapping * + GetUserMapping(Oid userid, Oid serverid) + { + Form_pg_user_mapping umform; + Datum datum; + HeapTuple tp; + bool isnull; + UserMapping *um; + + tp = SearchSysCache(USERMAPPINGUSERSERVER, + ObjectIdGetDatum(userid), + ObjectIdGetDatum(serverid), + 0, 0); + + if (!HeapTupleIsValid(tp)) + { + /* Not found for the specific user -- try PUBLIC */ + tp = SearchSysCache(USERMAPPINGUSERSERVER, + 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_user_mapping) GETSTRUCT(tp); + + /* Extract the umoptions */ + datum = SysCacheGetAttr(USERMAPPINGUSERSERVER, + tp, + Anum_pg_user_mapping_umoptions, + &isnull); + + um = palloc(sizeof(UserMapping)); + 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; + UserMapping *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 = GetUserMapping(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; + UserMapping *um; + List *umoptions = NIL; + + tp = SearchSysCache(USERMAPPINGOID, + ObjectIdGetDatum(umId), + 0, 0, 0); + + if (!HeapTupleIsValid(tp)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("user mapping %u not found", umId))); + + srvId = ((Form_pg_user_mapping) GETSTRUCT(tp))->umserver; + useId = ((Form_pg_user_mapping) GETSTRUCT(tp))->umuser; + + ReleaseSysCache(tp); + + um = GetUserMapping(useId, srvId); + + /* Server owner -- OK to show user mapping options */ + if (pg_foreign_server_ownercheck(srvId, GetUserId())) + umoptions = um->options; + + deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, umoptions); + + return (Datum) 0; + } diff -x CVS -cdNr ../cvs-pgsql/src/backend/foreign/pgsql/Makefile ./src/backend/foreign/pgsql/Makefile *** ../cvs-pgsql/src/backend/foreign/pgsql/Makefile 1970-01-01 02:00:00.000000000 +0200 --- ./src/backend/foreign/pgsql/Makefile 2008-12-09 17:51:40.000000000 +0200 *************** *** 0 **** --- 1,27 ---- + #------------------------------------------------------------------------- + # + # Makefile-- + # Makefile for pgsql foreign-data wrapper + # + # IDENTIFICATION + # $PostgreSQL$ + # + #------------------------------------------------------------------------- + + subdir = src/backend/foreign/pgsql + top_builddir = ../../../.. + include $(top_builddir)/src/Makefile.global + + NAME = pgsql_fdw + OBJS = pgsql_fdw.o + + include $(top_srcdir)/src/Makefile.shlib + + all: all-shared-lib + + install: all install-lib + + installdirs: installdirs-lib + + clean distclean maintainer-clean: clean-lib + rm -f $(OBJS) diff -x CVS -cdNr ../cvs-pgsql/src/backend/foreign/pgsql/pgsql_fdw.c ./src/backend/foreign/pgsql/pgsql_fdw.c *** ../cvs-pgsql/src/backend/foreign/pgsql/pgsql_fdw.c 1970-01-01 02:00:00.000000000 +0200 --- ./src/backend/foreign/pgsql/pgsql_fdw.c 2008-12-09 17:59:33.000000000 +0200 *************** *** 0 **** --- 1,197 ---- + /*------------------------------------------------------------------------- + * + * 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, + UserMapping *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; + } diff -x CVS -cdNr ../cvs-pgsql/src/backend/nodes/copyfuncs.c ./src/backend/nodes/copyfuncs.c *** ../cvs-pgsql/src/backend/nodes/copyfuncs.c 2008-12-05 15:06:23.000000000 +0200 --- ./src/backend/nodes/copyfuncs.c 2008-12-08 09:40:17.000000000 +0200 *************** *** 2033,2038 **** --- 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 **** --- 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 **** --- 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 **** --- 3972,3980 ---- case T_DefElem: retval = _copyDefElem(from); break; + case T_OptionDefElem: + retval = _copyOptionDefElem(from); + break; case T_LockingClause: retval = _copyLockingClause(from); break; diff -x CVS -cdNr ../cvs-pgsql/src/backend/nodes/equalfuncs.c ./src/backend/nodes/equalfuncs.c *** ../cvs-pgsql/src/backend/nodes/equalfuncs.c 2008-12-05 15:06:23.000000000 +0200 --- ./src/backend/nodes/equalfuncs.c 2008-12-08 09:40:17.000000000 +0200 *************** *** 1519,1524 **** --- 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 **** --- 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 **** --- 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 **** --- 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; diff -x CVS -cdNr ../cvs-pgsql/src/backend/nodes/makefuncs.c ./src/backend/nodes/makefuncs.c *** ../cvs-pgsql/src/backend/nodes/makefuncs.c 2008-09-02 15:10:44.000000000 +0300 --- ./src/backend/nodes/makefuncs.c 2008-12-08 13:51:47.000000000 +0200 *************** *** 361,363 **** --- 361,376 ---- res->arg = arg; return res; } + + /* + * makeOptionDefElem - + * build an OptionDefElem node + */ + OptionDefElem * + makeOptionDefElem(int op, DefElem *def) + { + OptionDefElem *res = makeNode(OptionDefElem); + res->alter_op = op; + res->def = def; + return res; + } diff -x CVS -cdNr ../cvs-pgsql/src/backend/parser/gram.y ./src/backend/parser/gram.y *** ../cvs-pgsql/src/backend/parser/gram.y 2008-12-05 15:06:24.000000000 +0200 --- ./src/backend/parser/gram.y 2008-12-09 12:43:22.000000000 +0200 *************** *** 156,161 **** --- 156,162 ---- FunctionParameterMode fun_param_mode; FuncWithArgs *funwithargs; DefElem *defelt; + OptionDefElem *optdef; SortBy *sortby; JoinExpr *jexpr; IndexElem *ielem; *************** *** 172,190 **** } %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 **** --- 226,235 ---- %type OptRoleList %type OptRoleElem + %type opt_type + %type foreign_server_version opt_foreign_server_version + %type auth_ident + %type OptSchemaName %type OptSchemaEltList *************** *** 274,279 **** --- 282,288 ---- prep_type_clause execute_param_clause using_clause returning_clause enum_val_list table_func_column_list + create_generic_options alter_generic_options %type OptTempTableName %type into_clause create_as_target *************** *** 342,347 **** --- 351,362 ---- %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 **** 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 --- 451,457 ---- 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 **** 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 --- 460,466 ---- 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 **** 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 --- 474,480 ---- 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 **** 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 --- 489,495 ---- 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 *************** *** 562,567 **** --- 577,584 ---- AlterDatabaseStmt | AlterDatabaseSetStmt | AlterDomainStmt + | AlterFdwStmt + | AlterForeignServerStmt | AlterFunctionStmt | AlterGroupStmt | AlterObjectSchemaStmt *************** *** 572,577 **** --- 589,595 ---- | AlterRoleStmt | AlterTSConfigurationStmt | AlterTSDictionaryStmt + | AlterUserMappingStmt | AlterUserSetStmt | AlterUserStmt | AnalyzeStmt *************** *** 586,591 **** --- 604,611 ---- | CreateCastStmt | CreateConversionStmt | CreateDomainStmt + | CreateFdwStmt + | CreateForeignServerStmt | CreateFunctionStmt | CreateGroupStmt | CreateOpClassStmt *************** *** 599,604 **** --- 619,625 ---- | CreateTrigStmt | CreateRoleStmt | CreateUserStmt + | CreateUserMappingStmt | CreatedbStmt | DeallocateStmt | DeclareCursorStmt *************** *** 607,612 **** --- 628,635 ---- | DiscardStmt | DropAssertStmt | DropCastStmt + | DropFdwStmt + | DropForeignServerStmt | DropGroupStmt | DropOpClassStmt | DropOpFamilyStmt *************** *** 618,623 **** --- 641,647 ---- | DropTrigStmt | DropRoleStmt | DropUserStmt + | DropUserMappingStmt | DropdbStmt | ExecuteStmt | ExplainStmt *************** *** 2717,2722 **** --- 2741,3050 ---- /***************************************************************************** * + * QUERY: + * CREATE FOREIGN DATA WRAPPER any_name LIBRARY 'library_name' LANGUAGE C + * + *****************************************************************************/ + + CreateFdwStmt: CREATE FOREIGN DATA_P WRAPPER any_name LIBRARY Sconst LANGUAGE ColId create_generic_options + { + CreateFdwStmt *n = makeNode(CreateFdwStmt); + n->fdwname = $5; + n->library = $7; + n->options = $10; + $$ = (Node *) n; + + if (pg_strcasecmp($9, "C") != 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("language for foreign-data wrapper must be C"), + scanner_errposition(@9))); + } + ; + + /***************************************************************************** + * + * 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 alter_generic_options + { + 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 alter_generic_options + { + AlterFdwStmt *n = makeNode(AlterFdwStmt); + n->fdwname = $5; + n->options = $6; + $$ = (Node *) n; + } + ; + + /* Options definition for CREATE FDW, SERVER and USER MAPPING */ + create_generic_options: + 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 */ + alter_generic_options: + 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 opt_type opt_foreign_server_version + FOREIGN DATA_P WRAPPER any_name create_generic_options + { + CreateForeignServerStmt *n = makeNode(CreateForeignServerStmt); + n->servername = $3; + n->servertype = $4; + n->version = $5; + n->fdwname = $9; + n->options = $10; + $$ = (Node *) n; + } + ; + + opt_type: + TYPE_P Sconst { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + + + foreign_server_version: + VERSION_P Sconst { $$ = $2; } + | VERSION_P NULL_P { $$ = NULL; } + ; + + opt_foreign_server_version: + foreign_server_version { $$ = $1; } + | /*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 foreign_server_version alter_generic_options + { + AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt); + n->servername = $3; + n->version = $4; + n->options = $5; + n->has_version = true; + $$ = (Node *) n; + } + | ALTER SERVER any_name foreign_server_version + { + AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt); + n->servername = $3; + n->version = $4; + n->has_version = true; + $$ = (Node *) n; + } + | ALTER SERVER any_name alter_generic_options + { + AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt); + n->servername = $3; + n->options = $4; + $$ = (Node *) n; + } + ; + + /***************************************************************************** + * + * QUERY: + * CREATE USER MAPPING FOR SERVER [OPTIONS] + * + *****************************************************************************/ + + CreateUserMappingStmt: CREATE USER MAPPING FOR auth_ident SERVER any_name create_generic_options + { + CreateUserMappingStmt *n = makeNode(CreateUserMappingStmt); + n->username = $5; + n->servername = $7; + n->options = $8; + $$ = (Node *) n; + } + ; + + /* User mapping authorization identifier */ + auth_ident: + 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 auth_ident 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 auth_ident 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 auth_ident SERVER any_name alter_generic_options + { + AlterUserMappingStmt *n = makeNode(AlterUserMappingStmt); + n->username = $5; + n->servername = $7; + n->options = $8; + $$ = (Node *) n; + } + ; + + /***************************************************************************** + * * QUERIES : * CREATE TRIGGER ... * DROP TRIGGER ... *************** *** 3912,3917 **** --- 4240,4259 ---- n->objs = $2; $$ = n; } + | FOREIGN DATA_P WRAPPER any_name_list + { + PrivTarget *n = makeNode(PrivTarget); + n->objtype = ACL_OBJECT_FDW; + n->objs = $4; + $$ = n; + } + | FOREIGN SERVER any_name_list + { + PrivTarget *n = makeNode(PrivTarget); + n->objtype = ACL_OBJECT_FOREIGN_SERVER; + n->objs = $3; + $$ = n; + } | FUNCTION function_with_argtypes_list { PrivTarget *n = makeNode(PrivTarget); *************** *** 5122,5127 **** --- 5464,5485 ---- 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 **** --- 9913,9919 ---- | INVOKER | ISOLATION | KEY + | LIBRARY | LANCOMPILER | LANGUAGE | LARGE_P *************** *** 9593,9598 **** --- 9952,9958 ---- | OIDS | OPERATOR | OPTION + | OPTIONS | OWNED | OWNER | PARSER *************** *** 9628,9633 **** --- 9988,9994 ---- | ROWS | RULE | SAVEPOINT + | SERVER | SCHEMA | SCROLL | SEARCH *************** *** 9680,9685 **** --- 10041,10047 ---- | WHITESPACE_P | WITHOUT | WORK + | WRAPPER | WRITE | XML_P | YEAR_P diff -x CVS -cdNr ../cvs-pgsql/src/backend/parser/keywords.c ./src/backend/parser/keywords.c *** ../cvs-pgsql/src/backend/parser/keywords.c 2008-10-28 14:31:25.000000000 +0200 --- ./src/backend/parser/keywords.c 2008-12-08 09:40:17.000000000 +0200 *************** *** 229,234 **** --- 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 **** --- 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 **** --- 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 **** --- 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}, diff -x CVS -cdNr ../cvs-pgsql/src/backend/tcop/utility.c ./src/backend/tcop/utility.c *** ../cvs-pgsql/src/backend/tcop/utility.c 2008-12-05 15:06:25.000000000 +0200 --- ./src/backend/tcop/utility.c 2008-12-08 09:40:17.000000000 +0200 *************** *** 203,208 **** --- 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 **** --- 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 **** --- 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 **** --- 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 **** --- 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; diff -x CVS -cdNr ../cvs-pgsql/src/backend/utils/adt/acl.c ./src/backend/utils/adt/acl.c *** ../cvs-pgsql/src/backend/utils/adt/acl.c 2008-09-11 15:30:00.000000000 +0300 --- ./src/backend/utils/adt/acl.c 2008-12-08 09:40:17.000000000 +0200 *************** *** 577,582 **** --- 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 */ diff -x CVS -cdNr ../cvs-pgsql/src/backend/utils/cache/syscache.c ./src/backend/utils/cache/syscache.c *** ../cvs-pgsql/src/backend/utils/cache/syscache.c 2008-06-19 03:46:05.000000000 +0300 --- ./src/backend/utils/cache/syscache.c 2008-12-09 10:40:03.000000000 +0200 *************** *** 32,37 **** --- 32,39 ---- #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" #include "catalog/pg_enum.h" + #include "catalog/pg_foreign_data_wrapper.h" + #include "catalog/pg_foreign_server.h" #include "catalog/pg_language.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" *************** *** 46,51 **** --- 48,54 ---- #include "catalog/pg_ts_parser.h" #include "catalog/pg_ts_template.h" #include "catalog/pg_type.h" + #include "catalog/pg_user_mapping.h" #include "utils/rel.h" #include "utils/syscache.h" *************** *** 365,370 **** --- 368,421 ---- }, 256 }, + {ForeignDataWrapperRelationId, /* FOREIGNDATAWRAPPEROID */ + ForeignDataWrapperOidIndexId, + 0, + 1, + { + ObjectIdAttributeNumber, + 0, + 0, + 0 + }, + 8 + }, + {ForeignDataWrapperRelationId, /* FOREIGNDATAWRAPPERNAMENSP */ + ForeignDataWrapperNameNspIndexId, + 0, + 2, + { + Anum_pg_foreign_data_wrapper_fdwname, + Anum_pg_foreign_data_wrapper_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 + }, {IndexRelationId, /* INDEXRELID */ IndexRelidIndexId, Anum_pg_index_indrelid, *************** *** 676,681 **** --- 727,756 ---- 0 }, 1024 + }, + {UserMappingRelationId, /* USERMAPPINGOID */ + UserMappingOidIndexId, + 0, + 1, + { + ObjectIdAttributeNumber, + 0, + 0, + 0 + }, + 128 + }, + {UserMappingRelationId, /* USERMAPPINGUSERSERVER */ + UserMappingUserServerIndexId, + 0, + 2, + { + Anum_pg_user_mapping_umuser, + Anum_pg_user_mapping_umserver, + 0, + 0 + }, + 128 } }; diff -x CVS -cdNr ../cvs-pgsql/src/bin/pg_dump/common.c ./src/bin/pg_dump/common.c *** ../cvs-pgsql/src/bin/pg_dump/common.c 2008-05-10 02:32:04.000000000 +0300 --- ./src/bin/pg_dump/common.c 2008-12-09 11:56:22.000000000 +0200 *************** *** 91,96 **** --- 91,98 ---- TSTemplateInfo *tmplinfo; TSDictInfo *dictinfo; TSConfigInfo *cfginfo; + FdwInfo *fdwinfo; + ForeignServerInfo *srvinfo; int numNamespaces; int numAggregates; int numInherits; *************** *** 104,109 **** --- 106,113 ---- int numTSTemplates; int numTSDicts; int numTSConfigs; + int numForeignDataWrappers; + int numForeignServers; if (g_verbose) write_msg(NULL, "reading schemas\n"); *************** *** 155,160 **** --- 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); diff -x CVS -cdNr ../cvs-pgsql/src/bin/pg_dump/dumputils.c ./src/bin/pg_dump/dumputils.c *** ../cvs-pgsql/src/bin/pg_dump/dumputils.c 2008-09-11 15:30:02.000000000 +0300 --- ./src/bin/pg_dump/dumputils.c 2008-12-08 09:40:17.000000000 +0200 *************** *** 687,692 **** --- 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(); diff -x CVS -cdNr ../cvs-pgsql/src/bin/pg_dump/pg_backup_archiver.c ./src/bin/pg_dump/pg_backup_archiver.c *** ../cvs-pgsql/src/bin/pg_dump/pg_backup_archiver.c 2008-09-11 15:30:02.000000000 +0300 --- ./src/bin/pg_dump/pg_backup_archiver.c 2008-12-08 09:40:17.000000000 +0200 *************** *** 2455,2461 **** 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 **** 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(); diff -x CVS -cdNr ../cvs-pgsql/src/bin/pg_dump/pg_dump.c ./src/bin/pg_dump/pg_dump.c *** ../cvs-pgsql/src/bin/pg_dump/pg_dump.c 2008-12-05 15:06:28.000000000 +0200 --- ./src/bin/pg_dump/pg_dump.c 2008-12-09 12:51:44.000000000 +0200 *************** *** 158,163 **** --- 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 **** --- 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 **** --- 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 **** --- 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' LANGUAGE C", + 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_mappings 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 diff -x CVS -cdNr ../cvs-pgsql/src/bin/pg_dump/pg_dump.h ./src/bin/pg_dump/pg_dump.h *** ../cvs-pgsql/src/bin/pg_dump/pg_dump.h 2008-11-13 10:44:41.000000000 +0200 --- ./src/bin/pg_dump/pg_dump.h 2008-12-08 09:40:18.000000000 +0200 *************** *** 131,136 **** --- 131,138 ---- DO_TSDICT, DO_TSTEMPLATE, DO_TSCONFIG, + DO_FDW, + DO_FOREIGN_SERVER, DO_BLOBS, DO_BLOB_COMMENTS } DumpableObjectType; *************** *** 418,423 **** --- 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 **** --- 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 */ diff -x CVS -cdNr ../cvs-pgsql/src/bin/pg_dump/pg_dump_sort.c ./src/bin/pg_dump/pg_dump_sort.c *** ../cvs-pgsql/src/bin/pg_dump/pg_dump_sort.c 2008-09-11 15:30:02.000000000 +0300 --- ./src/bin/pg_dump/pg_dump_sort.c 2008-12-08 09:40:18.000000000 +0200 *************** *** 50,55 **** --- 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 **** --- 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 **** --- 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)", diff -x CVS -cdNr ../cvs-pgsql/src/bin/psql/command.c ./src/bin/psql/command.c *** ../cvs-pgsql/src/bin/psql/command.c 2008-11-24 09:49:53.000000000 +0200 --- ./src/bin/psql/command.c 2008-12-08 09:40:18.000000000 +0200 *************** *** 416,421 **** --- 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; diff -x CVS -cdNr ../cvs-pgsql/src/bin/psql/describe.c ./src/bin/psql/describe.c *** ../cvs-pgsql/src/bin/psql/describe.c 2008-11-13 10:44:41.000000000 +0200 --- ./src/bin/psql/describe.c 2008-12-09 12:04:39.000000000 +0200 *************** *** 2766,2768 **** --- 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_foreign_data_wrapper_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_mappings um ON s.oid=um.srvid\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; + } diff -x CVS -cdNr ../cvs-pgsql/src/bin/psql/describe.h ./src/bin/psql/describe.h *** ../cvs-pgsql/src/bin/psql/describe.h 2008-01-01 21:45:56.000000000 +0200 --- ./src/bin/psql/describe.h 2008-12-08 09:40:18.000000000 +0200 *************** *** 66,70 **** --- 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 */ diff -x CVS -cdNr ../cvs-pgsql/src/bin/psql/help.c ./src/bin/psql/help.c *** ../cvs-pgsql/src/bin/psql/help.c 2008-11-13 10:44:41.000000000 +0200 --- ./src/bin/psql/help.c 2008-12-09 11:59:11.000000000 +0200 *************** *** 209,220 **** --- 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"); diff -x CVS -cdNr ../cvs-pgsql/src/bin/psql/tab-complete.c ./src/bin/psql/tab-complete.c *** ../cvs-pgsql/src/bin/psql/tab-complete.c 2008-11-21 11:46:51.000000000 +0200 --- ./src/bin/psql/tab-complete.c 2008-12-09 12:10:22.000000000 +0200 *************** *** 346,351 **** --- 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_foreign_data_wrapper_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 **** --- 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 **** --- 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 **** --- 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 **** --- 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 **** 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); } *************** *** 1640,1645 **** --- 1674,1681 ---- pg_strcasecmp(prev_wd, "ON") == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, " UNION SELECT 'DATABASE'" + " UNION SELECT 'FOREIGN DATA WRAPPER'" + " UNION SELECT 'FOREIGN SERVER'" " UNION SELECT 'FUNCTION'" " UNION SELECT 'LANGUAGE'" " UNION SELECT 'SCHEMA'" *************** *** 2075,2080 **** --- 2111,2124 ---- 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 **** --- 2148,2155 ---- 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 **** --- 2162,2169 ---- 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) diff -x CVS -cdNr ../cvs-pgsql/src/include/catalog/catversion.h ./src/include/catalog/catversion.h *** ../cvs-pgsql/src/include/catalog/catversion.h 2008-12-05 15:06:29.000000000 +0200 --- ./src/include/catalog/catversion.h 2008-12-08 15:28:11.000000000 +0200 *************** *** 4,10 **** * "Catalog version number" for PostgreSQL. * * The catalog version number is used to flag incompatible changes in ! * the PostgreSQL system catalogs. Whenever anyone changes the format of * a system catalog relation, or adds, deletes, or modifies standard * catalog entries in such a way that an updated backend wouldn't work * with an old database (or vice versa), the catalog version number --- 4,10 ---- * "Catalog version number" for PostgreSQL. * * The catalog version number is used to flag incompatible changes in ! * the PostgreSQL system catalogs. Whenever anyone changes the format of * a system catalog relation, or adds, deletes, or modifies standard * catalog entries in such a way that an updated backend wouldn't work * with an old database (or vice versa), the catalog version number *************** *** 53,58 **** */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 200812041 #endif --- 53,58 ---- */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 200812081 #endif diff -x CVS -cdNr ../cvs-pgsql/src/include/catalog/dependency.h ./src/include/catalog/dependency.h *** ../cvs-pgsql/src/include/catalog/dependency.h 2008-06-09 01:41:04.000000000 +0300 --- ./src/include/catalog/dependency.h 2008-12-09 09:49:34.000000000 +0200 *************** *** 143,148 **** --- 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_user_mapping */ MAX_OCLASS /* MUST BE LAST */ } ObjectClass; diff -x CVS -cdNr ../cvs-pgsql/src/include/catalog/indexing.h ./src/include/catalog/indexing.h *** ../cvs-pgsql/src/include/catalog/indexing.h 2008-06-19 03:46:06.000000000 +0300 --- ./src/include/catalog/indexing.h 2008-12-08 17:58:46.000000000 +0200 *************** *** 252,257 **** --- 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_user_mapping_oid_index, 174, on pg_user_mapping using btree(oid oid_ops)); + #define UserMappingOidIndexId 174 + + DECLARE_UNIQUE_INDEX(pg_user_mapping_user_server_index, 175, on pg_user_mapping using btree(umuser oid_ops, umserver oid_ops)); + #define UserMappingUserServerIndexId 175 + /* last step of initialization script: build the indexes declared above */ BUILD_INDICES diff -x CVS -cdNr ../cvs-pgsql/src/include/catalog/namespace.h ./src/include/catalog/namespace.h *** ../cvs-pgsql/src/include/catalog/namespace.h 2008-12-05 15:06:29.000000000 +0200 --- ./src/include/catalog/namespace.h 2008-12-08 09:40:18.000000000 +0200 *************** *** 50,55 **** --- 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); diff -x CVS -cdNr ../cvs-pgsql/src/include/catalog/pg_foreign_data_wrapper.h ./src/include/catalog/pg_foreign_data_wrapper.h *** ../cvs-pgsql/src/include/catalog/pg_foreign_data_wrapper.h 1970-01-01 02:00:00.000000000 +0200 --- ./src/include/catalog/pg_foreign_data_wrapper.h 2008-12-09 11:59:52.000000000 +0200 *************** *** 0 **** --- 1,74 ---- + /*------------------------------------------------------------------------- + * + * pg_foreign_data_wrapper.h + * definition of the system "foreign-data wrapper" relation (pg_foreign_data_wrapper) + * 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_FOREIGN_DATA_WRAPPER_H + #define PG_FOREIGN_DATA_WRAPPER_H + + #include "catalog/genbki.h" + + /* ---------------- + * pg_foreign_data_wrapper 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]; /* FDW 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_foreign_data_wrapper 6 + #define Anum_pg_foreign_data_wrapper_fdwname 1 + #define Anum_pg_foreign_data_wrapper_fdwnamespace 2 + #define Anum_pg_foreign_data_wrapper_fdwowner 3 + #define Anum_pg_foreign_data_wrapper_fdwlibrary 4 + #define Anum_pg_foreign_data_wrapper_fdwacl 5 + #define Anum_pg_foreign_data_wrapper_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_FOREIGN_DATA_WRAPPER_H */ diff -x CVS -cdNr ../cvs-pgsql/src/include/catalog/pg_foreign_server.h ./src/include/catalog/pg_foreign_server.h *** ../cvs-pgsql/src/include/catalog/pg_foreign_server.h 1970-01-01 02:00:00.000000000 +0200 --- ./src/include/catalog/pg_foreign_server.h 2008-12-08 15:32:25.000000000 +0200 *************** *** 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]; /* FDW-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 */ diff -x CVS -cdNr ../cvs-pgsql/src/include/catalog/pg_proc.h ./src/include/catalog/pg_proc.h *** ../cvs-pgsql/src/include/catalog/pg_proc.h 2008-12-05 15:06:30.000000000 +0200 --- ./src/include/catalog/pg_proc.h 2008-12-09 12:05:34.000000000 +0200 *************** *** 3198,3203 **** --- 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_foreign_data_wrapper_is_visible PGNSP PGUID 12 1 0 0 f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_foreign_data_wrapper_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 **** --- 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 diff -x CVS -cdNr ../cvs-pgsql/src/include/catalog/pg_user_mapping.h ./src/include/catalog/pg_user_mapping.h *** ../cvs-pgsql/src/include/catalog/pg_user_mapping.h 1970-01-01 02:00:00.000000000 +0200 --- ./src/include/catalog/pg_user_mapping.h 2008-12-08 17:45:50.000000000 +0200 *************** *** 0 **** --- 1,58 ---- + /*------------------------------------------------------------------------- + * + * pg_user_mapping.h + * definition of the system "user mapping" relation (pg_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 UserMappingRelationId 1418 + + CATALOG(pg_user_mapping,1418) + { + Oid umuser; /* Id of the user, InvalidOid if PUBLIC is wanted */ + Oid umserver; /* server of this mapping */ + + /* + * VARIABLE LENGTH FIELDS start here. These fields may be NULL, too. + */ + + text umoptions[1]; /* user mapping options */ + } FormData_pg_user_mapping; + + /* ---------------- + * Form_pg_user_mapping corresponds to a pointer to a tuple with + * the format of pg_user_mapping relation. + * ---------------- + */ + typedef FormData_pg_user_mapping *Form_pg_user_mapping; + + /* ---------------- + * compiler constants for pg_user_mapping + * ---------------- + */ + + #define Natts_pg_user_mapping 3 + #define Anum_pg_user_mapping_umuser 1 + #define Anum_pg_user_mapping_umserver 2 + #define Anum_pg_user_mapping_umoptions 3 + + #endif /* PG_USER_MAPPING_H */ diff -x CVS -cdNr ../cvs-pgsql/src/include/commands/defrem.h ./src/include/commands/defrem.h *** ../cvs-pgsql/src/include/commands/defrem.h 2008-12-05 15:06:30.000000000 +0200 --- ./src/include/commands/defrem.h 2008-12-08 09:40:18.000000000 +0200 *************** *** 118,123 **** --- 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); diff -x CVS -cdNr ../cvs-pgsql/src/include/foreign/foreign.h ./src/include/foreign/foreign.h *** ../cvs-pgsql/src/include/foreign/foreign.h 1970-01-01 02:00:00.000000000 +0200 --- ./src/include/foreign/foreign.h 2008-12-09 12:00:36.000000000 +0200 *************** *** 0 **** --- 1,102 ---- + /*------------------------------------------------------------------------- + * + * 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 UserMapping + { + Oid userid; /* local user Oid */ + Oid serverid; /* server Oid */ + List *options; /* useoptions as DefElem list */ + } UserMapping; + + + /* + * Foreign-data wrapper library function types. + */ + typedef void (*OptionListValidatorFunc)(ForeignDataWrapper *, + GenericOptionFlags, + List *); + typedef List * (*GetConnectionInfoFunc)(ForeignDataWrapper *, + ForeignServer *, + UserMapping *); + + /* + * 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 UserMapping *GetUserMapping(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 */ diff -x CVS -cdNr ../cvs-pgsql/src/include/nodes/makefuncs.h ./src/include/nodes/makefuncs.h *** ../cvs-pgsql/src/include/nodes/makefuncs.h 2008-09-02 15:10:59.000000000 +0300 --- ./src/include/nodes/makefuncs.h 2008-12-08 09:40:18.000000000 +0200 *************** *** 67,70 **** --- 67,72 ---- extern DefElem *makeDefElem(char *name, Node *arg); + extern OptionDefElem *makeOptionDefElem(int op, DefElem *def); + #endif /* MAKEFUNC_H */ diff -x CVS -cdNr ../cvs-pgsql/src/include/nodes/nodes.h ./src/include/nodes/nodes.h *** ../cvs-pgsql/src/include/nodes/nodes.h 2008-11-24 09:49:54.000000000 +0200 --- ./src/include/nodes/nodes.h 2008-12-08 09:40:18.000000000 +0200 *************** *** 325,330 **** --- 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 **** --- 357,363 ---- T_IndexElem, T_Constraint, T_DefElem, + T_OptionDefElem, T_RangeTblEntry, T_SortGroupClause, T_FkConstraint, diff -x CVS -cdNr ../cvs-pgsql/src/include/nodes/parsenodes.h ./src/include/nodes/parsenodes.h *** ../cvs-pgsql/src/include/nodes/parsenodes.h 2008-12-08 09:11:13.000000000 +0200 --- ./src/include/nodes/parsenodes.h 2008-12-09 12:01:07.000000000 +0200 *************** *** 49,54 **** --- 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 **** #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, FDWs, 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 **** --- 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 *************** *** 930,935 **** --- 948,955 ---- OBJECT_CONVERSION, OBJECT_DATABASE, OBJECT_DOMAIN, + OBJECT_FDW, + OBJECT_FOREIGN_SERVER, OBJECT_FUNCTION, OBJECT_INDEX, OBJECT_LANGUAGE, *************** *** 1076,1081 **** --- 1096,1103 ---- ACL_OBJECT_RELATION, /* table, view */ ACL_OBJECT_SEQUENCE, /* sequence */ ACL_OBJECT_DATABASE, /* database */ + ACL_OBJECT_FDW, /* foreign-data wrapper */ + ACL_OBJECT_FOREIGN_SERVER, /* foreign server */ ACL_OBJECT_FUNCTION, /* function */ ACL_OBJECT_LANGUAGE, /* procedural language */ ACL_OBJECT_NAMESPACE, /* namespace */ *************** *** 1329,1334 **** --- 1351,1446 ---- } 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 * ---------------------- */ diff -x CVS -cdNr ../cvs-pgsql/src/include/utils/acl.h ./src/include/utils/acl.h *** ../cvs-pgsql/src/include/utils/acl.h 2008-09-11 15:30:05.000000000 +0300 --- ./src/include/utils/acl.h 2008-12-09 10:35:39.000000000 +0200 *************** *** 146,151 **** --- 146,153 ---- #define ACL_ALL_RIGHTS_RELATION (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_DELETE|ACL_TRUNCATE|ACL_REFERENCES|ACL_TRIGGER) #define ACL_ALL_RIGHTS_SEQUENCE (ACL_USAGE|ACL_SELECT|ACL_UPDATE) #define ACL_ALL_RIGHTS_DATABASE (ACL_CREATE|ACL_CREATE_TEMP|ACL_CONNECT) + #define ACL_ALL_RIGHTS_FDW (ACL_USAGE) + #define ACL_ALL_RIGHTS_FOREIGN_SERVER (ACL_USAGE) #define ACL_ALL_RIGHTS_FUNCTION (ACL_EXECUTE) #define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE) #define ACL_ALL_RIGHTS_NAMESPACE (ACL_USAGE|ACL_CREATE) *************** *** 184,189 **** --- 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 **** --- 265,274 ---- AclMode mask, AclMaskHow how); extern AclMode pg_tablespace_aclmask(Oid spc_oid, Oid roleid, AclMode mask, AclMaskHow how); + extern AclMode pg_foreign_data_wrapper_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 **** --- 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_foreign_data_wrapper_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 **** --- 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 */ diff -x CVS -cdNr ../cvs-pgsql/src/include/utils/syscache.h ./src/include/utils/syscache.h *** ../cvs-pgsql/src/include/utils/syscache.h 2008-05-07 04:04:49.000000000 +0300 --- ./src/include/utils/syscache.h 2008-12-08 17:56:16.000000000 +0200 *************** *** 51,56 **** --- 51,60 ---- DATABASEOID, ENUMOID, ENUMTYPOIDNAME, + FOREIGNDATAWRAPPEROID, + FOREIGNDATAWRAPPERNAMENSP, + FOREIGNSERVEROID, + FOREIGNSERVERNAMENSP, INDEXRELID, LANGNAME, LANGOID, *************** *** 76,82 **** TSTEMPLATENAMENSP, TSTEMPLATEOID, TYPENAMENSP, ! TYPEOID }; extern void InitCatalogCache(void); --- 80,88 ---- TSTEMPLATENAMENSP, TSTEMPLATEOID, TYPENAMENSP, ! TYPEOID, ! USERMAPPINGOID, ! USERMAPPINGUSERSERVER }; extern void InitCatalogCache(void); diff -x CVS -cdNr ../cvs-pgsql/src/test/regress/expected/foreign_data.out ./src/test/regress/expected/foreign_data.out *** ../cvs-pgsql/src/test/regress/expected/foreign_data.out 1970-01-01 02:00:00.000000000 +0200 --- ./src/test/regress/expected/foreign_data.out 2008-12-09 12:53:03.000000000 +0200 *************** *** 0 **** --- 1,1037 ---- + -- + -- Test foreign-data wrapper and server management. + -- + -- At this point we should have 2 built-in wrappers and no servers. + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------ + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + (2 rows) + + SELECT srvname, srvoptions FROM pg_foreign_server; + srvname | srvoptions + ---------+------------ + (0 rows) + + SELECT * FROM pg_user_mapping; + umuser | umserver | umoptions + --------+----------+----------- + (0 rows) + + -- CREATE FOREIGN DATA WRAPPER + CREATE FOREIGN DATA WRAPPER foo LIBRARY '' LANGUAGE C; -- ERROR + ERROR: could not access file "": No such file or directory + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'plpgsql' LANGUAGE C; -- ERROR + ERROR: "plpgsql" is not a valid foreign-data wrapper library. + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------ + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | default_fdw | + (3 rows) + + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C; -- duplicate + ERROR: foreign-data wrapper "foo" already exists + CREATE FOREIGN DATA WRAPPER "Foo" LIBRARY 'default_fdw' LANGUAGE C; + DROP FOREIGN DATA WRAPPER "Foo"; + DROP FOREIGN DATA WRAPPER foo; + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C OPTIONS (testing '1'); + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------- + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | default_fdw | {testing=1} + (3 rows) + + DROP FOREIGN DATA WRAPPER foo; + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C OPTIONS (testing '1', testing '2'); -- ERROR + ERROR: option "testing" provided more than once + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C OPTIONS (testing '1', another '2'); + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+----------------------- + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | default_fdw | {testing=1,another=2} + (3 rows) + + CREATE SCHEMA fdwtest; + CREATE FOREIGN DATA WRAPPER fdwtest.foo LIBRARY 'default_fdw' LANGUAGE C; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+----------------------- + fdwtest | foo | default_fdw | + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | default_fdw | {testing=1,another=2} + (4 rows) + + SET search_path = fdwtest; + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C; -- duplicate + ERROR: foreign-data wrapper "foo" already exists + DROP FOREIGN DATA WRAPPER foo; + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C; + RESET search_path; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+----------------------- + fdwtest | foo | default_fdw | + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | default_fdw | {testing=1,another=2} + (4 rows) + + CREATE ROLE fdwtest_role; + CREATE ROLE fdwtest_role_super SUPERUSER; + DROP FOREIGN DATA WRAPPER foo; + SET ROLE fdwtest_role; + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C; -- 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' LANGUAGE C; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------ + fdwtest | foo | default_fdw | + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | 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'; + WARNING: changing the foreign-data wrapper library can cause the options for dependent objects to become invalid + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------ + fdwtest | foo | default_fdw | + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | default_fdw | + (4 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 + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------ + fdwtest | foo | default_fdw | + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | default_fdw | {a=1,b=2} + (4 rows) + + ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4'); + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------ + fdwtest | foo | default_fdw | + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | 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: option "b" provided more than once + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+--------------- + fdwtest | foo | default_fdw | + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | 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'); + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------------- + fdwtest | foo | default_fdw | + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | 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; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------------- + fdwtest | foo | default_fdw | + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | default_fdw | {b=3,c=4,a=2,d=5} + (4 rows) + + SET search_path=fdwtest; + ALTER FOREIGN DATA WRAPPER foo OPTIONS (another 'fdw'); + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------------- + fdwtest | foo | default_fdw | {another=fdw} + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | default_fdw | {b=3,c=4,a=2,d=5} + (4 rows) + + ALTER FOREIGN DATA WRAPPER fdwtest.foo OPTIONS (with 'qualified name'); + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------------------------------- + fdwtest | foo | default_fdw | {another=fdw,"with=qualified name"} + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | default_fdw | {b=3,c=4,a=2,d=5} + (4 rows) + + RESET search_path; + -- DROP FOREIGN DATA WRAPPER + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------------------------------- + fdwtest | foo | default_fdw | {another=fdw,"with=qualified name"} + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | 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 + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------------------------------- + fdwtest | foo | default_fdw | {another=fdw,"with=qualified name"} + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | 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; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------------------------------- + fdwtest | foo | default_fdw | {another=fdw,"with=qualified name"} + pg_catalog | default | default_fdw | + pg_catalog | pgsql | 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 + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------ + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + (2 rows) + + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C; + CREATE SERVER s1 FOREIGN DATA WRAPPER foo; + CREATE USER MAPPING FOR current_user SERVER s1; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------ + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + public | foo | default_fdw | + (3 rows) + + \dr+ + List of foreign servers + Schema | Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options + --------+------+-------+----------------------+-------------------+------+---------+--------- + public | s1 | peter | foo | | | | + (1 row) + + \dm+ + List of user mappings + Schema | Server | Username | Options + --------+--------+----------+--------- + public | s1 | peter | + (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 peter 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; + NOTICE: drop cascades to 2 other objects + DETAIL: drop cascades to server s1 + drop cascades to user mapping for peter + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------ + pg_catalog | default | default_fdw | + pg_catalog | pgsql | 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' LANGUAGE C 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 | peter | foo | | | | + public | s2 | peter | foo | | | | {host=a,dbname=b} + public | s3 | peter | foo | | oracle | | + public | s4 | peter | foo | | oracle | | {host=a,dbname=b} + public | s5 | peter | foo | | | 15.0 | + public | s6 | peter | foo | | | 16.0 | {host=a,dbname=b} + public | s7 | peter | foo | | oracle | 17.0 | {host=a,dbname=b} + public | s8 | peter | 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 | peter | foo | | | | + public | s2 | peter | foo | | | | {host=a,dbname=b} + public | s3 | peter | foo | | oracle | | + public | s4 | peter | foo | | oracle | | {host=a,dbname=b} + public | s5 | peter | foo | | | 15.0 | + public | s6 | peter | foo | | | 16.0 | {host=a,dbname=b} + public | s7 | peter | foo | | oracle | 17.0 | {host=a,dbname=b} + public | s8 | peter | 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 | peter | 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 | peter | foo + server_test | s2 | peter | 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 | peter | foo | | | | + public | s2 | peter | foo | | | | {host=a,dbname=b} + public | s3 | peter | foo | | oracle | | + public | s4 | peter | foo | | oracle | | {host=a,dbname=b} + public | s5 | peter | foo | | | 15.0 | + public | s6 | peter | foo | | | 16.0 | {host=a,dbname=b} + public | s7 | peter | foo | | oracle | 17.0 | {host=a,dbname=b} + public | s8 | peter | pgsql | | | | {host=localhost,dbname=s8db} + public | st1 | server_test_role | foo | | | | + public | st2 | server_test_role | foo | | | | + server_test | s1 | peter | foo | | | | + server_test | s2 | peter | 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 FOREIGN SERVER s1 TO server_test_role; + \dr+ *.* + List of foreign servers + Schema | Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options + -------------+------+------------------+----------------------+------------------------------------------+--------+---------+------------------------------ + public | s1 | peter | foo | {peter=U/peter,server_test_role=U/peter} | | 1.0 | {servername=s1} + public | s2 | peter | foo | | | 1.1 | {host=a,dbname=b} + public | s3 | peter | foo | | oracle | | {tnsname=orcl,port=1521} + public | s4 | peter | foo | | oracle | | {host=a,dbname=b} + public | s5 | peter | foo | | | 15.0 | + public | s6 | peter | foo | | | 16.0 | {host=a,dbname=b} + public | s7 | peter | foo | | oracle | 17.0 | {host=a,dbname=b} + public | s8 | peter | pgsql | | | | {host=localhost,dbname=s8db} + public | st1 | server_test_role | foo | | | | + public | st2 | server_test_role | foo | | | | + server_test | s1 | peter | foo | | | | + server_test | s2 | peter | 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 | {peter=U/peter,server_test_role=U/peter} | | 1.1 | {servername=s1} + public | s2 | peter | foo | | | 1.1 | {host=a,dbname=b} + public | s3 | peter | foo | | oracle | | {tnsname=orcl,port=1521} + public | s4 | peter | foo | | oracle | | {host=a,dbname=b} + public | s5 | peter | foo | | | 15.0 | + public | s6 | peter | foo | | | 16.0 | {host=a,dbname=b} + public | s7 | peter | foo | | oracle | 17.0 | {host=a,dbname=b} + public | s8 | peter | pgsql | | | | {dbname=db1,connect_timeout=30} + public | st1 | server_test_role | foo | | | | + public | st2 | server_test_role | foo | | | | + server_test | s1 | peter | foo | | | | + server_test | s2 | peter | 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 | peter | foo + public | s3 | peter | foo + public | s4 | peter | foo + public | s5 | peter | foo + public | s6 | peter | foo + public | s7 | peter | foo + public | s8 | peter | pgsql + public | st1 | server_test_role | foo + public | st2 | server_test_role | foo + server_test | s1 | peter | foo + server_test | s2 | peter | 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 | peter | foo + public | s3 | peter | foo + public | s4 | peter | foo + public | s5 | peter | foo + public | s6 | peter | foo + public | s7 | peter | foo + public | s8 | peter | pgsql + public | st1 | server_test_role | foo + public | st2 | server_test_role | foo + server_test | s1 | peter | foo + server_test | s2 | peter | 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 | peter | foo + public | s4 | peter | foo + public | s5 | peter | foo + public | s6 | peter | foo + public | s7 | peter | foo + public | s8 | peter | pgsql + public | st1 | server_test_role | foo + public | st2 | server_test_role | foo + server_test | s1 | peter | foo + server_test | s2 | peter | foo + (10 rows) + + CREATE USER MAPPING FOR current_user SERVER s3; + \dm *.* + List of user mappings + Schema | Server | Username + --------+--------+---------- + public | s3 | peter + (1 row) + + DROP SERVER s3; -- ERROR + ERROR: cannot drop server s3 because other objects depend on it + DETAIL: user mapping for peter 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 peter + \dr *.* + List of foreign servers + Schema | Name | Owner | Foreign-data wrapper + -------------+------+------------------+---------------------- + public | s4 | peter | foo + public | s5 | peter | foo + public | s6 | peter | foo + public | s7 | peter | foo + public | s8 | peter | pgsql + public | st1 | server_test_role | foo + public | st2 | server_test_role | foo + server_test | s1 | peter | foo + server_test | s2 | peter | 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 "peter" 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 | peter + public | s4 | public + public | s5 | server_test_role + public | s6 | server_test_role + public | s8 | peter + 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: option "modified" provided more than once + 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 | peter | + public | s5 | server_test_role | {modified=1} + public | s6 | server_test_role | {username=test} + public | s8 | peter | {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 | peter + public | s4 | public + public | s5 | server_test_role + public | s6 | server_test_role + public | s8 | peter + public | s8 | public + (6 rows) + + -- Information schema + SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2, 3, 4; + foreign_data_wrapper_catalog | foreign_data_wrapper_name | authorization_identifier | library_name | foreign_data_wrapper_language + ------------------------------+---------------------------+--------------------------+--------------+------------------------------- + regression | default | peter | default_fdw | + regression | foo | peter | default_fdw | + regression | pgsql | peter | pgsql_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 | | peter + regression | st1 | regression | foo | | | server_test_role + regression | server_test.s2 | regression | foo | | | peter + regression | st2 | regression | foo | | | server_test_role + regression | s8 | regression | pgsql | | | peter + 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 + peter | regression | s4 + public | regression | s8 + peter | 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 + peter | 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' LANGUAGE C; -- 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 FOREIGN 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' LANGUAGE C; -- 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 FOREIGN SERVER s6 TO server_test_role; -- ERROR + ERROR: permission denied for foreign server s6 + GRANT USAGE ON FOREIGN 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 FOREIGN 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 FOREIGN 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 FOREIGN 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 peter + 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 peter + 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 built-in wrappers and no servers or mappings. + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwschema | fdwname | fdwlibrary | fdwoptions + ------------+---------+-------------+------------ + pg_catalog | default | default_fdw | + pg_catalog | pgsql | pgsql_fdw | + (2 rows) + + SELECT srvname, srvoptions FROM pg_foreign_server; + srvname | srvoptions + ---------+------------ + (0 rows) + + SELECT * FROM pg_user_mapping; + umuser | umserver | umoptions + --------+----------+----------- + (0 rows) + diff -x CVS -cdNr ../cvs-pgsql/src/test/regress/expected/opr_sanity.out ./src/test/regress/expected/opr_sanity.out *** ../cvs-pgsql/src/test/regress/expected/opr_sanity.out 2008-12-05 15:06:35.000000000 +0200 --- ./src/test/regress/expected/opr_sanity.out 2008-12-08 09:40:18.000000000 +0200 *************** *** 117,125 **** 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. diff -x CVS -cdNr ../cvs-pgsql/src/test/regress/expected/rules.out ./src/test/regress/expected/rules.out *** ../cvs-pgsql/src/test/regress/expected/rules.out 2008-11-13 10:44:53.000000000 +0200 --- ./src/test/regress/expected/rules.out 2008-12-08 18:20:23.000000000 +0200 *************** *** 1313,1318 **** --- 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_mappings | SELECT u.oid AS umid, s.oid AS srvid, s.srvname, u.umuser, CASE WHEN (u.umuser = (0)::oid) THEN 'public'::name ELSE a.rolname END AS usename FROM ((pg_user_mapping u LEFT JOIN pg_authid a ON ((a.oid = u.umuser))) JOIN pg_foreign_server s ON ((u.umserver = 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 **** 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; diff -x CVS -cdNr ../cvs-pgsql/src/test/regress/expected/sanity_check.out ./src/test/regress/expected/sanity_check.out *** ../cvs-pgsql/src/test/regress/expected/sanity_check.out 2007-11-24 21:49:23.000000000 +0200 --- ./src/test/regress/expected/sanity_check.out 2008-12-08 18:20:26.000000000 +0200 *************** *** 99,104 **** --- 99,106 ---- pg_depend | t pg_description | t pg_enum | t + pg_foreign_data_wrapper | t + pg_foreign_server | t pg_index | t pg_inherits | t pg_language | t *************** *** 122,127 **** --- 124,130 ---- pg_ts_parser | t pg_ts_template | t pg_type | t + pg_user_mapping | t point_tbl | f polygon_tbl | t ramp | f *************** *** 149,155 **** 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 diff -x CVS -cdNr ../cvs-pgsql/src/test/regress/parallel_schedule ./src/test/regress/parallel_schedule *** ../cvs-pgsql/src/test/regress/parallel_schedule 2008-11-03 16:31:26.000000000 +0200 --- ./src/test/regress/parallel_schedule 2008-12-08 17:39:04.000000000 +0200 *************** *** 77,83 **** # ---------- # 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 foreign_data # ---------- # Another group of parallel tests diff -x CVS -cdNr ../cvs-pgsql/src/test/regress/serial_schedule ./src/test/regress/serial_schedule *** ../cvs-pgsql/src/test/regress/serial_schedule 2008-11-03 16:31:26.000000000 +0200 --- ./src/test/regress/serial_schedule 2008-12-08 17:38:39.000000000 +0200 *************** *** 108,113 **** --- 108,114 ---- test: without_oid test: conversion test: tsdicts + test: foreign_data test: truncate test: alter_table test: sequence diff -x CVS -cdNr ../cvs-pgsql/src/test/regress/sql/foreign_data.sql ./src/test/regress/sql/foreign_data.sql *** ../cvs-pgsql/src/test/regress/sql/foreign_data.sql 1970-01-01 02:00:00.000000000 +0200 --- ./src/test/regress/sql/foreign_data.sql 2008-12-09 12:50:51.000000000 +0200 *************** *** 0 **** --- 1,406 ---- + -- + -- Test foreign-data wrapper and server management. + -- + + -- At this point we should have 2 built-in wrappers and no servers. + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + SELECT srvname, srvoptions FROM pg_foreign_server; + SELECT * FROM pg_user_mapping; + + -- CREATE FOREIGN DATA WRAPPER + CREATE FOREIGN DATA WRAPPER foo LIBRARY '' LANGUAGE C; -- ERROR + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'plpgsql' LANGUAGE C; -- ERROR + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C; -- duplicate + CREATE FOREIGN DATA WRAPPER "Foo" LIBRARY 'default_fdw' LANGUAGE C; + DROP FOREIGN DATA WRAPPER "Foo"; + DROP FOREIGN DATA WRAPPER foo; + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C OPTIONS (testing '1'); + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + DROP FOREIGN DATA WRAPPER foo; + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C OPTIONS (testing '1', testing '2'); -- ERROR + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C OPTIONS (testing '1', another '2'); + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + CREATE SCHEMA fdwtest; + CREATE FOREIGN DATA WRAPPER fdwtest.foo LIBRARY 'default_fdw' LANGUAGE C; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + SET search_path = fdwtest; + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C; -- duplicate + DROP FOREIGN DATA WRAPPER foo; + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C; + RESET search_path; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + CREATE ROLE fdwtest_role; + CREATE ROLE fdwtest_role_super SUPERUSER; + + DROP FOREIGN DATA WRAPPER foo; + SET ROLE fdwtest_role; + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C; -- ERROR + SET ROLE fdwtest_role_super; + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'pgsql_fdw' LANGUAGE C; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + 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'; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + 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 + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4'); + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2'); + ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4'); -- ERROR + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + 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'); + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + 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; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + SET search_path=fdwtest; + ALTER FOREIGN DATA WRAPPER foo OPTIONS (another 'fdw'); + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + ALTER FOREIGN DATA WRAPPER fdwtest.foo OPTIONS (with 'qualified name'); + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + RESET search_path; + + -- DROP FOREIGN DATA WRAPPER + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + 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; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + 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; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + DROP SCHEMA fdwtest; -- ERROR + DROP SCHEMA fdwtest CASCADE; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C; + CREATE SERVER s1 FOREIGN DATA WRAPPER foo; + CREATE USER MAPPING FOR current_user SERVER s1; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + \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; + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + \dr+ + \dm+ + + -- exercise CREATE SERVER + CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR + CREATE FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw' LANGUAGE C 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 FOREIGN 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 ORDER BY 1, 2, 3, 4; + 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' LANGUAGE C; -- 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 FOREIGN 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' LANGUAGE C; -- 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 FOREIGN SERVER s6 TO server_test_role; -- ERROR + GRANT USAGE ON FOREIGN 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 FOREIGN 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 FOREIGN SERVER s9 TO unprivileged_role; + SET ROLE unprivileged_role; + ALTER SERVER s9 VERSION '1.2'; -- ERROR + GRANT USAGE ON FOREIGN 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 built-in wrappers and no servers or mappings. + SELECT (SELECT nspname FROM pg_namespace WHERE oid = fdwnamespace) AS fdwschema, fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + SELECT srvname, srvoptions FROM pg_foreign_server; + SELECT * FROM pg_user_mapping;