On 2015/08/27 17:30, Etsuro Fujita wrote:
I think we would probably need others' opinions about this issue.

Attached is an updated version of the patch [1]. I'd be happy if it helps people discuss about this issue.

Changes:
* rebased to HEAD.
* add some more docs and comments.
* fix a bug in handling tlist of a ForeignScan node when the node is the top node. * fix a bug in doing ExecAssignScanTypeFromOuterPlan at the top of a ForeignScan node.

Best regards,
Etsuro Fujita

[1] http://www.postgresql.org/message-id/55cb2d45.7040...@lab.ntt.co.jp
*** a/contrib/file_fdw/file_fdw.c
--- b/contrib/file_fdw/file_fdw.c
***************
*** 525,530 **** fileGetForeignPaths(PlannerInfo *root,
--- 525,531 ----
  									 total_cost,
  									 NIL,		/* no pathkeys */
  									 NULL,		/* no outer rel either */
+ 									 NULL,		/* no alternative path */
  									 coptions));
  
  	/*
***************
*** 563,569 **** fileGetForeignPlan(PlannerInfo *root,
  							scan_relid,
  							NIL,	/* no expressions to evaluate */
  							best_path->fdw_private,
! 							NIL /* no custom tlist */ );
  }
  
  /*
--- 564,571 ----
  							scan_relid,
  							NIL,	/* no expressions to evaluate */
  							best_path->fdw_private,
! 							NIL,	/* no custom tlist */
! 							NIL /* no remote quals */ );
  }
  
  /*
*** a/contrib/postgres_fdw/postgres_fdw.c
--- b/contrib/postgres_fdw/postgres_fdw.c
***************
*** 560,565 **** postgresGetForeignPaths(PlannerInfo *root,
--- 560,566 ----
  								   fpinfo->total_cost,
  								   NIL, /* no pathkeys */
  								   NULL,		/* no outer rel either */
+ 								   NULL,		/* no alternative path */
  								   NIL);		/* no fdw_private list */
  	add_path(baserel, (Path *) path);
  
***************
*** 727,732 **** postgresGetForeignPaths(PlannerInfo *root,
--- 728,734 ----
  									   total_cost,
  									   NIL,		/* no pathkeys */
  									   param_info->ppi_req_outer,
+ 									   NULL,	/* no alternative path */
  									   NIL);	/* no fdw_private list */
  		add_path(baserel, (Path *) path);
  	}
***************
*** 748,753 **** postgresGetForeignPlan(PlannerInfo *root,
--- 750,756 ----
  	Index		scan_relid = baserel->relid;
  	List	   *fdw_private;
  	List	   *remote_conds = NIL;
+ 	List	   *remote_exprs = NIL;
  	List	   *local_exprs = NIL;
  	List	   *params_list = NIL;
  	List	   *retrieved_attrs;
***************
*** 769,776 **** postgresGetForeignPlan(PlannerInfo *root,
  	 *
  	 * This code must match "extract_actual_clauses(scan_clauses, false)"
  	 * except for the additional decision about remote versus local execution.
! 	 * Note however that we only strip the RestrictInfo nodes from the
! 	 * local_exprs list, since appendWhereClause expects a list of
  	 * RestrictInfos.
  	 */
  	foreach(lc, scan_clauses)
--- 772,779 ----
  	 *
  	 * 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)
***************
*** 784,794 **** postgresGetForeignPlan(PlannerInfo *root,
--- 787,803 ----
  			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, baserel, rinfo->clause))
+ 		{
  			remote_conds = lappend(remote_conds, rinfo);
+ 			remote_exprs = lappend(remote_exprs, rinfo->clause);
+ 		}
  		else
  			local_exprs = lappend(local_exprs, rinfo->clause);
  	}
