Simplified connection cache in contrib/postgresql_fdw to fit the FDW.
authorShigeru Hanada <[email protected]>
Wed, 13 Oct 2010 11:12:14 +0000 (20:12 +0900)
committerShigeru Hanada <[email protected]>
Wed, 13 Oct 2010 11:12:14 +0000 (20:12 +0900)
contrib/postgresql_fdw/expected/postgresql_fdw.out
contrib/postgresql_fdw/fsconnection.c
contrib/postgresql_fdw/fsconnection.h
contrib/postgresql_fdw/postgresql_fdw.c
contrib/postgresql_fdw/postgresql_fdw.sql.in
contrib/postgresql_fdw/sql/postgresql_fdw.sql
contrib/postgresql_fdw/uninstall_postgresql_fdw.sql
doc/src/sgml/postgresql-fdw.sgml

index 0acedda5b4b592c6f3a5d00178c122dd41013bc5..f3114f7711edf67f5ed19d60aef604c4e13450ef 100644 (file)
@@ -5,533 +5,102 @@ SET DATESTYLE = 'Postgres, MDY';
 -- =============================================================================
 -- connect database for regression test
 \c contrib_regression
--- install handler function
-\i postgresql_fdw.sql
-/* contrib/postgresql/postgresql.sql.in */
--- Adjust this setting to control where the objects get created.
-set search_path = public;
-CREATE OR REPLACE FUNCTION postgresql_fdw_handler ()
-RETURNS fdw_handler
-AS '$libdir/postgresql_fdw','postgresql_fdw_handler'
-LANGUAGE C STRICT;
--- create test user
-CREATE ROLE contrib_regression_role_1;
--- create remote database 1
-CREATE DATABASE contrib_regression_f1;
-\c contrib_regression_f1
-CREATE TABLE person (
-   id integer not null,
-   name text not null,
-   birthday date,
-   update_ts timestamp with time zone,
-   constraint pk_person primary key (id)
-);
-NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pk_person" for table "person"
-BEGIN;
-INSERT INTO person VALUES(1, 'foo', '01-31-2000', '01-31-2000 00:00:00+00:00');
-INSERT INTO person VALUES(2, 'bar', '01-31-1900', '01-31-1900 00:00:00+00:00');
-INSERT INTO person VALUES(3, 'buz', NULL, NULL);
-COMMIT;
--- create remote database 2
-CREATE DATABASE contrib_regression_f2;
-\c contrib_regression_f2
-CREATE TABLE person (
-   id integer not null,
-   name text not null,
-   birthday date,
-   update_ts timestamp with time zone,
-   constraint pk_person primary key (id)
-);
-NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pk_person" for table "person"
-BEGIN;
-INSERT INTO person VALUES(1, 'foo', '01-31-2000', '01-31-2000 00:00:00+00:00');
-INSERT INTO person VALUES(2, 'bar', '01-31-1900', '01-31-1900 00:00:00+00:00');
-INSERT INTO person VALUES(3, 'buz', NULL, NULL);
-COMMIT;
--- connect database for regression test
-\c contrib_regression
--- create FOREIGN DATA WRAPPER for PostgresSQL
-CREATE FOREIGN DATA WRAPPER contrib_regression_wrapper
+-- install postgresql_fdw module
+SET client_min_messages = warning;
+\set ECHO none
+-- define fdw-related objects
+CREATE FOREIGN DATA WRAPPER postgresql_fdw
    HANDLER postgresql_fdw_handler
    VALIDATOR postgresql_fdw_validator;
