Earlier I started working on recursive queries. Unfortunately due to other
work I haven't had a chance to look at it in about a week. I probably won't be
back on it for another week.

Here's a work-in-progress patch for review. If anyone else wants to hack on it
while I'm distracted that's fine. Hopefully there'll still be some work for me
to do when I get back to it :)

I haven't written up a plan for the next step yet so there's some planning
work to be done first. I think the first thing is to force the subqueries to
be planned individually as Initplans instead of being inlined directly into
the query to avoid duplicated plans (though a cost-based heuristic is needed
to determine when inline is advantageous). Once we have that the patch might
actually be worth applying as it represents good support for non-recursive
WITH clauses. 

If not I would love to get some feedback on whether I'm on the wrong track
anywhere.

In particular the issues I'm nervous about is whether I'm storing the right
kinds of information in the right places, such as when it comes to using the
pstate versus nodes of the parse tree. Also whether I'm doing work at the
right phase when it comes to parse versus analysis/transformation versus
postponing work for later in the optimizer and executor.

One thing that isn't working the way I was expecting is that I thought storing
the list of common table expression names in scope in the pstate would have
the right semantics and it isn't. It has to build up a list of cte names that
are in scope from all parent scopes. pstates are inherited when parsing
subqueries and reset to saved copies afterwards which seemed like the right
place. However:

postgres=# with a(x) as (select 1) select * from a;
 x 
---
 1
(1 row)

postgres=# with a(x) as (select 1) select * from (select * from a) as x;
ERROR:  relation "a" does not exist



Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /home/stark/src/REPOSITORY/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.360
diff -c -r1.360 copyfuncs.c
*** src/backend/nodes/copyfuncs.c	9 Jan 2007 02:14:11 -0000	1.360
--- src/backend/nodes/copyfuncs.c	30 Jan 2007 17:35:15 -0000
***************
*** 1808,1813 ****
--- 1808,1814 ----
  	COPY_NODE_FIELD(limitOffset);
  	COPY_NODE_FIELD(limitCount);
  	COPY_NODE_FIELD(lockingClause);
+ 	COPY_NODE_FIELD(with_cte_list);
  	COPY_SCALAR_FIELD(op);
  	COPY_SCALAR_FIELD(all);
  	COPY_NODE_FIELD(larg);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /home/stark/src/REPOSITORY/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.294
diff -c -r1.294 equalfuncs.c
*** src/backend/nodes/equalfuncs.c	9 Jan 2007 02:14:12 -0000	1.294
--- src/backend/nodes/equalfuncs.c	30 Jan 2007 17:35:31 -0000
***************
*** 746,751 ****
--- 746,752 ----
  	COMPARE_NODE_FIELD(limitOffset);
  	COMPARE_NODE_FIELD(limitCount);
  	COMPARE_NODE_FIELD(lockingClause);
+ 	COMPARE_NODE_FIELD(with_cte_list);
  	COMPARE_SCALAR_FIELD(op);
  	COMPARE_SCALAR_FIELD(all);
  	COMPARE_NODE_FIELD(larg);
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /home/stark/src/REPOSITORY/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.292
diff -c -r1.292 outfuncs.c
*** src/backend/nodes/outfuncs.c	9 Jan 2007 02:14:12 -0000	1.292
--- src/backend/nodes/outfuncs.c	30 Jan 2007 17:36:05 -0000
***************
*** 1419,1424 ****
--- 1419,1425 ----
  	WRITE_NODE_FIELD(limitOffset);
  	WRITE_NODE_FIELD(limitCount);
  	WRITE_NODE_FIELD(lockingClause);
+ 	WRITE_NODE_FIELD(with_cte_list);
  	WRITE_ENUM_FIELD(op, SetOperation);
  	WRITE_BOOL_FIELD(all);
  	WRITE_NODE_FIELD(larg);
Index: src/backend/parser/analyze.c
===================================================================
RCS file: /home/stark/src/REPOSITORY/pgsql/src/backend/parser/analyze.c,v
retrieving revision 1.355
diff -c -r1.355 analyze.c
*** src/backend/parser/analyze.c	9 Jan 2007 02:14:13 -0000	1.355
--- src/backend/parser/analyze.c	30 Jan 2007 16:03:59 -0000
***************
*** 2097,2102 ****
--- 2097,2105 ----
  	/* make FOR UPDATE/FOR SHARE info available to addRangeTableEntry */
  	pstate->p_locking_clause = stmt->lockingClause;
  
+ 	/* process the WITH clause (pull ctes into the pstate's ctenamespace) */
+ 	transformWithClause(pstate, stmt->with_cte_list);
+ 
  	/* process the FROM clause */
  	transformFromClause(pstate, stmt->fromClause);
  
Index: src/backend/parser/gram.y
===================================================================
RCS file: /home/stark/src/REPOSITORY/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.573
diff -c -r2.573 gram.y
*** src/backend/parser/gram.y	9 Jan 2007 02:14:14 -0000	2.573
--- src/backend/parser/gram.y	30 Jan 2007 17:45:46 -0000
***************
*** 102,108 ****
  static SelectStmt *findLeftmostSelect(SelectStmt *node);
  static void insertSelectOptions(SelectStmt *stmt,
  								List *sortClause, List *lockingClause,
! 								Node *limitOffset, Node *limitCount);
  static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
  static Node *doNegate(Node *n, int location);
  static void doNegateFloat(Value *v);
--- 102,109 ----
  static SelectStmt *findLeftmostSelect(SelectStmt *node);
  static void insertSelectOptions(SelectStmt *stmt,
  								List *sortClause, List *lockingClause,
! 								Node *limitOffset, Node *limitCount,
! 								List *with_cte_list);
  static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
  static Node *doNegate(Node *n, int location);
  static void doNegateFloat(Value *v);
***************
*** 350,355 ****
--- 351,360 ----
  %type <node>	xml_root_version opt_xml_root_standalone
  %type <boolean>	document_or_content xml_whitespace_option
  
+ %type <node> 	common_table_expression
+ %type <list> 	with_cte_list cte_list
+ %type <boolean> recursive_is_depth_first
+ 
  
  /*
   * If you make any token changes, update the keyword table in
***************
*** 364,370 ****
  	ASSERTION ASSIGNMENT ASYMMETRIC AT AUTHORIZATION
  
  	BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
! 	BOOLEAN_P BOTH BY
  
  	CACHE CALLED CASCADE CASCADED CASE CAST CHAIN CHAR_P
  	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
--- 369,375 ----
  	ASSERTION ASSIGNMENT ASYMMETRIC AT AUTHORIZATION
  
  	BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
! 	BOOLEAN_P BOTH BREADTH BY
  
  	CACHE CALLED CASCADE CASCADED CASE CAST CHAIN CHAR_P
  	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
***************
*** 376,382 ****
  
  	DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
  	DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
! 	DESC DISABLE_P DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
  
  	EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING
  	EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
--- 381,387 ----
  
  	DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
  	DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
! 	DEPTH DESC DISABLE_P DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
  
  	EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING
  	EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
***************
*** 416,426 ****
  
  	QUOTE
  
! 	READ REAL REASSIGN RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME
  	REPEATABLE REPLACE RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT
  	ROLE ROLLBACK ROW ROWS RULE
  
! 	SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
  	SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
  	SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE_P START STATEMENT
  	STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P
--- 421,431 ----
  
  	QUOTE
  
! 	READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REINDEX RELATIVE_P RELEASE RENAME
  	REPEATABLE REPLACE RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT
  	ROLE ROLLBACK ROW ROWS RULE
  
! 	SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE
  	SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
  	SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE_P START STATEMENT
  	STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P
***************
*** 5666,5686 ****
  			| select_clause sort_clause
  				{
  					insertSelectOptions((SelectStmt *) $1, $2, NIL,
! 										NULL, NULL);
  					$$ = $1;
  				}
  			| select_clause opt_sort_clause for_locking_clause opt_select_limit
  				{
  					insertSelectOptions((SelectStmt *) $1, $2, $3,
! 										list_nth($4, 0), list_nth($4, 1));
  					$$ = $1;
  				}
  			| select_clause opt_sort_clause select_limit opt_for_locking_clause
  				{
  					insertSelectOptions((SelectStmt *) $1, $2, $4,
! 										list_nth($3, 0), list_nth($3, 1));
  					$$ = $1;
  				}
  		;
  
  select_clause:
--- 5671,5722 ----
  			| select_clause sort_clause
  				{
  					insertSelectOptions((SelectStmt *) $1, $2, NIL,
! 										NULL, NULL, NIL);
  					$$ = $1;
  				}
  			| select_clause opt_sort_clause for_locking_clause opt_select_limit
  				{
  					insertSelectOptions((SelectStmt *) $1, $2, $3,
! 										list_nth($4, 0), list_nth($4, 1),
! 										NIL);
  					$$ = $1;
  				}
  			| select_clause opt_sort_clause select_limit opt_for_locking_clause
  				{
  					insertSelectOptions((SelectStmt *) $1, $2, $4,
! 										list_nth($3, 0), list_nth($3, 1),
! 										NIL);
  					$$ = $1;
  				}
+ 			| with_cte_list simple_select
+ 				{ 
+ 					insertSelectOptions((SelectStmt *) $2, 
+ 										NULL, NIL,
+ 										NULL, NULL,
+ 										$1);
+ 					$$ = $2; 
+ 				}
+ 			| with_cte_list select_clause sort_clause
+ 				{
+ 					insertSelectOptions((SelectStmt *) $2, $3, NIL,
+ 										NULL, NULL,
+ 										$1);
+ 					$$ = $2;
+ 				}
+ 			| with_cte_list select_clause opt_sort_clause for_locking_clause opt_select_limit
+ 				{
+ 					insertSelectOptions((SelectStmt *) $2, $3, $4,
+ 										list_nth($5, 0), list_nth($5, 1),
+ 										$1);
+ 					$$ = $2;
+ 				}
+ 			| with_cte_list select_clause opt_sort_clause select_limit opt_for_locking_clause
+ 				{
+ 					insertSelectOptions((SelectStmt *) $2, $3, $5,
+ 										list_nth($4, 0), list_nth($4, 1),
+ 										$1);
+ 					$$ = $2;
+ 				}
  		;
  
  select_clause:
***************
*** 5742,5747 ****
--- 5778,5847 ----
  				}
  		;
  
+ /*
+  * ANSI standard WITH clause looks like:
+  * WITH [ RECURSIVE ] <query name> [ (<column>,...) ] AS (query) [SEARCH or CYCLE clause] 
+  * 
+  * It seems "with_cte_list" has to be a separate token or else there's a s/r
+  * conflict between RECURSIVE and the cte name. This means we'll need a silly
+  * node just to hold the list and the recursive flag :( it doesn't seem like
+  * making RECURSIVE a fully reserved word would be very nice as it's probably a
+  * common column name.
+  *
+  * For now we don't support recursive so just ignore it and pass up the list
+  *
+  */
+ with_cte_list:
+ 		  WITH cte_list
+ 			{ 
+ 				$$ = $2; 
+ 			}
+           | WITH RECURSIVE cte_list 
+ 			{ 
+ 				elog(WARNING, "WITH RECURSIVE not supported yet");  
+ 				$$ = $3; 
+ 			}
+ 		  ;
+ 
+ cte_list:
+ 		  common_table_expression 								{ $$ = list_make1($1); }
+ 		| cte_list ',' common_table_expression 					{ $$ = lappend($1, $3); }
+         ;
+ 
+ common_table_expression:  name opt_name_list AS select_with_parens recursive_search_or_cycle_clause
+ 				{ 
+ 					RangeSubselect *n = makeNode(RangeSubselect);
+ 					Alias *a = makeNode(Alias);
+ 
+ 					n->subquery = $4;
+ 					n->alias = a;
+ 					n->alias->aliasname = $1;
+ 					n->alias->colnames  = $2;
+ 
+ 					$$ = (Node *) n;
+ 				}
+           ;
+ 
+ recursive_search_or_cycle_clause: 
+ 			  recursive_search_clause 
+ 			| recursive_cycle_clause
+ 			| recursive_search_clause recursive_cycle_clause
+ 			| /* EMPTY */			
+ 		;
+ 
+ recursive_search_clause:
+ 		SEARCH recursive_is_depth_first FIRST_P BY sortby_list SET ColId
+ 		;
+ 
+ recursive_is_depth_first: 
+ 			DEPTH {$$ = TRUE;} 
+ 			| BREADTH {$$ = FALSE;}
+ 		;
+ 
+ recursive_cycle_clause:
+ 		CYCLE columnList SET ColId TO a_expr DEFAULT a_expr USING ColId
+ 		;
+ 
  into_clause:
  			INTO OptTempTableName					{ $$ = $2; }
  			| /*EMPTY*/								{ $$ = NULL; }
***************
*** 8524,8529 ****
--- 8624,8630 ----
  			| BACKWARD
  			| BEFORE
  			| BEGIN_P
+ 			| BREADTH
  			| BY
  			| CACHE
  			| CALLED
***************
*** 8560,8565 ****
--- 8661,8667 ----
  			| DELETE_P
  			| DELIMITER
  			| DELIMITERS
+ 			| DEPTH
  			| DISABLE_P
  			| DOCUMENT_P
  			| DOMAIN_P
***************
*** 8669,8684 ****
  			| ROLE
  			| ROLLBACK
  			| ROWS
  			| RULE
  			| SAVEPOINT
  			| SCHEMA
  			| SCROLL
  			| SECOND_P
  			| SECURITY
  			| SEQUENCE
  			| SERIALIZABLE
  			| SESSION
- 			| SET
  			| SHARE
  			| SHOW
  			| SIMPLE
--- 8771,8787 ----
  			| ROLE
  			| ROLLBACK
  			| ROWS
+ 			| RECURSIVE
  			| RULE
  			| SAVEPOINT
  			| SCHEMA
  			| SCROLL
+ 			| SEARCH
  			| SECOND_P
  			| SECURITY
  			| SEQUENCE
  			| SERIALIZABLE
  			| SESSION
  			| SHARE
  			| SHOW
  			| SIMPLE
***************
*** 8719,8725 ****
  			| VIEW
  			| VOLATILE
  			| WHITESPACE_P
- 			| WITH
  			| WITHOUT
  			| WORK
  			| WRITE
--- 8822,8827 ----
***************
*** 8888,8893 ****
--- 8990,8996 ----
  			| RETURNING
  			| SELECT
  			| SESSION_USER
+ 			| SET
  			| SOME
  			| SYMMETRIC
  			| TABLE
***************
*** 8901,8906 ****
--- 9004,9010 ----
  			| USING
  			| WHEN
  			| WHERE
+ 			| WITH
  		;
  
  
***************
*** 9167,9174 ****
  static void
  insertSelectOptions(SelectStmt *stmt,
  					List *sortClause, List *lockingClause,
! 					Node *limitOffset, Node *limitCount)
  {
  	/*
  	 * Tests here are to reject constructs like
  	 *	(SELECT foo ORDER BY bar) ORDER BY baz
--- 9271,9281 ----
  static void
  insertSelectOptions(SelectStmt *stmt,
  					List *sortClause, List *lockingClause,
! 					Node *limitOffset, Node *limitCount,
! 					List *with_cte_list)
  {
+ 	Assert(IsA(stmt, SelectStmt));
+ 
  	/*
  	 * Tests here are to reject constructs like
  	 *	(SELECT foo ORDER BY bar) ORDER BY baz
***************
*** 9199,9204 ****
--- 9306,9319 ----
  					 errmsg("multiple LIMIT clauses not allowed")));
  		stmt->limitCount = limitCount;
  	}
+ 	if (with_cte_list)
+ 	{
+ 		if (stmt->with_cte_list)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 					 errmsg("multiple WITH clauses not allowed")));
+ 		stmt->with_cte_list = with_cte_list;
+ 	}
  }
  
  static Node *
Index: src/backend/parser/keywords.c
===================================================================
RCS file: /home/stark/src/REPOSITORY/pgsql/src/backend/parser/keywords.c,v
retrieving revision 1.181
diff -c -r1.181 keywords.c
*** src/backend/parser/keywords.c	9 Jan 2007 02:14:14 -0000	1.181
--- src/backend/parser/keywords.c	26 Jan 2007 16:29:29 -0000
***************
*** 63,68 ****
--- 63,69 ----
  	{"bit", BIT},
  	{"boolean", BOOLEAN_P},
  	{"both", BOTH},
+ 	{"breadth", BREADTH},
  	{"by", BY},
  	{"cache", CACHE},
  	{"called", CALLED},
***************
*** 120,125 ****
--- 121,127 ----
  	{"delete", DELETE_P},
  	{"delimiter", DELIMITER},
  	{"delimiters", DELIMITERS},
+ 	{"depth", DEPTH},
  	{"desc", DESC},
  	{"disable", DISABLE_P},
  	{"distinct", DISTINCT},
***************
*** 279,284 ****
--- 281,287 ----
  	{"read", READ},
  	{"real", REAL},
  	{"reassign", REASSIGN},
+ 	{"recursive", RECURSIVE},
  	{"recheck", RECHECK},
  	{"references", REFERENCES},
  	{"reindex", REINDEX},
***************
*** 302,307 ****
--- 305,311 ----
  	{"savepoint", SAVEPOINT},
  	{"schema", SCHEMA},
  	{"scroll", SCROLL},
+ 	{"search", SEARCH},
  	{"second", SECOND_P},
  	{"security", SECURITY},
  	{"select", SELECT},
Index: src/backend/parser/parse_clause.c
===================================================================
RCS file: /home/stark/src/REPOSITORY/pgsql/src/backend/parser/parse_clause.c,v
retrieving revision 1.162
diff -c -r1.162 parse_clause.c
*** src/backend/parser/parse_clause.c	9 Jan 2007 02:14:14 -0000	1.162
--- src/backend/parser/parse_clause.c	30 Jan 2007 18:16:08 -0000
***************
*** 68,73 ****
--- 68,120 ----
  
  
  /*
+  * transformWithClause -
+  *    Process the list of WITH clause "common table expressions" into Query nodes. 
+  *
+  * We just replace the 
+ 
+ 
+  * We need to add the name of the common table expression to a list that is
+  * used later to find them. But we do _not_ add the table itself to the current
+  * namespace because that would implicitly join all of them which isn't right.
+  *
+  */
+ 
+ void
+ transformWithClause(ParseState *pstate, List *with_cte_list) 
+ {
+ 	ListCell *cte;
+ 
+ 	
+ 	foreach(cte, with_cte_list)
+ 	{
+ 		RangeSubselect *qry = lfirst(cte), *cte;
+ 		List 	*parsetrees = parse_sub_analyze(qry->subquery, pstate);
+ 		Query 	*query;
+ 
+ 		/* Same checkes that FROM does on subqueries XXX refactor? */
+ 		if (list_length(parsetrees) != 1)
+ 			elog(ERROR, "unexpected parse analysis result for subquery in WITH");
+ 		query = (Query *) linitial(parsetrees);
+ 		if (query == NULL || !IsA(query, Query))
+ 			elog(ERROR, "unexpected parse analysis result for subquery in WITH");
+ 
+ 		if (query->commandType != CMD_SELECT)
+ 			elog(ERROR, "expected SELECT query from subquery in WITH");
+ 		if (query->into != NULL)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 					 errmsg("subquery in WITH may not have SELECT INTO")));
+ 
+ 		cte = makeNode(RangeSubselect);
+ 		cte->subquery = (Node*) query;
+ 		cte->alias = copyObject(qry->alias);
+ 
+ 		pstate->p_ctenamespace = lappend(pstate->p_ctenamespace, cte);
+ 	}
+ }
+ 
+ /*
   * transformFromClause -
   *	  Process the FROM clause and add items to the query's range table,
   *	  joinlist, and namespaces.
***************
*** 410,415 ****
--- 457,511 ----
  	return rte;
  }
  
+ /*
+  * transformRangeCTE --- transform a RangeVar which references a common table
+  * expression (ie, a sub-SELECT defined in a WITH clause)
+  */
+ static RangeTblEntry *
+ transformRangeCTE(ParseState *pstate, RangeVar *n, RangeSubselect *r)
+ {
+ 	RangeTblEntry *rte;
+ 
+ 	/* Unlike transformRangeSubselect we do not have to worry about:
+ 	 *
+ 	 * . checking for an alias because the grammar for WITH always gives us an
+ 	 *   alias
+ 	 *
+ 	 * . transforming the subquery as transformWithClause has already done that
+ 	 *   and the RangeSubselect contains the query tree, not the raw parse tree
+ 	 * 
+ 	 * . checking for lateral references since WITH subqueries have their own
+ 	 *   scope. Since they were transformed prior to any range table entries
+ 	 *   being created in our pstate they were all planned with a fresh copy of
+ 	 *   our empty pstate (unless we're in a subquery already of course).
+ 	 */
+ 
+ 	/* This is a kluge for now. Effectively we're inlining all the WITH clauses
+ 	 * which isn't what we want to do */
+ 
+ 
+ 	/* One tricky bit. We potentially have two aliases here. The WITH clause
+ 	 * always specifies a relation alias and may or may not specify column
+ 	 * aliases. The rangevar also may or may not specify a relation alias and
+ 	 * may or may not specify column aliases.
+ 	 */
+ 
+ 
+ 	Alias *a = copyObject(r->alias);
+ 	if (n->alias && n->alias->aliasname)
+ 		a->aliasname = n->alias->aliasname;
+ 	if (n->alias && n->alias->colnames)
+ 		a->colnames = n->alias->colnames;
+ 	/*
+ 	 * OK, build an RTE for the subquery.
+ 	 */
+ 	rte = addRangeTableEntryForSubquery(pstate, (Query*) r->subquery, a, true);
+ 
+ 	return rte;
+ }
+ 
+ 
+ 
  
  /*
   * transformRangeSubselect --- transform a sub-SELECT appearing in FROM
***************
*** 597,607 ****
  	if (IsA(n, RangeVar))
  	{
  		/* Plain relation reference */
  		RangeTblRef *rtr;
! 		RangeTblEntry *rte;
  		int			rtindex;
  
- 		rte = transformTableEntry(pstate, (RangeVar *) n);
  		/* assume new rte is at end */
  		rtindex = list_length(pstate->p_rtable);
  		Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
--- 693,720 ----
  	if (IsA(n, RangeVar))
  	{
  		/* Plain relation reference */
+ 		RangeVar *rv = (RangeVar *)n;
  		RangeTblRef *rtr;
! 		RangeTblEntry *rte = NULL;
  		int			rtindex;
+ 		ListCell *cte;
+ 
+ 		if (pstate->p_ctenamespace && !rv->schemaname) {
+ 			/* We have to check if this is a reference to a common table
+ 			 * expression (ie subquery defined in the WITH clause) */
+ 			foreach(cte, pstate->p_ctenamespace)
+ 			{
+ 				RangeSubselect *r = (RangeSubselect*) lfirst(cte);
+ 				if (!strcmp(rv->relname, r->alias->aliasname))
+ 				{
+ 					rte = transformRangeCTE(pstate, rv, r);
+ 					break;
+ 				}
+ 			}
+ 		}
+ 		if (!rte)
+ 			rte = transformTableEntry(pstate, rv);
  
  		/* assume new rte is at end */
  		rtindex = list_length(pstate->p_rtable);
  		Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /home/stark/src/REPOSITORY/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.338
diff -c -r1.338 parsenodes.h
*** src/include/nodes/parsenodes.h	9 Jan 2007 02:14:15 -0000	1.338
--- src/include/nodes/parsenodes.h	30 Jan 2007 18:09:27 -0000
***************
*** 789,794 ****
--- 789,795 ----
  	/*
  	 * These fields are used only in upper-level SelectStmts.
  	 */
+ 	List		*with_cte_list; /* List of Common Table Expressions (ie WITH clause) */
  	SetOperation op;			/* type of set op */
  	bool		all;			/* ALL specified? */
  	struct SelectStmt *larg;	/* left child */
Index: src/include/parser/parse_clause.h
===================================================================
RCS file: /home/stark/src/REPOSITORY/pgsql/src/include/parser/parse_clause.h,v
retrieving revision 1.48
diff -c -r1.48 parse_clause.h
*** src/include/parser/parse_clause.h	9 Jan 2007 02:14:15 -0000	1.48
--- src/include/parser/parse_clause.h	30 Jan 2007 16:07:39 -0000
***************
*** 16,21 ****
--- 16,22 ----
  
  #include "parser/parse_node.h"
  
+ extern void transformWithClause(ParseState *pstate, List *with_cte_list);
  extern void transformFromClause(ParseState *pstate, List *frmList);
  extern int setTargetTable(ParseState *pstate, RangeVar *relation,
  			   bool inh, bool alsoSource, AclMode requiredPerms);
Index: src/include/parser/parse_node.h
===================================================================
RCS file: /home/stark/src/REPOSITORY/pgsql/src/include/parser/parse_node.h,v
retrieving revision 1.51
diff -c -r1.51 parse_node.h
*** src/include/parser/parse_node.h	5 Jan 2007 22:19:57 -0000	1.51
--- src/include/parser/parse_node.h	30 Jan 2007 16:07:01 -0000
***************
*** 58,63 ****
--- 58,70 ----
   * of ParseStates, only the topmost ParseState contains paramtype info; but
   * we copy the p_variableparams flag down to the child nodes for speed in
   * coerce_type.
+  *
+  * [1] Note that p_ctenamespace is a namespace for "relations" but distinct
+  *     from p_relnamespace. p_ctenamespace is the list of relations that can be
+  *     referred to in a FROM or JOIN clause. p_relnamespace is the list of
+  *     relations which already have and therefore can be referred to in
+  *     qualified variable references. Also, note that p_ctenamespace is a list
+  *     of RangeSubselects, not a list of range table entries.
   */
  typedef struct ParseState
  {
***************
*** 68,73 ****
--- 75,81 ----
  								 * node's fromlist) */
  	List	   *p_relnamespace; /* current namespace for relations */
  	List	   *p_varnamespace; /* current namespace for columns */
+ 	List	   *p_ctenamespace; /* current namespace for common table expressions [1] */
  	Oid		   *p_paramtypes;	/* OIDs of types for $n parameter symbols */
  	int			p_numparams;	/* allocated size of p_paramtypes[] */
  	int			p_next_resno;	/* next targetlist resno to assign */


-- 
  Gregory Stark
  EnterpriseDB          http://www.enterprisedb.com
---------------------------(end of broadcast)---------------------------
TIP 1: if posting/reading through Usenet, please send an appropriate
       subscribe-nomail command to [EMAIL PROTECTED] so that your
       message can get through to the mailing list cleanly

Reply via email to