| val9 | val10 | val11
(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
--
'SELECT rowid, rowdt, attribute, val FROM cth 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);
-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
SELECT * FROM crosstab(
'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
'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);
-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
SELECT * FROM crosstab(
'SELECT rowid, rowdt, attribute, val FROM cth WHERE false ORDER BY 1',
-------+-------+-------------+-------------+----------------+-------
(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
create type my_crosstab_result as (
rowid text, rowdt timestamp,
-- 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);
-ERROR: invalid return type
-DETAIL: First two columns must be the same type.
+ERROR: invalid connectby return type
+DETAIL: Source key type integer does not match return key type text.
-- 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);
-ERROR: invalid return type
-DETAIL: SQL key field type double precision does not match return key field type integer.
+ERROR: invalid connectby return type
+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
-- query with one column - failed
SELECT * FROM connectby('connectby_int', '1; --', 'parent_keyid', '2', 0) AS t(keyid int, parent_keyid int, level int);
-ERROR: invalid return type
-DETAIL: Query must return at least two columns.
+ERROR: invalid connectby source data query
+DETAIL: The query must return at least two columns.
-- 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);
keyid | parent_keyid | level
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
--
'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);
+-- 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
SELECT * FROM crosstab(
'SELECT rowid, rowdt, attribute, val 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);
+-- 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
create type my_crosstab_result as (
-- 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 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
-- query with one column - failed
TupleDesc tupdesc,
bool randomAccess);
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 get_normal_pair(float8 *x1, float8 *x2);
static Tuplestorestate *connectby(char *relname,
if (spi_tupdesc->natts != 3)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid source data SQL statement"),
- errdetail("The provided SQL must return 3 "
- "columns: rowid, category, and values.")));
+ errmsg("invalid crosstab source data query"),
+ errdetail("The query must return 3 columns: row_name, category, and value.")));
/* get a tuple descriptor for our result type */
switch (get_call_result_type(fcinfo, NULL, &tupdesc))
* Check that return tupdesc is compatible with the data we got from SPI,
* at least based on number and type of attributes
*/
- if (!compatCrosstabTupleDescs(tupdesc, spi_tupdesc))
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("return and sql tuple descriptions are " \
- "incompatible")));
+ compatCrosstabTupleDescs(tupdesc, spi_tupdesc);
/*
* switch to long-lived memory context
*/
if (tupdesc->natts < 2)
ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("query-specified return tuple and " \
- "crosstab function are not compatible")));
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("invalid crosstab return type"),
+ errdetail("Return row must have at least two columns.")));
/* load up the categories hash table */
crosstab_hash = load_categories_hash(cats_sql, per_query_ctx);
*/
if (spi_tupdesc->natts != 1)
ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("provided \"categories\" SQL must " \
- "return 1 column of at least one row")));
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid crosstab categories query"),
+ errdetail("The query must return one column.")));
for (i = 0; i < proc; i++)
{
catname = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
if (catname == NULL)
ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("provided \"categories\" SQL must " \
- "not return NULL values")));
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("crosstab category value must not be null")));
SPIcontext = MemoryContextSwitchTo(per_query_ctx);
{
/* no qualifying category tuples */
ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("provided \"categories\" SQL must " \
- "return 1 column of at least one row")));
+ (errcode(ERRCODE_CARDINALITY_VIOLATION),
+ errmsg("crosstab categories query must return at least one row")));
}
/*
if (ncols < 3)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid source data SQL statement"),
- errdetail("The provided SQL must return 3 " \
- " columns; rowid, category, and values.")));
+ errmsg("invalid crosstab source data query"),
+ errdetail("The query must return at least 3 columns: row_name, category, and value.")));
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)
ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid return type"),
- errdetail("Query-specified return " \
- "tuple has %d columns but crosstab " \
- "returns %d.", tupdesc->natts, result_ncols)));
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("invalid crosstab return type"),
+ errdetail("Return row must have %d columns, not %d.",
+ result_ncols, tupdesc->natts)));
/* allocate space and make sure it's clear */
values = (char **) palloc0(result_ncols * sizeof(char *));
static void
validateConnectbyTupleDesc(TupleDesc td, bool show_branch, bool show_serial)
{
- int serial_column = 0;
-
- if (show_serial)
- serial_column = 1;
+ int expected_cols;
/* are there the correct number of columns */
if (show_branch)
- {
- 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.")));
- }
+ expected_cols = CONNECTBY_NCOLS;
else
- {
- if (td->natts != CONNECTBY_NCOLS_NOBRANCH + serial_column)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("invalid return type"),
- errdetail("Query-specified return tuple has " \
- "wrong number of columns.")));
- }
+ expected_cols = CONNECTBY_NCOLS_NOBRANCH;
+ if (show_serial)
+ expected_cols++;
- /* check that the types of the first two columns match */
- if (TupleDescAttr(td, 0)->atttypid != TupleDescAttr(td, 1)->atttypid)
+ if (td->natts != expected_cols)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("invalid return type"),
- errdetail("First two columns must be the same type.")));
+ errmsg("invalid connectby return 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 */
if (TupleDescAttr(td, 2)->atttypid != INT4OID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("invalid return type"),
- errdetail("Third column must be type %s.",
+ errmsg("invalid connectby return type"),
+ errdetail("Third return column (depth) must be type %s.",
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)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("invalid return type"),
- errdetail("Fourth column must be type %s.",
+ errmsg("invalid connectby return type"),
+ errdetail("Fourth return column (branch) must be type %s.",
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 &&
TupleDescAttr(td, 4)->atttypid != INT4OID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("query-specified return tuple not valid for Connectby: "
- "fifth column must be type %s",
- format_type_be(INT4OID))));
-
- /* check that the type of the fourth column is INT4 */
+ errmsg("invalid connectby return type"),
+ errdetail("Fifth return column (serial) must be type %s.",
+ format_type_be(INT4OID))));
if (!show_branch && show_serial &&
TupleDescAttr(td, 3)->atttypid != INT4OID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("query-specified return tuple not valid for Connectby: "
- "fourth column must be type %s",
- format_type_be(INT4OID))));
+ errmsg("invalid connectby return type"),
+ errdetail("Fourth return column (serial) must be type %s.",
+ format_type_be(INT4OID))));
/* 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
compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
int32 sql_atttypmod;
/*
- * Result must have at least 2 columns.
+ * Query result must have at least 2 columns.
*/
if (sql_tupdesc->natts < 2)
ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("invalid return type"),
- errdetail("Query must return at least two columns.")));
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid connectby source data query"),
+ errdetail("The query must return at least two columns.")));
/*
* These columns must match the result type indicated by the calling
(ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("invalid return type"),
- errdetail("SQL key field type %s does " \
- "not match return key field type %s.",
- format_type_with_typemod(ret_atttypid, ret_atttypmod),
- format_type_with_typemod(sql_atttypid, sql_atttypmod))));
+ errmsg("invalid connectby return type"),
+ errdetail("Source key type %s does not match return key type %s.",
+ format_type_with_typemod(sql_atttypid, sql_atttypmod),
+ format_type_with_typemod(ret_atttypid, ret_atttypmod))));
ret_atttypid = TupleDescAttr(ret_tupdesc, 1)->atttypid;
sql_atttypid = TupleDescAttr(sql_tupdesc, 1)->atttypid;
(ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("invalid return type"),
- errdetail("SQL parent key field type %s does " \
- "not match return parent key field type %s.",
- format_type_with_typemod(ret_atttypid, ret_atttypmod),
- format_type_with_typemod(sql_atttypid, sql_atttypmod))));
+ errmsg("invalid connectby return type"),
+ errdetail("Source parent key type %s does not match return parent key type %s.",
+ format_type_with_typemod(sql_atttypid, sql_atttypmod),
+ format_type_with_typemod(ret_atttypid, ret_atttypmod))));
/* 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)
{
int i;
Form_pg_attribute sql_attr;
Oid sql_atttypid;
- if (ret_tupdesc->natts < 2 ||
- sql_tupdesc->natts < 3)
- return false;
+ if (ret_tupdesc->natts < 2)
+ ereport(ERROR,
+ (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 */
ret_atttypid = TupleDescAttr(ret_tupdesc, 0)->atttypid;
if (ret_atttypid != sql_atttypid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("invalid return type"),
- errdetail("SQL rowid datatype does not match " \
- "return rowid datatype.")));
+ errmsg("invalid crosstab return type"),
+ errdetail("Source row_name datatype %s does not match return row_name datatype %s.",
+ format_type_be(sql_atttypid),
+ format_type_be(ret_atttypid))));
/*
* - attribute [1] of the sql tuple is the category; no need to check it -
ret_attr = TupleDescAttr(ret_tupdesc, i);
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 */
- return true;
}