Hierarchical Queries--Status
All,
In the spirit of our previous discussion, I am writing to inform you
that Mark Cave-Ayland and I will be working on this TODO-item
together. We are thinking through a new design (not based on the
current patch) and will post it to -hackers for approval soon.
--
Jonah H. Harris, Software Architect | phone: 732.331.1300
EnterpriseDB Corporation | fax: 732.331.1301
33 Wood Ave S, 2nd Floor | jharris@enterprisedb.com
Iselin, New Jersey 08830 | http://www.enterprisedb.com/
Jonah H. Harris wrote:
All,
In the spirit of our previous discussion, I am writing to inform you
that Mark Cave-Ayland and I will be working on this TODO-item
together. We are thinking through a new design (not based on the
current patch) and will post it to -hackers for approval soon.
Was it posted?
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
On 8/26/06, Alvaro Herrera <alvherre@commandprompt.com> wrote:
Was it posted?
No, the patch has not yet been posted. I'm not sure of where Mark's
at with it at this point in time.
--
Jonah H. Harris, Software Architect | phone: 732.331.1300
EnterpriseDB Corporation | fax: 732.331.1301
33 Wood Ave S, 2nd Floor | jharris@enterprisedb.com
Iselin, New Jersey 08830 | http://www.enterprisedb.com/
Jonah H. Harris wrote:
On 8/26/06, Alvaro Herrera <alvherre@commandprompt.com> wrote:
Was it posted?
No, the patch has not yet been posted. I'm not sure of where Mark's
at with it at this point in time.
Actually I was thinking in the design rather than the code ...
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
On 8/26/06, Alvaro Herrera <alvherre@commandprompt.com> wrote:
Actually I was thinking in the design rather than the code ...
Doh! We hadn't posted the design just yet. Let me write him and see
where he's at and we'll throw something together for the list.
--
Jonah H. Harris, Software Architect | phone: 732.331.1300
EnterpriseDB Corporation | fax: 732.331.1301
33 Wood Ave S, 2nd Floor | jharris@enterprisedb.com
Iselin, New Jersey 08830 | http://www.enterprisedb.com/
On Sat, 2006-08-26 at 22:46 -0400, Jonah H. Harris wrote:
On 8/26/06, Alvaro Herrera <alvherre@commandprompt.com> wrote:
Actually I was thinking in the design rather than the code ...
Doh! We hadn't posted the design just yet. Let me write him and see
where he's at and we'll throw something together for the list.
[Note to Jonah: I've tried sending a similar version of this email to
you a couple of times, but I'm not sure that it's getting through, hence
the post to -hackers in the hope you may be able to pick it up there.]
Hi everyone,
I've had a chance to sit down for a day or so to see how to approach
adding hierarchical queries to PostgreSQL, so I thought I'd post my
initial thoughts on how to proceed. My aim is to refine the list below
based upon feedback from hackers until it gets to the point where I can
start work on it. Here's what I've got so far:
1) Add detection of the WITH clause to the parser.
2) Create a new type of RangeTblEntry to represent each common table
expression specified within the WITH clause where the subquery field
points to the nodes representing the common table expression.
3) Add planner support so that WITH clauses are mapped to a new type of
node that utilises two tuplestores - an output tuplestore and a working
tuplestore. The output tuple store will in effect be the contents of the
table expression while the working tuplestore holds the results of the
last iteration if recursive. Also implement some kind of WithState node
which keeps track of the recursion state.
Having spent some more time today looking at 1) and also at the SQL 2003
spec, it would seem that other databases offer the WITH clause within
subqueries and also as part of a view. I'd be interested to hear
feedback from other developers as to whether it would be possible to
achieve this level of support within PostgreSQL.
Many thanks,
Mark.
On Mon, Sep 04, 2006 at 05:15:57PM +0100, Mark Cave-Ayland wrote:
3) Add planner support so that WITH clauses are mapped to a new type of
node that utilises two tuplestores - an output tuplestore and a working
tuplestore. The output tuple store will in effect be the contents of the
table expression while the working tuplestore holds the results of the
last iteration if recursive. Also implement some kind of WithState node
which keeps track of the recursion state.
That's basically what I came up with. Basically you have a sort of loop
in the execution plan where tuples that come out are copied into a
tuplestore and run through a particular part of the executor again. The
top-down approach of the executor makes it a bit trickier...
Having spent some more time today looking at 1) and also at the SQL 2003
spec, it would seem that other databases offer the WITH clause within
subqueries and also as part of a view. I'd be interested to hear
feedback from other developers as to whether it would be possible to
achieve this level of support within PostgreSQL.
Absolutly possible. The question is how much work :)
Incidently, if you find a way to support common subplans (where a part
of the executor is shared between two executions) that might provide a
way to solve some of the trickier multiple evaluation problems with
rules. Again, it would just be a tuplestore the stored the results for
multiple executions.
Have a nice day,
--
Martijn van Oosterhout <kleptog@svana.org> http://svana.org/kleptog/
Show quoted text
From each according to his ability. To each according to his ability to litigate.
Hi everyone,
After spending several days reading PostgreSQL source code (and another
couple of days coding), I've managed to come up with some alpha code
that attempts to implement non-recursive WITH common table expressions.
Having got this far, I feel that I need to ask for advice from some more
experienced PostgreSQL hackers and so I post the alpha version here.
The patch attempts to alter the parser grammar, and using code modified
from that used when processing subselects in the FROM clause, attempts
to treat the CTE as if it were presented the same as a FROM subselect.
My main issue at the moment is that the code in transformFromClauseItem
seems a terrible hack, mainly because the grammar returns each string
within the FROM clause as a RangeVar, and transformFromClauseItem
assumes that each RangeVar represents a physical relation. Of course,
this is not the case when referencing a CTE and so the code first checks
to see if an entry has already been created when processing the WITH
clause; if it does then we return NULL to indicate that
transformFromClause should do nothing. Messy, but I wanted to see what
other developers thought before jumping in and rewriting this part of
the code.
Another point to think about is what should a query return if the SELECT
doesn't refer to a CTE? For example:
- WITH foo(a, b) AS (SELECT * FROM pg_class) SELECT * FROM pg_class;
Since I've inserted the CTE as a range table entry of type RTE_SUBQUERY
then the current behaviour is to perform a cross-join between foo and
bar which I'm not sure is the correct behaviour since CTEs seem to be
more like views in this respect.
Finally, the current code fails when supplying an additional alias to a
CTE in the select statement and then trying to refer to it, e.g.
- with myrel(p1) as (select oid from pg_class) select myrel.p1 from
myrel AS foo, pg_class AS bar WHERE myrel.p1 = bar.oid; -- WORKS
- with myrel(p1) as (select oid from pg_class) select myrel.p1 from
myrel AS foo, pg_class AS bar WHERE foo.p1 = bar.oid; -- FAILS
ERROR: missing FROM-clause entry for table "foo" at character 103
So in this case, should foo be accepted as a valid alias for myrel? My
feeling is that I will end up with an RTE_CTE range table entry kind
which borrows from the current subquery behaviour, but it would be very
useful to see where the similarity between the two range table entry
types ends.
TIA,
Mark.
Attachments:
pgsql_with.patchtext/x-patch; charset=UTF-8; name=pgsql_with.patchDownload
Index: src/backend/parser/analyze.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/analyze.c,v
retrieving revision 1.351
diff -c -r1.351 analyze.c
*** src/backend/parser/analyze.c 18 Sep 2006 16:04:04 -0000 1.351
--- src/backend/parser/analyze.c 20 Sep 2006 19:02:44 -0000
***************
*** 2087,2092 ****
--- 2087,2095 ----
/* make FOR UPDATE/FOR SHARE info available to addRangeTableEntry */
pstate->p_locking_clause = stmt->lockingClause;
+ /* process the WITH clause */
+ transformWithClause(pstate, stmt->withClause);
+
/* process the FROM clause */
transformFromClause(pstate, stmt->fromClause);
Index: src/backend/parser/gram.y
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.565
diff -c -r2.565 gram.y
*** src/backend/parser/gram.y 3 Sep 2006 22:37:05 -0000 2.565
--- src/backend/parser/gram.y 20 Sep 2006 19:02:52 -0000
***************
*** 346,351 ****
--- 346,354 ----
%type <str> OptTableSpace OptConsTableSpace OptTableSpaceOwner
%type <list> opt_check_option
+ %type <list> OptCommonTableExpressionList CommonTableExpressionList
+ %type <node> CommonTableExpression
+
/*
* If you make any token changes, update the keyword table in
***************
*** 5594,5599 ****
--- 5597,5638 ----
| WITHOUT HOLD { $$ = FALSE; }
;
+
+
+ /*****************************************************************************
+ *
+ * COMMON TABLE EXPRESSIONS
+ * WITH STATEMENTS
+ *
+ *****************************************************************************/
+
+
+ OptCommonTableExpressionList:
+ WITH CommonTableExpressionList { $$ = $2; }
+ | /* EMPTY */ { $$ = NIL; }
+ ;
+
+ CommonTableExpressionList:
+ CommonTableExpression { $$ = list_make1($1); }
+ | CommonTableExpressionList ',' CommonTableExpression { $$ = lappend($1, $3); }
+ ;
+
+ CommonTableExpression:
+ ColId '(' columnList ')' AS select_with_parens
+ {
+ Alias *a = makeNode(Alias);
+ a->aliasname = $1;
+ a->colnames = $3;
+
+ RangeSubselect *n = makeNode(RangeSubselect);
+ n->subquery = $6;
+ n->alias = a;
+ $$ = (Node *) n;
+ }
+ ;
+
+
+
/*****************************************************************************
*
* QUERY:
***************
*** 5705,5723 ****
* However, this is not checked by the grammar; parse analysis must check it.
*/
simple_select:
! SELECT opt_distinct target_list
into_clause from_clause where_clause
group_clause having_clause
{
SelectStmt *n = makeNode(SelectStmt);
! n->distinctClause = $2;
! n->targetList = $3;
! n->into = $4;
n->intoColNames = NIL;
! n->fromClause = $5;
! n->whereClause = $6;
! n->groupClause = $7;
! n->havingClause = $8;
$$ = (Node *)n;
}
| values_clause { $$ = $1; }
--- 5746,5765 ----
* However, this is not checked by the grammar; parse analysis must check it.
*/
simple_select:
! OptCommonTableExpressionList SELECT opt_distinct target_list
into_clause from_clause where_clause
group_clause having_clause
{
SelectStmt *n = makeNode(SelectStmt);
! n->withClause = $1;
! n->distinctClause = $3;
! n->targetList = $4;
! n->into = $5;
n->intoColNames = NIL;
! n->fromClause = $6;
! n->whereClause = $7;
! n->groupClause = $8;
! n->havingClause = $9;
$$ = (Node *)n;
}
| values_clause { $$ = $1; }
***************
*** 8752,8758 ****
| VARYING
| VIEW
| VOLATILE
- | WITH
| WITHOUT
| WORK
| WRITE
--- 8794,8799 ----
***************
*** 8925,8930 ****
--- 8966,8972 ----
| USING
| WHEN
| WHERE
+ | WITH
;
Index: src/backend/parser/parse_clause.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/parse_clause.c,v
retrieving revision 1.157
diff -c -r1.157 parse_clause.c
*** src/backend/parser/parse_clause.c 14 Aug 2006 23:39:32 -0000 1.157
--- src/backend/parser/parse_clause.c 20 Sep 2006 19:02:54 -0000
***************
*** 56,61 ****
--- 56,65 ----
RangeSubselect *r);
static RangeTblEntry *transformRangeFunction(ParseState *pstate,
RangeFunction *r);
+ static Node *transformWithClauseItem(ParseState *pstate, Node *n,
+ RangeTblEntry **top_rte, int *top_rti,
+ List **relnamespace,
+ Relids *containedRels);
static Node *transformFromClauseItem(ParseState *pstate, Node *n,
RangeTblEntry **top_rte, int *top_rti,
List **relnamespace,
***************
*** 67,72 ****
--- 71,115 ----
/*
+ * transformWithClause -
+ * Process the WITH clause by adding the CTEs to the query's range
+ * table.
+ */
+ void
+ transformWithClause(ParseState *pstate, List *withList)
+ {
+
+ ListCell *fl;
+
+ /*
+ * The grammar will have produced a list of RangeSubselects. Transform
+ * each one (possibly adding entries to the rtable), check for duplicate
+ * refnames, and then add it to the namespaces.
+ */
+ foreach(fl, withList)
+ {
+ Node *n = lfirst(fl);
+ RangeTblEntry *rte;
+ int rtindex;
+ List *relnamespace;
+ Relids containedRels;
+
+ n = transformWithClauseItem(pstate, n,
+ &rte,
+ &rtindex,
+ &relnamespace,
+ &containedRels);
+ checkNameSpaceConflicts(pstate, pstate->p_relnamespace, relnamespace);
+ pstate->p_joinlist = lappend(pstate->p_joinlist, n);
+ pstate->p_relnamespace = list_concat(pstate->p_relnamespace,
+ relnamespace);
+ pstate->p_varnamespace = lappend(pstate->p_varnamespace, rte);
+ bms_free(containedRels);
+ }
+ }
+
+
+ /*
* transformFromClause -
* Process the FROM clause and add items to the query's range table,
* joinlist, and namespaces.
***************
*** 105,116 ****
&rtindex,
&relnamespace,
&containedRels);
! checkNameSpaceConflicts(pstate, pstate->p_relnamespace, relnamespace);
! pstate->p_joinlist = lappend(pstate->p_joinlist, n);
! pstate->p_relnamespace = list_concat(pstate->p_relnamespace,
! relnamespace);
! pstate->p_varnamespace = lappend(pstate->p_varnamespace, rte);
! bms_free(containedRels);
}
}
--- 148,166 ----
&rtindex,
&relnamespace,
&containedRels);
! /*
! * If transformFromClauseItem returns NULL then it means we don't have to do
! * anything since the RangeVar points to a CTE already in the range table
! */
! if (n)
! {
! checkNameSpaceConflicts(pstate, pstate->p_relnamespace, relnamespace);
! pstate->p_joinlist = lappend(pstate->p_joinlist, n);
! pstate->p_relnamespace = list_concat(pstate->p_relnamespace,
! relnamespace);
! pstate->p_varnamespace = lappend(pstate->p_varnamespace, rte);
! bms_free(containedRels);
! }
}
}
***************
*** 561,566 ****
--- 611,642 ----
}
+
+ static Node *
+ transformWithClauseItem(ParseState *pstate, Node *n,
+ RangeTblEntry **top_rte, int *top_rti,
+ List **relnamespace,
+ Relids *containedRels)
+ {
+ /* sub-SELECT is like a plain relation */
+ RangeTblRef *rtr;
+ RangeTblEntry *rte;
+ int rtindex;
+
+ rte = transformRangeSubselect(pstate, (RangeSubselect *) n);
+ /* assume new rte is at end */
+ rtindex = list_length(pstate->p_rtable);
+ Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
+ *top_rte = rte;
+ *top_rti = rtindex;
+ *relnamespace = list_make1(rte);
+ *containedRels = bms_make_singleton(rtindex);
+ rtr = makeNode(RangeTblRef);
+ rtr->rtindex = rtindex;
+ return (Node *) rtr;
+ }
+
+
/*
* transformFromClauseItem -
* Transform a FROM-clause item, adding any required entries to the
***************
*** 596,616 ****
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));
! *top_rte = rte;
! *top_rti = rtindex;
! *relnamespace = list_make1(rte);
! *containedRels = bms_make_singleton(rtindex);
! rtr = makeNode(RangeTblRef);
! rtr->rtindex = rtindex;
! return (Node *) rtr;
}
else if (IsA(n, RangeSubselect))
{
--- 672,715 ----
if (IsA(n, RangeVar))
{
/* Plain relation reference */
+ RangeVar *rv = (RangeVar *)n;
RangeTblRef *rtr;
RangeTblEntry *rte;
int rtindex;
! /*
! * When a FROM clause is parsed, it is impossible to determine at
! * parse time whether the RangeVar represents a physical relation
! * or a CTE. Since transformWithClause() has already created
! * range table entries for each CTE, we first search the range table
! * to see if have a match before moving on to check the physical
! * relation.
! */
! rte = refnameRangeTblEntry(pstate, rv->schemaname, rv->relname, NULL);
! if (!rte)
! {
! /*
! * Can't find an entry already in the range table, so assume
! * we are looking at a physical relation
! */
! 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));
!
! *top_rte = rte;
! *top_rti = rtindex;
! *relnamespace = list_make1(rte);
! *containedRels = bms_make_singleton(rtindex);
! rtr = makeNode(RangeTblRef);
! rtr->rtindex = rtindex;
! return (Node *) rtr;
! }
! else
! {
! /* Indicate to the caller that the RangeVar references a CTE already in the range table */
! return NULL;
! }
}
else if (IsA(n, RangeSubselect))
{
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.330
diff -c -r1.330 parsenodes.h
*** src/include/nodes/parsenodes.h 5 Sep 2006 21:08:36 -0000 1.330
--- src/include/nodes/parsenodes.h 20 Sep 2006 19:02:59 -0000
***************
*** 732,737 ****
--- 732,738 ----
Node *whereClause; /* WHERE qualification */
List *groupClause; /* GROUP BY clauses */
Node *havingClause; /* HAVING conditional-expression */
+ List *withClause; /* WITH common table expressions */
/*
* In a "leaf" node representing a VALUES list, the above fields are all
Index: src/include/parser/parse_clause.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/parser/parse_clause.h,v
retrieving revision 1.46
diff -c -r1.46 parse_clause.h
*** src/include/parser/parse_clause.h 3 Jul 2006 22:45:40 -0000 1.46
--- src/include/parser/parse_clause.h 20 Sep 2006 19:02:59 -0000
***************
*** 16,21 ****
--- 16,22 ----
#include "parser/parse_node.h"
+ extern void transformWithClause(ParseState *pstate, List *withList);
extern void transformFromClause(ParseState *pstate, List *frmList);
extern int setTargetTable(ParseState *pstate, RangeVar *relation,
bool inh, bool alsoSource, AclMode requiredPerms);
Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> writes:
My main issue at the moment is that the code in transformFromClauseItem
seems a terrible hack, mainly because the grammar returns each string
within the FROM clause as a RangeVar, and transformFromClauseItem
assumes that each RangeVar represents a physical relation. Of course,
this is not the case when referencing a CTE and so the code first checks
to see if an entry has already been created when processing the WITH
clause; if it does then we return NULL to indicate that
transformFromClause should do nothing. Messy, but I wanted to see what
other developers thought before jumping in and rewriting this part of
the code.
You really can't get away with having the identical representation for
CTEs and ordinary sub-selects in the range table. For instance, it
looks like your patch will think that
select ... from (select ...) as x, x, ...
is legal when it certainly is not. I think you need either a new
RTEKind or an additional flag in the RTE to show that it's a CTE rather
than a plain subselect. I'm not entirely sure that you even want the
CTEs in the rangetable at all --- that still needs some thought.
Another point to think about is what should a query return if the SELECT
doesn't refer to a CTE?
The spec ought to make this perfectly clear ... or perhaps not so clear,
but I'm sure it's defined.
- with myrel(p1) as (select oid from pg_class) select myrel.p1 from
myrel AS foo, pg_class AS bar WHERE foo.p1 = bar.oid; -- FAILS
So in this case, should foo be accepted as a valid alias for myrel?
This comes back to the question of whether the CTE per se should be an
RTE at all. Maybe only the reference to it should be an RTE. The
behavior when seeing a plain RangeVar in FROM would be to first search
the side list of valid CTEs, and only on failure go looking for a real
table.
regards, tom lane
Hi Tom,
Thanks for your initial thoughts on this.
On Wed, 2006-09-20 at 20:13 -0400, Tom Lane wrote:
(cut)
You really can't get away with having the identical representation for
CTEs and ordinary sub-selects in the range table. For instance, it
looks like your patch will think thatselect ... from (select ...) as x, x, ...
is legal when it certainly is not. I think you need either a new
RTEKind or an additional flag in the RTE to show that it's a CTE rather
than a plain subselect. I'm not entirely sure that you even want the
CTEs in the rangetable at all --- that still needs some thought.
For semantic reasons, I can see why you are questioning whether or not
the CTE should be contained within the rangetable - there is an implicit
hint that RTEs reflect entries within the FROM clause, but then I also
see that the rewriter adds RTEs when substituting view definitions into
queries. The comments in parsenodes.h also suggest that an RTE is a
namespace/data source reference for a named entity within the query.
The main problem I can see with keeping the CTEs outside the rangetable
is that according to the source, jointree nodes must currently have
RANGETBLREF nodes as leaf nodes; as I understand it, your suggestion of
maintaining the CTEs separately would involve something along the lines
of keeping a separate CTETable and creating some form of CTETBLREF node
that could be referenced within the jointree. While arguably it may be
semantically neater, it appears to involve quite a bit of extra work...
could you explain in more detail as to why you feel that CTEs should
remain outside the rangetable?
This comes back to the question of whether the CTE per se should be an
RTE at all. Maybe only the reference to it should be an RTE. The
behavior when seeing a plain RangeVar in FROM would be to first search
the side list of valid CTEs, and only on failure go looking for a real
table.
This is effectively what the patch does, albeit not particularly
elegantly. I'll spend some time on making those changes a bit more
refined.
Kind regards,
Mark.
Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> writes:
The main problem I can see with keeping the CTEs outside the rangetable
is that according to the source, jointree nodes must currently have
RANGETBLREF nodes as leaf nodes; as I understand it, your suggestion of
maintaining the CTEs separately would involve something along the lines
of keeping a separate CTETable and creating some form of CTETBLREF node
that could be referenced within the jointree.
No, what I'm thinking is that a *reference* to a CTE, from within the
main query's FROM list, would create a "CTERef" RTE and then you'd have
a normal RANGETBLREF node linking to that in the jointree. This solves
the problem of where do you put the alias: on the RTE. What's not clear
to me at this point is whether there can be multiple references in a
query to the same CTE --- if there can, I suspect you must have a data
structure like this.
regards, tom lane
Who is working on this item?
---------------------------------------------------------------------------
Martijn van Oosterhout wrote:
-- Start of PGP signed section.
On Mon, Sep 04, 2006 at 05:15:57PM +0100, Mark Cave-Ayland wrote:
3) Add planner support so that WITH clauses are mapped to a new type of
node that utilises two tuplestores - an output tuplestore and a working
tuplestore. The output tuple store will in effect be the contents of the
table expression while the working tuplestore holds the results of the
last iteration if recursive. Also implement some kind of WithState node
which keeps track of the recursion state.That's basically what I came up with. Basically you have a sort of loop
in the execution plan where tuples that come out are copied into a
tuplestore and run through a particular part of the executor again. The
top-down approach of the executor makes it a bit trickier...Having spent some more time today looking at 1) and also at the SQL 2003
spec, it would seem that other databases offer the WITH clause within
subqueries and also as part of a view. I'd be interested to hear
feedback from other developers as to whether it would be possible to
achieve this level of support within PostgreSQL.Absolutly possible. The question is how much work :)
Incidently, if you find a way to support common subplans (where a part
of the executor is shared between two executions) that might provide a
way to solve some of the trickier multiple evaluation problems with
rules. Again, it would just be a tuplestore the stored the results for
multiple executions.Have a nice day,
--
Martijn van Oosterhout <kleptog@svana.org> http://svana.org/kleptog/From each according to his ability. To each according to his ability to litigate.
-- End of PGP section, PGP failed!
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://www.enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
On Thu, 2007-02-08 at 20:49 -0500, Bruce Momjian wrote:
Who is working on this item?
Jonah was trying to complete this for 8.3, but I believe that he has
handed it onto Gregory Stark - I think
http://archives.postgresql.org/pgsql-hackers/2007-01/msg01586.php is the
latest update.
Kind regards,
Mark.
"Mark Cave-Ayland" <mark.cave-ayland@ilande.co.uk> writes:
On Thu, 2007-02-08 at 20:49 -0500, Bruce Momjian wrote:
Who is working on this item?
Jonah was trying to complete this for 8.3, but I believe that he has
handed it onto Gregory Stark - I think
http://archives.postgresql.org/pgsql-hackers/2007-01/msg01586.php is the
latest update.
There's also
http://archives.postgresql.org/pgsql-patches/2007-02/msg00086.php
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com