diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index fb72f45..7a2a67b 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -93,9 +93,10 @@ typedef struct foreign_loc_cxt typedef struct deparse_expr_cxt { PlannerInfo *root; /* global planner state */ - RelOptInfo *foreignrel; /* the foreign relation we are planning for */ + Relids relids; /* Relids for which to deparse */ StringInfo buf; /* output buffer to append to */ List **params_list; /* exprs that will become remote Params */ + bool is_joinrel; /* Are we deparsing for a join relation */ } deparse_expr_cxt; #define REL_ALIAS_PREFIX "r" @@ -122,8 +123,7 @@ static void deparseTargetList(StringInfo buf, Bitmapset *attrs_used, List **retrieved_attrs, bool qualify_col); -static void deparseExplicitTargetList(List *tlist, List **retrieved_attrs, - deparse_expr_cxt *context); +static void deparseExplicitTargetList(List *tlist, deparse_expr_cxt *context); static void deparseReturningList(StringInfo buf, PlannerInfo *root, Index rtindex, Relation rel, bool trig_after_row, @@ -151,13 +151,15 @@ static void printRemoteParam(int paramindex, Oid paramtype, int32 paramtypmod, deparse_expr_cxt *context); static void printRemotePlaceholder(Oid paramtype, int32 paramtypmod, deparse_expr_cxt *context); -static void deparseSelectSql(List *tlist, List **retrieved_attrs, - deparse_expr_cxt *context); +static void deparseSelectSql(List *tlist, RelOptInfo *rel, + deparse_expr_cxt *context); static void deparseLockingClause(deparse_expr_cxt *context); static void appendOrderByClause(List *pathkeys, deparse_expr_cxt *context); static void appendConditions(List *exprs, deparse_expr_cxt *context); static void deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *joinrel, bool use_alias, List **params_list); +static List *build_tlist_from_attrs_used(Index rtindex, Bitmapset *attrs_used, + PlannerInfo *root, List **retrieved_attrs); /* @@ -715,26 +717,119 @@ deparse_type_name(Oid type_oid, int32 typemod) } /* + * Convert input bitmap representation of columns into targetlist of + * corresponding Var nodes. + * + * List of columns selected is returned in retrieved_attrs. + */ +List * +build_tlist_from_attrs_used(Index rtindex, Bitmapset *attrs_used, PlannerInfo *root, + List **retrieved_attrs) +{ + /* + * For a base relation fpinfo->attrs_used gives the list of columns + * required to be fetched from the foreign server. + */ + RangeTblEntry *rte = planner_rt_fetch(rtindex, root); + /* + * Core code already has some lock on each rel being planned, so we + * can use NoLock here. + */ + Relation rel = heap_open(rte->relid, NoLock); + + TupleDesc tupdesc = RelationGetDescr(rel); + bool have_wholerow; + int i; + List *var_list = NIL; + + *retrieved_attrs = NIL; + + /* If there's a whole-row reference, we'll need all the columns. */ + have_wholerow = bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, + attrs_used); + + for (i = 1; i <= tupdesc->natts; i++) + { + Form_pg_attribute attr = tupdesc->attrs[i - 1]; + + /* Ignore dropped attributes. */ + if (attr->attisdropped) + continue; + + if (have_wholerow || + bms_is_member(i - FirstLowInvalidHeapAttributeNumber, + attrs_used)) + { + var_list = lappend(var_list, makeVar(rtindex, i, attr->atttypid, + attr->atttypmod, + attr->attcollation, 0)); + *retrieved_attrs = lappend_int(*retrieved_attrs, i); + } + } + + /* + * Add ctid if needed. We currently don't support retrieving any other + * system columns. + */ + if (bms_is_member(SelfItemPointerAttributeNumber - FirstLowInvalidHeapAttributeNumber, + attrs_used)) + { + + var_list = lappend(var_list, makeVar(rtindex, + SelfItemPointerAttributeNumber, + TIDOID, -1, InvalidOid, 0)); + *retrieved_attrs = lappend_int(*retrieved_attrs, + SelfItemPointerAttributeNumber); + } + + heap_close(rel, NoLock); + + return add_to_flat_tlist(NIL, var_list); +} + +/* * Build the targetlist for given relation to be deparsed as SELECT clause. * * The output targetlist contains the columns that need to be fetched from the * foreign server for the given relation. + * + * List of columns selected is returned in retrieved_attrs. */ List * -build_tlist_to_deparse(RelOptInfo *foreignrel) +build_tlist_to_deparse(RelOptInfo *foreignrel, PlannerInfo *root, + List **retrieved_attrs) { List *tlist = NIL; PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; - /* - * We require columns specified in foreignrel->reltargetlist and those - * required for evaluating the local conditions. - */ - tlist = add_to_flat_tlist(tlist, foreignrel->reltargetlist); - tlist = add_to_flat_tlist(tlist, - pull_var_clause((Node *) fpinfo->local_conds, - PVC_REJECT_AGGREGATES, - PVC_RECURSE_PLACEHOLDERS)); + if (foreignrel->reloptkind == RELOPT_JOINREL) + { + ListCell *lc; + int i = 1; + + /* + * We require columns specified in foreignrel->reltargetlist and those + * required for evaluating the local conditions. + */ + tlist = add_to_flat_tlist(tlist, foreignrel->reltargetlist); + tlist = add_to_flat_tlist(tlist, + pull_var_clause((Node *) fpinfo->local_conds, + PVC_REJECT_AGGREGATES, + PVC_RECURSE_PLACEHOLDERS)); + /* + * For a join relation, retrieved attributes is nothing but list of + * continously increasing integers starting from 1 upto the number of + * entries in the targetlist, representing the attribute numbers of the + * output of join. + */ + *retrieved_attrs = NIL; + foreach (lc, tlist) + *retrieved_attrs = lappend_int(*retrieved_attrs, i++); + } + else + tlist = build_tlist_from_attrs_used(foreignrel->relid, + fpinfo->attrs_used, root, + retrieved_attrs); return tlist; } @@ -756,13 +851,11 @@ build_tlist_to_deparse(RelOptInfo *foreignrel) * so Params and other-relation Vars should be replaced by dummy values. * * pathkeys is the list of pathkeys to order the result by. - * - * List of columns selected is returned in retrieved_attrs. */ extern void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, List *tlist, List *remote_conds, List *pathkeys, - List **retrieved_attrs, List **params_list) + List **params_list) { deparse_expr_cxt context; @@ -774,11 +867,12 @@ deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, /* Fill portions of context common to join and base relation */ context.buf = buf; context.root = root; - context.foreignrel = rel; + context.relids = rel->relids; context.params_list = params_list; + context.is_joinrel = (rel->reloptkind == RELOPT_JOINREL); /* Construct SELECT clause and FROM clause */ - deparseSelectSql(tlist, retrieved_attrs, &context); + deparseSelectSql(tlist, rel, &context); /* * Construct WHERE clause @@ -802,55 +896,26 @@ deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, * of the specified foreign table, and append it to "buf". The output * contains just "SELECT ... FROM ....". * - * We also create an integer List of the columns being retrieved, which is - * returned to *retrieved_attrs. - * * tlist is the list of desired columns. Read prologue of * deparseSelectStmtForRel() for details. */ static void -deparseSelectSql(List *tlist, List **retrieved_attrs, deparse_expr_cxt *context) +deparseSelectSql(List *tlist, RelOptInfo *rel, deparse_expr_cxt *context) { StringInfo buf = context->buf; - RelOptInfo *foreignrel = context->foreignrel; PlannerInfo *root = context->root; - PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; /* * Construct SELECT list */ appendStringInfoString(buf, "SELECT "); - - if (foreignrel->reloptkind == RELOPT_JOINREL) - { - /* For a join relation use the input tlist */ - deparseExplicitTargetList(tlist, retrieved_attrs, context); - } - else - { - /* - * For a base relation fpinfo->attrs_used gives the list of columns - * required to be fetched from the foreign server. - */ - RangeTblEntry *rte = planner_rt_fetch(foreignrel->relid, root); - - /* - * Core code already has some lock on each rel being planned, so we - * can use NoLock here. - */ - Relation rel = heap_open(rte->relid, NoLock); - - deparseTargetList(buf, root, foreignrel->relid, rel, fpinfo->attrs_used, - retrieved_attrs, false); - heap_close(rel, NoLock); - } + deparseExplicitTargetList(tlist, context); /* * Construct FROM clause */ appendStringInfoString(buf, " FROM "); - deparseFromExprForRel(buf, root, foreignrel, - (foreignrel->reloptkind == RELOPT_JOINREL), + deparseFromExprForRel(buf, root, rel, context->is_joinrel, context->params_list); } @@ -872,62 +937,18 @@ deparseTargetList(StringInfo buf, List **retrieved_attrs, bool qualify_col) { - TupleDesc tupdesc = RelationGetDescr(rel); - bool have_wholerow; - bool first; - int i; - - *retrieved_attrs = NIL; - - /* If there's a whole-row reference, we'll need all the columns. */ - have_wholerow = bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, - attrs_used); - - first = true; - for (i = 1; i <= tupdesc->natts; i++) - { - Form_pg_attribute attr = tupdesc->attrs[i - 1]; - - /* Ignore dropped attributes. */ - if (attr->attisdropped) - continue; - - if (have_wholerow || - bms_is_member(i - FirstLowInvalidHeapAttributeNumber, - attrs_used)) - { - if (!first) - appendStringInfoString(buf, ", "); - first = false; - - deparseColumnRef(buf, rtindex, i, root, qualify_col); - - *retrieved_attrs = lappend_int(*retrieved_attrs, i); - } - } - - /* - * Add ctid if needed. We currently don't support retrieving any other - * system columns. - */ - if (bms_is_member(SelfItemPointerAttributeNumber - FirstLowInvalidHeapAttributeNumber, - attrs_used)) - { - if (!first) - appendStringInfoString(buf, ", "); - first = false; - - if (qualify_col) - ADD_REL_QUALIFIER(buf, rtindex); - appendStringInfoString(buf, "ctid"); + deparse_expr_cxt context; + List *tlist = build_tlist_from_attrs_used(rtindex, attrs_used, root, + retrieved_attrs); - *retrieved_attrs = lappend_int(*retrieved_attrs, - SelfItemPointerAttributeNumber); - } + context.buf = buf; + context.root = root; + context.relids = bms_make_singleton(rtindex); + /* Columns need to be qualified for join relation */ + context.is_joinrel = qualify_col; + context.params_list = NULL; - /* Don't generate bad syntax if no undropped columns */ - if (first) - appendStringInfoString(buf, "NULL"); + deparseExplicitTargetList(tlist, &context); } /* @@ -939,10 +960,10 @@ deparseLockingClause(deparse_expr_cxt *context) { StringInfo buf = context->buf; PlannerInfo *root = context->root; - RelOptInfo *rel = context->foreignrel; + Relids relids = context->relids; int relid = -1; - while ((relid = bms_next_member(rel->relids, relid)) >= 0) + while ((relid = bms_next_member(relids, relid)) >= 0) { /* * Add FOR UPDATE/SHARE if appropriate. We apply locking during the @@ -963,7 +984,7 @@ deparseLockingClause(deparse_expr_cxt *context) appendStringInfoString(buf, " FOR UPDATE"); /* Add the relation alias if we are here for a join relation */ - if (rel->reloptkind == RELOPT_JOINREL) + if (context->is_joinrel) appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid); } else @@ -999,8 +1020,7 @@ deparseLockingClause(deparse_expr_cxt *context) } /* Add the relation alias if we are here for a join relation */ - if (rel->reloptkind == RELOPT_JOINREL && - rc->strength != LCS_NONE) + if (context->is_joinrel && rc->strength != LCS_NONE) appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid); } } @@ -1084,20 +1104,14 @@ get_jointype_name(JoinType jointype) * Deparse given targetlist and append it to context->buf. * * tlist is list of TargetEntry's which in turn contain Var nodes. - * - * retrieved_attrs is the list of continuously increasing integers starting - * from 1. It has same number of entries as tlist. */ static void -deparseExplicitTargetList(List *tlist, List **retrieved_attrs, - deparse_expr_cxt *context) +deparseExplicitTargetList(List *tlist, deparse_expr_cxt *context) { ListCell *lc; StringInfo buf = context->buf; int i = 0; - *retrieved_attrs = NIL; - foreach(lc, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(lc); @@ -1113,8 +1127,6 @@ deparseExplicitTargetList(List *tlist, List **retrieved_attrs, appendStringInfoString(buf, ", "); deparseVar(var, context); - *retrieved_attrs = lappend_int(*retrieved_attrs, i + 1); - i++; } @@ -1162,9 +1174,10 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, deparse_expr_cxt context; context.buf = buf; - context.foreignrel = foreignrel; + context.relids = foreignrel->relids; context.root = root; context.params_list = params_list; + context.is_joinrel = true; appendStringInfo(buf, "("); appendConditions(fpinfo->joinclauses, &context); @@ -1688,9 +1701,9 @@ deparseExpr(Expr *node, deparse_expr_cxt *context) static void deparseVar(Var *node, deparse_expr_cxt *context) { - bool qualify_col = (context->foreignrel->reloptkind == RELOPT_JOINREL); + bool qualify_col = context->is_joinrel; - if (bms_is_member(node->varno, context->foreignrel->relids) && + if (bms_is_member(node->varno, context->relids) && node->varlevelsup == 0) deparseColumnRef(context->buf, node->varno, node->varattno, context->root, qualify_col); @@ -2278,7 +2291,7 @@ appendOrderByClause(List *pathkeys, deparse_expr_cxt *context) ListCell *lcell; int nestlevel; char *delim = " "; - RelOptInfo *baserel = context->foreignrel; + Relids relids = context->relids; StringInfo buf = context->buf; /* Make sure any constants in the exprs are printed portably */ @@ -2290,7 +2303,7 @@ appendOrderByClause(List *pathkeys, deparse_expr_cxt *context) PathKey *pathkey = lfirst(lcell); Expr *em_expr; - em_expr = find_em_expr_for_rel(pathkey->pk_eclass, baserel); + em_expr = find_em_expr_for_rel(pathkey->pk_eclass, relids); Assert(em_expr != NULL); appendStringInfoString(buf, delim); diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 23b4b23..71466ba 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -691,7 +691,7 @@ get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel) * checking ec_has_volatile here saves some cycles. */ if (pathkey_ec->ec_has_volatile || - !(em_expr = find_em_expr_for_rel(pathkey_ec, rel)) || + !(em_expr = find_em_expr_for_rel(pathkey_ec, rel->relids)) || !is_foreign_expr(root, rel, em_expr)) { query_pathkeys_ok = false; @@ -744,7 +744,7 @@ get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel) continue; /* If no pushable expression for this rel, skip it. */ - em_expr = find_em_expr_for_rel(cur_ec, rel); + em_expr = find_em_expr_for_rel(cur_ec, rel->relids); if (em_expr == NULL || !is_foreign_expr(root, rel, em_expr)) continue; @@ -1084,10 +1084,11 @@ postgresGetForeignPlan(PlannerInfo *root, remote_conds = fpinfo->remote_conds; local_exprs = fpinfo->local_conds; - /* Build the list of columns to be fetched from the foreign server. */ - fdw_scan_tlist = build_tlist_to_deparse(foreignrel); } + /* Build the list of columns to be fetched from the foreign server. */ + fdw_scan_tlist = build_tlist_to_deparse(foreignrel, root, &retrieved_attrs); + /* * Build the query string to be sent for execution, and identify * expressions to be sent as parameters. @@ -1095,7 +1096,7 @@ postgresGetForeignPlan(PlannerInfo *root, initStringInfo(&sql); deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist, remote_conds, best_path->path.pathkeys, - &retrieved_attrs, ¶ms_list); + ¶ms_list); /* * Build the fdw_private list that will be available to the executor. @@ -1108,6 +1109,11 @@ postgresGetForeignPlan(PlannerInfo *root, if (foreignrel->reloptkind == RELOPT_JOINREL) fdw_private = lappend(fdw_private, makeString(fpinfo->relation_name->data)); + else + { + /* We shouldn't set fdw_scan_tlist for a base relation */ + fdw_scan_tlist = NIL; + } /* * Create the ForeignScan node for the given relation. @@ -2070,7 +2076,7 @@ estimate_path_cost_size(PlannerInfo *root, List *fdw_scan_tlist = NIL; List *remote_conds; - /* Required only to be passed to deparseSelectStmtForRel */ + /* Required only to be passed to build_tlist_to_deparse(). */ List *retrieved_attrs; /* @@ -2081,10 +2087,8 @@ estimate_path_cost_size(PlannerInfo *root, &remote_param_join_conds, &local_param_join_conds); /* Build the list of columns to be fetched from the foreign server. */ - if (foreignrel->reloptkind == RELOPT_JOINREL) - fdw_scan_tlist = build_tlist_to_deparse(foreignrel); - else - fdw_scan_tlist = NIL; + fdw_scan_tlist = build_tlist_to_deparse(foreignrel, root, + &retrieved_attrs); /* * The complete list of remote conditions includes everything from @@ -2102,8 +2106,7 @@ estimate_path_cost_size(PlannerInfo *root, initStringInfo(&sql); appendStringInfoString(&sql, "EXPLAIN "); deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist, - remote_conds, pathkeys, &retrieved_attrs, - NULL); + remote_conds, pathkeys, NULL); /* Get the remote estimate */ conn = GetConnection(fpinfo->user, false); @@ -3833,7 +3836,7 @@ conversion_error_callback(void *arg) * the indicated relation. */ extern Expr * -find_em_expr_for_rel(EquivalenceClass *ec, RelOptInfo *rel) +find_em_expr_for_rel(EquivalenceClass *ec, Relids relids) { ListCell *lc_em; @@ -3841,7 +3844,7 @@ find_em_expr_for_rel(EquivalenceClass *ec, RelOptInfo *rel) { EquivalenceMember *em = lfirst(lc_em); - if (bms_equal(em->em_relids, rel->relids)) + if (bms_equal(em->em_relids, relids)) { /* * If there is more than one equivalence member whose Vars are diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h index 4633d46..a39ad6b 100644 --- a/contrib/postgres_fdw/postgres_fdw.h +++ b/contrib/postgres_fdw/postgres_fdw.h @@ -138,12 +138,12 @@ extern void deparseAnalyzeSizeSql(StringInfo buf, Relation rel); extern void deparseAnalyzeSql(StringInfo buf, Relation rel, List **retrieved_attrs); extern void deparseStringLiteral(StringInfo buf, const char *val); -extern Expr *find_em_expr_for_rel(EquivalenceClass *ec, RelOptInfo *rel); -extern List *build_tlist_to_deparse(RelOptInfo *foreign_rel); +extern Expr *find_em_expr_for_rel(EquivalenceClass *ec, Relids relids); +extern List *build_tlist_to_deparse(RelOptInfo *foreign_rel, PlannerInfo *root, + List **retrieved_attrs); extern void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, List *tlist, - List *remote_conds, List *pathkeys, - List **retrieved_attrs, List **params_list); + List *remote_conds, List *pathkeys, List **params_list); /* in shippable.c */ extern bool is_builtin(Oid objectId);