diff --git a/deps/oblib/src/lib/ob_name_def.h b/deps/oblib/src/lib/ob_name_def.h index 505f6aecff..0b90059a59 100644 --- a/deps/oblib/src/lib/ob_name_def.h +++ b/deps/oblib/src/lib/ob_name_def.h @@ -1068,4 +1068,5 @@ #define N_UPDATEXML "updatexml" #define N_NLS_INITCAP "nls_initcap" #define N_TEMP_TABLE_SSID "temp_table_ssid" +#define N_ALIGN_DATE4CMP "align_date4cmp" #endif //OCEANBASE_LIB_OB_NAME_DEF_H_ diff --git a/deps/oblib/src/lib/timezone/ob_time_convert.h b/deps/oblib/src/lib/timezone/ob_time_convert.h index d0145cb48f..b4e00b626f 100644 --- a/deps/oblib/src/lib/timezone/ob_time_convert.h +++ b/deps/oblib/src/lib/timezone/ob_time_convert.h @@ -635,6 +635,9 @@ public: ObYearWeekWdaySpec spec_[YEAR_WEEK_WDAY_COUNT]; } */ +public: + static int validate_datetime(ObTime &ob_time, const bool is_dayofmonth, + const ObDateSqlMode date_sql_mode); private: // date add / sub / diff. static int merge_date_interval(int64_t base_value, const ObString &interval_str, @@ -644,8 +647,6 @@ private: const ObDateSqlMode date_sql_mode); // other utility functions. static int validate_year(int64_t year); - static int validate_datetime(ObTime &ob_time, const bool is_dayofmonth, - const ObDateSqlMode date_sql_mode); static int validate_oracle_timestamp(const ObTime &ob_time); static int validate_basic_part_of_ob_time_oracle(const ObTime &ob_time); static int validate_tz_part_of_ob_time_oracle(const ObTime &ob_time); diff --git a/src/objit/include/objit/common/ob_item_type.h b/src/objit/include/objit/common/ob_item_type.h index d3566eded9..c232d546d1 100755 --- a/src/objit/include/objit/common/ob_item_type.h +++ b/src/objit/include/objit/common/ob_item_type.h @@ -819,6 +819,7 @@ typedef enum ObItemType T_FUN_SYS_RANDOM = 1806, T_FUN_SYS_RANDSTR = 1807, T_FUN_SYS_END = 2000, + T_ALIGN_DATE4CMP = 2010, T_MAX_OP = 3000, diff --git a/src/sql/CMakeLists.txt b/src/sql/CMakeLists.txt index 1d5c9e5bee..10052ff8f3 100644 --- a/src/sql/CMakeLists.txt +++ b/src/sql/CMakeLists.txt @@ -432,6 +432,7 @@ ob_set_subtarget(ob_sql engine_expr engine/expr/ob_expr_name_const.cpp engine/expr/ob_expr_neg.cpp engine/expr/ob_expr_nlssort.cpp + engine/expr/ob_expr_align_date4cmp.cpp engine/expr/ob_expr_not.cpp engine/expr/ob_expr_not_between.cpp engine/expr/ob_expr_not_equal.cpp diff --git a/src/sql/engine/expr/ob_expr_align_date4cmp.cpp b/src/sql/engine/expr/ob_expr_align_date4cmp.cpp new file mode 100644 index 0000000000..e9fb1396c1 --- /dev/null +++ b/src/sql/engine/expr/ob_expr_align_date4cmp.cpp @@ -0,0 +1,419 @@ +/** + * 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_align_date4cmp.h" +#include "lib/oblog/ob_log.h" +#include "share/object/ob_obj_cast.h" +#include "common/object/ob_obj_compare.h" +#include "lib/timezone/ob_time_convert.h" +#include "sql/resolver/expr/ob_raw_expr.h" + +namespace oceanbase +{ +using namespace common; +namespace sql +{ + +static const int8_t DAYS_OF_MON[2][12 + 1] = { + {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +}; + +#define IS_LEAP_YEAR(y) ((((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) ? 1 : 0) + +ObExprAlignDate4Cmp::ObExprAlignDate4Cmp(common::ObIAllocator &alloc) + : ObFuncExprOperator(alloc, T_ALIGN_DATE4CMP, N_ALIGN_DATE4CMP, 3, VALID_FOR_GENERATED_COL, NOT_ROW_DIMENSION) +{ +} + +int ObExprAlignDate4Cmp::calc_result_type3(ObExprResType &type, + ObExprResType &type1, + ObExprResType &type2, + ObExprResType &type3, + ObExprTypeCtx &type_ctx) const +{ + int ret = OB_SUCCESS; + ObRawExpr *raw_expr = get_raw_expr(); + ObOpRawExpr *op_expr = static_cast(raw_expr); + if (OB_ISNULL(op_expr) || OB_UNLIKELY(op_expr->get_param_count() != 3)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("op raw expr is null or param_count error", K(ret), K(op_expr)); + } else { + ObRawExpr *param3 = op_expr->get_param_expr(2); + if (!param3->is_const_expr()) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("raw expr is not const expr", K(ret), KPC(param3)); + } else { + ObConstRawExpr *const_param = static_cast(param3); + if (OB_ISNULL(const_param)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("third child of the expr is null", K(ret), K(const_param)); + } else { + if (const_param->get_value().is_null()) { + type.set_null(); + } else { + ObObjType res_type = ObObjType(const_param->get_value().get_int()); + switch(res_type) { + case ObNullType: { + type.set_null(); + break; + } + case ObDateTimeType: { + type.set_datetime(); + break; + } + case ObDateType: { + type.set_date(); + break; + } + default: { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("unexpected res type.", K(ret), K(res_type)); + break; + } + } + } + } + } + } + return ret; +} + +int ObExprAlignDate4Cmp::cg_expr(ObExprCGCtx &expr_cg_ctx, const ObRawExpr &raw_expr, + ObExpr &rt_expr) const +{ + int ret = OB_SUCCESS; + UNUSED(expr_cg_ctx); + UNUSED(raw_expr); + if (rt_expr.arg_cnt_ != 3) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("the arg_cnt of expr_align_date4cmp 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 expr_align_date4cmp is null.", K(ret), K(rt_expr)); + } else { + rt_expr.eval_func_ = eval_align_date4cmp; + } + return ret; +} + +int ObExprAlignDate4Cmp::eval_align_date4cmp(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &res) +{ + int ret = OB_SUCCESS; + ObDatum* date_datum = NULL; + ObDatum* cmp_type_datum = NULL; + ObDatum* res_type_datum = NULL; + DateArgType date_arg_type = NON_DATE; + ObObjType res_type; + ObTime ob_time; + ObObjType date_arg_obj_type = ObMaxType; + + if (OB_FAIL(expr.args_[0]->eval(ctx, date_datum))) { + LOG_WARN("expr_align_date4cmp eval arg[0] date fail.", K(ret), K(expr)); + } else if (OB_FAIL(expr.args_[1]->eval(ctx, cmp_type_datum))) { + LOG_WARN("expr_align_date4cmp eval arg[1] cmp_type fail.", K(ret), K(expr)); + } else if (OB_FAIL(expr.args_[2]->eval(ctx, res_type_datum))) { + LOG_WARN("expr_align_date4cmp eval arg[2] res_type fail.", K(ret), K(expr)); + } else { + date_arg_obj_type = expr.args_[0]->datum_meta_.type_; + res_type = ObObjType(res_type_datum->get_int()); + if(OB_FAIL(datum_to_ob_time(date_datum, date_arg_obj_type, date_arg_type, ob_time))) { + LOG_WARN("datum_to_ob_time fail.", K(ret), K(date_datum), K(date_arg_obj_type)); + } + } + + bool offset = false; + const bool is_zero_on_warn = CM_IS_ZERO_ON_WARN(expr.extra_); + if (OB_SUCC(ret)) { + switch(date_arg_type) { + case VALID_DATE: { + const bool is_valid_time = true; + if (OB_FAIL(set_res(res, ob_time, res_type, is_valid_time, offset, is_zero_on_warn))) { + LOG_WARN("set_res fail.", K(ret), K(ob_time), K(res_type)); + } + break; + } + case INVALID_DATE: { + bool is_valid_time = false; + int cmp_type = cmp_type_datum->get_int(); + // if cmp_type == T_OP_EQ or T_OP_NSEQ or T_OP_NE, + // return null or 0 depending on is_zero_on_warn. + if (cmp_type == T_OP_EQ || cmp_type == T_OP_NSEQ || cmp_type == T_OP_NE) { + if (OB_FAIL(set_res(res, ob_time, res_type, is_valid_time, offset, is_zero_on_warn))) { + LOG_WARN("set_res fail.", K(ret), K(ob_time), K(res_type)); + } + } else { + if (day_over_limit(ob_time)) { + offset = (cmp_type == T_OP_GT || cmp_type == T_OP_LE) ? false : true; + set_valid_time_floor(ob_time); + is_valid_time = true; + if (OB_FAIL(set_res(res, ob_time, res_type, is_valid_time, offset, is_zero_on_warn))) { + LOG_WARN("set_res fail.", K(ret), K(ob_time), K(res_type)); + } + } else if (OB_FAIL(set_res(res, ob_time, res_type, is_valid_time, offset, is_zero_on_warn))) { + LOG_WARN("set_res fail.", K(ret), K(ob_time), K(res_type)); + } + } + break; + } + case NON_DATE: { + const bool is_valid_time = false; + if (OB_FAIL(set_res(res, ob_time, res_type, is_valid_time, offset, is_zero_on_warn))) { + LOG_WARN("set_res fail.", K(ret), K(ob_time), K(res_type)); + } + break; + } + case NULL_DATE: { + res.set_null(); + break; + } + } + } + + return ret; +} + + +void ObExprAlignDate4Cmp::set_valid_time_floor(ObTime& ob_time) +{ + int ret = OB_SUCCESS; + if(IS_LEAP_YEAR(ob_time.parts_[DT_YEAR])) { + ob_time.parts_[DT_MDAY] = DAYS_OF_MON[1][ob_time.parts_[DT_MON]]; + } else { + ob_time.parts_[DT_MDAY] = DAYS_OF_MON[0][ob_time.parts_[DT_MON]]; + } + + ob_time.parts_[DT_HOUR] = HOURS_PER_DAY - 1; + ob_time.parts_[DT_MIN] = MINS_PER_HOUR - 1; + ob_time.parts_[DT_SEC] = SECS_PER_MIN - 1; + ob_time.parts_[DT_USEC] = USECS_PER_SEC - 1; + + ob_time.parts_[DT_DATE] = ObTimeConverter::ob_time_to_date(ob_time); +} + +bool ObExprAlignDate4Cmp::day_over_limit(const ObTime& ob_time) +{ + bool res = true; + if(IS_LEAP_YEAR(ob_time.parts_[DT_YEAR])) { + res = ob_time.parts_[DT_MDAY] > DAYS_OF_MON[1][ob_time.parts_[DT_MON]]; + } else { + res = ob_time.parts_[DT_MDAY] > DAYS_OF_MON[0][ob_time.parts_[DT_MON]]; + } + return res; +} + +int ObExprAlignDate4Cmp::integer_to_ob_time(const int64_t& date, + DateArgType& date_arg_type, + ObTime& ob_time) +{ + int ret = OB_SUCCESS; + ObDateSqlMode date_sql_mode; + // First try to perform a lenient type conversion. + // If it fails, it means the value is not convertible to time. + date_sql_mode.allow_invalid_dates_ = true; + date_sql_mode.no_zero_date_ = false; + if (OB_FAIL(ObTimeConverter::int_to_ob_time_with_date(date, ob_time, true, date_sql_mode))) { + date_arg_type = NON_DATE; + ret = OB_SUCCESS; + } else { + // Then perform a strict type conversion check. + // If successful, it indicates a VALID_DATE; + // if unsuccessful, it indicates an INVALID_DATE. + date_sql_mode.allow_invalid_dates_ = false; + date_sql_mode.no_zero_date_ = true; + if (OB_SUCC(ObTimeConverter::validate_datetime(ob_time, false, date_sql_mode))) { + date_arg_type = VALID_DATE; + } else { + date_arg_type = INVALID_DATE; + ret = OB_SUCCESS; + } + } + return ret; +} + +int ObExprAlignDate4Cmp::integer_to_ob_time(const ObDatum* date_datum, DateArgType& date_arg_type, ObTime& ob_time) { + return integer_to_ob_time(date_datum->get_int(), date_arg_type, ob_time); +} + +int ObExprAlignDate4Cmp::double_to_ob_time(const double& date, DateArgType& date_arg_type, ObTime& ob_time) { + return integer_to_ob_time((int64_t)date, date_arg_type, ob_time); +} + +int ObExprAlignDate4Cmp::double_to_ob_time(const ObDatum* date_datum, DateArgType& date_arg_type, ObTime& ob_time) { + return double_to_ob_time(date_datum->get_double(), date_arg_type, ob_time); +} + +int ObExprAlignDate4Cmp::number_to_ob_time(const number::ObNumber& date, DateArgType& date_arg_type, ObTime& ob_time) { + int ret = OB_SUCCESS; + int64_t date_int = 0; + if (OB_FAIL(date.extract_valid_int64_with_trunc(date_int))) { + LOG_WARN("extract_valid_int64_with_trunc fail.", K(ret), K(date)); + } else if (OB_FAIL(integer_to_ob_time(date_int, date_arg_type, ob_time))) { + LOG_WARN("cast integer to ob_time fail.", K(ret), K(date_int)); + } + return ret; +} + +int ObExprAlignDate4Cmp::number_to_ob_time(const ObDatum* date_datum, DateArgType& date_arg_type, ObTime& ob_time) { + return number_to_ob_time(date_datum->get_number(), date_arg_type, ob_time); +} + +int ObExprAlignDate4Cmp::str_to_ob_time(const ObString& date, DateArgType& date_arg_type, ObTime& ob_time) { + int ret = OB_SUCCESS; + ObDateSqlMode date_sql_mode; + date_sql_mode.allow_invalid_dates_ = true; + date_sql_mode.no_zero_date_ = false; + if (OB_FAIL(ObTimeConverter::str_to_ob_time_with_date(date, ob_time, NULL, true, date_sql_mode))) { + date_arg_type = NON_DATE; + ret = OB_SUCCESS; + } else { + date_sql_mode.allow_invalid_dates_ = false; + date_sql_mode.no_zero_date_ = true; + if (OB_SUCC(ObTimeConverter::validate_datetime(ob_time, false, date_sql_mode))) { + date_arg_type = VALID_DATE; + } else { + date_arg_type = INVALID_DATE; + ret = OB_SUCCESS; + } + } + return ret; +} + +int ObExprAlignDate4Cmp::str_to_ob_time(const ObDatum* date_datum, DateArgType& date_arg_type, ObTime& ob_time) { + return str_to_ob_time(date_datum->get_string(), date_arg_type, ob_time); +} + +int ObExprAlignDate4Cmp::datum_to_ob_time(const ObDatum* date_datum, const ObObjType& date_arg_obj_type, DateArgType& date_arg_type, ObTime& ob_time) +{ + int ret = OB_SUCCESS; + if (date_datum->is_null()) { + date_arg_type = NULL_DATE; + } else if (ob_is_string_type(date_arg_obj_type)) { + if (OB_FAIL(str_to_ob_time(date_datum, date_arg_type, ob_time))) { + LOG_WARN("str_to_ob_time fail.", K(ret), K(date_datum)); + } + } else { + switch(date_arg_obj_type) { + case ObNullType: { + date_arg_type = NULL_DATE; + break; + } + case ObTinyIntType: + case ObSmallIntType: + case ObMediumIntType: + case ObInt32Type: + case ObIntType: + case ObUTinyIntType: + case ObUSmallIntType: + case ObUMediumIntType: + case ObUInt32Type: + case ObUInt64Type: { + if (OB_FAIL(integer_to_ob_time(date_datum, date_arg_type, ob_time))) { + LOG_WARN("integer_to_ob_time fail.", K(ret), K(date_datum)); + } + break; + } + case ObFloatType: + case ObDoubleType: + case ObUFloatType: + case ObUDoubleType: { + if (OB_FAIL(double_to_ob_time(date_datum, date_arg_type, ob_time))) { + LOG_WARN("double_to_ob_time fail.", K(ret), K(date_datum)); + } + break; + } + case ObNumberType: + case ObUNumberType: { + if (OB_FAIL(number_to_ob_time(date_datum, date_arg_type, ob_time))) { + LOG_WARN("number_to_ob_time fail.", K(ret), K(date_datum)); + } + break; + } + default: { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("date_arg_obj_type error: ", K(ret), K(date_arg_obj_type)); + break; + } + } + } + + return ret; +} + +int ObExprAlignDate4Cmp::set_res(ObDatum &res, ObTime &ob_time, + const ObObjType &res_type, + const bool is_valid_time, + const bool offset, + const bool is_zero_on_warn) +{ + int ret = OB_SUCCESS; + switch(res_type) { + case ObNullType: { + res.set_null(); + break; + } + case ObDateTimeType: { + if (!is_valid_time) { + if (is_zero_on_warn) { + res.set_datetime(0); + } else { + res.set_null(); + } + } else { + int64_t datetime_value = 0; + ObTimeConvertCtx cvrt_ctx(NULL, false); + if (OB_FAIL(ObTimeConverter::ob_time_to_datetime(ob_time, cvrt_ctx, datetime_value))) { + LOG_WARN("ob_time convert to datetime fail.", K(ret), K(ob_time)); + } else { + res.set_datetime(datetime_value + offset); + } + } + break; + } + case ObDateType: { + if (!is_valid_time) { + if (is_zero_on_warn) { + res.set_date(0); + } else { + res.set_null(); + } + } else { + int32_t date_value = ObTimeConverter::ob_time_to_date(ob_time); + res.set_date(date_value + offset); + } + break; + } + default: { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("unexpected res type.", K(ret), K(res_type)); + break; + } + } + + return ret; +} + +bool ObExprAlignDate4Cmp::is_align_date4cmp_support_obj_type(const ObObjType& obj_type) { + bool res = false; + if (ObNullType <= obj_type && obj_type <= ObUNumberType) { + res = true; + } else if (ob_is_string_type(obj_type)) { + res = true; + } + return res; +} + +} +} \ No newline at end of file diff --git a/src/sql/engine/expr/ob_expr_align_date4cmp.h b/src/sql/engine/expr/ob_expr_align_date4cmp.h new file mode 100644 index 0000000000..d15ce46da0 --- /dev/null +++ b/src/sql/engine/expr/ob_expr_align_date4cmp.h @@ -0,0 +1,83 @@ +/** + * 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_ALIGN_DATE4CMP_H_ +#define _OB_EXPR_ALIGN_DATE4CMP_H_ + +#include "sql/engine/expr/ob_expr_operator.h" + +namespace oceanbase +{ +namespace sql +{ +// This expression is used to handle issues encountered when comparing datetime or date type with other types: +// the other type will be converted to an illegal date, resulting in a comparison result that is incompatible with MySQL. +// such as: +// create table t1(c1 int primary key, c2 date); +// insert into t1 values(1, '1998-11-30'); +// insert into t1 values(2, '1998-12-01'); +// select * from t1 where c2 > '1998-11-31'; +// This expression is used to convert illegal date into logical and valid date. +// such as: 1998-11-31 -> 1998-11-30/1998-12-01 depend on compare type. +class ObExprAlignDate4Cmp : public ObFuncExprOperator +{ +public: + explicit ObExprAlignDate4Cmp(common::ObIAllocator &alloc); + virtual ~ObExprAlignDate4Cmp() {}; + + 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_align_date4cmp(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &res); + + static bool is_align_date4cmp_support_obj_type(const ObObjType& obj_type); + + enum DateArgType + { + VALID_DATE = 1, + INVALID_DATE = 2, + NON_DATE = 3, + NULL_DATE = 4, + }; + +private: + //disallow copy + DISALLOW_COPY_AND_ASSIGN(ObExprAlignDate4Cmp); + + static bool day_over_limit(const ObTime &ob_time); + static void set_valid_time_floor(ObTime &ob_time); + static int integer_to_ob_time(const int64_t &date, DateArgType &date_arg_type, ObTime &ob_time); + static int integer_to_ob_time(const ObDatum *date_datum, DateArgType &date_arg_type, ObTime &ob_time); + static int double_to_ob_time(const double &date, DateArgType &date_arg_type, ObTime &ob_time); + static int double_to_ob_time(const ObDatum *date_datum, DateArgType &date_arg_type, ObTime &ob_time); + static int number_to_ob_time(const number::ObNumber &date, DateArgType &date_arg_type, ObTime &ob_time); + static int number_to_ob_time(const ObDatum *date_datum, DateArgType &date_arg_type, ObTime &ob_time); + static int str_to_ob_time(const ObString &date, DateArgType &date_arg_type, ObTime &ob_time); + static int str_to_ob_time(const ObDatum *date_datum, DateArgType &date_arg_type, ObTime &ob_time); + static int datum_to_ob_time(const ObDatum *date_datum, + const ObObjType &date_arg_obj_type, + DateArgType &date_arg_type, + ObTime &ob_time); + static int set_res(ObDatum &res, ObTime &ob_time, + const ObObjType &res_type, + const bool is_valid_time, + const bool offset, + const bool is_zero_on_warn); +}; +} +} +#endif /* _OB_EXPR_ALIGN_DATE4CMP_H_ */ diff --git a/src/sql/engine/expr/ob_expr_eval_functions.cpp b/src/sql/engine/expr/ob_expr_eval_functions.cpp index f6344052eb..d1bc746455 100644 --- a/src/sql/engine/expr/ob_expr_eval_functions.cpp +++ b/src/sql/engine/expr/ob_expr_eval_functions.cpp @@ -333,6 +333,7 @@ #include "ob_expr_prefix_pattern.h" #include "ob_expr_initcap.h" #include "ob_expr_temp_table_ssid.h" +#include "ob_expr_align_date4cmp.h" namespace oceanbase { @@ -1036,6 +1037,7 @@ static ObExpr::EvalFunc g_expr_eval_functions[] = { ObExprJoinFilter::eval_in_filter, /* 605 */ ObExprCurrentScn::eval_current_scn, /* 606 */ ObExprTempTableSSID::calc_temp_table_ssid, /* 607 */ + ObExprAlignDate4Cmp::eval_align_date4cmp, /* 608 */ }; static ObExpr::EvalBatchFunc g_expr_eval_batch_functions[] = { diff --git a/src/sql/engine/expr/ob_expr_operator_factory.cpp b/src/sql/engine/expr/ob_expr_operator_factory.cpp index 76921f1ca0..376ac93968 100644 --- a/src/sql/engine/expr/ob_expr_operator_factory.cpp +++ b/src/sql/engine/expr/ob_expr_operator_factory.cpp @@ -402,6 +402,7 @@ #include "sql/engine/expr/ob_expr_xmlcast.h" #include "sql/engine/expr/ob_expr_update_xml.h" #include "sql/engine/expr/ob_expr_temp_table_ssid.h" +#include "sql/engine/expr/ob_expr_align_date4cmp.h" using namespace oceanbase::common; namespace oceanbase @@ -997,6 +998,7 @@ void ObExprOperatorFactory::register_expr_operators() REG_OP(ObExprRandom); REG_OP(ObExprRandstr); REG_OP(ObExprPrefixPattern); + REG_OP(ObExprAlignDate4Cmp); }(); // 注册oracle系统函数 REG_OP_ORCL(ObExprSysConnectByPath); diff --git a/src/sql/rewrite/ob_transform_pre_process.cpp b/src/sql/rewrite/ob_transform_pre_process.cpp index cb85e82cb6..7f5bd09c97 100644 --- a/src/sql/rewrite/ob_transform_pre_process.cpp +++ b/src/sql/rewrite/ob_transform_pre_process.cpp @@ -43,6 +43,7 @@ #include "pl/ob_pl_resolver.h" #include "sql/privilege_check/ob_ora_priv_check.h" #include "sql/resolver/dml/ob_insert_all_stmt.h" +#include "sql/engine/expr/ob_expr_align_date4cmp.h" using namespace oceanbase::common; using namespace oceanbase::share; @@ -5266,6 +5267,16 @@ int ObTransformPreProcess::transform_expr(ObRawExprFactory &expr_factory, LOG_WARN("transform arg case failed", K(ret)); } } + if (OB_SUCC(ret)) { + const uint64_t ob_version = GET_MIN_CLUSTER_VERSION(); + // The rewriting is done for the purpose of MySQL compatibility. + if ((ob_version >= CLUSTER_VERSION_4_2_1_0) && lib::is_mysql_mode()) { + if (OB_FAIL(replace_align_date4cmp_recursively( + expr_factory, expr))) { + LOG_WARN("replace align_date4cmp failed", K(ret), K(expr)); + } + } + } return ret; } @@ -5793,6 +5804,171 @@ int ObTransformPreProcess::replace_in_or_notin_recursively(ObRawExprFactory &exp return ret; } +ObItemType ObTransformPreProcess::reverse_cmp_type_of_align_date4cmp(const ObItemType &cmp_type) +{ + ObItemType new_cmp_type = cmp_type; + switch (cmp_type) { + case T_OP_LE: { + new_cmp_type = T_OP_GE; + break; + } + case T_OP_LT: { + new_cmp_type = T_OP_GT; + break; + } + case T_OP_GE: { + new_cmp_type = T_OP_LE; + break; + } + case T_OP_GT: { + new_cmp_type = T_OP_LT; + break; + } + default: { + break; + } + } + return new_cmp_type; +} + +int ObTransformPreProcess::replace_cast_expr_align_date4cmp(ObRawExprFactory &expr_factory, + const ObItemType &cmp_type, + ObRawExpr *&expr) +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(expr)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("expr is null.", K(ret)); + } else if (expr->get_expr_type() == T_FUN_SYS_CAST) { + const bool is_implicit_cast = !CM_IS_EXPLICIT_CAST(expr->get_extra()); + ObExprResType cast_res_type = expr->get_result_type(); + if (is_implicit_cast && (cast_res_type.is_date() || cast_res_type.is_datetime())) { + ObRawExpr *cast_child = expr->get_param_expr(0); + if (OB_ISNULL(cast_child)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("cast child expr is null.", K(ret)); + } else if (ObExprAlignDate4Cmp::is_align_date4cmp_support_obj_type(cast_child->get_data_type())) { + // create a new align_date4cmp_expr to replace const_expr + ObSysFunRawExpr *align_date4cmp_expr = NULL; + if (OB_FAIL(expr_factory.create_raw_expr(T_ALIGN_DATE4CMP, align_date4cmp_expr))) { + LOG_WARN("create align_date4cmp_expr fail.", K(ret), K(align_date4cmp_expr)); + } else if (OB_ISNULL(align_date4cmp_expr)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("align_date4cmp_expr is null.", K(ret)); + } else { + align_date4cmp_expr->set_func_name("INTERNAL_FUNCTION"); + // Copy cast_mode to facilitate determining the method of handling invalid_time. + align_date4cmp_expr->set_extra(expr->get_extra()); + // add first param_expr(just the cast_child) to align_date4cmp_expr + if (OB_FAIL(align_date4cmp_expr->add_param_expr(cast_child))) { + LOG_WARN("align_date4cmp_expr add param cast_expr fail.", K(ret), KPC(cast_child)); + } else { + // create second param_expr (a ObConstRawExpr used to represent cmp_type) + // and add it to align_date4cmp_expr. + ObConstRawExpr *cmp_type_expr = NULL; + if (OB_FAIL(ObRawExprUtils::build_const_int_expr(expr_factory, + ObIntType, cmp_type, cmp_type_expr))) { + LOG_WARN("failed to build const int cmp_type_expr", K(ret)); + } else if (OB_ISNULL(cmp_type_expr)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("cmp_type_expr is null.", K(ret)); + } else if (OB_FAIL(align_date4cmp_expr->add_param_expr(cmp_type_expr))){ + LOG_WARN("align_date4cmp_expr add param cmp_type_expr fail.", K(ret), KPC(cmp_type_expr)); + } else { + // create third param_expr (a ObConstRawExpr used to represent res_type) + // and add it to align_date4cmp_expr. + ObConstRawExpr *res_type_expr = NULL; + ObObjType cast_res_type = expr->get_result_type().get_type(); + int64_t cast_res_type_int = cast_res_type; + if (OB_FAIL(ObRawExprUtils::build_const_int_expr(expr_factory, + ObIntType, cast_res_type_int, res_type_expr))) { + LOG_WARN("failed to build const int res_type_expr", K(ret)); + } else if (OB_ISNULL(res_type_expr)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("res_type_expr is null.", K(ret)); + } else if (OB_FAIL(align_date4cmp_expr->add_param_expr(res_type_expr))){ + LOG_WARN("align_date4cmp_expr add param res_type_expr fail.", K(ret), KPC(res_type_expr)); + } else { + // Change the expr pointer pointing to cast_expr + // to point to the newly created align_date4cmp_expr. + expr = align_date4cmp_expr; + } + } + } + } + } + } + } + return ret; +} + +int ObTransformPreProcess::check_and_transform_align_date4cmp(ObRawExprFactory &expr_factory, + ObRawExpr *&cmp_expr, + const ObItemType &cmp_type) +{ + int ret = OB_SUCCESS; + if (OB_UNLIKELY(2 != cmp_expr->get_param_count())) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid param cnt", K(ret), K(cmp_expr->get_param_count())); + } else { + ObRawExpr *expr0 = cmp_expr->get_param_expr(0); + ObRawExpr *expr1 = cmp_expr->get_param_expr(1); + if (OB_ISNULL(expr0) || OB_ISNULL(expr1)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid null params", K(ret), K(cmp_expr->get_param_expr(0)), + K(cmp_expr->get_param_expr(1))); + } else { + // By default, align_date4cmp_expr expects the first parameter to be + // the value on the right side of the comparison operator. + // Therefore, if you pass the value on the left side, + // you need to reverse the comparison operator that is passed in. + const ObItemType reverse_cmp_type = reverse_cmp_type_of_align_date4cmp(cmp_type); + if (OB_FAIL(replace_cast_expr_align_date4cmp(expr_factory, reverse_cmp_type, expr0))) { + LOG_WARN("replace left cast_expr fail.", K(ret), KPC(expr0)); + } else { + cmp_expr->get_param_expr(0) = expr0; + } + + if (OB_SUCC(ret)) { + if (OB_FAIL(replace_cast_expr_align_date4cmp(expr_factory, cmp_type, expr1))) { + LOG_WARN("replace right cast_expr fail.", K(ret), KPC(expr1)); + } else { + cmp_expr->get_param_expr(1) = expr1; + } + } + } + } + return ret; +} + +int ObTransformPreProcess::replace_align_date4cmp_recursively(ObRawExprFactory &expr_factory, + ObRawExpr *&root_expr) +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(root_expr)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("root_expr is null.", K(ret)); + } + // Traverse all exprs from the root. + for (int 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_align_date4cmp_recursively(expr_factory, + root_expr->get_param_expr(i))))) { + LOG_WARN("failed to replace in or notin recursively", K(ret)); + } + } + // If the current expression is cmp_expr, check if align_date4cmp transformation is necessary. + if (OB_SUCC(ret) && IS_COMMON_COMPARISON_OP(root_expr->get_expr_type())) { + if (OB_FAIL(check_and_transform_align_date4cmp( + expr_factory, root_expr, root_expr->get_expr_type()))) { + LOG_WARN("failed to check and transform", K(ret)); + } + } + 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 26d899ef89..bed3708dbc 100644 --- a/src/sql/rewrite/ob_transform_pre_process.h +++ b/src/sql/rewrite/ob_transform_pre_process.h @@ -428,6 +428,15 @@ struct DistinctObjMeta const ObSQLSessionInfo &session, ObRawExpr *&root_expr, bool &trans_happened); + static ObItemType reverse_cmp_type_of_align_date4cmp(const ObItemType &cmp_type); + static int replace_cast_expr_align_date4cmp(ObRawExprFactory &expr_factory, + const ObItemType &cmp_type, + ObRawExpr *&expr); + static int check_and_transform_align_date4cmp(ObRawExprFactory &expr_factory, + ObRawExpr *&in_expr, + const ObItemType &cmp_type); + static int replace_align_date4cmp_recursively(ObRawExprFactory &expr_factory, + ObRawExpr *&root_expr); int transformer_aggr_expr(ObDMLStmt *stmt, bool &trans_happened); int transform_rownum_as_limit_offset(const ObIArray &parent_stmts, ObDMLStmt *&stmt, diff --git a/tools/deploy/mysql_test/test_suite/static_engine/r/mysql/static_engine_cmp_null.result b/tools/deploy/mysql_test/test_suite/static_engine/r/mysql/static_engine_cmp_null.result index 66fcfc3707..2d00538927 100644 --- a/tools/deploy/mysql_test/test_suite/static_engine/r/mysql/static_engine_cmp_null.result +++ b/tools/deploy/mysql_test/test_suite/static_engine/r/mysql/static_engine_cmp_null.result @@ -904,7 +904,7 @@ Query Plan =============================================== Outputs & filters: ------------------------------------- - 0 - output([cast(t.tinyint_t, DATETIME(-1, -1)) = t.datetime_t]), filter(nil), rowset=16 + 0 - output([INTERNAL_FUNCTION(t.tinyint_t, 110, 17) = t.datetime_t]), filter(nil), rowset=16 access([t.tinyint_t], [t.datetime_t]), partitions(p0) limit(1), offset(nil), is_index_back=false, is_global_index=false, range_key([t.__pk_increment]), range(MIN ; MAX)always true @@ -942,7 +942,7 @@ Query Plan =============================================== Outputs & filters: ------------------------------------- - 0 - output([cast(t.tinyint_t, DATE(-1, -1)) = t.date_t]), filter(nil), rowset=16 + 0 - output([INTERNAL_FUNCTION(t.tinyint_t, 110, 19) = t.date_t]), filter(nil), rowset=16 access([t.tinyint_t], [t.date_t]), partitions(p0) limit(1), offset(nil), is_index_back=false, is_global_index=false, range_key([t.__pk_increment]), range(MIN ; MAX)always true diff --git a/tools/deploy/mysql_test/test_suite/transformer/r/mysql/transformer_predicate_deduce.result b/tools/deploy/mysql_test/test_suite/transformer/r/mysql/transformer_predicate_deduce.result index 8b4ab84edb..be7349c6b0 100644 --- a/tools/deploy/mysql_test/test_suite/transformer/r/mysql/transformer_predicate_deduce.result +++ b/tools/deploy/mysql_test/test_suite/transformer/r/mysql/transformer_predicate_deduce.result @@ -1451,8 +1451,8 @@ Query Plan =============================================== Outputs & filters: ------------------------------------- - 0 - output([t3.c1], [t3.c2], [t3.c3]), filter([cast(t3.c1, DATETIME(-1, -1)) = cast('2010-10-10 00:00:00', DATETIME(0, 0))], [cast(t3.c1, DECIMAL(20, - 0)) = cast(t3.c2, DECIMAL(-1, -1))]), rowset=16 + 0 - output([t3.c1], [t3.c2], [t3.c3]), filter([INTERNAL_FUNCTION(t3.c1, 110, 17) = cast('2010-10-10 00:00:00', DATETIME(0, 0))], [cast(t3.c1, DECIMAL(20, + 0)) = cast(t3.c2, DECIMAL(-1, -1))]), rowset=16 access([t3.c1], [t3.c2], [t3.c3]), partitions(p0) is_index_back=false, is_global_index=false, filter_before_indexback[false,false], range_key([t3.__pk_increment]), range(MIN ; MAX)always true @@ -1471,8 +1471,8 @@ Query Plan =============================================== Outputs & filters: ------------------------------------- - 0 - output([t3.c1], [t3.c2], [t3.c3]), filter([cast(t3.c1, DATETIME(-1, -1)) = cast('2010-10-10 00:00:00', DATETIME(0, 0))], [cast(t3.c1, DECIMAL(20, - 0)) = cast(t3.c2, DECIMAL(-1, -1))]), rowset=16 + 0 - output([t3.c1], [t3.c2], [t3.c3]), filter([INTERNAL_FUNCTION(t3.c1, 110, 17) = cast('2010-10-10 00:00:00', DATETIME(0, 0))], [cast(t3.c1, DECIMAL(20, + 0)) = cast(t3.c2, DECIMAL(-1, -1))]), rowset=16 access([t3.c1], [t3.c2], [t3.c3]), partitions(p0) is_index_back=false, is_global_index=false, filter_before_indexback[false,false], range_key([t3.__pk_increment]), range(MIN ; MAX)always true @@ -1496,8 +1496,8 @@ Outputs & filters: 0 - output(nil), filter(nil) table_columns([{t3: ({t3: (t3.__pk_increment, t3.c1, t3.c2, t3.c3)})}]), update([t3.c1=column_conv(BIGINT,PS:(20,0),NULL,1)]) - 1 - output([t3.__pk_increment], [t3.c1], [t3.c2], [t3.c3]), filter([cast(t3.c1, DATETIME(-1, -1)) = cast('2010-10-10 00:00:00', DATETIME(0, 0))], [cast(t3.c1, - DECIMAL(20, 0)) = cast(t3.c2, DECIMAL(-1, -1))]), rowset=16 + 1 - output([t3.__pk_increment], [t3.c1], [t3.c2], [t3.c3]), filter([INTERNAL_FUNCTION(t3.c1, 110, 17) = cast('2010-10-10 00:00:00', DATETIME(0, 0))], + [cast(t3.c1, DECIMAL(20, 0)) = cast(t3.c2, DECIMAL(-1, -1))]), rowset=16 access([t3.__pk_increment], [t3.c1], [t3.c2], [t3.c3]), partitions(p0) is_index_back=false, is_global_index=false, filter_before_indexback[false,false], range_key([t3.__pk_increment]), range(MIN ; MAX)always true