Commit 6f0d6a50 authored by Tom Lane's avatar Tom Lane

Rethink \crosstabview's argument parsing logic.

\crosstabview interpreted its arguments in an unusual way, including
doing case-insensitive matching of unquoted column names, which is
surely not the right thing.  Rip that out in favor of doing something
equivalent to the dequoting/case-folding rules used by other psql
commands.  To keep it simple, change the syntax so that the optional
sort column is specified as a separate argument, instead of the
also-quite-unusual syntax that attached it to the colH argument with
a colon.

Also, rework the error messages to be closer to project style.
parent 4b74c6a4
...@@ -993,9 +993,10 @@ testdb=> ...@@ -993,9 +993,10 @@ testdb=>
<varlistentry id="APP-PSQL-meta-commands-crosstabview"> <varlistentry id="APP-PSQL-meta-commands-crosstabview">
<term><literal>\crosstabview [ <term><literal>\crosstabview [
<replaceable class="parameter">colV</replaceable> <replaceable class="parameter">colV</replaceable>
<replaceable class="parameter">colH</replaceable>[:<replaceable class="parameter">scolH</replaceable>] [ <replaceable class="parameter">colH</replaceable>
[<replaceable class="parameter">colD</replaceable>] [ <replaceable class="parameter">colD</replaceable>
] </literal></term> [ <replaceable class="parameter">sortcolH</replaceable>
] ] ] ] </literal></term>
<listitem> <listitem>
<para> <para>
Executes the current query buffer (like <literal>\g</literal>) and Executes the current query buffer (like <literal>\g</literal>) and
...@@ -1004,16 +1005,11 @@ testdb=&gt; ...@@ -1004,16 +1005,11 @@ testdb=&gt;
The output column identified by <replaceable class="parameter">colV</> The output column identified by <replaceable class="parameter">colV</>
becomes a vertical header and the output column identified by becomes a vertical header and the output column identified by
<replaceable class="parameter">colH</replaceable> <replaceable class="parameter">colH</replaceable>
becomes a horizontal header, optionally sorted by ranking data obtained becomes a horizontal header.
from column <replaceable class="parameter">scolH</replaceable>.
<replaceable class="parameter">colD</replaceable> identifies <replaceable class="parameter">colD</replaceable> identifies
the output column to display within the grid. the output column to display within the grid.
If <replaceable class="parameter">colD</replaceable> is not <replaceable class="parameter">sortcolH</replaceable> identifies
specified and there are exactly three columns in the result set, an optional sort column for the horizontal header.
the column that is neither
<replaceable class="parameter">colV</replaceable> nor
<replaceable class="parameter">colH</replaceable>
is displayed; if there are more columns, an error is reported.
</para> </para>
<para> <para>
...@@ -1024,6 +1020,12 @@ testdb=&gt; ...@@ -1024,6 +1020,12 @@ testdb=&gt;
and <replaceable class="parameter">colH</replaceable> as column 2. and <replaceable class="parameter">colH</replaceable> as column 2.
<replaceable class="parameter">colH</replaceable> must differ from <replaceable class="parameter">colH</replaceable> must differ from
<replaceable class="parameter">colV</replaceable>. <replaceable class="parameter">colV</replaceable>.
If <replaceable class="parameter">colD</replaceable> is not
specified, then there must be exactly three columns in the query
result, and the column that is neither
<replaceable class="parameter">colV</replaceable> nor
<replaceable class="parameter">colH</replaceable>
is taken to be <replaceable class="parameter">colD</replaceable>.
</para> </para>
<para> <para>
...@@ -1037,11 +1039,11 @@ testdb=&gt; ...@@ -1037,11 +1039,11 @@ testdb=&gt;
found in column <replaceable class="parameter">colH</replaceable>, found in column <replaceable class="parameter">colH</replaceable>,
with duplicates removed. By default, these appear in the same order with duplicates removed. By default, these appear in the same order
as in the query results. But if the as in the query results. But if the
optional <replaceable class="parameter">scolH</> argument is given, it optional <replaceable class="parameter">sortcolH</> argument is given,
identifies a column whose values must be integer numbers, and the it identifies a column whose values must be integer numbers, and the
values from <replaceable class="parameter">colH</replaceable> will values from <replaceable class="parameter">colH</replaceable> will
appear in the horizontal header sorted according to the appear in the horizontal header sorted according to the
corresponding <replaceable class="parameter">scolH</> values. corresponding <replaceable class="parameter">sortcolH</> values.
</para> </para>
<para> <para>
......
...@@ -368,13 +368,11 @@ exec_command(const char *cmd, ...@@ -368,13 +368,11 @@ exec_command(const char *cmd,
/* \crosstabview -- execute a query and display results in crosstab */ /* \crosstabview -- execute a query and display results in crosstab */
else if (strcmp(cmd, "crosstabview") == 0) else if (strcmp(cmd, "crosstabview") == 0)
{ {
pset.ctv_col_V = psql_scan_slash_option(scan_state, int i;
OT_NORMAL, NULL, false);
pset.ctv_col_H = psql_scan_slash_option(scan_state,
OT_NORMAL, NULL, false);
pset.ctv_col_D = psql_scan_slash_option(scan_state,
OT_NORMAL, NULL, false);
for (i = 0; i < lengthof(pset.ctv_args); i++)
pset.ctv_args[i] = psql_scan_slash_option(scan_state,
OT_NORMAL, NULL, true);
pset.crosstab_flag = true; pset.crosstab_flag = true;
status = PSQL_CMD_SEND; status = PSQL_CMD_SEND;
} }
......
...@@ -1130,6 +1130,7 @@ SendQuery(const char *query) ...@@ -1130,6 +1130,7 @@ SendQuery(const char *query)
PGTransactionStatusType transaction_status; PGTransactionStatusType transaction_status;
double elapsed_msec = 0; double elapsed_msec = 0;
bool OK = false; bool OK = false;
int i;
bool on_error_rollback_savepoint = false; bool on_error_rollback_savepoint = false;
static bool on_error_rollback_warning = false; static bool on_error_rollback_warning = false;
...@@ -1362,20 +1363,10 @@ sendquery_cleanup: ...@@ -1362,20 +1363,10 @@ sendquery_cleanup:
/* reset \crosstabview trigger */ /* reset \crosstabview trigger */
pset.crosstab_flag = false; pset.crosstab_flag = false;
if (pset.ctv_col_V) for (i = 0; i < lengthof(pset.ctv_args); i++)
{
free(pset.ctv_col_V);
pset.ctv_col_V = NULL;
}
if (pset.ctv_col_H)
{
free(pset.ctv_col_H);
pset.ctv_col_H = NULL;
}
if (pset.ctv_col_D)
{ {
free(pset.ctv_col_D); pg_free(pset.ctv_args[i]);
pset.ctv_col_D = NULL; pset.ctv_args[i] = NULL;
} }
return OK; return OK;
......
This diff is collapsed.
...@@ -94,9 +94,7 @@ typedef struct _psqlSettings ...@@ -94,9 +94,7 @@ typedef struct _psqlSettings
char *gset_prefix; /* one-shot prefix argument for \gset */ char *gset_prefix; /* one-shot prefix argument for \gset */
bool gexec_flag; /* one-shot flag to execute query's results */ bool gexec_flag; /* one-shot flag to execute query's results */
bool crosstab_flag; /* one-shot request to crosstab results */ bool crosstab_flag; /* one-shot request to crosstab results */
char *ctv_col_V; /* \crosstabview 1st argument */ char *ctv_args[4]; /* \crosstabview arguments */
char *ctv_col_H; /* \crosstabview 2nd argument */
char *ctv_col_D; /* \crosstabview 3nd argument */
bool notty; /* stdin or stdout is not a tty (as determined bool notty; /* stdin or stdout is not a tty (as determined
* on startup) */ * on startup) */
......
...@@ -35,7 +35,7 @@ SELECT v, EXTRACT(year FROM d), count(*) ...@@ -35,7 +35,7 @@ SELECT v, EXTRACT(year FROM d), count(*)
-- ordered months in horizontal header, quoted column name -- ordered months in horizontal header, quoted column name
SELECT v, to_char(d, 'Mon') AS "month name", EXTRACT(month FROM d) AS num, SELECT v, to_char(d, 'Mon') AS "month name", EXTRACT(month FROM d) AS num,
count(*) FROM ctv_data GROUP BY 1,2,3 ORDER BY 1 count(*) FROM ctv_data GROUP BY 1,2,3 ORDER BY 1
\crosstabview v "month name":num 4 \crosstabview v "month name" 4 num
v | Jan | Apr | Jul | Dec v | Jan | Apr | Jul | Dec
----+-----+-----+-----+----- ----+-----+-----+-----+-----
v0 | | | 2 | 1 v0 | | | 2 | 1
...@@ -50,7 +50,7 @@ SELECT EXTRACT(year FROM d) AS year, to_char(d,'Mon') AS "month name", ...@@ -50,7 +50,7 @@ SELECT EXTRACT(year FROM d) AS year, to_char(d,'Mon') AS "month name",
FROM ctv_data FROM ctv_data
GROUP BY EXTRACT(year FROM d), to_char(d,'Mon'), EXTRACT(month FROM d) GROUP BY EXTRACT(year FROM d), to_char(d,'Mon'), EXTRACT(month FROM d)
ORDER BY month ORDER BY month
\crosstabview "month name" year:year format \crosstabview "month name" year format year
month name | 2014 | 2015 month name | 2014 | 2015
------------+-----------------+---------------- ------------+-----------------+----------------
Jan | | sum=3 avg=3.0 Jan | | sum=3 avg=3.0
...@@ -74,7 +74,7 @@ SELECT v, h, string_agg(c, E'\n') FROM ctv_data GROUP BY v, h ORDER BY 1,2,3 ...@@ -74,7 +74,7 @@ SELECT v, h, string_agg(c, E'\n') FROM ctv_data GROUP BY v, h ORDER BY 1,2,3
-- horizontal ASC order from window function -- horizontal ASC order from window function
SELECT v,h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h) AS r SELECT v,h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h) AS r
FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 FROM ctv_data GROUP BY v, h ORDER BY 1,3,2
\crosstabview v h:r c \crosstabview v h c r
v | h0 | h1 | h2 | h4 | v | h0 | h1 | h2 | h4 |
----+-----+-----+------+-----+----- ----+-----+-----+------+-----+-----
v0 | | | | qux+| qux v0 | | | | qux+| qux
...@@ -87,7 +87,7 @@ FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 ...@@ -87,7 +87,7 @@ FROM ctv_data GROUP BY v, h ORDER BY 1,3,2
-- horizontal DESC order from window function -- horizontal DESC order from window function
SELECT v, h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h DESC) AS r SELECT v, h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h DESC) AS r
FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 FROM ctv_data GROUP BY v, h ORDER BY 1,3,2
\crosstabview v h:r c \crosstabview v h c r
v | | h4 | h2 | h1 | h0 v | | h4 | h2 | h1 | h0
----+-----+-----+------+-----+----- ----+-----+-----+------+-----+-----
v0 | qux | qux+| | | v0 | qux | qux+| | |
...@@ -100,7 +100,7 @@ FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 ...@@ -100,7 +100,7 @@ FROM ctv_data GROUP BY v, h ORDER BY 1,3,2
-- horizontal ASC order from window function, NULLs pushed rightmost -- horizontal ASC order from window function, NULLs pushed rightmost
SELECT v,h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h NULLS LAST) AS r SELECT v,h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h NULLS LAST) AS r
FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 FROM ctv_data GROUP BY v, h ORDER BY 1,3,2
\crosstabview v h:r c \crosstabview v h c r
v | h0 | h1 | h2 | h4 | v | h0 | h1 | h2 | h4 |
----+-----+-----+------+-----+----- ----+-----+-----+------+-----+-----
v0 | | | | qux+| qux v0 | | | | qux+| qux
...@@ -112,7 +112,7 @@ FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 ...@@ -112,7 +112,7 @@ FROM ctv_data GROUP BY v, h ORDER BY 1,3,2
-- only null, no column name, 2 columns: error -- only null, no column name, 2 columns: error
SELECT null,null \crosstabview SELECT null,null \crosstabview
The query must return at least two columns to be shown in crosstab \crosstabview: query must return at least three columns
-- only null, no column name, 3 columns: works -- only null, no column name, 3 columns: works
SELECT null,null,null \crosstabview SELECT null,null,null \crosstabview
?column? | ?column? |
...@@ -163,23 +163,39 @@ FROM ctv_data GROUP BY v, h ORDER BY h,v ...@@ -163,23 +163,39 @@ FROM ctv_data GROUP BY v, h ORDER BY h,v
| | | | dbl | | | | | dbl |
(3 rows) (3 rows)
-- refer to columns by quoted names, check downcasing of unquoted name
SELECT 1 as "22", 2 as b, 3 as "Foo"
\crosstabview "22" B "Foo"
22 | 2
----+---
1 | 3
(1 row)
-- error: bad column name -- error: bad column name
SELECT v,h,c,i FROM ctv_data SELECT v,h,c,i FROM ctv_data
\crosstabview v h j \crosstabview v h j
Invalid column name: j \crosstabview: column name not found: "j"
-- error: need to quote name
SELECT 1 as "22", 2 as b, 3 as "Foo"
\crosstabview 1 2 Foo
\crosstabview: column name not found: "foo"
-- error: need to not quote name
SELECT 1 as "22", 2 as b, 3 as "Foo"
\crosstabview 1 "B" "Foo"
\crosstabview: column name not found: "B"
-- error: bad column number -- error: bad column number
SELECT v,h,i,c FROM ctv_data SELECT v,h,i,c FROM ctv_data
\crosstabview 2 1 5 \crosstabview 2 1 5
Invalid column number: 5 \crosstabview: invalid column number: "5"
-- error: same H and V columns -- error: same H and V columns
SELECT v,h,i,c FROM ctv_data SELECT v,h,i,c FROM ctv_data
\crosstabview 2 h 4 \crosstabview 2 h 4
The same column cannot be used for both vertical and horizontal headers \crosstabview: vertical and horizontal headers must be different columns
-- error: too many columns -- error: too many columns
SELECT a,a,1 FROM generate_series(1,3000) AS a SELECT a,a,1 FROM generate_series(1,3000) AS a
\crosstabview \crosstabview
Maximum number of columns (1600) exceeded \crosstabview: maximum number of columns (1600) exceeded
-- error: only one column -- error: only one column
SELECT 1 \crosstabview SELECT 1 \crosstabview
The query must return at least two columns to be shown in crosstab \crosstabview: query must return at least three columns
DROP TABLE ctv_data; DROP TABLE ctv_data;
...@@ -23,7 +23,7 @@ SELECT v, EXTRACT(year FROM d), count(*) ...@@ -23,7 +23,7 @@ SELECT v, EXTRACT(year FROM d), count(*)
-- ordered months in horizontal header, quoted column name -- ordered months in horizontal header, quoted column name
SELECT v, to_char(d, 'Mon') AS "month name", EXTRACT(month FROM d) AS num, SELECT v, to_char(d, 'Mon') AS "month name", EXTRACT(month FROM d) AS num,
count(*) FROM ctv_data GROUP BY 1,2,3 ORDER BY 1 count(*) FROM ctv_data GROUP BY 1,2,3 ORDER BY 1
\crosstabview v "month name":num 4 \crosstabview v "month name" 4 num
-- ordered months in vertical header, ordered years in horizontal header -- ordered months in vertical header, ordered years in horizontal header
SELECT EXTRACT(year FROM d) AS year, to_char(d,'Mon') AS "month name", SELECT EXTRACT(year FROM d) AS year, to_char(d,'Mon') AS "month name",
...@@ -32,7 +32,7 @@ SELECT EXTRACT(year FROM d) AS year, to_char(d,'Mon') AS "month name", ...@@ -32,7 +32,7 @@ SELECT EXTRACT(year FROM d) AS year, to_char(d,'Mon') AS "month name",
FROM ctv_data FROM ctv_data
GROUP BY EXTRACT(year FROM d), to_char(d,'Mon'), EXTRACT(month FROM d) GROUP BY EXTRACT(year FROM d), to_char(d,'Mon'), EXTRACT(month FROM d)
ORDER BY month ORDER BY month
\crosstabview "month name" year:year format \crosstabview "month name" year format year
-- combine contents vertically into the same cell (V/H duplicates) -- combine contents vertically into the same cell (V/H duplicates)
SELECT v, h, string_agg(c, E'\n') FROM ctv_data GROUP BY v, h ORDER BY 1,2,3 SELECT v, h, string_agg(c, E'\n') FROM ctv_data GROUP BY v, h ORDER BY 1,2,3
...@@ -41,17 +41,17 @@ SELECT v, h, string_agg(c, E'\n') FROM ctv_data GROUP BY v, h ORDER BY 1,2,3 ...@@ -41,17 +41,17 @@ SELECT v, h, string_agg(c, E'\n') FROM ctv_data GROUP BY v, h ORDER BY 1,2,3
-- horizontal ASC order from window function -- horizontal ASC order from window function
SELECT v,h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h) AS r SELECT v,h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h) AS r
FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 FROM ctv_data GROUP BY v, h ORDER BY 1,3,2
\crosstabview v h:r c \crosstabview v h c r
-- horizontal DESC order from window function -- horizontal DESC order from window function
SELECT v, h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h DESC) AS r SELECT v, h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h DESC) AS r
FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 FROM ctv_data GROUP BY v, h ORDER BY 1,3,2
\crosstabview v h:r c \crosstabview v h c r
-- horizontal ASC order from window function, NULLs pushed rightmost -- horizontal ASC order from window function, NULLs pushed rightmost
SELECT v,h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h NULLS LAST) AS r SELECT v,h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h NULLS LAST) AS r
FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 FROM ctv_data GROUP BY v, h ORDER BY 1,3,2
\crosstabview v h:r c \crosstabview v h c r
-- only null, no column name, 2 columns: error -- only null, no column name, 2 columns: error
SELECT null,null \crosstabview SELECT null,null \crosstabview
...@@ -76,10 +76,22 @@ SELECT v,h, string_agg(i::text, E'\n') AS i, string_agg(c, E'\n') AS c ...@@ -76,10 +76,22 @@ SELECT v,h, string_agg(i::text, E'\n') AS i, string_agg(c, E'\n') AS c
FROM ctv_data GROUP BY v, h ORDER BY h,v FROM ctv_data GROUP BY v, h ORDER BY h,v
\crosstabview 1 "h" 4 \crosstabview 1 "h" 4
-- refer to columns by quoted names, check downcasing of unquoted name
SELECT 1 as "22", 2 as b, 3 as "Foo"
\crosstabview "22" B "Foo"
-- error: bad column name -- error: bad column name
SELECT v,h,c,i FROM ctv_data SELECT v,h,c,i FROM ctv_data
\crosstabview v h j \crosstabview v h j
-- error: need to quote name
SELECT 1 as "22", 2 as b, 3 as "Foo"
\crosstabview 1 2 Foo
-- error: need to not quote name
SELECT 1 as "22", 2 as b, 3 as "Foo"
\crosstabview 1 "B" "Foo"
-- error: bad column number -- error: bad column number
SELECT v,h,i,c FROM ctv_data SELECT v,h,i,c FROM ctv_data
\crosstabview 2 1 5 \crosstabview 2 1 5
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment