Alvaro Herrera wrote:
> I messed with that code some more, as it looked unnecessarily
> complicated; please see attached and verify that it still behaves
> sanely. This needs those regression tests you promised. I tested a few
> cases and it seems good to me.
I've fixed a couple things over v16:
- avoid passing every cell through psprintf, which happened due
to cont.cells being pre-initialized to empty strings.
- adjusted the loop freeing allocated_cells
and added the regression tests.
Attached is the diff over v16, tested with make check and valgrind.
Best regards,
--
Daniel Vérité
PostgreSQL-powered mailer: http://www.manitou-mail.org
Twitter: @DanielVerite
diff --git a/src/bin/psql/crosstabview.c b/src/bin/psql/crosstabview.c
index 0d70e47..a20296e 100644
--- a/src/bin/psql/crosstabview.c
+++ b/src/bin/psql/crosstabview.c
@@ -360,7 +360,6 @@ printCrosstab(const PGresult *results,
printQueryOpt popt = pset.popt;
printTableContent cont;
int i,
- j,
rn;
char col_align;
int *horiz_map;
@@ -414,9 +413,6 @@ printCrosstab(const PGresult *results,
cont.cells[k * (num_columns + 1)] = piv_rows[i].name ?
piv_rows[i].name :
(popt.nullPrint ? popt.nullPrint : "");
- /* Initialize all cells inside the grid to an empty value */
- for (j = 0; j < num_columns; j++)
- cont.cells[k * (num_columns + 1) + j + 1] = "";
}
cont.cellsadded = num_rows * (num_columns + 1);
@@ -506,14 +502,10 @@ printCrosstab(const PGresult *results,
* first column of each row, separate with a newline
* instead.
*/
- if (allocated_cells[idx] != NULL)
- new_content = psprintf("%s%s%s",
- allocated_cells[idx],
- i == 0 ? "\n" : " ",
- content);
- else
- new_content = psprintf("%s", content);
-
+ new_content = psprintf("%s%s%s",
+ cont.cells[idx],
+ i == 0 ? "\n" : " ",
+ content);
cont.cells[idx] = new_content;
if (allocated_cells[idx] != NULL)
pg_free(allocated_cells[idx]);
@@ -528,10 +520,20 @@ printCrosstab(const PGresult *results,
}
}
+ /*
+ * The non-initialized cells must be set to an empty string for the print
+ * functions
+ */
+ for (i = 0; i < cont.cellsadded; i++)
+ {
+ if (cont.cells[i] == NULL)
+ cont.cells[i] = "";
+ }
+
printTable(&cont, pset.queryFout, false, pset.logfile);
printTableCleanup(&cont);
- for (i = 0; i < num_rows * num_columns; i++)
+ for (i = 0; i < (num_rows + 1) * (num_columns + 1); i++)
{
if (allocated_cells[i] != NULL)
pg_free(allocated_cells[i]);
diff --git a/src/test/regress/expected/psql_crosstabview.out b/src/test/regress/expected/psql_crosstabview.out
new file mode 100644
index 0000000..df3824a
--- /dev/null
+++ b/src/test/regress/expected/psql_crosstabview.out
@@ -0,0 +1,158 @@
+--
+-- tests for \crosstabview
+--
+CREATE VIEW vct_data as
+select * from ( values
+ ('v1','h2','foo', 3, '2015-04-01'::date),
+ ('v2','h1','bar', 3, '2015-01-02'),
+ ('v1','h0','baz', NULL, '2015-07-12'),
+ ('v0','h4','qux', 4, '2015-07-15'),
+ ('v0','h4','dbl', -3, '2014-12-15'),
+ ('v0',NULL,'qux', 5, '2014-03-15')
+ ) as l(v,h,c,i,d);
+-- 2 columns with implicit 'X' as 3rd column
+select v,i from vct_data order by 1,2 \crosstabview v i
+ v | -3 | 4 | 5 | 3 |
+----+----+---+---+---+---
+ v0 | X | X | X | |
+ v1 | | | | X | X
+ v2 | | | | X |
+(3 rows)
+
+-- basic usage with 3 columns
+select v, extract(year from d),count(*) from vct_data
+ group by 1, 2 order by 1,2
+ \crosstabview
+ v | 2014 | 2015
+----+------+------
+ v0 | 2 | 1
+ v1 | | 2
+ v2 | | 1
+(3 rows)
+
+-- ordered months in horizontal header, enclosed column name
+select v, to_char(d,'Mon') as "month name", extract(month from d) as num,
+ count(*) from vct_data group by 1,2,3 order by 1
+ \crosstabview v "month name":num 4
+ v | Jan | Mar | Apr | Jul | Dec
+----+-----+-----+-----+-----+-----
+ v0 | | 1 | | 1 | 1
+ v1 | | | 1 | 1 |
+ v2 | 1 | | | |
+(3 rows)
+
+-- combine contents vertically into the same cell (V/H duplicates)
+select v,h,c from vct_data order by 1,2,3
+ \crosstabview 1 2 3
+ v | h4 | | h0 | h2 | h1
+----+-----+-----+-----+-----+-----
+ v0 | dbl+| qux | | |
+ | qux | | | |
+ v1 | | | baz | foo |
+ v2 | | | | | bar
+(3 rows)
+
+-- horizontal ASC order from window function
+select v,h,c, row_number() over(order by h) as r from vct_data order by 1,3,2
+ \crosstabview v h:r c
+ v | h0 | h1 | h2 | h4 |
+----+-----+-----+-----+-----+-----
+ v0 | | | | dbl+| qux
+ | | | | qux |
+ v1 | baz | | foo | |
+ v2 | | bar | | |
+(3 rows)
+
+-- horizontal DESC order from window function
+select v,h,c, row_number() over(order by h DESC) as r from vct_data order by 1,3,2
+ \crosstabview v h:r c
+ v | | h4 | h2 | h1 | h0
+----+-----+-----+-----+-----+-----
+ v0 | qux | dbl+| | |
+ | | qux | | |
+ v1 | | | foo | | baz
+ v2 | | | | bar |
+(3 rows)
+
+-- horizontal ASC order from window function, NULLs pushed rightmost
+select v,h,c, row_number() over(order by h nulls last) as r from vct_data order by 1,3,2
+ \crosstabview v h:r c
+ v | h0 | h1 | h2 | h4 |
+----+-----+-----+-----+-----+-----
+ v0 | | | | dbl+| qux
+ | | | | qux |
+ v1 | baz | | foo | |
+ v2 | | bar | | |
+(3 rows)
+
+-- only null, no column name, 2 columns
+select null,null \crosstabview
+ ?column? |
+----------+---
+ | X
+(1 row)
+
+-- only null, no column name, 3 columns
+select null,null,null \crosstabview
+ ?column? |
+----------+--
+ |
+(1 row)
+
+-- null combined with cell contents
+\pset null '#null#'
+select v,h,c,i from vct_data order by h,v
+ \crosstabview
+ v | h0 | h1 | h2 | h4 | #null#
+----+------------+-------+-------+--------+--------
+ v1 | baz #null# | | foo 3 | |
+ v2 | | bar 3 | | |
+ v0 | | | | qux 4 +| qux 5
+ | | | | dbl -3 |
+(3 rows)
+
+\pset null ''
+-- refer to columns by position
+select v,h,i,c from vct_data order by h,v
+ \crosstabview 2 1 4
+ h | v1 | v2 | v0
+----+-----+-----+-----
+ h0 | baz | |
+ h1 | | bar |
+ h2 | foo | |
+ h4 | | | qux+
+ | | | dbl
+ | | | qux
+(5 rows)
+
+-- refer to columns by positions and names mixed
+select v,h,i,c from vct_data order by h,v
+ \crosstabview 1 "h" 4
+ v | h0 | h1 | h2 | h4 |
+----+-----+-----+-----+-----+-----
+ v1 | baz | | foo | |
+ v2 | | bar | | |
+ v0 | | | | qux+| qux
+ | | | | dbl |
+(3 rows)
+
+-- error: bad column name
+select v,h,c,i from vct_data
+ \crosstabview v h j
+Invalid column name: j
+-- error: bad column number
+select v,h,i,c from vct_data
+ \crosstabview 2 1 5
+Invalid column number: 5
+-- error: same H and V columns
+select v,h,i,c from vct_data
+ \crosstabview 2 h 4
+The same column cannot be used for both vertical and horizontal headers
+-- error: too many columns
+select a,a,1 from generate_series(1,3000) as a
+ \crosstabview
+Maximum number of columns (1600) exceeded
+-- error: only one column
+select 1 \crosstabview
+The query must return at least two columns to be shown in crosstab
+DROP VIEW vct_data;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 7c7b58d..a398c6b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -89,7 +89,7 @@ test: brin gin gist spgist privileges security_label collate matview lock replic
# ----------
# Another group of parallel tests
# ----------
-test: alter_generic alter_operator misc psql async dbsize misc_functions
+test: alter_generic alter_operator misc psql psql_crosstabview async dbsize misc_functions
# rules cannot run concurrently with any test that creates a view
test: rules
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 1b66516..8cbffe6 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -119,6 +119,7 @@ test: alter_generic
test: alter_operator
test: misc
test: psql
+test: psql_crosstabview
test: async
test: dbsize
test: misc_functions
diff --git a/src/test/regress/sql/psql_crosstabview.sql b/src/test/regress/sql/psql_crosstabview.sql
new file mode 100644
index 0000000..48a7fe1
--- /dev/null
+++ b/src/test/regress/sql/psql_crosstabview.sql
@@ -0,0 +1,83 @@
+--
+-- tests for \crosstabview
+--
+
+CREATE VIEW vct_data as
+select * from ( values
+ ('v1','h2','foo', 3, '2015-04-01'::date),
+ ('v2','h1','bar', 3, '2015-01-02'),
+ ('v1','h0','baz', NULL, '2015-07-12'),
+ ('v0','h4','qux', 4, '2015-07-15'),
+ ('v0','h4','dbl', -3, '2014-12-15'),
+ ('v0',NULL,'qux', 5, '2014-03-15')
+ ) as l(v,h,c,i,d);
+
+-- 2 columns with implicit 'X' as 3rd column
+select v,i from vct_data order by 1,2 \crosstabview v i
+
+-- basic usage with 3 columns
+select v, extract(year from d),count(*) from vct_data
+ group by 1, 2 order by 1,2
+ \crosstabview
+
+-- ordered months in horizontal header, enclosed column name
+select v, to_char(d,'Mon') as "month name", extract(month from d) as num,
+ count(*) from vct_data group by 1,2,3 order by 1
+ \crosstabview v "month name":num 4
+
+-- combine contents vertically into the same cell (V/H duplicates)
+select v,h,c from vct_data order by 1,2,3
+ \crosstabview 1 2 3
+
+-- horizontal ASC order from window function
+select v,h,c, row_number() over(order by h) as r from vct_data order by 1,3,2
+ \crosstabview v h:r c
+
+-- horizontal DESC order from window function
+select v,h,c, row_number() over(order by h DESC) as r from vct_data order by 1,3,2
+ \crosstabview v h:r c
+
+-- horizontal ASC order from window function, NULLs pushed rightmost
+select v,h,c, row_number() over(order by h nulls last) as r from vct_data order by 1,3,2
+ \crosstabview v h:r c
+
+-- only null, no column name, 2 columns
+select null,null \crosstabview
+
+-- only null, no column name, 3 columns
+select null,null,null \crosstabview
+
+-- null combined with cell contents
+\pset null '#null#'
+select v,h,c,i from vct_data order by h,v
+ \crosstabview
+\pset null ''
+
+-- refer to columns by position
+select v,h,i,c from vct_data order by h,v
+ \crosstabview 2 1 4
+
+-- refer to columns by positions and names mixed
+select v,h,i,c from vct_data order by h,v
+ \crosstabview 1 "h" 4
+
+-- error: bad column name
+select v,h,c,i from vct_data
+ \crosstabview v h j
+
+-- error: bad column number
+select v,h,i,c from vct_data
+ \crosstabview 2 1 5
+
+-- error: same H and V columns
+select v,h,i,c from vct_data
+ \crosstabview 2 h 4
+
+-- error: too many columns
+select a,a,1 from generate_series(1,3000) as a
+ \crosstabview
+
+-- error: only one column
+select 1 \crosstabview
+
+DROP VIEW vct_data;
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers