diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 6e8f6a5705..d4b5b2ade1 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -2337,7 +2337,11 @@ process_function_rte_ref(RangeTblEntry *rte, AttrNumber attnum, { TupleDesc tupdesc; - tupdesc = get_expr_result_tupdesc(rtfunc->funcexpr, true); + /* If it has a coldeflist, it certainly returns RECORD */ + if (rtfunc->funccolnames != NIL) + tupdesc = NULL; /* no need to work hard */ + else + tupdesc = get_expr_result_tupdesc(rtfunc->funcexpr, true); if (tupdesc && tupdesc->tdtypeid != RECORDOID) { /* diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 4badb6ff58..fb768ad52c 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1854,6 +1854,10 @@ pull_up_constant_function(PlannerInfo *root, Node *jtnode, if (rtf->funccolcount != 1) return jtnode; /* definitely composite */ + /* If it has a coldeflist, it certainly returns RECORD */ + if (rtf->funccolnames != NIL) + return jtnode; /* must be a one-column RECORD type */ + functypclass = get_expr_result_type(rtf->funcexpr, &funcrettype, &tupdesc); diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index b50fe58d1c..59487cbd79 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -4431,12 +4431,11 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, * Can't simplify if it returns RECORD. The immediate problem is that it * will be needing an expected tupdesc which we can't supply here. * - * In the case where it has OUT parameters, it could get by without an - * expected tupdesc, but we still have issues: get_expr_result_type() - * doesn't know how to extract type info from a RECORD constant, and in - * the case of a NULL function result there doesn't seem to be any clean - * way to fix that. In view of the likelihood of there being still other - * gotchas, seems best to leave the function call unreduced. + * In the case where it has OUT parameters, we could build an expected + * tupdesc from those, but there may be other gotchas lurking. In + * particular, if the function were to return NULL, we would produce a + * null constant with no remaining indication of which concrete record + * type it is. For now, seems best to leave the function call unreduced. */ if (funcform->prorettype == RECORDOID) return NULL; diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 7ca793a369..2f64eaf0e3 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -2738,12 +2738,17 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, { RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); TypeFuncClass functypclass; - Oid funcrettype; - TupleDesc tupdesc; + Oid funcrettype = InvalidOid; + TupleDesc tupdesc = NULL; + + /* If it has a coldeflist, it returns RECORD */ + if (rtfunc->funccolnames != NIL) + functypclass = TYPEFUNC_RECORD; + else + functypclass = get_expr_result_type(rtfunc->funcexpr, + &funcrettype, + &tupdesc); - functypclass = get_expr_result_type(rtfunc->funcexpr, - &funcrettype, - &tupdesc); if (functypclass == TYPEFUNC_COMPOSITE || functypclass == TYPEFUNC_COMPOSITE_DOMAIN) { @@ -3369,6 +3374,10 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) { TupleDesc tupdesc; + /* If it has a coldeflist, it returns RECORD */ + if (rtfunc->funccolnames != NIL) + return false; /* can't have any dropped columns */ + tupdesc = get_expr_result_tupdesc(rtfunc->funcexpr, true); if (tupdesc) diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index 5c03704ef9..05d763fa06 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -287,6 +287,13 @@ get_call_result_type(FunctionCallInfo fcinfo, /* * get_expr_result_type * As above, but work from a calling expression node tree + * + * Beware of using this on the funcexpr of a RTE that has a coldeflist. + * The correct conclusion in such cases is always that the function returns + * RECORD with the columns defined by the coldeflist fields (funccolnames etc). + * If it does not, it's the executor's responsibility to catch the discrepancy + * at runtime; but code processing the query in advance of that point might + * come to inconsistent conclusions if it checks the actual expression. */ TypeFuncClass get_expr_result_type(Node *expr, @@ -537,7 +544,8 @@ internal_get_result_type(Oid funcid, * if noError is true, else throws error. * * This is a simpler version of get_expr_result_type() for use when the caller - * is only interested in determinate rowtype results. + * is only interested in determinate rowtype results. As with that function, + * beware of using this on the funcexpr of a RTE that has a coldeflist. */ TupleDesc get_expr_result_tupdesc(Node *expr, bool noError) diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 2bda957f43..397a8b35d6 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -2498,3 +2498,6 @@ with a(b) as (values (row(1,2,3))) select * from a, coalesce(b) as c(d int, e int, f float); -- fail ERROR: function return row and query-specified return row do not match DETAIL: Returned type integer at ordinal position 3, but query expects double precision. +select * from int8_tbl, coalesce(row(1)) as (a int, b int); -- fail +ERROR: function return row and query-specified return row do not match +DETAIL: Returned row contains 1 attribute, but query expects 2. diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql index 06d598c5e9..3c47c98e11 100644 --- a/src/test/regress/sql/rangefuncs.sql +++ b/src/test/regress/sql/rangefuncs.sql @@ -824,3 +824,4 @@ with a(b) as (values (row(1,2,3))) select * from a, coalesce(b) as c(d int, e int, f int, g int); -- fail with a(b) as (values (row(1,2,3))) select * from a, coalesce(b) as c(d int, e int, f float); -- fail +select * from int8_tbl, coalesce(row(1)) as (a int, b int); -- fail