866 lines
35 KiB
C++
866 lines
35 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_expr_pullup.h"
|
|
#include "sql/resolver/expr/ob_raw_expr_util.h"
|
|
#include "sql/optimizer/ob_optimizer_util.h"
|
|
#include "sql/rewrite/ob_transform_utils.h"
|
|
#include "common/ob_smart_call.h"
|
|
|
|
namespace oceanbase {
|
|
namespace sql {
|
|
using namespace common;
|
|
|
|
/**
|
|
* @brief Pullup select exprs from the inline view to current stmt
|
|
*/
|
|
int ObTransformExprPullup::transform_one_stmt(ObIArray<ObParentDMLStmt> &parent_stmts,
|
|
ObDMLStmt *&stmt,
|
|
bool &trans_happened)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
UNUSED(parent_stmts);
|
|
ObSEArray<ObSelectStmt *, 4> transformed_views;
|
|
bool is_valid = false;
|
|
|
|
if (OB_FAIL(check_stmt_validity(stmt, is_valid))) {
|
|
LOG_WARN("fail check stmt validity", K(ret));
|
|
} else if (is_valid) {
|
|
ObSelectStmt &select_stmt = static_cast<ObSelectStmt &>(*stmt);
|
|
bool stmt_may_reduce_row_count = false;
|
|
//for exprs in the first calcing scope which may reduce result row count of the stmt
|
|
ObExprNodeMap parent_reject_expr_map;
|
|
ObExprNodeMap parent_reject_subquery_map;
|
|
|
|
if (OB_FAIL(is_stmt_may_reduce_row_count(select_stmt, stmt_may_reduce_row_count))) {
|
|
LOG_WARN("fail to check if stmt may reduce row count", K(ret));
|
|
} else if (OB_FAIL(build_parent_reject_exprs_map(select_stmt,
|
|
parent_reject_expr_map,
|
|
parent_reject_subquery_map))) {
|
|
LOG_WARN("fail to build parent expr map", K(ret));
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < stmt->get_from_item_size(); ++i) {
|
|
FromItem &from_item = stmt->get_from_item(i);
|
|
TableItem *table_item = NULL;
|
|
if (from_item.is_joined_) {
|
|
table_item = stmt->get_joined_table(from_item.table_id_);
|
|
} else {
|
|
table_item = stmt->get_table_item_by_id(from_item.table_id_);
|
|
}
|
|
if (OB_FAIL(transform_view_recursively(table_item,
|
|
select_stmt,
|
|
parent_reject_expr_map,
|
|
parent_reject_subquery_map,
|
|
transformed_views,
|
|
stmt_may_reduce_row_count,
|
|
trans_happened))) {
|
|
LOG_WARN("fail to transform view recursively", K(ret));
|
|
}
|
|
} //end for
|
|
|
|
if (OB_SUCC(ret) && trans_happened) {
|
|
if (OB_FAIL(add_transform_hint(select_stmt, &transformed_views))) {
|
|
LOG_WARN("failed to add transform hint", K(ret));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformExprPullup::transform_one_stmt_with_outline(
|
|
ObIArray<ObParentDMLStmt> &parent_stmts,
|
|
ObDMLStmt *&stmt,
|
|
bool &trans_happened)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
UNUSED(parent_stmts);
|
|
ObSEArray<ObSelectStmt *, 4> transformed_views;
|
|
bool is_valid = false;
|
|
|
|
if (OB_FAIL(check_stmt_validity(stmt, is_valid))) {
|
|
LOG_WARN("fail check stmt validity", K(ret));
|
|
} else if (is_valid) {
|
|
ObSelectStmt &select_stmt = static_cast<ObSelectStmt &>(*stmt);
|
|
bool stmt_may_reduce_row_count = false;
|
|
bool transform_happend_for_current_outline = false;
|
|
//for exprs in the first calcing scope which may reduce result row count of the stmt
|
|
ObExprNodeMap parent_reject_expr_map;
|
|
ObExprNodeMap parent_reject_subquery_map;
|
|
|
|
if (OB_FAIL(is_stmt_may_reduce_row_count(select_stmt, stmt_may_reduce_row_count))) {
|
|
LOG_WARN("fail to check if stmt may reduce row count", K(ret));
|
|
} else if (OB_FAIL(build_parent_reject_exprs_map(select_stmt,
|
|
parent_reject_expr_map,
|
|
parent_reject_subquery_map))) {
|
|
LOG_WARN("fail to build parent expr map", K(ret));
|
|
}
|
|
|
|
do {
|
|
transform_happend_for_current_outline = false;
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < stmt->get_from_item_size(); ++i) {
|
|
FromItem &from_item = stmt->get_from_item(i);
|
|
TableItem *table_item = NULL;
|
|
if (from_item.is_joined_) {
|
|
table_item = stmt->get_joined_table(from_item.table_id_);
|
|
} else {
|
|
table_item = stmt->get_table_item_by_id(from_item.table_id_);
|
|
}
|
|
if (OB_FAIL(transform_view_recursively(table_item,
|
|
select_stmt,
|
|
parent_reject_expr_map,
|
|
parent_reject_subquery_map,
|
|
transformed_views,
|
|
stmt_may_reduce_row_count,
|
|
transform_happend_for_current_outline))) {
|
|
LOG_WARN("fail to transform view recursively", K(ret));
|
|
}
|
|
} //end for
|
|
if (transform_happend_for_current_outline) {
|
|
trans_happened = true;
|
|
++ctx_->trans_list_loc_;
|
|
}
|
|
} while (OB_SUCC(ret) && transform_happend_for_current_outline);
|
|
|
|
if (OB_SUCC(ret) && trans_happened) {
|
|
if (OB_FAIL(add_transform_hint(select_stmt, &transformed_views))) {
|
|
LOG_WARN("failed to add transform hint", K(ret));
|
|
}
|
|
}
|
|
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformExprPullup::need_transform(const ObIArray<ObParentDMLStmt> &parent_stmts,
|
|
const int64_t current_level,
|
|
const ObDMLStmt &stmt,
|
|
bool &need_trans)
|
|
{
|
|
UNUSED(current_level);
|
|
UNUSED(parent_stmts);
|
|
int ret = OB_SUCCESS;
|
|
bool is_valid = false;
|
|
need_trans = false;
|
|
|
|
if (OB_FAIL(check_stmt_validity(&stmt, is_valid))) {
|
|
LOG_WARN("fail check stmt validity", K(ret));
|
|
} else if (is_valid) {
|
|
const ObSelectStmt &select_stmt = static_cast<const ObSelectStmt &>(stmt);
|
|
const ObQueryHint *query_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), KP(ctx_), KP(query_hint));
|
|
} else if (!query_hint->has_outline_data()) {
|
|
need_trans = true;
|
|
} else {
|
|
const ObHint *outline_hint = NULL;
|
|
//acting according to outline
|
|
if (OB_NOT_NULL(outline_hint = query_hint->get_outline_trans_hint(ctx_->trans_list_loc_))) {
|
|
if (outline_hint->get_hint_type() != get_hint_type()) {
|
|
need_trans = false;
|
|
OPT_TRACE("outline reject transform");
|
|
} else {
|
|
for (int64_t i = 0; !need_trans && OB_SUCC(ret) && i < stmt.get_table_size(); ++i) {
|
|
const TableItem *table = NULL;
|
|
if (OB_ISNULL(table = stmt.get_table_item(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("table item is null", K(ret));
|
|
} else if (!table->is_generated_table() || OB_ISNULL(table->ref_query_)) {
|
|
//continue
|
|
} else {
|
|
need_trans = query_hint->is_valid_outline_transform(
|
|
ctx_->trans_list_loc_, get_hint(table->ref_query_->get_stmt_hint()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformExprPullup::transform_view_recursively(TableItem *table_item,
|
|
ObSelectStmt &stmt,
|
|
ObExprNodeMap &parent_reject_expr_map,
|
|
ObExprNodeMap &parent_reject_subquery_map,
|
|
ObIArray<ObSelectStmt *> &transformed_views,
|
|
bool stmt_may_reduce_row_count,
|
|
bool &trans_happened)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool is_stack_overflow = false;
|
|
|
|
if (OB_FAIL(check_stack_overflow(is_stack_overflow))) {
|
|
LOG_WARN("check stack overflow failed", K(ret));
|
|
} else if (is_stack_overflow) {
|
|
ret = OB_SIZE_OVERFLOW;
|
|
LOG_WARN("too deep recursive", K(ret));
|
|
} else if (OB_ISNULL(table_item)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("table item is null", K(ret));
|
|
} else if (!table_item->is_joined_table()) {
|
|
if (OB_FAIL(pullup_expr_from_view(table_item, stmt, parent_reject_expr_map,
|
|
parent_reject_subquery_map, transformed_views,
|
|
stmt_may_reduce_row_count, trans_happened))) {
|
|
LOG_WARN("fail to pullup epr from view", K(ret));
|
|
}
|
|
} else {
|
|
JoinedTable *joined_table = dynamic_cast<JoinedTable*>(table_item);
|
|
if (OB_ISNULL(joined_table)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("joined table item is null", K(ret));
|
|
} else if (IS_OUTER_JOIN(joined_table->joined_type_)) {
|
|
// the expr result of outer joined tables may be determined by the logic of outer join
|
|
// (fill null or fill the origin value).
|
|
// Do not rewrite for the non-determined side.
|
|
if (joined_table->is_left_join()) {
|
|
if (OB_FAIL(transform_view_recursively(joined_table->left_table_, stmt,
|
|
parent_reject_expr_map,
|
|
parent_reject_subquery_map,
|
|
transformed_views,
|
|
stmt_may_reduce_row_count, trans_happened))) {
|
|
LOG_WARN("fail to pullup epr from view", K(ret));
|
|
}
|
|
} else if (joined_table->is_right_join()) {
|
|
if (OB_FAIL(transform_view_recursively(joined_table->right_table_, stmt,
|
|
parent_reject_expr_map,
|
|
parent_reject_subquery_map,
|
|
transformed_views,
|
|
stmt_may_reduce_row_count, trans_happened))) {
|
|
LOG_WARN("fail to pullup epr from view", K(ret));
|
|
}
|
|
}
|
|
} else {
|
|
if (OB_FAIL(transform_view_recursively(joined_table->left_table_, stmt,
|
|
parent_reject_expr_map,
|
|
parent_reject_subquery_map,
|
|
transformed_views,
|
|
stmt_may_reduce_row_count, trans_happened))) {
|
|
LOG_WARN("fail to pullup epr from view", K(ret));
|
|
} else if (OB_FAIL(transform_view_recursively(joined_table->right_table_, stmt,
|
|
parent_reject_expr_map,
|
|
parent_reject_subquery_map,
|
|
transformed_views,
|
|
stmt_may_reduce_row_count, trans_happened))) {
|
|
LOG_WARN("fail to pullup epr from view", K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObExprNodeMap::add_expr_map(ObRawExpr *expr)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool is_exist = false;
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
} else if (expr->is_const_expr()) {
|
|
// do nothing
|
|
} else {
|
|
ExprCounter counter;
|
|
uint64_t hash_v = get_hash_value(expr);
|
|
ret = expr_map_.get_refactored(hash_v, counter);
|
|
if (OB_SUCCESS == ret || OB_HASH_NOT_EXIST == ret) {
|
|
is_exist = (OB_SUCCESS == ret);
|
|
counter.count_ = is_exist ? counter.count_ + 1 : 1;
|
|
if (OB_FAIL(expr_map_.set_refactored(hash_v, counter, 1))) {
|
|
LOG_WARN("fail to set hash map", K(ret));
|
|
}
|
|
} else {
|
|
LOG_WARN("get hash map failed", K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret) && !is_exist) {
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < expr->get_param_count(); i++) {
|
|
if (OB_FAIL(SMART_CALL(add_expr_map(expr->get_param_expr(i))))) {
|
|
LOG_WARN("fail to preorder search expr", K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObExprNodeMap::init()
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_FAIL(expr_map_.create(256, ObModIds::OB_HASH_BUCKET, ObModIds::OB_HASH_NODE))) {
|
|
LOG_WARN("fail to init hash map", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObExprNodeMap::is_exist(ObRawExpr *expr, bool &is_exist)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int64_t ref_count = 0;
|
|
ret = get_ref_count(expr, ref_count);
|
|
is_exist = (ref_count > 0);
|
|
return ret;
|
|
}
|
|
|
|
int ObExprNodeMap::get_ref_count(ObRawExpr *expr, int64_t &ref_count)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ExprCounter counter;
|
|
ref_count = 0;
|
|
ret = expr_map_.get_refactored(get_hash_value(expr), counter);
|
|
|
|
if (OB_SUCCESS == ret) {
|
|
ref_count = counter.count_;
|
|
} else if (OB_HASH_NOT_EXIST == ret) {
|
|
ret = OB_SUCCESS;
|
|
} else {
|
|
LOG_WARN("fail to get hash map", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* To judge if the pullup expr can make less calculation,
|
|
* we take clear look at parent exprs at each scope.
|
|
* The operations in parent stmt that may reduce the result row count are:
|
|
* filter operation by where conds, group by operation, distinct operation
|
|
* and limit operation.
|
|
* If there is an operation happened and the pulluped expr is included
|
|
* by the operation (E.g. pulluped expr used in parent where cond)
|
|
* the calculation can not avoid.
|
|
* To test expr can realy help reduce calcuation, we build a reject map.
|
|
*/
|
|
int ObTransformExprPullup::build_parent_reject_exprs_map(ObSelectStmt &parent,
|
|
ObExprNodeMap &expr_reject_map,
|
|
ObExprNodeMap &subquery_reject_map)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<ObRawExpr *, 16> exprs_need_check;
|
|
ObSEArray<ObRawExpr *, 16> exprs_need_check_subquery;
|
|
ObIArray<ObRawExpr*> &group_exprs = parent.get_group_exprs();
|
|
ObIArray<ObQueryRefRawExpr*> &subquery_exprs = parent.get_subquery_exprs();
|
|
int64_t the_first_scope_to_search = T_NONE_SCOPE;
|
|
|
|
if (OB_FAIL(expr_reject_map.init()) || OB_FAIL(subquery_reject_map.init())) {
|
|
LOG_WARN("fail to init expr node map", K(ret));
|
|
}
|
|
|
|
//check ref column in subqueries
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < subquery_exprs.count(); ++i) {
|
|
if(OB_FAIL(ObRawExprUtils::extract_column_exprs(subquery_exprs.at(i),
|
|
exprs_need_check_subquery))) {
|
|
LOG_WARN("extract column exprs failed", K(ret));
|
|
}
|
|
}
|
|
|
|
//check where cond
|
|
if (OB_SUCC(ret) && T_NONE_SCOPE == the_first_scope_to_search) {
|
|
if (OB_FAIL(parent.get_where_scope_conditions(exprs_need_check, true))) {
|
|
LOG_WARN("fail to get where conds", K(ret));
|
|
} else if (OB_FAIL(append(exprs_need_check_subquery, exprs_need_check))) {
|
|
LOG_WARN("fail to append array", K(ret));
|
|
} else if (exprs_need_check.count() + parent.get_condition_size() > 1) {
|
|
//if there are more than 1 filter, pullup expr maybe good.
|
|
exprs_need_check.reuse();
|
|
the_first_scope_to_search = T_WHERE_SCOPE;
|
|
} else if (exprs_need_check.empty() && parent.get_condition_exprs().empty()) {
|
|
/* do nothing */
|
|
} else if (OB_FAIL(append(exprs_need_check, parent.get_condition_exprs()))) {
|
|
LOG_WARN("fail to append array", K(ret));
|
|
} else {
|
|
the_first_scope_to_search = T_WHERE_SCOPE;
|
|
}
|
|
}
|
|
|
|
//check group by and aggr function
|
|
if (OB_SUCC(ret) && T_NONE_SCOPE == the_first_scope_to_search) {
|
|
if (OB_FAIL(append(exprs_need_check, parent.get_group_exprs()))) {
|
|
LOG_WARN("fail to append array", K(ret));
|
|
} else if (OB_FAIL(append(exprs_need_check, parent.get_aggr_items()))) {
|
|
LOG_WARN("fail to append array", K(ret));
|
|
} else if (exprs_need_check.count() > 0) {
|
|
the_first_scope_to_search = T_GROUP_SCOPE;
|
|
}
|
|
}
|
|
|
|
//check distinct
|
|
if (OB_SUCC(ret) && T_NONE_SCOPE == the_first_scope_to_search) {
|
|
if (parent.has_distinct()) {
|
|
if (OB_FAIL(parent.get_select_exprs(exprs_need_check))) {
|
|
LOG_WARN("fail to get select exprs", K(ret));
|
|
}
|
|
the_first_scope_to_search = T_FIELD_LIST_SCOPE;
|
|
}
|
|
}
|
|
|
|
//check limit
|
|
if (OB_SUCC(ret) && T_NONE_SCOPE == the_first_scope_to_search) {
|
|
if (parent.has_limit()) {
|
|
if (OB_FAIL(parent.get_order_exprs(exprs_need_check))) {
|
|
LOG_WARN("fail to get order exprs", K(ret));
|
|
}
|
|
the_first_scope_to_search = T_ORDER_SCOPE;
|
|
}
|
|
}
|
|
|
|
//build map
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < exprs_need_check.count(); ++i) {
|
|
if (OB_FAIL(expr_reject_map.add_expr_map(exprs_need_check.at(i)))) {
|
|
LOG_WARN("fail to add where cond to expr map", K(ret));
|
|
}
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < exprs_need_check_subquery.count(); ++i) {
|
|
if (OB_FAIL(subquery_reject_map.add_expr_map(exprs_need_check_subquery.at(i)))) {
|
|
LOG_WARN("fail to add where cond to expr map", K(ret));
|
|
}
|
|
}
|
|
|
|
LOG_DEBUG("check parent first scope to search", K(the_first_scope_to_search));
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Exprs in select list may not able to pullup because of its childrens.
|
|
* Split the expr into 2 parts:
|
|
* Part1: sub-trees of the expr which can not pullup
|
|
* Part2: the rest of the expr can pullup
|
|
* Part1 would be left in child stmt and projected as params of Part2 which
|
|
* would pullup to parent stmt.
|
|
* This function do the Part1 job.
|
|
*/
|
|
int ObTransformExprPullup::extract_params(ObRawExpr *expr,
|
|
ObExprNodeMap &child_reject_map,
|
|
ObIArray<ObRawExpr *> ¶m_exprs)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool is_exist = false;
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
} else if (OB_FAIL(child_reject_map.is_exist(expr, is_exist))) {
|
|
LOG_WARN("fail to check shared expr", K(ret));
|
|
} else if (is_exist) {
|
|
//exist can not pullup, will be project by child stmt
|
|
if (!ObRawExprUtils::find_expr(param_exprs, expr)) {
|
|
if (OB_FAIL(param_exprs.push_back(expr))) {
|
|
LOG_WARN("fail to push back expr", K(ret));
|
|
}
|
|
}
|
|
} else {
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < expr->get_param_count(); i++) {
|
|
if (OB_FAIL(SMART_CALL(extract_params(expr->get_param_expr(i), child_reject_map, param_exprs)))) {
|
|
LOG_WARN("fail to preorder search expr", K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool ObTransformExprPullup::expr_need_pullup(ObRawExpr *expr)
|
|
{
|
|
return OB_NOT_NULL(expr)
|
|
&& !expr->is_const_expr();
|
|
}
|
|
|
|
bool ObTransformExprPullup::expr_can_pullup(ObRawExpr *expr)
|
|
{
|
|
return OB_NOT_NULL(expr)
|
|
&& !(T_OP_ASSIGN == expr->get_expr_type()
|
|
|| expr->has_flag(IS_ROWNUM)
|
|
|| expr->is_pseudo_column_expr()
|
|
|| expr->is_column_ref_expr()
|
|
|| expr->is_aggr_expr()
|
|
|| expr->is_win_func_expr());
|
|
}
|
|
|
|
bool ObTransformExprPullup::is_view_acceptable_for_rewrite(TableItem &view)
|
|
{
|
|
return view.is_generated_table()
|
|
&& OB_NOT_NULL(view.ref_query_)
|
|
&& !view.ref_query_->is_hierarchical_query()
|
|
&& !view.ref_query_->has_select_into()
|
|
&& !view.ref_query_->is_set_stmt()
|
|
&& !view.ref_query_->is_distinct()
|
|
&& !view.ref_query_->is_contains_assignment();
|
|
}
|
|
|
|
int ObTransformExprPullup::check_stmt_validity(const ObDMLStmt *stmt, bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
|
|
is_valid = false;
|
|
if (OB_ISNULL(stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("stmt is null", K(ret));
|
|
} else if (stmt->is_select_stmt()) {
|
|
const ObSelectStmt *select_stmt = static_cast<const ObSelectStmt*>(stmt);
|
|
if (!select_stmt->is_set_stmt()
|
|
&& !select_stmt->is_hierarchical_query()
|
|
&& !select_stmt->has_rollup()) {
|
|
is_valid = true;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformExprPullup::search_expr_cannot_pullup(ObRawExpr *expr,
|
|
ObExprNodeMap &expr_map,
|
|
ObIArray<ObRawExpr *> &expr_cannot_pullup)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int64_t ref_count = 0;
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
} else if (!expr_can_pullup(expr)) {
|
|
if (OB_FAIL(expr_cannot_pullup.push_back(expr))) {
|
|
LOG_WARN("fail to push back expr", K(ret));
|
|
}
|
|
} else if (OB_FAIL(expr_map.get_ref_count(expr, ref_count))) {
|
|
LOG_WARN("failed to get expr ref count", K(ret));
|
|
} else if (ref_count > 1) {
|
|
if (OB_FAIL(expr_cannot_pullup.push_back(expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
}
|
|
} else {
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < expr->get_param_count(); i++) {
|
|
if (OB_FAIL(SMART_CALL(search_expr_cannot_pullup(expr->get_param_expr(i),
|
|
expr_map,
|
|
expr_cannot_pullup)))) {
|
|
LOG_WARN("fail to preorder search expr", K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformExprPullup::adjust_subquery(ObRawExpr *expr,
|
|
ObSelectStmt &child,
|
|
ObSelectStmt &parent)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
} else if (expr->is_query_ref_expr()
|
|
&& !ObRawExprUtils::find_expr(parent.get_subquery_exprs(), expr)) {
|
|
ObQueryRefRawExpr &query_ref = static_cast<ObQueryRefRawExpr &>(*expr);
|
|
if (OB_FAIL(parent.add_subquery_ref(&query_ref))) {
|
|
LOG_WARN("fail to add subquery ref", K(ret));
|
|
}
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < expr->get_param_count(); i++) {
|
|
if (OB_FAIL(SMART_CALL(adjust_subquery(expr->get_param_expr(i), child, parent)))) {
|
|
LOG_WARN("fail to preorder search expr", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformExprPullup::rewrite_decision_by_hint(ObSelectStmt &parent,
|
|
ObSelectStmt &child,
|
|
bool stmt_may_reduce_row_count,
|
|
bool &go_rewrite)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const ObQueryHint *query_hint = child.get_stmt_hint().query_hint_;
|
|
const ObHint *pullup_hint = get_hint(child.get_stmt_hint());
|
|
enum REASON {
|
|
CTRL_INVALID,
|
|
CTRL_BY_PULLUP_HINT,
|
|
CTRL_BY_NO_REWRITE_HINT,
|
|
CTRL_BY_SELF_DECISION
|
|
};
|
|
REASON reason = CTRL_INVALID;
|
|
|
|
if (OB_NOT_NULL(query_hint) && query_hint->has_outline_data()) {
|
|
if (query_hint->is_valid_outline_transform(ctx_->trans_list_loc_, pullup_hint)) {
|
|
go_rewrite = pullup_hint->is_enable_hint();
|
|
reason = CTRL_BY_PULLUP_HINT;
|
|
if (!go_rewrite && OB_FAIL(ctx_->add_used_trans_hint(pullup_hint))) {
|
|
LOG_WARN("failed to add used trans hint", K(ret));
|
|
}
|
|
} else {
|
|
go_rewrite = false;
|
|
OPT_TRACE("outline reject transform");
|
|
}
|
|
} else {
|
|
const ObHint *child_no_rewrite = child.get_stmt_hint().get_no_rewrite_hint();
|
|
const ObHint *parent_no_rewrite = parent.get_stmt_hint().get_no_rewrite_hint();
|
|
|
|
if (OB_NOT_NULL(pullup_hint)) {
|
|
go_rewrite = pullup_hint->is_enable_hint();
|
|
if (!go_rewrite && OB_FAIL(ctx_->add_used_trans_hint(pullup_hint))) {
|
|
LOG_WARN("failed to add used trans hint", K(ret));
|
|
} else if (!go_rewrite) {
|
|
OPT_TRACE("hint reject transform");
|
|
}
|
|
reason = CTRL_BY_PULLUP_HINT;
|
|
} else if (OB_NOT_NULL(child_no_rewrite) || OB_NOT_NULL(parent_no_rewrite)) {
|
|
go_rewrite = false;
|
|
if (OB_FAIL(ctx_->add_used_trans_hint(child_no_rewrite))) {
|
|
LOG_WARN("failed to add used trans hint", K(ret));
|
|
} else if (OB_FAIL(ctx_->add_used_trans_hint(parent_no_rewrite))) {
|
|
LOG_WARN("failed to add used trans hint", K(ret));
|
|
}
|
|
reason = CTRL_BY_NO_REWRITE_HINT;
|
|
OPT_TRACE("parent or child`s no rewrite hint reject transform");
|
|
} else {
|
|
go_rewrite = stmt_may_reduce_row_count;
|
|
reason = CTRL_BY_SELF_DECISION;
|
|
OPT_TRACE("stmt may reduce row count:", stmt_may_reduce_row_count);
|
|
}
|
|
}
|
|
LOG_DEBUG("check hint rewrite control", K(go_rewrite), K(stmt_may_reduce_row_count), K(reason));
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformExprPullup::construct_transform_hint(ObDMLStmt &stmt, void *trans_params)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
UNUSED(stmt);
|
|
ObIArray<ObSelectStmt*> *merged_stmts = NULL;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->allocator_) || OB_ISNULL(trans_params)
|
|
|| OB_ISNULL(merged_stmts = static_cast<ObIArray<ObSelectStmt*>*>(trans_params))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret), KP(ctx_), KP(trans_params), KP(merged_stmts));
|
|
} else {
|
|
ObHint *hint = NULL;
|
|
ObDMLStmt *child_stmt = NULL;
|
|
ObString child_qb_name;
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < merged_stmts->count(); ++i) {
|
|
if (OB_ISNULL(child_stmt = merged_stmts->at(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret), K(child_stmt));
|
|
} else if (OB_FAIL(ObQueryHint::create_hint(ctx_->allocator_, T_PULLUP_EXPR, hint))) {
|
|
LOG_WARN("failed to create hint", K(ret));
|
|
} else if (OB_FAIL(child_stmt->get_qb_name(child_qb_name))) {
|
|
LOG_WARN("failed to get qb name", K(ret), K(child_stmt->get_stmt_id()));
|
|
} else if (OB_FAIL(ctx_->add_src_hash_val(child_qb_name))) {
|
|
LOG_WARN("failed to add src hash val", K(ret));
|
|
} else if (OB_FAIL(ctx_->outline_trans_hints_.push_back(hint))) {
|
|
LOG_WARN("failed to push back hint", K(ret));
|
|
} else {
|
|
hint->set_qb_name(child_qb_name);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformExprPullup::pullup_expr_from_view(TableItem *view,
|
|
ObSelectStmt &select_stmt,
|
|
ObExprNodeMap &parent_reject_expr_map,
|
|
ObExprNodeMap &parent_reject_subquery_map,
|
|
ObIArray<ObSelectStmt *> &transformed_stmts,
|
|
bool stmt_may_reduce_row_count,
|
|
bool &trans_happened)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool try_rewrite = false;
|
|
if (OB_ISNULL(view)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("view is null", K(ret));
|
|
} else if (!is_view_acceptable_for_rewrite(*view)) {
|
|
//skip everying
|
|
} else if (OB_FAIL(rewrite_decision_by_hint(select_stmt, *(view->ref_query_),
|
|
stmt_may_reduce_row_count, try_rewrite))) {
|
|
LOG_WARN("fail to check is allowed by hint", K(ret));
|
|
} else if (try_rewrite) {
|
|
bool cur_trans_happened = false;
|
|
ObSelectStmt &child = static_cast<ObSelectStmt &>(*(view->ref_query_));
|
|
ObSelectStmt &parent = select_stmt;
|
|
ObSEArray<ObRawExpr *, 16> select_exprs;
|
|
ObSqlBitSet<> select_expr_idxs;
|
|
ObExprNodeMap child_reject_map;
|
|
ObExprNodeMap child_select_map;
|
|
ObSEArray<ObRawExpr *, 16> expr_params;
|
|
ObSEArray<ObRawExpr*, 4> old_child_project_columns;
|
|
ObSEArray<ObRawExpr*, 4> select_exprs_can_pullup;
|
|
ObSEArray<ObRawExpr*, 4> new_child_project_columns;
|
|
|
|
if (OB_FAIL(child.get_select_exprs(select_exprs))) {
|
|
LOG_WARN("fail to get select exprs", K(ret));
|
|
}
|
|
|
|
//search for exprs cannot pullup
|
|
if (OB_SUCC(ret)) {
|
|
ObSEArray<ObRawExpr *, 16> expr_cannot_pullup;
|
|
ObStmtExprGetter visitor;
|
|
visitor.remove_scope(SCOPE_SELECT);
|
|
if (OB_FAIL(child_reject_map.init())) {
|
|
LOG_WARN("fail to init shared exprs", K(ret));
|
|
} else if (OB_FAIL(child_select_map.init())) {
|
|
LOG_WARN("failed to init select map", K(ret));
|
|
} else if (OB_FAIL(child.get_relation_exprs(expr_cannot_pullup, visitor))) {
|
|
LOG_WARN("fail to get relation exprs", K(ret));
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < select_exprs.count(); ++i) {
|
|
if (OB_FAIL(child_select_map.add_expr_map(select_exprs.at(i)))) {
|
|
LOG_WARN("failed to add expr map", K(ret));
|
|
}
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < select_exprs.count(); ++i) {
|
|
if (OB_FAIL(search_expr_cannot_pullup(select_exprs.at(i),
|
|
child_select_map,
|
|
expr_cannot_pullup))) {
|
|
LOG_WARN("fail to get relation exprs", K(ret));
|
|
}
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < expr_cannot_pullup.count(); ++i) {
|
|
if (OB_FAIL(child_reject_map.add_expr_map(expr_cannot_pullup.at(i)))) {
|
|
LOG_WARN("fail to get relation exprs", K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
//collect exprs can pullup, the corresponding view columns, and the params of exprs
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < child.get_select_items().count(); i++) {
|
|
SelectItem &select_item = child.get_select_items().at(i);
|
|
ObRawExpr *view_project_column_expr = NULL;
|
|
if (OB_ISNULL(view_project_column_expr =
|
|
parent.get_column_expr_by_id(view->table_id_, i + OB_APP_MIN_COLUMN_ID))) {
|
|
//view_project_column_expr is null which means parent maynot use this expr
|
|
//do not pullup expr
|
|
} else {
|
|
// child_reject_map elimiates the expr that cannot pullup to parent
|
|
// parent_reject_map elimiates the expr that cannot help reduce calcuation when pullup to parent
|
|
bool is_child_reject = false;
|
|
bool is_parent_reject = false;
|
|
bool is_parent_reject_subquery = false;
|
|
if (OB_ISNULL(select_item.expr_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret), K(select_item.expr_));
|
|
} else if (OB_FAIL(child_reject_map.is_exist(select_item.expr_, is_child_reject))) {
|
|
LOG_WARN("fail to check expr exist", K(ret));
|
|
} else if (OB_FAIL(parent_reject_expr_map.is_exist(view_project_column_expr, is_parent_reject))) {
|
|
LOG_WARN("fail to check expr exist", K(ret));
|
|
} else if (select_item.expr_->has_flag(CNT_SUB_QUERY)
|
|
&& OB_FAIL(parent_reject_subquery_map.is_exist(view_project_column_expr, is_parent_reject_subquery))) {
|
|
LOG_WARN("fail to check expr exist", K(ret));
|
|
} else if (is_child_reject || is_parent_reject || is_parent_reject_subquery
|
|
|| !expr_need_pullup(select_item.expr_)) {
|
|
LOG_DEBUG("can not pullup expr", K(is_child_reject), K(is_parent_reject),
|
|
K(is_parent_reject_subquery), KPC(select_item.expr_));
|
|
} else {
|
|
ObSEArray<ObRawExpr *, 16> new_expr_params;
|
|
|
|
if (OB_FAIL(extract_params(select_item.expr_, child_reject_map, new_expr_params))) {
|
|
LOG_WARN("fail to add shared params", K(ret));
|
|
} else if (OB_FAIL(append(expr_params, new_expr_params))) {
|
|
LOG_WARN("fail to append expr params", K(ret));
|
|
} else if (OB_FAIL(select_exprs_can_pullup.push_back(select_item.expr_))) {
|
|
LOG_WARN("fail to push back expr", K(ret));
|
|
} else if (OB_FAIL(select_expr_idxs.add_member(i))) {
|
|
LOG_WARN("fail to push back expr", K(ret));
|
|
} else if (OB_FAIL(old_child_project_columns.push_back(view_project_column_expr))) {
|
|
LOG_WARN("fail to push back arr", K(ret));
|
|
}
|
|
LOG_DEBUG("expr need pullup", KPC(select_item.expr_), K(new_expr_params));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret) && select_exprs_can_pullup.count() > 0) {
|
|
cur_trans_happened = true;
|
|
//remove exprs from child
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(ObTransformUtils::remove_select_items(ctx_, view->table_id_,
|
|
child,
|
|
parent,
|
|
select_expr_idxs))) {
|
|
LOG_WARN("fail to remove select items", K(ret));
|
|
}
|
|
}
|
|
|
|
//project expr params in child by adding new select items
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(ObTransformUtils::create_columns_for_view(ctx_,
|
|
*view,
|
|
&parent,
|
|
expr_params,
|
|
new_child_project_columns))) {
|
|
LOG_WARN("failed to create select item", K(ret));
|
|
} else if (OB_FAIL(child.adjust_subquery_list())) {
|
|
LOG_WARN("fail to adjust subquery list", K(ret));
|
|
}
|
|
}
|
|
|
|
//adjust exprs params to new project columns
|
|
// and replace old exprs in parent
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(ObTransformUtils::replace_exprs(expr_params,
|
|
new_child_project_columns,
|
|
select_exprs_can_pullup))) {
|
|
LOG_WARN("fail to replace exprs", K(ret));
|
|
} else {
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < select_exprs_can_pullup.count(); i++) {
|
|
if (OB_FAIL(select_exprs_can_pullup.at(i)->formalize(ctx_->session_info_))) {
|
|
LOG_WARN("fail to formalize expr", K(ret));
|
|
} else if (OB_FAIL(adjust_subquery(select_exprs_can_pullup.at(i), child, parent))) {
|
|
LOG_WARN("fail to add subquery ref", K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(parent.replace_relation_exprs(old_child_project_columns,
|
|
select_exprs_can_pullup))) {
|
|
LOG_WARN("fail to replace inner stmt expr", K(ret));
|
|
} else if (OB_FAIL(parent.formalize_stmt(ctx_->session_info_))) {
|
|
LOG_WARN("fail to formalize stmt", K(ret));
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && cur_trans_happened) {
|
|
if (OB_FAIL(transformed_stmts.push_back(&child))) {
|
|
LOG_WARN("fail to push array", K(ret));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && cur_trans_happened) {
|
|
const ObHint *pullup_hint = get_hint(child.get_stmt_hint());
|
|
if (OB_NOT_NULL(pullup_hint)) {
|
|
if (OB_FAIL(ctx_->add_used_trans_hint(pullup_hint))) {
|
|
LOG_WARN("failed to add used trans hint", K(ret));
|
|
}
|
|
}
|
|
}
|
|
trans_happened |= cur_trans_happened;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformExprPullup::is_stmt_may_reduce_row_count(const ObSelectStmt &stmt, bool &is_true)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
is_true = false;
|
|
ObSEArray<ObRawExpr*, 8> where_cond_exprs;
|
|
if (stmt.get_table_size() != 1) {
|
|
//TODO need cost-based check
|
|
is_true = false;
|
|
} else if (OB_FAIL(stmt.get_where_scope_conditions(where_cond_exprs))) {
|
|
LOG_WARN("fail to get where scope expr", K(ret));
|
|
} else if (stmt.get_group_expr_size() > 0 || where_cond_exprs.count() > 0 || stmt.has_limit()) {
|
|
is_true = true;
|
|
LOG_DEBUG("check is stmt may reduce row count", K(stmt.get_group_expr_size()), K(where_cond_exprs.count()), K(stmt.has_limit()));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
}
|
|
}
|