Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/backend/parser/parse_func.c
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,34 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,

cancel_parser_errposition_callback(&pcbstate);

/*
* If we found a package function/procedure or subprocedure, the fargs
* list may have been reordered and expanded with default arguments. We
* need to rebuild the actual_arg_types array to match the new argument
* order, otherwise type coercion will fail when trying to match reordered
* arguments.
*
* For subprocedures, the fix in pl_subproc_function.c also rebuilds
* true_typeids (declared_arg_types) in declared order after reordering,
* so that both arrays match the reordered fargs.
*/
if ((function_from == FUNC_FROM_PACKAGE ||
function_from == FUNC_FROM_SUBPROCFUNC) &&
fdresult != FUNCDETAIL_NOTFOUND)
{
ListCell *lc;
int i = 0;

foreach(lc, fargs)
{
Node *arg = lfirst(lc);

actual_arg_types[i++] = exprType(arg);
}
/* Update nargs to reflect the reordered/expanded argument list */
nargs = i;
}

/*
* Check for various wrong-kind-of-routine cases.
*/
Expand Down
193 changes: 193 additions & 0 deletions src/pl/plisql/src/expected/plisql_call.out
Original file line number Diff line number Diff line change
Expand Up @@ -678,3 +678,196 @@ NOTICE: f_print_x(1)
NOTICE: f_get_x(2)
NOTICE: f_print_x(2)
ROLLBACK;
-- Test for issue #1006: Mixed positional and named parameters with variables
-- calling package procedures with default parameters
CREATE OR REPLACE PACKAGE test_mixed_params_pkg AS
PROCEDURE proc_with_defaults(
p1 VARCHAR2,
p2 VARCHAR2 DEFAULT 'default_p2',
p3 VARCHAR2 DEFAULT 'default_p3'
);
END test_mixed_params_pkg;
/
CREATE OR REPLACE PACKAGE BODY test_mixed_params_pkg AS
PROCEDURE proc_with_defaults(
p1 VARCHAR2,
p2 VARCHAR2 DEFAULT 'default_p2',
p3 VARCHAR2 DEFAULT 'default_p3'
) IS
BEGIN
RAISE NOTICE 'p1=%, p2=%, p3=%', p1, p2, p3;
END;
END test_mixed_params_pkg;
/
-- Test 1: All positional parameters (should work)
DO $$
BEGIN
test_mixed_params_pkg.proc_with_defaults('a', 'b', 'c');
END;
$$;
NOTICE: p1=a, p2=b, p3=c
-- Test 2: All named parameters with variable (should work)
DO $$
DECLARE
v_val VARCHAR2(20) := 'var_value';
BEGIN
test_mixed_params_pkg.proc_with_defaults(p1=>'a', p2=>'b', p3=>v_val);
END;
$$;
NOTICE: p1=a, p2=b, p3=var_value
-- Test 3: Mixed positional and named with literal (should work)
DO $$
BEGIN
test_mixed_params_pkg.proc_with_defaults('a', p3=>'c');
END;
$$;
NOTICE: p1=a, p2=default_p2, p3=c
-- Test 4: Mixed positional and named with variable (issue #1006)
-- This would fail before the fix with:
-- ERROR: failed to find conversion function from unknown to varchar2
DO $$
DECLARE
v_val VARCHAR2(20) := 'var_value';
BEGIN
test_mixed_params_pkg.proc_with_defaults('a', p3=>v_val);
END;
$$;
NOTICE: p1=a, p2=default_p2, p3=var_value
-- Test 5: Multiple variables with mixed notation
DO $$
DECLARE
v1 VARCHAR2(20) := 'value1';
v2 VARCHAR2(20) := 'value2';
BEGIN
test_mixed_params_pkg.proc_with_defaults(v1, p3=>v2);
END;
$$;
NOTICE: p1=value1, p2=default_p2, p3=value2
DROP PACKAGE test_mixed_params_pkg;
-- Test subprocedures with mixed positional and named parameters (Issue #1006)
-- This tests the same bug scenario but for subprocedures instead of packages
DO $$
DECLARE
v_name VARCHAR2(100) := 'Test Name';
v_value NUMBER := 42;
v_result VARCHAR2(200);

PROCEDURE test_subproc(p_id NUMBER, p_name VARCHAR2, p_value NUMBER DEFAULT 0) AS
BEGIN
RAISE NOTICE 'test_subproc called: id=%, name=%, value=%', p_id, p_name, p_value;
END;

FUNCTION test_subfunc(p_id NUMBER, p_name VARCHAR2, p_value NUMBER DEFAULT 0) RETURN VARCHAR2 AS
BEGIN
RETURN 'id=' || p_id || ', name=' || p_name || ', value=' || p_value;
END;
BEGIN
-- Test 1: All positional parameters
test_subproc(1, 'literal', 10);

-- Test 2: All named parameters with variables
test_subproc(p_id => 2, p_name => v_name, p_value => v_value);

-- Test 3: Mixed positional and named with literals
test_subproc(3, p_name => 'mixed literal');

-- Test 4: Mixed positional and named with variables (the bug case)
test_subproc(4, p_name => v_name);

-- Test 5: Multiple variables with mixed notation
test_subproc(5, p_name => v_name, p_value => v_value);

-- Test 6: Function with mixed parameters and variables
v_result := test_subfunc(6, p_name => v_name, p_value => v_value);
RAISE NOTICE 'Function result: %', v_result;
END;
$$ LANGUAGE plisql;
NOTICE: test_subproc called: id=1, name=literal, value=10
NOTICE: test_subproc called: id=2, name=Test Name, value=42
NOTICE: test_subproc called: id=3, name=mixed literal, value=0
NOTICE: test_subproc called: id=4, name=Test Name, value=0
NOTICE: test_subproc called: id=5, name=Test Name, value=42
NOTICE: Function result: id=6, name=Test Name, value=42
-- Test subprocedure overloading still works with the fix
DO $$
DECLARE
v_int INTEGER := 10;
v_str VARCHAR2(50) := 'hello';
v_result INTEGER;

FUNCTION overloaded(p_val INTEGER) RETURN INTEGER AS
BEGIN
RAISE NOTICE 'overloaded(INTEGER) called with %', p_val;
RETURN p_val * 2;
END;

FUNCTION overloaded(p_val VARCHAR2) RETURN INTEGER AS
BEGIN
RAISE NOTICE 'overloaded(VARCHAR2) called with %', p_val;
RETURN LENGTH(p_val);
END;

FUNCTION overloaded(p_id INTEGER, p_name VARCHAR2) RETURN INTEGER AS
BEGIN
RAISE NOTICE 'overloaded(INTEGER, VARCHAR2) called with %, %', p_id, p_name;
RETURN p_id + LENGTH(p_name);
END;
BEGIN
-- Test overload resolution with different types
v_result := overloaded(v_int);
RAISE NOTICE 'Result 1: %', v_result;

v_result := overloaded(v_str);
RAISE NOTICE 'Result 2: %', v_result;

-- Test overload with multiple args
v_result := overloaded(5, 'test');
RAISE NOTICE 'Result 3: %', v_result;

-- Test overload with named parameters
v_result := overloaded(p_id => v_int, p_name => v_str);
RAISE NOTICE 'Result 4: %', v_result;
END;
$$ LANGUAGE plisql;
NOTICE: overloaded(INTEGER) called with 10
NOTICE: Result 1: 20
NOTICE: overloaded(VARCHAR2) called with hello
NOTICE: Result 2: 5
NOTICE: overloaded(INTEGER, VARCHAR2) called with 5, test
NOTICE: Result 3: 9
NOTICE: overloaded(INTEGER, VARCHAR2) called with 10, hello
NOTICE: Result 4: 15
-- Minimal test case for issue #1006 subprocedure fix
-- This is the exact bug scenario: mixed positional + named params with variable
-- The bug occurs when argument types need coercion after reordering
-- Before fix: ERROR: failed to find conversion function from unknown to varchar2
DO $$
DECLARE
v_str VARCHAR2(50) := 'test_value';
v_num NUMBER := 99;

-- Test with different type combinations that require proper type alignment
PROCEDURE test_types(p_num NUMBER, p_str VARCHAR2, p_extra NUMBER DEFAULT 0) AS
BEGIN
RAISE NOTICE 'test_types: num=%, str=%, extra=%', p_num, p_str, p_extra;
END;
BEGIN
-- Case 1: Mixed positional + named with variable (varchar arg)
-- Call order: (1, p_str => v_str)
-- Declared order: (p_num NUMBER, p_str VARCHAR2, p_extra NUMBER)
test_types(1, p_str => v_str);

-- Case 2: Mixed positional + named with variable (number arg)
test_types(2, p_str => 'literal', p_extra => v_num);

-- Case 3: Named parameters in different order than declared
test_types(p_str => v_str, p_num => 3);

-- Case 4: All named with variables
test_types(p_num => v_num, p_str => v_str, p_extra => 10);
END;
$$ LANGUAGE plisql;
NOTICE: test_types: num=1, str=test_value, extra=0
NOTICE: test_types: num=2, str=literal, extra=99
NOTICE: test_types: num=3, str=test_value, extra=0
NOTICE: test_types: num=99, str=test_value, extra=10
25 changes: 25 additions & 0 deletions src/pl/plisql/src/pl_subproc_function.c
Original file line number Diff line number Diff line change
Expand Up @@ -1516,9 +1516,34 @@ plisql_get_subprocfunc_detail(ParseState *pstate,
}
}
if (fargnames != NIL || defaultnumber != NIL)
{
*fargs = plisql_expand_and_reorder_functionargs(pstate, subprocfunc, best_candidate->nargs,
*fargs, defaultnumber, argdefaults);

/*
* After reordering fargs to declared order, we must also rebuild
* true_typeids (which becomes declared_arg_types in the caller) to
* match. The original best_candidate->args is in call order for
* overload resolution, but make_fn_arguments needs declared order
* to match the reordered fargs.
*/
if (best_candidate->argnumbers != NULL)
{
Oid *declared_order_types;
ListCell *lc;
int i = 0;

declared_order_types = palloc(best_candidate->nargs * sizeof(Oid));
foreach(lc, subprocfunc->arg)
{
PLiSQL_function_argitem *argitem = lfirst(lc);

declared_order_types[i++] = argitem->type->typoid;
}
*true_typeids = declared_order_types;
}
}

if (subprocfunc->is_proc)
return FUNCDETAIL_PROCEDURE;
else
Expand Down
Loading
Loading