Skip to content

Commit

Permalink
Add support for deparsing semi-joins to contrib/postgres_fdw
Browse files Browse the repository at this point in the history
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
  • Loading branch information
akorotkov committed Dec 5, 2023
1 parent 278eb13 commit 824dbea
Show file tree
Hide file tree
Showing 5 changed files with 697 additions and 81 deletions.
234 changes: 188 additions & 46 deletions contrib/postgres_fdw/deparse.c
Expand Up @@ -180,11 +180,15 @@ static void appendConditions(List *exprs, deparse_expr_cxt *context);
static void deparseFromExprForRel(StringInfo buf, PlannerInfo *root,
RelOptInfo *foreignrel, bool use_alias,
Index ignore_rel, List **ignore_conds,
List **additional_conds,
List **params_list);
static void appendWhereClause(List *exprs, List *additional_conds,
deparse_expr_cxt *context);
static void deparseFromExpr(List *quals, deparse_expr_cxt *context);
static void deparseRangeTblRef(StringInfo buf, PlannerInfo *root,
RelOptInfo *foreignrel, bool make_subquery,
Index ignore_rel, List **ignore_conds, List **params_list);
Index ignore_rel, List **ignore_conds,
List **additional_conds, List **params_list);
static void deparseAggref(Aggref *node, deparse_expr_cxt *context);
static void appendGroupByClause(List *tlist, deparse_expr_cxt *context);
static void appendOrderBySuffix(Oid sortop, Oid sortcoltype, bool nulls_first,
Expand Down Expand Up @@ -1370,6 +1374,7 @@ deparseFromExpr(List *quals, deparse_expr_cxt *context)
{
StringInfo buf = context->buf;
RelOptInfo *scanrel = context->scanrel;
List *additional_conds = NIL;

/* For upper relations, scanrel must be either a joinrel or a baserel */
Assert(!IS_UPPER_REL(context->foreignrel) ||
Expand All @@ -1379,14 +1384,11 @@ deparseFromExpr(List *quals, deparse_expr_cxt *context)
appendStringInfoString(buf, " FROM ");
deparseFromExprForRel(buf, context->root, scanrel,
(bms_membership(scanrel->relids) == BMS_MULTIPLE),
(Index) 0, NULL, context->params_list);

/* Construct WHERE clause */
if (quals != NIL)
{
appendStringInfoString(buf, " WHERE ");
appendConditions(quals, context);
}
(Index) 0, NULL, &additional_conds,
context->params_list);
appendWhereClause(quals, additional_conds, context);
if (additional_conds != NIL)
list_free_deep(additional_conds);
}

/*
Expand Down Expand Up @@ -1598,6 +1600,42 @@ appendConditions(List *exprs, deparse_expr_cxt *context)
reset_transmission_modes(nestlevel);
}

/*
* Append WHERE clause, containing conditions from exprs and additional_conds,
* to context->buf.
*/
static void
appendWhereClause(List *exprs, List *additional_conds, deparse_expr_cxt *context)
{
StringInfo buf = context->buf;
bool need_and = false;
ListCell *lc;

if (exprs != NIL || additional_conds != NIL)
appendStringInfoString(buf, " WHERE ");

/*
* If there are some filters, append them.
*/
if (exprs != NIL)
{
appendConditions(exprs, context);
need_and = true;
}

/*
* If there are some EXISTS conditions, coming from SEMI-JOINS, append
* them.
*/
foreach(lc, additional_conds)
{
if (need_and)
appendStringInfoString(buf, " AND ");
appendStringInfoString(buf, (char *) lfirst(lc));
need_and = true;
}
}

/* Output join name for given join type */
const char *
get_jointype_name(JoinType jointype)
Expand All @@ -1616,6 +1654,9 @@ get_jointype_name(JoinType jointype)
case JOIN_FULL:
return "FULL";

case JOIN_SEMI:
return "SEMI";

default:
/* Shouldn't come here, but protect from buggy code. */
elog(ERROR, "unsupported join type %d", jointype);
Expand Down Expand Up @@ -1712,11 +1753,14 @@ deparseSubqueryTargetList(deparse_expr_cxt *context)
* of DELETE; it deparses the join relation as if the relation never contained
* the target relation, and creates a List of conditions to be deparsed into
* the top-level WHERE clause, which is returned to *ignore_conds.
*
* 'additional_conds' is a pointer to a list of strings to be appended to
* the WHERE clause, coming from lower-level SEMI-JOINs.
*/
static void
deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
bool use_alias, Index ignore_rel, List **ignore_conds,
List **params_list)
List **additional_conds, List **params_list)
{
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;

Expand All @@ -1728,6 +1772,8 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
RelOptInfo *innerrel = fpinfo->innerrel;
bool outerrel_is_target = false;
bool innerrel_is_target = false;
List *additional_conds_i = NIL;
List *additional_conds_o = NIL;

if (ignore_rel > 0 && bms_is_member(ignore_rel, foreignrel->relids))
{
Expand Down Expand Up @@ -1764,7 +1810,8 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
initStringInfo(&join_sql_o);
deparseRangeTblRef(&join_sql_o, root, outerrel,
fpinfo->make_outerrel_subquery,
ignore_rel, ignore_conds, params_list);
ignore_rel, ignore_conds, &additional_conds_o,
params_list);

/*
* If inner relation is the target relation, skip deparsing it.
Expand All @@ -1780,6 +1827,12 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
Assert(fpinfo->jointype == JOIN_INNER);
Assert(fpinfo->joinclauses == NIL);
appendBinaryStringInfo(buf, join_sql_o.data, join_sql_o.len);
/* Pass EXISTS conditions to upper level */
if (additional_conds_o != NIL)
{
Assert(*additional_conds == NIL);
*additional_conds = additional_conds_o;
}
return;
}
}
Expand All @@ -1790,7 +1843,54 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
initStringInfo(&join_sql_i);
deparseRangeTblRef(&join_sql_i, root, innerrel,
fpinfo->make_innerrel_subquery,
ignore_rel, ignore_conds, params_list);
ignore_rel, ignore_conds, &additional_conds_i,
params_list);

