/** * 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 "ob_stmt_comparer.h" #include "ob_transform_utils.h" #include "sql/optimizer/ob_optimizer_util.h" #include "sql/ob_sql_context.h" using namespace oceanbase::sql; int ObStmtCompareContext::init(const ObQueryCtx* context) { int ret = OB_SUCCESS; if (OB_ISNULL(context)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else { context_ = context; } return ret; } int ObStmtCompareContext::init(const ObDMLStmt* inner, const ObDMLStmt* outer, const ObIArray& table_map) { int ret = OB_SUCCESS; TableItem* inner_table = NULL; TableItem* outer_table = NULL; if (OB_ISNULL(inner) || OB_ISNULL(outer) || OB_ISNULL(inner->get_query_ctx())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("stmt is invalid", K(ret), K(inner), K(outer)); } else if (OB_UNLIKELY(inner->get_table_size() != table_map.count())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid table map size", K(ret)); } else { context_ = inner->get_query_ctx(); } for (int64_t inner_idx = 0; OB_SUCC(ret) && inner_idx < table_map.count(); ++inner_idx) { int64_t outer_idx = table_map.at(inner_idx); if (outer_idx < 0 || outer_idx >= outer->get_table_size()) { // do nothing } else if (OB_ISNULL(inner_table = inner->get_table_items().at(inner_idx)) || OB_ISNULL(outer_table = outer->get_table_items().at(outer_idx))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("table items are null", K(ret)); } else { std::pair id_pair(inner_table->table_id_, outer_table->table_id_); if (OB_FAIL(table_id_pairs_.push_back(id_pair))) { LOG_WARN("failed to push back table id pairs", K(ret)); } } } return ret; } bool ObStmtCompareContext::compare_column(const ObColumnRefRawExpr& inner, const ObColumnRefRawExpr& outer) { bool bret = false; if (inner.get_column_id() == outer.get_column_id()) { bret = inner.get_table_id() == outer.get_table_id(); for (int64_t i = 0; !bret && i < table_id_pairs_.count(); ++i) { bret = inner.get_table_id() == table_id_pairs_.at(i).first && outer.get_table_id() == table_id_pairs_.at(i).second; } } return bret; } bool ObStmtCompareContext::compare_const(const ObConstRawExpr& left, const ObConstRawExpr& right) { int& ret = err_code_; bool bret = false; if (OB_SUCC(ret) && left.get_result_type() == right.get_result_type()) { if (&left == &right) { bret = true; } else if (left.has_flag(IS_PARAM) && right.has_flag(IS_PARAM)) { bool is_left_calc_item = false; bool is_right_calc_item = false; if (OB_FAIL(is_pre_calc_item(left, is_left_calc_item))) { LOG_WARN("failed to is pre calc item", K(ret)); } else if (OB_FAIL(is_pre_calc_item(right, is_right_calc_item))) { LOG_WARN("failed to is pre calc item", K(ret)); } else if (is_left_calc_item && is_right_calc_item) { const ObRawExpr* left_param = NULL; const ObRawExpr* right_param = NULL; if (OB_FAIL(get_calc_expr(left.get_value().get_unknown(), left_param))) { LOG_WARN("faield to get calculable expr", K(ret)); } else if (OB_FAIL(get_calc_expr(right.get_value().get_unknown(), right_param))) { LOG_WARN("failed to get calculable expr", K(ret)); } else if (OB_ISNULL(left_param) || OB_ISNULL(right_param)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("param exprs are null", K(ret), K(left_param), K(right_param)); } else { bret = left_param->same_as(*right_param, this); } } else if (is_left_calc_item || is_right_calc_item) { bret = false; } else if (left.get_result_type().get_param().is_equal(right.get_result_type().get_param(), CS_TYPE_BINARY)) { ObPCParamEqualInfo info; info.first_param_idx_ = left.get_value().get_unknown(); info.second_param_idx_ = right.get_value().get_unknown(); bret = true; if (info.first_param_idx_ != info.second_param_idx_ && OB_FAIL(equal_param_info_.push_back(info))) { LOG_WARN("failed to push back equal param info", K(ret)); } } } else if (left.has_flag(IS_PARAM) || right.has_flag(IS_PARAM)) { bret = ObExprEqualCheckContext::compare_const(left, right); } else { bret = left.get_value().is_equal(right.get_value(), CS_TYPE_BINARY); } } return bret; } int ObStmtCompareContext::is_pre_calc_item(const ObConstRawExpr& const_expr, bool& is_calc) { int ret = OB_SUCCESS; int64_t calc_count = 0; is_calc = false; if (OB_ISNULL(context_) || OB_UNLIKELY((calc_count = context_->calculable_items_.count()) < 0 || const_expr.get_expr_type() != T_QUESTIONMARK)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected error", K(ret), K(context_), K(const_expr.get_expr_type()), K(calc_count)); } else if (const_expr.has_flag(IS_EXEC_PARAM)) { is_calc = true; } else if (calc_count > 0) { int64_t q_idx = const_expr.get_value().get_unknown(); int64_t min_calc_index = context_->calculable_items_.at(0).hidden_idx_; if (OB_UNLIKELY(q_idx < 0 || min_calc_index < 0)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get invalid argument", K(q_idx), K(min_calc_index)); } else if (q_idx - min_calc_index >= 0 && q_idx - min_calc_index < calc_count) { is_calc = true; } else { /*do nothing*/ } } return ret; } bool ObStmtCompareContext::compare_query(const ObQueryRefRawExpr& first, const ObQueryRefRawExpr& second) { bool bret = false; if (&first == &second) { bret = true; } return bret; } int ObStmtCompareContext::get_calc_expr(const int64_t param_idx, const ObRawExpr*& expr) { int ret = OB_SUCCESS; if (OB_ISNULL(context_) || context_->calculable_items_.count() <= 0) { ret = OB_ERR_UNEXPECTED; LOG_WARN("query context is null", K(ret)); } else { int64_t offset = param_idx - context_->calculable_items_.at(0).hidden_idx_; if (offset < 0 || offset >= context_->calculable_items_.count() || param_idx != context_->calculable_items_.at(offset).hidden_idx_) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid param index", K(ret), K(param_idx), K(offset)); } else { expr = context_->calculable_items_.at(offset).expr_; } } return ret; } int ObStmtComparer::compute_stmt_overlap(ObDMLStmt* first, ObDMLStmt* second, ObStmtMapInfo& map_info) { int ret = OB_SUCCESS; int64_t match_count = 0; map_info.reset(); if (OB_ISNULL(first) || OB_ISNULL(second)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("stmts are null", K(ret), K(first), K(second)); } else if (OB_FAIL(compute_from_items_map(first, second, map_info, match_count))) { LOG_WARN("failed to compute from items map", K(ret)); } else if (OB_FAIL(map_info.cond_map_.prepare_allocate(first->get_condition_size()))) { LOG_WARN("failed to pre-allocate condition map", K(ret)); } else if (OB_FAIL(compute_conditions_map(first, second, first->get_condition_exprs(), second->get_condition_exprs(), map_info, map_info.cond_map_, match_count))) { LOG_WARN("failed to compute condition map", K(ret)); } else { LOG_TRACE("stmt map info", K(map_info)); } return ret; } /** * @brief ObTransformUtils::is_same_from * compare whether two from items are same * @return */ int ObStmtComparer::is_same_from( ObDMLStmt* first, const FromItem& first_from, ObDMLStmt* second, const FromItem& second_from, bool& is_same) { int ret = OB_SUCCESS; TableItem* first_table = NULL; TableItem* second_table = NULL; bool is_semi_table = false; is_same = false; if (OB_ISNULL(first) || OB_ISNULL(second)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("stmts have null", K(ret), K(first), K(second)); } else if (first_from.is_joined_ || second_from.is_joined_) { // do nothing } else if (OB_ISNULL(first_table = first->get_table_item_by_id(first_from.table_id_))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("first table item is null", K(ret), K(first)); } else if (OB_ISNULL(second_table = second->get_table_item_by_id(second_from.table_id_))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("second table item is null", K(ret), K(second)); } else if (OB_FAIL(ObTransformUtils::is_semi_join_right_table(*first, first_table->table_id_, is_semi_table))) { LOG_WARN("failed to check is semi join right table", K(ret)); } else if (is_semi_table) { // do nothing } else if (OB_FAIL(ObTransformUtils::is_semi_join_right_table(*second, second_table->table_id_, is_semi_table))) { LOG_WARN("failed to check is semi join right table", K(ret)); } else if (is_semi_table) { // do nothing } else if (first_table->is_basic_table() && second_table->is_basic_table()) { QueryRelation relation = QueryRelation::UNCOMPARABLE; // be careful for partition hint if (OB_FAIL(compare_basic_table_item(first, first_table, second, second_table, relation))) { LOG_WARN("compare table part failed", K(ret), K(first_table), K(second_table)); } else if (QueryRelation::EQUAL == relation) { is_same = true; } else { /*do noting*/ } } else if (first_table->is_generated_table() && first_table->is_view_table_ && second_table->is_generated_table() && second_table->is_view_table_) { if (OB_ISNULL(first_table->ref_query_) || OB_ISNULL(second_table->ref_query_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("ref query is null", K(ret)); } else { is_same = (first_table->ref_query_->get_view_ref_id() == second_table->ref_query_->get_view_ref_id()) && first_table->ref_query_->get_view_ref_id() != OB_INVALID_ID; } } return ret; } int ObStmtComparer::check_stmt_containment( ObDMLStmt* first, ObDMLStmt* second, ObStmtMapInfo& map_info, QueryRelation& relation) { int ret = OB_SUCCESS; int64_t first_count = 0; int64_t second_count = 0; int64_t match_count = 0; ObSelectStmt* first_sel = NULL; ObSelectStmt* second_sel = NULL; relation = QueryRelation::UNCOMPARABLE; if (OB_ISNULL(first) || OB_ISNULL(second)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(first), K(second), K(ret)); } else if (!first->is_select_stmt() || !second->is_select_stmt()) { /*do nothing*/ } else if (FALSE_IT(first_sel = static_cast(first))) { /*do nothing*/ } else if (FALSE_IT(second_sel = static_cast(second))) { /*do nothing*/ } else if (first_sel->is_set_stmt() || second_sel->is_set_stmt() || first_sel->has_recusive_cte() || second_sel->has_recusive_cte() || first_sel->has_hierarchical_query() || second_sel->has_hierarchical_query() || first_sel->is_contains_assignment() || first_sel->get_semi_info_size() > 0 || second_sel->get_semi_info_size() > 0 || second_sel->is_contains_assignment()) { /*do nothing*/ } else if (first_sel->get_from_item_size() != second_sel->get_from_item_size()) { /*do nothing*/ } else { // check from items if (OB_FAIL(compute_from_items_map(first_sel, second_sel, map_info, match_count))) { LOG_WARN("failed to compute from items map", K(ret)); } else if (match_count != first_sel->get_from_item_size()) { relation = QueryRelation::UNCOMPARABLE; LOG_TRACE("succeed to check from item map", K(relation), K(map_info)); } else { relation = QueryRelation::EQUAL; LOG_TRACE("succeed to check from item map", K(relation), K(map_info)); } // check condition exprs if (OB_SUCC(ret) && QueryRelation::EQUAL == relation) { first_count = first_sel->get_condition_size(); second_count = second_sel->get_condition_size(); if (OB_FAIL(compute_conditions_map(first_sel, second_sel, first_sel->get_condition_exprs(), second_sel->get_condition_exprs(), map_info, map_info.cond_map_, match_count))) { LOG_WARN("failed to compute conditions map", K(ret)); } else if (match_count == first_count && match_count == second_count) { relation = QueryRelation::EQUAL; LOG_TRACE("succeed to check conditions map", K(relation), K(map_info)); } else if (match_count == first_count && match_count < second_count) { relation = QueryRelation::RIGHT_SUBSET; LOG_TRACE("succeed to check conditions map", K(relation), K(map_info)); } else if (match_count < first_count && match_count == second_count) { relation = QueryRelation::LEFT_SUBSET; LOG_TRACE("succeed to check conditions map", K(relation), K(map_info)); } else { relation = QueryRelation::UNCOMPARABLE; LOG_TRACE("succeed to check conditions map", K(relation), K(map_info)); } } // check group by exprs if (OB_SUCC(ret) && QueryRelation::UNCOMPARABLE != relation) { bool is_consistent = false; common::ObSEArray first_groupby_rollup_exprs; common::ObSEArray second_groupby_rollup_exprs; if (OB_FAIL(append(first_groupby_rollup_exprs, first_sel->get_group_exprs()))) { LOG_WARN("failed to append group by rollup exprs.", K(ret)); } else if (OB_FAIL(append(first_groupby_rollup_exprs, first_sel->get_rollup_exprs()))) { LOG_WARN("failed to append group by rollup exprs.", K(ret)); } else if (OB_FAIL(append(second_groupby_rollup_exprs, second_sel->get_group_exprs()))) { LOG_WARN("failed to append group by rollup exprs.", K(ret)); } else if (OB_FAIL(append(second_groupby_rollup_exprs, second_sel->get_rollup_exprs()))) { LOG_WARN("failed to append group by rollup exprs.", K(ret)); } else { /*do nothing.*/ } first_count = first_groupby_rollup_exprs.count(); second_count = second_groupby_rollup_exprs.count(); if (OB_FAIL(ret)) { } else if ((first_sel->get_aggr_item_size() > 0 || second_sel->get_aggr_item_size() > 0) && relation != QueryRelation::EQUAL) { relation = QueryRelation::UNCOMPARABLE; LOG_TRACE("succeed to check group by map", K(relation), K(map_info)); } else if (0 == first_count && 0 == second_count) { /*do nothing*/ } else if (OB_FAIL(ObTransformUtils::check_group_by_consistent(first_sel, is_consistent))) { LOG_WARN("failed to check whether group by is consistent", K(ret)); } else if (!is_consistent) { relation = QueryRelation::UNCOMPARABLE; } else if (OB_FAIL(ObTransformUtils::check_group_by_consistent(second_sel, is_consistent))) { LOG_WARN("failed to check whether group by is consistent", K(ret)); } else if (!is_consistent) { relation = QueryRelation::UNCOMPARABLE; } else if (OB_FAIL(compute_conditions_map(first_sel, second_sel, first_groupby_rollup_exprs, second_groupby_rollup_exprs, map_info, map_info.group_map_, match_count))) { LOG_WARN("failed to compute group by expr map", K(ret)); } else if (match_count != first_count || match_count != second_count) { relation = QueryRelation::UNCOMPARABLE; LOG_TRACE("succeed to check group by map", K(relation), K(map_info)); } else { LOG_TRACE("succeed to check group by map", K(relation), K(map_info)); } } // check having exprs if (OB_SUCC(ret) && QueryRelation::UNCOMPARABLE != relation) { first_count = first_sel->get_having_expr_size(); second_count = second_sel->get_having_expr_size(); if (0 == first_count && 0 == second_count) { /*do nothing*/ } else if (OB_FAIL(compute_conditions_map(first_sel, second_sel, first_sel->get_having_exprs(), second_sel->get_having_exprs(), map_info, map_info.having_map_, match_count))) { LOG_WARN("failed to compute having expr map", K(ret)); } else if (match_count == first_count && match_count == second_count) { LOG_TRACE("succeed to check having map", K(relation), K(map_info)); } else if (match_count == first_count && match_count < second_count && (relation == QueryRelation::RIGHT_SUBSET || relation == QueryRelation::EQUAL)) { relation = QueryRelation::RIGHT_SUBSET; LOG_TRACE("succeed to check having map", K(relation), K(map_info)); } else if (match_count == second_count && match_count < first_count && (relation == QueryRelation::LEFT_SUBSET || relation == QueryRelation::EQUAL)) { relation = QueryRelation::LEFT_SUBSET; LOG_TRACE("succeed to check having map", K(relation), K(map_info)); } else { relation = QueryRelation::UNCOMPARABLE; LOG_TRACE("succeed to check having map", K(relation), K(map_info)); } } // check window function exprs if (OB_SUCC(ret) && QueryRelation::UNCOMPARABLE != relation) { first_count = first_sel->get_window_func_count(); second_count = second_sel->get_window_func_count(); if (0 == first_count && 0 == second_count) { /*do nothing*/ } else if (relation != QueryRelation::EQUAL) { relation = QueryRelation::UNCOMPARABLE; LOG_TRACE("succeed to check window function map", K(relation), K(map_info)); } else { LOG_TRACE("succeed to check window function map", K(relation), K(map_info)); } } // check distinct exprs if (OB_SUCC(ret) && QueryRelation::UNCOMPARABLE != relation) { if ((!first_sel->has_distinct() && !second_sel->has_distinct()) || (first_sel->has_distinct() && second_sel->has_distinct())) { LOG_TRACE("succeed to check distinct expr map", K(relation), K(map_info)); } else if (first_sel->has_distinct() && !second_sel->has_distinct() && (relation == QueryRelation::LEFT_SUBSET || relation == QueryRelation::EQUAL)) { relation = QueryRelation::LEFT_SUBSET; LOG_TRACE("succeed to check distinct expr map", K(relation), K(map_info)); } else if (!first_sel->has_distinct() && second_sel->has_distinct() && (relation == QueryRelation::RIGHT_SUBSET || relation == QueryRelation::EQUAL)) { relation = QueryRelation::RIGHT_SUBSET; LOG_TRACE("succeed to check distinct expr map", K(relation), K(map_info)); } else { relation = QueryRelation::UNCOMPARABLE; LOG_TRACE("succeed to check distinct expr map", K(relation), K(map_info)); } } // check limit exprs if (OB_SUCC(ret) && QueryRelation::UNCOMPARABLE != relation) { if (!first_sel->has_limit() && !second_sel->has_limit()) { LOG_TRACE("succeed to check limit expr", K(relation), K(map_info)); } else if (first_sel->has_limit() && !second_sel->has_limit() && (relation == QueryRelation::LEFT_SUBSET || relation == QueryRelation::EQUAL)) { relation = QueryRelation::LEFT_SUBSET; LOG_TRACE("succeed to check limit expr", K(relation), K(map_info)); } else if (!first_sel->has_limit() && second_sel->has_limit() && (relation == QueryRelation::RIGHT_SUBSET || relation == QueryRelation::EQUAL)) { relation = QueryRelation::RIGHT_SUBSET; LOG_TRACE("succeed to check limit expr", K(relation), K(map_info)); } else { relation = QueryRelation::UNCOMPARABLE; LOG_TRACE("succeed to check limit expr", K(relation), K(map_info)); } } // compute map for select items output if (OB_SUCC(ret) && QueryRelation::UNCOMPARABLE != relation) { ObSEArray first_exprs; ObSEArray second_exprs; if (OB_FAIL(first_sel->get_select_exprs(first_exprs))) { LOG_WARN("failed to get select exprs", K(ret)); } else if (OB_FAIL(second_sel->get_select_exprs(second_exprs))) { LOG_WARN("failed to get select exprs", K(ret)); } else if (OB_FAIL(compute_conditions_map(first_sel, second_sel, first_exprs, second_exprs, map_info, map_info.select_item_map_, match_count))) { LOG_WARN("failed to compute output expr map", K(ret)); } else { LOG_TRACE("succeed to check stmt containment", K(relation), K(map_info)); } } } return ret; } int ObStmtComparer::compute_from_items_map( ObDMLStmt* first, ObDMLStmt* second, ObStmtMapInfo& map_info, int64_t& match_count) { int ret = OB_SUCCESS; match_count = 0; if (OB_ISNULL(first) || OB_ISNULL(second)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(first), K(second), K(ret)); } else if (OB_FAIL(map_info.table_map_.prepare_allocate(first->get_table_size()))) { LOG_WARN("failed to pre-allocate table map", K(ret)); } else if (OB_FAIL(map_info.from_map_.prepare_allocate(first->get_from_item_size()))) { LOG_WARN("failed to pre-allocate from map", K(ret)); } else { ObSqlBitSet<> matched_items; for (int64_t i = 0; i < map_info.table_map_.count(); ++i) { map_info.table_map_.at(i) = OB_INVALID_ID; } for (int64_t i = 0; OB_SUCC(ret) && i < first->get_from_item_size(); ++i) { const FromItem& first_from = first->get_from_item(i); bool is_match = false; map_info.from_map_.at(i) = OB_INVALID_ID; for (int64_t j = 0; OB_SUCC(ret) && !is_match && j < second->get_from_item_size(); ++j) { const FromItem& second_from = second->get_from_item(j); if (matched_items.has_member(j)) { // do nothing } else if (OB_FAIL(is_same_from(first, first_from, second, second_from, is_match))) { LOG_WARN("failed to check the from item same", K(ret)); } else if (!is_match) { // do nothing } else if (OB_FAIL(matched_items.add_member(j))) { LOG_WARN("failed to add member", K(ret)); } else { match_count++; map_info.from_map_.at(i) = j; const int32_t first_table_index = first->get_table_bit_index(first_from.table_id_); const int32_t second_table_index = second->get_table_bit_index(second_from.table_id_); if (OB_INVALID_ID == first_table_index || OB_INVALID_ID == second_table_index) { ret = OB_ERR_UNEXPECTED; LOG_WARN("table bit index is invalid", K(ret), K(first_table_index), K(second_table_index)); } else { map_info.table_map_.at(first_table_index - 1) = second_table_index - 1; } } } } } return ret; } int ObStmtComparer::compute_conditions_map(ObDMLStmt* first, ObDMLStmt* second, const ObIArray& first_exprs, const ObIArray& second_exprs, ObStmtMapInfo& map_info, ObIArray& condition_map, int64_t& match_count) { int ret = OB_SUCCESS; ObSqlBitSet<> matched_items; ObStmtCompareContext context; match_count = 0; if (OB_ISNULL(first) || OB_ISNULL(second)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(first), K(second), K(ret)); } else if (OB_FAIL(context.init(first, second, map_info.table_map_))) { LOG_WARN("failed to set table map", K(ret)); } else if (OB_FAIL(condition_map.prepare_allocate(first_exprs.count()))) { LOG_WARN("failed to preallocate array", K(ret)); } else { for (int64_t i = 0; OB_SUCC(ret) && i < first_exprs.count(); ++i) { bool is_match = false; condition_map.at(i) = OB_INVALID_ID; for (int64_t j = 0; OB_SUCC(ret) && !is_match && j < second_exprs.count(); ++j) { if (matched_items.has_member(j)) { // do nothing } else if (OB_FAIL(is_same_condition(first_exprs.at(i), second_exprs.at(j), context, is_match))) { LOG_WARN("failed to check is condition equal", K(ret)); } else if (!is_match) { // do nothing } else if (OB_FAIL(append(map_info.equal_param_map_, context.equal_param_info_))) { LOG_WARN("failed to append exprs", K(ret)); } else if (OB_FAIL(matched_items.add_member(j))) { LOG_WARN("failed to add member", K(ret)); } else { match_count++; condition_map.at(i) = j; } } } } return ret; } int ObStmtComparer::is_same_condition(ObRawExpr* left, ObRawExpr* right, ObStmtCompareContext& context, bool& is_same) { int ret = OB_SUCCESS; is_same = false; context.equal_param_info_.reset(); if (OB_ISNULL(left) || OB_ISNULL(right)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("expr is null", K(ret)); } else if (!(is_same = left->same_as(*right, &context))) { context.equal_param_info_.reset(); if (!IS_COMMON_COMPARISON_OP(left->get_expr_type()) || get_opposite_compare_type(left->get_expr_type()) != right->get_expr_type()) { // do nothing } else if (OB_ISNULL(left->get_param_expr(0)) || OB_ISNULL(left->get_param_expr(1)) || OB_ISNULL(right->get_param_expr(0)) || OB_ISNULL(right->get_param_expr(1))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("param exprs are null", K(ret)); } else if (!left->get_param_expr(0)->same_as(*right->get_param_expr(1), &context)) { // do nothing } else if (!left->get_param_expr(1)->same_as(*right->get_param_expr(0), &context)) { // do nothing } else { is_same = true; } } return ret; } int ObStmtComparer::compare_basic_table_item(ObDMLStmt* first, const TableItem* first_table, ObDMLStmt* second, const TableItem* second_table, QueryRelation& relation) { int ret = OB_SUCCESS; relation = QueryRelation::UNCOMPARABLE; if (OB_ISNULL(first) || OB_ISNULL(first_table) || OB_ISNULL(second) || OB_ISNULL(second_table)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("param has null", K(first), K(first_table), K(second), K(second_table)); } else if (first_table->is_basic_table() && second_table->is_basic_table() && first_table->ref_id_ == second_table->ref_id_) { const ObPartHint* first_table_hint = first->get_stmt_hint().get_part_hint(first_table->table_id_); const ObPartHint* second_table_hint = second->get_stmt_hint().get_part_hint(second_table->table_id_); if (OB_ISNULL(first_table_hint) && OB_ISNULL(second_table_hint)) { relation = QueryRelation::EQUAL; } else if (OB_ISNULL(first_table_hint)) { relation = QueryRelation::RIGHT_SUBSET; } else if (OB_ISNULL(second_table_hint)) { relation = QueryRelation::LEFT_SUBSET; } else { // part ids for subpartition is a large int64_t number, here can not use bit set. bool left_subset = ObOptimizerUtil::is_subset(first_table_hint->part_ids_, second_table_hint->part_ids_); bool right_subset = ObOptimizerUtil::is_subset(second_table_hint->part_ids_, first_table_hint->part_ids_); if (left_subset && right_subset) { relation = QueryRelation::EQUAL; } else if (left_subset) { relation = QueryRelation::LEFT_SUBSET; } else if (right_subset) { relation = QueryRelation::RIGHT_SUBSET; } else { relation = QueryRelation::UNCOMPARABLE; } } } else if (first_table->is_generated_table() && second_table->is_generated_table() && first_table->ref_query_ == second_table->ref_query_) { relation = QueryRelation::EQUAL; } else { /*do nothing*/ } return ret; }