Hi, I noticed that the postgres_fdw join pushdown patch retrieves system columns other than ctid (and oid) from the remote server as shown in the example:
postgres=# explain verbose select foo.tableoid, foo.xmin, foo.cmin, foo.xmax, foo.cmax, foo.* from foo, bar where foo.a = bar.a; QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------------- -------- Foreign Scan (cost=100.00..102.09 rows=2 width=28) Output: foo.tableoid, foo.xmin, foo.cmin, foo.xmax, foo.cmax, foo.a, foo.b Relations: (public.foo) INNER JOIN (public.bar) Remote SQL: SELECT r1.tableoid, r1.xmin, r1.cmin, r1.xmax, r1.cmax, r1.a, r1.b FROM (public.foo r1 INNER JOIN public.bar r2 ON (TRUE)) WHERE ((r1.a = r2.a)) (4 rows) BUT: we don't make any effort to ensure that local and remote values match, so system columns other than ctid and oid should not be retrieved from the remote server. So, I'd like to propose: (1) when tableoids are requested from the remote server, postgres_fdw sets valid values for them locally, instead (core should support that?) and (2) when any of xmins, xmaxs, cmins, and cmaxs are requested, postgres_fdw gives up pushing down foreign joins. (We might be able to set appropriate values for them locally the same way as for tableoids, but I'm not sure it's worth complicating the code.) I think that would be probably OK, because users wouldn't retrieve any such columns in practice. Attached is a proposed patch for that. Best regards, Etsuro Fujita
*** a/contrib/postgres_fdw/deparse.c --- b/contrib/postgres_fdw/deparse.c *************** *** 123,129 **** static void deparseTargetList(StringInfo buf, 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, --- 123,131 ---- Bitmapset *attrs_used, bool qualify_col, List **retrieved_attrs); ! static void deparseExplicitTargetList(List *tlist, ! List **retrieved_attrs, ! List **tableoid_attrs, deparse_expr_cxt *context); static void deparseReturningList(StringInfo buf, PlannerInfo *root, Index rtindex, Relation rel, *************** *** 152,158 **** 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 deparseLockingClause(deparse_expr_cxt *context); static void appendOrderByClause(List *pathkeys, deparse_expr_cxt *context); --- 154,162 ---- deparse_expr_cxt *context); static void printRemotePlaceholder(Oid paramtype, int32 paramtypmod, deparse_expr_cxt *context); ! static void deparseSelectSql(List *tlist, ! List **retrieved_attrs, ! List **tableoid_attrs, deparse_expr_cxt *context); static void deparseLockingClause(deparse_expr_cxt *context); static void appendOrderByClause(List *pathkeys, deparse_expr_cxt *context); *************** *** 762,768 **** build_tlist_to_deparse(RelOptInfo *foreignrel) extern void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, List *tlist, List *remote_conds, List *pathkeys, ! List **retrieved_attrs, List **params_list) { deparse_expr_cxt context; --- 766,773 ---- extern void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, List *tlist, List *remote_conds, List *pathkeys, ! List **retrieved_attrs, List **tableoid_attrs, ! List **params_list) { deparse_expr_cxt context; *************** *** 778,784 **** deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, context.params_list = params_list; /* Construct SELECT clause and FROM clause */ ! deparseSelectSql(tlist, retrieved_attrs, &context); /* * Construct WHERE clause --- 783,789 ---- context.params_list = params_list; /* Construct SELECT clause and FROM clause */ ! deparseSelectSql(tlist, retrieved_attrs, tableoid_attrs, &context); /* * Construct WHERE clause *************** *** 809,815 **** deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, * deparseSelectStmtForRel() for details. */ static void ! deparseSelectSql(List *tlist, List **retrieved_attrs, deparse_expr_cxt *context) { StringInfo buf = context->buf; RelOptInfo *foreignrel = context->foreignrel; --- 814,823 ---- * deparseSelectStmtForRel() for details. */ static void ! deparseSelectSql(List *tlist, ! List **retrieved_attrs, ! List **tableoid_attrs, ! deparse_expr_cxt *context) { StringInfo buf = context->buf; RelOptInfo *foreignrel = context->foreignrel; *************** *** 824,830 **** deparseSelectSql(List *tlist, List **retrieved_attrs, deparse_expr_cxt *context) if (foreignrel->reloptkind == RELOPT_JOINREL) { /* For a join relation use the input tlist */ ! deparseExplicitTargetList(tlist, retrieved_attrs, context); } else { --- 832,838 ---- if (foreignrel->reloptkind == RELOPT_JOINREL) { /* For a join relation use the input tlist */ ! deparseExplicitTargetList(tlist, retrieved_attrs, tableoid_attrs, context); } else { *************** *** 1091,1130 **** get_jointype_name(JoinType jointype) * * 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"); } --- 1099,1163 ---- * * tlist is list of TargetEntry's which in turn contain Var nodes. * ! * We create an integer List of the columns being retrieved, which is ! * returned to *retrieved_attrs, and an integer List of the tableoid ! * columns in tlist. */ static void ! deparseExplicitTargetList(List *tlist, ! List **retrieved_attrs, ! List **tableoid_attrs, deparse_expr_cxt *context) { StringInfo buf = context->buf; ! ListCell *lc; ! bool first; ! int i; *retrieved_attrs = NIL; + *tableoid_attrs = NIL; + i = 1; + first = true; foreach(lc, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(lc); Var *var; Assert(IsA(tle, TargetEntry)); + /* Extract expression */ var = (Var *) tle->expr; /* We expect only Var nodes here */ Assert(IsA(var, Var)); ! if (var->varattno < 0 && ! var->varattno != ObjectIdAttributeNumber && ! var->varattno != SelfItemPointerAttributeNumber) ! { ! /* ! * varattno should be tableoid, since we don't consider pushing down ! * any foreign joins that retrieve any of xmin, xmax, cmin, and cmax. ! * See comments in postgresGetForeignRelSize. ! */ ! Assert(var->varattno == TableOidAttributeNumber); ! *tableoid_attrs = lappend_int(*tableoid_attrs, i); ! } ! else ! { ! if (!first) ! appendStringInfoString(buf, ", "); ! first = false; ! ! deparseVar(var, context); ! ! *retrieved_attrs = lappend_int(*retrieved_attrs, i); ! } i++; } ! if (first) appendStringInfoString(buf, "NULL"); } *** a/contrib/postgres_fdw/expected/postgres_fdw.out --- b/contrib/postgres_fdw/expected/postgres_fdw.out *************** *** 1671,1676 **** SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM --- 1671,1704 ---- 1 (10 rows) + -- System columns, except oid and ctid, should not be retrieved from remote + EXPLAIN (COSTS false, VERBOSE) + SELECT t1.tableoid::regclass, t1.c1, t2.tableoid::regclass, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; + QUERY PLAN + ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit + Output: ((t1.tableoid)::regclass), t1.c1, ((t2.tableoid)::regclass), t2.c1, t1.c3 + -> Foreign Scan + Output: (t1.tableoid)::regclass, t1.c1, (t2.tableoid)::regclass, t2.c1, t1.c3 + Relations: (public.ft1 t1) INNER JOIN (public.ft2 t2) + Remote SQL: SELECT r1."C 1", r1.c3, r2."C 1" FROM ("S 1"."T 1" r1 INNER JOIN "S 1"."T 1" r2 ON (TRUE)) WHERE ((r1."C 1" = r2."C 1")) ORDER BY r1.c3 ASC NULLS LAST, r1."C 1" ASC NULLS LAST + (6 rows) + + SELECT t1.tableoid::regclass, t1.c1, t2.tableoid::regclass, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; + tableoid | c1 | tableoid | c1 + ----------+-----+----------+----- + ft1 | 101 | ft2 | 101 + ft1 | 102 | ft2 | 102 + ft1 | 103 | ft2 | 103 + ft1 | 104 | ft2 | 104 + ft1 | 105 | ft2 | 105 + ft1 | 106 | ft2 | 106 + ft1 | 107 | ft2 | 107 + ft1 | 108 | ft2 | 108 + ft1 | 109 | ft2 | 109 + ft1 | 110 | ft2 | 110 + (10 rows) + -- create another user for permission, user mapping, effective user tests CREATE USER view_owner; -- grant privileges on ft4 and ft5 to view_owner *** a/contrib/postgres_fdw/postgres_fdw.c --- b/contrib/postgres_fdw/postgres_fdw.c *************** *** 72,78 **** enum FdwScanPrivateIndex * String describing join i.e. names of relations being joined and types * of join, added when the scan is join */ ! FdwScanPrivateRelations }; /* --- 72,81 ---- * String describing join i.e. names of relations being joined and types * of join, added when the scan is join */ ! FdwScanPrivateRelations, ! ! /* Integer list of attribute numbers of tableoids */ ! FdwScanPrivateTableOidAttrs }; /* *************** *** 98,111 **** enum FdwModifyPrivateIndex }; /* * Execution state of a foreign scan using postgres_fdw. */ typedef struct PgFdwScanState { Relation rel; /* relcache entry for the foreign table. NULL * for a foreign join scan. */ - TupleDesc tupdesc; /* tuple descriptor of scan */ AttInMetadata *attinmeta; /* attribute datatype conversion metadata */ /* extracted fdw_private data */ char *query; /* text of SELECT command */ --- 101,126 ---- }; /* + * Struct for extra information for making tuple for a foreign join + */ + typedef struct TupleExtraData + { + TupleDesc tupdesc; /* tuple descriptor for the tuple */ + List *fdw_scan_tlist; /* tlist describing contents of the tuple */ + List *tableoid_attrs; /* indexes of tableoids in fdw_scan_tlist */ + List *tableoid_vals; /* values of tableoids in fdw_scan_tlist */ + List *rtables; /* EState's list of RangeTblEntry */ + } TupleExtraData; + + /* * Execution state of a foreign scan using postgres_fdw. */ typedef struct PgFdwScanState { Relation rel; /* relcache entry for the foreign table. NULL * for a foreign join scan. */ AttInMetadata *attinmeta; /* attribute datatype conversion metadata */ + TupleExtraData *extra; /* info about result tup, if foreign join */ /* extracted fdw_private data */ char *query; /* text of SELECT command */ *************** *** 193,207 **** typedef struct PgFdwAnalyzeState typedef struct ConversionLocation { Relation rel; /* foreign table's relcache entry. */ AttrNumber cur_attno; /* attribute number being processed, or 0 */ - - /* - * In case of foreign join push down, fdw_scan_tlist is used to identify - * the Var node corresponding to the error location and - * fsstate->ss.ps.state gives access to the RTEs of corresponding relation - * to get the relation name and attribute name. - */ - ForeignScanState *fsstate; } ConversionLocation; /* Callback argument for ec_member_matches_foreign */ --- 208,215 ---- typedef struct ConversionLocation { Relation rel; /* foreign table's relcache entry. */ + TupleExtraData *extra; /* info about result tup, if foreign join */ AttrNumber cur_attno; /* attribute number being processed, or 0 */ } ConversionLocation; /* Callback argument for ec_member_matches_foreign */ *************** *** 321,328 **** static HeapTuple make_tuple_from_result_row(PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, - ForeignScanState *fsstate, MemoryContext temp_context); static void conversion_error_callback(void *arg); static bool foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, --- 329,336 ---- int row, Relation rel, AttInMetadata *attinmeta, + TupleExtraData *extra, List *retrieved_attrs, MemoryContext temp_context); static void conversion_error_callback(void *arg); static bool foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, *************** *** 407,415 **** postgresGetForeignRelSize(PlannerInfo *root, fpinfo = (PgFdwRelationInfo *) palloc0(sizeof(PgFdwRelationInfo)); baserel->fdw_private = (void *) fpinfo; - /* Base foreign tables need to be push down always. */ - fpinfo->pushdown_safe = true; - /* Look up foreign-table catalog info. */ fpinfo->table = GetForeignTable(foreigntableid); fpinfo->server = GetForeignServer(fpinfo->table->serverid); --- 415,420 ---- *************** *** 492,497 **** postgresGetForeignRelSize(PlannerInfo *root, --- 497,521 ---- } /* + * The table is pushdown-safe unless any of xmin, xmax, cmin, and cmax + * are requested from the table. Note that we don't make any effort to + * ensure that local and remote values match. We don't care about ctid, + * oid, and tableoid; ctid and oid are retrieved from the remote server, + * and tableoid is filled with a valid value locally. + */ + if (bms_is_member(MinTransactionIdAttributeNumber - FirstLowInvalidHeapAttributeNumber, + fpinfo->attrs_used) || + bms_is_member(MaxTransactionIdAttributeNumber - FirstLowInvalidHeapAttributeNumber, + fpinfo->attrs_used) || + bms_is_member(MinCommandIdAttributeNumber - FirstLowInvalidHeapAttributeNumber, + fpinfo->attrs_used) || + bms_is_member(MaxCommandIdAttributeNumber - FirstLowInvalidHeapAttributeNumber, + fpinfo->attrs_used)) + fpinfo->pushdown_safe = false; + else + fpinfo->pushdown_safe = true; + + /* * Compute the selectivity and cost of the local_conds, so we don't have * to do it over again for each path. The best we can do for these * conditions is to estimate selectivity on the basis of local statistics. *************** *** 998,1003 **** postgresGetForeignPlan(PlannerInfo *root, --- 1022,1028 ---- List *local_exprs = NIL; List *params_list = NIL; List *retrieved_attrs; + List *tableoid_attrs; StringInfoData sql; ListCell *lc; List *fdw_scan_tlist = NIL; *************** *** 1116,1122 **** postgresGetForeignPlan(PlannerInfo *root, initStringInfo(&sql); deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist, remote_conds, best_path->path.pathkeys, ! &retrieved_attrs, ¶ms_list); /* * Build the fdw_private list that will be available to the executor. --- 1141,1147 ---- initStringInfo(&sql); deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist, remote_conds, best_path->path.pathkeys, ! &retrieved_attrs, &tableoid_attrs, ¶ms_list); /* * Build the fdw_private list that will be available to the executor. *************** *** 1127,1134 **** postgresGetForeignPlan(PlannerInfo *root, --- 1152,1162 ---- makeInteger(fpinfo->fetch_size), makeInteger(foreignrel->umid)); if (foreignrel->reloptkind == RELOPT_JOINREL) + { fdw_private = lappend(fdw_private, makeString(fpinfo->relation_name->data)); + fdw_private = lappend(fdw_private, tableoid_attrs); + } /* * Create the ForeignScan node for the given relation. *************** *** 1158,1163 **** postgresBeginForeignScan(ForeignScanState *node, int eflags) --- 1186,1192 ---- EState *estate = node->ss.ps.state; PgFdwScanState *fsstate; UserMapping *user; + TupleDesc tupdesc; int numParams; int i; ListCell *lc; *************** *** 1241,1251 **** postgresBeginForeignScan(ForeignScanState *node, int eflags) * into local representation and error reporting during that process. */ if (fsplan->scan.scanrelid > 0) ! fsstate->tupdesc = RelationGetDescr(fsstate->rel); else ! fsstate->tupdesc = node->ss.ss_ScanTupleSlot->tts_tupleDescriptor; ! fsstate->attinmeta = TupleDescGetAttInMetadata(fsstate->tupdesc); /* Prepare for output conversion of parameters used in remote query. */ numParams = list_length(fsplan->fdw_exprs); --- 1270,1312 ---- * into local representation and error reporting during that process. */ if (fsplan->scan.scanrelid > 0) ! tupdesc = RelationGetDescr(fsstate->rel); else ! tupdesc = node->ss.ss_ScanTupleSlot->tts_tupleDescriptor; ! ! fsstate->attinmeta = TupleDescGetAttInMetadata(tupdesc); ! /* Create extra info for making tuples, if foreign join */ ! if (fsplan->scan.scanrelid > 0) ! fsstate->extra = NULL; ! else ! { ! TupleExtraData *extra; ! ! extra = (TupleExtraData *) palloc0(sizeof(TupleExtraData)); ! ! extra->tupdesc = tupdesc; ! extra->fdw_scan_tlist = fsplan->fdw_scan_tlist; ! extra->tableoid_attrs = (List *) list_nth(fsplan->fdw_private, ! FdwScanPrivateTableOidAttrs); ! extra->tableoid_vals = NIL; ! foreach(lc, extra->tableoid_attrs) ! { ! TargetEntry *tle; ! Var *var; ! RangeTblEntry *rte; ! ! i = lfirst_int(lc); ! tle = (TargetEntry *) list_nth(fsplan->fdw_scan_tlist, i - 1); ! var = (Var *) tle->expr; ! rte = rt_fetch(var->varno, estate->es_range_table); ! ! extra->tableoid_vals = lappend_oid(extra->tableoid_vals, rte->relid); ! } ! extra->rtables = estate->es_range_table; ! ! fsstate->extra = extra; ! } /* Prepare for output conversion of parameters used in remote query. */ numParams = list_length(fsplan->fdw_exprs); *************** *** 2092,2097 **** estimate_path_cost_size(PlannerInfo *root, --- 2153,2159 ---- /* Required only to be passed to deparseSelectStmtForRel */ List *retrieved_attrs; + List *tableoid_attrs; /* * param_join_conds might contain both clauses that are safe to send *************** *** 2123,2129 **** estimate_path_cost_size(PlannerInfo *root, appendStringInfoString(&sql, "EXPLAIN "); deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist, remote_conds, pathkeys, &retrieved_attrs, ! NULL); /* Get the remote estimate */ conn = GetConnection(fpinfo->user, false); --- 2185,2191 ---- appendStringInfoString(&sql, "EXPLAIN "); deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist, remote_conds, pathkeys, &retrieved_attrs, ! &tableoid_attrs, NULL); /* Get the remote estimate */ conn = GetConnection(fpinfo->user, false); *************** *** 2536,2543 **** fetch_more_data(ForeignScanState *node) make_tuple_from_result_row(res, i, fsstate->rel, fsstate->attinmeta, fsstate->retrieved_attrs, - node, fsstate->temp_cxt); } --- 2598,2605 ---- make_tuple_from_result_row(res, i, fsstate->rel, fsstate->attinmeta, + fsstate->extra, fsstate->retrieved_attrs, fsstate->temp_cxt); } *************** *** 2755,2762 **** store_returning_result(PgFdwModifyState *fmstate, newtup = make_tuple_from_result_row(res, 0, fmstate->rel, fmstate->attinmeta, - fmstate->retrieved_attrs, NULL, fmstate->temp_cxt); /* tuple will be deleted when it is cleared from the slot */ ExecStoreTuple(newtup, slot, InvalidBuffer, true); --- 2817,2824 ---- newtup = make_tuple_from_result_row(res, 0, fmstate->rel, fmstate->attinmeta, NULL, + fmstate->retrieved_attrs, fmstate->temp_cxt); /* tuple will be deleted when it is cleared from the slot */ ExecStoreTuple(newtup, slot, InvalidBuffer, true); *************** *** 3066,3073 **** analyze_row_processor(PGresult *res, int row, PgFdwAnalyzeState *astate) astate->rows[pos] = make_tuple_from_result_row(res, row, astate->rel, astate->attinmeta, - astate->retrieved_attrs, NULL, astate->temp_cxt); MemoryContextSwitchTo(oldcontext); --- 3128,3135 ---- astate->rows[pos] = make_tuple_from_result_row(res, row, astate->rel, astate->attinmeta, NULL, + astate->retrieved_attrs, astate->temp_cxt); MemoryContextSwitchTo(oldcontext); *************** *** 3736,3743 **** make_tuple_from_result_row(PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, - ForeignScanState *fsstate, MemoryContext temp_context) { HeapTuple tuple; --- 3798,3805 ---- int row, Relation rel, AttInMetadata *attinmeta, + TupleExtraData *extra, List *retrieved_attrs, MemoryContext temp_context) { HeapTuple tuple; *************** *** 3764,3774 **** make_tuple_from_result_row(PGresult *res, tupdesc = RelationGetDescr(rel); else { ! PgFdwScanState *fdw_sstate; ! ! Assert(fsstate); ! fdw_sstate = (PgFdwScanState *) fsstate->fdw_state; ! tupdesc = fdw_sstate->tupdesc; } values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum)); --- 3826,3833 ---- tupdesc = RelationGetDescr(rel); else { ! Assert(extra); ! tupdesc = extra->tupdesc; } values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum)); *************** *** 3780,3787 **** make_tuple_from_result_row(PGresult *res, * Set up and install callback to report where conversion error occurs. */ errpos.rel = rel; errpos.cur_attno = 0; - errpos.fsstate = fsstate; errcallback.callback = conversion_error_callback; errcallback.arg = (void *) &errpos; errcallback.previous = error_context_stack; --- 3839,3846 ---- * Set up and install callback to report where conversion error occurs. */ errpos.rel = rel; + errpos.extra = extra; errpos.cur_attno = 0; errcallback.callback = conversion_error_callback; errcallback.arg = (void *) &errpos; errcallback.previous = error_context_stack; *************** *** 3841,3846 **** make_tuple_from_result_row(PGresult *res, --- 3900,3920 ---- if (j > 0 && j != PQnfields(res)) elog(ERROR, "remote query result does not match the foreign table"); + /* Set valid values for tableoids in the tuple, if foreign join */ + if (extra) + { + ListCell *lc2; + + forboth(lc, extra->tableoid_attrs, lc2, extra->tableoid_vals) + { + int i = lfirst_int(lc); + Oid tableoid = lfirst_oid(lc2); + + nulls[i - 1] = false; + values[i - 1] = ObjectIdGetDatum(tableoid); + } + } + /* * Build the result tuple in caller's memory context. */ *************** *** 3889,3909 **** conversion_error_callback(void *arg) else { /* error occurred in a scan against a foreign join */ - ForeignScanState *fsstate = errpos->fsstate; - ForeignScan *fsplan = (ForeignScan *) fsstate->ss.ps.plan; - EState *estate = fsstate->ss.ps.state; TargetEntry *tle; Var *var; RangeTblEntry *rte; ! Assert(IsA(fsplan, ForeignScan)); ! tle = (TargetEntry *) list_nth(fsplan->fdw_scan_tlist, errpos->cur_attno - 1); Assert(IsA(tle, TargetEntry)); var = (Var *) tle->expr; Assert(IsA(var, Var)); ! rte = rt_fetch(var->varno, estate->es_range_table); relname = get_rel_name(rte->relid); attname = get_relid_attribute_name(rte->relid, var->varattno); } --- 3963,3980 ---- else { /* error occurred in a scan against a foreign join */ TargetEntry *tle; Var *var; RangeTblEntry *rte; ! Assert(errpos->cur_attno > 0); ! tle = (TargetEntry *) list_nth(errpos->extra->fdw_scan_tlist, errpos->cur_attno - 1); Assert(IsA(tle, TargetEntry)); var = (Var *) tle->expr; Assert(IsA(var, Var)); ! rte = rt_fetch(var->varno, errpos->extra->rtables); relname = get_rel_name(rte->relid); attname = get_relid_attribute_name(rte->relid, var->varattno); } *** a/contrib/postgres_fdw/postgres_fdw.h --- b/contrib/postgres_fdw/postgres_fdw.h *************** *** 143,149 **** extern List *build_tlist_to_deparse(RelOptInfo *foreign_rel); extern void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, List *tlist, List *remote_conds, List *pathkeys, ! List **retrieved_attrs, List **params_list); /* in shippable.c */ extern bool is_builtin(Oid objectId); --- 143,150 ---- extern void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, List *tlist, List *remote_conds, List *pathkeys, ! List **retrieved_attrs, List **tableoid_attrs, ! List **params_list); /* in shippable.c */ extern bool is_builtin(Oid objectId); *** a/contrib/postgres_fdw/sql/postgres_fdw.sql --- b/contrib/postgres_fdw/sql/postgres_fdw.sql *************** *** 430,435 **** SELECT t1c1, avg(t1c1 + t2c1) FROM (SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 --- 430,439 ---- EXPLAIN (COSTS false, VERBOSE) SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM ft1 t2, ft2 t3 WHERE t2.c1 = t3.c1 AND t2.c2 = t1.c2) q ORDER BY t1."C 1" OFFSET 10 LIMIT 10; SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM ft1 t2, ft2 t3 WHERE t2.c1 = t3.c1 AND t2.c2 = t1.c2) q ORDER BY t1."C 1" OFFSET 10 LIMIT 10; + -- System columns, except oid and ctid, should not be retrieved from remote + EXPLAIN (COSTS false, VERBOSE) + SELECT t1.tableoid::regclass, t1.c1, t2.tableoid::regclass, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; + SELECT t1.tableoid::regclass, t1.c1, t2.tableoid::regclass, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; -- create another user for permission, user mapping, effective user tests CREATE USER view_owner;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers