Skip to content

Commit

Permalink
postgres_fdw: Disable batch insertion when there are WCO constraints.
Browse files Browse the repository at this point in the history
When inserting a view referencing a foreign table that has WITH CHECK
OPTION constraints, in single-insert mode postgres_fdw retrieves the
data that was actually inserted on the remote side so that the WITH
CHECK OPTION constraints are enforced with the data locally, but in
batch-insert mode it cannot currently retrieve the data (except for the
row first inserted through the view), resulting in enforcing the WITH
CHECK OPTION constraints with the data passed from the core (except for
the first-inserted row), which led to incorrect results when inserting
into a view referencing a foreign table in which a remote BEFORE ROW
INSERT trigger changes the rows inserted through the view so that they
violate the view's WITH CHECK OPTION constraint.  Also, the query
inserting into the view caused an assertion failure in assert-enabled
builds.

Fix these by disabling batch insertion when inserting into such a view.

Back-patch to v14 where batch insertion was added.

Discussion: https://postgr.es/m/CAPmGK17LpbTZs4m4a_6THP54UBeK9fHvX8aVVA%2BC6yEZDZwQcg%40mail.gmail.com
  • Loading branch information
Etsuro Fujita committed Aug 5, 2022
1 parent 731d514 commit 4a9bc2e
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 2 deletions.
44 changes: 44 additions & 0 deletions contrib/postgres_fdw/expected/postgres_fdw.out
Expand Up @@ -6373,6 +6373,29 @@ SELECT * FROM foreign_tbl;
20 | 30
(1 row)

-- We don't allow batch insert when there are any WCO constraints
ALTER SERVER loopback OPTIONS (ADD batch_size '10');
EXPLAIN (VERBOSE, COSTS OFF)
INSERT INTO rw_view VALUES (0, 15), (0, 5);
QUERY PLAN
--------------------------------------------------------------------------------
Insert on public.foreign_tbl
Remote SQL: INSERT INTO public.base_tbl(a, b) VALUES ($1, $2) RETURNING a, b
Batch Size: 1
-> Values Scan on "*VALUES*"
Output: "*VALUES*".column1, "*VALUES*".column2
(5 rows)

INSERT INTO rw_view VALUES (0, 15), (0, 5); -- should fail
ERROR: new row violates check option for view "rw_view"
DETAIL: Failing row contains (10, 5).
SELECT * FROM foreign_tbl;
a | b
----+----
20 | 30
(1 row)

ALTER SERVER loopback OPTIONS (DROP batch_size);
DROP FOREIGN TABLE foreign_tbl CASCADE;
NOTICE: drop cascades to view rw_view
DROP TRIGGER row_before_insupd_trigger ON base_tbl;
Expand Down Expand Up @@ -6465,6 +6488,27 @@ SELECT * FROM foreign_tbl;
20 | 30
(1 row)

-- We don't allow batch insert when there are any WCO constraints
ALTER SERVER loopback OPTIONS (ADD batch_size '10');
EXPLAIN (VERBOSE, COSTS OFF)
INSERT INTO rw_view VALUES (0, 15), (0, 5);
QUERY PLAN
--------------------------------------------------------
Insert on public.parent_tbl
-> Values Scan on "*VALUES*"
Output: "*VALUES*".column1, "*VALUES*".column2
(3 rows)

INSERT INTO rw_view VALUES (0, 15), (0, 5); -- should fail
ERROR: new row violates check option for view "rw_view"
DETAIL: Failing row contains (10, 5).
SELECT * FROM foreign_tbl;
a | b
----+----
20 | 30
(1 row)

ALTER SERVER loopback OPTIONS (DROP batch_size);
DROP FOREIGN TABLE foreign_tbl CASCADE;
DROP TRIGGER row_before_insupd_trigger ON child_tbl;
DROP TABLE parent_tbl CASCADE;
Expand Down
6 changes: 4 additions & 2 deletions contrib/postgres_fdw/postgres_fdw.c
Expand Up @@ -2043,15 +2043,17 @@ postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
batch_size = get_batch_size_option(resultRelInfo->ri_RelationDesc);

/*
* Disable batching when we have to use RETURNING or there are any
* BEFORE/AFTER ROW INSERT triggers on the foreign table.
* Disable batching when we have to use RETURNING, there are any
* BEFORE/AFTER ROW INSERT triggers on the foreign table, or there are any
* WITH CHECK OPTION constraints from parent views.
*
* When there are any BEFORE ROW INSERT triggers on the table, we can't
* support it, because such triggers might query the table we're inserting
* into and act differently if the tuples that have already been processed
* and prepared for insertion are not there.
*/
if (resultRelInfo->ri_projectReturning != NULL ||
resultRelInfo->ri_WithCheckOptions != NIL ||
(resultRelInfo->ri_TrigDesc &&
(resultRelInfo->ri_TrigDesc->trig_insert_before_row ||
resultRelInfo->ri_TrigDesc->trig_insert_after_row)))
Expand Down
16 changes: 16 additions & 0 deletions contrib/postgres_fdw/sql/postgres_fdw.sql
Expand Up @@ -1441,6 +1441,14 @@ UPDATE rw_view SET b = b + 15;
UPDATE rw_view SET b = b + 15; -- ok
SELECT * FROM foreign_tbl;

-- We don't allow batch insert when there are any WCO constraints
ALTER SERVER loopback OPTIONS (ADD batch_size '10');
EXPLAIN (VERBOSE, COSTS OFF)
INSERT INTO rw_view VALUES (0, 15), (0, 5);
INSERT INTO rw_view VALUES (0, 15), (0, 5); -- should fail
SELECT * FROM foreign_tbl;
ALTER SERVER loopback OPTIONS (DROP batch_size);

DROP FOREIGN TABLE foreign_tbl CASCADE;
DROP TRIGGER row_before_insupd_trigger ON base_tbl;
DROP TABLE base_tbl;
Expand Down Expand Up @@ -1479,6 +1487,14 @@ UPDATE rw_view SET b = b + 15;
UPDATE rw_view SET b = b + 15; -- ok
SELECT * FROM foreign_tbl;

-- We don't allow batch insert when there are any WCO constraints
ALTER SERVER loopback OPTIONS (ADD batch_size '10');
EXPLAIN (VERBOSE, COSTS OFF)
INSERT INTO rw_view VALUES (0, 15), (0, 5);
INSERT INTO rw_view VALUES (0, 15), (0, 5); -- should fail
SELECT * FROM foreign_tbl;
ALTER SERVER loopback OPTIONS (DROP batch_size);

DROP FOREIGN TABLE foreign_tbl CASCADE;
DROP TRIGGER row_before_insupd_trigger ON child_tbl;
DROP TABLE parent_tbl CASCADE;
Expand Down

0 comments on commit 4a9bc2e

Please sign in to comment.