Files
oceanbase/src/sql/engine/expr/ob_expr_to_interval.cpp
2023-04-27 11:11:24 +00:00

468 lines
16 KiB
C++

/**
* 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_to_interval.h"
#include "lib/ob_name_def.h"
#include "lib/timezone/ob_time_convert.h"
#include "sql/session/ob_sql_session_info.h"
#include "sql/engine/expr/ob_expr_util.h"
namespace oceanbase
{
using namespace common;
using namespace share;
namespace sql
{
static bool is_mul_overflow(int64_t x, int64_t y, int64_t &result)
{
result = x * y;
return (x != 0) && result / x != y;
}
ObExprToYMInterval::ObExprToYMInterval(ObIAllocator &alloc)
: ObFuncExprOperator(alloc, T_FUN_SYS_TO_YMINTERVAL, N_TO_YMINTERVAL, 1, VALID_FOR_GENERATED_COL, NOT_ROW_DIMENSION)
{
}
ObExprToYMInterval::~ObExprToYMInterval()
{
}
int ObExprToYMInterval::calc_result_type1(ObExprResType &type, ObExprResType &type1, ObExprTypeCtx &type_ctx) const
{
int ret = OB_SUCCESS;
UNUSED(type_ctx);
type.set_interval_ym();
type.set_scale(ObAccuracy::MAX_ACCURACY2[ORACLE_MODE][ObIntervalYMType].get_scale());
type1.set_calc_type(ObVarcharType);
const ObSQLSessionInfo *session = type_ctx.get_session();
if (OB_ISNULL(session)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("session is NULL", K(ret));
} else {
ObSEArray<ObExprResType*, 1, ObNullAllocator> params;
OZ(params.push_back(&type1));
ObExprResType tmp_type;
OZ(aggregate_string_type_and_charset_oracle(*session, params, tmp_type));
OZ(deduce_string_param_calc_type_and_charset(*session, tmp_type, params));
}
return ret;
}
int ObExprToYMInterval::cg_expr(ObExprCGCtx &op_cg_ctx,
const ObRawExpr &raw_expr,
ObExpr &rt_expr) const
{
UNUSED(op_cg_ctx);
UNUSED(raw_expr);
int ret = OB_SUCCESS;
if (rt_expr.arg_cnt_ != 1) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("to_yminterval expr should have one param", K(ret), K(rt_expr.arg_cnt_));
} else if (OB_ISNULL(rt_expr.args_) || OB_ISNULL(rt_expr.args_[0])) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("children of to_yminterval expr is null", K(ret), K(rt_expr.args_));
} else {
rt_expr.eval_func_ = ObExprToYMInterval::calc_to_yminterval;
}
return ret;
}
int ObExprToYMInterval::calc_to_yminterval(const ObExpr &expr, ObEvalCtx &ctx,
ObDatum &expr_datum)
{
int ret = OB_SUCCESS;
ObDatum *param = NULL;
if (OB_FAIL(expr.args_[0]->eval(ctx, param))) {
LOG_WARN("calc first param failed", K(ret));
} else if (param->is_null()) {
expr_datum.set_null();
} else {
ObString str = param->get_string();
ObIntervalYMValue value;
ObScale max_scale = ObAccuracy::MAX_ACCURACY2[ORACLE_MODE][ObIntervalYMType].get_scale();
ObScale scale = max_scale;
if ((NULL == str.find('P')) ? //有P的是ISO格式
OB_FAIL(ObTimeConverter::str_to_interval_ym(str, value, scale))
: OB_FAIL(ObTimeConverter::iso_str_to_interval_ym(str, value))) {
LOG_WARN("fail to convert string", K(ret), K(str));
} else {
expr_datum.set_interval_ym(value.get_nmonth());
}
}
return ret;
}
ObExprToDSInterval::ObExprToDSInterval(ObIAllocator &alloc)
: ObFuncExprOperator(alloc, T_FUN_SYS_TO_DSINTERVAL, N_TO_DSINTERVAL, 1, VALID_FOR_GENERATED_COL, NOT_ROW_DIMENSION)
{
}
ObExprToDSInterval::~ObExprToDSInterval()
{
}
int ObExprToDSInterval::calc_result_type1(ObExprResType &type, ObExprResType &type1, ObExprTypeCtx &type_ctx) const
{
int ret = OB_SUCCESS;
UNUSED(type_ctx);
type.set_interval_ds();
type.set_scale(ObAccuracy::MAX_ACCURACY2[ORACLE_MODE][ObIntervalDSType].get_scale());
type1.set_calc_type(ObVarcharType);
const ObSQLSessionInfo *session = type_ctx.get_session();
if (OB_ISNULL(session)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("session is NULL", K(ret));
} else {
ObSEArray<ObExprResType*, 1, ObNullAllocator> params;
OZ(params.push_back(&type1));
ObExprResType tmp_type;
OZ(aggregate_string_type_and_charset_oracle(*session, params, tmp_type));
OZ(deduce_string_param_calc_type_and_charset(*session, tmp_type, params));
}
return ret;
}
int ObExprToDSInterval::cg_expr(ObExprCGCtx &op_cg_ctx,
const ObRawExpr &raw_expr,
ObExpr &rt_expr) const
{
UNUSED(op_cg_ctx);
UNUSED(raw_expr);
int ret = OB_SUCCESS;
if (rt_expr.arg_cnt_ != 1) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("to_dsinterval expr should have one param", K(ret), K(rt_expr.arg_cnt_));
} else if (OB_ISNULL(rt_expr.args_) || OB_ISNULL(rt_expr.args_[0])) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("children of to_dsinterval expr is null", K(ret), K(rt_expr.args_));
} else {
rt_expr.eval_func_ = ObExprToDSInterval::calc_to_dsinterval;
}
return ret;
}
int ObExprToDSInterval::calc_to_dsinterval(const ObExpr &expr, ObEvalCtx &ctx,
ObDatum &expr_datum)
{
int ret = OB_SUCCESS;
ObDatum *param = NULL;
if (OB_FAIL(expr.args_[0]->eval(ctx, param))) {
LOG_WARN("calc first param failed", K(ret));
} else if (param->is_null()) {
expr_datum.set_null();
} else {
ObString str = param->get_string();
ObIntervalDSValue value;
ObScale max_scale = ObAccuracy::MAX_ACCURACY2[ORACLE_MODE][ObIntervalDSType].get_scale();
ObScale scale = max_scale;
if ((NULL == str.find('P')) ? //有P的是ISO格式
OB_FAIL(ObTimeConverter::str_to_interval_ds(str, value, scale))
: OB_FAIL(ObTimeConverter::iso_str_to_interval_ds(str, value))) {
LOG_WARN("fail to convert string", K(ret), K(str));
} else {
expr_datum.set_interval_ds(value);
}
}
return ret;
}
ObExprNumToYMInterval::ObExprNumToYMInterval(ObIAllocator &alloc)
: ObFuncExprOperator(alloc, T_FUN_SYS_NUMTOYMINTERVAL, N_NUMTOYMINTERVAL, 2, VALID_FOR_GENERATED_COL, NOT_ROW_DIMENSION)
{
}
ObExprNumToYMInterval::~ObExprNumToYMInterval()
{
}
int ObExprNumToYMInterval::calc_result_type2(ObExprResType &type,
ObExprResType &type1,
ObExprResType &type2,
ObExprTypeCtx &type_ctx) const
{
int ret = OB_SUCCESS;
UNUSED(type_ctx);
type.set_interval_ym();
type.set_scale(ObAccuracy::MAX_ACCURACY2[ORACLE_MODE][ObIntervalYMType].get_scale());
type1.set_calc_type(ObNumberType);
type1.set_calc_scale(ORA_NUMBER_SCALE_UNKNOWN_YET);
type2.set_calc_type(ObVarcharType);
const ObSQLSessionInfo *session = type_ctx.get_session();
if (OB_ISNULL(session)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("session is NULL", K(ret));
} else {
ObSEArray<ObExprResType*, 1, ObNullAllocator> params;
OZ(params.push_back(&type2));
ObExprResType tmp_type;
OZ(aggregate_string_type_and_charset_oracle(*session, params, tmp_type));
OZ(deduce_string_param_calc_type_and_charset(*session, tmp_type, params));
}
return ret;
}
template <class T>
int ObExprNumToYMInterval::calc_result_common(const T &obj1,
const T &obj2,
ObIAllocator &calc_buf,
ObIntervalYMValue &ym_value)
{
int ret = OB_SUCCESS;
const number::ObNumber num_n = obj1.get_number();
ObString unit = obj2.get_string();
int64_t nmonth = 0;
int64_t mul_f = 0;
unit = unit.trim();
if (0 == unit.case_compare(ob_date_unit_type_str(DATE_UNIT_YEAR))) {
mul_f = ObIntervalYMValue::MONTHS_IN_YEAR;
} else if (0 == unit.case_compare(ob_date_unit_type_str(DATE_UNIT_MONTH))) {
mul_f = 1;
} else {
ret = OB_ERR_ILLEGAL_ARGUMENT_FOR_FUNCTION;
LOG_WARN("illegal argument for function", K(ret), K(unit));
}
if (OB_SUCC(ret)) {
int64_t n = 0;
if (num_n.is_valid_int64(n)) {
//如果是整数,短路径
if (OB_UNLIKELY(is_mul_overflow(mul_f, n, nmonth))) {
ret = OB_ERR_THE_LEADING_PRECISION_OF_THE_INTERVAL_IS_TOO_SMALL;
LOG_WARN("mul size overflow", K(ret));
}
} else {
number::ObNumber month_per_year;
number::ObNumber num_result;
ObNumStackOnceAlloc tmp_alloc;
if (mul_f == 1) {
if (OB_FAIL(num_result.from(num_n, tmp_alloc))) {
LOG_WARN("copy num failed", K(ret));
}
} else {
if (OB_FAIL(month_per_year.from(mul_f, calc_buf))) {
LOG_WARN("number from failed", K(ret));
} else if (OB_FAIL(num_n.mul(month_per_year, num_result, calc_buf))) {
LOG_WARN("number mul failed", K(ret));
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(num_result.round(0))) {
LOG_WARN("number found failed", K(ret));
} else if (OB_UNLIKELY(!num_result.is_valid_int64(nmonth))) {
ret = OB_ERR_THE_LEADING_PRECISION_OF_THE_INTERVAL_IS_TOO_SMALL;
LOG_WARN("number should be valid int64", K(ret));
}
}
}
}
if (OB_SUCC(ret)) {
ym_value.nmonth_ = nmonth;
if (OB_FAIL(ym_value.validate())) {
LOG_WARN("fail to validate interval", K(ret), K(ym_value));
}
}
return ret;
}
int ObExprNumToYMInterval::cg_expr(ObExprCGCtx &op_cg_ctx,
const ObRawExpr &raw_expr,
ObExpr &rt_expr) const
{
UNUSED(op_cg_ctx);
UNUSED(raw_expr);
int ret = OB_SUCCESS;
if (rt_expr.arg_cnt_ != 2) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("num_to_yminterval expr should have two params", K(ret), K(rt_expr.arg_cnt_));
} else if (OB_ISNULL(rt_expr.args_) || OB_ISNULL(rt_expr.args_[0])
|| OB_ISNULL(rt_expr.args_[1])) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("children of num_to_yminterval expr is null", K(ret), K(rt_expr.args_));
} else {
rt_expr.eval_func_ = ObExprNumToYMInterval::calc_num_to_yminterval;
}
return ret;
}
int ObExprNumToYMInterval::calc_num_to_yminterval(const ObExpr &expr, ObEvalCtx &ctx,
ObDatum &expr_datum)
{
int ret = OB_SUCCESS;
ObDatum *param1 = NULL;
ObDatum *param2 = NULL;
ObIntervalYMValue ym_value;
ObEvalCtx::TempAllocGuard alloc_guard(ctx);
if (OB_FAIL(expr.args_[0]->eval(ctx, param1))) {
LOG_WARN("calc first param failed", K(ret));
} else if (param1->is_null()) {
expr_datum.set_null();
} else if (OB_FAIL(expr.args_[1]->eval(ctx, param2))) {
LOG_WARN("calc second param failed", K(ret));
} else if (param2->is_null()) {
expr_datum.set_null();
} else if (OB_FAIL(calc_result_common(*param1, *param2, alloc_guard.get_allocator(), ym_value))) {
LOG_WARN("calc result failed", K(ret));
} else {
expr_datum.set_interval_ym(ym_value.get_nmonth());
}
return ret;
}
ObExprNumToDSInterval::ObExprNumToDSInterval(ObIAllocator &alloc)
: ObFuncExprOperator(alloc, T_FUN_SYS_NUMTODSINTERVAL, N_NUMTODSINTERVAL, 2, VALID_FOR_GENERATED_COL, NOT_ROW_DIMENSION)
{
}
ObExprNumToDSInterval::~ObExprNumToDSInterval()
{
}
int ObExprNumToDSInterval::calc_result_type2(ObExprResType &type,
ObExprResType &type1,
ObExprResType &type2,
ObExprTypeCtx &type_ctx) const
{
int ret = OB_SUCCESS;
UNUSED(type_ctx);
type.set_interval_ds();
type.set_scale(ObAccuracy::MAX_ACCURACY2[ORACLE_MODE][ObIntervalDSType].get_scale());
type1.set_calc_type(ObNumberType);
type1.set_calc_scale(ORA_NUMBER_SCALE_UNKNOWN_YET);
type2.set_calc_type(ObVarcharType);
const ObSQLSessionInfo *session = type_ctx.get_session();
if (OB_ISNULL(session)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("session is NULL", K(ret));
} else {
ObSEArray<ObExprResType*, 1, ObNullAllocator> params;
OZ(params.push_back(&type2));
ObExprResType tmp_type;
OZ(aggregate_string_type_and_charset_oracle(*session, params, tmp_type));
OZ(deduce_string_param_calc_type_and_charset(*session, tmp_type, params));
}
return ret;
}
template <class R, class T>
int ObExprNumToDSInterval::calc_result_common(R &result,
const T &obj1,
const T &obj2,
ObIAllocator &calc_buf)
{
int ret = OB_SUCCESS;
static int64_t mul_f[DATE_UNIT_DAY - DATE_UNIT_SECOND + 1] = {
1,
ObIntervalDSValue::SECONDS_IN_MINUTE,
ObIntervalDSValue::SECONDS_IN_MINUTE * ObIntervalDSValue::MINUTES_IN_HOUR,
ObIntervalDSValue::SECONDS_IN_MINUTE * ObIntervalDSValue::MINUTES_IN_HOUR * ObIntervalDSValue::HOURS_IN_DAY,
};
ObString unit = obj2.get_string();
number::ObNumber num_n = obj1.get_number();
int64_t nsecond = 0;
int64_t fs = 0;
int matched_idx = OB_INVALID_INDEX;
unit = unit.trim();
for (int i = DATE_UNIT_SECOND; i <= DATE_UNIT_DAY; ++i) {
if (0 == unit.case_compare(ob_date_unit_type_str(static_cast<ObDateUnitType>(i)))) {
matched_idx = i - DATE_UNIT_SECOND;
break;
}
}
if (OB_UNLIKELY(OB_INVALID_INDEX == matched_idx)) {
ret = OB_ERR_ILLEGAL_ARGUMENT_FOR_FUNCTION;
LOG_WARN("illegal argument for function", K(ret));
} else {
int64_t n = 0;
if (num_n.is_valid_int64(n)) {
//如果是整数,短路径
if (OB_UNLIKELY(is_mul_overflow(mul_f[matched_idx], n, nsecond))) {
ret = OB_ERR_THE_LEADING_PRECISION_OF_THE_INTERVAL_IS_TOO_SMALL;
LOG_WARN("mul size overflow", K(ret));
}
} else {
number::ObNumber num_mul_f;
number::ObNumber num_nsecond;
if (OB_FAIL(num_mul_f.from(mul_f[matched_idx], calc_buf))) {
LOG_WARN("number from failed", K(ret));
} else if (OB_FAIL(num_n.mul(num_mul_f, num_nsecond, calc_buf))) {
LOG_WARN("number mul failed", K(ret));
} else if (OB_FAIL(num_nsecond.round(MAX_SCALE_FOR_ORACLE_TEMPORAL))) {
LOG_WARN("number round failed", K(ret));
} else if (OB_UNLIKELY(!num_nsecond.is_int_parts_valid_int64(nsecond, fs))) {
ret = OB_ERR_THE_LEADING_PRECISION_OF_THE_INTERVAL_IS_TOO_SMALL;
LOG_WARN("number should be valid int64", K(ret));
}
}
}
if (OB_SUCC(ret)) {
if (nsecond < 0) {
fs = -fs;
}
ObIntervalDSValue value = ObIntervalDSValue(nsecond, static_cast<int32_t>(fs));
if (OB_FAIL(value.validate())) {
LOG_WARN("invalid interval result", K(ret), K(value));
} else {
result.set_interval_ds(value);
}
}
return ret;
}
int ObExprNumToDSInterval::cg_expr(ObExprCGCtx &op_cg_ctx,
const ObRawExpr &raw_expr,
ObExpr &rt_expr) const
{
UNUSED(op_cg_ctx);
UNUSED(raw_expr);
int ret = OB_SUCCESS;
if (rt_expr.arg_cnt_ != 2) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("num_to_dsinterval expr should have two params", K(ret), K(rt_expr.arg_cnt_));
} else if (OB_ISNULL(rt_expr.args_) || OB_ISNULL(rt_expr.args_[0])
|| OB_ISNULL(rt_expr.args_[1])) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("children of num_to_dsinterval expr is null", K(ret), K(rt_expr.args_));
} else {
rt_expr.eval_func_ = ObExprNumToDSInterval::calc_num_to_dsinterval;
}
return ret;
}
int ObExprNumToDSInterval::calc_num_to_dsinterval(const ObExpr &expr, ObEvalCtx &ctx,
ObDatum &expr_datum)
{
int ret = OB_SUCCESS;
ObDatum *param1 = NULL;
ObDatum *param2 = NULL;
ObEvalCtx::TempAllocGuard alloc_guard(ctx);
if (OB_FAIL(expr.args_[0]->eval(ctx, param1))) {
LOG_WARN("calc first param failed", K(ret));
} else if (param1->is_null()) {
expr_datum.set_null();
} else if (OB_FAIL(expr.args_[1]->eval(ctx, param2))) {
LOG_WARN("calc second param failed", K(ret));
} else if (param2->is_null()) {
expr_datum.set_null();
} else if (OB_FAIL(calc_result_common(expr_datum, *param1, *param2, alloc_guard.get_allocator()))) {
LOG_WARN("calc result failed", K(ret));
}
return ret;
}
} //namespace sql
} //namespace oceanbase