Files
oceanbase/src/sql/rewrite/ob_transform_or_expansion.cpp
2024-03-04 03:45:04 +00:00

3422 lines
155 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_or_expansion.h"
#include "sql/resolver/dml/ob_dml_stmt.h"
#include "sql/resolver/expr/ob_raw_expr_util.h"
#include "sql/rewrite/ob_transform_utils.h"
#include "sql/rewrite/ob_transformer_impl.h"
#include "sql/optimizer/ob_optimizer_util.h"
#include "common/ob_smart_call.h"
#include "sql/optimizer/ob_log_set.h"
#include "sql/optimizer/ob_log_table_scan.h"
#include "sql/optimizer/ob_log_join.h"
namespace oceanbase
{
using namespace common;
namespace sql
{
const int64_t ObTransformOrExpansion::MAX_STMT_NUM_FOR_OR_EXPANSION = 10;
const int64_t ObTransformOrExpansion::MAX_TIMES_FOR_OR_EXPANSION = 5;
int ObTransformOrExpansion::transform_one_stmt(ObIArray<ObParentDMLStmt> &parent_stmts,
ObDMLStmt *&stmt,
bool &trans_happened)
{
int ret = OB_SUCCESS;
trans_happened = false;
bool is_stmt_valid = false;
if (OB_FAIL(check_stmt_valid_for_expansion(stmt, is_stmt_valid))) {
LOG_WARN("failed to check stmt valid for expansion", K(ret));
} else if (!is_stmt_valid) {
/* do nothing */
} else if (OB_FAIL(transform_in_joined_table(parent_stmts, stmt, trans_happened))) {
LOG_WARN("failed to do or expansion in joined condition", K(ret));
} else if (trans_happened) {
/* do nothing */
} else if (OB_FAIL(transform_in_semi_info(parent_stmts, stmt, trans_happened))) {
LOG_WARN("failed to do or expansion in semi condition", K(ret));
} else if (trans_happened) {
/* do nothing */
} else if (OB_FAIL(transform_in_where_conditon(parent_stmts, stmt, trans_happened))) {
LOG_WARN("failed to do or expansion in where condition", K(ret));
}
if (OB_SUCC(ret) && trans_happened) {
add_trans_type(ctx_->happened_cost_based_trans_,
OR_EXPANSION);
}
return ret;
}
int ObTransformOrExpansion::transform_one_stmt_with_outline(ObIArray<ObParentDMLStmt> &parent_stmts,
ObDMLStmt *&stmt,
bool &trans_happened)
{
int ret = OB_SUCCESS;
const ObHint *hint = NULL;
if (OB_ISNULL(ctx_) || OB_ISNULL(stmt)|| OB_ISNULL(hint = get_hint(stmt->get_stmt_hint()))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(ctx_), K(stmt), K(hint));
} else if (!static_cast<const ObOrExpandHint*>(hint)->is_explicit()) {
LOG_TRACE("use_concat hint has no explicit condition in outline", K(ctx_->src_qb_name_),
K(*hint));
} else if (OB_FAIL(transform_one_stmt(parent_stmts, stmt, trans_happened))) {
LOG_WARN("failed to transform one stmt for or expansion", K(ret));
} else if (trans_happened) {
++ctx_->trans_list_loc_;
LOG_TRACE("succeed to do or expansion with outline", K(ctx_->src_qb_name_));
} else {
LOG_TRACE("can not do or expansion with outline", K(ctx_->src_qb_name_));
}
return ret;
}
int ObTransformOrExpansion::need_transform(const common::ObIArray<ObParentDMLStmt> &parent_stmts,
const int64_t current_level,
const ObDMLStmt &stmt,
bool &need_trans)
{
int ret = OB_SUCCESS;
bool is_valid = true;
bool contain_inner_table = false;
const ObHint *my_hint = get_hint(stmt.get_stmt_hint());
need_trans = false;
if (OB_FAIL(ObTransformRule::need_transform(parent_stmts,
current_level,
stmt,
need_trans))) {
LOG_WARN("failed to check need transformation", K(ret));
} else if (!need_trans) {
// do nothing
} else if (OB_FAIL(check_basic_validity(stmt, is_valid))) {
LOG_WARN("failed to check basic validity", K(ret));
} else if (!is_valid) {
need_trans = false;
} else if (my_hint != NULL && my_hint->is_enable_hint()) {
need_trans = true;
} else if (OB_FAIL(stmt.check_if_contain_inner_table(contain_inner_table))) {
LOG_WARN("failed to check contain inner table", K(ret));
} else if (contain_inner_table) {
need_trans = false;
OPT_TRACE("stmt contain inner table, will not expand or expr");
}
return ret;
}
int ObTransformOrExpansion::transform_in_where_conditon(ObIArray<ObParentDMLStmt> &parent_stmts,
ObDMLStmt *&stmt,
bool &trans_happened)
{
int ret = OB_SUCCESS;
trans_happened = false;
bool is_valid = false;
ObSEArray<OrExpandInfo, 2> trans_infos;
ObSEArray<ObRawExpr*, 4> expect_ordering;
ObCostBasedRewriteCtx ctx;
ObDMLStmt *upper_stmt = NULL;
ObSelectStmt *spj_stmt = NULL;
ObTryTransHelper try_trans_helper1;
ObTryTransHelper try_trans_helper2;
OPT_TRACE("try to expand where condition");
if (OB_ISNULL(stmt) || OB_ISNULL(ctx_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(stmt), K(ctx_));
} else if (reached_max_times_for_or_expansion()) {
/*do nothing*/
OPT_TRACE("retry count reached max times:", try_times_);
} else if (OB_FAIL(has_valid_condition(*stmt, ctx, stmt->get_condition_exprs(),
is_valid, &expect_ordering))) {
LOG_WARN("failed to check where condition", K(ret));
} else if (!is_valid) {
/* do nothing */
OPT_TRACE("can not expand where condition");
} else if (OB_FAIL(ctx_->add_src_hash_val(ObTransformerCtx::SRC_STR_OR_EXPANSION_WHERE))) {
LOG_WARN("failed to add src hash val", K(ret));
} else if (OB_FAIL(try_trans_helper1.fill_helper(stmt->get_query_ctx()))) {
LOG_WARN("failed to fill try trans helper", K(ret));
} else if (OB_FAIL(get_trans_view(stmt, upper_stmt, spj_stmt))) {
LOG_WARN("failed to get spj stmt", K(ret));
} else if (OB_ISNULL(upper_stmt) || OB_ISNULL(spj_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(upper_stmt), K(spj_stmt));
} else if (OB_FAIL(convert_expect_ordering(stmt, spj_stmt, expect_ordering))) {
LOG_WARN("failed to convert expect ordering", K(ret));
} else if (OB_FAIL(gather_transform_infos(spj_stmt, ctx, spj_stmt->get_condition_exprs(),
expect_ordering, NULL, trans_infos))) {
LOG_WARN("failed to get conds trans infos", K(ret));
} else if (OB_FAIL(try_trans_helper2.fill_helper(stmt->get_query_ctx()))) {
LOG_WARN("failed to fill try trans helper after pre operate", K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && !trans_happened && i < trans_infos.count(); ++i) {
ctx.or_expand_type_ = trans_infos.at(i).or_expand_type_;
ctx.is_set_distinct_ = trans_infos.at(i).is_set_distinct_;
ctx.is_valid_topk_ = trans_infos.at(i).is_valid_topk_;
ctx.expand_exprs_.reuse();
ObDMLStmt *trans_stmt = upper_stmt;
ObSelectStmt *transformed_union_stmt = NULL;
StmtUniqueKeyProvider unique_key_provider;
try_trans_helper2.unique_key_provider_ = &unique_key_provider;
if (reached_max_times_for_or_expansion()) {
/*do nothing*/
OPT_TRACE("retry count reached max times:", try_times_);
} else if (OB_FAIL(transform_or_expansion(spj_stmt,
OB_INVALID_ID,
trans_infos.at(i).pos_,
ctx,
transformed_union_stmt,
unique_key_provider))) {
LOG_WARN("failed to do transformation", K(ret));
} else if (OB_FAIL(merge_stmt(trans_stmt, spj_stmt, transformed_union_stmt))) {
LOG_WARN("failed to merge stmt", K(ret));
} else if (OB_FAIL(accept_transform(parent_stmts, stmt, trans_stmt,
NULL != ctx.hint_, false,
trans_happened, &ctx))) {
LOG_WARN("failed to accept transform", K(ret));
} else if (trans_happened && OB_FAIL(add_transform_hint(*trans_stmt, &ctx))) {
LOG_WARN("failed to add transform hint", K(ret));
} else if (!trans_happened && OB_FAIL(try_trans_helper2.recover(stmt->get_query_ctx()))) {
LOG_WARN("failed to recover params", K(ret));
} else {
++try_times_;
LOG_TRACE("transform or expansion in where conds", K(trans_happened));
}
}
if (OB_FAIL(ret)) {
} else if (!trans_happened && OB_FAIL(try_trans_helper1.recover(stmt->get_query_ctx()))) {
LOG_WARN("failed to recover params", K(ret));
} else {
ctx_->src_hash_val_.pop_back();
}
}
return ret;
}
// 1. semi join semi condition try expand to union distinct
// cannot expand to union all, for A = (1), B = (1, 1), (null, 1),
// A semi join B on (A.c1 = B.c1 or A.c1 = B.c2) result is (1),
// A semi join B on (A.c1 = B.c1) union all
// A semi join B on (lnnvl(A.c1 = B.c1) and A.c1 = B.c2) result is (1), (1)
// 2. anti join semi condition try expand to intersect
int ObTransformOrExpansion::transform_in_semi_info(ObIArray<ObParentDMLStmt> &parent_stmts,
ObDMLStmt *&stmt,
bool &trans_happened)
{
int ret = OB_SUCCESS;
trans_happened = false;
int64_t begin_idx = OB_INVALID_INDEX;
ObCostBasedRewriteCtx ctx;
ObDMLStmt *upper_stmt = NULL;
ObSelectStmt *spj_stmt = NULL;
ObTryTransHelper try_trans_helper1;
ObTryTransHelper try_trans_helper2;
OPT_TRACE("try to expand semi condition");
if (OB_ISNULL(stmt) || OB_ISNULL(ctx_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(stmt), K(ctx_));
} else if (reached_max_times_for_or_expansion()) {
/*do nothing*/
OPT_TRACE("retry count reached max times:", try_times_);
} else if (OB_FAIL(has_valid_semi_anti_cond(*stmt, ctx, begin_idx))) {
LOG_WARN("failed to check has valid semi anti condition", K(ret));
} else if (OB_INVALID_INDEX == begin_idx) {
/*do nothing*/
OPT_TRACE("no valid semi condition");
} else if (OB_FAIL(ctx_->add_src_hash_val(ObTransformerCtx::SRC_STR_OR_EXPANSION_SEMI))) {
LOG_WARN("failed to add src hash val", K(ret));
} else if (OB_FAIL(try_trans_helper1.fill_helper(stmt->get_query_ctx()))) {
LOG_WARN("failed to fill try trans helper", K(ret));
} else if (OB_FAIL(get_trans_view(stmt, upper_stmt, spj_stmt))) {
LOG_WARN("failed to get spj stmt", K(ret));
} else if (OB_ISNULL(upper_stmt) || OB_ISNULL(spj_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(upper_stmt), K(spj_stmt));
} else if (OB_FAIL(ObTransformUtils::check_stmt_unique(spj_stmt, ctx_->session_info_,
ctx_->schema_checker_, true /* strict */,
ctx.is_unique_))) {
LOG_WARN("failed to check stmt unique", K(ret));
} else if (OB_FAIL(try_trans_helper2.fill_helper(stmt->get_query_ctx()))) {
// after create_single_joined_table_stmt, may generate new stmt
LOG_WARN("failed to fill try trans helper after pre operate", K(ret));
} else {
ctx.is_set_distinct_ = true;
const int64_t N = spj_stmt->get_semi_info_size();
SemiInfo *semi_info = NULL;
for (int64_t idx = begin_idx; OB_SUCC(ret) && !trans_happened && idx < N; ++idx) {
if (OB_ISNULL(semi_info = spj_stmt->get_semi_infos().at(idx))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(semi_info));
} else {
ObIArray<ObRawExpr*> &conds = semi_info->semi_conditions_;
ObDMLStmt *trans_stmt = NULL;
for (int64_t i = 0; OB_SUCC(ret) && !trans_happened && i < conds.count(); ++i) {
ctx.expand_exprs_.reuse();
ObSelectStmt *transformed_union_stmt = NULL;
trans_stmt = upper_stmt;
StmtUniqueKeyProvider unique_key_provider;
try_trans_helper2.unique_key_provider_ = &unique_key_provider;
if (reached_max_times_for_or_expansion()) {
/*do nothing*/
OPT_TRACE("retry count reached max times:", try_times_);
} else if (OB_FAIL(is_valid_semi_anti_cond(spj_stmt, ctx, conds.at(i), semi_info,
ctx.or_expand_type_))) {
LOG_WARN("failed to check is valid semi anti cond", K(ret), K(*semi_info));
} else if (INVALID_OR_EXPAND_TYPE == ctx.or_expand_type_) {
/*do nothing*/
} else if (OB_FAIL(transform_or_expansion(spj_stmt,
semi_info->semi_id_,
i,
ctx,
transformed_union_stmt,
unique_key_provider))) {
LOG_WARN("failed to do transformation", K(ret));
} else if (OB_FAIL(merge_stmt(trans_stmt, spj_stmt, transformed_union_stmt))) {
LOG_WARN("failed to merge stmt", K(ret));
} else if (OB_FAIL(accept_transform(parent_stmts, stmt, trans_stmt,
NULL != ctx.hint_, false,
trans_happened, &ctx))) {
LOG_WARN("failed to accept transform", K(ret));
} else if (trans_happened && OB_FAIL(add_transform_hint(*trans_stmt, &ctx))) {
LOG_WARN("failed to add transform hint", K(ret));
} else if (!trans_happened && OB_FAIL(try_trans_helper2.recover(stmt->get_query_ctx()))) {
LOG_WARN("failed to recover params", K(ret));
} else {
++try_times_;
LOG_TRACE("transform or expansion in semi info", K(trans_happened), K(i), K(*semi_info));
}
}
}
}
if (OB_FAIL(ret)) {
} else if (!trans_happened && OB_FAIL(try_trans_helper1.recover(stmt->get_query_ctx()))) {
LOG_WARN("failed to recover params", K(ret));
} else {
ctx_->src_hash_val_.pop_back();
}
}
return ret;
}
int ObTransformOrExpansion::transform_in_joined_table(ObIArray<ObParentDMLStmt> &parent_stmts,
ObDMLStmt *&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 {
ObIArray<JoinedTable*> &joined_table = stmt->get_joined_tables();
for (int64_t i = 0; OB_SUCC(ret) && !trans_happened && i < joined_table.count(); ++i) {
ret = transform_in_joined_table(parent_stmts, stmt, joined_table.at(i), trans_happened);
}
}
return ret;
}
int ObTransformOrExpansion::transform_in_joined_table(ObIArray<ObParentDMLStmt> &parent_stmts,
ObDMLStmt *&stmt,
TableItem *table,
bool &trans_happened)
{
int ret = OB_SUCCESS;
trans_happened = false;
JoinedTable *joined_table = NULL;
if (OB_ISNULL(stmt) || OB_ISNULL(table)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(stmt), K(table));
} else if (!table->is_joined_table()) {
/* do nothing */
} else if (OB_FALSE_IT(joined_table = static_cast<JoinedTable*>(table))) {
} else if (OB_FAIL(SMART_CALL(transform_in_joined_table(parent_stmts, stmt,
joined_table->left_table_,
trans_happened)))) {
LOG_WARN("failed to transform left join for left table", K(ret));
} else if (trans_happened) {
/* do nothing */
} else if (OB_FAIL(SMART_CALL(transform_in_joined_table(parent_stmts, stmt,
joined_table->right_table_,
trans_happened)))) {
LOG_WARN("failed to transform left join for right table", K(ret));
} else if (trans_happened) {
/* do nothing */
} else if (reached_max_times_for_or_expansion()) {
/* do nothing */
OPT_TRACE("retry count reached max times:", try_times_);
} else if (joined_table->is_inner_join()) {
OPT_TRACE("try", joined_table);
ret = try_do_transform_inner_join(parent_stmts, stmt, joined_table, trans_happened);
} else if (joined_table->is_left_join()) {
ret = try_do_transform_left_join(parent_stmts, stmt, joined_table, trans_happened);
} else if (joined_table->is_right_join()) {
OPT_TRACE("try", joined_table);
TableItem *l_child = joined_table->left_table_;
joined_table->left_table_ = joined_table->right_table_;
joined_table->right_table_ = l_child;
joined_table->joined_type_ = LEFT_OUTER_JOIN;
if (OB_FAIL(try_do_transform_left_join(parent_stmts, stmt, joined_table, trans_happened))) {
LOG_WARN("failed to transform joined table", K(ret), K(*joined_table));
} else if (!trans_happened) {
l_child = joined_table->left_table_;
joined_table->left_table_ = joined_table->right_table_;
joined_table->right_table_ = l_child;
joined_table->joined_type_ = RIGHT_OUTER_JOIN;
}
}
return ret;
}
// try do or expansion for inner join:
// 1. check is valid then deep copy stmt
// 2. create view from inner join table
// 3. do or expansion in view
int ObTransformOrExpansion::try_do_transform_inner_join(ObIArray<ObParentDMLStmt> &parent_stmts,
ObDMLStmt *&stmt,
JoinedTable *joined_table,
bool &trans_happened)
{
int ret = OB_SUCCESS;
trans_happened = false;
ObDMLStmt *origin_trans_stmt = NULL;
TableItem *view_table = NULL;
ObSelectStmt *ref_query = NULL;
bool is_valid = false;
ObCostBasedRewriteCtx ctx;
ObSEArray<OrExpandInfo, 2> trans_infos;
ObSEArray<ObRawExpr*, 1> dummy_exprs;
ObTryTransHelper try_trans_helper1;
ObTryTransHelper try_trans_helper2;
if (OB_ISNULL(stmt) || OB_ISNULL(joined_table) || OB_ISNULL(ctx_)
|| OB_ISNULL(ctx_->stmt_factory_) || OB_ISNULL(ctx_->expr_factory_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(stmt), K(joined_table), K(ctx_));
} else if (OB_FAIL(has_valid_condition(*stmt, ctx, joined_table->get_join_conditions(),
is_valid, NULL))) {
LOG_WARN("failed to check has valid condition", K(ret));
} else if (!is_valid) {
/*do nothing*/
OPT_TRACE("can not expand join condition");
} else if (OB_FAIL(ctx_->add_src_hash_val(ObTransformerCtx::SRC_STR_OR_EXPANSION_INNER_JOIN))) {
LOG_WARN("failed to add src hash val", K(ret));
} else if (OB_FAIL(try_trans_helper1.fill_helper(stmt->get_query_ctx()))) {
LOG_WARN("failed to fill try trans helper", K(ret));
} else if (OB_FAIL(ObTransformUtils::deep_copy_stmt(*ctx_->stmt_factory_, *ctx_->expr_factory_,
stmt, origin_trans_stmt))) {
LOG_WARN("failed to deep copy stmt", K(ret));
} else if (OB_FAIL(disable_pdml_for_upd_del_stmt(*origin_trans_stmt))) {
LOG_WARN("failed to disable pdml for upd_del_stmt", K(ret));
} else if (OB_FAIL(create_single_joined_table_stmt(origin_trans_stmt, joined_table->table_id_,
view_table, ref_query))) {
LOG_WARN("failed to create view with table", K(ret));
} else if (OB_ISNULL(ref_query)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected view table", K(ret), K(ref_query));
} else if (OB_FAIL(ObTransformUtils::flatten_joined_table(ref_query))) {
LOG_WARN("failed to flatten joined tbale", K(ret), K(ref_query));
} else if (OB_FAIL(gather_transform_infos(ref_query, ctx, ref_query->get_condition_exprs(),
dummy_exprs, NULL, trans_infos))) {
LOG_WARN("failed to get conds trans infos", K(ret));
} else if (OB_FAIL(try_trans_helper2.fill_helper(stmt->get_query_ctx()))) {
// after create_single_joined_table_stmt, may generate new stmt
LOG_WARN("failed to fill try trans helper after pre operate", K(ret));
} else {
ObIArray<ObRawExpr*> &conds = ref_query->get_condition_exprs();
for (int64_t i = 0; OB_SUCC(ret) && !trans_happened && i < trans_infos.count(); ++i) {
ctx.or_expand_type_ = trans_infos.at(i).or_expand_type_;
ctx.is_set_distinct_ = trans_infos.at(i).is_set_distinct_;
ctx.expand_exprs_.reuse();
ObDMLStmt *trans_stmt = ref_query;
ObSelectStmt *union_stmt = NULL;
StmtUniqueKeyProvider unique_key_provider;
try_trans_helper2.unique_key_provider_ = &unique_key_provider;
if (reached_max_times_for_or_expansion()) {
/*do nothing*/
OPT_TRACE("retry count reached max times:", try_times_);
} else if (OB_FAIL(transform_or_expansion(ref_query,
OB_INVALID_ID,
trans_infos.at(i).pos_,
ctx,
union_stmt,
unique_key_provider))) {
LOG_WARN("failed to do transformation", K(ret));
} else if (OB_FAIL(merge_stmt(trans_stmt, ref_query, union_stmt))) {
LOG_WARN("failed to merge stmt", K(ret));
} else if (OB_FALSE_IT(NULL == view_table ? origin_trans_stmt = trans_stmt
: view_table->ref_query_ = static_cast<ObSelectStmt*>(trans_stmt))) {
} else if (OB_FAIL(accept_transform(parent_stmts, stmt, origin_trans_stmt,
NULL != ctx.hint_, false,
trans_happened, &ctx))) {
LOG_WARN("failed to accept transform", K(ret));
} else if (trans_happened && OB_FAIL(add_transform_hint(*trans_stmt, &ctx))) {
LOG_WARN("failed to add transform hint", K(ret));
} else if (!trans_happened && OB_FAIL(try_trans_helper2.recover(stmt->get_query_ctx()))) {
LOG_WARN("failed to recover params", K(ret));
} else {
++try_times_;
LOG_TRACE("transform or expansion in inner join", K(trans_happened), K(i), K(*conds.at(i)));
}
}
if (OB_FAIL(ret)) {
} else if (!trans_happened && OB_FAIL(try_trans_helper1.recover(stmt->get_query_ctx()))) {
LOG_WARN("failed to recover params", K(ret));
} else {
ctx_->src_hash_val_.pop_back();
}
}
return ret;
}
// try do or expansion for left join:
// 1. check is valid then deep copy stmt
// 2. create view from left join table v1
// 3. do or expansion in view create set stmt v2
// 4. add win func in v1, add or condition in stmt
// select * from t1 left join t2 on t1.c1 = t2.c1 or t1.c2 = t2.c2;
// -->
// select * from (
// select v.*, row_number() over (partition by pk1 order by pk2 nulls last) as rn from (
// select t1.pk pk1, t2.pk pk2 from t1 left join t2 on t1.c1 = t2.c1
// union all
// select t1.pk , t2.pk from t1 left join t2 on lnnvl(t1.c2 = t2.c1) and t1.c2 = t2.c2
// ) v2
// ) v1 where rn = 1 or pk2 is not null;
int ObTransformOrExpansion::try_do_transform_left_join(ObIArray<ObParentDMLStmt> &parent_stmts,
ObDMLStmt *&stmt,
JoinedTable *joined_table,
bool &trans_happened)
{
int ret = OB_SUCCESS;
trans_happened = false;
ObDMLStmt *origin_trans_stmt = NULL;
TableItem *view_table = NULL;
ObSelectStmt *ref_query = NULL;
JoinedTable *cur_joined_table = NULL;
int64_t origin_select_item_count = 0;
TableItem *not_null_side_table = NULL;
ObSqlBitSet<> left_unique_pos;
ObSqlBitSet<> right_flag_pos;
bool is_valid = false;
ObCostBasedRewriteCtx ctx;
ObSEArray<OrExpandInfo, 2> trans_infos;
ObSEArray<ObRawExpr*, 1> dummy_exprs;
ObTryTransHelper try_trans_helper1;
ObTryTransHelper try_trans_helper2;
int64_t flag_view_sel_count = 0;
ObSelectStmt *orig_flag_stmt = NULL;
StmtUniqueKeyProvider unique_key_provider1;
try_trans_helper1.unique_key_provider_ = &unique_key_provider1;
if (OB_ISNULL(stmt) || OB_ISNULL(joined_table) || OB_ISNULL(ctx_) ||
OB_ISNULL(ctx_->stmt_factory_) || OB_ISNULL(ctx_->expr_factory_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(stmt), K(joined_table), K(ctx_));
} else if (OB_FAIL(has_valid_condition(*stmt, ctx, joined_table->get_join_conditions(),
is_valid, NULL))) {
LOG_WARN("failed to check has valid condition", K(ret));
} else if (!is_valid) {
/* do nothing */
OPT_TRACE("can not expand join condition");
} else if (OB_FAIL(check_child_table_valid(joined_table, not_null_side_table))) {
LOG_WARN("failed to check child table valid", K(ret));
} else if (NULL == not_null_side_table) {
/* do nothing */
} else if (OB_FAIL(ctx_->add_src_hash_val(ObTransformerCtx::SRC_STR_OR_EXPANSION_OUTER_JOIN))) {
LOG_WARN("failed to add src hash val", K(ret));
} else if (OB_FAIL(try_trans_helper1.fill_helper(stmt->get_query_ctx()))) {
LOG_WARN("failed to fill try trans helper", K(ret));
} else if (OB_FAIL(ObTransformUtils::deep_copy_stmt(*ctx_->stmt_factory_, *ctx_->expr_factory_,
stmt, origin_trans_stmt))) {
LOG_WARN("failed to deep copy stmt", K(ret));
} else if (OB_FAIL(disable_pdml_for_upd_del_stmt(*origin_trans_stmt))) {
LOG_WARN("failed to disable pdml for upd_del_stmt", K(ret));
} else if (OB_FAIL(create_single_joined_table_stmt(origin_trans_stmt, joined_table->table_id_,
view_table, ref_query))) {
LOG_WARN("failed to create view with table", K(ret));
} else if (OB_ISNULL(ref_query) || OB_UNLIKELY(1 != ref_query->get_joined_tables().count())
|| OB_ISNULL(cur_joined_table = ref_query->get_joined_tables().at(0))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected view table", K(ret), K(ref_query), K(cur_joined_table));
} else if (OB_FALSE_IT(origin_select_item_count = ref_query->get_select_item_size())) {
} else if (OB_FAIL(gather_transform_infos(ref_query, ctx, cur_joined_table->get_join_conditions(),
dummy_exprs, cur_joined_table, trans_infos))) {
LOG_WARN("failed to get conds trans infos", K(ret));
} else if (OB_FAIL(add_select_item_to_ref_query(ref_query, not_null_side_table->table_id_,
unique_key_provider1,
left_unique_pos, right_flag_pos,
flag_view_sel_count, orig_flag_stmt))) {
LOG_WARN("failed to set stmt unique", K(ret));
} else if (OB_FAIL(try_trans_helper2.fill_helper(stmt->get_query_ctx()))) {
// after create_single_joined_table_stmt, may generate new stmt
LOG_WARN("failed to fill try trans helper after pre operate", K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && !trans_happened && i < trans_infos.count(); ++i) {
ctx.or_expand_type_ = trans_infos.at(i).or_expand_type_;
ctx.is_set_distinct_ = trans_infos.at(i).is_set_distinct_;
ctx.expand_exprs_.reuse();
ObSelectStmt *trans_ref_query = NULL;
ObDMLStmt *trans_stmt = origin_trans_stmt;
StmtUniqueKeyProvider unique_key_provider2;
try_trans_helper2.unique_key_provider_ = &unique_key_provider2;
if (reached_max_times_for_or_expansion()) {
/*do nothing*/
OPT_TRACE("retry count reached max times:", try_times_);
} else if (OB_FAIL(transform_or_expansion(ref_query,
joined_table->table_id_,
trans_infos.at(i).pos_,
ctx,
trans_ref_query,
unique_key_provider2))) {
LOG_WARN("failed to do transformation", K(ret));
} else if (OB_FAIL(do_transform_for_left_join(trans_ref_query, left_unique_pos,
right_flag_pos))) {
LOG_WARN("failed to add win func and filter", K(ret));
} else if (OB_FAIL(remove_stmt_select_item(trans_ref_query, origin_select_item_count))) {
LOG_WARN("just stmt select item failed", K(ret));
} else if (OB_FALSE_IT(NULL == view_table ? trans_stmt = trans_ref_query
: view_table->ref_query_ = trans_ref_query)) {
} else if (OB_FAIL(accept_transform(parent_stmts, stmt, trans_stmt,
NULL != ctx.hint_, false,
trans_happened, &ctx))) {
LOG_WARN("failed to accept transform", K(ret));
} else if (trans_happened && OB_FAIL(add_transform_hint(*trans_stmt, &ctx))) {
LOG_WARN("failed to add transform hint", K(ret));
} else if (!trans_happened && OB_FAIL(try_trans_helper2.recover(stmt->get_query_ctx()))) {
LOG_WARN("failed to recover params", K(ret));
} else {
++try_times_;
LOG_TRACE("transform or expansion in left join", K(trans_happened), K(i), K(*joined_table));
}
}
if (OB_FAIL(ret)) {
} else if (!trans_happened && OB_FAIL(recover_flag_temp_table(ref_query,
not_null_side_table->table_id_,
flag_view_sel_count,
orig_flag_stmt))) {
LOG_WARN("failed to recover flag temp table", K(ret));
} else if (!trans_happened && OB_FAIL(try_trans_helper1.recover(stmt->get_query_ctx()))) {
LOG_WARN("failed to recover params", K(ret));
} else {
ctx_->src_hash_val_.pop_back();
}
}
return ret;
}
int ObTransformOrExpansion::create_single_joined_table_stmt(ObDMLStmt *trans_stmt,
uint64_t joined_table_id,
TableItem *&view_table,
ObSelectStmt *&ref_query)
{
int ret = OB_SUCCESS;
TableItem *cur_table = NULL;
view_table = NULL;
ref_query = NULL;
if (OB_ISNULL(trans_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(trans_stmt));
} else if (OB_FAIL(trans_stmt->get_general_table_by_id(joined_table_id, cur_table))) {
LOG_WARN("failed to get table", K(ret));
} else if (OB_ISNULL(cur_table)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(cur_table));
} else if (OB_FAIL(ObTransformUtils::replace_with_empty_view(ctx_,
trans_stmt,
view_table,
cur_table))) {
LOG_WARN("failed to create empty view table", K(ret));
} else if (OB_FAIL(ObTransformUtils::create_inline_view(ctx_,
trans_stmt,
view_table,
cur_table))) {
LOG_WARN("failed to create inline view", K(ret));
} else if (OB_ISNULL(view_table)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("view table is null", K(ret), K(view_table));
} else {
ref_query = view_table->ref_query_;
}
return ret;
}
// if is valid, return a not null not_null_side_table
int ObTransformOrExpansion::check_child_table_valid(JoinedTable *cur_table,
TableItem *&not_null_side_table)
{
int ret = OB_SUCCESS;
not_null_side_table = NULL;
TableItem *left_table = NULL;
ObSelectStmt *child_stmt = NULL;
bool can_set_unique = false;
if (OB_ISNULL(cur_table) || OB_ISNULL(left_table = cur_table->left_table_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(cur_table), K(left_table));
} else if (OB_FAIL(get_table_not_on_null_side(cur_table->right_table_, not_null_side_table))) {
LOG_WARN("failed to get table not on null side", K(ret));
} else if (NULL == not_null_side_table) {
/* do nothing */
} else if (left_table->is_basic_table()) {
/* do nothing */
} else if (!left_table->is_generated_table() && !left_table->is_temp_table()) {
not_null_side_table = NULL;
} else if (OB_ISNULL(child_stmt = left_table->ref_query_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(child_stmt));
} else if (child_stmt->is_set_stmt()) {
//union all can not set unique
not_null_side_table = child_stmt->is_set_distinct() ? not_null_side_table : NULL;
} else if (child_stmt->is_hierarchical_query() || child_stmt->has_rollup()) {
not_null_side_table = NULL;
} else if (OB_FAIL(StmtUniqueKeyProvider::check_can_set_stmt_unique(child_stmt, can_set_unique))) {
LOG_WARN("failed to check can set stmt unique", K(ret));
} else if (!can_set_unique) {
not_null_side_table = NULL;
}
return ret;
}
// get a basic/generate target_table in cur_table, target_table is not on null side.
int ObTransformOrExpansion::get_table_not_on_null_side(TableItem *cur_table,
TableItem *&target_table)
{
int ret = OB_SUCCESS;
JoinedTable *joined_table = static_cast<JoinedTable*>(cur_table);
target_table = NULL;
if (OB_ISNULL(cur_table)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (cur_table->is_basic_table() || cur_table->is_temp_table() || cur_table->is_generated_table()) {
target_table = cur_table;
} else if (!cur_table->is_joined_table()) {
/* do nothing */
} else if (OB_FALSE_IT(joined_table = static_cast<JoinedTable*>(cur_table))) {
} else if (joined_table->is_left_join()) {
ret = SMART_CALL(get_table_not_on_null_side(joined_table->left_table_, target_table));
} else if (joined_table->is_right_join()) {
ret = SMART_CALL(get_table_not_on_null_side(joined_table->right_table_, target_table));
} else if (!joined_table->is_inner_join()) {
/* do nothing */
} else if (OB_FAIL(SMART_CALL(get_table_not_on_null_side(joined_table->left_table_,
target_table)))) {
LOG_WARN("failed to get in right table", K(ret));
} else if (NULL != target_table) {
/* do nothing */
} else if (OB_FAIL(SMART_CALL(get_table_not_on_null_side(joined_table->right_table_,
target_table)))) {
LOG_WARN("failed to get in right table", K(ret));
}
return ret;
}
// add left table unique key, right table falg expr to stmt select
int ObTransformOrExpansion::add_select_item_to_ref_query(ObSelectStmt *stmt,
const uint64_t flag_table_id,
StmtUniqueKeyProvider &unique_key_provider,
ObSqlBitSet<> &left_unique_pos,
ObSqlBitSet<> &right_flag_pos,
int64_t &flag_view_sel_count,
ObSelectStmt *&orig_flag_stmt)
{
int ret = OB_SUCCESS;
JoinedTable *joined_table = NULL;
TableItem *right_table = NULL;
TableItem *flag_table = NULL;
ObSqlBitSet<> right_tables;
ObSEArray<ObRawExpr*, 4> left_unique_exprs;
ObSEArray<ObRawExpr*, 4> right_flag_exprs;
ObSEArray<ObRawExpr*, 4> select_exprs;
if (OB_ISNULL(stmt) || OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_)
|| OB_ISNULL(ctx_->allocator_) || OB_ISNULL(ctx_->session_info_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null", K(ret), K(stmt), K(ctx_));
} else if (OB_UNLIKELY(1 != stmt->get_from_item_size()) ||
OB_ISNULL(joined_table = stmt->get_joined_table(stmt->get_from_item(0).table_id_)) ||
OB_UNLIKELY(!joined_table->is_left_join()) ||
OB_ISNULL(right_table = joined_table->right_table_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect ref query", K(ret), K(joined_table), K(right_table));
} else if (OB_FAIL(stmt->get_select_exprs(select_exprs))) {
LOG_WARN("failed to get select exprs", K(ret));
} else if (OB_FAIL(stmt->get_table_rel_ids(*right_table, right_tables))) {
LOG_WARN("faield to get table rel ids", K(ret), K(*right_table));
} else if (OB_FAIL(unique_key_provider.generate_unique_key(ctx_, stmt, right_tables,
left_unique_exprs))) {
LOG_WARN("faield to get table rel ids", K(ret), K(*right_table));
} else if (OB_ISNULL(flag_table = stmt->get_table_item_by_id(flag_table_id))) {
LOG_WARN("faield to get table item", K(ret), K(flag_table), K(flag_table_id));
} else if (flag_table->is_generated_table() || flag_table->is_temp_table()) {
// add const to generate table select
ObSelectStmt *view_stmt = NULL;
ObConstRawExpr *const_expr = NULL;
ObSEArray<ObRawExpr*, 4> select_list;
orig_flag_stmt = flag_table->ref_query_;
if (OB_ISNULL(view_stmt = flag_table->ref_query_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("view_stmt is null", K(ret), K(view_stmt));
} else if (OB_FALSE_IT(flag_view_sel_count = view_stmt->get_select_item_size())) {
} else if (view_stmt->is_set_stmt() &&
OB_FAIL(ObTransformUtils::create_stmt_with_generated_table(ctx_, view_stmt, flag_table->ref_query_))) {
LOG_WARN("failed to create stmt with generated table", K(ret));
} else if (OB_FAIL(ObRawExprUtils::build_const_number_expr(*ctx_->expr_factory_, ObNumberType,
number::ObNumber::get_positive_one(), const_expr))) {
LOG_WARN("failed to build const expr", K(ret));
} else if (OB_FAIL(const_expr->formalize(ctx_->session_info_))) {
LOG_WARN("failed to formalize const number expr", K(ret));
} else if (OB_FAIL(select_list.push_back(const_expr))) {
LOG_WARN("failed to push back expr", K(ret));
} else if (OB_FAIL(ObTransformUtils::create_columns_for_view(ctx_, *flag_table, stmt,
select_list, right_flag_exprs))) {
LOG_WARN("failed to create columns for view", K(ret));
}
} else if (OB_FAIL(ObTransformUtils::generate_unique_key_for_basic_table(ctx_, stmt, flag_table,
right_flag_exprs))) {
LOG_WARN("failed to generate unique key for basic table", K(ret));
} else if (OB_UNLIKELY(right_flag_exprs.empty())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect exprs", K(ret), K(right_flag_exprs));
} else {
ObRawExpr *expr = NULL;
ObRawExpr *target_expr = NULL;
for (int64_t i = 0; OB_SUCC(ret) && NULL == target_expr && i < right_flag_exprs.count(); ++i) {
if (OB_ISNULL(expr = right_flag_exprs.at(i)) || OB_UNLIKELY(!expr->is_column_ref_expr())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect expr", K(ret), K(expr));
} else if (OB_HIDDEN_PK_INCREMENT_COLUMN_ID ==
static_cast<ObColumnRefRawExpr*>(expr)->get_column_id()) {
target_expr = expr;
}
}
if (OB_SUCC(ret)) {
target_expr = NULL == target_expr ? right_flag_exprs.at(0) : target_expr;
right_flag_exprs.reuse();
ret = right_flag_exprs.push_back(target_expr);
}
}
// add to select list and get pos
if (OB_FAIL(ret)) {
} else if (OB_UNLIKELY(left_unique_exprs.empty() || right_flag_exprs.empty())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect exprs", K(ret), K(left_unique_exprs), K(right_flag_exprs));
} else {
int64_t idx = OB_INVALID_INDEX;
if (ObOptimizerUtil::find_item(select_exprs, right_flag_exprs.at(0), &idx)) {
ret = right_flag_pos.add_member(idx);
} else if (OB_FAIL(right_flag_pos.add_member(stmt->get_select_item_size()))) {
LOG_WARN("failed to push back expr", K(ret));
} else if (OB_FAIL(ObTransformUtils::create_select_item(*ctx_->allocator_, right_flag_exprs.at(0),
stmt))) {
LOG_WARN("failed to create select item", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && i < left_unique_exprs.count(); ++i) {
if (ObOptimizerUtil::find_item(select_exprs, left_unique_exprs.at(i), &idx)) {
ret = left_unique_pos.add_member(idx);
} else if (OB_FAIL(left_unique_pos.add_member(stmt->get_select_item_size()))) {
LOG_WARN("failed to push back expr", K(ret));
} else if (OB_FAIL(ObTransformUtils::create_select_item(*ctx_->allocator_,
left_unique_exprs.at(i), stmt))) {
LOG_WARN("failed to create select item", K(ret));
}
}
}
return ret;
}
int ObTransformOrExpansion::recover_flag_temp_table(ObSelectStmt *stmt,
const uint64_t flag_table_id,
const int64_t orig_sel_count,
ObSelectStmt *orig_flag_stmt)
{
int ret = OB_SUCCESS;
TableItem *flag_table = NULL;
ObSelectStmt *view_stmt = NULL;
if (OB_ISNULL(stmt) || OB_ISNULL(ctx_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null", K(ret), K(stmt));
} else if (OB_ISNULL(flag_table = stmt->get_table_item_by_id(flag_table_id))) {
LOG_WARN("faield to get table item", K(ret), K(flag_table), K(flag_table_id));
} else if (!flag_table->is_temp_table()) {
// do nothing
} else if (OB_ISNULL(view_stmt = flag_table->ref_query_) || OB_ISNULL(orig_flag_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(view_stmt), K(orig_flag_stmt));
} else if (view_stmt != orig_flag_stmt) {
if (OB_UNLIKELY(!orig_flag_stmt->is_set_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected origin flag stmt", K(ret), KPC(orig_flag_stmt));
} else if (OB_UNLIKELY(orig_flag_stmt->get_select_item_size() != orig_sel_count)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("select item count mismatch", K(ret), K(orig_sel_count), KPC(orig_flag_stmt));
} else {
flag_table->ref_query_ = orig_flag_stmt;
}
} else if (OB_UNLIKELY(orig_sel_count <= 0
|| orig_sel_count > view_stmt->get_select_item_size())) {
ret= OB_ERR_UNEXPECTED;
LOG_WARN("unexpected original select item count", K(ret), K(orig_sel_count), KPC(view_stmt));
} else {
ObOptimizerUtil::revert_items(view_stmt->get_select_items(), orig_sel_count);
}
return ret;
}
// do transform after get a union stmt:
// 1. add win func level
// 2. add filter level
int ObTransformOrExpansion::do_transform_for_left_join(ObSelectStmt *&stmt,
ObSqlBitSet<> &left_unique_pos,
ObSqlBitSet<> &right_flag_pos)
{
int ret = OB_SUCCESS;
ObSelectStmt *view_stmt = NULL;
ObSelectStmt *win_func_stmt = NULL;
ObSelectStmt *filter_stmt = NULL;
ObSEArray<ObRawExpr*, 2> partition_exprs;
ObSEArray<ObRawExpr*, 1> order_exprs;
ObSEArray<ObRawExpr*, 1> flag_exprs;
ObRawExpr *rn_expr = NULL;
ObWinFunRawExpr *win_expr = NULL;
SelectItem select_item;
select_item.alias_name_ = "RN";
select_item.expr_name_ = "RN";
if (OB_ISNULL(view_stmt = stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(view_stmt));
} else if (OB_FAIL(ObTransformUtils::create_stmt_with_generated_table(ctx_, view_stmt,
win_func_stmt))) {
LOG_WARN("create stmt with generated_table failed", K(ret));
} else if (OB_FAIL(convert_exprs_to_win_func_level(win_func_stmt, left_unique_pos,
right_flag_pos, partition_exprs,
order_exprs))) {
LOG_WARN("failed to convert exprs to win func level", K(ret));
} else if (OB_FAIL(create_row_number_window_function(partition_exprs, order_exprs, win_expr))) {
LOG_WARN("Failed to add select item", K(select_item), K(ret));
} else if (OB_FAIL(win_func_stmt->add_window_func_expr(win_expr))) {
LOG_WARN("failed to add window func expr", K(ret));
} else if (OB_FALSE_IT(select_item.expr_ = win_expr)) {
} else if (OB_FAIL(win_func_stmt->add_select_item(select_item))) {
LOG_WARN("Failed to add select item", K(select_item), K(ret));
} else if (OB_FAIL(ObTransformUtils::create_stmt_with_generated_table(ctx_, win_func_stmt,
filter_stmt))) {
LOG_WARN("create stmt with generated_table failed", K(ret));
} else if (OB_FAIL(convert_exprs_to_filter_level(filter_stmt, select_item.expr_,
order_exprs, rn_expr, flag_exprs))) {
LOG_WARN("failed to convert exprs to filter level", K(ret));
} else if (OB_FAIL(add_filter_to_stmt(filter_stmt, rn_expr, flag_exprs))) {
LOG_WARN("failed to add filter to stmt", K(ret));
} else {
stmt = filter_stmt;
}
return ret;
}
int ObTransformOrExpansion::add_filter_to_stmt(ObSelectStmt *stmt,
ObRawExpr *rn_expr,
ObIArray<ObRawExpr*> &flag_exprs)
{
int ret = OB_SUCCESS;
ObConstRawExpr *const_one = NULL;
ObRawExpr *equal_expr = NULL;
ObOpRawExpr *is_not_null = NULL;
ObSEArray<ObRawExpr*, 2> conds;
ObRawExpr *or_expr = NULL;
if (OB_ISNULL(stmt) || OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_)
|| OB_ISNULL(rn_expr) || OB_UNLIKELY(1 != flag_exprs.count())
|| OB_ISNULL(flag_exprs.at(0))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected param", K(ret), K(stmt), K(rn_expr), K(flag_exprs));
} else if (is_oracle_mode() && ObRawExprUtils::build_const_number_expr(*ctx_->expr_factory_,
ObNumberType, number::ObNumber::get_positive_one(), const_one)) {
LOG_WARN("failed to build const expr", K(ret));
} else if (!is_oracle_mode() && ObRawExprUtils::build_const_int_expr(*ctx_->expr_factory_,
ObIntType, 1, const_one)) {
LOG_WARN("failed to build const expr", K(ret));
} else if (OB_FAIL(ObRawExprUtils::build_common_binary_op_expr(*ctx_->expr_factory_, T_OP_EQ,
rn_expr, const_one,
equal_expr))) {
LOG_WARN("failed to build common binary op expr", K(ret));
} else if (OB_FAIL(conds.push_back(equal_expr))) {
LOG_WARN("failed to push back expr", K(ret));
} else if (OB_FAIL(ObTransformUtils::add_is_not_null(ctx_, stmt, flag_exprs.at(0),
is_not_null))) {
LOG_WARN("failed to add is not null", K(ret));
} else if (OB_FAIL(conds.push_back(is_not_null))) {
LOG_WARN("failed to push back expr", K(ret));
} else if (OB_FAIL(ObRawExprUtils::build_or_exprs(*ctx_->expr_factory_, conds, or_expr))) {
LOG_WARN("make or expr failed", K(ret));
} else if (OB_ISNULL(or_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("or expr is null", K(ret));
} else if (OB_FAIL(or_expr->formalize(ctx_->session_info_))) {
LOG_WARN("failed to formalize windown function", K(ret));
} else if (OB_FAIL(or_expr->pull_relation_id())) {
LOG_WARN("failed to pull relation id and levels", K(ret));
} else if (OB_FAIL(stmt->get_condition_exprs().push_back(or_expr))) {
LOG_WARN("failed to push back expr", K(ret));
}
return ret;
}
int ObTransformOrExpansion::convert_exprs_to_filter_level(ObSelectStmt *stmt,
ObRawExpr *rn_expr,
ObIArray<ObRawExpr*> &flag_exprs,
ObRawExpr *&upper_rn_expr,
ObIArray<ObRawExpr*> &upper_flag_exprs)
{
int ret = OB_SUCCESS;
TableItem *table = NULL;
ObSelectStmt *child_stmt = NULL;
ObSEArray<ObRawExpr*, 4> select_exprs;
upper_rn_expr = NULL;
upper_flag_exprs.reuse();
if (OB_ISNULL(stmt) || OB_UNLIKELY(!stmt->is_single_table_stmt()) ||
OB_ISNULL(table = stmt->get_table_item(0)) ||
OB_ISNULL(child_stmt = table->ref_query_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected stmt", K(ret), K(stmt), K(table), K(child_stmt));
} else if (OB_ISNULL(rn_expr) || OB_UNLIKELY(flag_exprs.empty())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected imput expr", K(ret), K(rn_expr), K(flag_exprs));
} else if (OB_FAIL(select_exprs.assign(flag_exprs))) {
LOG_WARN("failed to assign expr", K(ret));
} else if (OB_FAIL(select_exprs.push_back(rn_expr))) {
LOG_WARN("failed to push back expr", K(ret));
} else if(OB_FAIL(ObTransformUtils::convert_select_expr_to_column_expr(select_exprs, *child_stmt,
*stmt, table->table_id_, upper_flag_exprs))) {
LOG_WARN("failed to convert expr to column epxr", K(ret));
} else if (OB_FALSE_IT(upper_rn_expr = upper_flag_exprs.at(flag_exprs.count()))) {
LOG_WARN("failed to assign expr", K(ret));
} else {
upper_flag_exprs.pop_back();
}
return ret;
}
int ObTransformOrExpansion::convert_exprs_to_win_func_level(ObSelectStmt *stmt,
ObSqlBitSet<> &left_unique_pos,
ObSqlBitSet<> &right_flag_pos,
ObIArray<ObRawExpr*> &partition_exprs,
ObIArray<ObRawExpr*> &order_exprs)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(stmt));
} else {
ObIArray<SelectItem> &select_items = stmt->get_select_items();
for (int64_t i = 0; OB_SUCC(ret) && i < select_items.count(); ++i) {
if (left_unique_pos.has_member(i)) {
ret = partition_exprs.push_back(select_items.at(i).expr_);
} else if (right_flag_pos.has_member(i)) {
ret = order_exprs.push_back(select_items.at(i).expr_);
}
}
}
return ret;
}
int ObTransformOrExpansion::create_row_number_window_function(ObIArray<ObRawExpr *> &partition_exprs,
ObIArray<ObRawExpr *> &order_exprs,
ObWinFunRawExpr *&win_expr)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid argument", K(ret), K(ctx_), K(ctx_->expr_factory_));
} else if (OB_FAIL(ctx_->expr_factory_->create_raw_expr(T_WINDOW_FUNCTION, win_expr))) {
LOG_WARN("create window function expr failed", K(ret));
} else if (OB_ISNULL(win_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid argument", K(ret), K(win_expr));
} else if (OB_FAIL(win_expr->set_partition_exprs(partition_exprs))) {
LOG_WARN("fail to set partition exprs", K(ret));
} else {
Bound upper;
Bound lower;
upper.type_ = BOUND_UNBOUNDED;
lower.type_ = BOUND_UNBOUNDED;
upper.is_preceding_ = true;
lower.is_preceding_ = false;
win_expr->set_func_type(T_WIN_FUN_ROW_NUMBER);
win_expr->set_window_type(WINDOW_RANGE);
win_expr->set_is_between(true);
win_expr->set_upper(upper);
win_expr->set_lower(lower);
for (int64_t i = 0; OB_SUCC(ret) && i < order_exprs.count(); ++i) {
OrderItem sort_key(order_exprs.at(i), NULLS_LAST_ASC);
if (OB_FAIL(win_expr->get_order_items().push_back(sort_key))) {
LOG_WARN("failed to add sort key", K(ret));
}
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(win_expr->formalize(ctx_->session_info_))) {
LOG_WARN("failed to formalize windown function", K(ret));
} else if (OB_FAIL(win_expr->pull_relation_id())) {
LOG_WARN("failed to pull relation id and levels", K(ret));
}
}
return ret;
}
int ObTransformOrExpansion::adjust_transform_types(uint64_t &transform_types)
{
int ret = OB_SUCCESS;
if (cost_based_trans_tried_) {
transform_types &= (~(1 << transformer_type_));
}
return ret;
}
int ObTransformOrExpansion::has_odd_function(const ObDMLStmt &stmt, bool &has)
{
int ret = OB_SUCCESS;
has = false;
if (stmt.is_select_stmt()) {
const ObSelectStmt &select_stmt = static_cast<const ObSelectStmt &>(stmt);
has = (select_stmt.is_contains_assignment()
|| NULL != select_stmt.get_select_into()
|| !select_stmt.get_cte_exprs().empty()
|| !select_stmt.get_cycle_items().empty()
|| !select_stmt.get_search_by_items().empty());
}
if (OB_SUCC(ret) && !has) {
ObSEArray<ObSelectStmt *, 4> child_stmts;
if (OB_FAIL(stmt.get_child_stmts(child_stmts))) {
LOG_WARN("failed to get child stmt", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && !has && i < child_stmts.count(); ++i) {
if (OB_ISNULL(child_stmts.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("child stmt is null", K(ret));
} else if (OB_FAIL(SMART_CALL(has_odd_function(*child_stmts.at(i), has)))) {
LOG_WARN("failed to check has odd function", K(ret));
}
}
}
return ret;
}
int ObTransformOrExpansion::check_basic_validity(const ObDMLStmt &stmt, bool &is_valid)
{
int ret = OB_SUCCESS;
bool has_odd_func = false;
bool check_status = true;
is_valid = false;
if (reached_max_times_for_or_expansion()) {
is_valid = false;
OPT_TRACE("retry count reached max times:", try_times_);
LOG_TRACE("reached max times for or expansion.", K(is_valid));
} else if (stmt.is_set_stmt()
|| stmt.get_from_item_size() == 0
|| !stmt.is_sel_del_upd()) {
// do nothing
} else if ((stmt.is_update_stmt() || stmt.is_delete_stmt()) &&
OB_FAIL(check_upd_del_stmt_validity(static_cast<const ObDelUpdStmt&>(stmt),
check_status))) {
LOG_WARN("failed to check upd del stmt validity", K(ret));
} else if (!check_status) {
/*do nothing */
} else if (OB_FAIL(has_odd_function(stmt, has_odd_func))) {
LOG_WARN("failed to check has odd function", K(ret));
} else if (has_odd_func) {
/*do nothing */
OPT_TRACE("stmt has odd function, will not expand or expr");
} else {
is_valid = true;
}
return ret;
}
// pre-check conditions
int ObTransformOrExpansion::has_valid_condition(ObDMLStmt &stmt,
ObCostBasedRewriteCtx &ctx,
const ObIArray<ObRawExpr*> &conds,
bool &has_valid,
ObIArray<ObRawExpr*> *expect_ordering)
{
int ret = OB_SUCCESS;
has_valid = false;
if (!conds.empty()) {
ctx.hint_ = static_cast<const ObOrExpandHint*>(get_hint(stmt.get_stmt_hint()));
bool is_topk = false;
bool can_set_distinct = false;
OPT_TRACE("check conditions:", conds);
if (NULL == expect_ordering) {
/* do nothing */
} else if (OB_FAIL(StmtUniqueKeyProvider::check_can_set_stmt_unique(&stmt, can_set_distinct))) {
LOG_WARN("failed to check can set stmt unique", K(ret));
} else if (!can_set_distinct) {
/* do nothing */
OPT_TRACE("stmt can not set unique");
} else if (OB_FAIL(get_expect_ordering(stmt, *expect_ordering))) {
LOG_WARN("failed to get expected ordering", K(ret));
} else if (!expect_ordering->empty()) {
is_topk = true;
}
bool using_same_cols = false;
ObSEArray<ObRawExpr*, 4> common_cols;
for (int64_t i = 0; OB_SUCC(ret) && !has_valid && i < conds.count(); ++i) {
if (OB_FAIL(check_condition_valid_basic(&stmt, ctx, conds.at(i), is_topk,
has_valid, common_cols, using_same_cols))) {
LOG_WARN("failed to check condition valid basic", K(ret));
}
}
}
return ret;
}
int ObTransformOrExpansion::get_expect_ordering(const ObDMLStmt &stmt,
ObIArray<ObRawExpr*> &expect_ordering)
{
int ret = OB_SUCCESS;
expect_ordering.reuse();
EqualSets &equal_sets = ctx_->equal_sets_;
ObArenaAllocator alloc;
ObSEArray<ObRawExpr *, 4> const_exprs;
const ObSelectStmt *sel_stmt = stmt.is_select_stmt()
? static_cast<const ObSelectStmt*>(&stmt) : NULL;
if (NULL != sel_stmt && (sel_stmt->has_distinct() || sel_stmt->has_group_by()
|| sel_stmt->has_window_function())) {
/* do nothing */
} else if (!stmt.has_order_by() || !stmt.has_limit()) {
/* do nothing */
} else if (OB_FAIL(const_cast<ObDMLStmt&>(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(stmt.get_condition_exprs(),
const_exprs))) {
LOG_WARN("failed to compute const equivalent exprs", K(ret));
} else {
const ObIArray<OrderItem> &order_items = stmt.get_order_items();
bool is_valid = true;
bool is_lossless = false;
ObRawExpr *expr = NULL;
bool is_const = false;
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < order_items.count(); ++i) {
if (OB_ISNULL(expr = order_items.at(i).expr_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::is_const_expr(expr, equal_sets, const_exprs, is_const))) {
LOG_WARN("failed to check is_const_expr", K(ret));
} else if (is_const) {
/* do nothing */
} else if (OB_FAIL(ObOptimizerUtil::is_lossless_column_cast(expr, is_lossless))) {
LOG_WARN("failed to check is losskess column cast", K(ret));
} else if (is_lossless && OB_ISNULL(expr = expr->get_param_expr(0))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("expr is null");
} else if (!expr->is_column_ref_expr()) {
is_valid = false;
} else if (OB_FAIL(expect_ordering.push_back(expr))) {
LOG_WARN("failed to assign exprs", K(ret));
}
}
}
equal_sets.reuse();
return ret;
}
// expect_ordering contains column exprs from orig_stmt,
// this function convert them to column exprs from spj_stmt.
int ObTransformOrExpansion::convert_expect_ordering(ObDMLStmt *orig_stmt,
ObDMLStmt *spj_stmt,
ObIArray<ObRawExpr*> &expect_ordering)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(orig_stmt) || OB_ISNULL(spj_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (expect_ordering.empty() || orig_stmt == spj_stmt) {
/* do nothing */
} else {
ObSEArray<ObRawExpr*, 4> spj_expect_ordering;
ObColumnRefRawExpr *col = NULL;
ObColumnRefRawExpr *spj_col = NULL;
TableItem *table = NULL;
ObRawExpr *expr = NULL;
int64_t idx = -1;
const int64_t table_size = spj_stmt->get_table_size();
for (int64_t i = 0; OB_SUCC(ret) && i < expect_ordering.count(); ++i) {
if (OB_ISNULL(expr = expect_ordering.at(i)) || !expr->is_column_ref_expr()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected expr", K(ret), K(expr));
} else if (OB_FALSE_IT(col = static_cast<ObColumnRefRawExpr*>(expr))) {
} else if (OB_FAIL(orig_stmt->get_table_item_idx(col->get_table_id(), idx))) {
LOG_WARN("failed to get table item idx", K(ret), K(idx), K(table_size));
} else if (OB_UNLIKELY(idx < 0 || idx > table_size)
|| OB_ISNULL(table = spj_stmt->get_table_item(idx))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected idx", K(ret), K(idx), K(table_size), K(table));
} else if (OB_ISNULL(spj_col = spj_stmt->get_column_expr_by_id(table->table_id_,
col->get_column_id()))) {
LOG_WARN("failed to get column expr by id", K(ret), K(spj_col));
} else if (OB_FAIL(spj_expect_ordering.push_back(spj_col))) {
LOG_WARN("failed to push back exprs", K(ret), K(spj_col));
}
}
if (OB_SUCC(ret) && OB_FAIL(expect_ordering.assign(spj_expect_ordering))) {
LOG_WARN("failed to assign exprs", K(ret), K(spj_expect_ordering.count()));
}
}
return ret;
}
//or-expansion打开dml只能针对单表的更新、删除;对于多表的更新、删除目前不能打开,因为一个目前实现的方式一个视图
//只能设置一个base table item,无法处理多表的情况, 如:delete t1,t2 from t1,t2;
int ObTransformOrExpansion::check_upd_del_stmt_validity(const ObDelUpdStmt &stmt,
bool &is_valid)
{
int ret = OB_SUCCESS;
is_valid = true;
ObQueryCtx *query_ctx = stmt.get_query_ctx();
ObSEArray<ObDmlTableInfo*, 2> table_infos;
if (OB_FAIL(const_cast<ObDelUpdStmt&>(stmt).get_dml_table_infos(table_infos))) {
LOG_WARN("failed to get dml table infos", K(ret));
} else if (1 != table_infos.count()) {
is_valid = false;
OPT_TRACE("multi dml table not support or expansion");
}
return ret;
}
// for update/delete stmt, disable pdml after or expansion
int ObTransformOrExpansion::disable_pdml_for_upd_del_stmt(ObDMLStmt &stmt)
{
int ret = OB_SUCCESS;
if (stmt.is_update_stmt() || stmt.is_delete_stmt()) {
ObSEArray<ObDmlTableInfo*, 2> table_infos;
if (OB_FAIL(static_cast<ObDelUpdStmt&>(stmt).get_dml_table_infos(table_infos))) {
LOG_WARN("failed to get dml table infos", K(ret));
} else if (OB_UNLIKELY(table_infos.empty()) || OB_ISNULL(table_infos.at(0))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null param", K(ret));
} else if (NULL != stmt.get_part_expr(table_infos.at(0)->loc_table_id_,
table_infos.at(0)->ref_table_id_)) {
OPT_TRACE("disable parallel dml after or expansion");
static_cast<ObDelUpdStmt&>(stmt).set_pdml_disabled();
}
}
return ret;
}
/**
* @brief ObTransformOrExpansion::create_spj_view
* 将 stmt 分解成两层:
* 内层做 table scan, join 和 filter,构成一次 SPJ 查询
* 外层做 distinct, group-by, order-by, window function 等非 SPJ 的操作
*/
int ObTransformOrExpansion::get_trans_view(ObDMLStmt *stmt,
ObDMLStmt *&upper_stmt,
ObSelectStmt *&child_stmt)
{
int ret = OB_SUCCESS;
ObStmtFactory *stmt_factory = NULL;
ObRawExprFactory *expr_factory = NULL;
child_stmt = NULL;
if (OB_ISNULL(ctx_) || OB_ISNULL(stmt) || OB_ISNULL(stmt_factory = ctx_->stmt_factory_)
|| OB_ISNULL(expr_factory = ctx_->expr_factory_)
|| OB_UNLIKELY(!stmt->is_sel_del_upd())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("have invalid params", K(ret), K(ctx_), K(stmt), K(stmt_factory),
K(expr_factory), K(stmt->is_sel_del_upd()));
} else if (stmt->is_select_stmt() && static_cast<ObSelectStmt*>(stmt)->is_spj()) {
upper_stmt = stmt;
child_stmt = static_cast<ObSelectStmt*>(stmt);
} else if (OB_FAIL(ObTransformUtils::deep_copy_stmt(
*stmt_factory, *expr_factory, stmt, upper_stmt))) {
LOG_WARN("failed to deep copy stmt", K(ret));
} else if (OB_ISNULL(upper_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected error", K(ret), K(upper_stmt));
} else if (OB_FAIL(disable_pdml_for_upd_del_stmt(*upper_stmt))) {
LOG_WARN("failed to disable pdml for upd_del_stmt", K(ret));
} else if (OB_FAIL(ObTransformUtils::create_simple_view(ctx_, upper_stmt, child_stmt, false))) {
LOG_WARN("failed to create simple view", K(ret));
} else if (OB_FAIL(upper_stmt->formalize_stmt_expr_reference(expr_factory, ctx_->session_info_))) {
LOG_WARN("failed to formalize stmt expr reference", K(ret));
}
return ret;
}
int ObTransformOrExpansion::merge_stmt(ObDMLStmt *&upper_stmt,
ObSelectStmt *input_stmt,
ObSelectStmt *union_stmt)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(upper_stmt) || OB_ISNULL(union_stmt) || OB_ISNULL(input_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("params have null", K(ret), K(upper_stmt), K(union_stmt), K(input_stmt));
} else if (upper_stmt == input_stmt) {
int64_t origin_select_item_count = input_stmt->get_select_item_size();
if (origin_select_item_count == union_stmt->get_select_item_size()) {
upper_stmt = union_stmt;
} else {
ObSelectStmt *temp_stmt = NULL;
if (OB_FAIL(ObTransformUtils::create_stmt_with_generated_table(ctx_,
union_stmt,
temp_stmt))) {
LOG_WARN("create stmt with generated_table failed", K(ret));
} else if (OB_FAIL(remove_stmt_select_item(temp_stmt, origin_select_item_count))) {
LOG_WARN("just stmt select item failed", K(ret));
} else {
upper_stmt = temp_stmt;
}
}
} else if (OB_UNLIKELY(upper_stmt->get_table_size() != 1) ||
OB_ISNULL(upper_stmt->get_table_item(0))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid upper stmt", K(ret));
} else {
upper_stmt->get_table_item(0)->ref_query_ = union_stmt;
}
return ret;
}
int ObTransformOrExpansion::remove_stmt_select_item(ObSelectStmt *select_stmt,
int64_t select_item_count)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(select_stmt) || select_item_count < 0
|| select_stmt->get_select_item_size() < select_item_count) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid argument", K(select_stmt), K(select_item_count),
K(select_stmt->get_select_item_size()), K(ret));
} else {
int64_t i_select_item = select_stmt->get_select_item_size();
do {
if (OB_FAIL(select_stmt->get_select_items().remove(i_select_item - 1))) {
LOG_WARN("remove select item failed", K(ret));
} else {
-- i_select_item;
}
} while (OB_SUCC(ret) && i_select_item != select_item_count);
}
return ret;
}
/**
* @brief ObTransformOrExpansion::check_condition_on_same_columns
* 检查 or 条件的参数是不是同构的:
* 1. 使用相同的列集合
* 2. 所有的列都是关联同一张表的
* e.g. select * from t where t.a = 1 or t.a = 2;
* select * from t where (1 < t.a and t.a < 2) or (5 < t.a and t.a < 6);
* select * from t where (t.a = 1 and t.b = 2) or (t.a = 10 and t.b = 20);
* 以下情况认为 or 条件的参数不同构
* e.g. select * from t where t.a = 1 or t.b = 2;
* select * from t, s where t.a = s.a or t.b = s.b;
* 第二个SQL进行 or expansion 后,我们可以将 or 中的等值条件下降为等值连接条件。
*/
int ObTransformOrExpansion::check_condition_on_same_columns(const ObDMLStmt &stmt,
const ObRawExpr &expr,
bool &using_same_cols)
{
int ret = OB_SUCCESS;
using_same_cols = false;
if (T_OP_OR == expr.get_expr_type()) {
int64_t table_id = OB_INVALID_ID;
ColumnBitSet column_bit_set;
using_same_cols = true;
for (int64_t i = 0; OB_SUCC(ret) && using_same_cols &&
i < expr.get_param_count(); ++i) {
bool from_same_table = true;
ColumnBitSet tmp;
if (OB_FAIL(extract_columns(expr.get_param_expr(i), table_id, from_same_table, tmp))) {
LOG_WARN("failed to extract columns info", K(ret));
} else if (!from_same_table) {
using_same_cols = false;
} else if (0 == i) {
column_bit_set.add_members(tmp);
using_same_cols = column_bit_set.bit_count() > 0;
} else if (!column_bit_set.equal(tmp)) {
using_same_cols = false;
}
}
} else if (T_OP_IN == expr.get_expr_type() &&
OB_NOT_NULL(expr.get_param_expr(1)) &&
T_OP_ROW == expr.get_param_expr(1)->get_expr_type()) {
using_same_cols = true;
}
return ret;
}
int ObTransformOrExpansion::extract_columns(const ObRawExpr *expr,
int64_t &table_id, bool &from_same_table,
ColumnBitSet &col_bit_set)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("expr is null");
} else if (expr->is_column_ref_expr()) {
const ObColumnRefRawExpr *col_expr = static_cast<const ObColumnRefRawExpr *>(expr);
if (OB_INVALID_ID == table_id) {
table_id = col_expr->get_table_id();
}
if (table_id != col_expr->get_table_id()) {
from_same_table = false;
} else if (OB_FAIL(col_bit_set.add_member(col_expr->get_column_id()))) {
LOG_WARN("failed to add member", K(ret));
}
} else if (expr->has_flag(CNT_COLUMN)) {
for (int64_t i = 0; OB_SUCC(ret) && i < expr->get_param_count(); ++i) {
if (OB_FAIL(SMART_CALL(extract_columns(expr->get_param_expr(i),
table_id, from_same_table, col_bit_set)))) {
LOG_WARN("failed to extract columns", K(ret));
} else if (!from_same_table) {
break;
}
}
}
return ret;
}
/**
* @brief ObTransformOrExpansion::get_common_columns_in_condition
* get common column exprs use in T_OP_OR/T_OP_IN:
* 1. 使用相同的列集合
*/
int ObTransformOrExpansion::get_common_columns_in_condition(const ObDMLStmt *stmt,
const ObRawExpr *expr,
ObIArray<ObRawExpr*> &common_cols)
{
int ret = OB_SUCCESS;
common_cols.reuse();
if (OB_ISNULL(stmt) || OB_ISNULL(expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(stmt), K(expr));
} else if (T_OP_OR == expr->get_expr_type()) {
ObSEArray<ObRawExpr*, 4> pre_cols;
ObSEArray<ObRawExpr*, 4> cur_cols;
if (OB_UNLIKELY(1 >= expr->get_param_count())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected expr", K(ret), K(*expr));
} else if (OB_FAIL(inner_get_common_columns_in_condition(stmt, expr->get_param_expr(0),
pre_cols))) {
LOG_WARN("failed to inner get same columns in condition", K(ret));
}
for (int64_t i = 1; OB_SUCC(ret) && !pre_cols.empty() && i < expr->get_param_count(); ++i) {
cur_cols.reuse();
if (OB_FAIL(inner_get_common_columns_in_condition(stmt, expr->get_param_expr(i), cur_cols))) {
LOG_WARN("failed to inner get same columns in condition", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::intersect(pre_cols, cur_cols, pre_cols))) {
LOG_WARN("failed to intercet expr", K(ret));
}
}
if (OB_SUCC(ret) && OB_FAIL(common_cols.assign(pre_cols))) {
LOG_WARN("failed to assign exprs", K(ret));
}
} else if (T_OP_IN == expr->get_expr_type() &&
OB_FAIL(inner_get_common_columns_in_condition(stmt, expr, common_cols))) {
LOG_WARN("failed to inner get same columns in condition", K(ret));
}
return ret;
}
int ObTransformOrExpansion::inner_get_common_columns_in_condition(const ObDMLStmt *stmt,
const ObRawExpr *expr,
ObIArray<ObRawExpr*> &cols)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(stmt) || OB_ISNULL(expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(stmt), K(expr));
} else if (T_OP_AND == expr->get_expr_type()) {
for (int64_t i = 0; OB_SUCC(ret) && i < expr->get_param_count(); ++i) {
if (OB_FAIL(SMART_CALL(inner_get_common_columns_in_condition(stmt, expr->get_param_expr(i),
cols)))) {
LOG_WARN("failed to inner get same columns in condition", K(ret));
}
}
} else if (T_OP_IN == expr->get_expr_type()) {
const ObRawExpr *param = NULL;
bool is_lossless = false;
if (OB_ISNULL(expr->get_param_expr(1)) || OB_ISNULL(param = expr->get_param_expr(0))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("expr is null", K(ret), K(expr->get_param_expr(1)));
} else if (T_OP_ROW != expr->get_param_expr(1)->get_expr_type()) {
/* do nothing */
} else if (OB_FAIL(ObOptimizerUtil::is_lossless_column_cast(param, is_lossless))) {
LOG_WARN("failed to check is lossless column cast", K(ret));
} else if (is_lossless && FALSE_IT(param = param->get_param_expr(0))) {
} else if (OB_ISNULL(param) || !param->is_column_ref_expr()) {
/* do nothing */
} else if (OB_FAIL(add_var_to_array_no_dup(cols, const_cast<ObRawExpr*>(param)))) {
LOG_WARN("failed to push back expr", K(ret));
}
} else if (expr->get_expr_type() == T_OP_EQ) {
ObRawExpr *const_expr = NULL;
if (OB_FAIL(ObOptimizerUtil::compute_const_exprs(const_cast<ObRawExpr*>(expr),
const_expr))) {
LOG_WARN("failed to compute const exprs", K(ret), K(*expr));
} else if (NULL == const_expr || !const_expr->is_column_ref_expr()) {
/* do nothing */
} else if (OB_FAIL(add_var_to_array_no_dup(cols, const_expr))) {
LOG_WARN("failed to push back expr", K(ret));
}
} else {
/* do nothing */
}
return ret;
}
int ObTransformOrExpansion::is_valid_left_join_cond(const ObDMLStmt *stmt,
const ObRawExpr *expr,
ObSqlBitSet<> &right_table_set,
bool &is_valid)
{
int ret = OB_SUCCESS;
is_valid = false;
if (OB_ISNULL(stmt) || OB_ISNULL(expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null expr", K(ret));
} else if (!expr->get_relation_ids().overlap(right_table_set)) {
/* do nothing */
} else if (OB_FAIL(is_contain_join_cond(stmt, expr, is_valid))) {
LOG_WARN("failed to check is contain join cond", K(ret));
} else if (is_valid) {
/* do nothing */
} else if (OB_FAIL(is_valid_table_filter(expr, right_table_set, is_valid))) {
LOG_WARN("failed to check is valid table filter", K(ret));
}
return ret;
}
int ObTransformOrExpansion::is_valid_inner_join_cond(const ObDMLStmt *stmt,
const ObRawExpr *expr,
bool &is_valid)
{
int ret = OB_SUCCESS;
is_valid = false;
if (OB_ISNULL(stmt) || OB_ISNULL(expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null expr", K(ret));
} else if (OB_FAIL(is_contain_join_cond(stmt, expr, is_valid))) {
LOG_WARN("failed to check is contain join cond", K(ret));
} else if (is_valid) {
/* do nothing */
} else if (OB_FAIL(is_simple_cond(stmt, expr, is_valid))) {
LOG_WARN("failed to check is simple cond", K(ret));
}
return ret;
}
int ObTransformOrExpansion::is_simple_cond(const ObDMLStmt *stmt,
const ObRawExpr *expr,
bool &is_simple)
{
int ret = OB_SUCCESS;
is_simple = false;
if (OB_ISNULL(expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null expr", K(ret));
} else if (1 >= expr->get_relation_ids().num_members()) {
is_simple = true;
} else if (T_OP_AND == expr->get_expr_type()) {
is_simple = true;
int64_t N = expr->get_param_count();
for (int64_t i = 0; OB_SUCC(ret) && is_simple && i < N; ++i) {
if (OB_FAIL(SMART_CALL(is_simple_cond(stmt,
expr->get_param_expr(i),
is_simple)))) {
LOG_WARN("failed to check is simple cond", K(ret));
}
}
}
return ret;
}
// expr is table filter.
// for T_OP_AND expr, child exprs are all table filters.
int ObTransformOrExpansion::is_valid_table_filter(const ObRawExpr *expr,
ObSqlBitSet<> &right_table_set,
bool &is_contain)
{
int ret = OB_SUCCESS;
is_contain = false;
if (OB_ISNULL(expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null expr", K(ret));
} else if (expr->get_relation_ids().is_subset(right_table_set)
|| !expr->get_relation_ids().overlap(right_table_set)) {
is_contain = true;
} else if (T_OP_AND == expr->get_expr_type()) {
int64_t N = expr->get_param_count();
is_contain = true;
for (int64_t i = 0; OB_SUCC(ret) && is_contain && i < N; ++i) {
if (OB_FAIL(SMART_CALL(is_valid_table_filter(expr->get_param_expr(i),
right_table_set,
is_contain)))) {
LOG_WARN("failed to check is simple cond", K(ret));
}
}
}
return ret;
}
// todo: can check join condition match index
int ObTransformOrExpansion::is_contain_join_cond(const ObDMLStmt *stmt,
const ObRawExpr *expr,
bool &is_contain)
{
int ret = OB_SUCCESS;
is_contain = false;
if (OB_ISNULL(stmt) || OB_ISNULL(expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null expr", K(ret));
} else if (expr->has_flag(IS_JOIN_COND)) {
is_contain = true;
} else if (T_OP_AND == expr->get_expr_type()) {
for (int64_t i = 0; OB_SUCC(ret) && !is_contain && i < expr->get_param_count(); ++i) {
if (OB_FAIL(SMART_CALL(is_contain_join_cond(stmt,
expr->get_param_expr(i),
is_contain)))) {
LOG_WARN("failed to check is contain join cond", K(ret));
}
}
}
return ret;
}
int ObTransformOrExpansion::is_match_index(const ObDMLStmt *stmt,
const ObRawExpr *expr,
EqualSets &equal_sets,
ObIArray<ObRawExpr*> &const_exprs,
bool &is_match)
{
int ret = OB_SUCCESS;
is_match = false;
if (OB_ISNULL(ctx_) || OB_ISNULL(stmt) || OB_ISNULL(expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null ctx", K(ret));
} else if (expr->has_flag(IS_SIMPLE_COND) ||
expr->has_flag(IS_RANGE_COND) ||
T_OP_IS == expr->get_expr_type()) {
ObSEArray<ObRawExpr*, 2> column_exprs;
ObColumnRefRawExpr *col_expr = NULL;
if (OB_FAIL(ObRawExprUtils::extract_column_exprs(expr, column_exprs))) {
LOG_WARN("failed to extrace column exprs", K(ret));
} else if (1 != column_exprs.count()) {
//do nothing
} else if (OB_ISNULL(column_exprs.at(0))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null expr", K(ret));
} else if (!column_exprs.at(0)->is_column_ref_expr()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("expect column ref expr", K(*column_exprs.at(0)), K(ret));
} else if (OB_FALSE_IT(col_expr = static_cast<ObColumnRefRawExpr*>(column_exprs.at(0)))) {
} else if (OB_ISNULL(stmt->get_table_item_by_id(col_expr->get_table_id()))) {
//do nothing
} else if (OB_FAIL(ObTransformUtils::is_match_index(ctx_->sql_schema_guard_,
stmt,
col_expr,
is_match,
&equal_sets,
&const_exprs))) {
LOG_WARN("failed to check is match index", K(ret));
}
} else if (T_OP_IN == expr->get_expr_type()) {
const ObRawExpr *right_expr = NULL;
if (OB_UNLIKELY(2 != expr->get_param_count())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("in expr should have 2 param", K(expr->get_param_count()), K(ret));
} else if (OB_ISNULL(right_expr = expr->get_param_expr(1))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("null expr", K(ret));
} else if (T_OP_ROW == right_expr->get_expr_type()) {
bool is_const = true;
for (int64_t i = 0;
OB_SUCC(ret) && is_const && i < right_expr->get_param_count(); i++) {
const ObRawExpr *temp_expr = right_expr->get_param_expr(i);
if (OB_ISNULL(temp_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("null expr", K(ret));
} else if (!temp_expr->is_const_expr()) {
is_const = false;
} else { /*do nothing*/ }
}
const ObColumnRefRawExpr *col_expr = NULL;
if (OB_FAIL(ret) || !is_const) {
//do nothing
} else if (OB_ISNULL(expr->get_param_expr(0))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null expr", K(ret));
} else if (!expr->get_param_expr(0)->is_column_ref_expr()) {
//do nothing
} else if (OB_FALSE_IT(col_expr = static_cast<const ObColumnRefRawExpr*>(expr->get_param_expr(0)))) {
} else if (OB_ISNULL(stmt->get_table_item_by_id(col_expr->get_table_id()))) {
//do nothing
} else if (OB_FAIL(ObTransformUtils::is_match_index(ctx_->sql_schema_guard_,
stmt,
col_expr,
is_match,
&equal_sets,
&const_exprs))) {
LOG_WARN("failed to check is match index", K(ret));
}
} else { /*do nothing*/ }
} else if (T_OP_AND == expr->get_expr_type()) {
for (int64_t i = 0; OB_SUCC(ret) && !is_match && i < expr->get_param_count(); ++i) {
if (OB_FAIL(SMART_CALL(is_match_index(stmt,
expr->get_param_expr(i),
equal_sets,
const_exprs,
is_match)))) {
LOG_WARN("failed to check is match index", K(ret));
}
}
} else if (expr->has_flag(CNT_COLUMN) &&
(expr->has_flag(IS_ROWID_SIMPLE_COND) ||
expr->has_flag(IS_ROWID_RANGE_COND))) {
//rowid = const or rowid belong const range can choose primary key.
is_match = true;
}
return ret;
}
// todo: can also check refquery unnest hint
int ObTransformOrExpansion::is_valid_subquery_cond(const ObDMLStmt &stmt,
const ObRawExpr &expr,
bool &is_valid)
{
int ret = OB_SUCCESS;
is_valid = false;
if (T_OP_EXISTS == expr.get_expr_type() || T_OP_NOT_EXISTS == expr.get_expr_type()) {
const ObRawExpr *child_expr = NULL;
if (OB_ISNULL(child_expr = static_cast<const ObOpRawExpr*>(&expr)->get_param_expr(0)) ||
OB_UNLIKELY(!child_expr->is_query_ref_expr())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected expr", K(child_expr), K(ret));
} else {
is_valid = static_cast<const ObQueryRefRawExpr*>(child_expr)->has_exec_param();
}
} else if (expr.has_flag(IS_WITH_ALL) || expr.has_flag(IS_WITH_ANY)
|| expr.has_flag(IS_WITH_SUBQUERY)) {
const ObOpRawExpr *op_expr = static_cast<const ObOpRawExpr*>(&expr);
const ObRawExpr *left_expr = NULL;
const ObRawExpr *right_expr = NULL;
if (OB_ISNULL(left_expr = op_expr->get_param_expr(0))
|| OB_ISNULL(right_expr = op_expr->get_param_expr(1))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected expr", K(left_expr), K(right_expr), K(ret));
} else {
is_valid |= left_expr->is_query_ref_expr() ?
static_cast<const ObQueryRefRawExpr*>(left_expr)->has_exec_param():
!left_expr->get_relation_ids().is_empty();
is_valid |= right_expr->is_query_ref_expr() ?
static_cast<const ObQueryRefRawExpr*>(right_expr)->has_exec_param():
!right_expr->get_relation_ids().is_empty();
}
} else if (T_OP_AND == expr.get_expr_type()) {
const ObRawExpr *child_expr = NULL;
for (int64_t i = 0; OB_SUCC(ret) && !is_valid && i < expr.get_param_count(); ++i) {
if (OB_ISNULL(child_expr = expr.get_param_expr(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null expr", K(ret));
} else if (OB_FAIL(SMART_CALL(is_valid_subquery_cond(stmt, *child_expr, is_valid)))) {
LOG_WARN("failed to check is valid subquery cond", K(ret));
}
}
}
return ret;
}
/*
* topk: 1. common columns in equal condition / in.
* 2. column expr in expect_ordering can match index after used common_cols.
*/
int ObTransformOrExpansion::is_valid_topk_cond(const ObDMLStmt &stmt,
const ObIArray<ObRawExpr*> &expect_ordering,
const ObIArray<ObRawExpr*> &common_cols,
EqualSets &equal_sets,
ObIArray<ObRawExpr*> &const_exprs,
OrExpandInfo &trans_info)
{
int ret = OB_SUCCESS;
if (!common_cols.empty()) {
bool is_const = false;
ObRawExpr *expr = NULL;
ObColumnRefRawExpr *col_expr = NULL;
for (int64_t i = 0; OB_SUCC(ret) && NULL == col_expr && i < expect_ordering.count(); i++) {
if (OB_ISNULL(expr = expect_ordering.at(i)) || OB_UNLIKELY(!expr->is_column_ref_expr())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(expr));
} else if (OB_FAIL(ObOptimizerUtil::is_const_expr(expr, equal_sets, const_exprs, is_const))) {
LOG_WARN("failed to check is const expr", K(ret));
} else if (is_const) {
/* do nothing */
} else if (ObOptimizerUtil::find_equal_expr(common_cols, expr, equal_sets)) {
/* do nothing */
} else {
col_expr = static_cast<ObColumnRefRawExpr*>(expr);
}
}
if (OB_SUCC(ret) && NULL != col_expr) {
ObSEArray<ObColumnRefRawExpr*, 4> col_exprs;
bool is_match = false;
for (int64_t i = 0; OB_SUCC(ret) && i < common_cols.count(); i++) {
if (OB_ISNULL(expr = common_cols.at(i)) || OB_UNLIKELY(!expr->is_column_ref_expr())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(expr));
} else if (OB_FAIL(col_exprs.push_back(static_cast<ObColumnRefRawExpr*>(expr)))) {
LOG_WARN("failed to push back exprs", K(ret));
}
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(ObTransformUtils::is_match_index(ctx_->sql_schema_guard_,
&stmt, col_expr, is_match,
&equal_sets, &const_exprs,
&col_exprs, true))) {
LOG_WARN("failed to check is ordering match index", K(ret));
} else if (is_match) {
trans_info.is_set_distinct_ = true;
trans_info.or_expand_type_ |= OR_EXPAND_TOP_K;
trans_info.is_valid_topk_ = true;
}
}
}
return ret;
}
/**
* @brief ObTransformOrExpansion::check_condition_valid_basic
* basic check for condition:
* 1. or_expr_count is less than MAX_STMT_NUM_FOR_OR_EXPANSION
* 2. is OR expr, and not anti or condition for anti join
* 3. is IN expr, contains no subquery, right expr is a const row
*/
int ObTransformOrExpansion::check_condition_valid_basic(const ObDMLStmt *stmt,
ObCostBasedRewriteCtx &ctx,
const ObRawExpr *expr,
const bool is_topk,
bool &is_valid,
ObIArray<ObRawExpr*> &common_cols,
bool &using_same_cols)
{
int ret = OB_SUCCESS;
is_valid = false;
using_same_cols = true;
common_cols.reuse();
int classify_count = 0;
OPT_TRACE("check expr:", expr);
if (OB_ISNULL(stmt) || OB_ISNULL(expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(stmt), K(expr));
} else if (expr->has_flag(CNT_ROWNUM)) {
is_valid = false;
OPT_TRACE("expr has rownum");
} else if (T_OP_OR != expr->get_expr_type() && T_OP_IN != expr->get_expr_type()) {
is_valid = false;
OPT_TRACE("not or/in expr");
} else if (T_OP_OR == expr->get_expr_type()) {
if (OB_FAIL(pre_classify_or_expr(expr, classify_count))) {
LOG_WARN("failed to classify or expr", K(ret));
} else {
is_valid = expr->get_param_count() > 1 && classify_count <= MAX_STMT_NUM_FOR_OR_EXPANSION;
}
} else if (is_topk && T_OP_IN == expr->get_expr_type() && !expr->has_flag(CNT_SUB_QUERY)) {
const ObRawExpr *right_expr = NULL;
if (OB_UNLIKELY(2 != expr->get_param_count())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("in expr should have 2 param", K(expr->get_param_count()), K(ret));
} else if (OB_ISNULL(right_expr = expr->get_param_expr(1))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("null expr", K(ret));
} else if (T_OP_ROW == right_expr->get_expr_type() &&
right_expr->get_param_count() <= MAX_STMT_NUM_FOR_OR_EXPANSION &&
right_expr->get_param_count() >= 2) {
const ObRawExpr *temp_expr = NULL;
is_valid = right_expr->get_param_count() > 1;
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < right_expr->get_param_count(); ++i) {
if (OB_ISNULL(temp_expr = right_expr->get_param_expr(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("null expr", K(ret));
} else if (temp_expr->is_const_expr()) {
/*do nothing*/
} else {
is_valid = false;
OPT_TRACE("in expr has none const param");
}
}
} else { /*do nothing*/ }
} else {
OPT_TRACE("not or/in expr");
}
if (OB_FAIL(ret) || !is_valid) {
} else if (is_topk && // top-K, get common cols in conditions
OB_FAIL(get_common_columns_in_condition(stmt, expr, common_cols))) {
LOG_WARN("failed to get get same columns in condition", K(ret));
} else if (OB_FAIL(check_condition_on_same_columns(*stmt, *expr, using_same_cols))) {
LOG_WARN("failed to check condition on same columns", K(ret));
} else if (common_cols.empty() && using_same_cols && !expr->has_flag(CNT_SUB_QUERY)) {
is_valid = false;
OPT_TRACE("or expr from same table, will not expand");
} else if (using_same_cols && T_OP_OR == expr->get_expr_type()
&& expr->get_param_count() > MAX_STMT_NUM_FOR_OR_EXPANSION) {
is_valid = false;
OPT_TRACE("or expr param count > MAX_STMT_NUM_FOR_OR_EXPANSION, will not expand");
} else {
is_valid = NULL == ctx.hint_ || ctx.hint_->enable_use_concat(*expr);
if (!is_valid) {
OPT_TRACE("hint disable transform");
}
}
return ret;
}
/**
* @brief ObTransformOrExpansion::is_condition_valid
* 1. use_concat hint: do transfrom if match basic condition.
* 2. topk: expect_ordering is not empty, has same column
* 3. can do subquery pullup/can generate join condition/can use different index
*/
int ObTransformOrExpansion::is_condition_valid(const ObDMLStmt *stmt,
ObCostBasedRewriteCtx &ctx,
const ObRawExpr *expr,
const ObIArray<ObRawExpr*> &expect_ordering,
const JoinedTable *joined_table,
EqualSets &equal_sets,
ObIArray<ObRawExpr*> &const_exprs,
OrExpandInfo &trans_info)
{
int ret = OB_SUCCESS;
const bool can_set_distinct = trans_info.is_set_distinct_;
trans_info.or_expand_type_ = INVALID_OR_EXPAND_TYPE;
trans_info.is_set_distinct_ = false;
bool is_valid = false;
ObSEArray<ObRawExpr*, 4> common_cols;
bool using_same_cols = false;
if (OB_ISNULL(stmt) || OB_ISNULL(expr) ||
OB_UNLIKELY(NULL != joined_table && NULL == joined_table->right_table_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(stmt), K(expr), K(joined_table));
} else if (OB_FAIL(check_condition_valid_basic(stmt, ctx, expr,
!expect_ordering.empty() && can_set_distinct,
is_valid, common_cols, using_same_cols))) {
LOG_WARN("failed to check condition valid basic", K(ret));
} else if (!is_valid) {
/*do nothing*/
} else if (!common_cols.empty() && // 1. topk: can eliminate ordering
OB_FAIL(is_valid_topk_cond(*stmt, expect_ordering, common_cols,
equal_sets, const_exprs, trans_info))) {
LOG_WARN("failed to check is valid topk cond", K(ret));
} else if ((trans_info.or_expand_type_ & OR_EXPAND_TOP_K)
&& T_OP_OR == expr->get_expr_type()
&& expr->get_param_count() > MAX_STMT_NUM_FOR_OR_EXPANSION) {
trans_info.or_expand_type_ = INVALID_OR_EXPAND_TYPE; // means is_valid == false
} else if (NULL != ctx.hint_ && ctx.hint_->is_enable_hint()) {
// 2. match basic condition, do transform if use hint.
if (OB_FAIL(get_use_hint_expand_type(*expr, can_set_distinct, trans_info))) {
LOG_WARN("failed to get expand type use hint", K(ret));
}
} else if (INVALID_OR_EXPAND_TYPE != trans_info.or_expand_type_) {
/*do nothing*/
LOG_TRACE("valid topk condition", K(*expr), K(trans_info));
} else if (T_OP_OR != expr->get_expr_type()) {
/*do nothing*/
} else if (using_same_cols && !expr->has_flag(CNT_SUB_QUERY)) {
/*do nothing*/
} else {
ObSqlBitSet<> right_table_set;
ObJoinType join_type = UNKNOWN_JOIN;
if (NULL == joined_table) {
join_type = 1 < expr->get_relation_ids().num_members() ? INNER_JOIN : UNKNOWN_JOIN;
} else if (joined_table->is_inner_join()) {
join_type = INNER_JOIN;
} else if (OB_FAIL(stmt->get_table_rel_ids(*joined_table->right_table_, right_table_set))) {
LOG_WARN("failed to get table rel ids", K(ret));
} else {
join_type = LEFT_OUTER_JOIN;
}
const int64_t N = expr->get_param_count();
const ObRawExpr *child_expr = NULL;
bool has_valid_subquery = false;
int64_t subquery_count = 0;
bool match_index = (join_type == UNKNOWN_JOIN);
bool join_cond_valid = !match_index;
for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) {
if (OB_ISNULL(child_expr = expr->get_param_expr(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("expr is null", K(ret), K(child_expr));
} else if (!join_cond_valid) {
/*do nothing*/
} else if (LEFT_OUTER_JOIN == join_type && OB_FAIL(is_valid_left_join_cond(stmt, child_expr,
right_table_set, join_cond_valid))) {
LOG_WARN("failed to check is valid left join cond", K(ret));
} else if (INNER_JOIN == join_type && OB_FAIL(is_valid_inner_join_cond(stmt,
child_expr, join_cond_valid))) {
LOG_WARN("failed to check is valid inner join cond", K(ret));
}
if (OB_SUCC(ret) && match_index &&
OB_FAIL(is_match_index(stmt, child_expr, equal_sets, const_exprs, match_index))) {
LOG_WARN("failed to check is match index", K(ret));
}
if (OB_SUCC(ret) && !has_valid_subquery &&
OB_FAIL(is_valid_subquery_cond(*stmt, *child_expr, has_valid_subquery))) {
LOG_WARN("failed to check is valid subquery cond", K(ret));
}
if (OB_SUCC(ret) && child_expr->has_flag(CNT_SUB_QUERY)) {
++subquery_count;
}
}
if (OB_SUCC(ret)) {
if (!can_set_distinct && subquery_count > 1) {
// do nothing
} else {
trans_info.is_set_distinct_ = subquery_count > 1;
trans_info.or_expand_type_ |= join_cond_valid ? OR_EXPAND_JOIN : 0;
trans_info.or_expand_type_ |= has_valid_subquery ? OR_EXPAND_SUB_QUERY : 0;
trans_info.or_expand_type_ |= match_index ? OR_EXPAND_MULTI_INDEX : 0;
}
LOG_TRACE("after check is condition valid", K(join_cond_valid), K(match_index),
K(has_valid_subquery), K(trans_info), K(*expr));
}
}
return ret;
}
int ObTransformOrExpansion::get_use_hint_expand_type(const ObRawExpr &expr,
const bool can_set_distinct,
OrExpandInfo &trans_info)
{
int ret = OB_SUCCESS;
trans_info.or_expand_type_ = OR_EXPAND_HINT;
if (!can_set_distinct) {
trans_info.is_set_distinct_ = false;
} else {
trans_info.is_set_distinct_ |= (T_OP_IN == expr.get_expr_type());
int64_t sub_num = 0;
int64_t N = expr.get_param_count();
for (int64_t i = 0; OB_SUCC(ret) && !trans_info.is_set_distinct_ && i < N; i++) {
if (OB_ISNULL(expr.get_param_expr(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("null expr", K(ret));
} else if (expr.get_param_expr(i)->has_flag(CNT_SUB_QUERY)) {
++sub_num;
trans_info.is_set_distinct_ = sub_num > 1;
}
}
}
return ret;
}
// expand_anti_or_cond is generate when pullup where subquery: t1.c1 in (select t2.c1 ...)
// t1.c1 = t2.c1 or t1.c1 is null or t2.c1 is null
int ObTransformOrExpansion::is_expand_anti_or_cond(const ObRawExpr &expr,
bool &is_anti_or_cond)
{
int ret = OB_SUCCESS;
is_anti_or_cond = false;
if (T_OP_OR == expr.get_expr_type()) {
is_anti_or_cond = true;
int64_t is_null_count = 0;
const ObRawExpr *cond_expr = NULL;
const ObRawExpr *param_expr = NULL;
for (int64_t i = 0; OB_SUCC(ret) && is_anti_or_cond && i < expr.get_param_count(); i++) {
if (OB_ISNULL(cond_expr = expr.get_param_expr(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("null expr", K(ret));
} else if (T_OP_IS != cond_expr->get_expr_type()) {
is_anti_or_cond = T_OP_EQ == cond_expr->get_expr_type() ||
IS_RANGE_CMP_OP(cond_expr->get_expr_type());
} else if (OB_UNLIKELY(2 != cond_expr->get_param_count()) ||
OB_ISNULL(param_expr = cond_expr->get_param_expr(1))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected expr", K(ret), K(*cond_expr));
} else if (T_NULL == param_expr->get_expr_type()) {
++is_null_count;
}
}
if (OB_SUCC(ret) && is_anti_or_cond) {
is_anti_or_cond = is_null_count + 1 == expr.get_param_count();
}
}
return ret;
}
// trans_id is OB_INVALID_ID, do transform in where condition;
// trans_id is a semi id, do transform in semi condition;
// trans_id is a joined table id, do transform in on condition;
int ObTransformOrExpansion::transform_or_expansion(ObSelectStmt *stmt,
const uint64_t trans_id,
const int64_t expr_pos,
ObCostBasedRewriteCtx &ctx,
ObSelectStmt *&trans_stmt,
StmtUniqueKeyProvider &unique_key_provider)
{
int ret = OB_SUCCESS;
ObSelectStmt *copy_stmt = NULL;
ObStmtFactory *stmt_factory = NULL;
ObRawExprFactory *expr_factory = NULL;
ObSQLSessionInfo *session_info = NULL;
ObIArray<ObRawExpr*> *conds_exprs = NULL;
ObSelectStmt *view_stmt = NULL;
TableItem* view_table = NULL;
int64_t view_expr_pos = expr_pos;
if (OB_ISNULL(stmt) || OB_ISNULL(ctx_) ||
OB_ISNULL(session_info = ctx_->session_info_) ||
OB_ISNULL(stmt_factory = ctx_->stmt_factory_) ||
OB_ISNULL(expr_factory = ctx_->expr_factory_)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(stmt), K(ctx_),
K(session_info), K(stmt_factory), K(expr_factory), K(ret));
} else if (OB_FAIL(get_expand_conds(*stmt, trans_id, conds_exprs))) {
LOG_WARN("failed to get expand conds", K(ret));
} else if (OB_ISNULL(conds_exprs) || OB_UNLIKELY(expr_pos < 0 || expr_pos >= conds_exprs->count())
|| OB_ISNULL(ctx.orig_expr_ = conds_exprs->at(expr_pos))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid condition expr pos", K(expr_pos), K(conds_exprs), K(ctx.orig_expr_), K(ret));
} else if (OB_FAIL(stmt_factory->create_stmt(copy_stmt))) {
LOG_WARN("failed to create stmt factory", K(copy_stmt), K(ret));
} else if (OB_ISNULL(copy_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null stmt", K(ret), K(copy_stmt));
} else if (OB_FAIL(copy_stmt->deep_copy(*stmt_factory, *expr_factory, *stmt))) {
LOG_WARN("failed to deep copy child statement", K(ret));
} else if (OB_FAIL(copy_stmt->get_stmt_hint().reset_explicit_trans_hint(T_USE_CONCAT))) {
LOG_WARN("failed to reset explicit trans hint", K(ret));
} else if (OB_FAIL(get_expand_conds(*copy_stmt, trans_id, conds_exprs))) {
LOG_WARN("failed to get expand conds", K(ret));
} else if (FALSE_IT(view_stmt = copy_stmt)) {
// never reach
} else if (OB_INVALID_ID == trans_id && !ctx.is_valid_topk_ &&
OB_FAIL(get_condition_related_view(copy_stmt, view_stmt, view_table,
view_expr_pos, conds_exprs,
ctx.is_set_distinct_))) {
LOG_WARN("failed to create view for tables", K(ret));
} else if (!ctx.is_set_distinct_ && OB_FAIL(preprocess_or_condition(*view_stmt, trans_id, view_expr_pos))) {
LOG_WARN("failed to preprocess or condition", K(ret));
} else if (ctx.is_set_distinct_ && !ctx.is_unique_ &&
OB_FAIL(unique_key_provider.recursive_set_stmt_unique(view_stmt, ctx_, true))) {
LOG_WARN("failed to set stmt unique", K(ret));
} else if (!ctx.is_valid_topk_
&& OB_FAIL(classify_or_expr(*view_stmt, conds_exprs->at(view_expr_pos)))) {
LOG_WARN("failed to classify or expr", K(ret), KPC(conds_exprs->at(view_expr_pos)));
} else {
const uint64_t or_expr_count = get_or_expr_count(*conds_exprs->at(view_expr_pos));
ObSEArray<ObSelectStmt*, 2> child_stmts;
ObSelectStmt *set_stmt = NULL;
SemiInfo *semi_info = view_stmt->get_semi_info_by_id(trans_id);
const ObSelectStmt::SetOperator set_type = NULL != semi_info && semi_info->is_anti_join()
? ObSelectStmt::INTERSECT : ObSelectStmt::UNION;
for (int64_t i = 0; OB_SUCC(ret) && i < or_expr_count; i++) {
ObSelectStmt *child_stmt = view_stmt;
if (i < or_expr_count - 1 && OB_FAIL(stmt_factory->create_stmt(child_stmt))) {
LOG_WARN("failed to create stmt factory", K(child_stmt), K(ret));
} else if (OB_ISNULL(child_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null stmt", K(ret), K(child_stmt));
} else if (i < or_expr_count - 1 &&
OB_FAIL(child_stmt->deep_copy(*stmt_factory, *expr_factory, *view_stmt))) {
LOG_WARN("failed to deep copy child statement", K(ret));
} else if (OB_FAIL(child_stmt->recursive_adjust_statement_id(ctx_->allocator_,
ctx_->src_hash_val_,
i + 1))) {
LOG_WARN("failed to recursive adjust statement id", K(ret));
} else if (OB_FAIL(get_expand_conds(*child_stmt, trans_id, conds_exprs))) {
LOG_WARN("failed to get expand conds", K(ret));
} else if (OB_FAIL(child_stmt->update_stmt_table_id(ctx_->allocator_, *view_stmt))) {
//update stmt table id after find conds_exprs
LOG_WARN("failed to update table id", K(ret));
} else if (OB_FAIL(adjust_or_expansion_stmt(conds_exprs, view_expr_pos, i,
ctx, child_stmt))) {
LOG_WARN("failed to adjust children stmt", K(ret));
} else if (OB_FAIL(child_stmts.push_back(child_stmt))) {
LOG_WARN("failed to push back stmt", K(ret));
} else { /* do nothing */ }
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(ObTransformUtils::create_set_stmt(ctx_, set_type, ctx.is_set_distinct_,
child_stmts, set_stmt))) {
LOG_WARN("failed to create union stmt", K(set_stmt), K(ret));
} else if (OB_ISNULL(set_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(set_stmt));
} else if (OB_FAIL(set_stmt->formalize_stmt(session_info))) {
LOG_WARN("failed to formalize stmt", K(ret));
} else {
if (NULL != view_table) {
view_table->ref_query_ = set_stmt;
trans_stmt = copy_stmt;
} else {
trans_stmt = set_stmt;
}
ctx.trans_id_ = set_stmt->get_stmt_id();
}
//ignore for temp table optimization
if (OB_SUCC(ret)) {
if (OB_FAIL(append(ctx_->temp_table_ignore_stmts_, child_stmts))) {
LOG_WARN("failed to append child stmts", K(ret));
}
}
}
return ret;
}
int ObTransformOrExpansion::adjust_or_expansion_stmt(ObIArray<ObRawExpr*> *conds_exprs,
const int64_t expr_pos,
const int64_t param_pos,
ObCostBasedRewriteCtx &ctx,
ObSelectStmt *&or_expansion_stmt)
{
int ret = OB_SUCCESS;
ObSQLSessionInfo *session_info = NULL;
ObRawExprFactory *expr_factory = NULL;
ObRawExpr *transformed_expr = NULL;
ObSEArray<ObQueryRefRawExpr *, 4> removed_subqueries;
if (OB_ISNULL(conds_exprs) || OB_ISNULL(ctx_)
|| OB_ISNULL(session_info = ctx_->session_info_)
|| OB_ISNULL(expr_factory = ctx_->expr_factory_)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("null point error", K(ctx_), K(session_info), K(expr_factory), K(ret));
} else if (OB_ISNULL(or_expansion_stmt)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("null stmt", K(ret));
} else if (OB_UNLIKELY(expr_pos < 0) || OB_UNLIKELY(expr_pos >= conds_exprs->count())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid condition expr pos", K(expr_pos), K(conds_exprs->count()), K(ret));
} else if (OB_ISNULL(transformed_expr = conds_exprs->at(expr_pos))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("null transformed expr", K(ret));
} else if (OB_FAIL(conds_exprs->remove(expr_pos))) {
LOG_WARN("failed to remove condition expr", K(ret));
} else if (T_OP_OR == transformed_expr->get_expr_type()) {
ObSEArray<ObRawExpr*, 4> generated_exprs;
if (OB_FAIL(create_expr_for_or_expr(*transformed_expr,
param_pos,
ctx,
generated_exprs))) {
LOG_WARN("failed to create expr", K(ret));
} else if (OB_FAIL(append(*conds_exprs, generated_exprs))) {
LOG_WARN("failed to append expr", K(ret));
} else { /*do nothing*/ }
} else if (T_OP_IN == transformed_expr->get_expr_type()) {
ObSEArray<ObRawExpr*, 4> generated_exprs;
if (OB_FAIL(create_expr_for_in_expr(*transformed_expr,
param_pos,
ctx,
generated_exprs))) {
LOG_WARN("failed to create expr", K(ret), K(generated_exprs));
} else if (OB_FAIL(append(*conds_exprs, generated_exprs))) {
LOG_WARN("failed to append exprs", K(ret));
} else { /*do nothing*/ }
} else {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected expr type", K(transformed_expr->get_expr_type()), K(ret));
}
if (OB_SUCC(ret)) {
if (OB_FAIL(or_expansion_stmt->formalize_stmt(session_info))) {
LOG_WARN("failed to formalize stmt", K(ret));
} else {
// clean unused subqueries
ObSEArray<ObRawExpr*, 4> relation_exprs;
ObSEArray<ObQueryRefRawExpr *, 8> used_subquery_exprs;
if (OB_FAIL(or_expansion_stmt->get_relation_exprs(relation_exprs))) {
LOG_WARN("failed to get relation exprs");
} else if (OB_FAIL(ObTransformUtils::extract_query_ref_expr(relation_exprs, used_subquery_exprs))) {
LOG_WARN("failed to get query exprs");
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < or_expansion_stmt->get_subquery_exprs().count(); ++i) {
ObQueryRefRawExpr *expr = or_expansion_stmt->get_subquery_exprs().at(i);
if (ObOptimizerUtil::find_item(used_subquery_exprs, expr)) {
// do nothing
} else if (OB_FAIL(ObOptimizerUtil::remove_item(or_expansion_stmt->get_subquery_exprs(), expr))) {
LOG_WARN("failed to remove item from subquery exprs", K(ret));
}
}
}
}
}
return ret;
}
int ObTransformOrExpansion::create_expr_for_in_expr(const ObRawExpr &transformed_expr,
const int64_t param_pos,
ObCostBasedRewriteCtx &ctx,
ObIArray<ObRawExpr *> &generated_exprs)
{
int ret = OB_SUCCESS;
const ObRawExpr *left_expr = NULL;
const ObRawExpr *right_expr = NULL;
ObRawExpr *temp_expr = NULL;
ObRawExprFactory *expr_factory = NULL;
ObSQLSessionInfo *session_info = NULL;
if (OB_ISNULL(ctx_) ||
OB_ISNULL(expr_factory = ctx_->expr_factory_) ||
OB_ISNULL(session_info = ctx_->session_info_)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ctx_), K(session_info), K(expr_factory), K(ret));
} else if (OB_UNLIKELY(T_OP_IN != transformed_expr.get_expr_type()
|| !ctx.is_set_distinct_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected expr type or trans type", K(transformed_expr.get_expr_type()),
K(ctx.is_set_distinct_), K(ret));
} else if (OB_UNLIKELY(2 != transformed_expr.get_param_count())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("in expr should have 2 param", K(transformed_expr.get_param_count()), K(ret));
} else if (OB_ISNULL(left_expr = transformed_expr.get_param_expr(0)) ||
OB_ISNULL(right_expr = transformed_expr.get_param_expr(1))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("null expr", K(left_expr), K(right_expr), K(ret));
} else if (T_OP_ROW != right_expr->get_expr_type()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected type", K(right_expr->get_expr_type()), K(ret));
} else if (OB_UNLIKELY(param_pos < 0) || OB_UNLIKELY(param_pos >= right_expr->get_param_count())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected expr pos", K(param_pos),
K(right_expr->get_param_count()), K(ret));
} else if (OB_FAIL(ObRawExprUtils::create_equal_expr(*expr_factory,
session_info,
left_expr,
right_expr->get_param_expr(param_pos),
temp_expr))) {
LOG_WARN("failed to create double op expr", K(ret));
} else if (OB_FAIL(ctx.expand_exprs_.push_back(temp_expr))) {
LOG_WARN("failed to push back expand exprs", K(ret));
} else if (OB_FAIL(generated_exprs.push_back(temp_expr))) {
LOG_WARN("failed to push back temp expr", K(ret));
}
return ret;
}
int ObTransformOrExpansion::create_expr_for_or_expr(ObRawExpr &transformed_expr,
const int64_t param_pos,
ObCostBasedRewriteCtx &ctx,
common::ObIArray<ObRawExpr*> &generated_exprs)
{
int ret = OB_SUCCESS;
ObRawExprFactory *expr_factory = NULL;
if (OB_ISNULL(ctx_) || OB_ISNULL(expr_factory = ctx_->expr_factory_)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("null point error", K(ctx_), K(expr_factory), K(ret));
} else if (OB_UNLIKELY(T_OP_OR != transformed_expr.get_expr_type())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected expr type", K(transformed_expr.get_expr_type()), K(ret));
} else if (OB_UNLIKELY(param_pos < 0) ||
OB_UNLIKELY(param_pos >= transformed_expr.get_param_count())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected expr pos", K(param_pos),
K(transformed_expr.get_param_count()), K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && i <= param_pos; i++) {
ObRawExpr *expr = NULL;
ObRawExpr *temp_expr = NULL;
if (OB_ISNULL(expr = transformed_expr.get_param_expr(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("null expr", K(ret));
} else if (i == param_pos) {
if (OB_FAIL(ctx.expand_exprs_.push_back(expr))) {
LOG_WARN("failed to push back expr", K(ret));
} else if (OB_FAIL(ObTransformUtils::flatten_expr(expr, generated_exprs))) {
LOG_WARN("failed to flatten expr", K(ret));
} else { /*do nothing*/ }
} else if (ctx.is_set_distinct_) {
/*do nothing */
} else if (OB_FAIL(ObRawExprUtils::build_lnnvl_expr(*expr_factory, expr, temp_expr))) {
LOG_WARN("failed to create lnnvl expr", K(ret));
} else if (OB_FAIL(generated_exprs.push_back(temp_expr))) {
LOG_WARN("failed to push back expr", K(ret));
} else { /*do nothing*/ }
}
}
return ret;
}
// pre-check conditions for semi/anti condition
int ObTransformOrExpansion::has_valid_semi_anti_cond(ObDMLStmt &stmt,
ObCostBasedRewriteCtx &ctx,
int64_t &begin_idx)
{
int ret = OB_SUCCESS;
begin_idx = OB_INVALID_INDEX;
bool can_set_unique = false;
bool has_lob = false;
if (OB_FAIL(check_select_expr_has_lob(stmt, has_lob))) {
LOG_WARN("failed to check stmt has lob", K(ret));
} else if (has_lob) {
// do nothing
OPT_TRACE("select expr has lob expr");
} else if (OB_FAIL(StmtUniqueKeyProvider::check_can_set_stmt_unique(&stmt, can_set_unique))) {
LOG_WARN("failed to check can set stmt unique", K(ret));
} else if (!can_set_unique) {
// do nothing
OPT_TRACE("stmt can not be unique");
} else {
ctx.hint_ = static_cast<const ObOrExpandHint*>(get_hint(stmt.get_stmt_hint()));
bool has_valid = false;
uint64_t trans_type = INVALID_OR_EXPAND_TYPE;
SemiInfo *semi_info = NULL;
for (int64_t i = 0; OB_SUCC(ret) && !has_valid && i < stmt.get_semi_info_size(); ++i) {
if (OB_ISNULL(semi_info = stmt.get_semi_infos().at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(semi_info));
} else {
OPT_TRACE("check semi conditions:", semi_info->semi_conditions_);
const int64_t N = semi_info->semi_conditions_.count();
for (int64_t j = 0; OB_SUCC(ret) && !has_valid && j < N; ++j) {
if (OB_FAIL(is_valid_semi_anti_cond(&stmt, ctx, semi_info->semi_conditions_.at(j),
semi_info, trans_type))) {
LOG_WARN("failed to check has valid condition", K(ret));
} else if (INVALID_OR_EXPAND_TYPE != trans_type) {
has_valid = true;
}
}
}
if (OB_SUCC(ret) && has_valid) {
begin_idx = i;
}
}
}
return ret;
}
/**
* @brief ObTransformOrExpansion::is_valid_semi_anti_cond
* check expand expr can get join condition or table filter
*/
int ObTransformOrExpansion::is_valid_semi_anti_cond(const ObDMLStmt *stmt,
ObCostBasedRewriteCtx &ctx,
const ObRawExpr *expr,
const SemiInfo *semi_info,
uint64_t &trans_type)
{
int ret = OB_SUCCESS;
trans_type = INVALID_OR_EXPAND_TYPE;
bool check_status = false;
int classify_count = 0;
OPT_TRACE("check expr:", expr);
if (OB_ISNULL(stmt) || OB_ISNULL(expr) || OB_ISNULL(semi_info)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(stmt), K(expr), K(semi_info));
} else if (expr->has_flag(CNT_ROWNUM)) {
/* do nothing */
OPT_TRACE("expr has rownum");
} else if (T_OP_OR != expr->get_expr_type() || expr->get_param_count() <= 1) {
/* do nothing */
OPT_TRACE("not or expr");
} else if (OB_FAIL(check_condition_on_same_columns(*stmt, *expr, check_status))) {
LOG_WARN("failed to check condition on same columns", K(ret));
} else if (check_status) {
/* do nothing */
OPT_TRACE("or expr param from same table");
} else if (OB_FAIL(pre_classify_or_expr(expr, classify_count))) {
LOG_WARN("failed to classify or expr", K(ret));
} else if (classify_count > MAX_STMT_NUM_FOR_OR_EXPANSION) {
/* do nothing */
OPT_TRACE("or expr classify count more than MAX_STMT_NUM_FOR_OR_EXPANSION");
} else if (NULL != ctx.hint_) {
trans_type = ctx.hint_->enable_use_concat(*expr) ? OR_EXPAND_HINT : INVALID_OR_EXPAND_TYPE;
if (INVALID_OR_EXPAND_TYPE == trans_type) {
OPT_TRACE("hint disable transform");
}
} else if (semi_info->is_anti_join() && OB_FAIL(is_expand_anti_or_cond(*expr, check_status))) {
LOG_WARN("failed to check is expand anti or cond", K(ret));
} else if (check_status) {
/* do nothing */
OPT_TRACE("is anti join or condition, do not expand");
} else {
const int64_t N = expr->get_param_count();
const ObRawExpr *child_expr = NULL;
ObSqlBitSet<> right_table_set;
if (OB_FAIL(stmt->get_table_rel_ids(semi_info->right_table_id_, right_table_set))) {
LOG_WARN("failed to get table rel ids", K(ret));
} else if (expr->get_relation_ids().is_subset(right_table_set)
|| !expr->get_relation_ids().overlap(right_table_set)) {
/* semi/anti join or condition is a table filter. if it's a anti join left table filter,
need not expand, else it can be expand as a normal where condition. */
} else {
check_status = true;
for (int64_t i = 0; OB_SUCC(ret) && check_status && i < N; ++i) {
if (OB_ISNULL(child_expr = expr->get_param_expr(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("expr is null", K(ret), K(child_expr));
} else if (OB_FAIL(is_contain_join_cond(stmt, child_expr, check_status))) {
LOG_WARN("failed to check is contain join cond", K(ret));
} else if (check_status) {
/* do nothing */
} else if (OB_FAIL(is_valid_table_filter(child_expr, right_table_set, check_status))) {
LOG_WARN("failed to check is contain table filter", K(ret));
}
}
if (OB_SUCC(ret) && check_status) {
trans_type = OR_EXPAND_JOIN;
LOG_TRACE("get valid semi anti cond", K(semi_info->is_anti_join()), K(*expr));
}
}
}
return ret;
}
int ObTransformOrExpansion::gather_transform_infos(ObSelectStmt *stmt,
ObCostBasedRewriteCtx &ctx,
const common::ObIArray<ObRawExpr*> &candi_conds,
const ObIArray<ObRawExpr*> &expect_ordering,
const JoinedTable *joined_table,
ObIArray<OrExpandInfo> &trans_infos)
{
int ret = OB_SUCCESS;
ctx.is_unique_ = false;
trans_infos.reuse();
bool can_set_distinct = false;
bool need_check_unique = false;
ObArenaAllocator alloc;
EqualSets &equal_sets = ctx_->equal_sets_;
ObSEArray<ObRawExpr *, 4> const_exprs;
OrExpandInfo trans_info;
bool has_lob = false;
if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(stmt));
} else 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(stmt->get_condition_exprs(),
const_exprs))) {
LOG_WARN("failed to compute const equivalent exprs", K(ret));
} else if (OB_FAIL(check_select_expr_has_lob(*stmt, has_lob))) {
LOG_WARN("failed to check stmt has lob", K(ret));
} else if (has_lob) {
can_set_distinct = false;
} else if (OB_FAIL(StmtUniqueKeyProvider::check_can_set_stmt_unique(stmt, can_set_distinct))) {
LOG_WARN("failed to check can set stmt unique", K(ret));
}
// get all trans_info
for (int64_t i = 0; OB_SUCC(ret) && i < candi_conds.count(); ++i) {
trans_info.pos_ = i;
trans_info.is_set_distinct_ = can_set_distinct;
if (OB_FAIL(is_condition_valid(stmt, ctx, candi_conds.at(i), expect_ordering, joined_table,
equal_sets, const_exprs, trans_info))) {
LOG_WARN("failed to check condition is valid", K(ret));
} else if (INVALID_OR_EXPAND_TYPE == trans_info.or_expand_type_) {
// do nothing
} else if (OB_FAIL(trans_infos.push_back(trans_info))) {
LOG_WARN("failed to push back trans info", K(ret));
} else {
need_check_unique |= trans_info.is_set_distinct_;
}
}
equal_sets.reuse();
// adjust ordering of trans_info in trans_infos
int64_t head = 0;
for (int64_t i = 0; OB_SUCC(ret) && i < trans_infos.count(); ++i) {
if (trans_infos.at(i).is_set_distinct_ && i > head) {
trans_info = trans_infos.at(i);
trans_infos.at(i) = trans_infos.at(head);
trans_infos.at(head++) = trans_info;
}
}
if (OB_SUCC(ret) && need_check_unique &&
OB_FAIL(ObTransformUtils::check_stmt_unique(stmt, ctx_->session_info_,
ctx_->schema_checker_, true /* strict */,
ctx.is_unique_))) {
LOG_WARN("failed to check stmt unique", K(ret));
}
return ret;
}
int64_t ObTransformOrExpansion::get_or_expr_count(const ObRawExpr &expr)
{
int64_t or_expr_count = 0;
if (T_OP_OR == expr.get_expr_type()) {
or_expr_count = expr.get_param_count();
} else if (T_OP_IN == expr.get_expr_type() && OB_NOT_NULL(expr.get_param_expr(1))) {
or_expr_count = expr.get_param_expr(1)->get_param_count();
}
return or_expr_count;
}
int ObTransformOrExpansion::get_expand_conds(ObSelectStmt &stmt,
uint64_t trans_id,
ObIArray<ObRawExpr*> *&conds_exprs)
{
int ret = OB_SUCCESS;
conds_exprs = NULL;
SemiInfo *semi_info = NULL;
JoinedTable *joined_table = NULL;
if (OB_INVALID_ID == trans_id) {
conds_exprs = &stmt.get_condition_exprs();
} else if (NULL != (semi_info = stmt.get_semi_info_by_id(trans_id))) {
conds_exprs = &semi_info->semi_conditions_;
} else if (OB_ISNULL(joined_table = stmt.get_joined_table(trans_id))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect trans id", K(ret), K(trans_id));
} else {
conds_exprs = &joined_table->get_join_conditions();
}
return ret;
}
/**
* @brief ObTransformOrExpansion::preprocess_or_condition
* move params with subquery to the tail of the expr
*/
int ObTransformOrExpansion::preprocess_or_condition(ObSelectStmt &stmt,
const uint64_t trans_id,
const int64_t expr_pos)
{
int ret = OB_SUCCESS;
ObRawExpr *expr = NULL;
ObIArray<ObRawExpr*> *conds_exprs = NULL;
if (OB_FAIL(get_expand_conds(stmt, trans_id, conds_exprs))) {
LOG_WARN("failed to get expand conds", K(ret));
} else if (OB_ISNULL(conds_exprs)
|| OB_UNLIKELY(expr_pos < 0 || expr_pos >= conds_exprs->count())
|| OB_ISNULL(expr = conds_exprs->at(expr_pos))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid condition expr pos", K(expr_pos), K(conds_exprs), K(expr), K(ret));
} else if (expr->has_flag(CNT_SUB_QUERY) && T_OP_OR == expr->get_expr_type()) {
int64_t tail = expr->get_param_count() - 1;
ObRawExpr *param = NULL;
for (int64_t i = expr->get_param_count() - 1; OB_SUCC(ret) && i >= 0; --i) {
if (OB_ISNULL(param = expr->get_param_expr(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("param expr is null", K(ret));
} else if (param->has_flag(CNT_SUB_QUERY)) {
expr->get_param_expr(i) = expr->get_param_expr(tail);
expr->get_param_expr(tail--) = param;
}
}
}
return ret;
}
/*
1. normal where condition: single table scan use index
2. inner join with join condition in where:
3. subquery where condition: subplan filter is less then origin plan
4. topk: union child operator has no sort or has a prefix sort
5. outer/semi/anti join: use nlj with exec param push down
or use merge/hash and origin plan is nlj
*/
int ObTransformOrExpansion::is_expected_plan(ObLogPlan *plan, void *check_ctx, bool is_trans_plan, bool &is_valid)
{
int ret = OB_SUCCESS;
ObCostBasedRewriteCtx *ctx = static_cast<ObCostBasedRewriteCtx *>(check_ctx);
ObLogicalOperator* log_set = NULL;
is_valid = false;
if (OB_ISNULL(ctx) || OB_ISNULL(plan)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null param", K(ret));
} else if (!is_trans_plan) {
// do nothing
} else if (OB_FAIL(find_trans_log_set(plan->get_plan_root(), ctx->trans_id_, log_set))) {
LOG_WARN("failed to get join operator", K(ret));
} else if (NULL == log_set) {
//do nothing
} else if (OB_FAIL(check_is_expected_plan(log_set, *ctx, is_valid))) {
LOG_WARN("failed to check is expected plan", K(ret));
} else {
LOG_DEBUG("debug check or expansion is expected plan", K(is_valid), K(ctx->or_expand_type_),
K(*plan));
}
return ret;
}
int ObTransformOrExpansion::find_trans_log_set(ObLogicalOperator* op,
const uint64_t trans_id,
ObLogicalOperator *&log_set)
{
int ret = OB_SUCCESS;
log_set = NULL;
if (OB_ISNULL(op) || OB_ISNULL(op->get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null", K(ret), K(op));
} else if (log_op_def::LOG_SET == op->get_type() && op->get_stmt()->is_set_stmt()
&& trans_id == op->get_stmt()->get_stmt_id()) {
log_set = op;
} else {
for (int64_t i = 0; OB_SUCC(ret) && NULL == log_set && i < op->get_num_of_child(); ++i) {
if (OB_FAIL(SMART_CALL(find_trans_log_set(op->get_child(i), trans_id, log_set)))) {
LOG_WARN("failed to find operator", K(ret));
}
}
}
return ret;
}
int ObTransformOrExpansion::check_is_expected_plan(ObLogicalOperator* op,
ObCostBasedRewriteCtx &ctx,
bool &is_valid)
{
int ret = OB_SUCCESS;
is_valid = false;
const uint64_t or_expand_type = ctx.or_expand_type_;
if ((OR_EXPAND_TOP_K & or_expand_type)
&& OB_FAIL(is_expected_topk_plan(op, is_valid))) {
LOG_WARN("failed to check is expected topk plan", K(ret));
} else if (is_valid) {
/* do nothing */
} else if (OR_EXPAND_SUB_QUERY & or_expand_type) {
is_valid = true;
} else if (OR_EXPAND_JOIN & or_expand_type) {
is_valid = true;
} else if ((OR_EXPAND_MULTI_INDEX & or_expand_type)
&& OB_FAIL(is_expected_multi_index_plan(op, ctx, is_valid))) {
LOG_WARN("failed to check is expected multi index plan", K(ret));
}
return ret;
}
int ObTransformOrExpansion::is_expected_topk_plan(ObLogicalOperator* op,
bool &is_valid)
{
int ret = OB_SUCCESS;
is_valid = false;
if (NULL == op) {
is_valid = false;
} else if (log_op_def::LOG_SET == op->get_type()) {
if (OB_ISNULL(op->get_stmt()) || OB_UNLIKELY(!op->get_stmt()->is_set_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect op", K(ret), K(*op));
} else {
// child query is a set from the same or expansion transform, need check recursive
is_valid = true;
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < op->get_num_of_child(); ++i) {
if (OB_FAIL(SMART_CALL(is_expected_topk_plan(op->get_child(i), is_valid)))) {
LOG_WARN("failed to check is expected topk plan", K(ret));
}
}
}
} else {
while (NULL != op && log_op_def::LOG_TABLE_SCAN != op->get_type() && !op->is_block_op()) {
op = op->get_child(0);
}
if (NULL == op || op->is_block_op()) {
is_valid = false;
} else if (log_op_def::LOG_TABLE_SCAN == op->get_type()) {
is_valid = true;
}
}
return ret;
}
int ObTransformOrExpansion::is_expected_multi_index_plan(ObLogicalOperator* op,
ObCostBasedRewriteCtx &ctx,
bool &is_valid)
{
int ret = OB_SUCCESS;
is_valid = true;
const int64_t N = ctx.expand_exprs_.count();
ObSEArray<ObRawExpr*, 4> candi_exprs;
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < N; ++i) {
candi_exprs.reuse();
if (OB_FAIL(get_candi_match_index_exprs(ctx.expand_exprs_.at(i), candi_exprs))) {
LOG_WARN("failed to get candi match index exprs", K(ret));
} else if (OB_FAIL(remove_filter_exprs(op, candi_exprs))) {
LOG_WARN("failed to remove filter exprs", K(ret));
} else if (candi_exprs.empty()) {
is_valid = false;
}
}
return ret;
}
int ObTransformOrExpansion::remove_filter_exprs(ObLogicalOperator* op,
ObIArray<ObRawExpr*> &candi_exprs)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(op) || candi_exprs.empty()) {
/* do nothing */
} else if (!op->get_filter_exprs().empty() &&
OB_FAIL(ObOptimizerUtil::except_exprs(candi_exprs,
op->get_filter_exprs(),
candi_exprs))) {
LOG_WARN("failed to get except exprs", K(ret));
} else if (log_op_def::LOG_JOIN == op->get_type() &&
!static_cast<ObLogJoin*>(op)->get_join_filters().empty() &&
OB_FAIL(ObOptimizerUtil::except_exprs(candi_exprs,
static_cast<ObLogJoin*>(op)->get_join_filters(),
candi_exprs))) {
LOG_WARN("failed to get except exprs", K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && !candi_exprs.empty() && i < op->get_num_of_child(); ++i) {
if (OB_FAIL(SMART_CALL(remove_filter_exprs(op->get_child(i), candi_exprs)))) {
LOG_WARN("failed to remove filter exprs", K(ret));
}
}
}
return ret;
}
int ObTransformOrExpansion::is_candi_match_index_exprs(ObRawExpr *expr, bool &result)
{
int ret = OB_SUCCESS;
result = true;
if (OB_ISNULL(expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect NULL", K(ret));
} else if (expr->has_flag(IS_SIMPLE_COND) || expr->has_flag(IS_RANGE_COND)
|| T_OP_IN == expr->get_expr_type()) {
if (expr->get_relation_ids().is_empty()) {
// can not use as range candition
result = false;
}
} else if (T_OP_OR == expr->get_expr_type() || T_OP_AND == expr->get_expr_type()) {
for (int64_t i = 0; OB_SUCC(ret) && result && i < expr->get_param_count(); ++i) {
if (OB_FAIL(SMART_CALL(is_candi_match_index_exprs(expr->get_param_expr(i),
result)))) {
LOG_WARN("failed to check is candi match index exprs", K(ret));
}
}
} else if (expr->has_flag(IS_ROWID_SIMPLE_COND) ||
expr->has_flag(IS_ROWID_RANGE_COND)) {
//rowid = const or rowid belong const range can choose primary key.
} else {
result = false;
}
return ret;
}
int ObTransformOrExpansion::get_candi_match_index_exprs(ObRawExpr *expr,
ObIArray<ObRawExpr*> &candi_exprs)
{
int ret = OB_SUCCESS;
bool is_candi = true;
if (OB_ISNULL(expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect NULL", K(ret));
} else if (T_OP_AND == expr->get_expr_type()) {
for (int64_t i = 0; OB_SUCC(ret) && i < expr->get_param_count(); ++i) {
if (OB_FAIL(SMART_CALL(get_candi_match_index_exprs(expr->get_param_expr(i),
candi_exprs)))) {
LOG_WARN("failed to get candi match index exprs", K(ret));
}
}
} else if (OB_FAIL(is_candi_match_index_exprs(expr, is_candi))) {
LOG_WARN("failed to check is candi match index exprs", K(ret));
} else if (is_candi && OB_FAIL(candi_exprs.push_back(expr))) {
LOG_WARN("failed to push back expr", K(ret));
} else {/*do nothing*/}
return ret;
}
// check select items contain lob type
int ObTransformOrExpansion::check_select_expr_has_lob(ObDMLStmt &stmt, bool &has_lob)
{
int ret = OB_SUCCESS;
has_lob = false;
if (stmt.is_select_stmt()) {
ObIArray<SelectItem> &select_items = static_cast<ObSelectStmt&>(stmt).get_select_items();
ObRawExpr *select_expr = NULL;
for (int64_t i = 0; OB_SUCC(ret) && !has_lob && i < select_items.count(); ++i) {
if (OB_ISNULL(select_expr = select_items.at(i).expr_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null expr", K(ret), K(select_items.at(i)));
} else {
has_lob = ObLongTextType == select_expr->get_data_type() ||
ObLobType == select_expr->get_data_type();
}
}
} else if (!stmt.is_update_stmt() && !stmt.is_delete_stmt()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect stmt type", K(ret), K(stmt.get_stmt_type()));
} else {
const ObIArray<ColumnItem> &column_items = static_cast<ObDelUpdStmt&>(stmt).get_column_items();
ObRawExpr *column_expr = NULL;
for (int64_t i = 0; OB_SUCC(ret) && !has_lob && i < column_items.count(); ++i) {
if (OB_ISNULL(column_expr = column_items.at(i).expr_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null expr", K(ret), K(column_items.at(i)));
} else {
has_lob = ObLongTextType == column_expr->get_data_type() ||
ObLobType == column_expr->get_data_type();
}
}
}
return ret;
}
int ObTransformOrExpansion::construct_transform_hint(ObDMLStmt &stmt, void *trans_params)
{
int ret = OB_SUCCESS;
ObOrExpandHint *hint = NULL;
const ObRawExpr *expr = NULL;
ObCostBasedRewriteCtx *eval_cost_ctx = static_cast<ObCostBasedRewriteCtx*>(trans_params);
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->allocator_) || OB_ISNULL(eval_cost_ctx)
|| OB_ISNULL(expr = eval_cost_ctx->orig_expr_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(ctx_), K(eval_cost_ctx), K(expr));
} else if (OB_FAIL(ctx_->add_used_trans_hint(eval_cost_ctx->hint_))) {
LOG_WARN("failed to push back hint", K(ret));
} else if (OB_FAIL(ObQueryHint::create_hint(ctx_->allocator_, T_USE_CONCAT, hint))) {
LOG_WARN("failed to create hint", K(ret));
} else if (OB_FAIL(ctx_->outline_trans_hints_.push_back(hint))) {
LOG_WARN("failed to push back hint", K(ret));
} else if (OB_FAIL(hint->set_expand_condition(*ctx_->allocator_, *expr))) {
LOG_WARN("failed to set expand condition", K(ret));
} else {
hint->set_qb_name(ctx_->src_qb_name_);
}
return ret;
}
// Classify or expr to get union count before do_transform
int ObTransformOrExpansion::pre_classify_or_expr(const ObRawExpr *expr, int &count)
{
int ret = OB_SUCCESS;
ObSEArray<TableColBitSet, 4> table_col_bit_sets;
count = 0;
if (OB_ISNULL(expr) || T_OP_OR != expr->get_expr_type()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected expr", K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < expr->get_param_count(); ++i) {
TableColBitSet table_col_bit_set;
bool from_same_table = true;
int64_t table_id = OB_INVALID_ID;
const ObRawExpr *branch = NULL;
if (OB_ISNULL(branch = expr->get_param_expr(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret));
} else if (branch->has_flag(CNT_SUB_QUERY) || branch->get_relation_ids().is_empty()) {
// conditions with subqueries will be classfied separately
// irrelevant conditions will be classfied separately
++count;
} else if (OB_FAIL(extract_columns(branch,
table_id,
from_same_table,
table_col_bit_set.column_bit_set_))) {
LOG_WARN("failed to extract columns info", K(ret), KPC(branch));
} else if (!from_same_table) {
// conditions of multiple tables will be classfied separately
++count;
} else if (OB_UNLIKELY(OB_INVALID_ID == table_id)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid table id", K(ret), KPC(branch), K(table_id));
} else if (FALSE_IT(table_col_bit_set.table_id_ = table_id)) {
} else if (!ObOptimizerUtil::find_item(table_col_bit_sets,
table_col_bit_set)) {
if (OB_FAIL(table_col_bit_sets.push_back(table_col_bit_set))) {
LOG_WARN("failed to push back bit set", K(ret));
} else {
++count;
}
}
}
}
return ret;
}
// Classify isomorphic predicates into the same class
// For example:
// select * from t1,t2 where t1.a=1 or t1.a=t2.b or t1.a=t2.b+1 or t1.a>2 or t1.c=3
// => select * from t1,t2 where (t1.a=1 or t1.a>2) or (t1.a=t2.b) or (t1.a=t2.b+1) or (t1.c=3)
int ObTransformOrExpansion::classify_or_expr(const ObDMLStmt &stmt, ObRawExpr *&expr)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected or expr", K(ret));
} else if (2 == expr->get_param_count()
|| T_OP_OR != expr->get_expr_type()) {
// do nothing
} else {
ObSEArray<ObRawExpr*, 4> expr_classes;
ObSEArray<TableColBitSet, 4> table_col_bit_sets;
bool classify_happened = false;
for (int64_t i = 0; OB_SUCC(ret) &&
i < expr->get_param_count(); ++i) {
TableColBitSet table_col_bit_set;
bool from_same_table = true;
int64_t isomorphism_idx = OB_INVALID_ID;
int64_t table_id = OB_INVALID_ID;
ObRawExpr *branch = NULL;
if (OB_ISNULL(branch = expr->get_param_expr(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret));
} else if (branch->has_flag(CNT_SUB_QUERY)) {
// conditions with subqueries will be classfied separately
} else if (branch->get_relation_ids().is_empty()) {
// irrelevant conditions will be classfied separately
} else if (OB_FAIL(extract_columns(branch,
table_id,
from_same_table,
table_col_bit_set.column_bit_set_))) {
LOG_WARN("failed to extract columns info", K(ret), KPC(branch));
} else if (!from_same_table) {
table_id = OB_INVALID_ID;
// conditions of multiple tables will be classfied separately
} else if (OB_UNLIKELY(OB_INVALID_ID == table_id)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid table id", K(ret), KPC(branch), K(table_id));
} else if (FALSE_IT(table_col_bit_set.table_id_ = table_id)) {
// never reach
} else if (!ObOptimizerUtil::find_item(table_col_bit_sets,
table_col_bit_set,
&isomorphism_idx)) {
isomorphism_idx = OB_INVALID_ID;
}
if (OB_FAIL(ret)) {
} else if (OB_INVALID_ID == isomorphism_idx) {
// contain subquery or irrelevant expr or not from same table or not found,
// create a new class
if (OB_FAIL(table_col_bit_sets.push_back(table_col_bit_set))) {
LOG_WARN("failed to push back bit set", K(ret));
} else if (OB_FAIL(expr_classes.push_back(branch))) {
LOG_WARN("failed to push back expr class", K(ret));
}
} else {
//merge the expr into the partition
classify_happened = true;
ObRawExpr*& expr_class = expr_classes.at(isomorphism_idx);
if (OB_FAIL(merge_expr_class(expr_class, branch))) {
LOG_WARN("failed to merge expr class", K(ret));
}
}
}
if (OB_FAIL(ret) || !classify_happened) {
} else if (1 == expr_classes.count()) {
//do nothing
} else {
//modify the expr according to the partitions
ObOpRawExpr *op_expr = static_cast<ObOpRawExpr *>(expr);
op_expr->clear_child();
for (int64_t i = 0; OB_SUCC(ret) && i < expr_classes.count(); i++) {
if (OB_FAIL(op_expr->add_param_expr(expr_classes.at(i)))) {
LOG_WARN("failed to add param expr", K(ret));
}
}
}
LOG_DEBUG("or expr after classify", KPC(expr));
}
return ret;
}
int ObTransformOrExpansion::merge_expr_class(ObRawExpr *&expr_class, ObRawExpr *expr)
{
int ret = OB_SUCCESS;
ObOpRawExpr *new_or_expr = NULL;
if (OB_ISNULL(expr_class) || OB_ISNULL(expr) ||
OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret));
} else if (T_OP_OR == expr_class->get_expr_type()) {
new_or_expr = static_cast<ObOpRawExpr *>(expr_class);
if (OB_FAIL(new_or_expr->add_param_expr(expr))) {
LOG_WARN("add param expr to or expr failed", K(ret));
}
} else if (OB_FAIL(ctx_->expr_factory_->create_raw_expr(T_OP_OR, new_or_expr))) {
LOG_WARN("failed to create or expr", K(ret));
} else if (OB_FAIL(new_or_expr->add_param_expr(expr_class))) {
LOG_WARN("add param expr to or expr failed", K(ret));
} else if (OB_FAIL(new_or_expr->add_param_expr(expr))) {
LOG_WARN("add param expr to or expr failed", K(ret));
} else {
expr_class = new_or_expr;
}
return ret;
}
int ObTransformOrExpansion::get_condition_related_tables(ObSelectStmt &stmt,
int64_t expr_pos,
const ObIArray<ObRawExpr*> &conds_exprs,
bool &create_view,
ObIArray<TableItem *> &or_expr_tables,
ObIArray<SemiInfo *> &or_semi_infos)
{
int ret = OB_SUCCESS;
create_view = false;
or_expr_tables.reuse();
or_semi_infos.reuse();
bool contain_shared_subqueries = false;
ObRelIds &or_expr_rel_ids = conds_exprs.at(expr_pos)->get_relation_ids();
// do not create view if or_expr has no rel_ids
// for example: select * from t1
// where (exists (select 1 from t2) or exists (select 1 from t3))
// and (exists (select 1 from t4))
if (!or_expr_rel_ids.is_empty()) {
// create view only if:
// 1. the expansion cond does not contain shared subqueries; and
ObSEArray<ObQueryRefRawExpr *, 4> expand_subqueries;
if (OB_FAIL(ObTransformUtils::extract_query_ref_expr(conds_exprs.at(expr_pos),
expand_subqueries,
true/*with_nested*/))) {
LOG_WARN("failed to extract subqueries", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && !contain_shared_subqueries && i < expand_subqueries.count(); i++) {
if (OB_ISNULL(expand_subqueries.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected NULL", K(ret));
} else if (expand_subqueries.at(i)->get_ref_count() > 1){
contain_shared_subqueries = true;
}
}
if (OB_SUCC(ret) && !contain_shared_subqueries) {
// 2. a. other conds contain subqueries; or
for (int64_t i = 0; !create_view && OB_SUCC(ret) && i < conds_exprs.count(); i++) {
if (i != expr_pos) {
if (OB_ISNULL(conds_exprs.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret));
} else {
create_view = conds_exprs.at(i)->has_flag(CNT_SUB_QUERY);
}
}
}
// b. rel_ids of expr is the proper subset of stmt table ids
// and is related to only one basic table
ObSqlBitSet<> all_table_set;
bool related_to_only_one = false;
if (OB_FAIL(ret)) {
} else if (OB_FAIL(stmt.get_table_rel_ids(stmt.get_table_items(), all_table_set))) {
LOG_WARN("failed to get table ids", K(ret));
} else if (!or_expr_rel_ids.equal(all_table_set) && or_expr_rel_ids.num_members() == 1) {
// the only one related table should not be in a joined table
if (OB_FAIL(stmt.relids_to_table_items(or_expr_rel_ids, or_expr_tables))) {
LOG_WARN("failed to get table items", K(ret));
} else if (OB_UNLIKELY(or_expr_tables.count() != 1)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected table count", K(ret));
} else if (OB_FAIL(check_valid_rel_table(stmt, or_expr_rel_ids, or_expr_tables.at(0), related_to_only_one))) {
LOG_WARN("failed to check valid rel table", K(ret));
} else if (related_to_only_one) {
create_view = true;
}
}
if (OB_SUCC(ret) && create_view && !related_to_only_one) {
// There are exprs to be delayed
// and the or expr is related to more than one tables.
// We need create view for all tables.
// e.g. select * from t1, t2, t3
// where t1.a = t2.a and t2.b = t3.b and
// (t1.c = 1 or t3.c = 1) and
// t1.d in (select d from t4 where t4.a > t1.a limit 10)
// => select * from
// (select * from t1, t2, t3
// where t1.a = t2.a and t2.b = t3.b and
// (t1.c = 1 or t3.c = 1)) v
// where v.d in (select d from t4 where t4.a > v.a limit 10)
or_expr_tables.reuse();
if (OB_FAIL(or_semi_infos.assign(stmt.get_semi_infos()))) {
LOG_WARN("failed to assign semi infos", K(ret));
} else if (OB_FAIL(stmt.get_from_tables(or_expr_tables))) {
LOG_WARN("failed to get from tables", K(ret));
}
}
}
}
return ret;
}
// Create a view of partial tables which are related to the condition
// to avoid repeated computation of subqueries or joins
// For example:
// select * from t1, t2 where (t1.a = 1 or t1.b = 1) and t1.c = t2.c
// => select * from (select * from t1 where (t1.a = 1 or t1.b = 1)) v, t2 where v.c = t2.c
//
// select * from t1 where (t1.a = 1 or t1.a = 2) and exists (select 1 from t2 where t1.a=t2.a)
// => select * from (select * from t1 where (t1.a = 1 or t1.a = 2)) v
// where exists (select 1 from t2 where v.a=t2.a)
int ObTransformOrExpansion::get_condition_related_view(ObSelectStmt *stmt,
ObSelectStmt *&view_stmt,
TableItem *&view_table,
int64_t& expr_pos,
ObIArray<ObRawExpr*> *&conds_exprs,
bool &is_set_distinct)
{
int ret = OB_SUCCESS;
ObStmtFactory *stmt_factory = NULL;
ObRawExprFactory *expr_factory = NULL;
view_stmt = stmt;
view_table = NULL;
bool create_view = false;
ObSEArray<TableItem *, 4> or_expr_tables;
ObSEArray<SemiInfo *, 4> or_semi_infos;
ObSqlBitSet<> table_set;
int64_t new_expr_pos = OB_INVALID_ID;
if (OB_ISNULL(ctx_) || OB_ISNULL(stmt) || OB_ISNULL(stmt_factory = ctx_->stmt_factory_)
|| OB_ISNULL(expr_factory = ctx_->expr_factory_) || OB_ISNULL(conds_exprs)
|| OB_UNLIKELY(!stmt->is_select_stmt()) || OB_UNLIKELY(expr_pos < 0)
|| OB_UNLIKELY(expr_pos >= conds_exprs->count())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("have invalid params", K(ret), K(ctx_), K(stmt), K(stmt_factory),
K(expr_factory), K(conds_exprs), K(expr_pos),
K(stmt->is_select_stmt()));
} else if (OB_FAIL(get_condition_related_tables(*stmt, expr_pos, *conds_exprs,
create_view, or_expr_tables,
or_semi_infos))) {
LOG_WARN("failed to get condition related tables", K(ret));
} else if (!create_view) {
// do nothing
} else {
// push down a predicate, if:
// 1. it is the or expansion cond; or
// 2. a. it does not contain not onetime subquery; and
// b. it is only related to view tables
ObSEArray<ObRawExpr *, 4> push_conditions;
if (OB_FAIL(stmt->get_table_rel_ids(or_expr_tables, table_set))) {
LOG_WARN("failed to get table rel ids", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && i < conds_exprs->count(); i++) {
ObRawExpr *cond = NULL;
if (OB_ISNULL(cond = (conds_exprs->at(i)))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret));
} else if (i != expr_pos &&
(conds_exprs->at(i)->has_flag(CNT_SUB_QUERY) ||
!cond->get_relation_ids().is_subset(table_set))) {
// do not push
} else if (OB_FAIL(push_conditions.push_back(cond))) {
LOG_WARN("failed to push cond", K(ret));
} else if (i == expr_pos){
new_expr_pos = push_conditions.count() - 1;
}
}
// get push down conditions
if (OB_FAIL(ret)) {
// do nothing
} else if (OB_FAIL(ObOptimizerUtil::remove_item(*conds_exprs,
push_conditions))) {
LOG_WARN("failed to remove pushed conditions", K(ret));
} else if (OB_FAIL(ObTransformUtils::replace_with_empty_view(ctx_,
stmt,
view_table,
or_expr_tables,
&or_semi_infos))) {
LOG_WARN("failed to create empty view table", K(ret));
} else if (OB_FAIL(ObTransformUtils::create_inline_view(ctx_,
stmt,
view_table,
or_expr_tables,
&push_conditions,
&or_semi_infos))) {
LOG_WARN("failed to create inline view", K(ret));
} else if (OB_ISNULL(view_table) || OB_ISNULL(view_stmt = view_table->ref_query_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("view table is null", K(ret), K(view_table), K(view_stmt));
} else {
expr_pos = new_expr_pos;
conds_exprs = &view_stmt->get_condition_exprs();
if (is_set_distinct) {
bool has_lob = false;
if (OB_FAIL(check_select_expr_has_lob(*view_stmt, has_lob))) {
LOG_WARN("failed to check lob", K(ret));
} else {
is_set_distinct = !has_lob;
}
}
}
}
return ret;
}
// an expression computation will be delayed
// iff it contains a correlated subquery
int ObTransformOrExpansion::check_delay_expr(ObRawExpr* expr, bool &delay) {
int ret = OB_SUCCESS;
if (!delay) {
if (expr->is_query_ref_expr()) {
delay = expr->get_param_count() != 0;
} else if (expr->has_flag(CNT_SUB_QUERY)){
for (int64_t i = 0; OB_SUCC(ret) && !delay && i < expr->get_param_count(); i++) {
if (OB_FAIL(SMART_CALL(check_delay_expr(expr->get_param_expr(i), delay)))) {
LOG_WARN("failed to check expr to be delayed", K(ret));
}
}
}
}
return ret;
}
int ObTransformOrExpansion::check_valid_rel_table(ObSelectStmt &stmt,
ObRelIds &rel_ids,
TableItem *rel_table,
bool &is_valid)
{
int ret = OB_SUCCESS;
is_valid = false;
bool join_subset = false;
bool left_bottom = false;
ObIArray<JoinedTable*>& joined_tables = stmt.get_joined_tables();
for (int64_t i = 0; OB_SUCC(ret) && !join_subset && i < joined_tables.count(); i++) {
ObSqlBitSet<> table_rel_ids;
TableItem *table = NULL;
if (OB_ISNULL(table = joined_tables.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret));
} else if (OB_FAIL(stmt.get_table_rel_ids(*table,
table_rel_ids))) {
LOG_WARN("failed to get table rel id", K(ret));
} else if (FALSE_IT(join_subset = rel_ids.is_subset(table_rel_ids))) {
// never reach
} else if (join_subset &&
OB_FAIL(check_left_bottom_table(stmt,
rel_table,
joined_tables.at(i),
left_bottom))) {
LOG_WARN("failed to check in join tables", K(ret));
}
}
if (OB_SUCC(ret)) {
is_valid = !(join_subset && !left_bottom);
}
return ret;
}
int ObTransformOrExpansion::check_left_bottom_table(ObSelectStmt &stmt,
TableItem *rel_table,
TableItem *table,
bool &left_bottom)
{
int ret = OB_SUCCESS;
left_bottom = false;
if (OB_ISNULL(table)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected param", K(ret));
} else if (table->is_joined_table()) {
JoinedTable *joined_table = static_cast<JoinedTable *>(table);
if (joined_table->is_left_join() || joined_table->is_right_join()) {
TableItem* left = joined_table->is_left_join() ?
joined_table->left_table_ :
joined_table->right_table_;
if (OB_FAIL(SMART_CALL(check_left_bottom_table(stmt,
rel_table,
left,
left_bottom)))) {
LOG_WARN("failed to check in joined tables", K(ret));
}
}
} else if (rel_table == table){
left_bottom = true;
}
return ret;
}
int ObTransformOrExpansion::check_stmt_valid_for_expansion(ObDMLStmt *stmt, bool &is_stmt_valid)
{
int ret = OB_SUCCESS;
is_stmt_valid = true;
if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected param", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && is_stmt_valid && i < stmt->get_table_size(); i++) {
TableItem *table_item = stmt->get_table_items().at(i);
if (OB_ISNULL(table_item)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret));
} else if (table_item->is_fake_cte_table()) {
is_stmt_valid = false;
}
}
return ret;
}
} /* namespace sql */
} /* namespace oceanbase */