Skip to content

Commit 27a4011

Browse files
authored
Merge pull request #1173 from jiaoshuntian/ivy5_bp_1123
fix: prevent segfault when package has subproc with param-initialized…
2 parents 1d0c6d5 + 2c99f71 commit 27a4011

File tree

4 files changed

+302
-2
lines changed

4 files changed

+302
-2
lines changed

src/pl/plisql/src/expected/plisql_nested_subproc.out

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3954,3 +3954,127 @@ begin
39543954
end; $$ language plisql;
39553955
DROP TABLE rec_typ2;
39563956
DROP TABLE r1;
3957+
-- Test for issue #1005: subproc with local variable initialized from parameter
3958+
-- This should not crash when the parent function is called
3959+
-- Test 1: Package with private procedure that has param-initialized local var
3960+
CREATE OR REPLACE PACKAGE test_pkg_1005 IS
3961+
FUNCTION get_value RETURN NUMBER;
3962+
END test_pkg_1005;
3963+
/
3964+
CREATE OR REPLACE PACKAGE BODY test_pkg_1005 IS
3965+
PROCEDURE private_proc(p_val NUMBER) IS
3966+
v_local NUMBER := p_val; -- local var initialized from parameter
3967+
BEGIN
3968+
NULL; -- private_proc does nothing visible
3969+
END private_proc;
3970+
FUNCTION get_value RETURN NUMBER IS
3971+
BEGIN
3972+
RETURN 42;
3973+
END get_value;
3974+
END test_pkg_1005;
3975+
/
3976+
-- Should return 42 without crash (private_proc is never called)
3977+
SELECT test_pkg_1005.get_value() FROM dual;
3978+
get_value
3979+
-----------
3980+
42
3981+
(1 row)
3982+
3983+
-- Test 2: Call the private procedure to verify it works when actually invoked
3984+
CREATE OR REPLACE PACKAGE BODY test_pkg_1005 IS
3985+
PROCEDURE private_proc(p_val NUMBER) IS
3986+
v_local NUMBER := p_val;
3987+
BEGIN
3988+
RAISE INFO 'v_local=%', v_local;
3989+
END private_proc;
3990+
FUNCTION get_value RETURN NUMBER IS
3991+
BEGIN
3992+
private_proc(100); -- now call private_proc
3993+
RETURN 42;
3994+
END get_value;
3995+
END test_pkg_1005;
3996+
/
3997+
-- Should print v_local=100 and return 42
3998+
SELECT test_pkg_1005.get_value() FROM dual;
3999+
INFO: v_local=100
4000+
get_value
4001+
-----------
4002+
42
4003+
(1 row)
4004+
4005+
DROP PACKAGE test_pkg_1005;
4006+
-- Test 3: Standalone function with nested procedure (not in package)
4007+
CREATE OR REPLACE FUNCTION test_func_1005(p_input NUMBER) RETURN NUMBER AS
4008+
v_result NUMBER := 0;
4009+
PROCEDURE helper(p_val NUMBER) IS
4010+
v_local NUMBER := p_val; -- local var initialized from parameter
4011+
BEGIN
4012+
v_result := v_local * 2;
4013+
END helper;
4014+
BEGIN
4015+
helper(p_input);
4016+
RETURN v_result;
4017+
END;
4018+
/
4019+
-- Should return 200 (100 * 2)
4020+
SELECT test_func_1005(100) FROM dual;
4021+
test_func_1005
4022+
----------------
4023+
200
4024+
(1 row)
4025+
4026+
DROP FUNCTION test_func_1005;
4027+
-- Test 4: Nested function with param-initialized local var that is never called
4028+
CREATE OR REPLACE FUNCTION test_func_1005_nocall(p_input NUMBER) RETURN NUMBER AS
4029+
FUNCTION never_called(p_val NUMBER) RETURN NUMBER IS
4030+
v_local NUMBER := p_val;
4031+
BEGIN
4032+
RETURN v_local;
4033+
END never_called;
4034+
BEGIN
4035+
-- never_called is declared but never invoked
4036+
RETURN p_input + 1;
4037+
END;
4038+
/
4039+
-- Should return 101 without any issues
4040+
SELECT test_func_1005_nocall(100) FROM dual;
4041+
test_func_1005_nocall
4042+
-----------------------
4043+
101
4044+
(1 row)
4045+
4046+
DROP FUNCTION test_func_1005_nocall;
4047+
-- Test 5: Multiple nested subprocs with param-initialized local vars
4048+
-- Note: func1 is intentionally not called due to issue #1124
4049+
CREATE OR REPLACE FUNCTION test_func_1005_multi(p_input NUMBER) RETURN NUMBER AS
4050+
v_sum NUMBER := 0;
4051+
PROCEDURE proc1(p_val NUMBER) IS
4052+
v_local1 NUMBER := p_val;
4053+
BEGIN
4054+
v_sum := v_sum + v_local1;
4055+
END proc1;
4056+
PROCEDURE proc2(p_val NUMBER) IS
4057+
v_local2 NUMBER := p_val * 2;
4058+
BEGIN
4059+
v_sum := v_sum + v_local2;
4060+
END proc2;
4061+
FUNCTION func1(p_val NUMBER) RETURN NUMBER IS
4062+
v_local3 NUMBER := p_val * 3;
4063+
BEGIN
4064+
RETURN v_local3;
4065+
END func1;
4066+
BEGIN
4067+
proc1(10); -- v_sum = 10
4068+
proc2(10); -- v_sum = 10 + 20 = 30
4069+
-- Not calling: v_sum := v_sum + func1(10) due to issue #1124
4070+
RETURN v_sum;
4071+
END;
4072+
/
4073+
-- Should return 30 (proc1 adds 10, proc2 adds 20)
4074+
SELECT test_func_1005_multi(10) FROM dual;
4075+
test_func_1005_multi
4076+
----------------------
4077+
30
4078+
(1 row)
4079+
4080+
DROP FUNCTION test_func_1005_multi;

src/pl/plisql/src/expected/plisql_type_rowtype.out

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,8 @@ ERROR: invalid input syntax for type integer: "a"
273273
CONTEXT: PL/iSQL function fun3(pg_catalog.int4) line 1 during statement block local variable initialization
274274
SELECT fun4(4) FROM dual; --failed
275275
ERROR: invalid input syntax for type integer: "a"
276-
CONTEXT: PL/iSQL function fun4(pg_catalog.int4) line 2 during statement block local variable initialization
276+
CONTEXT: PL/iSQL function inf1 line 2 during statement block local variable initialization
277+
PL/iSQL function fun4(pg_catalog.int4) line 7 at RETURN
277278
SELECT fun5(5) FROM dual; --successfully
278279
NOTICE: 5
279280
fun5
@@ -307,7 +308,8 @@ ERROR: invalid input syntax for type integer: "a"
307308
CONTEXT: PL/iSQL function fun3(pg_catalog.int4) line 1 during statement block local variable initialization
308309
SELECT fun4(4) FROM dual; --failed
309310
ERROR: invalid input syntax for type integer: "a"
310-
CONTEXT: PL/iSQL function fun4(pg_catalog.int4) line 2 during statement block local variable initialization
311+
CONTEXT: PL/iSQL function inf1 line 2 during statement block local variable initialization
312+
PL/iSQL function fun4(pg_catalog.int4) line 7 at RETURN
311313
SELECT fun5(5) FROM dual; --successfully
312314
NOTICE: 5
313315
fun5

src/pl/plisql/src/pl_comp.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3266,6 +3266,43 @@ plisql_finish_datums(PLiSQL_function * function)
32663266
}
32673267

32683268

