diff --git a/deps/oblib/src/lib/ob_name_def.h b/deps/oblib/src/lib/ob_name_def.h index a37967757e..36a6d9eba0 100644 --- a/deps/oblib/src/lib/ob_name_def.h +++ b/deps/oblib/src/lib/ob_name_def.h @@ -1075,4 +1075,5 @@ #define N_INEER_IS_TRUE "inner_is_true" #define N_INNER_DECODE_LIKE "inner_decode_like" #define N_EXTRACT_CERT_EXPIRED_TIME "extract_cert_expired_time" +#define N_INNER_ROW_CMP_VALUE "inner_row_cmp_value" #endif //OCEANBASE_LIB_OB_NAME_DEF_H_ diff --git a/src/objit/include/objit/common/ob_item_type.h b/src/objit/include/objit/common/ob_item_type.h index 12de97a117..8309cf98a1 100755 --- a/src/objit/include/objit/common/ob_item_type.h +++ b/src/objit/include/objit/common/ob_item_type.h @@ -867,6 +867,7 @@ typedef enum ObItemType T_FUN_SYS_DECODE_TRACE_ID = 1816, T_FUN_SYS_END = 2000, T_FUN_SYS_ALIGN_DATE4CMP = 2010, + T_FUN_SYS_INNER_ROW_CMP_VALUE = 2011, T_MAX_OP = 3000, diff --git a/src/sql/CMakeLists.txt b/src/sql/CMakeLists.txt index 111015c50f..eae22652ac 100644 --- a/src/sql/CMakeLists.txt +++ b/src/sql/CMakeLists.txt @@ -692,6 +692,7 @@ ob_set_subtarget(ob_sql engine_expr engine/expr/vector_cast/vector_cast.cpp engine/expr/ob_expr_extract_cert_expired_time.cpp engine/expr/ob_expr_transaction_id.cpp + engine/expr/ob_expr_inner_row_cmp_val.cpp ) ob_set_subtarget(ob_sql engine_join diff --git a/src/sql/engine/expr/ob_expr_eval_functions.cpp b/src/sql/engine/expr/ob_expr_eval_functions.cpp index 270e4a6989..e0b821811a 100644 --- a/src/sql/engine/expr/ob_expr_eval_functions.cpp +++ b/src/sql/engine/expr/ob_expr_eval_functions.cpp @@ -339,6 +339,7 @@ #include "ob_expr_align_date4cmp.h" #include "ob_expr_extract_cert_expired_time.h" #include "ob_expr_transaction_id.h" +#include "ob_expr_inner_row_cmp_val.h" namespace oceanbase { @@ -1115,6 +1116,7 @@ static ObExpr::EvalFunc g_expr_eval_functions[] = { NULL, //ObExprInnerTableOptionPrinter::eval_inner_table_option_printer, /* 672 */ NULL, //ObExprInnerTableSequenceGetter::eval_inner_table_sequence_getter, /* 673 */ NULL, //ObExprDecodeTraceId::calc_decode_trace_id_expr, /* 674 */ + ObExprInnerRowCmpVal::eval_inner_row_cmp_val, /* 675 */ }; static ObExpr::EvalBatchFunc g_expr_eval_batch_functions[] = { diff --git a/src/sql/engine/expr/ob_expr_inner_row_cmp_val.cpp b/src/sql/engine/expr/ob_expr_inner_row_cmp_val.cpp new file mode 100644 index 0000000000..6129131ee8 --- /dev/null +++ b/src/sql/engine/expr/ob_expr_inner_row_cmp_val.cpp @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2021 OceanBase + * OceanBase CE is licensed under Mulan PubL v2. + * You can use this software according to the terms and conditions of the Mulan PubL v2. + * You may obtain a copy of Mulan PubL v2 at: + * http://license.coscl.org.cn/MulanPubL-2.0 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PubL v2 for more details. + */ + +#define USING_LOG_PREFIX SQL_ENG +#include "sql/engine/expr/ob_expr_inner_row_cmp_val.h" +#include "lib/oblog/ob_log.h" +#include "sql/resolver/expr/ob_raw_expr.h" +#include "sql/engine/expr/ob_expr_util.h" + +namespace oceanbase +{ +using namespace common; +namespace sql +{ + +ObExprInnerRowCmpVal::ObExprInnerRowCmpVal(common::ObIAllocator &alloc) + : ObFuncExprOperator(alloc, T_FUN_SYS_INNER_ROW_CMP_VALUE, N_INNER_ROW_CMP_VALUE, 3, + NOT_VALID_FOR_GENERATED_COL, NOT_ROW_DIMENSION) +{ +} + +int ObExprInnerRowCmpVal::calc_result_type3(ObExprResType &type, + ObExprResType &type1, + ObExprResType &type2, + ObExprResType &type3, + ObExprTypeCtx &type_ctx) const +{ + int ret = OB_SUCCESS; + if (!ob_is_decimal_int(type1.get_type())) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("the 1th param type is unexpected", K(ret), K(type1)); + } else { + type2.set_calc_meta(type1.get_obj_meta()); + type2.set_calc_accuracy(type1.get_accuracy()); + const uint64_t cast_mode = type1.get_cast_mode(); + if (0 != (cast_mode & CM_CONST_TO_DECIMAL_INT_UP)) { + type2.add_cast_mode(CM_CONST_TO_DECIMAL_INT_DOWN); + } else if (0 != (cast_mode & CM_CONST_TO_DECIMAL_INT_DOWN)) { + type2.add_cast_mode(CM_CONST_TO_DECIMAL_INT_UP); + } else { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("unexpected cast mode", K(ret), K(cast_mode)); + } + } + if (OB_SUCC(ret)) { + type.set_meta(type3.get_obj_meta()); + type.set_accuracy(type3.get_accuracy()); + } + return ret; +} + +int ObExprInnerRowCmpVal::cg_expr(ObExprCGCtx &expr_cg_ctx, const ObRawExpr &raw_expr, + ObExpr &rt_expr) const +{ + int ret = OB_SUCCESS; + UNUSED(expr_cg_ctx); + if (rt_expr.arg_cnt_ != 3) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("the arg_cnt of eval_inner_row_cmp_val error.", K(ret), K(rt_expr.arg_cnt_)); + } else if (OB_ISNULL(rt_expr.args_[0]) || OB_ISNULL(rt_expr.args_[1]) || + OB_ISNULL(rt_expr.args_[2])) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("the arg of eval_inner_row_cmp_val is null.", K(ret), K(rt_expr)); + } else if (!ob_is_decimal_int_tc(rt_expr.args_[0]->datum_meta_.type_) || + !ob_is_decimal_int_tc(rt_expr.args_[1]->datum_meta_.type_)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("the 1th arg of eval_inner_row_cmp_val is invalid.", K(ret)); + } else if (rt_expr.args_[0]->datum_meta_.precision_ != rt_expr.args_[1]->datum_meta_.precision_ + || rt_expr.args_[0]->datum_meta_.scale_ != rt_expr.args_[1]->datum_meta_.scale_) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("datum meta mismatch", K(ret), K(rt_expr.args_[0]->datum_meta_), + K(rt_expr.args_[1]->datum_meta_)); + } else { + rt_expr.eval_func_ = eval_inner_row_cmp_val; + rt_expr.extra_ = raw_expr.get_extra(); + } + return ret; +} + +int ObExprInnerRowCmpVal::eval_inner_row_cmp_val(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &res) +{ + int ret = OB_SUCCESS; + ObDatum* left_datum = NULL; + ObDatum* right_datum = NULL; + ObDatum* real_val_datum = NULL; + if (OB_FAIL(expr.args_[0]->eval(ctx, left_datum))) { + LOG_WARN("fail to eval eq expr", K(ret), K(expr)); + } else if (OB_FAIL(expr.args_[1]->eval(ctx, right_datum))) { + LOG_WARN("fail to eval eq expr", K(ret), K(expr)); + } else if ((left_datum->is_null() != right_datum->is_null()) || + left_datum->len_ != right_datum->len_) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("datum result mismatch", K(ret), K(*left_datum), K(*right_datum)); + } else if (!left_datum->is_null() && + (0 != MEMCMP(left_datum->ptr_, right_datum->ptr_, left_datum->len_))) { + ret = -static_cast(expr.extra_); + } else if (OB_FAIL(expr.args_[2]->eval(ctx, real_val_datum))) { + LOG_WARN("fail to eval real res datum fail.", K(ret), K(expr)); + } else { + res.set_datum(*real_val_datum); + } + return ret; +} + +} +} \ No newline at end of file diff --git a/src/sql/engine/expr/ob_expr_inner_row_cmp_val.h b/src/sql/engine/expr/ob_expr_inner_row_cmp_val.h new file mode 100644 index 0000000000..0f9461ee8d --- /dev/null +++ b/src/sql/engine/expr/ob_expr_inner_row_cmp_val.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2021 OceanBase + * OceanBase CE is licensed under Mulan PubL v2. + * You can use this software according to the terms and conditions of the Mulan PubL v2. + * You may obtain a copy of Mulan PubL v2 at: + * http://license.coscl.org.cn/MulanPubL-2.0 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PubL v2 for more details. + */ + +#ifndef _OB_EXPR_INNER_ROW_CMP_VAL_H_ +#define _OB_EXPR_INNER_ROW_CMP_VAL_H_ + +#include "sql/engine/expr/ob_expr_operator.h" + +namespace oceanbase +{ +namespace sql +{ +class ObExprInnerRowCmpVal : public ObFuncExprOperator +{ +public: + explicit ObExprInnerRowCmpVal(common::ObIAllocator &alloc); + virtual ~ObExprInnerRowCmpVal() {}; + + virtual int calc_result_type3(ObExprResType &type, + ObExprResType &type1, + ObExprResType &type2, + ObExprResType &type3, + common::ObExprTypeCtx &type_ctx) const; + virtual int cg_expr(ObExprCGCtx &expr_cg_ctx, const ObRawExpr &raw_expr, + ObExpr &rt_expr) const override; + + static int eval_inner_row_cmp_val(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &res); + +private: + //disallow copy + DISALLOW_COPY_AND_ASSIGN(ObExprInnerRowCmpVal); +}; +} +} +#endif /* _OB_EXPR_INNER_ROW_CMP_VAL_H_ */ diff --git a/src/sql/engine/expr/ob_expr_operator.cpp b/src/sql/engine/expr/ob_expr_operator.cpp index e1893a18ff..d6904e33a3 100644 --- a/src/sql/engine/expr/ob_expr_operator.cpp +++ b/src/sql/engine/expr/ob_expr_operator.cpp @@ -3101,6 +3101,20 @@ int ObSubQueryRelationalExpr::calc_result_typeN(ObExprResType &type, ObExprResType tmp_res_type; OZ(ObRelationalExprOperator::deduce_cmp_type( *this, tmp_res_type, types[i], types[i + row_dimension_], type_ctx)); + // For the multi-dimensional comparison of decimal int and varchar (for T_OP_SQ_LT and + // T_OP_SQ_LT), we set the calc type to number to avoid the correctness problem introduced + // by the loss of precision of one-sided cast. + if ((type_ == T_OP_SQ_LT || type_ == T_OP_SQ_GT) && + ob_is_decimal_int(tmp_res_type.get_calc_meta().get_type())) { + ObExprResType &left = types[i]; + ObExprResType &right = types[i + row_dimension_]; + if ((ob_is_decimal_int_tc(left.get_type()) && ob_is_string_or_lob_type(right.get_type())) || + (ob_is_string_or_lob_type(left.get_type()) && ob_is_decimal_int_tc(right.get_type()))) { + left.set_calc_type(ObNumberType); + right.set_calc_type(ObNumberType); + tmp_res_type.set_calc_type(ObNumberType); + } + } OZ(type.get_row_calc_cmp_types().push_back(tmp_res_type.get_calc_meta())); if (OB_SUCC(ret)) { if (ob_is_string_type(tmp_res_type.get_calc_type())) { @@ -6541,11 +6555,21 @@ int ObRelationalExprOperator::row_cmp( // locate first non-equal pair for (; OB_SUCC(ret) && i < expr.inner_func_cnt_; i++) { if (OB_FAIL(l_row[i]->eval(l_ctx, left))) { - LOG_WARN("failed to eval left in row cmp", K(ret)); + if (OB_FAIL(try_get_inner_row_cmp_ret(ret, first_nonequal_cmp_ret))) { + LOG_WARN("failed to eval left in row cmp", K(ret)); + } else { + --i; + break; + } } else if (left->is_null()) { cnt_row_null = true; } else if (OB_FAIL(r_row[i]->eval(r_ctx, right))) { - LOG_WARN("failed to eval right in row cmp", K(ret)); + if (OB_FAIL(try_get_inner_row_cmp_ret(ret, first_nonequal_cmp_ret))) { + LOG_WARN("failed to eval right in row cmp", K(ret)); + } else { + --i; + break; + } } else if (right->is_null()) { cnt_row_null = true; } else if (OB_FAIL(((DatumCmpFunc)expr.inner_functions_[i])(*left, *right, first_nonequal_cmp_ret))) { @@ -6580,6 +6604,20 @@ int ObRelationalExprOperator::row_cmp( return ret; } +template +int ObRelationalExprOperator::try_get_inner_row_cmp_ret(const int ret_code, int &cmp_ret) +{ + int ret = OB_SUCCESS; + if (ret_code == OB_ERR_MAX_VALUE) { + cmp_ret = IS_LEFT ? 1 : -1; + } else if (ret_code == OB_ERR_MIN_VALUE) { + cmp_ret = IS_LEFT ? -1 : 1; + } else { + ret = ret_code; + } + return ret; +} + void *ObInplaceAllocator::alloc(const int64_t size) { void *mem = NULL; diff --git a/src/sql/engine/expr/ob_expr_operator.h b/src/sql/engine/expr/ob_expr_operator.h index 6eeafbae9f..7e4287d353 100644 --- a/src/sql/engine/expr/ob_expr_operator.h +++ b/src/sql/engine/expr/ob_expr_operator.h @@ -1194,6 +1194,8 @@ public: // CAUTION: null safe equal row compare is not included. static int row_cmp(const ObExpr &expr, ObDatum &expr_datum, ObExpr **l_row, ObEvalCtx &l_ctx, ObExpr **r_row, ObEvalCtx &r_ctx); + template + static int try_get_inner_row_cmp_ret(const int ret_code, int &cmp_ret); OB_INLINE static int get_comparator_operands( const ObExpr &expr, diff --git a/src/sql/engine/expr/ob_expr_operator_factory.cpp b/src/sql/engine/expr/ob_expr_operator_factory.cpp index 2ce4e998d9..36b268e01c 100644 --- a/src/sql/engine/expr/ob_expr_operator_factory.cpp +++ b/src/sql/engine/expr/ob_expr_operator_factory.cpp @@ -405,6 +405,7 @@ #include "sql/engine/expr/ob_expr_align_date4cmp.h" #include "sql/engine/expr/ob_expr_extract_cert_expired_time.h" #include "sql/engine/expr/ob_expr_transaction_id.h" +#include "sql/engine/expr/ob_expr_inner_row_cmp_val.h" using namespace oceanbase::common; namespace oceanbase @@ -1003,6 +1004,7 @@ void ObExprOperatorFactory::register_expr_operators() REG_OP(ObExprAlignDate4Cmp); REG_OP(ObExprExtractExpiredTime); REG_OP(ObExprTransactionId); + REG_OP(ObExprInnerRowCmpVal); }(); // 注册oracle系统函数 REG_OP_ORCL(ObExprSysConnectByPath); @@ -1315,6 +1317,7 @@ void ObExprOperatorFactory::register_expr_operators() REG_OP_ORCL(ObExprTempTableSSID); REG_OP_ORCL(ObExprJsonObjectStar); REG_OP_ORCL(ObExprTransactionId); + REG_OP_ORCL(ObExprInnerRowCmpVal); } bool ObExprOperatorFactory::is_expr_op_type_valid(ObExprOperatorType type) diff --git a/src/sql/ob_sql_utils.cpp b/src/sql/ob_sql_utils.cpp index d46a09a6f7..7255f90fdb 100644 --- a/src/sql/ob_sql_utils.cpp +++ b/src/sql/ob_sql_utils.cpp @@ -417,6 +417,17 @@ int ObSQLUtils::calc_const_or_calculable_expr( // do nothing } else if (OB_FAIL(calc_const_expr(*exec_ctx, raw_expr, result, allocator, *params))) { + if (T_FUN_SYS_INNER_ROW_CMP_VALUE == raw_expr->get_expr_type()) { + if (ret == OB_ERR_MIN_VALUE) { + result.set_min_value(); + is_valid = true; + ret = OB_SUCCESS; + } else if (ret == OB_ERR_MAX_VALUE) { + result.set_max_value(); + is_valid = true; + ret = OB_SUCCESS; + } + } if (ignore_failure && !IS_SPATIAL_EXPR(raw_expr->get_expr_type())) { LOG_TRACE("failed to calc const expr, ignore the failure", K(ret)); ret = OB_SUCCESS; @@ -429,7 +440,13 @@ int ObSQLUtils::calc_const_or_calculable_expr( LOG_WARN("failed to store result to ctx", K(ret)); } } - if (OB_SUCC(ret) && !is_valid) { + bool add_calc_failure_cons = false; + if (OB_SUCC(ret) && T_FUN_SYS_INNER_ROW_CMP_VALUE == raw_expr->get_expr_type()) { + if (result.is_min_value() || result.is_max_value()) { + add_calc_failure_cons = true; + } + } + if (OB_SUCC(ret) && (!is_valid || add_calc_failure_cons)) { if (NULL == constraints) { // do nothing } else if (OB_FAIL(add_calc_failure_constraint(raw_expr, *constraints))) { diff --git a/src/sql/optimizer/ob_optimizer_util.cpp b/src/sql/optimizer/ob_optimizer_util.cpp index 63632c1ba0..e65c1370a2 100644 --- a/src/sql/optimizer/ob_optimizer_util.cpp +++ b/src/sql/optimizer/ob_optimizer_util.cpp @@ -6210,6 +6210,10 @@ int ObOptimizerUtil::get_expr_without_lossless_cast(const ObRawExpr* ori_expr, if (OB_ISNULL(ori_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); + } else if (ori_expr->get_expr_type() == T_FUN_SYS_INNER_ROW_CMP_VALUE) { + if (OB_FAIL(get_expr_without_lossless_cast(ori_expr->get_param_expr(2), expr))) { + LOG_WARN("failed to check is lossless column cast", K(ret)); + } } else if (OB_FAIL(is_lossless_column_cast(ori_expr, is_lossless))) { LOG_WARN("failed to check is lossless column cast", K(ret)); } else if (is_lossless) { diff --git a/src/sql/resolver/expr/ob_raw_expr.cpp b/src/sql/resolver/expr/ob_raw_expr.cpp index b0fa9142e5..ef49038ef7 100644 --- a/src/sql/resolver/expr/ob_raw_expr.cpp +++ b/src/sql/resolver/expr/ob_raw_expr.cpp @@ -4167,6 +4167,9 @@ int ObSysFunRawExpr::get_name_internal(char *buf, const int64_t buf_len, int64_t } } else if (T_FUN_SYS_PART_ID == get_expr_type()) { //ignore the print of T_FUN_SYS_PART_ID expr + } else if (T_FUN_SYS_INNER_ROW_CMP_VALUE == get_expr_type()) { + CK(3 == get_param_count()); + OZ(get_param_expr(2)->get_name(buf, buf_len, pos, type)); } else { int64_t i = 0; if (get_param_count() > 1) { diff --git a/src/sql/resolver/expr/ob_raw_expr_deduce_type.cpp b/src/sql/resolver/expr/ob_raw_expr_deduce_type.cpp index 72889b1801..5333bb17ad 100644 --- a/src/sql/resolver/expr/ob_raw_expr_deduce_type.cpp +++ b/src/sql/resolver/expr/ob_raw_expr_deduce_type.cpp @@ -471,6 +471,9 @@ int ObRawExprDeduceType::calc_result_type(ObNonTerminalRawExpr &expr, if (op->get_result_type().has_result_flag(DECIMAL_INT_ADJUST_FLAG)) { result_type.set_result_flag(DECIMAL_INT_ADJUST_FLAG); } + if (ob_is_decimal_int_tc(expr.get_result_type().get_type())) { + result_type.add_cast_mode(expr.get_result_type().get_cast_mode()); + } // 预先把所有参数的calc_type都设置成和type一致, // 以防calc_result_typeX没有对其进行设置 diff --git a/src/sql/resolver/expr/ob_raw_expr_printer.cpp b/src/sql/resolver/expr/ob_raw_expr_printer.cpp index 5d20a41c28..d2c8b627e5 100644 --- a/src/sql/resolver/expr/ob_raw_expr_printer.cpp +++ b/src/sql/resolver/expr/ob_raw_expr_printer.cpp @@ -3249,6 +3249,12 @@ int ObRawExprPrinter::print(ObSysFunRawExpr *expr) OZ(inner_print_fun_params(*expr)); break; } + case T_FUN_SYS_INNER_ROW_CMP_VALUE: { + CK(3 == expr->get_param_count()); + DATA_PRINTF("inner_row_cmp_value"); + PRINT_EXPR(expr->get_param_expr(2)); + break; + } default: { DATA_PRINTF("%.*s", LEN_AND_PTR(func_name)); OZ(inner_print_fun_params(*expr)); diff --git a/src/sql/resolver/expr/ob_raw_expr_util.cpp b/src/sql/resolver/expr/ob_raw_expr_util.cpp index ec4b4709be..d0cd343156 100644 --- a/src/sql/resolver/expr/ob_raw_expr_util.cpp +++ b/src/sql/resolver/expr/ob_raw_expr_util.cpp @@ -7710,6 +7710,8 @@ int ObRawExprUtils::create_real_cast_expr(ObRawExprFactory &expr_factory, } if (OB_FAIL(func_expr->add_param_expr(dst_expr))) { LOG_WARN("add dest type expr failed", K(ret)); + } else { + func_expr->set_result_type(dst_type); } LOG_DEBUG("create_cast_expr debug", K(ret), K(*src_expr), K(dst_type), K(*func_expr), K(lbt()), K(func_expr)); @@ -8493,6 +8495,40 @@ int ObRawExprUtils::build_pack_expr(ObRawExprFactory &expr_factory, return ret; } +int ObRawExprUtils::build_inner_row_cmp_expr(ObRawExprFactory &expr_factory, + const ObSQLSessionInfo *session_info, + ObRawExpr *cast_expr, + ObRawExpr *input_expr, + ObRawExpr *next_expr, + const uint64_t ret_code, + ObSysFunRawExpr *&new_expr) +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(session_info) || OB_ISNULL(cast_expr) || + OB_ISNULL(input_expr) || OB_ISNULL(next_expr)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("get unexpected null", K(ret), K(session_info), K(cast_expr), K(input_expr), + K(next_expr)); + } else if (OB_FAIL(expr_factory.create_raw_expr(T_FUN_SYS_INNER_ROW_CMP_VALUE, new_expr))) { + LOG_WARN("create inner row cmp value expr failed", K(ret)); + } else if (OB_ISNULL(new_expr)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("inner row cmp value expr is null", K(ret)); + } else if (OB_FAIL(new_expr->add_param_expr(cast_expr))) { + LOG_WARN("fail to add param expr", K(ret)); + } else if (OB_FAIL(new_expr->add_param_expr(input_expr))) { + LOG_WARN("fail to add param expr", K(ret)); + } else if (OB_FAIL(new_expr->add_param_expr(next_expr))) { + LOG_WARN("fail to add param expr", K(ret)); + } else if (OB_FAIL(new_expr->formalize(session_info))) { + LOG_WARN("fail to formalize expr", K(*new_expr), K(ret)); + } else { + new_expr->set_func_name("INTERNAL_FUNCTION"); + new_expr->set_extra(ret_code); + } + return ret; +} + // 这个函数只会在 pl 里被调到,会设置 ObRawExpr 的被调用模式,用于区分是在 pl 还是 sql 中被调用 int ObRawExprUtils::set_call_in_pl(ObRawExpr *&raw_expr) { diff --git a/src/sql/resolver/expr/ob_raw_expr_util.h b/src/sql/resolver/expr/ob_raw_expr_util.h index e1106a0634..da2d06578f 100644 --- a/src/sql/resolver/expr/ob_raw_expr_util.h +++ b/src/sql/resolver/expr/ob_raw_expr_util.h @@ -1068,6 +1068,13 @@ public: const ObIArray *field_array, const ObIArray &input_exprs, ObRawExpr *&pack_expr); + static int build_inner_row_cmp_expr(ObRawExprFactory &expr_factory, + const ObSQLSessionInfo *session_info, + ObRawExpr *cast_expr, + ObRawExpr *input_expr, + ObRawExpr *next_expr, + const uint64_t ret_code, + ObSysFunRawExpr *&new_expr); static int set_call_in_pl(ObRawExpr *&raw_expr); diff --git a/src/sql/rewrite/ob_query_range.cpp b/src/sql/rewrite/ob_query_range.cpp index 5bbd575900..fce5e152b7 100644 --- a/src/sql/rewrite/ob_query_range.cpp +++ b/src/sql/rewrite/ob_query_range.cpp @@ -1989,15 +1989,22 @@ int ObQueryRange::get_row_key_part(const ObRawExpr *l_expr, bool is_bound_modified = false; const ObRawExpr *l_expr = l_row->get_param_expr(i); const ObRawExpr *r_expr = r_row->get_param_expr(i); - if (OB_FAIL(check_null_param_compare_in_row(l_expr, - r_expr, - tmp_key_part))) { + ObItemType real_cmp_type = i < num - 1 ? c_type : cmp_type; + bool use_ori_cmp_type = false; + if ((i < num - 1 && (T_OP_LT == cmp_type || T_OP_GT == cmp_type)) && + OB_FAIL(check_inner_row_cmp_type(l_row->get_param_expr(i + 1), + r_row->get_param_expr(i + 1), + use_ori_cmp_type))) { + LOG_WARN("fail to check can use ori cmp type", K(ret)); + } else if (OB_FAIL(check_null_param_compare_in_row(l_expr, + r_expr, + tmp_key_part))) { LOG_WARN("failed to check null param compare in row", K(ret)); } else if (tmp_key_part == NULL && OB_FAIL(get_basic_query_range(l_expr, r_expr, NULL, - i < num - 1 ? c_type : cmp_type, + use_ori_cmp_type ? cmp_type : real_cmp_type, res_type, tmp_key_part, dtc_params, @@ -3923,6 +3930,35 @@ int ObQueryRange::check_null_param_compare_in_row(const ObRawExpr *l_expr, return ret; } +int ObQueryRange::check_inner_row_cmp_type(const ObRawExpr *l_expr, + const ObRawExpr *r_expr, + bool &use_ori_cmp_type) +{ + int ret = OB_SUCCESS; + use_ori_cmp_type = false; + if (OB_ISNULL(l_expr) || OB_ISNULL(r_expr)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("get unexpected error", K(ret), KP(l_expr), KP(r_expr)); + } else if ((l_expr->has_flag(IS_COLUMN) && r_expr->is_const_expr()) || + (l_expr->is_const_expr() && r_expr->has_flag(IS_COLUMN))) { + const ObRawExpr *const_expr = l_expr->is_const_expr() ? l_expr : r_expr; + if (const_expr->has_flag(CNT_DYNAMIC_PARAM)) { + // do nothing + } else if (T_FUN_SYS_INNER_ROW_CMP_VALUE == const_expr->get_expr_type()) { + ObObj const_val; + bool is_valid = false; + if (OB_FAIL(get_calculable_expr_val(const_expr, const_val, is_valid))) { + LOG_WARN("failed to get calculable expr val", K(ret)); + } else if (is_valid && (const_val.is_min_value() || const_val.is_max_value())) { + // if const val is min/max value, it means the previous expr value range is expanding, + // use origin cmp type to calc row range. + use_ori_cmp_type = true; + } + } + } + return ret; +} + void ObQueryRange::print_keypart(const ObKeyPart *keypart, const ObString &prefix) const { // or dir diff --git a/src/sql/rewrite/ob_query_range.h b/src/sql/rewrite/ob_query_range.h index 7a36f7b614..e2a1814206 100644 --- a/src/sql/rewrite/ob_query_range.h +++ b/src/sql/rewrite/ob_query_range.h @@ -899,6 +899,9 @@ private: int check_null_param_compare_in_row(const ObRawExpr *l_expr, const ObRawExpr *r_expr, ObKeyPart *&out_key_part); + int check_inner_row_cmp_type(const ObRawExpr *l_expr, + const ObRawExpr *r_expr, + bool &use_ori_cmp_type); private: static const int64_t RANGE_BUCKET_SIZE = 1000; static const int64_t MAX_RANGE_SIZE_OLD = 10000; diff --git a/src/sql/rewrite/ob_transform_pre_process.cpp b/src/sql/rewrite/ob_transform_pre_process.cpp index 0d6995d491..9db038e1e6 100644 --- a/src/sql/rewrite/ob_transform_pre_process.cpp +++ b/src/sql/rewrite/ob_transform_pre_process.cpp @@ -5322,6 +5322,12 @@ int ObTransformPreProcess::transform_expr(ObRawExprFactory &expr_factory, } } } + if (OB_SUCC(ret)) { + if (OB_FAIL(replace_inner_row_cmp_val_recursively(expr_factory, session, + expr, trans_happened))) { + LOG_WARN("replace inner row cmp value failed", K(ret), K(expr)); + } + } return ret; } @@ -6061,6 +6067,199 @@ int ObTransformPreProcess::replace_align_date4cmp_recursively(ObRawExprFactory & return ret; } +int ObTransformPreProcess::replace_inner_row_cmp_val_recursively(ObRawExprFactory &expr_factory, + const ObSQLSessionInfo &session, + ObRawExpr *&root_expr, + bool &trans_happened) +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(root_expr)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid null param expr.", K(ret)); + } + for (int64_t i = 0; OB_SUCC(ret) && i < root_expr->get_param_count(); i++) { + if (OB_ISNULL(root_expr->get_param_expr(i))) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid null param expr", K(ret)); + } else if (OB_FAIL(SMART_CALL(replace_inner_row_cmp_val_recursively(expr_factory, + session, + root_expr->get_param_expr(i), + trans_happened)))) { + LOG_WARN("failed to replace row cmp val recursively", K(ret)); + } + } + if (OB_FAIL(ret)) { + } else if (T_OP_LT == root_expr->get_expr_type() || T_OP_GT == root_expr->get_expr_type()) { + // For row comparisons that are T_OP_LT/ or T_OP_GT expressions, need to check whether the + // range may be expanded. If so, change it to the corresponding inner expression. + int row_dim = -1; + if (OB_FAIL(ObRelationalExprOperator::is_row_cmp(*root_expr, row_dim))) { + LOG_WARN("failed to get row dimension", K(ret)); + } else if (row_dim > 0) { + if (OB_FAIL(check_and_transform_inner_row_cmp_val( + expr_factory, session, root_expr, trans_happened))) { + LOG_WARN("failed to check and transform", K(ret)); + } + } + } else if (T_OP_SQ_LT == root_expr->get_expr_type() || T_OP_SQ_GT == root_expr->get_expr_type()) { + // We also need to handle the comparison of subqueries, only focus on the op_row part of + // the subquery comparison. + if (OB_UNLIKELY(2 != root_expr->get_param_count())) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("invalid param count", K(ret), K(root_expr->get_param_count())); + } else if ((T_OP_ROW == root_expr->get_param_expr(0)->get_expr_type() && + root_expr->get_param_expr(0)->get_expr_type() > 1) || + (T_OP_ROW == root_expr->get_param_expr(1)->get_expr_type() && + root_expr->get_param_expr(1)->get_expr_type() > 1)) { + if (OB_FAIL(check_and_transform_inner_row_cmp_val( + expr_factory, session, root_expr, trans_happened))) { + LOG_WARN("failed to check and transform", K(ret)); + } + } + } + return ret; +} + +int ObTransformPreProcess::check_and_transform_inner_row_cmp_val(ObRawExprFactory &expr_factory, + const ObSQLSessionInfo &session, + ObRawExpr *&row_cmp_expr, + bool &trans_happened) +{ + int ret = OB_SUCCESS; + ObRawExpr *left = NULL; + ObRawExpr *right = NULL; + bool row_cmp_trans_happened = false; + const ObItemType op_type = row_cmp_expr->get_expr_type(); + if (OB_UNLIKELY(2 != row_cmp_expr->get_param_count())) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid param cnt", K(ret), K(row_cmp_expr->get_param_count())); + } else if (OB_ISNULL(left = row_cmp_expr->get_param_expr(0)) || + OB_ISNULL(right = row_cmp_expr->get_param_expr(1))) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid null params", K(ret), KP(left), KP(right)); + } else if (T_OP_LT != op_type && T_OP_GT != op_type && + T_OP_SQ_LT != op_type && T_OP_SQ_GT != op_type) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid op type", K(ret), K(op_type)); + } else if (T_OP_ROW != left->get_expr_type() && T_OP_ROW != right->get_expr_type()) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid types params", K(ret), K(left->get_expr_type()), K(right->get_expr_type())); + } else if (T_OP_ROW == left->get_expr_type() && + OB_FAIL(transform_inner_op_row_cmp_for_decimal_int(expr_factory, + session, + row_cmp_expr, + left, + row_cmp_trans_happened))) { + LOG_INFO("fail to transfrom left op row cmp", K(ret), K(op_type)); + } else if (T_OP_ROW == right->get_expr_type() && + OB_FAIL(transform_inner_op_row_cmp_for_decimal_int(expr_factory, + session, + row_cmp_expr, + right, + row_cmp_trans_happened))) { + LOG_INFO("fail to transfrom left op row cmp", K(ret), K(op_type)); + } else if (row_cmp_trans_happened) { + ObOpRawExpr *op_raw_expr = dynamic_cast(row_cmp_expr); + if (OB_ISNULL(op_raw_expr)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("invalid null op_raw_expr", K(ret)); + } else if (OB_FALSE_IT(op_raw_expr->get_param_expr(0) = left)) { + } else if (OB_FALSE_IT(op_raw_expr->get_param_expr(1) = right)) { + } else if (OB_FAIL(op_raw_expr->formalize(&session))) { + LOG_WARN("formalize expr failed", K(ret)); + } else { + trans_happened = row_cmp_trans_happened; + LOG_DEBUG("After Transform", K(*op_raw_expr)); + } + } + return ret; +} + +template +int ObTransformPreProcess::transform_inner_op_row_cmp_for_decimal_int( + ObRawExprFactory &expr_factory, + const ObSQLSessionInfo &session, + ObRawExpr *&row_cmp_expr, + ObRawExpr *&row_expr, + bool &trans_happened) +{ + int ret = OB_SUCCESS; + // The inner expression will return the result based on whether param 1 and param 2 are equal. + // If they are not equal, it means that the original range has been enlarged. At this time, + // an error code needs to be returned. The rules are as follows: + // if the input expr is on the left and it is greater than the value of the column, + // OB_ERR_MIN_VALUE should be returned at this time, otherwise it is the opposite. + const ObItemType op_type = row_cmp_expr->get_expr_type(); + const bool is_err_min_val = + (IS_LEFT && (op_type == T_OP_GT || op_type == T_OP_SQ_GT)) || // (cast_expr, cast_expr) > (c1, c2) + (!IS_LEFT && (op_type == T_OP_LT || op_type == T_OP_SQ_LT)); // (c1, c2) < (cast_expr, cast_expr) + const int error_ret = is_err_min_val ? -OB_ERR_MIN_VALUE : -OB_ERR_MAX_VALUE; + // save the error code in the extra field of the expression to pass information + const uint64_t extra = static_cast(error_ret); + const int64_t row_count = row_expr->get_param_count(); + ObSEArray new_params; + for (int64_t i = 0; OB_SUCC(ret) && i < row_count - 1; ++i) { + ObRawExpr *param_expr = row_expr->get_param_expr(i); + ObRawExpr *next_expr = row_expr->get_param_expr(i + 1); + ObRawExpr *new_expr = next_expr; + const ObExprResType &res_type = param_expr->get_result_type(); + if (OB_ISNULL(param_expr) || OB_ISNULL(next_expr)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid null params", K(ret), KP(param_expr), KP(next_expr)); + } else if (param_expr->get_expr_type() == T_FUN_SYS_CAST && + ob_is_decimal_int_tc(res_type.get_type()) && + CM_IS_CONST_TO_DECIMAL_INT(res_type.get_cast_mode())) { + // try to transform cast expr + ObRawExpr *input_expr = param_expr->get_param_expr(0); + ObSysFunRawExpr *inner_row_cmp_expr = NULL; + if (OB_ISNULL(input_expr)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid null params", K(ret), KP(input_expr)); + } else if (ob_is_int_uint_tc(input_expr->get_result_type().get_type()) || + ob_is_temporal_type(input_expr->get_result_type().get_type()) || + ob_is_bit_tc(input_expr->get_result_type().get_type())) { + // ignore this type as input, because it has no decimals there is no loss of precision + } else if (ob_is_number_or_decimal_int_tc(input_expr->get_result_type().get_type()) && + input_expr->get_result_type().get_scale() != SCALE_UNKNOWN_YET && + input_expr->get_result_type().get_scale() <= res_type.get_scale()) { + // there are more decimal places, no precision loss, no conversion required + } else if (OB_FAIL(ObRawExprUtils::build_inner_row_cmp_expr(expr_factory, + &session, + param_expr, + input_expr, + next_expr, + error_ret, + inner_row_cmp_expr))) { + LOG_WARN("fail to build inner row cmp expr", K(ret)); + } else { + new_expr = inner_row_cmp_expr; + trans_happened = true; + } + } + if (OB_SUCC(ret)) { + // The first parameter does not need to be converted + if (i == 0 && OB_FAIL(new_params.push_back(param_expr))) { + LOG_WARN("fail to push back first param expr", K(ret)); + } else if (OB_FAIL(new_params.push_back(new_expr))) { + LOG_WARN("fail to push back new expr", K(ret)); + } + } + } + // replace all params expr to new param exprs + if (OB_FAIL(ret)) { + } else if (new_params.count() != row_count) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("unexpected row count", K(ret), K(new_params.count()), K(row_count)); + } else { + // This vector may have multiple values, such as (C1, C2, C3, ..., C). After conversion, + // it is (C1, inner(c2), inner(c3), ..., inner(c)) + for (int64_t i = 0; OB_SUCC(ret) && trans_happened && i < row_count; ++i) { + row_expr->get_param_expr(i) = new_params.at(i); + } + } + return ret; +} + /*@brief ObTransformPreProcess::transformer_aggr_expr 用于将一些复杂的聚合函数展开为普通的聚合运算; * eg:var_pop(expr) ==> SUM(expr*expr) - SUM(expr)* SUM(expr)/ COUNT(expr)) / COUNT(expr) * 其中ObExpandAggregateUtils这个类主要涉及到相关的函数用于展开复杂的聚合函数: diff --git a/src/sql/rewrite/ob_transform_pre_process.h b/src/sql/rewrite/ob_transform_pre_process.h index 7380bdca02..c43f32fab4 100644 --- a/src/sql/rewrite/ob_transform_pre_process.h +++ b/src/sql/rewrite/ob_transform_pre_process.h @@ -443,6 +443,20 @@ struct DistinctObjMeta const ObItemType &cmp_type); static int replace_align_date4cmp_recursively(ObRawExprFactory &expr_factory, ObRawExpr *&root_expr); + static int replace_inner_row_cmp_val_recursively(ObRawExprFactory &expr_factory, + const ObSQLSessionInfo &session, + ObRawExpr *&root_expr, + bool &trans_happened); + static int check_and_transform_inner_row_cmp_val(ObRawExprFactory &expr_factory, + const ObSQLSessionInfo &session, + ObRawExpr *&row_cmp_expr, + bool &trans_happened); + template + static int transform_inner_op_row_cmp_for_decimal_int(ObRawExprFactory &expr_factory, + const ObSQLSessionInfo &session, + ObRawExpr *&row_cmp_expr, + ObRawExpr *&row_expr, + bool &trans_happened); int transformer_aggr_expr(ObDMLStmt *stmt, bool &trans_happened); int transform_rownum_as_limit_offset(const ObIArray &parent_stmts, ObDMLStmt *&stmt,