3019 lines
138 KiB
C++
3019 lines
138 KiB
C++
/**
|
|
* 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 "objit/common/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"
|
|
#include "sql/rewrite/ob_stmt_comparer.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(B.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::do_transform(ObDMLStmt *&stmt, bool &trans_happened)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool aggr_first_happened = false;
|
|
bool join_first_happened = false;
|
|
bool aggr_first_for_having_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_aggr_first_for_having(stmt, aggr_first_for_having_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 | aggr_first_for_having_happened;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::transform_one_stmt(common::ObIArray<ObParentDMLStmt> &parent_stmts,
|
|
ObDMLStmt *&stmt,
|
|
bool &trans_happened)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
UNUSED(parent_stmts);
|
|
trans_stmt_infos_.reset();
|
|
join_first_happened_ = false;
|
|
if (OB_FAIL(do_transform(stmt, trans_happened))) {
|
|
LOG_WARN("failed to do transform", K(ret));
|
|
} else if (trans_happened && OB_FAIL(add_transform_hint(*stmt, &trans_stmt_infos_))) {
|
|
LOG_WARN("failed to add transform hint", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::check_hint_status(const ObDMLStmt &stmt, bool &need_trans)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
need_trans = false;
|
|
const ObQueryHint *query_hint = NULL;
|
|
const ObHint *cur_trans_hint = NULL;
|
|
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 == (cur_trans_hint = query_hint->get_outline_trans_hint(ctx_->trans_list_loc_)) ||
|
|
!(cur_trans_hint->is_aggr_first_unnest_hint() ||
|
|
cur_trans_hint->is_join_first_unnest_hint())) {
|
|
/*do nothing*/
|
|
} else {
|
|
ObQueryRefRawExpr* subquery_expr = nullptr;
|
|
ObSelectStmt* select_stmt = nullptr;
|
|
for (int64_t i = 0; !need_trans && OB_SUCC(ret) && i < stmt.get_subquery_expr_size(); ++i) {
|
|
if (OB_ISNULL(subquery_expr = stmt.get_subquery_exprs().at(i)) ||
|
|
OB_ISNULL(select_stmt = subquery_expr->get_ref_stmt())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get unexpected null", K(ret), K(subquery_expr), K(select_stmt));
|
|
} else if (query_hint->is_valid_outline_transform(ctx_->trans_list_loc_,
|
|
get_sub_unnest_hint(*select_stmt, AGGR_FIRST))) {
|
|
need_trans = true;
|
|
} else if (query_hint->is_valid_outline_transform(ctx_->trans_list_loc_,
|
|
get_sub_unnest_hint(*select_stmt, JOIN_FIRST))) {
|
|
need_trans = true;
|
|
} else if (query_hint->is_valid_outline_transform(ctx_->trans_list_loc_,
|
|
select_stmt->get_stmt_hint().get_normal_hint(T_UNNEST))) {
|
|
need_trans = true;
|
|
}
|
|
}
|
|
}
|
|
if (!need_trans) {
|
|
OPT_TRACE("outline reject transform");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::construct_transform_hint(ObDMLStmt &stmt, void *trans_params)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObIArray<TransStmtInfo> *trans_stmt_infos = NULL;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->allocator_) || OB_ISNULL(trans_params)
|
|
|| OB_ISNULL(trans_stmt_infos = static_cast<ObIArray<TransStmtInfo>*>(trans_params))
|
|
|| OB_UNLIKELY(trans_stmt_infos->empty())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret), K(ctx_), K(trans_params), K(trans_stmt_infos));
|
|
} else {
|
|
ObTransHint *hint = NULL;
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < trans_stmt_infos->count(); ++i) {
|
|
TransStmtInfo& info = trans_stmt_infos->at(i);
|
|
if (OB_FAIL(ObQueryHint::create_hint(ctx_->allocator_,
|
|
get_unnest_strategy(info.pullup_strategy_),
|
|
hint))) {
|
|
LOG_WARN("failed to create hint", K(ret));
|
|
} else if (OB_FAIL(ctx_->add_src_hash_val(info.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 (nullptr != info.unnest_ && OB_FAIL(ctx_->add_used_trans_hint(info.unnest_))) {
|
|
LOG_WARN("failed to add used trans hint", K(ret));
|
|
} else {
|
|
hint->set_qb_name(info.qb_name_);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::transform_with_aggregation_first(ObDMLStmt *&stmt,
|
|
bool &trans_happened)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<ObRawExpr *, 4> exprs;
|
|
const bool with_vector_assgin = true;
|
|
const ObQueryHint* query_hint = nullptr;
|
|
bool is_hsfu = false;
|
|
OPT_TRACE("try aggregation first");
|
|
if (OB_ISNULL(stmt) || 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));
|
|
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()) {
|
|
OPT_TRACE("hierarchical/set/insert/merge query can not transform");
|
|
} else if (OB_FAIL(stmt->is_hierarchical_for_update(is_hsfu))) {
|
|
LOG_WARN("failed to check hierarchical for update", K(ret));
|
|
} else if (is_hsfu) {
|
|
OPT_TRACE("hierarchical for update query can not transform");
|
|
} 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;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::transform_with_aggr_first_for_having(ObDMLStmt *&stmt, bool &trans_happened)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSelectStmt *view_stmt = NULL;
|
|
bool need_spj = false;
|
|
if (OB_ISNULL(stmt)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("invalid params", K(ret), K(stmt));
|
|
} else if (!stmt->is_select_stmt()) {
|
|
// do nothing
|
|
} else if (OB_FAIL(check_need_spj(stmt, need_spj))) {
|
|
LOG_WARN("check need spj failed", K(ret));
|
|
} else if (!need_spj) {
|
|
//do nothing
|
|
} else if (OB_FAIL(ObTransformUtils::create_simple_view(ctx_, stmt, view_stmt, true, true, true))) {
|
|
LOG_WARN("create simple view failed", K(ret));
|
|
} else if (OB_FAIL(transform_with_aggregation_first(stmt, trans_happened))) {
|
|
LOG_WARN("transform with aggr first failed", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::check_need_spj(ObDMLStmt *stmt, bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSelectStmt *sel_stmt = NULL;
|
|
bool has_rand = false;
|
|
if (OB_ISNULL(stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("stmt is null", K(ret));
|
|
} else if (!stmt->is_select_stmt()) {
|
|
is_valid = false;
|
|
} else if (OB_ISNULL(sel_stmt = static_cast<ObSelectStmt *>(stmt))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("stmt is null", K(ret));
|
|
} else if (sel_stmt->get_having_exprs().count() == 0 ||
|
|
sel_stmt->has_rollup()) {
|
|
is_valid = false;
|
|
} else if (OB_FAIL(sel_stmt->has_rand(has_rand))) {
|
|
LOG_WARN("sel stmt has rand failed", K(ret));
|
|
} else if (has_rand) {
|
|
is_valid = false;
|
|
} else {
|
|
bool exist_valid_subquery = false;
|
|
ObSEArray<TransformParam, 4> transform_params;
|
|
for (int64_t i = 0; OB_SUCC(ret) && !exist_valid_subquery && i < sel_stmt->get_having_exprs().count(); i++) {
|
|
transform_params.reuse();
|
|
ObRawExpr *expr = sel_stmt->get_having_exprs().at(i);
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
} else if (!expr->has_flag(CNT_SUB_QUERY)) {
|
|
//do nothing
|
|
} else if (OB_FAIL(gather_transform_params(*stmt, expr, expr, AGGR_FIRST, false, transform_params))) {
|
|
LOG_WARN("gather transform param failed", K(ret));
|
|
} else if (transform_params.count() > 0) {
|
|
exist_valid_subquery = true;
|
|
}
|
|
}
|
|
is_valid = exist_valid_subquery;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief ObTransformAggrSubquery::do_aggr_first_transform
|
|
* STEP 1: 找到一个可改写的子查询
|
|
* STEP 2: 根据子查询的特点,决定改写的方式(OUTER JOIN/INNER JOIN)
|
|
* STEP 3: 执行改写
|
|
* @return
|
|
*/
|
|
int ObTransformAggrSubquery::do_aggr_first_transform(ObDMLStmt *&stmt,
|
|
ObRawExpr *expr,
|
|
bool &trans_happened)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<TransformParam, 4> transform_params;
|
|
ObSEArray<ObRawExpr *, 4> filters;
|
|
bool is_select_item_expr = false;
|
|
const ObQueryHint* query_hint = nullptr;
|
|
if (OB_ISNULL(expr) || OB_ISNULL(stmt) ||
|
|
OB_ISNULL(query_hint = stmt->get_stmt_hint().query_hint_)) {
|
|
ret= OB_ERR_UNEXPECTED;
|
|
LOG_WARN("condition is null", K(ret), K(expr), K(stmt), K(query_hint));
|
|
} else if (stmt->is_select_stmt() && FALSE_IT(is_select_item_expr =
|
|
static_cast<ObSelectStmt*>(stmt)->check_is_select_item_expr(expr))) {
|
|
// never reach
|
|
} else if (OB_FAIL(gather_transform_params(*stmt, expr, expr, AGGR_FIRST, is_select_item_expr, 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;
|
|
ObSelectStmt *origin_subquery = NULL;
|
|
if (OB_ISNULL(query_ref = trans_param.ja_query_ref_) ||
|
|
OB_ISNULL(subquery = trans_param.ja_query_ref_->get_ref_stmt()) ||
|
|
OB_ISNULL(origin_subquery = subquery)) {
|
|
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 (use_outer_join(trans_param.pullup_flag_) && stmt->get_table_size() == 0) {
|
|
// skip
|
|
} else if (trans_param.upper_filters_.count() > 0 && use_outer_join(trans_param.pullup_flag_)) {
|
|
// skip
|
|
} else if (trans_param.exists_to_aggr_ && trans_param.nested_conditions_.empty()) {
|
|
// skip
|
|
} else if (trans_param.limit_to_aggr_ &&
|
|
OB_FAIL(convert_limit_as_aggr(subquery, trans_param))) {
|
|
LOG_WARN("failed to transform subquery with limit");
|
|
} else if (trans_param.exists_to_aggr_ &&
|
|
OB_FAIL(convert_exists_as_scalar_subquery(stmt, query_ref, subquery, trans_param))) {
|
|
LOG_WARN("failed to convert exists as scalar subquery", K(ret));
|
|
} else if (trans_param.any_all_to_aggr_ &&
|
|
OB_FAIL(convert_any_all_as_scalar_subquery(stmt, query_ref, subquery, trans_param))) {
|
|
LOG_WARN("failed to convert exists as scalar subquery", K(ret));
|
|
} else if (OB_FAIL(fill_query_refs(stmt, expr->has_flag(CNT_ALIAS), 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 if (OB_FAIL(add_trans_stmt_info(*origin_subquery, AGGR_FIRST))) {
|
|
LOG_WARN("failed add trans stmt info", K(ret));
|
|
} else {
|
|
trans_happened = true;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* find valid JA-subquery in the root expr and select item expr.
|
|
* @param is_select_item_expr: is root expr in select item, only used in aggr first rewrite
|
|
**/
|
|
int ObTransformAggrSubquery::gather_transform_params(ObDMLStmt &stmt,
|
|
ObRawExpr *root_expr,
|
|
ObRawExpr *child_expr,
|
|
int64_t pullup_strategy,
|
|
const bool is_select_item_expr,
|
|
ObIArray<TransformParam> &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 (!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<ObQueryRefRawExpr*>(child_expr);
|
|
ObSelectStmt *subquery = trans_param.ja_query_ref_->get_ref_stmt();
|
|
bool is_valid = false;
|
|
bool hint_allowed = false;
|
|
int64_t limit_value = 0;
|
|
OPT_TRACE("try to pullup JA subquery", child_expr);
|
|
if (ObOptimizerUtil::find_item(no_rewrite_exprs_, child_expr)) {
|
|
LOG_TRACE("subquery in select expr and can use index");
|
|
OPT_TRACE("subquery in select expr and can use index, no need transfrom");
|
|
} else if (OB_FAIL(check_hint_allowed_unnest(stmt, *subquery,
|
|
ctx_->trans_list_loc_ + transform_params.count(),
|
|
pullup_strategy,
|
|
hint_allowed))) {
|
|
LOG_WARN("failed to check hint allowed unnest", K(ret));
|
|
} else if (!hint_allowed) {
|
|
OPT_TRACE("hint reject transform");
|
|
} else if (aggr_first(pullup_strategy)) {
|
|
OPT_TRACE("try aggregation first transform");
|
|
if (OB_FAIL(ObTransformUtils::find_parent_expr(root_expr, child_expr, trans_param.parent_expr_of_query_ref))) {
|
|
LOG_WARN("failed to find parent expr of subquery expr", K(ret));
|
|
} else if (OB_ISNULL(trans_param.parent_expr_of_query_ref)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("find no parent expr of subquery expr", K(ret));
|
|
} else if (OB_FAIL(check_aggr_first_validity(stmt,
|
|
*trans_param.ja_query_ref_,
|
|
root_expr->has_flag(CNT_ALIAS),
|
|
root_expr,
|
|
*trans_param.parent_expr_of_query_ref,
|
|
trans_param.nested_conditions_,
|
|
trans_param.upper_filters_,
|
|
is_select_item_expr,
|
|
is_valid,
|
|
limit_value,
|
|
trans_param.limit_to_aggr_,
|
|
trans_param.any_all_to_aggr_,
|
|
trans_param.exists_to_aggr_,
|
|
trans_param.equal_param_info_))) {
|
|
LOG_WARN("failed to check subquery validity for aggr first", K(ret));
|
|
} else {
|
|
trans_param.limit_for_exists_ = false;
|
|
trans_param.limit_value_ = limit_value;
|
|
}
|
|
} else if (join_first(pullup_strategy) && trans_param.ja_query_ref_->has_exec_param()) {
|
|
OPT_TRACE("try join first transform");
|
|
if (OB_FAIL(ObTransformUtils::find_parent_expr(root_expr, child_expr, trans_param.parent_expr_of_query_ref))) {
|
|
LOG_WARN("failed to find parent expr of subquery expr", K(ret));
|
|
} else if (OB_ISNULL(trans_param.parent_expr_of_query_ref)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("find no parent expr of subquery expr", K(ret));
|
|
} else if (OB_FAIL(check_join_first_validity(*trans_param.ja_query_ref_,
|
|
root_expr->has_flag(CNT_ALIAS),
|
|
is_exists_op(root_expr->get_expr_type()),
|
|
*trans_param.parent_expr_of_query_ref,
|
|
IS_SUBQUERY_COMPARISON_OP(trans_param.parent_expr_of_query_ref->get_expr_type()),
|
|
trans_param.not_null_const_,
|
|
trans_param.limit_for_exists_,
|
|
limit_value,
|
|
is_valid,
|
|
trans_param.equal_param_info_))) {
|
|
LOG_WARN("failed to check subquery validity for join first", K(ret));
|
|
} else {
|
|
trans_param.limit_value_ = limit_value;
|
|
}
|
|
}
|
|
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 {
|
|
OPT_TRACE("expr can not be transformed");
|
|
}
|
|
} else {
|
|
// join-first ja can rewrite exists predicate (must be a root relation expr in where/having)
|
|
bool is_valid_exists_filter = aggr_first(pullup_strategy) ||
|
|
(is_exists_op(child_expr->get_expr_type()) && root_expr == child_expr);
|
|
if (is_valid_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(stmt, root_expr,
|
|
child_expr->get_param_expr(i),
|
|
pullup_strategy,
|
|
is_select_item_expr,
|
|
transform_params)))) {
|
|
LOG_WARN("failed to gather transform params", K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief ObTransformAggrSubquery::check_aggr_first_validity
|
|
* check the valiaidty of the subquery
|
|
*/
|
|
int ObTransformAggrSubquery::check_aggr_first_validity(ObDMLStmt &stmt,
|
|
ObQueryRefRawExpr &query_ref,
|
|
const bool vector_assign,
|
|
ObRawExpr *root_expr,
|
|
ObRawExpr &parent_expr,
|
|
ObIArray<ObRawExpr *> &nested_conditions,
|
|
ObIArray<ObRawExpr*> &upper_filters,
|
|
const bool is_select_item_expr,
|
|
bool &is_valid,
|
|
int64_t &limit_value,
|
|
bool &limit_to_aggr,
|
|
bool &any_all_to_aggr,
|
|
bool &exists_to_aggr,
|
|
ObIArray<ObPCParamEqualInfo> &equal_param_info)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSelectStmt *subquery = NULL;
|
|
bool has_rownum = false;
|
|
bool has_ref_assign_user_var = false;
|
|
bool has_equal_correlation = false;
|
|
bool is_correlated = false;
|
|
nested_conditions.reuse();
|
|
upper_filters.reuse();
|
|
is_valid = true;
|
|
bool is_group_single_set = false;
|
|
bool is_limit_single_set = false;
|
|
limit_to_aggr = false;
|
|
any_all_to_aggr = false;
|
|
exists_to_aggr = false;
|
|
limit_value = -1;
|
|
uint64_t opt_version = 0;
|
|
bool check_match_index = true;
|
|
bool hint_allowed_transform = false;
|
|
if (OB_ISNULL(subquery = query_ref.get_ref_stmt()) || OB_ISNULL(stmt.get_query_ctx()) || OB_ISNULL(ctx_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("subquery is null", K(ret));
|
|
} else if (OB_FALSE_IT(opt_version = stmt.get_query_ctx()->optimizer_features_enable_version_)) {
|
|
} else if (OB_FAIL(check_nested_subquery(query_ref, is_valid))) {
|
|
LOG_WARN("failed to check nested subquery", K(ret));
|
|
} else if (!is_valid) {
|
|
OPT_TRACE("exec param reference another subquery");
|
|
// 0. check single set
|
|
} else if (OB_FAIL(check_single_set_subquery(*subquery,
|
|
is_group_single_set,
|
|
is_limit_single_set,
|
|
limit_value))) {
|
|
LOG_WARN("failed to check is single set query", K(ret));
|
|
} else if (is_limit_single_set && OB_FAIL(check_limit_single_set_validity(*subquery,
|
|
parent_expr,
|
|
limit_to_aggr,
|
|
equal_param_info))) {
|
|
LOG_WARN("failed to check select validity for limit 1", K(ret));
|
|
} else if (opt_version >= COMPAT_VERSION_4_3_2 && is_select_item_expr &&
|
|
OB_FAIL(check_can_trans_any_all_as_scalar_subquery(stmt,
|
|
subquery,
|
|
&parent_expr,
|
|
root_expr,
|
|
any_all_to_aggr))) {
|
|
LOG_WARN("failed to check can trans any all as aggr subquery", K(ret));
|
|
} else if (opt_version >= COMPAT_VERSION_4_3_2 && is_select_item_expr &&
|
|
OB_FAIL(check_can_trans_exists_as_scalar_subquery(query_ref,
|
|
parent_expr,
|
|
limit_value,
|
|
exists_to_aggr))) {
|
|
LOG_WARN("failed to check can trans exists as aggr subquery", K(ret));
|
|
} else if (!is_group_single_set && !limit_to_aggr && !any_all_to_aggr && !exists_to_aggr) {
|
|
is_valid = false;
|
|
OPT_TRACE("not scalar subquery or equivalent scalar subquery");
|
|
// 1. check stmt components
|
|
} else if (!any_all_to_aggr && (IS_SUBQUERY_COMPARISON_OP(parent_expr.get_expr_type()) &&
|
|
(parent_expr.has_flag(IS_WITH_ANY) || parent_expr.has_flag(IS_WITH_ALL)))) {
|
|
is_valid = false;
|
|
} else if (subquery->has_rollup() ||
|
|
subquery->has_having() ||
|
|
NULL != subquery->get_limit_percent_expr() ||
|
|
NULL != subquery->get_offset_expr() ||
|
|
subquery->has_window_function() ||
|
|
subquery->has_sequence() ||
|
|
subquery->is_set_stmt() ||
|
|
subquery->is_hierarchical_query()) {
|
|
is_valid = false;
|
|
LOG_TRACE("invalid subquery", K(is_valid), K(*subquery));
|
|
OPT_TRACE("subquery has rollup/having/limit offset/limit percent/win_func/sequence");
|
|
} 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));
|
|
OPT_TRACE("subquery has rownum");
|
|
} 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) {
|
|
OPT_TRACE("has AVG aggregation");
|
|
} 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));
|
|
OPT_TRACE("has assignment ref user variable");
|
|
// 2. check the select item
|
|
} else if (!vector_assign && subquery->get_select_item_size() > 1) {
|
|
is_valid = false;
|
|
OPT_TRACE("select item more than one");
|
|
LOG_TRACE("select item size is invalid",
|
|
K(vector_assign), K(subquery->get_select_item_size()));
|
|
} else if (OB_FAIL(check_subquery_select(query_ref, is_valid))) {
|
|
LOG_WARN("failed to check select validity", K(ret));
|
|
} else if (!is_valid) {
|
|
LOG_TRACE("select list is invalid", K(is_valid));
|
|
OPT_TRACE("subquery select item contain subquery");
|
|
// 3. check from list is not correlated
|
|
} else if (OB_FAIL(ObTransformUtils::is_table_item_correlated(query_ref.get_exec_params(),
|
|
*subquery,
|
|
is_correlated))) {
|
|
LOG_WARN("failed to check table item correlated or not", K(ret));
|
|
} else if (is_correlated) {
|
|
is_valid = false;
|
|
OPT_TRACE("subquery`s table item is correlated");
|
|
// 4. check correlated join on contiditons
|
|
// 5. check correlated semi contiditons
|
|
} else if (OB_FAIL(ObTransformUtils::is_join_conditions_correlated(query_ref.get_exec_params(),
|
|
subquery,
|
|
is_correlated))) {
|
|
LOG_WARN("failed to check is join condition correlated", K(ret));
|
|
} else if (is_correlated) {
|
|
is_valid = false;
|
|
OPT_TRACE("subquery`s outer/semi join condition is correlated");
|
|
// 6. check correlated join contiditons
|
|
} else if (OB_FALSE_IT(hint_allowed_transform = (subquery->get_stmt_hint().has_enable_hint(T_UNNEST) ||
|
|
subquery->get_stmt_hint().has_enable_hint(T_AGGR_FIRST_UNNEST)))) {
|
|
} else if (OB_FALSE_IT(check_match_index = hint_allowed_transform ? false :
|
|
is_select_item_expr || limit_to_aggr)) {
|
|
} else if (OB_FAIL(check_subquery_conditions(query_ref,
|
|
*subquery,
|
|
nested_conditions,
|
|
upper_filters,
|
|
check_match_index,
|
|
is_valid,
|
|
has_equal_correlation))) {
|
|
LOG_WARN("failed to check subquery conditions", K(ret));
|
|
} else if (!is_valid) {
|
|
OPT_TRACE("subquery's condition is not valid");
|
|
} else if (limit_to_aggr && !has_equal_correlation) {
|
|
// transform limit 1 to aggr only if subquery has equal correlation
|
|
is_valid = false;
|
|
OPT_TRACE("subquery does not have euqal correlation condition");
|
|
} else if (!is_valid) {
|
|
OPT_TRACE("select item is lob or const");
|
|
} else if (OB_FAIL(check_subquery_orderby(query_ref, is_valid))) {
|
|
LOG_WARN("failed to check order_by validity", K(ret));
|
|
} else if (!is_valid) {
|
|
LOG_TRACE("order by item is invalid", K(is_valid));
|
|
OPT_TRACE("subquery order by item contain correlated subquery");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::check_subquery_select(const ObQueryRefRawExpr &query_ref,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const ObSelectStmt *subquery = NULL;
|
|
if (OB_ISNULL(subquery = query_ref.get_ref_stmt())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("subquery stmt is null", K(ret));
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery->get_select_item_size(); ++i) {
|
|
const ObRawExpr *select_expr = NULL;
|
|
bool bret = false;
|
|
if (OB_ISNULL(select_expr = subquery->get_select_item(i).expr_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("select expr is null", K(ret));
|
|
} else if (select_expr->has_flag(CNT_SUB_QUERY)) {
|
|
is_valid = false;
|
|
} else if (OB_FAIL(ObTransformUtils::is_correlated_expr(query_ref.get_exec_params(), select_expr, bret))) {
|
|
LOG_WARN("failed to check is correlated", K(ret));
|
|
} else if (bret) {
|
|
is_valid = false;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// whether 'order by' subquery in current subquery has any correlated expr.
|
|
int ObTransformAggrSubquery::check_subquery_orderby(const ObQueryRefRawExpr &query_ref,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const ObSelectStmt *subquery = NULL;
|
|
is_valid = true;
|
|
if (OB_ISNULL(subquery = query_ref.get_ref_stmt())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("subquery stmt is null", K(ret));
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery->get_order_item_size(); ++i) {
|
|
const ObRawExpr *expr = NULL;
|
|
bool is_correlated = false;
|
|
if (OB_ISNULL(expr = subquery->get_order_item(i).expr_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("select expr is null", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::is_correlated_expr(query_ref.get_exec_params(),
|
|
expr,
|
|
is_correlated))) {
|
|
LOG_WARN("failed to check is correlated", K(ret));
|
|
} else if (is_correlated) {
|
|
is_valid = false;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
int ObTransformAggrSubquery::check_limit_single_set_validity(const ObSelectStmt &subquery,
|
|
const ObRawExpr &parent_expr,
|
|
bool &is_valid,
|
|
ObIArray<ObPCParamEqualInfo> &equal_param_info)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
// check select item for transform limit 1 to group by
|
|
// 1. only one select item
|
|
// 2. not lob or const
|
|
// 3. same as the first expr in order by
|
|
is_valid = false;
|
|
if (1 != subquery.get_select_item_size()) {
|
|
// do nothing
|
|
} else if (is_exists_op(parent_expr.get_expr_type()) ||
|
|
(IS_SUBQUERY_COMPARISON_OP(parent_expr.get_expr_type()) && (
|
|
parent_expr.has_flag(IS_WITH_ANY) || parent_expr.has_flag(IS_WITH_ALL)))) {
|
|
// do nothing
|
|
} else {
|
|
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 (ObLobType == select_expr->get_data_type()
|
|
|| select_expr->is_const_expr()) {
|
|
is_valid = false;
|
|
} else if (subquery.has_order_by()) {
|
|
ObStmtCompareContext context;
|
|
const OrderItem& first_order = subquery.get_order_item(0);
|
|
if (OB_ISNULL(first_order.expr_) || OB_ISNULL(subquery.get_query_ctx())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret));
|
|
} else if (FALSE_IT(context.init(&subquery.get_query_ctx()->calculable_items_))) {
|
|
// never reach
|
|
} else if (!select_expr->same_as(*first_order.expr_, &context)) {
|
|
is_valid = false;
|
|
OPT_TRACE("order does not match");
|
|
} else if (NULLS_LAST_ASC == first_order.order_type_ ||
|
|
NULLS_LAST_DESC == first_order.order_type_){
|
|
if (OB_FAIL(equal_param_info.assign(context.equal_param_info_))) {
|
|
LOG_WARN("failed to assign equal param info", K(ret));
|
|
} else {
|
|
is_valid = true;
|
|
OPT_TRACE("can convert limit 1 to aggr");
|
|
}
|
|
} else {
|
|
is_valid = false;
|
|
}
|
|
} else {
|
|
is_valid = true;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::check_join_first_condition_for_limit_1(ObQueryRefRawExpr &query_ref,
|
|
ObSelectStmt &subquery,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
EqualSets &equal_sets = ctx_->equal_sets_;
|
|
ObSEArray<ObRawExpr *, 4> const_exprs;
|
|
ObArenaAllocator alloc;
|
|
bool has_equal_correlation = false;
|
|
is_valid = true;
|
|
if (OB_FAIL(subquery.get_stmt_equal_sets(equal_sets, alloc, true))) {
|
|
LOG_WARN("failed to get stmt equal sets", K(ret));
|
|
} else if (OB_FAIL(ObOptimizerUtil::compute_const_exprs(subquery.get_condition_exprs(),
|
|
const_exprs))) {
|
|
LOG_WARN("failed to compute const equivalent exprs", K(ret));
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery.get_condition_size(); ++i) {
|
|
ObRawExpr *cond = NULL;
|
|
bool is_eq_correlation = false;
|
|
|
|
// transform limit 1 to group by only if subquery has equal correlation
|
|
if (OB_ISNULL(cond = subquery.get_condition_expr(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("condition expr is null", K(ret));
|
|
} else if (has_equal_correlation) {
|
|
// do nothing
|
|
} else if (OB_FAIL(ObTransformUtils::is_equal_correlation(query_ref.get_exec_params(),
|
|
cond,
|
|
is_eq_correlation))) {
|
|
LOG_WARN("failed to check is equal correlation", K(ret));
|
|
} else if (is_eq_correlation) {
|
|
has_equal_correlation = true;
|
|
}
|
|
|
|
// correlation condition should not match index
|
|
if (OB_SUCC(ret) && IS_COMMON_COMPARISON_OP(cond->get_expr_type())) {
|
|
ObColumnRefRawExpr *column_expr = NULL;
|
|
ObRawExpr *const_expr = NULL;
|
|
bool is_match = false;
|
|
if (OB_FAIL(ObTransformUtils::is_simple_correlated_pred(query_ref.get_exec_params(),
|
|
cond,
|
|
column_expr,
|
|
const_expr))) {
|
|
LOG_WARN("failed to check is simple correlated pred", K(ret));
|
|
} else if (column_expr == NULL) {
|
|
// do nothing
|
|
} else if (OB_FAIL(ObTransformUtils::is_match_index(ctx_->sql_schema_guard_,
|
|
&subquery,
|
|
column_expr,
|
|
is_match,
|
|
&equal_sets,
|
|
&const_exprs))) {
|
|
LOG_WARN("failed to check is match index", K(ret));
|
|
} else if (is_match) {
|
|
LOG_TRACE("condition match index", KPC(cond));
|
|
OPT_TRACE("subquery`s correlated condition match index");
|
|
is_valid = false;
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && !has_equal_correlation) {
|
|
is_valid = false;
|
|
OPT_TRACE("subquery does not have euqal correlation condition");
|
|
}
|
|
equal_sets.reuse();
|
|
return ret;
|
|
}
|
|
|
|
|
|
int ObTransformAggrSubquery::check_subquery_conditions(ObQueryRefRawExpr &query_ref,
|
|
ObSelectStmt &subquery,
|
|
ObIArray<ObRawExpr *> &nested_conds,
|
|
ObIArray<ObRawExpr *> &upper_filters,
|
|
const bool check_idx,
|
|
bool &is_valid,
|
|
bool &has_equal_correlation)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
is_valid = true;
|
|
has_equal_correlation = false;
|
|
ObArenaAllocator alloc;
|
|
EqualSets &equal_sets = ctx_->equal_sets_;
|
|
ObSEArray<ObRawExpr *, 4> const_exprs;
|
|
if (OB_FAIL(subquery.get_stmt_equal_sets(equal_sets, alloc, true))) {
|
|
LOG_WARN("failed to get stmt equal sets", K(ret));
|
|
} else if (OB_FAIL(ObOptimizerUtil::compute_const_exprs(subquery.get_condition_exprs(),
|
|
const_exprs))) {
|
|
LOG_WARN("failed to compute const equivalent exprs", K(ret));
|
|
} else {
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery.get_condition_size(); ++i) {
|
|
ObRawExpr *cond = NULL;
|
|
ObRawExpr *outer_expr = NULL;
|
|
ObRawExpr *inner_expr = NULL;
|
|
bool is_correlated = false;
|
|
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(query_ref.get_exec_params(),
|
|
cond,
|
|
is_correlated))) {
|
|
LOG_WARN("failed to check is correlated expr", K(ret));
|
|
} else if (!is_correlated) {
|
|
// do nothing
|
|
} else if (cond->has_flag(CNT_SUB_QUERY)) {
|
|
is_valid = false;
|
|
OPT_TRACE("subquery`s condition has subquery");
|
|
} else if (cond->is_const_expr()) {
|
|
// filter of upper stmt
|
|
uint64_t opt_version = subquery.get_query_ctx()->optimizer_features_enable_version_;
|
|
if ((opt_version >= COMPAT_VERSION_4_2_1_BP8 && opt_version < COMPAT_VERSION_4_2_2) ||
|
|
(opt_version >= COMPAT_VERSION_4_2_4 && opt_version < COMPAT_VERSION_4_3_0) ||
|
|
opt_version >= COMPAT_VERSION_4_3_2) {
|
|
if (OB_FAIL(upper_filters.push_back(cond))) {
|
|
LOG_WARN("failed to add upper filter", K(ret));
|
|
}
|
|
} else {
|
|
is_valid = false;
|
|
OPT_TRACE("subquery`s condition has pure upper filters");
|
|
}
|
|
} else if (OB_FAIL(ObTransformUtils::is_equal_correlation(query_ref.get_exec_params(),
|
|
cond,
|
|
is_valid,
|
|
&outer_expr,
|
|
&inner_expr))) {
|
|
LOG_WARN("failed to check is equal correlation", K(ret));
|
|
} else if (!is_valid) {
|
|
OPT_TRACE("subquery`s correlated condition is not equal cond");
|
|
} else if (OB_FAIL(nested_conds.push_back(cond))) {
|
|
LOG_WARN("failed to push back nested conditions", K(ret));
|
|
} else if (check_idx && inner_expr->is_column_ref_expr()) {
|
|
bool is_match = false;
|
|
if (OB_FAIL(ObTransformUtils::is_match_index(ctx_->sql_schema_guard_,
|
|
&subquery,
|
|
static_cast<ObColumnRefRawExpr*>(inner_expr),
|
|
is_match,
|
|
&equal_sets, &const_exprs))) {
|
|
LOG_WARN("failed to check is match index", K(ret));
|
|
} else if (is_match) {
|
|
LOG_TRACE("inner expr match index", K(*inner_expr));
|
|
is_valid = false;
|
|
OPT_TRACE("subquery`s correlated condition match index");
|
|
} else {
|
|
has_equal_correlation = true;
|
|
}
|
|
} else {
|
|
has_equal_correlation = true;
|
|
}
|
|
}
|
|
}
|
|
equal_sets.reuse();
|
|
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<ObRawExpr *> &filters,
|
|
TransformParam &trans_param)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObQueryRefRawExpr *query_ref = trans_param.ja_query_ref_;
|
|
ObIArray<bool> &is_null_prop = trans_param.is_null_prop_;
|
|
ObSelectStmt *subquery = NULL;
|
|
ObSEArray<const ObRawExpr*, 4> vars;
|
|
bool is_null_propagate = true;
|
|
bool is_null_reject = false;
|
|
bool is_scalar_aggr = false;
|
|
int64_t null_prop_array_size = 0;
|
|
bool need_check_null_prop = 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 (OB_FALSE_IT(is_scalar_aggr = subquery->is_scala_group_by())) {
|
|
} else if (trans_param.any_all_to_aggr_ || trans_param.exists_to_aggr_) {
|
|
if (OB_FAIL(is_null_prop.prepare_allocate(1))) {
|
|
LOG_WARN("failed to prepare allocate case when array", K(ret));
|
|
} else {
|
|
is_null_prop.at(0) = false;
|
|
need_check_null_prop = false;
|
|
is_null_propagate = false;
|
|
}
|
|
} 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));
|
|
}
|
|
// 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) && need_check_null_prop && 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(ObTransformUtils::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() &&
|
|
trans_param.upper_filters_.empty() &&
|
|
is_scalar_aggr) {
|
|
// use inner join for non-correlated subquery
|
|
OPT_TRACE("use inner join for aggr first");
|
|
} else if (!is_null_propagate || !is_null_reject) {
|
|
trans_param.pullup_flag_ |= USE_OUTER_JOIN;
|
|
OPT_TRACE("use outer join for none null propagate expr");
|
|
} else {
|
|
OPT_TRACE("use inner join for null propagate expr");
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::transform_child_stmt(ObDMLStmt *stmt,
|
|
ObSelectStmt &subquery,
|
|
TransformParam ¶m)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObIArray<ObRawExpr *> &nested_conditions = param.nested_conditions_;
|
|
ObIArray<ObRawExpr *> &upper_filters = param.upper_filters_;
|
|
ObSEArray<ObRawExpr *, 4> old_group_exprs;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->allocator_) || OB_ISNULL(param.ja_query_ref_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("transform context is invalid", K(ret), K(ctx_), K(param.ja_query_ref_));
|
|
} 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 < upper_filters.count(); ++i) {
|
|
if (OB_FAIL(ObOptimizerUtil::remove_item(subquery.get_condition_exprs(), upper_filters.at(i)))) {
|
|
LOG_WARN("failed to remove item", K(ret));
|
|
}
|
|
}
|
|
|
|
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(param.ja_query_ref_->get_exec_params(),
|
|
cond_expr->get_param_expr(0),
|
|
left_is_correlated))) {
|
|
LOG_WARN("failed to check is left correlated", 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_item(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()) {
|
|
ObConstRawExpr *const_one = NULL;
|
|
if (is_oracle_mode()) {
|
|
if (OB_FAIL(ObRawExprUtils::build_const_number_expr(*ctx_->expr_factory_,
|
|
ObNumberType,
|
|
number::ObNumber::get_positive_one(),
|
|
const_one))) {
|
|
LOG_WARN("failed to build const one", K(ret));
|
|
}
|
|
} else {
|
|
if (OB_FAIL(ObRawExprUtils::build_const_int_expr(*ctx_->expr_factory_, ObIntType, 1 ,const_one))) {
|
|
LOG_WARN("failed to build const int expr", K(ret));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && OB_FAIL(subquery.get_group_exprs().push_back(const_one))) {
|
|
/// 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(eliminate_redundant_aggregation_if_need(subquery, param))) {
|
|
LOG_WARN("failed to eliminate redundant aggregation if need", K(ret));
|
|
} else if (OB_FAIL(eliminate_limit_if_need(subquery, param, false))) {
|
|
LOG_WARN("failed to eliminate limit if need", 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. 把子查询作为视图添加到主查询中
|
|
* 2. 根据视图的输出列,推导原始子查询结果的表达式 real_values, 用 real_values 替换掉原始子查询表达式的引用
|
|
* 3. 根据 pullup 的相关条件构造连接条件,使用恰当的方式连接提升产生的视图。
|
|
* @return
|
|
*/
|
|
int ObTransformAggrSubquery::transform_upper_stmt(ObDMLStmt &stmt, TransformParam ¶m)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
TableItem *view_table = NULL;
|
|
ObSEArray<ObRawExpr *, 4> view_columns;
|
|
ObSEArray<ObRawExpr *, 4> select_exprs;
|
|
ObSEArray<ObRawExpr *, 4> real_values;
|
|
ObRawExpr *real_parent_value = NULL;
|
|
ObQueryRefRawExpr *query_expr = param.ja_query_ref_;
|
|
ObIArray<ObRawExpr *> &pullup_conds = param.nested_conditions_;
|
|
ObIArray<ObRawExpr *> &upper_filters = param.upper_filters_;
|
|
ObSelectStmt *subquery = NULL;
|
|
int64_t idx = OB_INVALID_INDEX;
|
|
|
|
// 1. add the subquery as view
|
|
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(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)) {
|
|
ObSEArray<ObRawExpr*, 2> from_exprs;
|
|
ObSEArray<ObRawExpr*, 2> to_exprs;
|
|
// 2. 推导原始的子查询计算结果
|
|
if (param.any_all_to_aggr_ || param.exists_to_aggr_) {
|
|
if (OB_FAIL(deduce_query_values_for_exists(*ctx_,
|
|
stmt,
|
|
param.not_null_expr_,
|
|
param.parent_expr_of_query_ref,
|
|
real_parent_value))) {
|
|
LOG_WARN("failed to deduce query values for exists", K(ret));
|
|
} else if (OB_FAIL(from_exprs.push_back(param.parent_expr_of_query_ref))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
} else if (OB_FAIL(to_exprs.push_back(real_parent_value))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
} else if (OB_FAIL(stmt.replace_relation_exprs(from_exprs, to_exprs))) {
|
|
LOG_WARN("failed to replace inner stmt expr", K(ret));
|
|
}
|
|
} else {
|
|
if (OB_FAIL(ObTransformUtils::deduce_query_values(*ctx_,
|
|
stmt,
|
|
param.is_null_prop_,
|
|
param.not_null_expr_,
|
|
use_outer_join(param.pullup_flag_),
|
|
select_exprs, view_columns, real_values))) {
|
|
LOG_WARN("failed to deduce subquery output", K(ret));
|
|
} else if (OB_FAIL(stmt.replace_relation_exprs(param.query_refs_, real_values))) {
|
|
LOG_WARN("failed to replace inner stmt expr", K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
// 3. 构造连接条件,连接视图
|
|
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(ObTransformUtils::decorrelate(pullup_conds, query_expr->get_exec_params()))) {
|
|
LOG_WARN("failed to decorrelation", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::decorrelate(upper_filters, query_expr->get_exec_params()))) {
|
|
LOG_WARN("failed to decorrelate filters", K(ret));
|
|
} else if (OB_FAIL(transform_from_list(stmt, view_table, pullup_conds, upper_filters, 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));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::transform_from_list(ObDMLStmt &stmt,
|
|
TableItem *view_table_item,
|
|
const ObIArray<ObRawExpr *> &joined_conds,
|
|
const ObIArray<ObRawExpr *> &upper_filters,
|
|
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 if (OB_FAIL(stmt.add_condition_exprs(upper_filters))) {
|
|
LOG_WARN("failed to add condition exprs", K(ret));
|
|
}
|
|
} else {
|
|
// create outer join
|
|
TableItem *joined_table = NULL;
|
|
if (!upper_filters.empty()) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("upper filters should be empty for outer join", K(ret));
|
|
} else 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;
|
|
}
|
|
|
|
/**
|
|
* @brief ObTransformAggrSubquery::do_join_first_transform
|
|
* STEP 1: 找到一个可改写的子查询 (同时确定改写的策略 INNER JON/ OUTER JOIN)
|
|
* STEP 2: 从 STMT 上构造一个 SPJ 查询
|
|
* STEP 3: 在 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;
|
|
const ObQueryHint* query_hint = nullptr;
|
|
OPT_TRACE("try join first");
|
|
if (OB_ISNULL(stmt) || OB_ISNULL(query_hint = stmt->get_stmt_hint().query_hint_) ||
|
|
OB_ISNULL(ctx_->expr_factory_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get null stmt", K(ret), K(stmt), K(query_hint));
|
|
} 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) {
|
|
break;
|
|
} else if (!param.limit_for_exists_ &&
|
|
param.limit_value_ > 0 &&
|
|
OB_FAIL(convert_limit_as_aggr(param.ja_query_ref_->get_ref_stmt(),
|
|
param))) {
|
|
LOG_WARN("fail to transform for limit");
|
|
} else if (OB_FAIL(fill_query_refs(target_stmt, root_expr->has_flag(CNT_ALIAS), 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), KPC(view_stmt), KPC(param.ja_query_ref_));
|
|
} else if (OB_FAIL(do_join_first_transform(*view_stmt, param, root_expr, !join_first_happened_))) {
|
|
LOG_WARN("failed to do join first transform", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::add_param_not_null_constraint(*ctx_, param.not_null_const_))) {
|
|
LOG_WARN("failed to add param not null constraints", K(ret));
|
|
} else {
|
|
target_stmt = view_stmt;
|
|
trans_happened = true;
|
|
join_first_happened_ = true;
|
|
if (OB_FAIL(add_constraints_for_limit(param))) {
|
|
LOG_WARN("add constraints failed", K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::add_constraints_for_limit(TransformParam ¶m)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObConstRawExpr *value_expr = NULL;
|
|
ObConstRawExpr *zero_expr= NULL;
|
|
ObOpRawExpr *true_expr= NULL;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get null", K(ret));
|
|
} else if (!param.limit_for_exists_) {
|
|
} else if (OB_FAIL(ctx_->expr_factory_->create_raw_expr(T_INT, value_expr)) ||
|
|
OB_FAIL(ctx_->expr_factory_->create_raw_expr(T_INT, zero_expr)) ||
|
|
OB_FAIL(ctx_->expr_factory_->create_raw_expr(T_OP_GT, true_expr))) {
|
|
LOG_WARN("create raw expr fail", K(ret));
|
|
} else if (OB_ISNULL(value_expr) ||
|
|
OB_ISNULL(zero_expr) ||
|
|
OB_ISNULL(true_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null expr", K(ret));
|
|
} else if (OB_FAIL(true_expr->set_param_exprs(value_expr, zero_expr))) {
|
|
LOG_WARN("set param expr fail", K(ret));
|
|
} else {
|
|
ObObj obj_zero;
|
|
obj_zero.set_int(ObIntType, 0);
|
|
zero_expr->set_value(obj_zero);
|
|
|
|
ObObj obj_value;
|
|
obj_value.set_int(ObIntType, param.limit_value_);
|
|
value_expr->set_value(obj_value);
|
|
}
|
|
if (OB_SUCC(ret) && param.limit_for_exists_) {
|
|
if (OB_FAIL(true_expr->formalize(ctx_->exec_ctx_->get_my_session()))) {
|
|
LOG_WARN("fail to formalize expr", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::add_param_bool_constraint(ctx_, true_expr, true))) {
|
|
LOG_WARN("fail to add is true constraint", 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 ¶m,
|
|
ObRawExpr *&root_expr,
|
|
bool &post_group_by)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<TransformParam, 4> params;
|
|
ObSEArray<ObRawExpr *, 4> pre_group_by_exprs; // where, group exprs
|
|
ObSEArray<ObRawExpr *, 4> post_group_by_exprs; // having, select, assign values exprs
|
|
ObSEArray<ObRawExpr *, 4> 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<ObSelectStmt &>(stmt);
|
|
if (sel_stmt.has_rollup()) {
|
|
// we can create spj view with group-by pushed down for normal group by
|
|
} else 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.is_scala_group_by()) {
|
|
// there is no group by clause, we can directly pullup subqueries
|
|
} 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<ObUpdateStmt &>(stmt);
|
|
if (OB_FAIL(ObTransformUtils::get_post_join_exprs(&stmt, pre_group_by_exprs, true))) {
|
|
LOG_WARN("failed to get post join exprs", 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<ObRawExpr *, 4> 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(stmt, expr, expr, JOIN_FIRST,
|
|
false, // is_select_item_expr, for aggr first only
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* @brief ObTransformAggrSubquery::get_trans_view
|
|
* @param stmt 待改写的查询
|
|
* @param view_stmt 分离后可改写的视图
|
|
* @param root_expr 包含子查询的表达式
|
|
* @param post_group_by 当前表达式是否需要在 group-by 之后处理
|
|
* @return
|
|
*/
|
|
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<ObSelectStmt &>(stmt).has_group_by()) {
|
|
has_groupby = true;
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
TableItem *table = NULL;
|
|
ObAliasRefRawExpr *alias = NULL;
|
|
bool push_group_by = stmt.is_select_stmt() && post_group_by && has_groupby;
|
|
bool push_vector_assign = stmt.is_update_stmt() && root_expr->has_flag(CNT_ALIAS);
|
|
if (!has_rownum && !has_groupby && stmt.is_select_stmt()) {
|
|
view_stmt = static_cast<ObSelectStmt *>(&stmt);
|
|
} else if (push_vector_assign &&
|
|
OB_FAIL(ObRawExprUtils::find_alias_expr(root_expr, alias))) {
|
|
LOG_WARN("failed to find alias expr", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::create_simple_view(ctx_, &stmt, view_stmt,
|
|
true, true, push_group_by,
|
|
alias))) {
|
|
LOG_WARN("failed to create simple view", K(ret));
|
|
} else if (push_group_by) {
|
|
view_stmt = static_cast<ObSelectStmt *>(&stmt);
|
|
}
|
|
}
|
|
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.has_for_update()
|
|
|| !stmt.is_sel_del_upd()) {
|
|
is_valid = false;
|
|
} else if (OB_FAIL(StmtUniqueKeyProvider::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_join_first_validity
|
|
* check the valiaidty of the subquery
|
|
*/
|
|
int ObTransformAggrSubquery::check_join_first_validity(ObQueryRefRawExpr &query_ref,
|
|
const bool is_vector_assign,
|
|
const bool in_exists,
|
|
const ObRawExpr &parent_expr,
|
|
const bool is_vector_cmp,
|
|
ObIArray<ObRawExpr *> &constraints,
|
|
bool &add_limit_constraints,
|
|
int64_t &limit_value,
|
|
bool &is_valid,
|
|
ObIArray<ObPCParamEqualInfo> &equal_param_info)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool has_rownum = false;
|
|
bool has_correlated_cond = false;
|
|
bool has_ref_assign_user_var = false;
|
|
bool is_correlated = false;
|
|
bool limit_to_aggr = false;
|
|
ObSEArray<ObRawExpr *, 4> nested_conditions;
|
|
ObSelectStmt *subquery = NULL;
|
|
is_valid = true;
|
|
bool is_group_single_set = false;
|
|
bool is_limit_single_set = false;
|
|
if (OB_ISNULL(subquery = query_ref.get_ref_stmt()) || OB_ISNULL(ctx_) || OB_ISNULL(ctx_->exec_ctx_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("subquery is null", K(ret));
|
|
} else if (OB_FAIL(check_nested_subquery(query_ref, is_valid))) {
|
|
LOG_WARN("failed to check nested subquery", K(ret));
|
|
} else if (!is_valid) {
|
|
OPT_TRACE("exec param reference another subquery");
|
|
// 0. check single set
|
|
} else if (OB_FAIL(check_single_set_subquery(*subquery,
|
|
is_group_single_set,
|
|
is_limit_single_set,
|
|
limit_value,
|
|
!in_exists))) {
|
|
LOG_WARN("failed to check is single set query", K(ret));
|
|
} else if (!is_group_single_set && !is_limit_single_set) {
|
|
is_valid = false;
|
|
OPT_TRACE("not single set query");
|
|
// 1. check stmt components
|
|
} else if (subquery->get_group_expr_size() > 0 ||
|
|
subquery->has_rollup() ||
|
|
subquery->has_window_function() ||
|
|
subquery->has_sequence() ||
|
|
subquery->is_set_stmt() ||
|
|
subquery->is_hierarchical_query()) {
|
|
is_valid = false;
|
|
LOG_TRACE("invalid subquery", K(is_valid), K(*subquery));
|
|
OPT_TRACE("subquery has rollup/win_func/sequence");
|
|
} else if (in_exists) {
|
|
if (!subquery->has_having()) {
|
|
is_valid = false;
|
|
LOG_TRACE("invalid [not] exists subquery", K(is_valid), K(*subquery));
|
|
OPT_TRACE("subquery do not has having")
|
|
} else if (OB_FAIL(check_subquery_having(query_ref, *subquery, is_valid))) {
|
|
LOG_WARN("failed to check subquery having", K(ret));
|
|
} else if (!is_valid) {
|
|
LOG_TRACE("invalid having clause", K(is_valid));
|
|
OPT_TRACE("having condition has subquery / do not has aggr");
|
|
} else if (!subquery->has_limit()) {
|
|
/*do nothing*/
|
|
} else if (OB_FAIL(check_limit_validity(*subquery,
|
|
add_limit_constraints,
|
|
limit_value,
|
|
is_valid))) {
|
|
LOG_WARN("check limit failed", K(ret));
|
|
}
|
|
} else {
|
|
if (subquery->has_having() ||
|
|
NULL != subquery->get_limit_percent_expr() ||
|
|
NULL != subquery->get_offset_expr()) {
|
|
is_valid = false;
|
|
LOG_TRACE("invalid subquery", K(is_valid), K(*subquery));
|
|
OPT_TRACE("subquery has having or limit percent or limit offset");
|
|
} else if (OB_FAIL(check_subquery_select(query_ref, is_valid))) {
|
|
LOG_WARN("failed to check subquery select", K(ret));
|
|
} else if (!is_valid) {
|
|
OPT_TRACE("subquery select item has subquery");
|
|
}
|
|
}
|
|
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));
|
|
OPT_TRACE("has rownum expr");
|
|
} 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));
|
|
OPT_TRACE("has assignment ref user variable");
|
|
} else if (!is_vector_assign && !in_exists && !is_vector_cmp && subquery->get_select_item_size() > 1) {
|
|
is_valid = false;
|
|
OPT_TRACE("more than one select item");
|
|
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
|
|
OPT_TRACE("subquery has AVG aggregation");
|
|
} else if (OB_FAIL(check_count_const_validity(*subquery, constraints, 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));
|
|
OPT_TRACE("exists COUNT(NULL)");
|
|
// never reach
|
|
// 3. check from list is not correlated
|
|
} else if (OB_FAIL(ObTransformUtils::is_table_item_correlated(
|
|
query_ref.get_exec_params(), *subquery, is_correlated))) {
|
|
LOG_WARN("failed to check subquery table item is correlated", K(ret));
|
|
} else if (is_correlated) {
|
|
is_valid = false;
|
|
OPT_TRACE("subquery`s table item is correlated");
|
|
// 4. check correlated join on contiditons
|
|
// 5. check correlated semi contiditons
|
|
} else if (OB_FAIL(ObTransformUtils::is_join_conditions_correlated(query_ref.get_exec_params(),
|
|
subquery,
|
|
is_correlated))) {
|
|
LOG_WARN("failed to check join condition correlated", K(ret));
|
|
} else if (is_correlated) {
|
|
is_valid = false;
|
|
OPT_TRACE("subquery`s outer/semi join on condition is correlated");
|
|
}
|
|
// 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->has_flag(CNT_COLUMN)) {
|
|
// do nothing
|
|
} else if (OB_FAIL(ObTransformUtils::is_correlated_expr(query_ref.get_exec_params(),
|
|
cond,
|
|
is_correlated))) {
|
|
LOG_WARN("failed to check is correlated expr", K(ret));
|
|
} else if (!is_correlated || !cond->has_flag(CNT_COLUMN)) {
|
|
// do nothing
|
|
} else if (cond->has_flag(CNT_SUB_QUERY)) {
|
|
is_valid = false;
|
|
LOG_TRACE("is not valid equal or common comparsion correlation", K(i), K(*cond));
|
|
OPT_TRACE("is not valid equal or common comparsion correlation");
|
|
} 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 (OB_FAIL(ret) || !is_valid) {
|
|
} else if (!has_correlated_cond) {
|
|
is_valid = false;
|
|
LOG_TRACE("no correlated common comparsion condition found", K(is_valid), K(has_correlated_cond));
|
|
OPT_TRACE("no correlated common comparsion condition found");
|
|
} else if (is_limit_single_set && OB_FAIL(check_join_first_condition_for_limit_1(query_ref, *subquery, is_valid))) {
|
|
LOG_WARN("failed to check condition for limit 1", K(ret));
|
|
} else if (!is_valid) {
|
|
// do nothing
|
|
} else if (is_limit_single_set &&
|
|
OB_FAIL(check_limit_single_set_validity(*subquery, parent_expr, is_valid, equal_param_info))) {
|
|
LOG_WARN("failed to check select validity for limit 1", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
int ObTransformAggrSubquery::check_limit_validity(ObSelectStmt &subquery,
|
|
bool &add_limit_constraints,
|
|
int64_t &limit_value,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObPhysicalPlanCtx *plan_ctx = NULL;
|
|
ObRawExpr *limit_expr = NULL;
|
|
add_limit_constraints = false;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->exec_ctx_) ||
|
|
OB_ISNULL(plan_ctx = ctx_->exec_ctx_->get_physical_plan_ctx())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("subquery is null", K(ret));
|
|
} else if (OB_ISNULL(limit_expr = subquery.get_limit_expr())
|
|
|| OB_UNLIKELY(T_INT != limit_expr->get_expr_type()
|
|
&& T_QUESTIONMARK != limit_expr->get_expr_type())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get unexpected limit expr", K(ret));
|
|
} else if (T_INT == limit_expr->get_expr_type() &&
|
|
0 == static_cast<ObConstRawExpr *>(limit_expr)->get_value().get_int()) {
|
|
is_valid = false;
|
|
LOG_TRACE("get limit 0 in [not] exists expr", K(is_valid));
|
|
} else if (T_QUESTIONMARK == limit_expr->get_expr_type()) {
|
|
int64_t const_value = OB_INVALID;
|
|
ObObj value;
|
|
int64_t idx = static_cast<ObConstRawExpr *>(limit_expr)->get_value().get_unknown();
|
|
const ParamStore ¶m_store = plan_ctx->get_param_store();
|
|
|
|
if (idx < 0 || idx >= param_store.count()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("idx is invalid", K(idx), K(param_store.count()), K(ret));
|
|
} else {
|
|
number::ObNumber number;
|
|
value = param_store.at(idx);
|
|
if (value.is_integer_type()) {
|
|
const_value = value.get_int();
|
|
} else if (value.is_number()) {
|
|
if (OB_FAIL(value.get_number(number))) {
|
|
LOG_WARN("failed to get number", K(ret));
|
|
} else if (OB_UNLIKELY(!number.is_valid_int64(const_value))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("number is not valid int64", K(ret), K(value), K(number));
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
} else if (FALSE_IT(limit_value = const_value)) {
|
|
} else if (const_value == 0) {
|
|
is_valid = false;
|
|
add_limit_constraints = true;
|
|
LOG_TRACE("get limit 0 in [not] exists expr", K(is_valid));
|
|
}
|
|
}
|
|
}
|
|
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<const ObRawExpr*, 4> 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(ObTransformUtils::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;
|
|
ObRawExpr *parent_expr_of_query_ref = 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())
|
|
|| OB_ISNULL(parent_expr_of_query_ref = trans_param.parent_expr_of_query_ref)) {
|
|
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));
|
|
}
|
|
|
|
// modify aggr param expr to deal with empty-set-selected situation
|
|
if (OB_SUCC(ret) && use_outer_join(trans_param.pullup_flag_)) {
|
|
if (OB_FAIL(modify_aggr_param_expr_for_outer_join(trans_param))) {
|
|
LOG_WARN("failed to modify subquery aggr param expr", K(ret));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
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<ObRawExpr *, 4> select_exprs;
|
|
if (OB_FAIL(subquery->get_select_exprs(select_exprs))) {
|
|
LOG_WARN("failed to get select exprs", K(ret));
|
|
} else if (OB_FAIL(modify_vector_comparison_expr_if_necessary(select_stmt, expr_factory, select_exprs, parent_expr_of_query_ref))) {
|
|
LOG_WARN("failed to modify vector comparison expr", K(ret));
|
|
} else if (OB_FAIL(select_stmt.replace_relation_exprs(trans_param.query_refs_,
|
|
select_exprs))) {
|
|
LOG_WARN("failed to update query ref value expr", K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
// pullup subquery component
|
|
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_check_constraint_items(),
|
|
subquery->get_check_constraint_items()))) {
|
|
LOG_WARN("failed to append check constraint items", 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(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(ObTransformUtils::decorrelate(&select_stmt, query_ref_expr->get_exec_params()))) {
|
|
LOG_WARN("failed to decorrelate stmt", 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));
|
|
} else if (OB_FAIL(add_trans_stmt_info(*subquery, JOIN_FIRST))) {
|
|
LOG_WARN("failed to add trans stmt info", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief when subquery returns vector results and is used in comparison expr, two modifications are needed
|
|
* 1. select exprs of subquery need to be integrated into a T_OP_ROW expr
|
|
* 2. parent expr of subquery should be replaced to a new one with common comparision
|
|
*/
|
|
int ObTransformAggrSubquery::modify_vector_comparison_expr_if_necessary(
|
|
ObSelectStmt &select_stmt, ObRawExprFactory *expr_factory, ObSEArray<ObRawExpr*, 4> &select_exprs, ObRawExpr *parent_expr_of_query_ref) {
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(expr_factory) || OB_ISNULL(parent_expr_of_query_ref) || select_exprs.count() == 0) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("invalid argument", K(ret), K(expr_factory), K(parent_expr_of_query_ref), K(select_exprs));
|
|
} else if (IS_SUBQUERY_COMPARISON_OP(parent_expr_of_query_ref->get_expr_type())) {
|
|
// construct a T_OP_ROW expr through vector projection term of subquery
|
|
ObOpRawExpr* op_row_expr;
|
|
ObSEArray<ObRawExpr*, 4> temp_expr;
|
|
if (OB_FAIL(ObTransformUtils::build_row_expr(*expr_factory, select_exprs, op_row_expr))) {
|
|
LOG_WARN("failed to create op row expr", K(ret));
|
|
} else if (OB_FAIL(temp_expr.push_back(op_row_expr))) {
|
|
LOG_WARN("failed to push back op row expr", K(ret));
|
|
}
|
|
select_exprs.assign(temp_expr);
|
|
// replace parent_expr_of_query_ref with subquery comparison operator to the one of common comparison operator
|
|
ObItemType value_cmp_type = T_INVALID;
|
|
if (FAILEDx(ObTransformUtils::query_cmp_to_value_cmp(parent_expr_of_query_ref->get_expr_type(), value_cmp_type))) {
|
|
LOG_WARN("unexpected root_expr type", K(ret), K(value_cmp_type));
|
|
} else {
|
|
ObOpRawExpr *new_parent_expr = NULL;
|
|
ObSEArray<ObRawExpr*, 1> old_exprs;
|
|
ObSEArray<ObRawExpr*, 1> new_exprs;
|
|
|
|
if (OB_FAIL(expr_factory->create_raw_expr(value_cmp_type, new_parent_expr))) {
|
|
LOG_WARN("failed to build expr", K(ret), K(new_parent_expr));
|
|
} else {
|
|
for (int64_t i=0; i < parent_expr_of_query_ref->get_param_count(); i++) {
|
|
if (OB_FAIL(new_parent_expr->add_param_expr(parent_expr_of_query_ref->get_param_expr(i)))) {
|
|
LOG_WARN("failed to add param expr", K(ret));
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
} else if (OB_FAIL(old_exprs.push_back(parent_expr_of_query_ref)) ||
|
|
OB_FAIL(new_exprs.push_back(new_parent_expr))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
} else if (OB_FAIL(select_stmt.replace_relation_exprs(old_exprs, new_exprs))) {
|
|
LOG_WARN("failed to replace expr in stmt", K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* 获取from list中所有基表的primary key
|
|
*/
|
|
int ObTransformAggrSubquery::get_unique_keys(ObDMLStmt &stmt,
|
|
ObIArray<ObRawExpr *> &pkeys,
|
|
const bool is_first_trans)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSqlBitSet<> empty_ignore_tables;
|
|
TableItem *cur_table = NULL;
|
|
const ObQueryHint *query_hint = NULL;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_) ||
|
|
OB_ISNULL(query_hint = stmt.get_stmt_hint().query_hint_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("transform context is invalid", K(ret), K(ctx_), K(query_hint));
|
|
} else if (is_first_trans) {
|
|
// 第一次改写时需要生成所有from table的unique 可以
|
|
StmtUniqueKeyProvider unique_key_provider(false);
|
|
if (OB_FAIL(unique_key_provider.generate_unique_key(ctx_, &stmt, empty_ignore_tables, pkeys))) {
|
|
LOG_WARN("failed to generate unique key", K(ret));
|
|
}
|
|
} else if ((query_hint->has_outline_data() && stmt.get_table_items().count() < 1) ||
|
|
(!query_hint->has_outline_data() && 1 != stmt.get_table_items().count())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get unexpected table itemc", K(ret), K(stmt.get_table_items().count()));
|
|
} else if (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 itemc", K(ret), K(cur_table));
|
|
} else {
|
|
// 已经改写过了,可以直接取改写视图中的group by列
|
|
ObSelectStmt *view_stmt = NULL;
|
|
ObSEArray<ObRawExpr *, 4> select_list;
|
|
ObSEArray<ObRawExpr *, 4> column_list;
|
|
ObSEArray<ObRawExpr *, 4> group_keys;
|
|
ObRawExprCopier copier(*ctx_->expr_factory_);
|
|
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(stmt.get_view_output(*cur_table, select_list, column_list))) {
|
|
LOG_WARN("failed to get view output", K(ret));
|
|
} else if (OB_FAIL(copier.add_replaced_expr(select_list, column_list))) {
|
|
LOG_WARN("failed to replace pair", K(ret));
|
|
} else if (OB_FAIL(copier.copy_on_replace(view_stmt->get_group_exprs(), group_keys))) {
|
|
LOG_WARN("failed to copy on replace exprs", 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;
|
|
// 会生成一个新的joined table, 不需要append subquery的joined table
|
|
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<ObRawExpr *, 8> where_conditions;
|
|
ObIArray<ObRawExpr *> *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 ObQueryRefRawExpr &query_ref,
|
|
const ObSelectStmt &subquery,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
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);
|
|
bool bret = false;
|
|
if (OB_ISNULL(cur_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get null expr", K(ret));
|
|
} else if (!cur_expr->has_flag(CNT_AGG) || cur_expr->has_flag(CNT_SUB_QUERY)) {
|
|
is_valid = false;
|
|
LOG_TRACE("invalid select item", K(is_valid));
|
|
} else if (OB_FAIL(ObTransformUtils::is_correlated_expr(query_ref.get_exec_params(),
|
|
cur_expr,
|
|
bret))) {
|
|
LOG_WARN("failed to check is correlated expr", K(ret));
|
|
} else if (bret) {
|
|
is_valid = false;
|
|
LOG_TRACE("having condition is correlated", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::check_single_set_subquery(const ObSelectStmt &subquery,
|
|
bool &group_single_set,
|
|
bool &limit_single_set,
|
|
int64_t &limit_value,
|
|
bool check_limit /*= true*/)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
group_single_set = false;
|
|
limit_single_set = false;
|
|
bool valid_group_by = false;
|
|
bool is_null_value = true;
|
|
bool has_limit = false;
|
|
if (OB_FAIL(is_valid_group_by(subquery, valid_group_by))) {
|
|
LOG_WARN("failed to check is valid group by", K(ret));
|
|
} else if (check_limit && NULL != subquery.get_limit_expr()) {
|
|
has_limit = true;
|
|
ObPhysicalPlanCtx* plan_ctx = NULL;
|
|
ObRawExpr* limit_expr = subquery.get_limit_expr();
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->exec_ctx_) ||
|
|
OB_ISNULL(plan_ctx = ctx_->exec_ctx_->get_physical_plan_ctx()) ||
|
|
OB_ISNULL(ctx_->allocator_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::get_expr_int_value(limit_expr,
|
|
&plan_ctx->get_param_store(),
|
|
ctx_->exec_ctx_,
|
|
ctx_->allocator_,
|
|
limit_value,
|
|
is_null_value))) {
|
|
LOG_WARN("failed to get limit int value", K(ret));
|
|
}
|
|
}
|
|
|
|
// is_valid is true iff
|
|
if (OB_FAIL(ret)) {
|
|
} else if (valid_group_by) {
|
|
// 1. subquery is a valid group by (with no limit or limit > 0); or
|
|
group_single_set = !check_limit || !has_limit || (!is_null_value && limit_value >= 1);
|
|
} else if (check_limit && !subquery.has_group_by()) {
|
|
// 2. subquery is not group by but has limit 1 and is not with tie
|
|
limit_single_set = !is_null_value && limit_value == 1 &&
|
|
!subquery.is_fetch_with_ties();
|
|
}
|
|
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,
|
|
ObIArray<ObRawExpr *> &constraints,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
is_valid = true;
|
|
ObPhysicalPlanCtx *plan_ctx = NULL;
|
|
ObNotNullContext not_null_ctx;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->allocator_) || OB_ISNULL(ctx_->exec_ctx_) ||
|
|
OB_ISNULL(plan_ctx = ctx_->exec_ctx_->get_physical_plan_ctx())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret));
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i< subquery.get_aggr_item_size(); i++) {
|
|
bool is_count_const = false;
|
|
bool is_not_null = false;
|
|
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(ObTransformUtils::is_expr_not_null(not_null_ctx,
|
|
agg_expr->get_param_expr(0),
|
|
is_not_null,
|
|
&constraints))) {
|
|
LOG_WARN("failed to check expr not null", K(ret));
|
|
} else if (!is_not_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::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 modify_aggr_param_expr_for_outer_join
|
|
* rules for modifying aggr param expr:
|
|
* 1. when pullup strategy is determined to use INNER JOIN, aggr param expr in subquery should NOT be modified
|
|
* 2. when aggr param expr has null-propagate property, it should NOT be modified
|
|
* 3. when aggr item matches COUNT(CONST) pattern, the CONST param expr should be modified to a not-null-column-expr
|
|
* 4. In the remaining cases, aggr param expr should be modified as
|
|
* "CASE WHEN not-null-column-expr is not null THEN original-agg ELSE null END"
|
|
*/
|
|
int ObTransformAggrSubquery::modify_aggr_param_expr_for_outer_join(TransformParam& trans_param)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSelectStmt *subquery = NULL;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_)
|
|
|| OB_ISNULL(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(trans_param), K(subquery));
|
|
}
|
|
|
|
if (OB_SUCC(ret) && use_outer_join(trans_param.pullup_flag_)) {
|
|
// collect related columns for null propagate check
|
|
ObSEArray<const ObRawExpr*, 4> columns;
|
|
ObSEArray<ObRawExpr*, 4> tmp;
|
|
if (OB_FAIL(subquery->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));
|
|
}
|
|
|
|
// check && modify param expr for each aggr item
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < subquery->get_aggr_item_size(); ++i) {
|
|
ObRawExpr* aggr = subquery->get_aggr_item(i);
|
|
// check null-propagate && count const
|
|
bool is_null_propagate = false;
|
|
bool is_count_const = false;
|
|
if (OB_ISNULL(aggr)) {
|
|
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 (0 == aggr->get_param_count()) {
|
|
// do nothing
|
|
} else if (OB_FAIL(ObTransformUtils::is_null_propagate_expr(aggr->get_param_expr(0), columns, is_null_propagate))) {
|
|
LOG_WARN("failed to check is null propagate expr", K(ret));
|
|
}
|
|
|
|
// modify aggr param expr
|
|
if (is_null_propagate) {
|
|
// do nothing
|
|
} else if (is_count_const) {
|
|
if (OB_FAIL(replace_count_const(static_cast<ObAggFunRawExpr*>(aggr), trans_param.not_null_expr_))) {
|
|
LOG_WARN("failed to replace count const expr", K(ret));
|
|
}
|
|
} else {
|
|
// replace aggr param expr with case when expr
|
|
ObRawExpr* case_when_expr = NULL;
|
|
ObRawExpr* not_null_column = trans_param.not_null_expr_;
|
|
ObRawExpr* then_expr = aggr->get_param_expr(0);
|
|
ObRawExpr* else_expr = NULL;
|
|
if (OB_FAIL(ObRawExprUtils::build_null_expr(*ctx_->expr_factory_, else_expr))) {
|
|
LOG_WARN("failed to create const null expr", K(ret));
|
|
} else if (OB_FAIL(ObRawExprUtils::try_add_cast_expr_above(ctx_->expr_factory_, ctx_->session_info_, *else_expr, then_expr->get_result_type(), else_expr))) {
|
|
LOG_WARN("failed to modify result type of null expr", K(ret));
|
|
} else if (OB_ISNULL(not_null_column)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("not null column is invalid", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::build_case_when_expr(*subquery, not_null_column, then_expr, else_expr, case_when_expr, ctx_))) {
|
|
LOG_WARN("failed to build case when expr", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::replace_expr(then_expr, case_when_expr, aggr))) {
|
|
LOG_WARN("failed to build case when expr", K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 not null propagate && there is no not null column found
|
|
*/
|
|
int ObTransformAggrSubquery::check_can_use_outer_join(TransformParam ¶m,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObQueryRefRawExpr *query_ref = param.ja_query_ref_;
|
|
ObRawExpr *¬_null_expr = param.not_null_expr_;
|
|
ObSelectStmt *stmt = NULL;
|
|
bool has_nested = false;
|
|
bool limit_to_aggr = false;
|
|
if (OB_ISNULL(param.ja_query_ref_) ||
|
|
OB_ISNULL(stmt = param.ja_query_ref_->get_ref_stmt()) ||
|
|
OB_ISNULL(ctx_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("invalid transform param", K(ret));
|
|
} else if (FALSE_IT(limit_to_aggr = !stmt->has_group_by() &&
|
|
!param.limit_for_exists_ &&
|
|
param.limit_value_ == 1)) {
|
|
//never reach
|
|
} else if (OB_FAIL(ObTransformUtils::has_nested_subquery(query_ref, has_nested))) {
|
|
LOG_WARN("failed to check has nested subquery", K(ret));
|
|
} else {
|
|
is_valid = stmt->get_semi_info_size() == 0 && !has_nested;
|
|
if (!is_valid) {
|
|
OPT_TRACE("subquery has semi/anti join or has nested subquery, can not transform");
|
|
}
|
|
}
|
|
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);
|
|
if (!is_valid) {
|
|
OPT_TRACE("subquery`s where condition contain subquery, can not transform");
|
|
}
|
|
}
|
|
}
|
|
// reject outer join rewrite if any agg item satisfy: agg-param-expr not null-propagate && find no not-null-column
|
|
if (OB_SUCC(ret) && is_valid) {
|
|
if (not_null_expr != NULL) {
|
|
// do nothing
|
|
} else if (OB_FAIL(ObTransformUtils::find_not_null_expr(*stmt, not_null_expr, is_valid, ctx_))) {
|
|
LOG_WARN("failed to find not null expr", K(ret));
|
|
} else if (!is_valid) {
|
|
// collect related columns
|
|
ObSEArray<const ObRawExpr*, 4> columns;
|
|
if (OB_SUCC(ret) && is_valid) {
|
|
ObSEArray<ObRawExpr*, 4> 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));
|
|
}
|
|
}
|
|
// check null-propagate property for all agg-param-expr
|
|
bool all_agg_param_null_propagate = true;
|
|
for (int64_t i = 0; OB_SUCC(ret) && all_agg_param_null_propagate && i < stmt->get_aggr_item_size(); ++i) {
|
|
ObRawExpr* aggr = stmt->get_aggr_item(i);
|
|
bool is_null_propagate = false;
|
|
if (OB_ISNULL(aggr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("aggregation expr is invalid", K(ret));
|
|
} else if (0 == aggr->get_param_count()) {
|
|
all_agg_param_null_propagate = false;
|
|
} else if (OB_FAIL(ObTransformUtils::is_null_propagate_expr(aggr->get_param_expr(0), columns, is_null_propagate))) {
|
|
LOG_WARN("failed to check is null propagate expr", K(ret));
|
|
} else {
|
|
all_agg_param_null_propagate = is_null_propagate;
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && all_agg_param_null_propagate && limit_to_aggr) {
|
|
if (OB_UNLIKELY(stmt->get_select_item_size() != 1)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpeced select item size", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::is_null_propagate_expr(stmt->get_select_item(0).expr_,
|
|
columns,
|
|
all_agg_param_null_propagate))) {
|
|
LOG_WARN("failed to check is null propagate expr", K(ret));
|
|
}
|
|
}
|
|
is_valid = all_agg_param_null_propagate;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::fill_query_refs(ObDMLStmt *stmt,
|
|
bool cnt_alias,
|
|
TransformParam ¶m)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
param.query_refs_.reuse();
|
|
if (OB_ISNULL(stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("params are invalid" ,K(ret), K(stmt));
|
|
} else if (cnt_alias) {
|
|
if (stmt->is_update_stmt()) {
|
|
if (OB_FAIL(static_cast<ObUpdateStmt*>(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<ObRawExpr *, 8> sel_exprs;
|
|
if (OB_FAIL(static_cast<ObSelectStmt *>(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<ObRawExpr *, 4> const_exprs;
|
|
if (OB_FAIL(ObOptimizerUtil::compute_const_exprs(subquery.get_condition_exprs(),
|
|
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 (subquery.get_group_exprs().at(i)->is_const_expr()) {
|
|
// 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<const ObSelectStmt&>(stmt).get_having_exprs(),
|
|
expr);
|
|
}
|
|
return bret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::get_filters(ObDMLStmt &stmt,
|
|
ObRawExpr *expr,
|
|
ObIArray<ObRawExpr *> &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<ObSelectStmt &>(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;
|
|
}
|
|
|
|
/**
|
|
* 获取不需要改写的子查询
|
|
* 目前暂时不支持join reorder, select item中的子查询如果做了JA改写可能会出现无法走nest loop 条件下推的问题
|
|
* 因此如果select item中子查询的相关连接条件中内表的列上有索引可用, 就不做JA改写了。
|
|
*/
|
|
int ObTransformAggrSubquery::extract_no_rewrite_select_exprs(ObDMLStmt *&stmt)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(stmt) || OB_ISNULL(ctx_) || OB_ISNULL(stmt->get_query_ctx())) {
|
|
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<ObSelectStmt *>(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<ObQueryRefRawExpr *>(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().has_enable_hint(T_UNNEST) ||
|
|
subquery->get_stmt_hint().has_enable_hint(T_AGGR_FIRST_UNNEST) ||
|
|
subquery->get_stmt_hint().has_enable_hint(T_JOIN_FIRST_UNNEST)) {
|
|
//do nothing
|
|
} else if (OB_FAIL(ObTransformUtils::check_subquery_match_index(ctx_,
|
|
static_cast<ObQueryRefRawExpr *>(expr),
|
|
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;
|
|
}
|
|
ObHint* ObTransformAggrSubquery::get_sub_unnest_hint(ObSelectStmt &subquery,
|
|
int64_t pullup_strategy)
|
|
{
|
|
ObHint *myhint = NULL;
|
|
if (aggr_first(pullup_strategy)) {
|
|
myhint = subquery.get_stmt_hint().get_normal_hint(T_AGGR_FIRST_UNNEST);
|
|
} else if (join_first(pullup_strategy)) {
|
|
myhint = subquery.get_stmt_hint().get_normal_hint(T_JOIN_FIRST_UNNEST);
|
|
}
|
|
return myhint;
|
|
}
|
|
|
|
ObItemType ObTransformAggrSubquery::get_unnest_strategy(int64_t pullup_strategy) {
|
|
ObItemType hint = T_INVALID;
|
|
if (aggr_first(pullup_strategy)) {
|
|
hint = T_AGGR_FIRST_UNNEST;
|
|
} else if (join_first(pullup_strategy)) {
|
|
hint = T_JOIN_FIRST_UNNEST;
|
|
}
|
|
return hint;
|
|
}
|
|
int ObTransformAggrSubquery::check_hint_allowed_unnest(ObDMLStmt &stmt,
|
|
ObSelectStmt &subquery,
|
|
const int64_t hint_loc,
|
|
const int64_t pullup_strategy,
|
|
bool &allowed)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
allowed = true;
|
|
const ObQueryHint *query_hint = stmt.get_stmt_hint().query_hint_;
|
|
const ObHint *unnest_hint = subquery.get_stmt_hint().get_normal_hint(T_UNNEST);
|
|
const ObHint *subhint = get_sub_unnest_hint(subquery, pullup_strategy);
|
|
bool is_enable = false;
|
|
bool is_disable = false;
|
|
bool is_unnest_enable = false;
|
|
bool is_unnest_disable = false;
|
|
if (NULL != unnest_hint) {
|
|
is_unnest_enable = unnest_hint->is_enable_hint();
|
|
is_unnest_disable = unnest_hint->is_disable_hint();
|
|
}
|
|
if (NULL != subhint) {
|
|
is_enable = subhint->is_enable_hint();
|
|
is_disable = subhint->is_disable_hint();
|
|
}
|
|
const ObHint *no_rewrite1 = stmt.get_stmt_hint().get_no_rewrite_hint();
|
|
const ObHint *no_rewrite2 = subquery.get_stmt_hint().get_no_rewrite_hint();
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(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
|
|
allowed = query_hint->is_valid_outline_transform(hint_loc, subhint);
|
|
} else {
|
|
if (is_enable || (!is_disable && is_unnest_enable)) {
|
|
// enable hint
|
|
allowed = true;
|
|
} else if (NULL != no_rewrite1 || NULL != no_rewrite2 || is_disable || is_unnest_disable) {
|
|
// add disable transform hint here
|
|
allowed = false;
|
|
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(subhint))) {
|
|
LOG_WARN("failed to add used trans hint", K(ret));
|
|
} else if (is_unnest_disable && OB_FAIL(ctx_->add_used_trans_hint(unnest_hint))) {
|
|
LOG_WARN("failed to add used trans hint", K(ret));
|
|
}
|
|
} else { /* default enable */ }
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::add_trans_stmt_info(ObSelectStmt &subquery, int64_t flag)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
TransStmtInfo info;
|
|
info.pullup_strategy_ = flag;
|
|
info.unnest_ = get_sub_unnest_hint(subquery, flag);
|
|
if (NULL == info.unnest_) {
|
|
info.unnest_ = get_hint(subquery.get_stmt_hint());
|
|
}
|
|
if (OB_FAIL(subquery.get_qb_name(info.qb_name_))) {
|
|
LOG_WARN("failed to get qb name", K(ret));
|
|
} else if (OB_FAIL(trans_stmt_infos_.push_back(info))) {
|
|
LOG_WARN("failed to push back trans stmt info", K(ret), K(info));
|
|
} else {
|
|
ctx_->trans_list_loc_++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* select B.c1 from B where A.c2 = B.c2 limit 1;
|
|
* => select min(B.c1) from B where A.c2 = B.c2;
|
|
*
|
|
* MySQL
|
|
* select B.c1 from B where A.c2 = B.c2 order by B.c1 desc limit 1;
|
|
* => select max(B.c1) from B where A.c2 = B.c2;
|
|
*
|
|
* Oracle
|
|
* select B.c1 from B where A.c2 = B.c2 order by B.c1 asc fetch next 1 row only;
|
|
* => select min(B.c1) from B where A.c2 = B.c2;
|
|
*/
|
|
int ObTransformAggrSubquery::convert_limit_as_aggr(ObSelectStmt *subquery,
|
|
TransformParam &trans_param)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObRawExpr* expr = NULL;
|
|
if (OB_ISNULL(subquery)
|
|
|| OB_ISNULL(ctx_->expr_factory_)
|
|
|| OB_ISNULL(ctx_->session_info_)
|
|
|| OB_UNLIKELY(!subquery->has_group_by() && 1 != trans_param.limit_value_)
|
|
|| OB_UNLIKELY(!subquery->has_group_by() && 1 != subquery->get_select_item_size())
|
|
|| OB_ISNULL(expr = subquery->get_select_item(0).expr_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected param", K(ret), KPC(subquery));
|
|
} else {
|
|
// If there is already aggr, do not add new aggr
|
|
// If there is limit 1, add MIN to a column
|
|
bool already_has_group = subquery->has_group_by();
|
|
if (!already_has_group) {
|
|
ObAggFunRawExpr* aggr_expr = NULL;
|
|
ObItemType aggr_type = T_FUN_MIN;
|
|
if (subquery->has_order_by()) {
|
|
OrderItem& first_order = subquery->get_order_item(0);
|
|
if (NULLS_LAST_ASC == first_order.order_type_) {
|
|
aggr_type = T_FUN_MIN;
|
|
} else if (NULLS_LAST_DESC == first_order.order_type_) {
|
|
aggr_type = T_FUN_MAX;
|
|
} else {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected order type", K(ret));
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
} else if (OB_FAIL(ObRawExprUtils::build_common_aggr_expr(*ctx_->expr_factory_,
|
|
ctx_->session_info_,
|
|
aggr_type,
|
|
expr,
|
|
aggr_expr))) {
|
|
LOG_WARN("failed to build aggr expr", K(ret));
|
|
} else if (OB_FAIL(subquery->add_agg_item(*aggr_expr))) {
|
|
LOG_WARN("failed to add aggr expr", K(ret));
|
|
} else {
|
|
subquery->get_select_item(0).expr_ = aggr_expr;
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret) &&
|
|
OB_FAIL(eliminate_limit_if_need(*subquery, trans_param, false))) {
|
|
LOG_WARN("failed to eliminate limit if need", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::eliminate_limit_if_need(ObSelectStmt &subquery,
|
|
TransformParam &trans_param,
|
|
bool in_exist)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObConstRawExpr* one_expr = NULL;
|
|
ObRawExpr* cmp_expr = NULL;
|
|
ObRawExpr* limit_expr = NULL;
|
|
bool already_has_group = subquery.has_group_by();
|
|
ObItemType cmp_type = (already_has_group || in_exist) ? T_OP_GE : T_OP_EQ;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret));
|
|
} else if (OB_ISNULL(limit_expr = subquery.get_limit_expr())) {
|
|
// do nothing
|
|
} else if (OB_FAIL(ObRawExprUtils::build_const_int_expr(*ctx_->expr_factory_,
|
|
ObIntType, 1, one_expr))) {
|
|
LOG_WARN("fail to build int expr", K(ret));
|
|
} else if (OB_FAIL(ObRawExprUtils::create_double_op_expr(*ctx_->expr_factory_,
|
|
ctx_->session_info_,
|
|
cmp_type,
|
|
cmp_expr,
|
|
limit_expr,
|
|
one_expr))) {
|
|
LOG_WARN("fail to build cmp expr");
|
|
} else if (OB_FAIL(ObTransformUtils::add_param_bool_constraint(ctx_,
|
|
cmp_expr,
|
|
true/*is_true*/))) {
|
|
LOG_WARN("failed to add constraints", K(ret));
|
|
} else if (OB_FAIL(append(ctx_->equal_param_constraints_, trans_param.equal_param_info_))) {
|
|
LOG_WARN("append equal param info failed", K(ret));
|
|
} else {
|
|
subquery.set_limit_offset(NULL, NULL);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::check_nested_subquery(ObQueryRefRawExpr &query_ref,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < query_ref.get_exec_params().count(); i++) {
|
|
ObExecParamRawExpr* exec_param = NULL;
|
|
ObRawExpr* outer_expr = NULL;
|
|
if (OB_ISNULL(exec_param = query_ref.get_exec_params().at(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret));
|
|
} else if (OB_ISNULL(outer_expr = exec_param->get_ref_expr())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret));
|
|
} else if (outer_expr->has_flag(CNT_SUB_QUERY)) {
|
|
is_valid = false;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// for any/all subqueries used as condition,some of them can be converted to exists subquery.
|
|
// furthermore, such exists subquery can be considered as a scalar subquery. e.g.
|
|
// select case when c1 = ANY(select c1 from t2) then 1 else 0 end from t1;
|
|
// ==>
|
|
// select case when exists (select c1 from t2 where t1.c1 = t2.c1) then 1 else 0 end from t1;
|
|
// ==>
|
|
// select case when 0 < (select count(*) from t2 where t1.c1 = t2.c1) then 1 else 0 end from t1;
|
|
int ObTransformAggrSubquery::check_can_trans_any_all_as_scalar_subquery(ObDMLStmt &stmt,
|
|
ObSelectStmt *subquery,
|
|
ObRawExpr *parent_expr,
|
|
ObRawExpr *root_expr,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
is_valid = false;
|
|
bool has_rownum = false;
|
|
if (OB_ISNULL(subquery) || OB_ISNULL(parent_expr) || OB_ISNULL(root_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret));
|
|
} else if (parent_expr->get_expr_type() != T_OP_SQ_EQ || !parent_expr->has_flag(IS_WITH_ANY)) {
|
|
// only pure equal correlated condition can be converted to aggr-first join form
|
|
} else if (OB_FAIL(ObTransformUtils::check_expr_used_as_condition(&stmt,
|
|
root_expr,
|
|
parent_expr,
|
|
is_valid))) {
|
|
LOG_WARN("failed to check expr as condition", K(ret));
|
|
} else if (!is_valid) {
|
|
// do nothing
|
|
} else if (OB_FAIL(ObTransformUtils::check_can_trans_any_all_as_exists(ctx_,
|
|
parent_expr,
|
|
true,
|
|
false,
|
|
is_valid))) {
|
|
LOG_WARN("failed to check can trans any all as exists", 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 (subquery->has_group_by() ||
|
|
subquery->has_window_function() ||
|
|
subquery->has_limit() ||
|
|
subquery->has_sequence() ||
|
|
subquery->is_set_stmt() ||
|
|
subquery->is_hierarchical_query() ||
|
|
has_rownum) {
|
|
is_valid = false;
|
|
} else {
|
|
OPT_TRACE("can convert any all as scalar subquery");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// some exists subqueries can be considered as scalar subqueries, e.g.
|
|
// select exists (select 1 from t2 where t1.c1 = t2.c1) from t1;
|
|
// ==>
|
|
// select 0 < (select count(*) from t2 where t1.c1 = t2.c1) from t1;
|
|
int ObTransformAggrSubquery::check_can_trans_exists_as_scalar_subquery(ObQueryRefRawExpr &query_ref,
|
|
ObRawExpr &parent_expr,
|
|
int64_t limit_value,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool has_rownum = false;
|
|
is_valid = false;
|
|
ObSelectStmt *subquery = query_ref.get_ref_stmt();
|
|
if (parent_expr.get_expr_type() != T_OP_EXISTS && parent_expr.get_expr_type() != T_OP_NOT_EXISTS) {
|
|
// do nothing
|
|
} else if (OB_ISNULL(subquery)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret));
|
|
} else if (OB_FAIL(subquery->has_rownum(has_rownum))) {
|
|
LOG_WARN("failed to check subquery has rownum", K(ret));
|
|
} else if (subquery->has_group_by() ||
|
|
subquery->has_window_function() ||
|
|
NULL != subquery->get_limit_percent_expr() ||
|
|
NULL != subquery->get_offset_expr() ||
|
|
subquery->has_sequence() ||
|
|
subquery->is_set_stmt() ||
|
|
subquery->is_hierarchical_query() ||
|
|
has_rownum) {
|
|
// do nothing
|
|
} else if (OB_NOT_NULL(subquery->get_limit_expr()) && limit_value < 1) {
|
|
// do nothing
|
|
} else {
|
|
is_valid = true;
|
|
OPT_TRACE("can convert exists as scalar subquery");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::convert_exists_as_scalar_subquery(ObDMLStmt *stmt,
|
|
ObQueryRefRawExpr *query_ref,
|
|
ObSelectStmt *subquery,
|
|
TransformParam &trans_param)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObRawExpr *parent_expr = NULL;
|
|
ObAggFunRawExpr *count_expr = NULL;
|
|
ObItemType cmp_type = T_OP_GT;
|
|
ObRawExpr *cmp_expr = NULL;
|
|
ObConstRawExpr *zero_expr = NULL;
|
|
ObSEArray<ObRawExpr*, 2> from_exprs;
|
|
ObSEArray<ObRawExpr*, 2> to_exprs;
|
|
if (OB_ISNULL(query_ref) || OB_ISNULL(subquery) || OB_ISNULL(ctx_) ||
|
|
OB_ISNULL(ctx_->expr_factory_) || OB_ISNULL(stmt) ||
|
|
OB_ISNULL(parent_expr = trans_param.parent_expr_of_query_ref) ||
|
|
(parent_expr->get_expr_type() != T_OP_EXISTS && parent_expr->get_expr_type() != T_OP_NOT_EXISTS)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected param", K(ret));
|
|
// 1. repalce subquery's select items as count(*)
|
|
} else if (OB_FAIL(ctx_->expr_factory_->create_raw_expr(T_FUN_COUNT, count_expr))) {
|
|
LOG_WARN("create ObAggFunRawExpr failed", K(ret));
|
|
} else if (OB_ISNULL(count_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("agg expr is null", K(ret), K(count_expr));
|
|
} else if (OB_FAIL(count_expr->formalize(ctx_->session_info_))) {
|
|
LOG_WARN("failed to extract info", K(ret));
|
|
} else if (OB_FALSE_IT(subquery->get_select_items().reset())) {
|
|
} else if (OB_FALSE_IT(query_ref->get_column_types().reset())) {
|
|
} else if (OB_FALSE_IT(query_ref->set_output_column(1))) {
|
|
} else if (OB_FAIL(query_ref->get_column_types().push_back(count_expr->get_result_type()))) {
|
|
LOG_WARN("failed to add result type", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::create_select_item(*ctx_->allocator_,
|
|
count_expr,
|
|
subquery))) {
|
|
LOG_WARN("failed to create select item", K(ret));
|
|
} else if (OB_FAIL(subquery->add_agg_item(*count_expr))) {
|
|
LOG_WARN("failed to add agg item", K(ret));
|
|
// 2. replace `exists (select .. from ..)` as `(select count(*) from ...) > 0`
|
|
} else if (OB_FALSE_IT(query_ref->set_is_set(false))) {
|
|
} else if (is_oracle_mode() && OB_FAIL(ObRawExprUtils::build_const_number_expr(
|
|
*ctx_->expr_factory_,
|
|
ObNumberType,
|
|
number::ObNumber::get_zero(),
|
|
zero_expr))) {
|
|
LOG_WARN("failed to build const one", K(ret));
|
|
} else if (!is_oracle_mode() && OB_FAIL(ObRawExprUtils::build_const_int_expr(
|
|
*ctx_->expr_factory_,
|
|
ObIntType,
|
|
0,
|
|
zero_expr))) {
|
|
LOG_WARN("failed to build const one", K(ret));
|
|
} else if (OB_FALSE_IT(cmp_type = (parent_expr->get_expr_type() == T_OP_EXISTS) ? T_OP_GT : T_OP_LE)) {
|
|
} else if (OB_FAIL(ObRawExprUtils::build_common_binary_op_expr(*ctx_->expr_factory_,
|
|
cmp_type,
|
|
query_ref,
|
|
zero_expr,
|
|
cmp_expr))) {
|
|
LOG_WARN("failed to build common binary op expr", K(ret));
|
|
} else if (OB_ISNULL(cmp_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("agg expr is null", K(ret), K(count_expr));
|
|
} else if (OB_FAIL(cmp_expr->formalize(ctx_->session_info_))) {
|
|
LOG_WARN("failed to formalize expr", K(ret));
|
|
} else if (OB_FAIL(from_exprs.push_back(parent_expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
} else if (OB_FAIL(to_exprs.push_back(cmp_expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
} else if (OB_FAIL(stmt->replace_relation_exprs(from_exprs, to_exprs))) {
|
|
LOG_WARN("failed to replace inner stmt expr", K(ret));
|
|
} else {
|
|
trans_param.parent_expr_of_query_ref = cmp_expr;
|
|
}
|
|
|
|
if (OB_SUCC(ret) &&
|
|
OB_FAIL(eliminate_limit_if_need(*subquery, trans_param, false))) {
|
|
LOG_WARN("failed to eliminate limit if need", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformAggrSubquery::convert_any_all_as_scalar_subquery(ObDMLStmt *stmt,
|
|
ObQueryRefRawExpr *query_ref,
|
|
ObSelectStmt *&subquery,
|
|
TransformParam &trans_param)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObRawExpr *parent_expr = trans_param.parent_expr_of_query_ref;
|
|
bool trans_happened = false;
|
|
ObSEArray<ObRawExpr*, 2> from_exprs;
|
|
ObSEArray<ObRawExpr*, 2> to_exprs;
|
|
trans_param.nested_conditions_.reset();
|
|
if (OB_ISNULL(query_ref) || OB_ISNULL(subquery) ||
|
|
OB_ISNULL(stmt) || OB_ISNULL(ctx_) || OB_ISNULL(parent_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected param", K(ret));
|
|
} else if (OB_ISNULL(parent_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected param", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::do_trans_any_all_as_exists(ctx_,
|
|
parent_expr,
|
|
NULL,
|
|
trans_happened))) {
|
|
LOG_WARN("failed to transform any all as exists", K(ret));
|
|
} else if (OB_ISNULL(subquery = query_ref->get_ref_stmt())) {
|
|
// during the process of rewriting any/all as exists, it might be necessary to generate a SPJ view,
|
|
// and the subquery could change, which needs to be updated.
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected param", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::get_correlated_conditions(query_ref->get_exec_params(),
|
|
subquery->get_condition_exprs(),
|
|
trans_param.nested_conditions_))) {
|
|
LOG_WARN("failed to get correlated conditions", K(ret));
|
|
} else if (OB_FAIL(from_exprs.push_back(trans_param.parent_expr_of_query_ref))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
} else if (OB_FAIL(to_exprs.push_back(parent_expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
} else if (OB_FAIL(stmt->replace_relation_exprs(from_exprs, to_exprs))) {
|
|
LOG_WARN("failed to replace inner stmt expr", K(ret));
|
|
} else if (OB_FALSE_IT(trans_param.parent_expr_of_query_ref = parent_expr)) {
|
|
} else if (OB_FAIL(convert_exists_as_scalar_subquery(stmt,
|
|
query_ref,
|
|
subquery,
|
|
trans_param))) {
|
|
LOG_WARN("failed to convert exists as scalar subquery", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// for exists subquery, case when judgement for left join can be simplified:
|
|
// select case when v.c1 is not null then v.count(*) else 0 end > 0 from t1 left join (select count(*), c1 from t2 group by c1) v on t1.c1 = v.c1;
|
|
// ==>
|
|
// select v.c1 is not null from t1 left join (select count(*), c1 from t2 group by c1) v on t1.c1 = v.c1;
|
|
// Note: when v.c1 is not null, it can certainly be inferred that v.count(*) > 0
|
|
int ObTransformAggrSubquery::deduce_query_values_for_exists(ObTransformerCtx &ctx,
|
|
ObDMLStmt &stmt,
|
|
ObRawExpr *not_null_expr,
|
|
ObRawExpr *parent_expr_of_query_ref,
|
|
ObRawExpr *&real_parent_value)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObRawExpr *left_side = NULL;
|
|
ObRawExpr *right_side = NULL;
|
|
ObItemType cmp_type = T_INVALID;
|
|
if (OB_ISNULL(not_null_expr) || OB_ISNULL(parent_expr_of_query_ref) ||
|
|
OB_ISNULL(ctx.session_info_) || OB_ISNULL(ctx.expr_factory_) ||
|
|
(parent_expr_of_query_ref->get_expr_type() != T_OP_GT &&
|
|
parent_expr_of_query_ref->get_expr_type() != T_OP_LE) ||
|
|
parent_expr_of_query_ref->get_param_count() != 2 ||
|
|
OB_ISNULL(left_side = parent_expr_of_query_ref->get_param_expr(0)) ||
|
|
OB_ISNULL(right_side = parent_expr_of_query_ref->get_param_expr(1)) ||
|
|
!left_side->is_query_ref_expr() || !right_side->is_static_const_expr()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected param", K(ret));
|
|
} else if (OB_FALSE_IT(cmp_type = parent_expr_of_query_ref->get_expr_type())) {
|
|
} else if (OB_FAIL(ObRawExprUtils::build_is_not_null_expr(*ctx.expr_factory_,
|
|
not_null_expr,
|
|
cmp_type == T_OP_GT,
|
|
real_parent_value))) {
|
|
LOG_WARN("failed to add is not null", K(ret));
|
|
} else if (OB_ISNULL(real_parent_value)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected param", K(ret));
|
|
} else if (OB_FAIL(real_parent_value->formalize(ctx.session_info_))) {
|
|
LOG_WARN("failed to formalize expr", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// for exists/any subquery, redundant aggregation of subquery can by simplified:
|
|
// select v.c1 is not null from t1 left join (select count(*), c1 from t2 group by c1) v on t1.c1 = v.c1;
|
|
// ==>
|
|
// select v.c1 is not null from t1 left join (select distinct c1 from t2) v on t1.c1 = v.c1;
|
|
int ObTransformAggrSubquery::eliminate_redundant_aggregation_if_need(ObSelectStmt &stmt,
|
|
TransformParam &trans_param)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (!trans_param.any_all_to_aggr_ && !trans_param.exists_to_aggr_) {
|
|
// do nothing
|
|
} else {
|
|
stmt.get_group_exprs().reset();
|
|
stmt.get_aggr_items().reset();
|
|
stmt.assign_distinct();
|
|
ObSEArray<SelectItem, 4> new_select_items;
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < stmt.get_select_item_size(); ++i) {
|
|
SelectItem &select_item = stmt.get_select_item(i);
|
|
if (OB_ISNULL(select_item.expr_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected param", K(ret));
|
|
} else if (select_item.expr_->is_aggr_expr()) {
|
|
// do nothing
|
|
} else if (OB_FAIL(new_select_items.push_back(select_item))) {
|
|
LOG_WARN("failed to push back select item", K(ret));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && OB_FAIL(stmt.get_select_items().assign(new_select_items))) {
|
|
LOG_WARN("failed to assign select items", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
} |