1628 lines
76 KiB
C++
1628 lines
76 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_semi_to_inner.h"
|
|
#include "sql/rewrite/ob_transform_utils.h"
|
|
#include "sql/optimizer/ob_optimizer_util.h"
|
|
#include "sql/optimizer/ob_log_table_scan.h"
|
|
#include "sql/optimizer/ob_log_join.h"
|
|
#include "sql/optimizer/ob_log_function_table.h"
|
|
#include "common/ob_smart_call.h"
|
|
using namespace oceanbase::sql;
|
|
using namespace oceanbase::common;
|
|
|
|
/**
|
|
* @brief ObTransformSemiToInner::transform_one_stmt
|
|
* @param parent_stmts
|
|
* @param stmt
|
|
* @param trans_happened
|
|
* @return
|
|
*/
|
|
int ObTransformSemiToInner::transform_one_stmt(
|
|
common::ObIArray<ObParentDMLStmt> &parent_stmts, ObDMLStmt *&stmt, bool &trans_happened)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObDMLStmt *root_stmt = NULL;
|
|
bool accepted = false;
|
|
bool spj_view_added = false;
|
|
ObSEArray<SemiInfo*, 4> semi_infos;
|
|
ObSEArray<TableItem*, 4> trans_right_table_items;
|
|
ObCostBasedRewriteCtx ctx;
|
|
ObTryTransHelper try_trans_helper;
|
|
if (OB_ISNULL(stmt) || OB_ISNULL(ctx_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("param has null", K(ret), K(stmt), K(ctx_));
|
|
} else if (OB_FAIL(semi_infos.assign(stmt->get_semi_infos()))) {
|
|
LOG_WARN("failed to assign semi infos", K(ret));
|
|
} else {
|
|
bool cost_based_trans_tried = cost_based_trans_tried_;
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < semi_infos.count() && !spj_view_added; ++i) {
|
|
SemiInfo *semi_info = semi_infos.at(i);
|
|
TableItem *table_item = NULL;
|
|
ObDMLStmt *trans_stmt = NULL;
|
|
bool need_check_cost = false;
|
|
bool happened = false;
|
|
bool force_trans = false;
|
|
bool force_no_trans = false;
|
|
if (!parent_stmts.empty()) {
|
|
root_stmt = parent_stmts.at(parent_stmts.count()-1).stmt_;
|
|
} else {
|
|
root_stmt = stmt;
|
|
}
|
|
OPT_TRACE("try to transform semi join ", stmt->get_table_item_by_id(semi_info->right_table_id_));
|
|
if (OB_ISNULL(semi_info)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null semi info", K(ret));
|
|
} else if (semi_info->is_anti_join()) {
|
|
//do nothing
|
|
OPT_TRACE("anti join can not transform");
|
|
} else if (OB_ISNULL(table_item = stmt->get_table_item_by_id(semi_info->right_table_id_))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null table item", K(ret));
|
|
} else if (OB_FAIL(check_hint_valid(*stmt,
|
|
*table_item,
|
|
force_trans,
|
|
force_no_trans))) {
|
|
LOG_WARN("failed to check hint valid", K(ret));
|
|
} else if (force_no_trans) {
|
|
//do nothing
|
|
OPT_TRACE("hint reject transform");
|
|
} else if (OB_FALSE_IT(ctx.hint_force_ = force_trans)) {
|
|
} else if (OB_FAIL(try_trans_helper.fill_helper(stmt->get_query_ctx()))) {
|
|
LOG_WARN("failed to fill try trans helper", K(ret));
|
|
} else if (OB_FAIL(transform_semi_to_inner(root_stmt,
|
|
stmt,
|
|
semi_info,
|
|
trans_stmt,
|
|
ctx,
|
|
need_check_cost,
|
|
spj_view_added,
|
|
happened))) {
|
|
LOG_WARN("failed to transform semi join to inner join", K(ret));
|
|
} else if (!happened) {
|
|
OPT_TRACE("semi join can not transform to inner join");
|
|
LOG_TRACE("semi join can not transform to inner join", K(*semi_info));
|
|
} else if (OB_FAIL(accept_transform(parent_stmts, stmt, trans_stmt,
|
|
!need_check_cost || ctx.hint_force_,
|
|
accepted, &ctx))) {
|
|
LOG_WARN("failed to accept transform", K(ret));
|
|
} else if (!accepted) {
|
|
if (OB_FAIL(try_trans_helper.recover(stmt->get_query_ctx()))) {
|
|
LOG_WARN("failed to recover params", K(ret));
|
|
} else if (OB_FAIL(add_ignore_semi_info(semi_info->semi_id_))) {
|
|
LOG_WARN("failed to add ignore semi info", K(ret));
|
|
} else {
|
|
LOG_TRACE("semi join can not transform to inner join due to cost", K(*semi_info));
|
|
}
|
|
} else {
|
|
if (!need_check_cost || ctx.hint_force_) {
|
|
cost_based_trans_tried_ = cost_based_trans_tried;
|
|
} else {
|
|
cost_based_trans_tried = cost_based_trans_tried_;
|
|
add_trans_type(ctx_->happened_cost_based_trans_, SEMI_TO_INNER);
|
|
}
|
|
if (OB_FAIL(trans_right_table_items.push_back(table_item))) {
|
|
LOG_WARN("failed to add trans right table item", K(ret));
|
|
} else {
|
|
trans_happened = true;
|
|
LOG_TRACE("succeed to transform one semi join to inner join", K(need_check_cost),
|
|
K(*stmt), K(*semi_info));
|
|
}
|
|
}
|
|
}
|
|
if (OB_FAIL(ret) || !trans_happened) {
|
|
} else if (OB_FAIL(add_transform_hint(*stmt, &trans_right_table_items))) {
|
|
LOG_WARN("failed to add transform hint", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief transform_semi_to_inner
|
|
* 基于代价将semi join改写为inner join
|
|
* 规则:
|
|
* 1、如果SEMI JOIN的右表输出唯一时,可以直接改写。
|
|
* 2、如果SEMI JOIN的右表输出不唯一时,需要检查两种情况
|
|
* semi join左表是否可以生成有效的inner path(连接条件下推match index)
|
|
* semi join右表是否可以生成有效的inner path(连接条件下推match index)
|
|
*/
|
|
int ObTransformSemiToInner::transform_semi_to_inner(ObDMLStmt *root_stmt,
|
|
ObDMLStmt *stmt,
|
|
const SemiInfo *pre_semi_info,
|
|
ObDMLStmt *&trans_stmt,
|
|
ObCostBasedRewriteCtx &ctx,
|
|
bool &need_check_cost,
|
|
bool &spj_view_added,
|
|
bool &trans_happened)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
trans_happened = false;
|
|
bool is_valid = false;
|
|
SemiInfo *semi_info = NULL;
|
|
bool ignore = false;
|
|
TransformParam trans_param;
|
|
trans_stmt = NULL;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->stmt_factory_) ||
|
|
OB_ISNULL(ctx_->expr_factory_) || OB_ISNULL(stmt) ||
|
|
OB_ISNULL(pre_semi_info) || OB_ISNULL(root_stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("has null param", K(ret));
|
|
} else if (OB_FALSE_IT(semi_info = stmt->get_semi_info_by_id(pre_semi_info->semi_id_))) {
|
|
} else if (NULL == semi_info) {
|
|
/* do nothing */
|
|
} else if (OB_FAIL(check_basic_validity(root_stmt,
|
|
*stmt,
|
|
*semi_info,
|
|
ctx,
|
|
is_valid,
|
|
need_check_cost,
|
|
trans_param))) {
|
|
LOG_WARN("failed to check basic validity", K(ret));
|
|
} else if (!is_valid) {
|
|
//只有确定可以改写后才会去深拷贝stmt
|
|
} else if (!need_check_cost &&
|
|
OB_FALSE_IT(trans_stmt = stmt)) {
|
|
//如果右表不需要添加distinct算子,则基于规则改写,不会考虑代价,所以不需要深拷贝stmt
|
|
} else if (need_check_cost && OB_FAIL(is_ignore_semi_info(pre_semi_info->semi_id_, ignore))) {
|
|
LOG_WARN("failed to check is ignore semi info", K(ret));
|
|
} else if (ignore) {
|
|
LOG_TRACE("semi info has check cost", K(*semi_info));
|
|
OPT_TRACE("this semi join has checked cost, not need try again");
|
|
} else if (need_check_cost &&
|
|
OB_FAIL(ObTransformUtils::deep_copy_stmt(*ctx_->stmt_factory_,
|
|
*ctx_->expr_factory_,
|
|
stmt,
|
|
trans_stmt))) {
|
|
LOG_WARN("failed to deep copy stmt", K(ret));
|
|
} else if (OB_ISNULL(trans_stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null stmt", K(ret));
|
|
} else if (OB_ISNULL(semi_info = trans_stmt->get_semi_info_by_id(pre_semi_info->semi_id_))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null semi info", K(ret));
|
|
} else if (OB_FAIL(do_transform_by_rewrite_form(trans_stmt, semi_info, ctx, trans_param))) {
|
|
LOG_WARN("failed to do transform semi to inner", K(ret));
|
|
//Just in case different parameters hit same plan, firstly we need add const param constraint
|
|
} else {
|
|
trans_happened = true;
|
|
spj_view_added = trans_param.need_spj_view_;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief gather_params_by_rewrite_form
|
|
* collect some key parameters for each rewrite form separately. Note: This function assumes that the rewrite form has
|
|
* already been decided in the previous phase and is present in trans_param
|
|
*/
|
|
int ObTransformSemiToInner::gather_params_by_rewrite_form(ObDMLStmt* trans_stmt,
|
|
SemiInfo* semi_info,
|
|
TransformParam& trans_param)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<ObRawExpr*, 4> equal_join_conds;
|
|
ObSEArray<ObRawExpr*, 4> cmp_join_conds;
|
|
ObSEArray<ObRawExpr*, 4> filter_conds;
|
|
ObSEArray<ObRawExpr*, 4> invalid_conds;
|
|
ObSEArray<ObRawExpr*, 4> other_conds;
|
|
bool is_multi_join_condition = false;
|
|
bool is_all_left_filter = false;
|
|
|
|
if (OB_ISNULL(trans_stmt) || OB_ISNULL(semi_info)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret), K(trans_stmt), K(semi_info));
|
|
} else if (OB_FAIL(split_join_condition(*trans_stmt,
|
|
*semi_info,
|
|
equal_join_conds,
|
|
cmp_join_conds,
|
|
filter_conds,
|
|
invalid_conds,
|
|
other_conds,
|
|
is_multi_join_condition,
|
|
is_all_left_filter))) {
|
|
LOG_WARN("failed to check semi join condition", K(ret));
|
|
} else if (OB_FAIL(collect_param_exprs_of_correlated_conds(*trans_stmt,
|
|
*semi_info,
|
|
equal_join_conds,
|
|
trans_param.equal_left_exprs_,
|
|
trans_param.equal_right_exprs_))) {
|
|
LOG_WARN("failed to collect param exprs of equal correlated conditions", K(ret));
|
|
} else if (OB_FAIL(collect_filter_conds_related_to_right_table(*trans_stmt,
|
|
*semi_info,
|
|
filter_conds,
|
|
trans_param.filter_conds_on_right_))) {
|
|
LOG_WARN("failed to get filter conditions on right table", K(ret));
|
|
} else if (trans_param.use_inner()) {
|
|
// do nothing
|
|
} else if (trans_param.use_aggr_inner()) {
|
|
if (cmp_join_conds.count() != 1) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("one and only one compare-join-condition is required", K(ret));
|
|
} else if (OB_ISNULL(trans_param.cmp_join_cond_ = cmp_join_conds.at(0))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null expr", K(ret));
|
|
} else if (OB_FAIL(collect_param_expr_related_to_right_table(*trans_stmt,
|
|
*semi_info,
|
|
trans_param.cmp_join_cond_,
|
|
trans_param.cmp_right_expr_))) {
|
|
LOG_WARN("failed to get param expr related to right table", K(ret));
|
|
}
|
|
} else if (trans_param.use_inner_gby()) {
|
|
ObSEArray<ObSEArray<ObRawExpr*, 4>, 4> column_groups;
|
|
if (OB_FAIL(ret)) {
|
|
// do nothing
|
|
} else if (OB_FAIL(collect_unique_property_of_from_items(ctx_, trans_stmt, column_groups))) {
|
|
LOG_WARN("failed to find a unique column group on left tables", K(ret));
|
|
} else if (OB_FAIL(trans_param.unique_column_groups_.assign(column_groups))) {
|
|
LOG_WARN("failed to assign column group", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief do_transform_by_rewrite_form
|
|
* call different rewriting functions according to the rewrite form
|
|
*/
|
|
int ObTransformSemiToInner::do_transform_by_rewrite_form(ObDMLStmt* stmt,
|
|
SemiInfo* semi_info,
|
|
ObCostBasedRewriteCtx &ctx,
|
|
TransformParam& trans_param)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(stmt) || OB_ISNULL(semi_info)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null pointer", K(ret), K(ctx_), K(stmt), K(semi_info));
|
|
} else if (OB_FAIL(gather_params_by_rewrite_form(stmt, semi_info, trans_param))) {
|
|
LOG_WARN("failed to gather trans params", K(ret));
|
|
} else if (trans_param.use_inner()) {
|
|
if (OB_FAIL(do_transform(*stmt,
|
|
semi_info,
|
|
ctx,
|
|
trans_param))) {
|
|
LOG_WARN("failed to do transform (INNER)", K(ret));
|
|
// Just in case different parameters hit same plan, we need add const param constraint
|
|
} else if (!trans_param.need_add_limit_constraint_) {
|
|
// do nothing
|
|
} else if (OB_FAIL(ObTransformUtils::add_const_param_constraints(
|
|
stmt->get_limit_expr(), ctx_))) {
|
|
LOG_WARN("failed to add const param constriants", K(ret));
|
|
}
|
|
} else if (trans_param.use_aggr_inner()) {
|
|
if (OB_FAIL(do_transform_with_aggr(*stmt, semi_info, ctx, trans_param))) {
|
|
LOG_WARN("failed to do transform (AGGR INNER)", K(ret));
|
|
}
|
|
} else if (trans_param.use_inner_gby()) {
|
|
ObSelectStmt* view_stmt = NULL;
|
|
if (trans_param.need_spj_view_) {
|
|
if (OB_FAIL(ObTransformUtils::create_simple_view(ctx_, stmt, view_stmt))) {
|
|
LOG_WARN("failed to create spj view", K(ret));
|
|
}
|
|
} else {
|
|
view_stmt = static_cast<ObSelectStmt*>(stmt);
|
|
}
|
|
// do transform in spj stmt
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_ISNULL(view_stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null pointer", K(ret));
|
|
} else if (OB_FAIL(do_transform(*view_stmt,
|
|
semi_info,
|
|
ctx,
|
|
trans_param))) {
|
|
LOG_WARN("failed to do transform INNER first", K(ret));
|
|
} else {
|
|
TableItem *right_table = view_stmt->get_table_item_by_id(semi_info->right_table_id_);
|
|
ObSelectStmt* right_stmt = NULL;
|
|
if (OB_ISNULL(right_table)) {
|
|
LOG_WARN("unexpected null", K(ret));
|
|
} else if (right_table->is_generated_table()) {
|
|
if (OB_ISNULL(right_stmt = right_table->ref_query_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret));
|
|
} else if (OB_FAIL(ctx.view_table_id_.push_back(semi_info->right_table_id_))) {
|
|
LOG_WARN("fail to push back view table id", K(ret));
|
|
} else {
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < right_stmt->get_table_items().count(); ++i) {
|
|
TableItem *inner_right_table = right_stmt->get_table_item(i);
|
|
if (OB_ISNULL(inner_right_table)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null table item", K(ret));
|
|
} else if (OB_FAIL(ctx.view_table_id_.push_back(inner_right_table->table_id_))) {
|
|
LOG_WARN("fail to push back view table id", K(ret));
|
|
}
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)){
|
|
} else if (OB_FAIL(find_basic_table(right_table->ref_query_, ctx.table_id_))) {
|
|
LOG_WARN("failed to find basic table", K(ret));
|
|
}
|
|
} else if (OB_FAIL(ctx.view_table_id_.push_back(semi_info->right_table_id_))) {
|
|
LOG_WARN("fail to push back view table id", K(ret));
|
|
} else {
|
|
ctx.table_id_ = semi_info->right_table_id_;
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < trans_param.unique_column_groups_.count(); i++) {
|
|
ObIArray<ObRawExpr*> &unique_column_group = trans_param.unique_column_groups_.at(i);
|
|
for (int64_t j = 0; OB_SUCC(ret) && j < unique_column_group.count(); ++j) {
|
|
if (OB_FAIL(view_stmt->add_group_expr(unique_column_group.at(j)))) {
|
|
LOG_WARN("failed to add group by expr", K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief split_join_condition
|
|
* a more detailed splitting of semi join conditions based on some key properties
|
|
* (table dependency, equal-value comparator, less-greater comparator, expression morphology...)
|
|
* @param equal_join_conds equal-value correlated conditions in semi-join
|
|
* @param cmp_join_conds less/greater correlated conditions (>,<,>=,<=) in semi-join
|
|
* @param filter_conds non-correlated filter conditions that act on the left or right table alone
|
|
* @param invalid_conds conditon references a table other than the left or right table in semi-join
|
|
* @param other_conds condition references to the left and right tables are not located on either side of the operator
|
|
* OR uses operators beyond: =, >, <, >=, <=
|
|
*/
|
|
int ObTransformSemiToInner::split_join_condition(ObDMLStmt& stmt,
|
|
SemiInfo& semi_info,
|
|
ObIArray<ObRawExpr*>& equal_join_conds,
|
|
ObIArray<ObRawExpr*>& cmp_join_conds,
|
|
ObIArray<ObRawExpr*>& filter_conds,
|
|
ObIArray<ObRawExpr*>& invalid_conds,
|
|
ObIArray<ObRawExpr*>& other_conds,
|
|
bool& is_multi_join_cond,
|
|
bool& is_all_left_filter)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSqlBitSet<> left_table_set;
|
|
ObSqlBitSet<> right_table_set;
|
|
ObSqlBitSet<> union_table_set;
|
|
ObSqlBitSet<> join_cond_table_ids;
|
|
ObIArray<ObRawExpr*> & semi_conditions = semi_info.semi_conditions_;
|
|
is_multi_join_cond = false;
|
|
is_all_left_filter = true;
|
|
if (OB_FAIL(stmt.get_table_rel_ids(semi_info.left_table_ids_, left_table_set))) {
|
|
LOG_WARN("failed to get table rel ids", K(ret));
|
|
} else 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 (OB_FAIL(union_table_set.add_members(left_table_set))) {
|
|
LOG_WARN("failed to add menber of table bitset", K(ret));
|
|
} else if (OB_FAIL(union_table_set.add_members(right_table_set))) {
|
|
LOG_WARN("failed to add menber of table bitset", K(ret));
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < semi_conditions.count(); ++i) {
|
|
ObRawExpr* expr = semi_conditions.at(i);
|
|
ObRawExpr* left_param;
|
|
ObRawExpr* right_param;
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null expr", K(ret));
|
|
} else if (!union_table_set.is_superset(expr->get_relation_ids())) {
|
|
if (OB_FAIL(invalid_conds.push_back(expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
}
|
|
} else if (left_table_set.is_superset(expr->get_relation_ids())) {
|
|
if (OB_FAIL(filter_conds.push_back(expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
}
|
|
} else if (OB_FALSE_IT(is_all_left_filter = false)) {
|
|
} else if (expr->get_children_count() != 2) {
|
|
// select * from t1 semi join t2 on abs(t1.c1 + t1.c2)
|
|
if (OB_FAIL(other_conds.push_back(expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
}
|
|
} else if (OB_ISNULL(left_param = expr->get_param_expr(0)) || OB_ISNULL(right_param = expr->get_param_expr(1))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null param", K(ret), K(left_param), K(right_param));
|
|
} else if (right_table_set.is_superset(expr->get_relation_ids())) {
|
|
if (OB_FAIL(filter_conds.push_back(expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
}
|
|
} else {
|
|
bool left_param_use_left_table = left_table_set.overlap(left_param->get_relation_ids());
|
|
bool right_param_use_left_table = left_table_set.overlap(right_param->get_relation_ids());
|
|
bool left_param_use_right_table = right_table_set.overlap(left_param->get_relation_ids());
|
|
bool right_param_use_right_table = right_table_set.overlap(right_param->get_relation_ids());
|
|
if ((left_param_use_left_table && left_param_use_right_table) ||
|
|
(right_param_use_left_table && right_param_use_right_table)) {
|
|
if (OB_FAIL(other_conds.push_back(expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
}
|
|
} else if (T_OP_EQ != expr->get_expr_type()) {
|
|
bool less_or_greater = is_less_or_greater_expr(expr->get_expr_type());
|
|
if (less_or_greater && OB_FAIL(cmp_join_conds.push_back(expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
} else if (!less_or_greater && OB_FAIL(other_conds.push_back(expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
}
|
|
} else if (OB_FAIL(equal_join_conds.push_back(expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && !is_multi_join_cond && expr->get_relation_ids().num_members() > 1) {
|
|
if (join_cond_table_ids.is_empty()) {
|
|
if (OB_FAIL(join_cond_table_ids.add_members(expr->get_relation_ids()))) {
|
|
LOG_WARN("failed to add members", K(ret));
|
|
}
|
|
} else if (!expr->get_relation_ids().equal(join_cond_table_ids)){
|
|
is_multi_join_cond = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (semi_conditions.count() != invalid_conds.count() + filter_conds.count() + equal_join_conds.count() +
|
|
cmp_join_conds.count() + other_conds.count()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected condition splitting : count mismatch", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief check_basic_validity
|
|
* semi join转inner join的条件
|
|
* 1. 如果有left_expr = right_column形式的condition,并且
|
|
* right_column在semi右表的输出是唯一的,这种情况下我们直接转inner,
|
|
* 并且不需要加distinct
|
|
* 2. 如果右表输出不唯一,我们需要检查:
|
|
* a. 所有的semi condition是否都是left_expr = right_expr形式
|
|
* 如果不是则不能改写。例如select * from l where exists (select 1 from r where l.a > r.b)
|
|
* 不能改写,或者select * from l where exists (select 1 from r where l.a + r.b = r.c)
|
|
* 我们也不能改写
|
|
* b. 所有的right_expr是否是加distinct类型安全的
|
|
* 如果需要cast(left_expr)-->right_expr,则不能加distinct
|
|
* 如果需要cast(right_expr)-->left_expr,则需要为右表的expr包裹cast后,才能加distinct
|
|
* 如果左右expr的类型一致,直接加distinct
|
|
* c. semi condtion是否overlap左右表的索引
|
|
* 如果上面的条件都满足,则改写为inner
|
|
* 3.出现在嵌套子查询中的含有semi info信息的stmt,如果子查询的输出结果是否存在重复值不影响上层查询的输出结果,
|
|
* 那么可以直接将semi join改为inner join,不需要添加distinct
|
|
* eg: select * from T1 where exists (select 1 from T2 where T2.c2 in (select T3.c2 from T3 where T1.c1 = T3.c1));
|
|
*/
|
|
int ObTransformSemiToInner::check_basic_validity(ObDMLStmt *root_stmt,
|
|
ObDMLStmt &stmt,
|
|
SemiInfo &semi_info,
|
|
ObCostBasedRewriteCtx &ctx,
|
|
bool &is_valid,
|
|
bool &need_check_cost,
|
|
TransformParam& trans_param)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool is_unique = false;
|
|
bool is_all_left_filter = false;
|
|
bool is_one_row = false;
|
|
bool is_non_sens_dup_vals = false;
|
|
bool is_multi_join_cond = false;
|
|
TableItem *right_table = NULL;
|
|
bool can_add_deduplication = false;
|
|
ObIArray<uint64_t> & left_table_ids = semi_info.left_table_ids_;
|
|
ObSEArray<TableItem*, 4> left_tables;
|
|
ObSEArray<ObRawExpr*, 4> left_exprs;
|
|
ObSEArray<ObRawExpr*, 4> right_exprs;
|
|
is_valid = false;
|
|
need_check_cost = false;
|
|
bool need_add_limit_constraint = false;
|
|
bool condition_match_index = ctx.hint_force_;
|
|
int64_t cmp_join_conds_count = 0;
|
|
int64_t invalid_conds_count = 0;
|
|
int64_t other_conds_count = 0;
|
|
|
|
if (OB_ISNULL(root_stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret), K(root_stmt));
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < left_table_ids.count(); i++) {
|
|
TableItem* temp_table = NULL;
|
|
if (OB_ISNULL(temp_table = stmt.get_table_item_by_id(left_table_ids.at(i)))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("failed to get table items", K(ret), K(semi_info));
|
|
} else if (OB_FAIL(left_tables.push_back(temp_table))) {
|
|
LOG_WARN("failed to push back table item", K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
// do nothing
|
|
} else if (OB_ISNULL(right_table = stmt.get_table_item_by_id(semi_info.right_table_id_))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("failed to get table items", K(ret), K(semi_info));
|
|
} else if (OB_FAIL(check_semi_join_condition(stmt,
|
|
semi_info,
|
|
left_exprs,
|
|
right_exprs,
|
|
is_all_left_filter,
|
|
is_multi_join_cond,
|
|
cmp_join_conds_count,
|
|
invalid_conds_count,
|
|
other_conds_count))) {
|
|
LOG_WARN("failed to check semi join condition", K(ret));
|
|
} else if (OB_FAIL(check_right_table_output_one_row(*right_table, is_one_row))) {
|
|
LOG_WARN("failed to check right tables output one row", K(ret));
|
|
} else if (is_one_row) {
|
|
is_valid = true;
|
|
trans_param.set_transform_flag(TO_INNER);
|
|
OPT_TRACE("semi join right table output most one row, no need distinct");
|
|
} else if (OB_FAIL(ObTransformUtils::check_stmt_is_non_sens_dul_vals(ctx_, root_stmt, &stmt,
|
|
is_non_sens_dup_vals,
|
|
need_add_limit_constraint))) {
|
|
LOG_WARN("failed to check stmt is non sens dul vals", K(ret));
|
|
} else if (is_non_sens_dup_vals) {
|
|
is_valid = true;
|
|
trans_param.set_transform_flag(TO_INNER);
|
|
trans_param.need_add_limit_constraint_ = need_add_limit_constraint;
|
|
OPT_TRACE("stmt isn't sensitive to result of subquery has duplicated values");
|
|
LOG_TRACE("stmt isn't sensitive to result of subquery has duplicated values");
|
|
} else if (OB_FAIL(check_right_exprs_unique(stmt, right_table, right_exprs, is_unique))) {
|
|
LOG_WARN("failed to check exprs unique on table items", K(ret));
|
|
} else if (is_unique) {
|
|
is_valid = true;
|
|
trans_param.set_transform_flag(TO_INNER);
|
|
LOG_TRACE("semi right table output is unique");
|
|
OPT_TRACE("semi right table output is unique");
|
|
} else if (is_all_left_filter && (NULL == right_table->ref_query_ ||
|
|
NULL == right_table->ref_query_->get_limit_percent_expr())) {
|
|
is_valid = true;
|
|
trans_param.right_table_need_add_limit_ = true;
|
|
trans_param.set_transform_flag(TO_INNER);
|
|
OPT_TRACE("semi conditions are all left filters, will not add distinct, will add limit 1");
|
|
} else if (invalid_conds_count > 0) {
|
|
// do nothing
|
|
} else if (cmp_join_conds_count < 2 && OB_FAIL(check_can_add_deduplication(left_exprs, right_exprs, can_add_deduplication))) {
|
|
LOG_WARN("failed to check can add deduplication on right", K(ret));
|
|
} else if (!is_multi_join_cond && !ctx.hint_force_ &&
|
|
OB_FAIL(check_join_condition_match_index(root_stmt,
|
|
stmt,
|
|
semi_info,
|
|
semi_info.semi_conditions_,
|
|
condition_match_index))) {
|
|
LOG_WARN("failed to check join condition match index", K(ret));
|
|
} else if (!is_multi_join_cond && !condition_match_index) {
|
|
// do nothing
|
|
OPT_TRACE("semi condition not match index and is not multi join , will not transform");
|
|
} else if (cmp_join_conds_count == 0 && other_conds_count == 0 && can_add_deduplication) {
|
|
// TO_INNER (distinct) : for cases when only standard equal join condition(s) exist
|
|
trans_param.set_transform_flag(TO_INNER);
|
|
trans_param.need_add_distinct_ = true;
|
|
ctx.is_multi_join_cond_ = is_multi_join_cond;
|
|
need_check_cost = true;
|
|
is_valid = true;
|
|
} else if (cmp_join_conds_count == 1 && other_conds_count == 0 && can_add_deduplication) {
|
|
// TO_AGGR_INNER : for cases when there is one and only one compare-join-condition
|
|
is_valid = true;
|
|
trans_param.set_transform_flag(TO_AGGR_INNER);
|
|
trans_param.need_add_gby_ = (left_exprs.count() != 0);
|
|
ctx.is_multi_join_cond_ = is_multi_join_cond;
|
|
need_check_cost = true;
|
|
} else {
|
|
// TO_INNER_GBY : for following cases:
|
|
// 1. there are more than one compare-join-conditions
|
|
// 2. non-standard correlated condition(s) exist
|
|
// 3. failure cases for adding distinct(TO_INNER) or group by(TO_AGGR_INNER)
|
|
bool can_find_unique_column_group = false;
|
|
bool is_spj_stmt = (stmt.is_select_stmt() && static_cast<ObSelectStmt*>(&stmt)->is_spj());
|
|
bool can_add_spj_view = !(stmt.is_set_stmt() || stmt.is_hierarchical_query() || !stmt.is_sel_del_upd());
|
|
if (OB_FAIL(check_from_item_unique_property(ctx_,
|
|
&stmt,
|
|
can_find_unique_column_group))) {
|
|
LOG_WARN("failed to find column group with 'unique' property for left tables", K(ret));
|
|
} else if (!can_find_unique_column_group) {
|
|
// do nothing
|
|
} else if (!is_spj_stmt && !can_add_spj_view) {
|
|
// do nothing
|
|
} else {
|
|
is_valid = true;
|
|
trans_param.need_spj_view_ = !is_spj_stmt;
|
|
trans_param.set_transform_flag(TO_INNER_GBY);
|
|
need_check_cost = true;
|
|
ctx.is_multi_join_cond_ = is_multi_join_cond;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief collect_unique_property_of_from_items
|
|
* try to construct a unique column group based on unique column of each from item
|
|
*/
|
|
int ObTransformSemiToInner::collect_unique_property_of_from_items(ObTransformerCtx* ctx,
|
|
ObDMLStmt* stmt,
|
|
ObIArray<ObSEArray<ObRawExpr*, 4>>& unique_column_group)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(ctx) || OB_ISNULL(stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null", K(ret), K(ctx), K(stmt));
|
|
} else {
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < stmt->get_from_item_size(); i++) {
|
|
TableItem* table = stmt->get_table_item(stmt->get_from_item(i));
|
|
ObSEArray<ObRawExpr*, 4> pkeys;
|
|
if (OB_ISNULL(table)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null table", K(ret), K(table));
|
|
} else if (OB_FAIL(ObTransformUtils::generate_unique_key(ctx, stmt, table, pkeys))) {
|
|
LOG_WARN("failed to generate unique key", K(ret));
|
|
} else if (pkeys.empty()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("left table should have unique key", K(ret));
|
|
} else if (OB_FAIL(unique_column_group.push_back(pkeys))) {
|
|
LOG_WARN("failed to push back element", K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief check_from_item_unique_property
|
|
* check if each left table has unique key
|
|
*/
|
|
int ObTransformSemiToInner::check_from_item_unique_property(ObTransformerCtx* ctx,
|
|
ObDMLStmt* stmt,
|
|
bool& is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(ctx) || OB_ISNULL(stmt) || OB_ISNULL(ctx->schema_checker_) || OB_ISNULL(ctx->session_info_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null", K(ret), K(ctx), K(stmt));
|
|
} else {
|
|
bool temp = true;
|
|
const ObTableSchema *table_schema = NULL;
|
|
for (int64_t i = 0; OB_SUCC(ret) && temp && i < stmt->get_from_item_size(); i++) {
|
|
TableItem* table = stmt->get_table_item(stmt->get_from_item(i));
|
|
ObSEArray<ObRawExpr*, 4> pkeys;
|
|
if (OB_ISNULL(table)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null table", K(ret), K(table));
|
|
} else if (!table->is_basic_table()) {
|
|
temp = false;
|
|
} else if (OB_FAIL(ctx->schema_checker_->get_table_schema(ctx->session_info_->get_effective_tenant_id(), table->ref_id_, table_schema))) {
|
|
LOG_WARN("failed to get table schema", K(ret));
|
|
} else if (OB_ISNULL(table_schema)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("table schema is null", K(ret), K(table_schema));
|
|
//new heap table not add partition key in rowkey and the tablet id is unique in partition,
|
|
//we need add partition key to ensure the output unique.
|
|
} else if (table_schema->is_heap_table() &&
|
|
OB_FAIL(ObTransformUtils::add_part_column_exprs_for_heap_table(stmt, table_schema,
|
|
table->table_id_, pkeys))) {
|
|
LOG_WARN("failed to add part column exprs for heap table", K(ret));
|
|
} else if (!pkeys.empty()) {
|
|
// valid, do nothing
|
|
} else {
|
|
const ObRowkeyInfo &rowkey_info = table_schema->get_rowkey_info();
|
|
temp = rowkey_info.get_size() > 0;
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
is_valid = temp;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// check right_exprs is unique on right_table.
|
|
// if right_table is generate table, check right_table ref_query unique.
|
|
int ObTransformSemiToInner::check_right_exprs_unique(ObDMLStmt &stmt,
|
|
TableItem *right_table,
|
|
ObIArray<ObRawExpr*> &right_exprs,
|
|
bool &is_unique)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
is_unique = false;
|
|
ObSelectStmt *ref_query = NULL;
|
|
if (OB_ISNULL(right_table) || OB_ISNULL(ctx_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null ctx", K(ret), K(right_table), K(ctx_));
|
|
} else if (!right_table->is_generated_table() && !right_table->is_temp_table()) {
|
|
// baisc table
|
|
ObSEArray<TableItem*, 1> right_tables;
|
|
ObSEArray<ObRawExpr*, 1> dummy_conds;
|
|
if (OB_FAIL(right_tables.push_back(right_table))) {
|
|
LOG_WARN("failed to push back table", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::check_exprs_unique_on_table_items(&stmt,
|
|
ctx_->session_info_, ctx_->schema_checker_,
|
|
right_tables, right_exprs, dummy_conds,
|
|
false, is_unique))) {
|
|
LOG_WARN("failed to check exprs unique on table items", K(ret));
|
|
}
|
|
} else if (OB_ISNULL(ref_query = right_table->ref_query_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null ref query", K(ret));
|
|
} else {
|
|
ObSEArray<ObRawExpr*, 4> right_cols;
|
|
ObSEArray<ObRawExpr*, 4> right_select_exprs;
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < right_exprs.count(); ++i) {
|
|
if (OB_ISNULL(right_exprs.at(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null expr", K(ret));
|
|
} else if (right_exprs.at(i)->is_column_ref_expr()) {
|
|
ret = right_cols.push_back(right_exprs.at(i));
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
} else if (OB_FAIL(ObTransformUtils::convert_column_expr_to_select_expr(right_cols, *ref_query,
|
|
right_select_exprs))) {
|
|
LOG_WARN("failed to convert column expr to select expr", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::check_stmt_unique(ref_query, ctx_->session_info_,
|
|
ctx_->schema_checker_,
|
|
right_select_exprs, false,
|
|
is_unique))) {
|
|
LOG_WARN("failed to check ref query unique", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief check_semi_join_condition
|
|
* 检查semi condition,输出以下结果:
|
|
* left_exprs: 所有EQ表达式的属于左表的expr
|
|
* right_exprs: 所有EQ表达式的属于右表的expr
|
|
* right_columns: 如果有lef_expr = right_column,保存column expr
|
|
* is_all_euqal_cond:是否所有的表达式都是left_expr = right_expr形式
|
|
*/
|
|
int ObTransformSemiToInner::check_semi_join_condition(ObDMLStmt &stmt,
|
|
SemiInfo &semi_info,
|
|
ObIArray<ObRawExpr*> &equal_left_exprs,
|
|
ObIArray<ObRawExpr*> &equal_right_exprs,
|
|
bool &is_all_left_filter,
|
|
bool &is_multi_join_cond,
|
|
int64_t& cmp_join_conds_count,
|
|
int64_t& invalid_conds_count,
|
|
int64_t& other_conds_count)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<ObRawExpr*,4> equal_join_conds;
|
|
ObSEArray<ObRawExpr*,4> cmp_join_conds;
|
|
ObSEArray<ObRawExpr*,4> filter_conds;
|
|
ObSEArray<ObRawExpr*,4> filter_conds_on_right;
|
|
ObSEArray<ObRawExpr*,4> other_conds;
|
|
ObSEArray<ObRawExpr*,4> invalid_conds;
|
|
is_all_left_filter = true;
|
|
is_multi_join_cond = false;
|
|
int64_t total_count = semi_info.semi_conditions_.count();
|
|
if (OB_FAIL(split_join_condition(stmt,
|
|
semi_info,
|
|
equal_join_conds,
|
|
cmp_join_conds,
|
|
filter_conds,
|
|
invalid_conds,
|
|
other_conds,
|
|
is_multi_join_cond,
|
|
is_all_left_filter))) {
|
|
LOG_WARN("failed to split join conditions", K(ret));
|
|
} else if (OB_FAIL(collect_param_exprs_of_correlated_conds(stmt,
|
|
semi_info,
|
|
equal_join_conds,
|
|
equal_left_exprs,
|
|
equal_right_exprs))) {
|
|
LOG_WARN("failed to collect param exprs related to left/right table respectively", K(ret));
|
|
} else if (OB_FAIL(collect_filter_conds_related_to_right_table(stmt,
|
|
semi_info,
|
|
filter_conds,
|
|
filter_conds_on_right))) {
|
|
LOG_WARN("failed to get filter conditions on right table", K(ret));
|
|
} else if (OB_FAIL(collect_param_exprs_of_correlated_conds(stmt,
|
|
semi_info,
|
|
filter_conds_on_right,
|
|
equal_left_exprs,
|
|
equal_right_exprs,
|
|
true))) {
|
|
// collect equal info in right filters to check if right table is unique
|
|
LOG_WARN("failed to collect param exprs of equal correlated conditions", K(ret));
|
|
} else {
|
|
cmp_join_conds_count = cmp_join_conds.count();
|
|
invalid_conds_count = invalid_conds.count();
|
|
other_conds_count = other_conds.count();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief collect_param_exprs_of_correlated_conds
|
|
* collect the parameters related to the left/right table respectively. Note: this function assumes that param expr
|
|
* is either related to right table only or to left table only
|
|
* if collect_equal_info is true, collect equal info in right filters
|
|
*/
|
|
int ObTransformSemiToInner::collect_param_exprs_of_correlated_conds(ObDMLStmt& stmt,
|
|
SemiInfo& semi_info,
|
|
ObIArray<ObRawExpr*>& correlated_conds,
|
|
ObIArray<ObRawExpr*>& left_exprs,
|
|
ObIArray<ObRawExpr*>& right_exprs,
|
|
bool collect_equal_info)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSqlBitSet<> left_table_set;
|
|
ObSqlBitSet<> right_table_set;
|
|
if (OB_FAIL(stmt.get_table_rel_ids(semi_info.left_table_ids_, left_table_set))) {
|
|
LOG_WARN("failed to get table rel ids", K(ret));
|
|
} else 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));
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < correlated_conds.count(); i++) {
|
|
ObRawExpr* expr = correlated_conds.at(i);
|
|
ObRawExpr* left = NULL;
|
|
ObRawExpr* right = NULL;
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null expr", K(ret));
|
|
} else if (collect_equal_info && T_OP_EQ != expr->get_expr_type()) {
|
|
//do nothing
|
|
} else if (OB_ISNULL(left = expr->get_param_expr(0)) || OB_ISNULL(right = expr->get_param_expr(1))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null param", K(ret), K(*expr));
|
|
} else {
|
|
if (left_table_set.is_superset(left->get_relation_ids()) &&
|
|
right_table_set.is_superset(right->get_relation_ids())) {
|
|
if (OB_FAIL(left_exprs.push_back(left))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
} else if (OB_FAIL(right_exprs.push_back(right))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
}
|
|
} else if (right_table_set.is_superset(left->get_relation_ids()) &&
|
|
left_table_set.is_superset(right->get_relation_ids())) {
|
|
if (OB_FAIL(right_exprs.push_back(left))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
} else if (OB_FAIL(left_exprs.push_back(right))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
}
|
|
} else if (collect_equal_info && right_table_set.is_superset(expr->get_relation_ids())) {
|
|
//do nothing, right filter condition maybe like right = right
|
|
} else {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected equal correlated condition", K(ret), K(*expr));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::collect_param_expr_related_to_right_table(ObDMLStmt& stmt,
|
|
SemiInfo& semi_info,
|
|
ObRawExpr* correlated_condition,
|
|
ObRawExpr*& param_expr_related_to_right_table)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<ObRawExpr*, 4> correlated_conditions;
|
|
ObSEArray<ObRawExpr*, 4> right_exprs;
|
|
ObSEArray<ObRawExpr*, 4> left_exprs;
|
|
if (OB_ISNULL(correlated_condition)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret));
|
|
} else if (OB_FAIL(correlated_conditions.push_back(correlated_condition))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
} else if (OB_FAIL(collect_param_exprs_of_correlated_conds(stmt,
|
|
semi_info,
|
|
correlated_conditions,
|
|
left_exprs,
|
|
right_exprs))) {
|
|
LOG_WARN("failed to collect param exprs", K(ret));
|
|
} else if (left_exprs.count() != 1 || right_exprs.count() != 1) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected param expr count", K(ret), K(left_exprs.count()), K(right_exprs.count()), K(*correlated_condition));
|
|
} else if (OB_ISNULL(param_expr_related_to_right_table = right_exprs.at(0))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null expr", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief classify non-correlated filter conditions according to whether they are related to the right table or not
|
|
*/
|
|
int ObTransformSemiToInner::collect_filter_conds_related_to_right_table(ObDMLStmt& stmt,
|
|
SemiInfo& semi_info,
|
|
ObIArray<ObRawExpr*>& filter_conds,
|
|
ObIArray<ObRawExpr*>& filter_conds_on_right)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSqlBitSet<> left_table_set;
|
|
ObSqlBitSet<> right_table_set;
|
|
if (OB_FAIL(stmt.get_table_rel_ids(semi_info.left_table_ids_, left_table_set))) {
|
|
LOG_WARN("failed to get table rel ids", K(ret));
|
|
} else 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));
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < filter_conds.count(); i++) {
|
|
ObRawExpr* expr = filter_conds.at(i);
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null expr", K(ret));
|
|
} else if (left_table_set.is_superset(expr->get_relation_ids())) {
|
|
// do nothing
|
|
} else if (right_table_set.is_superset(expr->get_relation_ids())) {
|
|
if (OB_FAIL(filter_conds_on_right.push_back(expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
}
|
|
} else {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool ObTransformSemiToInner::is_less_or_greater_expr(ObItemType expr_type)
|
|
{
|
|
return (expr_type >= T_OP_LE && expr_type <= T_OP_GT);
|
|
}
|
|
|
|
/**
|
|
* 如果右表有limit 1或者unique_key = const表达式
|
|
* 说明右边输出至多一行
|
|
*/
|
|
int ObTransformSemiToInner::check_right_table_output_one_row(TableItem &right_table,
|
|
bool &is_one_row)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
is_one_row = false;
|
|
if (right_table.is_generated_table()) {
|
|
ObPCConstParamInfo const_param_info;
|
|
ObPhysicalPlanCtx *plan_ctx = NULL;
|
|
if (OB_ISNULL(right_table.ref_query_) || OB_ISNULL(ctx_) || OB_ISNULL(ctx_->exec_ctx_) ||
|
|
OB_ISNULL(plan_ctx = ctx_->exec_ctx_->get_physical_plan_ctx())){
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null param", K(right_table), K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::check_limit_value(*right_table.ref_query_,
|
|
ctx_->exec_ctx_,
|
|
ctx_->allocator_,
|
|
1,
|
|
is_one_row,
|
|
const_param_info))) {
|
|
LOG_WARN("failed to check limit value", K(ret));
|
|
} else if (!const_param_info.const_idx_.empty() &&
|
|
OB_FAIL(ctx_->plan_const_param_constraints_.push_back(const_param_info))) {
|
|
LOG_WARN("failed to push back const param info", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::check_can_add_deduplication(const ObIArray<ObRawExpr*> &left_exprs,
|
|
const ObIArray<ObRawExpr*> &right_exprs,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool need_add_cast = false;
|
|
is_valid = true;
|
|
if (left_exprs.count() != right_exprs.count()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect expr count", K(left_exprs), K(right_exprs), K(ret));
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < left_exprs.count(); ++i) {
|
|
ObRawExpr *left = left_exprs.at(i);
|
|
ObRawExpr *right = right_exprs.at(i);
|
|
if (OB_ISNULL(left) || OB_ISNULL(right)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null expr", K(ret), K(left), K(right));
|
|
} else if (OB_FAIL(check_need_add_cast(left,
|
|
right,
|
|
need_add_cast,
|
|
is_valid))) {
|
|
LOG_WARN("failed to check need add cast", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::check_need_add_cast(const ObRawExpr *left_arg,
|
|
const ObRawExpr *right_arg,
|
|
bool &need_add_cast,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
need_add_cast = false;
|
|
is_valid = false;
|
|
bool is_equal = false;
|
|
if (OB_ISNULL(left_arg) || OB_ISNULL(right_arg)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("left arg and right arg should not be NULL", K(ret), K(left_arg), K(right_arg));
|
|
} else if (OB_FAIL(ObRelationalExprOperator::is_equivalent(left_arg->get_result_type(),
|
|
left_arg->get_result_type(),
|
|
right_arg->get_result_type(),
|
|
is_valid))) {
|
|
LOG_WARN("failed to check expr is equivalent", K(ret));
|
|
} else if (!is_valid) {
|
|
LOG_TRACE("can not use left expr type as the (left, right) compare type", K(is_valid));
|
|
} else if (OB_FAIL(ObRelationalExprOperator::is_equivalent(left_arg->get_result_type(),
|
|
right_arg->get_result_type(),
|
|
right_arg->get_result_type(),
|
|
is_equal))) {
|
|
LOG_WARN("failed to check expr is equivalent", K(ret));
|
|
} else if (!is_equal) {
|
|
need_add_cast = true;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::check_join_condition_match_index(ObDMLStmt *root_stmt,
|
|
ObDMLStmt &stmt,
|
|
SemiInfo &semi_info,
|
|
const ObIArray<ObRawExpr*> &semi_conditions,
|
|
bool &is_match_index)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
is_match_index = false;
|
|
if (OB_ISNULL(ctx_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("ctx is null", K(ret), K(ctx_));
|
|
}
|
|
// check semi condition is match left table
|
|
for (int64_t i = 0; OB_SUCC(ret) && !is_match_index && i < semi_conditions.count(); ++i) {
|
|
ObSEArray<ObRawExpr*, 8> column_exprs;
|
|
if (OB_FAIL(ObRawExprUtils::extract_column_exprs(semi_conditions.at(i), column_exprs))) {
|
|
LOG_WARN("failed to extract column exprs", K(ret));
|
|
}
|
|
for (int64_t j = 0; OB_SUCC(ret) && !is_match_index && j < column_exprs.count(); ++j) {
|
|
ObRawExpr *e = column_exprs.at(j);
|
|
ObColumnRefRawExpr *col_expr = NULL;
|
|
if (OB_ISNULL(e) || OB_UNLIKELY(!e->is_column_ref_expr())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null expr", K(ret));
|
|
} else if (OB_FALSE_IT(col_expr = static_cast<ObColumnRefRawExpr*>(e))) {
|
|
} else if (!ObOptimizerUtil::find_item(semi_info.left_table_ids_, col_expr->get_table_id())) {
|
|
// do nothing
|
|
} else if (OB_FAIL(ObTransformUtils::check_column_match_index(root_stmt,
|
|
&stmt,
|
|
ctx_->sql_schema_guard_,
|
|
col_expr,
|
|
is_match_index))) {
|
|
LOG_WARN("failed to check column expr is match index", K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::do_transform(ObDMLStmt &stmt,
|
|
SemiInfo *semi_info,
|
|
ObCostBasedRewriteCtx &ctx,
|
|
TransformParam &trans_param)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
TableItem *right_table = NULL;
|
|
TableItem *view_table = NULL;
|
|
ObSelectStmt *ref_query = NULL;
|
|
bool need_add_distinct = trans_param.need_add_distinct_;
|
|
bool right_table_need_add_limit = trans_param.right_table_need_add_limit_;
|
|
ObSEArray<ObRawExpr *, 2> new_condition_exprs;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(semi_info)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null ctx", K(ret));
|
|
} else if (OB_FAIL(ObOptimizerUtil::remove_item(stmt.get_semi_infos(), semi_info))) {
|
|
LOG_WARN("failed to remove semi info", K(ret));
|
|
} else if (!need_add_distinct) {
|
|
if (OB_FAIL(append(stmt.get_condition_exprs(), semi_info->semi_conditions_))) {
|
|
LOG_WARN("failed to append semi conditions", K(ret));
|
|
} else if (OB_FAIL(stmt.add_from_item(semi_info->right_table_id_, false))) {
|
|
LOG_WARN("failed to add from items", K(ret));
|
|
} else if (!right_table_need_add_limit) {
|
|
/* do nothing */
|
|
} else if (OB_FAIL(ObTransformUtils::add_limit_to_semi_right_table(&stmt, ctx_, semi_info))) {
|
|
LOG_WARN("failed to add limit to semi right table", K(ret), K(stmt));
|
|
}
|
|
} else if (OB_FAIL(new_condition_exprs.assign(semi_info->semi_conditions_))) {
|
|
LOG_WARN("failed to assign semi join conditions", K(ret));
|
|
} else if (OB_FAIL(ObOptimizerUtil::remove_item(new_condition_exprs,
|
|
trans_param.filter_conds_on_right_))) {
|
|
LOG_WARN("failed to remove non-correlated filter conditions on right table", K(ret));
|
|
} else if (OB_FAIL(append(stmt.get_condition_exprs(), new_condition_exprs))) {
|
|
LOG_WARN("failed to append semi conditions", K(ret));
|
|
} else if (OB_ISNULL(right_table = stmt.get_table_item_by_id(semi_info->right_table_id_))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("right table item is null", K(ret), K(right_table));
|
|
} else if (OB_FAIL(ObTransformUtils::replace_with_empty_view(ctx_,
|
|
&stmt,
|
|
view_table,
|
|
right_table))) {
|
|
LOG_WARN("failed to create empty view table", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::create_inline_view(ctx_,
|
|
&stmt,
|
|
view_table,
|
|
right_table,
|
|
&trans_param.filter_conds_on_right_,
|
|
NULL,
|
|
&trans_param.equal_right_exprs_))) {
|
|
LOG_WARN("failed to create inline view", K(ret));
|
|
} else if (OB_ISNULL(view_table)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null table item", K(ret));
|
|
} else if (!view_table->is_generated_table()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("expect generated table item", K(*view_table), K(ret));
|
|
} else if (OB_ISNULL(ref_query = view_table->ref_query_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null ref query", K(ret));
|
|
} else if (OB_FAIL(add_distinct(*ref_query, trans_param.equal_left_exprs_, trans_param.equal_right_exprs_))) {
|
|
LOG_WARN("failed to add distinct exprs", K(ret));
|
|
} else if (OB_FAIL(stmt.add_from_item(view_table->table_id_, false))) {
|
|
LOG_WARN("failed to add from items", K(ret));
|
|
} else if (OB_FAIL(find_basic_table(ref_query, ctx.table_id_))) {
|
|
LOG_WARN("failed to find basic table", K(ret));
|
|
} else if (OB_FAIL(ctx.view_table_id_.push_back(view_table->table_id_))) {
|
|
LOG_WARN("fail to push back view table id");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::find_basic_table(ObSelectStmt* stmt, uint64_t &table_id)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool find = false;
|
|
table_id = OB_INVALID_ID;
|
|
if (OB_ISNULL(stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null stmt", K(ret));
|
|
} else if (stmt->is_set_stmt()) {
|
|
for (int64_t i = 0; OB_SUCC(ret) && OB_INVALID_ID == table_id && i < stmt->get_set_query().count(); ++i) {
|
|
if (OB_FAIL(SMART_CALL(find_basic_table(stmt->get_set_query(i),table_id)))) {
|
|
LOG_WARN("fail to find basic table in set stmt", K(ret));
|
|
}
|
|
}
|
|
} else {
|
|
for (int64_t i = 0; OB_SUCC(ret) && !find && i < stmt->get_table_items().count(); ++i) {
|
|
TableItem *table = stmt->get_table_item(i);
|
|
if (OB_ISNULL(table)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null table item", K(ret));
|
|
} else if (!table->is_generated_table()) {
|
|
table_id = table->table_id_;
|
|
find = true;
|
|
} else if (OB_FAIL(SMART_CALL(find_basic_table(table->ref_query_, table_id)))) {
|
|
LOG_WARN("failed to find basic table item", K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::do_transform_with_aggr(ObDMLStmt& stmt,
|
|
SemiInfo* semi_info,
|
|
ObCostBasedRewriteCtx &ctx,
|
|
TransformParam& trans_param)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
TableItem *right_table = NULL;
|
|
TableItem* view_table = NULL;
|
|
ObSelectStmt* ref_query = NULL;
|
|
ObSEArray<ObRawExpr*, 4> new_condition_exprs;
|
|
ObSEArray<ObRawExpr*, 4> view_select_exprs;
|
|
ObSEArray<ObRawExpr*, 4> view_filter_conds;
|
|
ObRawExpr* cmp_join_cond = trans_param.cmp_join_cond_;
|
|
ObRawExpr* cmp_right_expr = trans_param.cmp_right_expr_;
|
|
ObAggFunRawExpr* view_aggr_expr = NULL;
|
|
bool need_add_group_by = trans_param.need_add_gby_;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(semi_info) || OB_ISNULL(cmp_join_cond) || OB_ISNULL(cmp_right_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null ctx", K(ret));
|
|
} else if (OB_FAIL(create_min_max_aggr_expr(&stmt, ctx_->expr_factory_, cmp_join_cond, cmp_right_expr, view_aggr_expr))) {
|
|
LOG_WARN("failed to create min/max expr", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::replace_expr(cmp_right_expr, view_aggr_expr, cmp_join_cond))) {
|
|
LOG_WARN("failed to replace param expr", K(ret));
|
|
} else if (OB_FAIL(new_condition_exprs.assign(semi_info->semi_conditions_))) {
|
|
LOG_WARN("failed to assign semi join conditions", K(ret));
|
|
} else if (OB_FAIL(view_filter_conds.assign(trans_param.filter_conds_on_right_))) {
|
|
LOG_WARN("failed to assign view filter conditions", K(ret));
|
|
} else if (OB_FAIL(ObOptimizerUtil::remove_item(new_condition_exprs, view_filter_conds))) {
|
|
LOG_WARN("failed to remove non-correlated filter conditions on right table", K(ret));
|
|
} else if (OB_FAIL(view_select_exprs.assign(trans_param.equal_right_exprs_))) {
|
|
LOG_WARN("failed to assign view select exprs", K(ret));
|
|
} else if (OB_FAIL(view_select_exprs.push_back(view_aggr_expr))) {
|
|
LOG_WARN("failed to push back view select exprs", K(ret));
|
|
} else if (OB_FAIL(ObOptimizerUtil::remove_item(stmt.get_semi_infos(), semi_info))) {
|
|
LOG_WARN("failed to remove semi info", K(ret));
|
|
} else if (OB_FAIL(append(stmt.get_condition_exprs(), new_condition_exprs))) {
|
|
LOG_WARN("failed to append semi conditions", K(ret));
|
|
} else if (OB_ISNULL(right_table = stmt.get_table_item_by_id(semi_info->right_table_id_))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("right table item is null", K(ret), K(right_table));
|
|
} else if (OB_FAIL(ObTransformUtils::replace_with_empty_view(ctx_,
|
|
&stmt,
|
|
view_table,
|
|
right_table))) {
|
|
LOG_WARN("failed to create empty view table", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::create_inline_view(ctx_,
|
|
&stmt,
|
|
view_table,
|
|
right_table,
|
|
&view_filter_conds,
|
|
NULL,
|
|
&view_select_exprs))) {
|
|
LOG_WARN("failed to create inline view", K(ret));
|
|
} else if (OB_ISNULL(view_table)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null table item", K(ret));
|
|
} else if (!view_table->is_generated_table()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("expect generated table item", K(*view_table), K(ret));
|
|
} else if (OB_ISNULL(ref_query = view_table->ref_query_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null ref query", K(ret));
|
|
} else if (OB_FAIL(stmt.add_from_item(view_table->table_id_, false))) {
|
|
LOG_WARN("failed to add from items", K(ret));
|
|
} else if (need_add_group_by) {
|
|
if (OB_FAIL(add_group_by_with_cast(*ref_query, trans_param.equal_left_exprs_, trans_param.equal_right_exprs_))) {
|
|
LOG_WARN("failed to add group by expr in view", K(ret));
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
} else if (OB_FAIL(find_basic_table(ref_query, ctx.table_id_))) {
|
|
LOG_WARN("failed to find basic table", K(ret));
|
|
} else if (OB_FAIL(ctx.view_table_id_.push_back(view_table->table_id_))) {
|
|
LOG_WARN("fail to push back view table id");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::create_min_max_aggr_expr(ObDMLStmt* stmt,
|
|
ObRawExprFactory* expr_factory,
|
|
ObRawExpr* condition_expr,
|
|
ObRawExpr* target_param_expr,
|
|
ObAggFunRawExpr*& aggr_expr)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool target_at_right = false;
|
|
bool target_at_left = false;
|
|
bool is_greater_cmp = false;
|
|
bool is_less_cmp = false;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(expr_factory) || OB_ISNULL(condition_expr) || OB_ISNULL(target_param_expr) || OB_ISNULL(stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null pointer", K(ret), K(ctx_), K(expr_factory), K(condition_expr), K(target_param_expr), K(stmt));
|
|
} else {
|
|
target_at_left = condition_expr->get_param_expr(0) == target_param_expr;
|
|
target_at_right = condition_expr->get_param_expr(1) == target_param_expr;
|
|
is_greater_cmp = condition_expr->get_expr_type() == T_OP_GT || condition_expr->get_expr_type() == T_OP_GE;
|
|
is_less_cmp = condition_expr->get_expr_type() == T_OP_LT || condition_expr->get_expr_type() == T_OP_LE;
|
|
if (target_at_left == target_at_right || is_greater_cmp == is_less_cmp) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected conflict conditions", K(ret));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
ObItemType aggr_type = T_INVALID;
|
|
if ((target_at_right && is_greater_cmp) || (target_at_left && is_less_cmp)) {
|
|
aggr_type = T_FUN_MIN;
|
|
} else {
|
|
aggr_type = T_FUN_MAX;
|
|
}
|
|
if (OB_FAIL(expr_factory->create_raw_expr(aggr_type, aggr_expr))) {
|
|
LOG_WARN("fail to create raw expr", K(ret), K(aggr_expr));
|
|
} else if (OB_ISNULL(aggr_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("fail to create aggr expr", K(ret), K(aggr_type));
|
|
} else if (OB_FAIL(aggr_expr->add_real_param_expr(target_param_expr))) {
|
|
LOG_WARN("fail to add param expr", K(ret));
|
|
} else if (OB_FAIL(aggr_expr->formalize(ctx_->session_info_))) {
|
|
LOG_WARN("failed to formalize expr", K(ret));
|
|
} else if (OB_FAIL(aggr_expr->pull_relation_id())) {
|
|
LOG_WARN("failed to pull relation id", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::add_group_by_with_cast(ObSelectStmt& view,
|
|
const ObIArray<ObRawExpr*>& left_exprs,
|
|
const ObIArray<ObRawExpr*>& right_exprs)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null ctx", K(ret));
|
|
} else if (left_exprs.count() != right_exprs.count()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect expr count", K(ret), K(left_exprs), K(right_exprs));
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < left_exprs.count(); i++) {
|
|
ObRawExpr* left = left_exprs.at(i);
|
|
ObRawExpr* right = right_exprs.at(i);
|
|
ObSysFunRawExpr* cast_expr = NULL;
|
|
bool need_add_cast = false;
|
|
bool is_valid = false;
|
|
if (OB_ISNULL(left) || OB_ISNULL(right)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null expr", K(ret), K(left), K(right));
|
|
} else if (OB_FAIL(check_need_add_cast(left, right, need_add_cast, is_valid))) {
|
|
LOG_WARN("failed to check need add cast", K(ret));
|
|
} else if (!is_valid) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("expect valid cast expr", K(ret));
|
|
} else if (need_add_cast) {
|
|
if (OB_FAIL(ObRawExprUtils::create_cast_expr(*ctx_->expr_factory_,
|
|
right,
|
|
left->get_result_type(),
|
|
cast_expr,
|
|
ctx_->session_info_))) {
|
|
LOG_WARN("failed to create cast expr", K(ret));
|
|
} else {
|
|
right = cast_expr;
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(view.add_group_expr(right))) {
|
|
LOG_WARN("failed to add group by expr in view", K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::add_distinct(ObSelectStmt &view,
|
|
const ObIArray<ObRawExpr*> &left_exprs,
|
|
const ObIArray<ObRawExpr*> &right_exprs)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null ctx", K(ret));
|
|
} else if (left_exprs.count() != right_exprs.count()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect expr count", K(ret), K(left_exprs), K(right_exprs));
|
|
}
|
|
view.assign_distinct();
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < view.get_select_item_size(); ++i) {
|
|
SelectItem &item = view.get_select_item(i);
|
|
ObRawExpr *left = NULL;
|
|
ObRawExpr *right = NULL;
|
|
ObSysFunRawExpr *cast_expr = NULL;
|
|
bool need_add_cast = false;
|
|
for (int64_t j = 0; OB_SUCC(ret) && !need_add_cast && j < left_exprs.count(); ++j) {
|
|
left = left_exprs.at(j);
|
|
right = right_exprs.at(j);
|
|
bool is_valid = false;
|
|
if (OB_ISNULL(left) || OB_ISNULL(right)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null expr", K(ret));
|
|
} else if (right != item.expr_) {
|
|
//do nothing
|
|
} else if (OB_FAIL(check_need_add_cast(left,
|
|
right,
|
|
need_add_cast,
|
|
is_valid))) {
|
|
LOG_WARN("failed to check need add cast", K(ret));
|
|
} else if (!is_valid) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("expect valid cast expr", K(ret));
|
|
} else if (need_add_cast) {
|
|
LOG_TRACE("need cast expr", K(*left), K(*right));
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
//do nothing
|
|
} else if (!need_add_cast) {
|
|
//do nothing
|
|
} else if (OB_ISNULL(left) || OB_ISNULL(right)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null expr", K(ret));
|
|
} else if (OB_FAIL(ObRawExprUtils::create_cast_expr(*ctx_->expr_factory_,
|
|
right,
|
|
left->get_result_type(),
|
|
cast_expr,
|
|
ctx_->session_info_))) {
|
|
LOG_WARN("failed to create cast expr", K(ret));
|
|
} else {
|
|
item.expr_ = cast_expr;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::add_ignore_semi_info(const uint64_t semi_id)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(ctx_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("ctx has null param", K(ret));
|
|
} else if (OB_FAIL(ctx_->ignore_semi_infos_.push_back(semi_id))) {
|
|
LOG_WARN("failed to push back ignore semi info", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::is_ignore_semi_info(const uint64_t semi_id, bool &ignore)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ignore = false;
|
|
if (OB_ISNULL(ctx_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("ctx has null param", K(ret));
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && !ignore && i < ctx_->ignore_semi_infos_.count(); ++i) {
|
|
if (ctx_->ignore_semi_infos_.at(i) == semi_id) {
|
|
ignore = true;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::is_expected_plan(ObLogPlan *plan, void *check_ctx, bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObCostBasedRewriteCtx *ctx = static_cast<ObCostBasedRewriteCtx *>(check_ctx);
|
|
ObSEArray<ObLogicalOperator*, 4> parents;
|
|
ObLogicalOperator* table_op = NULL;
|
|
is_valid = false;
|
|
if (OB_ISNULL(ctx) || OB_ISNULL(plan)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null param", K(ret));
|
|
} else if (ctx->is_multi_join_cond_) {
|
|
is_valid = true;
|
|
} else if (OB_FAIL(find_operator(plan->get_plan_root(),
|
|
parents,
|
|
ctx->table_id_,
|
|
table_op))) {
|
|
LOG_WARN("failed to get join operator", K(ret));
|
|
} else if (NULL == table_op || parents.empty()) {
|
|
//do nothing
|
|
} else {
|
|
ObLogicalOperator *child = table_op;
|
|
ObLogicalOperator *parent = NULL;
|
|
for (int64_t i = 0; OB_SUCC(ret) && !is_valid && i < parents.count(); child = parent, ++i) {
|
|
parent = parents.at(i);
|
|
if (OB_ISNULL(parent)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null operator", K(ret));
|
|
} else if (log_op_def::LOG_JOIN == parent->get_type()) {
|
|
ObLogJoin *join_op = static_cast<ObLogJoin*>(parent);
|
|
//After semi to inner, it needs to be used as a driving table and
|
|
//semi condition generates a conditional down pressure path
|
|
if (!join_op->is_nlj_with_param_down() ||
|
|
child != join_op->get_left_table()) {
|
|
//do nothing
|
|
} else if (OB_FAIL(check_is_semi_condition(join_op->get_nl_params(),
|
|
ctx->view_table_id_,
|
|
is_valid))) {
|
|
LOG_WARN("failed to check is semi condition", K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::find_operator(ObLogicalOperator* root,
|
|
ObIArray<ObLogicalOperator*> &parents,
|
|
uint64_t table_id,
|
|
ObLogicalOperator *&table_op)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
table_op = NULL;
|
|
if (OB_ISNULL(root)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpect null logical operator", K(ret));
|
|
} else if (log_op_def::LOG_TABLE_SCAN == root->get_type()) {
|
|
ObLogTableScan *scan = static_cast<ObLogTableScan *>(root);
|
|
if (scan->get_table_id() == table_id) {
|
|
table_op = scan;
|
|
}
|
|
} else if (log_op_def::LOG_FUNCTION_TABLE == root->get_type()) {
|
|
ObLogFunctionTable *scan = static_cast<ObLogFunctionTable *>(root);
|
|
if (scan->get_table_id() == table_id) {
|
|
table_op = scan;
|
|
}
|
|
} else {
|
|
for (int64_t i = 0; OB_SUCC(ret) && NULL == table_op && i < root->get_num_of_child(); ++i) {
|
|
ObLogicalOperator *child = root->get_child(i);
|
|
if (OB_FAIL(SMART_CALL(find_operator(child, parents, table_id, table_op)))) {
|
|
LOG_WARN("failed to find operator", K(ret));
|
|
} else if (NULL == table_op) {
|
|
//do nothing
|
|
} else if (OB_FAIL(parents.push_back(root))) {
|
|
LOG_WARN("failed to push back operator", K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::check_is_semi_condition(ObIArray<ObExecParamRawExpr *> &nl_params,
|
|
ObIArray<uint64_t> &table_ids,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<ObRawExpr*, 4> param_exprs;
|
|
ObBitSet<> column_ids;
|
|
is_valid = false;
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < nl_params.count(); ++i) {
|
|
if (OB_ISNULL(nl_params.at(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("nl param is null", K(ret));
|
|
} else if (OB_FAIL(param_exprs.push_back(nl_params.at(i)->get_ref_expr()))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < table_ids.count(); ++i) {
|
|
uint64_t table_id = table_ids.at(i);
|
|
if (OB_FAIL(ObOptimizerUtil::extract_column_ids(param_exprs, table_id, column_ids))) {
|
|
LOG_WARN("failed to extract colulmn ids", K(ret));
|
|
} else if (!column_ids.is_empty()) {
|
|
is_valid = true;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::construct_transform_hint(ObDMLStmt &stmt, void *trans_params)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSemiToInnerHint *hint = NULL;
|
|
ObIArray<TableItem*> *trans_right_table_items = NULL;
|
|
const ObQueryHint *query_hint = NULL;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->allocator_) ||
|
|
OB_ISNULL(query_hint = stmt.get_stmt_hint().query_hint_) ||
|
|
OB_ISNULL(trans_right_table_items = static_cast<ObIArray<TableItem*> *>(trans_params))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret), K(ctx_), K(query_hint));
|
|
} else if (OB_FAIL(ObQueryHint::create_hint(ctx_->allocator_, T_SEMI_TO_INNER, hint))) {
|
|
LOG_WARN("failed to create hint", K(ret));
|
|
} else {
|
|
TableItem *table_item = NULL;
|
|
ObTableInHint table_hint;
|
|
bool use_hint = false;
|
|
const ObSemiToInnerHint *myhint = static_cast<const ObSemiToInnerHint*>(get_hint(stmt.get_stmt_hint()));
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < trans_right_table_items->count(); ++i) {
|
|
if (OB_ISNULL(table_item = trans_right_table_items->at(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret), K(table_item));
|
|
} else if (OB_FALSE_IT(table_hint.set_table(*table_item))) {
|
|
} else if (OB_FAIL(hint->get_tables().push_back(table_hint))) {
|
|
LOG_WARN("failed to push back table hint", K(ret));
|
|
} else if (OB_FAIL(ctx_->add_src_hash_val(table_item->get_table_name()))) {
|
|
LOG_WARN("failed to add src hash val", K(ret));
|
|
} else if (NULL != myhint && myhint->enable_semi_to_inner(query_hint->cs_type_, *table_item)) {
|
|
use_hint = true;
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
} else if (OB_FAIL(ctx_->outline_trans_hints_.push_back(hint))) {
|
|
LOG_WARN("failed to push back hint", K(ret));
|
|
} else if (use_hint && OB_FAIL(ctx_->add_used_trans_hint(myhint))) {
|
|
LOG_WARN("failed to add used trans hint", K(ret));
|
|
} else {
|
|
hint->set_qb_name(ctx_->src_qb_name_);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformSemiToInner::check_hint_valid(const ObDMLStmt &stmt,
|
|
const TableItem &table,
|
|
bool &force_trans,
|
|
bool &force_no_trans) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
force_trans = false;
|
|
force_no_trans = false;
|
|
const ObQueryHint *query_hint = NULL;
|
|
const ObSemiToInnerHint *myhint = static_cast<const ObSemiToInnerHint*>(get_hint(stmt.get_stmt_hint()));
|
|
if (OB_ISNULL(query_hint = stmt.get_stmt_hint().query_hint_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret), K(query_hint));
|
|
} else {
|
|
force_trans = NULL != myhint && myhint->enable_semi_to_inner(query_hint->cs_type_, table);
|
|
force_no_trans = !force_trans && query_hint->has_outline_data();
|
|
}
|
|
return ret;
|
|
} |