]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Allow extracting fields from a ROW() expression in more cases.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 15 Sep 2023 20:20:08 +0000 (16:20 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 15 Sep 2023 20:20:08 +0000 (16:20 -0400)
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 8b7a0f1d1 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 8b7a0f1d1 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

src/backend/utils/fmgr/funcapi.c
src/test/regress/expected/rowtypes.out
src/test/regress/sql/rowtypes.sql

index e8993602c71e6500ca6159efdaf0275f1ae2348c..cab34c323b03d821ad5f85b9e7d022b05e38b992 100644 (file)
@@ -251,6 +251,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), false);
+               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 */
index d30cd95085c00ab01dd9749bbbc519c3177f716d..798a182e5720ed2158e48cc23f0d8c5ab4aaace6 100644 (file)
@@ -346,6 +346,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 
index 82878b6fb296d3ebabfb297ac08e0faa7d37a2ed..67a748f82e662207c6a7296c5cad89c30153bddf 100644 (file)
@@ -149,6 +149,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;