Attached is a patch for contrib/tablefunc. It fixes two issues raised by Lars Boegild Thomsen (full email below) and also corrects the regression expected output for a recent backend message adjustment. Please apply.

Thanks,

Joe


Lars Boegild Thomsen wrote:
Dear Mr. Conway,

I've noticed that you're the author of the tablefunc functions in the
postgresql distribution.  I've come across a problem that I think is a bug.
I realize that you're of course under no obligation to support this
software, so I am just writing turn your attention to the problem - if
indeed it is a problem :)

I've go the following table defined:

create table domains (
    id int not null primary key default nextval('domain_id_seq'),
    name varchar(64) not null,
    description text,
    owner_id int
) without oids;

And for testing it contains the following:

ipsmart=> select * from domains;
 id |        name         |               description                |
owner_id
----+---------------------+------------------------------------------+------
----
  0 | justit.ws           | The overall owner of the IPSmart product |
  1 | sp1.justit.ws       | Domain 1                                 |
0
  2 | sp2.justit.ws       | Domain 2                                 |
0
  3 | test1.sp1.justit.ws | Sub Domain 1                             |
1
  4 | test2.sp1.justit.ws | Sub Domain 2                             |
1

The owner_id column referenfes the id column of the same table.

Now - I need to traverse this table from a certain level (and in production
it will become rather deep).

I've got two problems - one just an annoying "feature" of the connectby
function and the other I think is a bug.  Let's start with the bug :)
Traversing from node '0' is ok:

ipsmart=> select * from connectby('domains', 'id', 'owner_id', '0', 0) as
t(id int, owner_id int, level int);
 id | owner_id | level
----+----------+-------
  0 |          |     0
  1 |        0 |     1
  3 |        1 |     2
  4 |        1 |     2
  2 |        0 |     1
(5 rows)

No problem there.  Traversing from node 1 also works fine (results are 1, 3
and 4).  But traversing from node 2 - nothing is returned.

ipsmart=> select * from connectby('domains', 'id', 'owner_id', '2', 0) as
t(id int, owner_id int, level int);
 id | owner_id | level
----+----------+-------
(0 rows)

This is kind of weird since in the first two examples the root node was
clearly returned as well.  Is this a feature or a bug?

The other problem is not really a bug but rather a slight irritation :)
Originally I had the 'owner_id" defined as "not null'.  I let the root refer
to itself as parent and added the references constraint after I inserted the
first record.  This way I could guarantee that the parent was always
defined.  Unfortunately your "connectby" function wouldn't accept this and
it lead to an infinite recurse.   Wouldn't it be reasonable easy to let your
function stop if the node was referring to itself?

Anyway - as mentioned earlier - I don't really expect that you've got time
to look at this (although I would of course appreciate it a lot if you had).
Even if you don't have time - I can live with the minor problems unless
PostgreSQL get a real "connect by prior" construct :)

Regards,

Lars...

--
Lars Boegild Thomsen
Technical Director
JustIT Sdn. Bhd.
Cell Phone (MY): +60 (16) 323 1999
ICQ: 6478559
Yahoo Chat: [EMAIL PROTECTED]
MSN Chat: [EMAIL PROTECTED]
http://www.justit.ws


Index: contrib/tablefunc/tablefunc.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/contrib/tablefunc/tablefunc.c,v
retrieving revision 1.24
diff -c -r1.24 tablefunc.c
*** contrib/tablefunc/tablefunc.c       13 Sep 2003 21:44:50 -0000      1.24
--- contrib/tablefunc/tablefunc.c       1 Oct 2003 22:47:11 -0000
***************
*** 1295,1329 ****
        int                     ret;
        int                     proc;
        int                     serial_column;
  
        if (max_depth > 0 && level > max_depth)
                return tupstore;
  
        /* Build initial sql statement */
        if (!show_serial)
        {
!               appendStringInfo(sql, "SELECT %s, %s FROM %s WHERE %s = '%s' AND %s IS 
NOT NULL",
                                                 key_fld,
                                                 parent_key_fld,
                                                 relname,
                                                 parent_key_fld,
                                                 start_with,
!                                                key_fld);
                serial_column = 0;
        }
        else
        {
!               appendStringInfo(sql, "SELECT %s, %s FROM %s WHERE %s = '%s' AND %s IS 
NOT NULL ORDER BY %s",
                                                 key_fld,
                                                 parent_key_fld,
                                                 relname,
                                                 parent_key_fld,
                                                 start_with,
!                                                key_fld,
                                                 orderby_fld);
                serial_column = 1;
        }
  
        /* Retrieve the desired rows */
        ret = SPI_exec(sql->data, 0);
        proc = SPI_processed;
