diff --git a/src/pl/ob_pl.cpp b/src/pl/ob_pl.cpp index 3c9672ad2..3f4fa0447 100644 --- a/src/pl/ob_pl.cpp +++ b/src/pl/ob_pl.cpp @@ -2748,7 +2748,7 @@ int ObPL::insert_error_msg(int errcode) return ret; } -int ObPL::check_trigger_arg(const ParamStore ¶ms, const ObPLFunction &func) +int ObPL::check_trigger_arg(ParamStore ¶ms, const ObPLFunction &func) { int ret = OB_SUCCESS; if (TriggerHandle::is_trigger_body_routine(func.get_package_id(), func.get_routine_id(), func.get_proc_type())) { @@ -2774,6 +2774,7 @@ int ObPL::check_trigger_arg(const ParamStore ¶ms, const ObPLFunction &func) ObPLRecord *record = reinterpret_cast(params.at(i).get_ext()); CK (OB_NOT_NULL(record)); CK (record->get_count() == (static_cast(udt))->get_member_count()); + OX (params.at(i).set_udt_id(udt_id)); } } } @@ -3205,6 +3206,7 @@ int ObPLExecState::init_complex_obj(ObIAllocator &allocator, if (OB_FAIL(ret)) { } else if (real_pl_type->is_ref_cursor_type() || real_pl_type->is_sys_refcursor_type()) { OX (obj.set_is_ref_cursor_type(true)); + OX (obj.set_extend(0, PL_REF_CURSOR_TYPE)); } else if (real_pl_type->is_udt_type()) { ObPLUDTNS ns(*schema_guard); OZ (ns.init_complex_obj(allocator, *real_pl_type, obj, false, set_null)); @@ -3226,6 +3228,92 @@ int ObPLExecState::init_complex_obj(ObIAllocator &allocator, return ret; } +// This function is designed to defend against the modification of stored procedures during execution, which could lead +// to unexpected parameters being passed to the stored procedure, resulting in unknown errors. +// This function can be removed after the feature of defending the compilation cache of the executing stored procedure +// from being eliminated on the PL cache side. +int ObPLExecState::defend_stored_routine_change(const ObObjParam &actual_param, const ObPLDataType &formal_param_type) +{ + int ret = OB_SUCCESS; + if (actual_param.is_null() || actual_param.is_pl_mock_default_param()) { + // no actual param type info(eg: out params), skip check + } else if (!actual_param.is_ext()) { + if (!formal_param_type.is_obj_type()) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("incorrect argument type, expected complex, but get basic type", + K(ret), K(formal_param_type), K(actual_param)); + } + } else if ((actual_param.is_ref_cursor_type() /* refcursor */ + || PL_REF_CURSOR_TYPE == actual_param.get_meta().get_extend_type() /* also refcursor */ + || PL_CURSOR_TYPE == actual_param.get_meta().get_extend_type() /* cursor */) + && formal_param_type.is_cursor_type()) { + // skip check + } else { // user defined type + uint64_t formal_udt_id = OB_INVALID_ID; + uint64_t actual_udt_id = OB_INVALID_ID; + + OV (formal_param_type.is_composite_type(), OB_INVALID_ARGUMENT, formal_param_type); + OX (formal_udt_id = formal_param_type.get_user_type_id()); + OV (OB_INVALID_ID != formal_udt_id, OB_ERR_UNEXPECTED, formal_param_type); + + OX (actual_udt_id = actual_param.get_udt_id()); + if (OB_SUCC(ret) && OB_INVALID_ID == actual_udt_id) { + if (PL_RECORD_TYPE == actual_param.get_meta().get_extend_type() + || PL_NESTED_TABLE_TYPE == actual_param.get_meta().get_extend_type() + || PL_ASSOCIATIVE_ARRAY_TYPE == actual_param.get_meta().get_extend_type() + || PL_VARRAY_TYPE == actual_param.get_meta().get_extend_type()) { + const pl::ObPLComposite *actual_composite = nullptr; + CK (OB_NOT_NULL(actual_composite = reinterpret_cast(actual_param.get_ext()))); + OX (actual_udt_id = actual_composite->get_id()); + OV (OB_INVALID_ID != actual_udt_id || actual_composite->is_collection(), // anonymous array has invalid udt id + OB_ERR_UNEXPECTED, KPC(actual_composite), actual_param); + } else { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("unexpected extend type without udt id", K(ret), K(actual_param)); + } + } + + if (OB_FAIL(ret)) { + } else if (OB_INVALID_ID == actual_udt_id || formal_udt_id == actual_udt_id) { + // skip anonymous array & same udt id + } else { + bool is_compatible = true; + OZ (ObPLResolver::check_composite_compatible(ctx_, actual_udt_id, formal_udt_id, is_compatible)); + if (OB_INVALID_ARGUMENT == ret) { + LOG_INFO("the type of actual param is not found in the current procedure's lexical scope, " + "should search caller's scope", + K(ret), K(actual_udt_id), K(formal_udt_id)); + ret = OB_SUCCESS; + const ObUserDefinedType *actual_type = nullptr; + const ObUserDefinedType *formal_type = nullptr; + ObPLContext *pl_ctx = nullptr; + ObPLExecState *caller = nullptr; + CK (OB_NOT_NULL(ctx_.exec_ctx_)); + CK (OB_NOT_NULL(ctx_.exec_ctx_->get_my_session())); + CK (OB_NOT_NULL(pl_ctx = ctx_.exec_ctx_->get_my_session()->get_pl_context())); + CK (pl_ctx->get_exec_stack().count() >= 2); + CK (OB_NOT_NULL(caller = pl_ctx->get_exec_stack().at(pl_ctx->get_exec_stack().count() - 2))); + // fetch actual param type from caller + OZ (caller->get_exec_ctx().get_user_type(actual_udt_id, actual_type)); + // fetch formal param type from callee + OZ (get_exec_ctx().get_user_type(formal_udt_id, formal_type)); + OV (OB_NOT_NULL(actual_type) && OB_NOT_NULL(formal_type), OB_ERR_UNEXPECTED, actual_udt_id, formal_udt_id); + OZ (ObPLResolver::check_composite_compatible(actual_type, formal_type, is_compatible)); + } + OV (is_compatible, OB_INVALID_ARGUMENT, formal_udt_id, actual_udt_id, formal_param_type, actual_param); + } + } + + if (OB_FAIL(ret)) { + LOG_WARN("param type not match, procedure/function could have been replaced", + K(ret), K(actual_param), K(formal_param_type)); + ret = OB_ERR_UNEXPECTED; // replace errno, indicating that it's an internal error + LOG_USER_ERROR(OB_ERR_UNEXPECTED, "param type not match, procedure/function could have been replaced"); + } + + return ret; +} + int ObPLExecState::check_anonymous_collection_compatible(ObPLComposite &composite, const ObPLDataType &dest_type, bool &need_cast) { int ret = OB_SUCCESS; @@ -3415,6 +3503,7 @@ int ObPLExecState::init_params(const ParamStore *params, bool is_anonymous) } } else if (func_.get_variables().at(i).is_ref_cursor_type()) { OX (param.set_is_ref_cursor_type(true)); + OX (param.set_extend(0, PL_REF_CURSOR_TYPE)); // CURSOR初始化为NULL } else if (func_.get_variables().at(i).is_cursor_type()) { // leave obj as null type, spi_init wil init it. @@ -3482,8 +3571,11 @@ do { \ * In Args of Anonymous, If need_to_check_type_ is true then check to convert. * else It will directly used by static sql, do not need to check. */ - if (func_.get_in_args().has_member(i)) { - const ObPLDataType &pl_type = func_.get_variables().at(i); + const ObPLDataType &pl_type = func_.get_variables().at(i); // formal param type + if (FAILEDx(defend_stored_routine_change(params->at(i), pl_type))) { + LOG_WARN("param type not match, procedure/function could have been replaced", + K(ret), K(i), K(params->at(i)), K(pl_type)); + } else if (func_.get_in_args().has_member(i)) { if (is_anonymous && !func_.get_params_info().at(i).flag_.need_to_check_type_) { OX (get_params().at(i) = params->at(i)); } else if (params->at(i).is_pl_mock_default_param()) { // 使用参数默认值 @@ -3604,7 +3696,9 @@ do { \ // same type, we already check this on resolve stage, here directly assign value to symbol. if (get_params().at(i).is_ref_cursor_type()) { get_params().at(i) = params->at(i); - get_params().at(i).set_is_ref_cursor_type(true); + get_params().at(i).set_is_ref_cursor_type(true); // last assignment statement could clear this flag + get_params().at(i).set_extend( + get_params().at(i).get_ext(), PL_REF_CURSOR_TYPE, get_params().at(i).get_val_len()); } else if (pl_type.is_collection_type() && OB_INVALID_ID == params->at(i).get_udt_id()) { ObPLComposite *composite = NULL; get_params().at(i) = params->at(i); @@ -3630,7 +3724,7 @@ do { \ } else { CHECK_NOT_NULL_VIOLATED(i, params->at(i)); if (OB_FAIL(ret)) { - } else if (func_.get_variables().at(i).is_obj_type()) { + } else if (pl_type.is_obj_type()) { // 纯OUT参数, 对于基础类型直接传入NULL的ObObj ObObj obj; // 基础类型apply一个空的OBJECT ObObjMeta null_meta = get_params().at(i).get_meta(); @@ -3643,8 +3737,7 @@ do { \ } OX (get_params().at(i).set_param_meta(null_meta)); } else if (is_anonymous - && (func_.get_variables().at(i).is_nested_table_type() - || func_.get_variables().at(i).is_varray_type()) + && (pl_type.is_nested_table_type() || pl_type.is_varray_type()) && params->at(i).is_ext()) { #ifndef OB_BUILD_ORACLE_PL ret = OB_NOT_SUPPORTED; @@ -3663,7 +3756,7 @@ do { \ // 这里先copy入参的值, 由init_complex_obj函数判断是否重新分配内存 OX (get_params().at(i) = params->at(i)); OZ (init_complex_obj(*(get_allocator()), - func_.get_variables().at(i), + pl_type, get_params().at(i), !(top_call_ && ctx_.exec_ctx_->get_sql_ctx()->is_execute_call_stmt_))); } diff --git a/src/pl/ob_pl.h b/src/pl/ob_pl.h index 053798839..4722ff763 100644 --- a/src/pl/ob_pl.h +++ b/src/pl/ob_pl.h @@ -729,10 +729,10 @@ public: virtual ~ObPLExecState(); int init(const ParamStore *params = NULL, bool is_anonymous = false); + int defend_stored_routine_change(const ObObjParam &actual_param, const ObPLDataType &formal_param_type); int check_routine_param_legal(ParamStore *params = NULL); int check_anonymous_collection_compatible(ObPLComposite &composite, const ObPLDataType &dest_type, bool &need_cast); int convert_composite(ObObjParam ¶m, const ObPLDataType &dest_type); - int init_params(const ParamStore *params = NULL, bool is_anonymous = false); int execute(); int final(int ret); @@ -1208,7 +1208,7 @@ public: static int simple_execute(ObPLExecCtx *ctx, int64_t argc, int64_t *argv); - static int check_trigger_arg(const ParamStore ¶ms, const ObPLFunction &func); + static int check_trigger_arg(ParamStore ¶ms, const ObPLFunction &func); std::pair& get_jit_lock() { return jit_lock_; } diff --git a/src/pl/ob_pl_resolver.cpp b/src/pl/ob_pl_resolver.cpp index 75c355d1c..960e32022 100644 --- a/src/pl/ob_pl_resolver.cpp +++ b/src/pl/ob_pl_resolver.cpp @@ -7330,22 +7330,11 @@ int ObPLResolver::check_in_param_type_legal(const ObIRoutineParam *param_info, is_legal)); } } else if (actually_type.get_user_type_id() != expected_type.get_user_type_id()) { -#ifdef OB_BUILD_ORACLE_PL - if (ObPlJsonUtil::is_pl_jsontype(actually_type.get_user_type_id())) { - OZ (check_composite_compatible(current_block_->get_namespace(), - expected_type.get_user_type_id(), - actually_type.get_user_type_id(), - is_legal)); - } else { -#endif - OZ (check_composite_compatible(current_block_->get_namespace(), - actually_type.get_user_type_id(), - expected_type.get_user_type_id(), - is_legal)); - } -#ifdef OB_BUILD_ORACLE_PL + OZ (check_composite_compatible(current_block_->get_namespace(), + actually_type.get_user_type_id(), + expected_type.get_user_type_id(), + is_legal)); } -#endif } else if (actually_type.is_composite_type() || expected_type.is_composite_type()) { if (actually_type.is_obj_type() && ObExtendType == actually_type.get_data_type()->get_obj_type()) { @@ -9798,67 +9787,97 @@ int ObPLResolver::build_raw_expr(const ParseNode *node, return ret; } -bool ObPLResolver::is_json_type_compatible(const ObUserDefinedType *left, const ObUserDefinedType *right) -{ +bool ObPLResolver::is_json_type_compatible(const ObUserDefinedType *actual_param_type, + const ObUserDefinedType *formal_param_type) { #ifdef OB_BUILD_ORACLE_PL - return (ObPlJsonUtil::is_pl_json_element_type(left->get_user_type_id()) - && (ObPlJsonUtil::is_pl_json_object_type(right->get_user_type_id()) - || ObPlJsonUtil::is_pl_json_array_type(right->get_user_type_id()))) ; + // TYPE JSON_OBJECT_T UNDER JSON_ELEMENT_T + // TYPE JSON_ARRAY_T UNDER JSON_ELEMENT_T + return (ObPlJsonUtil::is_pl_json_object_type(actual_param_type->get_user_type_id()) + || ObPlJsonUtil::is_pl_json_array_type(actual_param_type->get_user_type_id())) + && ObPlJsonUtil::is_pl_json_element_type(formal_param_type->get_user_type_id()); #else return false; #endif } int ObPLResolver::check_composite_compatible(const ObPLINS &ns, - uint64_t left_type_id, - uint64_t right_type_id, + uint64_t actual_param_type_id, + uint64_t formal_param_type_id, bool &is_compatible) { int ret = OB_SUCCESS; - const ObUserDefinedType *left_type = NULL; - const ObUserDefinedType *right_type = NULL; + const ObUserDefinedType *actual_param_type = nullptr; + const ObUserDefinedType *formal_param_type = nullptr; ObArenaAllocator allocator; is_compatible = false; - //NOTICE: do not call this function when left_type_id equal to right_type_id - CK (left_type_id != right_type_id); - OZ (ns.get_user_type(left_type_id, left_type, &allocator)); - OZ (ns.get_user_type(right_type_id, right_type, &allocator)); - CK (OB_NOT_NULL(left_type) && OB_NOT_NULL(right_type)); + // NOTICE: do not call this function when left_type_id equal to right_type_id + CK (actual_param_type_id != formal_param_type_id); + + // The type of the actual argument may not exist in the current function's lexical scope, an error needs to be + // reported to the upper level to search in the caller's lexical scope. Consider case below: + // ``` + // declare + // cursor cur is select * from some_table; + // begin + // for v in cur loop + // some_proc(v); + // end loop; + // end; + // ``` + // The type of actual param V passed to SOME_PROC can not be found in SOME_PROC's scope, it is recorded in the caller + // anonymous block's scope. + OZ (ns.get_user_type(actual_param_type_id, actual_param_type, &allocator)); + if (OB_FAIL(ret) || OB_ISNULL(actual_param_type)) { + LOG_WARN("failed to get user type of actual param", K(ret), K(actual_param_type_id)); + ret = OB_INVALID_ARGUMENT; // indicates that the type of the actual argument was not found + } + OV (OB_NOT_NULL(actual_param_type), OB_INVALID_ARGUMENT, actual_param_type_id, formal_param_type_id); + + // The type of the formal argument must exist in the current function's lexical scope. + OZ (ns.get_user_type(formal_param_type_id, formal_param_type, &allocator)); + CK (OB_NOT_NULL(formal_param_type)); + + OZ (check_composite_compatible(actual_param_type, formal_param_type, is_compatible)); + return ret; +} + +int ObPLResolver::check_composite_compatible(const ObUserDefinedType *actual_param_type, + const ObUserDefinedType *formal_param_type, + bool &is_compatible) +{ + int ret = OB_SUCCESS; // Assigning One Record Variable to Another // You can assign the value of one record variable to another record variable only in these cases: - // The two variables have the same RECORD type. - // The target variable is declared with a RECORD type, the source variable is declared with %ROWTYPE, - // their fields match in number and order, and corresponding fields have the same data type. - // For record components of composite variables, the types of the composite variables need not match. - if (OB_FAIL(ret)) { - } else if (is_json_type_compatible(left_type, right_type)) { + // 1. The two variables have the same RECORD type. + // 2. The target variable is declared with a RECORD type, the source variable is declared with %ROWTYPE, + // their fields match in number and order, and corresponding fields have the same data type. + // 3. For record components of composite variables, the types of the composite variables need not match. + if (is_json_type_compatible(actual_param_type, formal_param_type)) { is_compatible = true; - } else if (left_type->is_cursor_type() && right_type->is_cursor_type()) { + } else if (actual_param_type->is_cursor_type() && formal_param_type->is_cursor_type()) { is_compatible = true; - } else if (right_type->is_generic_type()) { - if ((right_type->is_generic_adt_type() - || right_type->is_generic_record_type()) - && left_type->is_record_type()) { + } else if (formal_param_type->is_generic_type()) { + if ((formal_param_type->is_generic_adt_type() || formal_param_type->is_generic_record_type()) + && actual_param_type->is_record_type()) { is_compatible = true; - } else if ((right_type->is_generic_varray_type() - || right_type->is_generic_v2_table_type() - || right_type->is_generic_table_type() - || right_type->is_generic_collection_type()) - && left_type->is_collection_type()) { + } else if ((formal_param_type->is_generic_varray_type() + || formal_param_type->is_generic_v2_table_type() + || formal_param_type->is_generic_table_type() + || formal_param_type->is_generic_collection_type()) + && actual_param_type->is_collection_type()) { is_compatible = true; - } else if (right_type->is_generic_ref_cursor_type() - && left_type->is_cursor_type()) { + } else if (formal_param_type->is_generic_ref_cursor_type() && actual_param_type->is_cursor_type()) { is_compatible = true; } - } else if (left_type->is_record_type() - && right_type->is_record_type() - && !left_type->is_udt_type() - && !right_type->is_udt_type() - && (left_type->is_rowtype_type() || right_type->is_rowtype_type())) { - const ObRecordType *left_r_type = static_cast(left_type); - const ObRecordType *right_r_type = static_cast(right_type); - CK (OB_NOT_NULL(left_r_type) && OB_NOT_NULL(right_r_type)); - OZ (left_r_type->is_compatble(*right_r_type, is_compatible)); + } else if (actual_param_type->is_record_type() + && formal_param_type->is_record_type() + && !actual_param_type->is_udt_type() + && !formal_param_type->is_udt_type() + && (actual_param_type->is_rowtype_type() || formal_param_type->is_rowtype_type())) { + const ObRecordType *actual_r_type = static_cast(actual_param_type); + const ObRecordType *formal_r_type = static_cast(formal_param_type); + CK (OB_NOT_NULL(actual_r_type) && OB_NOT_NULL(formal_r_type)); + OZ (actual_r_type->is_compatble(*formal_r_type, is_compatible)); } return ret; } @@ -10089,17 +10108,17 @@ int ObPLResolver::resolve_expr(const ParseNode *node, && expr->get_expr_type() != T_FUN_SYS_PDB_GET_RUNTIME_INFO) { bool is_compatible = false; OZ (check_composite_compatible(current_block_->get_namespace(), - expected_type->get_user_type_id(), expr->get_result_type().get_udt_id(), + expected_type->get_user_type_id(), is_compatible)); if (OB_FAIL(ret)) { } else if (!is_compatible) { ret = OB_ERR_INVALID_TYPE_FOR_OP; #ifdef OB_BUILD_ORACLE_PL // error code compiltable with oracle - if (ObPlJsonUtil::is_pl_json_object_type(expected_type->get_user_type_id()) - && ObPlJsonUtil::is_pl_json_element_type(expr->get_result_type().get_udt_id()) - && ObPlJsonUtil::is_pl_json_array_type(expr->get_result_type().get_udt_id())) { + if ((ObPlJsonUtil::is_pl_json_array_type(expected_type->get_user_type_id()) + || ObPlJsonUtil::is_pl_json_object_type(expected_type->get_user_type_id())) + && ObPlJsonUtil::is_pl_json_element_type(expr->get_result_type().get_udt_id())) { ret = OB_ERR_EXPRESSION_WRONG_TYPE; } #endif diff --git a/src/pl/ob_pl_resolver.h b/src/pl/ob_pl_resolver.h index c165ff539..abd99f34d 100644 --- a/src/pl/ob_pl_resolver.h +++ b/src/pl/ob_pl_resolver.h @@ -512,10 +512,15 @@ public: ObIArray& params, const ObUserDefinedType *user_type, ObPLDataType &pl_type); - static bool is_json_type_compatible( - const ObUserDefinedType *left, const ObUserDefinedType *right); + static bool is_json_type_compatible(const ObUserDefinedType *actual_param_type, + const ObUserDefinedType *formal_param_type); static int check_composite_compatible(const ObPLINS &ns, - uint64_t left_type_id, uint64_t right_type_id, bool &is_compatible); + uint64_t actual_param_type_id, + uint64_t formal_param_type_id, + bool &is_compatible); + static int check_composite_compatible(const ObUserDefinedType *actual_param_type, + const ObUserDefinedType *formal_param_type, + bool &is_compatible); static int resolve_nocopy_params(const share::schema::ObIRoutineInfo *routine_info, diff --git a/src/sql/engine/dml/ob_trigger_handler.h b/src/sql/engine/dml/ob_trigger_handler.h index e2cd5c450..54047376f 100644 --- a/src/sql/engine/dml/ob_trigger_handler.h +++ b/src/sql/engine/dml/ob_trigger_handler.h @@ -67,7 +67,8 @@ public: pl::ObProcType type) { bool is_trg_routine = false; - if (schema::ObTriggerInfo::is_trigger_body_package_id(package_id) && pl::ObProcType::PACKAGE_PROCEDURE == type) { + if (schema::ObTriggerInfo::is_trigger_body_package_id(package_id) + && (pl::ObProcType::PACKAGE_PROCEDURE == type || pl::ObProcType::PACKAGE_FUNCTION == type)) { if (lib::is_oracle_mode()) { is_trg_routine = (routine_id >= ROUTINE_IDX_CALC_WHEN && routine_id <= ROUTINE_IDX_AFTER_STMT); } else { diff --git a/src/sql/engine/user_defined_function/ob_pl_user_defined_agg_function.cpp b/src/sql/engine/user_defined_function/ob_pl_user_defined_agg_function.cpp index e9d2744ef..f085af259 100644 --- a/src/sql/engine/user_defined_function/ob_pl_user_defined_agg_function.cpp +++ b/src/sql/engine/user_defined_function/ob_pl_user_defined_agg_function.cpp @@ -231,7 +231,7 @@ int ObPlAggUdfFunction::build_in_params_store(ObObjParam &pl_obj, LOG_WARN("failed to push back param", K(ret)); } else if (obj_params != NULL && OB_FAIL(ObExprUDF::process_in_params(obj_params, param_num, params_desc, - params_type_, *udf_params, *allocator_))) { + params_type, *udf_params, *allocator_))) { LOG_WARN("failed to process in params", K(ret)); } else { LOG_TRACE("succeed to build in params store", K(pl_obj), K(obj_params), K(params_desc), diff --git a/src/sql/resolver/ob_resolver_utils.cpp b/src/sql/resolver/ob_resolver_utils.cpp index 0347e62a6..6b8b66261 100644 --- a/src/sql/resolver/ob_resolver_utils.cpp +++ b/src/sql/resolver/ob_resolver_utils.cpp @@ -1127,27 +1127,14 @@ int ObResolverUtils::check_type_match(const pl::ObPLResolveCtx &resolve_ctx, } else { // 复杂类型的TypeClass相同, 需要检查兼容性 bool is_compatible = false; -#ifdef OB_BUILD_ORACLE_PL - if (ObPlJsonUtil::is_pl_jsontype(src_type_id)) { - OZ (ObPLResolver::check_composite_compatible( - NULL == resolve_ctx.params_.secondary_namespace_ - ? static_cast(resolve_ctx) - : static_cast(*resolve_ctx.params_.secondary_namespace_), - dst_pl_type.get_user_type_id(), - src_type_id, - is_compatible), K(src_type_id), K(dst_pl_type), K(resolve_ctx.params_.is_execute_call_stmt_)); - } else { -#endif - OZ (ObPLResolver::check_composite_compatible( - NULL == resolve_ctx.params_.secondary_namespace_ - ? static_cast(resolve_ctx) - : static_cast(*resolve_ctx.params_.secondary_namespace_), - src_type_id, - dst_pl_type.get_user_type_id(), - is_compatible), K(src_type_id), K(dst_pl_type), K(resolve_ctx.params_.is_execute_call_stmt_)); -#ifdef OB_BUILD_ORACLE_PL - } -#endif + OZ (ObPLResolver::check_composite_compatible( + NULL == resolve_ctx.params_.secondary_namespace_ + ? static_cast(resolve_ctx) + : static_cast(*resolve_ctx.params_.secondary_namespace_), + src_type_id, + dst_pl_type.get_user_type_id(), + is_compatible), + K(src_type_id), K(dst_pl_type), K(resolve_ctx.params_.is_execute_call_stmt_)); if (OB_FAIL(ret)) { } else if (is_compatible) { OX (match_info = ObRoutineMatchInfo::MatchInfo(true, src_type, dst_type));