/** * 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_transform_aggr_subquery.h" #include "lib/allocator/ob_allocator.h" #include "lib/oblog/ob_log_module.h" #include "share/ob_errno.h" #include "common/ob_common_utility.h" #include "common/ob_smart_call.h" #include "ob_transformer_impl.h" #include "sql/parser/ob_item_type.h" #include "sql/resolver/expr/ob_raw_expr.h" #include "sql/resolver/expr/ob_raw_expr_util.h" #include "sql/resolver/dml/ob_dml_stmt.h" #include "sql/optimizer/ob_optimizer_util.h" #include "sql/ob_sql_context.h" #include "sql/resolver/ob_resolver_utils.h" #include "sql/rewrite/ob_transform_utils.h" #include "sql/resolver/dml/ob_update_stmt.h" using namespace oceanbase::common; using namespace oceanbase::sql; /** * select * from A where c1 > (select min(B.c1) from B where A.c2 = B.c2); * => select * from A, (select min(A.c1) as x, B.c2 as y from B group by B.c2) v where c1 > x and c2 = y; * * select * from A where c1 > (select count(B.c1) from B where A.c2 = B.c2); * => select * from A left join * (select count(B.c1) as x, B.c2 as y from B group by B.c2) v on c2 = y * where c1 > (case when y is not null then x else 0 end); * select A.c1, (select sum(B.c2) from B where A.c1 = B.c1 ) as sum_e from A where A.c1 > 0; * => select A.c1, temp.sum_e from B left join * (select A.c1, sum(B.c2) as sum_e from B group by B.c1) temp on A.c1 = temp.c1 * where A.c1 > 0; * * select A.c1, (select count(B.c2) from B where A.c1 = B.c2) as sum_e from A where A.c1 > 0; * => select A.c1, (case when y is not null then temp.sum_e else 0 end) from B left join * (select A.c1 as y, sum(B.c2) as sum_e from B group by B.c1) temp on A.c1 = temp.c1 * where A.c1 > 0; */ int ObTransformAggrSubquery::transform_one_stmt( common::ObIArray& parent_stmts, ObDMLStmt*& stmt, bool& trans_happened) { int ret = OB_SUCCESS; UNUSED(parent_stmts); bool aggr_first_happened = false; bool join_first_happened = false; trans_happened = false; /** * transform_with_aggregation_first unnest a subquery by transform the subquery as view * with aggregation, then join origin tables with view. * * transform_with_join_first unnest a subquery by join subquery's table with origin tables * first, then aggregate on origin tables' unique set. */ if (OB_FAIL(extract_no_rewrite_select_exprs(stmt))) { LOG_WARN("failed to extract no rewrite select exprs", K(ret)); } else if (OB_FAIL(transform_with_aggregation_first(stmt, aggr_first_happened))) { LOG_WARN("failed to transfrom with aggregation first", K(ret)); } else if (OB_FAIL(transform_with_join_first(stmt, join_first_happened))) { LOG_WARN("failed to transform stmt with join first ja", K(ret)); } else { trans_happened = aggr_first_happened | join_first_happened; } return ret; } int ObTransformAggrSubquery::transform_with_aggregation_first(ObDMLStmt*& stmt, bool& trans_happened) { int ret = OB_SUCCESS; ObSEArray exprs; const bool with_vector_assgin = true; if (OB_ISNULL(stmt)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid params", K(ret), K(stmt)); } else if (stmt->is_hierarchical_query() || stmt->is_set_stmt() || !stmt->is_sel_del_upd()) { // do nothing } else if (OB_FAIL(exprs.assign(stmt->get_condition_exprs()))) { LOG_WARN("failed to assign conditions", K(ret)); } else if (OB_FAIL(ObTransformUtils::get_post_join_exprs(stmt, exprs, with_vector_assgin))) { LOG_WARN("failed to get post join exprs", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < exprs.count(); ++i) { if (OB_FAIL(do_aggr_first_transform(stmt, exprs.at(i), trans_happened))) { LOG_WARN("failed to transform one expr", K(ret)); } } return ret; } /** * @brief ObTransformAggrSubquery::do_aggr_first_transform * STEP 1: find a valid subquery * STEP 2: check the type of subquery, and choose the rewrite way (OUTER/INNER JOIN) * STEP 3: do the transformation * @return */ int ObTransformAggrSubquery::do_aggr_first_transform(ObDMLStmt*& stmt, ObRawExpr* expr, bool& trans_happened) { int ret = OB_SUCCESS; ObSEArray transform_params; ObSEArray filters; if (OB_ISNULL(expr) || OB_ISNULL(stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("condition is null", K(ret), K(expr), K(stmt)); } else if (OB_FAIL(gather_transform_params(expr, expr, AGGR_FIRST, transform_params))) { LOG_WARN("failed to check the condition is valid for transformation", K(ret)); } else if (OB_FAIL(get_filters(*stmt, expr, filters))) { LOG_WARN("failed to get filters for the expr", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < transform_params.count(); ++i) { TransformParam& trans_param = transform_params.at(i); ObQueryRefRawExpr* query_ref = NULL; ObSelectStmt* subquery = NULL; if (OB_ISNULL(query_ref = trans_param.ja_query_ref_) || OB_ISNULL(subquery = trans_param.ja_query_ref_->get_ref_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid transform params", K(ret), K(trans_param.ja_query_ref_)); } else if (!ObOptimizerUtil::find_item(stmt->get_subquery_exprs(), query_ref)) { // skip, the subquery has been pullup } else if (OB_FAIL(choose_pullup_method(filters, trans_param))) { LOG_WARN("failed to choose pullup method", K(ret)); } else if (OB_FAIL(fill_query_refs(stmt, expr, trans_param))) { LOG_WARN("failed to fill query refs", K(ret)); } else if (OB_FAIL(transform_child_stmt(stmt, *subquery, trans_param))) { LOG_WARN("failed to transform subquery", K(ret)); } else if (OB_FAIL(transform_upper_stmt(*stmt, trans_param))) { LOG_WARN("failed to transform upper stmt", K(ret)); } else { trans_happened = true; } } return ret; } /** * find valid JA-subquery in the root expr and select item expr. **/ int ObTransformAggrSubquery::gather_transform_params( ObRawExpr* root_expr, ObRawExpr* child_expr, int64_t pullup_strategy, ObIArray& transform_params) { int ret = OB_SUCCESS; if (OB_ISNULL(child_expr) || OB_ISNULL(root_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("expr is null", K(ret), K(child_expr), K(root_expr)); } else if (IS_SUBQUERY_COMPARISON_OP(child_expr->get_expr_type()) || !child_expr->has_flag(CNT_SUB_QUERY)) { // do nothing } else if (child_expr->is_query_ref_expr()) { TransformParam trans_param(pullup_strategy); trans_param.ja_query_ref_ = static_cast(child_expr); ObSelectStmt* subquery = trans_param.ja_query_ref_->get_ref_stmt(); bool is_valid = false; if (ObOptimizerUtil::find_item(no_rewrite_exprs_, child_expr)) { LOG_TRACE("subquery in select expr and can use index"); } else if (aggr_first(pullup_strategy)) { if (OB_FAIL(check_subquery_validity( subquery, root_expr->has_flag(CNT_ALIAS), trans_param.nested_conditions_, is_valid))) { LOG_WARN("failed to check subquery validity for aggr first", K(ret)); } } else if (join_first(pullup_strategy)) { if (OB_FAIL(check_subquery_validity( subquery, root_expr->has_flag(CNT_ALIAS), is_exists_op(root_expr->get_expr_type()), is_valid))) { LOG_WARN("failed to check subquery validity for join first", K(ret)); } } if (OB_SUCC(ret) && is_valid) { if (OB_FAIL(transform_params.push_back(trans_param))) { LOG_WARN("failed to push back transform parameters", K(ret)); } } } else { // join-first ja can rewrite exists predicate (must be a root relation expr in where/having) bool is_exists_filter = is_exists_op(child_expr->get_expr_type()) && root_expr == child_expr && join_first(pullup_strategy); if (is_exists_filter || !is_exists_op(child_expr->get_expr_type())) { for (int64_t i = 0; OB_SUCC(ret) && i < child_expr->get_param_count(); ++i) { if (OB_FAIL(SMART_CALL(gather_transform_params( root_expr, child_expr->get_param_expr(i), pullup_strategy, transform_params)))) { LOG_WARN("failed to gather transform params", K(ret)); } } } } return ret; } /** * @brief ObTransformAggrSubquery::check_subquery_validity * check the valiaidty of the subquery */ int ObTransformAggrSubquery::check_subquery_validity( ObSelectStmt* subquery, const bool vector_assign, ObIArray& nested_conditions, bool& is_valid) { int ret = OB_SUCCESS; bool has_rownum = false; bool has_ref_assign_user_var = false; nested_conditions.reuse(); is_valid = true; // 1. check stmt components if (OB_ISNULL(subquery)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("subquery is null", K(ret)); } else if (subquery->get_stmt_hint().enable_no_unnest()) { is_valid = false; LOG_TRACE("has no unnest hint.", K(is_valid)); } else if (subquery->has_rollup() || subquery->has_having() || subquery->has_limit() || subquery->has_window_function() || subquery->has_sequence() || subquery->has_set_op() || subquery->is_hierarchical_query()) { is_valid = false; LOG_TRACE("invalid subquery", K(is_valid), K(*subquery)); } else if (OB_FAIL(is_valid_group_by(*subquery, is_valid))) { LOG_WARN("failed to check is single set query", K(ret)); } else if (!is_valid) { // do nothing } else if (OB_FAIL(subquery->has_rownum(has_rownum))) { LOG_WARN("failed to check subquery has rownum", K(ret)); } else if (has_rownum) { is_valid = false; LOG_TRACE("has rownum expr", K(is_valid)); } else if (OB_FAIL(check_subquery_aggr_item(*subquery, is_valid))) { LOG_WARN("failed to check subquery select item", K(ret)); } else if (!is_valid) { // do nothing } else if (OB_FAIL(subquery->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) { is_valid = false; LOG_TRACE("has assignment ref user variable", K(is_valid)); // 2. check the select item } else if (!vector_assign && subquery->get_select_item_size() > 1) { is_valid = false; LOG_TRACE("select item size is invalid", K(vector_assign), K(subquery->get_select_item_size())); } else if (OB_FAIL(check_subquery_select(*subquery, is_valid))) { LOG_WARN("failed to check select validity", K(ret)); } else if (!is_valid) { // do nothing LOG_TRACE("select list is invalid", K(is_valid)); // 3. check from list is not correlated } else if (OB_FAIL(check_subquery_table_item(*subquery, is_valid))) { LOG_WARN("failed to check subquery table item", K(ret)); } else if (!is_valid) { // do nothing // 4. check correlated join on contiditons } else if (OB_FAIL(check_subquery_on_conditions(*subquery, is_valid))) { LOG_WARN("failed to check subquery on conditions", K(ret)); } else if (!is_valid) { // do nothing // 5. check correlated semi contiditons } else if (OB_FAIL(check_subquery_semi_conditions(*subquery, is_valid))) { LOG_WARN("failed to check subquery semi conditions", K(ret)); } else if (!is_valid) { // do nothing // 6. check correlated join contiditons } else if (OB_FAIL(check_subquery_conditions(*subquery, nested_conditions, is_valid))) { LOG_WARN("failed to check subquery conditions", K(ret)); } return ret; } int ObTransformAggrSubquery::check_subquery_select(ObSelectStmt& subquery, bool& is_valid) { int ret = OB_SUCCESS; for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery.get_select_item_size(); ++i) { bool is_correlated = false; ObRawExpr* select_expr = NULL; if (OB_ISNULL(select_expr = subquery.get_select_item(0).expr_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("select expr is null", K(ret)); } else if (OB_FAIL(ObTransformUtils::is_correlated_expr( select_expr, subquery.get_current_level() - 1, is_correlated))) { LOG_WARN("failed to check is correlated expr", K(ret)); } else if (is_correlated || select_expr->has_flag(CNT_SUB_QUERY)) { // if we need add_case_when_expr for the select output, // in this case, we are required to build case when expr, // but we have no idea the value returned by a query_ref_expr // when the subquery's where clause is always false is_valid = false; } } return ret; } int ObTransformAggrSubquery::check_subquery_table_item(const ObSelectStmt& subquery, bool& is_valid) { int ret = OB_SUCCESS; bool is_correlated = false; int32_t stmt_level = subquery.get_current_level(); for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery.get_table_size(); ++i) { const TableItem* item; if (OB_ISNULL(item = subquery.get_table_item(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("table item is null", K(ret)); } else if (!item->is_generated_table()) { // do nothing } else if (OB_FAIL(ObTransformUtils::is_ref_outer_block_relation(item->ref_query_, stmt_level, is_correlated))) { LOG_WARN("failed to check the ref table is correlated", K(ret)); } else if (is_correlated) { is_valid = false; LOG_TRACE("from view is corelated with upper stmt", K(is_valid), K(*item->ref_query_)); } } return ret; } int ObTransformAggrSubquery::check_subquery_conditions( ObSelectStmt& subquery, ObIArray& nested_conds, bool& is_valid) { int ret = OB_SUCCESS; bool is_correlated = false; is_valid = true; for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery.get_condition_size(); ++i) { ObRawExpr* cond = NULL; if (OB_ISNULL(cond = subquery.get_condition_expr(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("condition expr is null", K(ret)); } else if (OB_FAIL(ObTransformUtils::is_correlated_expr(cond, subquery.get_current_level() - 1, is_correlated))) { LOG_WARN("failed to check is correlated condition", K(ret)); } else if (!is_correlated) { // do nothing } else if (!cond->get_expr_levels().has_member(subquery.get_current_level() - 1) || !cond->get_expr_levels().has_member(subquery.get_current_level()) || cond->has_flag(CNT_SUB_QUERY)) { is_valid = false; } else if (OB_FAIL(ObTransformUtils::is_equal_correlation(cond, subquery.get_current_level(), is_valid))) { LOG_WARN("failed to check is equal correlation", K(ret)); } else if (!is_valid) { // do nothing } else if (OB_FAIL(nested_conds.push_back(cond))) { LOG_WARN("failed to push back nested conditions", K(ret)); } } return ret; } /** * @brief check the select item, and the subquery comparison operator * determine the join method * filters: filters on the ja query refs **/ int ObTransformAggrSubquery::choose_pullup_method(ObIArray& filters, TransformParam& trans_param) { int ret = OB_SUCCESS; ObQueryRefRawExpr* query_ref = trans_param.ja_query_ref_; ObIArray& is_null_prop = trans_param.is_null_prop_; ObSelectStmt* subquery = NULL; ObSEArray vars; bool is_null_propagate = true; bool is_null_reject = false; bool is_scalar_aggr = false; if (OB_ISNULL(query_ref) || OB_ISNULL(subquery = query_ref->get_ref_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("params are invalid", K(ret), K(query_ref), K(subquery)); } else if (OB_FAIL(is_null_prop.prepare_allocate(subquery->get_select_item_size()))) { LOG_WARN("failed to prepare allocate case when array", K(ret)); } else { is_scalar_aggr = subquery->is_scala_group_by(); } // 1. scalar group by must return one row // if join result is empty, we need deduce results for the scalar aggr items // if join result is not empty, the scalar aggr items can be computed normally // 2. normal group by, return zero or one row // if return zero, the main query gots null from the subquery // if return one, the main query gots real values from the subquery for (int64_t i = 0; OB_SUCC(ret) && i < subquery->get_select_item_size(); ++i) { ObRawExpr* expr = NULL; is_null_prop.at(i) = false; if (!is_scalar_aggr) { is_null_prop.at(i) = true; } else if (OB_ISNULL(expr = subquery->get_select_item(i).expr_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("select expr is null", K(ret), K(expr)); } else if (OB_FAIL(extract_nullable_exprs(expr, vars))) { LOG_WARN("failed to extract nullable exprs", K(ret)); } else if (vars.count() <= 0) { // do nothing } else if (OB_FAIL(ObTransformUtils::is_null_propagate_expr(expr, vars, is_null_prop.at(i)))) { LOG_WARN("failed to check is null propagate expr", K(ret)); } if (OB_SUCC(ret) && !is_null_prop.at(i)) { is_null_propagate = false; } } if (OB_SUCC(ret) && is_null_propagate) { if (OB_FAIL(ObTransformUtils::has_null_reject_condition(filters, query_ref, is_null_reject))) { LOG_WARN("failed to check has null reject condition", K(ret)); } } if (OB_SUCC(ret)) { if (aggr_first(trans_param.pullup_flag_) && trans_param.nested_conditions_.empty() && is_scalar_aggr) { // use inner join for non-correlated subquery } else if (!is_null_propagate || !is_null_reject) { trans_param.pullup_flag_ |= USE_OUTER_JOIN; } } return ret; } int ObTransformAggrSubquery::transform_child_stmt(ObDMLStmt* stmt, ObSelectStmt& subquery, TransformParam& param) { int ret = OB_SUCCESS; ObIArray& nested_conditions = param.nested_conditions_; ObSEArray old_group_exprs; if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->allocator_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("transform context is invalid", K(ret), K(ctx_)); } else if (OB_FAIL(old_group_exprs.assign(subquery.get_group_exprs()))) { LOG_WARN("failed to assign group exprs", K(ret)); } else { subquery.get_group_exprs().reset(); } for (int64_t i = 0; OB_SUCC(ret) && i < nested_conditions.count(); ++i) { ObRawExpr* cond_expr = nested_conditions.at(i); bool left_is_correlated = false; if (OB_ISNULL(cond_expr) || OB_UNLIKELY(cond_expr->get_expr_type() != T_OP_EQ) || OB_ISNULL(cond_expr->get_param_expr(0)) || OB_ISNULL(cond_expr->get_param_expr(1))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("nested expr is invalid", K(ret), K(cond_expr)); } else if (OB_FAIL(ObOptimizerUtil::remove_item(subquery.get_condition_exprs(), cond_expr))) { LOG_WARN("failed to remove expr", K(ret)); } else if (OB_FAIL(ObTransformUtils::is_correlated_expr( cond_expr->get_param_expr(0), subquery.get_current_level() - 1, left_is_correlated))) { LOG_WARN("failed to check is correlated expr", K(ret)); } else { // construct a pullup condition from a nested condition int64_t inner_param_id = left_is_correlated ? 1 : 0; ObRawExpr* group_expr = cond_expr->get_param_expr(inner_param_id); if (group_expr->has_flag(IS_CONST)) { // do nothing for const expr } else if (ObOptimizerUtil::find_equal_expr(subquery.get_group_exprs(), group_expr)) { // do nothing } else if (OB_FAIL(subquery.add_group_expr(group_expr))) { LOG_WARN("failed to add group expr", K(ret)); } else if (OB_FAIL(ObTransformUtils::create_select_item(*ctx_->allocator_, group_expr, &subquery))) { LOG_WARN("failed to create select item", K(ret)); } else if (param.not_null_expr_ == NULL) { param.not_null_expr_ = group_expr; } } } if (OB_SUCC(ret) && subquery.get_group_exprs().empty() && !old_group_exprs.empty()) { if (OB_FAIL(subquery.get_group_exprs().assign(old_group_exprs))) { /// select * from t1 where c1 > (select count(d1) from t2 group by 1.0); /// => select * from t1, (select count(d1) as aggr from t2 group by 1.0) V where c1 > V.aggr LOG_WARN("failed to assign group exprs", K(ret)); } } if (OB_SUCC(ret)) { if (OB_FAIL(subquery.pullup_stmt_level())) { LOG_WARN("failed to pullup stmt level", K(ret)); } else if (OB_FAIL(subquery.adjust_view_parent_namespace_stmt(stmt->get_parent_namespace_stmt()))) { LOG_WARN("failed to adjust view parent namespace stmt", K(ret)); } else if (OB_FAIL(subquery.formalize_stmt(ctx_->session_info_))) { LOG_WARN("failed to formalize subquery", K(ret)); } } return ret; } /** * @brief ObTransformAggrSubquery::transform_upper_stmt * 1. unnest the subquery, add it into the main query as a generated table * 2. given the output of generated table, deduce the results of the subquery, * and replace the subquery expr with its results * 3. given the correlated conditions, use proper join method * @return */ int ObTransformAggrSubquery::transform_upper_stmt(ObDMLStmt& stmt, TransformParam& param) { int ret = OB_SUCCESS; TableItem* view_table = NULL; ObSEArray view_columns; ObSEArray select_exprs; ObSEArray real_values; ObQueryRefRawExpr* query_expr = param.ja_query_ref_; ObIArray& pullup_conds = param.nested_conditions_; ObSelectStmt* subquery = NULL; int64_t idx = OB_INVALID_INDEX; if (OB_ISNULL(ctx_) || OB_ISNULL(query_expr) || OB_ISNULL(subquery = query_expr->get_ref_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("params are invalid", K(ret), K(ctx_), K(subquery)); } else if (OB_FAIL(ObOptimizerUtil::remove_item(stmt.get_subquery_exprs(), query_expr))) { LOG_WARN("failed to remove subquery expr", K(ret)); } else if (OB_FAIL(ObTransformUtils::add_new_table_item(ctx_, &stmt, subquery, view_table))) { LOG_WARN("failed to add new table item", K(ret)); } else if (OB_ISNULL(view_table)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("table item is invalid", K(ret)); } else if (OB_FAIL(ObTransformUtils::create_columns_for_view(ctx_, *view_table, &stmt, view_columns))) { LOG_WARN("failed to create columns for view stmt", K(ret)); } else if (OB_FAIL(subquery->get_select_exprs(select_exprs))) { LOG_WARN("failed to get select exprs", K(ret)); } else if (ObOptimizerUtil::find_item(select_exprs, param.not_null_expr_, &idx)) { param.not_null_expr_ = view_columns.at(idx); } if (OB_SUCC(ret)) { if (OB_FAIL(deduce_query_values(stmt, param, select_exprs, view_columns, real_values))) { LOG_WARN("failed to deduce subquery output", K(ret)); } else if (OB_FAIL(stmt.replace_inner_stmt_expr(param.query_refs_, real_values))) { LOG_WARN("failed to replace inner stmt expr", K(ret)); } else if (OB_FAIL(ObTransformUtils::replace_exprs(select_exprs, view_columns, pullup_conds))) { LOG_WARN("failed to replace pullup conditions", K(ret)); } else if (OB_FAIL(transform_from_list(stmt, view_table, pullup_conds, param.pullup_flag_))) { LOG_WARN("failed to transform from list", K(ret)); // 4. post process } else if (OB_FAIL(stmt.formalize_stmt(ctx_->session_info_))) { LOG_WARN("failed to formalize stmt", K(ret)); } else if (stmt.is_update_stmt() && param.query_refs_.count() >= 2) { if (OB_FAIL(static_cast(stmt).check_assign())) { LOG_WARN("failed to check assign", K(ret)); } } } return ret; } int ObTransformAggrSubquery::deduce_query_values(ObDMLStmt& stmt, TransformParam& param, ObIArray& view_select, ObIArray& view_columns, ObIArray& real_values) { int ret = OB_SUCCESS; ObRawExpr* not_null_expr = param.not_null_expr_; ObIArray& is_null_prop = param.is_null_prop_; const bool is_outer_join = use_outer_join(param.pullup_flag_); if (OB_ISNULL(ctx_->session_info_) || OB_UNLIKELY(is_null_prop.count() > view_select.count()) || OB_UNLIKELY(is_null_prop.count() > view_columns.count())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid session or invalid array size", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < is_null_prop.count(); ++i) { // replace_columns_and_aggrs() may change expr result type, e.g.: sum() from ObNumberType // to ObNullType. This may cause operand implicit cast be added twice, so we erase it first. ObRawExpr* default_expr = NULL; if (OB_FAIL(real_values.push_back(view_columns.at(i)))) { LOG_WARN("failed to push back view columns", K(ret)); } else if (is_null_prop.at(i) || !is_outer_join) { continue; } else if (OB_ISNULL(not_null_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("not null expr is null", K(ret)); } else if (OB_FAIL(ObRawExprUtils::copy_expr( *ctx_->expr_factory_, view_select.at(i), default_expr, COPY_REF_DEFAULT))) { LOG_WARN("failed to copy select expr", K(ret)); } else if (OB_FAIL(ObRawExprUtils::erase_operand_implicit_cast(default_expr, default_expr))) { LOG_WARN("remove operand implicit cast failed", K(ret)); } else if (OB_FAIL(replace_columns_and_aggrs(default_expr, ctx_))) { LOG_WARN("failed to replace variables", K(ret)); } if (OB_SUCC(ret) && ctx_->session_info_->use_static_typing_engine()) { // After replace, result type of %default_expr may differ with original expr, // may cause unexpected implicit cast be added too, cast to original expr type first. if (OB_FAIL(default_expr->formalize(ctx_->session_info_))) { LOG_WARN("formalize expr failed", K(ret)); } else if (OB_FAIL(ObRawExprUtils::try_add_cast_expr_above(ctx_->expr_factory_, ctx_->session_info_, *default_expr, view_select.at(i)->get_result_type(), default_expr))) { LOG_WARN("try add cast expr failed", K(ret)); } } if (OB_SUCC(ret)) { if (OB_FAIL(ObTransformUtils::build_case_when_expr( stmt, not_null_expr, view_columns.at(i), default_expr, real_values.at(i), ctx_))) { LOG_WARN("failed to build case when expr", K(ret)); } } } return ret; } int ObTransformAggrSubquery::replace_columns_and_aggrs(ObRawExpr*& expr, ObTransformerCtx* ctx) { int ret = OB_SUCCESS; if (OB_ISNULL(expr) || OB_ISNULL(ctx) || OB_ISNULL(ctx->expr_factory_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("expr is null", K(ret), K(expr), K(ctx)); } else if (expr->get_expr_type() == T_FUN_COUNT) { ObConstRawExpr* const_zero = NULL; if (OB_FAIL(ObTransformUtils::build_const_expr_for_count(*ctx->expr_factory_, 0, const_zero))) { LOG_WARN("failed to replace count with 0", K(ret)); } else { expr = const_zero; } } else if (expr->is_column_ref_expr() || expr->is_aggr_expr()) { // replace with NULL if (OB_FAIL(ObRawExprUtils::build_null_expr(*ctx->expr_factory_, expr))) { LOG_WARN("failed to replace variable with null", K(ret)); } } else if (expr->has_flag(CNT_COLUMN) || expr->has_flag(CNT_AGG)) { for (int64_t i = 0; OB_SUCC(ret) && i < expr->get_param_count(); ++i) { if (OB_FAIL(SMART_CALL(replace_columns_and_aggrs(expr->get_param_expr(i), ctx)))) { LOG_WARN("failed to replace variables for param", K(ret)); } } } return ret; } int ObTransformAggrSubquery::transform_from_list( ObDMLStmt& stmt, TableItem* view_table_item, const ObIArray& joined_conds, const int64_t pullup_flag) { int ret = OB_SUCCESS; if (!use_outer_join(pullup_flag)) { // create inner join for null-reject condition if (OB_FAIL(stmt.add_from_item(view_table_item->table_id_, false))) { LOG_WARN("failed to push back from item", K(ret)); } else if (OB_FAIL(stmt.add_condition_exprs(joined_conds))) { LOG_WARN("failed to add condition exprs", K(ret)); } } else { // create outer join TableItem* joined_table = NULL; if (OB_FAIL(ObTransformUtils::merge_from_items_as_inner_join(ctx_, stmt, joined_table))) { LOG_WARN("failed to merge from items as inner join", K(ret)); } else if (OB_FAIL(ObTransformUtils::add_new_joined_table( ctx_, stmt, LEFT_OUTER_JOIN, joined_table, view_table_item, joined_conds, joined_table))) { LOG_WARN("failed to add new joined table", K(ret)); } else if (OB_ISNULL(joined_table)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("joined table is null", K(ret)); } else if (OB_FAIL(stmt.add_from_item(joined_table->table_id_, true))) { LOG_WARN("failed to add from item", K(ret)); } } return ret; } int ObTransformAggrSubquery::extract_nullable_exprs(const ObRawExpr* expr, ObIArray& vars) { int ret = OB_SUCCESS; if (OB_ISNULL(expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("expr is null", K(ret)); } else if (expr->is_column_ref_expr() || (expr->is_aggr_expr() && expr->get_expr_type() != T_FUN_COUNT)) { if (OB_FAIL(vars.push_back(expr))) { LOG_WARN("failed to push back expr", K(ret)); } } else { for (int64_t i = 0; OB_SUCC(ret) && i < expr->get_param_count(); ++i) { if (OB_FAIL(SMART_CALL(extract_nullable_exprs(expr->get_param_expr(i), vars)))) { LOG_WARN("failed to extract nullable expr", K(ret)); } } } return ret; } /** * @brief ObTransformAggrSubquery::do_join_first_transform * STEP 1: find a valid subquery, choose the transformation method * STEP 2: create a spj from the origin stmt * STEP 3: rewrite the spj * @return */ int ObTransformAggrSubquery::transform_with_join_first(ObDMLStmt*& stmt, bool& trans_happened) { int ret = OB_SUCCESS; bool is_valid = false; int64_t query_num = 0; ObDMLStmt* target_stmt = stmt; if (OB_ISNULL(stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get null stmt", K(ret), K(stmt)); } else if (OB_FAIL(check_stmt_valid(*stmt, is_valid))) { LOG_WARN("failed to check stmt valid", K(ret)); } else if (is_valid) { query_num = stmt->get_subquery_expr_size(); } for (int64_t i = 0; OB_SUCC(ret) && i < query_num; ++i) { TransformParam param; ObSelectStmt* view_stmt = NULL; ObRawExpr* root_expr = NULL; bool post_group_by = false; if (OB_FAIL(get_trans_param(*target_stmt, param, root_expr, post_group_by))) { LOG_WARN("failed to find trans params", K(ret)); } else if (NULL == root_expr) { // should remove alias_ref from target_stmt if it has alias_ref ObSelectStmt *sel_stmt = NULL; if (OB_ISNULL(target_stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null stmt", K(ret)); } else if (target_stmt->is_select_stmt()) { if (OB_FALSE_IT(sel_stmt = static_cast(target_stmt))) { } else if (OB_FAIL(revert_vec_assign_exprs(sel_stmt, stmt))) { LOG_WARN("failed to replace alias exprs", K(ret)); } } break; } else if (OB_FAIL(fill_query_refs(target_stmt, root_expr, param))) { LOG_WARN("failed to fill query refs", K(ret)); } else if (OB_FAIL(get_trans_view(*target_stmt, view_stmt, root_expr, post_group_by))) { LOG_WARN("failed to get transform view", K(ret)); } else if (OB_UNLIKELY(!ObOptimizerUtil::find_item(view_stmt->get_subquery_exprs(), param.ja_query_ref_))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("the subquery is not found in view stmt", K(ret)); } else if (OB_FAIL(do_join_first_transform(*view_stmt, param, root_expr, 0 == i))) { LOG_WARN("failed to do join first transform", K(ret)); } else { target_stmt = view_stmt; trans_happened = true; } } if (OB_SUCC(ret) && trans_happened && stmt->is_update_stmt()) { if (OB_FAIL(static_cast(stmt)->check_assign())) { LOG_WARN("failed to check assign", K(ret)); } } return ret; } /** * @brief ObTransformAggrSubquery::get_trans_param * process subqueries from `where, group by, having, select, update assign` * @return */ int ObTransformAggrSubquery::get_trans_param( ObDMLStmt& stmt, TransformParam& param, ObRawExpr*& root_expr, bool& post_group_by) { int ret = OB_SUCCESS; ObSEArray params; ObSEArray pre_group_by_exprs; // where, group exprs ObSEArray post_group_by_exprs; // having, select, assign values exprs ObSEArray invalid_list; bool has_rownum = false; if (OB_FAIL(pre_group_by_exprs.assign(stmt.get_condition_exprs()))) { LOG_WARN("failed to assign conditions", K(ret)); } else if (OB_FAIL(stmt.has_rownum(has_rownum))) { LOG_WARN("failed to check has rownum", K(ret)); } else if (has_rownum) { // do nothing } else if (stmt.is_select_stmt()) { ObSelectStmt& sel_stmt = static_cast(stmt); if (OB_FAIL(append(pre_group_by_exprs, sel_stmt.get_group_exprs()))) { LOG_WARN("failed to append group by exprs", K(ret)); } else if (OB_FAIL(append(pre_group_by_exprs, sel_stmt.get_aggr_items()))) { LOG_WARN("failed to append aggr items", K(ret)); } else if (sel_stmt.has_rollup() || sel_stmt.is_scala_group_by()) { // do nothing // 1. there is no group by clause, we can directly pullup subqueries // 1. there is normal group by, we can create spj view with group-by pushed down } else if (OB_FAIL(append(post_group_by_exprs, sel_stmt.get_having_exprs()))) { LOG_WARN("failed to append having exprs", K(ret)); } else if (OB_FAIL(sel_stmt.get_select_exprs(post_group_by_exprs))) { LOG_WARN("failed to get select exprs", K(ret)); } } else if (stmt.is_update_stmt()) { ObUpdateStmt& upd_stmt = static_cast(stmt); if (OB_FAIL(upd_stmt.get_assign_values(pre_group_by_exprs))) { LOG_WARN("failed to get assign values", K(ret)); } } int64_t pre_count = pre_group_by_exprs.count(); int64_t post_count = post_group_by_exprs.count(); for (int64_t i = 0; OB_SUCC(ret) && NULL == root_expr && i < pre_count + post_count; ++i) { params.reset(); post_group_by = (i >= pre_count); ObRawExpr* expr = !post_group_by ? pre_group_by_exprs.at(i) : post_group_by_exprs.at(i - pre_count); ObSEArray filters; bool is_filter = check_is_filter(stmt, expr); if (is_exists_op(expr->get_expr_type()) && !is_filter) { // only rewrite [not] exists in where or having } else if (OB_FAIL(gather_transform_params(expr, expr, JOIN_FIRST, params))) { LOG_WARN("failed to gather transform params", K(ret)); } else if (OB_FAIL(get_filters(stmt, expr, filters))) { LOG_WARN("failed to get filters", K(ret)); } for (int64_t j = 0; OB_SUCC(ret) && NULL == root_expr && j < params.count(); ++j) { bool is_valid = true; if (ObOptimizerUtil::find_item(invalid_list, params.at(j).ja_query_ref_)) { is_valid = false; } else if (is_exists_op(expr->get_expr_type())) { if (OB_FAIL(choose_pullup_method_for_exists(params.at(j).ja_query_ref_, params.at(j).pullup_flag_, T_OP_NOT_EXISTS == expr->get_expr_type(), is_valid))) { LOG_WARN("failed to choose pullup method for exists", K(ret)); } } else { if (OB_FAIL(choose_pullup_method(filters, params.at(j)))) { LOG_WARN("failed to choose pullup method", K(ret)); } } if (OB_SUCC(ret) && is_valid && use_outer_join(params.at(j).pullup_flag_)) { if (OB_FAIL(check_can_use_outer_join(params.at(j), is_valid))) { LOG_WARN("failed to check use outer join", K(ret)); } } if (OB_SUCC(ret)) { if (!is_valid) { if (OB_FAIL(invalid_list.push_back(params.at(j).ja_query_ref_))) { LOG_WARN("failed to push back invalid subquery", K(ret)); } } else { root_expr = expr; post_group_by = (i >= pre_count); param = params.at(j); } } } } return ret; } int ObTransformAggrSubquery::get_trans_view( ObDMLStmt& stmt, ObSelectStmt*& view_stmt, ObRawExpr* root_expr, bool post_group_by) { int ret = OB_SUCCESS; bool has_rownum = false; bool has_groupby = false; if (OB_FAIL(stmt.has_rownum(has_rownum))) { LOG_WARN("failed to check has rownum", K(ret)); } else if (stmt.is_select_stmt() && static_cast(stmt).has_group_by()) { has_groupby = true; } if (OB_SUCC(ret)) { TableItem* table = NULL; if (!has_rownum && !has_groupby && stmt.is_select_stmt()) { view_stmt = static_cast(&stmt); } else if (OB_FAIL(ObTransformUtils::create_simple_view(ctx_, &stmt, view_stmt))) { LOG_WARN("failed to create simple view", K(ret)); } else { for (int64_t i = 0; OB_SUCC(ret) && NULL == table && 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->ref_query_ != view_stmt) { table = NULL; } } if (OB_SUCC(ret) && stmt.is_select_stmt()) { if (has_groupby && post_group_by) { if (OB_FAIL(ObTransformUtils::push_down_groupby(ctx_, static_cast(&stmt), table))) { LOG_WARN("failed to push down group by", K(ret)); } else { view_stmt = static_cast(&stmt); } } } if (OB_SUCC(ret) && stmt.is_update_stmt()) { if (root_expr->has_flag(CNT_ALIAS)) { ObAliasRefRawExpr* alias = NULL; if (OB_FAIL(ObRawExprUtils::find_alias_expr(root_expr, alias))) { LOG_WARN("failed to find alias expr", K(ret)); } else if (OB_FAIL(ObTransformUtils::push_down_vector_assign( ctx_, static_cast(&stmt), alias, table))) { LOG_WARN("failed to push down vector query", K(ret)); } } } } } return ret; } int ObTransformAggrSubquery::check_stmt_valid(ObDMLStmt& stmt, bool& is_valid) { int ret = OB_SUCCESS; is_valid = true; bool can_set_unique = false; if (stmt.is_set_stmt() || stmt.is_hierarchical_query() || !stmt.is_sel_del_upd()) { is_valid = false; } else if (OB_FAIL(ObTransformUtils::check_can_set_stmt_unique(&stmt, can_set_unique))) { LOG_WARN("failed to check can set stmt unque", K(ret)); } else if (!can_set_unique) { is_valid = false; } return ret; } /** * @brief ObTransformAggrSubquery::check_subquery_validity * check the valiaidty of the subquery */ int ObTransformAggrSubquery::check_subquery_validity( ObSelectStmt* subquery, const bool is_vector_assign, const bool in_exists, bool& is_valid) { int ret = OB_SUCCESS; bool has_rownum = false; int32_t stmt_level = -1; is_valid = true; ObRawExpr* limit_expr = NULL; bool has_correlated_cond = false; bool has_ref_assign_user_var = false; ObSEArray nested_conditions; // 1. check stmt components if (OB_ISNULL(subquery)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("subquery is null", K(ret)); } else if (subquery->get_stmt_hint().enable_no_unnest()) { is_valid = false; LOG_TRACE("has no unnest hint.", K(is_valid)); } else if (subquery->is_eliminated()) { is_valid = false; LOG_TRACE("subquery is eliminated", K(subquery->is_eliminated())); } else if (subquery->get_group_expr_size() > 0 || subquery->has_rollup() || subquery->has_window_function() || subquery->has_sequence() || subquery->has_set_op() || subquery->is_hierarchical_query()) { is_valid = false; LOG_TRACE("invalid subquery", K(is_valid), K(*subquery)); } else if (!subquery->is_scala_group_by()) { is_valid = false; LOG_TRACE("subquery is not scalar group by", K(is_valid)); } else if (in_exists) { if (!subquery->has_having()) { is_valid = false; LOG_TRACE("invalid [not] exists subquery", K(is_valid), K(*subquery)); } else if (OB_FAIL(check_subquery_having(*subquery, is_valid))) { LOG_WARN("failed to check subquery having", K(ret)); } else if (!is_valid) { LOG_TRACE("invalid having clause", K(is_valid)); } else if (!subquery->has_limit()) { /*do nothing*/ } else if (OB_ISNULL(limit_expr = subquery->get_limit_expr()) || OB_UNLIKELY(T_INT != limit_expr->get_expr_type())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected limit expr", K(ret)); } else if (0 == static_cast(limit_expr)->get_value().get_int()) { is_valid = false; LOG_TRACE("get limit 0 in [not] exists expr", K(is_valid)); } } else { if (subquery->has_having() || subquery->has_limit()) { is_valid = false; LOG_TRACE("invalid subquery", K(is_valid), K(*subquery)); } else if (OB_FAIL(check_subquery_select(*subquery, is_valid))) { LOG_WARN("failed to check subquery select", K(ret)); } } if (OB_FAIL(ret) || !is_valid) { } else if (OB_FAIL(subquery->has_rownum(has_rownum))) { LOG_WARN("failed to check subquery has rownum", K(ret)); } else if (has_rownum) { is_valid = false; LOG_TRACE("has rownum expr", K(is_valid)); } else if (OB_FAIL(subquery->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) { is_valid = false; LOG_TRACE("has assignment ref user variable", K(is_valid)); } else if (!is_vector_assign && !in_exists && subquery->get_select_item_size() > 1) { is_valid = false; LOG_TRACE("select item size is invalid", K(is_vector_assign), K(subquery->get_select_item_size())); // 2. check the aggr item } else if (OB_FAIL(check_subquery_aggr_item(*subquery, is_valid))) { LOG_WARN("failed to check subquery aggregation item", K(ret)); } else if (!is_valid) { // do nothing } else if (OB_FAIL(check_count_const_validity(*subquery, is_valid))) { LOG_WARN("failed to check is valid count const", K(ret)); } else if (!is_valid) { LOG_TRACE("aggr item is invalid", K(is_valid)); // never reach // 3. check from list is not correlated } else if (OB_FAIL(check_subquery_table_item(*subquery, is_valid))) { LOG_WARN("failed to check subquery table item", K(ret)); } else if (!is_valid) { // do nothing // 4. check correlated join on contiditons } else if (OB_FAIL(check_subquery_on_conditions(*subquery, is_valid))) { LOG_WARN("failed to check subquery on conditions", K(ret)); } else if (!is_valid) { // do nothing // 5. check correlated semi contiditons } else if (OB_FAIL(check_subquery_semi_conditions(*subquery, is_valid))) { LOG_WARN("failed to check subquery semi conditions", K(ret)); } else { stmt_level = subquery->get_current_level(); } // 5. check correlated join contiditons for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery->get_condition_size(); ++i) { ObRawExpr* cond = subquery->get_condition_expr(i); if (OB_ISNULL(cond)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("condition expr is null", K(ret)); } else if (IS_COMMON_COMPARISON_OP(cond->get_expr_type()) && T_OP_NSEQ != cond->get_expr_type() && cond->get_expr_levels().has_member(stmt_level - 1) && cond->get_expr_levels().has_member(stmt_level)) { if (cond->has_flag(CNT_SUB_QUERY)) { is_valid = false; LOG_TRACE("is not valid equal or common comparsion correlation", K(i), K(*cond)); } else if (OB_FAIL(nested_conditions.push_back(cond))) { LOG_WARN("failed to push back expr", K(ret)); } else { has_correlated_cond = true; } } } /** * for count(const), transfrom is valid if one of following condition is satisfied * 1. from item only has one basic table * 2. one side of correlated condition only has subquery's expr */ if (!has_correlated_cond) { is_valid = false; LOG_TRACE("no correlated common comparsion condition found", K(is_valid), K(has_correlated_cond)); } return ret; } /** * @brief check the select item, and the subquery comparison operator * determine the join method **/ int ObTransformAggrSubquery::choose_pullup_method_for_exists( ObQueryRefRawExpr* query_ref, int64_t& pullup_flag, const bool with_not, bool& is_valid) { int ret = OB_SUCCESS; ObSelectStmt* subquery = NULL; ObSEArray vars; is_valid = true; if (OB_ISNULL(query_ref) || OB_ISNULL(subquery = query_ref->get_ref_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("params are invalid", K(ret), K(query_ref), K(subquery)); } else if (with_not) { pullup_flag |= USE_OUTER_JOIN; } else { // use outer join if all having expr is not null reject bool all_not_null_reject = true; for (int64_t i = 0; OB_SUCC(ret) && all_not_null_reject && i < subquery->get_having_expr_size(); ++i) { bool is_null_reject = false; vars.reuse(); ObRawExpr* cur_expr = subquery->get_having_exprs().at(i); if (OB_ISNULL(cur_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("having expr is null", K(ret)); } else if (!(IS_COMMON_COMPARISON_OP(cur_expr->get_expr_type()) && T_OP_NSEQ != cur_expr->get_expr_type())) { is_valid = false; } else if (OB_FAIL(extract_nullable_exprs(cur_expr, vars))) { LOG_WARN("failed to extract nullable exprs", K(ret)); } else if (vars.count() <= 0) { // do nothing } else if (OB_FAIL(ObTransformUtils::is_null_reject_condition(cur_expr, vars, is_null_reject))) { LOG_WARN("failed to check is null reject condition", K(ret)); } if (OB_SUCC(ret) && is_null_reject) { all_not_null_reject = false; } } if (OB_SUCC(ret) && all_not_null_reject) { pullup_flag |= USE_OUTER_JOIN; } } return ret; } int ObTransformAggrSubquery::do_join_first_transform( ObSelectStmt& select_stmt, TransformParam& trans_param, ObRawExpr* root_expr, const bool is_first_trans) { int ret = OB_SUCCESS; ObQueryRefRawExpr *query_ref_expr = NULL; ObSelectStmt *subquery = NULL; ObRawExprFactory *expr_factory = NULL; if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->session_info_) || OB_ISNULL(root_expr) || OB_ISNULL(expr_factory = ctx_->expr_factory_) || OB_ISNULL(query_ref_expr = trans_param.ja_query_ref_) || OB_ISNULL(subquery = trans_param.ja_query_ref_->get_ref_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid argument", K(ret), K(ctx_), K(root_expr), K(query_ref_expr), K(subquery)); } else if (OB_FAIL(get_unique_keys(select_stmt, select_stmt.get_group_exprs(), is_first_trans))) { LOG_WARN("failed to get unique exprs", K(ret)); } if (OB_SUCC(ret) && trans_param.not_null_expr_ != NULL) { for (int64_t i = 0; OB_SUCC(ret) && i < subquery->get_aggr_item_size(); ++i) { if (OB_FAIL(replace_count_const(subquery->get_aggr_item(i), trans_param.not_null_expr_))) { LOG_WARN("failed to replace count const expr", K(ret)); } } } if (OB_SUCC(ret)) { if (OB_FAIL(ObOptimizerUtil::remove_item(select_stmt.get_subquery_exprs(), query_ref_expr))) { LOG_WARN("failed to remove query ref expr", K(ret)); } else if (is_exists_op(root_expr->get_expr_type())) { bool need_lnnvl = root_expr->get_expr_type() == T_OP_NOT_EXISTS; if (OB_FAIL(ObOptimizerUtil::remove_item(select_stmt.get_condition_exprs(), root_expr))) { LOG_WARN("failed to remove exprs", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < subquery->get_having_expr_size(); ++i) { ObRawExpr* cond = subquery->get_having_exprs().at(i); if (need_lnnvl && OB_FAIL(ObRawExprUtils::build_lnnvl_expr(*expr_factory, cond, cond))) { LOG_WARN("failed to build lnnvl expr", K(ret)); } else if (OB_FAIL(select_stmt.add_having_expr(cond))) { LOG_WARN("failed to add having condition", K(ret)); } } } else { ObSEArray select_exprs; if (OB_FAIL(subquery->get_select_exprs(select_exprs))) { LOG_WARN("failed to get select exprs", K(ret)); } else if (OB_FAIL(select_stmt.replace_inner_stmt_expr(trans_param.query_refs_, select_exprs))) { LOG_WARN("failed to update query ref value expr", K(ret)); } } } if (OB_SUCC(ret)) { // pullup subquery component subquery->set_eliminated(true); if (OB_FAIL(append(select_stmt.get_table_items(), subquery->get_table_items()))) { LOG_WARN("failed to append table items", K(ret)); } else if (OB_FAIL(transform_from_list(select_stmt, *subquery, trans_param.pullup_flag_))) { LOG_WARN("failed to transform from list", K(ret)); } else if (OB_FAIL(append(select_stmt.get_column_items(), subquery->get_column_items()))) { LOG_WARN("failed to append column items", K(ret)); } else if (OB_FAIL(append(select_stmt.get_part_exprs(), subquery->get_part_exprs()))) { LOG_WARN("failed to append part exprs", K(ret)); } else if (OB_FAIL(append(select_stmt.get_deduced_exprs(), subquery->get_deduced_exprs()))) { LOG_WARN("failed to append deduced exprs", K(ret)); } else if (OB_FAIL(append(select_stmt.get_aggr_items(), subquery->get_aggr_items()))) { LOG_WARN("failed to append aggr items", K(ret)); } else if (OB_FAIL(append(select_stmt.get_subquery_exprs(), subquery->get_subquery_exprs()))) { LOG_WARN("failed to append subquery exprs", K(ret)); } else if (OB_FAIL(subquery->pullup_stmt_level())) { LOG_WARN("failed to pullup subquery", K(ret)); } else if (OB_FAIL(subquery->adjust_view_parent_namespace_stmt(select_stmt.get_parent_namespace_stmt()))) { LOG_WARN("failed to adjust view parent namespace stmt", K(ret)); } else if (OB_FAIL(select_stmt.rebuild_tables_hash())) { LOG_WARN("failed to rebuild table hash", K(ret)); } else if (OB_FAIL(select_stmt.update_column_item_rel_id())) { LOG_WARN("failed to update colun item rel id", K(ret)); } else if (OB_FAIL(append(select_stmt.get_semi_infos(), subquery->get_semi_infos()))) { LOG_WARN("failed to append semi infos", K(ret)); } else if (OB_FAIL(select_stmt.formalize_stmt(ctx_->session_info_))) { LOG_WARN("failed to formalize stmt", K(ret)); } else if (OB_FAIL(rebuild_conditon(select_stmt, *subquery))) { LOG_WARN("failed to rebuild condition", K(ret)); } } return ret; } int ObTransformAggrSubquery::revert_vec_assign_exprs(ObSelectStmt *&child_stmt, ObDMLStmt *&upper_stmt) { int ret = OB_SUCCESS; ObSEArray alias_exprs; ObSEArray col_exprs; TableItem *tab_item = NULL; if (OB_ISNULL(child_stmt) || OB_ISNULL(upper_stmt) || OB_ISNULL(ctx_) || OB_ISNULL(ctx_->session_info_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (upper_stmt->get_table_size() == 0) { } else if (OB_ISNULL(tab_item = upper_stmt->get_table_item(0))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null table item", K(ret)); } else { for (int64_t i = 0; OB_SUCC(ret) && i < child_stmt->get_select_items().count(); ++i) { ObRawExpr *sel_expr = NULL; ObRawExpr *expr = NULL; if (OB_ISNULL(sel_expr = child_stmt->get_select_item(i).expr_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null select expr", K(ret)); } else if (!sel_expr->is_alias_ref_expr()) { } else if (OB_FAIL(alias_exprs.push_back(sel_expr))) { LOG_WARN("failed to push back alias exprs", K(ret)); } else if (OB_ISNULL(expr = upper_stmt->get_column_expr_by_id(tab_item->table_id_, i + OB_APP_MIN_COLUMN_ID))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null column expr", K(ret)); } else if (OB_FAIL(col_exprs.push_back(expr))) { LOG_WARN("failed to push back alias ref", K(ret)); } } // we can not call remove_select_items if col_exprs or alias_exprs is empty // since it unavoidablely adjusts column ids if (OB_FAIL(ret) || col_exprs.empty() || alias_exprs.empty()) { } else if (OB_FAIL(upper_stmt->replace_inner_stmt_expr(col_exprs, alias_exprs))) { LOG_WARN("failed to replace inner stmt expr", K(ret)); } else if (OB_FAIL(ObTransformUtils::remove_select_items( ctx_, tab_item->table_id_, *child_stmt, *upper_stmt, alias_exprs))) { LOG_WARN("failed to remove select items", K(ret)); } else if (OB_FAIL(upper_stmt->formalize_stmt(ctx_->session_info_))) { LOG_WARN("failed to formalize stmt", K(ret)); } } return ret; } /** * 获取from list中所有基表的primary key */ int ObTransformAggrSubquery::get_unique_keys(ObDMLStmt &stmt, ObIArray &pkeys, const bool is_first_trans) { int ret = OB_SUCCESS; ObSqlBitSet<> empty_ignore_tables; TableItem* cur_table = NULL; if (is_first_trans) { if (OB_FAIL(ObTransformUtils::generate_unique_key(ctx_, &stmt, empty_ignore_tables, pkeys))) { LOG_WARN("failed to generate unique key", K(ret)); } } else if (OB_UNLIKELY(1 != stmt.get_table_items().count()) || OB_ISNULL(cur_table = stmt.get_table_item(0)) || OB_UNLIKELY(!cur_table->is_generated_table())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected table item", K(ret), K(stmt.get_table_items().count()), K(cur_table)); } else { // already perform JA rewrite, get group by column from view directly ObSelectStmt* view_stmt = NULL; ObSEArray select_list; ObSEArray column_list; ObSEArray group_keys; if (OB_ISNULL(view_stmt = cur_table->ref_query_) || OB_UNLIKELY(0 == view_stmt->get_group_expr_size())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("view is not a normal group by", K(ret), K(view_stmt)); } else if (OB_FAIL(ObRawExprUtils::copy_exprs( *ctx_->expr_factory_, view_stmt->get_group_exprs(), group_keys, COPY_REF_DEFAULT))) { LOG_WARN("failed to copy exprs", K(ret)); } else if (OB_FAIL(stmt.get_view_output(*cur_table, select_list, column_list))) { LOG_WARN("failed to get view output", K(ret)); } else if (OB_FAIL(ObTransformUtils::replace_exprs(select_list, column_list, group_keys))) { LOG_WARN("failed to replace group keys", K(ret)); } else if (OB_FAIL(append(pkeys, group_keys))) { LOG_WARN("failed to append group keys", K(ret)); } } return ret; } /* * merge from table of parent stmt and subquery stmt * */ int ObTransformAggrSubquery::transform_from_list(ObDMLStmt& stmt, ObSelectStmt& subquery, const int64_t pullup_flag) { int ret = OB_SUCCESS; if (!use_outer_join(pullup_flag)) { // create inner join for null-reject condition if (OB_FAIL(append(stmt.get_joined_tables(), subquery.get_joined_tables()))) { LOG_WARN("failed to append joined tables", K(ret)); } else if (OB_FAIL(append(stmt.get_from_items(), subquery.get_from_items()))) { LOG_WARN("failed to push back from items", K(ret)); } else if (OB_FAIL(append(stmt.get_condition_exprs(), subquery.get_condition_exprs()))) { LOG_WARN("failed to push back condition exprs", K(ret)); } } else { // create outer join TableItem* joined_table_main = NULL; TableItem* joined_table_sub = NULL; if (OB_FAIL(ObTransformUtils::merge_from_items_as_inner_join(ctx_, stmt, joined_table_main))) { LOG_WARN("failed to merge from items as inner join", K(ret)); } else if (OB_FAIL(ObTransformUtils::merge_from_items_as_inner_join(ctx_, subquery, joined_table_sub))) { LOG_WARN("failed to merge from items as inner join", K(ret)); } else if (OB_FAIL(ObTransformUtils::add_new_joined_table(ctx_, stmt, LEFT_OUTER_JOIN, joined_table_main, joined_table_sub, subquery.get_condition_exprs(), joined_table_main))) { LOG_WARN("failed to add new joined table", K(ret)); } else if (OB_ISNULL(joined_table_main)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("joined table is null", K(ret)); } else if (OB_FAIL(stmt.add_from_item(joined_table_main->table_id_, true))) { LOG_WARN("failed to add from item", K(ret)); } } return ret; } int ObTransformAggrSubquery::rebuild_conditon(ObSelectStmt& stmt, ObSelectStmt& subquery) { int ret = OB_SUCCESS; ObSEArray where_conditions; ObIArray* target_array = NULL; if (OB_FAIL(where_conditions.assign(stmt.get_condition_exprs()))) { LOG_WARN("failed to assign where conditions", K(ret)); } stmt.get_condition_exprs().reuse(); for (int64_t i = 0; OB_SUCC(ret) && i < where_conditions.count(); ++i) { ObRawExpr* cur_expr = NULL; target_array = NULL; if (OB_ISNULL(cur_expr = where_conditions.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("condition expr is null", K(ret)); } else if (cur_expr->has_flag(CNT_AGG)) { target_array = &stmt.get_having_exprs(); } else if (cur_expr->has_flag(CNT_SUB_QUERY)) { if (ObOptimizerUtil::find_item(subquery.get_condition_exprs(), cur_expr)) { target_array = &stmt.get_condition_exprs(); } else { target_array = &stmt.get_having_exprs(); } } else { target_array = &stmt.get_condition_exprs(); } if (OB_SUCC(ret) && OB_NOT_NULL(target_array)) { if (OB_FAIL(target_array->push_back(cur_expr))) { LOG_WARN("failed to push back expr", K(ret)); } } } return ret; } int ObTransformAggrSubquery::check_subquery_having(const ObSelectStmt& subquery, bool& is_valid) { int ret = OB_SUCCESS; bool is_correlated = false; is_valid = true; // check having expr valid for [not] exists subquery for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery.get_having_expr_size(); ++i) { const ObRawExpr* cur_expr = subquery.get_having_exprs().at(i); if (OB_ISNULL(cur_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get null expr", K(ret)); } else if (OB_FAIL( ObTransformUtils::is_correlated_expr(cur_expr, subquery.get_current_level() - 1, is_correlated))) { LOG_WARN("failed to check is correlated expr", K(ret)); } else if (is_correlated || !cur_expr->has_flag(CNT_AGG) || cur_expr->has_flag(CNT_SUB_QUERY)) { is_valid = false; LOG_TRACE("invalid select item", K(is_valid)); } } return ret; } int ObTransformAggrSubquery::check_subquery_on_conditions(ObSelectStmt& subquery, bool& is_valid) { int ret = OB_SUCCESS; is_valid = true; ObSEArray on_conditions; if (OB_FAIL(ObTransformUtils::get_on_conditions(subquery, on_conditions))) { LOG_WARN("failed to get on conditions", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < on_conditions.count(); ++i) { bool is_correlated = false; ObRawExpr* cond = NULL; if (OB_ISNULL(cond = on_conditions.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("condition expr is null", K(ret)); } else if (OB_FAIL(ObTransformUtils::is_correlated_expr(cond, subquery.get_current_level() - 1, is_correlated))) { LOG_WARN("failed to check is correlated condition", K(ret)); } else if (is_correlated) { is_valid = false; } } return ret; } int ObTransformAggrSubquery::check_subquery_aggr_item(const ObSelectStmt& subquery, bool& is_valid) { int ret = OB_SUCCESS; is_valid = true; for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery.get_aggr_item_size(); i++) { const ObAggFunRawExpr* agg_expr = subquery.get_aggr_item(i); if (OB_ISNULL(agg_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("aggr expr is null", K(ret)); } else { const ObItemType type = agg_expr->get_expr_type(); is_valid = (type == T_FUN_SUM || type == T_FUN_COUNT || type == T_FUN_MIN || type == T_FUN_MAX); } } return ret; } /** * @brief ObTransformAggrSubquery::check_count_const_validity * @param subquery should not contain const(null) for join-first pullup * @param is_valid * @return */ int ObTransformAggrSubquery::check_count_const_validity(const ObSelectStmt& subquery, bool& is_valid) { int ret = OB_SUCCESS; is_valid = true; for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery.get_aggr_item_size(); i++) { bool is_count_const = false; bool is_const_null = false; const ObAggFunRawExpr* agg_expr = subquery.get_aggr_item(i); if (OB_ISNULL(agg_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("aggr expr is null", K(ret)); } else if (OB_FAIL(is_count_const_expr(agg_expr, is_count_const))) { LOG_WARN("failed to check is count const expr", K(ret)); } else if (!is_count_const || agg_expr->get_param_count() == 0) { // do nothing } else if (OB_FAIL(is_const_null_value(subquery, agg_expr->get_param_expr(0), is_const_null))) { LOG_WARN("failed to check is count null expr", K(ret)); } else if (is_const_null) { is_valid = false; } } return ret; } int ObTransformAggrSubquery::is_count_const_expr(const ObRawExpr* expr, bool& is_count_const) { int ret = OB_SUCCESS; is_count_const = false; if (OB_ISNULL(expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get null expr", K(ret)); } else if (T_FUN_COUNT != expr->get_expr_type()) { // do nothing } else if (0 == expr->get_param_count()) { is_count_const = true; LOG_TRACE("expr is count(*)", K(*expr)); } else if (OB_ISNULL(expr->get_param_expr(0))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get null expr", K(ret)); } else if (!expr->get_param_expr(0)->has_flag(CNT_COLUMN) && !expr->get_param_expr(0)->has_flag(CNT_SUB_QUERY)) { is_count_const = true; LOG_TRACE("expr is count(const)", K(*expr)); } return ret; } int ObTransformAggrSubquery::is_const_null_value( const ObDMLStmt& stmt, const ObRawExpr* param_expr, bool& is_const_null) { int ret = OB_SUCCESS; ObPhysicalPlanCtx* plan_ctx = NULL; is_const_null = true; if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->exec_ctx_) || OB_ISNULL(param_expr) || OB_ISNULL(plan_ctx = ctx_->exec_ctx_->get_physical_plan_ctx())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get null expr", K(ret), K(ctx_), K(plan_ctx)); } else if (!param_expr->is_const_expr()) { // something like: case when 1 then null else 1 end; } else if (T_NULL == param_expr->get_expr_type()) { // do nothing, null expr } else if (T_QUESTIONMARK == param_expr->get_expr_type()) { const ObObj& value = static_cast(param_expr)->get_value(); bool is_pre_param = false; if (ObTransformUtils::is_question_mark_pre_param(stmt, value.get_unknown(), is_pre_param)) { LOG_WARN("failed to check is question mark pre param", K(ret)); } else if (OB_UNLIKELY(!is_pre_param)) { // do nothing pre-calc expr } else if (OB_UNLIKELY(value.get_unknown() < 0 || value.get_unknown() >= plan_ctx->get_param_store().count())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("param index is invalid", K(ret), K(value.get_unknown()), K(plan_ctx->get_param_store().count())); } else if (plan_ctx->get_param_store().at(value.get_unknown()).is_null()) { // do nothing null param expr } else { // not-null param is_const_null = false; } } else { // not-null const is_const_null = false; } return ret; } int ObTransformAggrSubquery::replace_count_const(ObAggFunRawExpr* agg_expr, ObRawExpr* not_null_expr) { int ret = OB_SUCCESS; bool is_count_const = false; if (OB_ISNULL(agg_expr) || OB_ISNULL(not_null_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get null expr", K(ret), K(agg_expr), K(not_null_expr)); } else if (OB_FAIL(is_count_const_expr(agg_expr, is_count_const))) { LOG_WARN("failed to check is count const expr", K(ret)); } else if (!is_count_const) { // do nothing } else if (0 == agg_expr->get_param_count()) { if (OB_FAIL(agg_expr->add_real_param_expr(not_null_expr))) { LOG_WARN("failed to add real param expr", K(ret)); } } else { agg_expr->get_param_expr(0) = not_null_expr; } return ret; } /** * @brief check_can_use_outer_join * check if can use outer join for join fisrt ja rewrite * following case is not valid: * 1. child_stmt has semi join or subquery in where condition * 2. aggr item's parameter is null propagate * 3. if there is count(1), count(*), the subquery should have a not-null expr */ int ObTransformAggrSubquery::check_can_use_outer_join(TransformParam& param, bool& is_valid) { int ret = OB_SUCCESS; ObRawExpr*& not_null_expr = param.not_null_expr_; ObSelectStmt* stmt = NULL; if (OB_ISNULL(param.ja_query_ref_) || OB_ISNULL(stmt = param.ja_query_ref_->get_ref_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid transform param", K(ret)); } else { is_valid = stmt->get_semi_info_size() == 0; } for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < stmt->get_condition_size(); ++i) { if (OB_ISNULL(stmt->get_condition_expr(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("condition expr is null", K(ret)); } else { is_valid = !stmt->get_condition_expr(i)->has_flag(CNT_SUB_QUERY); } } ObSEArray columns; if (OB_SUCC(ret) && is_valid) { ObSEArray tmp; if (OB_FAIL(stmt->get_column_exprs(tmp))) { LOG_WARN("failed to get column exprs", K(ret)); } else if (OB_FAIL(append(columns, tmp))) { LOG_WARN("failed to convert column array", K(ret)); } } for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < stmt->get_aggr_item_size(); ++i) { bool is_count_const = false; ObRawExpr* aggr = stmt->get_aggr_item(i); if (OB_ISNULL(aggr = stmt->get_aggr_item(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("aggregation expr is invalid", K(ret)); } else if (OB_FAIL(is_count_const_expr(aggr, is_count_const))) { LOG_WARN("failed to check is count const expr", K(ret)); } else if (is_count_const) { if (not_null_expr != NULL) { // do nothing } else if (OB_FAIL(ObTransformUtils::find_not_null_expr(*stmt, not_null_expr, is_valid))) { LOG_WARN("failed to find not null expr", K(ret)); } } else { bool is_null_prop = false; if (OB_FAIL(ObTransformUtils::is_null_propagate_expr(aggr->get_param_expr(0), columns, is_null_prop))) { LOG_WARN("failed to check is null propagate expr", K(ret)); } else { is_valid = is_null_prop; } } } return ret; } int ObTransformAggrSubquery::fill_query_refs(ObDMLStmt* stmt, ObRawExpr* expr, TransformParam& param) { int ret = OB_SUCCESS; param.query_refs_.reuse(); if (OB_ISNULL(stmt) || OB_ISNULL(expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("params are invalid", K(ret), K(stmt), K(expr)); } else if (expr->has_flag(CNT_ALIAS)) { if (stmt->is_update_stmt()) { if (OB_FAIL(static_cast(stmt)->get_vector_assign_values(param.ja_query_ref_, param.query_refs_))) { LOG_WARN("failed to get vector assign values", K(ret)); } } else if (stmt->is_select_stmt()) { // select stmt can also contain alias ref exprs ObSEArray sel_exprs; if (OB_FAIL(static_cast(stmt)->get_select_exprs(sel_exprs))) { LOG_WARN("failed to get select exprs", K(ret)); } else { for (int64_t i = 0; OB_SUCC(ret) && i < sel_exprs.count(); ++i) { ObRawExpr *sel_expr = NULL; if (OB_ISNULL(sel_expr = sel_exprs.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null expr", K(ret)); } else if (sel_expr->has_flag(CNT_ALIAS)) { if (OB_FAIL(param.query_refs_.push_back(sel_expr))) { LOG_WARN("failed to push back alias ref expr", K(ret)); } } } } } } else if (OB_FAIL(param.query_refs_.push_back(param.ja_query_ref_))) { LOG_WARN("failed to push back query refs", K(ret)); } return ret; } // 1. scalar group by // 2. group by const expr int ObTransformAggrSubquery::is_valid_group_by(const ObSelectStmt& subquery, bool& is_valid) { int ret = OB_SUCCESS; is_valid = false; if (subquery.is_scala_group_by()) { is_valid = true; } else if (subquery.get_group_expr_size() > 0 && subquery.get_rollup_expr_size() == 0) { // check is group by const is_valid = true; ObSEArray const_exprs; if (OB_FAIL(ObOptimizerUtil::compute_const_exprs( subquery.get_condition_exprs(), subquery.get_current_level(), const_exprs))) { LOG_WARN("failed to compute const exprs", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery.get_group_expr_size(); ++i) { if (ObOptimizerUtil::is_const_expr(subquery.get_group_exprs().at(i), subquery.get_current_level())) { // do nothing } else if (ObOptimizerUtil::find_item(const_exprs, subquery.get_group_exprs().at(i))) { // do nothing } else { is_valid = false; } } } LOG_TRACE("check single set query", K(is_valid), K(subquery.is_scala_group_by())); return ret; } bool ObTransformAggrSubquery::check_is_filter(const ObDMLStmt& stmt, const ObRawExpr* expr) { bool bret = false; bret = ObOptimizerUtil::find_item(stmt.get_condition_exprs(), expr); if (!bret && stmt.is_select_stmt()) { bret = ObOptimizerUtil::find_item(static_cast(stmt).get_having_exprs(), expr); } return bret; } int ObTransformAggrSubquery::get_filters(ObDMLStmt& stmt, ObRawExpr* expr, ObIArray& filters) { int ret = OB_SUCCESS; if (ObOptimizerUtil::find_item(stmt.get_condition_exprs(), expr)) { // is where if (OB_FAIL(filters.assign(stmt.get_condition_exprs()))) { LOG_WARN("failed to assign condition exprs", K(ret)); } } else if (stmt.is_select_stmt()) { ObSelectStmt& sel_stmt = static_cast(stmt); if (ObOptimizerUtil::find_item(sel_stmt.get_group_exprs(), expr) || ObOptimizerUtil::find_item(sel_stmt.get_aggr_items(), expr) || ObOptimizerUtil::find_item(sel_stmt.get_having_exprs(), expr)) { if (OB_FAIL(filters.assign(sel_stmt.get_having_exprs()))) { LOG_WARN("failed to find items", K(ret)); } } } return ret; } /** * given a select subquery, if we can push the correlated condition onto the index, we do not rewrite */ int ObTransformAggrSubquery::extract_no_rewrite_select_exprs(ObDMLStmt*& stmt) { int ret = OB_SUCCESS; if (OB_ISNULL(stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (stmt->is_select_stmt() && stmt->get_subquery_expr_size() > 0) { ObSelectStmt* sel_stmt = static_cast(stmt); for (int64_t i = 0; OB_SUCC(ret) && i < sel_stmt->get_select_item_size(); ++i) { if (OB_FAIL(extract_no_rewrite_expr(sel_stmt->get_select_item(i).expr_))) { LOG_WARN("failed to extract no rewrite expr", K(ret)); } } } return ret; } int ObTransformAggrSubquery::extract_no_rewrite_expr(ObRawExpr* expr) { int ret = OB_SUCCESS; if (OB_ISNULL(expr) || OB_ISNULL(ctx_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (expr->is_query_ref_expr()) { ObSelectStmt* subquery = static_cast(expr)->get_ref_stmt(); bool is_match = false; if (OB_ISNULL(subquery)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (subquery->get_stmt_hint().enable_unnest()) { // do nothing } else if (OB_FAIL(ObTransformUtils::check_subquery_match_index(ctx_, subquery, is_match))) { LOG_WARN("failed to check subquery match index", K(ret)); } else if (!is_match) { // do nothing } else if (OB_FAIL(add_var_to_array_no_dup(no_rewrite_exprs_, expr))) { LOG_WARN("failed to add var to array no dup", K(ret)); } } else if (expr->has_flag(CNT_SUB_QUERY)) { for (int64_t i = 0; OB_SUCC(ret) && i < expr->get_param_count(); ++i) { if (OB_FAIL(extract_no_rewrite_expr(expr->get_param_expr(i)))) { LOG_WARN("failed to extract no rewrite expr", K(ret)); } } } return ret; } int ObTransformAggrSubquery::check_subquery_semi_conditions(ObSelectStmt &subquery, bool &is_valid) { int ret = OB_SUCCESS; is_valid = true; for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery.get_semi_info_size(); ++i) { bool is_correlated = false; SemiInfo *semi_info = subquery.get_semi_infos().at(i); if (OB_ISNULL(semi_info)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else { for (int64_t j = 0; OB_SUCC(ret) && is_valid && j < semi_info->semi_conditions_.count(); ++j) { ObRawExpr *cond = NULL; if (OB_ISNULL(cond = semi_info->semi_conditions_.at(j))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("condition expr is null", K(ret)); } else if (OB_FAIL(ObTransformUtils::is_correlated_expr( cond, subquery.get_current_level() - 1, is_correlated))) { LOG_WARN("failed to check is correlated condition", K(ret)); } else if (is_correlated) { is_valid = false; } } } } return ret; }