--- 1295,1394 ----
        int                     ret;
        int                     proc;
        int                     serial_column;
+       StringInfo      branchstr = NULL;
+       StringInfo      chk_branchstr = NULL;
+       StringInfo      chk_current_key = NULL;
+       char      **values;
+       char       *current_key;
+       char       *current_key_parent;
+       char            current_level[INT32_STRLEN];
+       char            serial_str[INT32_STRLEN];
+       char       *current_branch;
+       HeapTuple       tuple;
  
        if (max_depth > 0 && level > max_depth)
                return tupstore;
  
+       /* start a new branch */
+       branchstr = makeStringInfo();
+ 
+       /* need these to check for recursion */
+       chk_branchstr = makeStringInfo();
+       chk_current_key = makeStringInfo();
+ 
        /* Build initial sql statement */
        if (!show_serial)
        {
!               appendStringInfo(sql, "SELECT %s, %s FROM %s WHERE %s = '%s' AND %s IS 
NOT NULL AND %s <> %s",
                                                 key_fld,
                                                 parent_key_fld,
                                                 relname,
                                                 parent_key_fld,
                                                 start_with,
!                                                key_fld, key_fld, parent_key_fld);
                serial_column = 0;
        }
        else
        {
!               appendStringInfo(sql, "SELECT %s, %s FROM %s WHERE %s = '%s' AND %s IS 
NOT NULL AND %s <> %s ORDER BY %s",
                                                 key_fld,
                                                 parent_key_fld,
                                                 relname,
                                                 parent_key_fld,
                                                 start_with,
!                                                key_fld, key_fld, parent_key_fld,
                                                 orderby_fld);
                serial_column = 1;
        }
  
+       if (show_branch)
+               values = (char **) palloc((CONNECTBY_NCOLS + serial_column) * 
sizeof(char *));
+       else
+               values = (char **) palloc((CONNECTBY_NCOLS_NOBRANCH + serial_column) * 
sizeof(char *));
+ 
+       /* First time through, do a little setup */
+       if (level == 0)
+       {
+               /* root value is the one we initially start with */
+               values[0] = start_with;
+ 
+               /* root value has no parent */
+               values[1] = NULL;
+ 
+               /* root level is 0 */
+               sprintf(current_level, "%d", level);
+               values[2] = current_level;
+ 
+               /* root branch is just starting root value */
+               if (show_branch)
+                       values[3] = start_with;
+ 
+               /* root starts the serial with 1 */
+               if (show_serial)
+               {
+                       sprintf(serial_str, "%d", (*serial)++);
+                       if (show_branch)
+                               values[4] = serial_str;
+                       else
+                               values[3] = serial_str;
+               }
+ 
+               /* construct the tuple */
+               tuple = BuildTupleFromCStrings(attinmeta, values);
+ 
+               /* switch to long lived context while storing the tuple */
+               oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ 
+               /* now store it */
+               tuplestore_puttuple(tupstore, tuple);
+ 
+               /* now reset the context */
+               MemoryContextSwitchTo(oldcontext);
+ 
+               /* increment level */
+               level++;
+       }
+ 
        /* Retrieve the desired rows */
        ret = SPI_exec(sql->data, 0);
        proc = SPI_processed;
