Skip to content

Commit 594f8d3

Browse files
author
Etsuro Fujita
committed
Allow batching of inserts during cross-partition updates.
Commit 927f453 disallowed batching added by commit b663a41 to be used for the inserts performed as part of cross-partition updates of partitioned tables, mainly because the previous code in nodeModifyTable.c couldn't handle pending inserts into foreign-table partitions that are also UPDATE target partitions. But we don't have such a limitation anymore (cf. commit ffbb7e6), so let's allow for this by removing from execPartition.c the restriction added by commit 927f453 that batching is only allowed if the query command type is CMD_INSERT. In postgres_fdw, since commit 86dc900 changed it to effectively disable cross-partition updates in the case where a foreign-table partition chosen to insert rows into is also an UPDATE target partition, allow batching in the case where a foreign-table partition chosen to do so is *not* also an UPDATE target partition. This is enabled by the "batch_size" option added by commit b663a41, which is disabled by default. This patch also adjusts the test case added by commit 927f453 to confirm that the inserts performed as part of a cross-partition update of a partitioned table indeed uses batching. Amit Langote, reviewed and/or tested by Georgios Kokolatos, Zhihong Yu, Bharath Rupireddy, Hou Zhijie, Vignesh C, and me. Discussion: http://postgr.es/m/CA%2BHiwqH1Lz1yJmPs%3DaD-pzd_HLLynLHvq5iYeT9mB0bBV7oJ6w%40mail.gmail.com
1 parent 3226f47 commit 594f8d3

File tree

4 files changed

+106
-26
lines changed

4 files changed

+106
-26
lines changed

contrib/postgres_fdw/expected/postgres_fdw.out

+53-11
Original file line numberDiff line numberDiff line change
@@ -10347,30 +10347,72 @@ SELECT COUNT(*) FROM batch_table;
1034710347
66
1034810348
(1 row)
1034910349

10350-
-- Check that enabling batched inserts doesn't interfere with cross-partition
10351-
-- updates
10350+
-- Clean up
10351+
DROP TABLE batch_table;
10352+
DROP TABLE batch_table_p0;
10353+
DROP TABLE batch_table_p1;
10354+
-- Check that batched mode also works for some inserts made during
10355+
-- cross-partition updates
1035210356
CREATE TABLE batch_cp_upd_test (a int) PARTITION BY LIST (a);
1035310357
CREATE TABLE batch_cp_upd_test1 (LIKE batch_cp_upd_test);
1035410358
CREATE FOREIGN TABLE batch_cp_upd_test1_f
1035510359
PARTITION OF batch_cp_upd_test
1035610360
FOR VALUES IN (1)
1035710361
SERVER loopback
1035810362
OPTIONS (table_name 'batch_cp_upd_test1', batch_size '10');
10359-
CREATE TABLE batch_cp_up_test1 PARTITION OF batch_cp_upd_test
10363+
CREATE TABLE batch_cp_upd_test2 PARTITION OF batch_cp_upd_test
1036010364
FOR VALUES IN (2);
10361-
INSERT INTO batch_cp_upd_test VALUES (1), (2);
10362-
-- The following moves a row from the local partition to the foreign one
10363-
UPDATE batch_cp_upd_test t SET a = 1 FROM (VALUES (1), (2)) s(a) WHERE t.a = s.a;
10364-
ERROR: cannot route tuples into foreign table to be updated "batch_cp_upd_test1_f"
10365-
SELECT tableoid::regclass, * FROM batch_cp_upd_test;
10365+
CREATE TABLE batch_cp_upd_test3 (LIKE batch_cp_upd_test);
10366+
CREATE FOREIGN TABLE batch_cp_upd_test3_f
10367+
PARTITION OF batch_cp_upd_test
10368+
FOR VALUES IN (3)
10369+
SERVER loopback
10370+
OPTIONS (table_name 'batch_cp_upd_test3', batch_size '1');
10371+
-- Create statement triggers on remote tables that "log" any INSERTs
10372+
-- performed on them.
10373+
CREATE TABLE cmdlog (cmd text);
10374+
CREATE FUNCTION log_stmt() RETURNS TRIGGER LANGUAGE plpgsql AS $$
10375+
BEGIN INSERT INTO public.cmdlog VALUES (TG_OP || ' on ' || TG_RELNAME); RETURN NULL; END;
10376+
$$;
10377+
CREATE TRIGGER stmt_trig AFTER INSERT ON batch_cp_upd_test1
10378+
FOR EACH STATEMENT EXECUTE FUNCTION log_stmt();
10379+
CREATE TRIGGER stmt_trig AFTER INSERT ON batch_cp_upd_test3
10380+
FOR EACH STATEMENT EXECUTE FUNCTION log_stmt();
10381+
-- This update moves rows from the local partition 'batch_cp_upd_test2' to the
10382+
-- foreign partition 'batch_cp_upd_test1', one that has insert batching
10383+
-- enabled, so a single INSERT for both rows.
10384+
INSERT INTO batch_cp_upd_test VALUES (2), (2);
10385+
UPDATE batch_cp_upd_test t SET a = 1 FROM (VALUES (1), (2)) s(a) WHERE t.a = s.a AND s.a = 2;
10386+
-- This one moves rows from the local partition 'batch_cp_upd_test2' to the
10387+
-- foreign partition 'batch_cp_upd_test2', one that has insert batching
10388+
-- disabled, so separate INSERTs for the two rows.
10389+
INSERT INTO batch_cp_upd_test VALUES (2), (2);
10390+
UPDATE batch_cp_upd_test t SET a = 3 FROM (VALUES (1), (2)) s(a) WHERE t.a = s.a AND s.a = 2;
10391+
SELECT tableoid::regclass, * FROM batch_cp_upd_test ORDER BY 1;
1036610392
tableoid | a
1036710393
----------------------+---
1036810394
batch_cp_upd_test1_f | 1
10369-
batch_cp_up_test1 | 2
10370-
(2 rows)
10395+
batch_cp_upd_test1_f | 1
10396+
batch_cp_upd_test3_f | 3
10397+
batch_cp_upd_test3_f | 3
10398+
(4 rows)
10399+
10400+
-- Should see 1 INSERT on batch_cp_upd_test1 and 2 on batch_cp_upd_test3 as
10401+
-- described above.
10402+
SELECT * FROM cmdlog ORDER BY 1;
10403+
cmd
10404+
------------------------------
10405+
INSERT on batch_cp_upd_test1
10406+
INSERT on batch_cp_upd_test3
10407+
INSERT on batch_cp_upd_test3
10408+
(3 rows)
1037110409

