postgres.git / summary / log / commit / refs
commit 254b15cbf1e91ca33620027ca016e67668a321c1
Author: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed Feb 11 21:53:14 2026 +0000
First released in: 14.22, 15.17, 16.13, 17.9, 18.3
Fix plpgsql's handling of "return simple_record_variable".
If the variable's value is null, exec_stmt_return() missed filling
in estate->rettype. This is a pretty old bug, but we'd managed not
to notice because that value isn't consulted for a null result ...
unless we have to cast it to a domain. That case led to a failure
with "cache lookup failed for type 0".
The correct way to assign the data type is known by exec_eval_datum.
While we could copy-and-paste that logic, it seems like a better
idea to just invoke exec_eval_datum, as the ROW case already does.
Reported-by: Pavel Stehule <pavel.stehule@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAFj8pRBT_ahexDf-zT-cyH8bMR_qcySKM8D5nv5MvTWPiatYGA@mail.gmail.com
Backpatch-through: 14
src/pl/plpgsql/src/expected/plpgsql_domain.out | 13 +++++++++++++
src/pl/plpgsql/src/pl_exec.c | 20 +++-----------------
src/pl/plpgsql/src/sql/plpgsql_domain.sql | 10 ++++++++++
3 files changed, 26 insertions(+), 17 deletions(-)
diff --git a/src/pl/plpgsql/src/expected/plpgsql_domain.out b/src/pl/plpgsql/src/expected/plpgsql_domain.out
index 516c2b9e08e..11c012ea024 100644
--- a/src/pl/plpgsql/src/expected/plpgsql_domain.out
+++ b/src/pl/plpgsql/src/expected/plpgsql_domain.out
@@ -395,3 +395,16 @@ SELECT * FROM test_assign_ordered_named_pairs(1,2,0); -- should fail someday
{"(1,2)"}
(1 row)
+CREATE FUNCTION test_null_ordered_named_pair()
+ RETURNS ordered_named_pair AS $$
+declare v ordered_named_pair;
+begin
+return v;
+end
+$$ LANGUAGE plpgsql;
+SELECT * FROM test_null_ordered_named_pair();
+ i | j
+---+---
+ |
+(1 row)
+
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index cff9c74e668..2f841d5af21 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3238,28 +3238,14 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
}
break;
- case PLPGSQL_DTYPE_REC:
- {
- PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;
-
- /* If record is empty, we return NULL not a row of nulls */
- if (rec->erh && !ExpandedRecordIsEmpty(rec->erh))
- {
- estate->retval = ExpandedRecordGetDatum(rec->erh);
- estate->retisnull = false;
- estate->rettype = rec->rectypeid;
- }
- }
- break;
-
case PLPGSQL_DTYPE_ROW:
+ case PLPGSQL_DTYPE_REC:
{
- PLpgSQL_row *row = (PLpgSQL_row *) retvar;
+ /* exec_eval_datum can handle these cases */
int32 rettypmod;
- /* We get here if there are multiple OUT parameters */
exec_eval_datum(estate,
- (PLpgSQL_datum *) row,
+ retvar,
&estate->rettype,
&rettypmod,
&estate->retval,
diff --git a/src/pl/plpgsql/src/sql/plpgsql_domain.sql b/src/pl/plpgsql/src/sql/plpgsql_domain.sql
index 8f99aae5a9f..4c5dd7dc707 100644
--- a/src/pl/plpgsql/src/sql/plpgsql_domain.sql
+++ b/src/pl/plpgsql/src/sql/plpgsql_domain.sql
@@ -277,3 +277,13 @@ $$ LANGUAGE plpgsql;
SELECT * FROM test_assign_ordered_named_pairs(1,2,3);
SELECT * FROM test_assign_ordered_named_pairs(2,1,3);
SELECT * FROM test_assign_ordered_named_pairs(1,2,0); -- should fail someday
+
+CREATE FUNCTION test_null_ordered_named_pair()
+ RETURNS ordered_named_pair AS $$
+declare v ordered_named_pair;
+begin
+return v;
+end
+$$ LANGUAGE plpgsql;
+
+SELECT * FROM test_null_ordered_named_pair();
[parent: ebc53ca7b2ff]