/** * 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_REWRITE #include "sql/rewrite/ob_transform_rule.h" #include "sql/rewrite/ob_predicate_deduce.h" #include "sql/optimizer/ob_optimizer_util.h" #include "sql/rewrite/ob_transform_utils.h" #include "sql/resolver/expr/ob_raw_expr_util.h" using namespace oceanbase::sql; using namespace oceanbase::common; int ObPredicateDeduce::add_predicate(ObRawExpr* pred, bool& is_added) { int ret = OB_SUCCESS; ObObjMeta cmp_meta; ObRawExpr* left_expr = NULL; ObRawExpr* right_expr = NULL; is_added = false; if (OB_ISNULL(pred) || OB_ISNULL(left_expr = pred->get_param_expr(0)) || OB_ISNULL(right_expr = pred->get_param_expr(1))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("predicate is invalid", K(ret), K(pred), K(left_expr), K(right_expr)); } else if (OB_FAIL(ObRelationalExprOperator::get_equal_meta( cmp_meta, left_expr->get_result_type(), right_expr->get_result_type()))) { LOG_WARN("failed to get equal meta", K(ret), K(*pred)); } else if (cmp_type_ == cmp_meta || input_preds_.empty()) { cmp_type_ = cmp_meta; if (OB_FAIL(input_preds_.push_back(pred))) { LOG_WARN("failed to push bach edges", K(ret)); } else if (OB_FAIL(add_var_to_array_no_dup(input_exprs_, left_expr))) { LOG_WARN("failed to push back nodes", K(ret)); } else if (OB_FAIL(add_var_to_array_no_dup(input_exprs_, right_expr))) { LOG_WARN("failed to push back nodes", K(ret)); } else { is_added = true; } } return ret; } int ObPredicateDeduce::check_deduce_validity(ObRawExpr* cond, bool& is_valid) { int ret = OB_SUCCESS; const ObRawExpr* left_expr = NULL; const ObRawExpr* right_expr = NULL; is_valid = true; if (OB_ISNULL(cond)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("condition is null", K(ret)); } else if (!is_simple_condition(cond->get_expr_type()) || contain_special_expr(*cond) || cond->has_flag(IS_CONST)) { is_valid = false; } else if (OB_ISNULL(left_expr = cond->get_param_expr(0)) || OB_ISNULL(right_expr = cond->get_param_expr(1))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("param exprs are null", K(ret), K(*cond)); } else if (!left_expr->get_result_type().is_valid() || !right_expr->get_result_type().is_valid()) { is_valid = false; } else if (left_expr == right_expr || left_expr->same_as(*right_expr)) { is_valid = false; } return ret; } int ObPredicateDeduce::deduce_simple_predicates(ObTransformerCtx& ctx, ObIArray& result) { int ret = OB_SUCCESS; ObArray chosen; if (OB_FAIL(init())) { LOG_WARN("failed to init graph", K(ret)); } else if (OB_FAIL(chosen.prepare_allocate(N * N))) { LOG_WARN("failed to prepaer allocate graph", K(ret)); } else { for (int64_t i = 0; i < chosen.count(); ++i) { chosen.at(i) = 0; } } if (OB_SUCC(ret)) { ObSqlBitSet<> expr_equal_with_const; if (OB_FAIL(choose_equal_preds(chosen, expr_equal_with_const))) { LOG_WARN("failed to choose equal predicates", K(ret)); } else if (OB_FAIL(choose_unequal_preds(ctx, chosen, expr_equal_with_const))) { LOG_WARN("failed to choose unequal predicates", K(ret)); } else if (OB_FAIL(choose_input_preds(chosen, result))) { LOG_WARN("failed to choose input preds", K(ret)); } else if (OB_FAIL(create_simple_preds(ctx, chosen, result))) { LOG_WARN("failed to create simple predicates", K(ret)); } } return ret; } int ObPredicateDeduce::init() { int ret = OB_SUCCESS; N = input_exprs_.count(); if (OB_FAIL(graph_.prepare_allocate(N * N))) { LOG_WARN("failed to prepare allocate graph", K(ret)); } else if (OB_FAIL(type_safety_.prepare_allocate(N * N))) { LOG_WARN("failed to prepare allocate type safe array", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) { for (int64_t j = 0; OB_SUCC(ret) && j < N; ++j) { bool& type_safe = type_safety_.at(i * N + j); type_safe = false; graph_.at(i * N + j) = 0; if (OB_FAIL(check_type_safe(i, j, type_safe))) { LOG_WARN("failed to check type safe", K(ret)); } } set(graph_.at(i * N + i), EQ); } for (int64_t i = 0; OB_SUCC(ret) && i < input_preds_.count(); ++i) { int64_t left_id = -1; int64_t right_id = -1; Type type; if (OB_FAIL(convert_pred(input_preds_.at(i), left_id, right_id, type))) { LOG_WARN("failed to check predicate", K(ret)); } else { set(graph_, left_id, right_id, type); } } if (OB_SUCC(ret)) { deduce(graph_); } return ret; } int ObPredicateDeduce::choose_equal_preds(ObIArray& chosen, ObSqlBitSet<>& expr_equal_with_const) { int ret = OB_SUCCESS; ObSEArray table_filter; ObSEArray equal_pairs; for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) { if (OB_FAIL(equal_pairs.push_back(INT64_MAX))) { LOG_WARN("failed to push back item", K(ret)); } } for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) { for (int64_t j = i + 1; OB_SUCC(ret) && j < N; ++j) { if (!has(graph_, i, j, EQ) || i == j || !is_type_safe(i, j)) { // do nothing } else if (is_raw_const(i) && is_raw_const(j)) { // do nothing for startup filter } else if (is_raw_const(i) || is_raw_const(j)) { set(chosen, i, j, EQ); int64_t const_id = is_raw_const(i) ? i : j; int64_t var_id = (const_id != i) ? i : j; if (equal_pairs.at(var_id) > const_id) { equal_pairs.at(var_id) = const_id; } if (OB_FAIL(expr_equal_with_const.add_member(var_id))) { LOG_WARN("failed to add member", K(ret)); } } else if (!is_table_filter(i, j)) { // do nothing } else if (OB_FAIL(table_filter.push_back(i * N + j))) { LOG_WARN("failed to push back table filter", K(ret)); } } } for (int64_t i = 0; OB_SUCC(ret) && i < table_filter.count(); ++i) { int64_t left = table_filter.at(i) / N; int64_t right = table_filter.at(i) % N; if (equal_pairs.at(left) == equal_pairs.at(right) && equal_pairs.at(right) != INT64_MAX) { // already implied } else { set(chosen, left, right, EQ); equal_pairs.at(left) = left; equal_pairs.at(right) = left; } } return ret; } int ObPredicateDeduce::choose_unequal_preds( ObTransformerCtx& ctx, ObIArray& chosen, ObSqlBitSet<>& expr_equal_with_const) { int ret = OB_SUCCESS; ObSEArray ordered_list; if (OB_FAIL(topo_sort(topo_order_))) { LOG_WARN("failed to topo sort", K(ret)); } for (int64_t i = 0; i < topo_order_.count(); ++i) { // if a variable A equal with a const B, // there is no need to deduce unequal predicates like A > c1 for A // because, it is better to replace that with B > c1 if (topo_order_.at(i) < 0 || topo_order_.at(i) >= N) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid expr id", K(ret), K(topo_order_.at(i))); } else if (expr_equal_with_const.has_member(topo_order_.at(i))) { // do nothing } else if (OB_FAIL(ordered_list.push_back(topo_order_.at(i)))) { LOG_WARN("failed to push back member", K(ret)); } } for (int64_t i = 1; OB_SUCC(ret) && i < ordered_list.count(); ++i) { int64_t left = ordered_list.at(i); for (int64_t j = 0; OB_SUCC(ret) && j < i; ++j) { int64_t right = ordered_list.at(j); bool skip_check = false; Type type = has(graph_, left, right, GT) ? GT : has(graph_, left, right, GE) ? GE : EQ; if (!is_type_safe(left, right) || (type == EQ)) { // do nothing } else if (!is_table_filter(left, right)) { // do nothing } else if (OB_FAIL(check_index_part_cond(ctx, input_exprs_.at(left), input_exprs_.at(right), skip_check))) { LOG_WARN("failed to check is index partition condition", K(ret)); } else { bool can_deduced = false; for (int64_t k = j + 1; !skip_check && !can_deduced && k < i; ++k) { int64_t mid = ordered_list.at(k); if (is_const(mid)) { can_deduced = check_deduciable(graph_, mid, left, right, type); } else if (skip_check) { // do nothing } else if (is_table_filter(left, mid) && is_table_filter(mid, right)) { can_deduced = check_deduciable(graph_, mid, left, right, type); } } if (!can_deduced) { set(chosen, left, right, type); } } } } return ret; } int ObPredicateDeduce::check_index_part_cond( ObTransformerCtx& ctx, ObRawExpr* left_expr, ObRawExpr* right_expr, bool& is_valid) { int ret = OB_SUCCESS; is_valid = false; ObRawExpr* check_expr = NULL; if (OB_ISNULL(left_expr) || OB_ISNULL(right_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid index", K(ret), K(left_expr), K(right_expr)); } else if (!left_expr->get_expr_levels().has_member(stmt_.get_current_level())) { check_expr = right_expr; } else if (!right_expr->get_expr_levels().has_member(stmt_.get_current_level())) { check_expr = left_expr; } if (NULL != check_expr) { if (check_expr->is_column_ref_expr() && check_expr->get_expr_level() == stmt_.get_current_level()) { ObColumnRefRawExpr* col = static_cast(check_expr); const share::schema::ObColumnSchemaV2* column_schema = NULL; TableItem* table = stmt_.get_table_item_by_id(col->get_table_id()); if (OB_ISNULL(table)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("table is null", K(ret), K(table)); } else if (!table->is_basic_table()) { } else if (OB_FAIL(ctx.schema_checker_->get_column_schema( table->ref_id_, col->get_column_id(), column_schema, true))) { LOG_WARN("failed to get column schema", K(ret), K(table->ref_id_), K(col->get_column_id())); } else if (OB_ISNULL(column_schema)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("column schema is null", K(ret)); } else if (column_schema->is_rowkey_column()) { is_valid = true; } else if (OB_FAIL(ctx.schema_checker_->check_column_has_index(table->ref_id_, col->get_column_id(), is_valid))) { LOG_WARN("failed to check column is a key", K(ret)); } else if (is_valid) { // do nothing } else if (OB_FAIL(ctx.schema_checker_->check_if_partition_key(table->ref_id_, col->get_column_id(), is_valid))) { LOG_WARN("failed to check if partition key", K(ret)); } } } return ret; } int ObPredicateDeduce::choose_input_preds(ObIArray& chosen, ObIArray& output_exprs) { int ret = OB_SUCCESS; int64_t left = -1; int64_t right = -1; Type type = EQ; ObArray deduced; ObArray keep; if (OB_FAIL(deduced.assign(chosen))) { LOG_WARN("failed to assign array", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) { if (i * N + i >= chosen.count()) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid index", K(ret)); } else { set(deduced, i, i, EQ); } } if (OB_SUCC(ret)) { deduce(deduced); } const Type types[3] = {EQ, GT, GE}; for (int64_t op_type = 0; OB_SUCC(ret) && op_type <= 2; ++op_type) { const Type check_type = types[op_type]; for (int64_t i = 0; OB_SUCC(ret) && i < input_preds_.count(); ++i) { ObRawExpr* pred = input_preds_.at(i); if (op_type == 0 && OB_FAIL(keep.push_back(false))) { LOG_WARN("failed to init array", K(ret)); } else if (OB_FAIL(convert_pred(pred, left, right, type))) { LOG_WARN("failed to convert predicate", K(ret)); } else if (check_type == type) { if (has(chosen, left, right, type)) { keep.at(i) = true; clear(chosen, left, right, type); } else if (!has(deduced, left, right, type)) { keep.at(i) = true; set(deduced, left, right, type); expand_graph(deduced, left, right); if (type == EQ) { expand_graph(deduced, right, left); } } } } } // we do not want to change the order of input preds // hence, all selected input preds are added into output in the order. for (int64_t i = 0; OB_SUCC(ret) && i < input_preds_.count(); ++i) { if (!keep.at(i)) { // do nothing } else if (OB_FAIL(output_exprs.push_back(input_preds_.at(i)))) { LOG_WARN("failed to push back input predicate", K(ret)); } } return ret; } int ObPredicateDeduce::create_simple_preds( ObTransformerCtx& ctx, ObIArray& chosen, ObIArray& output_exprs) { int ret = OB_SUCCESS; ObRawExpr* pred = NULL; ObRawExprFactory* expr_factory = ctx.expr_factory_; ObSQLSessionInfo* session_info = ctx.session_info_; if (OB_ISNULL(session_info) || OB_ISNULL(expr_factory)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("params are invalid", K(ret), K(session_info), K(expr_factory)); } for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) { for (int64_t j = 0; OB_SUCC(ret) && j < N; ++j) { const uint8_t edge = chosen.at(i * N + j); pred = NULL; if (OB_SUCC(ret) && has(edge, EQ)) { clear(chosen, i, j, EQ); if (OB_FAIL(ObRawExprUtils::create_double_op_expr( *expr_factory, session_info, T_OP_EQ, pred, input_exprs_.at(i), input_exprs_.at(j)))) { LOG_WARN("failed to create double op expr", K(ret)); } else if (OB_FAIL(pred->pull_relation_id_and_levels(stmt_.get_current_level()))) { LOG_WARN("failed to pull relation id and levels", K(ret)); } else if (OB_FAIL(output_exprs.push_back(pred))) { LOG_WARN("failed to push back pred", K(ret)); } } if (OB_SUCC(ret) && has(edge, GT)) { clear(chosen, i, j, GT); if (OB_FAIL(ObRawExprUtils::create_double_op_expr( *expr_factory, session_info, T_OP_GT, pred, input_exprs_.at(i), input_exprs_.at(j)))) { LOG_WARN("failed to create double op expr", K(ret)); } else if (OB_FAIL(pred->pull_relation_id_and_levels(stmt_.get_current_level()))) { LOG_WARN("failed to pull relation id and levels", K(ret)); } else if (OB_FAIL(output_exprs.push_back(pred))) { LOG_WARN("failed to push back pred", K(ret)); } } if (OB_SUCC(ret) && has(edge, GE)) { clear(chosen, i, j, GE); if (OB_FAIL(ObRawExprUtils::create_double_op_expr( *expr_factory, session_info, T_OP_GE, pred, input_exprs_.at(i), input_exprs_.at(j)))) { LOG_WARN("failed to create double op expr", K(ret)); } else if (OB_FAIL(pred->pull_relation_id_and_levels(stmt_.get_current_level()))) { LOG_WARN("failed to pull relation id and levels", K(ret)); } else if (OB_FAIL(output_exprs.push_back(pred))) { LOG_WARN("failed to push back pred", K(ret)); } } } } return ret; } int ObPredicateDeduce::convert_pred(const ObRawExpr* pred, int64_t& left_id, int64_t& right_id, Type& type) { int ret = OB_SUCCESS; if (OB_ISNULL(pred)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("predicate is null", K(ret), K(pred)); } else if (!ObOptimizerUtil::find_equal_expr(input_exprs_, pred->get_param_expr(0), left_id) || !ObOptimizerUtil::find_equal_expr(input_exprs_, pred->get_param_expr(1), right_id)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("does not find expr", K(ret), K(*pred), K(left_id), K(right_id)); } else if (pred->get_expr_type() == T_OP_EQ) { type = EQ; } else if (pred->get_expr_type() == T_OP_GT || pred->get_expr_type() == T_OP_GE) { type = (pred->get_expr_type() == T_OP_GT ? GT : GE); } else if (pred->get_expr_type() == T_OP_LT || pred->get_expr_type() == T_OP_LE) { type = (pred->get_expr_type() == T_OP_LT ? GT : GE); std::swap(left_id, right_id); } else { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid predicate", K(ret), K(*pred)); } return ret; } int ObPredicateDeduce::deduce(ObIArray& graph) { int ret = OB_SUCCESS; for (int64_t hub = 0; hub < N; ++hub) { for (int64_t left = 0; left < N; ++left) { for (int64_t right = 0; right < N; ++right) { connect(graph.at(left * N + right), graph.at(left * N + hub), graph.at(hub * N + right)); } } } return ret; } void ObPredicateDeduce::expand_graph(ObIArray& graph, int64_t hub1, int64_t hub2) { int64_t left = 0; int64_t right = hub2; for (left = 0; left < N; ++left) { connect(graph.at(left * N + right), graph.at(left * N + hub1), graph.at(hub1 * N + right)); } for (int64_t left = 0; left < N; ++left) { for (int64_t right = 0; right < N; ++right) { connect(graph.at(left * N + right), graph.at(left * N + hub2), graph.at(hub2 * N + right)); } } } void ObPredicateDeduce::connect(uint8_t& left_right, const uint8_t left_mid, const uint8_t mid_right) { if (!has(left_right, EQ)) { if (has(left_mid, EQ) && has(mid_right, EQ)) { set(left_right, EQ); } } if (!has(left_right, GE)) { if ((has(left_mid, EQ) && has(mid_right, GE)) || (has(left_mid, GE) && has(mid_right, EQ))) { set(left_right, GE); } } if (!has(left_right, GT)) { if ((has(left_mid, GT) && mid_right != 0) || (left_mid != 0 && has(mid_right, GT))) { set(left_right, GT); } } } int ObPredicateDeduce::check_type_safe(int64_t first, int64_t second, bool& type_safe) { int ret = OB_SUCCESS; ObRawExpr* first_expr = NULL; ObRawExpr* second_expr = NULL; ObObjMeta cmp_meta; type_safe = false; if (first < 0 || first >= input_exprs_.count() || second < 0 || second >= input_exprs_.count()) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid expr id", K(ret), K(first), K(second)); } else if (OB_ISNULL(first_expr = input_exprs_.at(first)) || OB_ISNULL(second_expr = input_exprs_.at(second))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("expr is null", K(ret), K(first_expr), K(second_expr)); } else if (OB_FAIL(ObRelationalExprOperator::get_equal_meta( cmp_meta, first_expr->get_result_type(), second_expr->get_result_type()))) { LOG_WARN("failed to get equal meta", K(ret)); } else { type_safe = (cmp_meta == cmp_type_); } return ret; } int ObPredicateDeduce::deduce_general_predicates(ObTransformerCtx& ctx, ObIArray& target_exprs, ObIArray& general_preds, ObIArray& result) { int ret = OB_SUCCESS; for (int64_t i = 0; OB_SUCC(ret) && i < general_preds.count(); ++i) { ObSEArray equal_exprs; bool is_valid = false; if (OB_FAIL(check_general_expr_validity(general_preds.at(i), is_valid))) { LOG_WARN("failed to check valid general expr", K(ret)); } else if (!is_valid) { // do nothing } else if (OB_FAIL(get_equal_exprs(general_preds.at(i), general_preds, target_exprs, equal_exprs))) { LOG_WARN("failed to find equal columns", K(ret)); } for (int64_t j = 0; OB_SUCC(ret) && j < equal_exprs.count(); ++j) { ObRawExpr* new_pred = NULL; if (OB_FAIL(ObRawExprUtils::copy_expr(*ctx.expr_factory_, general_preds.at(i), new_pred, COPY_REF_DEFAULT))) { LOG_WARN("failed to copy expr", K(ret)); } else if (OB_ISNULL(new_pred) || OB_ISNULL(new_pred->get_param_expr(0))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("predicate is null", K(ret), K(new_pred)); } else { new_pred->get_param_expr(0) = equal_exprs.at(j); if (OB_FAIL(new_pred->formalize(ctx.session_info_))) { LOG_WARN("failed to formalize expr", K(ret)); } else if (OB_FAIL(new_pred->pull_relation_id_and_levels(stmt_.get_current_level()))) { LOG_WARN("failed to pull relation id and levels", K(ret)); } else if (OB_FAIL(result.push_back(new_pred))) { LOG_WARN("failed to push back new pred", K(ret)); } } } } return ret; } int ObPredicateDeduce::check_general_expr_validity(ObRawExpr* general_expr, bool& is_valid) { int ret = OB_SUCCESS; is_valid = false; if (OB_ISNULL(general_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("expr is null", K(ret)); } else if (contain_special_expr(*general_expr)) { // do nothing } else if (!is_general_condition(general_expr->get_expr_type())) { // do nothing } else { is_valid = true; for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < general_expr->get_param_count(); ++i) { ObRawExpr* param_expr = NULL; if (OB_ISNULL(param_expr = general_expr->get_param_expr(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("param expr is null", K(ret), K(param_expr)); } else if (i == 0) { is_valid = param_expr->has_flag(IS_COLUMN); } else { is_valid = param_expr->has_flag(IS_CONST) || param_expr->has_flag(IS_CONST_EXPR); } } } return ret; } int ObPredicateDeduce::get_equal_exprs(ObRawExpr* pred, ObIArray& general_preds, ObIArray& target_exprs, ObIArray& equal_exprs) { int ret = OB_SUCCESS; ObSEArray first_params; int64_t param_idx = -1; ObRawExpr* param_expr = NULL; if (OB_ISNULL(pred) || OB_ISNULL(param_expr = pred->get_param_expr(0))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("prediate is invalid", K(ret), K(pred), K(param_expr)); } else if (OB_FAIL(find_similar_expr(pred, general_preds, first_params))) { LOG_WARN("failed to find general expr", K(ret)); } else if (ObOptimizerUtil::find_item(input_exprs_, param_expr, ¶m_idx)) { for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) { ObRawExpr* expr = input_exprs_.at(i); if (!has(graph_, param_idx, i, EQ) || i == param_idx) { // do nothing } else if (OB_ISNULL(expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("input expr is null", K(ret)); } else if (!expr->is_column_ref_expr() || !expr->get_expr_levels().has_member(stmt_.get_current_level())) { // do nothing } else if (expr->get_result_type() != param_expr->get_result_type()) { // do nothing } else if (!ObOptimizerUtil::find_item(target_exprs, expr)) { // do nothing } else if (ObOptimizerUtil::find_item(first_params, expr)) { // do nothing } else if (OB_FAIL(equal_exprs.push_back(expr))) { LOG_WARN("failed to push back equal expr", K(ret)); } } } return ret; } int ObPredicateDeduce::find_similar_expr( ObRawExpr* pred, ObIArray& general_preds, ObIArray& first_params) { int ret = OB_SUCCESS; if (OB_ISNULL(pred)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid param expr", K(ret), K(pred)); } for (int64_t i = 0; OB_SUCC(ret) && i < general_preds.count(); ++i) { bool is_similar = true; if (OB_ISNULL(general_preds.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("general predicate is null", K(ret)); } else if (general_preds.at(i) == pred) { is_similar = true; } else if (general_preds.at(i)->get_expr_type() == pred->get_expr_type() && general_preds.at(i)->get_param_count() == pred->get_param_count()) { for (int64_t j = 1; OB_SUCC(ret) && is_similar && j < pred->get_param_count(); ++j) { ObRawExpr* param1 = NULL; ObRawExpr* param2 = NULL; if (OB_ISNULL(param1 = pred->get_param_expr(j)) || OB_ISNULL(param2 = general_preds.at(i)->get_param_expr(j))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("param expr is null", K(ret)); } else { is_similar = param1->same_as(*param2); } } } if (OB_SUCC(ret) && is_similar) { if (OB_FAIL(first_params.push_back(general_preds.at(i)->get_param_expr(0)))) { LOG_WARN("failed to push back param expr", K(ret)); } } } return ret; } int ObPredicateDeduce::topo_sort(ObIArray& order) { int ret = OB_SUCCESS; ObSEArray visited; order.reuse(); if (OB_FAIL(visited.prepare_allocate(N))) { LOG_WARN("failed to prepare allocate", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) { visited.at(i) = false; } for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) { if (OB_FAIL(topo_sort(i, visited, order))) { LOG_WARN("failed to topo sort", K(ret)); } } return ret; } int ObPredicateDeduce::topo_sort(int64_t id, ObIArray& visited, ObIArray& order) { int ret = OB_SUCCESS; if (!visited.at(id)) { visited.at(id) = true; for (int64_t i = 0; i < N; ++i) { if (has(graph_, id, i, GT) || has(graph_, id, i, GE)) { topo_sort(i, visited, order); } } if (OB_FAIL(order.push_back(id))) { LOG_WARN("failed to push back id", K(ret)); } } return ret; } int ObPredicateDeduce::deduce_aggr_bound_predicates( ObTransformerCtx& ctx, ObIArray& target_exprs, ObIArray& aggr_bound_preds) { int ret = OB_SUCCESS; ObRawExpr* param_expr = NULL; bool is_valid = false; for (int64_t i = 0; OB_SUCC(ret) && i < target_exprs.count(); ++i) { ObRawExpr* lower_expr = NULL; ObRawExpr* upper_expr = NULL; Type lower_type = EQ; Type upper_type = EQ; if (OB_ISNULL(target_exprs.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("target expr is null", K(ret)); } else if (OB_FAIL(check_aggr_validity(target_exprs.at(i), param_expr, is_valid))) { LOG_WARN("failed to check aggr validity", K(ret)); } else if (!is_valid) { // do nothing } else if (OB_FAIL(get_expr_bound(param_expr, lower_expr, lower_type, upper_expr, upper_type))) { LOG_WARN("failed to get expr bound", K(ret)); } if (OB_SUCC(ret) && NULL != lower_expr && (lower_type == GT || lower_type == GE)) { ObRawExpr* new_pred = NULL; if (OB_FAIL(ObRawExprUtils::create_double_op_expr(*ctx.expr_factory_, ctx.session_info_, lower_type == GT ? T_OP_GT : T_OP_GE, new_pred, target_exprs.at(i), lower_expr))) { LOG_WARN("failed to create compare expr", K(ret)); } else if (OB_FAIL(new_pred->pull_relation_id_and_levels(stmt_.get_current_level()))) { LOG_WARN("failed to pull relation id and levels", K(ret)); } else if (OB_FAIL(aggr_bound_preds.push_back(new_pred))) { LOG_WARN("failed to push back new predicate", K(ret)); } } if (OB_SUCC(ret) && NULL != upper_expr && (upper_type == GT || upper_type == GE)) { ObRawExpr* new_pred = NULL; if (OB_FAIL(ObRawExprUtils::create_double_op_expr(*ctx.expr_factory_, ctx.session_info_, upper_type == GT ? T_OP_GT : T_OP_GE, new_pred, upper_expr, target_exprs.at(i)))) { LOG_WARN("failed to create compare expr", K(ret)); } else if (OB_FAIL(new_pred->pull_relation_id_and_levels(stmt_.get_current_level()))) { LOG_WARN("failed to pull relation id and levels", K(ret)); } else if (OB_FAIL(aggr_bound_preds.push_back(new_pred))) { LOG_WARN("failed to push back new predicate", K(ret)); } } } return ret; } int ObPredicateDeduce::check_aggr_validity(ObRawExpr* expr, ObRawExpr*& param_expr, bool& is_valid) { int ret = OB_SUCCESS; is_valid = false; if (OB_ISNULL(expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("expr is null", K(ret), K(expr)); } else if (expr->get_expr_type() == T_FUN_MAX || expr->get_expr_type() == T_FUN_MIN) { if (OB_ISNULL(param_expr = expr->get_param_expr(0))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("param expr is null", K(ret)); } else { is_valid = true; } } if (OB_SUCC(ret) && is_valid) { ObObjMeta cmp_meta; if (OB_FAIL(ObRelationalExprOperator::get_equal_meta( cmp_meta, expr->get_result_type(), param_expr->get_result_type()))) { LOG_WARN("failed to get equal meta", K(ret)); } else { is_valid = (cmp_meta == cmp_type_); } } return ret; } int ObPredicateDeduce::get_expr_bound( ObRawExpr* target, ObRawExpr*& lower, Type& lower_type, ObRawExpr*& upper, Type& upper_type) { int ret = OB_SUCCESS; int64_t target_idx = -1; bool check_lower = true; lower = upper = NULL; lower_type = upper_type = EQ; if (ObOptimizerUtil::find_equal_expr(input_exprs_, target, target_idx)) { for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) { int64_t expr_idx = topo_order_.at(i); bool has_gt = false; bool has_ge = false; if (target_idx == expr_idx) { check_lower = false; } else if (!is_const(expr_idx) || !is_type_safe(expr_idx, target_idx)) { // do nothing } else if (check_lower) { has_gt = has(graph_, target_idx, expr_idx, GT); has_ge = has(graph_, target_idx, expr_idx, GE); if (has_gt || has_ge) { lower = input_exprs_.at(expr_idx); lower_type = has_gt ? GT : GE; } } else { has_gt = has(graph_, expr_idx, target_idx, GT); has_ge = has(graph_, expr_idx, target_idx, GE); if (has_gt || has_ge) { upper = input_exprs_.at(expr_idx); upper_type = has_gt ? GT : GE; break; } } } } return ret; }