Skip to content

Commit 824dbea

Browse files
committed
Add support for deparsing semi-joins to contrib/postgres_fdw
SEMI-JOIN is deparsed as the EXISTS subquery. It references outer and inner relations, so it should be evaluated as the condition in the upper-level WHERE clause. The signatures of deparseFromExprForRel() and deparseRangeTblRef() are revised so that they can add conditions to the upper level. PgFdwRelationInfo now has a hidden_subquery_rels field, referencing the relids used in the inner parts of semi-join. They can't be referred to from upper relations and should be used internally for equivalence member searches. The planner can create semi-join, which refers to inner rel vars in its target list. However, we deparse semi-join as an exists() subquery. So we skip the case when the target list references to inner rel of semi-join. Author: Alexander Pyhalov Reviewed-by: Ashutosh Bapat, Ian Lawrence Barwick, Yuuki Fujii, Tomas Vondra Discussion: https://postgr.es/m/c9e2a757cf3ac2333714eaf83a9cc184@postgrespro.ru
1 parent 278eb13 commit 824dbea

File tree

5 files changed

+697
-81
lines changed

5 files changed

+697
-81
lines changed

contrib/postgres_fdw/deparse.c

Lines changed: 188 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,15 @@ static void appendConditions(List *exprs, deparse_expr_cxt *context);
180180
static void deparseFromExprForRel(StringInfo buf, PlannerInfo *root,
181181
RelOptInfo *foreignrel, bool use_alias,
182182
Index ignore_rel, List **ignore_conds,
183+
List **additional_conds,
183184
List **params_list);
185+
static void appendWhereClause(List *exprs, List *additional_conds,
186+
deparse_expr_cxt *context);
184187
static void deparseFromExpr(List *quals, deparse_expr_cxt *context);
185188
static void deparseRangeTblRef(StringInfo buf, PlannerInfo *root,
186189
RelOptInfo *foreignrel, bool make_subquery,
187-
Index ignore_rel, List **ignore_conds, List **params_list);
190+
Index ignore_rel, List **ignore_conds,
191+
List **additional_conds, List **params_list);
188192
static void deparseAggref(Aggref *node, deparse_expr_cxt *context);
189193
static void appendGroupByClause(List *tlist, deparse_expr_cxt *context);
190194
static void appendOrderBySuffix(Oid sortop, Oid sortcoltype, bool nulls_first,
@@ -1370,6 +1374,7 @@ deparseFromExpr(List *quals, deparse_expr_cxt *context)
13701374
{
13711375
StringInfo buf = context->buf;
13721376
RelOptInfo *scanrel = context->scanrel;
1377+
List *additional_conds = NIL;
13731378

13741379
/* For upper relations, scanrel must be either a joinrel or a baserel */
13751380
Assert(!IS_UPPER_REL(context->foreignrel) ||
@@ -1379,14 +1384,11 @@ deparseFromExpr(List *quals, deparse_expr_cxt *context)
13791384
appendStringInfoString(buf, " FROM ");
13801385
deparseFromExprForRel(buf, context->root, scanrel,
13811386
(bms_membership(scanrel->relids) == BMS_MULTIPLE),
1382-
(Index) 0, NULL, context->params_list);
1383-
1384-
/* Construct WHERE clause */
1385-
if (quals != NIL)
1386-
{
1387-
appendStringInfoString(buf, " WHERE ");
1388-
appendConditions(quals, context);
1389-
}
1387+
(Index) 0, NULL, &additional_conds,
1388+
context->params_list);
1389+
appendWhereClause(quals, additional_conds, context);
1390+
if (additional_conds != NIL)
1391+
list_free_deep(additional_conds);
13901392
}
13911393

13921394
/*
@@ -1598,6 +1600,42 @@ appendConditions(List *exprs, deparse_expr_cxt *context)
15981600
reset_transmission_modes(nestlevel);
15991601
}
16001602

1603+
/*
1604+
* Append WHERE clause, containing conditions from exprs and additional_conds,
1605+
* to context->buf.
1606+
*/
1607+
static void
1608+
appendWhereClause(List *exprs, List *additional_conds, deparse_expr_cxt *context)
1609+
{
1610+
StringInfo buf = context->buf;
1611+
bool need_and = false;
1612+
ListCell *lc;
1613+
1614+
if (exprs != NIL || additional_conds != NIL)
1615+
appendStringInfoString(buf, " WHERE ");
1616+
1617+
/*
1618+
* If there are some filters, append them.
1619+
*/
1620+
if (exprs != NIL)
1621+
{
1622+
appendConditions(exprs, context);
1623+
need_and = true;
1624+
}
1625+
1626+
/*
1627+
* If there are some EXISTS conditions, coming from SEMI-JOINS, append
1628+
* them.
1629+
*/
1630+
foreach(lc, additional_conds)
1631+
{
1632+
if (need_and)
1633+
appendStringInfoString(buf, " AND ");
1634+
appendStringInfoString(buf, (char *) lfirst(lc));
1635+
need_and = true;
1636+
}
1637+
}
1638+
16011639
/* Output join name for given join type */
16021640
const char *
16031641
get_jointype_name(JoinType jointype)
@@ -1616,6 +1654,9 @@ get_jointype_name(JoinType jointype)
16161654
case JOIN_FULL:
16171655
return "FULL";
16181656

1657+
case JOIN_SEMI:
1658+
return "SEMI";
1659+
16191660
default:
16201661
/* Shouldn't come here, but protect from buggy code. */
16211662
elog(ERROR, "unsupported join type %d", jointype);
@@ -1712,11 +1753,14 @@ deparseSubqueryTargetList(deparse_expr_cxt *context)
17121753
* of DELETE; it deparses the join relation as if the relation never contained
17131754
* the target relation, and creates a List of conditions to be deparsed into
17141755
* the top-level WHERE clause, which is returned to *ignore_conds.
1756+
*
1757+
* 'additional_conds' is a pointer to a list of strings to be appended to
1758+
* the WHERE clause, coming from lower-level SEMI-JOINs.
17151759
*/
17161760
static void
17171761
deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
17181762
bool use_alias, Index ignore_rel, List **ignore_conds,
1719-
List **params_list)
1763+
List **additional_conds, List **params_list)
17201764
{
17211765
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
17221766

@@ -1728,6 +1772,8 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
17281772
RelOptInfo *innerrel = fpinfo->innerrel;
17291773
bool outerrel_is_target = false;
17301774
bool innerrel_is_target = false;
1775+
List *additional_conds_i = NIL;
1776+
List *additional_conds_o = NIL;
17311777

17321778
if (ignore_rel > 0 && bms_is_member(ignore_rel, foreignrel->relids))
17331779
{
@@ -1764,7 +1810,8 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
17641810
initStringInfo(&join_sql_o);
17651811
deparseRangeTblRef(&join_sql_o, root, outerrel,
17661812
fpinfo->make_outerrel_subquery,
1767-
ignore_rel, ignore_conds, params_list);
1813+
ignore_rel, ignore_conds, &additional_conds_o,
1814+
params_list);
17681815

17691816
/*
17701817
* If inner relation is the target relation, skip deparsing it.
@@ -1780,6 +1827,12 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
17801827
Assert(fpinfo->jointype == JOIN_INNER);
17811828
Assert(fpinfo->joinclauses == NIL);
17821829
appendBinaryStringInfo(buf, join_sql_o.data, join_sql_o.len);
1830+
/* Pass EXISTS conditions to upper level */
1831+
if (additional_conds_o != NIL)
1832+
{
1833+
Assert(*additional_conds == NIL);
1834+
*additional_conds = additional_conds_o;
1835+
}
17831836
return;
17841837
}
17851838
}
@@ -1790,7 +1843,54 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
17901843
initStringInfo(&join_sql_i);
17911844
deparseRangeTblRef(&join_sql_i, root, innerrel,
17921845
fpinfo->make_innerrel_subquery,
1793-
ignore_rel, ignore_conds, params_list);
1846+
ignore_rel, ignore_conds, &additional_conds_i,
1847+
params_list);
1848+
1849+
/*
1850+
* SEMI-JOIN is deparsed as the EXISTS subquery. It references
1851+
* outer and inner relations, so it should be evaluated as the
1852+
* condition in the upper-level WHERE clause. We deparse the
1853+
* condition and pass it to upper level callers as an
1854+
* additional_conds list. Upper level callers are responsible for
1855+
* inserting conditions from the list where appropriate.
1856+
*/
1857+
if (fpinfo->jointype == JOIN_SEMI)
1858+
{
1859+
deparse_expr_cxt context;
1860+
StringInfoData str;
1861+
1862+
/* Construct deparsed condition from this SEMI-JOIN */
1863+
initStringInfo(&str);
1864+
appendStringInfo(&str, "EXISTS (SELECT NULL FROM %s",
1865+
join_sql_i.data);
1866+
1867+
context.buf = &str;
1868+
context.foreignrel = foreignrel;
1869+
context.scanrel = foreignrel;
1870+
context.root = root;
1871+
context.params_list = params_list;
1872+
1873+
/*
1874+
* Append SEMI-JOIN clauses and EXISTS conditions from lower
1875+
* levels to the current EXISTS subquery
1876+
*/
1877+
appendWhereClause(fpinfo->joinclauses, additional_conds_i, &context);
1878+
1879+
/*
1880+
* EXISTS conditions, coming from lower join levels, have just
1881+
* been processed.
1882+
*/
1883+
if (additional_conds_i != NIL)
1884+
{
1885+
list_free_deep(additional_conds_i);
1886+
additional_conds_i = NIL;
1887+
}
1888+
1889+
/* Close parentheses for EXISTS subquery */
1890+
appendStringInfo(&str, ")");
1891+
1892+
*additional_conds = lappend(*additional_conds, str.data);
1893+
}
17941894

