From 8ae4f3a8230ebf5cdfa0900e030abb8f535aac69 Mon Sep 17 00:00:00 2001 From: Jinbao Chen Date: Sun, 30 Jun 2024 10:56:17 -0700 Subject: [PATCH 1/5] Revert "Remove num_segments option from the foreign table layer" This reverts commit afbeb68a2cf173b05d7ef1a3eab1b16fc3b2ea94. --- src/backend/commands/foreigncmds.c | 1 + src/backend/foreign/foreign.c | 9 +++++++- src/backend/optimizer/util/pathnode.c | 15 +++--------- src/backend/optimizer/util/plancat.c | 3 +++ src/include/foreign/foreign.h | 3 ++- src/include/nodes/pathnodes.h | 3 ++- src/test/regress/expected/gp_foreign_data.out | 20 +++++++++------- src/test/regress/sql/gp_foreign_data.sql | 23 +++++++------------ 8 files changed, 39 insertions(+), 38 deletions(-) diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index a61b0b71d35..b39b2c69b43 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -191,6 +191,7 @@ transformGenericOptions(Oid catalogId, if (catalogId != UserMappingRelationId) { SeparateOutMppExecute(&resultOptions); + SeparateOutNumSegments(&resultOptions); } if (OidIsValid(fdwvalidator)) diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index 4e55bf5d606..ed619d47348 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -366,13 +366,20 @@ GetForeignTable(Oid relid) else ft->options = untransformRelOptions(datum); + ForeignServer *server = GetForeignServer(ft->serverid); + ft->exec_location = SeparateOutMppExecute(&ft->options); if (ft->exec_location == FTEXECLOCATION_NOT_DEFINED) { - ForeignServer *server = GetForeignServer(ft->serverid); ft->exec_location = server->exec_location; } + ft->num_segments = SeparateOutNumSegments(&ft->options); + if (ft->num_segments <= 0) + { + ft->num_segments = server->num_segments; + } + ReleaseSysCache(tp); return ft; diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 44daa2b1278..fab5b350db5 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -3639,7 +3639,7 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, pathnode->path.total_cost = total_cost; pathnode->path.pathkeys = pathkeys; - if (Gp_role == GP_ROLE_DISPATCH) + switch (rel->exec_location) { ForeignServer *server = NULL; @@ -3649,11 +3649,7 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, CdbPathLocus_MakeGeneral(&(pathnode->path.locus)); break; case FTEXECLOCATION_ALL_SEGMENTS: - server = GetForeignServer(rel->serverid); - if (server) - CdbPathLocus_MakeStrewn(&(pathnode->path.locus), server->num_segments, 0); - else - CdbPathLocus_MakeStrewn(&(pathnode->path.locus), getgpsegmentCount(), 0); + CdbPathLocus_MakeStrewn(&(pathnode->path.locus), rel->num_segments, 0); break; case FTEXECLOCATION_COORDINATOR: CdbPathLocus_MakeEntry(&(pathnode->path.locus)); @@ -3718,18 +3714,13 @@ create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel, pathnode->path.total_cost = total_cost; pathnode->path.pathkeys = pathkeys; - ForeignServer *server = NULL; switch (rel->exec_location) { case FTEXECLOCATION_ANY: CdbPathLocus_MakeGeneral(&(pathnode->path.locus)); break; case FTEXECLOCATION_ALL_SEGMENTS: - server = GetForeignServer(rel->serverid); - if (server) - CdbPathLocus_MakeStrewn(&(pathnode->path.locus), server->num_segments, 0); - else - CdbPathLocus_MakeStrewn(&(pathnode->path.locus), getgpsegmentCount(), 0); + CdbPathLocus_MakeStrewn(&(pathnode->path.locus), rel->num_segments, 0); break; case FTEXECLOCATION_COORDINATOR: CdbPathLocus_MakeEntry(&(pathnode->path.locus)); diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 95662fd05f8..1e64cfb06ee 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -60,6 +60,7 @@ #include "cdb/cdbappendonlyam.h" #include "cdb/cdbrelsize.h" +#include "cdb/cdbutil.h" #include "catalog/pg_appendonly.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_inherits.h" @@ -471,12 +472,14 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation)); rel->fdwroutine = GetFdwRoutineForRelation(relation, true); rel->exec_location = GetForeignTable(RelationGetRelid(relation))->exec_location; + rel->num_segments = GetForeignTable(RelationGetRelid(relation))->num_segments; } else { rel->serverid = InvalidOid; rel->fdwroutine = NULL; rel->exec_location = FTEXECLOCATION_NOT_DEFINED; + rel->num_segments = getgpsegmentCount(); } /* Collect info about relation's foreign keys, if relevant */ diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h index 04405241b0c..c8226060067 100644 --- a/src/include/foreign/foreign.h +++ b/src/include/foreign/foreign.h @@ -59,7 +59,8 @@ typedef struct ForeignTable Oid relid; /* relation Oid */ Oid serverid; /* server Oid */ List *options; /* ftoptions as DefElem list */ - char exec_location; /* execute on COORDINATOR, ANY or ALL SEGMENTS, Greenplum MPP specific */ + char exec_location; /* execute on COORDINATOR, ANY or ALL SEGMENTS, Cloudberry MPP specific */ + int32 num_segments; /* the number of segments of the foreign table */ } ForeignTable; /* Flags for GetForeignServerExtended */ diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 6cb36977a2b..695b4dbc5e3 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -924,7 +924,8 @@ typedef struct RelOptInfo Oid serverid; /* identifies server for the table or join */ Oid userid; /* identifies user to check access as */ bool useridiscurrent; /* join is only valid for current user */ - char exec_location; /* execute on MASTER, ANY or ALL SEGMENTS, Greenplum MPP specific */ + char exec_location; /* execute on MASTER, ANY or ALL SEGMENTS, Cloudberry MPP specific */ + int32 num_segments; /* number of segments, Cloudberry MPP specific */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; void *fdw_private; diff --git a/src/test/regress/expected/gp_foreign_data.out b/src/test/regress/expected/gp_foreign_data.out index 500d395404d..c4dbd5bea93 100644 --- a/src/test/regress/expected/gp_foreign_data.out +++ b/src/test/regress/expected/gp_foreign_data.out @@ -26,14 +26,18 @@ CREATE FOREIGN TABLE ft3 ( CREATE FOREIGN TABLE ft4 ( c1 int ) SERVER s0 OPTIONS (delimiter ',', mpp_execute 'all segments'); --- CREATE FOREIGN SERVER WITH num_segments -CREATE SERVER s1 FOREIGN DATA WRAPPER dummy OPTIONS (num_segments '5'); --- CHECK FOREIGN SERVER's OPTIONS -SELECT srvoptions FROM pg_foreign_server WHERE srvname = 's1'; - srvoptions ------------------- - {num_segments=5} -(1 row) +-- Test num_segments option +CREATE SERVER s1 FOREIGN DATA WRAPPER dummy OPTIONS (num_segments '3'); +CREATE FOREIGN TABLE ft5 ( + c1 int +) SERVER s1 OPTIONS (delimiter ',', mpp_execute 'all segments', num_segments '5'); +\d+ ft5 + Foreign table "public.ft5" + Column | Type | Collation | Nullable | Default | FDW options | Storage | Stats target | Description +--------+---------+-----------+----------+---------+-------------+---------+--------------+------------- + c1 | integer | | | | | plain | | +Server: s1 +FDW options: (delimiter ',', mpp_execute 'all segments', num_segments '5') --start_ignore DROP FOREIGN DATA WRAPPER dummy CASCADE; diff --git a/src/test/regress/sql/gp_foreign_data.sql b/src/test/regress/sql/gp_foreign_data.sql index 23c630797da..a26f96f9d50 100644 --- a/src/test/regress/sql/gp_foreign_data.sql +++ b/src/test/regress/sql/gp_foreign_data.sql @@ -2,12 +2,6 @@ -- Test foreign-data wrapper and server management. Cloudberry MPP specific -- --- start_ignore -DROP SERVER s0 CASCADE; -DROP SERVER s1 CASCADE; -DROP FOREIGN DATA WRAPPER dummy CASCADE; --- end_ignore - CREATE FOREIGN DATA WRAPPER dummy; COMMENT ON FOREIGN DATA WRAPPER dummy IS 'useless'; @@ -27,14 +21,13 @@ CREATE FOREIGN TABLE ft4 ( c1 int ) SERVER s0 OPTIONS (delimiter ',', mpp_execute 'all segments'); --- CREATE FOREIGN SERVER WITH num_segments -CREATE SERVER s1 FOREIGN DATA WRAPPER dummy OPTIONS (num_segments '5'); - --- CHECK FOREIGN SERVER's OPTIONS -SELECT srvoptions FROM pg_foreign_server WHERE srvname = 's1'; +-- Test num_segments option +CREATE SERVER s1 FOREIGN DATA WRAPPER dummy OPTIONS (num_segments '3'); +CREATE FOREIGN TABLE ft5 ( + c1 int +) SERVER s1 OPTIONS (delimiter ',', mpp_execute 'all segments', num_segments '5'); +\d+ ft5 --- start_ignore -DROP SERVER s0 CASCADE; -DROP SERVER s1 CASCADE; +--start_ignore DROP FOREIGN DATA WRAPPER dummy CASCADE; --- end_ignore +--end_ignore From 63d3b110299a65eeb431afb2ca677a82fd1d36ec Mon Sep 17 00:00:00 2001 From: Jinbao Chen Date: Mon, 6 Nov 2023 15:59:59 +0800 Subject: [PATCH 2/5] Use FDW to query multiple servers as shards This commit mainly meets the needs of users to query multiple clusters as external shards. fdw treats each data source as a whole without knowing its internal structure. It keeps requesters and data sources properly decoupled to maintain generality. Add a new catalog table pg_foreign_table_seg to enable multiple shards in foreign table. The foreign table should be treated as a shard table with strewn locus. Each QE scanning the foreign should got a shard from pg_foreign_table_seg. Considering that the size of the computing cluster and the number of shards of the foreign table may be inconsistent. Use flexible gang to generate the same number of scan nodes as foreign table shards. Considering that the data bandwidth between different data centers is limited, we need to reduce the data transmission of fdw as much as possible. Pushing the execution node down to the remote end as much as possible can reduce data transmission. If all tables of a subtree are distributed in the same foreign server collection, It can be pushed down. But in mpp-fdw, we should consider if a table only joinning the shared in same foreign server. So a new system attribute gp_foreign_server was add to the foreign table. If the customer add "t1.gp_foreign_server = t2.gp_foreign_server" to join condition. It should be pushed down. We can only push down the first stage of the two-stage aggregation. Multi-stage aggregation will use some intermediate types. Some of these intermediate types are external types that can be output externally, such as count, min, max, and sum. The intermediate and final types of these types are identical. Others are more complex internal types, such as avg, whose intermediate type is inconsistent with the final type and must be converted using a final function. Since the local node in FDW serves as a standard client to exchange data with the remote server, these internal types cannot be transmitted. So some of the aggregate functions such as "avg" should not be pushed down now... --- contrib/postgres_fdw/Makefile | 2 +- .../expected/mpp_postgres_fdw.out | 389 ++++++++++++++++++ contrib/postgres_fdw/sql/mpp_postgres_fdw.sql | 137 ++++++ src/backend/access/common/heaptuple.c | 5 + src/backend/catalog/Makefile | 2 +- src/backend/catalog/genbki.pl | 3 +- src/backend/catalog/heap.c | 39 +- src/backend/cdb/cdbgroupingpaths.c | 77 +++- src/backend/cdb/dispatcher/cdbgang.c | 15 +- src/backend/cdb/dispatcher/cdbgang_async.c | 2 +- src/backend/commands/foreigncmds.c | 61 +++ src/backend/executor/execTuples.c | 7 + src/backend/foreign/foreign.c | 199 ++++++++- .../gpopt/translate/CTranslatorUtils.cpp | 8 + .../include/naucrates/dxl/xml/dxltokens.h | 1 + .../gporca/libnaucrates/src/xml/dxltokens.cpp | 1 + src/backend/nodes/equalfuncs.c | 13 + src/backend/nodes/outfast.c | 13 + src/backend/nodes/readfast.c | 15 + src/backend/optimizer/path/allpaths.c | 22 + src/backend/optimizer/path/joinpath.c | 45 ++ src/backend/optimizer/plan/planner.c | 49 ++- src/backend/optimizer/plan/setrefs.c | 76 +++- src/backend/optimizer/util/pathnode.c | 29 +- src/backend/optimizer/util/plancat.c | 2 + src/backend/optimizer/util/relnode.c | 67 ++- src/backend/parser/gram.y | 22 +- src/backend/tcop/utility.c | 15 + src/backend/utils/adt/selfuncs.c | 1 + src/include/access/sysattr.h | 3 +- src/include/catalog/pg_foreign_table_seg.h | 28 ++ src/include/catalog/pg_proc.dat | 3 + src/include/cdb/cdbgang.h | 4 +- src/include/commands/defrem.h | 1 + src/include/foreign/foreign.h | 2 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 8 + src/include/nodes/pathnodes.h | 1 + src/include/tcop/cmdtaglist.h | 1 + .../modify_table_data_corrupt_optimizer.out | 16 +- .../regress/expected/gporca_optimizer.out | 16 +- src/test/regress/expected/misc_sanity.out | 2 + src/test/regress/expected/oidjoins.out | 2 + src/test/regress/expected/sanity_check.out | 1 + src/test/regress/expected/tsrf_optimizer.out | 4 +- src/test/regress/expected/update.out | 4 +- .../regress/expected/update_optimizer.out | 4 +- .../expected/misc_sanity.out | 2 + .../singlenode_regress/expected/oidjoins.out | 2 + .../expected/sanity_check.out | 1 + .../singlenode_regress/expected/update.out | 4 +- src/tools/pgindent/typedefs.list | 1 + 52 files changed, 1345 insertions(+), 83 deletions(-) create mode 100644 contrib/postgres_fdw/expected/mpp_postgres_fdw.out create mode 100644 contrib/postgres_fdw/sql/mpp_postgres_fdw.sql create mode 100644 src/include/catalog/pg_foreign_table_seg.h diff --git a/contrib/postgres_fdw/Makefile b/contrib/postgres_fdw/Makefile index 5379bfd96a2..6c071200923 100644 --- a/contrib/postgres_fdw/Makefile +++ b/contrib/postgres_fdw/Makefile @@ -21,7 +21,7 @@ SHLIB_LINK_INTERNAL = -Wl,-Bsymbolic -Wl,-Bstatic -Wl,-Bstatic $(libpq) -Wl,-Bdy EXTENSION = postgres_fdw DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql -REGRESS = gp_postgres_fdw #postgres_fdw +REGRESS = gp_postgres_fdw mpp_postgres_fdw #postgres_fdw ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/postgres_fdw/expected/mpp_postgres_fdw.out b/contrib/postgres_fdw/expected/mpp_postgres_fdw.out new file mode 100644 index 00000000000..2d437c71782 --- /dev/null +++ b/contrib/postgres_fdw/expected/mpp_postgres_fdw.out @@ -0,0 +1,389 @@ +CREATE EXTENSION IF NOT EXISTS postgres_fdw; +NOTICE: extension "postgres_fdw" already exists, skipping +CREATE SERVER testserver2 FOREIGN DATA WRAPPER postgres_fdw; +DO $d$ +BEGIN +EXECUTE $$CREATE SERVER mpps1 FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (dbname 'fdw1', + port '$$||current_setting('port')||$$' + )$$; +EXECUTE $$CREATE SERVER mpps2 FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (dbname 'fdw2', + port '$$||current_setting('port')||$$' + )$$; +EXECUTE $$CREATE SERVER mpps3 FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (dbname 'fdw3', + port '$$||current_setting('port')||$$' + )$$; +END; +$d$; + +CREATE USER MAPPING FOR CURRENT_USER SERVER mpps1; +CREATE USER MAPPING FOR CURRENT_USER SERVER mpps2; +CREATE USER MAPPING FOR CURRENT_USER SERVER mpps3; +DROP DATABASE IF EXISTS fdw1; +DROP DATABASE IF EXISTS fdw2; +DROP DATABASE IF EXISTS fdw3; +CREATE DATABASE fdw1; +CREATE DATABASE fdw2; +CREATE DATABASE fdw3; +\c fdw1 +create table t1(a int, b text); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Cloudberry Database data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +create table t2(a int, b text); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Cloudberry Database data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +insert into t1 values(1, 'fdw1'); +insert into t2 values(1, 'fdw1'); +\c fdw2 +create table t1(a int, b text); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Cloudberry Database data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +create table t2(a int, b text); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Cloudberry Database data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +insert into t1 values(1, 'fdw2'); +insert into t2 values(1, 'fdw2'); +\c fdw3 +create table t1(a int, b text); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Cloudberry Database data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +create table t2(a int, b text); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Cloudberry Database data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +insert into t1 values(1, 'fdw3'); +insert into t2 values(1, 'fdw3'); +\c contrib_regression +CREATE FOREIGN TABLE fs1 ( + a int, + b text + ) + SERVER mpps1 + OPTIONS (schema_name 'public', table_name 't1', mpp_execute 'all segments'); +ADD FOREIGN SEGMENT FROM SERVER mpps1 INTO fs1; +explain (costs off) select * from fs1; + QUERY PLAN +------------------------------------------ + Gather Motion 1:1 (slice1; segments: 1) + -> Foreign Scan on fs1 + Optimizer: Postgres query optimizer +(3 rows) + +select * from fs1; + a | b +---+------ + 1 | fdw1 +(1 row) + +ADD FOREIGN SEGMENT FROM SERVER mpps2 INTO fs1; +explain (costs off) select * from fs1; + QUERY PLAN +------------------------------------------ + Gather Motion 2:1 (slice1; segments: 2) + -> Foreign Scan on fs1 + Optimizer: Postgres query optimizer +(3 rows) + +select * from fs1; + a | b +---+------ + 1 | fdw1 + 1 | fdw2 +(2 rows) + +explain (costs off) select count(*) from fs1; + QUERY PLAN +------------------------------------------------ + Finalize Aggregate + -> Gather Motion 2:1 (slice1; segments: 2) + -> Foreign Scan + Relations: Aggregate on (fs1) + Optimizer: Postgres query optimizer +(5 rows) + +select count(*) from fs1; + count +------- + 2 +(1 row) + +select count(*),b from fs1 group by b; + count | b +-------+------ + 1 | fdw2 + 1 | fdw1 +(2 rows) + +ADD FOREIGN SEGMENT FROM SERVER mpps3 INTO fs1; +explain (costs off) select * from fs1; + QUERY PLAN +------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + -> Foreign Scan on fs1 + Optimizer: Postgres query optimizer +(3 rows) + +select * from fs1; + a | b +---+------ + 1 | fdw2 + 1 | fdw1 + 1 | fdw3 +(3 rows) + +explain (costs off) select count(*) from fs1; + QUERY PLAN +------------------------------------------------ + Finalize Aggregate + -> Gather Motion 3:1 (slice1; segments: 3) + -> Foreign Scan + Relations: Aggregate on (fs1) + Optimizer: Postgres query optimizer +(5 rows) + +select count(*) from fs1; + count +------- + 3 +(1 row) + +select count(*),b from fs1 group by b; + count | b +-------+------ + 1 | fdw2 + 1 | fdw1 + 1 | fdw3 +(3 rows) + +---------------------- +-- Test join push down +---------------------- +CREATE FOREIGN TABLE fs2 ( + a int, + b text + ) + SERVER mpps1 + OPTIONS (schema_name 'public', table_name 't2', mpp_execute 'all segments'); +ADD FOREIGN SEGMENT FROM SERVER mpps1 INTO fs2; +ADD FOREIGN SEGMENT FROM SERVER mpps2 INTO fs2; +ADD FOREIGN SEGMENT FROM SERVER mpps3 INTO fs2; +explain (costs off) select * from fs1, fs2 where fs1.a = fs2.a; + QUERY PLAN +------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + -> Hash Join + Hash Cond: (fs1.a = fs2.a) + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: fs1.a + -> Foreign Scan on fs1 + -> Hash + -> Redistribute Motion 3:3 (slice3; segments: 3) + Hash Key: fs2.a + -> Foreign Scan on fs2 + Optimizer: Postgres query optimizer +(11 rows) + +select * from fs1,fs2 where fs1.a = fs2.a; + a | b | a | b +---+------+---+------ + 1 | fdw1 | 1 | fdw2 + 1 | fdw1 | 1 | fdw1 + 1 | fdw1 | 1 | fdw3 + 1 | fdw2 | 1 | fdw2 + 1 | fdw2 | 1 | fdw1 + 1 | fdw2 | 1 | fdw3 + 1 | fdw3 | 1 | fdw2 + 1 | fdw3 | 1 | fdw1 + 1 | fdw3 | 1 | fdw3 +(9 rows) + +explain (costs off) select * from fs1, fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; + QUERY PLAN +------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + -> Foreign Scan + Relations: (fs1) INNER JOIN (fs2) + Optimizer: Postgres query optimizer +(4 rows) + +select * from fs1,fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; + a | b | a | b +---+------+---+------ + 1 | fdw3 | 1 | fdw3 + 1 | fdw1 | 1 | fdw1 + 1 | fdw2 | 1 | fdw2 +(3 rows) + +explain (costs off) select count(*) from fs1, fs2 where fs1.a = fs2.a; + QUERY PLAN +------------------------------------------------------------------------------ + Finalize Aggregate + -> Gather Motion 3:1 (slice1; segments: 3) + -> Partial Aggregate + -> Hash Join + Hash Cond: (fs1.a = fs2.a) + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: fs1.a + -> Foreign Scan on fs1 + -> Hash + -> Redistribute Motion 3:3 (slice3; segments: 3) + Hash Key: fs2.a + -> Foreign Scan on fs2 + Optimizer: Postgres query optimizer +(13 rows) + +select count(*) from fs1,fs2 where fs1.a = fs2.a; + count +------- + 9 +(1 row) + +explain (costs off) select count(*) from fs1, fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; + QUERY PLAN +---------------------------------------------------------------- + Finalize Aggregate + -> Gather Motion 3:1 (slice1; segments: 3) + -> Foreign Scan + Relations: Aggregate on ((fs1) INNER JOIN (fs2)) + Optimizer: Postgres query optimizer +(5 rows) + +select count(*) from fs1,fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; + count +------- + 3 +(1 row) + +---------------------------- +-- Test with enable parallel +---------------------------- +set enable_parallel to true; +explain (costs off) select * from fs1; + QUERY PLAN +------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + -> Foreign Scan on fs1 + Optimizer: Postgres query optimizer +(3 rows) + +select * from fs1; + a | b +---+------ + 1 | fdw1 + 1 | fdw2 + 1 | fdw3 +(3 rows) + +explain (costs off) select count(*) from fs1; + QUERY PLAN +------------------------------------------------ + Finalize Aggregate + -> Gather Motion 3:1 (slice1; segments: 3) + -> Foreign Scan + Relations: Aggregate on (fs1) + Optimizer: Postgres query optimizer +(5 rows) + +select count(*) from fs1; + count +------- + 3 +(1 row) + +explain (costs off) select * from fs1, fs2 where fs1.a = fs2.a; + QUERY PLAN +------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + -> Hash Join + Hash Cond: (fs1.a = fs2.a) + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: fs1.a + -> Foreign Scan on fs1 + -> Hash + -> Redistribute Motion 3:3 (slice3; segments: 3) + Hash Key: fs2.a + -> Foreign Scan on fs2 + Optimizer: Postgres query optimizer +(11 rows) + +select * from fs1,fs2 where fs1.a = fs2.a; + a | b | a | b +---+------+---+------ + 1 | fdw1 | 1 | fdw1 + 1 | fdw1 | 1 | fdw3 + 1 | fdw1 | 1 | fdw2 + 1 | fdw3 | 1 | fdw1 + 1 | fdw3 | 1 | fdw3 + 1 | fdw3 | 1 | fdw2 + 1 | fdw2 | 1 | fdw1 + 1 | fdw2 | 1 | fdw3 + 1 | fdw2 | 1 | fdw2 +(9 rows) + +select count(*),b from fs1 group by b; + count | b +-------+------ + 1 | fdw2 + 1 | fdw1 + 1 | fdw3 +(3 rows) + +explain (costs off) select * from fs1, fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; + QUERY PLAN +------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + -> Foreign Scan + Relations: (fs1) INNER JOIN (fs2) + Optimizer: Postgres query optimizer +(4 rows) + +select * from fs1,fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; + a | b | a | b +---+------+---+------ + 1 | fdw3 | 1 | fdw3 + 1 | fdw2 | 1 | fdw2 + 1 | fdw1 | 1 | fdw1 +(3 rows) + +explain (costs off) select count(*) from fs1, fs2 where fs1.a = fs2.a; + QUERY PLAN +------------------------------------------------------------------------------ + Finalize Aggregate + -> Gather Motion 3:1 (slice1; segments: 3) + -> Partial Aggregate + -> Hash Join + Hash Cond: (fs1.a = fs2.a) + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: fs1.a + -> Foreign Scan on fs1 + -> Hash + -> Redistribute Motion 3:3 (slice3; segments: 3) + Hash Key: fs2.a + -> Foreign Scan on fs2 + Optimizer: Postgres query optimizer +(13 rows) + +select count(*) from fs1,fs2 where fs1.a = fs2.a; + count +------- + 9 +(1 row) + +explain (costs off) select count(*) from fs1, fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; + QUERY PLAN +---------------------------------------------------------------- + Finalize Aggregate + -> Gather Motion 3:1 (slice1; segments: 3) + -> Foreign Scan + Relations: Aggregate on ((fs1) INNER JOIN (fs2)) + Optimizer: Postgres query optimizer +(5 rows) + +select count(*) from fs1,fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; + count +------- + 3 +(1 row) + +reset enable_parallel; diff --git a/contrib/postgres_fdw/sql/mpp_postgres_fdw.sql b/contrib/postgres_fdw/sql/mpp_postgres_fdw.sql new file mode 100644 index 00000000000..7fefecaf9c2 --- /dev/null +++ b/contrib/postgres_fdw/sql/mpp_postgres_fdw.sql @@ -0,0 +1,137 @@ +CREATE EXTENSION IF NOT EXISTS postgres_fdw; + +CREATE SERVER testserver2 FOREIGN DATA WRAPPER postgres_fdw; +DO $d$ +BEGIN +EXECUTE $$CREATE SERVER mpps1 FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (dbname 'fdw1', + port '$$||current_setting('port')||$$' + )$$; +EXECUTE $$CREATE SERVER mpps2 FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (dbname 'fdw2', + port '$$||current_setting('port')||$$' + )$$; +EXECUTE $$CREATE SERVER mpps3 FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (dbname 'fdw3', + port '$$||current_setting('port')||$$' + )$$; +END; +$d$; + +CREATE USER MAPPING FOR CURRENT_USER SERVER mpps1; +CREATE USER MAPPING FOR CURRENT_USER SERVER mpps2; +CREATE USER MAPPING FOR CURRENT_USER SERVER mpps3; + +DROP DATABASE IF EXISTS fdw1; +DROP DATABASE IF EXISTS fdw2; +DROP DATABASE IF EXISTS fdw3; + +CREATE DATABASE fdw1; +CREATE DATABASE fdw2; +CREATE DATABASE fdw3; + +\c fdw1 +create table t1(a int, b text); +create table t2(a int, b text); + +insert into t1 values(1, 'fdw1'); +insert into t2 values(1, 'fdw1'); + +\c fdw2 +create table t1(a int, b text); +create table t2(a int, b text); +insert into t1 values(1, 'fdw2'); +insert into t2 values(1, 'fdw2'); + +\c fdw3 +create table t1(a int, b text); +create table t2(a int, b text); +insert into t1 values(1, 'fdw3'); +insert into t2 values(1, 'fdw3'); + +\c contrib_regression + +CREATE FOREIGN TABLE fs1 ( + a int, + b text + ) + SERVER mpps1 + OPTIONS (schema_name 'public', table_name 't1', mpp_execute 'all segments'); + +ADD FOREIGN SEGMENT FROM SERVER mpps1 INTO fs1; + +explain (costs off) select * from fs1; +select * from fs1; + +ADD FOREIGN SEGMENT FROM SERVER mpps2 INTO fs1; + +explain (costs off) select * from fs1; +select * from fs1; + +explain (costs off) select count(*) from fs1; +select count(*) from fs1; + +select count(*),b from fs1 group by b; + +ADD FOREIGN SEGMENT FROM SERVER mpps3 INTO fs1; + +explain (costs off) select * from fs1; +select * from fs1; + +explain (costs off) select count(*) from fs1; +select count(*) from fs1; + +select count(*),b from fs1 group by b; + +---------------------- +-- Test join push down +---------------------- +CREATE FOREIGN TABLE fs2 ( + a int, + b text + ) + SERVER mpps1 + OPTIONS (schema_name 'public', table_name 't2', mpp_execute 'all segments'); + +ADD FOREIGN SEGMENT FROM SERVER mpps1 INTO fs2; +ADD FOREIGN SEGMENT FROM SERVER mpps2 INTO fs2; +ADD FOREIGN SEGMENT FROM SERVER mpps3 INTO fs2; + +explain (costs off) select * from fs1, fs2 where fs1.a = fs2.a; +select * from fs1,fs2 where fs1.a = fs2.a; + +explain (costs off) select * from fs1, fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; +select * from fs1,fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; + +explain (costs off) select count(*) from fs1, fs2 where fs1.a = fs2.a; +select count(*) from fs1,fs2 where fs1.a = fs2.a; + +explain (costs off) select count(*) from fs1, fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; +select count(*) from fs1,fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; + +---------------------------- +-- Test with enable parallel +---------------------------- +set enable_parallel to true; + +explain (costs off) select * from fs1; +select * from fs1; + +explain (costs off) select count(*) from fs1; +select count(*) from fs1; + +explain (costs off) select * from fs1, fs2 where fs1.a = fs2.a; +select * from fs1,fs2 where fs1.a = fs2.a; + +select count(*),b from fs1 group by b; + +explain (costs off) select * from fs1, fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; +select * from fs1,fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; + +explain (costs off) select count(*) from fs1, fs2 where fs1.a = fs2.a; +select count(*) from fs1,fs2 where fs1.a = fs2.a; + +explain (costs off) select count(*) from fs1, fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; +select count(*) from fs1,fs2 where fs1.a = fs2.a and fs1.gp_foreign_server = fs2.gp_foreign_server; + +reset enable_parallel; diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index 1bf115c0f93..66aee71c706 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -63,6 +63,7 @@ #include "access/sysattr.h" #include "access/tupdesc_details.h" #include "executor/tuptable.h" +#include "foreign/foreign.h" #include "utils/expandeddatum.h" #include "catalog/pg_type.h" @@ -396,6 +397,7 @@ heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc) case MaxTransactionIdAttributeNumber: case MaxCommandIdAttributeNumber: case GpSegmentIdAttributeNumber: /*CDB*/ + case GpForeignServerAttributeNumber: /* these are never null */ break; @@ -672,6 +674,9 @@ heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) case GpSegmentIdAttributeNumber: /*CDB*/ result = Int32GetDatum(GpIdentity.segindex); break; + case GpForeignServerAttributeNumber: + result = ObjectIdGetDatum(GetForeignServerSegByRelid(tup->t_tableOid)); + break; default: elog(ERROR, "invalid attnum: %d", attnum); result = 0; /* keep compiler quiet */ diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index c2e4d421696..9e973d0abd3 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -89,7 +89,7 @@ CATALOG_HEADERS := \ pg_proc_callback.h \ pg_type_encoding.h \ pg_stat_last_operation.h pg_stat_last_shoperation.h \ - pg_foreign_table.h pg_policy.h pg_replication_origin.h \ + pg_foreign_table.h pg_foreign_table_seg.h pg_policy.h pg_replication_origin.h \ pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \ pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \ pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \ diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl index 93ef2029b75..b4c20387703 100644 --- a/src/backend/catalog/genbki.pl +++ b/src/backend/catalog/genbki.pl @@ -886,7 +886,8 @@ sub gen_pg_attribute { name => 'xmax', type => 'xid' }, { name => 'cmax', type => 'cid' }, { name => 'tableoid', type => 'oid' }, - { name => 'gp_segment_id', type => 'int4' }); + { name => 'gp_segment_id', type => 'int4' }, + { name => 'gp_foreign_server', type => 'oid' }); foreach my $attr (@SYS_ATTRS) { $attnum--; diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 26eaf197f34..b33d0c2ecce 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -62,6 +62,7 @@ #include "catalog/pg_database.h" #include "catalog/pg_directory_table.h" #include "catalog/pg_foreign_table.h" +#include "catalog/pg_foreign_table_seg.h" #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" @@ -271,7 +272,7 @@ static const FormData_pg_attribute a6 = { }; /*CDB*/ -static FormData_pg_attribute a8 = { +static FormData_pg_attribute a7 = { .attname = {"gp_segment_id"}, .atttypid = INT4OID, .attlen = sizeof(int32), @@ -285,7 +286,21 @@ static FormData_pg_attribute a8 = { .attislocal = true, }; -static const FormData_pg_attribute *SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a8}; +static FormData_pg_attribute a8 = { + .attname = {"gp_foreign_server"}, + .atttypid = OIDOID, + .attlen = sizeof(Oid), + .attnum = GpForeignServerAttributeNumber, + .attcacheoff = -1, + .atttypmod = -1, + .attbyval = true, + .attstorage = 'p', + .attalign = 'i', + .attnotnull = true, + .attislocal = true, +}; + +static const FormData_pg_attribute *SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8}; /* * This function returns a Form_pg_attribute pointer for a system attribute. @@ -2447,6 +2462,26 @@ heap_drop_with_catalog(Oid relid) { Relation rel; HeapTuple tuple; + ScanKeyData ftkey; + SysScanDesc ftscan; + + /* + * Drop the record on pg_foreign_table_seg + */ + rel = table_open(ForeignTableRelationSegId, RowExclusiveLock); + + ScanKeyInit(&ftkey, + Anum_pg_foreign_table_seg_ftsrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + + ftscan = systable_beginscan(rel, InvalidOid, false, NULL, 1, &ftkey); + + while (HeapTupleIsValid(tuple = systable_getnext(ftscan))) + CatalogTupleDelete(rel, &tuple->t_self); + + systable_endscan(ftscan); + table_close(rel, RowExclusiveLock); rel = table_open(ForeignTableRelationId, RowExclusiveLock); diff --git a/src/backend/cdb/cdbgroupingpaths.c b/src/backend/cdb/cdbgroupingpaths.c index 9155bf928a2..ce8d9a8ed09 100644 --- a/src/backend/cdb/cdbgroupingpaths.c +++ b/src/backend/cdb/cdbgroupingpaths.c @@ -56,6 +56,7 @@ #include "cdb/cdbpathlocus.h" #include "cdb/cdbutil.h" #include "cdb/cdbvars.h" +#include "foreign/fdwapi.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" @@ -320,6 +321,13 @@ cdb_create_multistage_grouping_paths(PlannerInfo *root, ctx.groupingSets = parse->groupingSets; ctx.havingQual = havingQual; ctx.partial_rel = fetch_upper_rel(root, UPPERREL_CDB_FIRST_STAGE_GROUP_AGG, NULL); + ctx.partial_rel->fdwroutine = input_rel->fdwroutine; + ctx.partial_rel->serverid = input_rel->serverid; + ctx.partial_rel->segSeverids = input_rel->segSeverids; + ctx.partial_rel->userid = input_rel->userid; + ctx.partial_rel->exec_location = input_rel->exec_location; + ctx.partial_rel->num_segments = input_rel->num_segments; + /* create a partial rel similar to make_grouping_rel() */ if (IS_OTHER_REL(input_rel)) { @@ -505,17 +513,17 @@ cdb_create_multistage_grouping_paths(PlannerInfo *root, case MULTI_DQAS: { fetch_multi_dqas_info(root, cheapest_path, &ctx, &info); - /* + /* * GPDB_14_MERGE_FIXME: We have done some copy job in * make_partial_grouping_target, so that the agg references - * in plan is actually different from + * in plan is actually different from * agg_partial_costs->distinctAggrefs. And it has to be * different since we need to compute and set agg_expr_id for * tuple split cases. * However, we need to push multi dqa's filter to tuplesplit * to get the correct result. And thus we need to remove the * filter in aggref referenced by the plan. - * + * * It's not that trivial to fix it perfectly. By manually * removing the origin plan's aggfilter can work around * this problem. We'll look at it again later. @@ -743,6 +751,31 @@ create_two_stage_paths(PlannerInfo *root, cdb_agg_planning_context *ctx, } } + if (ctx->partial_rel->fdwroutine && + ctx->partial_rel->fdwroutine->GetForeignUpperPaths && + ctx->partial_rel->segSeverids) + { + GroupPathExtraData extra; + FdwRoutine *fdwroutine = ctx->partial_rel->fdwroutine; + ListCell *lc; + + extra.patype = PARTITIONWISE_AGGREGATE_NONE; + extra.havingQual = NULL; + + foreach(lc, ctx->partial_rel->reltarget->exprs) + { + Expr *expr; + + expr = lfirst(lc); + + if (IsA(expr, Aggref)) + ((Aggref*)expr)->aggsplit = AGGSPLIT_SIMPLE; + } + + fdwroutine->GetForeignUpperPaths(root, UPPERREL_GROUP_AGG, input_rel, + ctx->partial_rel, &extra); + } + /* * We now have partially aggregated paths in ctx->partial_rel. Consider * different ways of performing the Finalize Aggregate stage. @@ -753,6 +786,22 @@ create_two_stage_paths(PlannerInfo *root, cdb_agg_planning_context *ctx, set_cheapest(ctx->partial_rel); cheapest_first_stage_path = ctx->partial_rel->cheapest_total_path; + + if (!IsA(cheapest_first_stage_path, ForeignPath)) + { + ListCell *lc; + + foreach(lc, ctx->partial_rel->reltarget->exprs) + { + Expr *expr; + + expr = lfirst(lc); + + if (IsA(expr, Aggref)) + ((Aggref*)expr)->aggsplit = AGGSPLIT_INITIAL_SERIAL; + } + } + if (ctx->can_sort) { ListCell *lc; @@ -1124,17 +1173,18 @@ add_second_stage_group_agg_path(PlannerInfo *root, false, singleQE_locus); path = (Path *) create_agg_path(root, - output_rel, - path, - ctx->target, - (ctx->final_groupClause ? AGG_SORTED : AGG_PLAIN), - ctx->hasAggs ? AGGSPLIT_FINAL_DESERIAL : AGGSPLIT_SIMPLE, - false, /* streaming */ - ctx->final_groupClause, - ctx->havingQual, - ctx->agg_final_costs, - ctx->dNumGroupsTotal); + output_rel, + path, + ctx->target, + (ctx->final_groupClause ? AGG_SORTED : AGG_PLAIN), + ctx->hasAggs ? AGGSPLIT_FINAL_DESERIAL : AGGSPLIT_SIMPLE, + false, /* streaming */ + ctx->final_groupClause, + ctx->havingQual, + ctx->agg_final_costs, + ctx->dNumGroupsTotal); path->pathkeys = strip_gsetid_from_pathkeys(ctx->gsetid_sortref, path->pathkeys); + if (!is_partial) add_path(output_rel, path, root); else @@ -1286,6 +1336,7 @@ add_second_stage_hash_agg_path(PlannerInfo *root, ctx->havingQual, ctx->agg_final_costs, ctx->dNumGroupsTotal); + if (!is_partial) add_path(output_rel, path, root); else diff --git a/src/backend/cdb/dispatcher/cdbgang.c b/src/backend/cdb/dispatcher/cdbgang.c index f838ad5fadd..31df68b3658 100644 --- a/src/backend/cdb/dispatcher/cdbgang.c +++ b/src/backend/cdb/dispatcher/cdbgang.c @@ -73,6 +73,8 @@ int host_primary_segment_count = 0; */ int ic_htab_size = 0; +int qe_idx = 0; + Gang *CurrentGangCreating = NULL; CreateGangFunc pCreateGangFunc = cdbgang_createGang_async; @@ -452,7 +454,8 @@ makeOptions(char **options, char **diff_options) */ bool build_gpqeid_param(char *buf, int bufsz, - bool is_writer, int identifier, int hostSegs, int icHtabSize) + bool is_writer, int identifier, int hostSegs, int icHtabSize, + int qeidx) { int len; #ifdef HAVE_INT64_TIMESTAMP @@ -465,9 +468,10 @@ build_gpqeid_param(char *buf, int bufsz, #endif #endif - len = snprintf(buf, bufsz, "%d;" TIMESTAMP_FORMAT ";%s;%d;%d;%d", + len = snprintf(buf, bufsz, "%d;" TIMESTAMP_FORMAT ";%s;%d;%d;%d;%d", gp_session_id, PgStartTime, - (is_writer ? "true" : "false"), identifier, hostSegs, icHtabSize); + (is_writer ? "true" : "false"), identifier, hostSegs, icHtabSize, + qeidx); return (len > 0 && len < bufsz); } @@ -541,6 +545,11 @@ cdbgang_parse_gpqeid_params(struct Port *port pg_attribute_unused(), ic_htab_size = (int) strtol(cp, NULL, 10); } + if (gpqeid_next_param(&cp, &np)) + { + qe_idx = (int) strtol(cp, NULL, 10); + } + /* Too few items, or too many? */ if (!cp || np) goto bad; diff --git a/src/backend/cdb/dispatcher/cdbgang_async.c b/src/backend/cdb/dispatcher/cdbgang_async.c index dcd7effd0b1..9d597c66aa7 100644 --- a/src/backend/cdb/dispatcher/cdbgang_async.c +++ b/src/backend/cdb/dispatcher/cdbgang_async.c @@ -156,7 +156,7 @@ cdbgang_createGang_async(List *segments, SegmentType segmentType) segdbDesc->isWriter, segdbDesc->identifier, segdbDesc->segment_database_info->hostPrimaryCount, - totalSegs * 2); + totalSegs * 2, i); if (!ret) ereport(ERROR, diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index b39b2c69b43..dc5f502aab9 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -25,6 +25,7 @@ #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_foreign_table.h" +#include "catalog/pg_foreign_table_seg.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "catalog/pg_user_mapping.h" @@ -1459,6 +1460,66 @@ RemoveUserMapping(DropUserMappingStmt *stmt) return umId; } +static void +InsertForeignTableSeg(Oid relid, Oid serverid, Datum options) +{ + Relation ftsrel; + Datum values[Natts_pg_foreign_table_seg]; + bool nulls[Natts_pg_foreign_table_seg]; + HeapTuple tuple; + + ftsrel = table_open(ForeignTableRelationSegId, RowExclusiveLock); + + /* + * Insert tuple into pg_foreign_table_seg. + */ + memset(values, 0, sizeof(values)); + memset(nulls, false, sizeof(nulls)); + + values[Anum_pg_foreign_table_seg_ftsrelid - 1] = ObjectIdGetDatum(relid); + values[Anum_pg_foreign_table_seg_ftsserver - 1] = ObjectIdGetDatum(serverid); + if (PointerIsValid(DatumGetPointer(options))) + values[Anum_pg_foreign_table_seg_ftsoptions - 1] = options; + else + nulls[Anum_pg_foreign_table_seg_ftsoptions - 1] = true; + tuple = heap_form_tuple(ftsrel->rd_att, values, nulls); + + CatalogTupleInsert(ftsrel, tuple); + + heap_freetuple(tuple); + + table_close(ftsrel, RowExclusiveLock); +} + +void +AddForeignSeg(AddForeignSegStmt *stmt) +{ + Oid relid; + Datum ftoptions; + ForeignServer *server; + ForeignDataWrapper *fdw; + + relid = RelnameGetRelid(stmt->tablename); + Assert(OidIsValid(relid)); + + server = GetForeignServerByName(stmt->servername, false); + + fdw = GetForeignDataWrapper(server->fdwid); + /* Add table generic options */ + ftoptions = transformGenericOptions(ForeignTableRelationId, + PointerGetDatum(NULL), + stmt->options, + fdw->fdwvalidator); + InsertForeignTableSeg(relid, server->serverid, ftoptions); + + if (Gp_role == GP_ROLE_DISPATCH) + { + CdbDispatchUtilityStatement((Node *) stmt, + DF_WITH_SNAPSHOT | DF_CANCEL_ON_ERROR | DF_NEED_TWO_PHASE, + GetAssignedOidsForDispatch(), + NULL); + } +} /* * Create a foreign table diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 35eef2fc9fc..2c2106af9a7 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -61,6 +61,7 @@ #include "access/htup_details.h" #include "access/tupdesc_details.h" #include "catalog/pg_type.h" +#include "foreign/foreign.h" #include "funcapi.h" #include "nodes/nodeFuncs.h" #include "storage/bufmgr.h" @@ -152,6 +153,12 @@ tts_virtual_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull) return Int32GetDatum(GpIdentity.segindex); } + else if (attnum == GpForeignServerAttributeNumber) + { + *isnull = false; + + return ObjectIdGetDatum(GetForeignServerSegByRelid(slot->tts_tableOid)); + } elog(ERROR, "virtual tuple table slot does not have system attributes"); diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index ed619d47348..0f846382ba4 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -14,11 +14,15 @@ #include "access/htup_details.h" #include "access/reloptions.h" +#include "access/table.h" #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_foreign_table.h" +#include "catalog/pg_foreign_table_seg.h" #include "catalog/pg_user_mapping.h" +#include "cdb/cdbgang.h" #include "cdb/cdbutil.h" +#include "cdb/cdbvars.h" #include "commands/defrem.h" #include "foreign/fdwapi.h" #include "foreign/foreign.h" @@ -32,6 +36,7 @@ extern Datum pg_options_to_table(PG_FUNCTION_ARGS); extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS); +static int GetForeignTableSegNumbers(Oid relid); /* Get and separate out the mpp_execute option. */ char @@ -334,6 +339,116 @@ GetUserMapping(Oid userid, Oid serverid) return um; } +List * +GetForeignServerSegsByRelId(Oid relid) +{ + Form_pg_foreign_table_seg tablesegform; + Relation foreignTableRel; + ScanKeyData ftkey; + SysScanDesc ftscan; + HeapTuple fttuple; + List *segList = NIL; + + + foreignTableRel = table_open(ForeignTableRelationSegId, AccessShareLock); + + ScanKeyInit(&ftkey, + Anum_pg_foreign_table_seg_ftsrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + + ftscan = systable_beginscan(foreignTableRel, InvalidOid, + false, NULL, 1, &ftkey); + + while (HeapTupleIsValid(fttuple = systable_getnext(ftscan))) + { + tablesegform = (Form_pg_foreign_table_seg) GETSTRUCT(fttuple); + segList = lappend_oid(segList, tablesegform->ftsserver); + } + + systable_endscan(ftscan); + + table_close(foreignTableRel, AccessShareLock); + + return segList; +} + +static int +GetForeignTableSegNumbers(Oid relid) +{ + Relation foreignTableRel; + ScanKeyData ftkey; + SysScanDesc ftscan; + int number = 0; + + + foreignTableRel = table_open(ForeignTableRelationSegId, AccessShareLock); + + ScanKeyInit(&ftkey, + Anum_pg_foreign_table_seg_ftsrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + + ftscan = systable_beginscan(foreignTableRel, InvalidOid, + false, NULL, 1, &ftkey); + + while (HeapTupleIsValid(systable_getnext(ftscan))) + { + number++; + } + + systable_endscan(ftscan); + + table_close(foreignTableRel, AccessShareLock); + + return number; +} + +static ForeignTable * +GetForeignTableOnSegment(Oid relid) +{ + Form_pg_foreign_table_seg tablesegform; + ForeignTable *ft = NULL; + Relation foreignTableRel; + ScanKeyData ftkey; + SysScanDesc ftscan; + HeapTuple fttuple; + int i; + int segNumber; + + segNumber = GetForeignTableSegNumbers(relid); + + foreignTableRel = table_open(ForeignTableRelationSegId, AccessShareLock); + + ScanKeyInit(&ftkey, + Anum_pg_foreign_table_seg_ftsrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + + ftscan = systable_beginscan(foreignTableRel, InvalidOid, + false, NULL, 1, &ftkey); + + i = 0; + while (HeapTupleIsValid(fttuple = systable_getnext(ftscan))) + { + + if (i == qe_idx) + { + tablesegform = (Form_pg_foreign_table_seg) GETSTRUCT(fttuple); + ft = (ForeignTable *) palloc(sizeof(ForeignTable)); + ft->relid = relid; + ft->serverid = tablesegform->ftsserver; + break; + } + i++; + } + + systable_endscan(ftscan); + + table_close(foreignTableRel, AccessShareLock); + + return ft; +} /* * GetForeignTable - look up the foreign table definition by relation oid. @@ -366,22 +481,41 @@ GetForeignTable(Oid relid) else ft->options = untransformRelOptions(datum); - ForeignServer *server = GetForeignServer(ft->serverid); + ReleaseSysCache(tp); ft->exec_location = SeparateOutMppExecute(&ft->options); + + if (ft->exec_location == FTEXECLOCATION_ALL_SEGMENTS && + OidIsValid(ft->serverid) && + Gp_role == GP_ROLE_EXECUTE) + { + ForeignTable *segFt; + segFt = GetForeignTableOnSegment(relid); + if (segFt) + { + ft->serverid = segFt->serverid; + + pfree(segFt); + } + } + + ForeignServer *server = GetForeignServer(ft->serverid); + if (ft->exec_location == FTEXECLOCATION_NOT_DEFINED) { ft->exec_location = server->exec_location; } ft->num_segments = SeparateOutNumSegments(&ft->options); + + if (ft->num_segments <= 0) + ft->num_segments = GetForeignTableSegNumbers(relid); + if (ft->num_segments <= 0) { - ft->num_segments = server->num_segments; + ft->num_segments = server->num_segments; } - ReleaseSysCache(tp); - return ft; } @@ -964,3 +1098,60 @@ GetExistingLocalJoinPath(RelOptInfo *joinrel) } return NULL; } + +Oid +GetForeignServerSegByRelid(Oid relid) +{ + Form_pg_foreign_table tableform; + ForeignTable *ft; + HeapTuple tp; + Datum datum; + bool isnull; + Oid foreignServerId; + + if (Gp_role != GP_ROLE_EXECUTE) + { + tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tp)) + return InvalidOid; + tableform = (Form_pg_foreign_table) GETSTRUCT(tp); + + ft = (ForeignTable *) palloc(sizeof(ForeignTable)); + ft->relid = relid; + ft->serverid = tableform->ftserver; + + /* Extract the ftoptions */ + datum = SysCacheGetAttr(FOREIGNTABLEREL, + tp, + Anum_pg_foreign_table_ftoptions, + &isnull); + if (isnull) + ft->options = NIL; + else + ft->options = untransformRelOptions(datum); + + ReleaseSysCache(tp); + } + else + { + ft = GetForeignTableOnSegment(relid); + + if (!ft) + return InvalidOid; + } + + foreignServerId = ft->serverid; + pfree(ft); + + return foreignServerId; +} + +Datum +gp_foreign_server_id(PG_FUNCTION_ARGS) +{ + Oid relid; + + relid = PG_GETARG_OID(0); + + return GetForeignServerSegByRelid(relid); +} diff --git a/src/backend/gpopt/translate/CTranslatorUtils.cpp b/src/backend/gpopt/translate/CTranslatorUtils.cpp index 194c10c1860..277f8b6fd67 100644 --- a/src/backend/gpopt/translate/CTranslatorUtils.cpp +++ b/src/backend/gpopt/translate/CTranslatorUtils.cpp @@ -785,6 +785,9 @@ CTranslatorUtils::GetSystemColName(AttrNumber attno) case GpSegmentIdAttributeNumber: return CDXLTokens::GetDXLTokenStr(EdxltokenGpSegmentIdColName); + case GpForeignServerAttributeNumber: + return CDXLTokens::GetDXLTokenStr(EdxltokenGpForeignServerColName); + default: GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiPlStmt2DXLConversion, GPOS_WSZ_LIT("Invalid attribute number")); @@ -830,6 +833,10 @@ CTranslatorUtils::GetSystemColType(CMemoryPool *mp, AttrNumber attno) // int4 return GPOS_NEW(mp) CMDIdGPDB(GPDB_INT4); + case GpForeignServerAttributeNumber: + // int4 + return GPOS_NEW(mp) CMDIdGPDB(GPDB_INT4); + default: GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiPlStmt2DXLConversion, GPOS_WSZ_LIT("Invalid attribute number")); @@ -863,6 +870,7 @@ CTranslatorUtils::GetSystemColLength(AttrNumber attno) // cid type case GpSegmentIdAttributeNumber: + case GpForeignServerAttributeNumber: // int4 return 4; diff --git a/src/backend/gporca/libnaucrates/include/naucrates/dxl/xml/dxltokens.h b/src/backend/gporca/libnaucrates/include/naucrates/dxl/xml/dxltokens.h index c8b8c200bab..72af681b4eb 100644 --- a/src/backend/gporca/libnaucrates/include/naucrates/dxl/xml/dxltokens.h +++ b/src/backend/gporca/libnaucrates/include/naucrates/dxl/xml/dxltokens.h @@ -356,6 +356,7 @@ enum Edxltoken EdxltokenCmaxColName, EdxltokenTableOidColName, EdxltokenGpSegmentIdColName, + EdxltokenGpForeignServerColName, EdxltokenActionColId, EdxltokenOidColId, diff --git a/src/backend/gporca/libnaucrates/src/xml/dxltokens.cpp b/src/backend/gporca/libnaucrates/src/xml/dxltokens.cpp index abed23e7b1a..3291e3cc537 100644 --- a/src/backend/gporca/libnaucrates/src/xml/dxltokens.cpp +++ b/src/backend/gporca/libnaucrates/src/xml/dxltokens.cpp @@ -405,6 +405,7 @@ CDXLTokens::Init(CMemoryPool *mp) {EdxltokenCmaxColName, GPOS_WSZ_LIT("cmax")}, {EdxltokenTableOidColName, GPOS_WSZ_LIT("tableoid")}, {EdxltokenGpSegmentIdColName, GPOS_WSZ_LIT("gp_segment_id")}, + {EdxltokenGpForeignServerColName, GPOS_WSZ_LIT("gp_foreign_server")}, {EdxltokenActionColId, GPOS_WSZ_LIT("ActionCol")}, {EdxltokenOidColId, GPOS_WSZ_LIT("OidCol")}, diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index b222609948f..8af8252fefc 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -2148,6 +2148,16 @@ _equalCreateForeignServerStmt(const CreateForeignServerStmt *a, const CreateFore return true; } +static bool +_equalAddForeignSegStmt(const AddForeignSegStmt *a, const AddForeignSegStmt *b) +{ + COMPARE_STRING_FIELD(servername); + COMPARE_STRING_FIELD(tablename); + COMPARE_NODE_FIELD(options); + + return true; +} + static bool _equalAlterForeignServerStmt(const AlterForeignServerStmt *a, const AlterForeignServerStmt *b) { @@ -4071,6 +4081,9 @@ equal(const void *a, const void *b) case T_CreateForeignServerStmt: retval = _equalCreateForeignServerStmt(a, b); break; + case T_AddForeignSegStmt: + retval = _equalAddForeignSegStmt(a, b); + break; case T_AlterForeignServerStmt: retval = _equalAlterForeignServerStmt(a, b); break; diff --git a/src/backend/nodes/outfast.c b/src/backend/nodes/outfast.c index d2ef98d93ed..22387524273 100644 --- a/src/backend/nodes/outfast.c +++ b/src/backend/nodes/outfast.c @@ -640,6 +640,16 @@ _outCreateForeignServerStmt(StringInfo str, CreateForeignServerStmt *node) WRITE_NODE_FIELD(options); } +static void +_outAddForeignSegstmt(StringInfo str, AddForeignSegStmt *node) +{ + WRITE_NODE_TYPE("ADDFOREIGNSEGSTMT"); + + WRITE_STRING_FIELD(servername); + WRITE_STRING_FIELD(tablename); + WRITE_NODE_FIELD(options); +} + static void _outAlterForeignServerStmt(StringInfo str, AlterForeignServerStmt *node) { @@ -1759,6 +1769,9 @@ _outNode(StringInfo str, void *obj) case T_CreateForeignServerStmt: _outCreateForeignServerStmt(str, obj); break; + case T_AddForeignSegStmt: + _outAddForeignSegstmt(str, obj); + break; case T_AlterFdwStmt: _outAlterFdwStmt(str, obj); break; diff --git a/src/backend/nodes/readfast.c b/src/backend/nodes/readfast.c index 351c8eae1d5..daed3060d50 100644 --- a/src/backend/nodes/readfast.c +++ b/src/backend/nodes/readfast.c @@ -1441,6 +1441,18 @@ _readCreateForeignServerStmt(void) READ_DONE(); } +static AddForeignSegStmt * +_readAddForeignSegStmt(void) +{ + READ_LOCALS(AddForeignSegStmt); + + READ_STRING_FIELD(servername); + READ_STRING_FIELD(tablename); + READ_NODE_FIELD(options); + + READ_DONE(); +} + static AlterForeignServerStmt * _readAlterForeignServerStmt(void) { @@ -2713,6 +2725,9 @@ readNodeBinary(void) case T_CreateForeignServerStmt: return_value = _readCreateForeignServerStmt(); break; + case T_AddForeignSegStmt: + return_value = _readAddForeignSegStmt(); + break; case T_AlterFdwStmt: return_value = _readAlterFdwStmt(); break; diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 16e4adf94e1..f4efd8912f1 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1212,6 +1212,27 @@ set_foreign_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) rel->tuples = Max(rel->tuples, rel->rows); } +static void +adjust_costs_for_mpp_foreign_scan(List *pathlist) +{ + ListCell *cell; + + foreach(cell, pathlist) + { + Path *path; + + path = lfirst(cell); + if (IsA(path, ForeignPath)) + { + if (path->locus.locustype == CdbLocusType_Strewn && + path->locus.numsegments > 1) + { + path->rows *= (path->locus.numsegments * 10); + } + } + } +} + /* * set_foreign_pathlist * Build access paths for a foreign table RTE @@ -1221,6 +1242,7 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { /* Call the FDW's GetForeignPaths function to generate path(s) */ rel->fdwroutine->GetForeignPaths(root, rel, rte->relid); + adjust_costs_for_mpp_foreign_scan(rel->pathlist); } /* diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 7074a9b38a9..adaa8dfd541 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -374,10 +374,55 @@ add_paths_to_joinrel(PlannerInfo *root, */ if (joinrel->fdwroutine && joinrel->fdwroutine->GetForeignJoinPaths) + { + List *foreignRestrictlist = NIL; + List *oldRestrictlist = extra.restrictlist; + ListCell *cell; + + foreach(cell, extra.restrictlist) + { + RestrictInfo *info; + + info = lfirst(cell); + + if (IsA(info->clause, OpExpr)) + { + ListCell *cell2; + bool skip = false; + OpExpr *op = (OpExpr *) info->clause; + + foreach(cell2, op->args) + { + Var *var = lfirst(cell2); + if (var->varattno == GpForeignServerAttributeNumber) + skip = true; + } + + if (skip) + continue; + } + + foreignRestrictlist = lappend(foreignRestrictlist, info); + } + + extra.restrictlist = foreignRestrictlist; + joinrel->fdwroutine->GetForeignJoinPaths(root, joinrel, outerrel, innerrel, jointype, &extra); + extra.restrictlist = oldRestrictlist; + foreach(lc, joinrel->pathlist) + { + Path *path = (Path *) lfirst(lc); + + if (IsA(path, ForeignPath)) + { + path->locus.numsegments = getgpsegmentCount(); + } + } + } + /* * 6. Finally, give extensions a chance to manipulate the path list. */ diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 3b2cb809038..b6dd832ff53 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -27,6 +27,7 @@ #include "access/sysattr.h" #include "access/table.h" #include "access/xact.h" +#include "catalog/pg_aggregate.h" #include "catalog/pg_constraint.h" #include "catalog/pg_inherits.h" #include "catalog/pg_proc.h" @@ -2639,7 +2640,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) * let it consider adding ForeignPaths. */ if (final_rel->fdwroutine && - final_rel->fdwroutine->GetForeignUpperPaths) + final_rel->fdwroutine->GetForeignUpperPaths && + !final_rel->segSeverids) final_rel->fdwroutine->GetForeignUpperPaths(root, UPPERREL_FINAL, current_rel, final_rel, &extra); @@ -4199,7 +4201,36 @@ make_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel, /* * If the input rel belongs to a single FDW, so does the grouped rel. */ + if (OidIsValid(input_rel->serverid) && + input_rel->exec_location == FTEXECLOCATION_ALL_SEGMENTS) + { + ListCell *cell; + + foreach(cell, grouped_rel->reltarget->exprs) + { + Expr *expr = lfirst(cell); + + if (IsA(expr, Aggref)) + { + HeapTuple aggTuple; + Form_pg_aggregate aggregate; + Aggref *aggref = (Aggref *) expr; + + aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggref->aggfnoid)); + aggregate = (Form_pg_aggregate) GETSTRUCT(aggTuple); + if (OidIsValid(aggregate->aggfinalfn)) + { + ReleaseSysCache(aggTuple); + return grouped_rel; + } + + ReleaseSysCache(aggTuple); + } + } + } + grouped_rel->serverid = input_rel->serverid; + grouped_rel->segSeverids = input_rel->segSeverids; grouped_rel->userid = input_rel->userid; grouped_rel->useridiscurrent = input_rel->useridiscurrent; grouped_rel->fdwroutine = input_rel->fdwroutine; @@ -4432,7 +4463,8 @@ create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, * let it consider adding ForeignPaths. */ if (grouped_rel->fdwroutine && - grouped_rel->fdwroutine->GetForeignUpperPaths) + grouped_rel->fdwroutine->GetForeignUpperPaths && + !grouped_rel->segSeverids) grouped_rel->fdwroutine->GetForeignUpperPaths(root, UPPERREL_GROUP_AGG, input_rel, grouped_rel, extra); @@ -4849,7 +4881,8 @@ create_window_paths(PlannerInfo *root, * let it consider adding ForeignPaths. */ if (window_rel->fdwroutine && - window_rel->fdwroutine->GetForeignUpperPaths) + window_rel->fdwroutine->GetForeignUpperPaths && + !window_rel->segSeverids) window_rel->fdwroutine->GetForeignUpperPaths(root, UPPERREL_WINDOW, input_rel, window_rel, NULL); @@ -5249,7 +5282,8 @@ create_distinct_paths(PlannerInfo *root, * let it consider adding ForeignPaths. */ if (distinct_rel->fdwroutine && - distinct_rel->fdwroutine->GetForeignUpperPaths) + distinct_rel->fdwroutine->GetForeignUpperPaths && + !distinct_rel->segSeverids) distinct_rel->fdwroutine->GetForeignUpperPaths(root, UPPERREL_DISTINCT, input_rel, distinct_rel, NULL); @@ -5523,7 +5557,8 @@ create_ordered_paths(PlannerInfo *root, */ if (ordered_rel->fdwroutine && - ordered_rel->fdwroutine->GetForeignUpperPaths) + ordered_rel->fdwroutine->GetForeignUpperPaths && + !ordered_rel->segSeverids) ordered_rel->fdwroutine->GetForeignUpperPaths(root, UPPERREL_ORDERED, input_rel, ordered_rel, NULL); @@ -7886,6 +7921,7 @@ create_partial_grouping_paths(PlannerInfo *root, grouped_rel->consider_parallel; partially_grouped_rel->reloptkind = grouped_rel->reloptkind; partially_grouped_rel->serverid = grouped_rel->serverid; + partially_grouped_rel->segSeverids = grouped_rel->segSeverids; partially_grouped_rel->userid = grouped_rel->userid; partially_grouped_rel->useridiscurrent = grouped_rel->useridiscurrent; partially_grouped_rel->fdwroutine = grouped_rel->fdwroutine; @@ -8250,7 +8286,8 @@ create_partial_grouping_paths(PlannerInfo *root, * let it consider adding partially grouped ForeignPaths. */ if (partially_grouped_rel->fdwroutine && - partially_grouped_rel->fdwroutine->GetForeignUpperPaths) + partially_grouped_rel->fdwroutine->GetForeignUpperPaths && + !partially_grouped_rel->segSeverids) { FdwRoutine *fdwroutine = partially_grouped_rel->fdwroutine; diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 217f38e29f5..b1851c64739 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -19,6 +19,7 @@ #include "postgres.h" #include "access/transam.h" +#include "catalog/pg_foreign_table_seg.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" @@ -1142,6 +1143,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) case T_Agg: { Agg *agg = (Agg *) plan; + bool is_foreign_final_agg = false; if (DO_AGGSPLIT_DEDUPLICATED(agg->aggsplit)) { @@ -1155,6 +1157,11 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) agg->aggsplit &= ~AGGSPLITOP_DEDUPLICATED; } + if ((IsA(plan->lefttree, Motion) && + IsA(plan->lefttree->lefttree, ForeignScan)) || + IsA(plan->lefttree, ForeignScan)) + is_foreign_final_agg = true; + /* * If this node is combining partial-aggregation results, we * must convert its Aggrefs to contain references to the @@ -1165,7 +1172,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) { plan->targetlist = (List *) convert_combining_aggrefs((Node *) plan->targetlist, - NULL); + &is_foreign_final_agg); plan->qual = (List *) convert_combining_aggrefs((Node *) plan->qual, NULL); @@ -1702,12 +1709,43 @@ set_foreignscan_references(PlannerInfo *root, if (fscan->fdw_scan_tlist != NIL || fscan->scan.scanrelid == 0) { + ListCell *cell; + /* * Adjust tlist, qual, fdw_exprs, fdw_recheck_quals to reference * foreign scan tuple */ indexed_tlist *itlist = build_tlist_index(fscan->fdw_scan_tlist); + foreach(cell, fscan->scan.plan.targetlist) + { + TargetEntry *tle; + + tle = lfirst(cell); + + if (IsA(tle->expr, Var)) + { + Var *var; + + var = (Var*) tle->expr; + if (var->varattno == GpForeignServerAttributeNumber) + { + FuncExpr *funcExpr; + RangeTblEntry *rte; + Const *relid; + + rte = root->simple_rte_array[var->varno]; + relid = makeConst(OIDOID, -1, InvalidOid, sizeof(Oid), + ObjectIdGetDatum(rte->relid), false, true); + funcExpr = makeFuncExpr(GP_FOREIGN_SERVER_ID_FUNC, OIDOID, + list_make1(relid), InvalidOid, InvalidOid, + COERCE_EXPLICIT_CALL); + tle->expr = (Expr*) funcExpr; + } + } + + } + fscan->scan.plan.targetlist = (List *) fix_upper_expr(root, (Node *) fscan->scan.plan.targetlist, @@ -1744,6 +1782,37 @@ set_foreignscan_references(PlannerInfo *root, } else { + ListCell *cell; + + foreach(cell, fscan->scan.plan.targetlist) + { + TargetEntry *tle; + + tle = lfirst(cell); + + if (IsA(tle->expr, Var)) + { + Var *var; + + var = (Var*) tle->expr; + if (var->varattno == GpForeignServerAttributeNumber) + { + FuncExpr *funcExpr; + RangeTblEntry *rte; + Const *relid; + + rte = root->simple_rte_array[var->varno]; + relid = makeConst(OIDOID, -1, InvalidOid, sizeof(Oid), + ObjectIdGetDatum(rte->relid), false, true); + funcExpr = makeFuncExpr(GP_FOREIGN_SERVER_ID_FUNC, OIDOID, + list_make1(relid), InvalidOid, InvalidOid, + COERCE_EXPLICIT_CALL); + tle->expr = (Expr*) funcExpr; + } + } + + } + /* * Adjust tlist, qual, fdw_exprs, fdw_recheck_quals in the standard * way @@ -2699,7 +2768,10 @@ convert_combining_aggrefs(Node *node, void *context) * Now, set up child_agg to represent the first phase of partial * aggregation. For now, assume serialization is required. */ - mark_partial_aggref(child_agg, AGGSPLIT_INITIAL_SERIAL); + if (context && *(bool *)context) + mark_partial_aggref(child_agg, AGGSPLIT_SIMPLE); + else + mark_partial_aggref(child_agg, AGGSPLIT_INITIAL_SERIAL); /* * And set up parent_agg to represent the second phase. diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index fab5b350db5..c7b1025bced 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -23,6 +23,7 @@ #include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/extensible.h" +#include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" @@ -3639,23 +3640,21 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, pathnode->path.total_cost = total_cost; pathnode->path.pathkeys = pathkeys; - switch (rel->exec_location) + if (Gp_role == GP_ROLE_DISPATCH) { - ForeignServer *server = NULL; - switch (rel->exec_location) { - case FTEXECLOCATION_ANY: - CdbPathLocus_MakeGeneral(&(pathnode->path.locus)); - break; - case FTEXECLOCATION_ALL_SEGMENTS: - CdbPathLocus_MakeStrewn(&(pathnode->path.locus), rel->num_segments, 0); - break; - case FTEXECLOCATION_COORDINATOR: - CdbPathLocus_MakeEntry(&(pathnode->path.locus)); - break; - default: - elog(ERROR, "unrecognized exec_location '%c'", rel->exec_location); + case FTEXECLOCATION_ANY: + CdbPathLocus_MakeGeneral(&(pathnode->path.locus)); + break; + case FTEXECLOCATION_ALL_SEGMENTS: + CdbPathLocus_MakeStrewn(&(pathnode->path.locus), rel->num_segments, 0); + break; + case FTEXECLOCATION_COORDINATOR: + CdbPathLocus_MakeEntry(&(pathnode->path.locus)); + break; + default: + elog(ERROR, "unrecognized exec_location '%c'", rel->exec_location); } } else @@ -3780,7 +3779,7 @@ create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel, CdbPathLocus_MakeGeneral(&(pathnode->path.locus)); break; case FTEXECLOCATION_ALL_SEGMENTS: - CdbPathLocus_MakeStrewn(&(pathnode->path.locus), getgpsegmentCount(), 0); + CdbPathLocus_MakeStrewn(&(pathnode->path.locus), rel->num_segments, 0); break; case FTEXECLOCATION_COORDINATOR: CdbPathLocus_MakeEntry(&(pathnode->path.locus)); diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 1e64cfb06ee..535059309ac 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -470,6 +470,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) { rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation)); + rel->segSeverids = GetForeignServerSegsByRelId(RelationGetRelid(relation)); rel->fdwroutine = GetFdwRoutineForRelation(relation, true); rel->exec_location = GetForeignTable(RelationGetRelid(relation))->exec_location; rel->num_segments = GetForeignTable(RelationGetRelid(relation))->num_segments; @@ -477,6 +478,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, else { rel->serverid = InvalidOid; + rel->segSeverids = NIL; rel->fdwroutine = NULL; rel->exec_location = FTEXECLOCATION_NOT_DEFINED; rel->num_segments = getgpsegmentCount(); diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index f95f63aa787..7381a4ab9bd 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -71,7 +71,8 @@ static List *subbuild_joinrel_joinlist(RelOptInfo *joinrel, List *joininfo_list, List *new_joininfo); static void set_foreign_rel_properties(RelOptInfo *joinrel, - RelOptInfo *outer_rel, RelOptInfo *inner_rel); + RelOptInfo *outer_rel, RelOptInfo *inner_rel, + List *restrictlist); static void add_join_rel(PlannerInfo *root, RelOptInfo *joinrel); static void build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel, RelOptInfo *inner_rel, @@ -256,6 +257,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; + rel->segSeverids = NIL; rel->userid = rte->checkAsUser; rel->useridiscurrent = false; rel->exec_location = FTEXECLOCATION_NOT_DEFINED; @@ -636,37 +638,87 @@ find_join_rel(PlannerInfo *root, Relids relids) */ static void set_foreign_rel_properties(RelOptInfo *joinrel, RelOptInfo *outer_rel, - RelOptInfo *inner_rel) + RelOptInfo *inner_rel, List *restrictlist) { if (OidIsValid(outer_rel->serverid) && inner_rel->serverid == outer_rel->serverid && inner_rel->exec_location == outer_rel->exec_location) { + if (inner_rel->exec_location == FTEXECLOCATION_ALL_SEGMENTS) + { + ListCell *cell; + bool mppMatch = false; + List *l1; + List *l2; + + l1 = inner_rel->segSeverids; + l2 = outer_rel->segSeverids; + + if (list_difference_oid(l1, l2)) + return; + + foreach(cell, restrictlist) + { + RestrictInfo *info; + + info = lfirst(cell); + if (IsA(info->clause, OpExpr)) + { + Expr *larg; + Expr *rarg; + OpExpr *opExpr = (OpExpr *) info->clause; + + if (list_length(opExpr->args) != 2) + continue; + + larg = lfirst(list_head(opExpr->args)); + rarg = lfirst(list_second_cell(opExpr->args)); + + if (IsA(larg, Var) && IsA(rarg, Var) && + ((Var *) larg)->varattno == GpForeignServerAttributeNumber && + ((Var *) rarg)->varattno == GpForeignServerAttributeNumber) + { + mppMatch = true; + break; + } + } + } + + if (!mppMatch) + return; + } + if (inner_rel->userid == outer_rel->userid) { joinrel->serverid = outer_rel->serverid; + joinrel->segSeverids = outer_rel->segSeverids; joinrel->userid = outer_rel->userid; joinrel->useridiscurrent = outer_rel->useridiscurrent || inner_rel->useridiscurrent; joinrel->fdwroutine = outer_rel->fdwroutine; joinrel->exec_location = outer_rel->exec_location; + joinrel->num_segments = outer_rel->num_segments; } else if (!OidIsValid(inner_rel->userid) && outer_rel->userid == GetUserId()) { joinrel->serverid = outer_rel->serverid; + joinrel->segSeverids = outer_rel->segSeverids; joinrel->userid = outer_rel->userid; joinrel->useridiscurrent = true; joinrel->fdwroutine = outer_rel->fdwroutine; joinrel->exec_location = outer_rel->exec_location; + joinrel->num_segments = outer_rel->num_segments; } else if (!OidIsValid(outer_rel->userid) && inner_rel->userid == GetUserId()) { joinrel->serverid = outer_rel->serverid; + joinrel->segSeverids = outer_rel->segSeverids; joinrel->userid = inner_rel->userid; joinrel->useridiscurrent = true; joinrel->fdwroutine = outer_rel->fdwroutine; joinrel->exec_location = outer_rel->exec_location; + joinrel->num_segments = outer_rel->num_segments; } } } @@ -1005,6 +1057,7 @@ build_join_rel(PlannerInfo *root, joinrel->rel_parallel_workers = -1; joinrel->amflags = 0; joinrel->serverid = InvalidOid; + joinrel->segSeverids = NIL; joinrel->userid = InvalidOid; joinrel->useridiscurrent = false; joinrel->exec_location = FTEXECLOCATION_NOT_DEFINED; @@ -1030,9 +1083,6 @@ build_join_rel(PlannerInfo *root, joinrel->partexprs = NULL; joinrel->nullable_partexprs = NULL; - /* Compute information relevant to the foreign relations. */ - set_foreign_rel_properties(joinrel, outer_rel, inner_rel); - /* * Create a new tlist containing just the vars that need to be output from * this join (ie, are needed for higher joinclauses or final output). @@ -1079,6 +1129,10 @@ build_join_rel(PlannerInfo *root, */ restrictlist = build_joinrel_restrictlist(root, joinrel, outer_rel, inner_rel); + + /* Compute information relevant to the foreign relations. */ + set_foreign_rel_properties(joinrel, outer_rel, inner_rel, restrictlist); + if (restrictlist_ptr) *restrictlist_ptr = restrictlist; build_joinrel_joinlist(joinrel, outer_rel, inner_rel); @@ -1223,6 +1277,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel, joinrel->subplan_params = NIL; joinrel->amflags = 0; joinrel->serverid = InvalidOid; + joinrel->segSeverids = NIL; joinrel->userid = InvalidOid; joinrel->useridiscurrent = false; joinrel->fdwroutine = NULL; @@ -1248,7 +1303,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel, inner_rel->top_parent_relids); /* Compute information relevant to foreign relations. */ - set_foreign_rel_properties(joinrel, outer_rel, inner_rel); + set_foreign_rel_properties(joinrel, outer_rel, inner_rel, restrictlist); /* Compute information needed for mapping Vars to the child rel */ appinfos = find_appinfos_by_relids(root, joinrel->relids, &nappinfos); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index eecfeea40dd..6d70cd69a0a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -279,7 +279,7 @@ static void check_expressions_in_partition_key(PartitionSpec *spec, core_yyscan_ } %type stmt toplevel_stmt schema_stmt routine_body_stmt - AlterEventTrigStmt AlterCollationStmt + AddForeignSegStmt AlterEventTrigStmt AlterCollationStmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDirectoryTableStmt AlterDomainStmt AlterEnumStmt AlterFdwStmt AlterForeignServerStmt AlterGroupStmt AlterObjectDependsStmt AlterObjectSchemaStmt AlterOwnerStmt @@ -1384,7 +1384,8 @@ toplevel_stmt: ; stmt: - AlterEventTrigStmt + AddForeignSegStmt + | AlterEventTrigStmt | AlterCollationStmt | AlterDatabaseStmt | AlterDatabaseSetStmt @@ -8254,6 +8255,23 @@ import_qualification: } ; +/***************************************************************************** + * + * QUERY: + * ADD SEG foreign_seg FROM SERVER server_name INTO foreign_table + * + *****************************************************************************/ + +AddForeignSegStmt: + ADD_P FOREIGN SEGMENT FROM SERVER name create_generic_options INTO name + { + AddForeignSegStmt *n = makeNode(AddForeignSegStmt); + n->servername = $6; + n->options = $7; + n->tablename = $9; + $$ = (Node *) n; + } + ; /***************************************************************************** * * QUERY: diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 0fcb3624846..c6ef9e79ff1 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -198,6 +198,7 @@ ClassifyUtilityCommandAsReadOnly(Node *parsetree) case T_CreateFdwStmt: case T_CreateForeignServerStmt: case T_CreateForeignTableStmt: + case T_AddForeignSegStmt: case T_CreateFunctionStmt: case T_CreateOpClassStmt: case T_CreateOpFamilyStmt: @@ -1657,7 +1658,13 @@ ProcessUtilitySlow(ParseState *pstate, commandCollected = true; } break; + case T_AddForeignSegStmt: + { + AddForeignSegStmt *afsstmt = (AddForeignSegStmt *) parsetree; + AddForeignSeg(afsstmt); + } + break; case T_AlterTableStmt: { AlterTableStmt *atstmt = (AlterTableStmt *) parsetree; @@ -3220,6 +3227,10 @@ CreateCommandTag(Node *parsetree) tag = CMDTAG_CREATE_FOREIGN_TABLE; break; + case T_AddForeignSegStmt: + tag = CMDTAG_ADD_FOREIGN_TABLE_SEG; + break; + case T_ImportForeignSchemaStmt: tag = CMDTAG_IMPORT_FOREIGN_SCHEMA; break; @@ -4067,6 +4078,10 @@ GetCommandLogLevel(Node *parsetree) lev = LOGSTMT_DDL; break; + case T_AddForeignSegStmt: + lev = LOGSTMT_DDL; + break; + case T_CreateExternalStmt: lev = LOGSTMT_DDL; break; diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 086d627d4d2..6e824d1df3d 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5920,6 +5920,7 @@ get_variable_numdistinct(VariableStatData *vardata, bool *isdefault) stadistinct = 1.0; /* only 1 value */ break; case GpSegmentIdAttributeNumber: /*CDB*/ + case GpForeignServerAttributeNumber: stadistinct = getgpsegmentCount(); break; default: diff --git a/src/include/access/sysattr.h b/src/include/access/sysattr.h index 41bd79f314a..f8157dd0e1a 100644 --- a/src/include/access/sysattr.h +++ b/src/include/access/sysattr.h @@ -25,6 +25,7 @@ #define MaxCommandIdAttributeNumber (-5) #define TableOidAttributeNumber (-6) #define GpSegmentIdAttributeNumber (-7) /*CDB*/ -#define FirstLowInvalidHeapAttributeNumber (-8) +#define GpForeignServerAttributeNumber (-8) /*CDB*/ +#define FirstLowInvalidHeapAttributeNumber (-9) #endif /* SYSATTR_H */ diff --git a/src/include/catalog/pg_foreign_table_seg.h b/src/include/catalog/pg_foreign_table_seg.h new file mode 100644 index 00000000000..717de444618 --- /dev/null +++ b/src/include/catalog/pg_foreign_table_seg.h @@ -0,0 +1,28 @@ +#ifndef PG_FOREIGN_TABLE_SEG_H +#define PG_FOREIGN_TABLE_SEG_H + +#include "catalog/genbki.h" +#include "catalog/pg_foreign_table_seg_d.h" + +/* ---------------- + * pg_foreign_table_seg definition. cpp turns this into + * typedef struct FormData_pg_foreign_table + * ---------------- + */ +CATALOG(pg_foreign_table_seg,5110,ForeignTableRelationSegId) +{ + Oid ftsrelid BKI_LOOKUP(pg_class); /* OID of foreign table */ + Oid ftsserver BKI_LOOKUP(pg_foreign_server); /* OID of foreign server */ + +#ifdef CATALOG_VARLEN /* variable-length fields start here */ + text ftsoptions[1]; /* FDW-specific options */ +#endif +} FormData_pg_foreign_table_seg; + +typedef FormData_pg_foreign_table_seg *Form_pg_foreign_table_seg; + +DECLARE_INDEX(pg_foreign_table_seg_relid_index, 5111, on pg_foreign_table_seg using btree(ftsrelid oid_ops)); + +#define GP_FOREIGN_SERVER_ID_FUNC 6024 + +#endif /* PG_FOREIGN_TABLE_SEG_H */ diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index b36f4c88388..a5b36770597 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -11930,6 +11930,9 @@ { oid => 6023, descr => 'Highest oid used so far', proname => 'pg_highest_oid', provolatile => 'v', proparallel => 'u', prorettype => 'oid', proargtypes => '', prosrc => 'pg_highest_oid', prodataaccess => 'r' }, +{ oid => 6024, descr => 'segment foreign server id function', + proname => 'gp_foreign_server_id', proisstrict => 'f', provolatile => 'v', prorettype => 'oid', proargtypes => 'oid', prosrc => 'gp_foreign_server_id' }, + { oid => 6035, descr => 'view mpp distributed transaction state', proname => 'gp_distributed_xacts', prorows => '1000', proisstrict => 'f', proretset => 't', provolatile => 'v', proparallel => 'u', prorettype => 'record', proargtypes => '', prosrc => 'gp_distributed_xacts__' }, diff --git a/src/include/cdb/cdbgang.h b/src/include/cdb/cdbgang.h index b344546e535..56ed9b85df2 100644 --- a/src/include/cdb/cdbgang.h +++ b/src/include/cdb/cdbgang.h @@ -48,6 +48,7 @@ extern int qe_identifier; extern int host_primary_segment_count; extern int ic_htab_size; +extern int qe_idx; extern MemoryContext GangContext; extern Gang *CurrentGangCreating; @@ -80,7 +81,8 @@ extern void ResetAllGangs(void); extern struct SegmentDatabaseDescriptor *getSegmentDescriptorFromGang(const Gang *gp, int seg); Gang *buildGangDefinition(List *segments, SegmentType segmentType); -bool build_gpqeid_param(char *buf, int bufsz, bool is_writer, int identifier, int hostSegs, int icHtabSize); +bool build_gpqeid_param(char *buf, int bufsz, bool is_writer, int identifier, int hostSegs, + int icHtabSize, int qeidx); extern void makeOptions(char **options, char **diff_options); extern bool segment_failure_due_to_recovery(const char *error_message); diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index f632b4291d1..57a49126c0b 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -141,6 +141,7 @@ extern ObjectAddress CreateStorageUserMapping(CreateStorageUserMappingStmt *stmt extern ObjectAddress AlterStorageUserMapping(AlterStorageUserMappingStmt *stmt); extern Oid RemoveStorageUserMapping(DropStorageUserMappingStmt *stmt); extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid, bool skip_permission_check); +extern void AddForeignSeg(AddForeignSegStmt *stmt); extern void ImportForeignSchema(ImportForeignSchemaStmt *stmt); extern Datum transformGenericOptions(Oid catalogId, Datum oldOptions, diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h index c8226060067..6d7c289f6a8 100644 --- a/src/include/foreign/foreign.h +++ b/src/include/foreign/foreign.h @@ -89,6 +89,8 @@ extern List *GetForeignColumnOptions(Oid relid, AttrNumber attnum); extern Oid get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok); extern Oid get_foreign_server_oid(const char *servername, bool missing_ok); +extern Oid GetForeignServerSegByRelid(Oid tableOid); +extern List *GetForeignServerSegsByRelId(Oid relid); /* ---------------- * compiler constants for ForeignTable's exec_location diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 65af47ad2af..75ceaf3a2df 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -559,6 +559,7 @@ typedef enum NodeTag T_GpAlterPartitionCmd, T_CreateWarehouseStmt, T_DropWarehouseStmt, + T_AddForeignSegStmt, /* * TAGS FOR PARSE TREE NODES (parsenodes.h) diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 51229924c67..0e416ca0dae 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3039,6 +3039,14 @@ typedef struct ImportForeignSchemaStmt List *options; /* list of options to pass to FDW */ } ImportForeignSchemaStmt; +typedef struct AddForeignSegStmt +{ + NodeTag type; + char *servername; + char *tablename; + List *options; +} AddForeignSegStmt; + /*---------------------- * Create POLICY Statement *---------------------- diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 695b4dbc5e3..89d3336163a 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -922,6 +922,7 @@ typedef struct RelOptInfo /* Information about foreign tables and foreign joins */ Oid serverid; /* identifies server for the table or join */ + List *segSeverids; /* server segments for mpp fdw */ Oid userid; /* identifies user to check access as */ bool useridiscurrent; /* join is only valid for current user */ char exec_location; /* execute on MASTER, ANY or ALL SEGMENTS, Cloudberry MPP specific */ diff --git a/src/include/tcop/cmdtaglist.h b/src/include/tcop/cmdtaglist.h index dd63c7659e4..b0346e2b9b1 100644 --- a/src/include/tcop/cmdtaglist.h +++ b/src/include/tcop/cmdtaglist.h @@ -26,6 +26,7 @@ /* symbol name, textual name, event_trigger_ok, table_rewrite_ok, rowcount */ PG_CMDTAG(CMDTAG_UNKNOWN, "???", false, false, false) +PG_CMDTAG(CMDTAG_ADD_FOREIGN_TABLE_SEG, "ADD FOREIGN TABLE SEG", true, false, false) PG_CMDTAG(CMDTAG_ALTER_ACCESS_METHOD, "ALTER ACCESS METHOD", true, false, false) PG_CMDTAG(CMDTAG_ALTER_AGGREGATE, "ALTER AGGREGATE", true, false, false) PG_CMDTAG(CMDTAG_ALTER_CAST, "ALTER CAST", true, false, false) diff --git a/src/test/isolation2/expected/modify_table_data_corrupt_optimizer.out b/src/test/isolation2/expected/modify_table_data_corrupt_optimizer.out index 8f828bddab2..f3ec88f69af 100644 --- a/src/test/isolation2/expected/modify_table_data_corrupt_optimizer.out +++ b/src/test/isolation2/expected/modify_table_data_corrupt_optimizer.out @@ -131,13 +131,13 @@ explain (costs off) delete from tab1 using tab2, tab3 where tab1.a = tab2.a and -> Redistribute Motion 3:3 (slice1; segments: 3) Hash Key: tab1_1.b -> Hash Join - Hash Cond: (tab3.a = tab1_1.b) - -> Seq Scan on tab3 + Hash Cond: (tab2.a = tab1_1.b) + -> Seq Scan on tab2 -> Hash -> Broadcast Motion 3:3 (slice2; segments: 3) -> Hash Join - Hash Cond: (tab2.a = tab1_1.a) - -> Seq Scan on tab2 + Hash Cond: (tab3.a = tab1_1.a) + -> Seq Scan on tab3 -> Hash -> Broadcast Motion 3:3 (slice3; segments: 3) -> Seq Scan on tab1 tab1_1 @@ -162,13 +162,13 @@ explain (costs off) update tab1 set a = 999 from tab2, tab3 where tab1.a = tab2. Hash Key: tab1_1.b -> Split -> Hash Join - Hash Cond: (tab3.a = tab1_1.b) - -> Seq Scan on tab3 + Hash Cond: (tab2.a = tab1_1.b) + -> Seq Scan on tab2 -> Hash -> Broadcast Motion 3:3 (slice2; segments: 3) -> Hash Join - Hash Cond: (tab2.a = tab1_1.a) - -> Seq Scan on tab2 + Hash Cond: (tab3.a = tab1_1.a) + -> Seq Scan on tab3 -> Hash -> Broadcast Motion 3:3 (slice3; segments: 3) -> Seq Scan on tab1 tab1_1 diff --git a/src/test/regress/expected/gporca_optimizer.out b/src/test/regress/expected/gporca_optimizer.out index 6c43ab45982..eda9948ba65 100644 --- a/src/test/regress/expected/gporca_optimizer.out +++ b/src/test/regress/expected/gporca_optimizer.out @@ -12621,7 +12621,7 @@ where out.b in (select coalesce(tcorr2.a, 99) from tcorr1 left outer join tcorr2 on tcorr1.a=tcorr2.a+out.a); QUERY PLAN -------------------------------------------------------------------------------------------------- - Result (cost=0.00..1356692026.74 rows=1 width=8) + Result (cost=0.00..1356692031.36 rows=1 width=8) Filter: (SubPlan 1) -> Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..431.00 rows=1 width=8) -> Seq Scan on tcorr1 (cost=0.00..431.00 rows=1 width=8) @@ -12655,7 +12655,7 @@ where out.b in (select max(tcorr2.b + out.b - 1) where tcorr2.a=out.a); QUERY PLAN -------------------------------------------------------------------------------------------------------- - Result (cost=0.00..1324032.62 rows=1 width=8) + Result (cost=0.00..1324032.63 rows=1 width=8) Filter: (SubPlan 1) -> Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..431.00 rows=1 width=8) -> Seq Scan on tcorr1 (cost=0.00..431.00 rows=1 width=8) @@ -12690,12 +12690,12 @@ where out.b in (select coalesce(tcorr2_d.c, 99) group by a) tcorr2_d on tcorr1.a=tcorr2_d.a); QUERY PLAN -------------------------------------------------------------------------------------------------------------------- - Result (cost=0.00..1356692176.26 rows=1 width=8) + Result (cost=0.00..1356692180.88 rows=1 width=8) Filter: (SubPlan 1) -> Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..431.00 rows=1 width=8) -> Seq Scan on tcorr1 (cost=0.00..431.00 rows=1 width=8) SubPlan 1 - -> Nested Loop Left Join (cost=0.00..1324032.70 rows=3 width=8) + -> Nested Loop Left Join (cost=0.00..1324032.71 rows=3 width=8) Join Filter: (tcorr1_1.a = tcorr2.a) -> Materialize (cost=0.00..431.00 rows=1 width=4) -> Gather Motion 3:1 (slice3; segments: 3) (cost=0.00..431.00 rows=1 width=4) @@ -12745,7 +12745,7 @@ where out.b in (select coalesce(tcorr2.a, 99) from tcorr1 left outer join tcorr2 on tcorr1.a=tcorr2.a+out.a); QUERY PLAN -------------------------------------------------------------------------------------------------- - Result (cost=0.00..1356692026.74 rows=1 width=8) + Result (cost=0.00..1356692031.36 rows=1 width=8) Filter: (SubPlan 1) -> Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..431.00 rows=1 width=8) -> Seq Scan on tcorr1 (cost=0.00..431.00 rows=1 width=8) @@ -12779,7 +12779,7 @@ where out.b in (select max(tcorr2.b + out.b - 1) where tcorr2.a=out.a); QUERY PLAN -------------------------------------------------------------------------------------------------------- - Result (cost=0.00..1324032.62 rows=1 width=8) + Result (cost=0.00..1324032.63 rows=1 width=8) Filter: (SubPlan 1) -> Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..431.00 rows=1 width=8) -> Seq Scan on tcorr1 (cost=0.00..431.00 rows=1 width=8) @@ -12814,12 +12814,12 @@ where out.b in (select coalesce(tcorr2_d.c, 99) group by a) tcorr2_d on tcorr1.a=tcorr2_d.a); QUERY PLAN -------------------------------------------------------------------------------------------------------------------- - Result (cost=0.00..1356692176.26 rows=1 width=8) + Result (cost=0.00..1356692180.88 rows=1 width=8) Filter: (SubPlan 1) -> Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..431.00 rows=1 width=8) -> Seq Scan on tcorr1 (cost=0.00..431.00 rows=1 width=8) SubPlan 1 - -> Nested Loop Left Join (cost=0.00..1324032.70 rows=3 width=8) + -> Nested Loop Left Join (cost=0.00..1324032.71 rows=3 width=8) Join Filter: (tcorr1_1.a = tcorr2.a) -> Materialize (cost=0.00..431.00 rows=1 width=4) -> Gather Motion 3:1 (slice3; segments: 3) (cost=0.00..431.00 rows=1 width=4) diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out index a05cf9aba47..54e86e6e7a1 100644 --- a/src/test/regress/expected/misc_sanity.out +++ b/src/test/regress/expected/misc_sanity.out @@ -113,6 +113,7 @@ ORDER BY 1, 2; pg_class | relacl | aclitem[] pg_class | reloptions | text[] pg_class | relpartbound | pg_node_tree + pg_foreign_table_seg | ftsoptions | text[] pg_index | indexprs | pg_node_tree pg_index | indpred | pg_node_tree pg_largeobject | data | bytea @@ -165,6 +166,7 @@ ORDER BY 1; pg_compression pg_depend pg_extprotocol + pg_foreign_table_seg pg_password_history pg_proc_callback pg_profile diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out index 0ae8a90e731..f739326012d 100644 --- a/src/test/regress/expected/oidjoins.out +++ b/src/test/regress/expected/oidjoins.out @@ -245,6 +245,8 @@ NOTICE: checking pg_compression {compvalidator} => pg_proc {oid} NOTICE: checking pg_compression {compowner} => pg_authid {oid} NOTICE: checking pg_foreign_table {ftrelid} => pg_class {oid} NOTICE: checking pg_foreign_table {ftserver} => pg_foreign_server {oid} +NOTICE: checking pg_foreign_table_seg {ftsrelid} => pg_class {oid} +NOTICE: checking pg_foreign_table_seg {ftsserver} => pg_foreign_server {oid} NOTICE: checking pg_policy {polrelid} => pg_class {oid} NOTICE: checking pg_policy {polroles} => pg_authid {oid} NOTICE: checking pg_default_acl {defaclrole} => pg_authid {oid} diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index 2c8d696a0b1..6a79a5b3365 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -136,6 +136,7 @@ pg_extprotocol|t pg_foreign_data_wrapper|t pg_foreign_server|t pg_foreign_table|t +pg_foreign_table_seg|t pg_index|t pg_inherits|t pg_init_privs|t diff --git a/src/test/regress/expected/tsrf_optimizer.out b/src/test/regress/expected/tsrf_optimizer.out index 1df9437845e..117522c09e0 100644 --- a/src/test/regress/expected/tsrf_optimizer.out +++ b/src/test/regress/expected/tsrf_optimizer.out @@ -92,7 +92,7 @@ SELECT unnest(ARRAY[1, 2]) FROM few WHERE false; ProjectSet Output: unnest('{1,2}'::integer[]) -> Result - Output: NULL::integer, NULL::tid, NULL::xid, NULL::cid, NULL::xid, NULL::cid, NULL::oid, NULL::integer + Output: NULL::integer, NULL::tid, NULL::xid, NULL::cid, NULL::xid, NULL::cid, NULL::oid, NULL::integer, NULL::integer One-Time Filter: false Optimizer: Pivotal Optimizer (GPORCA) (6 rows) @@ -120,7 +120,7 @@ SELECT * FROM few f1, -> ProjectSet Output: unnest('{1,2}'::integer[]) -> Result - Output: NULL::integer, NULL::tid, NULL::xid, NULL::cid, NULL::xid, NULL::cid, NULL::oid, NULL::integer + Output: NULL::integer, NULL::tid, NULL::xid, NULL::cid, NULL::xid, NULL::cid, NULL::oid, NULL::integer, NULL::integer One-Time Filter: false Optimizer: Pivotal Optimizer (GPORCA) (15 rows) diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out index 4ef7e8ca4fc..ff703938885 100644 --- a/src/test/regress/expected/update.out +++ b/src/test/regress/expected/update.out @@ -856,7 +856,7 @@ insert into utrtest values (1, 'foo') insert into utrtest values (2, 'bar') returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; -- fails -ERROR: virtual tuple table slot does not have system attributes (execTuples.c:156) +ERROR: virtual tuple table slot does not have system attributes (execTuples.c:163) insert into utrtest values (2, 'bar') returning *, tableoid::regclass; a | b | tableoid @@ -874,7 +874,7 @@ update utrtest set b = b || b from (values (1), (2)) s(x) where a = s.x update utrtest set a = 3 - a from (values (1), (2)) s(x) where a = s.x returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; -- fails -ERROR: virtual tuple table slot does not have system attributes (execTuples.c:156) +ERROR: virtual tuple table slot does not have system attributes (execTuples.c:163) update utrtest set a = 3 - a from (values (1), (2)) s(x) where a = s.x returning *, tableoid::regclass; a | b | x | tableoid diff --git a/src/test/regress/expected/update_optimizer.out b/src/test/regress/expected/update_optimizer.out index 41fe48f6fa4..a3335abbe4d 100755 --- a/src/test/regress/expected/update_optimizer.out +++ b/src/test/regress/expected/update_optimizer.out @@ -857,7 +857,7 @@ insert into utrtest values (1, 'foo') insert into utrtest values (2, 'bar') returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; -- fails -ERROR: virtual tuple table slot does not have system attributes (execTuples.c:156) +ERROR: virtual tuple table slot does not have system attributes (execTuples.c:163) (seg0 slice1 127.0.1.1:7002 pid=1613506) (execTuples.c:163) insert into utrtest values (2, 'bar') returning *, tableoid::regclass; a | b | tableoid @@ -875,7 +875,7 @@ update utrtest set b = b || b from (values (1), (2)) s(x) where a = s.x update utrtest set a = 3 - a from (values (1), (2)) s(x) where a = s.x returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; -- fails -ERROR: virtual tuple table slot does not have system attributes (execTuples.c:156) +ERROR: virtual tuple table slot does not have system attributes (execTuples.c:163) (seg0 slice1 127.0.1.1:7002 pid=1613506) (execTuples.c:163) update utrtest set a = 3 - a from (values (1), (2)) s(x) where a = s.x returning *, tableoid::regclass; a | b | x | tableoid diff --git a/src/test/singlenode_regress/expected/misc_sanity.out b/src/test/singlenode_regress/expected/misc_sanity.out index a05cf9aba47..54e86e6e7a1 100644 --- a/src/test/singlenode_regress/expected/misc_sanity.out +++ b/src/test/singlenode_regress/expected/misc_sanity.out @@ -113,6 +113,7 @@ ORDER BY 1, 2; pg_class | relacl | aclitem[] pg_class | reloptions | text[] pg_class | relpartbound | pg_node_tree + pg_foreign_table_seg | ftsoptions | text[] pg_index | indexprs | pg_node_tree pg_index | indpred | pg_node_tree pg_largeobject | data | bytea @@ -165,6 +166,7 @@ ORDER BY 1; pg_compression pg_depend pg_extprotocol + pg_foreign_table_seg pg_password_history pg_proc_callback pg_profile diff --git a/src/test/singlenode_regress/expected/oidjoins.out b/src/test/singlenode_regress/expected/oidjoins.out index 87502771aae..6f9ee5fee87 100644 --- a/src/test/singlenode_regress/expected/oidjoins.out +++ b/src/test/singlenode_regress/expected/oidjoins.out @@ -245,6 +245,8 @@ NOTICE: checking pg_compression {compvalidator} => pg_proc {oid} NOTICE: checking pg_compression {compowner} => pg_authid {oid} NOTICE: checking pg_foreign_table {ftrelid} => pg_class {oid} NOTICE: checking pg_foreign_table {ftserver} => pg_foreign_server {oid} +NOTICE: checking pg_foreign_table_seg {ftsrelid} => pg_class {oid} +NOTICE: checking pg_foreign_table_seg {ftsserver} => pg_foreign_server {oid} NOTICE: checking pg_policy {polrelid} => pg_class {oid} NOTICE: checking pg_policy {polroles} => pg_authid {oid} NOTICE: checking pg_default_acl {defaclrole} => pg_authid {oid} diff --git a/src/test/singlenode_regress/expected/sanity_check.out b/src/test/singlenode_regress/expected/sanity_check.out index 484b3ea50d6..588ce5b2abc 100644 --- a/src/test/singlenode_regress/expected/sanity_check.out +++ b/src/test/singlenode_regress/expected/sanity_check.out @@ -136,6 +136,7 @@ pg_extprotocol|t pg_foreign_data_wrapper|t pg_foreign_server|t pg_foreign_table|t +pg_foreign_table_seg|t pg_index|t pg_inherits|t pg_init_privs|t diff --git a/src/test/singlenode_regress/expected/update.out b/src/test/singlenode_regress/expected/update.out index 34f0bfafe65..30e79d9ff20 100644 --- a/src/test/singlenode_regress/expected/update.out +++ b/src/test/singlenode_regress/expected/update.out @@ -866,7 +866,7 @@ insert into utrtest values (1, 'foo') insert into utrtest values (2, 'bar') returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; -- fails -ERROR: virtual tuple table slot does not have system attributes (execTuples.c:156) +ERROR: virtual tuple table slot does not have system attributes (execTuples.c:163) insert into utrtest values (2, 'bar') returning *, tableoid::regclass; a | b | tableoid @@ -884,7 +884,7 @@ update utrtest set b = b || b from (values (1), (2)) s(x) where a = s.x update utrtest set a = 3 - a from (values (1), (2)) s(x) where a = s.x returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; -- fails -ERROR: virtual tuple table slot does not have system attributes (execTuples.c:156) +ERROR: virtual tuple table slot does not have system attributes (execTuples.c:163) update utrtest set a = 3 - a from (values (1), (2)) s(x) where a = s.x returning *, tableoid::regclass; a | b | x | tableoid diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index c496a76a87a..008762da4e6 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -24,6 +24,7 @@ AclResult AcquireSampleRowsFunc ActionList ActiveSnapshotElt +AddForeignSegStmt AddForeignUpdateTargets_function AffixNode AffixNodeData From 68cc932145432ea581e1123d0342e7cb2964b726 Mon Sep 17 00:00:00 2001 From: Jinbao Chen Date: Tue, 23 Jul 2024 11:06:12 +0800 Subject: [PATCH 3/5] test --- src/test/regress/expected/misc_sanity.out | 4 ++-- src/test/singlenode_regress/expected/misc_sanity.out | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out index 54e86e6e7a1..88ae3d4e927 100644 --- a/src/test/regress/expected/misc_sanity.out +++ b/src/test/regress/expected/misc_sanity.out @@ -137,7 +137,7 @@ ORDER BY 1, 2; pg_task_run_history | return_message | text pg_task_run_history | status | text pg_task_run_history | username | text -(33 rows) +(34 rows) -- system catalogs without primary keys -- @@ -179,7 +179,7 @@ ORDER BY 1; pg_stat_last_operation pg_stat_last_shoperation pg_type_encoding -(27 rows) +(28 rows) -- system catalog unique indexes not wrapped in a constraint -- (There should be none.) diff --git a/src/test/singlenode_regress/expected/misc_sanity.out b/src/test/singlenode_regress/expected/misc_sanity.out index 54e86e6e7a1..88ae3d4e927 100644 --- a/src/test/singlenode_regress/expected/misc_sanity.out +++ b/src/test/singlenode_regress/expected/misc_sanity.out @@ -137,7 +137,7 @@ ORDER BY 1, 2; pg_task_run_history | return_message | text pg_task_run_history | status | text pg_task_run_history | username | text -(33 rows) +(34 rows) -- system catalogs without primary keys -- @@ -179,7 +179,7 @@ ORDER BY 1; pg_stat_last_operation pg_stat_last_shoperation pg_type_encoding -(27 rows) +(28 rows) -- system catalog unique indexes not wrapped in a constraint -- (There should be none.) From 3c09b65ae41d3715f8daae65f63d549a0497f393 Mon Sep 17 00:00:00 2001 From: Jinbao Chen Date: Wed, 24 Jul 2024 10:21:05 +0800 Subject: [PATCH 4/5] test --- src/test/regress/expected/misc_sanity_external_fts.out | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/regress/expected/misc_sanity_external_fts.out b/src/test/regress/expected/misc_sanity_external_fts.out index ca783a4aa70..970030b7cb8 100644 --- a/src/test/regress/expected/misc_sanity_external_fts.out +++ b/src/test/regress/expected/misc_sanity_external_fts.out @@ -112,6 +112,7 @@ ORDER BY 1, 2; pg_class | relacl | aclitem[] pg_class | reloptions | text[] pg_class | relpartbound | pg_node_tree + pg_foreign_table_seg | ftsoptions | text[] pg_index | indexprs | pg_node_tree pg_index | indpred | pg_node_tree pg_largeobject | data | bytea @@ -135,7 +136,7 @@ ORDER BY 1, 2; pg_task_run_history | return_message | text pg_task_run_history | status | text pg_task_run_history | username | text -(33 rows) +(34 rows) -- system catalogs without primary keys -- @@ -164,6 +165,7 @@ ORDER BY 1; pg_compression pg_depend pg_extprotocol + pg_foreign_table_seg pg_password_history pg_proc_callback pg_profile @@ -176,7 +178,7 @@ ORDER BY 1; pg_stat_last_operation pg_stat_last_shoperation pg_type_encoding -(27 rows) +(28 rows) -- system catalog unique indexes not wrapped in a constraint -- (There should be none.) From 2a4f61ce56e292a9dc907c1910e16b22deb7fabe Mon Sep 17 00:00:00 2001 From: oppenheimer01 Date: Fri, 26 Jul 2024 04:52:04 +0000 Subject: [PATCH 5/5] test --- .../modify_table_data_corrupt_optimizer.out | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/isolation2/expected/modify_table_data_corrupt_optimizer.out b/src/test/isolation2/expected/modify_table_data_corrupt_optimizer.out index f3ec88f69af..9fc7271eabd 100644 --- a/src/test/isolation2/expected/modify_table_data_corrupt_optimizer.out +++ b/src/test/isolation2/expected/modify_table_data_corrupt_optimizer.out @@ -131,13 +131,13 @@ explain (costs off) delete from tab1 using tab2, tab3 where tab1.a = tab2.a and -> Redistribute Motion 3:3 (slice1; segments: 3) Hash Key: tab1_1.b -> Hash Join - Hash Cond: (tab2.a = tab1_1.b) - -> Seq Scan on tab2 + Hash Cond: (tab2.a = tab1_1.a) + -> Seq Scan on tab2 -> Hash -> Broadcast Motion 3:3 (slice2; segments: 3) -> Hash Join - Hash Cond: (tab3.a = tab1_1.a) - -> Seq Scan on tab3 + Hash Cond: (tab3.a = tab1_1.b) + -> Seq Scan on tab3 -> Hash -> Broadcast Motion 3:3 (slice3; segments: 3) -> Seq Scan on tab1 tab1_1 @@ -162,13 +162,13 @@ explain (costs off) update tab1 set a = 999 from tab2, tab3 where tab1.a = tab2. Hash Key: tab1_1.b -> Split -> Hash Join - Hash Cond: (tab2.a = tab1_1.b) - -> Seq Scan on tab2 + Hash Cond: (tab2.a = tab1_1.a) + -> Seq Scan on tab2 -> Hash -> Broadcast Motion 3:3 (slice2; segments: 3) -> Hash Join - Hash Cond: (tab3.a = tab1_1.a) - -> Seq Scan on tab3 + Hash Cond: (tab3.a = tab1_1.b) + -> Seq Scan on tab3 -> Hash -> Broadcast Motion 3:3 (slice3; segments: 3) -> Seq Scan on tab1 tab1_1