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