1037210410
-- Clean up
10373-
DROP TABLE batch_table, batch_cp_upd_test, batch_table_p0, batch_table_p1 CASCADE;
10411+
DROP TABLE batch_cp_upd_test;
10412+
DROP TABLE batch_cp_upd_test1;
10413+
DROP TABLE batch_cp_upd_test3;
10414+
DROP TABLE cmdlog;
10415+
DROP FUNCTION log_stmt();
1037410416
-- Use partitioning
1037510417
ALTER SERVER loopback OPTIONS (ADD batch_size '10');
1037610418
CREATE TABLE batch_table ( x int, field1 text, field2 text) PARTITION BY HASH (x);

contrib/postgres_fdw/postgres_fdw.c

+5-5
Original file line numberDiff line numberDiff line change
@@ -2017,16 +2017,16 @@ static int
20172017
postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
20182018
{
20192019
int batch_size;
2020-
PgFdwModifyState *fmstate = resultRelInfo->ri_FdwState ?
2021-
(PgFdwModifyState *) resultRelInfo->ri_FdwState :
2022-
NULL;
2020+
PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
20232021

20242022
/* should be called only once */
20252023
Assert(resultRelInfo->ri_BatchSize == 0);
20262024

20272025
/*
2028-
* Should never get called when the insert is being performed as part of a
2029-
* row movement operation.
2026+
* Should never get called when the insert is being performed on a table
2027+
* that is also among the target relations of an UPDATE operation,
2028+
* because postgresBeginForeignInsert() currently rejects such insert
2029+
* attempts.
20302030
*/
20312031
Assert(fmstate == NULL || fmstate->aux_fmstate == NULL);
20322032

contrib/postgres_fdw/sql/postgres_fdw.sql

+47-8
Original file line numberDiff line numberDiff line change
@@ -3329,25 +3329,64 @@ CREATE TABLE batch_table_p2
33293329
INSERT INTO batch_table SELECT * FROM generate_series(1, 66) i;
33303330
SELECT COUNT(*) FROM batch_table;
33313331

3332-
-- Check that enabling batched inserts doesn't interfere with cross-partition
3333-
-- updates
3332+
-- Clean up
3333+
DROP TABLE batch_table;
3334+
DROP TABLE batch_table_p0;
3335+
DROP TABLE batch_table_p1;
3336+
3337+
-- Check that batched mode also works for some inserts made during
3338+
-- cross-partition updates
33343339
CREATE TABLE batch_cp_upd_test (a int) PARTITION BY LIST (a);
33353340
CREATE TABLE batch_cp_upd_test1 (LIKE batch_cp_upd_test);
33363341
CREATE FOREIGN TABLE batch_cp_upd_test1_f
33373342
PARTITION OF batch_cp_upd_test
33383343
FOR VALUES IN (1)
33393344
SERVER loopback
33403345
OPTIONS (table_name 'batch_cp_upd_test1', batch_size '10');
3341-
CREATE TABLE batch_cp_up_test1 PARTITION OF batch_cp_upd_test
3346+
CREATE TABLE batch_cp_upd_test2 PARTITION OF batch_cp_upd_test
33423347
FOR VALUES IN (2);
3343-
INSERT INTO batch_cp_upd_test VALUES (1), (2);
3348+
CREATE TABLE batch_cp_upd_test3 (LIKE batch_cp_upd_test);
3349+
CREATE FOREIGN TABLE batch_cp_upd_test3_f
3350+
PARTITION OF batch_cp_upd_test
3351+
FOR VALUES IN (3)
3352+
SERVER loopback
3353+
OPTIONS (table_name 'batch_cp_upd_test3', batch_size '1');
3354+
3355+
-- Create statement triggers on remote tables that "log" any INSERTs
3356+
-- performed on them.
3357+
CREATE TABLE cmdlog (cmd text);
3358+
CREATE FUNCTION log_stmt() RETURNS TRIGGER LANGUAGE plpgsql AS $$
3359+
BEGIN INSERT INTO public.cmdlog VALUES (TG_OP || ' on ' || TG_RELNAME); RETURN NULL; END;
3360+
$$;
3361+
CREATE TRIGGER stmt_trig AFTER INSERT ON batch_cp_upd_test1
3362+
FOR EACH STATEMENT EXECUTE FUNCTION log_stmt();
3363+
CREATE TRIGGER stmt_trig AFTER INSERT ON batch_cp_upd_test3
3364+
FOR EACH STATEMENT EXECUTE FUNCTION log_stmt();
3365+
3366+
-- This update moves rows from the local partition 'batch_cp_upd_test2' to the
3367+
-- foreign partition 'batch_cp_upd_test1', one that has insert batching
3368+
-- enabled, so a single INSERT for both rows.
3369+
INSERT INTO batch_cp_upd_test VALUES (2), (2);
3370+
UPDATE batch_cp_upd_test t SET a = 1 FROM (VALUES (1), (2)) s(a) WHERE t.a = s.a AND s.a = 2;
3371+
3372+
-- This one moves rows from the local partition 'batch_cp_upd_test2' to the
3373+
-- foreign partition 'batch_cp_upd_test2', one that has insert batching
3374+
-- disabled, so separate INSERTs for the two rows.
3375+
INSERT INTO batch_cp_upd_test VALUES (2), (2);
3376+
UPDATE batch_cp_upd_test t SET a = 3 FROM (VALUES (1), (2)) s(a) WHERE t.a = s.a AND s.a = 2;
3377+
3378+
SELECT tableoid::regclass, * FROM batch_cp_upd_test ORDER BY 1;
33443379

3345-
-- The following moves a row from the local partition to the foreign one
3346-
UPDATE batch_cp_upd_test t SET a = 1 FROM (VALUES (1), (2)) s(a) WHERE t.a = s.a;
3347-
SELECT tableoid::regclass, * FROM batch_cp_upd_test;
3380+
-- Should see 1 INSERT on batch_cp_upd_test1 and 2 on batch_cp_upd_test3 as
3381+
-- described above.
3382+
SELECT * FROM cmdlog ORDER BY 1;
33483383

33493384
-- Clean up
3350-
DROP TABLE batch_table, batch_cp_upd_test, batch_table_p0, batch_table_p1 CASCADE;
3385+
DROP TABLE batch_cp_upd_test;
3386+
DROP TABLE batch_cp_upd_test1;
3387+
DROP TABLE batch_cp_upd_test3;
3388+
DROP TABLE cmdlog;
3389+
DROP FUNCTION log_stmt();
33513390

33523391
-- Use partitioning
33533392
ALTER SERVER loopback OPTIONS (ADD batch_size '10');

src/backend/executor/execPartition.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -1018,8 +1018,7 @@ ExecInitRoutingInfo(ModifyTableState *mtstate,
10181018
*
10191019
* If the FDW does not support batching, we set the batch size to 1.
10201020
*/
1021-
if (mtstate->operation == CMD_INSERT &&
1022-
partRelInfo->ri_FdwRoutine != NULL &&
1021+
if (partRelInfo->ri_FdwRoutine != NULL &&
10231022
partRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
10241023
partRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
10251024
partRelInfo->ri_BatchSize =

0 commit comments

Comments
 (0)