Files
oceanbase/src/sql/rewrite/ob_transform_simplify_distinct.cpp
2023-03-28 09:44:35 +00:00

241 lines
9.5 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 "sql/rewrite/ob_transform_simplify_distinct.h"
#include "sql/optimizer/ob_optimizer_util.h"
#include "sql/rewrite/ob_transform_utils.h"
#include "common/ob_smart_call.h"
using namespace oceanbase::sql;
int ObTransformSimplifyDistinct::transform_one_stmt(common::ObIArray<ObParentDMLStmt> &parent_stmts,
ObDMLStmt *&stmt,
bool &trans_happened)
{
int ret = OB_SUCCESS;
bool is_const_distinct = false;
bool is_unique_distinct = false;
ObSelectStmt *sel_stmt = static_cast<ObSelectStmt *>(stmt);
UNUSED(parent_stmts);
if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("stmt is null", K(ret));
} else if (!stmt->is_select_stmt()) {
// do nothing
OPT_TRACE("not select stmt");
} else if (OB_FAIL(remove_distinct_on_const_exprs(sel_stmt, is_const_distinct))) {
LOG_WARN("failed to remove distinct for const exprs", K(ret));
} else if (OB_FAIL(remove_distinct_on_unique_exprs(sel_stmt, is_unique_distinct))) {
LOG_WARN("failed to remove distinct for unique exprs", K(ret));
} else {
trans_happened = is_const_distinct || is_unique_distinct;
}
if (OB_SUCC(ret) && trans_happened) {
if (OB_FAIL(add_transform_hint(*stmt))) {
LOG_WARN("failed to add transform hint", K(ret));
}
}
return ret;
}
int ObTransformSimplifyDistinct::remove_distinct_on_const_exprs(ObSelectStmt *stmt, bool &trans_happened)
{
int ret = OB_SUCCESS;
bool is_valid = false;
trans_happened = false;
ObConstRawExpr *limit_count_expr = NULL;
OPT_TRACE("try to remove distinct on const exprs");
if (OB_ISNULL(stmt) || OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("stmt is unexpected null", K(ret), K(stmt), K(ctx_));
} else if (OB_FAIL(distinct_can_be_eliminated(stmt, is_valid))) {
LOG_WARN("distinct_can_be_eliminated() fails unexpectedly", K(ret));
} else if (!is_valid) {
// do nothing
OPT_TRACE("can not eliminate");
} else if (OB_FAIL(ObRawExprUtils::build_const_int_expr(*ctx_->expr_factory_,
ObIntType,
1L,
limit_count_expr))) {
LOG_WARN("Failed to create expr", K(ret));
} else if (OB_ISNULL(limit_count_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("limit_count_expr is null", K(ret));
} else if (OB_FAIL(limit_count_expr->formalize(ctx_->session_info_))) {
LOG_WARN("failed to formalize a new expr", K(ret));
} else {
// Eliminate DISTINCT and create a `LIMIT 1`
stmt->set_limit_offset(limit_count_expr, NULL);
stmt->assign_all();
trans_happened = true;
}
return ret;
}
int ObTransformSimplifyDistinct::distinct_can_be_eliminated(ObSelectStmt *stmt, bool &can_be)
{
int ret = OB_SUCCESS;
can_be = false;
if (OB_ISNULL(stmt) || OB_ISNULL(ctx_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(stmt), K(ctx_));
} else if (stmt->is_contains_assignment() ||
stmt->is_calc_found_rows()) {
// Do nothing for non-select query.
// When there are `@var := ` assignment, don't eliminate distinct.
OPT_TRACE("stmt has assignment or calc found rows");
} else if (stmt->has_distinct() && !stmt->is_set_stmt() && stmt->get_from_item_size() > 0 &&
!stmt->has_rollup()) {
// Only try to eliminate DISTINCT for plain SELECT
int64_t limit_count = 0;
const ObConstRawExpr *limit_expr = static_cast<ObConstRawExpr *>(stmt->get_limit_expr());
const ObRawExpr *limit_offset_expr = stmt->get_offset_expr();
if (limit_expr != NULL) {
limit_count = limit_expr->get_value().get_int();
}
if (!stmt->has_limit() ||
(limit_offset_expr == NULL && limit_count > 0)) {
bool contain_only = true;
EqualSets &equal_sets = ctx_->equal_sets_;
ObArenaAllocator alloc;
ObSEArray<ObRawExpr *, 4> const_exprs;
const ObIArray<ObRawExpr *> &conditions = stmt->get_condition_exprs();
if (OB_FAIL(stmt->get_stmt_equal_sets(equal_sets, alloc, true, true))) {
LOG_WARN("failed to get stmt equal sets", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::compute_const_exprs(conditions, const_exprs))) {
LOG_WARN("failed to compute const equivalent exprs", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && contain_only && i < stmt->get_select_item_size(); ++i) {
ObRawExpr *expr = stmt->get_select_item(i).expr_;
bool is_const = false;
if (OB_FAIL(ObOptimizerUtil::is_const_expr(expr, equal_sets, const_exprs, is_const))) {
LOG_WARN("check expr whether const expr failed", K(ret));
} else {
contain_only = is_const;
}
}
equal_sets.reuse();
if (OB_SUCC(ret) && contain_only) {
can_be = true;
} else { /* Do nothing */ }
} else { /* Do nothing */ }
} else { /* Do nothing */ }
return ret;
}
int ObTransformSimplifyDistinct::remove_distinct_on_unique_exprs(ObSelectStmt *stmt, bool &trans_happened)
{
int ret = OB_SUCCESS;
bool is_unique = false;
ObSEArray<ObRawExpr *, 16> select_exprs;
OPT_TRACE("try to remove distinct on unique exprs");
if (OB_ISNULL(stmt) || OB_ISNULL(ctx_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(stmt), K(ctx_));
} else if (!stmt->has_distinct()) {
// do nothing
OPT_TRACE("stmt do not has distinct");
} else if (OB_FAIL(stmt->get_select_exprs(select_exprs))) {
LOG_WARN("failed to get select exprs", K(ret));
} else if (OB_FAIL(ObTransformUtils::check_stmt_unique(stmt, ctx_->session_info_,
ctx_->schema_checker_,
select_exprs,
true /* strict */,
is_unique, true))) {
LOG_WARN("failed to check stmt unique", K(ret));
} else if (is_unique) {
stmt->assign_all();
trans_happened = true;
} else {
OPT_TRACE("select expr is not unique, can not eliminate");
}
if (OB_SUCC(ret) && stmt->is_set_stmt()) {
OPT_TRACE("try to remove child stmt`s distinct for set stmt");
if (OB_FAIL(remove_child_stmt_distinct(stmt, trans_happened))) {
LOG_WARN("failed to remove child stmt distinct", K(ret));
}
}
return ret;
}
//消除distinct set op left/right query中的distinct
int ObTransformSimplifyDistinct::remove_child_stmt_distinct(ObSelectStmt *set_stmt,
bool &trans_happened)
{
int ret = OB_SUCCESS;
trans_happened = false;
if (OB_ISNULL(set_stmt) || !set_stmt->is_set_stmt()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected stmt", K(ret));
} else if (!set_stmt->is_set_distinct() ||
ObSelectStmt::RECURSIVE == set_stmt->get_set_op()) {
/*do nothing*/
OPT_TRACE("union all or recurisve union can not remove distinct");
} else {
bool child_happended = false;
ObIArray<ObSelectStmt*> &child_stmts = set_stmt->get_set_query();
for (int64_t i = 0; OB_SUCC(ret) && i < child_stmts.count(); ++i) {
if (OB_FAIL(try_remove_child_stmt_distinct(child_stmts.at(i), child_happended))) {
LOG_WARN("failed to try remove child stmt distinct", K(ret));
} else {
trans_happened |= child_happended;
}
}
}
return ret;
}
//递归消除disticnt条件:
// 1.stmt 为非 recursive 的 set op, 无 limit, 尝试向下递归删除
// 2.stmt 非 set, 无 sequence/limit/rownum, 可消除distinct
int ObTransformSimplifyDistinct::try_remove_child_stmt_distinct(ObSelectStmt *stmt,
bool &trans_happened)
{
int ret = OB_SUCCESS;
trans_happened = false;
if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(stmt));
} else if (!stmt->is_set_stmt()) {
bool has_rownum = false;
if (!stmt->has_distinct()
|| stmt->has_sequence()
|| stmt->has_limit()) {
/*do nothing*/
OPT_TRACE("stmt do not has distinct or has sequence/limit");
} else if (OB_FAIL(stmt->has_rownum(has_rownum))) {
LOG_WARN("failed to check has rownum", K(ret));
} else if (!has_rownum) {
stmt->assign_all();
trans_happened = true;
} else {
OPT_TRACE("stmt has rownum");
}
} else if (ObSelectStmt::RECURSIVE == stmt->get_set_op() || stmt->has_limit()) {
/*do nothing*/
OPT_TRACE("is recursive union or stmt has limit");
} else {
bool child_happended = false;
ObIArray<ObSelectStmt*> &child_stmts = stmt->get_set_query();
for (int64_t i = 0; OB_SUCC(ret) && i < child_stmts.count(); ++i) {
if (OB_FAIL(SMART_CALL(try_remove_child_stmt_distinct(child_stmts.at(i), child_happended)))) {
LOG_WARN("failed to try remove child stmt distinct", K(ret));
} else {
trans_happened |= child_happended;
}
}
}
return ret;
}