mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Improve wrong-tuple-type error reports in contrib/tablefunc.
These messages were fairly confusing, and didn't match the column names used in the SGML docs. Try to improve that. Also use error codes more specific than ERRCODE_SYNTAX_ERROR. Patch by me, reviewed by Joe Conway Discussion: https://postgr.es/m/18937.1709676295@sss.pgh.pa.us
This commit is contained in:
@ -145,6 +145,23 @@ SELECT * FROM crosstab_out('SELECT rowid, attribute, val FROM ct where rowclass
|
|||||||
| val9 | val10 | val11
|
| val9 | val10 | val11
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
-- check error reporting
|
||||||
|
SELECT * FROM crosstab('SELECT rowid, val FROM ct where rowclass = ''group1'' and (attribute = ''att2'' or attribute = ''att3'') ORDER BY 1,2;')
|
||||||
|
AS ct(row_name text, category_1 text, category_2 text);
|
||||||
|
ERROR: invalid crosstab source data query
|
||||||
|
DETAIL: The query must return 3 columns: row_name, category, and value.
|
||||||
|
SELECT * FROM crosstab('SELECT rowid, attribute, val FROM ct where rowclass = ''group1'' and (attribute = ''att2'' or attribute = ''att3'') ORDER BY 1,2;')
|
||||||
|
AS ct(row_name text);
|
||||||
|
ERROR: invalid crosstab return type
|
||||||
|
DETAIL: Return row must have at least two columns.
|
||||||
|
SELECT * FROM crosstab('SELECT rowid, attribute, val FROM ct where rowclass = ''group1'' and (attribute = ''att2'' or attribute = ''att3'') ORDER BY 1,2;')
|
||||||
|
AS ct(row_name int, category_1 text, category_2 text);
|
||||||
|
ERROR: invalid crosstab return type
|
||||||
|
DETAIL: Source row_name datatype text does not match return row_name datatype integer.
|
||||||
|
SELECT * FROM crosstab('SELECT rowid, attribute, val FROM ct where rowclass = ''group1'' and (attribute = ''att2'' or attribute = ''att3'') ORDER BY 1,2;')
|
||||||
|
AS ct(row_name text, category_1 text, category_2 int);
|
||||||
|
ERROR: invalid crosstab return type
|
||||||
|
DETAIL: Source value datatype text does not match return value datatype integer in column 3.
|
||||||
--
|
--
|
||||||
-- hash based crosstab
|
-- hash based crosstab
|
||||||
--
|
--
|
||||||
@ -216,13 +233,20 @@ SELECT * FROM crosstab(
|
|||||||
'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
|
'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
|
||||||
'SELECT DISTINCT attribute FROM cth WHERE attribute = ''a'' ORDER BY 1')
|
'SELECT DISTINCT attribute FROM cth WHERE attribute = ''a'' ORDER BY 1')
|
||||||
AS c(rowid text, rowdt timestamp, temperature int4, test_result text, test_startdate timestamp, volts float8);
|
AS c(rowid text, rowdt timestamp, temperature int4, test_result text, test_startdate timestamp, volts float8);
|
||||||
ERROR: provided "categories" SQL must return 1 column of at least one row
|
ERROR: crosstab categories query must return at least one row
|
||||||
-- if category query generates more than one column, get expected error
|
-- if category query generates more than one column, get expected error
|
||||||
SELECT * FROM crosstab(
|
SELECT * FROM crosstab(
|
||||||
'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
|
'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
|
||||||
'SELECT DISTINCT rowdt, attribute FROM cth ORDER BY 2')
|
'SELECT DISTINCT rowdt, attribute FROM cth ORDER BY 2')
|
||||||
AS c(rowid text, rowdt timestamp, temperature int4, test_result text, test_startdate timestamp, volts float8);
|
AS c(rowid text, rowdt timestamp, temperature int4, test_result text, test_startdate timestamp, volts float8);
|
||||||
ERROR: provided "categories" SQL must return 1 column of at least one row
|
ERROR: invalid crosstab categories query
|
||||||
|
DETAIL: The query must return one column.
|
||||||
|
-- if category query generates a NULL value, get expected error
|
||||||
|
SELECT * FROM crosstab(
|
||||||
|
'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
|
||||||
|
'SELECT NULL::text')
|
||||||
|
AS c(rowid text, rowdt timestamp, temperature int4, test_result text, test_startdate timestamp, volts float8);
|
||||||
|
ERROR: crosstab category value must not be null
|
||||||
-- if source query returns zero rows, get zero rows returned
|
-- if source query returns zero rows, get zero rows returned
|
||||||
SELECT * FROM crosstab(
|
SELECT * FROM crosstab(
|
||||||
'SELECT rowid, rowdt, attribute, val FROM cth WHERE false ORDER BY 1',
|
'SELECT rowid, rowdt, attribute, val FROM cth WHERE false ORDER BY 1',
|
||||||
@ -241,6 +265,26 @@ AS c(rowid text, rowdt timestamp, temperature text, test_result text, test_start
|
|||||||
-------+-------+-------------+-------------+----------------+-------
|
-------+-------+-------------+-------------+----------------+-------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
-- check errors with inappropriate input rowtype
|
||||||
|
SELECT * FROM crosstab(
|
||||||
|
'SELECT rowid, attribute 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);
|
||||||
|
ERROR: invalid crosstab source data query
|
||||||
|
DETAIL: The query must return at least 3 columns: row_name, category, and value.
|
||||||
|
SELECT * FROM crosstab(
|
||||||
|
'SELECT rowid, rowdt, 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);
|
||||||
|
ERROR: invalid crosstab return type
|
||||||
|
DETAIL: Return row must have 7 columns, not 6.
|
||||||
|
-- check errors with inappropriate result rowtype
|
||||||
|
SELECT * FROM crosstab(
|
||||||
|
'SELECT rowid, attribute, val FROM cth ORDER BY 1',
|
||||||
|
'SELECT DISTINCT attribute FROM cth ORDER BY 1')
|
||||||
|
AS c(rowid text);
|
||||||
|
ERROR: invalid crosstab return type
|
||||||
|
DETAIL: Return row must have at least two columns.
|
||||||
-- check it works with a named result rowtype
|
-- check it works with a named result rowtype
|
||||||
create type my_crosstab_result as (
|
create type my_crosstab_result as (
|
||||||
rowid text, rowdt timestamp,
|
rowid text, rowdt timestamp,
|
||||||
@ -381,17 +425,42 @@ SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 4, '~') A
|
|||||||
|
|
||||||
-- should fail as first two columns must have the same type
|
-- should fail as first two columns must have the same type
|
||||||
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid text, parent_keyid int, level int, branch text);
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid text, parent_keyid int, level int, branch text);
|
||||||
ERROR: invalid return type
|
ERROR: invalid connectby return type
|
||||||
DETAIL: First two columns must be the same type.
|
DETAIL: Source key type integer does not match return key type text.
|
||||||
-- should fail as key field datatype should match return datatype
|
-- should fail as key field datatype should match return datatype
|
||||||
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid float8, parent_keyid float8, level int, branch text);
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid float8, parent_keyid float8, level int, branch text);
|
||||||
ERROR: invalid return type
|
ERROR: invalid connectby return type
|
||||||
DETAIL: SQL key field type double precision does not match return key field type integer.
|
DETAIL: Source key type integer does not match return key type double precision.
|
||||||
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid int, parent_keyid float8, level int, branch text);
|
||||||
|
ERROR: invalid connectby return type
|
||||||
|
DETAIL: Source parent key type integer does not match return parent key type double precision.
|
||||||
|
-- check other rowtype mismatch cases
|
||||||
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0) AS t(keyid int, parent_keyid int, level int, branch text);
|
||||||
|
ERROR: invalid connectby return type
|
||||||
|
DETAIL: Return row must have 3 columns, not 4.
|
||||||
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid int, parent_keyid int, level int);
|
||||||
|
ERROR: invalid connectby return type
|
||||||
|
DETAIL: Return row must have 4 columns, not 3.
|
||||||
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0) AS t(keyid int, parent_keyid text, level int);
|
||||||
|
ERROR: invalid connectby return type
|
||||||
|
DETAIL: Source parent key type integer does not match return parent key type text.
|
||||||
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid int, parent_keyid int, level float, branch float);
|
||||||
|
ERROR: invalid connectby return type
|
||||||
|
DETAIL: Third return column (depth) must be type integer.
|
||||||
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid int, parent_keyid int, level int, branch float);
|
||||||
|
ERROR: invalid connectby return type
|
||||||
|
DETAIL: Fourth return column (branch) must be type text.
|
||||||
|
SELECT * FROM connectby('connectby_text', 'keyid', 'parent_keyid', 'pos', 'row2', 0, '~') AS t(keyid text, parent_keyid text, level int, branch text, pos text);
|
||||||
|
ERROR: invalid connectby return type
|
||||||
|
DETAIL: Fifth return column (serial) must be type integer.
|
||||||
|
SELECT * FROM connectby('connectby_text', 'keyid', 'parent_keyid', 'pos', 'row2', 0) AS t(keyid text, parent_keyid text, level int, pos text);
|
||||||
|
ERROR: invalid connectby return type
|
||||||
|
DETAIL: Fourth return column (serial) must be type integer.
|
||||||
-- tests for values using custom queries
|
-- tests for values using custom queries
|
||||||
-- query with one column - failed
|
-- query with one column - failed
|
||||||
SELECT * FROM connectby('connectby_int', '1; --', 'parent_keyid', '2', 0) AS t(keyid int, parent_keyid int, level int);
|
SELECT * FROM connectby('connectby_int', '1; --', 'parent_keyid', '2', 0) AS t(keyid int, parent_keyid int, level int);
|
||||||
ERROR: invalid return type
|
ERROR: invalid connectby source data query
|
||||||
DETAIL: Query must return at least two columns.
|
DETAIL: The query must return at least two columns.
|
||||||
-- query with two columns first value as NULL
|
-- query with two columns first value as NULL
|
||||||
SELECT * FROM connectby('connectby_int', 'NULL::int, 1::int; --', 'parent_keyid', '2', 0) AS t(keyid int, parent_keyid int, level int);
|
SELECT * FROM connectby('connectby_int', 'NULL::int, 1::int; --', 'parent_keyid', '2', 0) AS t(keyid int, parent_keyid int, level int);
|
||||||
keyid | parent_keyid | level
|
keyid | parent_keyid | level
|
||||||
|
@ -44,6 +44,16 @@ LANGUAGE C STABLE STRICT;
|
|||||||
|
|
||||||
SELECT * FROM crosstab_out('SELECT rowid, attribute, val FROM ct where rowclass = ''group1'' ORDER BY 1,2;');
|
SELECT * FROM crosstab_out('SELECT rowid, attribute, val FROM ct where rowclass = ''group1'' ORDER BY 1,2;');
|
||||||
|
|
||||||
|
-- check error reporting
|
||||||
|
SELECT * FROM crosstab('SELECT rowid, val FROM ct where rowclass = ''group1'' and (attribute = ''att2'' or attribute = ''att3'') ORDER BY 1,2;')
|
||||||
|
AS ct(row_name text, category_1 text, category_2 text);
|
||||||
|
SELECT * FROM crosstab('SELECT rowid, attribute, val FROM ct where rowclass = ''group1'' and (attribute = ''att2'' or attribute = ''att3'') ORDER BY 1,2;')
|
||||||
|
AS ct(row_name text);
|
||||||
|
SELECT * FROM crosstab('SELECT rowid, attribute, val FROM ct where rowclass = ''group1'' and (attribute = ''att2'' or attribute = ''att3'') ORDER BY 1,2;')
|
||||||
|
AS ct(row_name int, category_1 text, category_2 text);
|
||||||
|
SELECT * FROM crosstab('SELECT rowid, attribute, val FROM ct where rowclass = ''group1'' and (attribute = ''att2'' or attribute = ''att3'') ORDER BY 1,2;')
|
||||||
|
AS ct(row_name text, category_1 text, category_2 int);
|
||||||
|
|
||||||
--
|
--
|
||||||
-- hash based crosstab
|
-- hash based crosstab
|
||||||
--
|
--
|
||||||
@ -99,6 +109,12 @@ SELECT * FROM crosstab(
|
|||||||
'SELECT DISTINCT rowdt, attribute FROM cth ORDER BY 2')
|
'SELECT DISTINCT rowdt, attribute FROM cth ORDER BY 2')
|
||||||
AS c(rowid text, rowdt timestamp, temperature int4, test_result text, test_startdate timestamp, volts float8);
|
AS c(rowid text, rowdt timestamp, temperature int4, test_result text, test_startdate timestamp, volts float8);
|
||||||
|
|
||||||
|
-- if category query generates a NULL value, get expected error
|
||||||
|
SELECT * FROM crosstab(
|
||||||
|
'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
|
||||||
|
'SELECT NULL::text')
|
||||||
|
AS c(rowid text, rowdt timestamp, temperature int4, test_result text, test_startdate timestamp, volts float8);
|
||||||
|
|
||||||
-- if source query returns zero rows, get zero rows returned
|
-- if source query returns zero rows, get zero rows returned
|
||||||
SELECT * FROM crosstab(
|
SELECT * FROM crosstab(
|
||||||
'SELECT rowid, rowdt, attribute, val FROM cth WHERE false ORDER BY 1',
|
'SELECT rowid, rowdt, attribute, val FROM cth WHERE false ORDER BY 1',
|
||||||
@ -111,6 +127,22 @@ SELECT * FROM crosstab(
|
|||||||
'SELECT DISTINCT attribute FROM cth WHERE false ORDER BY 1')
|
'SELECT DISTINCT attribute FROM cth WHERE false ORDER BY 1')
|
||||||
AS c(rowid text, rowdt timestamp, temperature text, test_result text, test_startdate text, volts text);
|
AS c(rowid text, rowdt timestamp, temperature text, test_result text, test_startdate text, volts text);
|
||||||
|
|
||||||
|
-- check errors with inappropriate input rowtype
|
||||||
|
SELECT * FROM crosstab(
|
||||||
|
'SELECT rowid, attribute 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);
|
||||||
|
SELECT * FROM crosstab(
|
||||||
|
'SELECT rowid, rowdt, 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);
|
||||||
|
|
||||||
|
-- check errors with inappropriate result rowtype
|
||||||
|
SELECT * FROM crosstab(
|
||||||
|
'SELECT rowid, attribute, val FROM cth ORDER BY 1',
|
||||||
|
'SELECT DISTINCT attribute FROM cth ORDER BY 1')
|
||||||
|
AS c(rowid text);
|
||||||
|
|
||||||
-- check it works with a named result rowtype
|
-- check it works with a named result rowtype
|
||||||
|
|
||||||
create type my_crosstab_result as (
|
create type my_crosstab_result as (
|
||||||
@ -186,6 +218,16 @@ SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') A
|
|||||||
|
|
||||||
-- should fail as key field datatype should match return datatype
|
-- should fail as key field datatype should match return datatype
|
||||||
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid float8, parent_keyid float8, level int, branch text);
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid float8, parent_keyid float8, level int, branch text);
|
||||||
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid int, parent_keyid float8, level int, branch text);
|
||||||
|
|
||||||
|
-- check other rowtype mismatch cases
|
||||||
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0) AS t(keyid int, parent_keyid int, level int, branch text);
|
||||||
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid int, parent_keyid int, level int);
|
||||||
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0) AS t(keyid int, parent_keyid text, level int);
|
||||||
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid int, parent_keyid int, level float, branch float);
|
||||||
|
SELECT * FROM connectby('connectby_int', 'keyid', 'parent_keyid', '2', 0, '~') AS t(keyid int, parent_keyid int, level int, branch float);
|
||||||
|
SELECT * FROM connectby('connectby_text', 'keyid', 'parent_keyid', 'pos', 'row2', 0, '~') AS t(keyid text, parent_keyid text, level int, branch text, pos text);
|
||||||
|
SELECT * FROM connectby('connectby_text', 'keyid', 'parent_keyid', 'pos', 'row2', 0) AS t(keyid text, parent_keyid text, level int, pos text);
|
||||||
|
|
||||||
-- tests for values using custom queries
|
-- tests for values using custom queries
|
||||||
-- query with one column - failed
|
-- query with one column - failed
|
||||||
|
@ -52,7 +52,7 @@ static Tuplestorestate *get_crosstab_tuplestore(char *sql,
|
|||||||
TupleDesc tupdesc,
|
TupleDesc tupdesc,
|
||||||
bool randomAccess);
|
bool randomAccess);
|
||||||
static void validateConnectbyTupleDesc(TupleDesc td, bool show_branch, bool show_serial);
|
static void validateConnectbyTupleDesc(TupleDesc td, bool show_branch, bool show_serial);
|
||||||
static bool compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc);
|
static void compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc);
|
||||||
static void compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc);
|
static void compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc);
|
||||||
static void get_normal_pair(float8 *x1, float8 *x2);
|
static void get_normal_pair(float8 *x1, float8 *x2);
|
||||||
static Tuplestorestate *connectby(char *relname,
|
static Tuplestorestate *connectby(char *relname,
|
||||||
@ -418,9 +418,8 @@ crosstab(PG_FUNCTION_ARGS)
|
|||||||
if (spi_tupdesc->natts != 3)
|
if (spi_tupdesc->natts != 3)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("invalid source data SQL statement"),
|
errmsg("invalid crosstab source data query"),
|
||||||
errdetail("The provided SQL must return 3 "
|
errdetail("The query must return 3 columns: row_name, category, and value.")));
|
||||||
"columns: rowid, category, and values.")));
|
|
||||||
|
|
||||||
/* get a tuple descriptor for our result type */
|
/* get a tuple descriptor for our result type */
|
||||||
switch (get_call_result_type(fcinfo, NULL, &tupdesc))
|
switch (get_call_result_type(fcinfo, NULL, &tupdesc))
|
||||||
@ -447,11 +446,7 @@ crosstab(PG_FUNCTION_ARGS)
|
|||||||
* Check that return tupdesc is compatible with the data we got from SPI,
|
* Check that return tupdesc is compatible with the data we got from SPI,
|
||||||
* at least based on number and type of attributes
|
* at least based on number and type of attributes
|
||||||
*/
|
*/
|
||||||
if (!compatCrosstabTupleDescs(tupdesc, spi_tupdesc))
|
compatCrosstabTupleDescs(tupdesc, spi_tupdesc);
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("return and sql tuple descriptions are " \
|
|
||||||
"incompatible")));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* switch to long-lived memory context
|
* switch to long-lived memory context
|
||||||
@ -673,9 +668,9 @@ crosstab_hash(PG_FUNCTION_ARGS)
|
|||||||
*/
|
*/
|
||||||
if (tupdesc->natts < 2)
|
if (tupdesc->natts < 2)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("query-specified return tuple and " \
|
errmsg("invalid crosstab return type"),
|
||||||
"crosstab function are not compatible")));
|
errdetail("Return row must have at least two columns.")));
|
||||||
|
|
||||||
/* load up the categories hash table */
|
/* load up the categories hash table */
|
||||||
crosstab_hash = load_categories_hash(cats_sql, per_query_ctx);
|
crosstab_hash = load_categories_hash(cats_sql, per_query_ctx);
|
||||||
@ -750,9 +745,9 @@ load_categories_hash(char *cats_sql, MemoryContext per_query_ctx)
|
|||||||
*/
|
*/
|
||||||
if (spi_tupdesc->natts != 1)
|
if (spi_tupdesc->natts != 1)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("provided \"categories\" SQL must " \
|
errmsg("invalid crosstab categories query"),
|
||||||
"return 1 column of at least one row")));
|
errdetail("The query must return one column.")));
|
||||||
|
|
||||||
for (i = 0; i < proc; i++)
|
for (i = 0; i < proc; i++)
|
||||||
{
|
{
|
||||||
@ -767,9 +762,8 @@ load_categories_hash(char *cats_sql, MemoryContext per_query_ctx)
|
|||||||
catname = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
|
catname = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
|
||||||
if (catname == NULL)
|
if (catname == NULL)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
||||||
errmsg("provided \"categories\" SQL must " \
|
errmsg("crosstab category value must not be null")));
|
||||||
"not return NULL values")));
|
|
||||||
|
|
||||||
SPIcontext = MemoryContextSwitchTo(per_query_ctx);
|
SPIcontext = MemoryContextSwitchTo(per_query_ctx);
|
||||||
|
|
||||||
@ -837,9 +831,8 @@ get_crosstab_tuplestore(char *sql,
|
|||||||
{
|
{
|
||||||
/* no qualifying category tuples */
|
/* no qualifying category tuples */
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_CARDINALITY_VIOLATION),
|
||||||
errmsg("provided \"categories\" SQL must " \
|
errmsg("crosstab categories query must return at least one row")));
|
||||||
"return 1 column of at least one row")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -858,20 +851,18 @@ get_crosstab_tuplestore(char *sql,
|
|||||||
if (ncols < 3)
|
if (ncols < 3)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("invalid source data SQL statement"),
|
errmsg("invalid crosstab source data query"),
|
||||||
errdetail("The provided SQL must return 3 " \
|
errdetail("The query must return at least 3 columns: row_name, category, and value.")));
|
||||||
" columns; rowid, category, and values.")));
|
|
||||||
|
|
||||||
result_ncols = (ncols - 2) + num_categories;
|
result_ncols = (ncols - 2) + num_categories;
|
||||||
|
|
||||||
/* Recheck to make sure we tuple descriptor still looks reasonable */
|
/* Recheck to make sure output tuple descriptor looks reasonable */
|
||||||
if (tupdesc->natts != result_ncols)
|
if (tupdesc->natts != result_ncols)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("invalid return type"),
|
errmsg("invalid crosstab return type"),
|
||||||
errdetail("Query-specified return " \
|
errdetail("Return row must have %d columns, not %d.",
|
||||||
"tuple has %d columns but crosstab " \
|
result_ncols, tupdesc->natts)));
|
||||||
"returns %d.", tupdesc->natts, result_ncols)));
|
|
||||||
|
|
||||||
/* allocate space and make sure it's clear */
|
/* allocate space and make sure it's clear */
|
||||||
values = (char **) palloc0(result_ncols * sizeof(char *));
|
values = (char **) palloc0(result_ncols * sizeof(char *));
|
||||||
@ -1422,77 +1413,62 @@ build_tuplestore_recursively(char *key_fld,
|
|||||||
static void
|
static void
|
||||||
validateConnectbyTupleDesc(TupleDesc td, bool show_branch, bool show_serial)
|
validateConnectbyTupleDesc(TupleDesc td, bool show_branch, bool show_serial)
|
||||||
{
|
{
|
||||||
int serial_column = 0;
|
int expected_cols;
|
||||||
|
|
||||||
if (show_serial)
|
|
||||||
serial_column = 1;
|
|
||||||
|
|
||||||
/* are there the correct number of columns */
|
/* are there the correct number of columns */
|
||||||
if (show_branch)
|
if (show_branch)
|
||||||
{
|
expected_cols = CONNECTBY_NCOLS;
|
||||||
if (td->natts != (CONNECTBY_NCOLS + serial_column))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
||||||
errmsg("invalid return type"),
|
|
||||||
errdetail("Query-specified return tuple has " \
|
|
||||||
"wrong number of columns.")));
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
expected_cols = CONNECTBY_NCOLS_NOBRANCH;
|
||||||
if (td->natts != CONNECTBY_NCOLS_NOBRANCH + serial_column)
|
if (show_serial)
|
||||||
ereport(ERROR,
|
expected_cols++;
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
||||||
errmsg("invalid return type"),
|
|
||||||
errdetail("Query-specified return tuple has " \
|
|
||||||
"wrong number of columns.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check that the types of the first two columns match */
|
if (td->natts != expected_cols)
|
||||||
if (TupleDescAttr(td, 0)->atttypid != TupleDescAttr(td, 1)->atttypid)
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("invalid return type"),
|
errmsg("invalid connectby return type"),
|
||||||
errdetail("First two columns must be the same type.")));
|
errdetail("Return row must have %d columns, not %d.",
|
||||||
|
expected_cols, td->natts)));
|
||||||
|
|
||||||
|
/* the first two columns will be checked against the input tuples later */
|
||||||
|
|
||||||
/* check that the type of the third column is INT4 */
|
/* check that the type of the third column is INT4 */
|
||||||
if (TupleDescAttr(td, 2)->atttypid != INT4OID)
|
if (TupleDescAttr(td, 2)->atttypid != INT4OID)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("invalid return type"),
|
errmsg("invalid connectby return type"),
|
||||||
errdetail("Third column must be type %s.",
|
errdetail("Third return column (depth) must be type %s.",
|
||||||
format_type_be(INT4OID))));
|
format_type_be(INT4OID))));
|
||||||
|
|
||||||
/* check that the type of the fourth column is TEXT if applicable */
|
/* check that the type of the branch column is TEXT if applicable */
|
||||||
if (show_branch && TupleDescAttr(td, 3)->atttypid != TEXTOID)
|
if (show_branch && TupleDescAttr(td, 3)->atttypid != TEXTOID)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("invalid return type"),
|
errmsg("invalid connectby return type"),
|
||||||
errdetail("Fourth column must be type %s.",
|
errdetail("Fourth return column (branch) must be type %s.",
|
||||||
format_type_be(TEXTOID))));
|
format_type_be(TEXTOID))));
|
||||||
|
|
||||||
/* check that the type of the fifth column is INT4 */
|
/* check that the type of the serial column is INT4 if applicable */
|
||||||
if (show_branch && show_serial &&
|
if (show_branch && show_serial &&
|
||||||
TupleDescAttr(td, 4)->atttypid != INT4OID)
|
TupleDescAttr(td, 4)->atttypid != INT4OID)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("query-specified return tuple not valid for Connectby: "
|
errmsg("invalid connectby return type"),
|
||||||
"fifth column must be type %s",
|
errdetail("Fifth return column (serial) must be type %s.",
|
||||||
format_type_be(INT4OID))));
|
format_type_be(INT4OID))));
|
||||||
|
|
||||||
/* check that the type of the fourth column is INT4 */
|
|
||||||
if (!show_branch && show_serial &&
|
if (!show_branch && show_serial &&
|
||||||
TupleDescAttr(td, 3)->atttypid != INT4OID)
|
TupleDescAttr(td, 3)->atttypid != INT4OID)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("query-specified return tuple not valid for Connectby: "
|
errmsg("invalid connectby return type"),
|
||||||
"fourth column must be type %s",
|
errdetail("Fourth return column (serial) must be type %s.",
|
||||||
format_type_be(INT4OID))));
|
format_type_be(INT4OID))));
|
||||||
|
|
||||||
/* OK, the tupdesc is valid for our purposes */
|
/* OK, the tupdesc is valid for our purposes */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if spi sql tupdesc and return tupdesc are compatible
|
* Check if output tupdesc and SQL query's tupdesc are compatible
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
|
compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
|
||||||
@ -1503,13 +1479,13 @@ compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
|
|||||||
int32 sql_atttypmod;
|
int32 sql_atttypmod;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Result must have at least 2 columns.
|
* Query result must have at least 2 columns.
|
||||||
*/
|
*/
|
||||||
if (sql_tupdesc->natts < 2)
|
if (sql_tupdesc->natts < 2)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("invalid return type"),
|
errmsg("invalid connectby source data query"),
|
||||||
errdetail("Query must return at least two columns.")));
|
errdetail("The query must return at least two columns.")));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These columns must match the result type indicated by the calling
|
* These columns must match the result type indicated by the calling
|
||||||
@ -1523,11 +1499,10 @@ compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
|
|||||||
(ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
|
(ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("invalid return type"),
|
errmsg("invalid connectby return type"),
|
||||||
errdetail("SQL key field type %s does " \
|
errdetail("Source key type %s does not match return key type %s.",
|
||||||
"not match return key field type %s.",
|
format_type_with_typemod(sql_atttypid, sql_atttypmod),
|
||||||
format_type_with_typemod(ret_atttypid, ret_atttypmod),
|
format_type_with_typemod(ret_atttypid, ret_atttypmod))));
|
||||||
format_type_with_typemod(sql_atttypid, sql_atttypmod))));
|
|
||||||
|
|
||||||
ret_atttypid = TupleDescAttr(ret_tupdesc, 1)->atttypid;
|
ret_atttypid = TupleDescAttr(ret_tupdesc, 1)->atttypid;
|
||||||
sql_atttypid = TupleDescAttr(sql_tupdesc, 1)->atttypid;
|
sql_atttypid = TupleDescAttr(sql_tupdesc, 1)->atttypid;
|
||||||
@ -1537,19 +1512,18 @@ compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
|
|||||||
(ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
|
(ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("invalid return type"),
|
errmsg("invalid connectby return type"),
|
||||||
errdetail("SQL parent key field type %s does " \
|
errdetail("Source parent key type %s does not match return parent key type %s.",
|
||||||
"not match return parent key field type %s.",
|
format_type_with_typemod(sql_atttypid, sql_atttypmod),
|
||||||
format_type_with_typemod(ret_atttypid, ret_atttypmod),
|
format_type_with_typemod(ret_atttypid, ret_atttypmod))));
|
||||||
format_type_with_typemod(sql_atttypid, sql_atttypmod))));
|
|
||||||
|
|
||||||
/* OK, the two tupdescs are compatible for our purposes */
|
/* OK, the two tupdescs are compatible for our purposes */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if two tupdescs match in type of attributes
|
* Check if crosstab output tupdesc agrees with input tupdesc
|
||||||
*/
|
*/
|
||||||
static bool
|
static void
|
||||||
compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
|
compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -1558,9 +1532,12 @@ compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
|
|||||||
Form_pg_attribute sql_attr;
|
Form_pg_attribute sql_attr;
|
||||||
Oid sql_atttypid;
|
Oid sql_atttypid;
|
||||||
|
|
||||||
if (ret_tupdesc->natts < 2 ||
|
if (ret_tupdesc->natts < 2)
|
||||||
sql_tupdesc->natts < 3)
|
ereport(ERROR,
|
||||||
return false;
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("invalid crosstab return type"),
|
||||||
|
errdetail("Return row must have at least two columns.")));
|
||||||
|
Assert(sql_tupdesc->natts == 3); /* already checked by caller */
|
||||||
|
|
||||||
/* check the rowid types match */
|
/* check the rowid types match */
|
||||||
ret_atttypid = TupleDescAttr(ret_tupdesc, 0)->atttypid;
|
ret_atttypid = TupleDescAttr(ret_tupdesc, 0)->atttypid;
|
||||||
@ -1568,9 +1545,10 @@ compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
|
|||||||
if (ret_atttypid != sql_atttypid)
|
if (ret_atttypid != sql_atttypid)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("invalid return type"),
|
errmsg("invalid crosstab return type"),
|
||||||
errdetail("SQL rowid datatype does not match " \
|
errdetail("Source row_name datatype %s does not match return row_name datatype %s.",
|
||||||
"return rowid datatype.")));
|
format_type_be(sql_atttypid),
|
||||||
|
format_type_be(ret_atttypid))));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* - attribute [1] of the sql tuple is the category; no need to check it -
|
* - attribute [1] of the sql tuple is the category; no need to check it -
|
||||||
@ -1583,9 +1561,14 @@ compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
|
|||||||
ret_attr = TupleDescAttr(ret_tupdesc, i);
|
ret_attr = TupleDescAttr(ret_tupdesc, i);
|
||||||
|
|
||||||
if (ret_attr->atttypid != sql_attr->atttypid)
|
if (ret_attr->atttypid != sql_attr->atttypid)
|
||||||
return false;
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("invalid crosstab return type"),
|
||||||
|
errdetail("Source value datatype %s does not match return value datatype %s in column %d.",
|
||||||
|
format_type_be(sql_attr->atttypid),
|
||||||
|
format_type_be(ret_attr->atttypid),
|
||||||
|
i + 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OK, the two tupdescs are compatible for our purposes */
|
/* OK, the two tupdescs are compatible for our purposes */
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user