3269+
/*
3270+
* plisql_datum_belongs_to_subproc
3271+
* Check if a datum index falls within any subproc's datum range.
3272+
*
3273+
* Subproc datums (parameters and local variables) should not be initialized
3274+
* by the parent block, as they have their own initialization logic.
3275+
* Each subproc's datum range is [lastassignvardno, lastoutvardno).
3276+
*/
3277+
static bool
3278+
plisql_datum_belongs_to_subproc(int datum_idx)
3279+
{
3280+
int j;
3281+
3282+
for (j = 0; j < plisql_nsubprocFuncs; j++)
3283+
{
3284+
PLiSQL_subproc_function *subproc = plisql_subprocFuncs[j];
3285+
3286+
/*
3287+
* Skip subprocs that don't have a function body yet (forward declarations).
3288+
* Also skip if lastoutvardno is 0 (not yet set).
3289+
*/
3290+
if (subproc->function == NULL || subproc->lastoutvardno == 0)
3291+
continue;
3292+
3293+
/*
3294+
* Check if datum_idx falls within [lastassignvardno, lastoutvardno).
3295+
* lastassignvardno is set before building subproc args.
3296+
* lastoutvardno is set after the subproc body is fully compiled.
3297+
*/
3298+
if (datum_idx >= subproc->lastassignvardno &&
3299+
datum_idx < subproc->lastoutvardno)
3300+
return true;
3301+
}
3302+
3303+
return false;
3304+
}
3305+
32693306
/* ----------
32703307
* plisql_add_initdatums Make an array of the datum numbers of
32713308
* all the initializable datums created since the last call
@@ -3292,6 +3329,19 @@ plisql_add_initdatums(int **varnos)
32923329
*/
32933330
for (i = datums_last; i < plisql_nDatums; i++)
32943331
{
3332+
/*
3333+
* Skip datums that belong to subprocs. These datums (parameters and
3334+
* local variables of nested functions/procedures) should not be
3335+
* initialized by the parent block. They have their own initialization
3336+
* logic within the subproc's execution context.
3337+
*
3338+
* This prevents crashes when a subproc's local variable has a default
3339+
* expression that references the subproc's parameters, which don't
3340+
* exist in the parent's execution context.
3341+
*/
3342+
if (plisql_datum_belongs_to_subproc(i))
3343+
continue;
3344+
32953345
switch (plisql_Datums[i]->dtype)
32963346
{
32973347
case PLISQL_DTYPE_VAR:
@@ -3313,6 +3363,10 @@ plisql_add_initdatums(int **varnos)
33133363
n = 0;
33143364
for (i = datums_last; i < plisql_nDatums; i++)
33153365
{
3366+
/* Skip subproc datums */
3367+
if (plisql_datum_belongs_to_subproc(i))
3368+
continue;
3369+
33163370
switch (plisql_Datums[i]->dtype)
33173371
{
33183372
case PLISQL_DTYPE_VAR:

src/pl/plisql/src/sql/plisql_nested_subproc.sql

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3770,3 +3770,123 @@ end; $$ language plisql;
37703770

37713771
DROP TABLE rec_typ2;
37723772
DROP TABLE r1;
3773+
3774+
-- Test for issue #1005: subproc with local variable initialized from parameter
3775+
-- This should not crash when the parent function is called
3776+
3777+
-- Test 1: Package with private procedure that has param-initialized local var
3778+
CREATE OR REPLACE PACKAGE test_pkg_1005 IS
3779+
FUNCTION get_value RETURN NUMBER;
3780+
END test_pkg_1005;
3781+
/
3782+
3783+
CREATE OR REPLACE PACKAGE BODY test_pkg_1005 IS
3784+
PROCEDURE private_proc(p_val NUMBER) IS
3785+
v_local NUMBER := p_val; -- local var initialized from parameter
3786+
BEGIN
3787+
NULL; -- private_proc does nothing visible
3788+
END private_proc;
3789+
3790+
FUNCTION get_value RETURN NUMBER IS
3791+
BEGIN
3792+
RETURN 42;
3793+
END get_value;
3794+
END test_pkg_1005;
3795+
/
3796+
3797+
-- Should return 42 without crash (private_proc is never called)
3798+
SELECT test_pkg_1005.get_value() FROM dual;
3799+
3800+
-- Test 2: Call the private procedure to verify it works when actually invoked
3801+
CREATE OR REPLACE PACKAGE BODY test_pkg_1005 IS
3802+
PROCEDURE private_proc(p_val NUMBER) IS
3803+
v_local NUMBER := p_val;
3804+
BEGIN
3805+
RAISE INFO 'v_local=%', v_local;
3806+
END private_proc;
3807+
3808+
FUNCTION get_value RETURN NUMBER IS
3809+
BEGIN
3810+
private_proc(100); -- now call private_proc
3811+
RETURN 42;
3812+
END get_value;
3813+
END test_pkg_1005;
3814+
/
3815+
3816+
-- Should print v_local=100 and return 42
3817+
SELECT test_pkg_1005.get_value() FROM dual;
3818+
3819+
DROP PACKAGE test_pkg_1005;
3820+
3821+
-- Test 3: Standalone function with nested procedure (not in package)
3822+
CREATE OR REPLACE FUNCTION test_func_1005(p_input NUMBER) RETURN NUMBER AS
3823+
v_result NUMBER := 0;
3824+
3825+
PROCEDURE helper(p_val NUMBER) IS
3826+
v_local NUMBER := p_val; -- local var initialized from parameter
3827+
BEGIN
3828+
v_result := v_local * 2;
3829+
END helper;
3830+
BEGIN
3831+
helper(p_input);
3832+
RETURN v_result;
3833+
END;
3834+
/
3835+
3836+
-- Should return 200 (100 * 2)
3837+
SELECT test_func_1005(100) FROM dual;
3838+
3839+
DROP FUNCTION test_func_1005;
3840+
3841+
-- Test 4: Nested function with param-initialized local var that is never called
3842+
CREATE OR REPLACE FUNCTION test_func_1005_nocall(p_input NUMBER) RETURN NUMBER AS
3843+
FUNCTION never_called(p_val NUMBER) RETURN NUMBER IS
3844+
v_local NUMBER := p_val;
3845+
BEGIN
3846+
RETURN v_local;
3847+
END never_called;
3848+
BEGIN
3849+
-- never_called is declared but never invoked
3850+
RETURN p_input + 1;
3851+
END;
3852+
/
3853+
3854+
-- Should return 101 without any issues
3855+
SELECT test_func_1005_nocall(100) FROM dual;
3856+
3857+
DROP FUNCTION test_func_1005_nocall;
3858+
3859+
-- Test 5: Multiple nested subprocs with param-initialized local vars
3860+
-- Note: func1 is intentionally not called due to issue #1124
3861+
CREATE OR REPLACE FUNCTION test_func_1005_multi(p_input NUMBER) RETURN NUMBER AS
3862+
v_sum NUMBER := 0;
3863+
3864+
PROCEDURE proc1(p_val NUMBER) IS
3865+
v_local1 NUMBER := p_val;
3866+
BEGIN
3867+
v_sum := v_sum + v_local1;
3868+
END proc1;
3869+
3870+
PROCEDURE proc2(p_val NUMBER) IS
3871+
v_local2 NUMBER := p_val * 2;
3872+
BEGIN
3873+
v_sum := v_sum + v_local2;
3874+
END proc2;
3875+
3876+
FUNCTION func1(p_val NUMBER) RETURN NUMBER IS
3877+
v_local3 NUMBER := p_val * 3;
3878+
BEGIN
3879+
RETURN v_local3;
3880+
END func1;
3881+
BEGIN
3882+
proc1(10); -- v_sum = 10
3883+
proc2(10); -- v_sum = 10 + 20 = 30
3884+
-- Not calling: v_sum := v_sum + func1(10) due to issue #1124
3885+
RETURN v_sum;
3886+
END;
3887+
/
3888+
3889+
-- Should return 30 (proc1 adds 10, proc2 adds 20)
3890+
SELECT test_func_1005_multi(10) FROM dual;
3891+
3892+
DROP FUNCTION test_func_1005_multi;

0 commit comments

Comments
 (0)