***************
*** 874,880 **** postgresGetForeignPlan(PlannerInfo *root,
  							scan_relid,
  							params_list,
  							fdw_private,
! 							NIL /* no custom tlist */ );
  }
  
  /*
--- 883,890 ----
  							scan_relid,
  							params_list,
  							fdw_private,
! 							NIL,	/* no custom tlist */
! 							remote_exprs);
  }
  
  /*
*** a/doc/src/sgml/fdwhandler.sgml
--- b/doc/src/sgml/fdwhandler.sgml
***************
*** 333,339 **** GetForeignJoinPaths (PlannerInfo *root,
       remote join cannot be found from the system catalogs, the FDW must
       fill <structfield>fdw_scan_tlist</> with an appropriate list
       of <structfield>TargetEntry</> nodes, representing the set of columns
!      it will supply at runtime in the tuples it returns.
      </para>
  
      <para>
--- 333,346 ----
       remote join cannot be found from the system catalogs, the FDW must
       fill <structfield>fdw_scan_tlist</> with an appropriate list
       of <structfield>TargetEntry</> nodes, representing the set of columns
!      it will supply at runtime in the tuples it returns.  Yet another
!      difference is that the FDW must provide <structfield>fs_subplan</> with
!      an appropriate plan node involving local joining in preparation for
!      possible use in the <productname>PostgreSQL</productname> executor, while
!      <structfield>fdw_quals</> should be set to NIL, which represents the set
!      of restriction clauses to be enforced remotely in a case when a
!      <structname>ForeignScan</> node is created for a foreign table scan, not
!      a join.
      </para>
  
      <para>
*** a/src/backend/executor/nodeForeignscan.c
--- b/src/backend/executor/nodeForeignscan.c
***************
*** 72,79 **** ForeignNext(ForeignScanState *node)
  static bool
  ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
  {
! 	/* There are no access-method-specific conditions to recheck. */
! 	return true;
  }
  
  /* ----------------------------------------------------------------
--- 72,90 ----
  static bool
  ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
  {
! 	ExprContext *econtext;
! 
! 	/*
! 	 * extract necessary information from foreign scan node
! 	 */
! 	econtext = node->ss.ps.ps_ExprContext;
! 
! 	/* Does the tuple meet the remote qual condition? */
! 	econtext->ecxt_scantuple = slot;
! 
! 	ResetExprContext(econtext);
! 
! 	return ExecQual(node->fdw_quals, econtext, false);
  }
  
  /* ----------------------------------------------------------------
***************
*** 88,93 **** ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
--- 99,122 ----
  TupleTableSlot *
  ExecForeignScan(ForeignScanState *node)
  {
+ 	EState	   *estate = node->ss.ps.state;
+ 
+ 	if (estate->es_epqTuple != NULL)
+ 	{
+ 		/*
+ 		 * We are inside an EvalPlanQual recheck.  If foreign join, get next
+ 		 * tuple from subplan.
+ 		 */
+ 		Index		scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
+ 
+ 		if (scanrelid == 0)
+ 		{
+ 			PlanState  *outerPlan = outerPlanState(node);
+ 
+ 			return ExecProcNode(outerPlan);
+ 		}
+ 	}
+ 
  	return ExecScan((ScanState *) node,
  					(ExecScanAccessMtd) ForeignNext,
  					(ExecScanRecheckMtd) ForeignRecheck);
***************
*** 135,140 **** ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
--- 164,172 ----
  	scanstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
  					 (PlanState *) scanstate);
+ 	scanstate->fdw_quals = (List *)
+ 		ExecInitExpr((Expr *) node->fdw_quals,
+ 					 (PlanState *) scanstate);
  
  	/*
  	 * tuple table initialization
***************
*** 195,200 **** ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
--- 227,246 ----
  	 */
  	fdwroutine->BeginForeignScan(scanstate, eflags);
  
+ 	if (estate->es_epqTuple != NULL)
+ 	{
+ 		/*
+ 		 * We are inside an EvalPlanQual recheck.  If foreign join, initialize
+ 		 * subplan.
+ 		 */
+ 		if (scanrelid == 0)
+ 		{
+ 			Plan	   *subplan = node->fs_subplan;
+ 
+ 			outerPlanState(scanstate) = ExecInitNode(subplan, estate, eflags);
+ 		}
+ 	}
+ 
  	return scanstate;
  }
  
