Add normal_date_expr to the rewriting phase to solve the problem of comparing illegal date constants with date columns.
This commit is contained in:
1
deps/oblib/src/lib/ob_name_def.h
vendored
1
deps/oblib/src/lib/ob_name_def.h
vendored
@ -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_
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
419
src/sql/engine/expr/ob_expr_align_date4cmp.cpp
Normal file
419
src/sql/engine/expr/ob_expr_align_date4cmp.cpp
Normal file
@ -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<ObOpRawExpr *>(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<ObConstRawExpr *>(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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
83
src/sql/engine/expr/ob_expr_align_date4cmp.h
Normal file
83
src/sql/engine/expr/ob_expr_align_date4cmp.h
Normal file
@ -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_ */
|
||||
@ -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[] = {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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这个类主要涉及到相关的函数用于展开复杂的聚合函数:
|
||||
|
||||
@ -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<ObParentDMLStmt> &parent_stmts,
|
||||
ObDMLStmt *&stmt,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user