agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH v5 1/2] Make more clear the computation of min/max IO.. 4+ messages / 2 participants [nested] [flat]
* [PATCH v5 1/2] Make more clear the computation of min/max IO.. @ 2020-01-09 01:23 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 4+ messages in thread From: Justin Pryzby @ 2020-01-09 01:23 UTC (permalink / raw) ..and specifically the double use and effect of correlation. Avoid re-use of the "pages_fetched" variable --- src/backend/optimizer/path/costsize.c | 47 ++++++++++++++------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index b5a0033721..bdc23a075f 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -491,12 +491,13 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, csquared; double spc_seq_page_cost, spc_random_page_cost; - Cost min_IO_cost, + double min_pages_fetched, /* The min and max page count based on index correlation */ + max_pages_fetched; + Cost min_IO_cost, /* The min and max cost based on index correlation */ max_IO_cost; QualCost qpqual_cost; Cost cpu_per_tuple; double tuples_fetched; - double pages_fetched; double rand_heap_pages; double index_pages; @@ -579,7 +580,8 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, * (just after a CLUSTER, for example), the number of pages fetched should * be exactly selectivity * table_size. What's more, all but the first * will be sequential fetches, not the random fetches that occur in the - * uncorrelated case. So if the number of pages is more than 1, we + * uncorrelated case (the index is expected to read fewer pages, *and* each + * page read is cheaper). So if the number of pages is more than 1, we * ought to charge * spc_random_page_cost + (pages_fetched - 1) * spc_seq_page_cost * For partially-correlated indexes, we ought to charge somewhere between @@ -604,17 +606,17 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, * pro-rate the costs for one scan. In this case we assume all the * fetches are random accesses. */ - pages_fetched = index_pages_fetched(tuples_fetched * loop_count, + max_pages_fetched = index_pages_fetched(tuples_fetched * loop_count, baserel->pages, (double) index->pages, root); if (indexonly) - pages_fetched = ceil(pages_fetched * (1.0 - baserel->allvisfrac)); + max_pages_fetched = ceil(max_pages_fetched * (1.0 - baserel->allvisfrac)); - rand_heap_pages = pages_fetched; + rand_heap_pages = max_pages_fetched; - max_IO_cost = (pages_fetched * spc_random_page_cost) / loop_count; + max_IO_cost = (max_pages_fetched * spc_random_page_cost) / loop_count; /* * In the perfectly correlated case, the number of pages touched by @@ -626,17 +628,17 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, * where such a plan is actually interesting, only one page would get * fetched per scan anyway, so it shouldn't matter much.) */ - pages_fetched = ceil(indexSelectivity * (double) baserel->pages); + min_pages_fetched = ceil(indexSelectivity * (double) baserel->pages); - pages_fetched = index_pages_fetched(pages_fetched * loop_count, + min_pages_fetched = index_pages_fetched(min_pages_fetched * loop_count, baserel->pages, (double) index->pages, root); if (indexonly) - pages_fetched = ceil(pages_fetched * (1.0 - baserel->allvisfrac)); + min_pages_fetched = ceil(min_pages_fetched * (1.0 - baserel->allvisfrac)); - min_IO_cost = (pages_fetched * spc_random_page_cost) / loop_count; + min_IO_cost = (min_pages_fetched * spc_random_page_cost) / loop_count; } else { @@ -644,30 +646,31 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, * Normal case: apply the Mackert and Lohman formula, and then * interpolate between that and the correlation-derived result. */ - pages_fetched = index_pages_fetched(tuples_fetched, + + /* For the perfectly uncorrelated case (csquared=0) */ + max_pages_fetched = index_pages_fetched(tuples_fetched, baserel->pages, (double) index->pages, root); if (indexonly) - pages_fetched = ceil(pages_fetched * (1.0 - baserel->allvisfrac)); + max_pages_fetched = ceil(max_pages_fetched * (1.0 - baserel->allvisfrac)); - rand_heap_pages = pages_fetched; + rand_heap_pages = max_pages_fetched; - /* max_IO_cost is for the perfectly uncorrelated case (csquared=0) */ - max_IO_cost = pages_fetched * spc_random_page_cost; + max_IO_cost = max_pages_fetched * spc_random_page_cost; - /* min_IO_cost is for the perfectly correlated case (csquared=1) */ - pages_fetched = ceil(indexSelectivity * (double) baserel->pages); + /* For the perfectly correlated case (csquared=1) */ + min_pages_fetched = ceil(indexSelectivity * (double) baserel->pages); if (indexonly) - pages_fetched = ceil(pages_fetched * (1.0 - baserel->allvisfrac)); + min_pages_fetched = ceil(min_pages_fetched * (1.0 - baserel->allvisfrac)); - if (pages_fetched > 0) + if (min_pages_fetched > 0) { min_IO_cost = spc_random_page_cost; - if (pages_fetched > 1) - min_IO_cost += (pages_fetched - 1) * spc_seq_page_cost; + if (min_pages_fetched > 1) + min_IO_cost += (min_pages_fetched - 1) * spc_seq_page_cost; } else min_IO_cost = 0; -- 2.17.0 --16qp2B0xu0fRvRD7 Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v5-0002-Use-correlation-statistic-in-costing-bitmap-scans.patch" ^ permalink raw reply [nested|flat] 4+ messages in thread
* [PATCH v6 1/2] Make more clear the computation of min/max IO.. @ 2020-01-09 01:23 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 4+ messages in thread From: Justin Pryzby @ 2020-01-09 01:23 UTC (permalink / raw) ..and specifically the double use and effect of correlation. Avoid re-use of the "pages_fetched" variable --- src/backend/optimizer/path/costsize.c | 47 ++++++++++++++------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index f1dfdc1a4a..083448def7 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -503,12 +503,13 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, csquared; double spc_seq_page_cost, spc_random_page_cost; - Cost min_IO_cost, + double min_pages_fetched, /* The min and max page count based on index correlation */ + max_pages_fetched; + Cost min_IO_cost, /* The min and max cost based on index correlation */ max_IO_cost; QualCost qpqual_cost; Cost cpu_per_tuple; double tuples_fetched; - double pages_fetched; double rand_heap_pages; double index_pages; @@ -591,7 +592,8 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, * (just after a CLUSTER, for example), the number of pages fetched should * be exactly selectivity * table_size. What's more, all but the first * will be sequential fetches, not the random fetches that occur in the - * uncorrelated case. So if the number of pages is more than 1, we + * uncorrelated case (the index is expected to read fewer pages, *and* each + * page read is cheaper). So if the number of pages is more than 1, we * ought to charge * spc_random_page_cost + (pages_fetched - 1) * spc_seq_page_cost * For partially-correlated indexes, we ought to charge somewhere between @@ -616,17 +618,17 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, * pro-rate the costs for one scan. In this case we assume all the * fetches are random accesses. */ - pages_fetched = index_pages_fetched(tuples_fetched * loop_count, + max_pages_fetched = index_pages_fetched(tuples_fetched * loop_count, baserel->pages, (double) index->pages, root); if (indexonly) - pages_fetched = ceil(pages_fetched * (1.0 - baserel->allvisfrac)); + max_pages_fetched = ceil(max_pages_fetched * (1.0 - baserel->allvisfrac)); - rand_heap_pages = pages_fetched; + rand_heap_pages = max_pages_fetched; - max_IO_cost = (pages_fetched * spc_random_page_cost) / loop_count; + max_IO_cost = (max_pages_fetched * spc_random_page_cost) / loop_count; /* * In the perfectly correlated case, the number of pages touched by @@ -638,17 +640,17 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, * where such a plan is actually interesting, only one page would get * fetched per scan anyway, so it shouldn't matter much.) */ - pages_fetched = ceil(indexSelectivity * (double) baserel->pages); + min_pages_fetched = ceil(indexSelectivity * (double) baserel->pages); - pages_fetched = index_pages_fetched(pages_fetched * loop_count, + min_pages_fetched = index_pages_fetched(min_pages_fetched * loop_count, baserel->pages, (double) index->pages, root); if (indexonly) - pages_fetched = ceil(pages_fetched * (1.0 - baserel->allvisfrac)); + min_pages_fetched = ceil(min_pages_fetched * (1.0 - baserel->allvisfrac)); - min_IO_cost = (pages_fetched * spc_random_page_cost) / loop_count; + min_IO_cost = (min_pages_fetched * spc_random_page_cost) / loop_count; } else { @@ -656,30 +658,31 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, * Normal case: apply the Mackert and Lohman formula, and then * interpolate between that and the correlation-derived result. */ - pages_fetched = index_pages_fetched(tuples_fetched, + + /* For the perfectly uncorrelated case (csquared=0) */ + max_pages_fetched = index_pages_fetched(tuples_fetched, baserel->pages, (double) index->pages, root); if (indexonly) - pages_fetched = ceil(pages_fetched * (1.0 - baserel->allvisfrac)); + max_pages_fetched = ceil(max_pages_fetched * (1.0 - baserel->allvisfrac)); - rand_heap_pages = pages_fetched; + rand_heap_pages = max_pages_fetched; - /* max_IO_cost is for the perfectly uncorrelated case (csquared=0) */ - max_IO_cost = pages_fetched * spc_random_page_cost; + max_IO_cost = max_pages_fetched * spc_random_page_cost; - /* min_IO_cost is for the perfectly correlated case (csquared=1) */ - pages_fetched = ceil(indexSelectivity * (double) baserel->pages); + /* For the perfectly correlated case (csquared=1) */ + min_pages_fetched = ceil(indexSelectivity * (double) baserel->pages); if (indexonly) - pages_fetched = ceil(pages_fetched * (1.0 - baserel->allvisfrac)); + min_pages_fetched = ceil(min_pages_fetched * (1.0 - baserel->allvisfrac)); - if (pages_fetched > 0) + if (min_pages_fetched > 0) { min_IO_cost = spc_random_page_cost; - if (pages_fetched > 1) - min_IO_cost += (pages_fetched - 1) * spc_seq_page_cost; + if (min_pages_fetched > 1) + min_IO_cost += (min_pages_fetched - 1) * spc_seq_page_cost; } else min_IO_cost = 0; -- 2.17.0 --VV4b6MQE+OnNyhkM Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v6-0002-Use-correlation-statistic-in-costing-bitmap-scans.patch" ^ permalink raw reply [nested|flat] 4+ messages in thread
* [PATCH v4 1/2] Make more clear the computation of min/max IO.. @ 2020-01-09 01:23 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 4+ messages in thread From: Justin Pryzby @ 2020-01-09 01:23 UTC (permalink / raw) ..and specifically the double use and effect of correlation. Avoid re-use of the "pages_fetched" variable --- src/backend/optimizer/path/costsize.c | 47 +++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index b5a0033..bdc23a0 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -491,12 +491,13 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, csquared; double spc_seq_page_cost, spc_random_page_cost; - Cost min_IO_cost, + double min_pages_fetched, /* The min and max page count based on index correlation */ + max_pages_fetched; + Cost min_IO_cost, /* The min and max cost based on index correlation */ max_IO_cost; QualCost qpqual_cost; Cost cpu_per_tuple; double tuples_fetched; - double pages_fetched; double rand_heap_pages; double index_pages; @@ -579,7 +580,8 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, * (just after a CLUSTER, for example), the number of pages fetched should * be exactly selectivity * table_size. What's more, all but the first * will be sequential fetches, not the random fetches that occur in the - * uncorrelated case. So if the number of pages is more than 1, we + * uncorrelated case (the index is expected to read fewer pages, *and* each + * page read is cheaper). So if the number of pages is more than 1, we * ought to charge * spc_random_page_cost + (pages_fetched - 1) * spc_seq_page_cost * For partially-correlated indexes, we ought to charge somewhere between @@ -604,17 +606,17 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, * pro-rate the costs for one scan. In this case we assume all the * fetches are random accesses. */ - pages_fetched = index_pages_fetched(tuples_fetched * loop_count, + max_pages_fetched = index_pages_fetched(tuples_fetched * loop_count, baserel->pages, (double) index->pages, root); if (indexonly) - pages_fetched = ceil(pages_fetched * (1.0 - baserel->allvisfrac)); + max_pages_fetched = ceil(max_pages_fetched * (1.0 - baserel->allvisfrac)); - rand_heap_pages = pages_fetched; + rand_heap_pages = max_pages_fetched; - max_IO_cost = (pages_fetched * spc_random_page_cost) / loop_count; + max_IO_cost = (max_pages_fetched * spc_random_page_cost) / loop_count; /* * In the perfectly correlated case, the number of pages touched by @@ -626,17 +628,17 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, * where such a plan is actually interesting, only one page would get * fetched per scan anyway, so it shouldn't matter much.) */ - pages_fetched = ceil(indexSelectivity * (double) baserel->pages); + min_pages_fetched = ceil(indexSelectivity * (double) baserel->pages); - pages_fetched = index_pages_fetched(pages_fetched * loop_count, + min_pages_fetched = index_pages_fetched(min_pages_fetched * loop_count, baserel->pages, (double) index->pages, root); if (indexonly) - pages_fetched = ceil(pages_fetched * (1.0 - baserel->allvisfrac)); + min_pages_fetched = ceil(min_pages_fetched * (1.0 - baserel->allvisfrac)); - min_IO_cost = (pages_fetched * spc_random_page_cost) / loop_count; + min_IO_cost = (min_pages_fetched * spc_random_page_cost) / loop_count; } else { @@ -644,30 +646,31 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, * Normal case: apply the Mackert and Lohman formula, and then * interpolate between that and the correlation-derived result. */ - pages_fetched = index_pages_fetched(tuples_fetched, + + /* For the perfectly uncorrelated case (csquared=0) */ + max_pages_fetched = index_pages_fetched(tuples_fetched, baserel->pages, (double) index->pages, root); if (indexonly) - pages_fetched = ceil(pages_fetched * (1.0 - baserel->allvisfrac)); + max_pages_fetched = ceil(max_pages_fetched * (1.0 - baserel->allvisfrac)); - rand_heap_pages = pages_fetched; + rand_heap_pages = max_pages_fetched; - /* max_IO_cost is for the perfectly uncorrelated case (csquared=0) */ - max_IO_cost = pages_fetched * spc_random_page_cost; + max_IO_cost = max_pages_fetched * spc_random_page_cost; - /* min_IO_cost is for the perfectly correlated case (csquared=1) */ - pages_fetched = ceil(indexSelectivity * (double) baserel->pages); + /* For the perfectly correlated case (csquared=1) */ + min_pages_fetched = ceil(indexSelectivity * (double) baserel->pages); if (indexonly) - pages_fetched = ceil(pages_fetched * (1.0 - baserel->allvisfrac)); + min_pages_fetched = ceil(min_pages_fetched * (1.0 - baserel->allvisfrac)); - if (pages_fetched > 0) + if (min_pages_fetched > 0) { min_IO_cost = spc_random_page_cost; - if (pages_fetched > 1) - min_IO_cost += (pages_fetched - 1) * spc_seq_page_cost; + if (min_pages_fetched > 1) + min_IO_cost += (min_pages_fetched - 1) * spc_seq_page_cost; } else min_IO_cost = 0; -- 2.7.4 --gBdJBemW82xJqIAr Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v4-0002-Use-correlation-statistic-in-costing-bitmap-scans.patch" ^ permalink raw reply [nested|flat] 4+ messages in thread
* [PATCH v3 1/2] Test exposing bug when foreign table points to a partitioned table @ 2025-07-15 10:45 Jehan-Guillaume de Rorthais <[email protected]> 0 siblings, 0 replies; 4+ messages in thread From: Jehan-Guillaume de Rorthais @ 2025-07-15 10:45 UTC (permalink / raw) When a foreign table points to a partitioned table or an inheritance parent on the foreign server, a non-direct DML can affect multiple rows when only one row is intended to be affected. This happens because postgres_fdw uses only ctid to identify a row to work on. Though ctid uniquely identifies a row in a single table, in a partitioned table or in an inheritance hierarchy, there can be be multiple rows, in different partitions, with the same ctid. So DML statement sent to the foreign server by postgres_fdw ends up affecting more than one rows, only one of which is intended to be affected. This commit adds testcases to show the problem. A subsequent commit would have a fix to the problem. Author: Ashutosh Bapat <[email protected]> Reviewed-by: Kyotaro Horiguchi <[email protected]> Discussion: https://www.postgresql.org/message-id/flat/CAFjFpRfcgwsHRmpvoOK-GUQi-n8MgAS%2BOxcQo%3DaBDn1COywmcg%4... Rebased by Jehan-Guillaume de Rorthais <[email protected]> --- .../postgres_fdw/expected/postgres_fdw.out | 125 ++++++++++++++++++ contrib/postgres_fdw/sql/postgres_fdw.sql | 58 ++++++++ 2 files changed, 183 insertions(+) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 2185b42bb4f..62019eaa881 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -8954,6 +8954,131 @@ drop foreign table remt2; drop table loct1; drop table loct2; drop table parent; +-- test DML statement on a foreign table pointing to an inheritance hierarchy +-- on the remote server +CREATE TABLE a(aa TEXT); +ALTER TABLE a SET (autovacuum_enabled = 'false'); +CREATE TABLE b() INHERITS(a); +ALTER TABLE b SET (autovacuum_enabled = 'false'); +INSERT INTO a(aa) VALUES('aaa'); +INSERT INTO b(aa) VALUES('bbb'); +CREATE FOREIGN TABLE fa (aa TEXT) SERVER loopback OPTIONS (table_name 'a'); +SELECT tableoid::regclass, ctid, * FROM fa; + tableoid | ctid | aa +----------+-------+----- + fa | (0,1) | aaa + fa | (0,1) | bbb +(2 rows) + +-- use random() so that DML statement is not pushed down to the foreign +-- server +EXPLAIN (VERBOSE, COSTS OFF) +UPDATE fa SET aa = (CASE WHEN random() <= 1 THEN 'zzzz' ELSE NULL END) WHERE aa = 'aaa'; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------- + Update on public.fa + Remote SQL: UPDATE public.a SET aa = $2 WHERE ctid = $1 + -> Foreign Scan on public.fa + Output: CASE WHEN (random() <= '1'::double precision) THEN 'zzzz'::text ELSE NULL::text END, ctid, fa.* + Remote SQL: SELECT aa, ctid FROM public.a WHERE ((aa = 'aaa')) FOR UPDATE +(5 rows) + +UPDATE fa SET aa = (CASE WHEN random() <= 1 THEN 'zzzz' ELSE NULL END) WHERE aa = 'aaa'; +SELECT tableoid::regclass, ctid, * FROM fa; + tableoid | ctid | aa +----------+-------+------ + fa | (0,2) | zzzz + fa | (0,1) | bbb +(2 rows) + +-- repopulate tables so that we have rows with same ctid +TRUNCATE a, b; +INSERT INTO a(aa) VALUES('aaa'); +INSERT INTO b(aa) VALUES('bbb'); +EXPLAIN (VERBOSE, COSTS OFF) +DELETE FROM fa WHERE aa = (CASE WHEN random() <= 1 THEN 'aaa' ELSE 'bbb' END); + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- + Delete on public.fa + Remote SQL: DELETE FROM public.a WHERE ctid = $1 + -> Foreign Scan on public.fa + Output: ctid + Filter: (fa.aa = CASE WHEN (random() <= '1'::double precision) THEN 'aaa'::text ELSE 'bbb'::text END) + Remote SQL: SELECT aa, ctid FROM public.a FOR UPDATE +(6 rows) + +DELETE FROM fa WHERE aa = (CASE WHEN random() <= 1 THEN 'aaa' ELSE 'bbb' END); +SELECT tableoid::regclass, ctid, * FROM fa; + tableoid | ctid | aa +----------+-------+----- + fa | (0,1) | bbb +(1 row) + +-- cleanup +DROP FOREIGN TABLE fa; +DROP TABLE a CASCADE; +NOTICE: drop cascades to table b +-- =================================================================== +-- test foreign table pointing to a remote partitioned table +-- =================================================================== +-- test DML statement on foreign table pointing to a foreign partitioned table +CREATE TABLE plt (a int, b int) PARTITION BY LIST(a); +CREATE TABLE plt_p1 PARTITION OF plt FOR VALUES IN (1); +CREATE TABLE plt_p2 PARTITION OF plt FOR VALUES IN (2); +INSERT INTO plt VALUES (1, 1), (2, 2); +CREATE FOREIGN TABLE fplt (a int, b int) SERVER loopback OPTIONS (table_name 'plt'); +SELECT tableoid::regclass, ctid, * FROM fplt; + tableoid | ctid | a | b +----------+-------+---+--- + fplt | (0,1) | 1 | 1 + fplt | (0,1) | 2 | 2 +(2 rows) + +-- use random() so that DML statement is not pushed down to the foreign +-- server +EXPLAIN (VERBOSE, COSTS OFF) +UPDATE fplt SET b = (CASE WHEN random() <= 1 THEN 10 ELSE 20 END) WHERE a = 1; + QUERY PLAN +------------------------------------------------------------------------------------------------- + Update on public.fplt + Remote SQL: UPDATE public.plt SET b = $2 WHERE ctid = $1 + -> Foreign Scan on public.fplt + Output: CASE WHEN (random() <= '1'::double precision) THEN 10 ELSE 20 END, ctid, fplt.* + Remote SQL: SELECT a, b, ctid FROM public.plt WHERE ((a = 1)) FOR UPDATE +(5 rows) + +UPDATE fplt SET b = (CASE WHEN random() <= 1 THEN 10 ELSE 20 END) WHERE a = 1; +SELECT tableoid::regclass, ctid, * FROM fplt; + tableoid | ctid | a | b +----------+-------+---+---- + fplt | (0,2) | 1 | 10 + fplt | (0,1) | 2 | 2 +(2 rows) + +-- repopulate partitioned table so that we have rows with same ctid +TRUNCATE plt; +INSERT INTO plt VALUES (1, 1), (2, 2); +EXPLAIN (VERBOSE, COSTS OFF) +DELETE FROM fplt WHERE a = (CASE WHEN random() <= 1 THEN 1 ELSE 10 END); + QUERY PLAN +--------------------------------------------------------------------------------------------- + Delete on public.fplt + Remote SQL: DELETE FROM public.plt WHERE ctid = $1 + -> Foreign Scan on public.fplt + Output: ctid + Filter: (fplt.a = CASE WHEN (random() <= '1'::double precision) THEN 1 ELSE 10 END) + Remote SQL: SELECT a, ctid FROM public.plt FOR UPDATE +(6 rows) + +DELETE FROM fplt WHERE a = (CASE WHEN random() <= 1 THEN 1 ELSE 10 END); +SELECT tableoid::regclass, ctid, * FROM fplt; + tableoid | ctid | a | b +----------+-------+---+--- + fplt | (0,1) | 2 | 2 +(1 row) + +DROP TABLE plt; +DROP FOREIGN TABLE fplt; -- =================================================================== -- test tuple routing for foreign-table partitions -- =================================================================== diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql index e534b40de3c..ae3777b7edb 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -2494,6 +2494,64 @@ drop table loct1; drop table loct2; drop table parent; +-- test DML statement on a foreign table pointing to an inheritance hierarchy +-- on the remote server +CREATE TABLE a(aa TEXT); +ALTER TABLE a SET (autovacuum_enabled = 'false'); +CREATE TABLE b() INHERITS(a); +ALTER TABLE b SET (autovacuum_enabled = 'false'); +INSERT INTO a(aa) VALUES('aaa'); +INSERT INTO b(aa) VALUES('bbb'); +CREATE FOREIGN TABLE fa (aa TEXT) SERVER loopback OPTIONS (table_name 'a'); + +SELECT tableoid::regclass, ctid, * FROM fa; +-- use random() so that DML statement is not pushed down to the foreign +-- server +EXPLAIN (VERBOSE, COSTS OFF) +UPDATE fa SET aa = (CASE WHEN random() <= 1 THEN 'zzzz' ELSE NULL END) WHERE aa = 'aaa'; +UPDATE fa SET aa = (CASE WHEN random() <= 1 THEN 'zzzz' ELSE NULL END) WHERE aa = 'aaa'; +SELECT tableoid::regclass, ctid, * FROM fa; +-- repopulate tables so that we have rows with same ctid +TRUNCATE a, b; +INSERT INTO a(aa) VALUES('aaa'); +INSERT INTO b(aa) VALUES('bbb'); +EXPLAIN (VERBOSE, COSTS OFF) +DELETE FROM fa WHERE aa = (CASE WHEN random() <= 1 THEN 'aaa' ELSE 'bbb' END); +DELETE FROM fa WHERE aa = (CASE WHEN random() <= 1 THEN 'aaa' ELSE 'bbb' END); +SELECT tableoid::regclass, ctid, * FROM fa; + +-- cleanup +DROP FOREIGN TABLE fa; +DROP TABLE a CASCADE; + +-- =================================================================== +-- test foreign table pointing to a remote partitioned table +-- =================================================================== + +-- test DML statement on foreign table pointing to a foreign partitioned table +CREATE TABLE plt (a int, b int) PARTITION BY LIST(a); +CREATE TABLE plt_p1 PARTITION OF plt FOR VALUES IN (1); +CREATE TABLE plt_p2 PARTITION OF plt FOR VALUES IN (2); +INSERT INTO plt VALUES (1, 1), (2, 2); +CREATE FOREIGN TABLE fplt (a int, b int) SERVER loopback OPTIONS (table_name 'plt'); +SELECT tableoid::regclass, ctid, * FROM fplt; +-- use random() so that DML statement is not pushed down to the foreign +-- server +EXPLAIN (VERBOSE, COSTS OFF) +UPDATE fplt SET b = (CASE WHEN random() <= 1 THEN 10 ELSE 20 END) WHERE a = 1; +UPDATE fplt SET b = (CASE WHEN random() <= 1 THEN 10 ELSE 20 END) WHERE a = 1; +SELECT tableoid::regclass, ctid, * FROM fplt; +-- repopulate partitioned table so that we have rows with same ctid +TRUNCATE plt; +INSERT INTO plt VALUES (1, 1), (2, 2); +EXPLAIN (VERBOSE, COSTS OFF) +DELETE FROM fplt WHERE a = (CASE WHEN random() <= 1 THEN 1 ELSE 10 END); +DELETE FROM fplt WHERE a = (CASE WHEN random() <= 1 THEN 1 ELSE 10 END); +SELECT tableoid::regclass, ctid, * FROM fplt; + +DROP TABLE plt; +DROP FOREIGN TABLE fplt; + -- =================================================================== -- test tuple routing for foreign-table partitions -- =================================================================== -- 2.50.0 --MP_/4ZRYdF7Ah.pt5w65PuZdaPz Content-Type: text/x-patch Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=v3-0002-Error-out-if-one-iteration-of-non-direct-DML-affe.patch ^ permalink raw reply [nested|flat] 4+ messages in thread
end of thread, other threads:[~2025-07-15 10:45 UTC | newest] Thread overview: 4+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2020-01-09 01:23 [PATCH v5 1/2] Make more clear the computation of min/max IO.. Justin Pryzby <[email protected]> 2020-01-09 01:23 [PATCH v6 1/2] Make more clear the computation of min/max IO.. Justin Pryzby <[email protected]> 2020-01-09 01:23 [PATCH v4 1/2] Make more clear the computation of min/max IO.. Justin Pryzby <[email protected]> 2025-07-15 10:45 [PATCH v3 1/2] Test exposing bug when foreign table points to a partitioned table Jehan-Guillaume de Rorthais <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox