From: Shigeru Hanada Date: Wed, 13 Oct 2010 11:12:14 +0000 (+0900) Subject: Simplified connection cache in contrib/postgresql_fdw to fit the FDW. X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=07bad697d7b37c03b856504eb690bf1a983bf395;p=users%2Fhanada%2Fpostgres.git Simplified connection cache in contrib/postgresql_fdw to fit the FDW. --- diff --git a/contrib/postgresql_fdw/expected/postgresql_fdw.out b/contrib/postgresql_fdw/expected/postgresql_fdw.out index 0acedda5b4..f3114f7711 100644 --- a/contrib/postgresql_fdw/expected/postgresql_fdw.out +++ b/contrib/postgresql_fdw/expected/postgresql_fdw.out @@ -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; diff --git a/contrib/postgresql_fdw/fsconnection.c b/contrib/postgresql_fdw/fsconnection.c index fda112ecd8..62e50fdb2d 100644 --- a/contrib/postgresql_fdw/fsconnection.c +++ b/contrib/postgresql_fdw/fsconnection.c @@ -22,23 +22,23 @@ #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(); } diff --git a/contrib/postgresql_fdw/fsconnection.h b/contrib/postgresql_fdw/fsconnection.h index e1c886de2c..de6606df11 100644 --- a/contrib/postgresql_fdw/fsconnection.h +++ b/contrib/postgresql_fdw/fsconnection.h @@ -12,13 +12,12 @@ #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 */ diff --git a/contrib/postgresql_fdw/postgresql_fdw.c b/contrib/postgresql_fdw/postgresql_fdw.c index cf640ecee0..73e988c720 100644 --- a/contrib/postgresql_fdw/postgresql_fdw.c +++ b/contrib/postgresql_fdw/postgresql_fdw.c @@ -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); } diff --git a/contrib/postgresql_fdw/postgresql_fdw.sql.in b/contrib/postgresql_fdw/postgresql_fdw.sql.in index d2ed838f6a..60f26a2817 100644 --- a/contrib/postgresql_fdw/postgresql_fdw.sql.in +++ b/contrib/postgresql_fdw/postgresql_fdw.sql.in @@ -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; diff --git a/contrib/postgresql_fdw/sql/postgresql_fdw.sql b/contrib/postgresql_fdw/sql/postgresql_fdw.sql index a23a1c15b5..d600c63bb4 100644 --- a/contrib/postgresql_fdw/sql/postgresql_fdw.sql +++ b/contrib/postgresql_fdw/sql/postgresql_fdw.sql @@ -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; diff --git a/contrib/postgresql_fdw/uninstall_postgresql_fdw.sql b/contrib/postgresql_fdw/uninstall_postgresql_fdw.sql index 037551ec75..a124d65455 100644 --- a/contrib/postgresql_fdw/uninstall_postgresql_fdw.sql +++ b/contrib/postgresql_fdw/uninstall_postgresql_fdw.sql @@ -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); diff --git a/doc/src/sgml/postgresql-fdw.sgml b/doc/src/sgml/postgresql-fdw.sgml index 2855e2f2ce..572fca9b4d 100644 --- a/doc/src/sgml/postgresql-fdw.sgml +++ b/doc/src/sgml/postgresql-fdw.sgml @@ -38,7 +38,83 @@ + + + pg_foreign_connections() returns record + + + + + pg_foreign_connections 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. + + + + + + + + + Views + + + + + pg_foreign_connections + + + + + pg_foreign_connections is a view which + shows active connections which are estabilshed via postgresql_fdw. + + + + <structname>pg_foreign_connections</> Columns + + + + + Name + Type + References + Description + + + + + + conname + name + + Name of the connection, same as srvname in current version + + + + srvname + name + pg_foreign_server.srvname + Name of the foreign server + + + + usename + name + pg_authid.rolname + Name of the user who establish the connection + + + + +
+ +
+
+
+