Skip to content

Commit fd5a274

Browse files
pyhalovdanolivo
authored andcommitted
[SHRDM-762] extract info from foreign join
1 parent f2caa7c commit fd5a274

File tree

3 files changed

+114
-5
lines changed

3 files changed

+114
-5
lines changed

expected/aqo_fdw.out

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,39 @@ SELECT str FROM expln('
127127
JOINS: 0
128128
(8 rows)
129129

130+
CREATE TABLE local_a(aid int primary key, aval text);
131+
CREATE TABLE local_b(bid int primary key, aid int references local_a(aid), bval text);
132+
INSERT INTO local_a SELECT i, 'val_' || i FROM generate_series(1,100) i;
133+
INSERT INTO local_b SELECT i, mod((i+random()*10)::numeric, 10) + 1, 'val_' || i FROM generate_series(1,1000) i;
134+
ANALYZE local_a, local_b;
135+
CREATE FOREIGN TABLE frgn_a(aid int, aval text) SERVER loopback OPTIONS (table_name 'local_a');
136+
CREATE FOREIGN TABLE frgn_b(bid int, aid int, bval text) SERVER loopback OPTIONS (table_name 'local_b');
137+
EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF)
138+
SELECT * from frgn_a AS a, frgn_b AS b
139+
WHERE a.aid = b.aid AND b.bval like 'val%';
140+
QUERY PLAN
141+
-----------------------------------------------
142+
Foreign Scan (actual rows=1000 loops=1)
143+
AQO not used
144+
Relations: (frgn_a a) INNER JOIN (frgn_b b)
145+
Using aqo: true
146+
AQO mode: LEARN
147+
JOINS: 0
148+
(6 rows)
149+
150+
EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF)
151+
SELECT * from frgn_a AS a, frgn_b AS b
152+
WHERE a.aid = b.aid AND b.bval like 'val%';
153+
QUERY PLAN
154+
-----------------------------------------------
155+
Foreign Scan (actual rows=1000 loops=1)
156+
AQO: rows=1000, error=0%
157+
Relations: (frgn_a a) INNER JOIN (frgn_b b)
158+
Using aqo: true
159+
AQO mode: LEARN
160+
JOINS: 0
161+
(6 rows)
162+
130163
-- TODO: Non-mergejoinable join condition.
131164
EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF)
132165
SELECT * FROM frgn AS a, frgn AS b WHERE a.x<b.x;
@@ -147,7 +180,7 @@ SELECT str FROM expln('
147180
str
148181
--------------------------------------------------------------------------------------------------------
149182
Foreign Scan (actual rows=0 loops=1)
150-
AQO not used
183+
AQO: rows=1, error=100%
151184
Output: a.x, b.x
152185
Relations: (public.frgn a) INNER JOIN (public.frgn b)
153186
Remote SQL: SELECT r1.x, r2.x FROM (public.local r1 INNER JOIN public.local r2 ON (((r1.x < r2.x))))
@@ -158,8 +191,12 @@ SELECT str FROM expln('
158191

159192
DROP EXTENSION aqo CASCADE;
160193
DROP EXTENSION postgres_fdw CASCADE;
161-
NOTICE: drop cascades to 3 other objects
194+
NOTICE: drop cascades to 5 other objects
162195
DETAIL: drop cascades to server loopback
163196
drop cascades to user mapping for public on server loopback
164197
drop cascades to foreign table frgn
198+
drop cascades to foreign table frgn_a
199+
drop cascades to foreign table frgn_b
165200
DROP TABLE local;
201+
DROP TABLE local_b;
202+
DROP TABLE local_a;

path_utils.c

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
#include "nodes/readfuncs.h"
1818
#include "optimizer/optimizer.h"
1919
#include "path_utils.h"
20+
#include "postgres_fdw.h"
2021
#include "utils/syscache.h"
2122
#include "utils/lsyscache.h"
2223

2324
#include "aqo.h"
2425
#include "hash.h"
2526

27+
2628
/*
2729
* Hook on creation of a plan node. We need to store AQO-specific data to
2830
* support learning stage.
@@ -60,6 +62,31 @@ create_aqo_plan_node()
6062
return node;
6163
}
6264

65+
66+
/* Ensure that it's postgres_fdw's foreign server oid */
67+
static bool
68+
is_postgres_fdw_server(Oid serverid)
69+
{
70+
ForeignServer *server;
71+
ForeignDataWrapper *fdw;
72+
73+
if (!OidIsValid(serverid))
74+
return false;
75+
76+
server = GetForeignServerExtended(serverid, FSV_MISSING_OK);
77+
if (!server)
78+
return false;
79+
80+
fdw = GetForeignDataWrapperExtended(server->fdwid, FDW_MISSING_OK);
81+
if (!fdw || !fdw->fdwname)
82+
return false;
83+
84+
if (strcmp(fdw->fdwname, "postgres_fdw") != 0)
85+
return false;
86+
87+
return true;
88+
}
89+
6390
/*
6491
* Extract an AQO node from the plan private field.
6592
* If no one node was found, return pointer to the default value or return NULL.
@@ -497,7 +524,8 @@ aqo_create_plan_hook(PlannerInfo *root, Path *src, Plan **dest)
497524
return;
498525

499526
is_join_path = (src->type == T_NestPath || src->type == T_MergePath ||
500-
src->type == T_HashPath);
527+
src->type == T_HashPath ||
528+
(src->type == T_ForeignPath && IS_JOIN_REL(src->parent)));
501529

502530
node = get_aqo_plan_node(plan, true);
503531

@@ -513,8 +541,32 @@ aqo_create_plan_hook(PlannerInfo *root, Path *src, Plan **dest)
513541

514542
if (is_join_path)
515543
{
516-
node->clauses = aqo_get_clauses(root, ((JoinPath *) src)->joinrestrictinfo);
517-
node->jointype = ((JoinPath *) src)->jointype;
544+
if (IsA(src, ForeignPath))
545+
{
546+
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) src->parent->fdw_private;
547+
List *restrictclauses = NIL;
548+
549+
if (!fpinfo)
550+
return;
551+
552+
/* We have to ensure that this is postgres_fdw ForeignPath */
553+
if (!is_postgres_fdw_server(src->parent->serverid))
554+
return;
555+
556+
restrictclauses = list_concat(restrictclauses, fpinfo->joinclauses);
557+
restrictclauses = list_concat(restrictclauses, fpinfo->remote_conds);
558+
restrictclauses = list_concat(restrictclauses, fpinfo->local_conds);
559+
560+
node->clauses = aqo_get_clauses(root, restrictclauses);
561+
node->jointype = fpinfo->jointype;
562+
563+
list_free(restrictclauses);
564+
}
565+
else
566+
{
567+
node->clauses = aqo_get_clauses(root, ((JoinPath *) src)->joinrestrictinfo);
568+
node->jointype = ((JoinPath *) src)->jointype;
569+
}
518570
}
519571
else if (IsA(src, AggPath))
520572
/* Aggregation node must store grouping clauses. */

