{
/* SQL statement to execute remotely (as a String node) */
FdwScanPrivateSelectSql,
- /* List of restriction clauses that can be executed remotely */
+ /* List of qual clauses that can be executed remotely */
+ /* (DO NOT try to use these at runtime; see postgresGetForeignPlan) */
FdwScanPrivateRemoteConds,
/* Integer list of attribute numbers retrieved by the SELECT */
FdwScanPrivateRetrievedAttrs,
&fpinfo->attrs_used);
foreach(lc, fpinfo->local_conds)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
pull_varattnos((Node *) rinfo->clause, baserel->relid,
&fpinfo->attrs_used);
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
Index scan_relid;
List *fdw_private;
- List *remote_conds = NIL;
List *remote_exprs = NIL;
List *local_exprs = NIL;
List *params_list = NIL;
+ List *fdw_scan_tlist = NIL;
+ List *fdw_recheck_quals = NIL;
List *retrieved_attrs;
StringInfoData sql;
ListCell *lc;
- List *fdw_scan_tlist = NIL;
- /*
- * For base relations, set scan_relid as the relid of the relation. For
- * other kinds of relations set it to 0.
- */
if (IS_SIMPLE_REL(foreignrel))
+ {
+ /*
+ * For base relations, set scan_relid as the relid of the relation.
+ */
scan_relid = foreignrel->relid;
+
+ /*
+ * In a base-relation scan, we must apply the given scan_clauses.
+ *
+ * Separate the scan_clauses into those that can be executed remotely
+ * and those that can't. baserestrictinfo clauses that were
+ * previously determined to be safe or unsafe by classifyConditions
+ * are found in fpinfo->remote_conds and fpinfo->local_conds. Anything
+ * else in the scan_clauses list will be a join clause, which we have
+ * to check for remote-safety.
+ *
+ * Note: the join clauses we see here should be the exact same ones
+ * previously examined by postgresGetForeignPaths. Possibly it'd be
+ * worth passing forward the classification work done then, rather
+ * than repeating it here.
+ *
+ * This code must match "extract_actual_clauses(scan_clauses, false)"
+ * except for the additional decision about remote versus local
+ * execution.
+ */
+ foreach(lc, scan_clauses)
+ {
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+
+ /* Ignore any pseudoconstants, they're dealt with elsewhere */
+ if (rinfo->pseudoconstant)
+ continue;
+
+ if (list_member_ptr(fpinfo->remote_conds, rinfo))
+ remote_exprs = lappend(remote_exprs, rinfo->clause);
+ else if (list_member_ptr(fpinfo->local_conds, rinfo))
+ local_exprs = lappend(local_exprs, rinfo->clause);
+ else if (is_foreign_expr(root, foreignrel, rinfo->clause))
+ remote_exprs = lappend(remote_exprs, rinfo->clause);
+ else
+ local_exprs = lappend(local_exprs, rinfo->clause);
+ }
+
+ /*
+ * For a base-relation scan, we have to support EPQ recheck, which
+ * should recheck all the remote quals.
+ */
+ fdw_recheck_quals = remote_exprs;
+ }
else
{
+ /*
+ * Join relation or upper relation - set scan_relid to 0.
+ */
scan_relid = 0;
/*
- * create_scan_plan() and create_foreignscan_plan() pass
- * rel->baserestrictinfo + parameterization clauses through
- * scan_clauses. For a join rel->baserestrictinfo is NIL and we are
- * not considering parameterization right now, so there should be no
- * scan_clauses for a joinrel and upper rel either.
+ * For a join rel, baserestrictinfo is NIL and we are not considering
+ * parameterization right now, so there should be no scan_clauses for
+ * a joinrel or an upper rel either.
*/
Assert(!scan_clauses);
- }
- /*
- * Separate the scan_clauses into those that can be executed remotely and
- * those that can't. baserestrictinfo clauses that were previously
- * determined to be safe or unsafe by classifyConditions are shown in
- * fpinfo->remote_conds and fpinfo->local_conds. Anything else in the
- * scan_clauses list will be a join clause, which we have to check for
- * remote-safety.
- *
- * Note: the join clauses we see here should be the exact same ones
- * previously examined by postgresGetForeignPaths. Possibly it'd be worth
- * passing forward the classification work done then, rather than
- * repeating it here.
- *
- * This code must match "extract_actual_clauses(scan_clauses, false)"
- * except for the additional decision about remote versus local execution.
- * Note however that we don't strip the RestrictInfo nodes from the
- * remote_conds list, since appendWhereClause expects a list of
- * RestrictInfos.
- */
- foreach(lc, scan_clauses)
- {
- RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
-
- /* Ignore any pseudoconstants, they're dealt with elsewhere */
- if (rinfo->pseudoconstant)
- continue;
-
- if (list_member_ptr(fpinfo->remote_conds, rinfo))
- {
- remote_conds = lappend(remote_conds, rinfo);
- remote_exprs = lappend(remote_exprs, rinfo->clause);
- }
- else if (list_member_ptr(fpinfo->local_conds, rinfo))
- local_exprs = lappend(local_exprs, rinfo->clause);
- else if (is_foreign_expr(root, foreignrel, rinfo->clause))
- {
- remote_conds = lappend(remote_conds, rinfo);
- remote_exprs = lappend(remote_exprs, rinfo->clause);
- }
- else
- local_exprs = lappend(local_exprs, rinfo->clause);
- }
+ /*
+ * Instead we get the conditions to apply from the fdw_private
+ * structure.
+ */
+ remote_exprs = extract_actual_clauses(fpinfo->remote_conds, false);
+ local_exprs = extract_actual_clauses(fpinfo->local_conds, false);
- if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
- {
- /* For a join relation, get the conditions from fdw_private structure */
- remote_conds = fpinfo->remote_conds;
- local_exprs = fpinfo->local_conds;
+ /*
+ * We leave fdw_recheck_quals empty in this case, since we never need
+ * to apply EPQ recheck clauses. In the case of a joinrel, EPQ
+ * recheck is handled elsewhere --- see postgresGetForeignJoinPaths().
+ * If we're planning an upperrel (ie, remote grouping or aggregation)
+ * then there's no EPQ to do because SELECT FOR UPDATE wouldn't be
+ * allowed, and indeed we *can't* put the remote clauses into
+ * fdw_recheck_quals because the unaggregated Vars won't be available
+ * locally.
+ */
/* Build the list of columns to be fetched from the foreign server. */
fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
*/
initStringInfo(&sql);
deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
- remote_conds, best_path->path.pathkeys,
+ remote_exprs, best_path->path.pathkeys,
false, &retrieved_attrs, ¶ms_list);
/*
* Items in the list must match order in enum FdwScanPrivateIndex.
*/
fdw_private = list_make4(makeString(sql.data),
- remote_conds,
+ remote_exprs,
retrieved_attrs,
makeInteger(fpinfo->fetch_size));
if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
* Note that the remote parameter expressions are stored in the fdw_exprs
* field of the finished plan node; we can't keep them in private state
* because then they wouldn't be subject to later planner processing.
+ *
+ * We have some foreign qual conditions hidden away within fdw_private's
+ * FdwScanPrivateRemoteConds item, which would be unsafe per the above
+ * consideration. But those will only be used by postgresPlanDirectModify,
+ * which may extract them to use in a rewritten plan. We assume that
+ * nothing will be done between here and there that would need to modify
+ * those expressions.
*/
return make_foreignscan(tlist,
local_exprs,
params_list,
fdw_private,
fdw_scan_tlist,
- remote_exprs,
+ fdw_recheck_quals,
outer_plan);
}
rel = heap_open(rte->relid, NoLock);
/*
- * Extract the baserestrictinfo clauses that can be evaluated remotely.
+ * Extract the qual clauses that can be evaluated remotely. (These are
+ * bare clauses not RestrictInfos, but deparse.c's appendConditions()
+ * doesn't care.)
*/
remote_conds = (List *) list_nth(fscan->fdw_private,
FdwScanPrivateRemoteConds);
PgFdwRelationInfo *fpinfo_i;
ListCell *lc;
List *joinclauses;
- List *otherclauses;
/*
* We support pushing down INNER, LEFT, RIGHT and FULL OUTER joins.
if (fpinfo_o->local_conds || fpinfo_i->local_conds)
return false;
- /* Separate restrict list into join quals and quals on join relation */
- if (IS_OUTER_JOIN(jointype))
- extract_actual_join_clauses(extra->restrictlist, &joinclauses, &otherclauses);
- else
- {
- /*
- * Unlike an outer join, for inner join, the join result contains only
- * the rows which satisfy join clauses, similar to the other clause.
- * Hence all clauses can be treated as other quals. This helps to push
- * a join down to the foreign server even if some of its join quals
- * are not safe to pushdown.
- */
- otherclauses = extract_actual_clauses(extra->restrictlist, false);
- joinclauses = NIL;
- }
-
- /* Join quals must be safe to push down. */
- foreach(lc, joinclauses)
+ /*
+ * Separate restrict list into join quals and pushed-down (other) quals.
+ *
+ * Join quals belonging to an outer join must all be shippable, else we
+ * cannot execute the join remotely. Add such quals to 'joinclauses'.
+ *
+ * Add other quals to fpinfo->remote_conds if they are shippable, else to
+ * fpinfo->local_conds. In an inner join it's okay to execute conditions
+ * either locally or remotely; the same is true for pushed-down conditions
+ * at an outer join.
+ *
+ * Note we might return failure after having already scribbled on
+ * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
+ * won't consult those lists again if we deem the join unshippable.
+ */
+ joinclauses = NIL;
+ foreach(lc, extra->restrictlist)
{
- Expr *expr = (Expr *) lfirst(lc);
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+ bool is_remote_clause = is_foreign_expr(root, joinrel,
+ rinfo->clause);
- if (!is_foreign_expr(root, joinrel, expr))
- return false;
+ if (IS_OUTER_JOIN(jointype) && !rinfo->is_pushed_down)
+ {
+ if (!is_remote_clause)
+ return false;
+ joinclauses = lappend(joinclauses, rinfo);
+ }
+ else
+ {
+ if (is_remote_clause)
+ fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
+ else
+ fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
+ }
}
/*
/* Save the join clauses, for later use. */
fpinfo->joinclauses = joinclauses;
- /*
- * Other clauses are applied after the join has been performed and thus
- * need not be all pushable. We will push those which can be pushed to
- * reduce the number of rows fetched from the foreign server. Rest of them
- * will be applied locally after fetching join result. Add them to fpinfo
- * so that other joins involving this joinrel will know that this joinrel
- * has local clauses.
- */
- foreach(lc, otherclauses)
- {
- Expr *expr = (Expr *) lfirst(lc);
-
- if (!is_foreign_expr(root, joinrel, expr))
- fpinfo->local_conds = lappend(fpinfo->local_conds, expr);
- else
- fpinfo->remote_conds = lappend(fpinfo->remote_conds, expr);
- }
-
fpinfo->outerrel = outerrel;
fpinfo->innerrel = innerrel;
fpinfo->jointype = jointype;
foreach(lc, (List *) query->havingQual)
{
Expr *expr = (Expr *) lfirst(lc);
+ RestrictInfo *rinfo;
- if (!is_foreign_expr(root, grouped_rel, expr))
- fpinfo->local_conds = lappend(fpinfo->local_conds, expr);
+ /*
+ * Currently, the core code doesn't wrap havingQuals in
+ * RestrictInfos, so we must make our own.
+ */
+ Assert(!IsA(expr, RestrictInfo));
+ rinfo = make_restrictinfo(expr,
+ true,
+ false,
+ false,
+ root->qual_security_level,
+ grouped_rel->relids,
+ NULL,
+ NULL);
+ if (is_foreign_expr(root, grouped_rel, expr))
+ fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
else
- fpinfo->remote_conds = lappend(fpinfo->remote_conds, expr);
+ fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
}
}
*/
if (fpinfo->local_conds)
{
+ List *aggvars = NIL;
ListCell *lc;
- List *aggvars = pull_var_clause((Node *) fpinfo->local_conds,
- PVC_INCLUDE_AGGREGATES);
+
+ foreach(lc, fpinfo->local_conds)
+ {
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+
+ aggvars = list_concat(aggvars,
+ pull_var_clause((Node *) rinfo->clause,
+ PVC_INCLUDE_AGGREGATES));
+ }
foreach(lc, aggvars)
{
if (!is_foreign_expr(root, grouped_rel, expr))
return false;
- tlist = add_to_flat_tlist(tlist, aggvars);
+ tlist = add_to_flat_tlist(tlist, list_make1(expr));
}
}
}