diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index f5f26d035d9088b3fbd639dba9c0f5e9e50a1bb2..52be7a4d26dcd13910a9f3296681862162b66d8c 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.184 2008/12/18 18:20:33 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.185 2008/12/19 16:25:16 petere Exp $ --> <!-- Documentation of the system catalogs, directed toward PostgreSQL developers --> @@ -133,6 +133,16 @@ <entry>enum label and value definitions</entry> </row> + <row> + <entry><link linkend="catalog-pg-foreign-data-wrapper"><structname>pg_foreign_data_wrapper</structname></link></entry> + <entry>foreign-data wrapper definitions</entry> + </row> + + <row> + <entry><link linkend="catalog-pg-foreign-server"><structname>pg_foreign_server</structname></link></entry> + <entry>foreign server definitions</entry> + </row> + <row> <entry><link linkend="catalog-pg-index"><structname>pg_index</structname></link></entry> <entry>additional index information</entry> @@ -247,6 +257,11 @@ <entry><link linkend="catalog-pg-type"><structname>pg_type</structname></link></entry> <entry>data types</entry> </row> + + <row> + <entry><link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link></entry> + <entry>mappings of users to foreign servers</entry> + </row> </tbody> </tgroup> </table> @@ -2552,6 +2567,229 @@ </sect1> + <sect1 id="catalog-pg-foreign-data-wrapper"> + <title><structname>pg_foreign_data_wrapper</structname></title> + + <indexterm zone="catalog-pg-foreign-data-wrapper"> + <primary>pg_foreign_data_wrapper</primary> + </indexterm> + + <para> + The catalog <structname>pg_foreign_data_wrapper</structname> stores + foreign-data wrapper definitions. A foreign-data wrapper is the + mechanism by which external data, residing on foreign servers, is + accessed. + </para> + + <table> + <title><structname>pg_foreign_data_wrapper</> Columns</title> + + <tgroup cols=4> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>References</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><structfield>fdwname</structfield></entry> + <entry><type>name</type></entry> + <entry></entry> + <entry>Name of the foreign-data wrapper</entry> + </row> + + <row> + <entry><structfield>fdwowner</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry> + <entry>Owner of the foreign-data wrapper</entry> + </row> + + <row> + <entry><structfield>fdwlibrary</structfield></entry> + <entry><type>text</type></entry> + <entry></entry> + <entry>File name of the library implementing this foreign-data wrapper</entry> + </row> + + <row> + <entry><structfield>fdwacl</structfield></entry> + <entry><type>aclitem[]</type></entry> + <entry></entry> + <entry> + Access privileges; see + <xref linkend="sql-grant" endterm="sql-grant-title"> and + <xref linkend="sql-revoke" endterm="sql-revoke-title"> + for details + </entry> + </row> + + <row> + <entry><structfield>fdwoptions</structfield></entry> + <entry><type>text[]</type></entry> + <entry></entry> + <entry> + Foreign-data wrapper specific options, as <quote>keyword=value</> strings + </entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> + + + <sect1 id="catalog-pg-foreign-server"> + <title><structname>pg_foreign_server</structname></title> + + <indexterm zone="catalog-pg-foreign-server"> + <primary>pg_foreign_server</primary> + </indexterm> + + <para> + The catalog <structname>pg_foreign_server</structname> 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. + </para> + + <table> + <title><structname>pg_foreign_server</> Columns</title> + + <tgroup cols=4> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>References</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><structfield>srvname</structfield></entry> + <entry><type>name</type></entry> + <entry></entry> + <entry>Name of the foreign server</entry> + </row> + + <row> + <entry><structfield>srvowner</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry> + <entry>Owner of the foreign server</entry> + </row> + + <row> + <entry><structfield>srvfdw</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-foreign-data-wrapper"><structname>pg_foreign_data_wrapper</structname></link>.oid</literal></entry> + <entry>The OID of the foreign-data wrapper of this foreign server</entry> + </row> + + <row> + <entry><structfield>srvtype</structfield></entry> + <entry><type>text</type></entry> + <entry></entry> + <entry>Type of the server (optional)</entry> + </row> + + <row> + <entry><structfield>srvversion</structfield></entry> + <entry><type>text</type></entry> + <entry></entry> + <entry>Version of the server (optional)</entry> + </row> + + <row> + <entry><structfield>srvacl</structfield></entry> + <entry><type>aclitem[]</type></entry> + <entry></entry> + <entry> + Access privileges; see + <xref linkend="sql-grant" endterm="sql-grant-title"> and + <xref linkend="sql-revoke" endterm="sql-revoke-title"> + for details + </entry> + </row> + + <row> + <entry><structfield>srvoptions</structfield></entry> + <entry><type>text[]</type></entry> + <entry></entry> + <entry> + Foreign server specific options, as <quote>keyword=value</> strings. + </entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> + + + <sect1 id="catalog-pg-user-mapping"> + <title><structname>pg_user_mapping</structname></title> + + <indexterm zone="catalog-pg-user-mapping"> + <primary>pg_user_mapping</primary> + </indexterm> + + <para> + The catalog <structname>pg_user_mapping</structname> stores + the mappings from local user to remote. Access to this catalog is + restricted from normal users, use the view + <link linkend="view-pg-user-mappings"><structname>pg_user_mappings</structname></link> + instead. + </para> + + <table> + <title><structname>pg_user_mapping</> Columns</title> + + <tgroup cols=4> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>References</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><structfield>umuser</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry> + <entry>OID of the local role being mapped, 0 if the user mapping is public</entry> + </row> + + <row> + <entry><structfield>umserver</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-foreign-server"><structname>pg_foreign_server</structname></link>.oid</literal></entry> + <entry> + The OID of the foreign server that contains this mapping + </entry> + </row> + + <row> + <entry><structfield>umoptions</structfield></entry> + <entry><type>text[]</type></entry> + <entry></entry> + <entry> + User mapping specific options, as <quote>keyword=value</> strings. + </entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> + + <sect1 id="catalog-pg-index"> <title><structname>pg_index</structname></title> @@ -7019,6 +7257,91 @@ </sect1> + <sect1 id="view-pg-user-mappings"> + <title><structname>pg_user_mappings</structname></title> + + <indexterm zone="view-pg-user-mappings"> + <primary>pg_user_mappings</primary> + </indexterm> + + <para> + The view <structname>pg_user_mappings</structname> provides access + to information about user mappings. This is essentially a publicly + readable view of + <link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link> + that leaves out the options field if the user has no rights to use + it. + </para> + + <table> + <title><structname>pg_user_mappings</> Columns</title> + + <tgroup cols=3> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>References</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><structfield>umid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link>.oid</literal></entry> + <entry>OID of the user mapping</entry> + </row> + + <row> + <entry><structfield>srvid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-foreign-server"><structname>pg_foreign_server</structname></link>.oid</literal></entry> + <entry> + The OID of the foreign server that contains this mapping + </entry> + </row> + + <row> + <entry><structfield>srvname</structfield></entry> + <entry><type>text</type></entry> + <entry></entry> + <entry> + Name of the foreign server + </entry> + </row> + + <row> + <entry><structfield>umuser</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry> + <entry>OID of the local role being mapped, 0 if the user mapping is public</entry> + </row> + + <row> + <entry><structfield>usename</structfield></entry> + <entry><type>name</type></entry> + <entry></entry> + <entry>Name of the local user to be mapped</entry> + </row> + + <row> + <entry><structfield>umoptions</structfield></entry> + <entry><type>text[]</type></entry> + <entry></entry> + <entry> + User mapping specific options, as <quote>keyword=value</> + strings, if the current user is the owner of the foreign + server, else null. + </entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> + + <sect1 id="view-pg-views"> <title><structname>pg_views</structname></title> diff --git a/doc/src/sgml/features.sgml b/doc/src/sgml/features.sgml index 712ad4e4ecf207578f72b48ccdd062eabdebc042..56b5be3318307307a29834fcc5ffa069283201d8 100644 --- a/doc/src/sgml/features.sgml +++ b/doc/src/sgml/features.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/features.sgml,v 2.29 2008/11/27 12:12:02 petere Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/features.sgml,v 2.30 2008/12/19 16:25:16 petere Exp $ --> <appendix id="features"> <title>SQL Conformance</title> @@ -71,11 +71,11 @@ </para> <para> - The <productname>PostgreSQL</productname> core covers parts 1, 2, + The <productname>PostgreSQL</productname> 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, 9, and 10 + implementations of parts 4 and 10 for <productname>PostgreSQL</productname>. </para> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index c08c4801882c5929581c8e1128dba969bbc9edd2..de50c0e1d5691168ee639a176f127df764611fc8 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.462 2008/12/18 18:20:33 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.463 2008/12/19 16:25:16 petere Exp $ --> <chapter id="functions"> <title>Functions and Operators</title> @@ -11335,6 +11335,21 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, .. <entry><type>boolean</type></entry> <entry>does current user have privilege for database</entry> </row> + <row> + <entry><literal><function>has_foreign_data_wrapper_privilege</function>(<parameter>user</parameter>, + <parameter>fdw</parameter>, + <parameter>privilege</parameter>)</literal> + </entry> + <entry><type>boolean</type></entry> + <entry>does user have privilege for foreign-data wrapper</entry> + </row> + <row> + <entry><literal><function>has_foreign_data_wrapper_privilege</function>(<parameter>fdw</parameter>, + <parameter>privilege</parameter>)</literal> + </entry> + <entry><type>boolean</type></entry> + <entry>does current user have privilege for foreign-data wrapper</entry> + </row> <row> <entry><literal><function>has_function_privilege</function>(<parameter>user</parameter>, <parameter>function</parameter>, @@ -11380,6 +11395,21 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, .. <entry><type>boolean</type></entry> <entry>does current user have privilege for schema</entry> </row> + <row> + <entry><literal><function>has_server_privilege</function>(<parameter>user</parameter>, + <parameter>server</parameter>, + <parameter>privilege</parameter>)</literal> + </entry> + <entry><type>boolean</type></entry> + <entry>does user have privilege for foreign server</entry> + </row> + <row> + <entry><literal><function>has_server_privilege</function>(<parameter>server</parameter>, + <parameter>privilege</parameter>)</literal> + </entry> + <entry><type>boolean</type></entry> + <entry>does current user have privilege for foreign server</entry> + </row> <row> <entry><literal><function>has_table_privilege</function>(<parameter>user</parameter>, <parameter>table</parameter>, @@ -11435,12 +11465,18 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, .. <indexterm> <primary>has_function_privilege</primary> </indexterm> + <indexterm> + <primary>has_foreign_data_wrapper_privilege</primary> + </indexterm> <indexterm> <primary>has_language_privilege</primary> </indexterm> <indexterm> <primary>has_schema_privilege</primary> </indexterm> + <indexterm> + <primary>has_server_privilege</primary> + </indexterm> <indexterm> <primary>has_table_privilege</primary> </indexterm> @@ -11478,6 +11514,14 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute'); </programlisting> </para> + <para> + <function>has_foreign_data_wrapper_privilege</function> checks whether a user + can access a foreign-data wrapper in a particular way. The possibilities for its + arguments are analogous to <function>has_table_privilege</function>. + The desired access privilege type must evaluate to + <literal>USAGE</literal>. + </para> + <para> <function>has_language_privilege</function> checks whether a user can access a procedural language in a particular way. The possibilities @@ -11495,6 +11539,14 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute'); <literal>USAGE</literal>. </para> + <para> + <function>has_server_privilege</function> checks whether a user + can access a foreign server in a particular way. The possibilities for its + arguments are analogous to <function>has_table_privilege</function>. + The desired access privilege type must evaluate to + <literal>USAGE</literal>. + </para> + <para> <function>has_table_privilege</function> checks whether a user can access a table in a particular way. The user can be diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index da01c9cc5a71085eaf2dbb8ce85e251543393465..f645c1252d625d680c409e86bff09f40f9e2fad3 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/information_schema.sgml,v 1.35 2008/11/25 20:47:42 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/information_schema.sgml,v 1.36 2008/12/19 16:25:16 petere Exp $ --> <chapter id="information-schema"> <title>The Information Schema</title> @@ -2145,6 +2145,237 @@ ORDER BY c.ordinal_position; </table> </sect1> + <sect1 id="infoschema-foreign-data-wrapper-options"> + <title><literal>foreign_data_wrapper_options</literal></title> + + <para> + The view <literal>foreign_data_wrapper_options</literal> contains + all the options defined for foreign-data wrappers in the current + database. Only those foreign-data wrappers are shown that the + current user has access to (by way of being the owner or having + some privilege). + </para> + + <table> + <title><literal>foreign_data_wrapper_options</literal> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Data Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><literal>foreign_data_wrapper_catalog</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the database that the foreign-data wrapper is defined in (always the current database)</entry> + </row> + + <row> + <entry><literal>foreign_data_wrapper_name</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the foreign-data wrapper</entry> + </row> + + <row> + <entry><literal>option_name</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of an option</entry> + </row> + + <row> + <entry><literal>option_value</literal></entry> + <entry><type>character_data</type></entry> + <entry>Value of the option</entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> + + <sect1 id="infoschema-foreign-data-wrappers"> + <title><literal>foreign_data_wrappers</literal></title> + + <para> + The view <literal>foreign_data_wrappers</literal> contains all + foreign-data wrappers defined in the current database. Only those + foreign-data wrappers are shown that the current user has access to + (by way of being the owner or having some privilege). + </para> + + <table> + <title><literal>foreign_data_wrappers</literal> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Data Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><literal>foreign_data_wrapper_catalog</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the database that contains the foreign-data + wrapper (always the current database)</entry> + </row> + + <row> + <entry><literal>foreign_data_wrapper_name</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the foreign-data wrapper</entry> + </row> + + <row> + <entry><literal>authorization_identifier</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the owner of the foreign server</entry> + </row> + + <row> + <entry><literal>library_name</literal></entry> + <entry><type>character_data</type></entry> + <entry>File name of the library that implementing this foreign-data wrapper</entry> + </row> + + <row> + <entry><literal>foreign_data_wrapper_language</literal></entry> + <entry><type>character_data</type></entry> + <entry>Language used to implement this foreign-data wrapper</entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> + + <sect1 id="infoschema-foreign-server-options"> + <title><literal>foreign_server_options</literal></title> + + <para> + The view <literal>foreign_server_options</literal> contains all the + options defined for foreign servers in the current database. Only + those foreign servers are shown that the current user has access to + (by way of being the owner or having some privilege). + </para> + + <table> + <title><literal>foreign_server_options</literal> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Data Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><literal>foreign_server_catalog</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the database that the foreign server is defined in (always the current database)</entry> + </row> + + <row> + <entry><literal>foreign_server_name</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the foreign server</entry> + </row> + + <row> + <entry><literal>option_name</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of an option</entry> + </row> + + <row> + <entry><literal>option_value</literal></entry> + <entry><type>character_data</type></entry> + <entry>Value of the option</entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> + + <sect1 id="infoschema-foreign-servers"> + <title><literal>foreign_servers</literal></title> + + <para> + The view <literal>foreign_servers</literal> contains all foreign + servers defined in the current database. Only those foreign + servers are shown that the current user has access to (by way of + being the owner or having some privilege). + </para> + + <table> + <title><literal>foreign_servers</literal> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Data Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><literal>foreign_server_catalog</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the database that the foreign server is defined in (always the current database)</entry> + </row> + + <row> + <entry><literal>foreign_server_name</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the foreign server</entry> + </row> + + <row> + <entry><literal>foreign_data_wrapper_catalog</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the database that contains the foreign-data + wrapper used by the foreign server (always the current database)</entry> + </row> + + <row> + <entry><literal>foreign_data_wrapper_name</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the foreign-data wrapper used by the foreign server</entry> + </row> + + <row> + <entry><literal>foreign_server_type</literal></entry> + <entry><type>character_data</type></entry> + <entry>Foreign server type information, if specified upon creation</entry> + </row> + + <row> + <entry><literal>foreign_server_version</literal></entry> + <entry><type>character_data</type></entry> + <entry>Foreign server version information, if specified upon creation</entry> + </row> + + <row> + <entry><literal>authorization_identifier</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the owner of the foreign server</entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> + <sect1 id="infoschema-key-column-usage"> <title><literal>key_column_usage</literal></title> @@ -2848,15 +3079,11 @@ ORDER BY c.ordinal_position; <title><literal>role_usage_grants</literal></title> <para> - The view <literal>role_usage_grants</literal> is meant to identify + The view <literal>role_usage_grants</literal> identifies <literal>USAGE</literal> privileges granted on various kinds of - objects to a currently enabled role or by a currently enabled role. - In <productname>PostgreSQL</productname>, this currently only - applies to domains, and since domains do not have real privileges - in <productname>PostgreSQL</productname>, this view is empty. + objects where the grantor or grantee is a currently enabled role. Further information can be found under - <literal>usage_privileges</literal>. In the future, this view might - contain more useful information. + <literal>usage_privileges</literal>. </para> <table> @@ -2875,13 +3102,13 @@ ORDER BY c.ordinal_position; <row> <entry><literal>grantor</literal></entry> <entry><type>sql_identifier</type></entry> - <entry>In the future, the name of the role that granted the privilege</entry> + <entry>The name of the role that granted the privilege</entry> </row> <row> <entry><literal>grantee</literal></entry> <entry><type>sql_identifier</type></entry> - <entry>In the future, the name of the role that the privilege was granted to</entry> + <entry>The name of the role that the privilege was granted to</entry> </row> <row> @@ -2893,7 +3120,8 @@ ORDER BY c.ordinal_position; <row> <entry><literal>object_schema</literal></entry> <entry><type>sql_identifier</type></entry> - <entry>Name of the schema containing the object</entry> + <entry>Name of the schema containing the object, if applicable, + else an empty string</entry> </row> <row> @@ -2905,7 +3133,7 @@ ORDER BY c.ordinal_position; <row> <entry><literal>object_type</literal></entry> <entry><type>character_data</type></entry> - <entry>In the future, the type of the object</entry> + <entry><literal>DOMAIN</literal> or <literal>FOREIGN DATA WRAPPER</literal> or <literal>FOREIGN SERVER</literal></entry> </row> <row> @@ -4718,15 +4946,20 @@ ORDER BY c.ordinal_position; <title><literal>usage_privileges</literal></title> <para> - The view <literal>usage_privileges</literal> is meant to identify + The view <literal>usage_privileges</literal> identifies <literal>USAGE</literal> privileges granted on various kinds of objects to a currently enabled role or by a currently enabled role. - In <productname>PostgreSQL</productname>, this currently only - applies to domains, and since domains do not have real privileges + In <productname>PostgreSQL</productname>, this currently applies to + domains, foreign-data wrappers, and foreign servers. There is one + row for each combination of object, grantor, and grantee. + </para> + + <para> + Since domains do not have real privileges in <productname>PostgreSQL</productname>, this view shows implicit - <literal>USAGE</literal> privileges granted to - <literal>PUBLIC</literal> for all domains. In the future, this - view might contain more useful information. + non-grantable <literal>USAGE</literal> privileges granted by the + owner to <literal>PUBLIC</literal> for all domains. The other + object types, however, show real privileges. </para> <table> @@ -4745,13 +4978,13 @@ ORDER BY c.ordinal_position; <row> <entry><literal>grantor</literal></entry> <entry><type>sql_identifier</type></entry> - <entry>Currently set to the name of the owner of the object</entry> + <entry>Name of the role that granted the privilege</entry> </row> <row> <entry><literal>grantee</literal></entry> <entry><type>sql_identifier</type></entry> - <entry>Currently always <literal>PUBLIC</literal></entry> + <entry>Name of the role that the privilege was granted to</entry> </row> <row> @@ -4763,7 +4996,8 @@ ORDER BY c.ordinal_position; <row> <entry><literal>object_schema</literal></entry> <entry><type>sql_identifier</type></entry> - <entry>Name of the schema containing the object</entry> + <entry>Name of the schema containing the object, if applicable, + else an empty string</entry> </row> <row> @@ -4775,7 +5009,7 @@ ORDER BY c.ordinal_position; <row> <entry><literal>object_type</literal></entry> <entry><type>character_data</type></entry> - <entry>Currently always <literal>DOMAIN</literal></entry> + <entry><literal>DOMAIN</literal> or <literal>FOREIGN DATA WRAPPER</literal> or <literal>FOREIGN SERVER</literal></entry> </row> <row> @@ -4787,7 +5021,115 @@ ORDER BY c.ordinal_position; <row> <entry><literal>is_grantable</literal></entry> <entry><type>character_data</type></entry> - <entry>Currently always <literal>NO</literal></entry> + <entry><literal>YES</literal> if the privilege is grantable, <literal>NO</literal> if not</entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> + + <sect1 id="infoschema-user-mapping-options"> + <title><literal>user_mapping_options</literal></title> + + <para> + The view <literal>user_mapping_options</literal> contains all the + options defined for user mappings in the current database. Only + those user mappings are shown where the current user has access to + the corresponding foreign server (by way of being the owner or + having some privilege). + </para> + + <table> + <title><literal>user_mapping_options</literal> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Data Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><literal>authorization_identifier</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the user being mapped, + or <literal>PUBLIC</literal> if the mapping is public</entry> + </row> + + <row> + <entry><literal>foreign_server_catalog</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the database that the foreign server used by this + mapping is defined in (always the current database)</entry> + </row> + + <row> + <entry><literal>foreign_server_name</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the foreign server used by this mapping</entry> + </row> + + <row> + <entry><literal>option_name</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of an option</entry> + </row> + + <row> + <entry><literal>option_value</literal></entry> + <entry><type>character_data</type></entry> + <entry>Value of the option</entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> + + <sect1 id="infoschema-user-mappings"> + <title><literal>user_mappings</literal></title> + + <para> + The view <literal>user_mappings</literal> contains all user + mappings defined in the current database. Only those user mappings + are shown where the current user has access to the corresponding + foreign server (by way of being the owner or having some + privilege). + </para> + + <table> + <title><literal>user_mappings</literal> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Data Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><literal>authorization_identifier</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the user being mapped, + or <literal>PUBLIC</literal> if the mapping is public</entry> + </row> + + <row> + <entry><literal>foreign_server_catalog</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the database that the foreign server used by this + mapping is defined in (always the current database)</entry> + </row> + + <row> + <entry><literal>foreign_server_name</literal></entry> + <entry><type>sql_identifier</type></entry> + <entry>Name of the foreign server used by this mapping</entry> </row> </tbody> </tgroup> diff --git a/doc/src/sgml/keywords.sgml b/doc/src/sgml/keywords.sgml index de3bedfdcbe94aaf9fd8569211c141c14d52785e..f7f7d649563162c7e1a04b7d8e3cdb63daa66811 100644 --- a/doc/src/sgml/keywords.sgml +++ b/doc/src/sgml/keywords.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/keywords.sgml,v 2.21 2008/12/03 12:39:57 petere Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/keywords.sgml,v 2.22 2008/12/19 16:25:16 petere Exp $ --> <appendix id="sql-keywords-appendix"> <title><acronym>SQL</acronym> Key Words</title> @@ -2687,6 +2687,14 @@ <entry>reserved</entry> <entry>reserved</entry> </row> + <row> + <entry><token>LIBRARY</token></entry> + <entry>non-reserved</entry> + <entry>non-reserved</entry> + <entry>non-reserved</entry> + <entry></entry> + <entry></entry> + </row> <row> <entry><token>LIKE</token></entry> <entry>reserved (can be function or type)</entry> @@ -2818,8 +2826,8 @@ <row> <entry><token>MAPPING</token></entry> <entry>non-reserved</entry> - <entry></entry> - <entry></entry> + <entry>non-reserved</entry> + <entry>non-reserved</entry> <entry></entry> <entry></entry> </row> @@ -3441,7 +3449,7 @@ </row> <row> <entry><token>OPTIONS</token></entry> - <entry></entry> + <entry>non-reserved</entry> <entry>non-reserved</entry> <entry>non-reserved</entry> <entry>non-reserved</entry> @@ -4455,6 +4463,14 @@ <entry>non-reserved</entry> <entry>non-reserved</entry> </row> + <row> + <entry><token>SERVER</token></entry> + <entry>non-reserved</entry> + <entry>non-reserved</entry> + <entry>non-reserved</entry> + <entry></entry> + <entry></entry> + </row> <row> <entry><token>SERVER_NAME</token></entry> <entry></entry> @@ -5583,6 +5599,14 @@ <entry>reserved</entry> <entry>reserved</entry> </row> + <row> + <entry><token>WRAPPER</token></entry> + <entry>non-reserved</entry> + <entry>non-reserved</entry> + <entry>non-reserved</entry> + <entry></entry> + <entry></entry> + </row> <row> <entry><token>WRITE</token></entry> <entry>non-reserved</entry> diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index b80a2cf8f04895372b5b20c54490f49b72bac88f..6c20b623c49ad8f866edab0b3256de0b736fd639 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.73 2008/03/27 17:24:16 momjian Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.74 2008/12/19 16:25:16 petere Exp $ PostgreSQL documentation Complete list of usable sgml source files in this directory. --> @@ -10,6 +10,7 @@ Complete list of usable sgml source files in this directory. <!entity alterConversion system "alter_conversion.sgml"> <!entity alterDatabase system "alter_database.sgml"> <!entity alterDomain system "alter_domain.sgml"> +<!entity alterForeignDataWrapper system "alter_foreign_data_wrapper.sgml"> <!entity alterFunction system "alter_function.sgml"> <!entity alterGroup system "alter_group.sgml"> <!entity alterIndex system "alter_index.sgml"> @@ -19,6 +20,7 @@ Complete list of usable sgml source files in this directory. <!entity alterOperatorFamily system "alter_opfamily.sgml"> <!entity alterRole system "alter_role.sgml"> <!entity alterSchema system "alter_schema.sgml"> +<!entity alterServer system "alter_server.sgml"> <!entity alterSequence system "alter_sequence.sgml"> <!entity alterTable system "alter_table.sgml"> <!entity alterTableSpace system "alter_tablespace.sgml"> @@ -29,6 +31,7 @@ Complete list of usable sgml source files in this directory. <!entity alterTrigger system "alter_trigger.sgml"> <!entity alterType system "alter_type.sgml"> <!entity alterUser system "alter_user.sgml"> +<!entity alterUserMapping system "alter_user_mapping.sgml"> <!entity alterView system "alter_view.sgml"> <!entity analyze system "analyze.sgml"> <!entity begin system "begin.sgml"> @@ -45,6 +48,7 @@ Complete list of usable sgml source files in this directory. <!entity createConversion system "create_conversion.sgml"> <!entity createDatabase system "create_database.sgml"> <!entity createDomain system "create_domain.sgml"> +<!entity createForeignDataWrapper system "create_foreign_data_wrapper.sgml"> <!entity createFunction system "create_function.sgml"> <!entity createGroup system "create_group.sgml"> <!entity createIndex system "create_index.sgml"> @@ -56,6 +60,7 @@ Complete list of usable sgml source files in this directory. <!entity createRule system "create_rule.sgml"> <!entity createSchema system "create_schema.sgml"> <!entity createSequence system "create_sequence.sgml"> +<!entity createServer system "create_server.sgml"> <!entity createTable system "create_table.sgml"> <!entity createTableAs system "create_table_as.sgml"> <!entity createTableSpace system "create_tablespace.sgml"> @@ -66,6 +71,7 @@ Complete list of usable sgml source files in this directory. <!entity createTSTemplate system "create_tstemplate.sgml"> <!entity createType system "create_type.sgml"> <!entity createUser system "create_user.sgml"> +<!entity createUserMapping system "create_user_mapping.sgml"> <!entity createView system "create_view.sgml"> <!entity deallocate system "deallocate.sgml"> <!entity declare system "declare.sgml"> @@ -76,6 +82,7 @@ Complete list of usable sgml source files in this directory. <!entity dropConversion system "drop_conversion.sgml"> <!entity dropDatabase system "drop_database.sgml"> <!entity dropDomain system "drop_domain.sgml"> +<!entity dropForeignDataWrapper system "drop_foreign_data_wrapper.sgml"> <!entity dropFunction system "drop_function.sgml"> <!entity dropGroup system "drop_group.sgml"> <!entity dropIndex system "drop_index.sgml"> @@ -88,6 +95,7 @@ Complete list of usable sgml source files in this directory. <!entity dropRule system "drop_rule.sgml"> <!entity dropSchema system "drop_schema.sgml"> <!entity dropSequence system "drop_sequence.sgml"> +<!entity dropServer system "drop_server.sgml"> <!entity dropTable system "drop_table.sgml"> <!entity dropTableSpace system "drop_tablespace.sgml"> <!entity dropTrigger system "drop_trigger.sgml"> @@ -97,6 +105,7 @@ Complete list of usable sgml source files in this directory. <!entity dropTSTemplate system "drop_tstemplate.sgml"> <!entity dropType system "drop_type.sgml"> <!entity dropUser system "drop_user.sgml"> +<!entity dropUserMapping system "drop_user_mapping.sgml"> <!entity dropView system "drop_view.sgml"> <!entity end system "end.sgml"> <!entity execute system "execute.sgml"> diff --git a/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml b/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml new file mode 100644 index 0000000000000000000000000000000000000000..877c475517064f4eaa421b47740bde1629c272cc --- /dev/null +++ b/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml @@ -0,0 +1,132 @@ +<!-- +$PostgreSQL: pgsql/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $ +PostgreSQL documentation +--> + +<refentry id="SQL-ALTERFOREIGNDATAWRAPPER"> + <refmeta> + <refentrytitle id="sql-alterforeigndatawrapper-title">ALTER FOREIGN DATA WRAPPER</refentrytitle> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>ALTER FOREIGN DATA WRAPPER</refname> + <refpurpose>change the definition of a foreign-data wrapper</refpurpose> + </refnamediv> + + <indexterm zone="sql-alterforeigndatawrapper"> + <primary>ALTER FOREIGN DATA WRAPPER</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> + [ LIBRARY '<replaceable class="parameter">libraryname</replaceable>' ] + [ OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ]) ] +ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> OWNER TO <replaceable>new_owner</replaceable> +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>ALTER FOREIGN DATA WRAPPER</command> 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 (at least one clause is required). The second + form changes the owner of the foreign-data wrapper. + </para> + + <para> + Only superusers can alter foreign-data wrappers. Additionally, + only superusers can own foreign-data wrappers. + </para> + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><replaceable class="parameter">name</replaceable></term> + <listitem> + <para> + The name of an existing foreign-data wrapper. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">libraryname</replaceable></term> + <listitem> + <para> + New name of the foreign-data wrapper library. + </para> + + <para> + 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. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term> + <listitem> + <para> + Change options for the foreign-data + wrapper. <literal>ADD</>, <literal>SET</>, and <literal>DROP</> + specify the action to be performed. <literal>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. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + Change a foreign-data wrapper <literal>dbi</>, add + option <literal>foo</>, drop <literal>bar</>: +<programlisting> +ALTER FOREIGN DATA WRAPPER dbi OPTIONS (ADD foo '1', DROP 'bar'); +</programlisting> + </para> + + <para> + Change the foreign-data wrapper <literal>dbi</> library + to <literal>/home/bob/mylibrary.so</>: +<programlisting> +ALTER FOREIGN DATA WRAPPER dbi LIBRARY '/home/bob/mylibrary.so'; +</programlisting> + </para> + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + <command>ALTER FOREIGN DATA WRAPPER</command> conforms to ISO/IEC + 9075-9 (SQL/MED). The standard does not specify the <literal>OWNER + TO</> variant of the command. + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-createforeigndatawrapper" endterm="sql-createforeigndatawrapper-title"></member> + <member><xref linkend="sql-dropforeigndatawrapper" endterm="sql-dropforeigndatawrapper-title"></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/alter_server.sgml b/doc/src/sgml/ref/alter_server.sgml new file mode 100644 index 0000000000000000000000000000000000000000..0595bca63bb1de7e091ada19a338e68c891f0b6f --- /dev/null +++ b/doc/src/sgml/ref/alter_server.sgml @@ -0,0 +1,123 @@ +<!-- +$PostgreSQL: pgsql/doc/src/sgml/ref/alter_server.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $ +PostgreSQL documentation +--> + +<refentry id="SQL-ALTERSERVER"> + <refmeta> + <refentrytitle id="sql-alterserver-title">ALTER SERVER</refentrytitle> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>ALTER SERVER</refname> + <refpurpose>change the definition of a foreign server</refpurpose> + </refnamediv> + + <indexterm zone="sql-alterserver"> + <primary>ALTER SERVER</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +ALTER SERVER <replaceable class="parameter">servername</replaceable> [ VERSION 'newversion' ] + [ OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] ) ] +ALTER SERVER <replaceable class="PARAMETER">servername</replaceable> OWNER TO <replaceable>new_owner</replaceable> +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>ALTER SERVER</command> changes the definition of a foreign + server. The first form changes the server version string or the + generic options of the server (at least one clause is required). + The second form changes the owner of the server. + </para> + + <para> + 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 <literal>USAGE</> privilege on the server's foreign-data + wrapper. (Note that superusers satisfy all these criteria + automatically.) + </para> + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><replaceable class="parameter">servername</replaceable></term> + <listitem> + <para> + The name of an existing server. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">serverversion</replaceable></term> + <listitem> + <para> + New server version. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term> + <listitem> + <para> + Change options for the + server. <literal>ADD</>, <literal>SET</>, and <literal>DROP</> + specify the action to be performed. <literal>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. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + Alter server <literal>foo</>, add connection options: +<programlisting> +ALTER SERVER foo OPTIONS (host 'foo', dbname 'foodb'); +</programlisting> + </para> + + <para> + Alter server <literal>foo</>, change version, + change <literal>host</> option: +<programlisting> +ALTER SERVER foo VERSION '8.4' OPTIONS (SET host 'baz'); +</programlisting> + </para> + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + <command>ALTER SERVER</command> conforms to ISO/IEC 9075-9 (SQL/MED). + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-createserver" endterm="sql-createserver-title"></member> + <member><xref linkend="sql-dropserver" endterm="sql-dropserver-title"></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/alter_user_mapping.sgml b/doc/src/sgml/ref/alter_user_mapping.sgml new file mode 100644 index 0000000000000000000000000000000000000000..38bff39ece7204dbbab11761e8acbb1a218630ea --- /dev/null +++ b/doc/src/sgml/ref/alter_user_mapping.sgml @@ -0,0 +1,119 @@ +<!-- +$PostgreSQL: pgsql/doc/src/sgml/ref/alter_user_mapping.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $ +PostgreSQL documentation +--> + +<refentry id="SQL-ALTERUSERMAPPING"> + <refmeta> + <refentrytitle id="sql-alterusermapping-title">ALTER USER MAPPING</refentrytitle> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>ALTER USER MAPPING</refname> + <refpurpose>change the definition of a user mapping</refpurpose> + </refnamediv> + + <indexterm zone="sql-alterusermapping"> + <primary>ALTER USER MAPPING</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +ALTER USER MAPPING FOR { <replaceable class="parameter">username</replaceable> | USER | CURRENT_USER | PUBLIC } + SERVER <replaceable class="parameter">servername</replaceable> + OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] ) +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>ALTER USER MAPPING</command> changes the definition of a + user mapping. Only the owner of the server can change the user + mappings of that server. + </para> + + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><replaceable class="parameter">username</replaceable></term> + <listitem> + <para> + User name of the mapping. <literal>CURRENT_USER</> + and <literal>USER</> match the name of the current + user. <literal>PUBLIC</> is used to match all present and future + user names in the system. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">servername</replaceable></term> + <listitem> + <para> + Server name of the user mapping. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term> + <listitem> + <para> + Change options for the user mapping. The new options override + any previously specified + options. <literal>ADD</>, <literal>SET</>, and <literal>DROP</> + specify the action to be performed. <literal>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. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + Change the password for user mapping <literal>bob</>, server<literal> foo</>: +<programlisting> +ALTER USER MAPPING FOR bob SERVER foo OPTIONS (user 'bob', password 'public'); +</programlisting> + </para> + + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + <command>ALTER USER MAPPING</command> conforms to ISO/IEC 9075-9 + (SQL/MED). There is a subtle syntax issue: The standard omits + the <literal>FOR</literal> key word. Since both <literal>CREATE + USER MAPPING</literal> and <literal>DROP USER MAPPING</literal> use + <literal>FOR</literal> in analogous positions, and IBM DB2 (being + the other major SQL/MED implementation) also requires it + for <literal>ALTER USER MAPPING</literal>, PostgreSQL diverges from + the standard here in the interest of consistency and + interoperability. + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-createusermapping" endterm="sql-createusermapping-title"></member> + <member><xref linkend="sql-dropusermapping" endterm="sql-dropusermapping-title"></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/create_foreign_data_wrapper.sgml b/doc/src/sgml/ref/create_foreign_data_wrapper.sgml new file mode 100644 index 0000000000000000000000000000000000000000..d46e8e664065016fd69cdfdb95c70d0886bd1ab7 --- /dev/null +++ b/doc/src/sgml/ref/create_foreign_data_wrapper.sgml @@ -0,0 +1,185 @@ +<!-- +$PostgreSQL: pgsql/doc/src/sgml/ref/create_foreign_data_wrapper.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $ +PostgreSQL documentation +--> + +<refentry id="SQL-CREATEFOREIGNDATAWRAPPER"> + <refmeta> + <refentrytitle id="sql-createforeigndatawrapper-title">CREATE FOREIGN DATA WRAPPER</refentrytitle> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>CREATE FOREIGN DATA WRAPPER</refname> + <refpurpose>define a new foreign-data wrapper</refpurpose> + </refnamediv> + + <indexterm zone="sql-createforeigndatawrapper"> + <primary>CREATE FOREIGN DATA WRAPPER</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +CREATE FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> + LIBRARY '<replaceable class="parameter">libraryname</replaceable>' + LANGUAGE C + [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ] +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>CREATE FOREIGN DATA WRAPPER</command> creates a new + foreign-data wrapper. The user who defines a foreign-data wrapper + becomes its owner. + </para> + + <para> + The foreign-data wrapper name must be unique within the database. + </para> + + <para> + Only superusers can create foreign-data wrappers. + </para> + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><replaceable class="parameter">name</replaceable></term> + <listitem> + <para> + The name of the foreign-data wrapper to be created. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">libraryname</replaceable></term> + <listitem> + <para> + The name of the shared library implementing the foreign-data + wrapper. The file name is specified in the same way as for + shared library names in <xref linkend="sql-createfunction" + endterm="sql-createfunction-title">; in particular, one can rely + on a search path and automatic addition of the system's standard + shared library file name extension. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>LANGUAGE C</literal></term> + <listitem> + <para> + Currently, only the C programming language is supported for + implementing foreign-data wrappers. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>OPTIONS ( <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term> + <listitem> + <para> + 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. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para> + At the moment, the foreign-data wrapper functionality is very + rudimentary. The purpose of foreign-data wrappers, foreign + servers, and user mappings is to store this information in a + standard way so that it can be queried by interested applications. + The functionality to actually query external data does not exist + yet. + </para> + + <para> + The C language API for foreign-data wrappers is currently not + documented, stable, or complete. Would-be authors of functionality + interfacing with the SQL/MED functionality are advised to contact + the PostgreSQL developers. + </para> + + <para> + There are currently two foreign-data wrapper libraries + provided: <filename>dummy_fdw</filename>, which does nothing and + could be useful for testing, + and <filename>postgresql_fdw</filename>, which accepts options + corresponding to <application>libpq</> connection parameters. + </para> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + Create a foreign-data wrapper <literal>dummy</> with + library <literal>dummy_fdw</>: +<programlisting> +CREATE FOREIGN DATA WRAPPER dummy LIBRARY 'dummy_fdw' LANGUAGE C; +</programlisting> + </para> + + <para> + Create a foreign-data wrapper <literal>postgresql</> with + library <literal>postgresql_fdw</>: +<programlisting> +CREATE FOREIGN DATA WRAPPER postgresql LIBRARY 'postgresql_fdw' LANGUAGE C; +</programlisting> + </para> + + <para> + Create a foreign-data wrapper <literal>mywrapper</> with library + <literal>/home/bob/mywrapper.so</> and some options: +<programlisting> +CREATE FOREIGN DATA WRAPPER mywrapper + LIBRARY '/home/bob/mywrapper.so' + LANGUAGE C + OPTIONS (debug 'true'); +</programlisting> + </para> + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + <command>CREATE FOREIGN DATA WRAPPER</command> conforms to ISO/IEC + 9075-9 (SQL/MED), with the exception that + the <literal>LIBRARY</literal> clause is not optional in + PostgreSQL. + </para> + + <para> + Note, however, that the SQL/MED functionality as a whole is not yet + conforming. + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-alterforeigndatawrapper" endterm="sql-alterforeigndatawrapper-title"></member> + <member><xref linkend="sql-dropforeigndatawrapper" endterm="sql-dropforeigndatawrapper-title"></member> + <member><xref linkend="sql-createserver" endterm="sql-createserver-title"></member> + <member><xref linkend="sql-createusermapping" endterm="sql-createusermapping-title"></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/create_server.sgml b/doc/src/sgml/ref/create_server.sgml new file mode 100644 index 0000000000000000000000000000000000000000..894eecc55639215620bf69f5814e096dec1064cb --- /dev/null +++ b/doc/src/sgml/ref/create_server.sgml @@ -0,0 +1,140 @@ +<!-- +$PostgreSQL: pgsql/doc/src/sgml/ref/create_server.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $ +PostgreSQL documentation +--> + +<refentry id="SQL-CREATESERVER"> + <refmeta> + <refentrytitle id="sql-createserver-title">CREATE SERVER</refentrytitle> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>CREATE SERVER</refname> + <refpurpose>define a new foreign server</refpurpose> + </refnamediv> + + <indexterm zone="sql-createserver"> + <primary>CREATE SERVER</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +CREATE SERVER <replaceable class="parameter">servername</replaceable> [ TYPE 'servertype' ] [ VERSION 'serverversion' ] + FOREIGN DATA WRAPPER <replaceable class="parameter">fdwname</replaceable> + [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ] +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>CREATE SERVER</command> defines a new foreign server. The + user who defines the server becomes its owner. + </para> + + <para> + The server name must be unique within database. + </para> + + <para> + Creating a server requires <literal>USAGE</> privilege on the + foreign-data wrapper being used. + </para> + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><replaceable class="parameter">servername</replaceable></term> + <listitem> + <para> + The name of the foreign server to be created. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">servertype</replaceable></term> + <listitem> + <para> + Optional server type. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">serverversion</replaceable></term> + <listitem> + <para> + Optional server version. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">fdwname</replaceable></term> + <listitem> + <para> + The name of the foreign-data wrapper that manages the server. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>OPTIONS ( <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term> + <listitem> + <para> + 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. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + Create a server <literal>foo</> that uses the built-in foreign-data + wrapper <literal>default</>: +<programlisting> +CREATE SERVER foo FOREIGN DATA WRAPPER "default"; +</programlisting> + </para> + + <para> + Create a server <literal>myserver</> that uses the + foreign-data wrapper <literal>pgsql</>: +<programlisting> +CREATE SERVER myserver FOREIGN DATA WRAPPER pgsql OPTIONS (host 'foo', dbname 'foodb', port '5432'); +</programlisting> + </para> + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + <command>CREATE SERVER</command> conforms to ISO/IEC 9075-9 (SQL/MED). + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-alterserver" endterm="sql-alterserver-title"></member> + <member><xref linkend="sql-dropserver" endterm="sql-dropserver-title"></member> + <member><xref linkend="sql-createforeigndatawrapper" endterm="sql-createforeigndatawrapper-title"></member> + <member><xref linkend="sql-createusermapping" endterm="sql-createusermapping-title"></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/create_user_mapping.sgml b/doc/src/sgml/ref/create_user_mapping.sgml new file mode 100644 index 0000000000000000000000000000000000000000..07cfb0a4c209693ec607873668cbd6c207c244b9 --- /dev/null +++ b/doc/src/sgml/ref/create_user_mapping.sgml @@ -0,0 +1,111 @@ +<!-- +$PostgreSQL: pgsql/doc/src/sgml/ref/create_user_mapping.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $ +PostgreSQL documentation +--> + +<refentry id="SQL-CREATEUSERMAPPING"> + <refmeta> + <refentrytitle id="sql-createusermapping-title">CREATE USER MAPPING</refentrytitle> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>CREATE USER MAPPING</refname> + <refpurpose>define a new mapping of a user to a foreign server</refpurpose> + </refnamediv> + + <indexterm zone="sql-createusermapping"> + <primary>CREATE USER MAPPING</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +CREATE USER MAPPING FOR { <replaceable class="parameter">username</replaceable> | USER | CURRENT_USER | PUBLIC } + SERVER <replaceable class="parameter">servername</replaceable> + [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [ , ... ] ) ] +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>CREATE USER MAPPING</command> defines a mapping of a user + to a foreign server. You must be the owner of the server to define + user mappings for it. + </para> + + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><replaceable class="parameter">username</replaceable></term> + <listitem> + <para> + The name of an existing user that is mapped to foreign server. + <literal>CURRENT_USER</> and <literal>USER</> match the name of + the current user. <literal>PUBLIC</> is used to match all + present and future user names in the system. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">servername</replaceable></term> + <listitem> + <para> + The name of an existing server for which the user mapping is + to be created. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>OPTIONS ( <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term> + <listitem> + <para> + 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. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + Create a user mapping for user <literal>bob</>, server <literal>foo</>: +<programlisting> +CREATE USER MAPPING FOR bob SERVER foo OPTIONS (user 'bob', password 'secret'); +</programlisting> + </para> + + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + <command>CREATE USER MAPPING</command> conforms to ISO/IEC 9075-9 (SQL/MED). + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-alterusermapping" endterm="sql-alterusermapping-title"></member> + <member><xref linkend="sql-dropusermapping" endterm="sql-dropusermapping-title"></member> + <member><xref linkend="sql-createforeigndatawrapper" endterm="sql-createforeigndatawrapper-title"></member> + <member><xref linkend="sql-createserver" endterm="sql-createserver-title"></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/drop_foreign_data_wrapper.sgml b/doc/src/sgml/ref/drop_foreign_data_wrapper.sgml new file mode 100644 index 0000000000000000000000000000000000000000..e28b790ceafca11f6aa563a8d411c651fc643e13 --- /dev/null +++ b/doc/src/sgml/ref/drop_foreign_data_wrapper.sgml @@ -0,0 +1,112 @@ +<!-- +$PostgreSQL: pgsql/doc/src/sgml/ref/drop_foreign_data_wrapper.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $ +PostgreSQL documentation +--> + +<refentry id="SQL-DROPFOREIGNDATAWRAPPER"> + <refmeta> + <refentrytitle id="sql-dropforeigndatawrapper-title">DROP FOREIGN DATA WRAPPER</refentrytitle> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>DROP FOREIGN DATA WRAPPER</refname> + <refpurpose>remove a foreign-data wrapper</refpurpose> + </refnamediv> + + <indexterm zone="sql-dropforeigndatawrapper"> + <primary>DROP FOREIGN DATA WRAPPER</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +DROP FOREIGN DATA WRAPPER [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ CASCADE | RESTRICT ] +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>DROP FOREIGN DATA WRAPPER</command> removes an existing + foreign-data wrapper. To execute this command, the current user + must be the owner of the foreign-data wrapper. + </para> + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><literal>IF EXISTS</literal></term> + <listitem> + <para> + Do not throw an error if the foreign-data wrapper does not + exist. A notice is issued in this case. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">name</replaceable></term> + <listitem> + <para> + The name of an existing foreign-data wrapper. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>CASCADE</literal></term> + <listitem> + <para> + Automatically drop objects that depend on the foreign-data + wrapper (such as servers). + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>RESTRICT</literal></term> + <listitem> + <para> + Refuse to drop the foreign-data wrappers if any objects depend + on it. This is the default. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + Drop the foreign-data wrapper <literal>dbi</>: +<programlisting> +DROP FOREIGN DATA WRAPPER dbi; +</programlisting> + </para> + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + <command>DROP FOREIGN DATA WRAPPER</command> conforms to ISO/IEC + 9075-9 (SQL/MED). The <literal>IF EXISTS</> clause is + a <productname>PostgreSQL</> extension. + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-createforeigndatawrapper" endterm="sql-createforeigndatawrapper-title"></member> + <member><xref linkend="sql-alterforeigndatawrapper" endterm="sql-alterforeigndatawrapper-title"></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/drop_server.sgml b/doc/src/sgml/ref/drop_server.sgml new file mode 100644 index 0000000000000000000000000000000000000000..b9ddac51b577c836f665f0acf697db630438b68d --- /dev/null +++ b/doc/src/sgml/ref/drop_server.sgml @@ -0,0 +1,112 @@ +<!-- +$PostgreSQL: pgsql/doc/src/sgml/ref/drop_server.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $ +PostgreSQL documentation +--> + +<refentry id="SQL-DROPSERVER"> + <refmeta> + <refentrytitle id="sql-dropserver-title">DROP SERVER</refentrytitle> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>DROP SERVER</refname> + <refpurpose>remove a foreign server descriptor</refpurpose> + </refnamediv> + + <indexterm zone="sql-dropserver"> + <primary>DROP SERVER</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +DROP SERVER [ IF EXISTS ] <replaceable class="parameter">servername</replaceable> [ CASCADE | RESTRICT ] +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>DROP SERVER</command> removes an existing foreign server + descriptor. To execute this command, the current user must be the + owner of the server. + </para> + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><literal>IF EXISTS</literal></term> + <listitem> + <para> + Do not throw an error if the server does not exist. A notice is + issued in this case. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">servername</replaceable></term> + <listitem> + <para> + The name of an existing server. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>CASCADE</literal></term> + <listitem> + <para> + Automatically drop objects that depend on the server (such as + user mappings). + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>RESTRICT</literal></term> + <listitem> + <para> + Refuse to drop the server if any objects depend on it. This is + the default. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + Drop a server <literal>foo</> if it exists: +<programlisting> +DROP SERVER IF EXISTS foo; +</programlisting> + </para> + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + <command>DROP SERVER</command> conforms to ISO/IEC 9075-9 + (SQL/MED). The <literal>IF EXISTS</> clause is + a <productname>PostgreSQL</> extension. + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-createserver" endterm="sql-createserver-title"></member> + <member><xref linkend="sql-alterserver" endterm="sql-alterserver-title"></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/drop_user_mapping.sgml b/doc/src/sgml/ref/drop_user_mapping.sgml new file mode 100644 index 0000000000000000000000000000000000000000..c22dedb661ad5227a6cf3ac0fce85173052cdb2b --- /dev/null +++ b/doc/src/sgml/ref/drop_user_mapping.sgml @@ -0,0 +1,104 @@ +<!-- +$PostgreSQL: pgsql/doc/src/sgml/ref/drop_user_mapping.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $ +PostgreSQL documentation +--> + +<refentry id="SQL-DROPUSERMAPPING"> + <refmeta> + <refentrytitle id="sql-dropusermapping-title">DROP USER MAPPING</refentrytitle> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>DROP USER MAPPING</refname> + <refpurpose>remove a user mapping for a foreign server</refpurpose> + </refnamediv> + + <indexterm zone="sql-dropusermapping"> + <primary>DROP USER MAPPING</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +DROP USER MAPPING [ IF EXISTS ] FOR { <replaceable class="parameter">username</replaceable> | USER | CURRENT_USER | PUBLIC } SERVER <replaceable class="parameter">servername</replaceable> +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>DROP USER MAPPING</command> 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. + </para> + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><literal>IF EXISTS</literal></term> + <listitem> + <para> + Do not throw an error if the user mapping does not exist. A + notice is issued in this case. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">username</replaceable></term> + <listitem> + <para> + User name of the mapping. <literal>CURRENT_USER</> + and <literal>USER</> match the name of the current + user. <literal>PUBLIC</> is used to match all present and + future user names in the system. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">servername</replaceable></term> + <listitem> + <para> + Server name of the user mapping. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + Drop a user mapping <literal>bob</>, server <literal>foo</> if it exists: +<programlisting> +DROP USER MAPPING IF EXISTS FOR bob SERVER foo; +</programlisting> + </para> + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + <command>DROP USER MAPPING</command> conforms to ISO/IEC 9075-9 + (SQL/MED). The <literal>IF EXISTS</> clause is + a <productname>PostgreSQL</> extension. + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-createusermapping" endterm="sql-createusermapping-title"></member> + <member><xref linkend="sql-alterusermapping" endterm="sql-alterusermapping-title"></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml index 5c907e1ef0518ebb20ebc88f46a50ec8cacd1dbb..dafb8ffb523885fda8d99b1804a72d4380a8a10f 100644 --- a/doc/src/sgml/ref/grant.sgml +++ b/doc/src/sgml/ref/grant.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.72 2008/11/14 10:22:47 petere Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.73 2008/12/19 16:25:16 petere Exp $ PostgreSQL documentation --> @@ -35,6 +35,14 @@ GRANT { { CREATE | CONNECT | TEMPORARY | TEMP } [,...] | ALL [ PRIVILEGES ] } ON DATABASE <replaceable>dbname</replaceable> [, ...] TO { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ] +GRANT { USAGE | ALL [ PRIVILEGES ] } + ON FOREIGN DATA WRAPPER <replaceable>fdwname</> [, ...] + TO { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ] + +GRANT { USAGE | ALL [ PRIVILEGES ] } + ON FOREIGN SERVER <replaceable>servername</> [, ...] + TO { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ] + GRANT { EXECUTE | ALL [ PRIVILEGES ] } ON FUNCTION <replaceable>funcname</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) [, ...] TO { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ] @@ -61,10 +69,10 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <replaceable <para> The <command>GRANT</command> 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. + 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. </para> <para> @@ -299,6 +307,14 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <replaceable For sequences, this privilege allows the use of the <function>currval</function> and <function>nextval</function> functions. </para> + <para> + For foreign-data wrappers, this privilege enables the grantee + to create new servers using that foreign-data wrapper. + </para> + <para> + For servers, this privilege enables the grantee to query the + options of the server and associated user mappings. + </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index fa7f68caf40ecaeb5d47acb361535eb7159e9c22..fb394b16eefe4bf3d6d5785f739948d75921dd4a 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.213 2008/12/01 09:20:37 petere Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.214 2008/12/19 16:25:16 petere Exp $ PostgreSQL documentation --> @@ -946,6 +946,64 @@ testdb=> </varlistentry> + <varlistentry> + <term><literal>\des [ <replaceable class="parameter">pattern</replaceable> ]</literal></term> + <term><literal>\des+ [ <replaceable class="parameter">pattern</replaceable> ]</literal></term> + <listitem> + <para> + Lists all foreign servers (mnemonic: <quote>external + servers</quote>). + If <replaceable class="parameter">pattern</replaceable> is + specified, only those servers whose name matches the pattern + are listed. If the form <literal>\des+</literal> is used, a + full desription of each server is shown, including the + server's ACL, type, version, and options. + </para> + </listitem> + </varlistentry> + + + <varlistentry> + <term><literal>\deu [ <replaceable class="parameter">pattern</replaceable> ]</literal></term> + <term><literal>\deu+ [ <replaceable class="parameter">pattern</replaceable> ]</literal></term> + <listitem> + <para> + Lists all user mappings (mnemonic: <quote>external + users</quote>). + If <replaceable class="parameter">pattern</replaceable> is + specified, only those mappings whose user names match the + pattern are listed. If the form <literal>\deu+</literal> is + used, additional information about each mapping is shown. + </para> + + <caution> + <para> + <literal>\deu+</literal> might also display the user name and + password of the remote user, so care should be taken not to + disclose them. + </para> + </caution> + </listitem> + </varlistentry> + + + <varlistentry> + <term><literal>\dew [ <replaceable class="parameter">pattern</replaceable> ]</literal></term> + <term><literal>\dew+ [ <replaceable class="parameter">pattern</replaceable> ]</literal></term> + <listitem> + <para> + Lists all foreign-data wrappers (mnemonic: <quote>external + wrappers</quote>). + If <replaceable class="parameter">pattern</replaceable> is + specified, only those foreign-data wrappers whose name matches + the pattern are listed. If the form <literal>\dew+</literal> + is used, the ACL and options of the foreign-data wrapper are + also shown. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>\df [ <replaceable class="parameter">pattern</replaceable> ]</literal></term> <term><literal>\df+ [ <replaceable class="parameter">pattern</replaceable> ]</literal></term> diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml index 11f576e0fb7746837e2d04e7f373ad7886ea6de9..c8e91e0a159aa2a14d94508418ea418032be7dd9 100644 --- a/doc/src/sgml/ref/revoke.sgml +++ b/doc/src/sgml/ref/revoke.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/revoke.sgml,v 1.49 2008/11/14 10:22:47 petere Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/revoke.sgml,v 1.50 2008/12/19 16:25:16 petere Exp $ PostgreSQL documentation --> @@ -41,6 +41,18 @@ REVOKE [ GRANT OPTION FOR ] FROM { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] [ CASCADE | RESTRICT ] +REVOKE [ GRANT OPTION FOR ] + { USAGE | ALL [ PRIVILEGES ] } + ON FOREIGN DATA WRAPPER <replaceable>fdwname</replaceable> [, ...] + FROM { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { USAGE | ALL [ PRIVILEGES ] } + ON FOREIGN SERVER <replaceable>servername</replaceable> [, ...] + FROM { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] + [ CASCADE | RESTRICT ] + REVOKE [ GRANT OPTION FOR ] { EXECUTE | ALL [ PRIVILEGES ] } ON FUNCTION <replaceable>funcname</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) [, ...] diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index a0aed8edf535689d54f3a1ac4eca16b8fddc4bfd..d3a862959d900c224ecd0207fcc86f652cfe1974 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.66 2008/03/27 17:24:16 momjian Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.67 2008/12/19 16:25:16 petere Exp $ --> <part id="reference"> <title>Reference</title> @@ -38,6 +38,7 @@ &alterConversion; &alterDatabase; &alterDomain; + &alterForeignDataWrapper; &alterFunction; &alterGroup; &alterIndex; @@ -48,6 +49,7 @@ &alterRole; &alterSchema; &alterSequence; + &alterServer; &alterTable; &alterTableSpace; &alterTSConfig; @@ -57,6 +59,7 @@ &alterTrigger; &alterType; &alterUser; + &alterUserMapping; &alterView; &analyze; &begin; @@ -73,6 +76,7 @@ &createConversion; &createDatabase; &createDomain; + &createForeignDataWrapper; &createFunction; &createGroup; &createIndex; @@ -84,6 +88,7 @@ &createRule; &createSchema; &createSequence; + &createServer; &createTable; &createTableAs; &createTableSpace; @@ -94,6 +99,7 @@ &createTrigger; &createType; &createUser; + &createUserMapping; &createView; &deallocate; &declare; @@ -104,6 +110,7 @@ &dropConversion; &dropDatabase; &dropDomain; + &dropForeignDataWrapper; &dropFunction; &dropGroup; &dropIndex; @@ -116,6 +123,7 @@ &dropRule; &dropSchema; &dropSequence; + &dropServer; &dropTable; &dropTableSpace; &dropTSConfig; @@ -125,6 +133,7 @@ &dropTrigger; &dropType; &dropUser; + &dropUserMapping; &dropView; &end; &execute; diff --git a/src/Makefile b/src/Makefile index 61950967fde651165d9bcbb986448722e99d1ca7..13cbeb973456f9aa94e3c4435fd89bbfcc11c15c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,7 +4,7 @@ # # Copyright (c) 1994, Regents of the University of California # -# $PostgreSQL: pgsql/src/Makefile,v 1.43 2008/03/18 16:24:50 petere Exp $ +# $PostgreSQL: pgsql/src/Makefile,v 1.44 2008/12/19 16:25:16 petere Exp $ # #------------------------------------------------------------------------- @@ -19,6 +19,7 @@ all install installdirs uninstall distprep: $(MAKE) -C backend $@ $(MAKE) -C backend/utils/mb/conversion_procs $@ $(MAKE) -C backend/snowball $@ + $(MAKE) -C backend/foreign $@-fdw $(MAKE) -C include $@ $(MAKE) -C interfaces $@ $(MAKE) -C bin $@ diff --git a/src/backend/Makefile b/src/backend/Makefile index 7fd613f6c9fc5d6b93efd2ed0daae88a9b910bf7..1903e554cf952580b5ceb16a13dbe1f8ba25336f 100644 --- a/src/backend/Makefile +++ b/src/backend/Makefile @@ -5,7 +5,7 @@ # Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # -# $PostgreSQL: pgsql/src/backend/Makefile,v 1.130 2008/08/29 13:02:32 petere Exp $ +# $PostgreSQL: pgsql/src/backend/Makefile,v 1.131 2008/12/19 16:25:16 petere Exp $ # #------------------------------------------------------------------------- @@ -14,7 +14,7 @@ subdir = src/backend top_builddir = ../.. include $(top_builddir)/src/Makefile.global -SUBDIRS = access bootstrap catalog parser commands executor lib libpq \ +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 --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index bb4c42135c554f2b0fc447ee142ac3e27523e0d9..58973c9a88991588372d59b42e8ddae702447d84 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -2,7 +2,7 @@ # # Makefile for backend/catalog # -# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.67 2008/11/19 10:34:51 heikki Exp $ +# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.68 2008/12/19 16:25:16 petere Exp $ # #------------------------------------------------------------------------- @@ -36,6 +36,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ 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 --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index f25c7608e41e4ee3d5f9e9207b1d44bf52f62570..cecfe84e7a3c41f03ac56e7c05976fee07cf15ca 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.149 2008/11/02 01:45:27 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.150 2008/12/19 16:25:16 petere Exp $ * * NOTES * See acl.h. @@ -27,6 +27,8 @@ #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" @@ -38,6 +40,7 @@ #include "catalog/pg_ts_config.h" #include "catalog/pg_ts_dict.h" #include "commands/dbcommands.h" +#include "foreign/foreign.h" #include "miscadmin.h" #include "parser/parse_func.h" #include "utils/acl.h" @@ -50,6 +53,8 @@ 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,6 +193,12 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs, 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,6 +334,14 @@ ExecuteGrantStmt(GrantStmt *stmt) 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,6 +399,12 @@ ExecGrantStmt_oids(InternalGrant *istmt) 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,6 +545,24 @@ objectNamesToOids(GrantObjectType objtype, List *objnames) heap_close(relation, AccessShareLock); } break; + case ACL_OBJECT_FDW: + foreach(cell, objnames) + { + char *fdwname = strVal(lfirst(cell)); + Oid fdwid = GetForeignDataWrapperOidByName(fdwname, false); + + objects = lappend_oid(objects, fdwid); + } + break; + case ACL_OBJECT_FOREIGN_SERVER: + foreach(cell, objnames) + { + char *srvname = strVal(lfirst(cell)); + Oid srvid = GetForeignServerOidByName(srvname, false); + + objects = lappend_oid(objects, srvid); + } + break; default: elog(ERROR, "unrecognized GrantStmt.objtype: %d", (int) objtype); @@ -840,6 +883,239 @@ ExecGrant_Database(InternalGrant *istmt) heap_close(relation, RowExclusiveLock); } +static void +ExecGrant_Fdw(InternalGrant *istmt) +{ + Relation relation; + ListCell *cell; + + if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) + istmt->privileges = ACL_ALL_RIGHTS_FDW; + + relation = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock); + + foreach(cell, istmt->objects) + { + Oid fdwid = lfirst_oid(cell); + Form_pg_foreign_data_wrapper pg_fdw_tuple; + Datum aclDatum; + bool isNull; + AclMode avail_goptions; + AclMode this_privileges; + Acl *old_acl; + Acl *new_acl; + Oid grantorId; + Oid ownerId; + HeapTuple tuple; + HeapTuple newtuple; + Datum values[Natts_pg_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) { @@ -1428,7 +1704,11 @@ static const char *const no_priv_msg[MAX_ACL_KIND] = /* ACL_KIND_TSDICTIONARY */ gettext_noop("permission denied for text search dictionary %s"), /* ACL_KIND_TSCONFIGURATION */ - gettext_noop("permission denied for text search configuration %s") + 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,7 +1740,11 @@ static const char *const not_owner_msg[MAX_ACL_KIND] = /* ACL_KIND_TSDICTIONARY */ gettext_noop("must be owner of text search dictionary %s"), /* ACL_KIND_TSCONFIGURATION */ - gettext_noop("must be owner of text search configuration %s") + 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,6 +1818,10 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, Oid roleid, 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,6 +2250,131 @@ pg_tablespace_aclmask(Oid spc_oid, Oid roleid, 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,6 +2452,31 @@ pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode) 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,6 +2802,34 @@ pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid) 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 --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 8cd1abdf1b95428008ecb2f3f31036f8b07c9ce3..2cbc19f5a0621dd63be004216399a31601c45031 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.82 2008/11/10 21:49:16 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.83 2008/12/19 16:25:17 petere Exp $ * *------------------------------------------------------------------------- */ @@ -33,6 +33,8 @@ #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,6 +49,7 @@ #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,6 +58,7 @@ #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,6 +1109,18 @@ doDeletion(const ObjectAddress *object) 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,6 +2021,18 @@ getObjectClass(const ObjectAddress *object) 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,6 +2529,50 @@ getObjectDescription(const ObjectAddress *object) 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 --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 970b48b7dff3487898e06444acca14ef6995a803..2d9cdb8e2248d8b90aa6aab554e527e0fb10ab9c 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -4,7 +4,7 @@ * * Copyright (c) 2003-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.46 2008/09/08 00:47:40 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.47 2008/12/19 16:25:17 petere Exp $ */ /* @@ -1244,19 +1244,53 @@ GRANT SELECT ON role_table_grants TO PUBLIC; * ROLE_USAGE_GRANTS view */ --- See USAGE_PRIVILEGES. - CREATE VIEW role_usage_grants AS - SELECT CAST(null AS sql_identifier) AS grantor, - CAST(null AS sql_identifier) AS grantee, + + /* foreign-data wrappers */ + SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor, + CAST(g_grantee.rolname AS sql_identifier) AS grantee, CAST(current_database() AS sql_identifier) AS object_catalog, - CAST(null AS sql_identifier) AS object_schema, - CAST(null AS sql_identifier) AS object_name, - CAST(null AS character_data) AS object_type, + CAST('' AS sql_identifier) AS object_schema, + CAST(fdw.fdwname AS sql_identifier) AS object_name, + CAST('FOREIGN DATA WRAPPER' AS character_data) AS object_type, CAST('USAGE' AS character_data) AS privilege_type, - CAST(null AS character_data) AS is_grantable + CAST( + CASE WHEN aclcontains(fdw.fdwacl, + makeaclitem(g_grantee.oid, u_grantor.oid, 'USAGE', true)) + THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable - WHERE false; + FROM pg_foreign_data_wrapper fdw, + pg_authid u_grantor, + pg_authid g_grantee + + WHERE aclcontains(fdw.fdwacl, + makeaclitem(g_grantee.oid, u_grantor.oid, 'USAGE', false)) + AND (u_grantor.rolname IN (SELECT role_name FROM enabled_roles) + OR g_grantee.rolname IN (SELECT role_name FROM enabled_roles)) + + UNION ALL + + /* foreign server */ + SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor, + CAST(g_grantee.rolname AS sql_identifier) AS grantee, + CAST(current_database() AS sql_identifier) AS object_catalog, + CAST('' AS sql_identifier) AS object_schema, + CAST(srv.srvname AS sql_identifier) AS object_name, + CAST('FOREIGN SERVER' AS character_data) AS object_type, + CAST('USAGE' AS character_data) AS privilege_type, + CAST( + CASE WHEN aclcontains(srv.srvacl, + makeaclitem(g_grantee.oid, u_grantor.oid, 'USAGE', true)) + THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable + + FROM pg_foreign_server srv, + pg_authid u_grantor, + pg_authid g_grantee + + WHERE aclcontains(srv.srvacl, + makeaclitem(g_grantee.oid, u_grantor.oid, 'USAGE', false)) + AND (u_grantor.rolname IN (SELECT role_name FROM enabled_roles) + OR g_grantee.rolname IN (SELECT role_name FROM enabled_roles)); GRANT SELECT ON role_usage_grants TO PUBLIC; @@ -2007,11 +2041,10 @@ GRANT SELECT ON triggers TO PUBLIC; * USAGE_PRIVILEGES view */ --- Of the things currently implemented in PostgreSQL, usage privileges --- apply only to domains. Since domains have no real privileges, we --- represent all domains with implicit usage privilege here. - CREATE VIEW usage_privileges AS + + /* domains */ + -- Domains have no real privileges, so we represent all domains with implicit usage privilege here. SELECT CAST(u.rolname AS sql_identifier) AS grantor, CAST('PUBLIC' AS sql_identifier) AS grantee, CAST(current_database() AS sql_identifier) AS object_catalog, @@ -2027,7 +2060,65 @@ CREATE VIEW usage_privileges AS WHERE u.oid = t.typowner AND t.typnamespace = n.oid - AND t.typtype = 'd'; + AND t.typtype = 'd' + + UNION ALL + + /* foreign-data wrappers */ + SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor, + CAST(grantee.rolname AS sql_identifier) AS grantee, + CAST(current_database() AS sql_identifier) AS object_catalog, + CAST('' AS sql_identifier) AS object_schema, + CAST(fdw.fdwname AS sql_identifier) AS object_name, + CAST('FOREIGN DATA WRAPPER' AS character_data) AS object_type, + CAST('USAGE' AS character_data) AS privilege_type, + CAST( + CASE WHEN aclcontains(fdw.fdwacl, + makeaclitem(grantee.oid, u_grantor.oid, 'USAGE', true)) + THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable + + FROM pg_foreign_data_wrapper fdw, + pg_authid u_grantor, + ( + SELECT oid, rolname FROM pg_authid + UNION ALL + SELECT 0::oid, 'PUBLIC' + ) AS grantee (oid, rolname) + + WHERE aclcontains(fdw.fdwacl, + makeaclitem(grantee.oid, u_grantor.oid, 'USAGE', false)) + AND (pg_has_role(u_grantor.oid, 'USAGE') + OR pg_has_role(grantee.oid, 'USAGE') + OR grantee.rolname = 'PUBLIC') + + UNION ALL + + /* foreign servers */ + SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor, + CAST(grantee.rolname AS sql_identifier) AS grantee, + CAST(current_database() AS sql_identifier) AS object_catalog, + CAST('' AS sql_identifier) AS object_schema, + CAST(srv.srvname AS sql_identifier) AS object_name, + CAST('FOREIGN SERVER' AS character_data) AS object_type, + CAST('USAGE' AS character_data) AS privilege_type, + CAST( + CASE WHEN aclcontains(srv.srvacl, + makeaclitem(grantee.oid, u_grantor.oid, 'USAGE', true)) + THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable + + FROM pg_foreign_server srv, + pg_authid u_grantor, + ( + SELECT oid, rolname FROM pg_authid + UNION ALL + SELECT 0::oid, 'PUBLIC' + ) AS grantee (oid, rolname) + + WHERE aclcontains(srv.srvacl, + makeaclitem(grantee.oid, u_grantor.oid, 'USAGE', false)) + AND (pg_has_role(u_grantor.oid, 'USAGE') + OR pg_has_role(grantee.oid, 'USAGE') + OR grantee.rolname = 'PUBLIC'); GRANT SELECT ON usage_privileges TO PUBLIC; @@ -2313,3 +2404,139 @@ CREATE VIEW element_types AS 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, + w.fdwowner, + w.fdwoptions, + CAST(current_database() AS sql_identifier) AS foreign_data_wrapper_catalog, + CAST(fdwname AS sql_identifier) AS foreign_data_wrapper_name, + CAST(u.rolname AS sql_identifier) AS authorization_identifier, + CAST(fdwlibrary AS character_data) AS library_name, + CAST('c' AS character_data) AS foreign_data_wrapper_language + FROM pg_foreign_data_wrapper w, pg_authid u + WHERE u.oid = w.fdwowner + AND (pg_has_role(fdwowner, 'USAGE') + OR has_foreign_data_wrapper_privilege(w.oid, '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_options_to_table(w.fdwoptions)).option_name AS sql_identifier) AS option_name, + CAST((pg_options_to_table(w.fdwoptions)).option_value AS character_data) AS option_value + FROM _pg_foreign_data_wrappers w; + +GRANT SELECT ON foreign_data_wrapper_options TO PUBLIC; + + +/* + * 24.5 + * FOREIGN_DATA_WRAPPERS view + */ +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 w; + +GRANT SELECT ON foreign_data_wrappers TO PUBLIC; + + +/* Base view for foreign servers */ +CREATE VIEW _pg_foreign_servers AS + SELECT s.oid, + s.srvoptions, + CAST(current_database() AS sql_identifier) AS foreign_server_catalog, + CAST(srvname AS sql_identifier) 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_authid u + WHERE w.oid = s.srvfdw + AND u.oid = s.srvowner + AND (pg_has_role(s.srvowner, 'USAGE') + OR has_server_privilege(s.oid, 'USAGE')); + + +/* + * 24.6 + * FOREIGN_SERVER_OPTIONS view + */ +CREATE VIEW foreign_server_options AS + SELECT foreign_server_catalog, + foreign_server_name, + CAST((pg_options_to_table(s.srvoptions)).option_name AS sql_identifier) AS option_name, + CAST((pg_options_to_table(s.srvoptions)).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, + um.umoptions, + 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_options_to_table(um.umoptions)).option_name AS sql_identifier) AS option_name, + CAST((pg_options_to_table(um.umoptions)).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 --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt index 402bc5e3919020ee7c76f8715a5cded6ab7dffb6..5b387456fd3762ddaaca65fb1089821edf57f300 100644 --- a/src/backend/catalog/sql_features.txt +++ b/src/backend/catalog/sql_features.txt @@ -487,6 +487,31 @@ T652 SQL-dynamic statements in SQL routines NO 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 --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 07c0809609d6836679f334a3bf6627d45bc5c743..4223e5d1940420c4b176429b2b020fddd6b84eca 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -3,7 +3,7 @@ * * Copyright (c) 1996-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.56 2008/11/09 21:24:32 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.57 2008/12/19 16:25:17 petere Exp $ */ CREATE VIEW pg_roles AS @@ -381,6 +381,28 @@ CREATE VIEW pg_stat_bgwriter AS 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, + CASE WHEN pg_has_role(S.srvowner, 'USAGE') OR has_server_privilege(S.oid, 'USAGE') THEN + U.umoptions + ELSE + NULL + END AS umoptions + 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; + -- Tsearch debug function. Defined here because it'd be pretty unwieldy -- to put it into pg_proc.h diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile index 49aefde416458e3cf59ffa1e9c6206e8c09d7abd..e455e62c9aa8e69a501ae41c402a27c01abb0d75 100644 --- a/src/backend/commands/Makefile +++ b/src/backend/commands/Makefile @@ -4,7 +4,7 @@ # Makefile for backend/commands # # IDENTIFICATION -# $PostgreSQL: pgsql/src/backend/commands/Makefile,v 1.38 2008/02/19 10:30:07 petere Exp $ +# $PostgreSQL: pgsql/src/backend/commands/Makefile,v 1.39 2008/12/19 16:25:17 petere Exp $ # #------------------------------------------------------------------------- @@ -14,7 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \ conversioncmds.o copy.o \ - dbcommands.o define.o discard.o explain.o functioncmds.o \ + 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 --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index fce328bcc404b3942f5a40359d40f17af5d16c1a..3772638766cad20ffb856ec1d18f919add28630b 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.29 2008/06/15 01:25:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.30 2008/12/19 16:25:17 petere Exp $ * *------------------------------------------------------------------------- */ @@ -270,6 +270,15 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) AlterTSConfigurationOwner(stmt->object, newowner); break; + case OBJECT_FDW: + AlterForeignDataWrapperOwner(strVal(linitial(stmt->object)), + newowner); + break; + + case OBJECT_FOREIGN_SERVER: + AlterForeignServerOwner(strVal(linitial(stmt->object)), newowner); + break; + default: elog(ERROR, "unrecognized AlterOwnerStmt type: %d", (int) stmt->objectType); diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c new file mode 100644 index 0000000000000000000000000000000000000000..224a8f004e63a0b664237172a3f28af40e5c96d7 --- /dev/null +++ b/src/backend/commands/foreigncmds.c @@ -0,0 +1,1102 @@ +/*------------------------------------------------------------------------- + * + * foreigncmds.c + * foreign-data wrapper/server creation/manipulation commands + * + * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/commands/foreigncmds.c,v 1.1 2008/12/19 16:25:17 petere Exp $ + * + *------------------------------------------------------------------------- + */ +#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/pg_foreign_data_wrapper.h" +#include "catalog/pg_foreign_server.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; + } + + /* + * It is 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(const char *name, 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\"", + name), + 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\"", + name), + errhint("The owner of a foreign-data wrapper must be a superuser."))); + + rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock); + + tup = SearchSysCacheCopy(FOREIGNDATAWRAPPERNAME, + CStringGetDatum(name), + 0, 0, 0); + + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("foreign-data wrapper \"%s\" does not exist", name))); + + fdwId = HeapTupleGetOid(tup); + 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, + fdwId, + newOwnerId); + } + + heap_close(rel, NoLock); + heap_freetuple(tup); +} + + +/* + * Change foreign server owner + */ +void +AlterForeignServerOwner(const char *name, Oid newOwnerId) +{ + HeapTuple tup; + Relation rel; + Oid srvId; + AclResult aclresult; + Form_pg_foreign_server form; + + rel = heap_open(ForeignServerRelationId, RowExclusiveLock); + + tup = SearchSysCacheCopy(FOREIGNSERVERNAME, + CStringGetDatum(name), + 0, 0, 0); + + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("server \"%s\" does not exist", name))); + + srvId = HeapTupleGetOid(tup); + form = (Form_pg_foreign_server) GETSTRUCT(tup); + + 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, + name); + + /* Must be able to become new owner */ + check_is_member_of_role(GetUserId(), newOwnerId); + + /* 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; + Datum values[Natts_pg_foreign_data_wrapper]; + bool nulls[Natts_pg_foreign_data_wrapper]; + HeapTuple tuple; + Oid fdwId; + Datum fdwoptions = InvalidOid; + Oid ownerId; + ForeignDataWrapperLibrary *fdwlib; + + /* Must be super user */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to create foreign-data wrapper \"%s\"", + 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 (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("foreign-data wrapper \"%s\" already exists", + stmt->fdwname))); + + /* + * Insert tuple into pg_foreign_data_wrapper. + */ + rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock); + + MemSet(nulls, false, Natts_pg_foreign_data_wrapper); + + values[Anum_pg_foreign_data_wrapper_fdwname - 1] = + DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname)); + 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] = true; + + /* + * 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] = true; + + tuple = heap_form_tuple(rel->rd_att, values, nulls); + + fdwId = simple_heap_insert(rel, tuple); + CatalogUpdateIndexes(rel, tuple); + + heap_freetuple(tuple); + + 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\"", + stmt->fdwname), + errhint("Must be superuser to alter a foreign-data wrapper."))); + + tp = SearchSysCacheCopy(FOREIGNDATAWRAPPERNAME, + CStringGetDatum(stmt->fdwname), + 0, 0, 0); + + if (!HeapTupleIsValid(tp)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname))); + + fdwId = HeapTupleGetOid(tp); + + 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) +{ + Oid fdwId; + ObjectAddress object; + + fdwId = GetForeignDataWrapperOidByName(stmt->fdwname, true); + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to drop foreign-data wrapper \"%s\"", + 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", + stmt->fdwname))); + + /* IF EXISTS specified, just note it */ + ereport(NOTICE, + (errmsg("foreign-data wrapper \"%s\" does not exist, skipping", + 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]; + bool nulls[Natts_pg_foreign_server]; + HeapTuple tuple; + Oid srvId; + Oid ownerId; + AclResult aclresult; + ObjectAddress myself; + ObjectAddress referenced; + 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 (GetForeignServerByName(stmt->servername, true) != NULL) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("server \"%s\" already exists", + stmt->servername))); + + /* + * Check that the FDW exists and that we have USAGE on it. + * Also get the actual FDW for option validation etc. + */ + fdw = GetForeignDataWrapperByName(stmt->fdwname, false); + + aclresult = pg_foreign_data_wrapper_aclcheck(fdw->fdwid, ownerId, ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + + /* + * Insert tuple into pg_foreign_server. + */ + rel = heap_open(ForeignServerRelationId, RowExclusiveLock); + + MemSet(nulls, false, Natts_pg_foreign_server); + + values[Anum_pg_foreign_server_srvname - 1] = + DirectFunctionCall1(namein, CStringGetDatum(stmt->servername)); + values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId); + values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid); + + /* 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] = true; + + /* 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] = true; + + /* Start with a blank acl */ + nulls[Anum_pg_foreign_server_srvacl - 1] = true; + + /* 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] = true; + + tuple = heap_form_tuple(rel->rd_att, values, nulls); + + srvId = simple_heap_insert(rel, tuple); + + CatalogUpdateIndexes(rel, tuple); + + heap_freetuple(tuple); + + /* Add dependency on FDW and owner */ + myself.classId = ForeignServerRelationId; + myself.objectId = srvId; + myself.objectSubId = 0; + + referenced.classId = ForeignDataWrapperRelationId; + referenced.objectId = fdw->fdwid; + 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; + + tp = SearchSysCacheCopy(FOREIGNSERVERNAME, + CStringGetDatum(stmt->servername), + 0, 0, 0); + + if (!HeapTupleIsValid(tp)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("server \"%s\" does not exist", stmt->servername))); + + srvId = HeapTupleGetOid(tp); + srvForm = (Form_pg_foreign_server) GETSTRUCT(tp); + + /* + * 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, + stmt->servername); + + 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) +{ + Oid srvId; + ObjectAddress object; + + srvId = GetForeignServerOidByName(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", stmt->servername))); + + /* IF EXISTS specified, just note it */ + ereport(NOTICE, + (errmsg("server \"%s\" does not exist, skipping", + 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, + 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]; + bool nulls[Natts_pg_user_mapping]; + HeapTuple tuple; + Oid useId; + Oid umId; + Oid ownerId; + ObjectAddress myself; + ObjectAddress referenced; + ForeignServer *srv; + ForeignDataWrapper *fdw; + + ownerId = GetUserId(); + + useId = GetUserOidFromMapping(stmt->username, false); + + /* + * Check that the server exists and that the we own it. + */ + srv = GetForeignServerByName(stmt->servername, false); + + if (!pg_foreign_server_ownercheck(srv->serverid, ownerId)) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, + stmt->servername); + + /* + * Check that the user mapping is unique within server. + */ + umId = GetSysCacheOid(USERMAPPINGUSERSERVER, + ObjectIdGetDatum(useId), + ObjectIdGetDatum(srv->serverid), + 0, 0); + if (OidIsValid(umId)) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("user mapping \"%s\" already exists for server %s", + MappingUserName(useId), + stmt->servername))); + + fdw = GetForeignDataWrapper(srv->fdwid); + + /* + * Insert tuple into pg_user_mapping. + */ + rel = heap_open(UserMappingRelationId, RowExclusiveLock); + + MemSet(nulls, false, Natts_pg_user_mapping); + + values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId); + values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid); + + /* 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] = true; + + tuple = heap_form_tuple(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 = srv->serverid; + 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 umId; + ForeignServer *srv; + + useId = GetUserOidFromMapping(stmt->username, false); + srv = GetForeignServerByName(stmt->servername, false); + + umId = GetSysCacheOid(USERMAPPINGUSERSERVER, + ObjectIdGetDatum(useId), + ObjectIdGetDatum(srv->serverid), + 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(srv->serverid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, + 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; + Datum datum; + bool isnull; + + /* + * Process the options. + */ + + 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 umId; + ForeignServer *srv; + + useId = GetUserOidFromMapping(stmt->username, stmt->missing_ok); + srv = GetForeignServerByName(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 (!srv) + { + if (!stmt->missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("server \"%s\" does not exist", + stmt->servername))); + /* IF EXISTS, just note it */ + ereport(NOTICE, (errmsg("server does not exist, skipping"))); + return; + } + + umId = GetSysCacheOid(USERMAPPINGUSERSERVER, + ObjectIdGetDatum(useId), + ObjectIdGetDatum(srv->serverid), + 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(srv->serverid, GetUserId())) + { + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, + srv->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 --git a/src/backend/foreign/Makefile b/src/backend/foreign/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b3cc209c1611feefaaba5381aa84c4f860d9bc6d --- /dev/null +++ b/src/backend/foreign/Makefile @@ -0,0 +1,25 @@ +#------------------------------------------------------------------------- +# +# Makefile-- +# Makefile for foreign +# +# IDENTIFICATION +# $PostgreSQL: pgsql/src/backend/foreign/Makefile,v 1.1 2008/12/19 16:25:17 petere Exp $ +# +#------------------------------------------------------------------------- + +subdir = src/backend/foreign +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +OBJS= foreign.o + +include $(top_srcdir)/src/backend/common.mk + +FDW = dummy postgresql + +$(addsuffix -fdw,all install installdirs uninstall distprep): + for dir in $(FDW); do $(MAKE) -C $$dir `echo $@ | sed 's/-fdw$$//'` || exit; done + +clean distclean maintainer-clean: + for dir in $(FDW); do $(MAKE) -C $$dir $@ || exit; done diff --git a/src/backend/foreign/dummy/Makefile b/src/backend/foreign/dummy/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..8a05ada0197eaf37ca5d64757a29af29b1225762 --- /dev/null +++ b/src/backend/foreign/dummy/Makefile @@ -0,0 +1,27 @@ +#------------------------------------------------------------------------- +# +# Makefile-- +# Makefile for dummy foreign-data wrapper +# +# IDENTIFICATION +# $PostgreSQL: pgsql/src/backend/foreign/dummy/Makefile,v 1.1 2008/12/19 16:25:17 petere Exp $ +# +#------------------------------------------------------------------------- + +subdir = src/backend/foreign/dummy +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global + +NAME = dummy_fdw +OBJS = dummy_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 --git a/src/backend/foreign/dummy/dummy_fdw.c b/src/backend/foreign/dummy/dummy_fdw.c new file mode 100644 index 0000000000000000000000000000000000000000..2017c847136b938efcc01c66ed4039306627ce0c --- /dev/null +++ b/src/backend/foreign/dummy/dummy_fdw.c @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * dummy_fdw.c + * "dummy" foreign-data wrapper + * + * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/foreign/dummy/dummy_fdw.c,v 1.1 2008/12/19 16:25:17 petere Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "fmgr.h" +#include "foreign/foreign.h" + +PG_MODULE_MAGIC; + +/* + * This looks like a complete waste right now, but it is useful for + * testing, and will become more interesting as more parts of the + * interface are implemented. + */ diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c new file mode 100644 index 0000000000000000000000000000000000000000..2fdb84ac1b4b6ae98879115b20bc359a47b2e454 --- /dev/null +++ b/src/backend/foreign/foreign.c @@ -0,0 +1,389 @@ +/*------------------------------------------------------------------------- + * + * foreign.c + * support for foreign-data wrappers, servers and user mappings. + * + * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/foreign/foreign.c,v 1.1 2008/12/19 16:25:17 petere Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/reloptions.h" +#include "catalog/namespace.h" +#include "catalog/pg_foreign_data_wrapper.h" +#include "catalog/pg_foreign_server.h" +#include "catalog/pg_type.h" +#include "catalog/pg_user_mapping.h" +#include "foreign/foreign.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/parsenodes.h" +#include "utils/acl.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/syscache.h" + + +extern Datum pg_options_to_table(PG_FUNCTION_ARGS); + + +/* list of currently loaded foreign-data wrapper interfaces */ +static List *loaded_fdw_interfaces = NIL; + + +/* + * GetForeignDataWrapperLibrary - return the named FDW library. If it + * is already loaded, use that. Otherwise allocate, initialize, and + * store in cache. + */ +ForeignDataWrapperLibrary * +GetForeignDataWrapperLibrary(const char *libname) +{ + MemoryContext oldcontext; + void *libhandle = NULL; + ForeignDataWrapperLibrary *fdwl = NULL; + ListCell *cell; + + /* See if we have the FDW library is already loaded */ + foreach (cell, loaded_fdw_interfaces) + { + fdwl = lfirst(cell); + if (strcmp(fdwl->libname, libname) == 0) + return fdwl; + } + + /* + * We don't have it yet, so load and add. Attempt a load_file() + * first to filter out any missing or unloadable libraries. + */ + load_file(libname, false); + + oldcontext = MemoryContextSwitchTo(TopMemoryContext); + + fdwl = palloc(sizeof(*fdwl)); + fdwl->libname = pstrdup(libname); + loaded_fdw_interfaces = lappend(loaded_fdw_interfaces, fdwl); + + MemoryContextSwitchTo(oldcontext); + + /* + * Now look up the foreign data wrapper functions. + */ +#define LOOKUP_FUNCTION(name) \ + (void *)(libhandle ? \ + lookup_external_function(libhandle, name) \ + : load_external_function(fdwl->libname, name, false, &libhandle)) + + fdwl->validateOptionList = LOOKUP_FUNCTION("_pg_validateOptionList"); + + return fdwl; +} + + +/* + * GetForeignDataWrapper - look up the foreign-data wrapper by OID. + * + * 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->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 options */ + datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, + tp, + Anum_pg_foreign_data_wrapper_fdwoptions, + &isnull); + fdw->options = untransformRelOptions(datum); + + ReleaseSysCache(tp); + + return fdw; +} + + +/* + * GetForeignDataWrapperOidByName - look up the foreign-data wrapper + * OID by name. + */ +Oid +GetForeignDataWrapperOidByName(const char *fdwname, bool missing_ok) +{ + Oid fdwId; + + fdwId = GetSysCacheOid(FOREIGNDATAWRAPPERNAME, + CStringGetDatum(fdwname), + 0, 0, 0); + + if (!OidIsValid(fdwId) && !missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("foreign-data wrapper \"%s\" does not exist", fdwname))); + + return fdwId; +} + + +/* + * GetForeignDataWrapperByName - look up the foreign-data wrapper + * definition by name. + */ +ForeignDataWrapper * +GetForeignDataWrapperByName(const char *fdwname, bool missing_ok) +{ + Oid fdwId = GetForeignDataWrapperOidByName(fdwname, missing_ok); + + if (!OidIsValid(fdwId) && missing_ok) + return NULL; + + return GetForeignDataWrapper(fdwId); +} + + +/* + * 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->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; +} + + +/* + * GetForeignServerByName - look up the foreign server oid by name. + */ +Oid +GetForeignServerOidByName(const char *srvname, bool missing_ok) +{ + Oid serverid; + + serverid = GetSysCacheOid(FOREIGNSERVERNAME, + CStringGetDatum(srvname), + 0, 0, 0); + + if (!OidIsValid(serverid) && !missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("server \"%s\" does not exist", srvname))); + + return serverid; +} + + +/* + * GetForeignServerByName - look up the foreign server definition by name. + */ +ForeignServer * +GetForeignServerByName(const char *srvname, bool missing_ok) +{ + Oid serverid = GetForeignServerOidByName(srvname, missing_ok); + + if (!OidIsValid(serverid) && missing_ok) + return NULL; + + return GetForeignServer(serverid); +} + + +/* + * GetUserMapping - look up the user mapping. + * + * If no mapping is found for the supplied user, we also look for + * PUBLIC mappings (userid == 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; +} + + +/* + * 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); +} + + +/* + * Convert options array to name/value table. Useful for information + * schema and pg_dump. + */ +Datum +pg_options_to_table(PG_FUNCTION_ARGS) +{ + Datum array = PG_GETARG_DATUM(0); + + deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, untransformRelOptions(array)); + + return (Datum) 0; +} diff --git a/src/backend/foreign/postgresql/Makefile b/src/backend/foreign/postgresql/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..40ed90f8d7090f8a234ace8ab139e5e4ffccf424 --- /dev/null +++ b/src/backend/foreign/postgresql/Makefile @@ -0,0 +1,27 @@ +#------------------------------------------------------------------------- +# +# Makefile-- +# Makefile for postgresql foreign-data wrapper +# +# IDENTIFICATION +# $PostgreSQL: pgsql/src/backend/foreign/postgresql/Makefile,v 1.1 2008/12/19 16:25:17 petere Exp $ +# +#------------------------------------------------------------------------- + +subdir = src/backend/foreign/postgresql +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global + +NAME = postgresql_fdw +OBJS = postgresql_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 --git a/src/backend/foreign/postgresql/postgresql_fdw.c b/src/backend/foreign/postgresql/postgresql_fdw.c new file mode 100644 index 0000000000000000000000000000000000000000..f1c881e397662a10ca5c1ecf9b6d5def42bd721b --- /dev/null +++ b/src/backend/foreign/postgresql/postgresql_fdw.c @@ -0,0 +1,123 @@ +/*------------------------------------------------------------------------- + * + * postgresql_fdw.c + * foreign-data wrapper for postgresql (libpq) connections. + * + * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/foreign/postgresql/postgresql_fdw.c,v 1.1 2008/12/19 16:25:17 petere Exp $ + * + *------------------------------------------------------------------------- + */ +#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; + + +/* + * Describes the valid options for postgresql 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. + */ +void +_pg_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))); + } + } +} diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 6a0c3374ce8703ee223b71d063e3f59b6db38009..86f555a03a617566e7c5aa1dcd24694b54a7ee44 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.415 2008/12/04 17:51:26 petere Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.416 2008/12/19 16:25:17 petere Exp $ * *------------------------------------------------------------------------- */ @@ -2033,6 +2033,17 @@ _copyDefElem(DefElem *from) 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,6 +2880,117 @@ _copyDropTableSpaceStmt(DropTableSpaceStmt *from) return newnode; } +static CreateFdwStmt * +_copyCreateFdwStmt(CreateFdwStmt *from) +{ + CreateFdwStmt *newnode = makeNode(CreateFdwStmt); + + COPY_STRING_FIELD(fdwname); + COPY_STRING_FIELD(library); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterFdwStmt * +_copyAlterFdwStmt(AlterFdwStmt *from) +{ + AlterFdwStmt *newnode = makeNode(AlterFdwStmt); + + COPY_STRING_FIELD(fdwname); + COPY_STRING_FIELD(library); + COPY_NODE_FIELD(options); + + return newnode; +} + +static DropFdwStmt * +_copyDropFdwStmt(DropFdwStmt *from) +{ + DropFdwStmt *newnode = makeNode(DropFdwStmt); + + COPY_STRING_FIELD(fdwname); + COPY_SCALAR_FIELD(missing_ok); + COPY_SCALAR_FIELD(behavior); + + return newnode; +} + +static CreateForeignServerStmt * +_copyCreateForeignServerStmt(CreateForeignServerStmt *from) +{ + CreateForeignServerStmt *newnode = makeNode(CreateForeignServerStmt); + + COPY_STRING_FIELD(servername); + COPY_STRING_FIELD(servertype); + COPY_STRING_FIELD(version); + COPY_STRING_FIELD(fdwname); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterForeignServerStmt * +_copyAlterForeignServerStmt(AlterForeignServerStmt *from) +{ + AlterForeignServerStmt *newnode = makeNode(AlterForeignServerStmt); + + COPY_STRING_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_STRING_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_STRING_FIELD(servername); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterUserMappingStmt * +_copyAlterUserMappingStmt(AlterUserMappingStmt *from) +{ + AlterUserMappingStmt *newnode = makeNode(AlterUserMappingStmt); + + COPY_STRING_FIELD(username); + COPY_STRING_FIELD(servername); + COPY_NODE_FIELD(options); + + return newnode; +} + +static DropUserMappingStmt * +_copyDropUserMappingStmt(DropUserMappingStmt *from) +{ + DropUserMappingStmt *newnode = makeNode(DropUserMappingStmt); + + COPY_STRING_FIELD(username); + COPY_STRING_FIELD(servername); + COPY_SCALAR_FIELD(missing_ok); + + return newnode; +} + static CreateTrigStmt * _copyCreateTrigStmt(CreateTrigStmt *from) { @@ -3696,6 +3818,33 @@ copyObject(void *from) 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,6 +3972,9 @@ copyObject(void *from) case T_DefElem: retval = _copyDefElem(from); break; + case T_OptionDefElem: + retval = _copyOptionDefElem(from); + break; case T_LockingClause: retval = _copyLockingClause(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index ebf8cea2f0d80f35f5a6764959f479f1869dabbc..e5e2bc4422644e1052dd2fd19328465113c4e7ff 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -22,7 +22,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.340 2008/12/04 17:51:26 petere Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.341 2008/12/19 16:25:17 petere Exp $ * *------------------------------------------------------------------------- */ @@ -1518,6 +1518,99 @@ _equalDropTableSpaceStmt(DropTableSpaceStmt *a, DropTableSpaceStmt *b) return true; } +static bool +_equalCreateFdwStmt(CreateFdwStmt *a, CreateFdwStmt *b) +{ + COMPARE_STRING_FIELD(fdwname); + COMPARE_STRING_FIELD(library); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterFdwStmt(AlterFdwStmt *a, AlterFdwStmt *b) +{ + COMPARE_STRING_FIELD(fdwname); + COMPARE_STRING_FIELD(library); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalDropFdwStmt(DropFdwStmt *a, DropFdwStmt *b) +{ + COMPARE_STRING_FIELD(fdwname); + COMPARE_SCALAR_FIELD(missing_ok); + COMPARE_SCALAR_FIELD(behavior); + + return true; +} + +static bool +_equalCreateForeignServerStmt(CreateForeignServerStmt *a, CreateForeignServerStmt *b) +{ + COMPARE_STRING_FIELD(servername); + COMPARE_STRING_FIELD(servertype); + COMPARE_STRING_FIELD(version); + COMPARE_STRING_FIELD(fdwname); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterForeignServerStmt(AlterForeignServerStmt *a, AlterForeignServerStmt *b) +{ + COMPARE_STRING_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_STRING_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_STRING_FIELD(servername); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterUserMappingStmt(AlterUserMappingStmt *a, AlterUserMappingStmt *b) +{ + COMPARE_STRING_FIELD(username); + COMPARE_STRING_FIELD(servername); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalDropUserMappingStmt(DropUserMappingStmt *a, DropUserMappingStmt *b) +{ + COMPARE_STRING_FIELD(username); + COMPARE_STRING_FIELD(servername); + COMPARE_SCALAR_FIELD(missing_ok); + + return true; +} + static bool _equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b) { @@ -1956,6 +2049,15 @@ _equalDefElem(DefElem *a, DefElem *b) return true; } +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) { @@ -2534,6 +2636,33 @@ equal(void *a, void *b) 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,6 +2790,9 @@ equal(void *a, void *b) 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 --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 42539f6f97bfe0c023315c3c23ebc4ae6f598d96..cb4f09be5a1a56e2f3df04b3e1b85aaf97d9dd44 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.60 2008/09/01 20:42:44 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.61 2008/12/19 16:25:17 petere Exp $ * *------------------------------------------------------------------------- */ @@ -361,3 +361,16 @@ makeDefElem(char *name, Node *arg) 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 --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 337af633272c72269ac2aba617d678b697cf7cca..cc81e6564610d3a194a796f5cb3c57f8f04d1875 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.645 2008/12/18 18:20:34 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.646 2008/12/19 16:25:17 petere Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -156,6 +156,7 @@ static TypeName *TableFuncTypeName(List *columns); FunctionParameterMode fun_param_mode; FuncWithArgs *funwithargs; DefElem *defelt; + OptionDefElem *optdef; SortBy *sortby; JoinExpr *jexpr; IndexElem *ielem; @@ -172,19 +173,22 @@ static TypeName *TableFuncTypeName(List *columns); } %type <node> stmt schema_stmt - AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt + AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt + AlterForeignServerStmt AlterGroupStmt AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt - AlterUserStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt + AlterUserStmt AlterUserMappingStmt 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 + 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 ExplainStmt FetchStmt + DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt + DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt @@ -222,6 +226,10 @@ static TypeName *TableFuncTypeName(List *columns); %type <list> OptRoleList %type <defelt> OptRoleElem +%type <str> opt_type +%type <str> foreign_server_version opt_foreign_server_version +%type <str> auth_ident + %type <str> OptSchemaName %type <list> OptSchemaEltList @@ -274,6 +282,7 @@ static TypeName *TableFuncTypeName(List *columns); prep_type_clause execute_param_clause using_clause returning_clause enum_val_list table_func_column_list + create_generic_options alter_generic_options %type <range> OptTempTableName %type <into> into_clause create_as_target @@ -342,6 +351,12 @@ static TypeName *TableFuncTypeName(List *columns); %type <range> relation_expr_opt_alias %type <target> target_el single_set_clause set_target insert_column_item +%type <str> generic_option_name +%type <node> generic_option_arg +%type <defelt> generic_option_elem +%type <optdef> alter_generic_option_elem +%type <list> generic_option_list alter_generic_option_list + %type <typnam> Typename SimpleTypename ConstTypename GenericType Numeric opt_float Character ConstCharacter @@ -436,7 +451,7 @@ static TypeName *TableFuncTypeName(List *columns); KEY LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEAST LEFT LEVEL - LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION + LIBRARY LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOGIN_P MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE @@ -445,7 +460,7 @@ static TypeName *TableFuncTypeName(List *columns); NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC - OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR + 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,7 +474,7 @@ static TypeName *TableFuncTypeName(List *columns); REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE - SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE + 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,7 +489,7 @@ static TypeName *TableFuncTypeName(List *columns); VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING VERBOSE VERSION_P VIEW VOLATILE - WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE + WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRAPPER WRITE XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLFOREST XMLPARSE XMLPI XMLROOT XMLSERIALIZE @@ -562,6 +577,8 @@ stmt : AlterDatabaseStmt | AlterDatabaseSetStmt | AlterDomainStmt + | AlterFdwStmt + | AlterForeignServerStmt | AlterFunctionStmt | AlterGroupStmt | AlterObjectSchemaStmt @@ -572,6 +589,7 @@ stmt : | AlterRoleStmt | AlterTSConfigurationStmt | AlterTSDictionaryStmt + | AlterUserMappingStmt | AlterUserSetStmt | AlterUserStmt | AnalyzeStmt @@ -586,6 +604,8 @@ stmt : | CreateCastStmt | CreateConversionStmt | CreateDomainStmt + | CreateFdwStmt + | CreateForeignServerStmt | CreateFunctionStmt | CreateGroupStmt | CreateOpClassStmt @@ -599,6 +619,7 @@ stmt : | CreateTrigStmt | CreateRoleStmt | CreateUserStmt + | CreateUserMappingStmt | CreatedbStmt | DeallocateStmt | DeclareCursorStmt @@ -607,6 +628,8 @@ stmt : | DiscardStmt | DropAssertStmt | DropCastStmt + | DropFdwStmt + | DropForeignServerStmt | DropGroupStmt | DropOpClassStmt | DropOpFamilyStmt @@ -618,6 +641,7 @@ stmt : | DropTrigStmt | DropRoleStmt | DropUserStmt + | DropUserMappingStmt | DropdbStmt | ExecuteStmt | ExplainStmt @@ -2715,6 +2739,313 @@ DropTableSpaceStmt: DROP TABLESPACE name } ; +/***************************************************************************** + * + * QUERY: + * CREATE FOREIGN DATA WRAPPER name LIBRARY 'library_name' LANGUAGE C + * + *****************************************************************************/ + +CreateFdwStmt: CREATE FOREIGN DATA_P WRAPPER 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 name + * + ****************************************************************************/ + +DropFdwStmt: DROP FOREIGN DATA_P WRAPPER 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 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 name + * + ****************************************************************************/ + +AlterFdwStmt: ALTER FOREIGN DATA_P WRAPPER 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 name LIBRARY Sconst + { + AlterFdwStmt *n = makeNode(AlterFdwStmt); + n->fdwname = $5; + n->library = $7; + $$ = (Node *) n; + } + | ALTER FOREIGN DATA_P WRAPPER 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 '(' generic_option_list ')' { $$ = $3; } + | /*EMPTY*/ { $$ = NIL; } + ; + +generic_option_list: generic_option_elem + { + $$ = list_make1(makeOptionDefElem(ALTER_OPT_ADD, $1)); + } + | generic_option_list ',' generic_option_elem + { + $$ = lappend($1, makeOptionDefElem(ALTER_OPT_ADD, $3)); + } + ; + +/* Options definition for ALTER FDW, SERVER and USER MAPPING */ +alter_generic_options: + OPTIONS '(' alter_generic_option_list ')' { $$ = $3; } + ; + +alter_generic_option_list: + alter_generic_option_elem + { + $$ = list_make1($1); + } + | generic_option_elem + { + $$ = list_make1(makeOptionDefElem(ALTER_OPT_ADD, $1)); + } + | alter_generic_option_list ',' alter_generic_option_elem + { + $$ = lappend($1, $3); + } + | alter_generic_option_list ',' generic_option_elem + { + $$ = lappend($1, makeOptionDefElem(ALTER_OPT_ADD, $3)); + } + ; + +alter_generic_option_elem: + ADD_P generic_option_elem + { + $$ = makeOptionDefElem(ALTER_OPT_ADD, $2); + } + | SET generic_option_elem + { + $$ = makeOptionDefElem(ALTER_OPT_SET, $2); + } + | DROP generic_option_name + { + $$ = makeOptionDefElem(ALTER_OPT_DROP, + makeDefElem($2, NULL)); + } + ; + +generic_option_elem: + generic_option_name generic_option_arg { $$ = makeDefElem($1, $2); } + ; + +generic_option_name: + attr_name { $$ = $1; } + ; + +generic_option_arg: + Sconst { $$ = (Node *)makeString($1); } + ; + +/***************************************************************************** + * + * QUERY: + * CREATE SERVER name [TYPE] [VERSION] [OPTIONS] + * + *****************************************************************************/ + +CreateForeignServerStmt: CREATE SERVER name opt_type opt_foreign_server_version + FOREIGN DATA_P WRAPPER 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 name + * + ****************************************************************************/ + +DropForeignServerStmt: DROP SERVER 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 name opt_drop_behavior + { + DropForeignServerStmt *n = makeNode(DropForeignServerStmt); + n->servername = $5; + n->missing_ok = true; + n->behavior = $6; + $$ = (Node *) n; + } + ; + +/***************************************************************************** + * + * QUERY : + * ALTER SERVER name [VERSION] [OPTIONS] + * + ****************************************************************************/ + +AlterForeignServerStmt: ALTER SERVER 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 name foreign_server_version + { + AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt); + n->servername = $3; + n->version = $4; + n->has_version = true; + $$ = (Node *) n; + } + | ALTER SERVER name alter_generic_options + { + AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt); + n->servername = $3; + n->options = $4; + $$ = (Node *) n; + } + ; + +/***************************************************************************** + * + * QUERY: + * CREATE USER MAPPING FOR auth_ident SERVER name [OPTIONS] + * + *****************************************************************************/ + +CreateUserMappingStmt: CREATE USER MAPPING FOR auth_ident SERVER 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 auth_ident SERVER name + * + ****************************************************************************/ + +DropUserMappingStmt: DROP USER MAPPING FOR auth_ident SERVER 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 name + { + DropUserMappingStmt *n = makeNode(DropUserMappingStmt); + n->username = $7; + n->servername = $9; + n->missing_ok = true; + $$ = (Node *) n; + } + ; + +/***************************************************************************** + * + * QUERY : + * ALTER USER MAPPING FOR auth_ident SERVER name OPTIONS + * + ****************************************************************************/ + +AlterUserMappingStmt: ALTER USER MAPPING FOR auth_ident SERVER name alter_generic_options + { + AlterUserMappingStmt *n = makeNode(AlterUserMappingStmt); + n->username = $5; + n->servername = $7; + n->options = $8; + $$ = (Node *) n; + } + ; + /***************************************************************************** * * QUERIES : @@ -3912,6 +4243,20 @@ privilege_target: n->objs = $2; $$ = n; } + | FOREIGN DATA_P WRAPPER name_list + { + PrivTarget *n = makeNode(PrivTarget); + n->objtype = ACL_OBJECT_FDW; + n->objs = $4; + $$ = n; + } + | FOREIGN SERVER 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); @@ -5123,6 +5468,22 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId n->newowner = $8; $$ = (Node *)n; } + | ALTER FOREIGN DATA_P WRAPPER name OWNER TO RoleId + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_FDW; + n->object = list_make1(makeString($5)); + n->newowner = $8; + $$ = (Node *)n; + } + | ALTER SERVER name OWNER TO RoleId + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_FOREIGN_SERVER; + n->object = list_make1(makeString($3)); + n->newowner = $6; + $$ = (Node *)n; + } ; @@ -9556,6 +9917,7 @@ unreserved_keyword: | INVOKER | ISOLATION | KEY + | LIBRARY | LANCOMPILER | LANGUAGE | LARGE_P @@ -9594,6 +9956,7 @@ unreserved_keyword: | OIDS | OPERATOR | OPTION + | OPTIONS | OWNED | OWNER | PARSER @@ -9629,6 +9992,7 @@ unreserved_keyword: | ROWS | RULE | SAVEPOINT + | SERVER | SCHEMA | SCROLL | SEARCH @@ -9681,6 +10045,7 @@ unreserved_keyword: | WHITESPACE_P | WITHOUT | WORK + | WRAPPER | WRITE | XML_P | YEAR_P diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 9eb6d0822693bad5dba79c9e1139cb4cd74bd86a..bf7b1f6ad2e710dd480b91428b099103549e24c8 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.205 2008/10/27 09:37:47 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.206 2008/12/19 16:25:17 petere Exp $ * *------------------------------------------------------------------------- */ @@ -229,6 +229,7 @@ const ScanKeyword ScanKeywords[] = { {"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,6 +282,7 @@ const ScanKeyword ScanKeywords[] = { {"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,6 +341,7 @@ const ScanKeyword ScanKeywords[] = { {"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,6 +414,7 @@ const ScanKeyword ScanKeywords[] = { {"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 --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index f222b9cd75e0656420983be744edc98be1cc6853..555e8730dd9aefc888381f52551ce94688a4b132 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.302 2008/12/04 17:51:26 petere Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.303 2008/12/19 16:25:17 petere Exp $ * *------------------------------------------------------------------------- */ @@ -203,6 +203,15 @@ check_xact_readonly(Node *parsetree) 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,6 +461,42 @@ ProcessUtility(Node *parsetree, 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,6 +1355,42 @@ CreateCommandTag(Node *parsetree) 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,6 +1604,12 @@ CreateCommandTag(Node *parsetree) 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,6 +2124,18 @@ GetCommandLogLevel(Node *parsetree) 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 --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 963112ef74a369cb388d0b4388bc0472b6d47d7d..4641aecd59f2d6936839c7c93e0b9258afe532f3 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.143 2008/12/15 18:09:41 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.144 2008/12/19 16:25:17 petere Exp $ * *------------------------------------------------------------------------- */ @@ -22,6 +22,7 @@ #include "catalog/pg_type.h" #include "commands/dbcommands.h" #include "commands/tablespace.h" +#include "foreign/foreign.h" #include "miscadmin.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -577,6 +578,14 @@ acldefault(GrantObjectType objtype, Oid ownerId) 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 */ @@ -1823,6 +1832,156 @@ convert_database_priv_string(text *priv_type_text) } +/* + * has_foreign_data_wrapper_privilege variants + * These are all named "has_foreign_data_wrapper_privilege" at the SQL level. + * They take various combinations of foreign-data wrapper name, + * fdw OID, user name, user OID, or implicit user = current_user. + * + * The result is a boolean value: true if user has the indicated + * privilege, false if not. The variants that take an OID return + * NULL if the OID doesn't exist. + */ + +/* + * has_foreign_data_wrapper_privilege + * Check user privileges on a foreign-data wrapper. + */ +static Datum +has_foreign_data_wrapper_privilege(Oid roleid, Oid fdwid, text *priv_type_text) +{ + AclResult aclresult; + AclMode mode = ACL_NO_RIGHTS; + char *priv_type = text_to_cstring(priv_type_text); + + if (pg_strcasecmp(priv_type, "USAGE") == 0) + mode = ACL_USAGE; + else if (pg_strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0) + mode = ACL_GRANT_OPTION_FOR(ACL_USAGE); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized privilege type: \"%s\"", priv_type))); + + aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * has_foreign_data_wrapper_privilege_name_name + * Check user privileges on a foreign-data wrapper given + * name username, text fdwname, and text priv name. + */ +Datum +has_foreign_data_wrapper_privilege_name_name(PG_FUNCTION_ARGS) +{ + Name username = PG_GETARG_NAME(0); + char *fdwname = text_to_cstring(PG_GETARG_TEXT_P(1)); + text *priv_type_text = PG_GETARG_TEXT_P(2); + + return has_foreign_data_wrapper_privilege(get_roleid_checked(NameStr(*username)), + GetForeignDataWrapperOidByName(fdwname, false), + priv_type_text); +} + +/* + * has_foreign_data_wrapper_privilege_name + * Check user privileges on a foreign-data wrapper given + * text fdwname and text priv name. + * current_user is assumed + */ +Datum +has_foreign_data_wrapper_privilege_name(PG_FUNCTION_ARGS) +{ + char *fdwname = text_to_cstring(PG_GETARG_TEXT_P(0)); + text *priv_type_text = PG_GETARG_TEXT_P(1); + + return has_foreign_data_wrapper_privilege(GetUserId(), + GetForeignDataWrapperOidByName(fdwname, false), + priv_type_text); +} + +/* + * has_foreign_data_wrapper_privilege_name_id + * Check user privileges on a foreign-data wrapper given + * name usename, foreign-data wrapper oid, and text priv name. + */ +Datum +has_foreign_data_wrapper_privilege_name_id(PG_FUNCTION_ARGS) +{ + Name username = PG_GETARG_NAME(0); + Oid fdwid = PG_GETARG_OID(1); + text *priv_type_text = PG_GETARG_TEXT_P(2); + + if (!SearchSysCacheExists(FOREIGNDATAWRAPPEROID, + ObjectIdGetDatum(fdwid), + 0, 0, 0)) + PG_RETURN_NULL(); + + return has_foreign_data_wrapper_privilege(get_roleid_checked(NameStr(*username)), + fdwid, priv_type_text); +} + +/* + * has_foreign_data_wrapper_privilege_id + * Check user privileges on a foreign-data wrapper given + * foreign-data wrapper oid, and text priv name. + * current_user is assumed + */ +Datum +has_foreign_data_wrapper_privilege_id(PG_FUNCTION_ARGS) +{ + Oid fdwid = PG_GETARG_OID(0); + text *priv_type_text = PG_GETARG_TEXT_P(1); + + if (!SearchSysCacheExists(FOREIGNDATAWRAPPEROID, + ObjectIdGetDatum(fdwid), + 0, 0, 0)) + PG_RETURN_NULL(); + + return has_foreign_data_wrapper_privilege(GetUserId(), fdwid, + priv_type_text); +} + +/* + * has_foreign_data_wrapper_privilege_id_name + * Check user privileges on a foreign-data wrapper given + * roleid, text fdwname, and text priv name. + */ +Datum +has_foreign_data_wrapper_privilege_id_name(PG_FUNCTION_ARGS) +{ + Oid roleid = PG_GETARG_OID(0); + char *fdwname = text_to_cstring(PG_GETARG_TEXT_P(1)); + text *priv_type_text = PG_GETARG_TEXT_P(2); + + return has_foreign_data_wrapper_privilege(roleid, + GetForeignDataWrapperOidByName(fdwname, false), + priv_type_text); +} + +/* + * has_foreign_data_wrapper_privilege_id_id + * Check user privileges on a foreign-data wrapper given + * roleid, fdw oid, and text priv name. + */ +Datum +has_foreign_data_wrapper_privilege_id_id(PG_FUNCTION_ARGS) +{ + Oid roleid = PG_GETARG_OID(0); + Oid fdwid = PG_GETARG_OID(1); + text *priv_type_text = PG_GETARG_TEXT_P(2); + + if (!SearchSysCacheExists(FOREIGNDATAWRAPPEROID, + ObjectIdGetDatum(fdwid), + 0, 0, 0)) + PG_RETURN_NULL(); + + return has_foreign_data_wrapper_privilege(roleid, fdwid, priv_type_text); +} + + /* * has_function_privilege variants * These are all named "has_function_privilege" at the SQL level. @@ -2466,6 +2625,154 @@ convert_schema_priv_string(text *priv_type_text) return ACL_NO_RIGHTS; /* keep compiler quiet */ } +/* + * has_server_privilege variants + * These are all named "has_server_privilege" at the SQL level. + * They take various combinations of foreign server name, + * server OID, user name, user OID, or implicit user = current_user. + * + * The result is a boolean value: true if user has the indicated + * privilege, false if not. + */ + +/* + * has_server_privilege + * Check user privileges on a foreign server. + */ +static Datum +has_server_privilege(Oid roleid, Oid serverid, text *priv_type_text) +{ + AclResult aclresult; + AclMode mode = ACL_NO_RIGHTS; + char *priv_type = text_to_cstring(priv_type_text); + + if (pg_strcasecmp(priv_type, "USAGE") == 0) + mode = ACL_USAGE; + else if (pg_strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0) + mode = ACL_GRANT_OPTION_FOR(ACL_USAGE); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized privilege type: \"%s\"", priv_type))); + + aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * has_server_privilege_name_name + * Check user privileges on a foreign server given + * name username, text servername, and text priv name. + */ +Datum +has_server_privilege_name_name(PG_FUNCTION_ARGS) +{ + Name username = PG_GETARG_NAME(0); + char *servername = text_to_cstring(PG_GETARG_TEXT_P(1)); + text *priv_type_text = PG_GETARG_TEXT_P(2); + + return has_server_privilege(get_roleid_checked(NameStr(*username)), + GetForeignServerOidByName(servername, false), + priv_type_text); +} + +/* + * has_server_privilege_name + * Check user privileges on a foreign server given + * text servername and text priv name. + * current_user is assumed + */ +Datum +has_server_privilege_name(PG_FUNCTION_ARGS) +{ + char *servername = text_to_cstring(PG_GETARG_TEXT_P(0)); + text *priv_type_text = PG_GETARG_TEXT_P(1); + + return has_server_privilege(GetUserId(), + GetForeignServerOidByName(servername, false), + priv_type_text); +} + +/* + * has_server_privilege_name_id + * Check user privileges on a foreign server given + * name usename, foreign server oid, and text priv name. + */ +Datum +has_server_privilege_name_id(PG_FUNCTION_ARGS) +{ + Name username = PG_GETARG_NAME(0); + Oid serverid = PG_GETARG_OID(1); + text *priv_type_text = PG_GETARG_TEXT_P(2); + + if (!SearchSysCacheExists(FOREIGNSERVEROID, + ObjectIdGetDatum(serverid), + 0, 0, 0)) + PG_RETURN_NULL(); + + return has_server_privilege(get_roleid_checked(NameStr(*username)), serverid, + priv_type_text); +} + +/* + * has_server_privilege_id + * Check user privileges on a foreign server given + * server oid, and text priv name. + * current_user is assumed + */ +Datum +has_server_privilege_id(PG_FUNCTION_ARGS) +{ + Oid serverid = PG_GETARG_OID(0); + text *priv_type_text = PG_GETARG_TEXT_P(1); + + if (!SearchSysCacheExists(FOREIGNSERVEROID, + ObjectIdGetDatum(serverid), + 0, 0, 0)) + PG_RETURN_NULL(); + + return has_server_privilege(GetUserId(), serverid, priv_type_text); +} + +/* + * has_server_privilege_id_name + * Check user privileges on a foreign server given + * roleid, text servername, and text priv name. + */ +Datum +has_server_privilege_id_name(PG_FUNCTION_ARGS) +{ + Oid roleid = PG_GETARG_OID(0); + char *servername = text_to_cstring(PG_GETARG_TEXT_P(1)); + text *priv_type_text = PG_GETARG_TEXT_P(2); + + return has_server_privilege(roleid, + GetForeignServerOidByName(servername, false), + priv_type_text); +} + +/* + * has_server_privilege_id_id + * Check user privileges on a foreign server given + * roleid, server oid, and text priv name. + */ +Datum +has_server_privilege_id_id(PG_FUNCTION_ARGS) +{ + Oid roleid = PG_GETARG_OID(0); + Oid serverid = PG_GETARG_OID(1); + text *priv_type_text = PG_GETARG_TEXT_P(2); + + if (!SearchSysCacheExists(FOREIGNSERVEROID, + ObjectIdGetDatum(serverid), + 0, 0, 0)) + PG_RETURN_NULL(); + + return has_server_privilege(roleid, serverid, priv_type_text); +} + + /* * has_tablespace_privilege variants * These are all named "has_tablespace_privilege" at the SQL level. diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index 1ccf7b8d69b24a2aaec01fd4e93b71b069dcf47f..3502245687962c37cf4ff25a2be200bbfbee91ed 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.117 2008/06/19 00:46:05 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.118 2008/12/19 16:25:17 petere Exp $ * * NOTES * These routines allow the parser/planner/executor to perform @@ -32,6 +32,8 @@ #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,6 +48,7 @@ #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,6 +368,54 @@ static const struct cachedesc cacheinfo[] = { }, 256 }, + {ForeignDataWrapperRelationId, /* FOREIGNDATAWRAPPERNAME */ + ForeignDataWrapperNameIndexId, + 0, + 1, + { + Anum_pg_foreign_data_wrapper_fdwname, + 0, + 0, + 0 + }, + 8 + }, + {ForeignDataWrapperRelationId, /* FOREIGNDATAWRAPPEROID */ + ForeignDataWrapperOidIndexId, + 0, + 1, + { + ObjectIdAttributeNumber, + 0, + 0, + 0 + }, + 8 + }, + {ForeignServerRelationId, /* FOREIGNSERVERNAME */ + ForeignServerNameIndexId, + 0, + 1, + { + Anum_pg_foreign_server_srvname, + 0, + 0, + 0 + }, + 32 + }, + {ForeignServerRelationId, /* FOREIGNSERVEROID */ + ForeignServerOidIndexId, + 0, + 1, + { + ObjectIdAttributeNumber, + 0, + 0, + 0 + }, + 32 + }, {IndexRelationId, /* INDEXRELID */ IndexRelidIndexId, Anum_pg_index_indrelid, @@ -676,6 +727,30 @@ static const struct cachedesc cacheinfo[] = { 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 --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 8950dee0ca867cfe9fce10b55fab23189e8bbf67..3502c0481611f5fa7af9e53d2dcf2c27b236c0e6 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.104 2008/05/09 23:32:04 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.105 2008/12/19 16:25:17 petere Exp $ * *------------------------------------------------------------------------- */ @@ -91,6 +91,8 @@ getSchemaData(int *numTablesPtr) TSTemplateInfo *tmplinfo; TSDictInfo *dictinfo; TSConfigInfo *cfginfo; + FdwInfo *fdwinfo; + ForeignServerInfo *srvinfo; int numNamespaces; int numAggregates; int numInherits; @@ -104,6 +106,8 @@ getSchemaData(int *numTablesPtr) int numTSTemplates; int numTSDicts; int numTSConfigs; + int numForeignDataWrappers; + int numForeignServers; if (g_verbose) write_msg(NULL, "reading schemas\n"); @@ -154,6 +158,14 @@ getSchemaData(int *numTablesPtr) write_msg(NULL, "reading user-defined text search configurations\n"); 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 --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c index bf6fa6e445b7b9cc3ec1394c12ff368c0f641fef..d6143e96bb05a2c6c0e62c479db5cbd178a05b22 100644 --- a/src/bin/pg_dump/dumputils.c +++ b/src/bin/pg_dump/dumputils.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.41 2008/09/08 00:47:40 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.42 2008/12/19 16:25:17 petere Exp $ * *------------------------------------------------------------------------- */ @@ -687,6 +687,10 @@ do { \ } 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 --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index e25fcae031d142d65d9d81110aed92e4620d27ba..ec14f1b9426047ad686b723c0c0bde1105ef701b 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.158 2008/09/05 23:53:42 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.159 2008/12/19 16:25:17 petere Exp $ * *------------------------------------------------------------------------- */ @@ -2479,7 +2479,10 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH) /* objects named by just a name */ if (strcmp(type, "DATABASE") == 0 || strcmp(type, "PROCEDURAL LANGUAGE") == 0 || - strcmp(type, "SCHEMA") == 0) + strcmp(type, "SCHEMA") == 0 || + strcmp(type, "FOREIGN DATA WRAPPER") == 0 || + strcmp(type, "SERVER") == 0 || + strcmp(type, "USER MAPPING") == 0) { appendPQExpBuffer(buf, "%s %s", type, fmtId(te->tag)); return; @@ -2636,7 +2639,9 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat strcmp(te->desc, "VIEW") == 0 || strcmp(te->desc, "SEQUENCE") == 0 || strcmp(te->desc, "TEXT SEARCH DICTIONARY") == 0 || - strcmp(te->desc, "TEXT SEARCH CONFIGURATION") == 0) + strcmp(te->desc, "TEXT SEARCH CONFIGURATION") == 0 || + strcmp(te->desc, "FOREIGN DATA WRAPPER") == 0 || + strcmp(te->desc, "SERVER") == 0) { PQExpBuffer temp = createPQExpBuffer(); @@ -2653,7 +2658,8 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat strcmp(te->desc, "FK CONSTRAINT") == 0 || strcmp(te->desc, "INDEX") == 0 || strcmp(te->desc, "RULE") == 0 || - strcmp(te->desc, "TRIGGER") == 0) + strcmp(te->desc, "TRIGGER") == 0 || + strcmp(te->desc, "USER MAPPING") == 0) { /* these object types don't have separate owners */ } diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 04758761d4ee2b2a18b5f1574cc44447d7a53461..ae7233dbf83316f179964715bc2f99a31688c219 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.508 2008/12/18 18:20:34 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.509 2008/12/19 16:25:18 petere Exp $ * *------------------------------------------------------------------------- */ @@ -158,6 +158,11 @@ static void dumpTSParser(Archive *fout, TSParserInfo *prsinfo); 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,6 +5274,165 @@ getTSConfigurations(int *numTSConfigs) 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_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, " + "(%s fdwowner) as rolname, fdwlibrary, fdwacl," + "array_to_string(ARRAY(select option_name || ' ' || quote_literal(option_value) from pg_options_to_table(fdwoptions)), ', ') 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_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 = NULL; + 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_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, " + "(%s srvowner) as rolname, " + "srvfdw, srvtype, srvversion, srvacl," + "array_to_string(ARRAY(select option_name || ' ' || quote_literal(option_value) from pg_options_to_table(srvoptions)), ', ') 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_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 = NULL; + 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,6 +5835,12 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) 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, "", @@ -8977,6 +9147,224 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) 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(); + + 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"); + + appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n", + fmtId(fdwinfo->dobj.name)); + + ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId, + fdwinfo->dobj.name, + NULL, + 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, + NULL, 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 *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 fdwname " + "FROM pg_foreign_data_wrapper w " + "WHERE w.oid = '%u'", + 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(); + } + fdwname = PQgetvalue(res, 0, 0); + + 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 "); + appendPQExpBuffer(q, "%s", fmtId(fdwname)); + + if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0) + appendPQExpBuffer(q, " OPTIONS (%s)", srvinfo->srvoptions); + + appendPQExpBuffer(q, ";\n"); + + appendPQExpBuffer(delq, "DROP SERVER %s;\n", + fmtId(srvinfo->dobj.name)); + + ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId, + srvinfo->dobj.name, + NULL, + 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, + NULL, 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, NULL, + 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 delq; + PQExpBuffer query; + PQExpBuffer tag; + PGresult *res; + int ntups; + int i_umuser; + int i_umoptions; + int i; + + q = createPQExpBuffer(); + tag = createPQExpBuffer(); + delq = createPQExpBuffer(); + query = createPQExpBuffer(); + + appendPQExpBuffer(query, + "SELECT (%s umuser) AS umuser, " + "array_to_string(ARRAY(SELECT option_name || ' ' || quote_literal(option_value) FROM pg_options_to_table(umoptions)), ', ') AS umoptions\n" + "FROM pg_user_mapping WHERE umserver=%u", + username_subquery, + catalogId.oid); + + res = PQexec(g_conn, query->data); + check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); + + ntups = PQntuples(res); + i_umuser = PQfnumber(res, "umuser"); + i_umoptions = PQfnumber(res, "umoptions"); + + for (i = 0; i < ntups; i++) + { + char *umuser; + char *umoptions; + + umuser = PQgetvalue(res, i, i_umuser); + umoptions = PQgetvalue(res, i, i_umoptions); + + resetPQExpBuffer(q); + appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(umuser)); + appendPQExpBuffer(q, " SERVER %s", fmtId(servername)); + + if (umoptions && strlen(umoptions) > 0) + appendPQExpBuffer(q, " OPTIONS (%s)", umoptions); + + appendPQExpBuffer(q, ";\n"); + + resetPQExpBuffer(delq); + appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s SERVER %s;\n", fmtId(umuser), fmtId(servername)); + + resetPQExpBuffer(tag); + appendPQExpBuffer(tag, "USER MAPPING %s %s", fmtId(umuser), target); + + ArchiveEntry(fout, nilCatalogId, createDumpId(), + tag->data, + namespace, + NULL, + owner, false, + "USER MAPPING", q->data, + delq->data, NULL, + &dumpId, 1, + NULL, NULL); + } + + PQclear(res); + + destroyPQExpBuffer(query); + destroyPQExpBuffer(delq); + destroyPQExpBuffer(q); +} /*---------- * Write out grant/revoke information diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 9684163e3d58b26c26061a7c4a9b3b01fe05e509..a22e5abeb4f5bc20455122d42fa9856a9cd11073 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.143 2008/11/09 21:24:33 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.144 2008/12/19 16:25:18 petere Exp $ * *------------------------------------------------------------------------- */ @@ -131,6 +131,8 @@ typedef enum DO_TSDICT, DO_TSTEMPLATE, DO_TSCONFIG, + DO_FDW, + DO_FOREIGN_SERVER, DO_BLOBS, DO_BLOB_COMMENTS } DumpableObjectType; @@ -418,6 +420,26 @@ typedef struct _cfgInfo 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,5 +522,7 @@ extern TSParserInfo *getTSParsers(int *numTSParsers); 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 --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c index 7206d4eef4619d5d173383bf0817a50ca487b89b..52ece6f6ab766e334f6980973d759d627a222c8c 100644 --- a/src/bin/pg_dump/pg_dump_sort.c +++ b/src/bin/pg_dump/pg_dump_sort.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump_sort.c,v 1.21 2008/09/08 15:26:23 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump_sort.c,v 1.22 2008/12/19 16:25:18 petere Exp $ * *------------------------------------------------------------------------- */ @@ -50,6 +50,8 @@ static const int oldObjectTypePriority[] = 4, /* DO_TSDICT */ 3, /* DO_TSTEMPLATE */ 5, /* DO_TSCONFIG */ + 3, /* DO_FDW */ + 4, /* DO_FOREIGN_SERVER */ 10, /* DO_BLOBS */ 11 /* DO_BLOB_COMMENTS */ }; @@ -84,6 +86,8 @@ static const int newObjectTypePriority[] = 6, /* DO_TSDICT */ 5, /* DO_TSTEMPLATE */ 7, /* DO_TSCONFIG */ + 3, /* DO_FDW */ + 4, /* DO_FOREIGN_SERVER */ 14, /* DO_BLOBS */ 15 /* DO_BLOB_COMMENTS */ }; @@ -1123,6 +1127,16 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize) "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 --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 12b2f43abc3a282d2962cc22253f01f20ef7f4f3..147ee9125a1b4586707ac9a94ff1df6b88b6d953 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.198 2008/11/21 20:14:27 mha Exp $ + * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.199 2008/12/19 16:25:18 petere Exp $ */ #include "postgres_fe.h" #include "command.h" @@ -416,7 +416,23 @@ exec_command(const char *cmd, break; } break; - + case 'e': /* SQL/MED subsystem */ + switch(cmd[2]) + { + case 's': + success = listForeignServers(pattern, show_verbose); + break; + case 'u': + success = listUserMappings(pattern, show_verbose); + break; + case 'w': + success = listForeignDataWrappers(pattern, show_verbose); + break; + default: + status = PSQL_CMD_UNKNOWN; + break; + } + break; default: status = PSQL_CMD_UNKNOWN; } diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 5eb760513eccdfc5c0193396fa71b61b47715009..601e478aee52fe72a2f6894b7a4d662e36779bf2 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -8,7 +8,7 @@ * * Copyright (c) 2000-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.189 2008/12/19 14:39:58 alvherre Exp $ + * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.190 2008/12/19 16:25:18 petere Exp $ */ #include "postgres_fe.h" @@ -2785,3 +2785,156 @@ describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname, PQclear(res); return true; } + + +/* + * \dew + * + * Describes foreign-data wrappers + */ +bool +listForeignDataWrappers(const char *pattern, bool verbose) +{ + PQExpBufferData buf; + PGresult *res; + printQueryOpt myopt = pset.popt; + + initPQExpBuffer(&buf); + printfPQExpBuffer(&buf, + "SELECT fdwname AS \"%s\",\n" + " pg_catalog.pg_get_userbyid(fdwowner) AS \"%s\",\n" + " fdwlibrary AS \"%s\"\n", + 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_foreign_data_wrapper WHERE 1=1\n"); + + processSQLNamePattern(pset.db, &buf, pattern, true, false, + NULL, "fdwname", NULL, NULL); + + appendPQExpBuffer(&buf, "ORDER BY 1;"); + + 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; +} + +/* + * \des + * + * Describes servers. + */ +bool +listForeignServers(const char *pattern, bool verbose) +{ + PQExpBufferData buf; + PGresult *res; + printQueryOpt myopt = pset.popt; + + initPQExpBuffer(&buf); + printfPQExpBuffer(&buf, + "SELECT s.srvname AS \"%s\",\n" + " pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n" + " f.fdwname AS \"%s\"\n", + 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_foreign_server s\n" + "JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n"); + + processSQLNamePattern(pset.db, &buf, pattern, true, false, + NULL, "s.srvname", NULL, NULL); + + appendPQExpBuffer(&buf, "ORDER BY 1;"); + + 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; +} + +/* + * \deu + * + * Describes user mappings. + */ +bool +listUserMappings(const char *pattern, bool verbose) +{ + PQExpBufferData buf; + PGresult *res; + printQueryOpt myopt = pset.popt; + + initPQExpBuffer(&buf); + printfPQExpBuffer(&buf, + "SELECT um.srvname AS \"%s\",\n" + " um.usename AS \"%s\"", + gettext_noop("Server"), + gettext_noop("Username")); + + if (verbose) + appendPQExpBuffer(&buf, + ",\n um.umoptions AS \"%s\"", + gettext_noop("Options")); + + appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_user_mappings um WHERE 1=1\n"); + + processSQLNamePattern(pset.db, &buf, pattern, true, false, + NULL, "um.srvname", "um.usename", NULL); + + 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 --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h index 76e37d4e7f349690ee1a2c3b3e108a12a0fbe048..a0959576be77b19c390ce525314fa983c203533e 100644 --- a/src/bin/psql/describe.h +++ b/src/bin/psql/describe.h @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/describe.h,v 1.35 2008/01/01 19:45:56 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/describe.h,v 1.36 2008/12/19 16:25:18 petere Exp $ */ #ifndef DESCRIBE_H #define DESCRIBE_H @@ -66,5 +66,14 @@ extern bool listCasts(const char *pattern); /* \dn */ extern bool listSchemas(const char *pattern, bool verbose); +/* \dew */ +extern bool listForeignDataWrappers(const char *pattern, bool verbose); + +/* \des */ +extern bool listForeignServers(const char *pattern, bool verbose); + +/* \deu */ +extern bool listUserMappings(const char *pattern, bool verbose); + #endif /* DESCRIBE_H */ diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index 49db83c03d4f6f3b172868ecfe4fa49c6b839f13..164d17bba56c251e1bed5c4faa1f963adf6ab562 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.131 2008/11/06 15:18:36 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.132 2008/12/19 16:25:18 petere Exp $ */ #include "postgres_fe.h" @@ -203,6 +203,9 @@ slashUsage(unsigned short int pager) fprintf(output, _(" \\dC [PATTERN] list casts\n")); fprintf(output, _(" \\dd [PATTERN] show comment for object\n")); fprintf(output, _(" \\dD [PATTERN] list domains\n")); + fprintf(output, _(" \\des [PATTERN] list foreign servers (add \"+\" for more detail)\n")); + fprintf(output, _(" \\deu [PATTERN] list user mappings (add \"+\" for more detail)\n")); + fprintf(output, _(" \\dew [PATTERN] list foreign-data wrappers (add \"+\" for more detail)\n")); fprintf(output, _(" \\df [PATTERN] list functions (add \"+\" for more detail)\n")); fprintf(output, _(" \\dF [PATTERN] list text search configurations (add \"+\" for more detail)\n")); fprintf(output, _(" \\dFd [PATTERN] list text search dictionaries (add \"+\" for more detail)\n")); diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 635e0c7fa7b25e855a24afba38812722f7e2d907..54363a3e74e3f28aa7304616156154fe089dcf76 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.177 2008/11/20 14:04:46 petere Exp $ + * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.178 2008/12/19 16:25:18 petere Exp $ */ /*---------------------------------------------------------------------- @@ -497,6 +497,21 @@ static const SchemaQuery Query_for_list_of_views = { "SELECT pg_catalog.quote_ident(tmplname) FROM pg_catalog.pg_ts_template "\ " WHERE substring(pg_catalog.quote_ident(tmplname),1,%d)='%s'" +#define Query_for_list_of_fdws \ +" SELECT pg_catalog.quote_ident(fdwname) "\ +" FROM pg_catalog.pg_foreign_data_wrapper "\ +" WHERE substring(pg_catalog.quote_ident(fdwname),1,%d)='%s'" + +#define Query_for_list_of_servers \ +" SELECT pg_catalog.quote_ident(srvname) "\ +" FROM pg_catalog.pg_foreign_server "\ +" WHERE substring(pg_catalog.quote_ident(srvname),1,%d)='%s'" + +#define Query_for_list_of_user_mappings \ +" SELECT pg_catalog.quote_ident(usename) "\ +" FROM pg_catalog.pg_user_mappings "\ +" WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'" + /* * This is a list of all "things" in Pgsql, which can show up after CREATE or * DROP; and there is also a query to get a list of them. @@ -525,6 +540,7 @@ static const pgsql_thing_t words_after_create[] = { {"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,6 +552,7 @@ static const pgsql_thing_t words_after_create[] = { {"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", 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,6 +562,7 @@ static const pgsql_thing_t words_after_create[] = { {"TYPE", NULL, &Query_for_list_of_datatypes}, {"UNIQUE", NULL, NULL}, /* for CREATE UNIQUE INDEX ... */ {"USER", Query_for_list_of_roles}, + {"USER MAPPING FOR", NULL, NULL}, {"VIEW", NULL, &Query_for_list_of_views}, {NULL, NULL, NULL, false} /* end of list */ }; @@ -621,7 +639,7 @@ psql_completion(char *text, int start, int end) static const char *const backslash_commands[] = { "\\a", "\\connect", "\\C", "\\cd", "\\copy", "\\copyright", - "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\df", + "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\deu", "\\dew", "\\df", "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dn", "\\do", "\\dp", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du", "\\e", "\\echo", "\\encoding", @@ -686,9 +704,9 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev3_wd, "TABLE") != 0) { static const char *const list_ALTER[] = - {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FUNCTION", - "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", "SCHEMA", "SEQUENCE", "TABLE", - "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "VIEW", NULL}; + {"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 FOR", "VIEW", NULL}; COMPLETE_WITH_LIST(list_ALTER); } @@ -724,6 +742,18 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_LIST(list_ALTERDATABASE); } + /* ALTER FOREIGN DATA WRAPPER <name> */ + else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && + pg_strcasecmp(prev4_wd, "FOREIGN") == 0 && + pg_strcasecmp(prev3_wd, "DATA") == 0 && + pg_strcasecmp(prev2_wd, "WRAPPER") == 0) + { + static const char *const list_ALTER_FDW[] = + {"LIBRARY", "OPTIONS", "OWNER TO", NULL}; + + COMPLETE_WITH_LIST(list_ALTER_FDW); + } + /* ALTER INDEX <name> */ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && pg_strcasecmp(prev2_wd, "INDEX") == 0) @@ -746,6 +776,7 @@ psql_completion(char *text, int start, int end) /* ALTER USER,ROLE <name> */ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && + !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) && (pg_strcasecmp(prev2_wd, "USER") == 0 || pg_strcasecmp(prev2_wd, "ROLE") == 0)) { @@ -814,6 +845,15 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_LIST(list_ALTERSEQUENCE2); } + /* ALTER SERVER <name> */ + else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && + pg_strcasecmp(prev2_wd, "SERVER") == 0) + { + static const char *const list_ALTER_SERVER[] = + {"VERSION", "OPTIONS", "OWNER TO", NULL}; + + COMPLETE_WITH_LIST(list_ALTER_SERVER); + } /* ALTER VIEW <name> */ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && pg_strcasecmp(prev2_wd, "VIEW") == 0) @@ -1213,6 +1253,18 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev_wd, "TEMPLATE") == 0) COMPLETE_WITH_QUERY(Query_for_list_of_template_databases); + /* CREATE FOREIGN DATA WRAPPER */ + else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && + pg_strcasecmp(prev4_wd, "FOREIGN") == 0 && + pg_strcasecmp(prev3_wd, "DATA") == 0 && + pg_strcasecmp(prev2_wd, "WRAPPER") == 0) + COMPLETE_WITH_CONST("LIBRARY"); + + else if (pg_strcasecmp(prev5_wd, "DATA") == 0 && + pg_strcasecmp(prev4_wd, "WRAPPER") == 0 && + pg_strcasecmp(prev2_wd, "LIBRARY") == 0) + COMPLETE_WITH_CONST("LANGUAGE C"); + /* CREATE INDEX */ /* First off we complete CREATE UNIQUE with "INDEX" */ else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && @@ -1289,6 +1341,16 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev_wd, "TO") == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); +/* CREATE SERVER <name> */ + else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && + pg_strcasecmp(prev2_wd, "SERVER") == 0) + { + static const char *const list_CREATE_SERVER[] = + {"TYPE", "VERSION", "FOREIGN DATA WRAPPER", NULL}; + + COMPLETE_WITH_LIST(list_CREATE_SERVER); + } + /* CREATE TABLE */ /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */ else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && @@ -1357,6 +1419,7 @@ psql_completion(char *text, int start, int end) /* CREATE ROLE,USER,GROUP */ else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && + !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) && (pg_strcasecmp(prev2_wd, "ROLE") == 0 || pg_strcasecmp(prev2_wd, "GROUP") == 0 || pg_strcasecmp(prev2_wd, "USER") == 0)) { @@ -1472,12 +1535,17 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 || pg_strcasecmp(prev2_wd, "SCHEMA") == 0 || pg_strcasecmp(prev2_wd, "SEQUENCE") == 0 || + pg_strcasecmp(prev2_wd, "SERVER") == 0 || pg_strcasecmp(prev2_wd, "TABLE") == 0 || pg_strcasecmp(prev2_wd, "TYPE") == 0 || pg_strcasecmp(prev2_wd, "VIEW") == 0)) || (pg_strcasecmp(prev4_wd, "DROP") == 0 && pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 && prev_wd[strlen(prev_wd) - 1] == ')') || + (pg_strcasecmp(prev5_wd, "DROP") == 0 && + pg_strcasecmp(prev4_wd, "FOREIGN") == 0 && + pg_strcasecmp(prev3_wd, "DATA") == 0 && + pg_strcasecmp(prev2_wd, "WRAPPER") == 0) || (pg_strcasecmp(prev5_wd, "DROP") == 0 && pg_strcasecmp(prev4_wd, "TEXT") == 0 && pg_strcasecmp(prev3_wd, "SEARCH") == 0 && @@ -1607,6 +1675,14 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_LIST(list_FROMIN); } +/* FOREIGN DATA WRAPPER */ + /* applies in ALTER/DROP FDW and in CREATE SERVER */ + else if (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_QUERY(Query_for_list_of_fdws); + /* GRANT && REVOKE*/ /* Complete GRANT/REVOKE with a list of privileges */ else if (pg_strcasecmp(prev_wd, "GRANT") == 0 || @@ -1640,6 +1716,8 @@ psql_completion(char *text, int start, int end) 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'" @@ -1759,6 +1837,10 @@ psql_completion(char *text, int start, int end) else if (pg_strcasecmp(prev_wd, "NOTIFY") == 0) COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(relname) FROM pg_catalog.pg_listener WHERE substring(pg_catalog.quote_ident(relname),1,%d)='%s'"); +/* OPTIONS */ + else if (pg_strcasecmp(prev_wd, "OPTIONS") == 0) + COMPLETE_WITH_CONST("("); + /* OWNER TO - complete with available roles */ else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 && pg_strcasecmp(prev_wd, "TO") == 0) @@ -2012,6 +2094,32 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev4_wd, "UPDATE") == 0) COMPLETE_WITH_CONST("="); +/* USER MAPPING */ + else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 || + pg_strcasecmp(prev3_wd, "CREATE") == 0 || + pg_strcasecmp(prev3_wd, "DROP") == 0) && + pg_strcasecmp(prev2_wd, "USER") == 0 && + pg_strcasecmp(prev_wd, "MAPPING") == 0) + COMPLETE_WITH_CONST("FOR"); + else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && + pg_strcasecmp(prev3_wd, "USER") == 0 && + pg_strcasecmp(prev2_wd, "MAPPING") == 0 && + pg_strcasecmp(prev_wd, "FOR") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles); + else if ((pg_strcasecmp(prev4_wd, "ALTER") == 0 || + pg_strcasecmp(prev4_wd, "DROP") == 0) && + pg_strcasecmp(prev3_wd, "USER") == 0 && + pg_strcasecmp(prev2_wd, "MAPPING") == 0 && + pg_strcasecmp(prev_wd, "FOR") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings); + else if ((pg_strcasecmp(prev5_wd, "CREATE") == 0 || + pg_strcasecmp(prev5_wd, "ALTER") == 0 || + pg_strcasecmp(prev5_wd, "DROP") == 0) && + pg_strcasecmp(prev4_wd, "USER") == 0 && + pg_strcasecmp(prev3_wd, "MAPPING") == 0 && + pg_strcasecmp(prev2_wd, "FOR") == 0) + COMPLETE_WITH_CONST("SERVER"); + /* * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ] * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ] @@ -2088,6 +2196,12 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); else if (strcmp(prev_wd, "\\dD") == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); + else if (strcmp(prev_wd, "\\des") == 0 || strcmp(prev_wd, "\\des+") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_servers); + else if (strcmp(prev_wd, "\\deu") == 0 || strcmp(prev_wd, "\\deu+") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings); + else if (strcmp(prev_wd, "\\dew") == 0 || strcmp(prev_wd, "\\dew+") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_fdws); else if (strcmp(prev_wd, "\\df") == 0 || strcmp(prev_wd, "\\df+") == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); else if (strcmp(prev_wd, "\\dF") == 0 || strcmp(prev_wd, "\\dF+") == 0) diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 2ee189437f2061190aebef1f326fbdd575c18847..b1b2a12837361588e08f94a8343aa13793bb6f75 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -4,7 +4,7 @@ * "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 + * 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 @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.511 2008/12/04 17:51:27 petere Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.512 2008/12/19 16:25:18 petere Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200812041 +#define CATALOG_VERSION_NO 200812191 #endif diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 8929be6a3426a985338950bfbdaf95c9dc856cfe..8cd28f7e6c8fc8b6fd10ca30bff3cda6b293805d 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.36 2008/06/08 22:41:04 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.37 2008/12/19 16:25:18 petere Exp $ * *------------------------------------------------------------------------- */ @@ -143,6 +143,9 @@ typedef enum ObjectClass 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 --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 18defe8ee788cf034a39911f839e3259bb2ae363..49b2c17fa8a5d70fae1b44ce3b4814b640e1dade 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.103 2008/06/19 00:46:06 alvherre Exp $ + * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.104 2008/12/19 16:25:18 petere Exp $ * *------------------------------------------------------------------------- */ @@ -252,6 +252,24 @@ DECLARE_UNIQUE_INDEX(pg_type_oid_index, 2703, on pg_type using btree(oid oid_ops 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_index, 548, on pg_foreign_data_wrapper using btree(fdwname name_ops)); +#define ForeignDataWrapperNameIndexId 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_index, 549, on pg_foreign_server using btree(srvname name_ops)); +#define ForeignServerNameIndexId 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 --git a/src/include/catalog/pg_foreign_data_wrapper.h b/src/include/catalog/pg_foreign_data_wrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..aa245961f9e5e93d78585888b703f96bd02740ec --- /dev/null +++ b/src/include/catalog/pg_foreign_data_wrapper.h @@ -0,0 +1,62 @@ +/*------------------------------------------------------------------------- + * + * 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: pgsql/src/include/catalog/pg_foreign_data_wrapper.h,v 1.1 2008/12/19 16:25:18 petere Exp $ + * + * 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 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 5 +#define Anum_pg_foreign_data_wrapper_fdwname 1 +#define Anum_pg_foreign_data_wrapper_fdwowner 2 +#define Anum_pg_foreign_data_wrapper_fdwlibrary 3 +#define Anum_pg_foreign_data_wrapper_fdwacl 4 +#define Anum_pg_foreign_data_wrapper_fdwoptions 5 + +#endif /* PG_FOREIGN_DATA_WRAPPER_H */ diff --git a/src/include/catalog/pg_foreign_server.h b/src/include/catalog/pg_foreign_server.h new file mode 100644 index 0000000000000000000000000000000000000000..cc334c19159073e0e09124cbdddaed92233c35c6 --- /dev/null +++ b/src/include/catalog/pg_foreign_server.h @@ -0,0 +1,65 @@ +/*------------------------------------------------------------------------- + * + * 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: pgsql/src/include/catalog/pg_foreign_server.h,v 1.1 2008/12/19 16:25:18 petere Exp $ + * + * 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 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 7 +#define Anum_pg_foreign_server_srvname 1 +#define Anum_pg_foreign_server_srvowner 2 +#define Anum_pg_foreign_server_srvfdw 3 +#define Anum_pg_foreign_server_srvtype 4 +#define Anum_pg_foreign_server_srvversion 5 +#define Anum_pg_foreign_server_srvacl 6 +#define Anum_pg_foreign_server_srvoptions 7 + +#endif /* PG_FOREIGN_SERVER_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 9d4e6dcfffac589ab03aaf438b001f928cbe3e6a..63bcdc7c06e415aae0e2fc03983961414b2a3b6b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.531 2008/12/18 18:20:34 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.532 2008/12/19 16:25:18 petere Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -3582,6 +3582,32 @@ DESCR("current user privilege on tablespace by tablespace name"); DATA(insert OID = 2395 ( has_tablespace_privilege PGNSP PGUID 12 1 0 0 f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ has_tablespace_privilege_id _null_ _null_ _null_ )); DESCR("current user privilege on tablespace by tablespace oid"); +DATA(insert OID = 3000 ( has_foreign_data_wrapper_privilege PGNSP PGUID 12 1 0 0 f f t f s 3 0 16 "19 25 25" _null_ _null_ _null_ _null_ has_foreign_data_wrapper_privilege_name_name _null_ _null_ _null_ )); +DESCR("user privilege on foreign data wrapper by username, foreign data wrapper name"); +DATA(insert OID = 3001 ( has_foreign_data_wrapper_privilege PGNSP PGUID 12 1 0 0 f f t f s 3 0 16 "19 26 25" _null_ _null_ _null_ _null_ has_foreign_data_wrapper_privilege_name_id _null_ _null_ _null_ )); +DESCR("user privilege on foreign data wrapper by username, foreign data wrapper oid"); +DATA(insert OID = 3002 ( has_foreign_data_wrapper_privilege PGNSP PGUID 12 1 0 0 f f t f s 3 0 16 "26 25 25" _null_ _null_ _null_ _null_ has_foreign_data_wrapper_privilege_id_name _null_ _null_ _null_ )); +DESCR("user privilege on foreign data wrapper by user oid, foreign data wrapper name"); +DATA(insert OID = 3003 ( has_foreign_data_wrapper_privilege PGNSP PGUID 12 1 0 0 f f t f s 3 0 16 "26 26 25" _null_ _null_ _null_ _null_ has_foreign_data_wrapper_privilege_id_id _null_ _null_ _null_ )); +DESCR("user privilege on foreign data wrapper by user oid, foreign data wrapper oid"); +DATA(insert OID = 3004 ( has_foreign_data_wrapper_privilege PGNSP PGUID 12 1 0 0 f f t f s 2 0 16 "25 25" _null_ _null_ _null_ _null_ has_foreign_data_wrapper_privilege_name _null_ _null_ _null_ )); +DESCR("current user privilege on foreign data wrapper by foreign data wrapper name"); +DATA(insert OID = 3005 ( has_foreign_data_wrapper_privilege PGNSP PGUID 12 1 0 0 f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ has_foreign_data_wrapper_privilege_id _null_ _null_ _null_ )); +DESCR("current user privilege on foreign data wrapper by foreign data wrapper oid"); + +DATA(insert OID = 3006 ( has_server_privilege PGNSP PGUID 12 1 0 0 f f t f s 3 0 16 "19 25 25" _null_ _null_ _null_ _null_ has_server_privilege_name_name _null_ _null_ _null_ )); +DESCR("user privilege on server by username, server name"); +DATA(insert OID = 3007 ( has_server_privilege PGNSP PGUID 12 1 0 0 f f t f s 3 0 16 "19 26 25" _null_ _null_ _null_ _null_ has_server_privilege_name_id _null_ _null_ _null_ )); +DESCR("user privilege on server by username, server oid"); +DATA(insert OID = 3008 ( has_server_privilege PGNSP PGUID 12 1 0 0 f f t f s 3 0 16 "26 25 25" _null_ _null_ _null_ _null_ has_server_privilege_id_name _null_ _null_ _null_ )); +DESCR("user privilege on server by user oid, server name"); +DATA(insert OID = 3009 ( has_server_privilege PGNSP PGUID 12 1 0 0 f f t f s 3 0 16 "26 26 25" _null_ _null_ _null_ _null_ has_server_privilege_id_id _null_ _null_ _null_ )); +DESCR("user privilege on server by user oid, server oid"); +DATA(insert OID = 3010 ( has_server_privilege PGNSP PGUID 12 1 0 0 f f t f s 2 0 16 "25 25" _null_ _null_ _null_ _null_ has_server_privilege_name _null_ _null_ _null_ )); +DESCR("current user privilege on server by server name"); +DATA(insert OID = 3011 ( has_server_privilege PGNSP PGUID 12 1 0 0 f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ has_server_privilege_id _null_ _null_ _null_ )); +DESCR("current user privilege on server by server oid"); + DATA(insert OID = 2705 ( pg_has_role PGNSP PGUID 12 1 0 0 f f t f s 3 0 16 "19 19 25" _null_ _null_ _null_ _null_ pg_has_role_name_name _null_ _null_ _null_ )); DESCR("user privilege on role by username, role name"); DATA(insert OID = 2706 ( pg_has_role PGNSP PGUID 12 1 0 0 f f t f s 3 0 16 "19 26 25" _null_ _null_ _null_ _null_ pg_has_role_name_id _null_ _null_ _null_ )); @@ -4604,6 +4630,9 @@ DESCR("record greater than or equal"); 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"); +DATA(insert OID = 2998 ( pg_options_to_table PGNSP PGUID 12 1 3 0 f f t t s 1 0 2249 "1009" "{1009,25,25}" "{i,o,o}" "{options_array,option_name,option_value}" _null_ pg_options_to_table _null_ _null_ _null_ )); +DESCR("convert generic options array to name/value table"); + /* * Symbolic values for provolatile column: these indicate whether the result diff --git a/src/include/catalog/pg_user_mapping.h b/src/include/catalog/pg_user_mapping.h new file mode 100644 index 0000000000000000000000000000000000000000..e1770e012382345217aecd7f151be1ef9cf1563d --- /dev/null +++ b/src/include/catalog/pg_user_mapping.h @@ -0,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: pgsql/src/include/catalog/pg_user_mapping.h,v 1.1 2008/12/19 16:25:19 petere Exp $ + * + * 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 --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 61a852623c35530613621ebf7f66d133109822cb..71f7e71522509d5938f02fa03237656a194b0d3c 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.90 2008/12/04 17:51:27 petere Exp $ + * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.91 2008/12/19 16:25:19 petere Exp $ * *------------------------------------------------------------------------- */ @@ -118,6 +118,22 @@ extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId); extern text *serialize_deflist(List *deflist); extern List *deserialize_deflist(Datum txt); +/* commands/foreigncmds.c */ +extern void AlterForeignServerOwner(const char *name, Oid newOwnerId); +extern void AlterForeignDataWrapperOwner(const char *name, 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 --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h new file mode 100644 index 0000000000000000000000000000000000000000..7fce532888ef6e1757c6b2cb7c8caefed7e6ac2f --- /dev/null +++ b/src/include/foreign/foreign.h @@ -0,0 +1,101 @@ +/*------------------------------------------------------------------------- + * + * foreign.h + * support for foreign-data wrappers, servers and user mappings. + * + * + * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group + * + * $PostgreSQL: pgsql/src/include/foreign/foreign.h,v 1.1 2008/12/19 16:25:19 petere Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef FOREIGN_H +#define FOREIGN_H + +#include "nodes/parsenodes.h" +#include "nodes/pg_list.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 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 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 *); + +/* + * 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 */ + + OptionListValidatorFunc validateOptionList; +}; + + +extern ForeignServer *GetForeignServer(Oid serverid); +extern ForeignServer *GetForeignServerByName(const char *name, bool missing_ok); +extern Oid GetForeignServerOidByName(const char *name, bool missing_ok); +extern UserMapping *GetUserMapping(Oid userid, Oid serverid); +extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid); +extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name, + bool missing_ok); +extern Oid GetForeignDataWrapperOidByName(const char *name, bool missing_ok); +extern ForeignDataWrapperLibrary *GetForeignDataWrapperLibrary(const char *libname); + +/* Foreign data wrapper interface functions */ +extern void _pg_validateOptionList(ForeignDataWrapper *fdw, + GenericOptionFlags flags, List *options); + +#endif /* FOREIGN_H */ diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h index d4e58cfe5f31391d11d6060d8a2f352daab5b4e7..a863c1369b2471c7b1bd13d7fa5f861408f86242 100644 --- a/src/include/nodes/makefuncs.h +++ b/src/include/nodes/makefuncs.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/makefuncs.h,v 1.63 2008/09/01 20:42:45 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/makefuncs.h,v 1.64 2008/12/19 16:25:19 petere Exp $ * *------------------------------------------------------------------------- */ @@ -67,4 +67,6 @@ extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype, extern DefElem *makeDefElem(char *name, Node *arg); +extern OptionDefElem *makeOptionDefElem(int op, DefElem *def); + #endif /* MAKEFUNC_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 4161c08ec54da18cb2a680669a06d867d737aa31..7383697f6ce1fda801f23eb7d8ca0c2c9cb40fb5 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.215 2008/11/22 22:47:06 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.216 2008/12/19 16:25:19 petere Exp $ * *------------------------------------------------------------------------- */ @@ -325,6 +325,15 @@ typedef enum NodeTag 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,6 +357,7 @@ typedef enum NodeTag T_IndexElem, T_Constraint, T_DefElem, + T_OptionDefElem, T_RangeTblEntry, T_SortGroupClause, T_FkConstraint, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index c84a77585fc4fa1bf9bd414b6f58d0450cefba3b..2f22475bf10fe4008eba75da59c37937aaba729b 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -13,7 +13,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.383 2008/12/18 18:20:35 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.384 2008/12/19 16:25:19 petere Exp $ * *------------------------------------------------------------------------- */ @@ -49,6 +49,13 @@ typedef enum SortByNulls 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,7 +74,7 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */ #define ACL_REFERENCES (1<<5) #define ACL_TRIGGER (1<<6) #define ACL_EXECUTE (1<<7) /* for functions */ -#define ACL_USAGE (1<<8) /* for languages and namespaces */ +#define ACL_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 */ @@ -466,6 +473,17 @@ typedef struct DefElem Node *arg; /* a (Value *) or a (TypeName *) */ } 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 * @@ -930,6 +948,8 @@ typedef enum ObjectType OBJECT_CONVERSION, OBJECT_DATABASE, OBJECT_DOMAIN, + OBJECT_FDW, + OBJECT_FOREIGN_SERVER, OBJECT_FUNCTION, OBJECT_INDEX, OBJECT_LANGUAGE, @@ -1076,6 +1096,8 @@ typedef enum GrantObjectType 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 */ @@ -1328,6 +1350,96 @@ typedef struct DropTableSpaceStmt bool missing_ok; /* skip error if missing? */ } DropTableSpaceStmt; +/* ---------------------- + * Create/Drop FOREIGN DATA WRAPPER Statements + * ---------------------- + */ + +typedef struct CreateFdwStmt +{ + NodeTag type; + char *fdwname; /* foreign-data wrapper name */ + char *library; /* libray name */ + List *options; /* generic options to FDW */ +} CreateFdwStmt; + +typedef struct AlterFdwStmt +{ + NodeTag type; + char *fdwname; /* foreign-data wrapper name */ + char *library; /* libray name */ + List *options; /* generic options to FDW */ +} AlterFdwStmt; + +typedef struct DropFdwStmt +{ + NodeTag type; + char *fdwname; /* 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; + char *servername; /* server name */ + char *servertype; /* optional server type */ + char *version; /* optional server version */ + char *fdwname; /* FDW name */ + List *options; /* generic options to server */ +} CreateForeignServerStmt; + +typedef struct AlterForeignServerStmt +{ + NodeTag type; + char *servername; /* server name */ + char *version; /* optional server version */ + List *options; /* generic options to server */ + bool has_version; /* version specified */ +} AlterForeignServerStmt; + +typedef struct DropForeignServerStmt +{ + NodeTag type; + char *servername; /* 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 */ + char *servername; /* server name */ + List *options; /* generic options to server */ +} CreateUserMappingStmt; + +typedef struct AlterUserMappingStmt +{ + NodeTag type; + char *username; /* username or PUBLIC/CURRENT_USER */ + char *servername; /* server name */ + List *options; /* generic options to server */ +} AlterUserMappingStmt; + +typedef struct DropUserMappingStmt +{ + NodeTag type; + char *username; /* username or PUBLIC/CURRENT_USER */ + char *servername; /* server name */ + bool missing_ok; /* ignore missing mappings */ +} DropUserMappingStmt; + /* ---------------------- * Create/Drop TRIGGER Statements * ---------------------- diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 553ceabee3bdd8ab3fed151228f6c4e7df973d32..defbb47eab106de22e55e0a5177f96d5b390750d 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.104 2008/09/08 00:47:41 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.105 2008/12/19 16:25:19 petere Exp $ * * NOTES * An ACL array is simply an array of AclItems, representing the union @@ -146,6 +146,8 @@ typedef ArrayType Acl; #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,6 +186,8 @@ typedef enum AclObjectKind 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,6 +265,10 @@ extern AclMode pg_namespace_aclmask(Oid nsp_oid, Oid roleid, 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,6 +276,8 @@ extern AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode); 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,5 +296,6 @@ extern bool pg_database_ownercheck(Oid db_oid, Oid roleid); 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 --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 7f20cdf0015323b1b8e0e0522d86a3af49bf9b95..a00415aaa65d41f84f38385dc6032b5b02691e88 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.327 2008/12/04 17:51:28 petere Exp $ + * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.328 2008/12/19 16:25:19 petere Exp $ * *------------------------------------------------------------------------- */ @@ -34,6 +34,12 @@ extern Datum has_database_privilege_id_name(PG_FUNCTION_ARGS); extern Datum has_database_privilege_id_id(PG_FUNCTION_ARGS); extern Datum has_database_privilege_name(PG_FUNCTION_ARGS); extern Datum has_database_privilege_id(PG_FUNCTION_ARGS); +extern Datum has_foreign_data_wrapper_privilege_name_name(PG_FUNCTION_ARGS); +extern Datum has_foreign_data_wrapper_privilege_name_id(PG_FUNCTION_ARGS); +extern Datum has_foreign_data_wrapper_privilege_id_name(PG_FUNCTION_ARGS); +extern Datum has_foreign_data_wrapper_privilege_id_id(PG_FUNCTION_ARGS); +extern Datum has_foreign_data_wrapper_privilege_name(PG_FUNCTION_ARGS); +extern Datum has_foreign_data_wrapper_privilege_id(PG_FUNCTION_ARGS); extern Datum has_function_privilege_name_name(PG_FUNCTION_ARGS); extern Datum has_function_privilege_name_id(PG_FUNCTION_ARGS); extern Datum has_function_privilege_id_name(PG_FUNCTION_ARGS); @@ -52,6 +58,12 @@ extern Datum has_schema_privilege_id_name(PG_FUNCTION_ARGS); extern Datum has_schema_privilege_id_id(PG_FUNCTION_ARGS); extern Datum has_schema_privilege_name(PG_FUNCTION_ARGS); extern Datum has_schema_privilege_id(PG_FUNCTION_ARGS); +extern Datum has_server_privilege_name_name(PG_FUNCTION_ARGS); +extern Datum has_server_privilege_name_id(PG_FUNCTION_ARGS); +extern Datum has_server_privilege_id_name(PG_FUNCTION_ARGS); +extern Datum has_server_privilege_id_id(PG_FUNCTION_ARGS); +extern Datum has_server_privilege_name(PG_FUNCTION_ARGS); +extern Datum has_server_privilege_id(PG_FUNCTION_ARGS); extern Datum has_tablespace_privilege_name_name(PG_FUNCTION_ARGS); extern Datum has_tablespace_privilege_name_id(PG_FUNCTION_ARGS); extern Datum has_tablespace_privilege_id_name(PG_FUNCTION_ARGS); diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h index e6220477728218bb3aa01dfffde80a8fce932ba7..ded7a1d0523683db33e5fbc037a775b6aea1179e 100644 --- a/src/include/utils/syscache.h +++ b/src/include/utils/syscache.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.72 2008/05/07 01:04:49 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.73 2008/12/19 16:25:19 petere Exp $ * *------------------------------------------------------------------------- */ @@ -51,6 +51,10 @@ enum SysCacheIdentifier DATABASEOID, ENUMOID, ENUMTYPOIDNAME, + FOREIGNDATAWRAPPERNAME, + FOREIGNDATAWRAPPEROID, + FOREIGNSERVERNAME, + FOREIGNSERVEROID, INDEXRELID, LANGNAME, LANGOID, @@ -76,7 +80,9 @@ enum SysCacheIdentifier TSTEMPLATENAMENSP, TSTEMPLATEOID, TYPENAMENSP, - TYPEOID + TYPEOID, + USERMAPPINGOID, + USERMAPPINGUSERSERVER }; extern void InitCatalogCache(void); diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out new file mode 100644 index 0000000000000000000000000000000000000000..1d225eba14886e3928252b6e62b1e3a604ed4d16 --- /dev/null +++ b/src/test/regress/expected/foreign_data.out @@ -0,0 +1,1008 @@ +-- +-- Test foreign-data wrapper and server management. +-- +-- Clean up in case a prior regression run failed +-- Suppress NOTICE messages when roles don't exist +SET client_min_messages TO 'error'; +DROP ROLE IF EXISTS foreign_data_user, regress_test_role, regress_test_role2, regress_test_role_super, regress_test_indirect, unpriviled_role; +RESET client_min_messages; +CREATE ROLE foreign_data_user LOGIN SUPERUSER; +SET SESSION AUTHORIZATION 'foreign_data_user'; +CREATE ROLE regress_test_role; +CREATE ROLE regress_test_role2; +CREATE ROLE regress_test_role_super SUPERUSER; +CREATE ROLE regress_test_indirect; +CREATE ROLE unprivileged_role; +CREATE FOREIGN DATA WRAPPER dummy LIBRARY 'dummy_fdw' LANGUAGE C; +CREATE FOREIGN DATA WRAPPER postgresql LIBRARY 'postgresql_fdw' LANGUAGE C; +-- At this point we should have 2 built-in wrappers and no servers. +SELECT fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; + fdwname | fdwlibrary | fdwoptions +------------+----------------+------------ + dummy | dummy_fdw | + postgresql | postgresql_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; +DROP FOREIGN DATA WRAPPER foo; +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C; +\dew + List of foreign-data wrappers + Name | Owner | Library +------------+-------------------+---------------- + dummy | foreign_data_user | dummy_fdw + foo | foreign_data_user | dummy_fdw + postgresql | foreign_data_user | postgresql_fdw +(3 rows) + +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C; -- duplicate +ERROR: foreign-data wrapper "foo" already exists +CREATE FOREIGN DATA WRAPPER "Foo" LIBRARY 'dummy_fdw' LANGUAGE C; +DROP FOREIGN DATA WRAPPER "Foo"; +DROP FOREIGN DATA WRAPPER foo; +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C OPTIONS (testing '1'); +\dew+ + List of foreign-data wrappers + Name | Owner | Library | Access privileges | Options +------------+-------------------+----------------+-------------------+------------- + dummy | foreign_data_user | dummy_fdw | | + foo | foreign_data_user | dummy_fdw | | {testing=1} + postgresql | foreign_data_user | postgresql_fdw | | +(3 rows) + +DROP FOREIGN DATA WRAPPER foo; +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C OPTIONS (testing '1', testing '2'); -- ERROR +ERROR: option "testing" provided more than once +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C OPTIONS (testing '1', another '2'); +\dew+ + List of foreign-data wrappers + Name | Owner | Library | Access privileges | Options +------------+-------------------+----------------+-------------------+----------------------- + dummy | foreign_data_user | dummy_fdw | | + foo | foreign_data_user | dummy_fdw | | {testing=1,another=2} + postgresql | foreign_data_user | postgresql_fdw | | +(3 rows) + +DROP FOREIGN DATA WRAPPER foo; +SET ROLE regress_test_role; +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C; -- ERROR +ERROR: permission denied to create foreign-data wrapper "foo" +HINT: Must be superuser to create a foreign-data wrapper. +RESET ROLE; +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'postgresql_fdw' LANGUAGE C; +\dew+ + List of foreign-data wrappers + Name | Owner | Library | Access privileges | Options +------------+-------------------+----------------+-------------------+--------- + dummy | foreign_data_user | dummy_fdw | | + foo | foreign_data_user | postgresql_fdw | | + postgresql | foreign_data_user | postgresql_fdw | | +(3 rows) + +-- 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'; +WARNING: changing the foreign-data wrapper library can cause the options for dependent objects to become invalid +ALTER FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw'; +WARNING: changing the foreign-data wrapper library can cause the options for dependent objects to become invalid +\dew+ + List of foreign-data wrappers + Name | Owner | Library | Access privileges | Options +------------+-------------------+----------------+-------------------+--------- + dummy | foreign_data_user | dummy_fdw | | + foo | foreign_data_user | dummy_fdw | | + postgresql | foreign_data_user | postgresql_fdw | | +(3 rows) + +ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2'); +ALTER FOREIGN DATA WRAPPER foo OPTIONS (SET c '4'); -- ERROR +ERROR: option "c" not found +ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP c); -- ERROR +ERROR: option "c" not found +ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x); +\dew+ + List of foreign-data wrappers + Name | Owner | Library | Access privileges | Options +------------+-------------------+----------------+-------------------+----------- + dummy | foreign_data_user | dummy_fdw | | + foo | foreign_data_user | dummy_fdw | | {a=1,b=2} + postgresql | foreign_data_user | postgresql_fdw | | +(3 rows) + +ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4'); +\dew+ + List of foreign-data wrappers + Name | Owner | Library | Access privileges | Options +------------+-------------------+----------------+-------------------+----------- + dummy | foreign_data_user | dummy_fdw | | + foo | foreign_data_user | dummy_fdw | | {b=3,c=4} + postgresql | foreign_data_user | postgresql_fdw | | +(3 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 +\dew+ + List of foreign-data wrappers + Name | Owner | Library | Access privileges | Options +------------+-------------------+----------------+-------------------+--------------- + dummy | foreign_data_user | dummy_fdw | | + foo | foreign_data_user | dummy_fdw | | {b=3,c=4,a=2} + postgresql | foreign_data_user | postgresql_fdw | | +(3 rows) + +SET ROLE regress_test_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 regress_test_role_super; +ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5'); +\dew+ + List of foreign-data wrappers + Name | Owner | Library | Access privileges | Options +------------+-------------------+----------------+-------------------+------------------- + dummy | foreign_data_user | dummy_fdw | | + foo | foreign_data_user | dummy_fdw | | {b=3,c=4,a=2,d=5} + postgresql | foreign_data_user | postgresql_fdw | | +(3 rows) + +ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_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 regress_test_role_super; +ALTER ROLE regress_test_role_super NOSUPERUSER; +SET ROLE regress_test_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; +\dew+ + List of foreign-data wrappers + Name | Owner | Library | Access privileges | Options +------------+-------------------------+----------------+-------------------+------------------- + dummy | foreign_data_user | dummy_fdw | | + foo | regress_test_role_super | dummy_fdw | | {b=3,c=4,a=2,d=5} + postgresql | foreign_data_user | postgresql_fdw | | +(3 rows) + +-- DROP FOREIGN DATA WRAPPER +DROP FOREIGN DATA WRAPPER nonexistent; -- ERROR +ERROR: foreign-data wrapper "nonexistent" does not exist +DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent; +NOTICE: foreign-data wrapper "nonexistent" does not exist, skipping +\dew+ + List of foreign-data wrappers + Name | Owner | Library | Access privileges | Options +------------+-------------------------+----------------+-------------------+------------------- + dummy | foreign_data_user | dummy_fdw | | + foo | regress_test_role_super | dummy_fdw | | {b=3,c=4,a=2,d=5} + postgresql | foreign_data_user | postgresql_fdw | | +(3 rows) + +DROP ROLE regress_test_role_super; -- ERROR +ERROR: role "regress_test_role_super" cannot be dropped because some objects depend on it +DETAIL: owner of foreign-data wrapper foo +SET ROLE regress_test_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; +ALTER ROLE regress_test_role_super SUPERUSER; +DROP FOREIGN DATA WRAPPER foo; +DROP ROLE regress_test_role_super; +\dew+ + List of foreign-data wrappers + Name | Owner | Library | Access privileges | Options +------------+-------------------+----------------+-------------------+--------- + dummy | foreign_data_user | dummy_fdw | | + postgresql | foreign_data_user | postgresql_fdw | | +(2 rows) + +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C; +CREATE SERVER s1 FOREIGN DATA WRAPPER foo; +CREATE USER MAPPING FOR current_user SERVER s1; +\dew+ + List of foreign-data wrappers + Name | Owner | Library | Access privileges | Options +------------+-------------------+----------------+-------------------+--------- + dummy | foreign_data_user | dummy_fdw | | + foo | foreign_data_user | dummy_fdw | | + postgresql | foreign_data_user | postgresql_fdw | | +(3 rows) + +\des+ + List of foreign servers + Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options +------+-------------------+----------------------+-------------------+------+---------+--------- + s1 | foreign_data_user | foo | | | | +(1 row) + +\deu+ + List of user mappings + Server | Username | Options +--------+-------------------+--------- + s1 | foreign_data_user | +(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 foreign_data_user depends on server s1 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +SET ROLE regress_test_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 foreign_data_user +\dew+ + List of foreign-data wrappers + Name | Owner | Library | Access privileges | Options +------------+-------------------+----------------+-------------------+--------- + dummy | foreign_data_user | dummy_fdw | | + postgresql | foreign_data_user | postgresql_fdw | | +(2 rows) + +\des+ + List of foreign servers + Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options +------+-------+----------------------+-------------------+------+---------+--------- +(0 rows) + +\deu+ + List of user mappings + 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 'dummy_fdw' LANGUAGE C OPTIONS (test_wrapper 'true'); +CREATE SERVER s1 FOREIGN DATA WRAPPER foo; +CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR +ERROR: server "s1" already exists +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 "S6" FOREIGN DATA WRAPPER foo OPTIONS (mixed_case_names 'true'); +CREATE SERVER s7 TYPE 'oracle' VERSION '17.0' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b'); +CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql 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 postgresql OPTIONS (host 'localhost', dbname 's8db'); +\des+ + List of foreign servers + Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options +------+-------------------+----------------------+-------------------+--------+---------+------------------------------ + S6 | foreign_data_user | foo | | | | {mixed_case_names=true} + s1 | foreign_data_user | foo | | | | + s2 | foreign_data_user | foo | | | | {host=a,dbname=b} + s3 | foreign_data_user | foo | | oracle | | + s4 | foreign_data_user | foo | | oracle | | {host=a,dbname=b} + s5 | foreign_data_user | foo | | | 15.0 | + s6 | foreign_data_user | foo | | | 16.0 | {host=a,dbname=b} + s7 | foreign_data_user | foo | | oracle | 17.0 | {host=a,dbname=b} + s8 | foreign_data_user | postgresql | | | | {host=localhost,dbname=s8db} +(9 rows) + +SET ROLE regress_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 regress_test_role; +SET ROLE regress_test_role; +CREATE SERVER st1 FOREIGN DATA WRAPPER foo; +RESET ROLE; +\des+ + List of foreign servers + Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options +------+-------------------+----------------------+-------------------+--------+---------+------------------------------ + S6 | foreign_data_user | foo | | | | {mixed_case_names=true} + s1 | foreign_data_user | foo | | | | + s2 | foreign_data_user | foo | | | | {host=a,dbname=b} + s3 | foreign_data_user | foo | | oracle | | + s4 | foreign_data_user | foo | | oracle | | {host=a,dbname=b} + s5 | foreign_data_user | foo | | | 15.0 | + s6 | foreign_data_user | foo | | | 16.0 | {host=a,dbname=b} + s7 | foreign_data_user | foo | | oracle | 17.0 | {host=a,dbname=b} + s8 | foreign_data_user | postgresql | | | | {host=localhost,dbname=s8db} + st1 | regress_test_role | foo | | | | +(10 rows) + +REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role; +GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_indirect; +SET ROLE regress_test_role; +CREATE SERVER st2 FOREIGN DATA WRAPPER foo; -- ERROR +ERROR: permission denied for foreign-data wrapper foo +RESET ROLE; +GRANT regress_test_indirect TO regress_test_role; +SET ROLE regress_test_role; +CREATE SERVER st2 FOREIGN DATA WRAPPER foo; +\des+ + List of foreign servers + Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options +------+-------------------+----------------------+-------------------+--------+---------+------------------------------ + S6 | foreign_data_user | foo | | | | {mixed_case_names=true} + s1 | foreign_data_user | foo | | | | + s2 | foreign_data_user | foo | | | | {host=a,dbname=b} + s3 | foreign_data_user | foo | | oracle | | + s4 | foreign_data_user | foo | | oracle | | {host=a,dbname=b} + s5 | foreign_data_user | foo | | | 15.0 | + s6 | foreign_data_user | foo | | | 16.0 | {host=a,dbname=b} + s7 | foreign_data_user | foo | | oracle | 17.0 | {host=a,dbname=b} + s8 | foreign_data_user | postgresql | | | | {host=localhost,dbname=s8db} + st1 | regress_test_role | foo | | | | + st2 | regress_test_role | foo | | | | +(11 rows) + +RESET ROLE; +REVOKE regress_test_indirect FROM regress_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: server "s0" 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 regress_test_role; +GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION; +\des+ + List of foreign servers + Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options +------+-------------------+----------------------+---------------------------------------------------------------------------------+--------+---------+------------------------------ + S6 | foreign_data_user | foo | | | | {mixed_case_names=true} + s1 | foreign_data_user | foo | {foreign_data_user=U/foreign_data_user,regress_test_role=U/foreign_data_user} | | 1.0 | {servername=s1} + s2 | foreign_data_user | foo | | | 1.1 | {host=a,dbname=b} + s3 | foreign_data_user | foo | | oracle | | {tnsname=orcl,port=1521} + s4 | foreign_data_user | foo | | oracle | | {host=a,dbname=b} + s5 | foreign_data_user | foo | | | 15.0 | + s6 | foreign_data_user | foo | {foreign_data_user=U/foreign_data_user,regress_test_role2=U*/foreign_data_user} | | 16.0 | {host=a,dbname=b} + s7 | foreign_data_user | foo | | oracle | 17.0 | {host=a,dbname=b} + s8 | foreign_data_user | postgresql | | | | {host=localhost,dbname=s8db} + st1 | regress_test_role | foo | | | | + st2 | regress_test_role | foo | | | | +(11 rows) + +SET ROLE regress_test_role; +ALTER SERVER s1 VERSION '1.1'; -- ERROR +ERROR: must be owner of foreign server s1 +ALTER SERVER s1 OWNER TO regress_test_role; -- ERROR +ERROR: must be owner of foreign server s1 +RESET ROLE; +ALTER SERVER s1 OWNER TO regress_test_role; +GRANT regress_test_role2 TO regress_test_role; +SET ROLE regress_test_role; +ALTER SERVER s1 VERSION '1.1'; +ALTER SERVER s1 OWNER TO regress_test_role2; -- ERROR +ERROR: permission denied for foreign-data wrapper foo +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 regress_test_role; +ALTER SERVER s1 OWNER TO regress_test_indirect; -- ERROR +ERROR: must be member of role "regress_test_indirect" +RESET ROLE; +GRANT regress_test_indirect TO regress_test_role; +SET ROLE regress_test_role; +ALTER SERVER s1 OWNER TO regress_test_indirect; +RESET ROLE; +GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_indirect; +SET ROLE regress_test_role; +ALTER SERVER s1 OWNER TO regress_test_indirect; +RESET ROLE; +DROP ROLE regress_test_indirect; -- ERROR +ERROR: role "regress_test_indirect" cannot be dropped because some objects depend on it +DETAIL: owner of server s1 +access to foreign-data wrapper foo +\des+ + List of foreign servers + Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options +------+-----------------------+----------------------+---------------------------------------------------------------------------------+--------+---------+--------------------------------- + S6 | foreign_data_user | foo | | | | {mixed_case_names=true} + s1 | regress_test_indirect | foo | {foreign_data_user=U/foreign_data_user,regress_test_role=U/foreign_data_user} | | 1.1 | {servername=s1} + s2 | foreign_data_user | foo | | | 1.1 | {host=a,dbname=b} + s3 | foreign_data_user | foo | | oracle | | {tnsname=orcl,port=1521} + s4 | foreign_data_user | foo | | oracle | | {host=a,dbname=b} + s5 | foreign_data_user | foo | | | 15.0 | + s6 | foreign_data_user | foo | {foreign_data_user=U/foreign_data_user,regress_test_role2=U*/foreign_data_user} | | 16.0 | {host=a,dbname=b} + s7 | foreign_data_user | foo | | oracle | 17.0 | {host=a,dbname=b} + s8 | foreign_data_user | postgresql | | | | {dbname=db1,connect_timeout=30} + st1 | regress_test_role | foo | | | | + st2 | regress_test_role | foo | | | | +(11 rows) + +-- DROP SERVER +DROP SERVER nonexistent; -- ERROR +ERROR: server "nonexistent" does not exist +DROP SERVER IF EXISTS nonexistent; +NOTICE: server "nonexistent" does not exist, skipping +\des + List of foreign servers + Name | Owner | Foreign-data wrapper +------+-----------------------+---------------------- + S6 | foreign_data_user | foo + s1 | regress_test_indirect | foo + s2 | foreign_data_user | foo + s3 | foreign_data_user | foo + s4 | foreign_data_user | foo + s5 | foreign_data_user | foo + s6 | foreign_data_user | foo + s7 | foreign_data_user | foo + s8 | foreign_data_user | postgresql + st1 | regress_test_role | foo + st2 | regress_test_role | foo +(11 rows) + +SET ROLE regress_test_role; +DROP SERVER s2; -- ERROR +ERROR: must be owner of foreign server s2 +DROP SERVER s1; +RESET ROLE; +\des + List of foreign servers + Name | Owner | Foreign-data wrapper +------+-------------------+---------------------- + S6 | foreign_data_user | foo + s2 | foreign_data_user | foo + s3 | foreign_data_user | foo + s4 | foreign_data_user | foo + s5 | foreign_data_user | foo + s6 | foreign_data_user | foo + s7 | foreign_data_user | foo + s8 | foreign_data_user | postgresql + st1 | regress_test_role | foo + st2 | regress_test_role | foo +(10 rows) + +ALTER SERVER s2 OWNER TO regress_test_role; +SET ROLE regress_test_role; +DROP SERVER s2; +RESET ROLE; +\des + List of foreign servers + Name | Owner | Foreign-data wrapper +------+-------------------+---------------------- + S6 | foreign_data_user | foo + s3 | foreign_data_user | foo + s4 | foreign_data_user | foo + s5 | foreign_data_user | foo + s6 | foreign_data_user | foo + s7 | foreign_data_user | foo + s8 | foreign_data_user | postgresql + st1 | regress_test_role | foo + st2 | regress_test_role | foo +(9 rows) + +CREATE USER MAPPING FOR current_user SERVER s3; +\deu + List of user mappings + Server | Username +--------+------------------- + s3 | foreign_data_user +(1 row) + +DROP SERVER s3; -- ERROR +ERROR: cannot drop server s3 because other objects depend on it +DETAIL: user mapping for foreign_data_user 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 foreign_data_user +\des + List of foreign servers + Name | Owner | Foreign-data wrapper +------+-------------------+---------------------- + S6 | foreign_data_user | foo + s4 | foreign_data_user | foo + s5 | foreign_data_user | foo + s6 | foreign_data_user | foo + s7 | foreign_data_user | foo + s8 | foreign_data_user | postgresql + st1 | regress_test_role | foo + st2 | regress_test_role | foo +(8 rows) + +\deu +List of user mappings + 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: 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 "foreign_data_user" 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 regress_test_role; +ALTER SERVER s6 OWNER TO regress_test_indirect; +SET ROLE regress_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; +CREATE USER MAPPING FOR current_user SERVER "S6" OPTIONS (username 'test_mixed_case'); +ALTER SERVER st1 OWNER TO regress_test_indirect; +SET ROLE regress_test_role; +CREATE USER MAPPING FOR current_user SERVER st1 OPTIONS (username 'bob', password 'boo'); +CREATE USER MAPPING FOR public SERVER st1; +RESET ROLE; +\deu + List of user mappings + Server | Username +--------+------------------- + S6 | foreign_data_user + s4 | foreign_data_user + s4 | public + s5 | regress_test_role + s6 | regress_test_role + s8 | foreign_data_user + st1 | public + st1 | regress_test_role +(8 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: 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 regress_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 st1 OPTIONS (ADD modified '1'); +RESET ROLE; +\deu+ + List of user mappings + Server | Username | Options +--------+-------------------+----------------------------- + S6 | foreign_data_user | {username=test_mixed_case} + s4 | foreign_data_user | + s4 | public | {"mapping=is public"} + s5 | regress_test_role | {modified=1} + s6 | regress_test_role | {username=test} + s8 | foreign_data_user | {password=public} + st1 | public | {modified=1} + st1 | regress_test_role | {username=bob,password=boo} +(8 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: server "ss4" does not exist +DROP USER MAPPING FOR public SERVER s7; -- ERROR +ERROR: user mapping "public" does not exist for the server +DROP USER MAPPING IF EXISTS FOR bob SERVER s4; +NOTICE: role "bob" does not exist, skipping +DROP USER MAPPING IF EXISTS FOR user SERVER ss4; +NOTICE: server 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 regress_test_role; +DROP USER MAPPING FOR public SERVER s8; -- ERROR +ERROR: must be owner of foreign server s8 +RESET ROLE; +DROP SERVER s7; +\deu + List of user mappings + Server | Username +--------+------------------- + S6 | foreign_data_user + s4 | foreign_data_user + s4 | public + s5 | regress_test_role + s6 | regress_test_role + s8 | foreign_data_user + s8 | public + st1 | public + st1 | regress_test_role +(9 rows) + +-- Information schema +SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2; + foreign_data_wrapper_catalog | foreign_data_wrapper_name | authorization_identifier | library_name | foreign_data_wrapper_language +------------------------------+---------------------------+--------------------------+----------------+------------------------------- + regression | dummy | foreign_data_user | dummy_fdw | c + regression | foo | foreign_data_user | dummy_fdw | c + regression | postgresql | foreign_data_user | postgresql_fdw | c +(3 rows) + +SELECT * FROM information_schema.foreign_data_wrapper_options ORDER BY 1, 2, 3; + 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 ORDER BY 1, 2; + foreign_server_catalog | foreign_server_name | foreign_data_wrapper_catalog | foreign_data_wrapper_name | foreign_server_type | foreign_server_version | authorization_identifier +------------------------+---------------------+------------------------------+---------------------------+---------------------+------------------------+-------------------------- + regression | S6 | regression | foo | | | foreign_data_user + regression | s4 | regression | foo | oracle | | foreign_data_user + regression | s5 | regression | foo | | 15.0 | regress_test_role + regression | s6 | regression | foo | | 16.0 | regress_test_indirect + regression | s8 | regression | postgresql | | | foreign_data_user + regression | st1 | regression | foo | | | regress_test_indirect + regression | st2 | regression | foo | | | regress_test_role +(7 rows) + +SELECT * FROM information_schema.foreign_server_options ORDER BY 1, 2, 3; + foreign_server_catalog | foreign_server_name | option_name | option_value +------------------------+---------------------+------------------+-------------- + regression | S6 | mixed_case_names | true + regression | s4 | dbname | b + regression | s4 | host | a + regression | s6 | dbname | b + regression | s6 | host | a + regression | s8 | connect_timeout | 30 + regression | s8 | dbname | db1 +(7 rows) + +SELECT * FROM information_schema.user_mappings ORDER BY 1, 2, 3; + authorization_identifier | foreign_server_catalog | foreign_server_name +--------------------------+------------------------+--------------------- + PUBLIC | regression | s4 + PUBLIC | regression | s8 + PUBLIC | regression | st1 + foreign_data_user | regression | S6 + foreign_data_user | regression | s4 + foreign_data_user | regression | s8 + regress_test_role | regression | s5 + regress_test_role | regression | s6 + regress_test_role | regression | st1 +(9 rows) + +SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4; + authorization_identifier | foreign_server_catalog | foreign_server_name | option_name | option_value +--------------------------+------------------------+---------------------+-------------+----------------- + PUBLIC | regression | s4 | mapping | is public + PUBLIC | regression | st1 | modified | 1 + foreign_data_user | regression | S6 | username | test_mixed_case + foreign_data_user | regression | s8 | password | public + regress_test_role | regression | s5 | modified | 1 + regress_test_role | regression | s6 | username | test + regress_test_role | regression | st1 | password | boo + regress_test_role | regression | st1 | username | bob +(8 rows) + +SELECT * FROM information_schema.usage_privileges WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5; + grantor | grantee | object_catalog | object_schema | object_name | object_type | privilege_type | is_grantable +-------------------+-----------------------+----------------+---------------+-------------+----------------------+----------------+-------------- + foreign_data_user | foreign_data_user | regression | | foo | FOREIGN DATA WRAPPER | USAGE | NO + foreign_data_user | foreign_data_user | regression | | s6 | FOREIGN SERVER | USAGE | NO + foreign_data_user | regress_test_indirect | regression | | foo | FOREIGN DATA WRAPPER | USAGE | NO + foreign_data_user | regress_test_role2 | regression | | s6 | FOREIGN SERVER | USAGE | YES +(4 rows) + +SELECT * FROM information_schema.role_usage_grants WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5; + grantor | grantee | object_catalog | object_schema | object_name | object_type | privilege_type | is_grantable +-------------------+-----------------------+----------------+---------------+-------------+----------------------+----------------+-------------- + foreign_data_user | foreign_data_user | regression | | foo | FOREIGN DATA WRAPPER | USAGE | NO + foreign_data_user | foreign_data_user | regression | | s6 | FOREIGN SERVER | USAGE | NO + foreign_data_user | regress_test_indirect | regression | | foo | FOREIGN DATA WRAPPER | USAGE | NO + foreign_data_user | regress_test_role2 | regression | | s6 | FOREIGN SERVER | USAGE | YES +(4 rows) + +SET ROLE regress_test_role; +SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4; + authorization_identifier | foreign_server_catalog | foreign_server_name | option_name | option_value +--------------------------+------------------------+---------------------+-------------+-------------- + PUBLIC | regression | st1 | modified | 1 + regress_test_role | regression | s5 | modified | 1 + regress_test_role | regression | s6 | username | test + regress_test_role | regression | st1 | password | boo + regress_test_role | regression | st1 | username | bob +(5 rows) + +SELECT * FROM information_schema.usage_privileges WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5; + grantor | grantee | object_catalog | object_schema | object_name | object_type | privilege_type | is_grantable +-------------------+-----------------------+----------------+---------------+-------------+----------------------+----------------+-------------- + foreign_data_user | regress_test_indirect | regression | | foo | FOREIGN DATA WRAPPER | USAGE | NO + foreign_data_user | regress_test_role2 | regression | | s6 | FOREIGN SERVER | USAGE | YES +(2 rows) + +SELECT * FROM information_schema.role_usage_grants WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5; + grantor | grantee | object_catalog | object_schema | object_name | object_type | privilege_type | is_grantable +-------------------+-----------------------+----------------+---------------+-------------+----------------------+----------------+-------------- + foreign_data_user | regress_test_indirect | regression | | foo | FOREIGN DATA WRAPPER | USAGE | NO + foreign_data_user | regress_test_role2 | regression | | s6 | FOREIGN SERVER | USAGE | YES +(2 rows) + +DROP USER MAPPING FOR current_user SERVER st1; +RESET ROLE; +-- has_foreign_data_wrapper_privilege +SELECT has_foreign_data_wrapper_privilege('regress_test_role', + (SELECT oid FROM pg_foreign_data_wrapper WHERE fdwname='foo'), 'USAGE'); + has_foreign_data_wrapper_privilege +------------------------------------ + t +(1 row) + +SELECT has_foreign_data_wrapper_privilege('regress_test_role', 'foo', 'USAGE'); + has_foreign_data_wrapper_privilege +------------------------------------ + t +(1 row) + +SELECT has_foreign_data_wrapper_privilege( + (SELECT oid FROM pg_roles WHERE rolname='regress_test_role'), + (SELECT oid FROM pg_foreign_data_wrapper WHERE fdwname='foo'), 'USAGE'); + has_foreign_data_wrapper_privilege +------------------------------------ + t +(1 row) + +SELECT has_foreign_data_wrapper_privilege( + (SELECT oid FROM pg_foreign_data_wrapper WHERE fdwname='foo'), 'USAGE'); + has_foreign_data_wrapper_privilege +------------------------------------ + t +(1 row) + +SELECT has_foreign_data_wrapper_privilege( + (SELECT oid FROM pg_roles WHERE rolname='regress_test_role'), 'foo', 'USAGE'); + has_foreign_data_wrapper_privilege +------------------------------------ + t +(1 row) + +SELECT has_foreign_data_wrapper_privilege('foo', 'USAGE'); + has_foreign_data_wrapper_privilege +------------------------------------ + t +(1 row) + +GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; +SELECT has_foreign_data_wrapper_privilege('regress_test_role', 'foo', 'USAGE'); + has_foreign_data_wrapper_privilege +------------------------------------ + t +(1 row) + +-- has_server_privilege +SELECT has_server_privilege('regress_test_role', + (SELECT oid FROM pg_foreign_server WHERE srvname='s8'), 'USAGE'); + has_server_privilege +---------------------- + f +(1 row) + +SELECT has_server_privilege('regress_test_role', 's8', 'USAGE'); + has_server_privilege +---------------------- + f +(1 row) + +SELECT has_server_privilege( + (SELECT oid FROM pg_roles WHERE rolname='regress_test_role'), + (SELECT oid FROM pg_foreign_server WHERE srvname='s8'), 'USAGE'); + has_server_privilege +---------------------- + f +(1 row) + +SELECT has_server_privilege( + (SELECT oid FROM pg_foreign_server WHERE srvname='s8'), 'USAGE'); + has_server_privilege +---------------------- + t +(1 row) + +SELECT has_server_privilege( + (SELECT oid FROM pg_roles WHERE rolname='regress_test_role'), 's8', 'USAGE'); + has_server_privilege +---------------------- + f +(1 row) + +SELECT has_server_privilege('s8', 'USAGE'); + has_server_privilege +---------------------- + t +(1 row) + +GRANT USAGE ON FOREIGN SERVER s8 TO regress_test_role; +SELECT has_server_privilege('regress_test_role', 's8', 'USAGE'); + has_server_privilege +---------------------- + t +(1 row) + +REVOKE USAGE ON FOREIGN SERVER s8 FROM regress_test_role; +GRANT USAGE ON FOREIGN SERVER s4 TO regress_test_role; +DROP USER MAPPING FOR public SERVER s4; +ALTER SERVER s6 OPTIONS (DROP host, DROP dbname); +ALTER USER MAPPING FOR regress_test_role SERVER s6 OPTIONS (DROP username); +ALTER FOREIGN DATA WRAPPER foo LIBRARY 'plpgsql'; +WARNING: changing the foreign-data wrapper library can cause the options for dependent objects to become invalid +ALTER FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw'; +ERROR: could not access file "default_fdw": No such file or directory +-- Privileges +SET ROLE unprivileged_role; +CREATE FOREIGN DATA WRAPPER foobar LIBRARY 'dummy_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. +ALTER FOREIGN DATA WRAPPER foo OWNER TO unprivileged_role; -- ERROR +ERROR: permission denied to change owner of foreign-data wrapper "foo" +HINT: Must be superuser to change owner of 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 regress_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 +ALTER SERVER s4 OWNER TO unprivileged_role; -- 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 regress_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 regress_test_role SERVER s6 OPTIONS (gotcha 'true'); -- ERROR +ERROR: must be owner of foreign server s6 +DROP USER MAPPING FOR regress_test_role SERVER s6; -- ERROR +ERROR: must be owner of foreign server s6 +RESET ROLE; +GRANT USAGE ON FOREIGN DATA WRAPPER postgresql 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 'dummy_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 postgresql TO regress_test_role; -- WARNING +WARNING: no privileges were granted for "postgresql" +GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; +CREATE SERVER s9 FOREIGN DATA WRAPPER postgresql; +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 regress_test_role; -- ERROR +ERROR: permission denied for foreign server s6 +GRANT USAGE ON FOREIGN SERVER s9 TO regress_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 regress_test_role SERVER s6 OPTIONS (gotcha 'true'); -- ERROR +ERROR: must be owner of foreign server s6 +DROP USER MAPPING FOR regress_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 regress_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 regress_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 regress_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 regress_test_role; -- ERROR +ERROR: role "regress_test_role" cannot be dropped because some objects depend on it +DETAIL: access to server s4 +access to foreign-data wrapper foo +owner of user mapping for regress_test_role +owner of user mapping for regress_test_role +owner of server s5 +owner of server st2 +DROP SERVER s5 CASCADE; +NOTICE: drop cascades to user mapping for regress_test_role +DROP SERVER st1 CASCADE; +NOTICE: drop cascades to user mapping for public +DROP SERVER st2; +DROP USER MAPPING FOR regress_test_role SERVER s6; +DROP FOREIGN DATA WRAPPER foo CASCADE; +NOTICE: drop cascades to 6 other objects +DETAIL: drop cascades to server s4 +drop cascades to user mapping for foreign_data_user +drop cascades to server s6 +drop cascades to server S6 +drop cascades to user mapping for foreign_data_user +drop cascades to server s9 +DROP SERVER s8 CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to user mapping for foreign_data_user +drop cascades to user mapping for public +DROP ROLE regress_test_indirect; +DROP ROLE regress_test_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 postgresql +REVOKE ALL ON FOREIGN DATA WRAPPER postgresql FROM unprivileged_role; +DROP ROLE unprivileged_role; +DROP ROLE regress_test_role2; +DROP FOREIGN DATA WRAPPER postgresql CASCADE; +DROP FOREIGN DATA WRAPPER dummy CASCADE; +\c +DROP ROLE foreign_data_user; +-- At this point we should have no wrappers, no servers, and no mappings. +SELECT fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper; + fdwname | fdwlibrary | fdwoptions +---------+------------+------------ +(0 rows) + +SELECT srvname, srvoptions FROM pg_foreign_server; + srvname | srvoptions +---------+------------ +(0 rows) + +SELECT * FROM pg_user_mapping; + umuser | umserver | umoptions +--------+----------+----------- +(0 rows) + diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 80b002d5388440df5d0b0ad5edf000b474c9519f..977b17cd51b4014060df6b7d3758d2110120d205 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1313,6 +1313,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem 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, CASE WHEN (pg_has_role(s.srvowner, 'USAGE'::text) OR has_server_privilege(s.oid, 'USAGE'::text)) THEN u.umoptions ELSE NULL::text[] END AS umoptions 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,7 +1329,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color)))); street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); toyemp | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp; -(50 rows) +(51 rows) SELECT tablename, rulename, definition FROM pg_rules ORDER BY tablename, rulename; diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index 6946b2ec163c75b1e5396b444eb8e15a7c6583fd..e677d4580fdcafa85a05165db435bcc3e7a5e92a 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -99,6 +99,8 @@ SELECT relname, relhasindex 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,6 +124,7 @@ SELECT relname, relhasindex pg_ts_parser | t pg_ts_template | t pg_type | t + pg_user_mapping | t point_tbl | f polygon_tbl | t ramp | f @@ -149,7 +152,7 @@ SELECT relname, relhasindex timetz_tbl | f tinterval_tbl | f varchar_tbl | f -(138 rows) +(141 rows) -- -- another sanity check: every system catalog that has OIDs should have diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 5257b463018541c40779bee253a34f04bd87b87d..27b79aef256a5afd457222364523c2dd7e3081b0 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -1,5 +1,5 @@ # ---------- -# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.50 2008/10/31 09:17:16 heikki Exp $ +# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.51 2008/12/19 16:25:19 petere Exp $ # # By convention, we put no more than twenty tests in any one parallel group; # this limits the number of connections needed to run the tests. @@ -77,7 +77,7 @@ test: misc # ---------- # Another group of parallel tests # ---------- -test: select_views portals_p2 rules foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts +test: select_views portals_p2 rules foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 70dd424849ca5d63d50f031b8d507c3defa57456..da823d8423810f7a433bbaa7ef9c6a165061f3d3 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -1,4 +1,4 @@ -# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.47 2008/10/31 09:17:16 heikki Exp $ +# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.48 2008/12/19 16:25:19 petere Exp $ # This should probably be in an order similar to parallel_schedule. test: boolean test: char @@ -108,6 +108,7 @@ test: prepare test: without_oid test: conversion test: tsdicts +test: foreign_data test: truncate test: alter_table test: sequence diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql new file mode 100644 index 0000000000000000000000000000000000000000..03de174a6bc6cb806ab401696218a5b50b9536c0 --- /dev/null +++ b/src/test/regress/sql/foreign_data.sql @@ -0,0 +1,396 @@ +-- +-- Test foreign-data wrapper and server management. +-- + +-- Clean up in case a prior regression run failed + +-- Suppress NOTICE messages when roles don't exist +SET client_min_messages TO 'error'; + +DROP ROLE IF EXISTS foreign_data_user, regress_test_role, regress_test_role2, regress_test_role_super, regress_test_indirect, unpriviled_role; + +RESET client_min_messages; + +CREATE ROLE foreign_data_user LOGIN SUPERUSER; +SET SESSION AUTHORIZATION 'foreign_data_user'; + +CREATE ROLE regress_test_role; +CREATE ROLE regress_test_role2; +CREATE ROLE regress_test_role_super SUPERUSER; +CREATE ROLE regress_test_indirect; +CREATE ROLE unprivileged_role; + +CREATE FOREIGN DATA WRAPPER dummy LIBRARY 'dummy_fdw' LANGUAGE C; +CREATE FOREIGN DATA WRAPPER postgresql LIBRARY 'postgresql_fdw' LANGUAGE C; + +-- At this point we should have 2 built-in wrappers and no servers. +SELECT 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; +DROP FOREIGN DATA WRAPPER foo; +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C; +\dew + +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C; -- duplicate +CREATE FOREIGN DATA WRAPPER "Foo" LIBRARY 'dummy_fdw' LANGUAGE C; +DROP FOREIGN DATA WRAPPER "Foo"; +DROP FOREIGN DATA WRAPPER foo; +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C OPTIONS (testing '1'); +\dew+ + +DROP FOREIGN DATA WRAPPER foo; +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C OPTIONS (testing '1', testing '2'); -- ERROR +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C OPTIONS (testing '1', another '2'); +\dew+ + +DROP FOREIGN DATA WRAPPER foo; +SET ROLE regress_test_role; +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C; -- ERROR +RESET ROLE; +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'postgresql_fdw' LANGUAGE C; +\dew+ + +-- ALTER FOREIGN DATA WRAPPER +ALTER FOREIGN DATA WRAPPER foo LIBRARY ''; -- ERROR +ALTER FOREIGN DATA WRAPPER foo LIBRARY 'plpgsql'; +ALTER FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw'; +\dew+ + +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 +ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x); +\dew+ + +ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4'); +\dew+ + +ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2'); +ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4'); -- ERROR +\dew+ + +SET ROLE regress_test_role; +ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5'); -- ERROR +SET ROLE regress_test_role_super; +ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5'); +\dew+ + +ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role; -- ERROR +ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role_super; +ALTER ROLE regress_test_role_super NOSUPERUSER; +SET ROLE regress_test_role_super; +ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD e '6'); -- ERROR +RESET ROLE; +\dew+ + +-- DROP FOREIGN DATA WRAPPER +DROP FOREIGN DATA WRAPPER nonexistent; -- ERROR +DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent; +\dew+ + +DROP ROLE regress_test_role_super; -- ERROR +SET ROLE regress_test_role_super; +DROP FOREIGN DATA WRAPPER foo; -- ERROR +RESET ROLE; +ALTER ROLE regress_test_role_super SUPERUSER; +DROP FOREIGN DATA WRAPPER foo; +DROP ROLE regress_test_role_super; +\dew+ + +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C; +CREATE SERVER s1 FOREIGN DATA WRAPPER foo; +CREATE USER MAPPING FOR current_user SERVER s1; +\dew+ +\des+ +\deu+ +DROP FOREIGN DATA WRAPPER foo; -- ERROR +SET ROLE regress_test_role; +DROP FOREIGN DATA WRAPPER foo CASCADE; -- ERROR +RESET ROLE; +DROP FOREIGN DATA WRAPPER foo CASCADE; +\dew+ +\des+ +\deu+ + +-- exercise CREATE SERVER +CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR +CREATE FOREIGN DATA WRAPPER foo LIBRARY 'dummy_fdw' LANGUAGE C OPTIONS (test_wrapper 'true'); +CREATE SERVER s1 FOREIGN DATA WRAPPER foo; +CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR +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 "S6" FOREIGN DATA WRAPPER foo OPTIONS (mixed_case_names 'true'); +CREATE SERVER s7 TYPE 'oracle' VERSION '17.0' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b'); +CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (foo '1'); -- ERROR +CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (host 'localhost', dbname 's8db'); +\des+ +SET ROLE regress_test_role; +CREATE SERVER st1 FOREIGN DATA WRAPPER foo; -- ERROR: no usage on FDW +RESET ROLE; +GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; +SET ROLE regress_test_role; +CREATE SERVER st1 FOREIGN DATA WRAPPER foo; +RESET ROLE; +\des+ + +REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role; +GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_indirect; +SET ROLE regress_test_role; +CREATE SERVER st2 FOREIGN DATA WRAPPER foo; -- ERROR +RESET ROLE; +GRANT regress_test_indirect TO regress_test_role; +SET ROLE regress_test_role; +CREATE SERVER st2 FOREIGN DATA WRAPPER foo; +\des+ +RESET ROLE; +REVOKE regress_test_indirect FROM regress_test_role; + +-- ALTER SERVER +ALTER SERVER s0; -- ERROR +ALTER SERVER s0 OPTIONS (a '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 regress_test_role; +GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION; +\des+ +SET ROLE regress_test_role; +ALTER SERVER s1 VERSION '1.1'; -- ERROR +ALTER SERVER s1 OWNER TO regress_test_role; -- ERROR +RESET ROLE; +ALTER SERVER s1 OWNER TO regress_test_role; +GRANT regress_test_role2 TO regress_test_role; +SET ROLE regress_test_role; +ALTER SERVER s1 VERSION '1.1'; +ALTER SERVER s1 OWNER TO regress_test_role2; -- ERROR +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 regress_test_role; +ALTER SERVER s1 OWNER TO regress_test_indirect; -- ERROR +RESET ROLE; +GRANT regress_test_indirect TO regress_test_role; +SET ROLE regress_test_role; +ALTER SERVER s1 OWNER TO regress_test_indirect; +RESET ROLE; +GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_indirect; +SET ROLE regress_test_role; +ALTER SERVER s1 OWNER TO regress_test_indirect; +RESET ROLE; +DROP ROLE regress_test_indirect; -- ERROR +\des+ + +-- DROP SERVER +DROP SERVER nonexistent; -- ERROR +DROP SERVER IF EXISTS nonexistent; +\des +SET ROLE regress_test_role; +DROP SERVER s2; -- ERROR +DROP SERVER s1; +RESET ROLE; +\des +ALTER SERVER s2 OWNER TO regress_test_role; +SET ROLE regress_test_role; +DROP SERVER s2; +RESET ROLE; +\des +CREATE USER MAPPING FOR current_user SERVER s3; +\deu +DROP SERVER s3; -- ERROR +DROP SERVER s3 CASCADE; +\des +\deu + +-- 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 regress_test_role; +ALTER SERVER s6 OWNER TO regress_test_indirect; +SET ROLE regress_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; +CREATE USER MAPPING FOR current_user SERVER "S6" OPTIONS (username 'test_mixed_case'); + +ALTER SERVER st1 OWNER TO regress_test_indirect; +SET ROLE regress_test_role; +CREATE USER MAPPING FOR current_user SERVER st1 OPTIONS (username 'bob', password 'boo'); +CREATE USER MAPPING FOR public SERVER st1; +RESET ROLE; +\deu + +-- 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 regress_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 st1 OPTIONS (ADD modified '1'); +RESET ROLE; +\deu+ + +-- DROP USER MAPPING +DROP USER MAPPING FOR bob SERVER s4; -- ERROR +DROP USER MAPPING FOR user SERVER ss4; +DROP USER MAPPING FOR public SERVER s7; -- ERROR +DROP USER MAPPING IF EXISTS FOR bob SERVER s4; +DROP USER MAPPING IF EXISTS FOR user SERVER ss4; +DROP USER MAPPING IF EXISTS FOR public SERVER s7; +CREATE USER MAPPING FOR public SERVER s8; +SET ROLE regress_test_role; +DROP USER MAPPING FOR public SERVER s8; -- ERROR +RESET ROLE; +DROP SERVER s7; +\deu + +-- Information schema + +SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2; +SELECT * FROM information_schema.foreign_data_wrapper_options ORDER BY 1, 2, 3; +SELECT * FROM information_schema.foreign_servers ORDER BY 1, 2; +SELECT * FROM information_schema.foreign_server_options ORDER BY 1, 2, 3; +SELECT * FROM information_schema.user_mappings ORDER BY 1, 2, 3; +SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4; +SELECT * FROM information_schema.usage_privileges WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5; +SELECT * FROM information_schema.role_usage_grants WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5; +SET ROLE regress_test_role; +SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4; +SELECT * FROM information_schema.usage_privileges WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5; +SELECT * FROM information_schema.role_usage_grants WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5; +DROP USER MAPPING FOR current_user SERVER st1; +RESET ROLE; + + +-- has_foreign_data_wrapper_privilege +SELECT has_foreign_data_wrapper_privilege('regress_test_role', + (SELECT oid FROM pg_foreign_data_wrapper WHERE fdwname='foo'), 'USAGE'); +SELECT has_foreign_data_wrapper_privilege('regress_test_role', 'foo', 'USAGE'); +SELECT has_foreign_data_wrapper_privilege( + (SELECT oid FROM pg_roles WHERE rolname='regress_test_role'), + (SELECT oid FROM pg_foreign_data_wrapper WHERE fdwname='foo'), 'USAGE'); +SELECT has_foreign_data_wrapper_privilege( + (SELECT oid FROM pg_foreign_data_wrapper WHERE fdwname='foo'), 'USAGE'); +SELECT has_foreign_data_wrapper_privilege( + (SELECT oid FROM pg_roles WHERE rolname='regress_test_role'), 'foo', 'USAGE'); +SELECT has_foreign_data_wrapper_privilege('foo', 'USAGE'); +GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; +SELECT has_foreign_data_wrapper_privilege('regress_test_role', 'foo', 'USAGE'); + +-- has_server_privilege +SELECT has_server_privilege('regress_test_role', + (SELECT oid FROM pg_foreign_server WHERE srvname='s8'), 'USAGE'); +SELECT has_server_privilege('regress_test_role', 's8', 'USAGE'); +SELECT has_server_privilege( + (SELECT oid FROM pg_roles WHERE rolname='regress_test_role'), + (SELECT oid FROM pg_foreign_server WHERE srvname='s8'), 'USAGE'); +SELECT has_server_privilege( + (SELECT oid FROM pg_foreign_server WHERE srvname='s8'), 'USAGE'); +SELECT has_server_privilege( + (SELECT oid FROM pg_roles WHERE rolname='regress_test_role'), 's8', 'USAGE'); +SELECT has_server_privilege('s8', 'USAGE'); +GRANT USAGE ON FOREIGN SERVER s8 TO regress_test_role; +SELECT has_server_privilege('regress_test_role', 's8', 'USAGE'); +REVOKE USAGE ON FOREIGN SERVER s8 FROM regress_test_role; + +GRANT USAGE ON FOREIGN SERVER s4 TO regress_test_role; +DROP USER MAPPING FOR public SERVER s4; +ALTER SERVER s6 OPTIONS (DROP host, DROP dbname); +ALTER USER MAPPING FOR regress_test_role SERVER s6 OPTIONS (DROP username); +ALTER FOREIGN DATA WRAPPER foo LIBRARY 'plpgsql'; +ALTER FOREIGN DATA WRAPPER foo LIBRARY 'default_fdw'; + +-- Privileges +SET ROLE unprivileged_role; +CREATE FOREIGN DATA WRAPPER foobar LIBRARY 'dummy_fdw' LANGUAGE C; -- ERROR +ALTER FOREIGN DATA WRAPPER foo OPTIONS (gotcha 'true'); -- ERROR +ALTER FOREIGN DATA WRAPPER foo OWNER TO unprivileged_role; -- ERROR +DROP FOREIGN DATA WRAPPER foo; -- ERROR +GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; -- ERROR +CREATE SERVER s9 FOREIGN DATA WRAPPER foo; -- ERROR +ALTER SERVER s4 VERSION '0.5'; -- ERROR +ALTER SERVER s4 OWNER TO unprivileged_role; -- ERROR +DROP SERVER s4; -- ERROR +GRANT USAGE ON FOREIGN SERVER s4 TO regress_test_role; -- ERROR +CREATE USER MAPPING FOR public SERVER s4; -- ERROR +ALTER USER MAPPING FOR regress_test_role SERVER s6 OPTIONS (gotcha 'true'); -- ERROR +DROP USER MAPPING FOR regress_test_role SERVER s6; -- ERROR +RESET ROLE; + +GRANT USAGE ON FOREIGN DATA WRAPPER postgresql 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 'dummy_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 postgresql TO regress_test_role; -- WARNING +GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; +CREATE SERVER s9 FOREIGN DATA WRAPPER postgresql; +ALTER SERVER s6 VERSION '0.5'; -- ERROR +DROP SERVER s6; -- ERROR +GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role; -- ERROR +GRANT USAGE ON FOREIGN SERVER s9 TO regress_test_role; +CREATE USER MAPPING FOR public SERVER s6; -- ERROR +CREATE USER MAPPING FOR public SERVER s9; +ALTER USER MAPPING FOR regress_test_role SERVER s6 OPTIONS (gotcha 'true'); -- ERROR +DROP USER MAPPING FOR regress_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 regress_test_role; -- ERROR +CREATE SERVER s10 FOREIGN DATA WRAPPER foo; -- ERROR +ALTER SERVER s9 VERSION '1.1'; +GRANT USAGE ON FOREIGN SERVER s9 TO regress_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 regress_test_role; -- WARNING +CREATE USER MAPPING FOR current_user SERVER s9; -- ERROR +DROP SERVER s9 CASCADE; -- ERROR +RESET ROLE; + +-- Cleanup +DROP ROLE regress_test_role; -- ERROR +DROP SERVER s5 CASCADE; +DROP SERVER st1 CASCADE; +DROP SERVER st2; +DROP USER MAPPING FOR regress_test_role SERVER s6; +DROP FOREIGN DATA WRAPPER foo CASCADE; +DROP SERVER s8 CASCADE; +DROP ROLE regress_test_indirect; +DROP ROLE regress_test_role; +DROP ROLE unprivileged_role; -- ERROR +REVOKE ALL ON FOREIGN DATA WRAPPER postgresql FROM unprivileged_role; +DROP ROLE unprivileged_role; +DROP ROLE regress_test_role2; +DROP FOREIGN DATA WRAPPER postgresql CASCADE; +DROP FOREIGN DATA WRAPPER dummy CASCADE; +\c +DROP ROLE foreign_data_user; + +-- At this point we should have no wrappers, no servers, and no mappings. +SELECT fdwname, fdwlibrary, fdwoptions FROM pg_foreign_data_wrapper; +SELECT srvname, srvoptions FROM pg_foreign_server; +SELECT * FROM pg_user_mapping;