--- create FOREIGN SERVER for remote database 1
-CREATE SERVER contrib_regression_srv_1
-   FOREIGN DATA WRAPPER contrib_regression_wrapper
-   OPTIONS (host 'localhost', dbname 'contrib_regression_f1',
-            connection_cost '100.0', transfer_cost '0.01');
-CREATE USER MAPPING FOR PUBLIC SERVER contrib_regression_srv_1;
--- create FOREIGN SERVER for remote database 2
-CREATE SERVER contrib_regression_srv_2
-   FOREIGN DATA WRAPPER contrib_regression_wrapper
-   OPTIONS (host 'localhost', dbname 'contrib_regression_f2');
-CREATE USER MAPPING FOR PUBLIC SERVER contrib_regression_srv_2;
--- Check ALTER FOREIGN TABLE OWNER TO before create various test tables
-CREATE FOREIGN TABLE ft1 (c1 integer) SERVER contrib_regression_srv_2;
-ALTER FOREIGN TABLE ft1 OWNER TO contrib_regression_role_1;
-\d
-                     List of relations
- Schema | Name |     Type      |           Owner           
---------+------+---------------+---------------------------
- public | ft1  | foreign table | contrib_regression_role_1
-(1 row)
-
-DROP FOREIGN TABLE ft1;
--- create entity of local table with same contents
-CREATE TABLE person_l (
-   id integer not null,
-   name text not null,
-   birthday date,
-   update_ts timestamp with time zone,
-   constraint pk_person_l primary key (id)
+CREATE SERVER loopback1 FOREIGN DATA WRAPPER postgresql_fdw
+   OPTIONS (dbname 'contrib_regression');
+CREATE SERVER loopback2 FOREIGN DATA WRAPPER postgresql_fdw
+   OPTIONS (dbname 'contrib_regression');
+CREATE USER MAPPING FOR PUBLIC SERVER loopback1;
+CREATE USER MAPPING FOR PUBLIC SERVER loopback2 OPTIONS (user 'invalid');
+CREATE TABLE t1(
+   c1 integer,
+   c2 text,
+   c3 date
 );
-NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pk_person_l" for table "person_l"
-BEGIN;
-INSERT INTO person_l VALUES(1, 'foo', '01-31-2000', '01-31-2000 00:00:00+00:00');
-INSERT INTO person_l VALUES(2, 'bar', '01-31-1900', '01-31-1900 00:00:00+00:00');
-INSERT INTO person_l VALUES(4, 'bar', NULL, NULL);
-COMMIT;
--- create foreign table which references table 'person'
-CREATE FOREIGN TABLE person (
-   id integer not null,
-   name text not null,
-   birthday date,
-   update_ts timestamp with time zone
-) SERVER contrib_regression_srv_1;
-CREATE FOREIGN TABLE person2 (
-   id integer not null,
-   name text not null,
-   birthday date,
-   update_ts timestamp with time zone
-) SERVER contrib_regression_srv_2 OPTIONS (nspname 'public', relname 'person');
-\det+
-                        List of foreign tables
-  Table  |          Server          |             Options             
----------+--------------------------+---------------------------------
- person  | contrib_regression_srv_1 | 
- person2 | contrib_regression_srv_2 | {nspname=public,relname=person}
-(2 rows)
-
--- =============================================================================
--- Misc statement section
--- =============================================================================
-ALTER FOREIGN TABLE person INHERIT person_l;
--- row lock via view is not allowed too.
-CREATE OR REPLACE VIEW v_person AS SELECT * FROM person;
-SELECT * FROM v_person FOR UPDATE NOWAIT;
-ERROR:  SELECT FOR UPDATE/SHARE is not allowed with foreign tables
-DROP VIEW v_person;
--- row lock via CTE is not allowed but no error occurs.
-WITH t AS (SELECT * FROM person) SELECT * FROM t ORDER BY id FOR UPDATE NOWAIT; -- not error
- id | name |  birthday  |          update_ts           
-----+------+------------+------------------------------
-  1 | foo  | 01-31-2000 | Sun Jan 30 16:00:00 2000 PST
-  2 | bar  | 01-31-1900 | Tue Jan 30 16:00:00 1900 PST
-  3 | buz  |            | 
+COPY t1 FROM stdin;
+CREATE FOREIGN TABLE ft1 (
+   c1 integer,
+   c2 text,
+   c3 date
+) SERVER loopback1 OPTIONS (relname 't1');
+CREATE FOREIGN TABLE ft2 (
+   c1 integer,
+   c2 text,
+   c3 date
+) SERVER loopback2 OPTIONS (relname 'invalid');
+-- simple query and connection caching
+SELECT * FROM ft1 ORDER BY c1;
+ c1 | c2  |     c3     
+----+-----+------------
+  1 | foo | 01-01-1970
+  2 | bar | 01-02-1970
+  3 | buz | 01-03-1970
 (3 rows)
 
--- row lock in CTE is not allowed and an error occurs.
-WITH t AS (SELECT * FROM person FOR UPDATE) SELECT * FROM t ORDER BY id; -- error
-ERROR:  SELECT FOR UPDATE/SHARE is not allowed with foreign tables
--- cleanup
-ALTER FOREIGN TABLE person NO INHERIT person_l;
--- =============================================================================
--- Connection cache test section
--- XXX: some of these tests should been moved to regression test of core.
--- =============================================================================
--- clear connection cache
-DISCARD ALL;
--- access foreign table (no result needed)
-SELECT * FROM person WHERE 1 = 0;
- id | name | birthday | update_ts 
-----+------+----------+-----------
-(0 rows)
+SELECT * FROM ft2 ORDER BY c1; -- ERROR
+ERROR:  could not connect to server
+DETAIL:  FATAL:  role "invalid" does not exist
 
--- one connection must be cached
-SELECT srvname, (user = usename) usename, fdwname
-   FROM pg_foreign_connections ORDER BY srvname;
-         srvname          | usename |          fdwname           
---------------------------+---------+----------------------------
- contrib_regression_srv_1 | t       | contrib_regression_wrapper
+ALTER USER MAPPING FOR PUBLIC SERVER loopback2 OPTIONS (DROP user);
+SELECT * FROM postgresql_fdw_connections ORDER BY conname;
+  conname  |  srvname  | usename  
+-----------+-----------+----------
+ loopback1 | loopback1 | public
 (1 row)
 
--- access remote database 2 (no result needed)
-SELECT id, name, birthday FROM person2 WHERE 1 = 0;
- id | name | birthday 
-----+------+----------
-(0 rows)
-
--- two connection must be cached
-SELECT srvname, (user = usename) usename, fdwname
-   FROM pg_foreign_connections ORDER BY srvname;
-         srvname          | usename |          fdwname           
---------------------------+---------+----------------------------
- contrib_regression_srv_1 | t       | contrib_regression_wrapper
- contrib_regression_srv_2 | t       | contrib_regression_wrapper
-(2 rows)
-
--- disconnect from all servers
-DISCARD ALL;
--- connection cache must be empty
-SELECT srvname, (user = usename) usename, fdwname
-   FROM pg_foreign_connections ORDER BY srvname;
- srvname | usename | fdwname 
----------+---------+---------
-(0 rows)
-
--- access remote database 1 and 2 (no result needed)
-SELECT id, name, birthday FROM person WHERE 1 = 0;
- id | name | birthday 
-----+------+----------
-(0 rows)
-
-SELECT id, name, birthday FROM person2 WHERE 1 = 0;
- id | name | birthday 
-----+------+----------
-(0 rows)
-
--- two connection must be cached
-SELECT srvname, (user = usename) usename, fdwname
-   FROM pg_foreign_connections ORDER BY srvname;
-         srvname          | usename |          fdwname           
---------------------------+---------+----------------------------
- contrib_regression_srv_1 | t       | contrib_regression_wrapper
- contrib_regression_srv_2 | t       | contrib_regression_wrapper
+SELECT * FROM ft2 ORDER BY c1; -- ERROR
+ERROR:  could not execute foreign query
+DETAIL:  ERROR:  relation "public.invalid" does not exist
+LINE 1: SELECT c1, c2, c3 FROM public.invalid ft2
+                               ^
+
+HINT:  SELECT c1, c2, c3 FROM public.invalid ft2
+SELECT * FROM postgresql_fdw_connections ORDER BY conname;
+  conname  |  srvname  | usename  
+-----------+-----------+----------
+ loopback1 | loopback1 | public
+ loopback2 | loopback2 | public
 (2 rows)
 
--- change authorization identifier
-SET SESSION AUTHORIZATION contrib_regression_role_1;
--- connection cache must be empty
-SELECT srvname, (user = usename) usename, fdwname
-   FROM pg_foreign_connections ORDER BY srvname;
- srvname | usename | fdwname 
----------+---------+---------
-(0 rows)
-
--- cleanup
-RESET SESSION AUTHORIZATION;
-DROP FOREIGN TABLE person2;
-\det+
-           List of foreign tables
- Table  |          Server          | Options 
---------+--------------------------+---------
- person | contrib_regression_srv_1 | 
-(1 row)
-
--- =============================================================================
--- Query test section
--- =============================================================================
--- all tuples with ORDER BY clause
-SELECT id, name, birthday,
-   xmin, xmax, cmin, cmax, ctid, (tableoid = 'person'::regclass) tableoid
-   FROM person ORDER BY id;
- id | name |  birthday  | xmin | xmax | cmin | cmax |      ctid      | tableoid 
-----+------+------------+------+------+------+------+----------------+----------
-  1 | foo  | 01-31-2000 |    0 |    0 |    0 |    0 | (4294967295,0) | t
-  2 | bar  | 01-31-1900 |    0 |    0 |    0 |    0 | (4294967295,0) | t
-  3 | buz  |            |    0 |    0 |    0 |    0 | (4294967295,0) | t
+ALTER FOREIGN TABLE ft2 OPTIONS (SET relname 't1');
+SELECT * FROM ft2 ORDER BY c1;
+ c1 | c2  |     c3     
+----+-----+------------
+  1 | foo | 01-01-1970
+  2 | bar | 01-02-1970
+  3 | buz | 01-03-1970
 (3 rows)
 
--- operator and function call in SELECT clause
-SELECT id + 10 id, upper(name) upper_name, birthday FROM person ORDER BY id;
- id | upper_name |  birthday  
-----+------------+------------
- 11 | FOO        | 01-31-2000
- 12 | BAR        | 01-31-1900
- 13 | BUZ        | 
-(3 rows)
-
--- various join/subquery situations
-SELECT f.id, f.name, f.birthday, p.id, p.name, p.birthday FROM person f JOIN person_l p ON (f.id = p.id) ORDER BY f.id, p.id;
- id | name |  birthday  | id | name |  birthday  
-----+------+------------+----+------+------------
-  1 | foo  | 01-31-2000 |  1 | foo  | 01-31-2000
-  2 | bar  | 01-31-1900 |  2 | bar  | 01-31-1900
+SELECT * FROM postgresql_fdw_connections ORDER BY conname;
+  conname  |  srvname  | usename  
+-----------+-----------+----------
+ loopback1 | loopback1 | public
+ loopback2 | loopback2 | public
 (2 rows)
 
-SELECT f.id, f.name, f.birthday, p.id, p.name, p.birthday FROM person f LEFT JOIN person_l p ON (f.id = p.id) ORDER BY f.id, p.id;
- id | name |  birthday  | id | name |  birthday  
-----+------+------------+----+------+------------
-  1 | foo  | 01-31-2000 |  1 | foo  | 01-31-2000
-  2 | bar  | 01-31-1900 |  2 | bar  | 01-31-1900
-  3 | buz  |            |    |      | 
+-- query using join
+SELECT * FROM ft1 JOIN ft2 ON (ft1.c1 = ft2.c1) ORDER BY ft1.c1;
+ c1 | c2  |     c3     | c1 | c2  |     c3     
+----+-----+------------+----+-----+------------
+  1 | foo | 01-01-1970 |  1 | foo | 01-01-1970
+  2 | bar | 01-02-1970 |  2 | bar | 01-02-1970
+  3 | buz | 01-03-1970 |  3 | buz | 01-03-1970
 (3 rows)
 
-SELECT f.id, f.name, f.birthday, p.id, p.name, p.birthday FROM person f RIGHT JOIN person_l p ON (f.id = p.id) ORDER BY f.id, p.id;
- id | name |  birthday  | id | name |  birthday  
-----+------+------------+----+------+------------
-  1 | foo  | 01-31-2000 |  1 | foo  | 01-31-2000
-  2 | bar  | 01-31-1900 |  2 | bar  | 01-31-1900
-    |      |            |  4 | bar  | 
+SELECT * FROM ft1 t1 JOIN ft1 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1;
+ c1 | c2  |     c3     | c1 | c2  |     c3     
+----+-----+------------+----+-----+------------
+  1 | foo | 01-01-1970 |  1 | foo | 01-01-1970
+  2 | bar | 01-02-1970 |  2 | bar | 01-02-1970
+  3 | buz | 01-03-1970 |  3 | buz | 01-03-1970
 (3 rows)
 
-SELECT f.id, f.name, f.birthday, p.id, p.name, p.birthday FROM person f FULL OUTER JOIN person_l p ON (f.id = p.id) ORDER BY f.id, p.id;
- id | name |  birthday  | id | name |  birthday  
-----+------+------------+----+------+------------
-  1 | foo  | 01-31-2000 |  1 | foo  | 01-31-2000
-  2 | bar  | 01-31-1900 |  2 | bar  | 01-31-1900
-  3 | buz  |            |    |      | 
-    |      |            |  4 | bar  | 
-(4 rows)
-
-SELECT id, name, birthday FROM person f WHERE f.id = (SELECT min(p.id) FROM person_l p) ORDER BY f.id;
- id | name |  birthday  
-----+------+------------
-  1 | foo  | 01-31-2000
-(1 row)
-
-SELECT id, name, birthday FROM person f WHERE (f.id, f.name) IN (SELECT p.id, p.name FROM person_l p) ORDER BY f.id;
- id | name |  birthday  
-----+------+------------
-  1 | foo  | 01-31-2000
-  2 | bar  | 01-31-1900
-(2 rows)
-
--- union/intersect/except
-SELECT id, name, birthday FROM person f UNION SELECT id, name, birthday FROM person_l p ORDER BY id;
- id | name |  birthday  
-----+------+------------
-  1 | foo  | 01-31-2000
-  2 | bar  | 01-31-1900
-  3 | buz  | 
-  4 | bar  | 
-(4 rows)
-
-SELECT name FROM person f INTERSECT SELECT name FROM person_l p ORDER BY name;
- name 
-------
- bar
- foo
-(2 rows)
-
-SELECT name FROM person f EXCEPT SELECT name FROM person_l p ORDER BY name;
- name 
-------
- buz
-(1 row)
-
--- WHERE clause evaluation in the foreign server
-SELECT id, name, birthday FROM person WHERE id = 1;
- id | name |  birthday  
-----+------+------------
-  1 | foo  | 01-31-2000
-(1 row)
-
-SELECT id, name, birthday FROM person WHERE birthday IS NULL ORDER BY id;
- id | name | birthday 
-----+------+----------
-  3 | buz  | 
-(1 row)
-
-SELECT id, name, birthday FROM person WHERE id IN (1, -1, 5) ORDER BY id;
- id | name |  birthday  
-----+------+------------
-  1 | foo  | 01-31-2000
-(1 row)
-
-SELECT id, name, birthday FROM person WHERE id IS DISTINCT from 1 ORDER BY id;
- id | name |  birthday  
-----+------+------------
-  2 | bar  | 01-31-1900
-  3 | buz  | 
-(2 rows)
-
-SELECT id, name, birthday FROM person WHERE ARRAY[0,id,2] = ARRAY[0,1,2] ORDER BY id;
- id | name |  birthday  
-----+------+------------
-  1 | foo  | 01-31-2000
-(1 row)
-
--- WHERE clause evaluation in local server
-SELECT id, name, birthday FROM person WHERE update_ts < '01-31-1999'::date ORDER BY id;
- id | name |  birthday  
-----+------+------------
-  2 | bar  | 01-31-1900
-(1 row)
-
--- limit/offset
-SELECT id, name, birthday FROM person f ORDER BY id LIMIT 1 OFFSET 1;
- id | name |  birthday  
-----+------+------------
-  2 | bar  | 01-31-1900
-(1 row)
-
--- PREPARE/EXECUTE
-PREPARE ST1(integer, integer) AS SELECT $1 param, id, name, birthday FROM person f WHERE f.id = $2;
-EXECUTE ST1(0, 1);
- param | id | name |  birthday  
--------+----+------+------------
-     0 |  1 | foo  | 01-31-2000
-(1 row)
-
-EXECUTE ST1(1, 2);
- param | id | name |  birthday  
--------+----+------+------------
-     1 |  2 | bar  | 01-31-1900
-(1 row)
-
-DEALLOCATE ST1;
--- =============================================================================
--- DDL test section
--- =============================================================================
--- NOT NULL  and CHECK constraints in column constraint syntax
-CREATE FOREIGN TABLE ft2 (c1 integer NOT NULL, c2 text CHECK (length(c2) > 0)) SERVER contrib_regression_srv_1;
-\d+ ft2
-              Foreign table "public.ft2"
- Column |  Type   | Modifiers | Storage  | Description 
---------+---------+-----------+----------+-------------
- c1     | integer | not null  | plain    | 
- c2     | text    |           | extended | 
-Check constraints:
-    "ft2_c2_check" CHECK (length(c2) > 0)
-Server: contrib_regression_srv_1
-Has OIDs: no
-
--- CHECK constraints in table constraint syntax
-CREATE FOREIGN TABLE ft3 (c1 integer, c2 text, CONSTRAINT ft3_c2_check CHECK (length(c2) > 0)) SERVER contrib_regression_srv_1;
-\d+ ft3
-              Foreign table "public.ft3"
- Column |  Type   | Modifiers | Storage  | Description 
---------+---------+-----------+----------+-------------
- c1     | integer |           | plain    | 
- c2     | text    |           | extended | 
-Check constraints:
-    "ft3_c2_check" CHECK (length(c2) > 0)
-Server: contrib_regression_srv_1
-Has OIDs: no
-
--- PRIMARY KEY in column constraint syntax - error
-CREATE FOREIGN TABLE ft4 (c1 integer PRIMARY KEY) SERVER contrib_regression_srv_1; -- error
-ERROR:  syntax error at or near "PRIMARY"
-LINE 1: CREATE FOREIGN TABLE ft4 (c1 integer PRIMARY KEY) SERVER con...
-                                             ^
--- UNIQUE in column constraint syntax - error
-CREATE FOREIGN TABLE ft4 (c1 integer UNIQUE) SERVER contrib_regression_srv_1; -- error
-ERROR:  syntax error at or near "UNIQUE"
-LINE 1: CREATE FOREIGN TABLE ft4 (c1 integer UNIQUE) SERVER contrib_...
-                                             ^
--- FOREIGN KEY in column constraint syntax - error
-CREATE TABLE t1 (c1 integer PRIMARY KEY);
-NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1"
-CREATE FOREIGN TABLE ft4 (c1 integer REFERENCES t1 (c1)) SERVER contrib_regression_srv_1; -- error
-ERROR:  syntax error at or near "REFERENCES"
-LINE 1: CREATE FOREIGN TABLE ft4 (c1 integer REFERENCES t1 (c1)) SER...
-                                             ^
--- PRIMARY KEY in table constraint syntax - error
-CREATE FOREIGN TABLE ft4 (c1 integer, CONSTRAINT ft4_pkey PRIMARY KEY (c1)) SERVER contrib_regression_srv_1; -- error
-ERROR:  syntax error at or near "PRIMARY"
-LINE 1: ...OREIGN TABLE ft4 (c1 integer, CONSTRAINT ft4_pkey PRIMARY KE...
-                                                             ^
--- UNIQUE in table constraint syntax - error
-CREATE FOREIGN TABLE ft4 (c1 integer, CONSTRAINT ft4_c1_unique UNIQUE (c1)) SERVER contrib_regression_srv_1; -- error
-ERROR:  syntax error at or near "UNIQUE"
-LINE 1: ...N TABLE ft4 (c1 integer, CONSTRAINT ft4_c1_unique UNIQUE (c1...
-                                                             ^
--- FOREIGN KEY in table constraint syntax - error
-CREATE FOREIGN TABLE ft4 (c1 integer, CONSTRAINT ft4_c1_fkey FOREIGN KEY (c1) REFERENCES t1 (c1)) SERVER contrib_regression_srv_1; -- error
-ERROR:  syntax error at or near "FOREIGN"
-LINE 1: ...IGN TABLE ft4 (c1 integer, CONSTRAINT ft4_c1_fkey FOREIGN KE...
-                                                             ^
--- cleanup
-DROP FOREIGN TABLE ft2;
-DROP FOREIGN TABLE ft3;
-DROP TABLE t1;
--- delete all data to use as inherited (parent) table
-DELETE FROM person_l;
--- change table definition
-\c contrib_regression_f1
-ALTER TABLE person ADD COLUMN dummy text;
-UPDATE person SET dummy = 'dummy value';
-\c contrib_regression
-ALTER FOREIGN TABLE person INHERIT person_l;
-ALTER TABLE person_l ADD COLUMN dummy text; -- added to person too
-CREATE RULE rl_person_insert AS ON INSERT TO person DO INSTEAD INSERT INTO person_l VALUES (NEW.*);
-\d+ person;
-                       Foreign table "public.person"
-  Column   |           Type           | Modifiers | Storage  | Description 
------------+--------------------------+-----------+----------+-------------
- id        | integer                  | not null  | plain    | 
- name      | text                     | not null  | extended | 
- birthday  | date                     |           | plain    | 
- update_ts | timestamp with time zone |           | plain    | 
- dummy     | text                     |           | extended | 
-Rules:
-    rl_person_insert AS
-    ON INSERT TO person DO INSTEAD  INSERT INTO person_l (id, name, birthday, update_ts, dummy) 
-  VALUES (new.id, new.name, new.birthday, new.update_ts, new.dummy)
-Server: contrib_regression_srv_1
-Inherits: person_l
-Has OIDs: no
-
-\d+ person_l;
-                          Table "public.person_l"
-  Column   |           Type           | Modifiers | Storage  | Description 
------------+--------------------------+-----------+----------+-------------
- id        | integer                  | not null  | plain    | 
- name      | text                     | not null  | extended | 
- birthday  | date                     |           | plain    | 
- update_ts | timestamp with time zone |           | plain    | 
- dummy     | text                     |           | extended | 
-Indexes:
-    "pk_person_l" PRIMARY KEY, btree (id)
-Child tables: person
-Has OIDs: no
-
--- content of person must be showed
-INSERT INTO person VALUES (-1, 'FOO', '2100-01-31', null, 'DUMMY');
-SELECT * FROM person_l ORDER BY id;
- id | name |  birthday  |          update_ts           |    dummy    
-----+------+------------+------------------------------+-------------
- -1 | FOO  | 01-31-2100 |                              | DUMMY
-  1 | foo  | 01-31-2000 | Sun Jan 30 16:00:00 2000 PST | dummy value
-  2 | bar  | 01-31-1900 | Tue Jan 30 16:00:00 1900 PST | dummy value
-  3 | buz  |            |                              | dummy value
-(4 rows)
-
--- restore table definition
-\c contrib_regression_f1
-ALTER TABLE person DROP COLUMN dummy;
-\c contrib_regression
-DROP RULE rl_person_insert ON person;
-ALTER TABLE person_l DROP COLUMN dummy;
-ALTER FOREIGN TABLE person NO INHERIT person_l;
-\d+ person;
-                       Foreign table "public.person"
-  Column   |           Type           | Modifiers | Storage  | Description 
------------+--------------------------+-----------+----------+-------------
- id        | integer                  | not null  | plain    | 
- name      | text                     | not null  | extended | 
- birthday  | date                     |           | plain    | 
- update_ts | timestamp with time zone |           | plain    | 
-Server: contrib_regression_srv_1
-Has OIDs: no
-
--- no child table, result must be its own content
-SELECT * FROM person_l ORDER BY id;
- id | name |  birthday  | update_ts 
-----+------+------------+-----------
- -1 | FOO  | 01-31-2100 | 
-(1 row)
-
--- query for foreign table which has no column
-ALTER FOREIGN TABLE person DROP COLUMN id;
-ALTER FOREIGN TABLE person DROP COLUMN name;
-ALTER FOREIGN TABLE person DROP COLUMN birthday;
-ALTER FOREIGN TABLE person DROP COLUMN update_ts;
-SELECT * FROM person;
---
-(3 rows)
-
--- =============================================================================
--- Cleanup section
--- =============================================================================
-DISCARD ALL;
-DROP FOREIGN DATA WRAPPER contrib_regression_wrapper CASCADE;
-NOTICE:  drop cascades to 5 other objects
-DETAIL:  drop cascades to server contrib_regression_srv_1
-drop cascades to user mapping for public
-drop cascades to foreign table person
-drop cascades to server contrib_regression_srv_2
-drop cascades to user mapping for public
-DROP DATABASE contrib_regression_f1;
-DROP DATABASE contrib_regression_f2;
-DROP ROLE contrib_regression_role_1;
+-- clean up
+DROP FOREIGN DATA WRAPPER postgresql_fdw CASCADE;
+DROP TABLE t1 CASCADE;
index fda112ecd82155c2436303c4f7e174d637cefde2..62e50fdb2dbe4b63e69692d41185061349345965 100644 (file)
 
 #include "fsconnection.h"
 
-extern Datum pg_foreign_connections(PG_FUNCTION_ARGS);
+extern Datum postgresql_fdw_connections(PG_FUNCTION_ARGS);
 
 
 /*
  * Connection cache entry managed with hash table.
  */
-typedef struct FSConnCacheEntry
+typedef struct ConnCacheEntry
 {
    /* hash key must be first */
    char            name[NAMEDATALEN];  /* connection name; used as hash key */
-   Oid             serverid;   /* oid of foreign server */
-   Oid             userid;     /* oid of user (0 means PUBLIC) */
+   Oid             serverid;           /* oid of foreign server */
+   Oid             userid;             /* oid of user (0 means PUBLIC) */
    FdwRoutine     *routine;
-   FSConnection   *conn;       /* foreign server connection */
-} FSConnCacheEntry;
+   PGconn         *conn;               /* foreign server connection */
+} ConnCacheEntry;
 
-static FSConnCacheEntry *make_connection(const char *conname, bool *found);
+static ConnCacheEntry *make_connection(const char *conname, bool *found);
 static void cleanup_fsconnection(int code, Datum arg);
 
 /*
@@ -47,101 +47,40 @@ static void cleanup_fsconnection(int code, Datum arg);
  */
 static HTAB *FSConnectionHash;
 
-/*
- * Get established connection to the server with the user.
- *
- * Connections are cached until the backend shutdowns.
- */
-FSConnection *
-ConnectToForeignServer(FdwRoutine *routine,
-                      ForeignServer *server,
-                      UserMapping *user,
-                      const char *conname)
+
+void
+RegisterConnection(PGconn *conn,
+                  ForeignServer *server,
+                  UserMapping *user,
+                  const char *conname)
 {
+   ForeignDataWrapper *wrapper;
+   FdwRoutine         *routine;
    bool                found;
-   FSConnCacheEntry   *entry;
+   ConnCacheEntry     *entry;
 
-   AssertArg(routine != NULL);
+   AssertArg(conn != NULL);
    AssertArg(server != NULL);
    AssertArg(user != NULL);
    AssertArg(conname != NULL);
    AssertArg(strlen(conname) < NAMEDATALEN);
 
-   entry = make_connection(conname, &found);
-   if (found)
-   {
-       if (entry->routine != routine ||
-           entry->serverid != server->serverid)
-       {
-           ereport(ERROR,
-                   (errcode(ERRCODE_DUPLICATE_OBJECT),
-                    errmsg("connection \"%s\" is already used for another server",
-                           conname)));
-       }
-       return entry->conn;
-   }
-
-   /*
-    * If a new entry, connect to the foreign server with ConnectServer.
-    * The FSConnection will be left as NULL when the FDW does not support
-    * the function.
-    *
-    * OuterUserId is used to determine user mapping, because when a foreign
-    * table was accessed in a function which was defined with SECURITY DEFINER
-    * option, CurrentUserId is changed to the owner of the function and it
-    * could differ from the caller of the function.
-    *
-    * Note that even if the user mapping was for PUBLIC, user->userid is set
-    * to the OuterUserId in GetUserMapping().
-    */
-   entry->serverid = server->serverid;
-   entry->userid = user->userid;
-   entry->routine = routine;
-   entry->conn = NULL;
-
-   if (routine->ConnectServer != NULL)
-   {
-       PG_TRY();
-       {
-           entry->conn = entry->routine->ConnectServer(server, user);
-       }
-       PG_CATCH();
-       {
-           /* remove uninitialized entry when the attempt to connect failed. */
-           hash_search(FSConnectionHash, conname, HASH_REMOVE, &found);
-           PG_RE_THROW();
-       }
-       PG_END_TRY();
-   }
-   return entry->conn;
-}
-
-void
-RegisterFSConnection(FSConnection *conn,
-                    FdwRoutine *routine,
-                    const char *conname)
-{
-   bool                found;
-   FSConnCacheEntry   *entry;
-
-   AssertArg(routine != NULL);
-   AssertArg(conname != NULL);
-   AssertArg(strlen(conname) < NAMEDATALEN);
+   wrapper = GetForeignDataWrapper(server->fdwid);
+   routine = GetFdwRoutine(wrapper->fdwhandler);
 
    entry = make_connection(conname, &found);
    if (found)
        ereport(ERROR,
                (errcode(ERRCODE_DUPLICATE_OBJECT),
-                errmsg("connection \"%s\" is already used for another server",
-                       conname)));
+                errmsg("connection \"%s\" already exists", conname)));
 
-   entry->serverid = 0;
-   entry->userid = GetOuterUserId();
+   entry->serverid = server->serverid;
+   entry->userid = user->userid;
    entry->routine = routine;
    entry->conn = conn;
 }
 
-static FSConnCacheEntry *
+static ConnCacheEntry *
 make_connection(const char *conname, bool *found)
 {
    /* initialize connection cache */
@@ -149,12 +88,10 @@ make_connection(const char *conname, bool *found)
    {
        HASHCTL     ctl;
 
-       ereport(DEBUG1, (errmsg("initializing foreign server connection cache")));
-
        /* hash key is oid of pg_user_mapping */
        MemSet(&ctl, 0, sizeof(ctl));
        ctl.keysize = NAMEDATALEN;
-       ctl.entrysize = sizeof(FSConnCacheEntry);
+       ctl.entrysize = sizeof(ConnCacheEntry);
        /* allocate FSConnectionHash in the cache context */
        ctl.hcxt = CacheMemoryContext;
        FSConnectionHash = hash_create("Foreign Connections", 32,
@@ -162,15 +99,13 @@ make_connection(const char *conname, bool *found)
 
        /* register cleanup function */
        on_proc_exit(cleanup_fsconnection, 0);
-
-       ereport(DEBUG1, (errmsg("foreign server connection cache created")));
    }
 
    /*
     * Search from cache first. When an apporopriate connection was found in
     * the connection cache, use it.
     */
-   return (FSConnCacheEntry *) hash_search(FSConnectionHash,
+   return (ConnCacheEntry *) hash_search(FSConnectionHash,
                                            conname, HASH_ENTER, found);
 }
 
@@ -179,10 +114,10 @@ make_connection(const char *conname, bool *found)
  * Caller must check that the connection is related to correct foreign-data
  * wrapper.
  */
-FSConnection *
-GetFSConnectionByName(const char *conname, FdwRoutine **routine)
+PGconn *
+GetConnectionByName(const char *conname)
 {
-   FSConnCacheEntry   *entry;
+   ConnCacheEntry   *entry;
 
    AssertArg(conname != NULL);
    AssertArg(strlen(conname) < NAMEDATALEN);
@@ -190,72 +125,68 @@ GetFSConnectionByName(const char *conname, FdwRoutine **routine)
    if (FSConnectionHash == NULL)
        return NULL;
 
-   entry = (FSConnCacheEntry *) hash_search(FSConnectionHash,
+   entry = (ConnCacheEntry *) hash_search(FSConnectionHash,
                                             conname, HASH_FIND, NULL);
 
    if (entry)
-   {
-       if (routine)
-           *routine = entry->routine;
        return entry->conn;
-   }
    else
        return NULL;
 }
 
 /*
- * Remove the connection in the cache and disconnect from the foreign server.
+ * Unregister connection from connection cache.
  */
-bool
-DisconnectForeignServer(const char *conname)
+PGconn *
+UnregisterConnection(const char *conname)
 {
-   FSConnCacheEntry   *entry;
+   PGconn             *conn;
+   ConnCacheEntry     *entry;
 
+   Assert(FSConnectionHash != NULL);
    AssertArg(conname != NULL);
    AssertArg(strlen(conname) < NAMEDATALEN);
 
-   /* When the connection cache has not initialized yet, nothing to do. */
-   if (FSConnectionHash == NULL)
-       return false;
-
-   /* Remove from the connection cache. Do nothing if not found. */
-   entry = (FSConnCacheEntry *) hash_search(FSConnectionHash,
-                                            conname, HASH_REMOVE, NULL);
+   entry = (ConnCacheEntry *) hash_search(FSConnectionHash,
+                                            conname, HASH_FIND, NULL);
    if (entry == NULL)
-       return false;
-
-   elog(DEBUG1, "foreign server connection \"%s\" discarded", conname);
+       return NULL;
 
-   /* Disconnect from the foreign server with FreeFSConnection(). */
-   if (entry->routine->FreeFSConnection && entry->conn != NULL)
-       entry->routine->FreeFSConnection(entry->conn);
+   /* remove the connection from connection cache */
+   conn = entry->conn;
+   entry = (ConnCacheEntry *) hash_search(FSConnectionHash,
+                                            conname, HASH_REMOVE, NULL);
 
-   return true;
+   return conn;
 }
 
 /*
  * Discard all foreign server connections and free resources.
  */
 void
-DisconnectAllForeignServers(void)
+DisconnectAll(void)
 {
    HASH_SEQ_STATUS     seq;
-   FSConnCacheEntry   *entry;
+   ConnCacheEntry     *entry;
 
-elog(DEBUG1, "%s(%u)", __FUNCTION__, __LINE__);
    if (!FSConnectionHash)
        return;
 
    hash_seq_init(&seq, FSConnectionHash);
    while ((entry = hash_seq_search(&seq)) != NULL)
-       DisconnectForeignServer(entry->name);
+   {
+       entry->routine->FreeFSConnection((FSConnection *) entry->conn);
+   }
+
+   hash_destroy(FSConnectionHash);
+   FSConnectionHash = NULL;
 }
 
 /*
  * Retrieve foreign server connections.
  */
 Datum
-pg_foreign_connections(PG_FUNCTION_ARGS)
+postgresql_fdw_connections(PG_FUNCTION_ARGS)
 {
 #define PG_FOREIGN_SERVER_CONNECTIONS_COLS     3
    ReturnSetInfo      *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
@@ -290,7 +221,7 @@ pg_foreign_connections(PG_FUNCTION_ARGS)
    if (FSConnectionHash)
    {
        HASH_SEQ_STATUS     hash_seq;
-       FSConnCacheEntry   *entry;
+       ConnCacheEntry     *entry;
 
        MemoryContextSwitchTo(oldcontext);
 
@@ -326,5 +257,5 @@ pg_foreign_connections(PG_FUNCTION_ARGS)
 static void
 cleanup_fsconnection(int code, Datum arg)
 {
-   DisconnectAllForeignServers();
+   DisconnectAll();
 }
index e1c886de2cfee749ef5e8aeee168b52eef914f7e..de6606df113701a172d8d9b83e0ed6c4c1cfb581 100644 (file)
 #ifndef FSCONNECTION_H
 #define FSCONNECTION_H
 
-/* foreign server connections */
-extern FSConnection *ConnectToForeignServer(FdwRoutine *routine,
-           ForeignServer *server, UserMapping *user, const char *conname);
-extern void RegisterFSConnection(FSConnection *conn, FdwRoutine *routine,
-                                const char *conname);
-extern FSConnection *GetFSConnectionByName(const char *conname, FdwRoutine **routine);
-extern bool DisconnectForeignServer(const char *conname);
-extern void DisconnectAllForeignServers(void);
+#include "libpq-fe.h"
+
+/* managing connection cache */
+extern void RegisterConnection(PGconn *conn, ForeignServer *server, UserMapping *user, const char *conname);
+extern PGconn *GetConnectionByName(const char *conname);
+extern PGconn *UnregisterConnection(const char *conname);
+extern void DisconnectAll(void);
 
 #endif   /* FSCONNECTION_H */
index cf640ecee0bcb8c8f0f2afafb0a28d53298b9df1..73e988c720ff3a2a91fc38c469dc54618fb7463d 100644 (file)
@@ -77,7 +77,7 @@ typedef struct pgFdwReply
    Tuplestorestate    *tupstore;   /* result set */
 } pgFdwReply;
 
-FdwRoutine postgresql_fdw_routine =
+static FdwRoutine postgresql_fdw_routine =
 {
    pgConnectServer,
    pgFreeFSConnection,
@@ -113,6 +113,15 @@ pgConnectServer(ForeignServer *server, UserMapping *user)
    int             n;
    int             i, j;
 
+   /* First of all, lookup connection cache by name */
+   conn = GetConnectionByName(server->servername);
+   if (conn != NULL)
+   {
+       if (PQstatus(conn) == CONNECTION_OK)
+           return (FSConnection *) conn;
+       PQfinish(conn);
+   }
+
    /*
     * Construct connection params from generic options of ForeignServer and
     * UserMapping.  Generic options might not be a one of connection options.
@@ -153,6 +162,9 @@ pgConnectServer(ForeignServer *server, UserMapping *user)
    pfree(keywords);
    pfree(values);
 
+   /* register the connection into connection cache. */
+   RegisterConnection(conn, server, user, server->servername);
+
    return (FSConnection *) conn;
 }
 
@@ -547,13 +559,27 @@ pgIterate(ForeignScanState *scanstate)
                pfree((char *) values[i]);
            pfree(values);
        }
+
+       /*
+        * If the query has failed and the connection became invalid, we need to
+        * remove the connection from cache.  This clean-up might make next
+        * query on the same server successful.
+        */
        if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
        {
+           char *msg;
+
            PQclear(res);
+           msg = pstrdup(PQerrorMessage(conn));
+           if (PQstatus(conn) != CONNECTION_OK)
+           {
+               conn = UnregisterConnection(scanstate->server->servername);
+               PQfinish(conn);
+           }
+           
            ereport(ERROR, (
                    errmsg("could not execute foreign query"),
-                   errdetail("%s", PQerrorMessage(conn)),
-                   errhint("%s", reply->sql)));
+                   errdetail("%s", msg), errhint("%s", reply->sql)));
        }
 
        /* Note: use PG_TRY to ensure freeing PGresult. */
@@ -768,8 +794,8 @@ get_server_costs(Oid relid, double *connection_cost, double *transfer_cost)
        if (pg_strcasecmp(keywords[i], "connection_cost") == 0)
        {
            /* use connection cost if no connection is available. */
-           FSConnection   *conn;
-           conn = GetFSConnectionByName(server->servername, NULL);
+           PGconn   *conn;
+           conn = GetConnectionByName(server->servername);
            if (conn == NULL)
                *connection_cost = strtod(values[i], NULL);
        }
index d2ed838f6a661ab6e7ff3b0ed7a430c861385321..60f26a2817f8ab7bf6f4d8f56f8e4b7397de9e4b 100644 (file)
@@ -8,19 +8,19 @@ RETURNS fdw_handler
 AS 'MODULE_PATHNAME','postgresql_fdw_handler'
 LANGUAGE C STRICT;
 
-CREATE OR REPLACE FUNCTION pg_foreign_connections(conname OUT text, serverid OUT oid, userid OUT oid)
+CREATE OR REPLACE FUNCTION postgresql_fdw_connections(conname OUT text, serverid OUT oid, userid OUT oid)
     RETURNS SETOF record
-    AS 'MODULE_PATHNAME','pg_foreign_connections'
+    AS 'MODULE_PATHNAME','postgresql_fdw_connections'
     LANGUAGE C STRICT;
 
-CREATE OR REPLACE VIEW pg_foreign_connections AS
+CREATE OR REPLACE VIEW postgresql_fdw_connections AS
     SELECT c.conname as conname,
            s.srvname as srvname,
-           a.rolname as usename
-      FROM pg_foreign_connections() c
+           COALESCE(a.rolname, 'public') as usename
+      FROM postgresql_fdw_connections() c
            JOIN pg_foreign_server s ON (s.oid = c.serverid)
-           JOIN pg_authid a ON (a.oid = c.userid);
+           LEFT OUTER JOIN pg_authid a ON (a.oid = c.userid);
 
-GRANT EXECUTE ON FUNCTION pg_foreign_connections(conname OUT text, serverid OUT oid, userid OUT oid) TO public;
-GRANT SELECT ON pg_foreign_connections TO public;
+GRANT EXECUTE ON FUNCTION postgresql_fdw_connections(conname OUT text, serverid OUT oid, userid OUT oid) TO public;
+GRANT SELECT ON postgresql_fdw_connections TO public;
 
index a23a1c15b5a48097e92bbbeca576e3cfc96c4e97..d600c63bb43d59c1e851f3acbf10f44f85453026 100644 (file)
@@ -7,281 +7,64 @@ SET DATESTYLE = 'Postgres, MDY';
 -- connect database for regression test
 \c contrib_regression
 
--- install handler function
+-- install postgresql_fdw module
+SET client_min_messages = warning;
+\set ECHO none
 \i postgresql_fdw.sql
+\set ECHO all
 
--- create test user
-CREATE ROLE contrib_regression_role_1;
-
--- create remote database 1
-CREATE DATABASE contrib_regression_f1;
-\c contrib_regression_f1
-CREATE TABLE person (
-   id integer not null,
-   name text not null,
-   birthday date,
-   update_ts timestamp with time zone,
-   constraint pk_person primary key (id)
-);
-BEGIN;
-INSERT INTO person VALUES(1, 'foo', '01-31-2000', '01-31-2000 00:00:00+00:00');
-INSERT INTO person VALUES(2, 'bar', '01-31-1900', '01-31-1900 00:00:00+00:00');
-INSERT INTO person VALUES(3, 'buz', NULL, NULL);
-COMMIT;
-
--- create remote database 2
-CREATE DATABASE contrib_regression_f2;
-\c contrib_regression_f2
-CREATE TABLE person (
-   id integer not null,
-   name text not null,
-   birthday date,
-   update_ts timestamp with time zone,
-   constraint pk_person primary key (id)
-);
-BEGIN;
-INSERT INTO person VALUES(1, 'foo', '01-31-2000', '01-31-2000 00:00:00+00:00');
-INSERT INTO person VALUES(2, 'bar', '01-31-1900', '01-31-1900 00:00:00+00:00');
-INSERT INTO person VALUES(3, 'buz', NULL, NULL);
-COMMIT;
-
--- connect database for regression test
-\c contrib_regression
-
--- create FOREIGN DATA WRAPPER for PostgresSQL
-CREATE FOREIGN DATA WRAPPER contrib_regression_wrapper
+-- define fdw-related objects
+CREATE FOREIGN DATA WRAPPER postgresql_fdw
    HANDLER postgresql_fdw_handler
    VALIDATOR postgresql_fdw_validator;
 
--- create FOREIGN SERVER for remote database 1
-CREATE SERVER contrib_regression_srv_1
-   FOREIGN DATA WRAPPER contrib_regression_wrapper
-   OPTIONS (host 'localhost', dbname 'contrib_regression_f1',
-            connection_cost '100.0', transfer_cost '0.01');
-CREATE USER MAPPING FOR PUBLIC SERVER contrib_regression_srv_1;
-
--- create FOREIGN SERVER for remote database 2
-CREATE SERVER contrib_regression_srv_2
-   FOREIGN DATA WRAPPER contrib_regression_wrapper
-   OPTIONS (host 'localhost', dbname 'contrib_regression_f2');
-CREATE USER MAPPING FOR PUBLIC SERVER contrib_regression_srv_2;
+CREATE SERVER loopback1 FOREIGN DATA WRAPPER postgresql_fdw
+   OPTIONS (dbname 'contrib_regression');
+CREATE SERVER loopback2 FOREIGN DATA WRAPPER postgresql_fdw
+   OPTIONS (dbname 'contrib_regression');
 
--- Check ALTER FOREIGN TABLE OWNER TO before create various test tables
-CREATE FOREIGN TABLE ft1 (c1 integer) SERVER contrib_regression_srv_2;
-ALTER FOREIGN TABLE ft1 OWNER TO contrib_regression_role_1;
-\d
-DROP FOREIGN TABLE ft1;
+CREATE USER MAPPING FOR PUBLIC SERVER loopback1;
+CREATE USER MAPPING FOR PUBLIC SERVER loopback2 OPTIONS (user 'invalid');
 
--- create entity of local table with same contents
-CREATE TABLE person_l (
-   id integer not null,
-   name text not null,
-   birthday date,
-   update_ts timestamp with time zone,
-   constraint pk_person_l primary key (id)
+CREATE TABLE t1(
+   c1 integer,
+   c2 text,
+   c3 date
 );
-BEGIN;
-INSERT INTO person_l VALUES(1, 'foo', '01-31-2000', '01-31-2000 00:00:00+00:00');
-INSERT INTO person_l VALUES(2, 'bar', '01-31-1900', '01-31-1900 00:00:00+00:00');
-INSERT INTO person_l VALUES(4, 'bar', NULL, NULL);
-COMMIT;
-
--- create foreign table which references table 'person'
-CREATE FOREIGN TABLE person (
-   id integer not null,
-   name text not null,
-   birthday date,
-   update_ts timestamp with time zone
-) SERVER contrib_regression_srv_1;
-CREATE FOREIGN TABLE person2 (
-   id integer not null,
-   name text not null,
-   birthday date,
-   update_ts timestamp with time zone
-) SERVER contrib_regression_srv_2 OPTIONS (nspname 'public', relname 'person');
-\det+
-
--- =============================================================================
--- Misc statement section
--- =============================================================================
-ALTER FOREIGN TABLE person INHERIT person_l;
--- row lock via view is not allowed too.
-CREATE OR REPLACE VIEW v_person AS SELECT * FROM person;
-SELECT * FROM v_person FOR UPDATE NOWAIT;
-DROP VIEW v_person;
--- row lock via CTE is not allowed but no error occurs.
-WITH t AS (SELECT * FROM person) SELECT * FROM t ORDER BY id FOR UPDATE NOWAIT; -- not error
--- row lock in CTE is not allowed and an error occurs.
-WITH t AS (SELECT * FROM person FOR UPDATE) SELECT * FROM t ORDER BY id; -- error
-
--- cleanup
-ALTER FOREIGN TABLE person NO INHERIT person_l;
-
--- =============================================================================
--- Connection cache test section
--- XXX: some of these tests should been moved to regression test of core.
--- =============================================================================
--- clear connection cache
-DISCARD ALL;
-
--- access foreign table (no result needed)
-SELECT * FROM person WHERE 1 = 0;
-
--- one connection must be cached
-SELECT srvname, (user = usename) usename, fdwname
-   FROM pg_foreign_connections ORDER BY srvname;
-
--- access remote database 2 (no result needed)
-SELECT id, name, birthday FROM person2 WHERE 1 = 0;
-
--- two connection must be cached
-SELECT srvname, (user = usename) usename, fdwname
-   FROM pg_foreign_connections ORDER BY srvname;
-
--- disconnect from all servers
-DISCARD ALL;
-
--- connection cache must be empty
-SELECT srvname, (user = usename) usename, fdwname
-   FROM pg_foreign_connections ORDER BY srvname;
-
--- access remote database 1 and 2 (no result needed)
-SELECT id, name, birthday FROM person WHERE 1 = 0;
-SELECT id, name, birthday FROM person2 WHERE 1 = 0;
-
--- two connection must be cached
-SELECT srvname, (user = usename) usename, fdwname
-   FROM pg_foreign_connections ORDER BY srvname;
-
--- change authorization identifier
-SET SESSION AUTHORIZATION contrib_regression_role_1;
-
--- connection cache must be empty
-SELECT srvname, (user = usename) usename, fdwname
-   FROM pg_foreign_connections ORDER BY srvname;
-
--- cleanup
-RESET SESSION AUTHORIZATION;
-DROP FOREIGN TABLE person2;
-\det+
 
--- =============================================================================
--- Query test section
--- =============================================================================
--- all tuples with ORDER BY clause
-SELECT id, name, birthday,
-   xmin, xmax, cmin, cmax, ctid, (tableoid = 'person'::regclass) tableoid
-   FROM person ORDER BY id;
-
--- operator and function call in SELECT clause
-SELECT id + 10 id, upper(name) upper_name, birthday FROM person ORDER BY id;
-
--- various join/subquery situations
-SELECT f.id, f.name, f.birthday, p.id, p.name, p.birthday FROM person f JOIN person_l p ON (f.id = p.id) ORDER BY f.id, p.id;
-SELECT f.id, f.name, f.birthday, p.id, p.name, p.birthday FROM person f LEFT JOIN person_l p ON (f.id = p.id) ORDER BY f.id, p.id;
-SELECT f.id, f.name, f.birthday, p.id, p.name, p.birthday FROM person f RIGHT JOIN person_l p ON (f.id = p.id) ORDER BY f.id, p.id;
-SELECT f.id, f.name, f.birthday, p.id, p.name, p.birthday FROM person f FULL OUTER JOIN person_l p ON (f.id = p.id) ORDER BY f.id, p.id;
-SELECT id, name, birthday FROM person f WHERE f.id = (SELECT min(p.id) FROM person_l p) ORDER BY f.id;
-SELECT id, name, birthday FROM person f WHERE (f.id, f.name) IN (SELECT p.id, p.name FROM person_l p) ORDER BY f.id;
-
--- union/intersect/except
-SELECT id, name, birthday FROM person f UNION SELECT id, name, birthday FROM person_l p ORDER BY id;
-SELECT name FROM person f INTERSECT SELECT name FROM person_l p ORDER BY name;
-SELECT name FROM person f EXCEPT SELECT name FROM person_l p ORDER BY name;
-
--- WHERE clause evaluation in the foreign server
-SELECT id, name, birthday FROM person WHERE id = 1;
-SELECT id, name, birthday FROM person WHERE birthday IS NULL ORDER BY id;
-SELECT id, name, birthday FROM person WHERE id IN (1, -1, 5) ORDER BY id;
-SELECT id, name, birthday FROM person WHERE id IS DISTINCT from 1 ORDER BY id;
-SELECT id, name, birthday FROM person WHERE ARRAY[0,id,2] = ARRAY[0,1,2] ORDER BY id;
-
--- WHERE clause evaluation in local server
-SELECT id, name, birthday FROM person WHERE update_ts < '01-31-1999'::date ORDER BY id;
-
--- limit/offset
-SELECT id, name, birthday FROM person f ORDER BY id LIMIT 1 OFFSET 1;
-
--- PREPARE/EXECUTE
-PREPARE ST1(integer, integer) AS SELECT $1 param, id, name, birthday FROM person f WHERE f.id = $2;
-EXECUTE ST1(0, 1);
-EXECUTE ST1(1, 2);
-DEALLOCATE ST1;
-
--- =============================================================================
--- DDL test section
--- =============================================================================
--- NOT NULL  and CHECK constraints in column constraint syntax
-CREATE FOREIGN TABLE ft2 (c1 integer NOT NULL, c2 text CHECK (length(c2) > 0)) SERVER contrib_regression_srv_1;
-\d+ ft2
-
--- CHECK constraints in table constraint syntax
-CREATE FOREIGN TABLE ft3 (c1 integer, c2 text, CONSTRAINT ft3_c2_check CHECK (length(c2) > 0)) SERVER contrib_regression_srv_1;
-\d+ ft3
-
--- PRIMARY KEY in column constraint syntax - error
-CREATE FOREIGN TABLE ft4 (c1 integer PRIMARY KEY) SERVER contrib_regression_srv_1; -- error
-
--- UNIQUE in column constraint syntax - error
-CREATE FOREIGN TABLE ft4 (c1 integer UNIQUE) SERVER contrib_regression_srv_1; -- error
-
--- FOREIGN KEY in column constraint syntax - error
-CREATE TABLE t1 (c1 integer PRIMARY KEY);
-CREATE FOREIGN TABLE ft4 (c1 integer REFERENCES t1 (c1)) SERVER contrib_regression_srv_1; -- error
-
--- PRIMARY KEY in table constraint syntax - error
-CREATE FOREIGN TABLE ft4 (c1 integer, CONSTRAINT ft4_pkey PRIMARY KEY (c1)) SERVER contrib_regression_srv_1; -- error
-
--- UNIQUE in table constraint syntax - error
-CREATE FOREIGN TABLE ft4 (c1 integer, CONSTRAINT ft4_c1_unique UNIQUE (c1)) SERVER contrib_regression_srv_1; -- error
-
--- FOREIGN KEY in table constraint syntax - error
-CREATE FOREIGN TABLE ft4 (c1 integer, CONSTRAINT ft4_c1_fkey FOREIGN KEY (c1) REFERENCES t1 (c1)) SERVER contrib_regression_srv_1; -- error
-
--- cleanup
-DROP FOREIGN TABLE ft2;
-DROP FOREIGN TABLE ft3;
-DROP TABLE t1;
-
--- delete all data to use as inherited (parent) table
-DELETE FROM person_l;
--- change table definition
-\c contrib_regression_f1
-ALTER TABLE person ADD COLUMN dummy text;
-UPDATE person SET dummy = 'dummy value';
-\c contrib_regression
-ALTER FOREIGN TABLE person INHERIT person_l;
-ALTER TABLE person_l ADD COLUMN dummy text; -- added to person too
-CREATE RULE rl_person_insert AS ON INSERT TO person DO INSTEAD INSERT INTO person_l VALUES (NEW.*);
-\d+ person;
-\d+ person_l;
--- content of person must be showed
-INSERT INTO person VALUES (-1, 'FOO', '2100-01-31', null, 'DUMMY');
-SELECT * FROM person_l ORDER BY id;
-
--- restore table definition
-\c contrib_regression_f1
-ALTER TABLE person DROP COLUMN dummy;
-\c contrib_regression
-DROP RULE rl_person_insert ON person;
-ALTER TABLE person_l DROP COLUMN dummy;
-ALTER FOREIGN TABLE person NO INHERIT person_l;
-\d+ person;
--- no child table, result must be its own content
-SELECT * FROM person_l ORDER BY id;
-
--- query for foreign table which has no column
-ALTER FOREIGN TABLE person DROP COLUMN id;
-ALTER FOREIGN TABLE person DROP COLUMN name;
-ALTER FOREIGN TABLE person DROP COLUMN birthday;
-ALTER FOREIGN TABLE person DROP COLUMN update_ts;
-SELECT * FROM person;
-
--- =============================================================================
--- Cleanup section
--- =============================================================================
-DISCARD ALL;
-DROP FOREIGN DATA WRAPPER contrib_regression_wrapper CASCADE;
-DROP DATABASE contrib_regression_f1;
-DROP DATABASE contrib_regression_f2;
-DROP ROLE contrib_regression_role_1;
+COPY t1 FROM stdin;
+1  foo 1970-01-01
+2  bar 1970-01-02
+3  buz 1970-01-03
+\.
+
+CREATE FOREIGN TABLE ft1 (
+   c1 integer,
+   c2 text,
+   c3 date
+) SERVER loopback1 OPTIONS (relname 't1');
+
+CREATE FOREIGN TABLE ft2 (
+   c1 integer,
+   c2 text,
+   c3 date
+) SERVER loopback2 OPTIONS (relname 'invalid');
+
+-- simple query and connection caching
+SELECT * FROM ft1 ORDER BY c1;
+SELECT * FROM ft2 ORDER BY c1; -- ERROR
+ALTER USER MAPPING FOR PUBLIC SERVER loopback2 OPTIONS (DROP user);
+SELECT * FROM postgresql_fdw_connections ORDER BY conname;
+SELECT * FROM ft2 ORDER BY c1; -- ERROR
+SELECT * FROM postgresql_fdw_connections ORDER BY conname;
+ALTER FOREIGN TABLE ft2 OPTIONS (SET relname 't1');
+SELECT * FROM ft2 ORDER BY c1;
+SELECT * FROM postgresql_fdw_connections ORDER BY conname;
+
+-- query using join
+SELECT * FROM ft1 JOIN ft2 ON (ft1.c1 = ft2.c1) ORDER BY ft1.c1;
+SELECT * FROM ft1 t1 JOIN ft1 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1;
+
+-- clean up
+DROP FOREIGN DATA WRAPPER postgresql_fdw CASCADE;
+DROP TABLE t1 CASCADE;
index 037551ec7540b5e2b5c00b6598dc744198da1a12..a124d654558df9b6453d4350b899f77db2a6b32e 100644 (file)
@@ -4,6 +4,6 @@
 set search_path = public;
 
 DROP FUNCTION postgresql_fdw_handler ();
-DROP VIEW pg_foreign_connections;
-DROP FUNCTION pg_foreign_connections(conname OUT text, serverid OUT oid, userid OUT oid);
+DROP VIEW postgresql_fdw_connections;
+DROP FUNCTION postgresql_fdw_connections(conname OUT text, serverid OUT oid, userid OUT oid);
 
index 2855e2f2ce99c93c268318894cec289d69c06da6..572fca9b4de487861afb56ad77f803f461a8fc33 100644 (file)
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term>
+     <function>pg_foreign_connections() returns record</function>
+    </term>
+
+    <listitem>
+     <para>
+      <function>pg_foreign_connections</function> is a function which
+      returns list of active connections which are estabilshed via
+      postgresql_fdw.  This function was designed as a private function, used
+      from pg_foreign_connections view.
+     </para>
+    </listitem>
+   </varlistentry>
+
   </variablelist>
+
+ </sect2>
+
+ <sect2>
+  <title>Views</title>
+
+  <variablelist>
+   <varlistentry>
+    <term>
+     <function>pg_foreign_connections</function>
+    </term>
+
+    <listitem>
+     <para>
+      <structname>pg_foreign_connections</structname> is a view which
+      shows active connections which are estabilshed via postgresql_fdw.
+     </para>
+
+     <table>
+      <title><structname>pg_foreign_connections</> 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>conname</structfield></entry>
+         <entry><type>name</type></entry>
+         <entry></entry>
+         <entry>Name of the connection, same as srvname in current version</entry>
+        </row>
+   
+        <row>
+         <entry><structfield>srvname</structfield></entry>
+         <entry><type>name</type></entry>
+         <entry>pg_foreign_server.srvname</entry>
+         <entry>Name of the foreign server</entry>
+        </row>
+        <row>
+         <entry><structfield>usename</structfield></entry>
+         <entry><type>name</type></entry>
+         <entry>pg_authid.rolname</entry>
+         <entry>Name of the user who establish the connection</entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+
+    </listitem>
+   </varlistentry>
+  </variablelist>
+
  </sect2>
 
  <sect2>