@@ -7371,6 +7371,340 @@ NOTICE: drop cascades to foreign table bar2
7371
7371
drop table loct1;
7372
7372
drop table loct2;
7373
7373
-- ===================================================================
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
+ -- ===================================================================
7374
7708
-- test IMPORT FOREIGN SCHEMA
7375
7709
-- ===================================================================
7376
7710
CREATE SCHEMA import_source;
0 commit comments