Skip to content

Commit 6c7236e

Browse files
authored
Merge pull request #1133 from rophy/fix/1124
fix(plisql): resolve variable reset bug in nested function expressions (#1124)
2 parents 5b69fab + 48ff3fe commit 6c7236e

File tree

4 files changed

+291
-4
lines changed

4 files changed

+291
-4
lines changed

src/pl/plisql/src/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ REGRESS = plisql_array plisql_call plisql_control plisql_copy plisql_domain \
6363
plisql_record plisql_cache plisql_simple plisql_transaction \
6464
plisql_trap plisql_trigger plisql_varprops plisql_nested_subproc \
6565
plisql_nested_subproc2 plisql_out_parameter plisql_type_rowtype \
66-
plisql_exception plisql_for_loop_implicit plisql_autonomous
66+
plisql_exception plisql_for_loop_implicit plisql_autonomous plisql_bugs
6767

6868
# where to find ora_gen_keywordlist.pl and subsidiary files
6969
TOOLSDIR = $(top_srcdir)/src/tools
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
--
2+
-- Test for GitHub Issue #1124: Variable Reset Bug in Nested Functions
3+
--
4+
-- Bug: When evaluating expressions like "v_sum := v_sum + nested_func()",
5+
-- variables show original values instead of modified ones.
6+
--
7+
-- Test 1: Variable modification + nested function call in expression
8+
-- This was the core bug - v_sum's modified value (109) was being lost
9+
-- during expression evaluation, causing v_sum + get_30(10) to be 0 + 30 = 30
10+
-- instead of 109 + 30 = 139
11+
CREATE OR REPLACE FUNCTION test_bug1124_basic() RETURN NUMBER AS
12+
v_sum NUMBER := 99;
13+
v_result NUMBER;
14+
FUNCTION get_30(p_val NUMBER) RETURN NUMBER IS
15+
BEGIN
16+
RETURN p_val * 3;
17+
END get_30;
18+
BEGIN
19+
-- Modify v_sum
20+
v_sum := v_sum + 10; -- v_sum = 109
21+
-- Expression with nested function - this was buggy
22+
v_result := v_sum + get_30(10); -- Should be 109 + 30 = 139
23+
RETURN v_result;
24+
END;
25+
/
26+
SELECT test_bug1124_basic() AS result FROM dual;
27+
result
28+
--------
29+
139
30+
(1 row)
31+
32+
DROP FUNCTION test_bug1124_basic;
33+
-- Test 2: Multiple expressions to ensure consistent behavior
34+
CREATE OR REPLACE FUNCTION test_bug1124_multiple() RETURN NUMBER AS
35+
v_sum NUMBER := 99;
36+
v_temp NUMBER;
37+
FUNCTION get_30(p_val NUMBER) RETURN NUMBER IS
38+
BEGIN
39+
RETURN p_val * 3;
40+
END get_30;
41+
BEGIN
42+
v_sum := v_sum + 10; -- v_sum = 109
43+
-- First expression
44+
v_temp := v_sum + get_30(10); -- Should be 139
45+
-- Second expression (same) - should also be 139
46+
v_temp := v_sum + get_30(10);
47+
-- Third expression
48+
v_temp := v_sum + get_30(10);
49+
RETURN v_temp;
50+
END;
51+
/
52+
SELECT test_bug1124_multiple() AS result FROM dual;
53+
result
54+
--------
55+
139
56+
(1 row)
57+
58+
DROP FUNCTION test_bug1124_multiple;
59+
-- Test 3: Nested procedure modifying parent variable + nested function in expression
60+
CREATE OR REPLACE FUNCTION test_bug1124_proc_and_func() RETURN NUMBER AS
61+
v_sum NUMBER := 99;
62+
v_temp NUMBER;
63+
PROCEDURE add_val(p_val NUMBER) IS
64+
BEGIN
65+
v_sum := v_sum + p_val;
66+
END add_val;
67+
FUNCTION get_30(p_val NUMBER) RETURN NUMBER IS
68+
BEGIN
69+
RETURN p_val * 3;
70+
END get_30;
71+
BEGIN
72+
-- Call nested procedure to modify v_sum
73+
add_val(10); -- v_sum = 109
74+
-- Expression with nested function
75+
v_temp := v_sum + get_30(10); -- Should be 109 + 30 = 139
76+
RETURN v_temp;
77+
END;
78+
/
79+
SELECT test_bug1124_proc_and_func() AS result FROM dual;
80+
result
81+
--------
82+
139
83+
(1 row)
84+
85+
DROP FUNCTION test_bug1124_proc_and_func;
86+
-- Test 4: Test with integer type (not just NUMBER)
87+
CREATE OR REPLACE FUNCTION test_bug1124_integer() RETURN integer AS
88+
v_sum integer := 99;
89+
v_result integer;
90+
FUNCTION get_30(p_val integer) RETURN integer IS
91+
BEGIN
92+
RETURN p_val * 3;
93+
END get_30;
94+
BEGIN
95+
v_sum := v_sum + 10; -- v_sum = 109
96+
v_result := v_sum + get_30(10); -- Should be 109 + 30 = 139
97+
RETURN v_result;
98+
END;
99+
/
100+
SELECT test_bug1124_integer() AS result FROM dual;
101+
result
102+
--------
103+
139
104+
(1 row)
105+
106+
DROP FUNCTION test_bug1124_integer;
107+
-- Test 5: Nested function on left side of expression
108+
CREATE OR REPLACE FUNCTION test_bug1124_order() RETURN NUMBER AS
109+
v_sum NUMBER := 99;
110+
v_temp NUMBER;
111+
FUNCTION get_30(p_val NUMBER) RETURN NUMBER IS
112+
BEGIN
113+
RETURN p_val * 3;
114+
END get_30;
115+
BEGIN
116+
v_sum := v_sum + 10; -- v_sum = 109
117+
-- Nested function result on left side
118+
v_temp := get_30(10) + v_sum; -- Should be 30 + 109 = 139
119+
RETURN v_temp;
120+
END;
121+
/
122+
SELECT test_bug1124_order() AS result FROM dual;
123+
result
124+
--------
125+
139
126+
(1 row)
127+
128+
DROP FUNCTION test_bug1124_order;

src/pl/plisql/src/pl_exec.c

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7387,10 +7387,38 @@ plisql_param_eval_var_ro(ExprState *state, ExprEvalStep *op,
73877387
/*
73887388
* Inlined version of exec_eval_datum() ... and while we're at it, force
73897389
* expanded datums to read-only.
7390+
*
7391+
* IMPORTANT (Bug #1124 fix): For varlena types that are NOT expanded
7392+
* objects, we must make a copy of the value into the eval_mcontext,
7393+
* rather than just returning a pointer to the variable's storage. This is
7394+
* because nested function calls (a PL/iSQL feature) can happen during
7395+
* expression evaluation, and when they return, they copy back their global
7396+
* variables to the parent. This copy-back operation frees the parent's old
7397+
* variable storage, which would leave us with a dangling pointer if we had
7398+
* just returned var->value directly.
7399+
*
7400+
* For expanded objects (like composite types), we use
7401+
* MakeExpandedObjectReadOnly which handles them properly.
7402+
*
7403+
* By copying to eval_mcontext here, we ensure the value survives for the
7404+
* duration of the expression evaluation, even if the original variable
7405+
* storage is freed and reallocated.
73907406
*/
7391-
*op->resvalue = MakeExpandedObjectReadOnly(var->value,
7392-
var->isnull,
7393-
-1);
7407+
if (!var->isnull && var->datatype->typlen == -1 &&
7408+
!VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(var->value)))
7409+
{
7410+
/* Non-expanded varlena type - make a copy in eval_mcontext */
7411+
MemoryContext oldcxt = MemoryContextSwitchTo(get_eval_mcontext(estate));
7412+
*op->resvalue = datumCopy(var->value, false, -1);
7413+
MemoryContextSwitchTo(oldcxt);
7414+
}
7415+
else
7416+
{
7417+
/* Expanded object, pass-by-value, or null - use standard handling */
7418+
*op->resvalue = MakeExpandedObjectReadOnly(var->value,
7419+
var->isnull,
7420+
-1);
7421+
}
73947422
*op->resnull = var->isnull;
73957423

73967424
/* safety check -- an assertion should be sufficient */
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
--
2+
-- Test for GitHub Issue #1124: Variable Reset Bug in Nested Functions
3+
--
4+
-- Bug: When evaluating expressions like "v_sum := v_sum + nested_func()",
5+
-- variables show original values instead of modified ones.
6+
--
7+
8+
-- Test 1: Variable modification + nested function call in expression
9+
-- This was the core bug - v_sum's modified value (109) was being lost
10+
-- during expression evaluation, causing v_sum + get_30(10) to be 0 + 30 = 30
11+
-- instead of 109 + 30 = 139
12+
CREATE OR REPLACE FUNCTION test_bug1124_basic() RETURN NUMBER AS
13+
v_sum NUMBER := 99;
14+
v_result NUMBER;
15+
16+
FUNCTION get_30(p_val NUMBER) RETURN NUMBER IS
17+
BEGIN
18+
RETURN p_val * 3;
19+
END get_30;
20+
BEGIN
21+
-- Modify v_sum
22+
v_sum := v_sum + 10; -- v_sum = 109
23+
24+
-- Expression with nested function - this was buggy
25+
v_result := v_sum + get_30(10); -- Should be 109 + 30 = 139
26+
27+
RETURN v_result;
28+
END;
29+
/
30+
31+
SELECT test_bug1124_basic() AS result FROM dual;
32+
DROP FUNCTION test_bug1124_basic;
33+
34+
-- Test 2: Multiple expressions to ensure consistent behavior
35+
CREATE OR REPLACE FUNCTION test_bug1124_multiple() RETURN NUMBER AS
36+
v_sum NUMBER := 99;
37+
v_temp NUMBER;
38+
39+
FUNCTION get_30(p_val NUMBER) RETURN NUMBER IS
40+
BEGIN
41+
RETURN p_val * 3;
42+
END get_30;
43+
BEGIN
44+
v_sum := v_sum + 10; -- v_sum = 109
45+
46+
-- First expression
47+
v_temp := v_sum + get_30(10); -- Should be 139
48+
49+
-- Second expression (same) - should also be 139
50+
v_temp := v_sum + get_30(10);
51+
52+
-- Third expression
53+
v_temp := v_sum + get_30(10);
54+
55+
RETURN v_temp;
56+
END;
57+
/
58+
59+
SELECT test_bug1124_multiple() AS result FROM dual;
60+
DROP FUNCTION test_bug1124_multiple;
61+
62+
-- Test 3: Nested procedure modifying parent variable + nested function in expression
63+
CREATE OR REPLACE FUNCTION test_bug1124_proc_and_func() RETURN NUMBER AS
64+
v_sum NUMBER := 99;
65+
v_temp NUMBER;
66+
67+
PROCEDURE add_val(p_val NUMBER) IS
68+
BEGIN
69+
v_sum := v_sum + p_val;
70+
END add_val;
71+
72+
FUNCTION get_30(p_val NUMBER) RETURN NUMBER IS
73+
BEGIN
74+
RETURN p_val * 3;
75+
END get_30;
76+
BEGIN
77+
-- Call nested procedure to modify v_sum
78+
add_val(10); -- v_sum = 109
79+
80+
-- Expression with nested function
81+
v_temp := v_sum + get_30(10); -- Should be 109 + 30 = 139
82+
83+
RETURN v_temp;
84+
END;
85+
/
86+
87+
SELECT test_bug1124_proc_and_func() AS result FROM dual;
88+
DROP FUNCTION test_bug1124_proc_and_func;
89+
90+
-- Test 4: Test with integer type (not just NUMBER)
91+
CREATE OR REPLACE FUNCTION test_bug1124_integer() RETURN integer AS
92+
v_sum integer := 99;
93+
v_result integer;
94+
95+
FUNCTION get_30(p_val integer) RETURN integer IS
96+
BEGIN
97+
RETURN p_val * 3;
98+
END get_30;
99+
BEGIN
100+
v_sum := v_sum + 10; -- v_sum = 109
101+
102+
v_result := v_sum + get_30(10); -- Should be 109 + 30 = 139
103+
104+
RETURN v_result;
105+
END;
106+
/
107+
108+
SELECT test_bug1124_integer() AS result FROM dual;
109+
DROP FUNCTION test_bug1124_integer;
110+
111+
-- Test 5: Nested function on left side of expression
112+
CREATE OR REPLACE FUNCTION test_bug1124_order() RETURN NUMBER AS
113+
v_sum NUMBER := 99;
114+
v_temp NUMBER;
115+
116+
FUNCTION get_30(p_val NUMBER) RETURN NUMBER IS
117+
BEGIN
118+
RETURN p_val * 3;
119+
END get_30;
120+
BEGIN
121+
v_sum := v_sum + 10; -- v_sum = 109
122+
123+
-- Nested function result on left side
124+
v_temp := get_30(10) + v_sum; -- Should be 30 + 109 = 139
125+
126+
RETURN v_temp;
127+
END;
128+
/
129+
130+
SELECT test_bug1124_order() AS result FROM dual;
131+
DROP FUNCTION test_bug1124_order;

0 commit comments

Comments
 (0)