/** * 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_simplify_limit.h" #include "sql/rewrite/ob_transform_utils.h" #include "sql/resolver/expr/ob_raw_expr_util.h" using namespace oceanbase::sql; int ObTransformSimplifyLimit::transform_one_stmt(common::ObIArray &parent_stmts, ObDMLStmt *&stmt, bool &trans_happened) { int ret = OB_SUCCESS; bool is_happened = false; UNUSED(parent_stmts); if (OB_FAIL(add_limit_to_semi_right_table(stmt, is_happened))) { LOG_WARN("failed to add limit to semi right table", K(ret)); } else { trans_happened = is_happened; OPT_TRACE("add limit to semi right table:", is_happened); } if (OB_SUCC(ret)) { if (OB_FAIL(pushdown_limit_offset(stmt, is_happened))) { LOG_WARN("failed to push down limit offset", K(ret)); } else { trans_happened = (trans_happened || is_happened); OPT_TRACE("push down limit offset:", is_happened); } } if (OB_SUCC(ret)) { if (OB_FAIL(pushdown_limit_order_for_union(stmt, is_happened))) { LOG_WARN("failed to push down limit order for union", K(ret)); } else { trans_happened = (trans_happened || is_happened); OPT_TRACE("push down limit order for union:", is_happened); } } if (OB_SUCC(ret) && trans_happened) { if (OB_FAIL(add_transform_hint(*stmt))) { LOG_WARN("failed to add transform hint", K(ret)); } } return ret; } // if no use semi right table in semi conditions, add limit 1 to semi right table int ObTransformSimplifyLimit::add_limit_to_semi_right_table(ObDMLStmt *stmt, bool &trans_happened) { int ret = OB_SUCCESS; trans_happened = false; if (OB_ISNULL(stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("stmt is null", K(ret), K(stmt)); } else { bool need_add = false; for (int64_t i = 0; OB_SUCC(ret) && i < stmt->get_semi_info_size(); ++i) { if (OB_FAIL(check_need_add_limit_to_semi_right_table(stmt, stmt->get_semi_infos().at(i), need_add))) { LOG_WARN("failed to check ", K(ret), K(stmt)); } else if (!need_add) { /* do nothing */ } else if (OB_FAIL(ObTransformUtils::add_limit_to_semi_right_table(stmt, ctx_, stmt->get_semi_infos().at(i)))) { LOG_WARN("failed to add limit to semi right table", K(ret), K(*stmt)); } else { trans_happened = true; } } } return ret; } int ObTransformSimplifyLimit::check_need_add_limit_to_semi_right_table(ObDMLStmt *stmt, SemiInfo *semi_info, bool &need_add) { int ret = OB_SUCCESS; need_add = true; TableItem *right_table = NULL; ObSelectStmt *ref_query = NULL; if (OB_ISNULL(stmt) || OB_ISNULL(semi_info) || OB_ISNULL(right_table = stmt->get_table_item_by_id(semi_info->right_table_id_))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(stmt), K(semi_info)); } else if (!right_table->is_generated_table()) { need_add = !right_table->is_link_type(); } else if (OB_ISNULL(ref_query = right_table->ref_query_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(ref_query)); } else if (NULL != ref_query->get_limit_expr() || NULL != ref_query->get_limit_percent_expr()) { need_add = false; } if (OB_SUCC(ret)) { ObRawExpr *expr = NULL; int64_t right_idx = stmt->get_table_bit_index(semi_info->right_table_id_); for (int64_t i = 0; OB_SUCC(ret) && need_add && i < semi_info->semi_conditions_.count(); ++i) { if (OB_ISNULL(expr = semi_info->semi_conditions_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("expr is null", K(ret), K(expr)); } else if (expr->get_relation_ids().has_member(right_idx)) { need_add = false; } } } return ret; } /** * view merge 支持 view_stmt select 包含子查询的合并后需要移除此改写 * * @brief ObTransformSimplifyLimit::pushdown_limit_offset * 下推 limit offset, 修改 upper stmt 中 rownum * 为了使 view select 中 subquery1 减少无效计算 * select rownum rn, v.* from (select subquery1 from t) v offset 10 rows; * => select rownum + 10 rn, v.* from (select subquery1 from t offset 10 rows) v; */ int ObTransformSimplifyLimit::pushdown_limit_offset(ObDMLStmt *stmt, bool &trans_happened) { int ret = OB_SUCCESS; trans_happened = false; ObSelectStmt *sel_stmt = NULL; ObSelectStmt *view_stmt = NULL; bool is_valid = false; if (OB_ISNULL(stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("stmt is null", K(ret), K(ctx_)); } else if (!is_oracle_mode() || !stmt->is_select_stmt()) { /*do nothing*/ } else if (FALSE_IT(sel_stmt = static_cast(stmt))) { } else if (OB_FAIL(check_pushdown_limit_offset_validity(sel_stmt, view_stmt, is_valid))) { LOG_WARN("failed to check pushdown limit offset validity", K(ret)); } else if (!is_valid) { /*do nothing*/ } else if (OB_FAIL(do_pushdown_limit_offset(sel_stmt, view_stmt))) { LOG_WARN("failed to do pushdown limit offset", K(ret)); } else { trans_happened = true; } return ret; } int ObTransformSimplifyLimit::check_pushdown_limit_offset_validity(ObSelectStmt *upper_stmt, ObSelectStmt *&view_stmt, bool &is_valid) { int ret = OB_SUCCESS; is_valid = false; view_stmt = NULL; TableItem *table = NULL; if (OB_ISNULL(upper_stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("params have null", K(ret)); } else if (!upper_stmt->is_single_table_stmt() || OB_ISNULL(table = upper_stmt->get_table_item(0)) || !table->is_generated_table() || OB_ISNULL(view_stmt = table->ref_query_) || view_stmt->is_hierarchical_query()) { is_valid = false; } else if (!upper_stmt->has_limit() || NULL == upper_stmt->get_offset_expr() || NULL != upper_stmt->get_limit_percent_expr() || NULL != view_stmt->get_limit_percent_expr() || upper_stmt->is_fetch_with_ties() || view_stmt->is_fetch_with_ties()) { is_valid = false; } else if (0 != upper_stmt->get_condition_size() || upper_stmt->has_group_by() || upper_stmt->has_rollup() || upper_stmt->has_window_function() || upper_stmt->has_distinct() || upper_stmt->has_sequence() || upper_stmt->has_order_by()) { is_valid = false; } else { is_valid = true; } return ret; } int ObTransformSimplifyLimit::do_pushdown_limit_offset(ObSelectStmt *upper_stmt, ObSelectStmt *view_stmt) { int ret = OB_SUCCESS; if (OB_ISNULL(upper_stmt) || OB_ISNULL(view_stmt) || OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_) || OB_ISNULL(ctx_->session_info_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret)); } else { ObRawExpr *view_limit = view_stmt->get_limit_expr(); ObRawExpr *upper_limit = upper_stmt->get_limit_expr(); ObRawExpr *view_offset = view_stmt->get_offset_expr(); ObRawExpr *upper_offset = upper_stmt->get_offset_expr(); ObRawExpr *limit_expr = NULL; ObRawExpr *offset_expr = NULL; if (OB_FAIL(ObTransformUtils::merge_limit_offset(ctx_, view_limit, upper_limit, view_offset, upper_offset, limit_expr, offset_expr))) { LOG_WARN("failed to merge limit offset", K(ret)); } else { ObRawExpr *rownum_expr = NULL; ObOpRawExpr *add_expr = NULL; ObSEArray old_expr; ObSEArray new_expr; upper_stmt->set_limit_offset(upper_limit, NULL); upper_stmt->set_fetch_with_ties(false); upper_stmt->set_limit_percent_expr(NULL); upper_stmt->set_has_fetch(false); view_stmt->set_limit_offset(limit_expr, offset_expr); if (OB_FAIL(upper_stmt->get_rownum_expr(rownum_expr))) { LOG_WARN("failed to get rownum expr", K(ret)); } else if (NULL == rownum_expr || NULL == upper_offset) { /*do nothing*/ } else if (OB_FAIL(ctx_->expr_factory_->create_raw_expr(T_OP_ADD, add_expr))) { LOG_WARN("create add op expr failed", K(ret)); } else if (OB_ISNULL(add_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("add expr is null"); } else if (OB_FAIL(old_expr.push_back(rownum_expr))) { LOG_WARN("failed to push back expr", K(ret)); } else if (OB_FAIL(new_expr.push_back(add_expr))) { LOG_WARN("failed to push back expr", K(ret)); } else if (OB_FAIL(add_expr->set_param_exprs(rownum_expr, upper_offset))) { LOG_WARN("set param exprs failed", K(ret)); } else if (OB_FAIL(add_expr->formalize(ctx_->session_info_))) { LOG_WARN("formalize add operator failed", K(ret)); } else if (OB_FAIL(upper_stmt->replace_relation_exprs(old_expr, new_expr))) { LOG_WARN("failed to replace expr", K(ret)); } } } return ret; } /** * try push down limit and order by from non set stmt to union set stmt. */ int ObTransformSimplifyLimit::pushdown_limit_order_for_union(ObDMLStmt *stmt, bool& trans_happened) { int ret = OB_SUCCESS; trans_happened = false; bool can_push = false; ObSelectStmt* set_stmt = nullptr; if (OB_ISNULL(stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret), K(stmt)); } else if (!stmt->is_select_stmt()) { // do nothing } else { ObSelectStmt* sel_stmt = static_cast(stmt); if (OB_FAIL(check_can_pushdown_limit_order(*sel_stmt, set_stmt, can_push))) { LOG_WARN("failed to check can pre push", K(ret)); } else if(!can_push) { // do nothing } else if (OB_FAIL(do_pushdown_limit_order_for_union(*sel_stmt, set_stmt))) { LOG_WARN("failed to pushdown limit order for union", K(ret)); } else { trans_happened = true; } } return ret; } int ObTransformSimplifyLimit::check_can_pushdown_limit_order(ObSelectStmt& upper_stmt, ObSelectStmt*& view_stmt, bool& can_push) { int ret = OB_SUCCESS; can_push = false; TableItem* table = nullptr; if (!upper_stmt.is_single_table_stmt() || OB_ISNULL(table = upper_stmt.get_table_item(0)) || !table->is_generated_table() || OB_ISNULL(view_stmt = table->ref_query_) || view_stmt->is_hierarchical_query() || !view_stmt->is_set_stmt() || ObSelectStmt::UNION != view_stmt->get_set_op()) { can_push = false; } else if (view_stmt->has_limit() || NULL == upper_stmt.get_limit_expr() || NULL != upper_stmt.get_limit_percent_expr() || upper_stmt.has_fetch()) { can_push = false; } else if (0 < upper_stmt.get_condition_size() || upper_stmt.has_group_by() || upper_stmt.has_rollup() || upper_stmt.has_window_function() || upper_stmt.has_distinct() || upper_stmt.has_sequence()) { can_push = false; } else { // only push down generated table column in order by can_push = true; const uint64_t table_id = table->table_id_; ObRawExpr *order_expr = NULL; for (int64_t i = 0; OB_SUCC(ret) && can_push && i < upper_stmt.get_order_item_size(); ++i) { if (OB_ISNULL(order_expr = upper_stmt.get_order_item(i).expr_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret), K(order_expr)); } else if (!order_expr->is_column_ref_expr() || table_id != static_cast(order_expr)->get_table_id()) { can_push = false; } } } return ret; } int ObTransformSimplifyLimit::do_pushdown_limit_order_for_union(ObSelectStmt& upper_stmt, ObSelectStmt* view_stmt) { int ret = OB_SUCCESS; if (OB_ISNULL(view_stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret), K(view_stmt)); } else { view_stmt->set_limit_offset(upper_stmt.get_limit_expr(), upper_stmt.get_offset_expr()); upper_stmt.set_limit_offset(NULL, NULL); if (upper_stmt.get_order_items().empty()) { // do nothing } else if (OB_FAIL(view_stmt->get_order_items().assign(upper_stmt.get_order_items()))) { LOG_WARN("failed to assign order items", K(ret)); } else { ObRawExpr* expr = NULL; int64_t pos = OB_INVALID_INDEX; for (int64_t i = 0; OB_SUCC(ret) && i < upper_stmt.get_order_item_size(); ++i) { if (OB_ISNULL(expr = upper_stmt.get_order_item(i).expr_) || OB_UNLIKELY(!expr->is_column_ref_expr())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected expr", K(ret), KPC(expr)); } else if (FALSE_IT(pos = static_cast(expr)->get_column_id() - OB_APP_MIN_COLUMN_ID)) { /*do nothing*/ } else if (OB_UNLIKELY(pos < 0 || pos >= view_stmt->get_select_item_size())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid array pos", K(pos), K(view_stmt->get_select_item_size()), K(ret)); } else { view_stmt->get_order_item(i).expr_ = view_stmt->get_select_item(pos).expr_; } } if (OB_SUCC(ret)) { upper_stmt.get_order_items().reset(); } } } return ret; }