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
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