***************
*** 207,212 **** ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
--- 253,276 ----
  void
  ExecEndForeignScan(ForeignScanState *node)
  {
+ 	EState	   *estate = node->ss.ps.state;
+ 
+ 	if (estate->es_epqTuple != NULL)
+ 	{
+ 		/*
+ 		 * We are inside an EvalPlanQual recheck.  If foreign join, close down
+ 		 * subplan.
+ 		 */
+ 		Index		scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
+ 
+ 		if (scanrelid == 0)
+ 		{
+ 			PlanState  *outerPlan = outerPlanState(node);
+ 
+ 			ExecEndNode(outerPlan);
+ 		}
+ 	}
+ 
  	/* Let the FDW shut down */
  	node->fdwroutine->EndForeignScan(node);
  
***************
*** 231,236 **** ExecEndForeignScan(ForeignScanState *node)
--- 295,324 ----
  void
  ExecReScanForeignScan(ForeignScanState *node)
  {
+ 	EState	   *estate = node->ss.ps.state;
+ 
+ 	if (estate->es_epqTuple != NULL)
+ 	{
+ 		/*
+ 		 * We are inside an EvalPlanQual recheck.  If foreign join, re-scan
+ 		 * subplan.
+ 		 */
+ 		Index		scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
+ 
+ 		if (scanrelid == 0)
+ 		{
+ 			PlanState  *outerPlan = outerPlanState(node);
+ 
+ 			/*
+ 			 * If outerPlan->chgParam is not null then plan will be
+ 			 * automatically re-scanned by first ExecProcNode.
+ 			 */
+ 			if (outerPlan->chgParam == NULL)
+ 				ExecReScan(outerPlan);
+ 			return;
+ 		}
+ 	}
+ 
  	node->fdwroutine->ReScanForeignScan(node);
  
  	ExecScanReScan(&node->ss);
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 624,629 **** _copyForeignScan(const ForeignScan *from)
--- 624,631 ----
  	COPY_NODE_FIELD(fdw_exprs);
  	COPY_NODE_FIELD(fdw_private);
  	COPY_NODE_FIELD(fdw_scan_tlist);
+ 	COPY_NODE_FIELD(fdw_quals);
+ 	COPY_NODE_FIELD(fs_subplan);
  	COPY_BITMAPSET_FIELD(fs_relids);
  	COPY_SCALAR_FIELD(fsSystemCol);
  
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 579,584 **** _outForeignScan(StringInfo str, const ForeignScan *node)
--- 579,586 ----
  	WRITE_NODE_FIELD(fdw_exprs);
  	WRITE_NODE_FIELD(fdw_private);
  	WRITE_NODE_FIELD(fdw_scan_tlist);
+ 	WRITE_NODE_FIELD(fdw_quals);
+ 	WRITE_NODE_FIELD(fs_subplan);
  	WRITE_BITMAPSET_FIELD(fs_relids);
  	WRITE_BOOL_FIELD(fsSystemCol);
  }
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
***************
*** 2117,2125 **** create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
--- 2117,2134 ----
  			replace_nestloop_params(root, (Node *) scan_plan->scan.plan.qual);
  		scan_plan->fdw_exprs = (List *)
  			replace_nestloop_params(root, (Node *) scan_plan->fdw_exprs);
+ 		scan_plan->fdw_quals = (List *)
+ 			replace_nestloop_params(root, (Node *) scan_plan->fdw_quals);
  	}
  
  	/*
+ 	 * If we're scanning a join relation, generate the local join plan for
+ 	 * EvalPlanQual support.  (Irrelevant if scanning a base relation.)
+ 	 */
+ 	if (scan_relid == 0)
+ 		scan_plan->fs_subplan = create_plan_recurse(root, best_path->subpath);
+ 
+ 	/*
  	 * Detect whether any system columns are requested from rel.  This is a
  	 * bit of a kluge and might go away someday, so we intentionally leave it
  	 * out of the API presented to FDWs.
***************
*** 3702,3708 **** make_foreignscan(List *qptlist,
  				 Index scanrelid,
  				 List *fdw_exprs,
  				 List *fdw_private,
! 				 List *fdw_scan_tlist)
  {
  	ForeignScan *node = makeNode(ForeignScan);
  	Plan	   *plan = &node->scan.plan;
--- 3711,3718 ----
  				 Index scanrelid,
  				 List *fdw_exprs,
  				 List *fdw_private,
! 				 List *fdw_scan_tlist,
! 				 List *fdw_quals)
  {
  	ForeignScan *node = makeNode(ForeignScan);
  	Plan	   *plan = &node->scan.plan;
***************
*** 3718,3723 **** make_foreignscan(List *qptlist,
--- 3728,3736 ----
  	node->fdw_exprs = fdw_exprs;
  	node->fdw_private = fdw_private;
  	node->fdw_scan_tlist = fdw_scan_tlist;
+ 	node->fdw_quals = fdw_quals;
+ 	/* fs_subplan will be filled in by create_foreignscan_plan */
+ 	node->fs_subplan = NULL;
  	/* fs_relids will be filled in by create_foreignscan_plan */
  	node->fs_relids = NULL;
  	/* fsSystemCol will be filled in by create_foreignscan_plan */
