/** * 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_view_merge.h" #include "sql/rewrite/ob_transform_utils.h" #include "sql/resolver/dml/ob_dml_stmt.h" #include "sql/resolver/dml/ob_del_upd_stmt.h" #include "sql/resolver/dml/ob_update_stmt.h" #include "sql/optimizer/ob_optimizer_util.h" #include "common/ob_smart_call.h" namespace oceanbase { using namespace common; namespace sql { int ObTransformViewMerge::transform_one_stmt(common::ObIArray &parent_stmts, ObDMLStmt *&stmt, bool &trans_happened) { int ret = OB_SUCCESS; UNUSED(parent_stmts); trans_happened = false; bool is_from_item_happened = false; bool is_semi_info_happened = false; ObSEArray merged_stmts; if (OB_ISNULL(stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(stmt), K(ret)); } else if (OB_FAIL(transform_in_from_item(stmt, merged_stmts, is_from_item_happened))) { LOG_WARN("failed to do view merge in from item", K(ret)); } else if (OB_FAIL(transform_in_semi_info(stmt, merged_stmts, is_semi_info_happened))) { LOG_WARN("failed to do view merge in semi info", K(ret)); } else if (!is_from_item_happened && !is_semi_info_happened) { /*do nothing*/ } else if (OB_FAIL(stmt->formalize_query_ref_exprs())) { LOG_WARN("failed to fromalize query ref exprs", K(ret)); } else if (OB_FAIL(add_transform_hint(*stmt, &merged_stmts))) { LOG_WARN("failed to add transform hint", K(ret)); } else { trans_happened = true; LOG_TRACE("succeed to do view merge", K(is_from_item_happened), K(is_semi_info_happened)); } return ret; } int ObTransformViewMerge::transform_one_stmt_with_outline(ObIArray &parent_stmts, ObDMLStmt *&stmt, bool &trans_happened) { int ret = OB_SUCCESS; UNUSED(parent_stmts); trans_happened = false; bool is_happened = false; ObSEArray merged_stmts; do { is_happened = false; if (OB_FAIL(transform_in_from_item(stmt, merged_stmts, is_happened))) { LOG_WARN("failed to do view merge in from item", K(ret)); } else if (!is_happened && OB_FAIL(transform_in_semi_info(stmt, merged_stmts, is_happened))) { LOG_WARN("failed to do view merge in semi info", K(ret)); } else if (!is_happened) { LOG_TRACE("can not do view merge with outline", K(ctx_->src_qb_name_)); } else { ++ctx_->trans_list_loc_; trans_happened = true; LOG_TRACE("succeed to do view merge with outline", K(ctx_->src_qb_name_)); } } while (OB_SUCC(ret) && is_happened); if (OB_SUCC(ret) && trans_happened) { if (OB_FAIL(stmt->formalize_query_ref_exprs())) { LOG_WARN("failed to fromalize query ref exprs", K(ret)); } else if (OB_FAIL(add_transform_hint(*stmt, &merged_stmts))) { LOG_WARN("failed to add transform hint", K(ret)); } } return ret; } int ObTransformViewMerge::need_transform(const common::ObIArray &parent_stmts, const int64_t current_level, const ObDMLStmt &stmt, bool &need_trans) { int ret = OB_SUCCESS; need_trans = false; UNUSED(parent_stmts); UNUSED(current_level); const ObQueryHint *query_hint = NULL; const ObHint *trans_hint = NULL; if (!stmt.is_sel_del_upd() || stmt.has_instead_of_trigger() || stmt.is_hierarchical_query()) { need_trans = false; } else if (OB_ISNULL(ctx_) || OB_ISNULL(query_hint = stmt.get_stmt_hint().query_hint_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(ctx_), K(query_hint)); } else if (!query_hint->has_outline_data()) { need_trans = true; } else if (NULL == (trans_hint = query_hint->get_outline_trans_hint(ctx_->trans_list_loc_)) || !trans_hint->is_view_merge_hint() || !static_cast(trans_hint)->enable_view_merge(ctx_->src_qb_name_)) { /*do nothing*/ } else { const TableItem *table = NULL; for (int64_t i = 0; !need_trans && OB_SUCC(ret) && i < stmt.get_table_size(); ++i) { if (OB_ISNULL(table = stmt.get_table_item(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("table item is null", K(ret)); } else if (!table->is_generated_table()) { /*do nothing*/ } else if (OB_ISNULL(table->ref_query_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret), K(*table)); } else { need_trans = query_hint->is_valid_outline_transform(ctx_->trans_list_loc_, get_hint(table->ref_query_->get_stmt_hint())); } } if (!need_trans) { OPT_TRACE("outline reject transform"); } } return ret; } int ObTransformViewMerge::check_hint_allowed_merge(ObDMLStmt &stmt, ObSelectStmt &ref_query, bool &force_merge, bool &force_no_merge) { int ret = OB_SUCCESS; force_merge = false; force_no_merge = false; bool contain_inner_table = false; const ObQueryHint *query_hint = NULL; const ObViewMergeHint *myhint = static_cast(get_hint(ref_query.get_stmt_hint())); bool is_disable = (NULL != myhint && myhint->enable_no_view_merge()); const ObHint *no_rewrite1 = stmt.get_stmt_hint().get_no_rewrite_hint(); const ObHint *no_rewrite2 = ref_query.get_stmt_hint().get_no_rewrite_hint(); if (OB_ISNULL(ctx_) || OB_ISNULL(query_hint = stmt.get_stmt_hint().query_hint_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(ctx_), K(query_hint)); } else if (query_hint->has_outline_data()) { // outline data allowed merge if (myhint != NULL && query_hint->is_valid_outline_transform(ctx_->trans_list_loc_, myhint) && myhint->enable_view_merge(ctx_->src_qb_name_)) { force_merge = true; } else { force_no_merge = true; } } else if (NULL != myhint && myhint->enable_view_merge(ctx_->src_qb_name_)) { // enable transform hint added after transform in construct_transform_hint() force_merge = true; } else if (is_disable || NULL != no_rewrite1 || NULL != no_rewrite2) { // add disable transform hint here force_no_merge = true; if (OB_FAIL(ctx_->add_used_trans_hint(no_rewrite1))) { LOG_WARN("failed to add used trans hint", K(ret)); } else if (OB_FAIL(ctx_->add_used_trans_hint(no_rewrite2))) { LOG_WARN("failed to add used trans hint", K(ret)); } else if (is_disable && OB_FAIL(ctx_->add_used_trans_hint(myhint))) { LOG_WARN("failed to add used trans hint", K(ret)); } } else if (OB_FAIL(check_contain_inner_table(ref_query, contain_inner_table))) { LOG_WARN("failed to check contain inner table", K(ret)); } else if (contain_inner_table) { force_no_merge = true; OPT_TRACE("stmt contain inner table, can not merge"); } return ret; } int ObTransformViewMerge::transform_in_from_item(ObDMLStmt *stmt, ObIArray &merged_stmts, bool &trans_happened) { int ret = OB_SUCCESS; trans_happened = false; if (OB_ISNULL(stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(stmt), K(ret)); } else { int64_t i = 0; while (OB_SUCC(ret) && i < stmt->get_from_item_size()) { bool is_happened = false; TableItem *table_item = stmt->get_table_item(stmt->get_from_item(i)); if (OB_ISNULL(table_item)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("table item is null", K(ret)); } else if (table_item->is_joined_table()) { if (OB_FAIL(transform_joined_table(stmt, static_cast(table_item), true, merged_stmts, is_happened))) { LOG_WARN("failed to transform joined table", K(ret)); } else { trans_happened |= is_happened; LOG_TRACE("succeed to do view merge for joined table", K(is_happened), K(table_item->table_id_)); } } else if (table_item->is_generated_table()) { if (OB_FAIL(transform_generated_table(stmt, table_item, merged_stmts, is_happened))) { LOG_WARN("failed to transform basic table", K(ret)); } else { trans_happened |= is_happened; LOG_TRACE("succeed to do view merge for basic table", K(is_happened), K(table_item->table_id_)); } } if (OB_SUCC(ret) && !is_happened) { i++; } } } return ret; } // do view merge for semi right table int ObTransformViewMerge::transform_in_semi_info(ObDMLStmt *stmt, ObIArray &merged_stmts, bool &trans_happened) { int ret = OB_SUCCESS; trans_happened = false; if (OB_ISNULL(stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(stmt), K(ret)); } else { ObIArray &semi_infos = stmt->get_semi_infos(); bool can_be = false; SemiInfo *semi_info = NULL; ObSelectStmt *child_stmt = NULL; TableItem *right_table = NULL; for (int64_t i = 0; OB_SUCC(ret) && i < semi_infos.count(); ++i) { if (OB_ISNULL(stmt) || OB_ISNULL(semi_info = semi_infos.at(i)) || OB_ISNULL(right_table = stmt->get_table_item_by_id(semi_info->right_table_id_))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (!right_table->is_generated_table()) { /*do nothing*/ } else if (OB_ISNULL(child_stmt = right_table->ref_query_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (OB_FAIL(check_semi_right_table_can_be_merged(stmt, child_stmt, can_be))) { LOG_WARN("failed to check semi right table can be unnested", K(ret)); } else if (!can_be) { /*do nothing*/ } else if (OB_FAIL(do_view_merge_for_semi_right_table(stmt, child_stmt, semi_info))) { LOG_WARN("failed to do view merge", K(ret)); } else if (OB_FAIL(merged_stmts.push_back(child_stmt))) { LOG_WARN("failed to push back", K(ret)); } else { trans_happened = true; } } } return ret; } int ObTransformViewMerge::do_view_merge_for_semi_right_table(ObDMLStmt *parent_stmt, ObSelectStmt *child_stmt, SemiInfo *semi_info) { int ret = OB_SUCCESS; TableItem *right_table = NULL; ObSEArray old_column_exprs; ObSEArray new_column_exprs; // TODO link.zt I can refine here now // updatable view have some unexpected design, as a result we use a sepcial replace here // updatable view's part expr should be replaced while it belongs to table. // In normal case, we expect the part expr should be removed. ObStmtExprReplacer replacer; replacer.set_recursive(false); if (OB_ISNULL(ctx_) || OB_ISNULL(parent_stmt) || OB_ISNULL(child_stmt) || OB_ISNULL(semi_info) || OB_ISNULL(right_table = parent_stmt->get_table_item_by_id(semi_info->right_table_id_))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (OB_UNLIKELY(!child_stmt->is_single_table_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected semi info for view merge", K(ret)); } else if (OB_FAIL(parent_stmt->get_stmt_hint().merge_stmt_hint(child_stmt->get_stmt_hint(), LEFT_HINT_DOMINATED))) { LOG_WARN("failed to merge view stmt hint", K(ret)); } else if (OB_FAIL(parent_stmt->get_stmt_hint().replace_name_for_single_table_view(ctx_->allocator_, *parent_stmt, *right_table))) { LOG_WARN("failed to replace name for single table view", K(ret)); } else if (OB_FALSE_IT(semi_info->right_table_id_ = child_stmt->get_from_items().at(0).table_id_)) { } else if (OB_FAIL(parent_stmt->get_column_exprs(right_table->table_id_, old_column_exprs))) { LOG_WARN("failed to get column exprs", K(ret)); } else if (OB_FAIL(ObTransformUtils::convert_column_expr_to_select_expr(old_column_exprs, *child_stmt, new_column_exprs))) { LOG_WARN("failed to convert column expr to select expr", K(ret)); } else if (OB_FAIL(parent_stmt->remove_table_info(right_table))) { LOG_WARN("failed to remove right table info", K(ret)); } else if (OB_FAIL(replacer.add_replace_exprs(old_column_exprs, new_column_exprs))) { LOG_WARN("failed to add replace exprs", K(ret)); } else if (OB_FAIL(parent_stmt->iterate_stmt_expr(replacer))) { LOG_WARN("failed to replace stmt expr", K(ret)); } else if (OB_FAIL(adjust_updatable_view(parent_stmt, right_table))) { LOG_WARN("failed to adjust updatable view", K(ret)); } else if (OB_FAIL(append(parent_stmt->get_table_items(), child_stmt->get_table_items()))) { LOG_WARN("failed to append table items", K(ret)); } else if (OB_FAIL(append(parent_stmt->get_column_items(), child_stmt->get_column_items()))) { LOG_WARN("failed to append column items", K(ret)); } else if (OB_FAIL(append(parent_stmt->get_part_exprs(), child_stmt->get_part_exprs()))) { LOG_WARN("failed to append part exprs", K(ret)); } else if (OB_FAIL(append(parent_stmt->get_check_constraint_items(), child_stmt->get_check_constraint_items()))) { LOG_WARN("failed to append check constraint items", K(ret)); } else if (parent_stmt->is_select_stmt() && OB_FAIL(append(static_cast(parent_stmt)->get_sample_infos(), child_stmt->get_sample_infos()))) { LOG_WARN("failed to append exprs", K(ret)); } else if (OB_FAIL(ObTransformUtils::adjust_pseudo_column_like_exprs(*parent_stmt))) { LOG_WARN("failed to adjust pseudo column like exprs", K(ret)); } else if (OB_FAIL(parent_stmt->rebuild_tables_hash())) { LOG_WARN("failed to rebuild table hash", K(ret)); } else if (OB_FAIL(parent_stmt->update_column_item_rel_id())) { LOG_WARN("failed to update column item rel id", K(ret)); } return ret; } // check semi right table can merge int ObTransformViewMerge::check_semi_right_table_can_be_merged(ObDMLStmt *stmt, ObSelectStmt *ref_query, bool &can_be) { int ret = OB_SUCCESS; can_be = false; bool has_rownum = false; bool force_merge = false; bool force_no_merge = false; if (OB_ISNULL(stmt) || OB_ISNULL(ref_query)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret), K(stmt), K(ref_query)); } else if (OB_FAIL(check_hint_allowed_merge(*stmt, *ref_query, force_merge, force_no_merge))) { LOG_WARN("failed to check hint allowed merge", K(ret)); } else if (force_no_merge) { can_be = false; } else if (!ref_query->is_single_table_stmt() || !ref_query->get_condition_exprs().empty() || !ref_query->get_subquery_exprs().empty() || ref_query->has_distinct() || ref_query->has_group_by() || ref_query->has_rollup() || ref_query->has_window_function() || ref_query->has_limit() || ref_query->is_contains_assignment() || ref_query->has_sequence() || ref_query->is_hierarchical_query() || ref_query->has_ora_rowscn() || (lib::is_mysql_mode() && ref_query->has_for_update()) || ref_query->is_values_table_query()) { can_be = false; } else if (OB_FAIL(ref_query->has_rownum(has_rownum))) { LOG_WARN("failed to check has rownum expr", K(ret)); } else if (has_rownum) { can_be = false; } else { can_be = true; } return ret; } /*@brief transform_basic_table处理普通generated table */ int ObTransformViewMerge::transform_generated_table(ObDMLStmt *parent_stmt, TableItem *table_item, ObIArray &merged_stmts, bool &trans_happened) { int ret = OB_SUCCESS; bool can_be = false; ObSelectStmt *child_stmt = NULL; ViewMergeHelper helper; trans_happened = false; OPT_TRACE("try to merge view:", table_item); if (OB_ISNULL(parent_stmt) || OB_ISNULL(table_item)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null ptr", K(ret), K(parent_stmt), K(table_item)); } else if (!table_item->is_generated_table()) { /*do nothing*/ } else if (OB_ISNULL(child_stmt = table_item->ref_query_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (OB_FAIL(check_can_be_merged(parent_stmt, child_stmt, helper, parent_stmt->is_hierarchical_query(), false, can_be))) { LOG_WARN("failed to check can be unnested", K(ret)); } else if (!can_be) { /*do nothing*/ OPT_TRACE("view can no be merged"); } else if (OB_FAIL(do_view_merge(parent_stmt, child_stmt, table_item, helper))) { LOG_WARN("failed to do view merge", K(ret)); } else if (OB_FAIL(merged_stmts.push_back(child_stmt))) { LOG_WARN("failed to push back", K(ret)); } else { trans_happened = true; } return ret; } /*@brief transform_basic_table处理joined table中的generated table */ int ObTransformViewMerge::transform_generated_table(ObDMLStmt *parent_stmt, JoinedTable *parent_table, TableItem *table_item, bool need_check_where_condi, bool can_push_where, ObIArray &merged_stmts, bool &trans_happened) { int ret = OB_SUCCESS; bool can_be = false; ObSelectStmt *child_stmt = NULL; ViewMergeHelper helper; helper.parent_table = parent_table; helper.trans_table = table_item; helper.can_push_where = can_push_where; bool is_left_join_right_table = false; trans_happened = false; OPT_TRACE("try to merge view:", table_item); if (OB_ISNULL(parent_stmt) || OB_ISNULL(parent_table) || OB_ISNULL(table_item)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null ptr", K(ret), K(parent_stmt), K(parent_table), K(table_item)); } else if (!table_item->is_generated_table()) { /*do nothing*/ } else if (OB_ISNULL(child_stmt = table_item->ref_query_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (parent_table->joined_type_ == FULL_OUTER_JOIN) { //如果是full outer join,如果视图有null reject输出表达式,需要视图有非空列 helper.need_check_null_propagate = true; } else if (parent_table->joined_type_ == LEFT_OUTER_JOIN) { if (table_item == parent_table->right_table_) { //如果是left join的右表,如果视图有null reject输出表达式,需要视图有非空列 helper.need_check_null_propagate = true; is_left_join_right_table = true; } } else if (parent_table->joined_type_ == RIGHT_OUTER_JOIN) { if (table_item == parent_table->left_table_) { //如果是right join的左表,如果视图有null reject输出表达式,需要视图有非空列 helper.need_check_null_propagate = true; is_left_join_right_table = true; } } else {/*do nothing*/} if (OB_FAIL(ret) || !table_item->is_generated_table()) { /*do nothing*/ } else if (need_check_where_condi && child_stmt->get_condition_size() > 0) { /*do nothing*/ OPT_TRACE("view has conditions, can not merge view"); } else if (OB_FAIL(check_can_be_merged(parent_stmt, child_stmt, helper, !can_push_where, is_left_join_right_table, can_be))) { LOG_WARN("failed to check can be unnested", K(ret)); } else if (!can_be) { /*do nothing*/ } else if (OB_FAIL(do_view_merge(parent_stmt, child_stmt, table_item, helper))) { LOG_WARN("failed to do view merge in joined table", K(ret)); } else if (OB_FAIL(merged_stmts.push_back(child_stmt))) { LOG_WARN("failed to push back", K(ret)); } else { trans_happened = true; } return ret; } int ObTransformViewMerge::check_basic_validity(ObDMLStmt *parent_stmt, ObSelectStmt *child_stmt, bool &can_be) { int ret = OB_SUCCESS; can_be = false; bool has_assignment = false; bool has_rownum_expr = false; bool has_ref_assign_user_var = false; bool force_merge = false; bool force_no_merge = false; bool is_select_expr_valid = false; ObSEArray select_exprs; if (OB_ISNULL(parent_stmt) || OB_ISNULL(child_stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (OB_FAIL(check_hint_allowed_merge(*parent_stmt, *child_stmt, force_merge, force_no_merge))) { LOG_WARN("failed to check hint allowed merge", K(ret)); } else if (force_no_merge) { can_be = false; OPT_TRACE("hint reject transform"); } else if (child_stmt->is_hierarchical_query() || child_stmt->has_distinct() || child_stmt->has_group_by() || child_stmt->is_set_stmt() || child_stmt->has_rollup() || child_stmt->has_order_by() || child_stmt->has_limit() || child_stmt->get_aggr_item_size() != 0 || child_stmt->has_window_function() || child_stmt->has_sequence() || child_stmt->has_ora_rowscn() || child_stmt->is_values_table_query()) { can_be = false; OPT_TRACE("not a valid view"); } else if (!force_merge && parent_stmt->get_table_size() > 1 && child_stmt->get_table_size() > 1 && parent_stmt->get_table_size() + child_stmt->get_table_size() - 1 > 10) { // More than 10 tables may result in the inability to enumerate a valid join order. can_be = false; OPT_TRACE("Too Many Table Items"); } else if (OB_FAIL(ObTransformUtils::check_has_assignment(*child_stmt, has_assignment))) { LOG_WARN("check has assignment failed", K(ret)); } else if (has_assignment) { can_be = false; } else if (OB_FAIL(child_stmt->has_rownum(has_rownum_expr))) { LOG_WARN("failed to check has rownum expr", K(ret)); } else if (has_rownum_expr) { can_be = false; OPT_TRACE("view has rownum"); } else if (OB_FAIL(child_stmt->get_select_exprs(select_exprs))) { LOG_WARN("failed to get select exprs", K(ret)); } else if (OB_FAIL(ObTransformUtils::check_expr_valid_for_stmt_merge(select_exprs, is_select_expr_valid))) { LOG_WARN("failed to check select expr valid", K(ret)); } else if (!is_select_expr_valid) { can_be = false; } else if (0 == child_stmt->get_from_item_size()) { //当 view 为 select ... from dual, 若上层非层次查询, 允许视图合并 can_be = parent_stmt->is_single_table_stmt() && !parent_stmt->is_hierarchical_query(); } else if (OB_FAIL(child_stmt->has_ref_assign_user_var(has_ref_assign_user_var))) { LOG_WARN("failed to check stmt has assignment ref user var", K(ret)); } else if (has_ref_assign_user_var) { can_be = false; OPT_TRACE("view has user var"); } else if (parent_stmt->get_condition_size() > 0) { can_be = true; ObSEArray select_exprs; if (OB_FAIL(child_stmt->get_select_exprs(select_exprs))) { LOG_WARN("failed to get select exprs", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && can_be && i < select_exprs.count(); ++i) { ObRawExpr *expr = select_exprs.at(i); if (OB_ISNULL(expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpect null expr", K(ret)); } else if (expr->has_flag(CNT_SYS_CONNECT_BY_PATH)) { can_be = false; OPT_TRACE("view`s select expr contain connect by path func"); } } } else { can_be = true; } return ret; } int ObTransformViewMerge::check_contain_inner_table(const ObSelectStmt &stmt, bool &contain) { int ret = OB_SUCCESS; contain = false; const ObIArray &table_items = stmt.get_table_items(); for (int64_t i = 0; OB_SUCC(ret) && !contain && i < table_items.count(); ++i) { const TableItem *table = table_items.at(i); if (OB_ISNULL(table)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpect null table item", K(ret)); } else if (!table->is_basic_table()) { //do nothing } else { contain = is_virtual_table(table->ref_id_) || is_inner_table(table->ref_id_); } } return ret; } int ObTransformViewMerge::check_can_be_merged(ObDMLStmt *parent_stmt, ObSelectStmt *child_stmt, ViewMergeHelper &helper, bool need_check_subquery, bool is_left_join_right_table, bool &can_be) { int ret = OB_SUCCESS; can_be = true; bool has_rollup = false; if (OB_ISNULL(parent_stmt) || OB_ISNULL(child_stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (OB_FAIL(check_basic_validity(parent_stmt, child_stmt, can_be))) { LOG_WARN("failed to check", K(ret)); } else if (!can_be) { } else { has_rollup = parent_stmt->is_select_stmt() && static_cast(parent_stmt)->has_rollup(); //select expr不能包含subquery for (int64_t i = 0; OB_SUCC(ret) && can_be && i < child_stmt->get_select_item_size(); i++) { ObRawExpr *expr = child_stmt->get_select_item(i).expr_; if (OB_ISNULL(expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("NULL expr", K(ret)); } else if (expr->has_flag(CNT_SUB_QUERY)) { can_be = false; OPT_TRACE("view`s select expr contain subquery, can not merge"); } else if (expr->is_const_expr() && has_rollup) { can_be = false; OPT_TRACE("const expr can not be merged into rollup stmt"); } } //stmt不能包含rand函数 if (OB_SUCC(ret) && can_be) { bool has_rand = false; if (OB_FAIL(child_stmt->has_rand(has_rand))) { LOG_WARN("failed to get rand flag", K(ret)); } else if (has_rand) { can_be = false; OPT_TRACE("view has random expr, can not merge"); } } } //检查where condition是否存在子查询 if (OB_FAIL(ret) || !can_be) { /*do nothing*/ } else if (need_check_subquery){ if (child_stmt->get_semi_infos().count() > 0) { can_be =false; OPT_TRACE("view has semi info, can not merge"); } else { for (int64_t i = 0; OB_SUCC(ret) && can_be && i < child_stmt->get_condition_size(); i++) { const ObRawExpr *expr = child_stmt->get_condition_expr(i); if (OB_ISNULL(expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("NULL expr", K(ret)); } else if (expr->has_flag(CNT_SUB_QUERY)) { can_be = false; OPT_TRACE("view`s condition has subquery, can not merge"); } else { /*do nothing*/ } } } } else {/*do nothing*/} //Check if the left join right view expansion will increase the plan space. if (OB_FAIL(ret) || !can_be || !is_left_join_right_table) { /*do nothing*/ } else if (OB_ISNULL(helper.parent_table)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpect null table item", K(ret)); } else if (OB_FAIL(check_left_join_right_view_need_merge(parent_stmt, child_stmt, helper.trans_table, helper.parent_table, can_be))) { LOG_WARN("failed to check left join right view need merge", K(ret)); } //检查视图是否有空值拒绝表达式 if (OB_FAIL(ret) || !can_be) { /*do nothing*/ } else if (helper.need_check_null_propagate){ ObSEArray columns; ObSqlBitSet<> from_tables; ObSEArray column_exprs; if (OB_FAIL(child_stmt->get_from_tables(from_tables))) { LOG_WARN("failed to get from tables", K(ret)); } else if (OB_FAIL(child_stmt->get_column_exprs(columns))) { LOG_WARN("failed to get column exprs", K(ret)); } else if (OB_FAIL(ObTransformUtils::extract_table_exprs(*child_stmt, columns, from_tables, column_exprs))) { LOG_WARN("failed to extract table exprs", K(ret)); } else { //检查是否有空值拒绝表达式 bool find = false; for (int64_t i = 0; OB_SUCC(ret) && !find && i < child_stmt->get_select_item_size(); i++) { ObRawExpr *expr = child_stmt->get_select_item(i).expr_; bool is_null_propagate = true; if (OB_ISNULL(expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("NULL expr", K(ret)); } else if (OB_FAIL(ObTransformUtils::is_null_propagate_expr(expr, column_exprs, is_null_propagate))) { LOG_WARN("failed to is null propagate expr", K(ret)); } else if (!is_null_propagate) { find = true; } else {/*do nothing*/} } if (OB_FAIL(ret)) { /*do nothing*/ } else if (!find) { /*do nothing*/ } else if (OB_FAIL(find_not_null_column(*parent_stmt, *child_stmt, helper, column_exprs, can_be))){ LOG_WARN("failed to find not null column", K(ret)); } else if (!can_be) { OPT_TRACE("view has null propagate expr, but not found not null column"); } } } else {/*do nothing*/} return ret; } int ObTransformViewMerge::check_left_join_right_view_need_merge(ObDMLStmt *parent_stmt, ObSelectStmt* child_stmt, TableItem *view_table, JoinedTable *joined_table, bool &need_merge) { int ret = OB_SUCCESS; need_merge = false; bool force_merge = false; bool force_no_merge = false; if (OB_ISNULL(child_stmt) || OB_ISNULL(parent_stmt) || OB_ISNULL(view_table)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpect null stmt", K(ret)); } else if (2 > child_stmt->get_table_items().count()) { need_merge = true; } else if (OB_FAIL(check_hint_allowed_merge(*parent_stmt, *child_stmt, force_merge, force_no_merge))) { LOG_WARN("failed to check hint allowed merge", K(ret)); } else if (force_merge) { need_merge = true; } else if (ObTransformUtils::check_joined_table_combinable(parent_stmt, joined_table, view_table, true, need_merge)) { LOG_WARN("failed to check joined table combinable", K(ret)); } else if (!need_merge) { OPT_TRACE("right tables not combinable, no need merge view"); } return ret; } int ObTransformViewMerge::find_not_null_column(ObDMLStmt &parent_stmt, ObSelectStmt &child_stmt, ViewMergeHelper &helper, ObIArray &column_exprs, bool &can_be) { int ret = OB_SUCCESS; bool is_valid = false; if (OB_ISNULL(ctx_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid input", K(ret)); } else if (OB_FAIL(find_not_null_column_with_condition(parent_stmt, child_stmt, helper, column_exprs))) { LOG_WARN("failed to find not null column with join condition", K(ret)); } else if (OB_NOT_NULL(helper.not_null_column)) { //find not null column, do nothing } else if (OB_FAIL(ObTransformUtils::find_not_null_expr(child_stmt, helper.not_null_column, is_valid, ctx_))) { LOG_WARN("failed to find not null expr", K(ret)); } else if (is_valid) { //find not null column, do nothing } else { can_be = false; } return ret; } int ObTransformViewMerge::find_not_null_column_with_condition( ObDMLStmt &parent_stmt, ObSelectStmt &child_stmt, ViewMergeHelper &helper, ObIArray &column_exprs) { int ret = OB_SUCCESS; ObSEArray join_conditions; ObSEArray old_column_exprs; ObSEArray new_column_exprs; ObSEArray temp_exprs; if (OB_ISNULL(helper.parent_table) || OB_ISNULL(helper.trans_table)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpect null table item", K(ret)); } else if (OB_FAIL(join_conditions.assign(helper.parent_table->join_conditions_))) { LOG_WARN("failed to assign join conditions"); } else if (OB_FAIL(parent_stmt.get_column_exprs(helper.trans_table->table_id_, temp_exprs))) { LOG_WARN("failed to get column exprs", K(ret)); } else if (OB_FAIL(append(old_column_exprs, temp_exprs))) { LOG_WARN("failed to append exprs", K(ret)); } else if (OB_FAIL(ObTransformUtils::convert_column_expr_to_select_expr(old_column_exprs, child_stmt, new_column_exprs))) { LOG_WARN("failed to convert column expr to select expr", K(ret)); } else { bool find = false; for (int64_t i = 0; OB_SUCC(ret) && !find && i < old_column_exprs.count(); ++i) { bool has_null_reject = false; //首先找到null reject的select expr if (OB_FAIL(ObTransformUtils::has_null_reject_condition(join_conditions, old_column_exprs.at(i), has_null_reject))) { LOG_WARN("failed to check has null reject condition", K(ret)); } else if (!has_null_reject) { //do nothing } else if (OB_FAIL(find_null_propagate_column(new_column_exprs.at(i), column_exprs, helper.not_null_column, find))) { LOG_WARN("failed to find null propagate column", K(ret)); } else { //在对应的select expr中找到了null propagate的column expr //do nothing } } } return ret; } int ObTransformViewMerge::find_null_propagate_column(ObRawExpr *condition, ObIArray &columns, ObRawExpr *&null_propagate_column, bool &is_valid) { int ret = OB_SUCCESS; is_valid = false; null_propagate_column = NULL; bool is_null_propagate = false; ObSEArray dummy_exprs; for (int64_t i = 0; OB_SUCC(ret) && !is_valid && i < columns.count(); ++i) { dummy_exprs.reuse(); if (OB_FAIL(dummy_exprs.push_back(columns.at(i)))) { LOG_WARN("failed to push back column expr", K(ret)); } else if (OB_FAIL(ObTransformUtils::is_null_propagate_expr(condition, dummy_exprs, is_null_propagate))) { LOG_WARN("failed to check null propagate expr", K(ret)); } else if (!is_null_propagate) { //do nothing } else { null_propagate_column = columns.at(i); is_valid = true; } } return ret; } int ObTransformViewMerge::transform_joined_table(ObDMLStmt *stmt, JoinedTable *joined_table, bool parent_can_push_where, ObIArray &merged_stmts, bool &trans_happened) { int ret = OB_SUCCESS; bool is_stack_overflow = false; TableItem *left_table = NULL; TableItem *right_table = NULL; trans_happened = false; if (OB_ISNULL(stmt) || OB_ISNULL(joined_table) || OB_ISNULL(left_table = joined_table->left_table_) || OB_ISNULL(right_table = joined_table->right_table_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid argument", K(stmt), K(joined_table), K(left_table), K(right_table), K(ret)); } else if (OB_FAIL(check_stack_overflow(is_stack_overflow))) { LOG_WARN("check stack overflow failed", K(ret)); } else if (is_stack_overflow) { ret = OB_SIZE_OVERFLOW; LOG_WARN("too deep recursive", K(ret)); } else if (joined_table->joined_type_ == CONNECT_BY_JOIN) { // do nothing } else { //处理左表 bool can_push_where = true; bool need_check_where_condi = false; if (!parent_can_push_where || joined_table->joined_type_ == FULL_OUTER_JOIN || joined_table->joined_type_ == RIGHT_OUTER_JOIN) { can_push_where = false; if (joined_table->joined_type_ == LEFT_OUTER_JOIN || joined_table->joined_type_ == INNER_JOIN || joined_table->joined_type_ == FULL_OUTER_JOIN) { need_check_where_condi = true; } } bool is_happened = false; if (left_table->is_joined_table()) { JoinedTable *j_table = static_cast(left_table); if (OB_FAIL(SMART_CALL(transform_joined_table(stmt, j_table, can_push_where, merged_stmts, is_happened)))) { LOG_WARN("failed to transform joined table", K(ret)); } else { trans_happened |= is_happened; } } else if (OB_FAIL(transform_generated_table(stmt, joined_table, left_table, need_check_where_condi, can_push_where, merged_stmts, is_happened))) { LOG_WARN("failed to transform basic table", K(ret)); } else { trans_happened |= is_happened; } //处理右表 if (OB_SUCC(ret)) { can_push_where = true; need_check_where_condi = false; if (!parent_can_push_where || joined_table->joined_type_ == FULL_OUTER_JOIN || joined_table->joined_type_ == LEFT_OUTER_JOIN) { can_push_where = false; if (joined_table->joined_type_ == RIGHT_OUTER_JOIN || joined_table->joined_type_ == INNER_JOIN || joined_table->joined_type_ == FULL_OUTER_JOIN) { need_check_where_condi = true; } } if (right_table->is_joined_table()) { JoinedTable *j_table = static_cast(right_table); if (OB_FAIL(SMART_CALL(transform_joined_table(stmt, j_table, can_push_where, merged_stmts, is_happened)))) { LOG_WARN("failed to transform joined table", K(ret)); } else { trans_happened |= is_happened; } } else if (OB_FAIL(transform_generated_table(stmt, joined_table, right_table, need_check_where_condi, can_push_where, merged_stmts, is_happened))) { LOG_WARN("failed to transform basic table", K(ret)); } else { trans_happened |= is_happened; } } //处理joined table single table ids if (OB_SUCC(ret)) { joined_table->single_table_ids_.reset(); if (OB_FAIL(ObTransformUtils::add_joined_table_single_table_ids(*joined_table, *left_table))) { LOG_WARN("failed to add joined table single table ids",K(ret)); } else if (OB_FAIL(ObTransformUtils::add_joined_table_single_table_ids(*joined_table, *right_table))) { LOG_WARN("failed to add joined table single table ids",K(ret)); } else {/*do nothing*/} } } return ret; } int ObTransformViewMerge::create_joined_table_for_view(ObSelectStmt *child_stmt, TableItem *&new_table) { int ret = OB_SUCCESS; if (OB_ISNULL(child_stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid argument", K(child_stmt), K(ret)); } else { new_table = NULL; TableItem *right_table = NULL; const ObSEArray joined_conds; for (int64_t i = 0; OB_SUCC(ret) && i < child_stmt->get_from_item_size(); ++i) { FromItem &cur_from = child_stmt->get_from_item(i); if (cur_from.is_joined_) { right_table = child_stmt->get_joined_table(cur_from.table_id_); } else { right_table = child_stmt->get_table_item_by_id(cur_from.table_id_); } if (i == 0) { new_table = right_table; } else if (OB_FAIL(ObTransformUtils::add_new_joined_table(ctx_, *child_stmt, INNER_JOIN, new_table, right_table, joined_conds, new_table, false))) { LOG_WARN("failed to add new joined table", K(ret)); } } } return ret; } int ObTransformViewMerge::do_view_merge(ObDMLStmt *parent_stmt, ObSelectStmt *child_stmt, TableItem *table_item, ViewMergeHelper &helper) { int ret = OB_SUCCESS; JoinedTable *joined_table = helper.parent_table; if (OB_ISNULL(ctx_) || OB_ISNULL(parent_stmt) || OB_ISNULL(child_stmt) || OB_ISNULL(table_item)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid argument", K(ctx_), K(parent_stmt), K(child_stmt), K(table_item), K(ret)); // adjsut hint, replace name after merge stmt hint. } else if (OB_FAIL(parent_stmt->get_stmt_hint().merge_stmt_hint(child_stmt->get_stmt_hint(), LEFT_HINT_DOMINATED))) { LOG_WARN("failed to merge view stmt hint", K(ret)); } else if (OB_FAIL(parent_stmt->get_stmt_hint().replace_name_for_single_table_view(ctx_->allocator_, *parent_stmt, *table_item))) { LOG_WARN("failed to replace name for single table view", K(ret)); } else if (OB_NOT_NULL(joined_table)) {//调整joined table中generated table需要单独处理的部分 if (OB_FAIL(ObOptimizerUtil::remove_item(joined_table->single_table_ids_, table_item->table_id_))) { LOG_WARN("failed to remove table id", K(ret)); } else if (joined_table->left_table_ == table_item && OB_FAIL(create_joined_table_for_view(child_stmt, joined_table->left_table_))) { LOG_WARN("failed to create joined table for view", K(ret)); } else if (joined_table->right_table_ == table_item && OB_FAIL(create_joined_table_for_view(child_stmt, joined_table->right_table_))) { LOG_WARN("failed to create joined table for view", K(ret)); } else {/*do nothing*/} } else {//调整普通generated table需要单独处理的部分 if (OB_FAIL(append(parent_stmt->get_from_items(), child_stmt->get_from_items()))) { LOG_WARN("failed to append from items", K(ret)); } else if (OB_FAIL(append(parent_stmt->get_joined_tables(), child_stmt->get_joined_tables()))) { LOG_WARN("failed to append joined tables", K(ret)); } } if (OB_SUCC(ret)) { if (parent_stmt->is_select_stmt() && child_stmt->is_select_stmt() && child_stmt->is_hierarchical_query()) { ObSelectStmt *parent_sel_stmt = static_cast(parent_stmt); ObSelectStmt *child_sel_stmt = static_cast(child_stmt); parent_sel_stmt->set_order_siblings(child_sel_stmt->is_order_siblings()); parent_sel_stmt->set_has_prior(child_sel_stmt->has_prior()); parent_sel_stmt->set_hierarchical_query(true); parent_sel_stmt->set_nocycle(child_sel_stmt->is_nocycle()); if (OB_FAIL(append(parent_sel_stmt->get_order_items(), child_sel_stmt->get_order_items()))) { LOG_WARN("failed to append order items", K(ret)); } else if (OB_FAIL(append(parent_sel_stmt->get_connect_by_prior_exprs(), child_sel_stmt->get_connect_by_prior_exprs()))) { LOG_WARN("failed to append connect by prior exprs", K(ret)); } } } if (OB_SUCC(ret)) {//调整公共部分 if (OB_FAIL(replace_stmt_exprs(parent_stmt, child_stmt, table_item, helper, helper.need_check_null_propagate))) { LOG_WARN("failed to replace stmt exprs", K(ret)); } else if (OB_FAIL(adjust_updatable_view(parent_stmt, table_item))) { LOG_WARN("failed to adjust updatable view", K(ret)); } else if (OB_FAIL(append(parent_stmt->get_table_items(), child_stmt->get_table_items()))) { LOG_WARN("failed to append table items", K(ret)); } else if (OB_FAIL(parent_stmt->remove_table_info(table_item))) { LOG_WARN("failed to remove table item", K(ret)); } else if (OB_FAIL(append(parent_stmt->get_column_items(), child_stmt->get_column_items()))) { LOG_WARN("failed to append column items", K(ret)); } else if (OB_FAIL(parent_stmt->rebuild_tables_hash())) { LOG_WARN("failed to rebuild table hash", K(ret)); } else if (OB_FAIL(parent_stmt->update_column_item_rel_id())) { LOG_WARN("failed to update column item rel id", K(ret)); } else if (OB_FAIL(adjust_stmt_semi_infos(parent_stmt, child_stmt, table_item->table_id_))) { LOG_WARN("failed to adjust stmt semi infos", K(ret)); } else if (OB_FAIL(append(parent_stmt->get_semi_infos(), child_stmt->get_semi_infos()))) { LOG_WARN("failed to append semi infos", K(ret)); } else if (helper.can_push_where && OB_FAIL(append(parent_stmt->get_condition_exprs(), child_stmt->get_condition_exprs()))) { LOG_WARN("failed to append condition exprs", K(ret)); //joined table中的generated table的where不能直接下压到外层where里面的,只能下压到连接条件(on)里面 } else if (!helper.can_push_where && OB_FAIL(append(joined_table->get_join_conditions(), child_stmt->get_condition_exprs()))) { LOG_WARN("failed to append condition exprs", K(ret)); } else if (OB_FAIL(append(parent_stmt->get_part_exprs(), child_stmt->get_part_exprs()))) { LOG_WARN("failed to append part exprs", K(ret)); } else if (OB_FAIL(append(parent_stmt->get_check_constraint_items(), child_stmt->get_check_constraint_items()))) { LOG_WARN("failed to append check constraint items", K(ret)); } else if (parent_stmt->is_select_stmt() && OB_FAIL(append(static_cast(parent_stmt)->get_sample_infos(), child_stmt->get_sample_infos()))) { LOG_WARN("failed to append exprs", K(ret)); } else if (OB_FAIL(ObTransformUtils::pull_up_subquery(parent_stmt, child_stmt))) { LOG_WARN("failed to pull up subquery", K(ret)); } else if (OB_FAIL(parent_stmt->remove_from_item(table_item->table_id_))) { LOG_WARN("failed to remove from item", K(ret)); } else if (OB_FAIL(ObTransformUtils::adjust_pseudo_column_like_exprs(*parent_stmt))) { LOG_WARN("failed to adjust pseudo column like exprs", K(ret)); } else if (OB_FAIL(parent_stmt->rebuild_tables_hash())) { LOG_WARN("failed to rebuild table hash", K(ret)); } else if (OB_FAIL(parent_stmt->update_column_item_rel_id())) { LOG_WARN("failed to update column item rel id", K(ret)); } else { /*do nothing*/ } } return ret; } int ObTransformViewMerge::adjust_updatable_view(ObDMLStmt *parent_stmt, TableItem *table_item) { int ret = OB_SUCCESS; if (OB_ISNULL(parent_stmt) || OB_ISNULL(table_item)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(parent_stmt), K(table_item), K(ret)); } else if (OB_UNLIKELY(!table_item->is_generated_table())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected table type", K(table_item->type_), K(ret)); } else if (parent_stmt->is_update_stmt() && OB_FAIL(static_cast(parent_stmt)->remove_invalid_assignment())) { LOG_WARN("failed to remove invalid assignment", K(ret)); } else if (parent_stmt->is_delete_stmt() || parent_stmt->is_update_stmt()) { ObDelUpdStmt *del_upd_stmt = static_cast(parent_stmt); ObSEArray table_infos; if (OB_FAIL(del_upd_stmt->get_dml_table_infos(table_infos))) { LOG_WARN("failed to get dml table infos", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < table_infos.count(); ++i) { ObDmlTableInfo* table_info = table_infos.at(i); if (OB_ISNULL(table_info)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get null table info", K(ret)); } else if (table_info->table_id_ == table_item->table_id_) { if (OB_UNLIKELY(table_info->column_exprs_.empty()) || OB_ISNULL(table_info->column_exprs_.at(0))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("index dml info is invalid", K(ret), KPC(table_info)); } else { table_info->table_id_ = table_info->column_exprs_.at(0)->get_table_id(); } } } } return ret; } int ObTransformViewMerge::replace_stmt_exprs(ObDMLStmt *parent_stmt, ObSelectStmt *child_stmt, TableItem *table_item, ViewMergeHelper &helper, bool need_wrap_case_when) { int ret = OB_SUCCESS; ObSEArray old_column_exprs; ObSEArray new_column_exprs; ObSEArray temp_exprs; ObStmtExprReplacer replacer; replacer.set_recursive(false); if (OB_ISNULL(parent_stmt) || OB_ISNULL(child_stmt) || OB_ISNULL(table_item)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (OB_FAIL(parent_stmt->get_column_exprs(table_item->table_id_, temp_exprs))) { LOG_WARN("failed to get column exprs", K(ret)); } else if (OB_FAIL(append(old_column_exprs, temp_exprs))) { LOG_WARN("failed to append exprs", K(ret)); } else if (OB_FAIL(ObTransformUtils::convert_column_expr_to_select_expr(old_column_exprs, *child_stmt, new_column_exprs))) { LOG_WARN("failed to convert column expr to select expr", K(ret)); } else if (need_wrap_case_when && OB_FAIL(wrap_case_when_if_necessary(*child_stmt, helper, new_column_exprs))) { LOG_WARN("failed to wrap case when is necessary", K(ret)); } else if (OB_FAIL(parent_stmt->remove_table_info(table_item))) { LOG_WARN("failed to remove table item", K(ret)); } else if (OB_FAIL(replacer.add_replace_exprs(old_column_exprs, new_column_exprs))) { LOG_WARN("failed to add replace exprs", K(ret)); } else if (OB_FAIL(parent_stmt->iterate_stmt_expr(replacer))) { LOG_WARN("failed to replace stmt expr", K(ret)); } return ret; } int ObTransformViewMerge::wrap_case_when_if_necessary(ObSelectStmt &child_stmt, ViewMergeHelper &helper, ObIArray &exprs) { int ret = OB_SUCCESS; ObSEArray columns; ObSqlBitSet<> from_tables; ObSEArray column_exprs; if (OB_FAIL(child_stmt.get_from_tables(from_tables))) { LOG_WARN("failed to get from tables", K(ret)); } else if (OB_FAIL(child_stmt.get_column_exprs(columns))) { LOG_WARN("failed to get column exprs", K(ret)); } else if (OB_FAIL(ObTransformUtils::extract_table_exprs(child_stmt, columns, from_tables, column_exprs))) { LOG_WARN("failed to extract table exprs", K(ret)); } else { for (int64_t i = 0; OB_SUCC(ret) && i < exprs.count(); i++) { bool is_null_propagate = false; if (OB_ISNULL(exprs.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("NULL expr", K(ret)); } else if (OB_FAIL(ObTransformUtils::is_null_propagate_expr(exprs.at(i), column_exprs, is_null_propagate))) { LOG_WARN("failed to is null propagate expr", K(ret)); } else if (is_null_propagate) { //do nothing } else if (OB_FAIL(wrap_case_when(child_stmt, helper.not_null_column, exprs.at(i)))) { LOG_WARN("failed to wrap case when", K(ret)); } else { //do nothing } } } return ret; } int ObTransformViewMerge::wrap_case_when(ObSelectStmt &child_stmt, ObRawExpr *not_null_column, ObRawExpr *&expr) { int ret = OB_SUCCESS; if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("ctx is null", K(ctx_), K(ret)); } else if (OB_ISNULL(not_null_column)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpect null column expr", K(ret)); } else { ObRawExpr *null_expr = NULL; ObRawExpr *cast_expr = NULL; ObRawExpr *case_when_expr = NULL; ObRawExprFactory *factory = ctx_->expr_factory_; if (OB_FAIL(ObRawExprUtils::build_null_expr(*factory, null_expr))) { LOG_WARN("failed to build null expr", K(ret)); } else if (OB_ISNULL(null_expr) || OB_ISNULL(expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpect null expr", K(ret)); } else if (OB_FAIL(ObRawExprUtils::try_add_cast_expr_above( ctx_->expr_factory_, ctx_->session_info_, *null_expr, expr->get_result_type(), cast_expr))) { LOG_WARN("try add cast expr above failed", K(ret)); } else if (OB_FAIL(ObTransformUtils::build_case_when_expr(child_stmt, not_null_column, expr, cast_expr, case_when_expr, ctx_))) { LOG_WARN("failed to build case when expr", K(ret)); } else if (OB_ISNULL(case_when_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("case expr is null", K(ret), K(case_when_expr)); } else if (OB_FAIL(ObRawExprUtils::try_add_cast_expr_above(ctx_->expr_factory_, ctx_->session_info_, *case_when_expr, expr->get_result_type(), expr))) { LOG_WARN("failed to add cast expr above", K(ret)); } } return ret; } int ObTransformViewMerge::adjust_stmt_semi_infos(ObDMLStmt *parent_stmt, ObSelectStmt *child_stmt, uint64_t table_id) { int ret = OB_SUCCESS; if (OB_ISNULL(parent_stmt) || OB_ISNULL(child_stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(parent_stmt), K(child_stmt), K(ret)); } else { ObSEArray table_ids; bool removed = false; for (int64_t i = 0; OB_SUCC(ret) && i < child_stmt->get_from_item_size(); ++i) { FromItem &from = child_stmt->get_from_item(i); if (from.is_joined_) { JoinedTable *table = child_stmt->get_joined_table(from.table_id_); if (OB_ISNULL(table)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("failed to get joined table", K(ret)); } else if (OB_FAIL(append(table_ids, table->single_table_ids_))) { LOG_WARN("failed to get joined table", K(ret)); } } else if (OB_FAIL(table_ids.push_back(from.table_id_))) { LOG_WARN("failed to push back table id", K(ret)); } } for (int64_t i = 0; OB_SUCC(ret) && i < parent_stmt->get_semi_info_size(); i++) { SemiInfo *semi_info = parent_stmt->get_semi_infos().at(i); if (OB_ISNULL(semi_info)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (OB_FAIL(ObOptimizerUtil::remove_item(semi_info->left_table_ids_, table_id, &removed))) { LOG_WARN("failed to remove item", K(ret)); } else if (!removed) { // do nothing } else if (OB_FAIL(append(semi_info->left_table_ids_, table_ids))) { LOG_WARN("failed append table id", K(ret)); } } } return ret; } int ObTransformViewMerge::construct_transform_hint(ObDMLStmt &stmt, void *trans_params) { int ret = OB_SUCCESS; ObIArray *merged_stmts = NULL; UNUSED(stmt); if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->allocator_) || OB_ISNULL(trans_params) || OB_ISNULL(merged_stmts = static_cast*>(trans_params)) || OB_UNLIKELY(merged_stmts->empty())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(ctx_), K(trans_params), K(merged_stmts)); } else { ObViewMergeHint *hint = NULL; ObDMLStmt *child_stmt = NULL; ObString child_qb_name; const ObViewMergeHint *myhint = NULL; for (int64_t i = 0; OB_SUCC(ret) && i < merged_stmts->count(); ++i) { if (OB_ISNULL(child_stmt = merged_stmts->at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(child_stmt)); } else if (OB_FAIL(ObQueryHint::create_hint(ctx_->allocator_, T_MERGE_HINT, hint))) { LOG_WARN("failed to create hint", K(ret)); } else if (OB_FAIL(child_stmt->get_qb_name(child_qb_name))) { LOG_WARN("failed to get qb name", K(ret), K(child_stmt->get_stmt_id())); } else if (OB_FAIL(ctx_->add_src_hash_val(child_qb_name))) { LOG_WARN("failed to add src hash val", K(ret)); } else if (OB_FAIL(ctx_->outline_trans_hints_.push_back(hint))) { LOG_WARN("failed to push back hint", K(ret)); } else if (NULL != (myhint = static_cast(get_hint(child_stmt->get_stmt_hint()))) && myhint->enable_view_merge(ctx_->src_qb_name_) && OB_FAIL(ctx_->add_used_trans_hint(myhint))) { LOG_WARN("failed to add used trans hint", K(ret)); } else { hint->set_qb_name(child_qb_name); hint->set_parent_qb_name(ctx_->src_qb_name_); } } } return ret; } }//namespace sql }//namespqce oceanbase