Joe, are you nearly ready to apply this?

---------------------------------------------------------------------------

Joe Conway wrote:
> Joe Conway wrote:
> > Tom Lane wrote:
> >> A couple of minor thoughts:
> >>
> >> * You could reduce the ugliness of many of the tests by introducing a
> >> variant strcmp function that does the "right" things with NULL inputs.
> >> It might also be worth adding a variant pstrdup that takes a NULL.
> > 
> > I had thoughts along those lines -- it would certainly make the code 
> > more readable. I'll go ahead and do that but it won't be in time for a 
> > 26 October beta2.
> 
> I'm not quite ready to commit this, mostly because I'd like to give the 
> rest of tablefunc.c the once-over for similar issues related to not 
> checking for NULL return values from SPI_getvalue(). But it is close 
> enough if needed for a beta2 tomorrow -- let me know if we plan to 
> bundle up beta2 and I'll get it in.
> 
> Thanks,
> 
> Joe

[ text/x-patch is unsupported, treating like TEXT/PLAIN ]

> Index: tablefunc.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/contrib/tablefunc/tablefunc.c,v
> retrieving revision 1.47
> diff -c -r1.47 tablefunc.c
> *** tablefunc.c       3 Mar 2007 19:32:54 -0000       1.47
> --- tablefunc.c       26 Oct 2007 05:35:23 -0000
> ***************
> *** 106,111 ****
> --- 106,123 ----
>               } \
>       } while (0)
>   
> + #define xpstrdup(tgtvar_, srcvar_) \
> +     do { \
> +             if (srcvar_) \
> +                     tgtvar_ = pstrdup(srcvar_); \
> +             else \
> +                     tgtvar_ = NULL; \
> +     } while (0)
> + 
> + #define xstreq(tgtvar_, srcvar_) \
> +     (((tgtvar_ == NULL) && (srcvar_ == NULL)) || \
> +      ((tgtvar_ != NULL) && (srcvar_ != NULL) && (strcmp(tgtvar_, srcvar_) 
> == 0)))
> + 
>   /* sign, 10 digits, '\0' */
>   #define INT32_STRLEN        12
>   
> ***************
> *** 355,360 ****
> --- 367,373 ----
>       crosstab_fctx *fctx;
>       int                     i;
>       int                     num_categories;
> +     bool            firstpass = false;
>       MemoryContext oldcontext;
>   
>       /* stuff done only on the first call of the function */
> ***************
> *** 469,474 ****
> --- 482,488 ----
>               funcctx->max_calls = proc;
>   
>               MemoryContextSwitchTo(oldcontext);
> +             firstpass = true;
>       }
>   
>       /* stuff done on every call of the function */
> ***************
> *** 500,506 ****
>               HeapTuple       tuple;
>               Datum           result;
>               char      **values;
> !             bool            allnulls = true;
>   
>               while (true)
>               {
> --- 514,520 ----
>               HeapTuple       tuple;
>               Datum           result;
>               char      **values;
> !             bool            skip_tuple = false;
>   
>               while (true)
>               {
> ***************
> *** 530,555 ****
>                               rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
>   
>                               /*
> !                              * If this is the first pass through the values 
> for this rowid
> !                              * set it, otherwise make sure it hasn't 
> changed on us. Also
> !                              * check to see if the rowid is the same as 
> that of the last
> !                              * tuple sent -- if so, skip this tuple entirely
>                                */
>                               if (i == 0)
> -                                     values[0] = pstrdup(rowid);
> - 
> -                             if ((rowid != NULL) && (strcmp(rowid, 
> values[0]) == 0))
>                               {
> !                                     if ((lastrowid != NULL) && 
> (strcmp(rowid, lastrowid) == 0))
>                                               break;
> !                                     else if (allnulls == true)
> !                                             allnulls = false;
>   
>                                       /*
> !                                      * Get the next category item value, 
> which is alway
>                                        * attribute number three.
>                                        *
> !                                      * Be careful to sssign the value to 
> the array index based
>                                        * on which category we are presently 
> processing.
>                                        */
>                                       values[1 + i] = SPI_getvalue(spi_tuple, 
> spi_tupdesc, 3);
> --- 544,578 ----
>                               rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
>   
>                               /*
> !                              * If this is the first pass through the values 
> for this
> !                              * rowid, set the first column to rowid
>                                */
>                               if (i == 0)
>                               {
> !                                     xpstrdup(values[0], rowid);
> ! 
> !                                     /*
> !                                      * Check to see if the rowid is the 
> same as that of the last
> !                                      * tuple sent -- if so, skip this tuple 
> entirely
> !                                      */
> !                                     if (!firstpass && xstreq(lastrowid, 
> rowid))
> !                                     {
> !                                             skip_tuple = true;
>                                               break;
> !                                     }
> !                             }
>   
> +                             /*
> +                              * If rowid hasn't changed on us, continue 
> building the
> +                              * ouput tuple.
> +                              */
> +                             if (xstreq(rowid, values[0]))
> +                             {
>                                       /*
> !                                      * Get the next category item value, 
> which is always
>                                        * attribute number three.
>                                        *
> !                                      * Be careful to assign the value to 
> the array index based
>                                        * on which category we are presently 
> processing.
>                                        */
>                                       values[1 + i] = SPI_getvalue(spi_tuple, 
> spi_tupdesc, 3);
> ***************
> *** 572,597 ****
>                                       call_cntr = --funcctx->call_cntr;
>                                       break;
>                               }
> ! 
> !                             if (rowid != NULL)
> !                                     xpfree(rowid);
>                       }
>   
> !                     xpfree(fctx->lastrowid);
>   
> !                     if (values[0] != NULL)
> !                     {
> !                             /*
> !                              * switch to memory context appropriate for 
> multiple function
> !                              * calls
> !                              */
> !                             oldcontext = 
> MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
>   
> !                             lastrowid = fctx->lastrowid = 
> pstrdup(values[0]);
> !                             MemoryContextSwitchTo(oldcontext);
> !                     }
>   
> !                     if (!allnulls)
>                       {
>                               /* build the tuple */
>                               tuple = BuildTupleFromCStrings(attinmeta, 
> values);
> --- 595,616 ----
>                                       call_cntr = --funcctx->call_cntr;
>                                       break;
>                               }
> !                             xpfree(rowid);
>                       }
>   
> !                     /*
> !                      * switch to memory context appropriate for multiple 
> function
> !                      * calls
> !                      */
> !                     oldcontext = 
> MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
>   
> !                     xpfree(fctx->lastrowid);
> !                     xpstrdup(fctx->lastrowid, values[0]);
> !                     lastrowid = fctx->lastrowid;
>   
> !                     MemoryContextSwitchTo(oldcontext);
>   
> !                     if (!skip_tuple)
>                       {
>                               /* build the tuple */
>                               tuple = BuildTupleFromCStrings(attinmeta, 
> values);
> ***************
> *** 625,630 ****
> --- 644,652 ----
>                                       SPI_finish();
>                                       SRF_RETURN_DONE(funcctx);
>                               }
> + 
> +                             /* need to reset this before the next tuple is 
> started */
> +                             skip_tuple = false;
>                       }
>               }
>       }
> ***************
> *** 856,861 ****
> --- 878,884 ----
>               int                     ncols = spi_tupdesc->natts;
>               char       *rowid;
>               char       *lastrowid = NULL;
> +             bool            firstpass = true;
>               int                     i,
>                                       j;
>               int                     result_ncols;
> ***************
> *** 918,938 ****
>                       /* get the rowid from the current sql result tuple */
>                       rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
>   
> -                     /* if rowid is null, skip this tuple entirely */
> -                     if (rowid == NULL)
> -                             continue;
> - 
>                       /*
>                        * if we're on a new output row, grab the column values 
> up to
>                        * column N-2 now
>                        */
> !                     if ((lastrowid == NULL) || (strcmp(rowid, lastrowid) != 
> 0))
>                       {
>                               /*
>                                * a new row means we need to flush the old one 
> first, unless
>                                * we're on the very first row
>                                */
> !                             if (lastrowid != NULL)
>                               {
>                                       /* rowid changed, flush the previous 
> output row */
>                                       tuple = 
> BuildTupleFromCStrings(attinmeta, values);
> --- 941,957 ----
>                       /* get the rowid from the current sql result tuple */
>                       rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
>   
>                       /*
>                        * if we're on a new output row, grab the column values 
> up to
>                        * column N-2 now
>                        */
> !                     if (firstpass || !xstreq(lastrowid, rowid))
>                       {
>                               /*
>                                * a new row means we need to flush the old one 
> first, unless
>                                * we're on the very first row
>                                */
> !                             if (!firstpass)
>                               {
>                                       /* rowid changed, flush the previous 
> output row */
>                                       tuple = 
> BuildTupleFromCStrings(attinmeta, values);
> ***************
> *** 949,954 ****
> --- 968,976 ----
>                               values[0] = rowid;
>                               for (j = 1; j < ncols - 2; j++)
>                                       values[j] = SPI_getvalue(spi_tuple, 
> spi_tupdesc, j + 1);
> + 
> +                             /* we're no longer on the first pass */
> +                             firstpass = false;
>                       }
>   
>                       /* look up the category and fill in the appropriate 
> column */
> ***************
> *** 964,970 ****
>                       }
>   
>                       xpfree(lastrowid);
> !                     lastrowid = pstrdup(rowid);
>               }
>   
>               /* flush the last output row */
> --- 986,992 ----
>                       }
>   
>                       xpfree(lastrowid);
> !                     xpstrdup(lastrowid, rowid);
>               }
>   
>               /* flush the last output row */
> Index: data/ct.data
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/contrib/tablefunc/data/ct.data,v
> retrieving revision 1.1
> diff -c -r1.1 ct.data
> *** data/ct.data      12 Sep 2002 00:14:40 -0000      1.1
> --- data/ct.data      25 Oct 2007 21:45:49 -0000
> ***************
> *** 12,14 ****
> --- 12,18 ----
>   12  group2  test4   att1    val4
>   13  group2  test4   att2    val5
>   14  group2  test4   att3    val6
> + 15  group1  \N      att1    val9
> + 16  group1  \N      att2    val10
> + 17  group1  \N      att3    val11
> + 18  group1  \N      att4    val12
> Index: expected/tablefunc.out
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/contrib/tablefunc/expected/tablefunc.out,v
> retrieving revision 1.13
> diff -c -r1.13 tablefunc.out
> *** expected/tablefunc.out    27 Feb 2006 16:09:49 -0000      1.13
> --- expected/tablefunc.out    25 Oct 2007 22:24:01 -0000
> ***************
> *** 23,64 ****
>   ----------+------------+------------
>    test1    | val2       | val3
>    test2    | val6       | val7
> ! (2 rows)
>   
>   SELECT * FROM crosstab3('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group1'' and (attribute = ''att2'' or attribute = ''att3'') 
> ORDER BY 1,2;');
>    row_name | category_1 | category_2 | category_3 
>   ----------+------------+------------+------------
>    test1    | val2       | val3       | 
>    test2    | val6       | val7       | 
> ! (2 rows)
>   
>   SELECT * FROM crosstab4('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group1'' and (attribute = ''att2'' or attribute = ''att3'') 
> ORDER BY 1,2;');
>    row_name | category_1 | category_2 | category_3 | category_4 
>   ----------+------------+------------+------------+------------
>    test1    | val2       | val3       |            | 
>    test2    | val6       | val7       |            | 
> ! (2 rows)
>   
>   SELECT * FROM crosstab2('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group1'' ORDER BY 1,2;');
>    row_name | category_1 | category_2 
>   ----------+------------+------------
>    test1    | val1       | val2
>    test2    | val5       | val6
> ! (2 rows)
>   
>   SELECT * FROM crosstab3('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group1'' ORDER BY 1,2;');
>    row_name | category_1 | category_2 | category_3 
>   ----------+------------+------------+------------
>    test1    | val1       | val2       | val3
>    test2    | val5       | val6       | val7
> ! (2 rows)
>   
>   SELECT * FROM crosstab4('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group1'' ORDER BY 1,2;');
>    row_name | category_1 | category_2 | category_3 | category_4 
>   ----------+------------+------------+------------+------------
>    test1    | val1       | val2       | val3       | val4
>    test2    | val5       | val6       | val7       | val8
> ! (2 rows)
>   
>   SELECT * FROM crosstab2('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group2'' and (attribute = ''att1'' or attribute = ''att2'') 
> ORDER BY 1,2;');
>    row_name | category_1 | category_2 
> --- 23,70 ----
>   ----------+------------+------------
>    test1    | val2       | val3
>    test2    | val6       | val7
> !           | val10      | val11
> ! (3 rows)
>   
>   SELECT * FROM crosstab3('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group1'' and (attribute = ''att2'' or attribute = ''att3'') 
> ORDER BY 1,2;');
>    row_name | category_1 | category_2 | category_3 
>   ----------+------------+------------+------------
>    test1    | val2       | val3       | 
>    test2    | val6       | val7       | 
> !           | val10      | val11      | 
> ! (3 rows)
>   
>   SELECT * FROM crosstab4('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group1'' and (attribute = ''att2'' or attribute = ''att3'') 
> ORDER BY 1,2;');
>    row_name | category_1 | category_2 | category_3 | category_4 
>   ----------+------------+------------+------------+------------
>    test1    | val2       | val3       |            | 
>    test2    | val6       | val7       |            | 
> !           | val10      | val11      |            | 
> ! (3 rows)
>   
>   SELECT * FROM crosstab2('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group1'' ORDER BY 1,2;');
>    row_name | category_1 | category_2 
>   ----------+------------+------------
>    test1    | val1       | val2
>    test2    | val5       | val6
> !           | val9       | val10
> ! (3 rows)
>   
>   SELECT * FROM crosstab3('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group1'' ORDER BY 1,2;');
>    row_name | category_1 | category_2 | category_3 
>   ----------+------------+------------+------------
>    test1    | val1       | val2       | val3
>    test2    | val5       | val6       | val7
> !           | val9       | val10      | val11
> ! (3 rows)
>   
>   SELECT * FROM crosstab4('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group1'' ORDER BY 1,2;');
>    row_name | category_1 | category_2 | category_3 | category_4 
>   ----------+------------+------------+------------+------------
>    test1    | val1       | val2       | val3       | val4
>    test2    | val5       | val6       | val7       | val8
> !           | val9       | val10      | val11      | val12
> ! (3 rows)
>   
>   SELECT * FROM crosstab2('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group2'' and (attribute = ''att1'' or attribute = ''att2'') 
> ORDER BY 1,2;');
>    row_name | category_1 | category_2 
> ***************
> *** 103,127 ****
>   (2 rows)
>   
>   SELECT * FROM crosstab('SELECT rowid, attribute, val FROM ct where rowclass 
> = ''group1'' ORDER BY 1,2;') AS c(rowid text, att1 text, att2 text);
> !  rowid | att1 | att2 
> ! -------+------+------
>    test1 | val1 | val2
>    test2 | val5 | val6
> ! (2 rows)
>   
>   SELECT * FROM crosstab('SELECT rowid, attribute, val FROM ct where rowclass 
> = ''group1'' ORDER BY 1,2;') AS c(rowid text, att1 text, att2 text, att3 
> text);
> !  rowid | att1 | att2 | att3 
> ! -------+------+------+------
> !  test1 | val1 | val2 | val3
> !  test2 | val5 | val6 | val7
> ! (2 rows)
>   
>   SELECT * FROM crosstab('SELECT rowid, attribute, val FROM ct where rowclass 
> = ''group1'' ORDER BY 1,2;') AS c(rowid text, att1 text, att2 text, att3 
> text, att4 text);
> !  rowid | att1 | att2 | att3 | att4 
> ! -------+------+------+------+------
> !  test1 | val1 | val2 | val3 | val4
> !  test2 | val5 | val6 | val7 | val8
> ! (2 rows)
>   
>   -- check it works with OUT parameters, too
>   CREATE FUNCTION crosstab_out(text,
> --- 109,136 ----
>   (2 rows)
>   
>   SELECT * FROM crosstab('SELECT rowid, attribute, val FROM ct where rowclass 
> = ''group1'' ORDER BY 1,2;') AS c(rowid text, att1 text, att2 text);
> !  rowid | att1 | att2  
> ! -------+------+-------
>    test1 | val1 | val2
>    test2 | val5 | val6
> !        | val9 | val10
> ! (3 rows)
>   
>   SELECT * FROM crosstab('SELECT rowid, attribute, val FROM ct where rowclass 
> = ''group1'' ORDER BY 1,2;') AS c(rowid text, att1 text, att2 text, att3 
> text);
> !  rowid | att1 | att2  | att3  
> ! -------+------+-------+-------
> !  test1 | val1 | val2  | val3
> !  test2 | val5 | val6  | val7
> !        | val9 | val10 | val11
> ! (3 rows)
>   
>   SELECT * FROM crosstab('SELECT rowid, attribute, val FROM ct where rowclass 
> = ''group1'' ORDER BY 1,2;') AS c(rowid text, att1 text, att2 text, att3 
> text, att4 text);
> !  rowid | att1 | att2  | att3  | att4  
> ! -------+------+-------+-------+-------
> !  test1 | val1 | val2  | val3  | val4
> !  test2 | val5 | val6  | val7  | val8
> !        | val9 | val10 | val11 | val12
> ! (3 rows)
>   
>   -- check it works with OUT parameters, too
>   CREATE FUNCTION crosstab_out(text,
> ***************
> *** 130,140 ****
>   AS '$libdir/tablefunc','crosstab'
>   LANGUAGE C STABLE STRICT;
>   SELECT * FROM crosstab_out('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group1'' ORDER BY 1,2;');
> !  rowid | att1 | att2 | att3 
> ! -------+------+------+------
> !  test1 | val1 | val2 | val3
> !  test2 | val5 | val6 | val7
> ! (2 rows)
>   
>   --
>   -- hash based crosstab
> --- 139,150 ----
>   AS '$libdir/tablefunc','crosstab'
>   LANGUAGE C STABLE STRICT;
>   SELECT * FROM crosstab_out('SELECT rowid, attribute, val FROM ct where 
> rowclass = ''group1'' ORDER BY 1,2;');
> !  rowid | att1 | att2  | att3  
> ! -------+------+-------+-------
> !  test1 | val1 | val2  | val3
> !  test2 | val5 | val6  | val7
> !        | val9 | val10 | val11
> ! (3 rows)
>   
>   --
>   -- hash based crosstab
> ***************
> *** 150,187 ****
>   insert into cth values(DEFAULT,'test2','02 March 
> 2003','test_result','FAIL');
>   insert into cth values(DEFAULT,'test2','02 March 2003','test_startdate','01 
> March 2003');
>   insert into cth values(DEFAULT,'test2','02 March 2003','volts','3.1234');
>   -- return attributes as plain text
>   SELECT * FROM crosstab(
>     'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
>     'SELECT DISTINCT attribute FROM cth ORDER BY 1')
>   AS c(rowid text, rowdt timestamp, temperature text, test_result text, 
> test_startdate text, volts text);
> !  rowid |          rowdt           | temperature | test_result | 
> test_startdate | volts  
> ! 
> -------+--------------------------+-------------+-------------+----------------+--------
> !  test1 | Sat Mar 01 00:00:00 2003 | 42          | PASS        |             
>    | 2.6987
> !  test2 | Sun Mar 02 00:00:00 2003 | 53          | FAIL        | 01 March 
> 2003  | 3.1234
> ! (2 rows)
>   
>   -- this time without rowdt
>   SELECT * FROM crosstab(
>     'SELECT rowid, attribute, val FROM cth ORDER BY 1',
>     'SELECT DISTINCT attribute FROM cth ORDER BY 1')
>   AS c(rowid text, temperature text, test_result text, test_startdate text, 
> volts text);
> !  rowid | temperature | test_result | test_startdate | volts  
> ! -------+-------------+-------------+----------------+--------
> !  test1 | 42          | PASS        |                | 2.6987
> !  test2 | 53          | FAIL        | 01 March 2003  | 3.1234
> ! (2 rows)
>   
>   -- convert attributes to specific datatypes
>   SELECT * FROM crosstab(
>     'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
>     'SELECT DISTINCT attribute FROM cth ORDER BY 1')
>   AS c(rowid text, rowdt timestamp, temperature int4, test_result text, 
> test_startdate timestamp, volts float8);
> !  rowid |          rowdt           | temperature | test_result |      
> test_startdate      | volts  
> ! 
> -------+--------------------------+-------------+-------------+--------------------------+--------
> !  test1 | Sat Mar 01 00:00:00 2003 |          42 | PASS        |             
>              | 2.6987
> !  test2 | Sun Mar 02 00:00:00 2003 |          53 | FAIL        | Sat Mar 01 
> 00:00:00 2003 | 3.1234
> ! (2 rows)
>   
>   -- source query and category query out of sync
>   SELECT * FROM crosstab(
> --- 160,205 ----
>   insert into cth values(DEFAULT,'test2','02 March 
> 2003','test_result','FAIL');
>   insert into cth values(DEFAULT,'test2','02 March 2003','test_startdate','01 
> March 2003');
>   insert into cth values(DEFAULT,'test2','02 March 2003','volts','3.1234');
> + -- next group tests for NULL rowids
> + insert into cth values(DEFAULT,NULL,'25 October 2007','temperature','57');
> + insert into cth values(DEFAULT,NULL,'25 October 2007','test_result','PASS');
> + insert into cth values(DEFAULT,NULL,'25 October 2007','test_startdate','24 
> October 2007');
> + insert into cth values(DEFAULT,NULL,'25 October 2007','volts','1.41234');
>   -- return attributes as plain text
>   SELECT * FROM crosstab(
>     'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
>     'SELECT DISTINCT attribute FROM cth ORDER BY 1')
>   AS c(rowid text, rowdt timestamp, temperature text, test_result text, 
> test_startdate text, volts text);
> !  rowid |          rowdt           | temperature | test_result | 
> test_startdate  |  volts  
> ! 
> -------+--------------------------+-------------+-------------+-----------------+---------
> !  test1 | Sat Mar 01 00:00:00 2003 | 42          | PASS        |             
>     | 2.6987
> !  test2 | Sun Mar 02 00:00:00 2003 | 53          | FAIL        | 01 March 
> 2003   | 3.1234
> !        | Thu Oct 25 00:00:00 2007 | 57          | PASS        | 24 October 
> 2007 | 1.41234
> ! (3 rows)
>   
>   -- this time without rowdt
>   SELECT * FROM crosstab(
>     'SELECT rowid, attribute, val FROM cth ORDER BY 1',
>     'SELECT DISTINCT attribute FROM cth ORDER BY 1')
>   AS c(rowid text, temperature text, test_result text, test_startdate text, 
> volts text);
> !  rowid | temperature | test_result | test_startdate  |  volts  
> ! -------+-------------+-------------+-----------------+---------
> !  test1 | 42          | PASS        |                 | 2.6987
> !  test2 | 53          | FAIL        | 01 March 2003   | 3.1234
> !        | 57          | PASS        | 24 October 2007 | 1.41234
> ! (3 rows)
>   
>   -- convert attributes to specific datatypes
>   SELECT * FROM crosstab(
>     'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
>     'SELECT DISTINCT attribute FROM cth ORDER BY 1')
>   AS c(rowid text, rowdt timestamp, temperature int4, test_result text, 
> test_startdate timestamp, volts float8);
> !  rowid |          rowdt           | temperature | test_result |      
> test_startdate      |  volts  
> ! 
> -------+--------------------------+-------------+-------------+--------------------------+---------
> !  test1 | Sat Mar 01 00:00:00 2003 |          42 | PASS        |             
>              |  2.6987
> !  test2 | Sun Mar 02 00:00:00 2003 |          53 | FAIL        | Sat Mar 01 
> 00:00:00 2003 |  3.1234
> !        | Thu Oct 25 00:00:00 2007 |          57 | PASS        | Wed Oct 24 
> 00:00:00 2007 | 1.41234
> ! (3 rows)
>   
>   -- source query and category query out of sync
>   SELECT * FROM crosstab(
> ***************
> *** 192,198 ****
>   
> -------+--------------------------+-------------+-------------+--------------------------
>    test1 | Sat Mar 01 00:00:00 2003 |          42 | PASS        | 
>    test2 | Sun Mar 02 00:00:00 2003 |          53 | FAIL        | Sat Mar 01 
> 00:00:00 2003
> ! (2 rows)
>   
>   -- if category query generates no rows, get expected error
>   SELECT * FROM crosstab(
> --- 210,217 ----
>   
> -------+--------------------------+-------------+-------------+--------------------------
>    test1 | Sat Mar 01 00:00:00 2003 |          42 | PASS        | 
>    test2 | Sun Mar 02 00:00:00 2003 |          53 | FAIL        | Sat Mar 01 
> 00:00:00 2003
> !        | Thu Oct 25 00:00:00 2007 |          57 | PASS        | Wed Oct 24 
> 00:00:00 2007
> ! (3 rows)
>   
>   -- if category query generates no rows, get expected error
>   SELECT * FROM crosstab(
> ***************
> *** 235,245 ****
>   SELECT * FROM crosstab_named(
>     'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
>     'SELECT DISTINCT attribute FROM cth ORDER BY 1');
> !  rowid |          rowdt           | temperature | test_result |      
> test_startdate      | volts  
> ! 
> -------+--------------------------+-------------+-------------+--------------------------+--------
> !  test1 | Sat Mar 01 00:00:00 2003 |          42 | PASS        |             
>              | 2.6987
> !  test2 | Sun Mar 02 00:00:00 2003 |          53 | FAIL        | Sat Mar 01 
> 00:00:00 2003 | 3.1234
> ! (2 rows)
>   
>   -- check it works with OUT parameters
>   CREATE FUNCTION crosstab_out(text, text,
> --- 254,265 ----
>   SELECT * FROM crosstab_named(
>     'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
>     'SELECT DISTINCT attribute FROM cth ORDER BY 1');
> !  rowid |          rowdt           | temperature | test_result |      
> test_startdate      |  volts  
> ! 
> -------+--------------------------+-------------+-------------+--------------------------+---------
> !  test1 | Sat Mar 01 00:00:00 2003 |          42 | PASS        |             
>              |  2.6987
> !  test2 | Sun Mar 02 00:00:00 2003 |          53 | FAIL        | Sat Mar 01 
> 00:00:00 2003 |  3.1234
> !        | Thu Oct 25 00:00:00 2007 |          57 | PASS        | Wed Oct 24 
> 00:00:00 2007 | 1.41234
> ! (3 rows)
>   
>   -- check it works with OUT parameters
>   CREATE FUNCTION crosstab_out(text, text,
> ***************
> *** 252,262 ****
>   SELECT * FROM crosstab_out(
>     'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
>     'SELECT DISTINCT attribute FROM cth ORDER BY 1');
> !  rowid |          rowdt           | temperature | test_result |      
> test_startdate      | volts  
> ! 
> -------+--------------------------+-------------+-------------+--------------------------+--------
> !  test1 | Sat Mar 01 00:00:00 2003 |          42 | PASS        |             
>              | 2.6987
> !  test2 | Sun Mar 02 00:00:00 2003 |          53 | FAIL        | Sat Mar 01 
> 00:00:00 2003 | 3.1234
> ! (2 rows)
>   
>   --
>   -- connectby
> --- 272,283 ----
>   SELECT * FROM crosstab_out(
>     'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
>     'SELECT DISTINCT attribute FROM cth ORDER BY 1');
> !  rowid |          rowdt           | temperature | test_result |      
> test_startdate      |  volts  
> ! 
> -------+--------------------------+-------------+-------------+--------------------------+---------
> !  test1 | Sat Mar 01 00:00:00 2003 |          42 | PASS        |             
>              |  2.6987
> !  test2 | Sun Mar 02 00:00:00 2003 |          53 | FAIL        | Sat Mar 01 
> 00:00:00 2003 |  3.1234
> !        | Thu Oct 25 00:00:00 2007 |          57 | PASS        | Wed Oct 24 
> 00:00:00 2007 | 1.41234
> ! (3 rows)
>   
>   --
>   -- connectby
> Index: sql/tablefunc.sql
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/contrib/tablefunc/sql/tablefunc.sql,v
> retrieving revision 1.12
> diff -c -r1.12 tablefunc.sql
> *** sql/tablefunc.sql 27 Feb 2006 16:09:49 -0000      1.12
> --- sql/tablefunc.sql 25 Oct 2007 22:20:09 -0000
> ***************
> *** 61,66 ****
> --- 61,71 ----
>   insert into cth values(DEFAULT,'test2','02 March 
> 2003','test_result','FAIL');
>   insert into cth values(DEFAULT,'test2','02 March 2003','test_startdate','01 
> March 2003');
>   insert into cth values(DEFAULT,'test2','02 March 2003','volts','3.1234');
> + -- next group tests for NULL rowids
> + insert into cth values(DEFAULT,NULL,'25 October 2007','temperature','57');
> + insert into cth values(DEFAULT,NULL,'25 October 2007','test_result','PASS');
> + insert into cth values(DEFAULT,NULL,'25 October 2007','test_startdate','24 
> October 2007');
> + insert into cth values(DEFAULT,NULL,'25 October 2007','volts','1.41234');
>   
>   -- return attributes as plain text
>   SELECT * FROM crosstab(

> 
> ---------------------------(end of broadcast)---------------------------
> TIP 4: Have you searched our list archives?
> 
>                http://archives.postgresql.org

-- 
  Bruce Momjian  <[EMAIL PROTECTED]>        http://momjian.us
  EnterpriseDB                             http://postgres.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

---------------------------(end of broadcast)---------------------------
TIP 2: Don't 'kill -9' the postmaster

Reply via email to