*** a/src/backend/optimizer/plan/setrefs.c
--- b/src/backend/optimizer/plan/setrefs.c
***************
*** 1124,1139 **** set_foreignscan_references(PlannerInfo *root,
  		/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
  		fscan->fdw_scan_tlist =
  			fix_scan_list(root, fscan->fdw_scan_tlist, rtoffset);
  	}
  	else
  	{
! 		/* Adjust tlist, qual, fdw_exprs in the standard way */
  		fscan->scan.plan.targetlist =
  			fix_scan_list(root, fscan->scan.plan.targetlist, rtoffset);
  		fscan->scan.plan.qual =
  			fix_scan_list(root, fscan->scan.plan.qual, rtoffset);
  		fscan->fdw_exprs =
  			fix_scan_list(root, fscan->fdw_exprs, rtoffset);
  	}
  
  	/* Adjust fs_relids if needed */
--- 1124,1143 ----
  		/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
  		fscan->fdw_scan_tlist =
  			fix_scan_list(root, fscan->fdw_scan_tlist, rtoffset);
+ 		/* fs_subplan needs set_plan_refs() adjustments */
+ 		set_plan_refs(root, fscan->fs_subplan, rtoffset);
  	}
  	else
  	{
! 		/* Adjust tlist, qual, fdw_exprs, fdw_quals in the standard way */
  		fscan->scan.plan.targetlist =
  			fix_scan_list(root, fscan->scan.plan.targetlist, rtoffset);
  		fscan->scan.plan.qual =
  			fix_scan_list(root, fscan->scan.plan.qual, rtoffset);
  		fscan->fdw_exprs =
  			fix_scan_list(root, fscan->fdw_exprs, rtoffset);
+ 		fscan->fdw_quals =
+ 			fix_scan_list(root, fscan->fdw_quals, rtoffset);
  	}
  
  	/* Adjust fs_relids if needed */
*** a/src/backend/optimizer/plan/subselect.c
--- b/src/backend/optimizer/plan/subselect.c
***************
*** 2394,2403 **** finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
  			break;
  
  		case T_ForeignScan:
! 			finalize_primnode((Node *) ((ForeignScan *) plan)->fdw_exprs,
! 							  &context);
! 			/* We assume fdw_scan_tlist cannot contain Params */
! 			context.paramids = bms_add_members(context.paramids, scan_params);
  			break;
  
  		case T_CustomScan:
--- 2394,2435 ----
  			break;
  
  		case T_ForeignScan:
! 			{
! 				ForeignScan *fscan = (ForeignScan *) plan;
! 
! 				finalize_primnode((Node *) fscan->fdw_exprs, &context);
! 
! 				/* We assume fdw_scan_tlist cannot contain Params */
! 				context.paramids =
! 					bms_add_members(context.paramids, scan_params);
! 
! 				/*
! 				 * We need not look at fdw_quals, since it will have the same
! 				 * param references as fdw_exprs.
! 				 */
! 
! 				/* subplan node if foreign join */
! 				if (fscan->scan.scanrelid == 0)
! 				{
! 					/*
! 					 * grouping_planner might have replaced the targetlist of
! 					 * the ForeignScan node if the node was the top plan node.
! 					 * To be safe, replace the targetlist of the subplan node.
! 					 */
! 					fscan->fs_subplan->targetlist = plan->targetlist;
! 
! 					/*
! 					 * We need not include params in fs_subplan, since it will
! 					 * have the same param references as the ForeignScan node.
! 					 * However, fs_subplan itself needs finalize_plan()
! 					 * processing.
! 					 */
! 					finalize_plan(root,
! 								  fscan->fs_subplan,
! 								  valid_params,
! 								  scan_params);
! 				}
! 			}
  			break;
  
  		case T_CustomScan:
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
***************
*** 1462,1467 **** create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
--- 1462,1468 ----
  						double rows, Cost startup_cost, Cost total_cost,
  						List *pathkeys,
  						Relids required_outer,