***************
*** 1331,1364 ****
        /* Check for qualifying tuples */
        if ((ret == SPI_OK_SELECT) && (proc > 0))
        {
-               HeapTuple       tuple;
                HeapTuple       spi_tuple;
                SPITupleTable *tuptable = SPI_tuptable;
                TupleDesc       spi_tupdesc = tuptable->tupdesc;
                int                     i;
-               char       *current_key;
-               char       *current_key_parent;
-               char            current_level[INT32_STRLEN];
-               char            serial_str[INT32_STRLEN];
-               char       *current_branch;
-               char      **values;
-               StringInfo      branchstr = NULL;
-               StringInfo      chk_branchstr = NULL;
-               StringInfo      chk_current_key = NULL;
- 
-               /* start a new branch */
-               branchstr = makeStringInfo();
- 
-               /* need these to check for recursion */
-               chk_branchstr = makeStringInfo();
-               chk_current_key = makeStringInfo();
  
!               if (show_branch)
!                       values = (char **) palloc((CONNECTBY_NCOLS + serial_column) * 
sizeof(char *));
!               else
!                       values = (char **) palloc((CONNECTBY_NCOLS_NOBRANCH + 
serial_column) * sizeof(char *));
! 
!               /* First time through, do a little setup */
                if (level == 0)
                {
                        /*
--- 1396,1407 ----
        /* Check for qualifying tuples */
        if ((ret == SPI_OK_SELECT) && (proc > 0))
        {
                HeapTuple       spi_tuple;
                SPITupleTable *tuptable = SPI_tuptable;
                TupleDesc       spi_tupdesc = tuptable->tupdesc;
                int                     i;
  
!               /* First time through, do a little more setup */
                if (level == 0)
                {
                        /*
***************
*** 1373,1417 ****
                                                 errmsg("invalid return type"),
                                         errdetail("Return and SQL tuple descriptions 
are " \
                                                           "incompatible.")));
- 
-                       /* root value is the one we initially start with */
-                       values[0] = start_with;
- 
-                       /* root value has no parent */
-                       values[1] = NULL;
- 
-                       /* root level is 0 */
-                       sprintf(current_level, "%d", level);
-                       values[2] = current_level;
- 
-                       /* root branch is just starting root value */
-                       if (show_branch)
-                               values[3] = start_with;
- 
-                       /* root starts the serial with 1 */
-                       if (show_serial)
-                       {
-                               sprintf(serial_str, "%d", (*serial)++);
-                               if (show_branch)
-                                       values[4] = serial_str;
-                               else
-                                       values[3] = serial_str;
-                       }
- 
-                       /* construct the tuple */
-                       tuple = BuildTupleFromCStrings(attinmeta, values);
- 
-                       /* switch to long lived context while storing the tuple */
-                       oldcontext = MemoryContextSwitchTo(per_query_ctx);
- 
-                       /* now store it */
-                       tuplestore_puttuple(tupstore, tuple);
- 
-                       /* now reset the context */
-                       MemoryContextSwitchTo(oldcontext);
- 
-                       /* increment level */
-                       level++;
                }
  
                for (i = 0; i < proc; i++)
--- 1416,1421 ----
Index: contrib/tablefunc/expected/tablefunc.out
===================================================================
RCS file: /opt/src/cvs/pgsql-server/contrib/tablefunc/expected/tablefunc.out,v
retrieving revision 1.9
diff -c -r1.9 tablefunc.out
*** contrib/tablefunc/expected/tablefunc.out    13 Sep 2003 21:44:50 -0000      1.9
--- contrib/tablefunc/expected/tablefunc.out    1 Oct 2003 22:34:32 -0000
***************
*** 127,133 ****
  -- hash based crosstab
  --
  create table cth(id serial, rowid text, rowdt timestamp, attribute text, val text);
! NOTICE:  CREATE TABLE will create implicit sequence "cth_id_seq" for SERIAL column 
"cth.id"
  insert into cth values(DEFAULT,'test1','01 March 2003','temperature','42');
  insert into cth values(DEFAULT,'test1','01 March 2003','test_result','PASS');
  -- the next line is intentionally left commented and is therefore a "missing" 
attribute
--- 127,133 ----
  -- hash based crosstab
  --
  create table cth(id serial, rowid text, rowdt timestamp, attribute text, val text);
! NOTICE:  CREATE TABLE will create implicit sequence "cth_id_seq" for "serial" column 
"cth.id"
  insert into cth values(DEFAULT,'test1','01 March 2003','temperature','42');
  insert into cth values(DEFAULT,'test1','01 March 2003','test_result','PASS');
  -- the next line is intentionally left commented and is therefore a "missing" 
attribute
---------------------------(end of broadcast)---------------------------
TIP 6: Have you searched our list archives?

               http://archives.postgresql.org

Reply via email to