diff options
Diffstat (limited to 'contrib/postgres_fdw/deparse.c')
-rw-r--r-- | contrib/postgres_fdw/deparse.c | 531 |
1 files changed, 414 insertions, 117 deletions
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index 7c1a619b89b..27ecdcd944e 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -44,10 +44,12 @@ #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "commands/defrem.h" +#include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "nodes/plannodes.h" #include "optimizer/clauses.h" #include "optimizer/prep.h" +#include "optimizer/tlist.h" #include "optimizer/var.h" #include "parser/parsetree.h" #include "utils/builtins.h" @@ -96,6 +98,11 @@ typedef struct deparse_expr_cxt List **params_list; /* exprs that will become remote Params */ } deparse_expr_cxt; +#define REL_ALIAS_PREFIX "r" +/* Handy macro to add relation name qualification */ +#define ADD_REL_QUALIFIER(buf, varno) \ + appendStringInfo((buf), "%s%d.", REL_ALIAS_PREFIX, (varno)) + /* * Functions to determine whether an expression can be evaluated safely on * remote server. @@ -114,14 +121,17 @@ static void deparseTargetList(StringInfo buf, Relation rel, bool is_returning, Bitmapset *attrs_used, + bool qualify_col, List **retrieved_attrs); +static void deparseExplicitTargetList(List *tlist, List **retrieved_attrs, + deparse_expr_cxt *context); static void deparseReturningList(StringInfo buf, PlannerInfo *root, Index rtindex, Relation rel, bool trig_after_row, List *returningList, List **retrieved_attrs); static void deparseColumnRef(StringInfo buf, int varno, int varattno, - PlannerInfo *root); + PlannerInfo *root, bool qualify_col); static void deparseRelation(StringInfo buf, Relation rel); static void deparseExpr(Expr *expr, deparse_expr_cxt *context); static void deparseVar(Var *node, deparse_expr_cxt *context); @@ -142,11 +152,13 @@ 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(Bitmapset *attrs_used, List **retrieved_attrs, +static void deparseSelectSql(List *tlist, List **retrieved_attrs, deparse_expr_cxt *context); static void deparseLockingClause(deparse_expr_cxt *context); -static void appendWhereClause(List *exprs, 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); /* @@ -269,7 +281,7 @@ foreign_expr_walker(Node *node, * Param's collation, ie it's not safe for it to have a * non-default collation. */ - if (var->varno == glob_cxt->foreignrel->relid && + if (bms_is_member(var->varno, glob_cxt->foreignrel->relids) && var->varlevelsup == 0) { /* Var belongs to foreign table */ @@ -704,13 +716,38 @@ deparse_type_name(Oid type_oid, int32 typemod) } /* - * Deparse SELECT statement for given relation into buf. + * Build the targetlist for given relation to be deparsed as SELECT clause. * - * remote_conds is the list of conditions to be deparsed as WHERE clause. + * The output targetlist contains the columns that need to be fetched from the + * foreign server for the given relation. + */ +List * +build_tlist_to_deparse(RelOptInfo *foreignrel) +{ + 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)); + + return tlist; +} + +/* + * Deparse SELECT statement for given relation into buf. * - * pathkeys is the list of pathkeys to order the result by. + * tlist contains the list of desired columns to be fetched from foreign server. + * For a base relation fpinfo->attrs_used is used to construct SELECT clause, + * hence the tlist is ignored for a base relation. * - * List of columns selected is returned in retrieved_attrs. + * remote_conds is the list of conditions to be deparsed as WHERE clause. * * If params_list is not NULL, it receives a list of Params and other-relation * Vars used in the clauses; these values must be transmitted to the remote @@ -718,28 +755,40 @@ deparse_type_name(Oid type_oid, int32 typemod) * * If params_list is NULL, we're generating the query for EXPLAIN purposes, * 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 *remote_conds, List *pathkeys, + List *tlist, List *remote_conds, List *pathkeys, List **retrieved_attrs, List **params_list) { - PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private; deparse_expr_cxt context; - /* Initialize params_list if caller needs one */ - if (params_list) - *params_list = NIL; + /* We handle relations for foreign tables and joins between those */ + Assert(rel->reloptkind == RELOPT_JOINREL || + rel->reloptkind == RELOPT_BASEREL || + rel->reloptkind == RELOPT_OTHER_MEMBER_REL); + /* Fill portions of context common to join and base relation */ context.buf = buf; context.root = root; context.foreignrel = rel; context.params_list = params_list; - deparseSelectSql(fpinfo->attrs_used, retrieved_attrs, &context); + /* Construct SELECT clause and FROM clause */ + deparseSelectSql(tlist, retrieved_attrs, &context); + /* + * Construct WHERE clause + */ if (remote_conds) - appendWhereClause(remote_conds, &context); + { + appendStringInfo(buf, " WHERE "); + appendConditions(remote_conds, &context); + } /* Add ORDER BY clause if we found any useful pathkeys */ if (pathkeys) @@ -752,41 +801,58 @@ deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, /* * Construct a simple SELECT statement that retrieves desired columns * of the specified foreign table, and append it to "buf". The output - * contains just "SELECT ... FROM tablename". + * 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(Bitmapset *attrs_used, List **retrieved_attrs, - deparse_expr_cxt *context) +deparseSelectSql(List *tlist, List **retrieved_attrs, deparse_expr_cxt *context) { StringInfo buf = context->buf; RelOptInfo *foreignrel = context->foreignrel; PlannerInfo *root = context->root; - RangeTblEntry *rte = planner_rt_fetch(foreignrel->relid, root); - Relation rel; - - /* - * Core code already has some lock on each rel being planned, so we can - * use NoLock here. - */ - rel = heap_open(rte->relid, NoLock); + PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; /* * Construct SELECT list */ appendStringInfoString(buf, "SELECT "); - deparseTargetList(buf, root, foreignrel->relid, rel, false, attrs_used, - retrieved_attrs); + + 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, false, + fpinfo->attrs_used, false, retrieved_attrs); + heap_close(rel, NoLock); + } /* * Construct FROM clause */ appendStringInfoString(buf, " FROM "); - deparseRelation(buf, rel); - - heap_close(rel, NoLock); + deparseFromExprForRel(buf, root, foreignrel, + (foreignrel->reloptkind == RELOPT_JOINREL), + context->params_list); } /* @@ -796,6 +862,8 @@ deparseSelectSql(Bitmapset *attrs_used, List **retrieved_attrs, * * The tlist text is appended to buf, and we also create an integer List * of the columns being retrieved, which is returned to *retrieved_attrs. + * + * If qualify_col is true, add relation alias before the column name. */ static void deparseTargetList(StringInfo buf, @@ -804,6 +872,7 @@ deparseTargetList(StringInfo buf, Relation rel, bool is_returning, Bitmapset *attrs_used, + bool qualify_col, List **retrieved_attrs) { TupleDesc tupdesc = RelationGetDescr(rel); @@ -836,7 +905,7 @@ deparseTargetList(StringInfo buf, appendStringInfoString(buf, " RETURNING "); first = false; - deparseColumnRef(buf, rtindex, i, root); + deparseColumnRef(buf, rtindex, i, root, qualify_col); *retrieved_attrs = lappend_int(*retrieved_attrs, i); } @@ -855,6 +924,8 @@ deparseTargetList(StringInfo buf, appendStringInfoString(buf, " RETURNING "); first = false; + if (qualify_col) + ADD_REL_QUALIFIER(buf, rtindex); appendStringInfoString(buf, "ctid"); *retrieved_attrs = lappend_int(*retrieved_attrs, @@ -876,64 +947,81 @@ deparseLockingClause(deparse_expr_cxt *context) StringInfo buf = context->buf; PlannerInfo *root = context->root; RelOptInfo *rel = context->foreignrel; + int relid = -1; - /* - * Add FOR UPDATE/SHARE if appropriate. We apply locking during the - * initial row fetch, rather than later on as is done for local tables. - * The extra roundtrips involved in trying to duplicate the local - * semantics exactly don't seem worthwhile (see also comments for - * RowMarkType). - * - * Note: because we actually run the query as a cursor, this assumes that - * DECLARE CURSOR ... FOR UPDATE is supported, which it isn't before 8.3. - */ - if (rel->relid == root->parse->resultRelation && - (root->parse->commandType == CMD_UPDATE || - root->parse->commandType == CMD_DELETE)) + while ((relid = bms_next_member(rel->relids, relid)) >= 0) { - /* Relation is UPDATE/DELETE target, so use FOR UPDATE */ - appendStringInfoString(buf, " FOR UPDATE"); - } - else - { - PlanRowMark *rc = get_plan_rowmark(root->rowMarks, rel->relid); + /* + * Add FOR UPDATE/SHARE if appropriate. We apply locking during the + * initial row fetch, rather than later on as is done for local + * tables. The extra roundtrips involved in trying to duplicate the + * local semantics exactly don't seem worthwhile (see also comments + * for RowMarkType). + * + * Note: because we actually run the query as a cursor, this assumes + * that DECLARE CURSOR ... FOR UPDATE is supported, which it isn't + * before 8.3. + */ + if (relid == root->parse->resultRelation && + (root->parse->commandType == CMD_UPDATE || + root->parse->commandType == CMD_DELETE)) + { + /* Relation is UPDATE/DELETE target, so use FOR UPDATE */ + appendStringInfoString(buf, " FOR UPDATE"); - if (rc) + /* Add the relation alias if we are here for a join relation */ + if (rel->reloptkind == RELOPT_JOINREL) + appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid); + } + else { - /* - * Relation is specified as a FOR UPDATE/SHARE target, so handle - * that. (But we could also see LCS_NONE, meaning this isn't a - * target relation after all.) - * - * For now, just ignore any [NO] KEY specification, since (a) it's - * not clear what that means for a remote table that we don't have - * complete information about, and (b) it wouldn't work anyway on - * older remote servers. Likewise, we don't worry about NOWAIT. - */ - switch (rc->strength) + PlanRowMark *rc = get_plan_rowmark(root->rowMarks, relid); + + if (rc) { - case LCS_NONE: - /* No locking needed */ - break; - case LCS_FORKEYSHARE: - case LCS_FORSHARE: - appendStringInfoString(buf, " FOR SHARE"); - break; - case LCS_FORNOKEYUPDATE: - case LCS_FORUPDATE: - appendStringInfoString(buf, " FOR UPDATE"); - break; + /* + * Relation is specified as a FOR UPDATE/SHARE target, so + * handle that. (But we could also see LCS_NONE, meaning this + * isn't a target relation after all.) + * + * For now, just ignore any [NO] KEY specification, since (a) + * it's not clear what that means for a remote table that we + * don't have complete information about, and (b) it wouldn't + * work anyway on older remote servers. Likewise, we don't + * worry about NOWAIT. + */ + switch (rc->strength) + { + case LCS_NONE: + /* No locking needed */ + break; + case LCS_FORKEYSHARE: + case LCS_FORSHARE: + appendStringInfoString(buf, " FOR SHARE"); + break; + case LCS_FORNOKEYUPDATE: + case LCS_FORUPDATE: + appendStringInfoString(buf, " FOR UPDATE"); + break; + } + + /* Add the relation alias if we are here for a join relation */ + if (rel->reloptkind == RELOPT_JOINREL && + rc->strength != LCS_NONE) + appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid); } } } } /* - * Deparse WHERE clauses in given list of RestrictInfos and append them to - * context->buf. + * Deparse conditions from the provided list and append them to buf. + * + * The conditions in the list are assumed to be ANDed. This function is used to + * deparse both WHERE clauses and JOIN .. ON clauses. */ static void -appendWhereClause(List *exprs, deparse_expr_cxt *context) +appendConditions(List *exprs, deparse_expr_cxt *context) { int nestlevel; ListCell *lc; @@ -945,16 +1033,25 @@ appendWhereClause(List *exprs, deparse_expr_cxt *context) foreach(lc, exprs) { - RestrictInfo *ri = (RestrictInfo *) lfirst(lc); + Expr *expr = (Expr *) lfirst(lc); + + /* + * Extract clause from RestrictInfo, if required. See comments in + * declaration of PgFdwRelationInfo for details. + */ + if (IsA(expr, RestrictInfo)) + { + RestrictInfo *ri = (RestrictInfo *) expr; + + expr = ri->clause; + } /* Connect expressions with "AND" and parenthesize each condition. */ - if (is_first) - appendStringInfoString(buf, " WHERE "); - else + if (!is_first) appendStringInfoString(buf, " AND "); appendStringInfoChar(buf, '('); - deparseExpr(ri->clause, context); + deparseExpr(expr, context); appendStringInfoChar(buf, ')'); is_first = false; @@ -963,6 +1060,156 @@ appendWhereClause(List *exprs, deparse_expr_cxt *context) reset_transmission_modes(nestlevel); } +/* Output join name for given join type */ +extern const char * +get_jointype_name(JoinType jointype) +{ + switch (jointype) + { + case JOIN_INNER: + return "INNER"; + + case JOIN_LEFT: + return "LEFT"; + + case JOIN_RIGHT: + return "RIGHT"; + + case JOIN_FULL: + return "FULL"; + + default: + /* Shouldn't come here, but protect from buggy code. */ + elog(ERROR, "unsupported join type %d", jointype); + } + + /* Keep compiler happy */ + return NULL; +} + +/* + * 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) +{ + ListCell *lc; + StringInfo buf = context->buf; + int i = 0; + + *retrieved_attrs = NIL; + + foreach(lc, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(lc); + Var *var; + + /* Extract expression if TargetEntry node */ + Assert(IsA(tle, TargetEntry)); + var = (Var *) tle->expr; + /* We expect only Var nodes here */ + Assert(IsA(var, Var)); + + if (i > 0) + appendStringInfoString(buf, ", "); + deparseVar(var, context); + + *retrieved_attrs = lappend_int(*retrieved_attrs, i + 1); + + i++; + } + + if (i == 0) + appendStringInfoString(buf, "NULL"); +} + +/* + * Construct FROM clause for given relation + * + * The function constructs ... JOIN ... ON ... for join relation. For a base + * relation it just returns schema-qualified tablename, with the appropriate + * alias if so requested. + */ +void +deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, + bool use_alias, List **params_list) +{ + PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; + + if (foreignrel->reloptkind == RELOPT_JOINREL) + { + RelOptInfo *rel_o = fpinfo->outerrel; + RelOptInfo *rel_i = fpinfo->innerrel; + StringInfoData join_sql_o; + StringInfoData join_sql_i; + + /* Deparse outer relation */ + initStringInfo(&join_sql_o); + deparseFromExprForRel(&join_sql_o, root, rel_o, true, params_list); + + /* Deparse inner relation */ + initStringInfo(&join_sql_i); + deparseFromExprForRel(&join_sql_i, root, rel_i, true, params_list); + + /* + * 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); + + /* Append join clause; (TRUE) if no join clause */ + if (fpinfo->joinclauses) + { + deparse_expr_cxt context; + + context.buf = buf; + context.foreignrel = foreignrel; + context.root = root; + context.params_list = params_list; + + appendStringInfo(buf, "("); + appendConditions(fpinfo->joinclauses, &context); + appendStringInfo(buf, ")"); + } + else + appendStringInfoString(buf, "(TRUE)"); + + /* End the FROM clause entry. */ + appendStringInfo(buf, ")"); + } + else + { + 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); + + deparseRelation(buf, rel); + + /* + * Add a unique alias to avoid any conflict in relation names due to + * pulled up subqueries in the query being built for a pushed down + * join. + */ + if (use_alias) + appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, foreignrel->relid); + + heap_close(rel, NoLock); + } + return; +} + /* * deparse remote INSERT statement * @@ -996,7 +1243,7 @@ deparseInsertSql(StringInfo buf, PlannerInfo *root, appendStringInfoString(buf, ", "); first = false; - deparseColumnRef(buf, rtindex, attnum, root); + deparseColumnRef(buf, rtindex, attnum, root, false); } appendStringInfoString(buf, ") VALUES ("); @@ -1057,7 +1304,7 @@ deparseUpdateSql(StringInfo buf, PlannerInfo *root, appendStringInfoString(buf, ", "); first = false; - deparseColumnRef(buf, rtindex, attnum, root); + deparseColumnRef(buf, rtindex, attnum, root, false); appendStringInfo(buf, " = $%d", pindex); pindex++; } @@ -1120,7 +1367,7 @@ deparseReturningList(StringInfo buf, PlannerInfo *root, } if (attrs_used != NULL) - deparseTargetList(buf, root, rtindex, rel, true, attrs_used, + deparseTargetList(buf, root, rtindex, rel, true, attrs_used, false, retrieved_attrs); else *retrieved_attrs = NIL; @@ -1212,45 +1459,97 @@ deparseAnalyzeSql(StringInfo buf, Relation rel, List **retrieved_attrs) /* * Construct name to use for given column, and emit it into buf. * If it has a column_name FDW option, use that instead of attribute name. + * + * If qualify_col is true, qualify column name with the alias of relation. */ static void -deparseColumnRef(StringInfo buf, int varno, int varattno, PlannerInfo *root) +deparseColumnRef(StringInfo buf, int varno, int varattno, PlannerInfo *root, + bool qualify_col) { RangeTblEntry *rte; - char *colname = NULL; - List *options; - ListCell *lc; - /* varno must not be any of OUTER_VAR, INNER_VAR and INDEX_VAR. */ - Assert(!IS_SPECIAL_VARNO(varno)); + /* varattno can be a whole-row reference, ctid or a regular table column */ + if (varattno == SelfItemPointerAttributeNumber) + { + if (qualify_col) + ADD_REL_QUALIFIER(buf, varno); + appendStringInfoString(buf, "ctid"); + } + else if (varattno == 0) + { + /* Whole row reference */ + Relation rel; + Bitmapset *attrs_used; - /* Get RangeTblEntry from array in PlannerInfo. */ - rte = planner_rt_fetch(varno, root); + /* Required only to be passed down to deparseTargetList(). */ + List *retrieved_attrs; - /* - * If it's a column of a foreign table, and it has the column_name FDW - * option, use that value. - */ - options = GetForeignColumnOptions(rte->relid, varattno); - foreach(lc, options) + /* Get RangeTblEntry from array in PlannerInfo. */ + rte = planner_rt_fetch(varno, root); + + /* + * The lock on the relation will be held by upper callers, so it's + * fine to open it with no lock here. + */ + rel = heap_open(rte->relid, NoLock); + + /* + * The local name of the foreign table can not be recognized by the + * foreign server and the table it references on foreign server might + * have different column ordering or different columns than those + * declared locally. Hence we have to deparse whole-row reference as + * ROW(columns referenced locally). Construct this by deparsing a + * "whole row" attribute. + */ + attrs_used = bms_add_member(NULL, + 0 - FirstLowInvalidHeapAttributeNumber); + appendStringInfoString(buf, "ROW("); + deparseTargetList(buf, root, varno, rel, false, attrs_used, qualify_col, + &retrieved_attrs); + appendStringInfoString(buf, ")"); + heap_close(rel, NoLock); + bms_free(attrs_used); + } + else { - DefElem *def = (DefElem *) lfirst(lc); + char *colname = NULL; + List *options; + ListCell *lc; + + /* varno must not be any of OUTER_VAR, INNER_VAR and INDEX_VAR. */ + Assert(!IS_SPECIAL_VARNO(varno)); - if (strcmp(def->defname, "column_name") == 0) + /* Get RangeTblEntry from array in PlannerInfo. */ + rte = planner_rt_fetch(varno, root); + + /* + * If it's a column of a foreign table, and it has the column_name FDW + * option, use that value. + */ + options = GetForeignColumnOptions(rte->relid, varattno); + foreach(lc, options) { - colname = defGetString(def); - break; + DefElem *def = (DefElem *) lfirst(lc); + + if (strcmp(def->defname, "column_name") == 0) + { + colname = defGetString(def); + break; + } } - } - /* - * If it's a column of a regular table or it doesn't have column_name FDW - * option, use attribute name. - */ - if (colname == NULL) - colname = get_relid_attribute_name(rte->relid, varattno); + /* + * If it's a column of a regular table or it doesn't have column_name + * FDW option, use attribute name. + */ + if (colname == NULL) + colname = get_relid_attribute_name(rte->relid, varattno); + + if (qualify_col) + ADD_REL_QUALIFIER(buf, varno); - appendStringInfoString(buf, quote_identifier(colname)); + appendStringInfoString(buf, quote_identifier(colname)); + } } /* @@ -1395,14 +1694,12 @@ deparseExpr(Expr *node, deparse_expr_cxt *context) static void deparseVar(Var *node, deparse_expr_cxt *context) { - StringInfo buf = context->buf; + bool qualify_col = (context->foreignrel->reloptkind == RELOPT_JOINREL); - if (node->varno == context->foreignrel->relid && + if (bms_is_member(node->varno, context->foreignrel->relids) && node->varlevelsup == 0) - { - /* Var belongs to foreign table */ - deparseColumnRef(buf, node->varno, node->varattno, context->root); - } + deparseColumnRef(context->buf, node->varno, node->varattno, + context->root, qualify_col); else { /* Treat like a Param */ |