patch 4.0
This commit is contained in:
@ -17,134 +17,58 @@
|
||||
#include "sql/engine/expr/ob_expr_json_func_helper.h"
|
||||
#include "sql/session/ob_sql_session_info.h"
|
||||
|
||||
namespace oceanbase {
|
||||
namespace oceanbase
|
||||
{
|
||||
using namespace common;
|
||||
namespace sql {
|
||||
namespace sql
|
||||
{
|
||||
|
||||
ObExprOr::ObExprOr(ObIAllocator& alloc)
|
||||
ObExprOr::ObExprOr(ObIAllocator &alloc)
|
||||
: ObLogicalExprOperator(alloc, T_OP_OR, N_OR, PARAM_NUM_UNKNOWN, NOT_ROW_DIMENSION)
|
||||
{
|
||||
param_lazy_eval_ = true;
|
||||
}
|
||||
|
||||
int ObExprOr::calc_result_typeN(
|
||||
ObExprResType& type, ObExprResType* types_stack, int64_t param_num, ObExprTypeCtx& type_ctx) const
|
||||
int ObExprOr::calc_result_typeN(ObExprResType &type,
|
||||
ObExprResType *types_stack,
|
||||
int64_t param_num,
|
||||
ObExprTypeCtx &type_ctx) const
|
||||
{
|
||||
UNUSED(type_ctx);
|
||||
UNUSED(types_stack);
|
||||
UNUSED(param_num);
|
||||
int ret = OB_SUCCESS;
|
||||
// just keep enumset as origin
|
||||
//just keep enumset as origin
|
||||
type.set_int32();
|
||||
type.set_precision(DEFAULT_PRECISION_FOR_BOOL);
|
||||
type.set_scale(DEFAULT_SCALE_FOR_INTEGER);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// expect obj1 and obj2 already evaluated
|
||||
int ObExprOr::calc_result2(
|
||||
common::ObObj& result, const common::ObObj& obj1, const common::ObObj& obj2, common::ObExprCtx& expr_ctx) const
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
bool obj1_is_true = false;
|
||||
EXPR_SET_CAST_CTX_MODE(expr_ctx);
|
||||
bool is_obj1_json = ob_is_json(obj1.get_type());
|
||||
if (is_obj1_json) {
|
||||
// cause for json type, in some case can't transform to number type
|
||||
// add special process for json type
|
||||
// use the some logical as mysql
|
||||
int cmp_result = 0;
|
||||
if (obj1.is_null()) {
|
||||
} else if (OB_FAIL(ObJsonExprHelper::is_json_zero(obj1.get_string(), cmp_result))) {
|
||||
LOG_WARN("failed: compare json", K(ret));
|
||||
} else {
|
||||
obj1_is_true = cmp_result != 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_obj1_json && OB_FAIL(ObLogicalExprOperator::is_true(obj1, expr_ctx.cast_mode_ | CM_NO_RANGE_CHECK, obj1_is_true))) {
|
||||
LOG_WARN("fail to evaluate obj1", K(obj1), K(ret));
|
||||
} else if (obj1_is_true) {
|
||||
result.set_int32(static_cast<int32_t>(true));
|
||||
} else {
|
||||
if (OB_UNLIKELY(obj1.is_null() && obj2.is_null())) {
|
||||
result.set_null();
|
||||
} else if (OB_UNLIKELY(obj1.is_null())) {
|
||||
ret = cacl_res_with_one_param_null(result, obj1, obj2, expr_ctx);
|
||||
} else if (OB_UNLIKELY(obj2.is_null())) {
|
||||
ret = cacl_res_with_one_param_null(result, obj2, obj1, expr_ctx);
|
||||
} else {
|
||||
// obj1 must be false here.
|
||||
bool bool_v2 = false;
|
||||
|
||||
bool is_obj2_json = ob_is_json(obj2.get_type());
|
||||
if (is_obj2_json) {
|
||||
int cmp_result = 0;
|
||||
if (obj1.is_null()) {
|
||||
} else if (OB_FAIL(ObJsonExprHelper::is_json_zero(obj1.get_string(), cmp_result))) {
|
||||
LOG_WARN("failed: compare json", K(ret));
|
||||
} else {
|
||||
bool_v2 = cmp_result != 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (OB_FAIL(ObLogicalExprOperator::is_true(obj2, expr_ctx.cast_mode_ | CM_NO_RANGE_CHECK, bool_v2))) {
|
||||
LOG_WARN("fail to evaluate obj2", K(obj2), K(ret));
|
||||
} else {
|
||||
result.set_int32(static_cast<int32_t>(bool_v2));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObExprOr::calc_resultN(ObObj& result, const ObObj* objs, int64_t param_num, ObExprCtx& expr_ctx) const
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (OB_ISNULL(objs) || OB_UNLIKELY(1 >= param_num)) {
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
LOG_WARN("invalid arguments", K(ret), K(objs), K(param_num));
|
||||
} else if (OB_FAIL(param_eval(expr_ctx, objs[0], 0))) {
|
||||
LOG_WARN("failed: eval objs[0]", K(ret));
|
||||
} else {
|
||||
result = objs[0];
|
||||
if (result.is_true()) {
|
||||
result.set_int32(static_cast<int32_t>(true));
|
||||
}
|
||||
for (int64_t idx = 1; idx < param_num && OB_SUCCESS == ret && !result.is_true(); ++idx) {
|
||||
if (OB_FAIL(param_eval(expr_ctx, objs[idx], idx))) {
|
||||
LOG_WARN("failed: eval objs[idx]", K(idx), K(ret));
|
||||
} else {
|
||||
ret = calc_result2(result, result, objs[idx], expr_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObExprOr::cacl_res_with_one_param_null(
|
||||
common::ObObj& res, const common::ObObj& left, const common::ObObj& right, common::ObExprCtx& expr_ctx)
|
||||
int ObExprOr::calc_res_with_one_param_null(common::ObObj &res,
|
||||
const common::ObObj &left,
|
||||
const common::ObObj &right,
|
||||
common::ObExprCtx &expr_ctx)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (OB_UNLIKELY((!left.is_null()) || right.is_null())) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("unexpected params", K(ret), K(left), K(right));
|
||||
} else {
|
||||
// left is null while right is not null. evaluate the right obj
|
||||
//left is null while right is not null. evaluate the right obj
|
||||
bool value = false;
|
||||
|
||||
EXPR_SET_CAST_CTX_MODE(expr_ctx);
|
||||
if (OB_FAIL(ObObjEvaluator::is_true(right, expr_ctx.cast_mode_ | CM_NO_RANGE_CHECK, value))) {
|
||||
LOG_WARN("fail to evaluate obj1", K(right), K(ret));
|
||||
// ugly. but compatible with mysql.
|
||||
//ugly. but compatible with mysql.
|
||||
if (OB_ERR_TRUNCATED_WRONG_VALUE_FOR_FIELD == ret) {
|
||||
ret = OB_SUCCESS;
|
||||
res.set_null();
|
||||
}
|
||||
// By design, compatible with mysql and oracle:
|
||||
// null or false == NULL. null or true == true.
|
||||
// see "NULL and the three-valued logic"
|
||||
// see "NULL and the three-valued logic" https://en.wikipedia.org/wiki/Null_(SQL)#Comparisons_with_NULL_and_the_three-valued_logic_(3VL)
|
||||
} else if (value) {
|
||||
res.set_int32(static_cast<int32_t>(true));
|
||||
} else {
|
||||
@ -157,7 +81,7 @@ int ObExprOr::cacl_res_with_one_param_null(
|
||||
// create table t1 as (select null or ''); -> NULL;
|
||||
// create table t1 as (select 1 or ''); -> 1;
|
||||
// create table t1 as (select 0 or ''); -> 0;
|
||||
static int calc_with_one_empty_str(const ObDatum& in_datum, ObDatum& out_datum)
|
||||
/*static int calc_with_one_empty_str(const ObDatum &in_datum, ObDatum &out_datum)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (in_datum.is_null()) {
|
||||
@ -166,58 +90,39 @@ static int calc_with_one_empty_str(const ObDatum& in_datum, ObDatum& out_datum)
|
||||
out_datum.set_bool(in_datum.get_bool());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}*/
|
||||
|
||||
static int calc_or_expr2(const ObDatum& left, const ObDatum& right, const bool left_empty_str_cast_failed,
|
||||
const bool right_empty_str_cast_failed, ObDatum& res)
|
||||
static int calc_or_expr2(const ObDatum &left, const ObDatum &right, ObDatum &res)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (left_empty_str_cast_failed) {
|
||||
calc_with_one_empty_str(right, res);
|
||||
} else if (right_empty_str_cast_failed) {
|
||||
calc_with_one_empty_str(left, res);
|
||||
if (left.is_true() || right.is_true()) {
|
||||
res.set_true();
|
||||
} else if (left.is_null() || right.is_null()) {
|
||||
res.set_null();
|
||||
} else {
|
||||
if (left.is_true() || right.is_true()) {
|
||||
res.set_true();
|
||||
} else if (left.is_null() || right.is_null()) {
|
||||
res.set_null();
|
||||
} else {
|
||||
res.set_false();
|
||||
}
|
||||
res.set_false();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Special treatment:
|
||||
// 1. select null or''; -> or the result is 0
|
||||
// Reason: For the select statement, cast_mode will automatically add WARN_ON_FAIL,
|
||||
// so when the empty string is converted to number,
|
||||
// The result is 0, the error code is overwritten, the above statement will return 0
|
||||
// 2. create table t1 as (select null or''); -> the result of or is NULL
|
||||
// Reason: For non-select/explain statements, cast_mode does not have WARN_ON_FAIL,
|
||||
// so when the above empty string is converted to number,
|
||||
// Report OB_ERR_TRUNCATED_WRONG_VALUE_FOR_FIELD, but in order to be compatible with MySQL,
|
||||
// special processing will be carried out here,
|
||||
// Overwrite the error code, and the result of or is NULL instead of 0
|
||||
// 特殊处理:
|
||||
// 1. select null or ''; -> or结果为0
|
||||
// 原因:针对select语句,cast_mode会自动加上WARN_ON_FAIL,所以空串转为number时,
|
||||
// 结果为0,错误码被覆盖,上述语句就会返回0
|
||||
// 2. create table t1 as (select null or ''); -> or的结果为NULL
|
||||
// 原因:针对非select/explain语句,cast_mode没有WARN_ON_FAIL,所以上面空串转number时,
|
||||
// 报OB_ERR_TRUNCATED_WRONG_VALUE_FOR_FIELD,但是为了兼容MySQL,这里会进行特殊处理,
|
||||
// 将错误码覆盖,并且or的结果为NULL而非0
|
||||
//
|
||||
// The special processing for empty strings mentioned above is for MySQL,
|
||||
// because the child nodes on both sides of or must be in Oracle mode
|
||||
// An expression with Boolean semantics will not be an empty string directly
|
||||
int calc_or_exprN(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& res_datum)
|
||||
// 上面说的针对空串的特殊处理都是针对MySQL的,因为Oracle模式下or两边的子节点都必须是有
|
||||
// 布尔语义的表达式,不会直接是空串
|
||||
int calc_or_exprN(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &res_datum)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObDatum* tmp_res = NULL;
|
||||
ObDatum* child_res = NULL;
|
||||
bool left_empty_str_cast_failed = false;
|
||||
bool right_empty_str_cast_failed = false;
|
||||
ObDatum *tmp_res = NULL;
|
||||
ObDatum *child_res = NULL;
|
||||
if (OB_FAIL(expr.args_[0]->eval(ctx, tmp_res))) {
|
||||
// An empty string will fail to cast to number, which requires special handling
|
||||
if (OB_ERR_TRUNCATED_WRONG_VALUE_FOR_FIELD == ret) {
|
||||
ret = OB_SUCCESS;
|
||||
left_empty_str_cast_failed = true;
|
||||
} else {
|
||||
LOG_WARN("eval arg 0 failed", K(ret));
|
||||
}
|
||||
LOG_WARN("eval arg 0 failed", K(ret));
|
||||
} else if (tmp_res->is_null()) {
|
||||
res_datum.set_null();
|
||||
} else {
|
||||
@ -226,22 +131,13 @@ int calc_or_exprN(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& res_datum)
|
||||
if (OB_SUCC(ret) && !res_datum.is_true()) {
|
||||
for (int64_t i = 1; OB_SUCC(ret) && i < expr.arg_cnt_; ++i) {
|
||||
if (OB_FAIL(expr.args_[i]->eval(ctx, child_res))) {
|
||||
if (OB_ERR_TRUNCATED_WRONG_VALUE_FOR_FIELD == ret) {
|
||||
ret = OB_SUCCESS;
|
||||
right_empty_str_cast_failed = true;
|
||||
} else {
|
||||
LOG_WARN("eval arg failed", K(ret), K(i));
|
||||
}
|
||||
LOG_WARN("eval arg failed", K(ret), K(i));
|
||||
}
|
||||
if (OB_SUCC(ret)) {
|
||||
if (OB_FAIL(calc_or_expr2(
|
||||
res_datum, *child_res, left_empty_str_cast_failed, right_empty_str_cast_failed, res_datum))) {
|
||||
if (OB_FAIL(calc_or_expr2(res_datum, *child_res, res_datum))) {
|
||||
LOG_WARN("calc_or_expr2 failed", K(ret), K(i));
|
||||
} else if (res_datum.is_true()) {
|
||||
break;
|
||||
} else {
|
||||
left_empty_str_cast_failed = false;
|
||||
right_empty_str_cast_failed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -249,19 +145,163 @@ int calc_or_exprN(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& res_datum)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObExprOr::cg_expr(ObExprCGCtx& expr_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
|
||||
int ObExprOr::cg_expr(ObExprCGCtx &expr_cg_ctx, const ObRawExpr &raw_expr,
|
||||
ObExpr &rt_expr) const
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
UNUSED(expr_cg_ctx);
|
||||
if (OB_ISNULL(rt_expr.args_) || OB_UNLIKELY(2 > rt_expr.arg_cnt_) ||
|
||||
OB_UNLIKELY(rt_expr.arg_cnt_ != raw_expr.get_param_count())) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("args_ is NULL or arg_cnt_ is invalid or raw_expr is invalid", K(ret), K(rt_expr), K(raw_expr));
|
||||
LOG_WARN("args_ is NULL or arg_cnt_ is invalid or raw_expr is invalid",
|
||||
K(ret), K(rt_expr), K(raw_expr));
|
||||
} else {
|
||||
rt_expr.eval_func_ = calc_or_exprN;
|
||||
rt_expr.eval_batch_func_ = eval_or_batch_exprN;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace sql
|
||||
} // namespace oceanbase
|
||||
int ObExprOr::eval_or_batch_exprN(const ObExpr &expr, ObEvalCtx &ctx,
|
||||
const ObBitVector &skip, const int64_t batch_size)
|
||||
{
|
||||
LOG_DEBUG("eval or batch mode", K(batch_size));
|
||||
int ret = OB_SUCCESS;
|
||||
ObDatum *results = expr.locate_batch_datums(ctx);
|
||||
if (OB_ISNULL(results)) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("expr results frame is not init", K(ret));
|
||||
} else {
|
||||
ObBitVector &eval_flags = expr.get_evaluated_flags(ctx);
|
||||
ObBitVector &my_skip = expr.get_pvt_skip(ctx);
|
||||
my_skip.deep_copy(skip, batch_size);
|
||||
const int64_t real_param = batch_size - my_skip.accumulate_bit_cnt(batch_size);
|
||||
int64_t skip_cnt = 0; //记录一个batch中skip的数量, 所有参数被skip即可结束
|
||||
|
||||
/*为省略对results的初始化操作以及减少对my_skip的写入,分成3段求值
|
||||
*args_[first]需要设置eval flags, 设置初始值, 为true设置skip
|
||||
*args_[middle]需要设置非false的result, 为true设置skip
|
||||
*args_[last]只需要设置非false的result*/
|
||||
|
||||
//eval first
|
||||
if (real_param == skip_cnt) {
|
||||
} else if (OB_FAIL(expr.args_[0]->eval_batch(ctx, my_skip, batch_size))) {
|
||||
LOG_WARN("failed to eval batch result args0", K(ret));
|
||||
} else if (expr.args_[0]->is_batch_result()) {
|
||||
ObDatum *curr_datum = nullptr;
|
||||
ObDatum *datum_array = expr.args_[0]->locate_batch_datums(ctx);
|
||||
for (int64_t j = 0; OB_SUCC(ret) && j < batch_size; ++j) {
|
||||
if (my_skip.at(j)) {
|
||||
continue;
|
||||
}
|
||||
curr_datum = &datum_array[j];
|
||||
if (curr_datum->is_null()) {
|
||||
results[j].set_null();
|
||||
} else if (true == curr_datum->get_bool()) {
|
||||
results[j].set_bool(true);
|
||||
my_skip.set(j);
|
||||
} else {
|
||||
results[j].set_bool(false);
|
||||
}
|
||||
eval_flags.set(j);
|
||||
}
|
||||
} else {
|
||||
ObDatum *curr_datum = &expr.args_[0]->locate_expr_datum(ctx);
|
||||
for (int64_t j = 0; OB_SUCC(ret) && j < batch_size; ++j) {
|
||||
if (my_skip.at(j)) {
|
||||
continue;
|
||||
}
|
||||
if (curr_datum->is_null()) {
|
||||
results[j].set_null();
|
||||
} else if (true == curr_datum->get_bool()) {
|
||||
results[j].set_bool(true);
|
||||
my_skip.set(j);
|
||||
} else {
|
||||
results[j].set_bool(false);
|
||||
}
|
||||
eval_flags.set(j);
|
||||
}
|
||||
}
|
||||
//eval middle
|
||||
int64_t arg_idx = 1;
|
||||
for (; OB_SUCC(ret) && arg_idx < expr.arg_cnt_ - 1 && skip_cnt < real_param; ++arg_idx) {
|
||||
if (OB_FAIL(expr.args_[arg_idx]->eval_batch(ctx, my_skip, batch_size))) {
|
||||
LOG_WARN("failed to eval batch result", K(ret), K(arg_idx));
|
||||
} else if (expr.args_[arg_idx]->is_batch_result()) {
|
||||
ObDatum *curr_datum = nullptr;
|
||||
ObDatum *datum_array = expr.args_[arg_idx]->locate_batch_datums(ctx);
|
||||
for (int64_t j = 0; OB_SUCC(ret) && j < batch_size; ++j) {
|
||||
if (my_skip.at(j)) {
|
||||
continue;
|
||||
}
|
||||
curr_datum = &datum_array[j];
|
||||
if (curr_datum->is_null()) {
|
||||
results[j].set_null();
|
||||
} else if (true == curr_datum->get_bool()) {
|
||||
results[j].set_bool(true);
|
||||
my_skip.set(j);
|
||||
} else {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ObDatum *curr_datum = &expr.args_[arg_idx]->locate_expr_datum(ctx);
|
||||
for (int64_t j = 0; OB_SUCC(ret) && j < batch_size; ++j) {
|
||||
if (my_skip.at(j)) {
|
||||
continue;
|
||||
}
|
||||
if (curr_datum->is_null()) {
|
||||
results[j].set_null();
|
||||
} else if (true == curr_datum->get_bool()) {
|
||||
results[j].set_bool(true);
|
||||
my_skip.set(j);
|
||||
} else {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//eval last
|
||||
if (OB_FAIL(ret)) {
|
||||
} else if (real_param == skip_cnt) {
|
||||
} else if (OB_FAIL(expr.args_[arg_idx]->eval_batch(ctx, my_skip, batch_size))) {
|
||||
LOG_WARN("failed to eval batch result args0", K(ret));
|
||||
} else if (expr.args_[arg_idx]->is_batch_result()) {
|
||||
ObDatum *curr_datum = nullptr;
|
||||
ObDatum *datum_array = expr.args_[arg_idx]->locate_batch_datums(ctx);
|
||||
for (int64_t j = 0; OB_SUCC(ret) && j < batch_size; ++j) {
|
||||
if (my_skip.at(j)) {
|
||||
continue;
|
||||
}
|
||||
curr_datum = &datum_array[j];
|
||||
if (curr_datum->is_null()) {
|
||||
results[j].set_null();
|
||||
} else if (true == curr_datum->get_bool()) {
|
||||
results[j].set_bool(true);
|
||||
} else {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ObDatum *curr_datum = &expr.args_[arg_idx]->locate_expr_datum(ctx);
|
||||
for (int64_t j = 0; OB_SUCC(ret) && j < batch_size; ++j) {
|
||||
if (my_skip.at(j)) {
|
||||
continue;
|
||||
}
|
||||
if (curr_datum->is_null()) {
|
||||
results[j].set_null();
|
||||
} else if (true == curr_datum->get_bool()) {
|
||||
results[j].set_bool(true);
|
||||
} else {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user