-- =============================================================================
-- 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;
#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);
/*
*/
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 */
{
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,
/* 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);
}
* 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);
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;
if (FSConnectionHash)
{
HASH_SEQ_STATUS hash_seq;
- FSConnCacheEntry *entry;
+ ConnCacheEntry *entry;
MemoryContextSwitchTo(oldcontext);
static void
cleanup_fsconnection(int code, Datum arg)
{
- DisconnectAllForeignServers();
+ DisconnectAll();
}
#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 */
Tuplestorestate *tupstore; /* result set */
} pgFdwReply;
-FdwRoutine postgresql_fdw_routine =
+static FdwRoutine postgresql_fdw_routine =
{
pgConnectServer,
pgFreeFSConnection,
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.
pfree(keywords);
pfree(values);
+ /* register the connection into connection cache. */
+ RegisterConnection(conn, server, user, server->servername);
+
return (FSConnection *) conn;
}
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. */
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);
}
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;
-- 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;
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);
</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>