wcte.patch

text/plain

Filename: wcte.patch
Type: text/plain
Part: 0
Message: Status report on writeable CTEs

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
File+
src/backend/commands/copy.c 1 0
src/backend/commands/explain.c 12 0
src/backend/executor/execAmi.c 5 0
src/backend/executor/execMain.c 2 0
src/backend/executor/execProcnode.c 13 0
src/backend/executor/execUtils.c 2 0
src/backend/executor/functions.c 1 0
src/backend/executor/Makefile 1 0
src/backend/executor/nodeDtscan.c 261 0
src/backend/executor/spi.c 1 0
src/backend/optimizer/path/allpaths.c 13 0
src/backend/optimizer/path/costsize.c 29 0
src/backend/optimizer/plan/createplan.c 134 0
src/backend/optimizer/plan/setrefs.c 12 0
src/backend/optimizer/plan/subselect.c 8 0
src/backend/optimizer/util/pathnode.c 18 0
src/backend/rewrite/rewriteHandler.c 42 0
src/backend/tcop/pquery.c 34 0
src/backend/utils/mmgr/portalmem.c 1 0
src/include/executor/execdesc.h 3 0
src/include/executor/nodeDtscan.h 24 0
src/include/nodes/execnodes.h 19 0
src/include/nodes/nodes.h 2 0
src/include/nodes/parsenodes.h 6 0
src/include/nodes/plannodes.h 13 0
src/include/nodes/relation.h 2 0
src/include/optimizer/cost.h 1 0
src/include/optimizer/pathnode.h 1 0
src/include/utils/portal.h 2 0
*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
***************
*** 1092,1098 **** DoCopy(const CopyStmt *stmt, const char *queryString)
  		cstate->queryDesc = CreateQueryDesc(plan, queryString,
  											GetActiveSnapshot(),
  											InvalidSnapshot,
! 											dest, NULL, 0);
  
  		/*
  		 * Call ExecutorStart to prepare the plan for execution.
--- 1092,1098 ----
  		cstate->queryDesc = CreateQueryDesc(plan, queryString,
  											GetActiveSnapshot(),
  											InvalidSnapshot,
! 											dest, NULL, 0, NIL);
  
  		/*
  		 * Call ExecutorStart to prepare the plan for execution.
*** a/src/backend/commands/explain.c
--- b/src/backend/commands/explain.c
***************
*** 367,373 **** ExplainOnePlan(PlannedStmt *plannedstmt, ExplainState *es,
  	/* Create a QueryDesc requesting no output */
  	queryDesc = CreateQueryDesc(plannedstmt, queryString,
  								GetActiveSnapshot(), InvalidSnapshot,
! 								None_Receiver, params, instrument_option);
  
  	INSTR_TIME_SET_CURRENT(starttime);
  
--- 367,374 ----
  	/* Create a QueryDesc requesting no output */
  	queryDesc = CreateQueryDesc(plannedstmt, queryString,
  								GetActiveSnapshot(), InvalidSnapshot,
! 								None_Receiver, params, instrument_option,
! 								NIL);
  
  	INSTR_TIME_SET_CURRENT(starttime);
  
***************
*** 692,697 **** ExplainNode(Plan *plan, PlanState *planstate,
--- 693,701 ----
  		case T_CteScan:
  			pname = sname = "CTE Scan";
  			break;
+ 		case T_DtScan:
+ 			pname = sname = "Derived Table Scan";
+ 			break;
  		case T_WorkTableScan:
  			pname = sname = "WorkTable Scan";
  			break;
***************
*** 844,849 **** ExplainNode(Plan *plan, PlanState *planstate,
--- 848,854 ----
  		case T_ValuesScan:
  		case T_CteScan:
  		case T_WorkTableScan:
+ 		case T_DtScan:
  			ExplainScanTarget((Scan *) plan, es);
  			break;
  		case T_BitmapIndexScan:
***************
*** 1565,1570 **** ExplainScanTarget(Scan *plan, ExplainState *es)
--- 1570,1581 ----
  			objectname = rte->ctename;
  			objecttag = "CTE Name";
  			break;
+ 		case T_DtScan:
+ 			/* Assert it's on a non-self-reference CTE */
+ 			Assert(rte->rtekind == RTE_CTE);
+ 			Assert(!rte->self_reference);
+ 			objectname = rte->ctename;
+ 			objecttag = "Derived Table Name";
  		default:
  			break;
  	}
*** a/src/backend/executor/Makefile
--- b/src/backend/executor/Makefile
***************
*** 21,27 **** OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
         nodeMaterial.o nodeMergejoin.o nodeModifyTable.o \
         nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
         nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
!        nodeValuesscan.o nodeCtescan.o nodeWorktablescan.o \
         nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
         nodeWindowAgg.o tstoreReceiver.o spi.o
  
--- 21,27 ----
         nodeMaterial.o nodeMergejoin.o nodeModifyTable.o \
         nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
         nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
!        nodeValuesscan.o nodeCtescan.o nodeWorktablescan.o nodeDtscan.o \
         nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
         nodeWindowAgg.o tstoreReceiver.o spi.o
  
*** a/src/backend/executor/execAmi.c
--- b/src/backend/executor/execAmi.c
***************
*** 21,26 ****
--- 21,27 ----
  #include "executor/nodeBitmapIndexscan.h"
  #include "executor/nodeBitmapOr.h"
  #include "executor/nodeCtescan.h"
+ #include "executor/nodeDtscan.h"
  #include "executor/nodeFunctionscan.h"
  #include "executor/nodeGroup.h"
  #include "executor/nodeGroup.h"
***************
*** 189,194 **** ExecReScan(PlanState *node, ExprContext *exprCtxt)
--- 190,199 ----
  			ExecWorkTableScanReScan((WorkTableScanState *) node, exprCtxt);
  			break;
  
+ 		case T_DtScanState:
+ 			ExecDtScanReScan((DtScanState *) node, exprCtxt);
+ 			break;
+ 
  		case T_NestLoopState:
  			ExecReScanNestLoop((NestLoopState *) node, exprCtxt);
  			break;
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 146,151 **** standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
--- 146,153 ----
  
  	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
  
+ 	estate->es_derived = queryDesc->derived;
+ 
  	/*
  	 * Fill in external parameters, if any, from queryDesc; and allocate
  	 * workspace for internal parameters
*** a/src/backend/executor/execProcnode.c
--- b/src/backend/executor/execProcnode.c
***************
*** 85,90 ****
--- 85,91 ----
  #include "executor/nodeBitmapIndexscan.h"
  #include "executor/nodeBitmapOr.h"
  #include "executor/nodeCtescan.h"
+ #include "executor/nodeDtscan.h"
  #include "executor/nodeFunctionscan.h"
  #include "executor/nodeGroup.h"
  #include "executor/nodeHash.h"
***************
*** 226,231 **** ExecInitNode(Plan *node, EState *estate, int eflags)
--- 227,237 ----
  														 estate, eflags);
  			break;
  
+ 		case T_DtScan:
+ 			result = (PlanState *) ExecInitDtScan((DtScan *) node,
+ 												  estate, eflags);
+ 			break;
+ 
  			/*
  			 * join nodes
  			 */
***************
*** 412,417 **** ExecProcNode(PlanState *node)
--- 418,427 ----
  			result = ExecWorkTableScan((WorkTableScanState *) node);
  			break;
  
+ 		case T_DtScanState:
+ 			result = ExecDtScan((DtScanState *) node);
+ 			break;
+ 
  			/*
  			 * join nodes
  			 */
***************
*** 635,640 **** ExecEndNode(PlanState *node)
--- 645,653 ----
  		case T_WorkTableScanState:
  			ExecEndWorkTableScan((WorkTableScanState *) node);
  			break;
+ 		case T_DtScanState:
+ 			ExecEndDtScan((DtScanState *) node);
+ 			break;
  
  			/*
  			 * join nodes
*** a/src/backend/executor/execUtils.c
--- b/src/backend/executor/execUtils.c
***************
*** 145,150 **** CreateExecutorState(void)
--- 145,152 ----
  
  	estate->es_subplanstates = NIL;
  
+ 	estate->es_derived = NIL;
+ 
  	estate->es_per_tuple_exprcontext = NULL;
  
  	estate->es_epqTuple = NULL;
*** a/src/backend/executor/functions.c
--- b/src/backend/executor/functions.c
***************
*** 417,423 **** postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
  								 fcache->src,
  								 snapshot, InvalidSnapshot,
  								 dest,
! 								 fcache->paramLI, 0);
  	else
  		es->qd = CreateUtilityQueryDesc(es->stmt,
  										fcache->src,
--- 417,423 ----
  								 fcache->src,
  								 snapshot, InvalidSnapshot,
  								 dest,
! 								 fcache->paramLI, 0, NIL);
  	else
  		es->qd = CreateUtilityQueryDesc(es->stmt,
  										fcache->src,
*** /dev/null
--- b/src/backend/executor/nodeDtscan.c
***************
*** 0 ****
--- 1,261 ----
+ /*-------------------------------------------------------------------------
+  *
+  * nodeDtscan.c
+  *	  routines to handle DtScan nodes.
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  $PostgreSQL$
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include "postgres.h"
+ 
+ #include "executor/execdebug.h"
+ #include "executor/nodeDtscan.h"
+ #include "miscadmin.h"
+ 
+ static TupleTableSlot *DtScanNext(DtScanState *node);
+ 
+ /* ----------------------------------------------------------------
+  *		DtScanNext
+  *
+  *		This is a workhorse for ExecDtScan
+  * ----------------------------------------------------------------
+  */
+ static TupleTableSlot *
+ DtScanNext(DtScanState *node)
+ {
+ 	EState	   *estate;
+ 	ScanDirection dir;
+ 	bool		forward;
+ 	Tuplestorestate *tuplestorestate;
+ 	bool		eof_tuplestore;
+ 	TupleTableSlot *slot;
+ 
+ 	/*
+ 	 * get state info from node
+ 	 */
+ 	estate = node->ss.ps.state;
+ 	dir = estate->es_direction;
+ 	forward = ScanDirectionIsForward(dir);
+ 	tuplestorestate = node->tuplestore;
+ 	tuplestore_select_read_pointer(tuplestorestate, node->readptr);
+ 	slot = node->ss.ss_ScanTupleSlot;
+ 
+ 
+ 	Assert(forward);
+ #if 0
+ 	/*
+ 	 * If we are not at the end of the tuplestore, or are going backwards, try
+ 	 * to fetch a tuple from tuplestore.
+ 	 */
+ 	eof_tuplestore = tuplestore_ateof(tuplestorestate);
+ 
+ 	if (!forward && eof_tuplestore)
+ 	{
+ 		if (!node->leader->eof_cte)
+ 		{
+ 			/*
+ 			 * When reversing direction at tuplestore EOF, the first
+ 			 * gettupleslot call will fetch the last-added tuple; but we want
+ 			 * to return the one before that, if possible. So do an extra
+ 			 * fetch.
+ 			 */
+ 			if (!tuplestore_advance(tuplestorestate, forward))
+ 				return NULL;	/* the tuplestore must be empty */
+ 		}
+ 		eof_tuplestore = false;
+ 	}
+ #endif
+ 
+ 	/*
+ 	 * If we can fetch another tuple from the tuplestore, return it.
+ 	 *
+ 	 * Note: we have to use copy=true in the tuplestore_gettupleslot call,
+ 	 * because we are sharing the tuplestore with other nodes that might write
+ 	 * into the tuplestore before we get called again.
+ 	 */
+ 	eof_tuplestore = tuplestore_ateof(tuplestorestate);
+ 	if (!eof_tuplestore)
+ 	{
+ 		if (tuplestore_gettupleslot(tuplestorestate, forward, true, slot))
+ 			return slot;
+ 		if (forward)
+ 			eof_tuplestore = true;
+ 	}
+ 
+ 	/*
+ 	 * Nothing left ...
+ 	 */
+ 	return ExecClearTuple(slot);
+ }
+ 
+ /*
+  * DtScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
+  */
+ static bool
+ DtScanRecheck(DtScanState *node, TupleTableSlot *slot)
+ {
+ 	/* nothing to check */
+ 	return true;
+ }
+ 
+ /* ----------------------------------------------------------------
+  *		ExecDtScan(node)
+  *
+  *		Scans the DT sequentially and returns the next qualifying tuple.
+  *		We call the ExecScan() routine and pass it the appropriate
+  *		access method functions.
+  * ----------------------------------------------------------------
+  */
+ TupleTableSlot *
+ ExecDtScan(DtScanState *node)
+ {
+ 	return ExecScan(&node->ss,
+ 					(ExecScanAccessMtd) DtScanNext,
+ 					(ExecScanRecheckMtd) DtScanRecheck);
+ }
+ 
+ 
+ /* ----------------------------------------------------------------
+  *		ExecInitDtScan
+  * ----------------------------------------------------------------
+  */
+ DtScanState *
+ ExecInitDtScan(DtScan *node, EState *estate, int eflags)
+ {
+ 	DtScanState *scanstate;
+ 
+ 
+ 	/* check for unsupported flags */
+ 	Assert(!(eflags & EXEC_FLAG_MARK));
+ 
+ 	/*
+ 	 * For the moment we have to force the tuplestore to allow REWIND, because
+ 	 * we might be asked to rescan the CTE even though upper levels didn't
+ 	 * tell us to be prepared to do it efficiently.  Annoying, since this
+ 	 * prevents truncation of the tuplestore.  XXX FIXME
+ 	 */
+ 	eflags |= EXEC_FLAG_REWIND;
+ 
+ 	/*
+ 	 * DtScan should not have any children.
+ 	 */
+ 	Assert(outerPlan(node) == NULL);
+ 	Assert(innerPlan(node) == NULL);
+ 
+ 	/*
+ 	 * create new DtScanState for node
+ 	 */
+ 	scanstate = makeNode(DtScanState);
+ 	scanstate->ss.ps.plan = (Plan *) node;
+ 	scanstate->ss.ps.state = estate;
+ 	scanstate->eflags = eflags;
+ 
+ 
+ 	/* XXX hack hack */
+ 	if (true)
+ 	{
+ 		/* XXX don't try to alloc a read pointer if this is an EXPLAIN. */
+ 		if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
+ 		{
+ 			scanstate->tuplestore = (Tuplestorestate *) list_nth(estate->es_derived,
+ 																 node->dtTuplestoreId - 1);
+ 			scanstate->readptr =
+ 				tuplestore_alloc_read_pointer(scanstate->tuplestore,
+ 										  /* XXX scanstate->eflags*/ 0);
+ 		}
+ 		else
+ 		{
+ 			scanstate->tuplestore = NULL;
+ 			scanstate->readptr = -1;
+ 		}
+ 
+ 
+ 		/*
+ 		 * Miscellaneous initialization
+ 		 *
+ 		 * create expression context for node
+ 		 */
+ 		ExecAssignExprContext(estate, &scanstate->ss.ps);
+ 
+ 		/*
+ 		 * initialize child expressions
+ 		 */
+ 		scanstate->ss.ps.targetlist = (List *)
+ 			ExecInitExpr((Expr *) node->scan.plan.targetlist,
+ 						 (PlanState *) scanstate);
+ 		scanstate->ss.ps.qual = (List *)
+ 			ExecInitExpr((Expr *) node->scan.plan.qual,
+ 						 (PlanState *) scanstate);
+ 
+ 		/*
+ 		 * tuple table initialization
+ 		 */
+ 		ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
+ 		ExecInitScanTupleSlot(estate, &scanstate->ss);
+ 
+ 		/*
+ 		 * The scan tuple type (ie, the rowtype we expect to find in the work
+ 		 * table) is the same as the result rowtype of the CTE query.
+ 		 */
+ 		ExecAssignScanType(&scanstate->ss,
+ 						   node->dtTupledesc);
+ 
+ 		/*
+ 		 * Initialize result tuple type and projection info.
+ 		 */
+ 		ExecAssignResultTypeFromTL(&scanstate->ss.ps);
+ 		ExecAssignScanProjectionInfo(&scanstate->ss);
+ 	}
+ 	else
+ 	{
+ 		scanstate->ss.ss_currentRelation = NULL;
+ 		scanstate->ss.ss_currentScanDesc = NULL;
+ 		scanstate->ss.ps.ps_TupFromTlist = true;
+ 		return scanstate;
+ 	}
+ 	/* /hack hack */
+ 
+ 	scanstate->ss.ps.ps_TupFromTlist = false;
+ 
+ 	return scanstate;
+ }
+ 
+ /* ----------------------------------------------------------------
+  *		ExecEndDtScan
+  *
+  *		frees any storage allocated through C routines.
+  * ----------------------------------------------------------------
+  */
+ void
+ ExecEndDtScan(DtScanState *node)
+ {
+ 	/*
+ 	 * Free exprcontext
+ 	 */
+ 	ExecFreeExprContext(&node->ss.ps);
+ 
+ 	/*
+ 	 * clean out the tuple table
+ 	 */
+ 	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+ 	ExecClearTuple(node->ss.ss_ScanTupleSlot);
+ }
+ 
+ /* ----------------------------------------------------------------
+  *		ExecDtScanReScan
+  *
+  *		Rescans the relation.
+  * ----------------------------------------------------------------
+  */
+ void
+ ExecDtScanReScan(DtScanState *node, ExprContext *exprCtxt)
+ {
+ 	/* XXX TODO */
+ }
*** a/src/backend/executor/spi.c
--- b/src/backend/executor/spi.c
***************
*** 1905,1911 **** _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
  										plansource->query_string,
  										snap, crosscheck_snapshot,
  										dest,
! 										paramLI, 0);
  				res = _SPI_pquery(qdesc, fire_triggers,
  								  canSetTag ? tcount : 0);
  				FreeQueryDesc(qdesc);
--- 1905,1911 ----
  										plansource->query_string,
  										snap, crosscheck_snapshot,
  										dest,
! 										paramLI, 0, NIL);
  				res = _SPI_pquery(qdesc, fire_triggers,
  								  canSetTag ? tcount : 0);
  				FreeQueryDesc(qdesc);
*** a/src/backend/optimizer/path/allpaths.c
--- b/src/backend/optimizer/path/allpaths.c
***************
*** 697,705 **** set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
  	int			ndx;
  	ListCell   *lc;
  	int			plan_id;
  
  	/*
! 	 * Find the referenced CTE, and locate the plan previously made for it.
  	 */
  	levelsup = rte->ctelevelsup;
  	cteroot = root;
--- 697,707 ----
  	int			ndx;
  	ListCell   *lc;
  	int			plan_id;
+ 	CommonTableExpr *cte;
+ 
  
  	/*
! 	 * XXX Find the referenced CTE, and locate the plan previously made for it.
  	 */
  	levelsup = rte->ctelevelsup;
  	cteroot = root;
***************
*** 718,724 **** set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
  	ndx = 0;
  	foreach(lc, cteroot->parse->cteList)
  	{
! 		CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
  
  		if (strcmp(cte->ctename, rte->ctename) == 0)
  			break;
--- 720,726 ----
  	ndx = 0;
  	foreach(lc, cteroot->parse->cteList)
  	{
! 		cte = (CommonTableExpr *) lfirst(lc);
  
  		if (strcmp(cte->ctename, rte->ctename) == 0)
  			break;
***************
*** 726,731 **** set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
--- 728,742 ----
  	}
  	if (lc == NULL)				/* shouldn't happen */
  		elog(ERROR, "could not find CTE \"%s\"", rte->ctename);
+ 
+ 	if (cte->derived)
+ 	{
+ 		/* XXX */
+ 		add_path(rel, create_dtscan_path(root, rel));
+ 		set_cheapest(rel);
+ 		return;
+ 	}
+ 
  	if (ndx >= list_length(cteroot->cte_plan_ids))
  		elog(ERROR, "could not find plan for CTE \"%s\"", rte->ctename);
  	plan_id = list_nth_int(cteroot->cte_plan_ids, ndx);
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
***************
*** 1067,1072 **** cost_recursive_union(Plan *runion, Plan *nrterm, Plan *rterm)
--- 1067,1101 ----
  }
  
  /*
+  * cost_dtscan
+  *	  Determines and returns the cost of scanning a derived table.
+  */
+ void
+ cost_dtscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
+ {
+ 	Cost		startup_cost = 0;
+ 	Cost		run_cost = 0;
+ 	Cost		cpu_per_tuple;
+ 
+ 	/* XXX only allowed in CTEs for now */
+ 	Assert(baserel->relid > 0);
+ 	Assert(baserel->rtekind == RTE_CTE);
+ 
+ 	/* XXX CTE comment says: "Charge one CPU tuple cost per
+ 	 * row for tuplestore manipulation."  We don't modify
+ 	 * anything, so should this be 0? */
+ 	cpu_per_tuple = 0;
+ 
+ 	/* Add scanning CPU costs */
+ 	startup_cost += baserel->baserestrictcost.startup;
+ 	cpu_per_tuple += cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
+ 	run_cost += cpu_per_tuple * baserel->tuples;
+ 
+ 	path->startup_cost = startup_cost;
+ 	path->total_cost = startup_cost + run_cost;
+ }
+ 
+ /*
   * cost_sort
   *	  Determines and returns the cost of sorting a relation, including
   *	  the cost of reading the input data.
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
***************
*** 66,71 **** static CteScan *create_ctescan_plan(PlannerInfo *root, Path *best_path,
--- 66,73 ----
  					List *tlist, List *scan_clauses);
  static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_path,
  						  List *tlist, List *scan_clauses);
+ static DtScan *create_dtscan_plan(PlannerInfo *root, Path *best_path,
+ 					List *tlist, List *scan_clauses);
  static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path,
  					 Plan *outer_plan, Plan *inner_plan);
  static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path,
***************
*** 100,105 **** static CteScan *make_ctescan(List *qptlist, List *qpqual,
--- 102,109 ----
  			 Index scanrelid, int ctePlanId, int cteParam);
  static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
  				   Index scanrelid, int wtParam);
+ static DtScan *make_dtscan(List *qptlist, List *qpqual,
+ 			 Index scanrelid, int qwrid, TupleDesc td /*XXX */);
  static BitmapAnd *make_bitmap_and(List *bitmapplans);
  static BitmapOr *make_bitmap_or(List *bitmapplans);
  static NestLoop *make_nestloop(List *tlist,
***************
*** 162,167 **** create_plan(PlannerInfo *root, Path *best_path)
--- 166,172 ----
  		case T_ValuesScan:
  		case T_CteScan:
  		case T_WorkTableScan:
+ 		case T_DtScan:
  			plan = create_scan_plan(root, best_path);
  			break;
  		case T_HashJoin:
***************
*** 298,303 **** create_scan_plan(PlannerInfo *root, Path *best_path)
--- 303,315 ----
  													  scan_clauses);
  			break;
  
+ 		case T_DtScan:
+ 			plan = (Plan *) create_dtscan_plan(root,
+ 											   best_path,
+ 											   tlist,
+ 											   scan_clauses);
+ 			break;
+ 
  		default:
  			elog(ERROR, "unrecognized node type: %d",
  				 (int) best_path->pathtype);
***************
*** 1560,1565 **** create_worktablescan_plan(PlannerInfo *root, Path *best_path,
--- 1572,1672 ----
  }
  
  
+ /*
+  * create_dtscan_plan
+  *	 Returns a ctescan plan for the base relation scanned by 'best_path'
+  *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+  */
+ static DtScan *
+ create_dtscan_plan(PlannerInfo *root, Path *best_path,
+ 					List *tlist, List *scan_clauses)
+ {
+ 	DtScan    *scan_plan;
+ 	Index		scan_relid = best_path->parent->relid;
+ 	RangeTblEntry *rte;
+ 	SubPlan    *ctesplan = NULL;
+ 	int			plan_id;
+ 	int			cte_param_id;
+ 	PlannerInfo *cteroot;
+ 	Index		levelsup;
+ 	int			ndx;
+ 	ListCell   *lc;
+ 	CommonTableExpr *cte;
+ 	TupleDesc tupledesc;
+ 
+ 	Assert(scan_relid > 0);
+ 	rte = planner_rt_fetch(scan_relid, root);
+ 	Assert(rte->rtekind == RTE_CTE);
+ 	Assert(!rte->self_reference);
+ 
+ 	/*
+ 	 * Find the referenced CTE, and locate the SubPlan previously made for it.
+ 	 */
+ 	levelsup = rte->ctelevelsup;
+ 	cteroot = root;
+ 	while (levelsup-- > 0)
+ 	{
+ 		cteroot = cteroot->parent_root;
+ 		if (!cteroot)			/* shouldn't happen */
+ 			elog(ERROR, "bad levelsup for CTE \"%s\"", rte->ctename);
+ 	}
+ 
+ 	/*
+ 	 * Note: cte_plan_ids can be shorter than cteList, if we are still working
+ 	 * on planning the CTEs (ie, this is a side-reference from another CTE).
+ 	 * So we mustn't use forboth here.
+ 	 */
+ 	ndx = 1;
+ 	foreach(lc, cteroot->parse->cteList)
+ 	{
+ 		cte = (CommonTableExpr *) lfirst(lc);
+ 
+ 		if (strcmp(cte->ctename, rte->ctename) == 0)
+ 			break;
+ 
+ 		if (cte->derived)
+ 			ndx++;
+ 	}
+ 	if (lc == NULL)				/* shouldn't happen */
+ 		elog(ERROR, "could not find CTE \"%s\"", rte->ctename);
+ 
+ 	tupledesc = CreateTemplateTupleDesc(list_length(cte->ctecolnames), false /* XXX oids? */);
+ 	{
+ 		int resno = 1;
+ 		ListCell *namelc,
+ 				 *typelc,
+ 				 *typmodlc;
+ 
+ 		Assert(list_length(cte->ctecoltypes) == list_length(cte->ctecolnames));
+ 		Assert(list_length(cte->ctecoltypmods) == list_length(cte->ctecolnames));
+ 
+ 		typelc = list_head(cte->ctecoltypes);
+ 		typmodlc = list_head(cte->ctecoltypmods);
+ 		foreach(namelc, cte->ctecolnames)
+ 		{
+ 			TupleDescInitEntry(tupledesc,
+ 							   resno++,
+ 							   strVal(lfirst(namelc)),
+ 							   lfirst_oid(typelc),
+ 							   lfirst_int(typmodlc),
+ 							   0);
+ 
+ 			typelc = lnext(typelc);
+ 			typmodlc = lnext(typmodlc);
+ 		}
+ 	}
+ 
+ 	scan_clauses = order_qual_clauses(root, scan_clauses);
+ 	scan_clauses = extract_actual_clauses(scan_clauses, false);
+ 
+ 	scan_plan = make_dtscan(tlist, scan_clauses, scan_relid,
+ 							ndx, tupledesc /* XXX hack hack pass real id */);
+ 
+ 	copy_path_costsize(&scan_plan->scan.plan, best_path);
+ 
+ 	return scan_plan;
+ }
+ 
  /*****************************************************************************
   *
   *	JOIN METHODS
***************
*** 2634,2639 **** make_worktablescan(List *qptlist,
--- 2741,2773 ----
  	return node;
  }
  
+ static DtScan *
+ make_dtscan(List *qptlist,
+ 			List *qpqual,
+ 			Index scanrelid,
+ 			int qwrid /*XXX */,
+ 			TupleDesc td)
+ {
+ 	DtScan     *node = makeNode(DtScan);
+ 	Plan	   *plan = &node->scan.plan;
+ 
+ 	/* cost should be inserted by caller */
+ 	plan->targetlist = qptlist;
+ 	plan->qual = qpqual;
+ 	plan->lefttree = NULL;
+ 	plan->righttree = NULL;
+ 	node->scan.scanrelid = scanrelid;
+ 	/* hack hack */
+ 	node->dtTuplestoreId = qwrid;
+ 	node->dtTupledesc = td;
+ 	/* /hack hack */
+ 
+ 	//node->cteParam = cteParam;
+ 	//XXX tuplestorestate id
+ 
+ 	return node;
+ }
+ 
  Append *
  make_append(List *appendplans, List *tlist)
  {
*** a/src/backend/optimizer/plan/setrefs.c
--- b/src/backend/optimizer/plan/setrefs.c
***************
*** 398,403 **** set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
--- 398,415 ----
  					fix_scan_list(glob, splan->scan.plan.qual, rtoffset);
  			}
  			break;
+ 		case T_DtScan:
+ 			/* XXX TODO XXX */
+ 			{
+ 				DtScan *splan = (DtScan *) plan;
+ 
+ 				splan->scan.scanrelid += rtoffset;
+ 				splan->scan.plan.targetlist =
+ 					fix_scan_list(glob, splan->scan.plan.targetlist, rtoffset);
+ 				splan->scan.plan.qual =
+ 					fix_scan_list(glob, splan->scan.plan.qual, rtoffset);
+ 			}
+ 			break;
  		case T_NestLoop:
  		case T_MergeJoin:
  		case T_HashJoin:
*** a/src/backend/optimizer/plan/subselect.c
--- b/src/backend/optimizer/plan/subselect.c
***************
*** 875,883 **** SS_process_ctes(PlannerInfo *root)
  		Param	   *prm;
  
  		/*
  		 * Ignore CTEs that are not actually referenced anywhere.
  		 */
! 		if (cte->cterefcount == 0)
  		{
  			/* Make a dummy entry in cte_plan_ids */
  			root->cte_plan_ids = lappend_int(root->cte_plan_ids, -1);
--- 875,884 ----
  		Param	   *prm;
  
  		/*
+ 		 * XXX
  		 * Ignore CTEs that are not actually referenced anywhere.
  		 */
! 		if (cte->cterefcount == 0 || (!cte->cterecursive && cte->derived))
  		{
  			/* Make a dummy entry in cte_plan_ids */
  			root->cte_plan_ids = lappend_int(root->cte_plan_ids, -1);
***************
*** 1982,1987 **** finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
--- 1983,1994 ----
  			context.paramids = bms_add_members(context.paramids, scan_params);
  			break;
  
+ 		case T_DtScan:
+ 			/* XXX TODO */
+ 				context.paramids = bms_add_members(context.paramids,
+ 												   scan_params);
+ 			break;
+ 
  		case T_ModifyTable:
  			{
  				ModifyTable *mtplan = (ModifyTable *) plan;
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
***************
*** 1311,1316 **** create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel)
--- 1311,1334 ----
  }
  
  /*
+  * create_dtscan_path
+  *	  Creates a path corresponding to a scan of derived table.
+  */
+ Path *
+ create_dtscan_path(PlannerInfo *root, RelOptInfo *rel)
+ {
+ 	Path	   *pathnode = makeNode(Path);
+ 
+ 	pathnode->pathtype = T_DtScan;
+ 	pathnode->parent = rel;
+ 	pathnode->pathkeys = NIL;	/* XXX for now, result is always unordered */
+ 
+ 	cost_dtscan(pathnode, root, rel);
+ 
+ 	return pathnode;
+ }
+ 
+ /*
   * create_nestloop_path
   *	  Creates a pathnode corresponding to a nestloop join between two
   *	  relations.
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
***************
*** 1616,1621 **** fireRules(Query *parsetree,
--- 1616,1658 ----
  	return results;
  }
  
+ static List *
+ RewriteQuery(Query *parsetree, List *rewrite_events);
+ 
+ static List *
+ RewriteCteList(Query *parse)
+ {
+ 	/* XXX */
+ 	List* rewritten = NIL;
+ 	ListCell *lc;
+ 	CommonTableExpr *cte;
+ 	Query *query;
+ 	List* ctelist;
+ 
+ 	parse->derivedList = NIL;
+ 
+ 	ctelist = NIL;
+ 	foreach(lc, parse->cteList)
+ 	{
+ 		cte = (CommonTableExpr *) lfirst(lc);
+ 
+ 		if (!cte->cterecursive)
+ 		{
+ 			cte->derived = true;
+ 
+ 			query = (Query *) cte->ctequery;
+ 			query->querySource = QSRC_DERIVED;
+ 			query->canSetTag = false;
+ 
+ 			rewritten = lappend(rewritten, query);
+ 		}
+ 		else
+ 			ctelist = lappend(ctelist, cte);
+ 	}
+ 
+ 	return rewritten;
+ }
+ 
  
  /*
   * RewriteQuery -
***************
*** 1633,1638 **** RewriteQuery(Query *parsetree, List *rewrite_events)
--- 1670,1680 ----
  	Query	   *qual_product = NULL;
  	List	   *rewritten = NIL;
  
+ 	/* XXX */
+ 	if (parsetree->cteList != NIL)
+ 		rewritten = RewriteCteList(parsetree);
+ 
+ 
  	/*
  	 * If the statement is an update, insert or delete - fire rules on it.
  	 *
*** a/src/backend/tcop/pquery.c
--- b/src/backend/tcop/pquery.c
***************
*** 38,43 **** Portal		ActivePortal = NULL;
--- 38,44 ----
  static void ProcessQuery(PlannedStmt *plan,
  			 const char *sourceText,
  			 ParamListInfo params,
+ 			 List *derived,
  			 DestReceiver *dest,
  			 char *completionTag);
  static void FillPortalStore(Portal portal, bool isTopLevel);
***************
*** 67,73 **** CreateQueryDesc(PlannedStmt *plannedstmt,
  				Snapshot crosscheck_snapshot,
  				DestReceiver *dest,
  				ParamListInfo params,
! 				int instrument_options)
  {
  	QueryDesc  *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
  
--- 68,75 ----
  				Snapshot crosscheck_snapshot,
  				DestReceiver *dest,
  				ParamListInfo params,
! 				int instrument_options,
! 				List *derived)
  {
  	QueryDesc  *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
  
***************
*** 82,87 **** CreateQueryDesc(PlannedStmt *plannedstmt,
--- 84,90 ----
  	qd->params = params;		/* parameter values passed into query */
  	qd->instrument_options = instrument_options;		/* instrumentation
  														 * wanted? */
+ 	qd->derived = derived;
  
  	/* null these fields until set by ExecutorStart */
  	qd->tupDesc = NULL;
***************
*** 162,167 **** static void
--- 165,171 ----
  ProcessQuery(PlannedStmt *plan,
  			 const char *sourceText,
  			 ParamListInfo params,
+ 			 List *derived,
  			 DestReceiver *dest,
  			 char *completionTag)
  {
***************
*** 179,185 **** ProcessQuery(PlannedStmt *plan,
  	 */
  	queryDesc = CreateQueryDesc(plan, sourceText,
  								GetActiveSnapshot(), InvalidSnapshot,
! 								dest, params, 0);
  
  	/*
  	 * Set up to collect AFTER triggers
--- 183,189 ----
  	 */
  	queryDesc = CreateQueryDesc(plan, sourceText,
  								GetActiveSnapshot(), InvalidSnapshot,
! 								dest, params, 0, derived);
  
  	/*
  	 * Set up to collect AFTER triggers
***************
*** 517,523 **** PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
  											InvalidSnapshot,
  											None_Receiver,
  											params,
! 											0);
  
  				/*
  				 * We do *not* call AfterTriggerBeginQuery() here.	We assume
--- 521,528 ----
  											InvalidSnapshot,
  											None_Receiver,
  											params,
! 											0,
! 											NIL);
  
  				/*
  				 * We do *not* call AfterTriggerBeginQuery() here.	We assume
***************
*** 1220,1225 **** PortalRunMulti(Portal portal, bool isTopLevel,
--- 1225,1231 ----
  			   char *completionTag)
  {
  	ListCell   *stmtlist_item;
+ 	DestReceiver* ddtreceiver;
  
  	/*
  	 * If the destination is DestRemoteExecute, change to DestNone.  The
***************
*** 1236,1241 **** PortalRunMulti(Portal portal, bool isTopLevel,
--- 1242,1249 ----
  	if (altdest->mydest == DestRemoteExecute)
  		altdest = None_Receiver;
  
+ 	ddtreceiver = CreateDestReceiver(DestTuplestore);
+ 
  	/*
  	 * Loop to handle the individual queries generated from a single parsetree
  	 * by analysis and rewrite.
***************
*** 1268,1281 **** PortalRunMulti(Portal portal, bool isTopLevel,
--- 1276,1310 ----
  				ProcessQuery(pstmt,
  							 portal->sourceText,
  							 portal->portalParams,
+ 							 portal->ddts,
  							 dest, completionTag);
  			}
+ 			else if (true || /* XXX */stmt == linitial(portal->stmts))
+ 			{
+ 				Tuplestorestate *st;
+ 
+ 				st = tuplestore_begin_heap(true, false, 124124);
+ 				tuplestore_set_eflags(st, 0);
+ 				SetTuplestoreDestReceiverParams(ddtreceiver,
+ 												st,
+ 												portal->heap,
+ 												true);
+ 
+ 				ProcessQuery(pstmt,
+ 							 portal->sourceText,
+ 							 portal->portalParams,
+ 							 portal->ddts,
+ 							 ddtreceiver, NULL);
+ 
+ 				portal->ddts = lappend(portal->ddts, st);
+ 			}
  			else
  			{
  				/* stmt added by rewrite cannot set tag */
  				ProcessQuery(pstmt,
  							 portal->sourceText,
  							 portal->portalParams,
+ 							 portal->ddts,
  							 altdest, NULL);
  			}
  
***************
*** 1313,1318 **** PortalRunMulti(Portal portal, bool isTopLevel,
--- 1342,1349 ----
  		MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
  	}
  
+ 	(*ddtreceiver->rDestroy) (ddtreceiver);
+ 
  	/*
  	 * If a command completion tag was supplied, use it.  Otherwise use the
  	 * portal's commandTag as the default completion tag.
*** a/src/backend/utils/mmgr/portalmem.c
--- b/src/backend/utils/mmgr/portalmem.c
***************
*** 237,242 **** CreatePortal(const char *name, bool allowDup, bool dupSilent)
--- 237,243 ----
  	portal->atEnd = true;		/* disallow fetches until query is set */
  	portal->visible = true;
  	portal->creation_time = GetCurrentStatementStartTimestamp();
+ 	portal->ddts = NIL;
  
  	/* put portal in table (sets portal->name) */
  	PortalHashTableInsert(portal, name);
*** a/src/include/executor/execdesc.h
--- b/src/include/executor/execdesc.h
***************
*** 43,48 **** typedef struct QueryDesc
--- 43,49 ----
  	DestReceiver *dest;			/* the destination for tuple output */
  	ParamListInfo params;		/* param values being passed in */
  	int			instrument_options;		/* OR of InstrumentOption flags */
+ 	List	   *derived;		/* XXX */
  
  	/* These fields are set by ExecutorStart */
  	TupleDesc	tupDesc;		/* descriptor for result tuples */
***************
*** 60,66 **** extern QueryDesc *CreateQueryDesc(PlannedStmt *plannedstmt,
  				Snapshot crosscheck_snapshot,
  				DestReceiver *dest,
  				ParamListInfo params,
! 				int instrument_options);
  
  extern QueryDesc *CreateUtilityQueryDesc(Node *utilitystmt,
  					   const char *sourceText,
--- 61,68 ----
  				Snapshot crosscheck_snapshot,
  				DestReceiver *dest,
  				ParamListInfo params,
! 				int instrument_options,
! 				List *derived);
  
  extern QueryDesc *CreateUtilityQueryDesc(Node *utilitystmt,
  					   const char *sourceText,
*** /dev/null
--- b/src/include/executor/nodeDtscan.h
***************
*** 0 ****
--- 1,24 ----
+ /*-------------------------------------------------------------------------
+  *
+  * nodeDtscan.h
+  *
+  *
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * $PostgreSQL$
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef NODEDTSCAN_H
+ #define NODEDTSCAN_H
+ 
+ #include "nodes/execnodes.h"
+ 
+ extern DtScanState *ExecInitDtScan(DtScan *node, EState *estate, int eflags);
+ extern TupleTableSlot *ExecDtScan(DtScanState *node);
+ extern void ExecEndDtScan(DtScanState *node);
+ extern void ExecDtScanReScan(DtScanState *node, ExprContext *exprCtxt);
+ 
+ #endif   /* NODEDTSCAN_H */
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 363,368 **** typedef struct EState
--- 363,370 ----
  	/* Other working state: */
  	MemoryContext es_query_cxt; /* per-query context in which EState lives */
  
+ 	List	   *es_derived; /* XXX */
+ 
  	List	   *es_tupleTable;	/* List of TupleTableSlots */
  
  	List	   *es_rowMarks;	/* List of ExecRowMarks */
***************
*** 1355,1360 **** typedef struct WorkTableScanState
--- 1357,1379 ----
  	RecursiveUnionState *rustate;
  } WorkTableScanState;
  
+ 
+ /* ----------------
+  *	 DtScanState information
+  *
+  *		DtScan nodes are used to scan a derived table (XXX DerivedSomethingHere?)
+  *
+  * Multiple DtScan nodes can read out from the same derived tables.
+  * ----------------
+  */
+ typedef struct DtScanState
+ {
+ 	ScanState	ss;				/* its first field is NodeTag */
+ 	int			eflags;			/* capability flags to pass to tuplestore */
+ 	Tuplestorestate *tuplestore; /* XXX */
+ 	int			readptr;		/* index of my tuplestore read pointer */
+ } DtScanState;
+ 
  /* ----------------------------------------------------------------
   *				 Join State Information
   * ----------------------------------------------------------------
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 59,64 **** typedef enum NodeTag
--- 59,65 ----
  	T_ValuesScan,
  	T_CteScan,
  	T_WorkTableScan,
+ 	T_DtScan,
  	T_Join,
  	T_NestLoop,
  	T_MergeJoin,
***************
*** 100,105 **** typedef enum NodeTag
--- 101,107 ----
  	T_ValuesScanState,
  	T_CteScanState,
  	T_WorkTableScanState,
+ 	T_DtScanState,
  	T_JoinState,
  	T_NestLoopState,
  	T_MergeJoinState,
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 31,37 **** typedef enum QuerySource
  	QSRC_PARSER,				/* added by parse analysis (now unused) */
  	QSRC_INSTEAD_RULE,			/* added by unconditional INSTEAD rule */
  	QSRC_QUAL_INSTEAD_RULE,		/* added by conditional INSTEAD rule */
! 	QSRC_NON_INSTEAD_RULE		/* added by non-INSTEAD rule */
  } QuerySource;
  
  /* Sort ordering options for ORDER BY and CREATE INDEX */
--- 31,38 ----
  	QSRC_PARSER,				/* added by parse analysis (now unused) */
  	QSRC_INSTEAD_RULE,			/* added by unconditional INSTEAD rule */
  	QSRC_QUAL_INSTEAD_RULE,		/* added by conditional INSTEAD rule */
! 	QSRC_NON_INSTEAD_RULE,		/* added by non-INSTEAD rule */
! 	QSRC_DERIVED				/* XXX */
  } QuerySource;
  
  /* Sort ordering options for ORDER BY and CREATE INDEX */
***************
*** 121,126 **** typedef struct Query
--- 122,128 ----
  	bool		hasForUpdate;	/* FOR UPDATE or FOR SHARE was specified */
  
  	List	   *cteList;		/* WITH list (of CommonTableExpr's) */
+ 	List	   *derivedList;	/* list of derived tables XXX */
  
  	List	   *rtable;			/* list of range table entries */
  	FromExpr   *jointree;		/* table join tree (FROM and WHERE clauses) */
***************
*** 872,877 **** typedef struct CommonTableExpr
--- 874,882 ----
  	List	   *ctecolnames;	/* list of output column names */
  	List	   *ctecoltypes;	/* OID list of output column type OIDs */
  	List	   *ctecoltypmods;	/* integer list of output column typmods */
+ 
+ 	/* XXX rename this, at least to cteSOMETHING */
+ 	bool		derived;		/* was this CTE rewritten to a derived table? */
  } CommonTableExpr;
  
  /*****************************************************************************
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
***************
*** 15,20 ****
--- 15,21 ----
  #define PLANNODES_H
  
  #include "access/sdir.h"
+ #include "access/tupdesc.h"
  #include "nodes/bitmapset.h"
  #include "nodes/primnodes.h"
  #include "storage/itemptr.h"
***************
*** 400,405 **** typedef struct WorkTableScan
--- 401,418 ----
  	int			wtParam;		/* ID of Param representing work table */
  } WorkTableScan;
  
+ /* ----------------
+  *		DtScan node
+  * ----------------
+  */
+ typedef struct DtScan
+ {
+ 	Scan		scan;
+ 	int			dtTuplestoreId;
+ 	TupleDesc	dtTupledesc;
+ 	/* XXX paramid? */
+ } DtScan;
+ 
  
  /*
   * ==========
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
***************
*** 83,88 **** typedef struct PlannerGlobal
--- 83,90 ----
  	Index		lastPHId;		/* highest PlaceHolderVar ID assigned */
  
  	bool		transientPlan;	/* redo plan when TransactionXmin changes? */
+ 
+ 	List	   *derivedTables;	/* plans for derived tables */
  } PlannerGlobal;
  
  /* macro for fetching the Plan associated with a SubPlan node */
*** a/src/include/optimizer/cost.h
--- b/src/include/optimizer/cost.h
***************
*** 81,86 **** extern void cost_functionscan(Path *path, PlannerInfo *root,
--- 81,87 ----
  extern void cost_valuesscan(Path *path, PlannerInfo *root,
  				RelOptInfo *baserel);
  extern void cost_ctescan(Path *path, PlannerInfo *root, RelOptInfo *baserel);
+ extern void cost_dtscan(Path *path, PlannerInfo *root, RelOptInfo *baserel);
  extern void cost_recursive_union(Plan *runion, Plan *nrterm, Plan *rterm);
  extern void cost_sort(Path *path, PlannerInfo *root,
  		  List *pathkeys, Cost input_cost, double tuples, int width,
*** a/src/include/optimizer/pathnode.h
--- b/src/include/optimizer/pathnode.h
***************
*** 56,61 **** extern Path *create_functionscan_path(PlannerInfo *root, RelOptInfo *rel);
--- 56,62 ----
  extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel);
  extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel);
  extern Path *create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel);
+ extern Path *create_dtscan_path(PlannerInfo *root, RelOptInfo *rel);
  
  extern NestPath *create_nestloop_path(PlannerInfo *root,
  					 RelOptInfo *joinrel,
*** a/src/include/utils/portal.h
--- b/src/include/utils/portal.h
***************
*** 168,173 **** typedef struct PortalData
--- 168,175 ----
  	/* Presentation data, primarily used by the pg_cursors system view */
  	TimestampTz creation_time;	/* time at which this portal was defined */
  	bool		visible;		/* include this portal in pg_cursors? */
+ 
+ 	List	   *ddts;
  } PortalData;
  
  /*