17951895
/*
17961896
* If outer relation is the target relation, skip deparsing it.
@@ -1801,6 +1901,12 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
18011901
Assert(fpinfo->jointype == JOIN_INNER);
18021902
Assert(fpinfo->joinclauses == NIL);
18031903
appendBinaryStringInfo(buf, join_sql_i.data, join_sql_i.len);
1904+
/* Pass EXISTS conditions to the upper call */
1905+
if (additional_conds_i != NIL)
1906+
{
1907+
Assert(*additional_conds == NIL);
1908+
*additional_conds = additional_conds_i;
1909+
}
18041910
return;
18051911
}
18061912
}
@@ -1809,33 +1915,65 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
18091915
Assert(!outerrel_is_target && !innerrel_is_target);
18101916

18111917
/*
1812-
* For a join relation FROM clause entry is deparsed as
1813-
*
1814-
* ((outer relation) <join type> (inner relation) ON (joinclauses))
1918+
* For semijoin FROM clause is deparsed as an outer relation. An inner
1919+
* relation and join clauses are converted to EXISTS condition and
1920+
* passed to the upper level.
18151921
*/
1816-
appendStringInfo(buf, "(%s %s JOIN %s ON ", join_sql_o.data,
1817-
get_jointype_name(fpinfo->jointype), join_sql_i.data);
1818-
1819-
/* Append join clause; (TRUE) if no join clause */
1820-
if (fpinfo->joinclauses)
1922+
if (fpinfo->jointype == JOIN_SEMI)
18211923
{
1822-
deparse_expr_cxt context;
1924+
appendStringInfo(buf, "%s", join_sql_o.data);
1925+
}
1926+
else
1927+
{
1928+
/*
1929+
* For a join relation FROM clause, entry is deparsed as
1930+
*
1931+
* ((outer relation) <join type> (inner relation) ON
1932+
* (joinclauses))
1933+
*/
1934+
appendStringInfo(buf, "(%s %s JOIN %s ON ", join_sql_o.data,
1935+
get_jointype_name(fpinfo->jointype), join_sql_i.data);
18231936

1824-
context.buf = buf;
1825-
context.foreignrel = foreignrel;
1826-
context.scanrel = foreignrel;
1827-
context.root = root;
1828-
context.params_list = params_list;
1937+
/* Append join clause; (TRUE) if no join clause */
1938+
if (fpinfo->joinclauses)
1939+
{
1940+
deparse_expr_cxt context;
18291941

1830-
appendStringInfoChar(buf, '(');
1831-
appendConditions(fpinfo->joinclauses, &context);
1942+
context.buf = buf;
1943+
context.foreignrel = foreignrel;
1944+
context.scanrel = foreignrel;
1945+
context.root = root;
1946+
context.params_list = params_list;
1947+
1948+
appendStringInfoChar(buf, '(');
1949+
appendConditions(fpinfo->joinclauses, &context);
1950+
appendStringInfoChar(buf, ')');
1951+
}
1952+
else
1953+
appendStringInfoString(buf, "(TRUE)");
1954+
1955+
/* End the FROM clause entry. */
18321956
appendStringInfoChar(buf, ')');
18331957
}
1834-
else
1835-
appendStringInfoString(buf, "(TRUE)");
18361958

1837-
/* End the FROM clause entry. */
1838-
appendStringInfoChar(buf, ')');
1959+
/*
1960+
* Construct additional_conds to be passed to the upper caller from
1961+
* current level additional_conds and additional_conds, coming from
1962+
* inner and outer rels.
1963+
*/
1964+
if (additional_conds_o != NIL)
1965+
{
1966+
*additional_conds = list_concat(*additional_conds,
1967+
additional_conds_o);
1968+
list_free(additional_conds_o);
1969+
}
1970+
1971+
if (additional_conds_i != NIL)
1972+
{
1973+
*additional_conds = list_concat(*additional_conds,
1974+
additional_conds_i);
1975+
list_free(additional_conds_i);
1976+
}
18391977
}
18401978
else
18411979
{
@@ -1863,11 +2001,13 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
18632001

18642002
/*
18652003
* Append FROM clause entry for the given relation into buf.
2004+
* Conditions from lower-level SEMI-JOINs are appended to additional_conds
2005+
* and should be added to upper level WHERE clause.
18662006
*/
18672007
static void
18682008
deparseRangeTblRef(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
18692009
bool make_subquery, Index ignore_rel, List **ignore_conds,
1870-
List **params_list)
2010+
List **additional_conds, List **params_list)
18712011
{
18722012
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
18732013

@@ -1925,7 +2065,8 @@ deparseRangeTblRef(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
19252065
}
19262066
else
19272067
deparseFromExprForRel(buf, root, foreignrel, true, ignore_rel,
1928-
ignore_conds, params_list);
2068+
ignore_conds, additional_conds,
2069+
params_list);
19292070
}
19302071

19312072
/*
@@ -2148,6 +2289,7 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
21482289
RangeTblEntry *rte = planner_rt_fetch(rtindex, root);
21492290
ListCell *lc,
21502291
*lc2;
2292+
List *additional_conds = NIL;
21512293

21522294
/* Set up context struct for recursion */
21532295
context.root = root;
@@ -2189,17 +2331,17 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
21892331
{
21902332
List *ignore_conds = NIL;
21912333

2334+
21922335
appendStringInfoString(buf, " FROM ");
21932336
deparseFromExprForRel(buf, root, foreignrel, true, rtindex,
2194-
&ignore_conds, params_list);
2337+
&ignore_conds, &additional_conds, params_list);
21952338
remote_conds = list_concat(remote_conds, ignore_conds);
21962339
}
21972340

2198-
if (remote_conds)
2199-
{
2200-
appendStringInfoString(buf, " WHERE ");
2201-
appendConditions(remote_conds, &context);
2202-
}
2341+
appendWhereClause(remote_conds, additional_conds, &context);
2342+
2343+
if (additional_conds != NIL)
2344+
list_free_deep(additional_conds);
22032345

22042346
if (foreignrel->reloptkind == RELOPT_JOINREL)
22052347
deparseExplicitTargetList(returningList, true, retrieved_attrs,
@@ -2255,6 +2397,7 @@ deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root,
22552397
List **retrieved_attrs)
22562398
{
22572399
deparse_expr_cxt context;
2400+
List *additional_conds = NIL;
22582401

22592402
/* Set up context struct for recursion */
22602403
context.root = root;
@@ -2274,15 +2417,14 @@ deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root,
22742417

22752418
appendStringInfoString(buf, " USING ");
22762419
deparseFromExprForRel(buf, root, foreignrel, true, rtindex,
2277-
&ignore_conds, params_list);
2420+
&ignore_conds, &additional_conds, params_list);
22782421
remote_conds = list_concat(remote_conds, ignore_conds);
22792422
}
22802423

2281-
if (remote_conds)
2282-
{
2283-
appendStringInfoString(buf, " WHERE ");
2284-
appendConditions(remote_conds, &context);
2285-
}
2424+
appendWhereClause(remote_conds, additional_conds, &context);
2425+
2426+
if (additional_conds != NIL)
2427+
list_free_deep(additional_conds);
22862428

22872429
if (foreignrel->reloptkind == RELOPT_JOINREL)
22882430
deparseExplicitTargetList(returningList, true, retrieved_attrs,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy