Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3d956d9

Browse files
committed
Allow insert and update tuple routing and COPY for foreign tables.
Also enable this for postgres_fdw. Etsuro Fujita, based on an earlier patch by Amit Langote. The larger patch series of which this is a part has been reviewed by Amit Langote, David Fetter, Maksim Milyutin, Álvaro Herrera, Stephen Frost, and me. Minor documentation changes to the final version by me. Discussion: http://postgr.es/m/29906a26-da12-8c86-4fb9-d8f88442f2b9@lab.ntt.co.jp
1 parent cb1ff1e commit 3d956d9

File tree

16 files changed

+924
-91
lines changed

16 files changed

+924
-91
lines changed

contrib/file_fdw/input/file_fdw.source

+5
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ DELETE FROM agg_csv WHERE a = 100;
136136
-- but this should be allowed
137137
SELECT * FROM agg_csv FOR UPDATE;
138138

139+
-- copy from isn't supported either
140+
COPY agg_csv FROM STDIN;
141+
12 3.4
142+
\.
143+
139144
-- constraint exclusion tests
140145
\t on
141146
EXPLAIN (VERBOSE, COSTS FALSE) SELECT * FROM agg_csv WHERE a < 0;

contrib/file_fdw/output/file_fdw.source

+6-3
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ SELECT * FROM agg_csv FOR UPDATE;
221221
42 | 324.78
222222
(3 rows)
223223

224+
-- copy from isn't supported either
225+
COPY agg_csv FROM STDIN;
226+
ERROR: cannot insert into foreign table "agg_csv"
224227
-- constraint exclusion tests
225228
\t on
226229
EXPLAIN (VERBOSE, COSTS FALSE) SELECT * FROM agg_csv WHERE a < 0;
@@ -315,7 +318,7 @@ SELECT tableoid::regclass, * FROM p2;
315318
(0 rows)
316319

317320
COPY pt FROM '@abs_srcdir@/data/list2.bad' with (format 'csv', delimiter ','); -- ERROR
318-
ERROR: cannot route inserted tuples to a foreign table
321+
ERROR: cannot insert into foreign table "p1"
319322
CONTEXT: COPY pt, line 2: "1,qux"
320323
COPY pt FROM '@abs_srcdir@/data/list2.csv' with (format 'csv', delimiter ',');
321324
SELECT tableoid::regclass, * FROM pt;
@@ -342,10 +345,10 @@ SELECT tableoid::regclass, * FROM p2;
342345
(2 rows)
343346

344347
INSERT INTO pt VALUES (1, 'xyzzy'); -- ERROR
345-
ERROR: cannot route inserted tuples to a foreign table
348+
ERROR: cannot insert into foreign table "p1"
346349
INSERT INTO pt VALUES (2, 'xyzzy');
347350
UPDATE pt set a = 1 where a = 2; -- ERROR
348-
ERROR: cannot route inserted tuples to a foreign table
351+
ERROR: cannot insert into foreign table "p1"
349352
SELECT tableoid::regclass, * FROM pt;
350353
tableoid | a | b
351354
----------+---+-------

contrib/postgres_fdw/expected/postgres_fdw.out

+334
Original file line numberDiff line numberDiff line change
@@ -7371,6 +7371,340 @@ NOTICE: drop cascades to foreign table bar2
73717371
drop table loct1;
73727372
drop table loct2;
73737373
-- ===================================================================
7374+
-- test tuple routing for foreign-table partitions
7375+
-- ===================================================================
7376+
-- Test insert tuple routing
7377+
create table itrtest (a int, b text) partition by list (a);
7378+
create table loct1 (a int check (a in (1)), b text);
7379+
create foreign table remp1 (a int check (a in (1)), b text) server loopback options (table_name 'loct1');
7380+
create table loct2 (a int check (a in (2)), b text);
7381+
create foreign table remp2 (b text, a int check (a in (2))) server loopback options (table_name 'loct2');
7382+
alter table itrtest attach partition remp1 for values in (1);
7383+
alter table itrtest attach partition remp2 for values in (2);
7384+
insert into itrtest values (1, 'foo');
7385+
insert into itrtest values (1, 'bar') returning *;
7386+
a | b
7387+
---+-----
7388+
1 | bar
7389+
(1 row)
7390+
7391+
insert into itrtest values (2, 'baz');
7392+
insert into itrtest values (2, 'qux') returning *;
7393+
a | b
7394+
---+-----
7395+
2 | qux
7396+
(1 row)
7397+
7398+
insert into itrtest values (1, 'test1'), (2, 'test2') returning *;
7399+
a | b
7400+
---+-------
7401+
1 | test1
7402+
2 | test2
7403+
(2 rows)
7404+
7405+
select tableoid::regclass, * FROM itrtest;
7406+
tableoid | a | b
7407+
----------+---+-------
7408+
remp1 | 1 | foo
7409+
remp1 | 1 | bar
7410+
remp1 | 1 | test1
7411+
remp2 | 2 | baz
7412+
remp2 | 2 | qux
7413+
remp2 | 2 | test2
7414+
(6 rows)
7415+
7416+
select tableoid::regclass, * FROM remp1;
7417+
tableoid | a | b
7418+
----------+---+-------
7419+
remp1 | 1 | foo
7420+
remp1 | 1 | bar
7421+
remp1 | 1 | test1
7422+
(3 rows)
7423+
7424+
select tableoid::regclass, * FROM remp2;
7425+
tableoid | b | a
7426+
----------+-------+---
7427+
remp2 | baz | 2
7428+
remp2 | qux | 2
7429+
remp2 | test2 | 2
7430+
(3 rows)
7431+
7432+
delete from itrtest;
7433+
create unique index loct1_idx on loct1 (a);
7434+
-- DO NOTHING without an inference specification is supported
7435+
insert into itrtest values (1, 'foo') on conflict do nothing returning *;
7436+
a | b
7437+
---+-----
7438+
1 | foo
7439+
(1 row)
7440+
7441+
insert into itrtest values (1, 'foo') on conflict do nothing returning *;
7442+
a | b
7443+
---+---
7444+
(0 rows)
7445+
7446+
-- But other cases are not supported
7447+
insert into itrtest values (1, 'bar') on conflict (a) do nothing;
7448+
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
7449+
insert into itrtest values (1, 'bar') on conflict (a) do update set b = excluded.b;
7450+
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
7451+
select tableoid::regclass, * FROM itrtest;
7452+
tableoid | a | b
7453+
----------+---+-----
7454+
remp1 | 1 | foo
7455+
(1 row)
7456+
7457+
drop table itrtest;
7458+
drop table loct1;
7459+
drop table loct2;
7460+
-- Test update tuple routing
7461+
create table utrtest (a int, b text) partition by list (a);
7462+
create table loct (a int check (a in (1)), b text);
7463+
create foreign table remp (a int check (a in (1)), b text) server loopback options (table_name 'loct');
7464+
create table locp (a int check (a in (2)), b text);
7465+
alter table utrtest attach partition remp for values in (1);
7466+
alter table utrtest attach partition locp for values in (2);
7467+
insert into utrtest values (1, 'foo');
7468+
insert into utrtest values (2, 'qux');
7469+
select tableoid::regclass, * FROM utrtest;
7470+
tableoid | a | b
7471+
----------+---+-----
7472+
remp | 1 | foo
7473+
locp | 2 | qux
7474+
(2 rows)
7475+
7476+
select tableoid::regclass, * FROM remp;
7477+
tableoid | a | b
7478+
----------+---+-----
7479+
remp | 1 | foo
7480+
(1 row)
7481+
7482+
select tableoid::regclass, * FROM locp;
7483+
tableoid | a | b
7484+
----------+---+-----
7485+
locp | 2 | qux
7486+
(1 row)
7487+
7488+
-- It's not allowed to move a row from a partition that is foreign to another
7489+
update utrtest set a = 2 where b = 'foo' returning *;
7490+
ERROR: new row for relation "loct" violates check constraint "loct_a_check"
7491+
DETAIL: Failing row contains (2, foo).
7492+
CONTEXT: remote SQL command: UPDATE public.loct SET a = 2 WHERE ((b = 'foo'::text)) RETURNING a, b
7493+
-- But the reverse is allowed
7494+
update utrtest set a = 1 where b = 'qux' returning *;
7495+
a | b
7496+
---+-----
7497+
1 | qux
7498+
(1 row)
7499+
7500+
select tableoid::regclass, * FROM utrtest;
7501+
tableoid | a | b
7502+
----------+---+-----
7503+
remp | 1 | foo
7504+
remp | 1 | qux
7505+
(2 rows)
7506+
7507+
select tableoid::regclass, * FROM remp;
7508+
tableoid | a | b
7509+
----------+---+-----
7510+
remp | 1 | foo
7511+
remp | 1 | qux
7512+
(2 rows)
7513+
7514+
select tableoid::regclass, * FROM locp;
7515+
tableoid | a | b
7516+
----------+---+---
7517+
(0 rows)
7518+
7519+
-- The executor should not let unexercised FDWs shut down
7520+
update utrtest set a = 1 where b = 'foo';
7521+
drop table utrtest;
7522+
drop table loct;
7523+
-- Test copy tuple routing
7524+
create table ctrtest (a int, b text) partition by list (a);
7525+
create table loct1 (a int check (a in (1)), b text);
7526+
create foreign table remp1 (a int check (a in (1)), b text) server loopback options (table_name 'loct1');
7527+
create table loct2 (a int check (a in (2)), b text);
7528+
create foreign table remp2 (b text, a int check (a in (2))) server loopback options (table_name 'loct2');
7529+
alter table ctrtest attach partition remp1 for values in (1);
7530+
alter table ctrtest attach partition remp2 for values in (2);
7531+
copy ctrtest from stdin;
7532+
select tableoid::regclass, * FROM ctrtest;
7533+
tableoid | a | b
7534+
----------+---+-----
7535+
remp1 | 1 | foo
7536+
remp2 | 2 | qux
7537+
(2 rows)
7538+
7539+
select tableoid::regclass, * FROM remp1;
7540+
tableoid | a | b
7541+
----------+---+-----
7542+
remp1 | 1 | foo
7543+
(1 row)
7544+
7545+
select tableoid::regclass, * FROM remp2;
7546+
tableoid | b | a
7547+
----------+-----+---
7548+
remp2 | qux | 2
7549+
(1 row)
7550+
7551+
-- Copying into foreign partitions directly should work as well
7552+
copy remp1 from stdin;
7553+
select tableoid::regclass, * FROM remp1;
7554+
tableoid | a | b
7555+
----------+---+-----
7556+
remp1 | 1 | foo
7557+
remp1 | 1 | bar
7558+
(2 rows)
7559+
7560+
drop table ctrtest;
7561+
drop table loct1;
7562+
drop table loct2;
7563+
-- ===================================================================
7564+
-- test COPY FROM
7565+
-- ===================================================================
7566+
create table loc2 (f1 int, f2 text);
7567+
alter table loc2 set (autovacuum_enabled = 'false');
7568+
create foreign table rem2 (f1 int, f2 text) server loopback options(table_name 'loc2');
7569+
-- Test basic functionality
7570+
copy rem2 from stdin;
7571+
select * from rem2;
7572+
f1 | f2
7573+
----+-----
7574+
1 | foo
7575+
2 | bar
7576+
(2 rows)
7577+
7578+
delete from rem2;
7579+
-- Test check constraints
7580+
alter table loc2 add constraint loc2_f1positive check (f1 >= 0);
7581+
alter foreign table rem2 add constraint rem2_f1positive check (f1 >= 0);
7582+
-- check constraint is enforced on the remote side, not locally
7583+
copy rem2 from stdin;
7584+
copy rem2 from stdin; -- ERROR
7585+
ERROR: new row for relation "loc2" violates check constraint "loc2_f1positive"
7586+
DETAIL: Failing row contains (-1, xyzzy).
7587+
CONTEXT: remote SQL command: INSERT INTO public.loc2(f1, f2) VALUES ($1, $2)
7588+
COPY rem2, line 1: "-1 xyzzy"
7589+
select * from rem2;
7590+
f1 | f2
7591+
----+-----
7592+
1 | foo
7593+
2 | bar
7594+
(2 rows)
7595+
7596+
alter foreign table rem2 drop constraint rem2_f1positive;
7597+
alter table loc2 drop constraint loc2_f1positive;
7598+
delete from rem2;
7599+
-- Test local triggers
7600+
create trigger trig_stmt_before before insert on rem2
7601+
for each statement execute procedure trigger_func();
7602+
create trigger trig_stmt_after after insert on rem2
7603+
for each statement execute procedure trigger_func();
7604+
create trigger trig_row_before before insert on rem2
7605+
for each row execute procedure trigger_data(23,'skidoo');
7606+
create trigger trig_row_after after insert on rem2
7607+
for each row execute procedure trigger_data(23,'skidoo');
7608+
copy rem2 from stdin;
7609+
NOTICE: trigger_func(<NULL>) called: action = INSERT, when = BEFORE, level = STATEMENT
7610+
NOTICE: trig_row_before(23, skidoo) BEFORE ROW INSERT ON rem2
7611+
NOTICE: NEW: (1,foo)
7612+
NOTICE: trig_row_before(23, skidoo) BEFORE ROW INSERT ON rem2
7613+
NOTICE: NEW: (2,bar)
7614+
NOTICE: trig_row_after(23, skidoo) AFTER ROW INSERT ON rem2
7615+
NOTICE: NEW: (1,foo)
7616+
NOTICE: trig_row_after(23, skidoo) AFTER ROW INSERT ON rem2
7617+
NOTICE: NEW: (2,bar)
7618+
NOTICE: trigger_func(<NULL>) called: action = INSERT, when = AFTER, level = STATEMENT
7619+
select * from rem2;
7620+
f1 | f2
7621+
----+-----
7622+
1 | foo
7623+
2 | bar
7624+
(2 rows)
7625+
7626+
drop trigger trig_row_before on rem2;
7627+
drop trigger trig_row_after on rem2;
7628+
drop trigger trig_stmt_before on rem2;
7629+
drop trigger trig_stmt_after on rem2;
7630+
delete from rem2;
7631+
create trigger trig_row_before_insert before insert on rem2
7632+
for each row execute procedure trig_row_before_insupdate();
7633+
-- The new values are concatenated with ' triggered !'
7634+
copy rem2 from stdin;
7635+
select * from rem2;
7636+
f1 | f2
7637+
----+-----------------
7638+
1 | foo triggered !
7639+
2 | bar triggered !
7640+
(2 rows)
7641+
7642+
drop trigger trig_row_before_insert on rem2;
7643+
delete from rem2;
7644+
create trigger trig_null before insert on rem2
7645+
for each row execute procedure trig_null();
7646+
-- Nothing happens
7647+
copy rem2 from stdin;
7648+
select * from rem2;
7649+
f1 | f2
7650+
----+----
7651+
(0 rows)
7652+
7653+
drop trigger trig_null on rem2;
7654+
delete from rem2;
7655+
-- Test remote triggers
7656+
create trigger trig_row_before_insert before insert on loc2
7657+
for each row execute procedure trig_row_before_insupdate();
7658+
-- The new values are concatenated with ' triggered !'
7659+
copy rem2 from stdin;
7660+
select * from rem2;
7661+
f1 | f2
7662+
----+-----------------
7663+
1 | foo triggered !
7664+
2 | bar triggered !
7665+
(2 rows)
7666+
7667+
drop trigger trig_row_before_insert on loc2;
7668+
delete from rem2;
7669+
create trigger trig_null before insert on loc2
7670+
for each row execute procedure trig_null();
7671+
-- Nothing happens
7672+
copy rem2 from stdin;
7673+
select * from rem2;
7674+
f1 | f2
7675+
----+----
7676+
(0 rows)
7677+
7678+
drop trigger trig_null on loc2;
7679+
delete from rem2;
7680+
-- Test a combination of local and remote triggers
7681+
create trigger rem2_trig_row_before before insert on rem2
7682+
for each row execute procedure trigger_data(23,'skidoo');
7683+
create trigger rem2_trig_row_after after insert on rem2
7684+
for each row execute procedure trigger_data(23,'skidoo');
7685+
create trigger loc2_trig_row_before_insert before insert on loc2
7686+
for each row execute procedure trig_row_before_insupdate();
7687+
copy rem2 from stdin;
7688+
NOTICE: rem2_trig_row_before(23, skidoo) BEFORE ROW INSERT ON rem2
7689+
NOTICE: NEW: (1,foo)
7690+
NOTICE: rem2_trig_row_before(23, skidoo) BEFORE ROW INSERT ON rem2
7691+
NOTICE: NEW: (2,bar)
7692+
NOTICE: rem2_trig_row_after(23, skidoo) AFTER ROW INSERT ON rem2
7693+
NOTICE: NEW: (1,"foo triggered !")
7694+
NOTICE: rem2_trig_row_after(23, skidoo) AFTER ROW INSERT ON rem2
7695+
NOTICE: NEW: (2,"bar triggered !")
7696+
select * from rem2;
7697+
f1 | f2
7698+
----+-----------------
7699+
1 | foo triggered !
7700+
2 | bar triggered !
7701+
(2 rows)
7702+
7703+
drop trigger rem2_trig_row_before on rem2;
7704+
drop trigger rem2_trig_row_after on rem2;
7705+
drop trigger loc2_trig_row_before_insert on loc2;
7706+
delete from rem2;
7707+
-- ===================================================================
73747708
-- test IMPORT FOREIGN SCHEMA
73757709
-- ===================================================================
73767710
CREATE SCHEMA import_source;

0 commit comments

Comments
 (0)