Files
oceanbase/src/sql/rewrite/ob_transform_simplify_set.cpp
2023-03-03 02:43:52 +00:00

844 lines
36 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 "common/ob_common_utility.h"
#include "common/ob_smart_call.h"
#include "sql/ob_sql_context.h"
#include "sql/resolver/expr/ob_raw_expr.h"
#include "sql/resolver/expr/ob_raw_expr_util.h"
#include "sql/rewrite/ob_transform_simplify_set.h"
#include "sql/rewrite/ob_transformer_impl.h"
#include "sql/rewrite/ob_transform_utils.h"
using namespace oceanbase::common;
using namespace oceanbase::share;
using namespace oceanbase::share::schema;
namespace oceanbase
{
namespace sql
{
int ObTransformSimplifySet::SimplifySetHelper::assign(const SimplifySetHelper &other) {
int ret = OB_SUCCESS;
const_constraint_exprs_.reset();
precalc_constraint_exprs_.reset();
if (OB_FAIL(const_constraint_exprs_.assign(other.const_constraint_exprs_))) {
LOG_WARN("fail to assign const constraints", K(ret));
} else if (OB_FAIL(precalc_constraint_exprs_.assign(other.precalc_constraint_exprs_))) {
LOG_WARN("fail to assign precal constraints", K(ret));
}
return ret;
}
int ObTransformSimplifySet::transform_one_stmt(common::ObIArray<ObParentDMLStmt> &parent_stmts,
ObDMLStmt *&stmt,
bool &trans_happened)
{
int ret = OB_SUCCESS;
bool is_happened = false;
if (OB_ISNULL(stmt)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("stmt is NULL", K(ret));
} else if (!(stmt->is_select_stmt() && static_cast<ObSelectStmt*>(stmt)->is_set_stmt())) {
// do nothing
} else {
ObSelectStmt * sel_stmt = static_cast<ObSelectStmt*>(stmt);
if (OB_FAIL(pruning_set_query(parent_stmts, sel_stmt, is_happened))) {
LOG_WARN("failed to pruning set query", K(ret));
} else {
stmt = sel_stmt;
trans_happened |= is_happened;
OPT_TRACE("remove pruning set query:", is_happened);
LOG_TRACE("succeed to pruning set query.", K(is_happened));
}
if (OB_SUCC(ret)) {
if (stmt->is_set_stmt() &&
OB_FAIL(add_limit_order_distinct_for_union(parent_stmts, stmt, is_happened))) {
LOG_WARN("failed to add limit for union", K(ret));
} else {
trans_happened |= is_happened;
OPT_TRACE("add limit order distinct for union:", is_happened);
LOG_TRACE("succeed to add limit order distinct for union.", K(is_happened));
}
}
}
if (OB_SUCC(ret) && trans_happened && OB_FAIL(add_transform_hint(*stmt, NULL))) {
LOG_WARN("failed to add transform hint", K(ret));
}
return ret;
}
// 从upper_stmt下推distinct到stmt
int ObTransformSimplifySet::add_distinct(ObSelectStmt *stmt, ObSelectStmt *upper_stmt)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(stmt) || OB_ISNULL(upper_stmt)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("null pointer passed to add_distinct", K(stmt), K(upper_stmt));
} else if (upper_stmt->is_set_distinct()) {
if (stmt->is_set_stmt()) {
stmt->assign_set_distinct();
} else {
stmt->assign_distinct();
}
}
return ret;
}
// 从upper_stmt下推limit到stmt
// 上层有不带offset的limit,直接下压到两支
// 上层有带offset的limit,将limit+offset之后下压
int ObTransformSimplifySet::add_limit(ObSelectStmt *stmt, ObSelectStmt *upper_stmt)
{
int ret = OB_SUCCESS;
ObRawExpr *limit_count_expr = NULL;
ObRawExpr *limit_offset_expr = NULL;
ObRawExpr *limit_count_offset_expr = NULL;
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_) || OB_ISNULL(ctx_->session_info_)) {
ret = OB_NOT_INIT;
LOG_WARN("class data member is not inited", KP_(ctx));
} else if (OB_ISNULL(stmt) || OB_ISNULL(upper_stmt)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("null pointer passed to add_distinct", K(stmt), K(upper_stmt));
} else if (OB_ISNULL(upper_stmt->get_limit_expr()) || stmt->has_limit()) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("stmt should have limit", K(ret));
} else if (NULL == upper_stmt->get_offset_expr()) {
limit_count_offset_expr = upper_stmt->get_limit_expr();
} else if (OB_FAIL(ObTransformUtils::make_pushdown_limit_count(*ctx_->expr_factory_,
*ctx_->session_info_,
upper_stmt->get_limit_expr(),
upper_stmt->get_offset_expr(),
limit_count_offset_expr))) {
LOG_WARN("make pushdown limit expr failed", K(ret));
}
if (OB_SUCC(ret)) {
ObRawExprCopier copier(*ctx_->expr_factory_);
if (OB_FAIL(copier.copy(limit_count_offset_expr, limit_count_expr))) {
LOG_WARN("fail to copy limit count expr", K(ret));
} else if (OB_ISNULL(limit_count_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("left_limit_count_expr is NULL", K(ret));
} else {
stmt->set_limit_offset(limit_count_expr, limit_offset_expr);
stmt->set_fetch_with_ties(upper_stmt->is_fetch_with_ties());
}
}
return ret;
}
// 从upper_stmt下推order by到stmt
int ObTransformSimplifySet::add_order_by(ObSelectStmt *stmt, ObSelectStmt *upper_stmt)
{
int ret = OB_SUCCESS;
ObSEArray<ObRawExpr *, 4> set_exprs;
ObSEArray<ObRawExpr *, 4> select_exprs;
//add order by
if (OB_ISNULL(stmt) || OB_ISNULL(upper_stmt) || OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("null pointer passed to add_distinct", K(stmt), K(upper_stmt));
} else if (stmt->get_order_item_size() > 0) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("stmt should not have order by", K(ret), K(stmt->get_order_item_size()));
} else if (OB_FAIL(upper_stmt->get_select_exprs(select_exprs))) {
LOG_WARN("failed to get select exprs", K(ret));
} else if (OB_FAIL(ObTransformUtils::get_expr_in_cast(select_exprs, set_exprs))) {
LOG_WARN("failed to get expr in cast", K(ret));
} else {
ObRawExprCopier copier(*ctx_->expr_factory_);
for (int64_t i = 0; OB_SUCC(ret) && i < set_exprs.count(); ++i) {
if (OB_ISNULL(set_exprs.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("the set expr is invalid", K(ret), K(set_exprs.at(i)));
} else if (set_exprs.at(i)->is_set_op_expr()) {
int64_t idx = static_cast<ObSetOpRawExpr*>(set_exprs.at(i))->get_idx();
if (OB_UNLIKELY(idx < 0 || idx >= stmt->get_select_item_size())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid select item index", K(ret), K(idx), K(stmt->get_select_item_size()));
} else if (OB_FAIL(copier.add_replaced_expr(set_exprs.at(i),
stmt->get_select_item(idx).expr_))) {
LOG_WARN("failed to add replace expr pair", K(ret));
}
}
}
for (int64_t i = 0; OB_SUCC(ret) && i < upper_stmt->get_order_item_size(); ++i) {
OrderItem order_item = upper_stmt->get_order_item(i);
if (OB_FAIL(copier.copy(order_item.expr_, order_item.expr_))) {
LOG_WARN("failed to copy expr", K(ret));
} else if (OB_FAIL(stmt->add_order_item(order_item))) {
LOG_WARN("failed to add order item", K(ret));
}
}
}
return ret;
}
//
// 判断upper_stmt能否下推distinct/order by/limit操作到stmt // 当上层有不带offset的limit且下层没有limit时可以下推limit,distinct(如果上层有的话),
// 如果上层还有order by,
// 下推order by(下层order by肯定被消除了remove_order_by_for_set)
// 如果上层有order by 且 order by 跟的一个子查询,则不能下压:select * from t1 union select * from t2 order by (select min(c1) from t3) limit 1;
//
int ObTransformSimplifySet::check_can_push(ObSelectStmt *stmt, ObSelectStmt *upper_stmt, bool &can_push)
{
int ret = OB_SUCCESS;
can_push = true;
if (OB_ISNULL(stmt) || OB_ISNULL(upper_stmt)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("null pointer passed to add_distinct", K(ret), K(stmt), K(upper_stmt));
} else if (!upper_stmt->has_limit()) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("upper_stmt should have limit ", K(ret), K(upper_stmt->has_limit()));
} else if (stmt->has_limit() || stmt->is_contains_assignment()) {
// 下层有limit
can_push = false;
} else if ((0 < upper_stmt->get_order_item_size() && 0 < stmt->get_order_item_size())
|| NULL == upper_stmt->get_limit_expr()
|| NULL != upper_stmt->get_limit_percent_expr()
|| upper_stmt->is_fetch_with_ties()) {
// 上层有order by且下层有也不能下推
// 仅有 offset 不进行下推
// limit percent不能下推, 因为可能生成topn导致结果错误
can_push = false;
} else {
//上层有order by subquery 不能下推
for (int64_t i = 0; OB_SUCC(ret) && can_push && i < upper_stmt->get_order_item_size(); ++i) {
ObRawExpr *order_expr = upper_stmt->get_order_item(i).expr_;
if (OB_ISNULL(order_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(order_expr));
} else if (order_expr->has_flag(CNT_SUB_QUERY)) {
can_push = false;
} else if (!order_expr->has_flag(CNT_SET_OP)) {
can_push = false;
} else {/*do nothing*/}
}
}
return ret;
}
// 对Union的stmt执行以下改写:
// 下推ORDER BY/LIMIT/DISTINCT
//
int ObTransformSimplifySet::add_limit_order_distinct_for_union(const common::ObIArray<ObParentDMLStmt> &parent_stmts,
ObDMLStmt *&stmt,
bool &trans_happened)
{
int ret = OB_SUCCESS;
bool is_calc = false;
ObSelectStmt* select_stmt = NULL;
trans_happened = false;
if (OB_ISNULL(stmt)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("null pointer passed to transform", K(stmt), K(ret));
} else if (stmt->is_select_stmt()
&& static_cast<ObSelectStmt*>(stmt)->is_set_stmt()
&& ObSelectStmt::UNION == static_cast<ObSelectStmt*>(stmt)->get_set_op()) {
select_stmt = static_cast<ObSelectStmt*>(stmt);
if (OB_FAIL(is_calc_found_rows_for_union(parent_stmts, stmt, is_calc))) {
LOG_WARN("fail to judge calc found rows for union", K(ret));
} else if (is_calc) {
/*do nothing*/
} else if(select_stmt->has_limit()){
bool can_push = false;
ObSelectStmt* child_stmt = NULL;
ObIArray<ObSelectStmt*> &child_stmts = select_stmt->get_set_query();
for (int64_t i = 0; OB_SUCC(ret) && i < child_stmts.count(); ++i) {
if (OB_ISNULL(child_stmt = child_stmts.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret));
} else if (OB_FAIL(check_can_push(child_stmt, select_stmt, can_push))) {
LOG_WARN("Failed to check left stmt", K(ret));
} else if (!can_push) {
/*do nothing*/
} else if (OB_FAIL(add_distinct(child_stmt, select_stmt))) {
LOG_WARN("Failed to add distinct to left stmt", K(ret));
} else if (OB_FAIL(add_limit(child_stmt, select_stmt))) {
LOG_WARN("Failed to add limit to left stmt", K(ret));
} else if (OB_FAIL(add_order_by(child_stmt, select_stmt))) {
LOG_WARN("Failed to add order by to left stmt", K(ret));
} else {
trans_happened = true;
}
}
}
}
return ret;
}
int ObTransformSimplifySet::is_calc_found_rows_for_union(const common::ObIArray<ObParentDMLStmt> &parent_stmts,
ObDMLStmt *&stmt,
bool &is_calc)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(stmt)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument, stmt is NULL", K(stmt), K(ret));
} else {
ObSelectStmt *top_union_stmt = NULL;
if (stmt->is_select_stmt() && stmt->is_set_stmt() &&
ObSelectStmt::UNION == static_cast<ObSelectStmt*>(stmt)->get_set_op()) {
top_union_stmt = static_cast<ObSelectStmt*>(stmt);
} else { /* do nothing*/ }
for (int64_t i = parent_stmts.count() - 1; OB_SUCC(ret) && i >= 0; i--) {
ObDMLStmt *stmt = NULL;
if (OB_ISNULL(stmt = parent_stmts.at(i).stmt_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("stmt is null", K(ret));
} else if (stmt->is_select_stmt() && stmt->is_set_stmt() &&
ObSelectStmt::UNION == static_cast<ObSelectStmt*>(stmt)->get_set_op()) {
top_union_stmt = static_cast<ObSelectStmt*>(stmt);
} else {
break;
}
}
if (OB_SUCC(ret) && NULL != top_union_stmt) {
is_calc = top_union_stmt->is_calc_found_rows() ? true : false;
}
}
return ret;
}
// check wether the exprs is always false.
int ObTransformSimplifySet::check_exprs_constant_false(common::ObIArray<ObRawExpr*> &exprs,
bool &constant_false,
int64_t stmt_idx,
SimplifySetHelper &helper)
{
int ret = OB_SUCCESS;
bool is_valid_type = true;
constant_false |= false;
if (OB_FAIL(ObTransformUtils::check_integer_result_type(exprs, is_valid_type))) {
LOG_WARN("check valid type fail", K(ret));
} else if (!is_valid_type) {
LOG_TRACE("expr list is not valid for removing dummy exprs", K(is_valid_type));
} else {
ObSEArray<int64_t, 2> true_exprs;
ObSEArray<int64_t, 2> false_exprs;
if (OB_FAIL(ObTransformUtils::extract_const_bool_expr_info(ctx_,
exprs,
true_exprs,
false_exprs))) {
LOG_WARN("fail to extract exprs info", K(ret));
} else if (false_exprs.count() > 0) {
/* do the check.
* N: exprs.count(), M:false_exprs.count(), K:true_exprs.count();
* M > 0;
*/
constant_false = true;
// build precalc_constraints exprs.
ObSEArray<ObRawExpr*, 4> ob_params;
ObRawExpr *op_expr = NULL;
if (OB_FAIL(ObTransformUtils::extract_target_exprs_by_idx(exprs, false_exprs, ob_params))) {
LOG_WARN("fail to push back params expr", K(ret));
} else {
ObRawExpr *tmp_expr = NULL;
for (int64_t i = 0; OB_SUCC(ret) && i < ob_params.count(); i++) {
if (OB_ISNULL(tmp_expr = ob_params.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get null pointer", K(ret));
} else if (OB_FAIL(helper.precalc_constraint_exprs_.push_back(
std::pair<ObRawExpr*, int64_t>(tmp_expr, stmt_idx)))) {
LOG_WARN("fail to push back constraint exprs", K(ret));
}
}
}
}
}
return ret;
}
/*
* BACKGROUND:
* There are three kind of limit.
* 1. mysql's limit. (only positive number allowed)
* 2. oracle's fetch. (fetch can't coexist with set query,
* oracle's set query only support simple select)
* 3. limit that is converted from rownum. (could be complex expr)
*
* PRINCIPLES:
* There are three situations that the stmt returns empty:
* 1. limit_count_expr <= 0;
* 2. limit_percent_expr <= 0;
* 3. limit_offset_expr > max row count. (not support).
* When we detect the first two ruls, we consider the stmt is removable.
*/
int ObTransformSimplifySet::check_limit_zero_in_stmt(ObRawExpr *limit_expr,
ObRawExpr *offset_expr,
ObRawExpr *percent_expr,
bool &need_remove,
int64_t stmt_idx,
SimplifySetHelper &helper)
{
UNUSED(offset_expr);
int ret = OB_SUCCESS;
ObObj result;
need_remove = false;
bool is_valid = false;
if (OB_NOT_NULL(limit_expr)) {
if (OB_FAIL(ObTransformUtils::calc_const_expr_result(limit_expr, ctx_, result, is_valid))) {
LOG_WARN("fail to calc const expr", K(ret));
}
} else if (OB_NOT_NULL(percent_expr)) {
if (OB_FAIL(ObTransformUtils::calc_const_expr_result(percent_expr, ctx_, result, is_valid))) {
LOG_WARN("fail to calc const expr", K(ret));
}
} else {}
//check need_remove
if (OB_SUCC(ret) && is_valid) {
if (result.is_int()) {
need_remove = (result.get_int() <= 0);
} else if (result.is_double()) {
need_remove = (result.get_double() <= 0);
} else if (result.is_float()) {
need_remove = (result.get_float() <= 0);
} else if (result.is_number()) {
need_remove = (result.is_zero_number() || result.is_negative_number());
}
}
// only add constraint exprs here.
// the real constraints should be added after all transformation have done.
if (OB_SUCC(ret) && need_remove) {
if (OB_NOT_NULL(limit_expr)) {
if (OB_FAIL(helper.const_constraint_exprs_.push_back(
std::pair<ObRawExpr *, int64_t>(limit_expr, stmt_idx)))) {
LOG_WARN("fail to push back const constraints", K(ret));
}
} else if (OB_NOT_NULL(percent_expr)) {
if (OB_FAIL(helper.const_constraint_exprs_.push_back(
std::pair<ObRawExpr *, int64_t>(percent_expr, stmt_idx)))) {
LOG_WARN("fail to push back const constraints", K(ret));
}
}
}
return ret;
}
/* Why we should add constraints?
* ---- const constraint (for limit expr) ----
* since
* Q1: "select * from t1 where rownum < ? UNION select 1 from dual"
* will be converted to
* Q2: "select * from t1 limit ? UNION select 1 from dual"
* However, (?==0) and (?>0) should hit different plan.
* When ?==0, query "select * from t1 limit ?" in Q2 would be pruned.
*
* ---- precalc constraints (for constant false expr) ----
* Q1: "select * from t1 where ? union select * from t2 where ?"
* will be convert to (if the second ? is false)
* Q2: "select * from t1 where ?"
* if the second ? is true, the stmt shouldn't get the same plan as Q2.
*
* @param:
* need_remove: wether the stmt need to be removed
* stmt_idx: the index of the stmt in the parent_stmt, used to mark the constraints in helper.
* helper: store the exprs that is used to build constraints.
*/
int ObTransformSimplifySet::check_set_stmt_removable(ObSelectStmt *stmt,
bool &need_remove,
int64_t stmt_idx,
SimplifySetHelper &helper)
{
int ret = OB_SUCCESS;
int has_scalar_group_by = false;
need_remove = false;
if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get null pointer", K(ret));
} else if (!stmt->is_scala_group_by() &&
OB_FAIL(check_exprs_constant_false(stmt->get_condition_exprs(),
need_remove,
stmt_idx,
helper))) {
// since select count(*) from dual where 1=0, will still output a row, we should check
// scalar group by here.
LOG_WARN("fail to check exprs constant false", K(ret), K(stmt->get_condition_exprs()));
} else if (!need_remove && OB_FAIL(check_exprs_constant_false(stmt->get_having_exprs(),
need_remove,
stmt_idx,
helper))) {
LOG_WARN("fail to check exprs constant false", K(ret), K(stmt->get_having_exprs()));
} else if (!need_remove && OB_FAIL(check_limit_zero_in_stmt(stmt->get_limit_expr(),
stmt->get_offset_expr(),
stmt->get_limit_percent_expr(),
need_remove,
stmt_idx,
helper))) {
LOG_WARN("fail to check limit", K(ret));
}
return ret;
}
int ObTransformSimplifySet::pruning_set_query(common::ObIArray<ObParentDMLStmt> &parent_stmts,
ObSelectStmt *&select_stmt,
bool &trans_happened)
{
int ret = OB_SUCCESS;
SimplifySetHelper helper;
trans_happened = false;
bool first_can_remove = true;
if (OB_ISNULL(select_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get null pointer", K(ret));
} else if (select_stmt->get_set_op() == ObSelectStmt::UNION ||
select_stmt->get_set_op() == ObSelectStmt::EXCEPT ||
select_stmt->get_set_op() == ObSelectStmt::INTERSECT) {
// add removable child_stmt in remove_list.
ObSEArray<int64_t, 4> remove_list;
for (int64_t i = 0 ; OB_SUCC(ret) && i < select_stmt->get_set_query().count(); i++) {
ObSelectStmt* child_stmt = select_stmt->get_set_query(i);
bool need_remove = false;
if (OB_FAIL(check_set_stmt_removable(child_stmt, need_remove, i, helper))) {
LOG_WARN("fail to check set stmt removeable", K(ret));
} else if (need_remove && OB_FAIL(remove_list.push_back(i))) {
LOG_WARN("fail to push back", K(ret));
}
}
if (OB_SUCC(ret) && remove_list.count() > 0 &&
remove_list.at(0) == 0) {
if (OB_FAIL(check_first_stmt_removable(parent_stmts,
select_stmt,
remove_list,
first_can_remove))) {
LOG_WARN("failed to check first stmt removable", K(ret));
} else if(!first_can_remove &&
OB_FAIL(remove_list.remove(0))) {
LOG_WARN("fail to remove item", K(ret));
}
}
// do the remove.
if (OB_SUCC(ret) && remove_list.count() > 0) {
ObSEArray<int64_t, 1> constraints_idxs;
if (OB_FAIL(remove_set_query_in_stmt(select_stmt, remove_list,
constraints_idxs, trans_happened))) {
LOG_WARN("fail to remove dummy set query", K(ret));
} else if (trans_happened && OB_FAIL(add_constraints_by_idx(constraints_idxs, helper))) {
LOG_WARN("fail to add constraints by idx", K(ret));
}
}
}
return ret;
}
int ObTransformSimplifySet::remove_set_query_in_stmt(ObSelectStmt *&select_stmt,
ObIArray<int64_t> &remove_list,
common::ObIArray<int64_t> &constraints_idxs,
bool &trans_happened)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(select_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("null stmt", K(ret));
} else if (OB_UNLIKELY(remove_list.count() <= 0 || select_stmt->get_set_query().count() <= 1)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("incorrent remove_list coutn or set queries number", K(ret));
} else {
trans_happened = true;
ObSelectStmt *child_stmt = NULL;
// index of the stmt, that should add constraints.
switch (select_stmt->get_set_op()) {
case ObSelectStmt::UNION: {
if (select_stmt->is_recursive_union() && select_stmt->get_set_query().count() == 2) {
// recursive cte, the fake table branch is always at left.
if (remove_list.count() == 1 && remove_list.at(0) == 0) {
// if the branches should be pruned is left branch, do nothing.
trans_happened = false;
} else {
// if both branches should be removed or the right branch should be removed.
// keep the left branches.
child_stmt = select_stmt->get_set_query(0);
if (OB_FAIL(replace_set_stmt_with_child_stmt(select_stmt, child_stmt))) {
LOG_WARN("fail to replace stmt with child stmt", K(ret));
} else if (OB_FAIL(constraints_idxs.push_back(1))) {
// mark that need to add the second branches's constraints.
LOG_WARN("fail to add constraints", K(ret));
}
}
} else if (remove_list.count() >= select_stmt->get_set_query().count() - 1) {
// keep only one stmt. not a set query any more.
bool found = false;
for (int64_t i = 0; OB_SUCC(ret) && i < select_stmt->get_set_query().count(); i++) {
if (!found && (!has_exist_in_array(remove_list, i) ||
(i == select_stmt->get_set_query().count() - 1))) {
// if all child queries are removable, keep the last stmt.
found = true;
child_stmt = select_stmt->get_set_query(i);
} else if (OB_FAIL(constraints_idxs.push_back(i))){
// the i-th branch need to be removed, add the constraints
LOG_WARN("fail to push back constraints", K(ret));
}
}
if (OB_SUCC(ret) && found) {
if (OB_FAIL(replace_set_stmt_with_child_stmt(select_stmt, child_stmt))) {
LOG_WARN("fail to replace stmt with child stmt", K(ret));
}
}
} else {
for (int64_t i = remove_list.count() - 1; OB_SUCC(ret) && i >= 0; i--) {
if (OB_LIKELY(remove_list.at(i) >= 0 && remove_list.at(i) < select_stmt->get_set_query().count())) {
if (OB_FAIL(select_stmt->get_set_query().remove(remove_list.at(i)))) {
LOG_WARN("fail to remove set query", K(ret));
} else if (OB_FAIL(constraints_idxs.push_back(remove_list.at(i)))) {
LOG_WARN("fail to push back constraints", K(ret));
}
}
}
}
break;
}
case ObSelectStmt::INTERSECT: {
if (OB_UNLIKELY(select_stmt->get_set_query().count() != 2)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("branches num of intersect should be 2", K(ret));
} else if (remove_list.count() == 2 || (remove_list.count() == 1 &&
remove_list.at(0) == 1)) {
child_stmt = select_stmt->get_set_query(1);
if (OB_FAIL(constraints_idxs.push_back(0))) {
LOG_WARN("fail to push back constraints", K(ret));
}
} else if (remove_list.count() == 1 && remove_list.at(0) == 0) {
// remove second branch.
child_stmt = select_stmt->get_set_query(0);
if (OB_FAIL(constraints_idxs.push_back(1))) {
LOG_WARN("fail to push back constraints", K(ret));
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(replace_set_stmt_with_child_stmt(select_stmt, child_stmt))) {
LOG_WARN("fail to replace stmt with child stmt", K(ret));
}
}
break;
}
case ObSelectStmt::EXCEPT: {
if (OB_UNLIKELY(select_stmt->get_set_query().count() != 2)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("branches num of except should be 2", K(ret));
} else if (remove_list.count() == 2 || (remove_list.count() == 1 &&
remove_list.at(0) == 0)) {
// keep the first branch.
child_stmt = select_stmt->get_set_query(0);
if (OB_FAIL(constraints_idxs.push_back(0))) {
LOG_WARN("fail to push back constraints", K(ret));
}
} else if (remove_list.count() == 1 && remove_list.at(0) == 1) {
// keep the first branch. Since the stmt is not empty, we should keep the order by.
child_stmt = select_stmt->get_set_query(0);
if (OB_FAIL(constraints_idxs.push_back(1))) {
LOG_WARN("fail to push back constraints", K(ret));
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(replace_set_stmt_with_child_stmt(select_stmt, child_stmt))) {
LOG_WARN("fail to replace stmt with child stmt", K(ret));
}
}
break;
}
default:
break;
}
}
return ret;
}
int ObTransformSimplifySet::add_constraints_by_idx(common::ObIArray<int64_t> &constraints_idx,
SimplifySetHelper &helper)
{
int ret = OB_SUCCESS;
for (int64_t i = 0; OB_SUCC(ret) && i < constraints_idx.count(); i++) {
int64_t branch_idx = constraints_idx.at(i);
// precal constraints;
ObSEArray<ObRawExpr *, 4> ob_params;
ObRawExpr *op_expr = NULL;
bool search_end = false;
for (int64_t j = 0; OB_SUCC(ret) && !search_end && j < helper.precalc_constraint_exprs_.count(); j++) {
int64_t expr_branch_idx = helper.precalc_constraint_exprs_.at(j).second;
if (expr_branch_idx == branch_idx) {
if (OB_FAIL(ob_params.push_back(helper.precalc_constraint_exprs_.at(j).first))) {
LOG_WARN("fail to push back expr", K(ret));
}
} else if (expr_branch_idx > branch_idx) {
// since constraint exprs is sorted, we don't need to go through the whole array.
search_end = true;
}
}
if (ob_params.count() <= 0) {
} else if (OB_FAIL(ObRawExprUtils::build_and_expr(*ctx_->expr_factory_,
ob_params,
op_expr))) {
LOG_WARN("fail to build constaint expr", K(ret));
} else if (OB_ISNULL(op_expr)) {
LOG_WARN("get null pointer", K(ret));
} else if (OB_FAIL(op_expr->formalize(ctx_->session_info_))) {
LOG_WARN("fail to formalize expr", K(ret));
} else if (OB_FAIL(ctx_->expr_constraints_.push_back(
ObExprConstraint(op_expr, PreCalcExprExpectResult::PRE_CALC_RESULT_FALSE)))) {
LOG_WARN("fail to push back constraints", K(ret));
}
// const constraints.
// only one expr for each branches.
if (OB_SUCC(ret)) {
bool found = false;
ObRawExpr *tmp_expr = NULL;
for (int64_t j = 0; !found && j < helper.const_constraint_exprs_.count(); j++) {
int64_t expr_branch_idx = helper.const_constraint_exprs_.at(j).second;
if (expr_branch_idx == branch_idx) {
tmp_expr = helper.const_constraint_exprs_.at(j).first;
if (OB_FAIL(ObTransformUtils::add_const_param_constraints(tmp_expr, ctx_))) {
LOG_WARN("fail to add const constraints", K(ret));
}
found = true;
}
}
}
}
return ret;
}
// overwrite order by
// overwrite distinct -- for UNION/EXCEPT/INTERSECT, stmt should keep distinct attribute.
// overwrite limit.
int ObTransformSimplifySet::replace_set_stmt_with_child_stmt(ObSelectStmt *&parent_stmt,
ObSelectStmt *child_stmt)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(parent_stmt) || OB_ISNULL(child_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get null pointer", K(ret));
} else {
if ((parent_stmt->has_order_by() && (child_stmt->has_order_by() || child_stmt->has_limit()))
|| (parent_stmt->has_limit() && child_stmt->has_limit())
|| (parent_stmt->is_set_distinct() && child_stmt->has_limit())
|| (parent_stmt->has_select_into() && child_stmt->has_select_into())) {
/* 1. for limit clause
* if both parent_stmt and child_stmt has limit, we should create a view to hold parent's limit.
*
* 2. for add_order_by, child stmt shouldn't have order_by_items or limit items.
* if we meet conflict order by items. We can't directly reset child's order by.
* e.g., select * from t1 order by c1 limit 2 UNION ALL select * from t2 where 1=2 order by c2;
* if we overwrite the order by, the result is not correct.
*
* 3. for distinct.
* if child stmt has limit, we can't add distinct to child stmt. Since limit should be done before distinct.
*
* 4. select into is not allowed in subquery, we add the code here to handle conflict select into.
* just in case of some transformer rule generating conflict select into.
*/
ObSelectStmt *view_stmt = NULL;
if (OB_FAIL(ObTransformUtils::create_stmt_with_generated_table(ctx_, child_stmt, view_stmt))) {
LOG_WARN("fail to create view with generated table", K(ret));
} else if (OB_ISNULL(view_stmt)) {
LOG_WARN("view table is null", K(ret));
} else if (OB_FAIL(add_order_by(view_stmt, parent_stmt))) {
// set child's order by to view.
LOG_WARN("fail to assign parents's order items to child", K(ret));
} else {
//set child's limit to view. and reset child's order by items.
view_stmt->set_fetch_info(parent_stmt->get_offset_expr(),
parent_stmt->get_limit_expr(),
parent_stmt->get_limit_percent_expr());
if (parent_stmt->is_set_distinct()) {
view_stmt->assign_distinct();
}
if (parent_stmt->has_select_into()) {
view_stmt->set_select_into(parent_stmt->get_select_into());
}
parent_stmt = view_stmt;
}
} else {
/* order by clause*/
if (parent_stmt->has_order_by()) {
if (OB_FAIL(add_order_by(child_stmt, parent_stmt))) {
LOG_WARN("fail to assign parents's order items to child", K(ret));
}
}
if (OB_SUCC(ret) && parent_stmt->is_set_distinct()) {
if (child_stmt->is_set_stmt()) {
child_stmt->assign_set_distinct();
} else {
child_stmt->assign_distinct();
}
}
if (OB_SUCC(ret) && parent_stmt->has_select_into()) {
child_stmt->set_select_into(parent_stmt->get_select_into());
}
/*limit clause*/
if (OB_SUCC(ret) && parent_stmt->has_limit()) {
child_stmt->set_fetch_info(parent_stmt->get_offset_expr(),
parent_stmt->get_limit_expr(),
parent_stmt->get_limit_percent_expr());
}
parent_stmt = child_stmt;
}
}
return ret;
}
/*
consider following sql:
UPDATE test1 full join test2 ON test1.h3=null set test1.h2='null' where test2.h2='akeyashi';
The full join will be expanded into a set of left join and anti join. If the left join is eliminated at this time,
the null column in the anti join will be filled in the dml table info of the upper update statement,
which may cause an exception.
*/
int ObTransformSimplifySet::check_first_stmt_removable(common::ObIArray<ObParentDMLStmt> &parent_stmts,
ObSelectStmt *&stmt,
ObIArray<int64_t> &remove_list,
bool &can_remove)
{
int ret = OB_SUCCESS;
can_remove = true;
ObDMLStmt* parent_stmt = NULL;
TableItem* table_item = NULL;
bool is_dml_table = false;
if (parent_stmts.empty()) {
// do nothing
} else if (OB_ISNULL(parent_stmt = parent_stmts.at(parent_stmts.count() - 1).stmt_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (!ObStmt::is_dml_write_stmt(parent_stmt->get_stmt_type())) {
// do nothing
} else if (OB_FAIL(ObTransformUtils::get_generated_table_item(*parent_stmt, stmt, table_item))) {
LOG_WARN("failed to get table_item", K(ret));
} else if (OB_ISNULL(table_item)) {
// do nothing
} else if (OB_FAIL(static_cast<ObDelUpdStmt *>(parent_stmt)->has_dml_table_info(table_item->table_id_,
is_dml_table))) {
LOG_WARN("failed to check is dml table", K(ret));
} else if (is_dml_table) {
can_remove = false;
}
return ret;
}
}// namespace sql
} // namespace oceanbase