/*
* 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. We deparse the
* condition and pass it to upper level callers as an
* additional_conds list. Upper level callers are responsible for
* inserting conditions from the list where appropriate.
*/
if (fpinfo->jointype == JOIN_SEMI)
{
deparse_expr_cxt context;
StringInfoData str;

/* Construct deparsed condition from this SEMI-JOIN */
initStringInfo(&str);
appendStringInfo(&str, "EXISTS (SELECT NULL FROM %s",
join_sql_i.data);

context.buf = &str;
context.foreignrel = foreignrel;
context.scanrel = foreignrel;
context.root = root;
context.params_list = params_list;

/*
* Append SEMI-JOIN clauses and EXISTS conditions from lower
* levels to the current EXISTS subquery
*/
appendWhereClause(fpinfo->joinclauses, additional_conds_i, &context);

/*
* EXISTS conditions, coming from lower join levels, have just
* been processed.
*/
if (additional_conds_i != NIL)
{
list_free_deep(additional_conds_i);
additional_conds_i = NIL;
}

/* Close parentheses for EXISTS subquery */
appendStringInfo(&str, ")");

*additional_conds = lappend(*additional_conds, str.data);
}

/*
* If outer relation is the target relation, skip deparsing it.
Expand All @@ -1801,6 +1901,12 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
Assert(fpinfo->jointype == JOIN_INNER);
Assert(fpinfo->joinclauses == NIL);
appendBinaryStringInfo(buf, join_sql_i.data, join_sql_i.len);
/* Pass EXISTS conditions to the upper call */
if (additional_conds_i != NIL)
{
Assert(*additional_conds == NIL);
*additional_conds = additional_conds_i;
}
return;
}
}
Expand All @@ -1809,33 +1915,65 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
Assert(!outerrel_is_target && !innerrel_is_target);

/*
* For a join relation FROM clause entry is deparsed as
*
* ((outer relation) <join type> (inner relation) ON (joinclauses))
* For semijoin FROM clause is deparsed as an outer relation. An inner
* relation and join clauses are converted to EXISTS condition and
* passed to the upper level.
*/
appendStringInfo(buf, "(%s %s JOIN %s ON ", join_sql_o.data,
get_jointype_name(fpinfo->jointype), join_sql_i.data);

