aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2019-10-28 15:08:24 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2019-10-28 15:08:24 -0400
commit8b7a0f1d118282f612e5b9686d820edcddedd081 (patch)
treefaa74a53acdf4ac1c5fddd34a505302bc03f7386
parentf88544904e4b57b1a8e969a5d594288554417c88 (diff)
downloadpostgresql-8b7a0f1d118282f612e5b9686d820edcddedd081.tar.gz
postgresql-8b7a0f1d118282f612e5b9686d820edcddedd081.zip
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. Discussion: https://postgr.es/m/10872.1572202006@sss.pgh.pa.us
-rw-r--r--src/backend/utils/fmgr/funcapi.c32
-rw-r--r--src/test/regress/expected/rowtypes.out39
-rw-r--r--src/test/regress/sql/rowtypes.sql9
3 files changed, 80 insertions, 0 deletions
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index b7fac5d2954..4688fbc50c9 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -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 */
diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out
index a272305eb55..2a273f84049 100644
--- a/src/test/regress/expected/rowtypes.out
+++ b/src/test/regress/expected/rowtypes.out
@@ -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
diff --git a/src/test/regress/sql/rowtypes.sql b/src/test/regress/sql/rowtypes.sql
index 7e080c034cd..83cf4a1e791 100644
--- a/src/test/regress/sql/rowtypes.sql
+++ b/src/test/regress/sql/rowtypes.sql
@@ -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;