!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 Oid FuncNameAsType(List* funcname);
|
||||||
static Node* ParseComplexProjection(ParseState* pstate, char* funcname, Node* first_arg, int location);
|
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 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
|
* Parse a function call
|
||||||
@ -1820,7 +1822,14 @@ FuncDetailCode func_get_detail(List* funcname, List* fargs, List* fargnames, int
|
|||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_UNDEFINED_FUNCTION), errmodule(MOD_OPT), errmsg("not enough default arguments")));
|
(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)
|
if (pform->proisagg)
|
||||||
result = FUNCDETAIL_AGGREGATE;
|
result = FUNCDETAIL_AGGREGATE;
|
||||||
@ -2260,7 +2269,7 @@ Oid LookupAggNameTypeNames(List* aggname, List* argtypes, bool noError)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fetch default args if caller wants 'em
|
// 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;
|
HeapTuple tuple;
|
||||||
Form_pg_proc formproc;
|
Form_pg_proc formproc;
|
||||||
@ -2357,6 +2366,9 @@ static List* GetDefaultVale(Oid funcoid, const int* argnumbers, int ndargs)
|
|||||||
while (ndelete-- > 0) {
|
while (ndelete-- > 0) {
|
||||||
defaults = list_delete_first(defaults);
|
defaults = list_delete_first(defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*defaultValid = CheckDefaultArgsPosition(defaultargpos, pronargdefaults, ndargs,
|
||||||
|
pronallargs, pronargs, tuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argtypes != NULL)
|
if (argtypes != NULL)
|
||||||
@ -2376,6 +2388,49 @@ static List* GetDefaultVale(Oid funcoid, const int* argnumbers, int ndargs)
|
|||||||
return defaults;
|
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
|
* Replace column managed type with original type
|
||||||
* to identify overloaded functions
|
* 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},
|
{"allow_orderby_undistinct_column", OPT_ALLOW_ORDERBY_UNDISTINCT_COLUMN},
|
||||||
{"select_into_return_null", OPT_SELECT_INTO_RETURN_NULL},
|
{"select_into_return_null", OPT_SELECT_INTO_RETURN_NULL},
|
||||||
{"accept_empty_str", OPT_ACCEPT_EMPTY_STR},
|
{"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
|
// 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_SELECT_INTO_RETURN_NULL 67108864
|
||||||
#define OPT_ACCEPT_EMPTY_STR 134217728
|
#define OPT_ACCEPT_EMPTY_STR 134217728
|
||||||
#define OPT_PLPGSQL_DEPENDENCY 268435456
|
#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_FOR_LOOP 1
|
||||||
#define PLPSQL_OPT_OUTPARAM 2
|
#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 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 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 */
|
/* define database compatibility Attribute */
|
||||||
typedef struct {
|
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_array_of_record
|
||||||
#test: plpgsql_nest_compile
|
#test: plpgsql_nest_compile
|
||||||
test: arrayinterface_ted
|
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_cursor_rowtype
|
||||||
test: plpgsql_assign_list
|
test: plpgsql_assign_list
|
||||||
test: plpgsql_package_type plpgsql_package_param
|
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 for on update timestamp and generated column
|
||||||
test: on_update_session1 on_update_session2
|
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_array_of_record
|
||||||
#test: plpgsql_nest_compile
|
#test: plpgsql_nest_compile
|
||||||
test: arrayinterface_ted
|
test: arrayinterface_ted
|
||||||
test: plpgsql_inout_param
|
test: function_default_test plpgsql_inout_param
|
||||||
test: plpgsql_cursor_rowtype
|
test: plpgsql_cursor_rowtype
|
||||||
test: plpgsql_assign_list
|
test: plpgsql_assign_list
|
||||||
test: plpgsql_package_type plpgsql_package_param
|
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