Skip to content

Commit ece1154

Browse files
committed
Allow extracting fields from a ROW() expression in more cases.
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. This is a back-patch of the v13-era commit 8b7a0f1 into previous branches. At the time I'd judged it not important enough to back-patch, but the upcoming fix for bug #18077 includes a test case that depends on this working correctly; and 8b7a0f1 has now aged long enough to have good confidence that it won't break anything. Discussion: https://postgr.es/m/10872.1572202006@sss.pgh.pa.us Discussion: https://postgr.es/m/3607145.1694803130@sss.pgh.pa.us
1 parent bddbbdf commit ece1154

File tree

3 files changed

+80
-0
lines changed

3 files changed

+80
-0
lines changed

src/backend/utils/fmgr/funcapi.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,38 @@ get_expr_result_type(Node *expr,
251251
NULL,
252252
resultTypeId,
253253
resultTupleDesc);
254+
else if (expr && IsA(expr, RowExpr) &&
255+
((RowExpr *) expr)->row_typeid == RECORDOID)
256+
{
257+
/* We can resolve the record type by generating the tupdesc directly */
258+
RowExpr *rexpr = (RowExpr *) expr;
259+
TupleDesc tupdesc;
260+
AttrNumber i = 1;
261+
ListCell *lcc,
262+
*lcn;
263+
264+
tupdesc = CreateTemplateTupleDesc(list_length(rexpr->args), false);
265+
Assert(list_length(rexpr->args) == list_length(rexpr->colnames));
266+
forboth(lcc, rexpr->args, lcn, rexpr->colnames)
267+
{
268+
Node *col = (Node *) lfirst(lcc);
269+
char *colname = strVal(lfirst(lcn));
270+
271+
TupleDescInitEntry(tupdesc, i,
272+
colname,
273+
exprType(col),
274+
exprTypmod(col),
275+
0);
276+
TupleDescInitEntryCollation(tupdesc, i,
277+
exprCollation(col));
278+
i++;
279+
}
280+
if (resultTypeId)
281+
*resultTypeId = rexpr->row_typeid;
282+
if (resultTupleDesc)
283+
*resultTupleDesc = BlessTupleDesc(tupdesc);
284+
return TYPEFUNC_COMPOSITE;
285+
}
254286
else
255287
{
256288
/* handle as a generic expression; no chance to resolve RECORD */

src/test/regress/expected/rowtypes.out

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,45 @@ where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)');
346346
4567890123456789 | 123
347347
(2 rows)
348348

349+
-- Check ability to select columns from an anonymous rowtype
350+
select (row(1, 2.0)).f1;
351+
f1
352+
----
353+
1
354+
(1 row)
355+
356+
select (row(1, 2.0)).f2;
357+
f2
358+
-----
359+
2.0
360+
(1 row)
361+
362+
select (row(1, 2.0)).nosuch; -- fail
363+
ERROR: could not identify column "nosuch" in record data type
364+
LINE 1: select (row(1, 2.0)).nosuch;
365+
^
366+
select (row(1, 2.0)).*;
367+
f1 | f2
368+
----+-----
369+
1 | 2.0
370+
(1 row)
371+
372+
select (r).f1 from (select row(1, 2.0) as r) ss;
373+
f1
374+
----
375+
1
376+
(1 row)
377+
378+
select (r).f3 from (select row(1, 2.0) as r) ss; -- fail
379+
ERROR: could not identify column "f3" in record data type
380+
LINE 1: select (r).f3 from (select row(1, 2.0) as r) ss;
381+
^
382+
select (r).* from (select row(1, 2.0) as r) ss;
383+
f1 | f2
384+
----+-----
385+
1 | 2.0
386+
(1 row)
387+
349388
-- Check some corner cases involving empty rowtypes
350389
select ROW();
351390
row

src/test/regress/sql/rowtypes.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,15 @@ where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)');
149149
select * from int8_tbl i8
150150
where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)');
151151

152+
-- Check ability to select columns from an anonymous rowtype
153+
select (row(1, 2.0)).f1;
154+
select (row(1, 2.0)).f2;
155+
select (row(1, 2.0)).nosuch; -- fail
156+
select (row(1, 2.0)).*;
157+
select (r).f1 from (select row(1, 2.0) as r) ss;
158+
select (r).f3 from (select row(1, 2.0) as r) ss; -- fail
159+
select (r).* from (select row(1, 2.0) as r) ss;
160+
152161
-- Check some corner cases involving empty rowtypes
153162
select ROW();
154163
select ROW() IS NULL;

0 commit comments

Comments
 (0)