/* Append join clause; (TRUE) if no join clause */
if (fpinfo->joinclauses)
if (fpinfo->jointype == JOIN_SEMI)
{
deparse_expr_cxt context;
appendStringInfo(buf, "%s", join_sql_o.data);
}
else
{
/*
* For a join relation FROM clause, entry is deparsed as
*
* ((outer relation) <join type> (inner relation) ON
* (joinclauses))
*/
appendStringInfo(buf, "(%s %s JOIN %s ON ", join_sql_o.data,
get_jointype_name(fpinfo->jointype), join_sql_i.data);

context.buf = buf;
context.foreignrel = foreignrel;
context.scanrel = foreignrel;
context.root = root;
context.params_list = params_list;
/* Append join clause; (TRUE) if no join clause */
if (fpinfo->joinclauses)
{
deparse_expr_cxt context;

appendStringInfoChar(buf, '(');
appendConditions(fpinfo->joinclauses, &context);
context.buf = buf;
context.foreignrel = foreignrel;
context.scanrel = foreignrel;
context.root = root;
context.params_list = params_list;

appendStringInfoChar(buf, '(');
appendConditions(fpinfo->joinclauses, &context);
appendStringInfoChar(buf, ')');
}
else
appendStringInfoString(buf, "(TRUE)");

/* End the FROM clause entry. */
appendStringInfoChar(buf, ')');
}
else
appendStringInfoString(buf, "(TRUE)");

/* End the FROM clause entry. */
appendStringInfoChar(buf, ')');
/*
* Construct additional_conds to be passed to the upper caller from
* current level additional_conds and additional_conds, coming from
* inner and outer rels.
*/
if (additional_conds_o != NIL)
{
*additional_conds = list_concat(*additional_conds,
additional_conds_o);
list_free(additional_conds_o);
}

if (additional_conds_i != NIL)
{
*additional_conds = list_concat(*additional_conds,
additional_conds_i);
list_free(additional_conds_i);
}
}
else
{
Expand Down Expand Up @@ -1863,11 +2001,13 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,

/*
* Append FROM clause entry for the given relation into buf.
* Conditions from lower-level SEMI-JOINs are appended to additional_conds
* and should be added to upper level WHERE clause.
*/
static void
deparseRangeTblRef(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
bool make_subquery, Index ignore_rel, List **ignore_conds,
List **params_list)
List **additional_conds, List **params_list)
{
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;

Expand Down Expand Up @@ -1925,7 +2065,8 @@ deparseRangeTblRef(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
}
else
deparseFromExprForRel(buf, root, foreignrel, true, ignore_rel,
ignore_conds, params_list);
ignore_conds, additional_conds,
params_list);
}

/*
Expand Down Expand Up @@ -2148,6 +2289,7 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
RangeTblEntry *rte = planner_rt_fetch(rtindex, root);
ListCell *lc,
*lc2;
List *additional_conds = NIL;

/* Set up context struct for recursion */
context.root = root;
Expand Down Expand Up @@ -2189,17 +2331,17 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
{
List *ignore_conds = NIL;


appendStringInfoString(buf, " FROM ");
deparseFromExprForRel(buf, root, foreignrel, true, rtindex,
&ignore_conds, params_list);
&ignore_conds, &additional_conds, params_list);
remote_conds = list_concat(remote_conds, ignore_conds);
}

if (remote_conds)
{
appendStringInfoString(buf, " WHERE ");
appendConditions(remote_conds, &context);
}
appendWhereClause(remote_conds, additional_conds, &context);

if (additional_conds != NIL)
list_free_deep(additional_conds);

if (foreignrel->reloptkind == RELOPT_JOINREL)
deparseExplicitTargetList(returningList, true, retrieved_attrs,
Expand Down Expand Up @@ -2255,6 +2397,7 @@ deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root,
List **retrieved_attrs)
{
deparse_expr_cxt context;
List *additional_conds = NIL;

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

appendStringInfoString(buf, " USING ");
deparseFromExprForRel(buf, root, foreignrel, true, rtindex,
&ignore_conds, params_list);
&ignore_conds, &additional_conds, params_list);
remote_conds = list_concat(remote_conds, ignore_conds);
}

if (remote_conds)
{
appendStringInfoString(buf, " WHERE ");
appendConditions(remote_conds, &context);
}
appendWhereClause(remote_conds, additional_conds, &context);

if (additional_conds != NIL)
list_free_deep(additional_conds);

if (foreignrel->reloptkind == RELOPT_JOINREL)
deparseExplicitTargetList(returningList, true, retrieved_attrs,
Expand Down

0 comments on commit 824dbea

Please sign in to comment.