+ 						Path *subpath,
  						List *fdw_private)
  {
  	ForeignPath *pathnode = makeNode(ForeignPath);
***************
*** 1475,1480 **** create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
--- 1476,1482 ----
  	pathnode->path.total_cost = total_cost;
  	pathnode->path.pathkeys = pathkeys;
  
+ 	pathnode->subpath = subpath;
  	pathnode->fdw_private = fdw_private;
  
  	return pathnode;
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 1577,1582 **** typedef struct WorkTableScanState
--- 1577,1583 ----
  typedef struct ForeignScanState
  {
  	ScanState	ss;				/* its first field is NodeTag */
+ 	List	   *fdw_quals;		/* remote quals if foreign table */
  	/* use struct pointer to avoid including fdwapi.h here */
  	struct FdwRoutine *fdwroutine;
  	void	   *fdw_state;		/* foreign-data wrapper can keep state here */
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
***************
*** 521,526 **** typedef struct ForeignScan
--- 521,528 ----
  	List	   *fdw_exprs;		/* expressions that FDW may evaluate */
  	List	   *fdw_private;	/* private data for FDW */
  	List	   *fdw_scan_tlist; /* optional tlist describing scan tuple */
+ 	List	   *fdw_quals;		/* remote quals if foreign table */
+ 	Plan	   *fs_subplan;		/* local join plan if foreign join */
  	Bitmapset  *fs_relids;		/* RTIs generated by this scan */
  	bool		fsSystemCol;	/* true if any "system column" is needed */
  } ForeignScan;
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
***************
*** 897,906 **** typedef struct TidPath
--- 897,914 ----
   * generally a good idea to use a representation that can be dumped by
   * nodeToString(), so that you can examine the structure during debugging
   * with tools like pprint().
+  *
+  * If a ForeignPath node represents a remote join of foreign tables, subpath
+  * is a local join of those tables with equivalent results that will be used
+  * for EvalPlanQual testing.  The pathkeys and parameterization of subpath
+  * must be the same as that of the path's output.  (The requirement for the
+  * pathkeys is unnecessary, since the testing can return at most one tuple
+  * for any particular set of scan tuples of those tables, but let's be safe.)
   */
  typedef struct ForeignPath
  {
  	Path		path;
+ 	Path	   *subpath;
  	List	   *fdw_private;
  } ForeignPath;
  
*** a/src/include/optimizer/pathnode.h
--- b/src/include/optimizer/pathnode.h
***************
*** 83,88 **** extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
--- 83,89 ----
  						double rows, Cost startup_cost, Cost total_cost,
  						List *pathkeys,
  						Relids required_outer,
+ 						Path *subpath,
  						List *fdw_private);
  
  extern Relids calc_nestloop_required_outer(Path *outer_path, Path *inner_path);
*** a/src/include/optimizer/planmain.h
--- b/src/include/optimizer/planmain.h
***************
*** 45,51 **** extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
  				  Index scanrelid, Plan *subplan);
  extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual,
  				 Index scanrelid, List *fdw_exprs, List *fdw_private,
! 				 List *fdw_scan_tlist);
  extern Append *make_append(List *appendplans, List *tlist);
  extern RecursiveUnion *make_recursive_union(List *tlist,
  					 Plan *lefttree, Plan *righttree, int wtParam,
--- 45,51 ----
  				  Index scanrelid, Plan *subplan);
  extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual,
  				 Index scanrelid, List *fdw_exprs, List *fdw_private,
! 				 List *fdw_scan_tlist, List *fdw_quals);
  extern Append *make_append(List *appendplans, List *tlist);
  extern RecursiveUnion *make_recursive_union(List *tlist,
  					 Plan *lefttree, Plan *righttree, int wtParam,
-- 
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