Обсуждение: plpgsql: variables of domain of composite types are not correctly initialized

Поиск
Список
Период
Сортировка

plpgsql: variables of domain of composite types are not correctly initialized

От
Pavel Stehule
Дата:
Hi

I got a bug report for plpgsql_check related to domains of composite types.

While I played with code, maybe I found a bug:

create type t2 as (a int, b numeric);

create or replace function fx1()
returns t2 as $$
declare v t2;
begin
  return v;
end
$$ language plpgsql;

select fx1(); -- ok

create domain t2d as t2;

create or replace function fx2()
returns t2d as $$
declare v t2d;
begin
  return v;
end
$$ language plpgsql;

select fx2();

select fx2();
ERROR:  cache lookup failed for type 0
CONTEXT:  PL/pgSQL function fx2() while casting return value to function's return type

I found a workaround - the variable must be initialized as NULL composite value.

create or replace function fx2()
returns t2d as $$
declare v t2d;
begin v := (null, null);
  return v;
end
$$ language plpgsql;

and then it works.

Breakpoint 1, getTypeOutputInfo (type=0, typOutput=0x7ffe419d27dc, typIsVarlena=0x7ffe419d27db) at lsyscache.c:3064
3064 elog(ERROR, "cache lookup failed for type %u", type);
(gdb) bt
#0  getTypeOutputInfo (type=0, typOutput=0x7ffe419d27dc, typIsVarlena=0x7ffe419d27db) at lsyscache.c:3064
#1  0x00000000008ad217 in eval_const_expressions_mutator (node=0x1c473170, context=0x7ffe419d2f40) at clauses.c:3088
#2  0x00000000008ae9da in eval_const_expressions_mutator (node=0x1c4731a0, context=0x7ffe419d2f40) at clauses.c:3803
#3  0x00000000008aba3a in eval_const_expressions (root=0x7ffe419d2fa0, node=0x1c4731a0) at clauses.c:2279
#4  0x0000000000887d7e in expression_planner_with_deps (expr=0x1c4731a0, relationOids=0x7ffe419d33f0, invalItems=0x7ffe419d33e8)
    at planner.c:6869
#5  0x0000000000bbdbbc in GetCachedExpression (expr=0x1c4731a0) at plancache.c:1827
#6  0x00007ff91f3953d1 in get_cast_hashentry (estate=0x7ffe419d35d0, srctype=0, srctypmod=-1, dsttype=16393, dsttypmod=-1)
    at pl_exec.c:8085
#7  0x00007ff91f3950e2 in do_cast_value (estate=0x7ffe419d35d0, value=0, isnull=0x1c467924, valtype=0, valtypmod=-1, reqtype=16393,
    reqtypmod=-1) at pl_exec.c:7918
#8  0x00007ff91f39509a in exec_cast_value (estate=0x7ffe419d35d0, value=0, isnull=0x1c467924, valtype=0, valtypmod=-1, reqtype=16393,
    reqtypmod=-1) at pl_exec.c:7899
#9  0x00007ff91f387915 in plpgsql_exec_function (func=0x1c3a51f0, fcinfo=0x1c467908, simple_eval_estate=0x0, simple_eval_resowner=0x0,
    procedure_resowner=0x0, atomic=true) at pl_exec.c:782
#10 0x00007ff91f3a6b88 in plpgsql_call_handler (fcinfo=0x1c467908) at pl_handler.c:278
#11 0x00000000006f5abd in ExecInterpExpr (state=0x1c4677b0, econtext=0x1c467458, isnull=0x0) at execExprInterp.c:926
#12 0x00000000006f810a in ExecInterpExprStillValid (state=0x1c4677b0, econtext=0x1c467458, isNull=0x0) at execExprInterp.c:2299
#13 0x0000000000757adf in ExecEvalExprNoReturn (state=0x1c4677b0, econtext=0x1c467458) at ../../../src/include/executor/executor.h:423
#14 0x0000000000757b93 in ExecEvalExprNoReturnSwitchContext (state=0x1c4677b0, econtext=0x1c467458)
    at ../../../src/include/executor/executor.h:464
#15 0x0000000000757bf4 in ExecProject (projInfo=0x1c4677a8) at ../../../src/include/executor/executor.h:496
#16 0x0000000000757ddc in ExecResult (pstate=0x1c467348) at nodeResult.c:135
#17 0x00000000007116c8 in ExecProcNodeFirst (node=0x1c467348) at execProcnode.c:469
#18 0x00000000007039d9 in ExecProcNode (node=0x1c467348) at ../../../src/include/executor/executor.h:319
#19 0x000000000070674d in ExecutePlan (queryDesc=0x1c38fe10, operation=CMD_SELECT, sendTuples=true, numberTuples=0,
    direction=ForwardScanDirection, dest=0x1c392058) at execMain.c:1711
#20 0x0000000000703ff6 in standard_ExecutorRun (queryDesc=0x1c38fe10, direction=ForwardScanDirection, count=0) at execMain.c:366
#21 0x0000000000703e80 in ExecutorRun (queryDesc=0x1c38fe10, direction=ForwardScanDirection, count=0) at execMain.c:303
#22 0x00000000009ee61c in PortalRunSelect (portal=0x1c3e6240, forward=true, count=0, dest=0x1c392058) at pquery.c:916
#23 0x00000000009ee2d4 in PortalRun (portal=0x1c3e6240, count=9223372036854775807, isTopLevel=true, dest=0x1c392058,
    altdest=0x1c392058, qc=0x7ffe419d3f50) at pquery.c:760
#24 0x00000000009e7391 in exec_simple_query (query_string=0x1c3640a0 "select fx2();") at postgres.c:1277

Regards

Pavel


Re: plpgsql: variables of domain of composite types are not correctly initialized

От
Tom Lane
Дата:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> I got a bug report for plpgsql_check related to domains of composite types.
> While I played with code, maybe I found a bug:

Indeed.  Looks like exec_stmt_return's special case for a simple
variable reference forgets to fill estate->rettype when the variable
is a null record.  This is an old bug, but I think we'd managed not
to notice because that value isn't consulted unless we have to cast
to a domain.

The behavior we want is what exec_eval_datum does, and after looking
at it for a minute I wondered why we don't just use exec_eval_datum
instead of duplicating logic.  The ROW case already does that, so
we can fix the bug with strictly less code.

            regards, tom lane

diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index f80264e184e..723048ab833 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3255,28 +3255,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,

Re: plpgsql: variables of domain of composite types are not correctly initialized

От
Pavel Stehule
Дата:


st 11. 2. 2026 v 22:10 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> I got a bug report for plpgsql_check related to domains of composite types.
> While I played with code, maybe I found a bug:

Indeed.  Looks like exec_stmt_return's special case for a simple
variable reference forgets to fill estate->rettype when the variable
is a null record.  This is an old bug, but I think we'd managed not
to notice because that value isn't consulted unless we have to cast
to a domain.

The behavior we want is what exec_eval_datum does, and after looking
at it for a minute I wondered why we don't just use exec_eval_datum
instead of duplicating logic.  The ROW case already does that, so
we can fix the bug with strictly less code.

                        regards, tom lane

Nice, Thank you

Pavel