On 2016/03/29 15:37, Etsuro Fujita wrote:
I added two helper functions: GetFdwScanTupleExtraData and
FillFdwScanTupleSysAttrs.  The FDW author could use the former to get
info about system attributes other than ctids and oids in fdw_scan_tlist
during BeginForeignScan, and the latter to set values for these system
attributes during IterateForeignScan (InvalidTransactionId for
xmins/xmaxs, InvalidCommandId for cmins/cmaxs, and valid values for
tableoids).  Attached is a proposed patch for that.  I also slightly
simplified the changes to make_tuple_from_result_row and
conversion_error_callback made by the postgres_fdw join pushdown patch.
  What do you think about that?

I revised comments a little bit. Attached is an updated version of the patch. I think this issue should be fixed in advance of the PostgreSQL 9.6beta1 release.

Best regards,
Etsuro Fujita
*** a/contrib/postgres_fdw/deparse.c
--- b/contrib/postgres_fdw/deparse.c
***************
*** 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");
  }
  
--- 1091,1142 ----
   *
   * tlist is list of TargetEntry's which in turn contain Var nodes.
   *
!  * System columns other than ctid and oid are not retrieved from the remote
!  * server, since we set values for such columns locally.
!  *
!  * We create an integer List of the columns being retrieved, which is returned
!  * to *retrieved_attrs.
   */
  static void
  deparseExplicitTargetList(List *tlist, List **retrieved_attrs,
  						  deparse_expr_cxt *context)
  {
  	StringInfo	buf = context->buf;
! 	ListCell   *lc;
! 	bool		first;
! 	int			i;
  
  	*retrieved_attrs = NIL;
  
+ 	i = 1;
+ 	first = true;
  	foreach(lc, tlist)
  	{
  		TargetEntry *tle = (TargetEntry *) lfirst(lc);
  		Var		   *var;
  
  		Assert(IsA(tle, TargetEntry));
  		var = (Var *) tle->expr;
  		/* We expect only Var nodes here */
  		Assert(IsA(var, Var));
  
! 		if (var->varattno >= 0 ||
! 			var->varattno == SelfItemPointerAttributeNumber ||
! 			var->varattno == ObjectIdAttributeNumber)
! 		{
! 			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
***************
*** 1923,1928 **** SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM
--- 1923,1956 ----
     1
  (10 rows)
  
+ -- System columns, except ctid and oid, 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
***************
*** 127,134 **** 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 */
--- 127,134 ----
  {
  	Relation	rel;			/* relcache entry for the foreign table. NULL
  								 * for a foreign join scan. */
  	AttInMetadata *attinmeta;	/* attribute datatype conversion metadata */
+ 	FdwScanTupleExtraData *extra;	/* info about result tup, if foreign join */
  
  	/* extracted fdw_private data */
  	char	   *query;			/* text of SELECT command */
***************
*** 246,260 **** 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 */
--- 246,253 ----
  typedef struct ConversionLocation
  {
  	Relation	rel;			/* foreign table's relcache entry. */
+ 	FdwScanTupleExtraData *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 */
***************
*** 395,402 **** 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,
--- 388,395 ----
  						   int row,
  						   Relation rel,
  						   AttInMetadata *attinmeta,
+ 						   FdwScanTupleExtraData *extra,
  						   List *retrieved_attrs,
  						   MemoryContext temp_context);
  static void conversion_error_callback(void *arg);
  static bool foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel,
***************
*** 1235,1240 **** postgresBeginForeignScan(ForeignScanState *node, int eflags)
--- 1228,1234 ----
  	EState	   *estate = node->ss.ps.state;
  	PgFdwScanState *fsstate;
  	UserMapping *user;
+ 	TupleDesc	tupdesc;
  	int			numParams;
  
  	/*
***************
*** 1316,1326 **** 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 processing of parameters used in remote query, if any.
--- 1310,1327 ----
  	 * 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 scan tuples, if foreign join */
! 	if (fsplan->scan.scanrelid > 0)
! 		fsstate->extra = NULL;
! 	else
! 		fsstate->extra = GetFdwScanTupleExtraData(fsplan->fdw_scan_tlist,
! 												  estate->es_range_table);
  
  	/*
  	 * Prepare for processing of parameters used in remote query, if any.
***************
*** 2887,2894 **** fetch_more_data(ForeignScanState *node)
  				make_tuple_from_result_row(res, i,
  										   fsstate->rel,
  										   fsstate->attinmeta,
  										   fsstate->retrieved_attrs,
- 										   node,
  										   fsstate->temp_cxt);
  		}
  
--- 2888,2895 ----
  				make_tuple_from_result_row(res, i,
  										   fsstate->rel,
  										   fsstate->attinmeta,
+ 										   fsstate->extra,
  										   fsstate->retrieved_attrs,
  										   fsstate->temp_cxt);
  		}
  
***************
*** 3106,3113 **** 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);
--- 3107,3114 ----
  		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);
***************
*** 3207,3214 **** get_returning_data(ForeignScanState *node)
  												dmstate->next_tuple,
  												dmstate->rel,
  												dmstate->attinmeta,
- 												dmstate->retrieved_attrs,
  												NULL,
  												dmstate->temp_cxt);
  			ExecStoreTuple(newtup, slot, InvalidBuffer, false);
  		}
--- 3208,3215 ----
  												dmstate->next_tuple,
  												dmstate->rel,
  												dmstate->attinmeta,
  												NULL,
+ 												dmstate->retrieved_attrs,
  												dmstate->temp_cxt);
  			ExecStoreTuple(newtup, slot, InvalidBuffer, false);
  		}
***************
*** 3609,3616 **** 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);
--- 3610,3617 ----
  		astate->rows[pos] = make_tuple_from_result_row(res, row,
  													   astate->rel,
  													   astate->attinmeta,
  													   NULL,
+ 													 astate->retrieved_attrs,
  													   astate->temp_cxt);
  
  		MemoryContextSwitchTo(oldcontext);
***************
*** 4289,4296 **** make_tuple_from_result_row(PGresult *res,
  						   int row,
  						   Relation rel,
  						   AttInMetadata *attinmeta,
  						   List *retrieved_attrs,
- 						   ForeignScanState *fsstate,
  						   MemoryContext temp_context)
  {
  	HeapTuple	tuple;
--- 4290,4297 ----
  						   int row,
  						   Relation rel,
  						   AttInMetadata *attinmeta,
+ 						   FdwScanTupleExtraData *extra,
  						   List *retrieved_attrs,
  						   MemoryContext temp_context)
  {
  	HeapTuple	tuple;
***************
*** 4317,4327 **** 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));
--- 4318,4325 ----
  		tupdesc = RelationGetDescr(rel);
  	else
  	{
! 		Assert(extra);
! 		tupdesc = attinmeta->tupdesc;
  	}
  
  	values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
***************
*** 4333,4340 **** 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;
--- 4331,4338 ----
  	 * 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;
***************
*** 4399,4404 **** make_tuple_from_result_row(PGresult *res,
--- 4397,4408 ----
  	 */
  	MemoryContextSwitchTo(oldcontext);
  
+ 	/*
+ 	 * Set values for xmins/xmaxs, cmins/cmaxs, and tableoids, if foreign join.
+ 	 */
+ 	if (extra && extra->has_sys_attrs)
+ 		FillFdwScanTupleSysAttrs(extra, values, nulls);
+ 
  	tuple = heap_form_tuple(tupdesc, values, nulls);
  
  	/*
***************
*** 4442,4462 **** 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);
  	}
--- 4446,4463 ----
  	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->range_table);
  		relname = get_rel_name(rte->relid);
  		attname = get_relid_attribute_name(rte->relid, var->varattno);
  	}
*** a/contrib/postgres_fdw/sql/postgres_fdw.sql
--- b/contrib/postgres_fdw/sql/postgres_fdw.sql
***************
*** 466,471 **** SELECT t1c1, avg(t1c1 + t2c1) FROM (SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2
--- 466,475 ----
  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 ctid and oid, 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;
*** a/src/backend/foreign/foreign.c
--- b/src/backend/foreign/foreign.c
***************
*** 14,19 ****
--- 14,20 ----
  
  #include "access/htup_details.h"
  #include "access/reloptions.h"
+ #include "access/sysattr.h"
  #include "catalog/pg_foreign_data_wrapper.h"
  #include "catalog/pg_foreign_server.h"
  #include "catalog/pg_foreign_table.h"
***************
*** 22,27 ****
--- 23,29 ----
  #include "foreign/foreign.h"
  #include "lib/stringinfo.h"
  #include "miscadmin.h"
+ #include "parser/parsetree.h"
  #include "utils/builtins.h"
  #include "utils/memutils.h"
  #include "utils/rel.h"
***************
*** 909,911 **** GetExistingLocalJoinPath(RelOptInfo *joinrel)
--- 911,1048 ----
  	}
  	return NULL;
  }
+ 
+ /*
+  * Get extra information for making scan tuples described by fdw_scan_tlist
+  *
+  * Note: we assume that an expression in each TargetEntry in fdw_scan_tlist
+  * is a simple Var.
+  */
+ FdwScanTupleExtraData *
+ GetFdwScanTupleExtraData(List *fdw_scan_tlist, List *range_table)
+ {
+ 	FdwScanTupleExtraData *extra;
+ 	List	   *xmin_attrs = NIL;
+ 	List	   *cmin_attrs = NIL;
+ 	List	   *xmax_attrs = NIL;
+ 	List	   *cmax_attrs = NIL;
+ 	List	   *tableoid_attrs = NIL;
+ 	List	   *tableoid_values = NIL;
+ 	RangeTblEntry *rte;
+ 	ListCell   *lc;
+ 	int			i;
+ 
+ 	i = 1;
+ 	foreach(lc, fdw_scan_tlist)
+ 	{
+ 		TargetEntry *tle = (TargetEntry *) lfirst(lc);
+ 		Var		   *var = (Var *) tle->expr;
+ 
+ 		if (!IsA(var, Var))
+ 			elog(ERROR, "unexpected node type in fdw_scan_tlist target entry: %d",
+ 				 (int) nodeTag(var));
+ 
+ 		if (var->varattno < 0)
+ 		{
+ 			switch (var->varattno)
+ 			{
+ 				case SelfItemPointerAttributeNumber:
+ 				case ObjectIdAttributeNumber:
+ 					break;
+ 				case MinTransactionIdAttributeNumber:
+ 					xmin_attrs = lappend_int(xmin_attrs, i);
+ 					break;
+ 				case MinCommandIdAttributeNumber:
+ 					cmin_attrs = lappend_int(cmin_attrs, i);
+ 					break;
+ 				case MaxTransactionIdAttributeNumber:
+ 					xmax_attrs = lappend_int(xmax_attrs, i);
+ 					break;
+ 				case MaxCommandIdAttributeNumber:
+ 					cmax_attrs = lappend_int(cmax_attrs, i);
+ 					break;
+ 				case TableOidAttributeNumber:
+ 					rte = rt_fetch(var->varno, range_table);
+ 					tableoid_attrs = lappend_int(tableoid_attrs, i);
+ 					tableoid_values = lappend_oid(tableoid_values, rte->relid);
+ 					break;
+ 				default:
+ 					elog(ERROR, "invalid attnum: %d", var->varattno);
+ 					break;
+ 			}
+ 		}
+ 
+ 		i++;
+ 	}
+ 
+ 	extra = (FdwScanTupleExtraData *) palloc0(sizeof(FdwScanTupleExtraData));
+ 	extra->fdw_scan_tlist = fdw_scan_tlist;
+ 	if (xmin_attrs || cmin_attrs || xmax_attrs || cmax_attrs || tableoid_attrs)
+ 		extra->has_sys_attrs = true;
+ 	else
+ 		extra->has_sys_attrs = false;
+ 	extra->xmin_attrs = xmin_attrs;
+ 	extra->cmin_attrs = cmin_attrs;
+ 	extra->xmax_attrs = xmax_attrs;
+ 	extra->cmax_attrs = cmax_attrs;
+ 	extra->tableoid_attrs = tableoid_attrs;
+ 	extra->tableoid_values = tableoid_values;
+ 	extra->range_table = range_table;
+ 
+ 	return extra;
+ }
+ 
+ /*
+  * Set values for xmins/xmaxs, cmins/cmaxs, and tableoids in a given tuple.
+  */
+ void
+ FillFdwScanTupleSysAttrs(FdwScanTupleExtraData *extra,
+ 						 Datum *values, bool *nulls)
+ {
+ 	ListCell   *lc;
+ 	ListCell   *lc2;
+ 
+ 	Assert(extra);
+ 
+ 	/* Fill xmins/xmaxs with InvalidTransactionId */
+ 	foreach(lc, extra->xmin_attrs)
+ 	{
+ 		int			i = lfirst_int(lc);
+ 
+ 		nulls[i - 1] = false;
+ 		values[i - 1] = TransactionIdGetDatum(InvalidTransactionId);
+ 	}
+ 	foreach(lc, extra->xmax_attrs)
+ 	{
+ 		int			i = lfirst_int(lc);
+ 
+ 		nulls[i - 1] = false;
+ 		values[i - 1] = TransactionIdGetDatum(InvalidTransactionId);
+ 	}
+ 
+ 	/* Fill cmins/cmaxs with InvalidCommandId */
+ 	foreach(lc, extra->cmin_attrs)
+ 	{
+ 		int			i = lfirst_int(lc);
+ 
+ 		nulls[i - 1] = false;
+ 		values[i - 1] = CommandIdGetDatum(InvalidCommandId);
+ 	}
+ 	foreach(lc, extra->cmax_attrs)
+ 	{
+ 		int			i = lfirst_int(lc);
+ 
+ 		nulls[i - 1] = false;
+ 		values[i - 1] = CommandIdGetDatum(InvalidCommandId);
+ 	}
+ 
+ 	/* Fill tableoids with valid values */
+ 	forboth(lc, extra->tableoid_attrs, lc2, extra->tableoid_values)
+ 	{
+ 		int			i = lfirst_int(lc);
+ 		Oid			tableoid = lfirst_oid(lc2);
+ 
+ 		nulls[i - 1] = false;
+ 		values[i - 1] = ObjectIdGetDatum(tableoid);
+ 	}
+ }
*** a/src/include/foreign/fdwapi.h
--- b/src/include/foreign/fdwapi.h
***************
*** 224,229 **** typedef struct FdwRoutine
--- 224,247 ----
  	InitializeWorkerForeignScan_function InitializeWorkerForeignScan;
  } FdwRoutine;
  
+ /*
+  * Struct for extra information for making scan tuples described by
+  * fdw_scan_tlist
+  */
+ typedef struct FdwScanTupleExtraData
+ {
+ 	List	   *fdw_scan_tlist;	/* tlist describing the structure of tuple */
+ 	bool		has_sys_attrs;	/* does tuple contain any system attrs other
+ 								 * than ctids and oids */
+ 	List	   *xmin_attrs;		/* attribute numbers of xmins in tuple */
+ 	List	   *cmin_attrs;		/* attribute numbers of cmins in tuple */
+ 	List	   *xmax_attrs;		/* attribute numbers of xmaxs in tuple */
+ 	List	   *cmax_attrs;		/* attribute numbers of cmaxs in tuple */
+ 	List	   *tableoid_attrs;	/* attribute numbers of tableoids in tuple */
+ 	List	   *tableoid_values;	/* OIDs of tableoids in tuple */
+ 	List	   *range_table;	/* EState's list of RangeTblEntry */
+ } FdwScanTupleExtraData;
+ 
  
  /* Functions in foreign/foreign.c */
  extern FdwRoutine *GetFdwRoutine(Oid fdwhandler);
***************
*** 234,238 **** extern FdwRoutine *GetFdwRoutineForRelation(Relation relation, bool makecopy);
--- 252,260 ----
  extern bool IsImportableForeignTable(const char *tablename,
  						 ImportForeignSchemaStmt *stmt);
  extern Path *GetExistingLocalJoinPath(RelOptInfo *joinrel);
+ extern FdwScanTupleExtraData *GetFdwScanTupleExtraData(List *fdw_scan_tlist,
+ 						 List *range_table);
+ extern void FillFdwScanTupleSysAttrs(FdwScanTupleExtraData *extra,
+ 						 Datum *values, bool *nulls);
  
  #endif   /* FDWAPI_H */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to