!4752 处理issue: 存储过程调用default值传给了其他入参
Merge pull request !4752 from lukeman/master
This commit is contained in:
@ -39,8 +39,10 @@
|
||||
|
||||
static Oid FuncNameAsType(List* funcname);
|
||||
static Node* ParseComplexProjection(ParseState* pstate, char* funcname, Node* first_arg, int location);
|
||||
static List* GetDefaultVale(Oid funcoid, const int* argnumbers, int ndargs);
|
||||
static List* GetDefaultVale(Oid funcoid, const int* argnumbers, int ndargs, bool* defaultValid);
|
||||
static Oid cl_get_input_param_original_type(Oid func_oid, int argno);
|
||||
static bool CheckDefaultArgsPosition(int2vector* defaultargpos, int pronargdefaults, int ndargs,
|
||||
int pronallargs, int pronargs, HeapTuple procTup);
|
||||
|
||||
/*
|
||||
* Parse a function call
|
||||
@ -1820,7 +1822,14 @@ FuncDetailCode func_get_detail(List* funcname, List* fargs, List* fargnames, int
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_FUNCTION), errmodule(MOD_OPT), errmsg("not enough default arguments")));
|
||||
|
||||
*argdefaults = GetDefaultVale(*funcid, best_candidate->argnumbers, best_candidate->ndargs);
|
||||
bool defaultValid = true;
|
||||
*argdefaults = GetDefaultVale(*funcid, best_candidate->argnumbers, best_candidate->ndargs, &defaultValid);
|
||||
if (!defaultValid) {
|
||||
ereport(DEBUG3,
|
||||
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
||||
errmodule(MOD_OPT), errmsg("default arguments pos is not right.")));
|
||||
return FUNCDETAIL_NOTFOUND;
|
||||
}
|
||||
}
|
||||
if (pform->proisagg)
|
||||
result = FUNCDETAIL_AGGREGATE;
|
||||
@ -2260,7 +2269,7 @@ Oid LookupAggNameTypeNames(List* aggname, List* argtypes, bool noError)
|
||||
}
|
||||
|
||||
// fetch default args if caller wants 'em
|
||||
static List* GetDefaultVale(Oid funcoid, const int* argnumbers, int ndargs)
|
||||
static List* GetDefaultVale(Oid funcoid, const int* argnumbers, int ndargs, bool* defaultValid)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
Form_pg_proc formproc;
|
||||
@ -2357,6 +2366,9 @@ static List* GetDefaultVale(Oid funcoid, const int* argnumbers, int ndargs)
|
||||
while (ndelete-- > 0) {
|
||||
defaults = list_delete_first(defaults);
|
||||
}
|
||||
|
||||
*defaultValid = CheckDefaultArgsPosition(defaultargpos, pronargdefaults, ndargs,
|
||||
pronallargs, pronargs, tuple);
|
||||
}
|
||||
|
||||
if (argtypes != NULL)
|
||||
@ -2376,6 +2388,49 @@ static List* GetDefaultVale(Oid funcoid, const int* argnumbers, int ndargs)
|
||||
return defaults;
|
||||
}
|
||||
|
||||
static bool CheckDefaultArgs(int2vector* defaultargpos, int pronargdefaults, int pronallargs,
|
||||
int pronargs, HeapTuple proctup)
|
||||
{
|
||||
if (u_sess->attr.attr_sql.sql_compatibility != A_FORMAT || PROC_UNCHECK_DEFAULT_PARAM) {
|
||||
return true;
|
||||
}
|
||||
// if the func has out param, but did not enable_out_param_override, we don't check the defaultpos
|
||||
if (pronallargs > pronargs && !enable_out_param_override()) {
|
||||
return true;
|
||||
}
|
||||
// the pg_catalog's func can omit out param, so we don't check the defaultpos
|
||||
bool isnull = false;
|
||||
Datum namespaceDatum = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_pronamespace, &isnull);
|
||||
if (!isnull && IsAformatStyleFunctionOid(DatumGetObjectId(namespaceDatum))) {
|
||||
return true;
|
||||
}
|
||||
// if the func has the default args without position, we consider the position is at the end, no error
|
||||
if (defaultargpos->dim1 == 0 && pronargdefaults > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the parameter is in the appropriate position with default values
|
||||
*/
|
||||
static bool CheckDefaultArgsPosition(int2vector* defaultargpos, int pronargdefaults, int ndargs,
|
||||
int pronallargs, int pronargs, HeapTuple proctup)
|
||||
{
|
||||
if (CheckDefaultArgs(defaultargpos, pronargdefaults, pronallargs, pronargs, proctup)) {
|
||||
return true;
|
||||
}
|
||||
// Check whether the defaultpos are at the end of the parameter list from back to front
|
||||
int argIndex = pronallargs - 1;
|
||||
int defaultposIndex = pronargdefaults - 1;
|
||||
for (; argIndex >= pronallargs - ndargs; argIndex--, defaultposIndex--) {
|
||||
if (defaultargpos->values[defaultposIndex] != argIndex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace column managed type with original type
|
||||
* to identify overloaded functions
|
||||
|
@ -388,7 +388,8 @@ static const struct behavior_compat_entry behavior_compat_options[OPT_MAX] = {
|
||||
{"allow_orderby_undistinct_column", OPT_ALLOW_ORDERBY_UNDISTINCT_COLUMN},
|
||||
{"select_into_return_null", OPT_SELECT_INTO_RETURN_NULL},
|
||||
{"accept_empty_str", OPT_ACCEPT_EMPTY_STR},
|
||||
{"plpgsql_dependency", OPT_PLPGSQL_DEPENDENCY}
|
||||
{"plpgsql_dependency", OPT_PLPGSQL_DEPENDENCY},
|
||||
{"proc_uncheck_default_param", OPT_PROC_UNCHECK_DEFAULT_PARAM}
|
||||
};
|
||||
|
||||
// increase SQL_IGNORE_STRATEGY_NUM if we need more strategy
|
||||
|
@ -203,7 +203,8 @@ extern bool contain_backend_version(uint32 version_number);
|
||||
#define OPT_SELECT_INTO_RETURN_NULL 67108864
|
||||
#define OPT_ACCEPT_EMPTY_STR 134217728
|
||||
#define OPT_PLPGSQL_DEPENDENCY 268435456
|
||||
#define OPT_MAX 29
|
||||
#define OPT_PROC_UNCHECK_DEFAULT_PARAM 536870912
|
||||
#define OPT_MAX 30
|
||||
|
||||
#define PLPSQL_OPT_FOR_LOOP 1
|
||||
#define PLPSQL_OPT_OUTPARAM 2
|
||||
@ -246,6 +247,7 @@ extern bool contain_backend_version(uint32 version_number);
|
||||
|
||||
#define SELECT_INTO_RETURN_NULL (u_sess->utils_cxt.behavior_compat_flags & OPT_SELECT_INTO_RETURN_NULL)
|
||||
#define PLPGSQL_DEPENDENCY (u_sess->utils_cxt.behavior_compat_flags & OPT_PLPGSQL_DEPENDENCY)
|
||||
#define PROC_UNCHECK_DEFAULT_PARAM (u_sess->utils_cxt.behavior_compat_flags & OPT_PROC_UNCHECK_DEFAULT_PARAM)
|
||||
|
||||
/* define database compatibility Attribute */
|
||||
typedef struct {
|
||||
|
47
src/test/regress/expected/function_default_test.out
Normal file
47
src/test/regress/expected/function_default_test.out
Normal file
@ -0,0 +1,47 @@
|
||||
drop schema if exists function_default cascade;
|
||||
NOTICE: schema "function_default" does not exist, skipping
|
||||
create schema function_default;
|
||||
set current_schema = function_default;
|
||||
show behavior_compat_options;
|
||||
behavior_compat_options
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
|
||||
-- 创建存储过程
|
||||
create or replace procedure pro_default(p1 text,p2 int default 123,p3 int)
|
||||
as
|
||||
begin
|
||||
raise info 'p1:%',p1;
|
||||
raise info 'p2:%',p2;
|
||||
raise info 'p3:%',p3;
|
||||
end;
|
||||
/
|
||||
-- 调用存储过程,预期报错
|
||||
call pro_default('test',1);
|
||||
ERROR: function pro_default(unknown, integer) does not exist
|
||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
||||
|
||||
-- 开启逃生参数,保持原本表现
|
||||
set behavior_compat_options="proc_uncheck_default_param";
|
||||
show behavior_compat_options;
|
||||
behavior_compat_options
|
||||
----------------------------
|
||||
proc_uncheck_default_param
|
||||
(1 row)
|
||||
|
||||
call pro_default('test',1);
|
||||
INFO: p1:test
|
||||
INFO: p2:1
|
||||
INFO: p3:123
|
||||
pro_default
|
||||
-------------
|
||||
|
||||
(1 row)
|
||||
|
||||
|
||||
-- 清理环境
|
||||
drop schema function_default cascade;
|
||||
NOTICE: drop cascades to function pro_default(text,integer,integer)
|
||||
reset behavior_compat_options;
|
@ -760,7 +760,7 @@ test: plpgsql_assign_value_to_array_attribute
|
||||
test: plpgsql_array_of_record
|
||||
#test: plpgsql_nest_compile
|
||||
test: arrayinterface_ted
|
||||
test: plpgsql_inout_param record_slow_sql_in_proc
|
||||
test: function_default_test plpgsql_inout_param record_slow_sql_in_proc
|
||||
test: plpgsql_cursor_rowtype
|
||||
test: plpgsql_assign_list
|
||||
test: plpgsql_package_type plpgsql_package_param
|
||||
@ -1107,4 +1107,4 @@ test: enable_expr_fusion_flatten
|
||||
# test for on update timestamp and generated column
|
||||
test: on_update_session1 on_update_session2
|
||||
|
||||
test: ts_gb18030_utf8
|
||||
test: ts_gb18030_utf8
|
||||
|
@ -277,7 +277,7 @@ test: plpgsql_assign_value_to_array_attribute
|
||||
test: plpgsql_array_of_record
|
||||
#test: plpgsql_nest_compile
|
||||
test: arrayinterface_ted
|
||||
test: plpgsql_inout_param
|
||||
test: function_default_test plpgsql_inout_param
|
||||
test: plpgsql_cursor_rowtype
|
||||
test: plpgsql_assign_list
|
||||
test: plpgsql_package_type plpgsql_package_param
|
||||
|
25
src/test/regress/sql/function_default_test.sql
Executable file
25
src/test/regress/sql/function_default_test.sql
Executable file
@ -0,0 +1,25 @@
|
||||
drop schema if exists function_default cascade;
|
||||
create schema function_default;
|
||||
set current_schema = function_default;
|
||||
show behavior_compat_options;
|
||||
|
||||
-- 创建存储过程
|
||||
create or replace procedure pro_default(p1 text,p2 int default 123,p3 int)
|
||||
as
|
||||
begin
|
||||
raise info 'p1:%',p1;
|
||||
raise info 'p2:%',p2;
|
||||
raise info 'p3:%',p3;
|
||||
end;
|
||||
/
|
||||
-- 调用存储过程,预期报错
|
||||
call pro_default('test',1);
|
||||
|
||||
-- 开启逃生参数,保持原本表现
|
||||
set behavior_compat_options="proc_uncheck_default_param";
|
||||
show behavior_compat_options;
|
||||
call pro_default('test',1);
|
||||
|
||||
-- 清理环境
|
||||
drop schema function_default cascade;
|
||||
reset behavior_compat_options;
|
Reference in New Issue
Block a user