sql/aqo_fdw.sql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ SELECT str FROM expln('
5252
EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, VERBOSE)
5353
SELECT x FROM frgn WHERE x < 10;
5454
') AS str WHERE str NOT LIKE '%Query Identifier%';
55+
5556
EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF)
5657
SELECT x FROM frgn WHERE x < -10; -- AQO ignores constants
5758

@@ -67,6 +68,23 @@ SELECT str FROM expln('
6768
SELECT * FROM frgn AS a, frgn AS b WHERE a.x=b.x;
6869
') AS str WHERE str NOT LIKE '%Query Identifier%';
6970

71+
CREATE TABLE local_a(aid int primary key, aval text);
72+
CREATE TABLE local_b(bid int primary key, aid int references local_a(aid), bval text);
73+
INSERT INTO local_a SELECT i, 'val_' || i FROM generate_series(1,100) i;
74+
INSERT INTO local_b SELECT i, mod((i+random()*10)::numeric, 10) + 1, 'val_' || i FROM generate_series(1,1000) i;
75+
ANALYZE local_a, local_b;
76+
77+
CREATE FOREIGN TABLE frgn_a(aid int, aval text) SERVER loopback OPTIONS (table_name 'local_a');
78+
CREATE FOREIGN TABLE frgn_b(bid int, aid int, bval text) SERVER loopback OPTIONS (table_name 'local_b');
79+
80+
EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF)
81+
SELECT * from frgn_a AS a, frgn_b AS b
82+
WHERE a.aid = b.aid AND b.bval like 'val%';
83+
84+
EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF)
85+
SELECT * from frgn_a AS a, frgn_b AS b
86+
WHERE a.aid = b.aid AND b.bval like 'val%';
87+
7088
-- TODO: Non-mergejoinable join condition.
7189
EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF)
7290
SELECT * FROM frgn AS a, frgn AS b WHERE a.x<b.x;
@@ -78,4 +96,6 @@ SELECT str FROM expln('
7896
DROP EXTENSION aqo CASCADE;
7997
DROP EXTENSION postgres_fdw CASCADE;
8098
DROP TABLE local;
99+
DROP TABLE local_b;
100+
DROP TABLE local_a;
81101

0 commit comments

Comments
 (0)