Skip to content

Commit

Permalink
Allow extracting fields from a ROW() expression in more cases.
Browse files Browse the repository at this point in the history
Teach get_expr_result_type() to manufacture a tuple descriptor directly
from a RowExpr node.  If the RowExpr has type RECORD, this is the only
way to get a tupdesc for its result, since even if the rowtype has been
blessed, we don't have its typmod available at this point.  (If the
RowExpr has some named composite type, we continue to let the existing
code handle it, since the RowExpr might well not have the correct column
names embedded in it.)

This fixes assorted corner cases illustrated by the added regression
tests.

Discussion: https://postgr.es/m/10872.1572202006@sss.pgh.pa.us
  • Loading branch information
tglsfdc committed Oct 28, 2019
1 parent f885449 commit 8b7a0f1
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 0 deletions.
32 changes: 32 additions & 0 deletions src/backend/utils/fmgr/funcapi.c
Expand Up @@ -227,6 +227,38 @@ get_expr_result_type(Node *expr,
NULL,
resultTypeId,
resultTupleDesc);
else if (expr && IsA(expr, RowExpr) &&
((RowExpr *) expr)->row_typeid == RECORDOID)
{
/* We can resolve the record type by generating the tupdesc directly */
RowExpr *rexpr = (RowExpr *) expr;
TupleDesc tupdesc;
AttrNumber i = 1;
ListCell *lcc,
*lcn;

tupdesc = CreateTemplateTupleDesc(list_length(rexpr->args));
Assert(list_length(rexpr->args) == list_length(rexpr->colnames));
forboth(lcc, rexpr->args, lcn, rexpr->colnames)
{
Node *col = (Node *) lfirst(lcc);
char *colname = strVal(lfirst(lcn));

TupleDescInitEntry(tupdesc, i,
colname,
exprType(col),
exprTypmod(col),
0);
TupleDescInitEntryCollation(tupdesc, i,
exprCollation(col));
i++;
}
if (resultTypeId)
*resultTypeId = rexpr->row_typeid;
if (resultTupleDesc)
*resultTupleDesc = BlessTupleDesc(tupdesc);
return TYPEFUNC_COMPOSITE;
}
else
{
/* handle as a generic expression; no chance to resolve RECORD */
Expand Down
39 changes: 39 additions & 0 deletions src/test/regress/expected/rowtypes.out
Expand Up @@ -436,6 +436,45 @@ where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)');
4567890123456789 | 123
(2 rows)

-- Check ability to select columns from an anonymous rowtype
select (row(1, 2.0)).f1;
f1
----
1
(1 row)

select (row(1, 2.0)).f2;
f2
-----
2.0
(1 row)

select (row(1, 2.0)).nosuch; -- fail
ERROR: could not identify column "nosuch" in record data type
LINE 1: select (row(1, 2.0)).nosuch;
^
select (row(1, 2.0)).*;
f1 | f2
----+-----
1 | 2.0
(1 row)

select (r).f1 from (select row(1, 2.0) as r) ss;
f1
----
1
(1 row)

select (r).f3 from (select row(1, 2.0) as r) ss; -- fail
ERROR: could not identify column "f3" in record data type
LINE 1: select (r).f3 from (select row(1, 2.0) as r) ss;
^
select (r).* from (select row(1, 2.0) as r) ss;
f1 | f2
----+-----
1 | 2.0
(1 row)

-- Check some corner cases involving empty rowtypes
select ROW();
row
Expand Down
9 changes: 9 additions & 0 deletions src/test/regress/sql/rowtypes.sql
Expand Up @@ -171,6 +171,15 @@ where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)');
select * from int8_tbl i8
where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)');

-- Check ability to select columns from an anonymous rowtype
select (row(1, 2.0)).f1;
select (row(1, 2.0)).f2;
select (row(1, 2.0)).nosuch; -- fail
select (row(1, 2.0)).*;
select (r).f1 from (select row(1, 2.0) as r) ss;
select (r).f3 from (select row(1, 2.0) as r) ss; -- fail
select (r).* from (select row(1, 2.0) as r) ss;

-- Check some corner cases involving empty rowtypes
select ROW();
select ROW() IS NULL;
Expand Down

0 comments on commit 8b7a0f1

Please sign in to comment.