Files
oceanbase/src/sql/optimizer/ob_select_log_plan.cpp
2023-05-30 11:47:15 +00:00

6932 lines
345 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_OPT
#include "sql/optimizer/ob_select_log_plan.h"
#include "lib/container/ob_array_iterator.h"
#include "lib/hash/ob_placement_hashmap.h"
#include "lib/hash/ob_placement_hashset.h"
#include "share/ob_cluster_version.h"
#include "sql/ob_sql_utils.h"
#include "sql/resolver/expr/ob_raw_expr_util.h"
#include "sql/resolver/dml/ob_select_stmt.h"
#include "sql/rewrite/ob_query_range.h"
#include "sql/rewrite/ob_transform_utils.h"
#include "sql/optimizer/ob_log_operator_factory.h"
#include "sql/optimizer/ob_log_table_scan.h"
#include "sql/optimizer/ob_log_join.h"
#include "sql/optimizer/ob_log_group_by.h"
#include "sql/optimizer/ob_log_sort.h"
#include "sql/optimizer/ob_log_exchange.h"
#include "sql/optimizer/ob_log_subplan_filter.h"
#include "sql/optimizer/ob_join_order.h"
#include "sql/optimizer/ob_log_for_update.h"
#include "sql/optimizer/ob_log_limit.h"
#include "sql/optimizer/ob_log_set.h"
#include "sql/optimizer/ob_log_distinct.h"
#include "sql/optimizer/ob_log_expr_values.h"
#include "sql/optimizer/ob_log_window_function.h"
#include "sql/optimizer/ob_log_temp_table_insert.h"
#include "sql/optimizer/ob_log_temp_table_transformation.h"
#include "sql/optimizer/ob_log_topk.h"
#include "lib/utility/ob_tracepoint.h"
#include "sql/optimizer/ob_log_plan_factory.h"
#include "sql/optimizer/ob_optimizer_util.h"
#include "sql/optimizer/ob_opt_est_cost.h"
#include "sql/optimizer/ob_optimizer_context.h"
#include "sql/optimizer/ob_opt_est_cost.h"
#include "sql/optimizer/ob_log_unpivot.h"
#include "sql/optimizer/ob_log_link_scan.h"
#include "common/ob_smart_call.h"
#include "share/system_variable/ob_sys_var_class_type.h"
using namespace oceanbase;
using namespace sql;
using namespace oceanbase::common;
using namespace oceanbase::sql::log_op_def;
using namespace oceanbase::jit::expr;
using share::schema::ObTableSchema;
namespace oceanbase
{
namespace sql
{
ObSelectLogPlan::ObSelectLogPlan(ObOptimizerContext &ctx, const ObSelectStmt *stmt)
: ObLogPlan(ctx, stmt)
{
}
ObSelectLogPlan::~ObSelectLogPlan()
{
}
int ObSelectLogPlan::candi_allocate_group_by()
{
int ret = OB_SUCCESS;
bool is_unique = false;
bool having_has_rownum = false;
const ObSelectStmt *stmt = NULL;
ObLogicalOperator *best_plan = NULL;
ObSEArray<ObRawExpr*, 4> reduce_exprs;
ObSEArray<ObRawExpr*, 4> group_by_exprs;
ObSEArray<ObRawExpr*, 4> rollup_exprs;
ObSEArray<ObOrderDirection, 4> group_by_directions;
ObSEArray<ObOrderDirection, 4> rollup_directions;
ObSEArray<ObRawExpr*, 8> having_subquery_exprs;
ObSEArray<ObRawExpr*, 8> having_normal_exprs;
ObSEArray<ObRawExpr*, 8> candi_subquery_exprs;
if (OB_ISNULL(stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(append(candi_subquery_exprs, stmt->get_group_exprs())) ||
OB_FAIL(append(candi_subquery_exprs, stmt->get_rollup_exprs())) ||
OB_FAIL(append(candi_subquery_exprs, stmt->get_aggr_items()))) {
LOG_WARN("failed to append exprs", K(ret));
} else if (OB_FAIL(candi_allocate_subplan_filter_for_exprs(candi_subquery_exprs))) {
LOG_WARN("failed to allocate subplan filter for exprs", K(ret));
} else if (stmt->is_scala_group_by()) {
if (OB_FAIL(ObOptimizerUtil::classify_subquery_exprs(stmt->get_having_exprs(),
having_subquery_exprs,
having_normal_exprs))) {
LOG_WARN("failed to classify subquery exprs", K(ret));
} else if (OB_FAIL(candi_allocate_scala_group_by(stmt->get_aggr_items(),
having_normal_exprs,
stmt->is_from_pivot()))) {
LOG_WARN("failed to allocate scala group by", K(ret));
}
} else if (OB_FAIL(ObOptimizerUtil::classify_subquery_exprs(stmt->get_having_exprs(),
having_subquery_exprs,
having_normal_exprs))) {
LOG_WARN("failed to classify subquery exprs", K(ret));
} else if (OB_FAIL(ObTransformUtils::check_has_rownum(stmt->get_having_exprs(),
having_has_rownum))) {
LOG_WARN("check has_rownum error", K(ret));
} else if (OB_FAIL(candidates_.get_best_plan(best_plan))) {
LOG_WARN("failed to get best plan", K(ret));
} else if (OB_ISNULL(best_plan)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(best_plan), K(ret));
} else if (OB_FAIL(get_groupby_rollup_exprs(best_plan,
reduce_exprs,
group_by_exprs,
rollup_exprs,
group_by_directions,
rollup_directions))) {
LOG_WARN("failed to get groupby rollup exprs", K(ret));
} else if (0 == stmt->get_aggr_item_size() && !stmt->has_rollup() &&
OB_FAIL(ObOptimizerUtil::is_exprs_unique(group_by_exprs,
best_plan->get_table_set(),
best_plan->get_fd_item_set(),
best_plan->get_output_equal_sets(),
best_plan->get_output_const_exprs(),
is_unique))) {
LOG_WARN("failed to check group by exprs is unique", K(ret));
} else if (is_unique && !having_has_rownum) {
OPT_TRACE("group by expr is unique, no need group by", group_by_exprs);
LOG_TRACE("group by expr is unique, no need group by", K(group_by_exprs));
if (!having_normal_exprs.empty() && OB_FAIL(candi_allocate_filter(having_normal_exprs))) {
LOG_WARN("failed to allocate filter", K(ret));
} else { /*do nothing*/ }
} else {
if (OB_FAIL(candi_allocate_normal_group_by(reduce_exprs,
group_by_exprs,
group_by_directions,
rollup_exprs,
rollup_directions,
having_normal_exprs,
stmt->get_aggr_items(),
stmt->is_from_pivot()))) {
LOG_WARN("failed to allocate normal group by", K(ret));
} else { /*do nothing*/ }
}
if (OB_SUCC(ret) && !having_subquery_exprs.empty()) {
if (OB_FAIL(candi_allocate_subplan_filter(having_subquery_exprs, &having_subquery_exprs))) {
LOG_WARN("failed to allocate subplan filter", K(ret));
} else { /*do nothing*/ }
}
return ret;
}
int ObSelectLogPlan::get_groupby_rollup_exprs(const ObLogicalOperator *top,
ObIArray<ObRawExpr*> &reduce_exprs,
ObIArray<ObRawExpr *> &group_by_exprs,
common::ObIArray<ObRawExpr *> &rollup_exprs,
ObIArray<ObOrderDirection> &group_directions,
common::ObIArray<ObOrderDirection> &rollup_directions)
{
int ret = OB_SUCCESS;
// gather all group by columns
const ObSelectStmt *stmt = get_stmt();
reduce_exprs.reuse();
if (OB_ISNULL(stmt) || OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(stmt), K(top));
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < stmt->get_group_expr_size(); ++i) {
ObRawExpr *group_expr = stmt->get_group_exprs().at(i);
bool is_const = false;
if (OB_ISNULL(group_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("group expr is NULL", K(ret), K(stmt->get_group_exprs()));
} else if (OB_FAIL(ObOptimizerUtil::is_const_expr(group_expr,
top->get_output_equal_sets(),
top->get_output_const_exprs(),
get_onetime_query_refs(),
is_const))) {
LOG_WARN("check is const expr failed", K(ret));
} else if (is_const) {
//no need to group a const expr, skip it
} else if (OB_FAIL(reduce_exprs.push_back(group_expr))) {
LOG_WARN("failed to push array", K(ret));
} else { /*do nothing*/ }
}
if (OB_SUCC(ret)) {
if (OB_FAIL(ObOptimizerUtil::simplify_exprs(top->get_fd_item_set(),
top->get_output_equal_sets(),
top->get_output_const_exprs(),
reduce_exprs,
group_by_exprs))) {
LOG_WARN("failed to simplify group exprs", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::find_stmt_expr_direction(*stmt,
group_by_exprs,
top->get_output_equal_sets(),
group_directions))) {
} else if (OB_FAIL(rollup_exprs.assign(stmt->get_rollup_exprs()))) {
LOG_WARN("failed to assign to rollop exprs.", K(ret));
} else if (rollup_exprs.count() > 0) {
bool has_rollup_dir = stmt->has_rollup_dir();
if (OB_UNLIKELY(has_rollup_dir && (stmt->get_rollup_dir_size() != rollup_exprs.count()))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("failed to check rollup exprs and directions count.", K (ret));
} else {/* do nothing. */}
for (int64_t i = 0; OB_SUCC(ret) && i < rollup_exprs.count(); i++) {
ObOrderDirection dir = has_rollup_dir ? stmt->get_rollup_dirs().at(i) : default_asc_direction();
if (OB_FAIL(rollup_directions.push_back(dir))) {
LOG_WARN("failed to push back into directions.", K(ret));
} else { /* do nothing. */ }
}
} // do nothing
}
if (OB_SUCC(ret)) {
LOG_TRACE("succeed to get group by exprs and rollup exprs", K(reduce_exprs), K(group_by_exprs), K(rollup_exprs));
}
}
return ret;
}
int ObSelectLogPlan::candi_allocate_normal_group_by(const ObIArray<ObRawExpr*> &reduce_exprs,
const ObIArray<ObRawExpr*> &group_by_exprs,
const ObIArray<ObOrderDirection> &group_directions,
const ObIArray<ObRawExpr*> &rollup_exprs,
const ObIArray<ObOrderDirection> &rollup_directions,
const ObIArray<ObRawExpr*> &having_exprs,
const ObIArray<ObAggFunRawExpr*> &aggr_items,
const bool is_from_povit)
{
int ret = OB_SUCCESS;
SMART_VAR(GroupingOpHelper, groupby_helper) {
ObSEArray<CandidatePlan, 4> groupby_plans;
if (OB_FAIL(init_groupby_helper(group_by_exprs,
rollup_exprs,
aggr_items,
is_from_povit,
groupby_helper))) {
LOG_WARN("failed to init group by helper", K(ret));
} else if (groupby_helper.can_three_stage_pushdown_) {
if (OB_FAIL(candi_allocate_three_stage_group_by(reduce_exprs,
group_by_exprs,
group_directions,
rollup_exprs,
rollup_directions,
aggr_items,
having_exprs,
is_from_povit,
groupby_helper,
groupby_plans))) {
LOG_WARN("failed to candi allocate three stage group by", K(ret));
}
} else if (OB_FAIL(candi_allocate_normal_group_by(reduce_exprs,
group_by_exprs,
group_directions,
rollup_exprs,
rollup_directions,
having_exprs,
aggr_items,
is_from_povit,
groupby_helper,
false,
groupby_plans))) {
LOG_WARN("failed to inner allocate normal group by", K(ret));
} else if (!groupby_plans.empty()) {
LOG_TRACE("succeed to allocate group by using hint", K(groupby_plans.count()), K(groupby_helper));
} else if (OB_FAIL(get_log_plan_hint().check_status())) {
LOG_WARN("failed to generate plans with hint", K(ret));
} else if (OB_FAIL(candi_allocate_normal_group_by(reduce_exprs,
group_by_exprs,
group_directions,
rollup_exprs,
rollup_directions,
having_exprs,
aggr_items,
is_from_povit,
groupby_helper,
true,
groupby_plans))) {
LOG_WARN("failed to inner allocate normal group by", K(ret));
} else {
LOG_TRACE("succeed to allocate group by ignore hint", K(groupby_plans.count()), K(groupby_helper));
}
//add plans to candidates
if (OB_SUCC(ret)) {
int64_t check_scope = OrderingCheckScope::CHECK_WINFUNC |
OrderingCheckScope::CHECK_DISTINCT |
OrderingCheckScope::CHECK_SET |
OrderingCheckScope::CHECK_ORDERBY;
if (OB_FAIL(update_plans_interesting_order_info(groupby_plans, check_scope))) {
LOG_WARN("failed to update plans interesting order info", K(ret));
} else if (OB_FAIL(prune_and_keep_best_plans(groupby_plans))) {
LOG_WARN("failed to add plan", K(ret));
} else { /*do nothing*/ }
}
}
return ret;
}
// create three-stage push down plan
int ObSelectLogPlan::candi_allocate_three_stage_group_by(const ObIArray<ObRawExpr*> &reduce_exprs,
const ObIArray<ObRawExpr*> &group_by_exprs,
const ObIArray<ObOrderDirection> &group_directions,
const ObIArray<ObRawExpr*> &rollup_exprs,
const ObIArray<ObOrderDirection> &rollup_directions,
const ObIArray<ObAggFunRawExpr*> &aggr_items,
const ObIArray<ObRawExpr*> &having_exprs,
const bool is_from_povit,
GroupingOpHelper &groupby_helper,
ObIArray<CandidatePlan> &groupby_plans)
{
int ret = OB_SUCCESS;
ObSEArray<CandidatePlan, 16> best_plans;
bool is_partition_wise = false;
if (OB_FAIL(get_minimal_cost_candidates(candidates_.candidate_plans_, best_plans))) {
LOG_WARN("failed to get minimal cost candidate", K(ret));
} else {
CandidatePlan candidate_plan;
for (int64_t i = 0; OB_SUCC(ret) && i < best_plans.count(); i++) {
candidate_plan = best_plans.at(i);
if (OB_ISNULL(candidate_plan.plan_tree_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (candidate_plan.plan_tree_->is_distributed() && !reduce_exprs.empty() &&
OB_FAIL(candidate_plan.plan_tree_->check_sharding_compatible_with_reduce_expr(reduce_exprs,
is_partition_wise))) {
LOG_WARN("failed to check if sharding compatible with distinct expr", K(ret));
} else if (!candidate_plan.plan_tree_->is_distributed() || is_partition_wise) {
bool part_sort_valid = !groupby_helper.force_normal_sort_ && !group_by_exprs.empty();
bool normal_sort_valid = !groupby_helper.force_part_sort_;
bool can_ignore_merge_plan = !(groupby_plans.empty() || groupby_helper.force_use_merge_);
if (OB_FAIL(update_part_sort_method(part_sort_valid, normal_sort_valid))) {
LOG_WARN("fail to update part sort method", K(ret));
} else if (OB_FAIL(create_merge_group_plan(reduce_exprs,
group_by_exprs,
group_directions,
rollup_exprs,
rollup_directions,
aggr_items,
having_exprs,
is_from_povit,
groupby_helper,
candidate_plan,
groupby_plans,
part_sort_valid,
normal_sort_valid,
can_ignore_merge_plan))) {
LOG_WARN("failed to create merge group by plan", K(ret));
}
} else {
if (NULL == groupby_helper.aggr_code_expr_ &&
OB_FAIL(prepare_three_stage_info(group_by_exprs, rollup_exprs, groupby_helper))) {
LOG_WARN("failed to prepare three stage info", K(ret));
} else if (OB_FAIL(create_three_stage_group_plan(group_by_exprs,
rollup_exprs,
having_exprs,
groupby_helper,
candidate_plan.plan_tree_))) {
LOG_WARN("failed to create hash group by plan", K(ret));
} else if (OB_FAIL(groupby_plans.push_back(candidate_plan))) {
LOG_WARN("failed to push merge group by", K(ret));
}
}
}
}
return ret;
}
int ObSelectLogPlan::get_valid_aggr_algo(const ObIArray<ObRawExpr*> &group_by_exprs,
const GroupingOpHelper &groupby_helper,
const bool ignore_hint,
bool &use_hash_valid,
bool &use_merge_valid,
bool &part_sort_valid,
bool &normal_sort_valid)
{
int ret = OB_SUCCESS;
if (ignore_hint) {
use_hash_valid = true;
use_merge_valid = true;
part_sort_valid = true;
normal_sort_valid = true;
} else {
use_hash_valid = !groupby_helper.force_use_merge_;
use_merge_valid = !groupby_helper.force_use_hash_;
part_sort_valid = !groupby_helper.force_normal_sort_;
normal_sort_valid = !groupby_helper.force_part_sort_;
}
if (OB_ISNULL(get_stmt()) || OB_ISNULL(optimizer_context_.get_query_ctx())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(get_stmt()), K(optimizer_context_.get_query_ctx()), K(ret));
} else if (get_stmt()->has_rollup()
|| group_by_exprs.empty()
|| get_stmt()->has_distinct_or_concat_agg()) {
//group_concat and distinct aggregation hold all input rows temporary,
//too much memory consumption for hash aggregate.
use_hash_valid = false;
}
if (OB_FAIL(ret)) {
} else if (!use_merge_valid) {
part_sort_valid = false;
normal_sort_valid = false;
} else if (group_by_exprs.empty()) {
part_sort_valid = false;
/* if normal_sort_valid set as false by hint, will retry by ignore hint */
} else if (OB_FAIL(update_part_sort_method(part_sort_valid, normal_sort_valid))) {
LOG_WARN("fail to update part sort method", K(ret));
}
return ret;
}
// update partition sort method with opt_param hint
int ObSelectLogPlan::update_part_sort_method(bool &part_sort_valid,
bool &normal_sort_valid)
{
int ret = OB_SUCCESS;
if (!part_sort_valid || !normal_sort_valid) {
/* has sort method in no_use_hash_aggregation hint, ignore opt_param hint */
} else {
bool use_part_sort = false;
bool is_exists_opt = false;
if (OB_FAIL(optimizer_context_.get_query_ctx()->get_global_hint()
.opt_params_.get_bool_opt_param(
ObOptParamHint::USE_PART_SORT_MGB, use_part_sort, is_exists_opt))) {
LOG_WARN("fail to check partition sort merge group by enabled", K(ret));
} else if (!is_exists_opt) {
/* has no opt_param hint */
} else {
part_sort_valid = use_part_sort;
normal_sort_valid = !use_part_sort;
}
}
return ret;
}
int ObSelectLogPlan::candi_allocate_normal_group_by(const ObIArray<ObRawExpr*> &reduce_exprs,
const ObIArray<ObRawExpr*> &group_by_exprs,
const ObIArray<ObOrderDirection> &group_directions,
const ObIArray<ObRawExpr*> &rollup_exprs,
const ObIArray<ObOrderDirection> &rollup_directions,
const ObIArray<ObRawExpr*> &having_exprs,
const ObIArray<ObAggFunRawExpr*> &aggr_items,
const bool is_from_povit,
GroupingOpHelper &groupby_helper,
const bool ignore_hint,
ObIArray<CandidatePlan> &groupby_plans)
{
int ret = OB_SUCCESS;
CandidatePlan candidate_plan;
bool use_hash_valid = false;
bool use_merge_valid = false;
bool part_sort_valid = false;
bool normal_sort_valid = false;
if (OB_FAIL(get_valid_aggr_algo(group_by_exprs, groupby_helper, ignore_hint,
use_hash_valid, use_merge_valid,
part_sort_valid, normal_sort_valid))) {
LOG_WARN("failed to get valid aggr algo", K(ret));
}
// create hash group by plans
if (OB_SUCC(ret) && use_hash_valid) {
ObSEArray<CandidatePlan, 16> best_plans;
if (OB_FAIL(get_minimal_cost_candidates(candidates_.candidate_plans_, best_plans))) {
LOG_WARN("failed to get minimal cost candidate", K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < best_plans.count(); i++) {
candidate_plan = best_plans.at(i);
if (OB_FAIL(create_hash_group_plan(reduce_exprs,
group_by_exprs,
rollup_exprs,
aggr_items,
having_exprs,
is_from_povit,
groupby_helper,
candidate_plan.plan_tree_))) {
LOG_WARN("failed to create hash group by plan", K(ret));
} else if (NULL != candidate_plan.plan_tree_ &&
OB_FAIL(groupby_plans.push_back(candidate_plan))) {
LOG_WARN("failed to push merge group by", K(ret));
} else { /*do nothing*/ }
}
}
}
// create merge group by plans
if (OB_SUCC(ret) && use_merge_valid) {
bool can_ignore_merge_plan = !groupby_plans.empty();
for (int64_t i = 0; OB_SUCC(ret) && i < candidates_.candidate_plans_.count(); i++) {
candidate_plan = candidates_.candidate_plans_.at(i);
bool is_needed = false;
if (OB_FAIL(should_create_rollup_pushdown_plan(candidate_plan.plan_tree_,
reduce_exprs,
rollup_exprs,
groupby_helper,
is_needed))) {
LOG_WARN("failed to check should create rollup pushdown plan", K(ret));
} else if (is_needed) {
if (OB_FAIL(create_rollup_pushdown_plan(group_by_exprs,
rollup_exprs,
aggr_items,
having_exprs,
groupby_helper,
candidate_plan.plan_tree_))) {
LOG_WARN("failed to create rollup pushdown plan", K(ret));
} else if (NULL != candidate_plan.plan_tree_ &&
OB_FAIL(groupby_plans.push_back(candidate_plan))) {
LOG_WARN("failed to push merge group by", K(ret));
}
} else if (OB_FAIL(create_merge_group_plan(reduce_exprs,
group_by_exprs,
group_directions,
rollup_exprs,
rollup_directions,
aggr_items,
having_exprs,
is_from_povit,
groupby_helper,
candidate_plan,
groupby_plans,
part_sort_valid,
normal_sort_valid,
can_ignore_merge_plan))) {
LOG_WARN("failed to create merge group by plan", K(ret));
}
}
}
return ret;
}
int ObSelectLogPlan::should_create_rollup_pushdown_plan(ObLogicalOperator *top,
const ObIArray<ObRawExpr *> &reduce_exprs,
const ObIArray<ObRawExpr *> &rollup_exprs,
GroupingOpHelper &groupby_helper,
bool &is_needed)
{
int ret = OB_SUCCESS;
bool is_partition_wise = false;
is_needed = false;
ObSQLSessionInfo *session = get_optimizer_context().get_session_info();
if (OB_ISNULL(top) || OB_ISNULL(session)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("logical operator is null", K(ret), K(top), K(session));
} else if (rollup_exprs.empty() || !groupby_helper.can_rollup_pushdown_) {
// do nothing
} else if (top->is_distributed() &&
OB_FAIL(top->check_sharding_compatible_with_reduce_expr(reduce_exprs,
is_partition_wise))) {
LOG_WARN("failed to check is partition wise", K(ret));
} else if (!top->is_distributed() || is_partition_wise ) {
// do nothing
} else if (NULL == groupby_helper.rollup_id_expr_ &&
OB_FAIL(ObRawExprUtils::build_pseudo_rollup_id(get_optimizer_context().get_expr_factory(),
*session,
groupby_helper.rollup_id_expr_))) {
LOG_WARN("failed to build rollup id expr", K(ret));
} else {
is_needed = true;
}
return ret;
}
int ObSelectLogPlan::create_rollup_pushdown_plan(const ObIArray<ObRawExpr*> &group_by_exprs,
const ObIArray<ObRawExpr*> &rollup_exprs,
const ObIArray<ObAggFunRawExpr*> &aggr_items,
const ObIArray<ObRawExpr*> &having_exprs,
GroupingOpHelper &groupby_helper,
ObLogicalOperator *&top)
{
int ret = OB_SUCCESS;
// 1. allocate pre group by
ObSEArray<ObRawExpr *, 4> pre_group_exprs;
ObSEArray<ObRawExpr *, 4> rc_group_exprs; // rollup collector
ObSEArray<ObRawExpr *, 4> exch_keys;
ObSEArray<OrderItem, 4> rd_sort_keys;
ObSEArray<OrderItem, 4> rd_ecd_sort_keys;
OrderItem encode_sort_key;
bool enable_encode_sort = false;
ObSEArray<OrderItem, 4> sort_keys;
ObArray<ObRawExpr *> dummy;
ObExchangeInfo exch_info;
ObLogGroupBy *rollup_distributor = NULL;
ObLogGroupBy *rollup_collector = NULL;
bool can_sort_opt = true;
if (OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(append(pre_group_exprs, group_by_exprs)) ||
OB_FAIL(append(pre_group_exprs, rollup_exprs))) {
LOG_WARN("failed to append pre group exprs", K(ret));
} else if (OB_FAIL(allocate_group_by_as_top(top,
AggregateAlgo::HASH_AGGREGATE,
pre_group_exprs,
dummy,
aggr_items,
dummy,
false,
groupby_helper.group_ndv_,
top->get_card(),
false,
true,
false))) {
LOG_WARN("failed to allocate pre group by operator", K(ret));
} else if (OB_FAIL(allocate_group_by_as_top(top,
AggregateAlgo::MERGE_AGGREGATE,
group_by_exprs,
rollup_exprs,
aggr_items,
dummy,
false,
groupby_helper.group_ndv_,
top->get_card(),
false,
true,
false,
ObRollupStatus::ROLLUP_DISTRIBUTOR))) {
LOG_WARN("failed to allocate rollup distributor", K(ret));
} else if (OB_ISNULL(rollup_distributor = dynamic_cast<ObLogGroupBy *>(top))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("rollup distributor is null", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::make_sort_keys(pre_group_exprs,
default_asc_direction(),
rd_sort_keys))) {
LOG_WARN("failed to make sort keys", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::check_can_encode_sortkey(rd_sort_keys,
can_sort_opt, *this, top->get_card()))) {
LOG_WARN("failed to check encode sortkey expr", K(ret));
} else if (can_sort_opt
&& (OB_FAIL(ObSQLUtils::create_encode_sortkey_expr(get_optimizer_context().get_expr_factory(),
get_optimizer_context().get_exec_ctx(),
rd_sort_keys,
0,
encode_sort_key)
|| FALSE_IT(enable_encode_sort = true)
|| OB_FAIL(rd_ecd_sort_keys.push_back(encode_sort_key))))) {
LOG_WARN("failed to create encode sortkey expr", K(ret));
} else if (OB_FAIL(rollup_distributor->set_rollup_info(ObRollupStatus::ROLLUP_DISTRIBUTOR,
groupby_helper.rollup_id_expr_,
rd_sort_keys,
rd_ecd_sort_keys,
enable_encode_sort))) {
LOG_WARN("failed to set rollup distributor info", K(ret));
} else if (OB_FAIL(append(exch_keys, group_by_exprs)) ||
OB_FAIL(exch_keys.push_back(groupby_helper.rollup_id_expr_)) ||
OB_FAIL(append(exch_keys, rollup_exprs))) {
LOG_WARN("failed to append exchange keys", K(ret));
} else if (OB_FAIL(get_grouping_style_exchange_info(exch_keys,
top->get_output_equal_sets(),
exch_info))) {
LOG_WARN("failed to get rollup exchange info", K(ret));
} else if (FALSE_IT(exch_info.is_rollup_hybrid_ = true)) {
// do nothing
} else if (OB_FAIL(ObOptimizerUtil::make_sort_keys(exch_keys, default_asc_direction(), sort_keys))) {
LOG_WARN("failed to generate sort keys", K(ret));
} else if (OB_FAIL(allocate_sort_and_exchange_as_top(top,
exch_info,
sort_keys,
true,
0,
false))) {
LOG_WARN("failed to allocate sort and exchange as top", K(ret));
} else if (OB_FAIL(append(rc_group_exprs, group_by_exprs)) ||
OB_FAIL(rc_group_exprs.push_back(groupby_helper.rollup_id_expr_))) {
LOG_WARN("failed to create rollup collector group by keys", K(ret));
} else if (OB_FAIL(allocate_group_by_as_top(top,
AggregateAlgo::MERGE_AGGREGATE,
rc_group_exprs,
rollup_exprs,
aggr_items,
having_exprs,
false,
groupby_helper.group_ndv_,
top->get_card(),
false,
false,
false,
ObRollupStatus::ROLLUP_COLLECTOR))) {
LOG_WARN("failed to allocate rollup collector", K(ret));
} else if (OB_ISNULL(rollup_collector = dynamic_cast<ObLogGroupBy *>(top))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("rollup collector is null", K(ret));
} else if (rollup_collector->set_rollup_info(ObRollupStatus::ROLLUP_COLLECTOR,
groupby_helper.rollup_id_expr_)) {
LOG_WARN("failed to set rollup id expr", K(ret));
} else {
rollup_collector->set_group_by_outline_info(false, true);
}
return ret;
}
int ObSelectLogPlan::create_hash_group_plan(const ObIArray<ObRawExpr*> &reduce_exprs,
const ObIArray<ObRawExpr*> &group_by_exprs,
const ObIArray<ObRawExpr*> &rollup_exprs,
const ObIArray<ObAggFunRawExpr*> &aggr_items,
const ObIArray<ObRawExpr*> &having_exprs,
const bool is_from_povit,
GroupingOpHelper &groupby_helper,
ObLogicalOperator *&top)
{
int ret = OB_SUCCESS;
bool is_partition_wise = false;
double origin_child_card = 0.0;
if (OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(top), K(get_stmt()), K(ret));
} else if (OB_FALSE_IT(origin_child_card = top->get_card())) {
} else if (top->is_distributed() &&
OB_FAIL(top->check_sharding_compatible_with_reduce_expr(reduce_exprs,
is_partition_wise))) {
LOG_WARN("failed to check if sharding compatible", K(ret));
} else if (!top->is_distributed() || is_partition_wise) {
if (OB_FAIL(allocate_group_by_as_top(top,
AggregateAlgo::HASH_AGGREGATE,
group_by_exprs,
rollup_exprs,
aggr_items,
having_exprs,
is_from_povit,
groupby_helper.group_ndv_,
origin_child_card,
is_partition_wise))) {
LOG_WARN("failed to allocate group by as top", K(ret));
}
static_cast<ObLogGroupBy*>(top)->set_group_by_outline_info(true, false);
} else {
// allocate push down group by
if (groupby_helper.can_basic_pushdown_) {
/*
* create table t1(a int, b int, c int) partition by hash(a) partitions 4;
* select sum(a) from t1 group by c;
* push group by and aggregation
*/
ObSEArray<ObRawExpr*, 1> dummy_exprs;
ObSEArray<ObRawExpr*, 8> group_rollup_exprs;
if (OB_FAIL(append(group_rollup_exprs, group_by_exprs)) ||
OB_FAIL(append(group_rollup_exprs, rollup_exprs))) {
LOG_WARN("failed to append exprs", K(ret));
} else if (OB_FAIL(allocate_group_by_as_top(top,
AggregateAlgo::HASH_AGGREGATE,
group_rollup_exprs,
dummy_exprs,
aggr_items,
dummy_exprs,
is_from_povit,
groupby_helper.group_ndv_,
origin_child_card,
is_partition_wise,
true,
true))) {
LOG_WARN("failed to allocate group by as top", K(ret));
} else if (OB_FAIL(allocate_topk_for_hash_group_plan(top))) { // allocate top-k
LOG_WARN("failed to allocate topk for hash group by plan", K(ret));
} else { /*do nothing*/ }
}
// allocate exchange
if (OB_SUCC(ret)) {
ObExchangeInfo exch_info;
if (OB_FAIL(get_grouping_style_exchange_info(group_by_exprs,
top->get_output_equal_sets(),
exch_info))) {
LOG_WARN("failed to get grouping style exchange info", K(ret));
} else if (OB_FAIL(allocate_exchange_as_top(top, exch_info))) {
LOG_WARN("failed to allocate exchange as top", K(ret));
} else { /*do nothing*/ }
}
// allocate final group by
if (OB_SUCC(ret)) {
if (OB_FAIL(allocate_group_by_as_top(top,
AggregateAlgo::HASH_AGGREGATE,
group_by_exprs,
rollup_exprs,
aggr_items,
having_exprs,
is_from_povit,
groupby_helper.group_ndv_,
origin_child_card))) {
LOG_WARN("failed to allocate scala group by as top", K(ret));
} else {
static_cast<ObLogGroupBy*>(top)->set_group_by_outline_info(true, groupby_helper.can_basic_pushdown_);
}
}
}
return ret;
}
int ObSelectLogPlan::allocate_topk_for_hash_group_plan(ObLogicalOperator *&top)
{
int ret = OB_SUCCESS;
ObSEArray<ObRawExpr*, 16> order_by_exprs;
ObSEArray<ObOrderDirection, 16> directions;
const ObSelectStmt *select_stmt = NULL;
const ObGlobalHint &global_hint = get_optimizer_context().get_global_hint();
if (OB_ISNULL(top) || OB_ISNULL(select_stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(top), K(select_stmt), K(ret));
} else if (!select_stmt->is_match_topk()) {
/*do nothing*/
} else if (OB_FAIL(get_order_by_exprs(top, order_by_exprs, &directions))) {
LOG_WARN("failed to get order by exprs", K(ret));
} else if (order_by_exprs.empty()) {
// no need order by, directly allocate topk
if (OB_FAIL(allocate_topk_as_top(top,
select_stmt->get_limit_expr(),
select_stmt->get_offset_expr(),
global_hint.sharding_minimum_row_count_,
global_hint.topk_precision_))) {
LOG_WARN("failed to allocate topk as top", K(ret));
} else { /*do nothing*/ }
} else {
// allocate sort
ObSEArray<OrderItem, 8> sort_keys;
ObSEArray<OrderItem, 8> topk_sort_keys;
if (OB_FAIL(make_order_items(order_by_exprs, directions, sort_keys))) {
LOG_WARN("failed to make order items", K(ret));
} else if (OB_FAIL(clone_sort_keys_for_topk(sort_keys,
topk_sort_keys))) {
LOG_WARN("failed to clone sort keys for topk", K(ret));
} else if (OB_FAIL(allocate_topk_sort_as_top(top,
topk_sort_keys,
select_stmt->get_limit_expr(),
select_stmt->get_offset_expr(),
global_hint.sharding_minimum_row_count_,
global_hint.topk_precision_))) {
LOG_WARN("failed to allocate topk sort as top", K(ret));
} else { /*do nothing*/ }
}
return ret;
}
int ObSelectLogPlan::allocate_topk_sort_as_top(ObLogicalOperator *&top,
const ObIArray<OrderItem> &sort_keys,
ObRawExpr *limit_expr,
ObRawExpr *offset_expr,
int64_t minimum_row_count,
int64_t topk_precision)
{
int ret = OB_SUCCESS;
ObLogSort *sort = NULL;
if (OB_ISNULL(top) || OB_ISNULL(limit_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(top), K(limit_expr), K(ret));
} else if (OB_ISNULL(sort = static_cast<ObLogSort*>(get_log_op_factory().allocate(*this, LOG_SORT)))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("failed to allocate sort for order by", K(ret));
} else {
sort->set_child(ObLogicalOperator::first_child, top);
sort->set_topk_limit_expr(limit_expr);
sort->set_topk_offset_expr(offset_expr);
sort->set_minimal_row_count(minimum_row_count);
sort->set_topk_precision(topk_precision);
if (OB_FAIL(sort->set_sort_keys(sort_keys))) {
LOG_WARN("failed to set sort keys", K(ret));
} else if (OB_FAIL(sort->compute_property())) {
LOG_WARN("failed to compute property", K(ret));
} else {
top = sort;
}
}
return ret;
}
int ObSelectLogPlan::clone_sort_keys_for_topk(const ObIArray<OrderItem> &sort_keys,
ObIArray<OrderItem> &topk_sort_keys)
{
int ret = OB_SUCCESS;
for (int64_t i = 0; OB_SUCC(ret) && i < sort_keys.count(); ++i) {
ObRawExpr *new_sort_expr = NULL;
if (OB_FAIL(ObOptimizerUtil::clone_expr_for_topk(get_optimizer_context().get_expr_factory(),
sort_keys.at(i).expr_,
new_sort_expr))) {
LOG_WARN("failed to copy expr", K(ret));
} else if (OB_ISNULL(new_sort_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(new_sort_expr), K(ret));
} else {
OrderItem new_order_item;
new_order_item.expr_ = new_sort_expr;
new_order_item.order_type_ = sort_keys.at(i).order_type_;
if (OB_FAIL(topk_sort_keys.push_back(new_order_item))) {
LOG_WARN("failed to push back order_item", K(ret));
} else { /*do nothing*/ }
}
}
return ret;
}
int ObSelectLogPlan::allocate_topk_as_top(ObLogicalOperator *&top,
ObRawExpr *topk_limit_count,
ObRawExpr *topk_limit_offset,
int64_t minimum_row_count,
int64_t topk_precision)
{
int ret = OB_SUCCESS;
ObLogTopk *topk_op = NULL;
if (OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_ISNULL(topk_op = static_cast<ObLogTopk*>(log_op_factory_.allocate(
*this, log_op_def::LOG_TOPK)))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("failed to allocate topk operator", K(ret));
} else {
topk_op->set_child(ObLogicalOperator::first_child, top);
topk_op->set_topk_params(topk_limit_count, topk_limit_offset,
minimum_row_count, topk_precision);
if (OB_FAIL(topk_op->compute_property())) {
LOG_WARN("failed to compute property", K(ret));
} else {
top = topk_op;
}
}
return ret;
}
int ObSelectLogPlan::create_merge_group_plan(const ObIArray<ObRawExpr*> &reduce_exprs,
const ObIArray<ObRawExpr*> &group_by_exprs,
const ObIArray<ObOrderDirection> &group_directions,
const ObIArray<ObRawExpr*> &rollup_exprs,
const ObIArray<ObOrderDirection> &rollup_directions,
const ObIArray<ObAggFunRawExpr*> &aggr_items,
const ObIArray<ObRawExpr*> &having_exprs,
const bool is_from_povit,
GroupingOpHelper &groupby_helper,
CandidatePlan &candidate_plan,
ObIArray<CandidatePlan> &candidate_plans,
bool part_sort_valid,
bool normal_sort_valid,
bool can_ignore_merge_plan)
{
int ret = OB_SUCCESS;
CandidatePlan part_sort_mgb_plan = candidate_plan;
bool can_ignore_no_part_sort_plan = can_ignore_merge_plan;
if (part_sort_valid && OB_FAIL(inner_create_merge_group_plan(reduce_exprs,
group_by_exprs,
group_directions,
rollup_exprs,
rollup_directions,
aggr_items,
having_exprs,
is_from_povit,
groupby_helper,
part_sort_mgb_plan.plan_tree_,
true,
can_ignore_merge_plan))) {
LOG_WARN("failed to create partition sort merge group by plan", K(ret));
} else if (part_sort_valid && NULL != part_sort_mgb_plan.plan_tree_
&& OB_FAIL(candidate_plans.push_back(part_sort_mgb_plan))) {
LOG_WARN("failed to push merge group by", K(ret));
} else if (OB_FALSE_IT(can_ignore_no_part_sort_plan |= part_sort_valid && NULL != part_sort_mgb_plan.plan_tree_)) {
} else if (normal_sort_valid && OB_FAIL(inner_create_merge_group_plan(reduce_exprs,
group_by_exprs,
group_directions,
rollup_exprs,
rollup_directions,
aggr_items,
having_exprs,
is_from_povit,
groupby_helper,
candidate_plan.plan_tree_,
false,
can_ignore_no_part_sort_plan))) {
LOG_WARN("failed to create merge group by plan", K(ret));
} else if (normal_sort_valid && NULL != candidate_plan.plan_tree_ &&
OB_FAIL(candidate_plans.push_back(candidate_plan))) {
LOG_WARN("failed to push merge group by", K(ret));
}
return ret;
}
int ObSelectLogPlan::inner_create_merge_group_plan(const ObIArray<ObRawExpr*> &reduce_exprs,
const ObIArray<ObRawExpr*> &group_by_exprs,
const ObIArray<ObOrderDirection> &group_directions,
const ObIArray<ObRawExpr*> &rollup_exprs,
const ObIArray<ObOrderDirection> &rollup_directions,
const ObIArray<ObAggFunRawExpr*> &aggr_items,
const ObIArray<ObRawExpr*> &having_exprs,
const bool is_from_povit,
GroupingOpHelper &groupby_helper,
ObLogicalOperator *&top,
bool use_part_sort,
bool can_ignore_merge_plan)
{
int ret = OB_SUCCESS;
int64_t prefix_pos = 0;
int64_t push_prefix_pos = 0;
bool need_sort = false;
bool push_need_sort = false;
bool is_partition_wise = false;
bool is_fetch_with_ties = false;
int64_t part_cnt = use_part_sort ? group_by_exprs.count() : 0;
OrderItem hash_sortkey;
ObSEArray<ObRawExpr*, 1> dummy_exprs;
ObSEArray<ObAggFunRawExpr*, 1> dummy_aggrs;
ObSEArray<OrderItem, 4> sort_keys;
ObSEArray<OrderItem, 4> push_sort_keys;
ObSEArray<ObRawExpr*, 4> sort_exprs;
ObSEArray<ObOrderDirection, 4> sort_directions;
ObSEArray<ObRawExpr*, 4> adjusted_group_by_exprs;
ObSEArray<ObOrderDirection, 4> adjusted_group_directions;
int64_t interesting_order_info = OrderingFlag::NOT_MATCH;
double origin_child_card = 0.0;
if (OB_ISNULL(top) || OB_UNLIKELY(0 == part_cnt && use_part_sort)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected params", K(top), K(part_cnt), K(use_part_sort), K(ret));
} else if (OB_FALSE_IT(origin_child_card = top->get_card())) {
} else if (OB_FAIL(adjusted_group_by_exprs.assign(group_by_exprs)) ||
OB_FAIL(adjusted_group_directions.assign(group_directions))) {
LOG_WARN("failed to assign expr", K(ret));
} else if (OB_FAIL(adjust_sort_expr_ordering(adjusted_group_by_exprs,
adjusted_group_directions,
*top,
true))) {
LOG_WARN("failed to get group expr ordering", K(ret));
} else if (OB_FAIL(generate_merge_group_sort_keys(top,
adjusted_group_by_exprs,
adjusted_group_directions,
rollup_exprs,
rollup_directions,
sort_exprs,
sort_directions))) {
LOG_WARN("failed to generate merge group sort keys", K(ret));
} else if (OB_FAIL(make_order_items(sort_exprs,
sort_directions,
sort_keys))) {
LOG_WARN("failed to make order items", K(ret));
} else if (can_ignore_merge_plan && OB_FAIL(ObOptimizerUtil::compute_stmt_interesting_order(sort_keys,
get_stmt(),
false,
const_cast<EqualSets &>(top->get_output_equal_sets()),
top->get_output_const_exprs(),
get_is_parent_set_distinct(),
OrderingCheckScope::CHECK_ALL & (~OrderingCheckScope::CHECK_GROUP),
interesting_order_info))) {
LOG_WARN("failed to compute stmt interesting order", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::check_need_sort(sort_exprs,
&sort_directions,
top->get_op_ordering(),
top->get_fd_item_set(),
top->get_output_equal_sets(),
top->get_output_const_exprs(),
get_onetime_query_refs(),
top->get_is_at_most_one_row(),
need_sort,
prefix_pos,
part_cnt))) {
LOG_WARN("failed to check if need sort", K(ret));
} else if ((!need_sort && use_part_sort) ||
(need_sort && can_ignore_merge_plan &&
OrderingFlag::NOT_MATCH == interesting_order_info)) {
// 1. need generate partition sort plan, but need not sort
// 2. if need sort and no further op needs the output order, not generate merge groupby
top = NULL;
} else if (top->is_distributed() &&
OB_FAIL(top->check_sharding_compatible_with_reduce_expr(reduce_exprs,
is_partition_wise))) {
LOG_WARN("failed to check if sharding compatiable with reduce expr", K(ret));
} else if (!top->is_distributed() || is_partition_wise) {
if (OB_FAIL(try_allocate_sort_as_top(top, sort_keys, need_sort, prefix_pos, part_cnt))) {
LOG_WARN("failed to allcoate sort as top", K(ret));
} else if (OB_FAIL(allocate_group_by_as_top(top,
MERGE_AGGREGATE,
adjusted_group_by_exprs,
rollup_exprs,
aggr_items,
having_exprs,
is_from_povit,
groupby_helper.group_ndv_,
origin_child_card,
is_partition_wise))) {
LOG_WARN("failed to allocate group by as top", K(ret));
} else {
static_cast<ObLogGroupBy*>(top)->set_group_by_outline_info(false, false, use_part_sort);
}
} else if (use_part_sort &&
OB_FAIL(create_hash_sortkey(part_cnt, sort_keys, hash_sortkey))) {
LOG_WARN("failed to create hash sort key", K(ret), K(part_cnt), K(sort_keys));
} else {
// allocate push down group by
if (groupby_helper.can_basic_pushdown_) {
/*
* create table t1(a int, b int, c int) partition by hash(a) partitions 4;
* select sum(a) from t1 group by c;
* push group by and aggregation
*/
/* If the child has not allocated exchange,
then the down-pressed group by operator must be full partition wise,
and the gi operator can be allocated on the group by operator.
At this time, the child's order is globally ordered.*/
bool should_pullup_gi = false;
bool top_is_local_order = false;
bool is_partition_gi = false;
if (OB_FAIL(check_can_pullup_gi(*top,
is_partition_wise,
need_sort || sort_exprs.empty(),
should_pullup_gi))) {
LOG_WARN("failed to check can pullup gi", K(ret));
} else if (OB_FALSE_IT(is_partition_gi = top->is_partition_wise())) {
} else if (OB_FALSE_IT(top_is_local_order = top->get_is_local_order() && !should_pullup_gi)) {
} else if ((need_sort || top_is_local_order) && !sort_exprs.empty() &&
OB_FAIL(allocate_sort_as_top(top,
sort_keys,
top_is_local_order ? 0 : prefix_pos,
!need_sort && top_is_local_order,
nullptr,
is_fetch_with_ties,
use_part_sort ? &hash_sortkey : NULL))) {
LOG_WARN("failed to allcoate sort as top", K(ret));
} else if (OB_FAIL(allocate_group_by_as_top(top,
MERGE_AGGREGATE,
sort_exprs,
dummy_exprs,
aggr_items,
dummy_exprs,
is_from_povit,
groupby_helper.group_ndv_,
origin_child_card,
should_pullup_gi,
true,
is_partition_gi))) {
LOG_WARN("failed to allocate group by as top", K(ret));
} else if (OB_FAIL(allocate_topk_for_merge_group_plan(top))) {// allocate top-k
LOG_WARN("failed to allocate topk for merge group plan", K(ret));
} else {
need_sort = (need_sort && part_cnt > 0) ? true : false;
prefix_pos = 0;
}
}
// allocate final sort and group by
if (OB_SUCC(ret)) {
ObExchangeInfo exch_info;
if (OB_FAIL(get_grouping_style_exchange_info(group_by_exprs,
top->get_output_equal_sets(),
exch_info))) {
LOG_WARN("failed to get grouping style exchange info", K(ret));
} else if (OB_FAIL(allocate_sort_and_exchange_as_top(top,
exch_info,
sort_keys,
need_sort,
prefix_pos,
top->get_is_local_order(),
nullptr,
is_fetch_with_ties,
use_part_sort ? &hash_sortkey : NULL))) {
LOG_WARN("failed to allocate sort as top", K(ret));
} else if (OB_FAIL(allocate_group_by_as_top(top,
MERGE_AGGREGATE,
adjusted_group_by_exprs,
rollup_exprs,
aggr_items,
having_exprs,
is_from_povit,
groupby_helper.group_ndv_,
origin_child_card))) {
LOG_WARN("failed to allocate group by as top", K(ret));
} else {
static_cast<ObLogGroupBy*>(top)->set_group_by_outline_info(false,
groupby_helper.can_basic_pushdown_, use_part_sort);
}
}
}
return ret;
}
int ObSelectLogPlan::generate_merge_group_sort_keys(ObLogicalOperator *top,
const ObIArray<ObRawExpr*> &group_by_exprs,
const ObIArray<ObOrderDirection> &group_directions,
const ObIArray<ObRawExpr*> &rollup_exprs,
const ObIArray<ObOrderDirection> &rollup_directions,
ObIArray<ObRawExpr*> &sort_exprs,
ObIArray<ObOrderDirection> &sort_directions)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_UNLIKELY(group_by_exprs.count() != group_directions.count()) ||
OB_UNLIKELY(rollup_exprs.count() != rollup_directions.count())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected array count", K(group_by_exprs.count()), K(group_directions.count()),
K(rollup_exprs.count()), K(rollup_directions.count()), K(ret));
} else if (OB_FAIL(append(sort_exprs, group_by_exprs)) ||
OB_FAIL(append(sort_directions, group_directions))) {
LOG_WARN("failed to append sort keys", K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < rollup_exprs.count(); i++) {
bool is_const = false;
ObRawExpr *expr = NULL;
if (OB_ISNULL(expr = rollup_exprs.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (T_FUN_SYS_REMOVE_CONST == expr->get_expr_type()) {
/*do nothing*/
} else if (OB_FAIL(ObOptimizerUtil::is_const_expr(expr,
top->get_output_equal_sets(),
top->get_output_const_exprs(),
get_onetime_query_refs(),
is_const))) {
LOG_WARN("failed to check whether expr is const", K(ret));
} else if (is_const) {
/*do nothing*/
} else if (OB_FAIL(sort_exprs.push_back(expr)) ||
OB_FAIL(sort_directions.push_back(rollup_directions.at(i)))) {
LOG_WARN("failed to push back expr", K(ret));
} else { /*do nothing*/ }
}
}
return ret;
}
int ObSelectLogPlan::allocate_topk_for_merge_group_plan(ObLogicalOperator *&top)
{
int ret = OB_SUCCESS;
bool need_sort = false;
int64_t prefix_pos = 0;
ObSEArray<ObRawExpr*, 16> order_by_exprs;
ObSEArray<ObOrderDirection, 16> directions;
const ObSelectStmt *select_stmt = NULL;
if (OB_ISNULL(top) || OB_ISNULL(select_stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(top), K(select_stmt), K(ret));
} else if (!select_stmt->is_match_topk()) {
/*do nothing*/
} else if (OB_FAIL(get_order_by_exprs(top, order_by_exprs, &directions))) {
LOG_WARN("failed to get order by exprs", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::check_need_sort(order_by_exprs,
&directions,
top->get_op_ordering(),
top->get_fd_item_set(),
top->get_output_equal_sets(),
top->get_output_const_exprs(),
get_onetime_query_refs(),
top->get_is_at_most_one_row(),
need_sort,
prefix_pos))) {
LOG_WARN("failed to check need sort", K(ret));
} else {
if (need_sort) {
ObSEArray<OrderItem, 8> sort_keys;
ObSEArray<OrderItem, 8> topk_sort_keys;
if (OB_FAIL(make_order_items(order_by_exprs, directions, sort_keys))) {
LOG_WARN("failed to make order items", K(ret));
} else if (OB_FAIL(clone_sort_keys_for_topk(sort_keys,
topk_sort_keys))) {
LOG_WARN("failed to clone sort keys for topk", K(ret));
} else if (OB_FAIL(allocate_sort_as_top(top, topk_sort_keys))) {
LOG_WARN("failed to allocate sort as top", K(ret));
} else { /*do nothing*/}
} else {
if (OB_FAIL(allocate_material_as_top(top))) {
LOG_WARN("failed to allocate material as top", K(ret));
} else { /*do nothing*/ }
}
if (OB_SUCC(ret)) {
const ObGlobalHint &global_hint = get_optimizer_context().get_global_hint();
if (OB_FAIL(allocate_topk_as_top(top,
select_stmt->get_limit_expr(),
select_stmt->get_offset_expr(),
global_hint.sharding_minimum_row_count_,
global_hint.topk_precision_))) {
LOG_WARN("failed to allocate topk as top", K(ret));
} else { /*do nothing*/ }
}
}
return ret;
}
int ObSelectLogPlan::allocate_window_function_as_top(const ObIArray<ObWinFunRawExpr *> &win_exprs,
const bool match_parallel,
const bool is_partition_wise,
const int32_t role_type,
const ObIArray<OrderItem> &sort_keys,
const int64_t range_dist_keys_cnt,
const int64_t range_dist_pby_prefix,
ObLogicalOperator *&top,
ObOpPseudoColumnRawExpr *wf_aggr_status_expr,
const ObIArray<bool> &pushdown_info)
{
int ret = OB_SUCCESS;
ObLogWindowFunction *window_function = NULL;
if (OB_ISNULL(top)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("get unexpected null", K(ret), K(top));
} else if (OB_ISNULL(window_function = static_cast<ObLogWindowFunction *>(
get_log_op_factory().allocate(*this, LOG_WINDOW_FUNCTION)))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("allocate memory for ObLogWindowFunction failed", K(ret));
} else if (OB_FAIL(append(window_function->get_window_exprs(), win_exprs))) {
LOG_WARN("failed to add window expr", K(ret));
} else if (OB_FAIL(window_function->set_sort_keys(sort_keys))) {
LOG_WARN("set range distribution sort keys failed", K(ret));
} else {
window_function->set_rd_sort_keys_cnt(range_dist_keys_cnt);
window_function->set_single_part_parallel(match_parallel);
window_function->set_is_partition_wise(is_partition_wise);
window_function->set_child(ObLogicalOperator::first_child, top);
window_function->set_role_type(ObLogWindowFunction::WindowFunctionRoleType(role_type));
if (range_dist_keys_cnt > 0) {
window_function->set_ragne_dist_parallel(true);
window_function->set_rd_pby_sort_cnt(range_dist_pby_prefix);
}
if (ObLogWindowFunction::WindowFunctionRoleType::PARTICIPATOR == role_type) {
window_function->set_aggr_status_expr(wf_aggr_status_expr);
}
if (window_function->is_push_down()
&& OB_FAIL(window_function->set_pushdown_info(pushdown_info))) {
LOG_WARN("set_pushdown_info failed", K(ret));
} else if (OB_FAIL(window_function->compute_property())) {
LOG_WARN("failed to compute property", K(ret));
} else {
top = window_function;
}
}
return ret;
}
int ObSelectLogPlan::candi_allocate_distinct()
{
int ret = OB_SUCCESS;
bool is_unique = false;
const ObSelectStmt *stmt = NULL;
ObLogicalOperator *best_plan = NULL;
ObSEArray<ObRawExpr*, 8> reduce_exprs;
ObSEArray<ObRawExpr*, 8> distinct_exprs;
ObSEArray<ObRawExpr*, 8> candi_subquery_exprs;
if (OB_ISNULL(stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(candidates_.get_best_plan(best_plan))) {
LOG_WARN("failed to get current best plan", K(ret));
} else if (OB_ISNULL(best_plan)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(get_distinct_exprs(best_plan, reduce_exprs, distinct_exprs))) {
LOG_WARN("failed to get select columns", K(ret));
} else if (OB_FAIL(candi_allocate_subplan_filter_for_exprs(distinct_exprs))) {
LOG_WARN("failed to allocate subplan filter for exprs", K(ret));
} else if (distinct_exprs.empty()) {
// if all the distinct exprs are const, we add limit operator instead of distinct operator
ObConstRawExpr *limit_expr = NULL;
if (OB_FAIL(ObRawExprUtils::build_const_int_expr(get_optimizer_context().get_expr_factory(),
ObIntType,
1,
limit_expr))) {
LOG_WARN("failed to create const expr", K(ret));
} else if (OB_FAIL(candi_allocate_limit(limit_expr))) {
LOG_WARN("failed to allocate limit operator", K(ret));
} else {
OPT_TRACE("distinct exprs is const, need limit op instead of distinct op");
LOG_TRACE("distinct exprs is const, need limit op instead of distinct op", K(distinct_exprs));
}
} else if (OB_FAIL(ObOptimizerUtil::is_exprs_unique(distinct_exprs,
best_plan->get_table_set(),
best_plan->get_fd_item_set(),
best_plan->get_output_equal_sets(),
best_plan->get_output_const_exprs(),
is_unique))) {
LOG_WARN("failed to check whether distinct exprs is unique", K(ret));
} else if (is_unique) {
OPT_TRACE("distinct exprs is unique, no need distinct");
LOG_TRACE("distinct exprs is unique, no need distinct", K(distinct_exprs));
} else {
SMART_VAR(GroupingOpHelper, distinct_helper) {
CandidatePlan candidate_plan;
ObSEArray<CandidatePlan, 4> distinct_plans;
ObSEArray<ObOrderDirection, 4> distinct_directions;
if (OB_FAIL(init_distinct_helper(distinct_exprs, distinct_helper))) {
LOG_WARN("failed to init distinct helper", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::get_default_directions(distinct_exprs.count(),
distinct_directions))) {
LOG_WARN("failed to generate default directions", K(ret));
}
// create hash distinct
if (OB_SUCC(ret) && !distinct_helper.force_use_merge_) {
ObSEArray<CandidatePlan, 16> best_candidates;
if (OB_FAIL(get_minimal_cost_candidates(candidates_.candidate_plans_, best_candidates))) {
LOG_WARN("failed to get minimal cost candidates", K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < best_candidates.count(); i++) {
candidate_plan = best_candidates.at(i);
if (OB_FAIL(create_hash_distinct_plan(candidate_plan.plan_tree_,
distinct_helper,
reduce_exprs,
distinct_exprs))) {
LOG_WARN("failed to create hash distinct plan", K(ret));
} else if (OB_FAIL(distinct_plans.push_back(candidate_plan))) {
LOG_WARN("failed to push back hash distinct candidate plan", K(ret));
} else { /*do nothing*/ }
}
}
}
//create merge distinct plan
if (OB_SUCC(ret) && !distinct_helper.force_use_hash_) {
bool can_ignore_merge_plan = !(distinct_plans.empty() || distinct_helper.force_use_merge_);
bool is_plan_valid = false;
for(int64_t i = 0; OB_SUCC(ret) && i < candidates_.candidate_plans_.count(); i++) {
candidate_plan = candidates_.candidate_plans_.at(i);
if (OB_FAIL(create_merge_distinct_plan(candidate_plan.plan_tree_,
distinct_helper,
reduce_exprs,
distinct_exprs,
distinct_directions,
is_plan_valid,
can_ignore_merge_plan))) {
LOG_WARN("failed to allocate merge distinct plan", K(ret));
} else if (is_plan_valid && OB_FAIL(distinct_plans.push_back(candidate_plan))) {
LOG_WARN("failed to add merge distinct candidate plan", K(ret));
} else { /*do nothing*/ }
}
}
if (OB_SUCC(ret)) {
int64_t check_scope = OrderingCheckScope::CHECK_SET | OrderingCheckScope::CHECK_ORDERBY;
if (OB_FAIL(update_plans_interesting_order_info(distinct_plans, check_scope))) {
LOG_WARN("failed to update plans interesting order info", K(ret));
} else if (OB_FAIL(prune_and_keep_best_plans(distinct_plans))) {
LOG_WARN("Failed to add plans", K(ret));
} else { /* do nothing*/ }
}
}
}
return ret;
}
int ObSelectLogPlan::get_distinct_exprs(const ObLogicalOperator *top,
ObIArray<ObRawExpr *> &reduce_exprs,
ObIArray<ObRawExpr *> &distinct_exprs)
{
int ret = OB_SUCCESS;
const ObSelectStmt *select_stmt = get_stmt();
reduce_exprs.reuse();
if (OB_ISNULL(select_stmt) || OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(select_stmt), K(top), K(ret));
} else {
ObRawExpr *select_expr = NULL;
ObSEArray<ObRawExpr *, 8> new_distinct_exprs;
for (int64_t i = 0; OB_SUCC(ret) && i < select_stmt->get_select_item_size(); ++i) {
bool is_const = false;
select_expr = select_stmt->get_select_item(i).expr_;
if (OB_ISNULL(select_expr)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::is_const_expr(select_expr,
top->get_output_equal_sets(),
top->get_output_const_exprs(),
get_onetime_query_refs(),
is_const))) {
LOG_WARN("failed to check whether is const expr", K(ret));
} else if (is_const) {
//skip it
} else if (OB_FAIL(reduce_exprs.push_back(select_expr))) {
LOG_WARN("push expr to distinct exprs failed", K(ret));
} else { /*do nothing*/ }
}
if (OB_FAIL(ret)) {
/*do nothing*/
} else if (OB_FAIL(ObOptimizerUtil::simplify_exprs(top->get_fd_item_set(),
top->get_output_equal_sets(),
top->get_output_const_exprs(),
reduce_exprs,
distinct_exprs))) {
LOG_WARN("failed to simplify exprs", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::get_minset_of_exprs(distinct_exprs,new_distinct_exprs))) {
LOG_WARN("fail to get minset of distinct exprs", K(ret));
} else if (new_distinct_exprs.count() >= distinct_exprs.count()) {
//do nothing
} else if (OB_FAIL(distinct_exprs.assign(new_distinct_exprs))) {
LOG_WARN("fail to assign new distinct expr", K(ret));
}
}
return ret;
}
int ObSelectLogPlan::create_hash_distinct_plan(ObLogicalOperator *&top,
GroupingOpHelper &distinct_helper,
ObIArray<ObRawExpr*> &reduce_exprs,
ObIArray<ObRawExpr*> &distinct_exprs)
{
int ret = OB_SUCCESS;
bool is_partition_wise = false;
ObExchangeInfo exch_info;
OPT_TRACE("start generate hash distinct plan");
if (OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(top), K(ret));
} else if (top->is_distributed() &&
OB_FAIL(top->check_sharding_compatible_with_reduce_expr(reduce_exprs,
is_partition_wise))) {
LOG_WARN("failed to check sharding compatible with reduce expr", K(ret));
} else if (!top->is_distributed() || is_partition_wise) {
OPT_TRACE("is basic distinct:", !top->is_distributed());
OPT_TRACE("is partition wise distinct", is_partition_wise);
if (OB_FAIL(allocate_distinct_as_top(top,
AggregateAlgo::HASH_AGGREGATE,
distinct_exprs,
distinct_helper.group_ndv_,
is_partition_wise))) {
LOG_WARN("failed to allocate distinct as top", K(ret));
}
} else if (distinct_helper.can_basic_pushdown_ && //allocate push down distinct if necessary
OB_FAIL(allocate_distinct_as_top(top,
AggregateAlgo::HASH_AGGREGATE,
distinct_exprs,
distinct_helper.group_ndv_,
false,
true))) {
LOG_WARN("failed to allocate distinct as top", K(ret));
} else if (OB_FAIL(get_grouping_style_exchange_info(distinct_exprs,
top->get_output_equal_sets(),
exch_info))) {
LOG_WARN("failed to get grouping style exchange info", K(ret));
} else if (OB_FAIL(allocate_exchange_as_top(top, exch_info))) {
LOG_WARN("failed to allocate exchange as top", K(ret));
} else if (OB_FAIL(allocate_distinct_as_top(top, // allocate final distinct
AggregateAlgo::HASH_AGGREGATE,
distinct_exprs,
distinct_helper.group_ndv_))) {
LOG_WARN("failed to allocate distinct as top", K(ret));
} else { /*do nothing*/ }
return ret;
}
int ObSelectLogPlan::create_merge_distinct_plan(ObLogicalOperator *&top,
GroupingOpHelper &distinct_helper,
ObIArray<ObRawExpr*> &reduce_exprs,
ObIArray<ObRawExpr*> &distinct_exprs,
ObIArray<ObOrderDirection> &directions,
bool &is_plan_valid,
bool can_ignore_merge_plan)
{
int ret = OB_SUCCESS;
int64_t prefix_pos = 0;
bool need_sort = false;
bool is_partition_wise = false;
ObSEArray<OrderItem, 4> sort_keys;
int64_t interesting_order_info = OrderingFlag::NOT_MATCH;
is_plan_valid = true;
OPT_TRACE("start generate merge distinct plan");
if (OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(adjust_sort_expr_ordering(distinct_exprs,
directions,
*top,
false))) {
LOG_WARN("failed to adjust sort expr ordering", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::check_need_sort(distinct_exprs,
&directions,
top->get_op_ordering(),
top->get_fd_item_set(),
top->get_output_equal_sets(),
top->get_output_const_exprs(),
get_onetime_query_refs(),
top->get_is_at_most_one_row(),
need_sort,
prefix_pos))) {
LOG_WARN("failed to check need sort", K(ret));
} else if (OB_FAIL(make_order_items(distinct_exprs, &directions, sort_keys))) {
LOG_WARN("failed to make order items from exprs", K(ret));
} else if (can_ignore_merge_plan && OB_FAIL(ObOptimizerUtil::compute_stmt_interesting_order(sort_keys,
get_stmt(),
false,
const_cast<EqualSets &>(top->get_output_equal_sets()),
top->get_output_const_exprs(),
get_is_parent_set_distinct(),
OrderingCheckScope::CHECK_SET | OrderingCheckScope::CHECK_ORDERBY,
interesting_order_info))) {
LOG_WARN("failed to compute stmt interesting order", K(ret));
} else if (need_sort && can_ignore_merge_plan && OrderingFlag::NOT_MATCH == interesting_order_info) {
// if no further order needed, not generate merge style distinct
is_plan_valid = false;
} else if (top->is_distributed() &&
OB_FAIL(top->check_sharding_compatible_with_reduce_expr(reduce_exprs,
is_partition_wise))) {
LOG_WARN("failed to check sharding compatiable with reduce exprs", K(ret));
} else if (!top->is_distributed() || is_partition_wise) {
OPT_TRACE("is basic distinct:", !top->is_distributed());
OPT_TRACE("is partition wise distinct", is_partition_wise);
if (OB_FAIL(try_allocate_sort_as_top(top, sort_keys, need_sort, prefix_pos))) {
LOG_WARN("failed to allocate sort operator", K(ret));
} else if (OB_FAIL(allocate_distinct_as_top(top,
MERGE_AGGREGATE,
distinct_exprs,
distinct_helper.group_ndv_,
is_partition_wise))) {
LOG_WARN("failed to allocate distinct as top", K(ret));
}
} else {
// allocate push down distinct if necessary
if (distinct_helper.can_basic_pushdown_) {
/* If the child has not allocated exchange,
then the down-pressed distinct operator must be full partition wise,
and the gi operator can be allocated on the distinct operator.
At this time, the child's order is globally ordered.*/
OPT_TRACE("generate pushdown distinct plan");
bool should_pullup_gi = false;
bool top_is_local_order = false;
if (OB_FAIL(check_can_pullup_gi(*top, false, need_sort, should_pullup_gi))) {
LOG_WARN("failed to check can pullup gi", K(ret));
} else if (OB_FALSE_IT(top_is_local_order = top->get_is_local_order() && !should_pullup_gi)) {
} else if ((need_sort || top_is_local_order) &&
OB_FAIL(allocate_sort_as_top(top,
sort_keys,
need_sort && top_is_local_order ? 0 : prefix_pos,
!need_sort && top_is_local_order))) {
LOG_WARN("failed to allocate sort as top", K(ret));
} else if (OB_FAIL(allocate_distinct_as_top(top,
MERGE_AGGREGATE,
distinct_exprs,
distinct_helper.group_ndv_,
should_pullup_gi,
true))) {
LOG_WARN("failed to allocate distinct as top", K(ret));
} else {
prefix_pos = 0;
need_sort = false;
}
}
// allocate final distinct if necessary
if (OB_SUCC(ret)) {
ObExchangeInfo exch_info;
if (OB_FAIL(get_grouping_style_exchange_info(distinct_exprs,
top->get_output_equal_sets(),
exch_info))) {
LOG_WARN("failed to get grouping style exchange info", K(ret));
} else if (OB_FAIL(allocate_sort_and_exchange_as_top(top,
exch_info,
sort_keys,
need_sort,
prefix_pos,
top->get_is_local_order()))) {
LOG_WARN("failed to allocate operator for join style op", K(ret));
} else if (OB_FAIL(allocate_distinct_as_top(top,
MERGE_AGGREGATE,
distinct_exprs,
distinct_helper.group_ndv_))) {
LOG_WARN("failed to allocate distinct as top", K(ret));
} else { /*do nothing*/ }
}
}
return ret;
}
int ObSelectLogPlan::allocate_distinct_as_top(ObLogicalOperator *&top,
const AggregateAlgo algo,
const ObIArray<ObRawExpr*> &distinct_exprs,
const double total_ndv,
const bool is_partition_wise,
const bool is_pushed_down)
{
int ret = OB_SUCCESS;
ObLogDistinct *distinct_op = NULL;
if (OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(top), K(ret));
} else if (OB_ISNULL(distinct_op = static_cast<ObLogDistinct*>(get_log_op_factory().
allocate(*this, LOG_DISTINCT)))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("failed to allocate distinct operator", K(ret));
} else {
distinct_op->set_child(ObLogicalOperator::first_child, top);
distinct_op->set_algo_type(algo);
distinct_op->set_push_down(is_pushed_down);
distinct_op->set_total_ndv(total_ndv);
distinct_op->set_is_partition_wise(is_partition_wise);
distinct_op->set_force_push_down(FORCE_GPD & get_optimizer_context().get_aggregation_optimization_settings());
if (OB_FAIL(distinct_op->set_distinct_exprs(distinct_exprs))) {
LOG_WARN("failed to set group by columns", K(ret));
} else if (OB_FAIL(distinct_op->compute_property())) {
LOG_WARN("failed to compute property", K(ret));
} else {
top = distinct_op;
}
}
return ret;
}
int ObSelectLogPlan::generate_raw_plan_for_set()
{
int ret = OB_SUCCESS;
const ObSelectStmt *select_stmt = get_stmt();
ObSEArray<ObSelectLogPlan*, 2> child_plans;
ObSEArray<ObRawExpr *, 8> remain_filters;
ObSQLSessionInfo *session_info = NULL;
ObRawExprFactory *expr_factory = NULL;
if (OB_ISNULL(select_stmt) ||
OB_ISNULL(session_info = get_optimizer_context().get_session_info()) ||
OB_ISNULL(expr_factory = &get_optimizer_context().get_expr_factory())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null stmt", K(select_stmt), K(ret));
} else if (OB_UNLIKELY(!select_stmt->is_set_stmt()) ||
OB_UNLIKELY(2 > select_stmt->get_set_query().count())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected set_op stmt", K(ret));
} else {
const ObIArray<ObSelectStmt*> &child_stmts = select_stmt->get_set_query();
const int64_t child_size = child_stmts.count();
const bool is_set_distinct = select_stmt->is_set_distinct();
ObSEArray<ObRawExpr *, 8> child_input_filters;
ObSEArray<ObRawExpr *, 8> child_candi_filters;
ObSEArray<ObRawExpr *, 8> child_rename_filters;
ObSEArray<ObRawExpr *, 8> child_remain_filters;
bool can_pushdown = false;
const ObSelectStmt *child_stmt = NULL;
ObSelectLogPlan *child_plan = NULL;
for (int64 i = 0; OB_SUCC(ret) && i < child_size; ++i) {
child_input_filters.reuse();
child_candi_filters.reuse();
child_rename_filters.reuse();
child_remain_filters.reuse();
if (OB_ISNULL(child_stmt = child_stmts.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null stmt", K(child_stmt), K(ret));
} else if (!pushdown_filters_.empty() &&
OB_FAIL(child_input_filters.assign(pushdown_filters_))) {
LOG_WARN("failed to copy exprs", K(ret));
} else if (!child_input_filters.empty() &&
OB_FAIL(ObOptimizerUtil::pushdown_filter_into_subquery(*select_stmt,
*child_stmt,
get_optimizer_context(),
child_input_filters,
child_candi_filters,
child_remain_filters,
can_pushdown))) {
LOG_WARN("pushdown filters into left query failed", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::get_set_op_remain_filter(*select_stmt,
child_remain_filters,
remain_filters,
0 == i))) {
LOG_WARN("get remain filters failed", K(ret));
} else if (!child_candi_filters.empty() &&
OB_FAIL(ObOptimizerUtil::rename_pushdown_filter(*select_stmt, *child_stmt,
OB_INVALID, session_info,
*expr_factory,
child_candi_filters,
child_rename_filters))) {
LOG_WARN("failed to rename pushdown filter", K(ret));
} else if (OB_FAIL(generate_child_plan_for_set(child_stmt, child_plan,
child_rename_filters, i,
select_stmt->is_set_distinct()))) {
LOG_WARN("failed to generate left subquery plan", K(ret));
} else if (OB_FAIL(child_plans.push_back(child_plan))) {
LOG_WARN("failed to push back", K(ret));
}
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(candi_allocate_set(child_plans))) {
LOG_WARN("failed to allocate set op", K(ret));
} else if (!remain_filters.empty() && OB_FAIL(candi_allocate_filter(remain_filters))) {
LOG_WARN("failed to allocate filter", K(ret));
} else if (OB_FAIL(allocate_plan_top())) {
LOG_WARN("failed to allocate plan top", K(ret));
} else { /*do nothing*/ }
}
return ret;
}
int ObSelectLogPlan::candi_allocate_set(const ObIArray<ObSelectLogPlan*> &child_plans)
{
int ret = OB_SUCCESS;
const ObSelectStmt *select_stmt = NULL;
ObSEArray<CandidatePlan, 4> all_plans;
if (OB_ISNULL(select_stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret));
} else if (select_stmt->is_recursive_union()) {
// generate recursive union all plans
if (OB_FAIL(candi_allocate_recursive_union_all(child_plans))) {
LOG_WARN("failed to allocate recursive union all", K(ret));
} else { /*do nothing*/ }
} else if (ObSelectStmt::UNION == select_stmt->get_set_op() &&
(!select_stmt->is_set_distinct() || child_plans.count() > 2)) {
// generate union all or union distinct with more than two children plans
if (OB_FAIL(candi_allocate_union_all(child_plans))) {
LOG_WARN("failed to allocate union all", K(ret));
} else if (select_stmt->is_set_distinct() &&
OB_FAIL(candi_allocate_distinct())) {
LOG_WARN("failed to allocate distinct", K(ret));
} else { /*do nothing*/ }
} else if (OB_FAIL(candi_allocate_distinct_set(child_plans))) {
// generate plans for intersect/expect/union distinct with two children
LOG_WARN("failed to allocate distinct set op", K(ret));
} else { /*do nothing*/ }
if (OB_FAIL(ret)) {
} else if (OB_FAIL(update_set_sharding_info(child_plans))) {
LOG_WARN("failed to update set sharding info", K(ret));
} else { /*do nothing*/ }
return ret;
}
int ObSelectLogPlan::update_set_sharding_info(const ObIArray<ObSelectLogPlan*> &child_plans)
{
int ret = OB_SUCCESS;
ObSEArray<ObRawExpr*, 8> pure_set_exprs;
ObSEArray<ObRawExpr*, 8> old_exprs;
ObSEArray<ObRawExpr*, 8> new_exprs;
ObSEArray<ObRawExpr*, 8> partition_keys;
const ObSelectStmt *sel_stmt = NULL;
if (OB_ISNULL(sel_stmt = get_stmt()) || OB_UNLIKELY(!sel_stmt->is_select_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected error", K(ret));
} else if (OB_FAIL(sel_stmt->get_pure_set_exprs(pure_set_exprs))) {
LOG_WARN("failed to get pure set exprs", K(ret));
} else {
ObSEArray<ObShardingInfo*, 8> dist_shardings;
for (int64_t i = 0; OB_SUCC(ret) && i < candidates_.candidate_plans_.count(); i++) {
ObShardingInfo *sharding = NULL;
ObLogicalOperator *root = NULL;
if (OB_ISNULL(root = candidates_.candidate_plans_.at(i).plan_tree_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(root), K(ret));
} else {
sharding = root->get_strong_sharding();
if (NULL != sharding && sharding->is_distributed_with_partitioning() &&
OB_FAIL(add_var_to_array_no_dup(dist_shardings, sharding))) {
LOG_WARN("failed to push back sharding", K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < root->get_weak_sharding().count(); i++) {
if (OB_ISNULL(sharding = root->get_weak_sharding().at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (sharding->is_distributed_with_partitioning() &&
OB_FAIL(add_var_to_array_no_dup(dist_shardings, sharding))) {
LOG_WARN("failed to push back sharding info", K(ret));
} else { /*do nothing*/ }
}
}
}
}
for (int64_t i = 0; OB_SUCC(ret) && i < dist_shardings.count(); i++) {
ObShardingInfo *sharding = NULL;
partition_keys.reuse();
if (OB_ISNULL(sharding = dist_shardings.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(sharding->get_all_partition_keys(partition_keys))) {
LOG_WARN("failed to get partition keys", K(ret));
} else {
for (int64_t j = 0; OB_SUCC(ret) && j < child_plans.count(); j++) {
const ObSelectStmt *child_stmt = NULL;
ObSelectLogPlan *child_log_plan = NULL;
ObLogicalOperator *child_best_plan = NULL;
ObRawExprCopier copier(get_optimizer_context().get_expr_factory());
old_exprs.reuse();
new_exprs.reuse();
if (OB_ISNULL(child_log_plan = child_plans.at(j)) ||
OB_ISNULL(child_stmt = child_log_plan->get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(child_log_plan), K(child_stmt), K(ret));
} else if (OB_FAIL(child_log_plan->candidates_.get_best_plan(child_best_plan))) {
LOG_WARN("failed to get best plan", K(ret));
} else if (OB_ISNULL(child_best_plan)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(child_stmt->get_select_exprs(old_exprs))) {
LOG_WARN("failed to get select exprs", K(ret));
} else if (OB_FAIL(new_exprs.assign(pure_set_exprs))) {
LOG_WARN("failed to find new exprs", K(ret));
} else if (OB_UNLIKELY(old_exprs.count() != new_exprs.count())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected array count", K(new_exprs.count()),
K(new_exprs.count()), K(ret));
} else {
for (int64_t k = 0; OB_SUCC(ret) && k < partition_keys.count(); k++) {
int64_t idx = -1;
ObRawExpr *expr = NULL;
if (OB_ISNULL(expr = partition_keys.at(k))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (ObOptimizerUtil::find_item(old_exprs, expr)) {
/*do nothing*/
} else if (!ObOptimizerUtil::find_equal_expr(old_exprs,
expr,
child_best_plan->get_output_equal_sets(),
idx)) {
/*do nothing*/
} else if (OB_UNLIKELY(idx < 0 || idx >= pure_set_exprs.count())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected array index", K(idx), K(ret));
} else if (OB_FAIL(old_exprs.push_back(expr)) ||
OB_FAIL(new_exprs.push_back(pure_set_exprs.at(idx)))) {
LOG_WARN("failed to push back exprs", K(ret));
} else { /*do nothing*/ }
}
if (OB_SUCC(ret)) {
ObArray<ObRawExpr *> new_partition_keys;
ObArray<ObRawExpr *> new_subpartition_keys;
ObArray<ObRawExpr *> new_partition_funcs;
if (OB_FAIL(copier.add_replaced_expr(old_exprs, new_exprs))) {
LOG_WARN("failed to add exprs", K(ret));
} else if (OB_FAIL(copier.copy_on_replace(sharding->get_partition_keys(),
new_partition_keys))) {
LOG_WARN("failed to copy on replace exprs", K(ret));
} else if (OB_FAIL(sharding->get_partition_keys().assign(new_partition_keys))) {
LOG_WARN("failed to assign new partition keys", K(ret));
} else if (OB_FAIL(copier.copy_on_replace(sharding->get_sub_partition_keys(),
new_subpartition_keys))) {
LOG_WARN("failed to copy on replace exprs", K(ret));
} else if (OB_FAIL(sharding->get_sub_partition_keys().assign(new_subpartition_keys))) {
LOG_WARN("failed to assign sub partition keys", K(ret));
} else if (OB_FAIL(copier.copy_on_replace(sharding->get_partition_func(),
new_partition_funcs))) {
LOG_WARN("failed to copy on replace exprs", K(ret));
} else if (OB_FAIL(sharding->get_partition_func().assign(new_partition_funcs))) {
LOG_WARN("failed to assign partition funcs", K(ret));
} else { /*do nothing*/ }
}
}
}
}
}
}
return ret;
}
int ObSelectLogPlan::candi_allocate_union_all(const ObIArray<ObSelectLogPlan*> &child_plans)
{
int ret = OB_SUCCESS;
ObSEArray<CandidatePlan, 8> all_plans;
int64_t check_scope = OrderingCheckScope::CHECK_SET | OrderingCheckScope::CHECK_ORDERBY;
if (OB_FAIL(generate_union_all_plans(child_plans, false, all_plans))) {
LOG_WARN("failed to generate union all plans", K(ret));
} else if (!all_plans.empty()) {
LOG_TRACE("succeed to allocate union all using hint", K(all_plans.count()));
} else if (OB_FAIL(get_log_plan_hint().check_status())) {
LOG_WARN("failed to generate plans with hint", K(ret));
} else if (OB_FAIL(generate_union_all_plans(child_plans, true, all_plans))) {
LOG_WARN("failed to generate union all plans", K(ret));
} else {
LOG_TRACE("succeed to allocate union all ignore hint", K(all_plans.count()));
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(update_plans_interesting_order_info(all_plans, check_scope))) {
LOG_WARN("failed to update plans interesting order info", K(ret));
} else if (OB_FAIL(prune_and_keep_best_plans(all_plans))) {
LOG_WARN("failed to add all plans", K(ret));
} else { /*do nothing*/ }
return ret;
}
int ObSelectLogPlan::generate_union_all_plans(const ObIArray<ObSelectLogPlan*> &child_plans,
const bool ignore_hint,
ObIArray<CandidatePlan> &all_plans)
{
int ret = OB_SUCCESS;
if (child_plans.count() > 2) {
ObSEArray<ObLogicalOperator*, 2> child_ops;
// get best children plan
for (int64_t i = 0; OB_SUCC(ret) && i < child_plans.count(); i++) {
ObLogicalOperator *best_plan = NULL;
if (OB_ISNULL(child_plans.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(child_plans.at(i)->get_candidate_plans().get_best_plan(best_plan))) {
LOG_WARN("failed to get best plan", K(ret));
} else if (OB_ISNULL(best_plan)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(child_ops.push_back(best_plan))) {
LOG_WARN("failed to push back child ops", K(ret));
} else { /*do nothing*/ }
}
// generate union all plan
if (OB_SUCC(ret)) {
CandidatePlan candidate_plan;
if (OB_FAIL(create_union_all_plan(child_ops, ignore_hint, candidate_plan.plan_tree_))) {
LOG_WARN("failed to create union all plan", K(ret));
} else if (NULL == candidate_plan.plan_tree_) {
/*do nothing*/
} else if (OB_FAIL(all_plans.push_back(candidate_plan))) {
LOG_WARN("failed to push back candidate plan", K(ret));
} else { /*do nothing*/ }
}
} else {
bool has_next = true;
ObSEArray<int64_t, 8> move_pos;
ObSEArray<CandidatePlan, 8> best_plan;
ObSEArray<ObSEArray<CandidatePlan, 8>, 8> best_plan_list;
ObSEArray<ObLogicalOperator*, 8> child_ops;
for (int64_t i = 0; OB_SUCC(ret) && i < child_plans.count(); i++) {
ObSelectLogPlan *select_plan = NULL;
best_plan.reuse();
if (OB_ISNULL(select_plan = child_plans.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(get_minimal_cost_candidates(select_plan->get_candidate_plans().candidate_plans_,
best_plan))) {
LOG_WARN("failed to get minimal cost plans", K(ret));
} else if (OB_UNLIKELY(best_plan.empty())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected error", K(ret));
} else if (OB_FAIL(best_plan_list.push_back(best_plan))) {
LOG_WARN("failed to push back best plan", K(ret));
} else if (OB_FAIL(move_pos.push_back(0))) {
LOG_WARN("failed to push back move pos", K(ret));
} else { /*do nothing*/ }
}
while (OB_SUCC(ret) && has_next) {
child_ops.reuse();
// get child ops to generate plan
for (int64_t i = 0; OB_SUCC(ret) && i < move_pos.count(); i++) {
ObLogicalOperator *child_op = NULL;
int64_t size = best_plan_list.at(i).count();
if (OB_UNLIKELY(move_pos.at(i) < 0 || move_pos.at(i) >= size)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected array count", K(size), K(move_pos.at(i)), K(ret));
} else if (OB_ISNULL(child_op = best_plan_list.at(i).at(move_pos.at(i)).plan_tree_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(child_ops.push_back(child_op))) {
LOG_WARN("failed to push back child ops", K(ret));
} else { /*do nothing*/ }
}
// generate union all plan
if (OB_SUCC(ret)) {
CandidatePlan candidate_plan;
if (OB_FAIL(create_union_all_plan(child_ops, ignore_hint, candidate_plan.plan_tree_))) {
LOG_WARN("failed to create union all plan", K(ret));
} else if (NULL == candidate_plan.plan_tree_) {
/*do nothing*/
} else if (OB_FAIL(all_plans.push_back(candidate_plan))) {
LOG_WARN("failed to push back candidate plan", K(ret));
} else { /*do nothing*/ }
}
// reset pos for next generation
if (OB_SUCC(ret)) {
has_next = false;
for (int64_t i = move_pos.count() - 1; !has_next && OB_SUCC(ret) && i >= 0; i--) {
if (move_pos.at(i) < best_plan_list.at(i).count() - 1) {
++move_pos.at(i);
has_next = true;
for (int64_t j = i + 1; j < move_pos.count(); j++) {
move_pos.at(j) = 0;
}
}
}
}
}
}
return ret;
}
int ObSelectLogPlan::create_union_all_plan(const ObIArray<ObLogicalOperator*> &child_ops,
const bool ignore_hint,
ObLogicalOperator *&top)
{
int ret = OB_SUCCESS;
ObSEArray<ObLogicalOperator*, 8> set_child_ops;
top = NULL;
const ObSelectStmt* select_stmt = NULL;
uint64_t set_dist_methods = DistAlgo::DIST_BASIC_METHOD
| DistAlgo::DIST_PARTITION_WISE
| DistAlgo::DIST_SET_PARTITION_WISE
| DistAlgo::DIST_EXT_PARTITION_WISE
| DistAlgo::DIST_PULL_TO_LOCAL
| DistAlgo::DIST_SET_RANDOM;
int64_t random_none_idx = OB_INVALID_INDEX;
bool is_partition_wise = false;
bool is_ext_partition_wise = false;
bool is_set_partition_wise = false;
DistAlgo hint_dist_methods = get_log_plan_hint().get_valid_set_dist_algo(&random_none_idx);
if (OB_ISNULL(select_stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected error", K(select_stmt), K(ret));
} else if (!get_optimizer_context().is_var_assign_only_in_root_stmt() &&
get_optimizer_context().has_var_assign()) {
set_dist_methods &= DistAlgo::DIST_PULL_TO_LOCAL | DistAlgo::DIST_BASIC_METHOD;
} else if (!ignore_hint && DistAlgo::DIST_INVALID_METHOD != hint_dist_methods) {
set_dist_methods &= hint_dist_methods;
} else {
random_none_idx = OB_INVALID_INDEX;
}
if (OB_FAIL(ret)) {
//do nothing
} else if (!ignore_hint && DistAlgo::DIST_INVALID_METHOD != hint_dist_methods) {
//do nothing
} else if (select_stmt->is_set_stmt() && !select_stmt->is_set_distinct() && get_optimizer_context().force_serial_set_order()) {
//for union all to keep child branches execute serially from left to right
set_dist_methods &= (DistAlgo::DIST_PULL_TO_LOCAL | DistAlgo::DIST_BASIC_METHOD);
}
OPT_TRACE("start create unoin all plan");
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_BASIC_METHOD)) {
bool is_basic = false;
OPT_TRACE("check match basic method");
if (OB_FAIL(ObOptimizerUtil::check_basic_sharding_info(get_optimizer_context().get_local_server_addr(),
child_ops,
is_basic))) {
LOG_WARN("failed to check basic sharding info", K(ret));
} else if (!is_basic) {
set_dist_methods &= ~DIST_BASIC_METHOD;
OPT_TRACE("will not use basic method");
} else if (OB_FAIL(set_child_ops.assign(child_ops))) {
LOG_WARN("failed to assign child ops", K(ret));
} else {
set_dist_methods = DistAlgo::DIST_BASIC_METHOD;
OPT_TRACE("will use basic method, ignore other method");
}
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_PARTITION_WISE)) {
OPT_TRACE("check match partition wise");
if (OB_FAIL(check_if_union_all_match_partition_wise(child_ops, is_partition_wise))) {
LOG_WARN("failed to check if union all match partition wise", K(ret));
} else if (!is_partition_wise) {
set_dist_methods &= ~DIST_PARTITION_WISE;
OPT_TRACE("will not use partition wise");
} else if (OB_FAIL(set_child_ops.assign(child_ops))) {
LOG_WARN("failed to assign child ops", K(ret));
} else {
set_dist_methods = DistAlgo::DIST_PARTITION_WISE;
OPT_TRACE("will use partition wise, ignore other method");
}
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_EXT_PARTITION_WISE)) {
OPT_TRACE("check match extend partition wise");
if (OB_FAIL(check_if_union_all_match_extended_partition_wise(child_ops, is_ext_partition_wise))) {
LOG_WARN("failed to check if union all match extended partition wise", K(ret));
} else if (!is_ext_partition_wise) {
set_dist_methods &= ~DIST_EXT_PARTITION_WISE;
OPT_TRACE("will not use extend partition wise");
} else if (OB_FAIL(set_child_ops.assign(child_ops))) {
LOG_WARN("failed to assign child ops", K(ret));
} else {
set_dist_methods = DistAlgo::DIST_EXT_PARTITION_WISE;
OPT_TRACE("will use extend partition wise, ignore other method");
}
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_SET_PARTITION_WISE)) {
OPT_TRACE("check match set partition wise");
if (OB_FAIL(check_if_union_all_match_set_partition_wise(child_ops, is_set_partition_wise))) {
LOG_WARN("failed to check if union all match set partition wise", K(ret));
} else if (!is_set_partition_wise) {
set_dist_methods &= ~DIST_SET_PARTITION_WISE;
OPT_TRACE("will not use set partition wise");
} else if (OB_FAIL(set_child_ops.assign(child_ops))) {
LOG_WARN("failed to assign child ops", K(ret));
} else {
set_dist_methods = DistAlgo::DIST_SET_PARTITION_WISE;
OPT_TRACE("will use set partition wise, ignore other method");
}
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_SET_RANDOM)) {
OPT_TRACE("check match set random method");
// has distributed child, use random distribution
ObLogicalOperator *largest_op = NULL;
ObExchangeInfo exch_info;
if (OB_FAIL(get_largest_sharding_child(child_ops, random_none_idx, largest_op))) {
LOG_WARN("failed to get largest sharding children", K(ret));
} else if (NULL == largest_op) {
set_dist_methods &= ~DistAlgo::DIST_SET_RANDOM;
OPT_TRACE("all children`s sharding is distribute, no need shuffle");
OPT_TRACE("will not use set random");
} else if (OB_FAIL(exch_info.server_list_.assign(largest_op->get_server_list()))) {
LOG_WARN("failed to assign server list", K(ret));
} else {
OPT_TRACE("will use set random, ignore other method");
exch_info.parallel_ = largest_op->get_parallel();
exch_info.server_cnt_ = largest_op->get_server_cnt();
exch_info.dist_method_ = ObPQDistributeMethod::RANDOM;
set_dist_methods = DistAlgo::DIST_SET_RANDOM;
for (int64_t i = 0; OB_SUCC(ret) && i < child_ops.count(); i++) {
ObLogicalOperator *child_op = NULL;
if (OB_ISNULL(child_op = child_ops.at(i)) ||
OB_ISNULL(child_op->get_plan())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (child_op != largest_op &&
OB_FAIL(child_op->get_plan()->allocate_exchange_as_top(child_op, exch_info))) {
LOG_WARN("failed to allocate exchange as top", K(ret));
} else if (OB_FAIL(set_child_ops.push_back(child_op))) {
LOG_WARN("failed to push back child ops", K(ret));
} else { /*do nothing*/ }
}
}
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_PULL_TO_LOCAL)) {
OPT_TRACE("will use pull to local method");
ObExchangeInfo exch_info;
set_dist_methods = DistAlgo::DIST_PULL_TO_LOCAL;
for (int64_t i = 0; OB_SUCC(ret) && i < child_ops.count(); i++) {
ObLogicalOperator *child_op = NULL;
if (OB_ISNULL(child_op = child_ops.at(i)) ||
OB_ISNULL(child_op->get_plan())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (child_op->is_sharding() &&
OB_FAIL(child_op->get_plan()->allocate_exchange_as_top(child_op, exch_info))) {
LOG_WARN("failed to allocate exchange as top", K(ret));
} else if (OB_ISNULL(child_op)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(child_op), K(ret));
} else if (OB_FAIL(set_child_ops.push_back(child_op))) {
LOG_WARN("failed to push back child ops", K(ret));
} else { /*do nothing*/ }
}
}
if (OB_SUCC(ret)) {
DistAlgo dist_set_method = get_dist_algo(set_dist_methods);
if (DistAlgo::DIST_INVALID_METHOD == dist_set_method) {
/*do nothing*/
} else if (OB_FAIL(allocate_union_all_as_top(set_child_ops, dist_set_method, top))) {
LOG_WARN("failed to allocate union all as top", K(ret));
} else {
LOG_TRACE("succeed to allocate union all as top", K(dist_set_method));
}
}
return ret;
}
int ObSelectLogPlan::check_if_union_all_match_partition_wise(const ObIArray<ObLogicalOperator*> &child_ops,
bool &is_partition_wise)
{
int ret = OB_SUCCESS;
EqualSets equal_sets;
EqualSets first_equal_sets;
ObShardingInfo *first_sharding = NULL;
ObShardingInfo *child_sharding = NULL;
const ObSelectStmt *child_stmt = NULL;
ObSEArray<ObRawExpr*, 4> first_select_exprs;
ObSEArray<ObRawExpr*, 4> child_select_exprs;
is_partition_wise = true;
for (int64_t i = 0; OB_SUCC(ret) && is_partition_wise && i < child_ops.count(); i++) {
equal_sets.reset();
child_select_exprs.reset();
if (OB_ISNULL(child_ops.at(i)) ||
OB_ISNULL(child_sharding = child_ops.at(i)->get_sharding()) ||
OB_ISNULL(child_ops.at(i)->get_plan()) ||
OB_ISNULL(child_ops.at(i)->get_plan()->get_stmt()) ||
!child_ops.at(i)->get_plan()->get_stmt()->is_select_stmt() ||
OB_ISNULL(child_stmt = static_cast<const ObSelectStmt*>(child_ops.at(i)->get_plan()->get_stmt()))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(child_ops.at(i)), K(child_sharding), K(ret));
} else if (child_ops.at(i)->is_exchange_allocated()) {
is_partition_wise = false;
} else if (i == 0) {
first_sharding = child_sharding;
first_equal_sets = child_ops.at(i)->get_output_equal_sets();
if (OB_FAIL(child_stmt->get_select_exprs(first_select_exprs))) {
LOG_WARN("failed to get select exprs", K(ret));
}
} else if (OB_FAIL(child_stmt->get_select_exprs(child_select_exprs))) {
LOG_WARN("failed to get select exprs", K(ret));
} else if (first_select_exprs.count() != child_select_exprs.count()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("select exprs count doesn't match", K(first_select_exprs), K(child_select_exprs));
} else if (OB_FAIL(append(equal_sets, first_equal_sets))) {
LOG_WARN("failed to append equal sets", K(ret));
} else if (OB_FAIL(append(equal_sets, child_ops.at(i)->get_output_equal_sets()))) {
LOG_WARN("failed to append equal sets", K(ret));
} else if (OB_FAIL(ObShardingInfo::check_if_match_partition_wise(equal_sets,
first_select_exprs,
child_select_exprs,
first_sharding,
child_sharding,
is_partition_wise))) {
LOG_WARN("failed to check if union all match strict partition-wise", K(ret));
} else {
LOG_TRACE("succ to check union all matching pw", K(is_partition_wise));
}
}
return ret;
}
int ObSelectLogPlan::check_if_union_all_match_set_partition_wise(const ObIArray<ObLogicalOperator*> &child_ops,
bool &is_union_all_set_pw)
{
int ret = OB_SUCCESS;
ObSEArray<ObAddr, 4> first_server_list;
bool is_inherit_from_access_all = false;
is_union_all_set_pw = true;
for (int64_t i = 0; OB_SUCC(ret) && is_union_all_set_pw && i < child_ops.count(); i++) {
ObLogicalOperator *child_op = NULL;
ObShardingInfo *child_sharding = NULL;
const ObSelectStmt *child_stmt = NULL;
bool has_external_table_scan = false;
if (OB_ISNULL(child_op = child_ops.at(i))
|| OB_ISNULL(child_sharding = child_op->get_sharding())
|| !child_ops.at(i)->get_plan()->get_stmt()->is_select_stmt()
|| OB_ISNULL(child_stmt = static_cast<const ObSelectStmt*>(child_ops.at(i)->get_plan()->get_stmt()))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid input", K(ret), K(child_op), K(child_sharding));
} else if (!child_sharding->is_distributed()) {
is_union_all_set_pw = false;
OPT_TRACE("not distributed sharding, can not use set partition wise");
} else if (OB_FAIL(check_external_table_scan(const_cast<ObSelectStmt*>(child_stmt), has_external_table_scan))) {
LOG_WARN("fail to check has external table scan", K(ret));
} else if (has_external_table_scan) {
is_union_all_set_pw = false;
OPT_TRACE("has external table scan, can not use set partition wise");
} else if (i == 0) {
if (OB_FAIL(check_sharding_inherit_from_access_all(child_op,
is_inherit_from_access_all))) {
LOG_WARN("failed to check sharding inherit from access all", K(ret));
} else if (is_inherit_from_access_all) {
//partition wise flag conflict with access all flag
is_union_all_set_pw = false;
} else if (OB_FAIL(first_server_list.assign(child_op->get_server_list()))) {
LOG_WARN("failed to get first server list", K(ret));
} else {
LOG_TRACE("succ to check union all matching set pw", K(first_server_list));
}
} else if (OB_FAIL(check_sharding_inherit_from_access_all(child_op,
is_inherit_from_access_all))) {
LOG_WARN("failed to check sharding inherit from access all", K(ret));
} else if (is_inherit_from_access_all) {
//partition wise flag conflict with access all flag
is_union_all_set_pw = false;
} else if (OB_FAIL(ObShardingInfo::is_physically_equal_serverlist(first_server_list,
child_op->get_server_list(),
is_union_all_set_pw))) {
LOG_WARN("failed to check if equal server list", K(ret));
} else {
if (!is_union_all_set_pw) {
OPT_TRACE("server list not equal, can not use set partition wise");
}
LOG_TRACE("succ to check union all matching set pw",
K(first_server_list), K(child_op->get_server_list()), K(is_union_all_set_pw));
}
}
return ret;
}
int ObSelectLogPlan::check_sharding_inherit_from_access_all(ObLogicalOperator* op,
bool &is_inherit_from_access_all)
{
int ret = OB_SUCCESS;
is_inherit_from_access_all = false;
if (OB_ISNULL(op)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null op", K(ret));
} else if (log_op_def::LOG_JOIN == op->get_type()) {
ObLogJoin *log_join = static_cast<ObLogJoin*>(op);
if (DIST_BC2HOST_NONE == log_join->get_dist_method() &&
log_join->is_nlj_with_param_down()) {
is_inherit_from_access_all = true;
}
}
if (OB_SUCC(ret) &&
!is_inherit_from_access_all &&
op->get_inherit_sharding_index() != -1) {
int64_t idx = op->get_inherit_sharding_index();
if (idx < 0 || idx >= op->get_num_of_child()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect inherit sharding index", K(ret));
} else if (OB_FAIL(SMART_CALL(check_sharding_inherit_from_access_all(op->get_child(idx),
is_inherit_from_access_all)))) {
LOG_WARN("failed to check sharding inherit from bc2host", K(ret));
}
}
return ret;
}
int ObSelectLogPlan::check_if_union_all_match_extended_partition_wise(const ObIArray<ObLogicalOperator*> &child_ops,
bool &is_union_all_ext_pw)
{
int ret = OB_SUCCESS;
ObSEArray<ObAddr, 4> first_server_list;
is_union_all_ext_pw = true;
for (int64_t i = 0; OB_SUCC(ret) && is_union_all_ext_pw && i < child_ops.count(); i++) {
ObLogicalOperator *child_op = NULL;
ObShardingInfo *child_sharding = NULL;
if (OB_ISNULL(child_op = child_ops.at(i)) ||
OB_ISNULL(child_sharding = child_op->get_sharding())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid input", K(ret), K(child_op), K(child_sharding));
} else if (!child_sharding->is_distributed()) {
is_union_all_ext_pw = false;
OPT_TRACE("not distribute sharding, can not use extend partition wise");
} else if (i == 0) {
if (OB_FAIL(first_server_list.assign(child_op->get_server_list()))) {
LOG_WARN("failed to get first server list", K(ret));
} else {
LOG_TRACE("succ to check union all matching ext pw", K(first_server_list));
}
} else if (OB_FAIL(ObShardingInfo::is_physically_both_shuffled_serverlist(first_server_list,
child_op->get_server_list(),
is_union_all_ext_pw))) {
LOG_WARN("failed to check if both are shuffled server list", K(ret));
} else {
if (!is_union_all_ext_pw) {
OPT_TRACE("server list not match, can not use extend partition wise");
}
LOG_TRACE("succ to check union all matching ext pw",
K(first_server_list), K(child_op->get_server_list()), K(is_union_all_ext_pw));
}
}
return ret;
}
int ObSelectLogPlan::get_largest_sharding_child(const ObIArray<ObLogicalOperator*> &child_ops,
const int64_t candi_pos,
ObLogicalOperator *&largest_op)
{
int ret = OB_SUCCESS;
ObLogicalOperator *child_op = NULL;
ObShardingInfo *child_sharding = NULL;
largest_op = NULL;
if (OB_INVALID_INDEX != candi_pos) {
if (candi_pos < 0 || candi_pos >= child_ops.count()) {
/*do nothing*/
} else if (OB_ISNULL(child_op = child_ops.at(candi_pos)) ||
OB_ISNULL(child_sharding = child_op->get_sharding())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(child_op), K(child_sharding), K(ret));
} else if (child_sharding->is_distributed()) {
largest_op = child_op;
}
} else {
double largest_card = -1.0;
for (int64_t i = 0; OB_SUCC(ret) && i < child_ops.count(); i++) {
if (OB_ISNULL(child_op = child_ops.at(i)) ||
OB_ISNULL(child_sharding = child_op->get_sharding())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(child_op), K(child_sharding), K(ret));
} else if (child_op->get_parallel() <= ObGlobalHint::DEFAULT_PARALLEL) {
/* choose a parallel child */
} else if (child_sharding->is_distributed() && child_op->get_card() > largest_card) {
largest_op = child_op;
largest_card = child_op->get_card();
} else { /*do nothing*/ }
}
}
return ret;
}
int ObSelectLogPlan::allocate_union_all_as_top(const ObIArray<ObLogicalOperator*> &child_ops,
DistAlgo dist_set_method,
ObLogicalOperator *&top)
{
int ret = OB_SUCCESS;
ObLogSet *set_op = NULL;
if (OB_ISNULL((set_op = static_cast<ObLogSet*>(get_log_op_factory().allocate(*this, LOG_SET))))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("Allocate memory for ObLogSet failed", K(ret));
} else {
set_op->assign_set_distinct(false);
set_op->assign_set_op(ObSelectStmt::UNION);
set_op->set_algo_type(SetAlgo::MERGE_SET);
set_op->set_distributed_algo(dist_set_method);
if (OB_FAIL(set_op->add_child(child_ops))) {
LOG_WARN("failed to add child ops", K(ret));
} else if (OB_FAIL(set_op->compute_property())) {
LOG_WARN("failed to compute property", K(ret));
} else {
top = set_op;
}
}
return ret;
}
// 将多元 union 拆分为 union all 和 hash distinct, 这里分配 hash distinct, 并调整估行
int ObSelectLogPlan::allocate_set_distinct_as_top(ObLogicalOperator *&top)
{
int ret = OB_SUCCESS;
ObSEArray<ObRawExpr*, 8> distinct_exprs;
ObLogicalOperator *distinct_op = NULL;
if (OB_ISNULL(get_stmt()) || OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(get_stmt()->get_select_exprs(distinct_exprs))) {
LOG_WARN("failed to get current best plan", K(ret));
} else if (OB_ISNULL(distinct_op = get_log_op_factory().allocate(*this, LOG_DISTINCT))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("failed to allocate distinct operator", K(ret));
} else {
ObLogDistinct *distinct = static_cast<ObLogDistinct*>(distinct_op);
distinct->set_child(ObLogicalOperator::first_child, top);
distinct->set_algo_type(HASH_AGGREGATE);
if (OB_FAIL(distinct->set_distinct_exprs(distinct_exprs))) {
LOG_WARN("failed to set group by columns", K(ret));
} else if (OB_FAIL(distinct->compute_property())) {
LOG_WARN("failed to compute property", K(ret));
} else {
double distinct_cost = 0.0;
distinct->set_card(top->get_card());
distinct_cost = ObOptEstCost::cost_hash_distinct(top->get_card(),
top->get_card(),
top->get_width(),
distinct_exprs,
get_optimizer_context().get_cost_model_type());
distinct->set_cost(top->get_cost() + distinct_cost);
distinct->set_op_cost(distinct_cost);
top = distinct_op;
}
}
return ret;
}
int ObSelectLogPlan::candi_allocate_recursive_union_all(const ObIArray<ObSelectLogPlan*> &child_plans)
{
int ret = OB_SUCCESS;
ObSEArray<CandidatePlan, 8> all_plans;
ObSelectLogPlan *left_plan = NULL;
ObSelectLogPlan *right_plan = NULL;
ObSEArray<CandidatePlan, 8> left_best_plans;
ObSEArray<CandidatePlan, 8> right_best_plans;
const ObSelectStmt *select_stmt = NULL;
ObSEArray<OrderItem, 8> candi_order_items;
if (OB_UNLIKELY(2 != child_plans.count()) || OB_ISNULL(left_plan = child_plans.at(0)) ||
OB_ISNULL(right_plan = child_plans.at(1)) || OB_ISNULL(select_stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected error", K(child_plans.count()), K(left_plan), K(right_plan),
K(select_stmt), K(ret));
} else if (OB_FAIL(left_plan->decide_sort_keys_for_runion(select_stmt->get_search_by_items(),
candi_order_items))) {
LOG_WARN("failed to allocate sort as root", K(ret));
} else if (OB_FAIL(get_minimal_cost_candidates(left_plan->get_candidate_plans().candidate_plans_,
left_best_plans))) {
LOG_WARN("failed to get minimal cost candidates", K(ret));
} else if (OB_FAIL(get_minimal_cost_candidates(right_plan->get_candidate_plans().candidate_plans_,
right_best_plans))) {
LOG_WARN("failed to get minimal cost candidates", K(ret));
} else if (OB_FAIL(create_recursive_union_all_plan(left_best_plans,
right_best_plans,
candi_order_items,
false,
all_plans))) {
LOG_WARN("failed to create recursive union all plan", K(ret));
} else if (!all_plans.empty()) {
LOG_TRACE("succeed to generate set plans using hint", K(all_plans.count()));
} else if (OB_FAIL(get_log_plan_hint().check_status())) {
LOG_WARN("failed to generate plans with hint", K(ret));
} else if (OB_FAIL(create_recursive_union_all_plan(left_best_plans,
right_best_plans,
candi_order_items,
true,
all_plans))) {
LOG_WARN("failed to create recursive union all plan", K(ret));
} else {
LOG_TRACE("succeed to generate set plans ignore hint", K(all_plans.count()));
}
if (OB_SUCC(ret)) {
int64_t check_scope = OrderingCheckScope::CHECK_SET | OrderingCheckScope::CHECK_ORDERBY;
if (OB_FAIL(update_plans_interesting_order_info(all_plans, check_scope))) {
LOG_WARN("failed to update plans interesting order info", K(ret));
} else if (OB_FAIL(prune_and_keep_best_plans(all_plans))) {
LOG_WARN("failed to add all plans", K(ret));
}
}
return ret;
}
int ObSelectLogPlan::create_recursive_union_all_plan(ObIArray<CandidatePlan> &left_best_plans,
ObIArray<CandidatePlan> &right_best_plans,
const ObIArray<OrderItem> &order_items,
const bool ignore_hint,
ObIArray<CandidatePlan> &all_plans)
{
int ret = OB_SUCCESS;
CandidatePlan candidate_plan;
for (int64_t i = 0; OB_SUCC(ret) && i < left_best_plans.count(); i++) {
for (int64_t j = 0; OB_SUCC(ret) && j < right_best_plans.count(); j++) {
if (OB_FAIL(create_recursive_union_all_plan(left_best_plans.at(i).plan_tree_,
right_best_plans.at(j).plan_tree_,
order_items,
ignore_hint,
candidate_plan.plan_tree_))) {
LOG_WARN("failed to create recursive union all plan", K(ret));
} else if (NULL == candidate_plan.plan_tree_) {
/*do nothing*/
} else if (OB_FAIL(all_plans.push_back(candidate_plan))) {
LOG_WARN("failed to push back candidate plan", K(ret));
} else { /*do nothing*/ }
}
}
return ret;
}
int ObSelectLogPlan::create_recursive_union_all_plan(ObLogicalOperator *left_child,
ObLogicalOperator *right_child,
const ObIArray<OrderItem> &order_items,
const bool ignore_hint,
ObLogicalOperator *&top)
{
int ret = OB_SUCCESS;
bool is_basic = false;
bool need_sort = false;
int64_t prefix_pos = 0;
ObLogPlan *left_log_plan = NULL;
ObSEArray<ObLogicalOperator*, 2> child_ops;
DistAlgo dist_set_method = DistAlgo::DIST_INVALID_METHOD;
ObExchangeInfo left_exch_info;
left_exch_info.dist_method_ = ObPQDistributeMethod::NONE;
top = NULL;
uint64_t set_dist_methods = DistAlgo::DIST_BASIC_METHOD | DistAlgo::DIST_PULL_TO_LOCAL;
DistAlgo hint_dist_methods = get_log_plan_hint().get_valid_set_dist_algo();
if (!ignore_hint && DistAlgo::DIST_INVALID_METHOD != hint_dist_methods) {
set_dist_methods &= hint_dist_methods;
}
if (OB_ISNULL(left_child) || OB_ISNULL(right_child) ||
OB_ISNULL(left_log_plan = left_child->get_plan())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(left_child), K(right_child), K(left_log_plan), K(ret));
} else if (OB_FAIL(child_ops.push_back(left_child)) ||
OB_FAIL(child_ops.push_back(right_child))) {
LOG_WARN("failed to push back operator", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::check_basic_sharding_info(get_optimizer_context().get_local_server_addr(),
child_ops,
is_basic))) {
LOG_WARN("failed to check basic sharding info", K(ret));
} else if (is_basic) {
if (DistAlgo::DIST_BASIC_METHOD & set_dist_methods) {
dist_set_method = DistAlgo::DIST_BASIC_METHOD;
}
} else if (DistAlgo::DIST_PULL_TO_LOCAL & set_dist_methods) {
// pull to local
dist_set_method = DistAlgo::DIST_PULL_TO_LOCAL;
if (left_child->is_sharding()) {
left_exch_info.dist_method_ = ObPQDistributeMethod::LOCAL;
}
}
if (OB_SUCC(ret) && DistAlgo::DIST_INVALID_METHOD != dist_set_method) {
if (OB_FAIL(ObOptimizerUtil::check_need_sort(order_items,
left_child->get_op_ordering(),
left_child->get_fd_item_set(),
left_child->get_output_equal_sets(),
left_child->get_output_const_exprs(),
get_onetime_query_refs(),
left_child->get_is_at_most_one_row(),
need_sort,
prefix_pos))) {
LOG_WARN("failed to check need sort", K(ret));
} else if (OB_FAIL(left_log_plan->allocate_sort_and_exchange_as_top(left_child,
left_exch_info,
order_items,
need_sort,
prefix_pos,
left_child->get_is_local_order()))) {
LOG_WARN("failed to allocate operator for join style op", K(ret));
} else if (OB_FAIL(allocate_recursive_union_all_as_top(left_child, right_child, dist_set_method, top))) {
LOG_WARN("failed to allocate recursive union all as top", K(ret));
} else { /*do nothing*/ }
}
return ret;
}
int ObSelectLogPlan::allocate_recursive_union_all_as_top(ObLogicalOperator *left_child,
ObLogicalOperator *right_child,
DistAlgo dist_set_method,
ObLogicalOperator *&top)
{
int ret = OB_SUCCESS;
ObLogSet *set_op = NULL;
const ObSelectStmt *select_stmt = NULL;
top = NULL;
if (OB_ISNULL(select_stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_ISNULL((set_op = static_cast<ObLogSet*>(
get_log_op_factory().allocate(*this, LOG_SET))))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("Allocate memory for ObLogSet failed", K(ret));
} else {
set_op->set_left_child(left_child);
set_op->set_right_child(right_child);
set_op->assign_set_distinct(false);
set_op->assign_set_op(select_stmt->get_set_op());
set_op->set_algo_type(MERGE_SET);
set_op->set_distributed_algo(dist_set_method);
set_op->set_recursive_union(true);
set_op->set_is_breadth_search(select_stmt->is_breadth_search());
if (OB_FAIL(set_op->set_search_ordering(select_stmt->get_search_by_items()))) {
LOG_WARN("set search order failed", K(ret));
} else if (OB_FAIL(set_op->set_cycle_items(select_stmt->get_cycle_items()))) {
LOG_WARN("set cycle item failed", K(ret));
} else if (OB_FAIL(set_op->compute_property())) {
LOG_WARN("failed to compute property", K(ret));
} else {
top = set_op;
}
}
return ret;
}
int ObSelectLogPlan::candi_allocate_distinct_set(const ObIArray<ObSelectLogPlan*> &child_plans)
{
int ret = OB_SUCCESS;
EqualSets equal_sets;
ObSelectLogPlan *left_plan = NULL;
ObSelectLogPlan *right_plan = NULL;
const ObSelectStmt *left_stmt = NULL;
const ObSelectStmt *right_stmt = NULL;
const ObSelectStmt *current_stmt = NULL;
ObLogicalOperator *left_best_plan = NULL;
ObLogicalOperator *right_best_plan = NULL;
ObSEArray<ObRawExpr*, 4> left_select_exprs;
ObSEArray<ObRawExpr*, 4> right_select_exprs;
ObSEArray<CandidatePlan, 2> hash_set_plans;
ObSEArray<CandidatePlan, 4> merge_set_plans;
ObSEArray<CandidatePlan, 4> all_plans;
ObSEArray<CandidatePlan, 4> left_best_plans;
ObSEArray<CandidatePlan, 4> right_best_plans;
typedef ObSEArray<ObSEArray<CandidatePlan, 16>, 4> CandidatePlanArray;
const SetAlgo set_algo = get_log_plan_hint().get_valid_set_algo();
SMART_VARS_2((CandidatePlanArray, left_candidate_list),
(CandidatePlanArray, right_candidate_list)) {
int64_t check_scope = OrderingCheckScope::CHECK_SET | OrderingCheckScope::CHECK_ORDERBY;
if (OB_ISNULL(current_stmt = get_stmt()) || OB_UNLIKELY(2 != child_plans.count()) ||
OB_ISNULL(left_plan = child_plans.at(0)) || OB_ISNULL(right_plan = child_plans.at(1)) ||
OB_ISNULL(left_stmt = left_plan->get_stmt()) ||
OB_ISNULL(right_stmt = right_plan->get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected stmt type", K(current_stmt), K(left_plan), K(right_plan),
K(left_stmt), K(right_stmt), K(ret));
} else if (OB_FAIL(classify_candidates_based_on_sharding(left_plan->candidates_.candidate_plans_,
left_candidate_list))) {
LOG_WARN("failed to classify candidates based on sharding", K(ret));
} else if (OB_FAIL(classify_candidates_based_on_sharding(right_plan->candidates_.candidate_plans_,
right_candidate_list))) {
LOG_WARN("failed to classify candidates based on sharding", K(ret));
} else if (OB_FAIL(get_minimal_cost_candidates(left_candidate_list,
left_best_plans))) {
LOG_WARN("failed to get minimal cost candidates", K(ret));
} else if (OB_FAIL(get_minimal_cost_candidates(right_candidate_list,
right_best_plans))) {
LOG_WARN("failed to get minimal cost candidates", K(ret));
} else if (OB_FAIL(left_stmt->get_select_exprs(left_select_exprs))) {
LOG_WARN("failed to get select exprs", K(ret));
} else if (OB_FAIL(right_stmt->get_select_exprs(right_select_exprs))) {
LOG_WARN("failed to get select exprs", K(ret));
} else if (OB_FAIL(left_plan->get_candidate_plans().get_best_plan(left_best_plan))) {
LOG_WARN("failed to get best plan", K(ret));
} else if (OB_FAIL(right_plan->get_candidate_plans().get_best_plan(right_best_plan))) {
LOG_WARN("failed to get best plan", K(ret));
} else if (OB_ISNULL(left_best_plan) || OB_ISNULL(right_best_plan)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(left_best_plan), K(right_best_plan), K(ret));
} else if (OB_FAIL(append(equal_sets, left_best_plan->get_output_equal_sets()))) {
LOG_WARN("failed to append equal sets", K(ret));
} else if (OB_FAIL(append(equal_sets, right_best_plan->get_output_equal_sets()))) {
LOG_WARN("failed to append equal sets", K(ret));
} else if (MERGE_SET != set_algo &&
OB_FAIL(generate_hash_set_plans(equal_sets,
left_select_exprs,
right_select_exprs,
current_stmt->get_set_op(),
left_best_plans,
right_best_plans,
false,
hash_set_plans))) {
// generate hash set plans use hint
LOG_WARN("failed to generate hash set plans", K(ret));
} else if (HASH_SET != set_algo &&
OB_FAIL(generate_merge_set_plans(equal_sets,
left_select_exprs,
right_select_exprs,
current_stmt->get_set_op(),
left_candidate_list,
right_candidate_list,
false,
hash_set_plans.empty(),
merge_set_plans))) {
// generate merge set plans use hint
LOG_WARN("failed to generate merge set plans", K(ret));
} else if (!hash_set_plans.empty() || !merge_set_plans.empty()) {
OPT_TRACE("succeed to generate set plans using hint");
OPT_TRACE(" hash set plan count:", hash_set_plans.count());
OPT_TRACE(" merge set plan count:", merge_set_plans.count());
LOG_TRACE("succeed to generate set plans using hint", K(set_algo), K(hash_set_plans.count()), K(merge_set_plans.count()));
} else if (OB_FAIL(get_log_plan_hint().check_status())) {
LOG_WARN("failed to generate plans with hint", K(ret));
} else if (OB_FAIL(generate_hash_set_plans(equal_sets,
left_select_exprs,
right_select_exprs,
current_stmt->get_set_op(),
left_best_plans,
right_best_plans,
true,
hash_set_plans))) {
// generate hash set plans ignore hint
LOG_WARN("failed to generate hash set plans", K(ret));
} else if (OB_FAIL(generate_merge_set_plans(equal_sets,
left_select_exprs,
right_select_exprs,
current_stmt->get_set_op(),
left_candidate_list,
right_candidate_list,
true,
hash_set_plans.empty(),
merge_set_plans))) {
// generate merge set plans ignore hint
LOG_WARN("failed to generate merge set plans", K(ret));
} else {
OPT_TRACE("succeed to generate set plans ignore hint");
OPT_TRACE(" hash set plan count:", hash_set_plans.count());
OPT_TRACE(" merge set plan count:", merge_set_plans.count());
LOG_TRACE("succeed to generate set plans ignore hint", K(set_algo), K(hash_set_plans.count()),
K(merge_set_plans.count()));
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(append(all_plans, merge_set_plans))) {
LOG_WARN("failed to append plans", K(ret));
} else if (OB_FAIL(append(all_plans, hash_set_plans))) {
LOG_WARN("failed to append plans", K(ret));
} else if (OB_FAIL(update_plans_interesting_order_info(all_plans, check_scope))) {
LOG_WARN("failed to update plans interesting order info", K(ret));
} else if (OB_FAIL(prune_and_keep_best_plans(all_plans))) {
LOG_WARN("failed to add all plans", K(ret));
} else { /*do nothing*/ }
}
return ret;
}
int ObSelectLogPlan::get_distributed_set_methods(const EqualSets &equal_sets,
const ObIArray<ObRawExpr*> &left_set_keys,
const ObIArray<ObRawExpr*> &right_set_keys,
const ObSelectStmt::SetOperator set_op,
const SetAlgo set_method,
ObLogicalOperator &left_child,
ObLogicalOperator &right_child,
const bool ignore_hint,
int64_t &set_dist_methods)
{
int ret = OB_SUCCESS;
ObSEArray<ObLogicalOperator*, 2> child_ops;
ObShardingInfo *left_sharding = NULL;
ObShardingInfo *right_sharding = NULL;
const ObSelectStmt *select_stmt = NULL;
bool need_pull_to_local = false;
set_dist_methods = ignore_hint ? DistAlgo::DIST_INVALID_METHOD
: get_log_plan_hint().get_valid_set_dist_algo();
if (OB_ISNULL(left_sharding = left_child.get_sharding()) ||
OB_ISNULL(right_sharding = right_child.get_sharding()) ||
OB_ISNULL(select_stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid sharding", K(left_sharding), K(right_sharding), K(ret), K(left_child.get_type()), K(left_child.get_type()), K(get_op_name(left_child.get_type())));
} else if (OB_FAIL(child_ops.push_back(&left_child)) ||
OB_FAIL(child_ops.push_back(&right_child))) {
LOG_WARN("failed to push back child info", K(ret));
} else if (DistAlgo::DIST_INVALID_METHOD == set_dist_methods) {
set_dist_methods |= DistAlgo::DIST_BASIC_METHOD;
set_dist_methods |= DistAlgo::DIST_PARTITION_WISE;
set_dist_methods |= DistAlgo::DIST_NONE_ALL;
set_dist_methods |= DistAlgo::DIST_ALL_NONE;
if (MERGE_SET != set_method) {
// disable dist algo for merge set except basic & pwj
set_dist_methods |= DistAlgo::DIST_NONE_PARTITION;
set_dist_methods |= DistAlgo::DIST_NONE_HASH;
set_dist_methods |= DistAlgo::DIST_PARTITION_NONE;
set_dist_methods |= DistAlgo::DIST_HASH_NONE;
set_dist_methods |= DistAlgo::DIST_PULL_TO_LOCAL;
if (left_child.get_parallel() > 1 || right_child.get_parallel() > 1) {
set_dist_methods |= DistAlgo::DIST_HASH_HASH;
OPT_TRACE("candi hash set dist method:basic,partition wise, none all, all none, none partition,partition none,pull to local,hash hash");
} else {
OPT_TRACE("candi hash set dist method:basic,partition wise, none all, all none, none partition,partition none,pull to local");
}
} else {
OPT_TRACE("candi merge set dist method:basic, partition wise, none all, all none, ");
}
} else {
OPT_TRACE("use dist method with hint");
}
if (OB_SUCC(ret) && !get_optimizer_context().is_var_assign_only_in_root_stmt() &&
get_optimizer_context().has_var_assign()) {
set_dist_methods &= DIST_PULL_TO_LOCAL | DIST_BASIC_METHOD;
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_NONE_ALL)) {
if (left_sharding->is_distributed() && right_sharding->is_match_all() &&
!right_child.get_contains_das_op() && !right_child.get_contains_fake_cte() &&
ObSelectStmt::UNION != set_op) {
set_dist_methods = DistAlgo::DIST_NONE_ALL;
} else {
set_dist_methods &= ~DistAlgo::DIST_NONE_ALL;
}
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_ALL_NONE)) {
if (right_sharding->is_distributed() && left_sharding->is_match_all() &&
!left_child.get_contains_das_op() && !left_child.get_contains_fake_cte() &&
ObSelectStmt::UNION != set_op) {
set_dist_methods = DistAlgo::DIST_ALL_NONE;
} else {
set_dist_methods &= ~DistAlgo::DIST_ALL_NONE;
}
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_BASIC_METHOD)) {
OPT_TRACE("check basic method");
bool is_basic = false;
ObAddr &local_addr = get_optimizer_context().get_local_server_addr();
if (OB_FAIL(ObOptimizerUtil::check_basic_sharding_info(local_addr, child_ops, is_basic))) {
LOG_WARN("failed to check basic sharding info", K(ret));
} else if (is_basic) {
set_dist_methods = DistAlgo::DIST_BASIC_METHOD;
OPT_TRACE("plan will use basic method");
} else {
set_dist_methods &= ~DistAlgo::DIST_BASIC_METHOD;
OPT_TRACE("plan will not use basic method");
}
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_PARTITION_WISE)) {
OPT_TRACE("check partition wise method");
bool is_partition_wise = false;
if (OB_FAIL(ObShardingInfo::check_if_match_partition_wise(equal_sets,
left_set_keys,
right_set_keys,
left_child.get_strong_sharding(),
right_child.get_strong_sharding(),
is_partition_wise))) {
LOG_WARN("failed to check if match partition wise join", K(ret));
} else if (is_partition_wise) {
if (left_child.is_exchange_allocated() == right_child.is_exchange_allocated()) {
set_dist_methods = DistAlgo::DIST_PARTITION_WISE;
OPT_TRACE("plan will use partition wise method and prune other method");
}
} else {
set_dist_methods &= ~DistAlgo::DIST_PARTITION_WISE;
OPT_TRACE("plan will not use partition wise method");
}
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_EXT_PARTITION_WISE)) {
OPT_TRACE("check extended partition wise method");
bool is_ext_partition_wise = false;
if (!left_sharding->is_distributed_without_table_location_with_partitioning() ||
!ObShardingInfo::is_shuffled_server_list(left_child.get_server_list()) ||
!right_sharding->is_distributed_without_table_location_with_partitioning() ||
!ObShardingInfo::is_shuffled_server_list(right_child.get_server_list())) {
set_dist_methods &= ~DistAlgo::DIST_EXT_PARTITION_WISE;
OPT_TRACE("sharding or exchange is not expected, plan will not use extended partition wise method");
} else if (OB_FAIL(ObShardingInfo::check_if_match_extended_partition_wise(equal_sets,
left_child.get_server_list(),
right_child.get_server_list(),
left_set_keys,
right_set_keys,
left_child.get_strong_sharding(),
right_child.get_strong_sharding(),
is_ext_partition_wise))) {
LOG_WARN("failed to check if match partition wise join", K(ret));
} else if (is_ext_partition_wise) {
set_dist_methods = DistAlgo::DIST_EXT_PARTITION_WISE;
OPT_TRACE("plan will use extended partition wise method and prune other method");
} else {
set_dist_methods &= ~DistAlgo::DIST_EXT_PARTITION_WISE;
OPT_TRACE("plan will not use extended partition wise method");
}
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_PARTITION_NONE)) {
OPT_TRACE("check partition none method");
bool left_match_repart = false;
if (OB_FAIL(check_if_set_match_repart(equal_sets,
left_set_keys,
right_set_keys,
right_child,
left_match_repart))) {
LOG_WARN("failed to check if match repart", K(ret));
} else if (!left_match_repart) {
set_dist_methods &= ~DistAlgo::DIST_PARTITION_NONE;
OPT_TRACE("plan will not use partition none method");
} else {
need_pull_to_local = false;
set_dist_methods &= ~DistAlgo::DIST_HASH_NONE;
OPT_TRACE("plan will use partition none method");
}
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_HASH_NONE)) {
OPT_TRACE("check hash none method");
bool is_match_left_side_hash = false;
if (OB_FAIL(check_if_set_match_rehash(equal_sets,
left_set_keys,
right_set_keys,
right_child,
is_match_left_side_hash))) {
LOG_WARN("failed to check if match left side hash", K(ret));
} else if (!is_match_left_side_hash) {
set_dist_methods &= ~DistAlgo::DIST_HASH_NONE;
OPT_TRACE("plan will not use hash none method");
} else {
OPT_TRACE("plan will use hash none method");
}
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_NONE_PARTITION)) {
OPT_TRACE("check none partition method");
bool right_match_repart = false;
if (OB_FAIL(check_if_set_match_repart(equal_sets,
right_set_keys,
left_set_keys,
left_child,
right_match_repart))) {
LOG_WARN("failed to check_and_extract_repart_info", K(ret));
} else if (!right_match_repart) {
set_dist_methods &= ~DistAlgo::DIST_NONE_PARTITION;
OPT_TRACE("plan will not use none partition method");
} else {
need_pull_to_local = false;
set_dist_methods &= ~DistAlgo::DIST_NONE_HASH;
OPT_TRACE("plan will use none partition method");
}
}
if (OB_SUCC(ret) && (set_dist_methods & DistAlgo::DIST_NONE_HASH)) {
OPT_TRACE("check none hash method");
bool is_match_right_side_hash = false;
if (OB_FAIL(check_if_set_match_rehash(equal_sets,
left_set_keys,
right_set_keys,
left_child,
is_match_right_side_hash))) {
LOG_WARN("failed to check if match left side hash", K(ret));
} else if (!is_match_right_side_hash) {
set_dist_methods &= ~DistAlgo::DIST_NONE_HASH;
OPT_TRACE("plan will not use none hash method");
} else {
OPT_TRACE("plan will use none hash method");
}
}
if (OB_SUCC(ret) && (set_dist_methods & DIST_PULL_TO_LOCAL) && !need_pull_to_local) {
if ((set_dist_methods & DIST_PARTITION_NONE) ||
(set_dist_methods & DIST_NONE_PARTITION) ||
(set_dist_methods & DIST_HASH_NONE) ||
(set_dist_methods & DIST_NONE_HASH) ||
(set_dist_methods & DIST_HASH_HASH)) {
set_dist_methods &= ~DIST_PULL_TO_LOCAL;
OPT_TRACE("plan will not use pull to local");
}
}
return ret;
}
bool ObSelectLogPlan::is_set_partition_wise_valid(const ObLogicalOperator &left_plan,
const ObLogicalOperator &right_plan)
{
bool is_valid = true;
if ((left_plan.is_exchange_allocated() || right_plan.is_exchange_allocated()) &&
(left_plan.get_contains_pw_merge_op() || right_plan.get_contains_pw_merge_op())) {
is_valid = false;
} else { /*do nothing*/ }
return is_valid;
}
bool ObSelectLogPlan::is_set_repart_valid(const ObLogicalOperator &left_plan,
const ObLogicalOperator &right_plan,
const DistAlgo dist_algo)
{
bool is_valid = true;
if (DistAlgo::DIST_PARTITION_NONE == dist_algo && right_plan.get_contains_pw_merge_op()) {
is_valid = true;
} else if (DistAlgo::DIST_NONE_PARTITION == dist_algo && left_plan.get_contains_pw_merge_op()) {
is_valid = true;
} else {
is_valid = true;
}
return is_valid;
}
int ObSelectLogPlan::check_if_set_match_repart(const EqualSets &equal_sets,
const ObIArray<ObRawExpr *> &src_join_keys,
const ObIArray<ObRawExpr *> &target_join_keys,
const ObLogicalOperator &target_child,
bool &is_match_repart)
{
int ret = OB_SUCCESS;
ObSEArray<ObRawExpr*, 8> target_part_keys;
ObShardingInfo *target_sharding = NULL;
bool is_base_table_scan = false;
is_match_repart = false;
if (NULL == target_child.get_strong_sharding()) {
/* do nothing */
} else if (OB_ISNULL(target_sharding = target_child.get_sharding())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (!target_child.is_distributed() || NULL == target_sharding->get_phy_table_location_info()) {
/* do nothing */
} else if (OB_FAIL(target_sharding->get_all_partition_keys(target_part_keys, true))) {
LOG_WARN("failed to get partition keys", K(ret));
} else if (target_part_keys.empty()) {
/*do nothing*/
} else if (OB_FAIL(ObShardingInfo::check_if_match_repart_or_rehash(equal_sets,
src_join_keys,
target_join_keys,
target_part_keys,
is_match_repart))) {
LOG_WARN("failed to check if match repartition", K(ret));
} else {
LOG_TRACE("succeed to check whether matching repartition", K(is_match_repart));
}
return ret;
}
int ObSelectLogPlan::check_if_set_match_rehash(const EqualSets &equal_sets,
const ObIArray<ObRawExpr *> &src_join_keys,
const ObIArray<ObRawExpr *> &target_join_keys,
const ObLogicalOperator &target_child,
bool &is_match_single_side_hash)
{
int ret = OB_SUCCESS;
ObSEArray<ObRawExpr*, 8> target_part_keys;
ObShardingInfo *target_sharding = NULL;
is_match_single_side_hash = false;
if (NULL == target_child.get_strong_sharding()) {
/* do nothing */
} else if (OB_ISNULL(target_sharding = target_child.get_sharding())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (!target_sharding->is_distributed_without_table_location() ||
!ObShardingInfo::is_shuffled_server_list(target_child.get_server_list())) {
/* do nothing */
} else if (OB_FAIL(target_sharding->get_all_partition_keys(target_part_keys, true))) {
LOG_WARN("failed to get partition keys", K(ret));
} else if (target_part_keys.empty()) {
/* do nothing */
} else if (OB_FAIL(ObShardingInfo::check_if_match_repart_or_rehash(equal_sets,
src_join_keys,
target_join_keys,
target_part_keys,
is_match_single_side_hash))) {
LOG_WARN("failed to check if set match single side hash", K(ret));
} else {
LOG_TRACE("succeed to check whether set matching single side hash", K(is_match_single_side_hash));
}
return ret;
}
int ObSelectLogPlan::generate_merge_set_plans(const EqualSets &equal_sets,
const ObIArray<ObRawExpr*> &left_set_keys,
const ObIArray<ObRawExpr*> &right_set_keys,
const ObSelectStmt::SetOperator set_op,
ObIArray<ObSEArray<CandidatePlan, 16>> &left_candidate_list,
ObIArray<ObSEArray<CandidatePlan, 16>> &right_candidate_list,
const bool ignore_hint,
const bool no_hash_plans,
ObIArray<CandidatePlan> &merge_set_plans)
{
int ret = OB_SUCCESS;
ObArenaAllocator allocator;
ObSEArray<MergeKeyInfo*, 8> merge_key_list;
ObSEArray<ObSEArray<MergeKeyInfo*, 8>, 8> left_merge_keys;
ObSEArray<ObSEArray<MergeKeyInfo*, 8>, 8> right_merge_keys;
bool force_merge = MERGE_SET == get_log_plan_hint().get_valid_set_algo();
//if can_ignore_merge_plan = true, need to check sort_order for merge plan
bool can_ignore_merge_plan = ((ignore_hint && !no_hash_plans) || (!ignore_hint && !force_merge));
OPT_TRACE("start generate merge set plans");
bool no_swap = false;
bool swap = false;
if (OB_FAIL(get_allowed_branch_order(ignore_hint, set_op, no_swap, swap))) {
LOG_WARN("failed to get allowed branch order", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && i < left_candidate_list.count(); i++) {
merge_key_list.reuse();
if (OB_FAIL(init_merge_set_structure(allocator,
left_candidate_list.at(i),
left_set_keys,
merge_key_list,
can_ignore_merge_plan))) {
LOG_WARN("failed to initialize merge key", K(ret));
} else if (OB_FAIL(left_merge_keys.push_back(merge_key_list))) {
LOG_WARN("failed to push back merge keys", K(ret));
} else { /*do nothing*/ }
}
for (int64_t i = 0; OB_SUCC(ret) && i < right_candidate_list.count(); i++) {
merge_key_list.reuse();
if (OB_FAIL(init_merge_set_structure(allocator,
right_candidate_list.at(i),
right_set_keys,
merge_key_list,
can_ignore_merge_plan))) {
LOG_WARN("failed to init merge key", K(ret));
} else if (OB_FAIL(right_merge_keys.push_back(merge_key_list))) {
LOG_WARN("failed to push back merge keys", K(ret));
} else { /*do nothing*/ }
}
for (int64_t i = 0; OB_SUCC(ret) && i < left_candidate_list.count(); i++) {
for (int64_t j = 0; OB_SUCC(ret) && j < right_candidate_list.count(); j++) {
if (no_swap && OB_FAIL(inner_generate_merge_set_plans(equal_sets,
left_set_keys,
right_set_keys,
left_merge_keys.at(i),
set_op,
left_candidate_list.at(i),
right_candidate_list.at(j),
ignore_hint,
can_ignore_merge_plan,
no_hash_plans,
merge_set_plans))) {
LOG_WARN("failed to generate merge set plans", K(ret));
} else if (swap && OB_FAIL(inner_generate_merge_set_plans(equal_sets,
right_set_keys,
left_set_keys,
right_merge_keys.at(j),
set_op,
right_candidate_list.at(j),
left_candidate_list.at(i),
ignore_hint,
can_ignore_merge_plan,
no_hash_plans,
merge_set_plans))) {
LOG_WARN("failed to inner generate merge set plans", K(ret));
} else { /*do nothing*/}
}
}
return ret;
}
int ObSelectLogPlan::inner_generate_merge_set_plans(const EqualSets &equal_sets,
const ObIArray<ObRawExpr*> &left_set_keys,
const ObIArray<ObRawExpr*> &right_set_keys,
const ObIArray<MergeKeyInfo*> &left_merge_keys,
const ObSelectStmt::SetOperator set_op,
ObIArray<CandidatePlan> &left_candidates,
ObIArray<CandidatePlan> &right_candidates,
const bool ignore_hint,
const bool can_ignore_merge_plan,
const bool no_hash_plans,
ObIArray<CandidatePlan> &merge_set_plans)
{
int ret = OB_SUCCESS;
int64_t set_methods = 0;
int64_t best_prefix_pos = 0;
bool best_need_sort = false;
MergeKeyInfo *merge_key = NULL;
ObSEArray<OrderItem, 4> best_order_items;
ObLogicalOperator *left_child = NULL;
ObLogicalOperator *right_child = NULL;
CandidatePlan candidate_plan;
if (OB_UNLIKELY(left_candidates.empty()) || OB_UNLIKELY(right_candidates.empty()) ||
OB_ISNULL(left_child = left_candidates.at(0).plan_tree_) ||
OB_ISNULL(right_child = right_candidates.at(0).plan_tree_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(left_child), K(right_child), K(ret));
} else if (OB_FAIL(get_distributed_set_methods(equal_sets,
left_set_keys,
right_set_keys,
set_op,
MERGE_SET,
*left_child,
*right_child,
ignore_hint,
set_methods))) {
LOG_WARN("failed to get distributed set method", K(ret));
} else if (set_methods == 0) {
LOG_TRACE("no distributed merge set methods");
} else {
LOG_TRACE("distributed merge set methods", K(set_methods));
for (int64_t i = 0; OB_SUCC(ret) && i < left_candidates.count(); i++) {
if (OB_ISNULL(left_child = left_candidates.at(i).plan_tree_) ||
OB_ISNULL(merge_key = left_merge_keys.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(left_child), K(merge_key), K(ret));
} else if (merge_key->need_sort_ && !merge_key->order_needed_) {
// when merge_plan can not be ignore, need to check merge_key->need_sort_&merge_key->order_needed_s
// if no further order needed, not generate merge style plan
OPT_TRACE("no further order needed, merge set plans is not generated");
OPT_TRACE(" can_ignore merge plan", can_ignore_merge_plan);
OPT_TRACE(" merge_key need sort", merge_key->need_sort_);
OPT_TRACE(" merge_key order is needed", merge_key->order_needed_);
} else {
for (int64_t j = DistAlgo::DIST_BASIC_METHOD;
OB_SUCC(ret) && j <= DistAlgo::DIST_MAX_JOIN_METHOD; j = (j << 1)) {
if (set_methods & j) {
DistAlgo dist_algo = get_dist_algo(j);
const int64_t in_parallel = ObOptimizerUtil::get_join_style_parallel(left_child->get_parallel(),
right_candidates.at(0).plan_tree_->get_parallel(),
dist_algo, true);
if (OB_FAIL(get_minimal_cost_set_plan(in_parallel,
*left_child,
*merge_key,
right_set_keys,
right_candidates,
dist_algo,
best_order_items,
right_child,
best_need_sort,
best_prefix_pos,
can_ignore_merge_plan))) {
LOG_WARN("failed to get minimal cost set path", K(ret));
} else if (NULL == right_child) {
/*do nothing*/
} else if (OB_FAIL(create_merge_set_plan(equal_sets,
left_child,
right_child,
left_set_keys,
right_set_keys,
set_op,
dist_algo,
merge_key->order_directions_,
merge_key->map_array_,
merge_key->order_items_,
merge_key->need_sort_,
merge_key->prefix_pos_,
best_order_items,
best_need_sort,
best_prefix_pos,
candidate_plan))) {
LOG_WARN("failed to create merge set", K(ret));
} else if (OB_FAIL(merge_set_plans.push_back(candidate_plan))) {
LOG_WARN("failed to add merge plan", K(ret));
} else { /*do nothing*/ }
}
}
}
}
}
return ret;
}
int ObSelectLogPlan::get_minimal_cost_set_plan(const int64_t in_parallel,
const ObLogicalOperator &left_child,
const MergeKeyInfo &left_merge_key,
const ObIArray<ObRawExpr*> &right_set_exprs,
const ObIArray<CandidatePlan> &right_candidates,
const DistAlgo set_dist_algo,
ObIArray<OrderItem> &best_order_items,
ObLogicalOperator *&best_plan,
bool &best_need_sort,
int64_t &best_prefix_pos,
const bool can_ignore_merge_plan)
{
int ret = OB_SUCCESS;
double best_cost = 0.0;
double right_path_cost = 0.0;
int64_t right_prefix_pos = 0;
bool right_need_sort = false;
int64_t out_parallel = ObGlobalHint::UNSET_PARALLEL;
ObSEArray<ObRawExpr*, 8> right_order_exprs;
ObSEArray<OrderItem, 8> temp_order_items;
ObSEArray<OrderItem, 8> right_order_items;
best_plan = NULL;
best_need_sort = false;
best_prefix_pos = 0;
EstimateCostInfo info;
double right_output_rows = 0.0;
double right_orig_cost = 0.0;
for (int64_t i = 0; OB_SUCC(ret) && i < right_candidates.count(); i++) {
ObLogicalOperator *right_child = NULL;
ObLogPlan *right_plan = NULL;
right_order_exprs.reset();
temp_order_items.reset();
right_order_items.reset();
if (OB_ISNULL(right_child = right_candidates.at(i).plan_tree_) ||
OB_ISNULL(right_plan = right_child->get_plan())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(right_child), K(right_plan), K(ret));
} else if (DistAlgo::DIST_PARTITION_WISE == set_dist_algo &&
!is_set_partition_wise_valid(left_child, *right_child)) {
/*do nothing*/
} else if (!is_set_repart_valid(left_child, *right_child, set_dist_algo)) {
/*do nothing*/
} else if (OB_UNLIKELY(ObGlobalHint::DEFAULT_PARALLEL > (out_parallel = right_child->get_parallel())
|| ObGlobalHint::DEFAULT_PARALLEL > in_parallel)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected parallel degree", K(ret), K(out_parallel), K(in_parallel));
} else if (OB_FAIL(ObOptimizerUtil::adjust_exprs_by_mapping(right_set_exprs,
left_merge_key.map_array_,
right_order_exprs))) {
LOG_WARN("failed to adjust exprs by mapping", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::make_sort_keys(right_order_exprs,
left_merge_key.order_directions_,
temp_order_items))) {
LOG_WARN("failed to make sort keys", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::simplify_ordered_exprs(right_child->get_fd_item_set(),
right_child->get_output_equal_sets(),
right_child->get_output_const_exprs(),
get_onetime_query_refs(),
temp_order_items,
right_order_items))) {
LOG_WARN("failed to simplify ordered exprs", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::check_need_sort(right_order_items,
right_child->get_op_ordering(),
right_child->get_fd_item_set(),
right_child->get_output_equal_sets(),
right_child->get_output_const_exprs(),
get_onetime_query_refs(),
right_child->get_is_at_most_one_row(),
right_need_sort,
right_prefix_pos))) {
LOG_WARN("failed to check need sort", K(ret));
} else if ((DistAlgo::DIST_PARTITION_WISE == set_dist_algo ||
DistAlgo::DIST_BASIC_METHOD == set_dist_algo) &&
can_ignore_merge_plan && left_merge_key.need_sort_ && right_need_sort) {
// when bosh side need sort and merge key order is not needed, do not generer merge set
OPT_TRACE("bosh side need sort but merge key order is not needed, merge set plans is not generated");
} else {
bool is_fully_partition_wise = DistAlgo::DIST_PARTITION_WISE == set_dist_algo &&
!left_child.is_exchange_allocated() && !right_child->is_exchange_allocated();
bool is_local_order = right_child->get_is_local_order() && !is_fully_partition_wise;
ObPQDistributeMethod::Type dist_method = ObOptimizerUtil::get_right_dist_method
(*right_child->get_sharding(), set_dist_algo);
right_plan->get_selectivity_ctx().init_op_ctx(
&right_child->get_output_equal_sets(), right_child->get_card());
info.reset();
// is single, may allocate exchange above, set need_parallel_ as 1 and compute exchange cost in cost_sort_and_exchange
info.need_parallel_ = right_child->is_single() ? ObGlobalHint::DEFAULT_PARALLEL : in_parallel;
if (OB_FAIL(right_child->re_est_cost(info, right_output_rows, right_orig_cost))) {
LOG_WARN("failed to re estimate cost", K(ret));
} else if (OB_FAIL(ObOptEstCost::cost_sort_and_exchange(&right_plan->get_update_table_metas(),
&right_plan->get_selectivity_ctx(),
dist_method,
right_child->is_distributed(),
is_local_order,
right_output_rows,
right_child->get_width(),
right_orig_cost,
out_parallel,
left_child.get_server_cnt(),
in_parallel,
right_order_items,
right_need_sort,
right_prefix_pos,
right_path_cost,
get_optimizer_context().get_cost_model_type()))) {
LOG_WARN("failed to compute cost for merge join style op", K(ret));
} else if (NULL == best_plan || right_path_cost < best_cost) {
if (OB_FAIL(best_order_items.assign(right_order_items))) {
LOG_WARN("failed to assign exprs", K(ret));
} else {
best_plan = right_child;
best_need_sort = right_need_sort;
best_prefix_pos = right_prefix_pos;
best_cost = right_path_cost;
}
}
}
}
return ret;
}
int ObSelectLogPlan::convert_set_order_item(const ObDMLStmt *stmt,
const ObIArray<ObRawExpr*> &select_exprs,
ObIArray<OrderItem> &order_items)
{
/*
*the output order_items may have following case
* 1. select c1,c2,c3 from t1 union select c1,c2,c3 from t2 order by c2,c1,c3
* --> full match, the merge sort key could be [c2,c1,c3]
* 2. select c1,c2,c3 from t1 union select c1,c2,c3 from t2 order by c2,c1+c3,c1,
* --> pre match, the merge sort key could use pre match [c2] to adjust its merge sort key as [c2,c1,c3]
* 3. select c1,c2,c3 from t1 union select c1,c2,c3 from t2 order by c2,c1+c3,c1,
* --> no match, set order item not match any, merge sort key directly use it child select expr as [c1,c2,c3]
*/
int ret = OB_SUCCESS;
bool found = true;
ObSEArray<ObRawExpr*, 4> order_exprs;
ObSEArray<ObOrderDirection, 2> directions;
if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpect null pointer", K(ret));
} else if (OB_UNLIKELY(!(stmt->is_select_stmt() && static_cast<const ObSelectStmt*>(stmt)->is_set_stmt()))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("only use for set stmt to convert its order items expr with child order expr", K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && found && i < stmt->get_order_items().count(); ++i) {
int64_t idx = -1;
ObRawExpr *order_expr = NULL;
if (OB_ISNULL(order_expr = stmt->get_order_items().at(i).expr_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(stmt->get_order_items().at(i)));
} else if (!order_expr->is_set_op_expr()) {
found = false;
} else if (-1 == (idx = static_cast<ObSetOpRawExpr*>(order_expr)->get_idx())){
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected index", K(idx), K(ret));
} else if (OB_FAIL(order_exprs.push_back(select_exprs.at(idx)))){
LOG_WARN("fail to push back expr", K(ret));
} else if (OB_FAIL(directions.push_back(stmt->get_order_items().at(i).order_type_))) {
LOG_WARN("failed to push back", K(ret));
}
}
if (OB_FAIL(ret)) {
//do nothing
} else if (OB_FAIL(ObOptimizerUtil::make_sort_keys(order_exprs, directions, order_items))) {
LOG_WARN("failed to make sort keys", K(ret));
}
}
return ret;
}
int ObSelectLogPlan::create_merge_set_key(const ObIArray<OrderItem> &set_order_items,
const ObIArray<ObRawExpr*> &merge_exprs,
const EqualSets &equal_sets,
MergeKeyInfo &merge_key)
{
int ret = OB_SUCCESS;
ObSEArray<ObRawExpr*, 2> sort_exprs;
ObSEArray<ObOrderDirection, 2> directions;
ObSEArray<int64_t, 2> sort_map;
if (sort_exprs.empty() && !set_order_items.empty()) {
// find direction in order by exprs
if (OB_FAIL(ObOptimizerUtil::create_interesting_merge_key(merge_exprs, set_order_items, equal_sets, sort_exprs, directions, sort_map))) {
LOG_WARN("failed to create interesting key", K(ret));
} else {
LOG_TRACE("succeed to create merge key use order by items", K(sort_exprs), K(directions));
}
}
if (OB_SUCC(ret) && sort_exprs.empty()) {
for (int64_t i = 0; OB_SUCC(ret) && i < merge_exprs.count(); ++i) {
if (OB_FAIL(sort_exprs.push_back(merge_exprs.at(i)))) {
LOG_WARN("failed to push back", K(ret));
} else if (OB_FAIL(directions.push_back(default_asc_direction()))) {
LOG_WARN("failed to push back", K(ret));
} else if (OB_FAIL(sort_map.push_back(i))) {
LOG_WARN("failed to push back", K(ret));
}
}
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(merge_key.order_exprs_.assign(sort_exprs))) {
LOG_WARN("failed to assign exprs", K(ret));
} else if (OB_FAIL(merge_key.order_directions_.assign(directions))) {
LOG_WARN("failed to assign exprs", K(ret));
} else if (OB_FAIL(merge_key.map_array_.assign(sort_map))) {
LOG_WARN("failed to assign exprs", K(ret));
}
return ret;
}
int ObSelectLogPlan::decide_merge_set_sort_key(const ObIArray<OrderItem> &set_order_items,
const ObIArray<OrderItem> &input_ordering,
const ObFdItemSet &fd_item_set,
const EqualSets &equal_sets,
const ObIArray<ObRawExpr*> &const_exprs,
const ObIArray<ObRawExpr*> &exec_ref_exprs,
const bool is_at_most_one_row,
const ObIArray<ObRawExpr*> &merge_exprs,
const ObIArray<ObOrderDirection> &default_directions,
MergeKeyInfo &merge_key)
{
int ret = OB_SUCCESS;
int64_t prefix_count = -1;
bool input_ordering_all_used = false;
ObSEArray<OrderItem, 8> order_items;
ObSEArray<OrderItem, 8> final_items;
if (OB_FAIL(merge_key.order_exprs_.assign(merge_exprs))) {
LOG_WARN("failed to assign exprs", K(ret));
} else if (OB_FAIL(merge_key.order_directions_.assign(default_directions))) {
LOG_WARN("failed to assign exprs", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::adjust_exprs_by_ordering(merge_key.order_exprs_,
input_ordering,
equal_sets,
const_exprs,
exec_ref_exprs,
prefix_count,
input_ordering_all_used,
merge_key.order_directions_,
&merge_key.map_array_))) {
LOG_WARN("failed to adjust expr by ordering", K(ret));
} else if (!input_ordering_all_used && prefix_count <= 0 && OB_FAIL(create_merge_set_key(set_order_items, merge_exprs, equal_sets, merge_key))) {
LOG_WARN("failed to create merge set key", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::make_sort_keys(merge_key.order_exprs_,
merge_key.order_directions_,
order_items))) {
LOG_WARN("failed to make sort keys", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::simplify_ordered_exprs(fd_item_set,
equal_sets,
const_exprs,
exec_ref_exprs,
order_items,
final_items))) {
LOG_WARN("failed to simply ordered exprs", K(ret));
} else if (OB_FAIL(merge_key.order_items_.assign(final_items))) {
LOG_WARN("failed to assign final items", K(ret));
} else if (input_ordering_all_used) {
merge_key.need_sort_ = false;
} else if (OB_FAIL(ObOptimizerUtil::check_need_sort(merge_key.order_items_,
input_ordering,
fd_item_set,
equal_sets,
const_exprs,
exec_ref_exprs,
is_at_most_one_row,
merge_key.need_sort_,
merge_key.prefix_pos_))) {
LOG_WARN("failed to check need sort", K(ret));
}
return ret;
}
int ObSelectLogPlan::init_merge_set_structure(ObIAllocator &allocator,
const ObIArray<CandidatePlan> &plans,
const ObIArray<ObRawExpr*> &select_exprs,
ObIArray<MergeKeyInfo*> &merge_keys,
const bool can_ignore_merge_plan)
{
int ret = OB_SUCCESS;
const ObLogicalOperator *child = NULL;
ObSEArray<ObOrderDirection, 8> default_directions;
MergeKeyInfo *interesting_key = NULL;
MergeKeyInfo *merge_key = NULL;
ObSEArray<OrderItem, 8> order_items;
if (OB_FAIL(convert_set_order_item(get_stmt(), select_exprs, order_items))) {
LOG_WARN("failed to convert order item", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::get_default_directions(select_exprs.count(), default_directions))) {
LOG_WARN("failed to get default directions", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && i < plans.count(); ++i) {
if (OB_ISNULL(merge_key = static_cast<MergeKeyInfo*>(allocator.alloc(sizeof(MergeKeyInfo))))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("failed to alloc merge key info", K(ret));
} else if (OB_ISNULL(child = plans.at(i).plan_tree_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(child));
} else if (OB_FALSE_IT(merge_key = new (merge_key) MergeKeyInfo(allocator,
select_exprs.count()))) {
} else if (OB_FAIL(decide_merge_set_sort_key(order_items,
child->get_op_ordering(),
child->get_fd_item_set(),
child->get_output_equal_sets(),
child->get_output_const_exprs(),
get_onetime_query_refs(),
child->get_is_at_most_one_row(),
select_exprs,
default_directions,
*merge_key))) {
LOG_WARN("failed to decide sort key for merge set", K(ret));
} else if (OB_FAIL(merge_keys.push_back(merge_key))) {
LOG_WARN("failed to push back merge key", K(ret));
} else if (can_ignore_merge_plan) {
bool is_match = false;
if (OB_FAIL(ObOptimizerUtil::is_order_by_match(order_items,
merge_key->order_items_,
child->get_output_equal_sets(),
child->get_output_const_exprs(),
is_match))) {
LOG_WARN("failed to check is order by match", K(ret));
} else if (!is_match) {
merge_key->order_needed_ = false;
LOG_TRACE("ordering is not math order by");
}
}
}
return ret;
}
int ObSelectLogPlan::create_merge_set_plan(const EqualSets &equal_sets,
ObLogicalOperator *left_child,
ObLogicalOperator *right_child,
const ObIArray<ObRawExpr*> &left_set_keys,
const ObIArray<ObRawExpr*> &right_set_keys,
const ObSelectStmt::SetOperator set_op,
const DistAlgo dist_set_method,
const ObIArray<ObOrderDirection> &order_directions,
const ObIArray<int64_t> &map_array,
const ObIArray<OrderItem> &left_sort_keys,
const bool left_need_sort,
const int64_t left_prefix_pos,
const ObIArray<OrderItem> &right_sort_keys,
const bool right_need_sort,
const int64_t right_prefix_pos,
CandidatePlan &merge_plan)
{
int ret = OB_SUCCESS;
ObLogPlan *left_plan = NULL;
ObLogPlan *right_plan = NULL;
ObExchangeInfo left_exch_info;
ObExchangeInfo right_exch_info;
if (OB_ISNULL(left_child) || OB_ISNULL(right_child) ||
OB_ISNULL(left_plan = left_child->get_plan()) ||
OB_ISNULL(right_plan = right_child->get_plan())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(left_child), K(right_child), K(left_plan), K(right_plan));
} else {
bool is_fully_partition_wise = DistAlgo::DIST_PARTITION_WISE == dist_set_method &&
!left_child->is_exchange_allocated() && !right_child->is_exchange_allocated();
bool is_left_local_order = left_child->get_is_local_order() && !is_fully_partition_wise;
bool is_right_local_order = right_child->get_is_local_order() && !is_fully_partition_wise;
if (OB_FAIL(compute_set_exchange_info(equal_sets,
*left_child,
*right_child,
left_set_keys,
right_set_keys,
set_op,
dist_set_method,
left_exch_info,
right_exch_info))) {
LOG_WARN("failed to compute set exchange info", K(ret));
} else if (OB_FAIL(left_plan->allocate_sort_and_exchange_as_top(left_child,
left_exch_info,
left_sort_keys,
left_need_sort,
left_prefix_pos,
is_left_local_order))) {
LOG_WARN("failed to allocate operator for set path", K(ret));
} else if (OB_FAIL(right_plan->allocate_sort_and_exchange_as_top(right_child,
right_exch_info,
right_sort_keys,
right_need_sort,
right_prefix_pos,
is_right_local_order))) {
LOG_WARN("failed to allocate operator for set path", K(ret));
} else if (OB_FAIL(allocate_distinct_set_as_top(left_child,
right_child,
SetAlgo::MERGE_SET,
dist_set_method,
merge_plan.plan_tree_,
&order_directions,
&map_array))) {
LOG_WARN("failed to allocate distinct set as top", K(ret));
} else {
LOG_TRACE("succeed to create merge set plan", K(left_sort_keys), K(right_sort_keys),
K(order_directions), K(map_array), K(left_need_sort), K(right_need_sort));
}
}
return ret;
}
int ObSelectLogPlan::get_allowed_branch_order(const bool ignore_hint,
const ObSelectStmt::SetOperator set_op,
bool &no_swap,
bool &swap)
{
int ret = OB_SUCCESS;
no_swap = true;
swap = ObSelectStmt::EXCEPT != set_op;
bool hint_valid = false;
bool need_swap = false;
if (ignore_hint) {
/* do nothing */
} else if (OB_FAIL(get_log_plan_hint().check_valid_set_left_branch(get_stmt(),
hint_valid,
need_swap))) {
LOG_WARN("failed to check valid set left branch", K(ret));
} else if (!hint_valid) {
/* do nothing */
} else {
no_swap &= !need_swap;
swap &= need_swap;
}
return ret;
}
int ObSelectLogPlan::generate_hash_set_plans(const EqualSets &equal_sets,
const ObIArray<ObRawExpr*> &left_set_keys,
const ObIArray<ObRawExpr*> &right_set_keys,
const ObSelectStmt::SetOperator set_op,
const ObIArray<CandidatePlan> &left_best_plans,
const ObIArray<CandidatePlan> &right_best_plans,
const bool ignore_hint,
ObIArray<CandidatePlan> &hash_set_plans)
{
int ret = OB_SUCCESS;
OPT_TRACE("start generate hash set plans");
bool no_swap = false;
bool swap = false;
if (OB_FAIL(get_allowed_branch_order(ignore_hint, set_op, no_swap, swap))) {
LOG_WARN("failed to get allowed branch order", K(ret));
} else if (no_swap && OB_FAIL(inner_generate_hash_set_plans(equal_sets,
left_set_keys,
right_set_keys,
set_op,
left_best_plans,
right_best_plans,
ignore_hint,
hash_set_plans))) {
LOG_WARN("failed to generate hash set plans", K(ret));
} else if (swap && OB_FAIL(inner_generate_hash_set_plans(equal_sets,
right_set_keys,
left_set_keys,
set_op,
right_best_plans,
left_best_plans,
ignore_hint,
hash_set_plans))) {
LOG_WARN("failed to generate hash set plans", K(ret));
} else { /*do nothing*/ }
return ret;
}
int ObSelectLogPlan::inner_generate_hash_set_plans(const EqualSets &equal_sets,
const ObIArray<ObRawExpr*> &left_set_keys,
const ObIArray<ObRawExpr*> &right_set_keys,
const ObSelectStmt::SetOperator set_op,
const ObIArray<CandidatePlan> &left_best_plans,
const ObIArray<CandidatePlan> &right_best_plans,
const bool ignore_hint,
ObIArray<CandidatePlan> &hash_set_plans)
{
int ret = OB_SUCCESS;
ObLogicalOperator *tmp = NULL;
CandidatePlan candidate_plan;
for (int64_t i = 0; OB_SUCC(ret) && i < left_best_plans.count(); i++) {
ObLogicalOperator *left_best_plan = NULL;
if (OB_ISNULL(left_best_plan = left_best_plans.at(i).plan_tree_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else {
for (int64_t j = 0; OB_SUCC(ret) && j < right_best_plans.count(); j++) {
ObLogicalOperator *right_best_plan = NULL;
int64_t set_methods = 0;
if (OB_ISNULL(right_best_plan = right_best_plans.at(j).plan_tree_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(get_distributed_set_methods(equal_sets,
left_set_keys,
right_set_keys,
set_op,
HASH_SET,
*left_best_plan,
*right_best_plan,
ignore_hint,
set_methods))) {
LOG_WARN("failed to get distributed set methods", K(ret));
} else {
LOG_TRACE("distributed hash set methods", K(set_methods));
for (int64_t k = DistAlgo::DIST_BASIC_METHOD;
OB_SUCC(ret) && k < DistAlgo::DIST_MAX_JOIN_METHOD; k = (k << 1)) {
DistAlgo dist_algo = get_dist_algo(k);
if ((set_methods & k) && (DistAlgo::DIST_PARTITION_WISE != dist_algo ||
is_set_partition_wise_valid(*left_best_plan, *right_best_plan)) &&
is_set_repart_valid(*left_best_plan, *right_best_plan, dist_algo)) {
if (OB_FAIL(create_hash_set_plan(equal_sets,
left_best_plan,
right_best_plan,
left_set_keys,
right_set_keys,
set_op,
dist_algo,
candidate_plan))) {
LOG_WARN("failed to create hash set", K(ret));
} else if (OB_FAIL(hash_set_plans.push_back(candidate_plan))) {
LOG_WARN("failed to add hash plan", K(ret));
} else { /*do nothing*/ }
}
}
}
}
}
}
return ret;
}
int ObSelectLogPlan::create_hash_set_plan(const EqualSets &equal_sets,
ObLogicalOperator *left_child,
ObLogicalOperator *right_child,
const ObIArray<ObRawExpr*> &left_set_keys,
const ObIArray<ObRawExpr*> &right_set_keys,
const ObSelectStmt::SetOperator set_op,
DistAlgo dist_set_method,
CandidatePlan &hash_plan)
{
int ret = OB_SUCCESS;
ObExchangeInfo left_exch_info;
ObExchangeInfo right_exch_info;
ObLogPlan *left_log_plan = NULL;
ObLogPlan *right_log_plan = NULL;
const ObSelectStmt *select_stmt = get_stmt();
if (OB_ISNULL(left_child) || OB_ISNULL(right_child) ||
OB_ISNULL(left_log_plan = left_child->get_plan()) ||
OB_ISNULL(right_log_plan = right_child->get_plan())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(left_child), K(right_child),
K(left_log_plan), K(right_log_plan), K(ret));
} else if (OB_FAIL(compute_set_exchange_info(equal_sets,
*left_child,
*right_child,
left_set_keys,
right_set_keys,
set_op,
dist_set_method,
left_exch_info,
right_exch_info))) {
LOG_WARN("failed to compute set exchange info", K(ret));
} else if (left_exch_info.need_exchange() &&
OB_FAIL(left_log_plan->allocate_exchange_as_top(left_child, left_exch_info))) {
LOG_WARN("failed to allocate exchange as top", K(ret));
} else if (right_exch_info.need_exchange() &&
OB_FAIL(right_log_plan->allocate_exchange_as_top(right_child, right_exch_info))) {
LOG_WARN("failed to allocate exchange as top", K(ret));
} else if (OB_FAIL(allocate_distinct_set_as_top(left_child,
right_child,
SetAlgo::HASH_SET,
dist_set_method,
hash_plan.plan_tree_))) {
LOG_WARN("failed to allocate distinct set as top", K(ret));
} else {
LOG_TRACE("succeed to create hash set plan");
}
return ret;
}
int ObSelectLogPlan::allocate_distinct_set_as_top(ObLogicalOperator *left_child,
ObLogicalOperator *right_child,
SetAlgo set_method,
DistAlgo dist_set_method,
ObLogicalOperator *&top,
const ObIArray<ObOrderDirection> *order_directions,
const ObIArray<int64_t> *map_array)
{
int ret = OB_SUCCESS;
ObLogSet *set_op = NULL;
const ObSelectStmt *select_stmt = NULL;
top = NULL;
if (OB_ISNULL(select_stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_ISNULL((set_op = static_cast<ObLogSet*>(get_log_op_factory().
allocate(*this, LOG_SET))))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("Allocate memory for ObLogSet failed", K(ret));
} else if (NULL != order_directions && OB_FAIL(set_op->set_set_directions(*order_directions))) {
LOG_WARN("failed to set order directions", K(ret));
} else if (NULL != map_array && OB_FAIL(set_op->set_map_array(*map_array))) {
LOG_WARN("failed to set map array", K(ret));
} else {
set_op->set_left_child(left_child);
set_op->set_right_child(right_child);
set_op->assign_set_distinct(select_stmt->is_set_distinct());
set_op->assign_set_op(select_stmt->get_set_op());
set_op->set_algo_type(set_method);
set_op->set_distributed_algo(dist_set_method);
if (OB_FAIL(set_op->compute_property())) {
LOG_WARN("failed to compute property", K(ret));
} else {
top = set_op;
}
}
return ret;
}
int ObSelectLogPlan::compute_set_exchange_info(const EqualSets &equal_sets,
ObLogicalOperator &left_child,
ObLogicalOperator &right_child,
const ObIArray<ObRawExpr*> &left_set_keys,
const ObIArray<ObRawExpr*> &right_set_keys,
const ObSelectStmt::SetOperator set_op,
DistAlgo set_method,
ObExchangeInfo &left_exch_info,
ObExchangeInfo &right_exch_info)
{
int ret = OB_SUCCESS;
const ObLogicalOperator *parallel_source = NULL;
left_exch_info.dist_method_ = ObPQDistributeMethod::NONE;
right_exch_info.dist_method_ = ObPQDistributeMethod::NONE;
if (DistAlgo::DIST_BASIC_METHOD == set_method ||
DistAlgo::DIST_PARTITION_WISE == set_method ||
DistAlgo::DIST_SET_PARTITION_WISE == set_method ||
DistAlgo::DIST_NONE_ALL == set_method ||
DistAlgo::DIST_ALL_NONE == set_method) {
/*do nothing*/
} else if (DistAlgo::DIST_PULL_TO_LOCAL == set_method) {
if (left_child.is_sharding()) {
left_exch_info.dist_method_ = ObPQDistributeMethod::LOCAL;
}
if (right_child.is_sharding()) {
right_exch_info.dist_method_ = ObPQDistributeMethod::LOCAL;
}
} else if (DistAlgo::DIST_HASH_HASH == set_method) {
parallel_source = left_child.get_parallel() > right_child.get_parallel()
? &left_child : &right_child;
ObShardingInfo *sharding_info = NULL;
if (OB_FAIL(compute_set_hash_hash_sharding(equal_sets,
left_set_keys,
right_set_keys,
sharding_info))) {
LOG_WARN("failed to compute set hash-hash sharding", K(ret));
} else if (OB_ISNULL(sharding_info)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(left_exch_info.append_hash_dist_expr(left_set_keys))) {
LOG_WARN("failed to append hash dist expr", K(ret));
} else if (OB_FAIL(right_exch_info.append_hash_dist_expr(right_set_keys))) {
LOG_WARN("failed to append hash dist expr", K(ret));
} else {
left_exch_info.strong_sharding_ = sharding_info;
left_exch_info.dist_method_ = ObPQDistributeMethod::HASH;
right_exch_info.strong_sharding_ = sharding_info;
right_exch_info.dist_method_ = ObPQDistributeMethod::HASH;
}
} else if (DistAlgo::DIST_HASH_NONE == set_method) {
parallel_source = &right_child;
if (OB_FAIL(compute_single_side_hash_distribution_info(equal_sets,
left_set_keys,
right_set_keys,
right_child,
left_exch_info))) {
LOG_WARN("failed to compute repartition distribution info", K(ret));
} else if (ObSelectStmt::SetOperator::UNION == set_op ||
ObSelectStmt::SetOperator::EXCEPT == set_op) {
ObPQDistributeMethod::Type dist_method = ObPQDistributeMethod::RANDOM;
bool is_unique = false;
if (OB_FAIL(ObOptimizerUtil::is_exprs_unique(left_set_keys,
left_child.get_table_set(),
left_child.get_fd_item_set(),
left_child.get_output_equal_sets(),
left_child.get_output_const_exprs(),
is_unique))) {
LOG_WARN("failed to check is left unique", K(ret));
} else if (!is_unique) {
dist_method = ObPQDistributeMethod::HASH;
}
if (OB_SUCC(ret)) {
left_exch_info.unmatch_row_dist_method_ = dist_method;
left_exch_info.strong_sharding_ = get_optimizer_context().get_distributed_sharding();
}
} else {
if (OB_FAIL(left_exch_info.weak_sharding_.assign(right_child.get_weak_sharding()))) {
LOG_WARN("failed to assign weak sharding", K(ret));
} else {
left_exch_info.unmatch_row_dist_method_ = ObPQDistributeMethod::DROP;
left_exch_info.strong_sharding_ = right_child.get_strong_sharding();
}
}
} else if (DistAlgo::DIST_NONE_HASH == set_method) {
parallel_source = &left_child;
if (OB_FAIL(compute_single_side_hash_distribution_info(equal_sets,
right_set_keys,
left_set_keys,
left_child,
right_exch_info))) {
LOG_WARN("failed to compute repartition distribution info", K(ret));
} else if (ObSelectStmt::SetOperator::UNION == set_op) {
ObPQDistributeMethod::Type dist_method = ObPQDistributeMethod::RANDOM;
bool is_unique = false;
if (OB_FAIL(ObOptimizerUtil::is_exprs_unique(right_set_keys,
right_child.get_table_set(),
right_child.get_fd_item_set(),
right_child.get_output_equal_sets(),
right_child.get_output_const_exprs(),
is_unique))) {
LOG_WARN("failed to check is right unique", K(ret));
} else if (!is_unique) {
dist_method = ObPQDistributeMethod::HASH;
}
if (OB_SUCC(ret)) {
right_exch_info.unmatch_row_dist_method_ = dist_method;
right_exch_info.strong_sharding_ = get_optimizer_context().get_distributed_sharding();
}
} else {
if (OB_FAIL(right_exch_info.weak_sharding_.assign(left_child.get_weak_sharding()))) {
LOG_WARN("failed to assign weak sharding", K(ret));
} else {
right_exch_info.unmatch_row_dist_method_ = ObPQDistributeMethod::DROP;
right_exch_info.strong_sharding_ = left_child.get_strong_sharding();
}
}
} else if (DistAlgo::DIST_PARTITION_NONE == set_method) {
parallel_source = &right_child;
if (OB_FAIL(compute_repartition_distribution_info(equal_sets,
left_set_keys,
right_set_keys,
right_child,
left_exch_info))) {
LOG_WARN("failed to compute repartition distribution info", K(ret));
} else if (ObSelectStmt::SetOperator::UNION == set_op ||
ObSelectStmt::SetOperator::EXCEPT == set_op) {
ObPQDistributeMethod::Type dist_method = ObPQDistributeMethod::RANDOM;
bool is_unique = false;
if (OB_FAIL(ObOptimizerUtil::is_exprs_unique(left_set_keys,
left_child.get_table_set(),
left_child.get_fd_item_set(),
left_child.get_output_equal_sets(),
left_child.get_output_const_exprs(),
is_unique))) {
LOG_WARN("failed to check is left unique", K(ret));
} else if (!is_unique) {
dist_method = ObPQDistributeMethod::HASH;
if (OB_FAIL(left_exch_info.append_hash_dist_expr(left_set_keys))) {
LOG_WARN("failed to append hash dist expr", K(ret));
}
}
if (OB_SUCC(ret)) {
left_exch_info.unmatch_row_dist_method_ = dist_method;
left_exch_info.strong_sharding_ = get_optimizer_context().get_distributed_sharding();
}
} else {
if (OB_FAIL(left_exch_info.weak_sharding_.assign(right_child.get_weak_sharding()))) {
LOG_WARN("failed to assign weak sharding", K(ret));
} else {
left_exch_info.unmatch_row_dist_method_ = ObPQDistributeMethod::DROP;
left_exch_info.strong_sharding_ = right_child.get_strong_sharding();
}
}
} else if (DistAlgo::DIST_NONE_PARTITION == set_method) {
parallel_source = &left_child;
if (OB_FAIL(compute_repartition_distribution_info(equal_sets,
right_set_keys,
left_set_keys,
left_child,
right_exch_info))) {
LOG_WARN("failed to compute repartition distribution info", K(ret));
} else if (ObSelectStmt::SetOperator::UNION == set_op) {
ObPQDistributeMethod::Type dist_method = ObPQDistributeMethod::RANDOM;
bool is_unique = false;
if (OB_FAIL(ObOptimizerUtil::is_exprs_unique(right_set_keys,
right_child.get_table_set(),
right_child.get_fd_item_set(),
right_child.get_output_equal_sets(),
right_child.get_output_const_exprs(),
is_unique))) {
LOG_WARN("failed to check is right unique", K(ret));
} else if (!is_unique) {
dist_method = ObPQDistributeMethod::HASH;
if (OB_FAIL(right_exch_info.append_hash_dist_expr(right_set_keys))) {
LOG_WARN("failed to append hash dist expr", K(ret));
}
}
if (OB_SUCC(ret)) {
right_exch_info.unmatch_row_dist_method_ = dist_method;
right_exch_info.strong_sharding_ = get_optimizer_context().get_distributed_sharding();
}
} else {
if (OB_FAIL(right_exch_info.weak_sharding_.assign(left_child.get_weak_sharding()))) {
LOG_WARN("failed to assign weak sharding", K(ret));
} else {
right_exch_info.unmatch_row_dist_method_ = ObPQDistributeMethod::DROP;
right_exch_info.strong_sharding_ = left_child.get_strong_sharding();
}
}
} else {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
}
if (OB_FAIL(ret) || NULL == parallel_source) {
} else if (OB_FAIL(left_exch_info.server_list_.assign(parallel_source->get_server_list()))
|| OB_FAIL(right_exch_info.server_list_.assign(parallel_source->get_server_list()))) {
LOG_WARN("failed to assign server list", K(ret));
} else {
left_exch_info.parallel_ = parallel_source->get_parallel();
left_exch_info.server_cnt_ = parallel_source->get_server_cnt();
right_exch_info.parallel_ = parallel_source->get_parallel();
right_exch_info.server_cnt_ = parallel_source->get_server_cnt();
}
return ret;
}
int ObSelectLogPlan::compute_set_hash_hash_sharding(const EqualSets &equal_sets,
const ObIArray<ObRawExpr*> &left_keys,
const ObIArray<ObRawExpr*> &right_keys,
ObShardingInfo *&sharding)
{
int ret = OB_SUCCESS;
sharding = NULL;
if (OB_FAIL(get_cached_hash_sharding_info(left_keys, equal_sets, sharding))) {
LOG_WARN("failed to get cached hash sharding info", K(ret));
} else if (NULL != sharding) {
/*do nothing*/
} else if (OB_FAIL(get_cached_hash_sharding_info(right_keys, equal_sets, sharding))) {
LOG_WARN("failed to get cached hash sharding", K(ret));
} else if (NULL != sharding) {
/*do nothing*/
} else if (OB_ISNULL(sharding = reinterpret_cast<ObShardingInfo*>(get_allocator().alloc(sizeof(ObShardingInfo))))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("failed to allocate memory", K(ret));
} else {
sharding = new (sharding) ObShardingInfo();
sharding->set_distributed();
if (OB_FAIL(sharding->get_partition_keys().assign(left_keys))) {
LOG_WARN("failed to assign partition exprs", K(ret));
} else if (OB_FAIL(get_hash_dist_info().push_back(sharding))) {
LOG_WARN("failed to push back sharding info", K(ret));
} else { /*do nothing*/ }
}
return ret;
}
int ObSelectLogPlan::generate_normal_raw_plan()
{
int ret = OB_SUCCESS;
const ObSelectStmt *select_stmt = get_stmt();
OPT_TRACE("generate plan for ", select_stmt);
if (OB_ISNULL(select_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (select_stmt->is_set_stmt()) {
ret = SMART_CALL(generate_raw_plan_for_set());
} else if (0 == select_stmt->get_from_item_size()) {
ret = generate_raw_plan_for_expr_values();
} else {
ret = SMART_CALL(generate_raw_plan_for_plain_select());
}
return ret;
}
int ObSelectLogPlan::generate_dblink_raw_plan()
{
int ret = OB_SUCCESS;
ObQueryCtx *query_ctx = NULL;
// dblink_info hint
int64_t tx_id = -1;
int64_t tm_sessid = -1;
uint64_t dblink_id = OB_INVALID_ID;
const ObSelectStmt *stmt = get_stmt();
ObLogicalOperator *top = NULL;
if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null ptr", K(ret));
} else if (NULL == (query_ctx = stmt->get_query_ctx())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (FALSE_IT(dblink_id = stmt->get_dblink_id())) {
} else if (0 == dblink_id) { //dblink id = 0 means @!/@xxxx!
ObSQLSessionInfo *session = get_optimizer_context().get_session_info();
oceanbase::sql::ObReverseLink *reverse_dblink_info = NULL;
if (NULL == session) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), KP(session));
} else if (OB_FAIL(session->get_dblink_context().get_reverse_link(reverse_dblink_info))) {
LOG_WARN("failed to get reverse link info from session", K(ret), K(session->get_sessid()));
} else if (NULL == reverse_dblink_info) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else {
// set dblink_info, to unparse a link sql with dblink_info hint
query_ctx->get_query_hint_for_update().get_global_hint().merge_dblink_info_hint(
reverse_dblink_info->get_tx_id(),
reverse_dblink_info->get_tm_sessid());
LOG_DEBUG("set tx_id_ and tm_sessid to stmt",
K(reverse_dblink_info->get_tx_id()),
K(reverse_dblink_info->get_tm_sessid()));
}
} else {
// save dblink_info hint
tx_id = query_ctx->get_query_hint_for_update().get_global_hint().get_dblink_tx_id_hint();
tm_sessid = query_ctx->get_query_hint_for_update().get_global_hint().get_dblink_tm_sessid_hint();
// reset dblink hint, to unparse a link sql without dblink_info hint
query_ctx->get_query_hint_for_update().get_global_hint().reset_dblink_info_hint();
}
if (OB_FAIL(ret)) {
//do nothing
} else if (OB_FAIL(allocate_link_scan_as_top(top))) {
LOG_WARN("failed to allocate link dml as top", K(ret));
} else {
top->set_dblink_id(dblink_id);
ObLogLinkScan *link_scan = static_cast<ObLogLinkScan *>(top);
if (OB_FAIL(link_scan->set_link_stmt(stmt))) {
LOG_WARN("failed to set link stmt", K(ret));
} else if (0 == dblink_id) {
link_scan->set_reverse_link(true);
}
}
if (OB_FAIL(ret)) {
// do nothing
} else if (optimizer_context_.has_multiple_link_stmt()
&& OB_FAIL(allocate_material_as_top(top))) {
LOG_WARN("allocate material above link scan failed", K(ret));
} else if (OB_FAIL(make_candidate_plans(top))) {
LOG_WARN("failed to make candidate plans", K(ret));
} else {
top->mark_is_plan_root();
top->get_plan()->set_plan_root(top);
if (0 == dblink_id) {
// reset dblink info, to avoid affecting the next execution flow
query_ctx->get_query_hint_for_update().get_global_hint().reset_dblink_info_hint();
} else {
// restore dblink_info hint, ensure that the next execution process can get the correct dblink_info
query_ctx->get_query_hint_for_update().get_global_hint().merge_dblink_info_hint(tx_id, tm_sessid);
}
LOG_TRACE("succeed to allocate loglinkscan", K(dblink_id));
}
return ret;
}
int ObSelectLogPlan::allocate_link_scan_as_top(ObLogicalOperator *&old_top)
{
int ret = OB_SUCCESS;
ObLogLinkScan *link_scan = NULL;
const ObSelectStmt *stmt = get_stmt();
ObSEArray<ObRawExpr*, 4> select_exprs;
if (NULL != old_top) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("old_top should be null", K(ret), K(get_stmt()));
} else if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null ptr", K(ret), K(stmt));
} else if (OB_ISNULL(link_scan = static_cast<ObLogLinkScan *>(get_log_op_factory().
allocate(*this, LOG_LINK_SCAN)))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("failed to allocate link dml operator", K(ret));
} else if (OB_FAIL(stmt->get_select_exprs(select_exprs))) {
LOG_WARN("failed to get select exprs", K(ret));
} else if (OB_FAIL(link_scan->get_select_exprs().assign(select_exprs))) {
LOG_WARN("failed to assign select exprs", K(ret));
} else if (OB_FAIL(link_scan->compute_property())) {
LOG_WARN("failed to compute property", K(ret));
} else {
old_top = link_scan;
}
return ret;
}
// 1. 生成log plan tree, 包括Join/TableScan/SubplanScan/Sort/Material算子
// 2. 分配top算子,包括Count/Group By/SubPlanFilter/Window Sort/Distinct/Order By
// Limit/Late Materialization/Select Into
// 3. 选出最终代价最小的plan
int ObSelectLogPlan::generate_raw_plan_for_plain_select()
{
int ret = OB_SUCCESS;
if (OB_FAIL(generate_plan_tree())) {
LOG_WARN("failed to generate plan tree for plain select", K(ret));
} else if (OB_FAIL(allocate_plan_top())) {
LOG_WARN("failed to allocate top operator of plan tree for plain select", K(ret));
} else {
LOG_TRACE("succeed to generate best plan");
}
return ret;
}
int ObSelectLogPlan::allocate_plan_top()
{
int ret = OB_SUCCESS;
const ObSelectStmt *select_stmt = NULL;
if (OB_ISNULL(select_stmt = get_stmt()) || OB_ISNULL(optimizer_context_.get_query_ctx())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else {
bool need_limit = true;
bool for_update_is_allocated = false;
ObSEArray<OrderItem, 4> order_items;
LOG_TRACE("start to allocate operators for ", "sql", optimizer_context_.get_query_ctx()->get_sql_stmt());
// step. allocate subplan filter if needed, mainly for the subquery in where statement
if (OB_SUCC(ret)) {
if (get_subquery_filters().count() > 0) {
LOG_TRACE("start to allocate subplan filter for where statement", K(ret));
if (OB_FAIL(candi_allocate_subplan_filter_for_where())) {
LOG_WARN("failed to allocate subplan filter for where statement", K(ret));
} else {
LOG_TRACE("succeed to allocate subplan filter for where statement",
K(candidates_.candidate_plans_.count()));
}
}
}
// step. allocate 'count' if needed
if (OB_SUCC(ret)) {
bool has_rownum = false;
if (OB_FAIL(select_stmt->has_rownum(has_rownum))) {
LOG_WARN("failed to get rownum info", K(ret));
} else if (has_rownum) {
if (OB_FAIL(candi_allocate_count())) {
LOG_WARN("failed to allocate count operator", K(ret));
} else {
LOG_TRACE("succeed to allocate count operator",
K(candidates_.candidate_plans_.count()));
}
}
}
// step. allocate 'group-by' if needed
if (OB_SUCC(ret) && (select_stmt->has_group_by() || select_stmt->has_rollup())) {
// group-by or rollup both allocate group by logical operator.
// mysql mode for update need allocate before group by becauese group by isn't pk preserving.
if (lib::is_mysql_mode() && select_stmt->has_for_update()) {
if (OB_FAIL(candi_allocate_for_update())) {
LOG_WARN("failed to allocate for update operator", K(ret));
} else {
for_update_is_allocated = true;
LOG_TRACE("succeed to allocate for update", K(candidates_.candidate_plans_.count()));
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(candi_allocate_group_by())) {
LOG_WARN("failed to allocate group-by operator", K(ret));
} else {
LOG_TRACE("succeed to allocate group-by opeartor",
K(candidates_.candidate_plans_.count()));
}
}
}
// step. allocate 'window-sort' if needed
if (OB_SUCC(ret) && select_stmt->has_window_function()) {
if (OB_FAIL(candi_allocate_window_function())) {
LOG_WARN("failed to allocate window function", K(ret));
} else {
LOG_TRACE("succeed to allocate window function",
K(candidates_.candidate_plans_.count()));
}
}
// step. allocate 'distinct' if needed
if (OB_SUCC(ret) && select_stmt->has_distinct()) {
// mysql mode for update need allocate before distinct becauese distinct isn't pk preserving.
if (lib::is_mysql_mode() && select_stmt->has_for_update() && !for_update_is_allocated) {
if (OB_FAIL(candi_allocate_for_update())) {
LOG_WARN("failed to allocate for update operator", K(ret));
} else {
for_update_is_allocated = true;
LOG_TRACE("succeed to allocate for update", K(candidates_.candidate_plans_.count()));
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(candi_allocate_distinct())) {
LOG_WARN("failed to allocate distinct operator", K(ret));
} else {
LOG_TRACE("succeed to allocate distinct operator",
K(candidates_.candidate_plans_.count()));
}
}
}
// step. allocate 'sequence' if needed
if (OB_SUCC(ret) && select_stmt->has_sequence()) {
if (OB_FAIL(candi_allocate_sequence())) {
LOG_WARN("failed to allocate sequence operator", K(ret));
} else {
LOG_TRACE("succeed to allocate sequence operator",
K(candidates_.candidate_plans_.count()));
}
}
// step. allocate 'order-by' if needed
if (OB_SUCC(ret) && select_stmt->has_order_by() && !select_stmt->is_order_siblings() &&
!get_optimizer_context().is_online_ddl()) {
candidates_.is_final_sort_ = true;
if (OB_FAIL(candi_allocate_order_by(need_limit, order_items))) {
LOG_WARN("failed to allocate order by operator", K(ret));
} else {
candidates_.is_final_sort_ = false;
LOG_TRACE("succeed to allocate order by operator",
K(candidates_.candidate_plans_.count()));
}
}
// step. allocate 'limit' if needed
if (OB_SUCC(ret) && select_stmt->has_limit() && need_limit) {
if (OB_FAIL(candi_allocate_limit(order_items))) {
LOG_WARN("failed to allocate limit operator", K(ret));
} else {
LOG_TRACE("succeed to allocate limit operator",
K(candidates_.candidate_plans_.count()));
}
}
// step. allocate late materialization if needed
if (OB_SUCC(ret) && select_stmt->has_limit()) {
if (OB_FAIL(candi_allocate_late_materialization())) {
LOG_WARN("failed to allocate late-materialization operator", K(ret));
} else {
LOG_TRACE("succeed to allocate late-materialization operator",
K(candidates_.candidate_plans_.count()));
}
}
// step. allocate subplan filter if needed, mainly for subquery in select item
if (OB_SUCC(ret)) {
if (OB_FAIL(candi_allocate_subplan_filter_for_select_item())) {
LOG_WARN("failed to allocate subplan filter for subquery in select item", K(ret));
} else {
LOG_TRACE("succeed to allocate subplan filter for subquery in select item",
K(candidates_.candidate_plans_.count()));
}
}
// step. allocate 'unpivot' if needed
if (OB_SUCC(ret) && select_stmt->is_unpivot_select()) {
// group-by or rollup both allocate group by logical operator.
if (OB_FAIL(candi_allocate_unpivot())) {
LOG_WARN("failed to allocate unpovit operator", K(ret));
} else {
LOG_TRACE("succeed to allocate unpovit operator",
K(candidates_.candidate_plans_.count()));
}
}
if (OB_SUCC(ret) && select_stmt->has_for_update() && !for_update_is_allocated) {
if (OB_FAIL(candi_allocate_for_update())) {
LOG_WARN("failed to allocate for update operator", K(ret));
} else {
LOG_TRACE("succeed to allocate for update", K(candidates_.candidate_plans_.count()));
}
}
// step. allocate 'select_into' if needed
if (OB_SUCC(ret) && select_stmt->has_select_into()) {
if (OB_FAIL(candi_allocate_select_into())) {
LOG_WARN("failed to allocate select into operator", K(ret));
} else {
LOG_TRACE("succeed to allocate select into cluase",
K(candidates_.candidate_plans_.count()));
}
}
if (OB_SUCC(ret) && NULL != get_insert_stmt() && get_insert_stmt()->is_error_logging()) {
if (OB_FAIL(candi_allocate_err_log(get_insert_stmt()))) {
LOG_WARN("failed to allocate err log", K(ret));
} else {
LOG_TRACE("succeed to allocate err log", K(candidates_.candidate_plans_.count()));
}
}
// allocate root exchange
if (OB_SUCC(ret) && is_final_root_plan()) {
// allocate material if there is for update.
if (optimizer_context_.has_for_update() && OB_FAIL(candi_allocate_for_update_material())) {
LOG_WARN("failed to allocate material", K(ret));
//allocate temp-table transformation if needed.
} else if (!get_optimizer_context().get_temp_table_infos().empty() &&
OB_FAIL(candi_allocate_temp_table_transformation())) {
LOG_WARN("failed to allocate transformation operator", K(ret));
} else if (OB_FAIL(candi_allocate_root_exchange())) {
LOG_WARN("failed to allocate root exchange", K(ret));
} else {
LOG_TRACE("succeed to allocate root exchange", K(candidates_.candidate_plans_.count()));
}
}
}
return ret;
}
int ObSelectLogPlan::generate_raw_plan_for_expr_values()
{
int ret = OB_SUCCESS;
const ObSelectStmt *select_stmt = get_stmt();
if (OB_ISNULL(select_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else {
// classify subquery filters and rownum filters
ObSEArray<ObRawExpr*, 8> filter_exprs;
const ObIArray<ObRawExpr *> &condition_exprs = select_stmt->get_condition_exprs();
for (int64_t i = 0; OB_SUCC(ret) && i < condition_exprs.count(); i++) {
ObRawExpr *temp = NULL;
if (OB_ISNULL(temp = condition_exprs.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("null exprs", K(ret));
} else if (temp->has_flag(CNT_ROWNUM)) {
if (OB_FAIL(add_rownum_expr(temp))) {
LOG_WARN("failed to add rownum expr", K(ret));
} else { /*do nothing*/ }
} else if (temp->has_flag(CNT_SUB_QUERY)) {
if (OB_FAIL(add_subquery_filter(temp))) {
LOG_WARN("failed to add subquery filter", K(ret));
} else { /*do nothing*/ }
} else {
if (OB_FAIL(filter_exprs.push_back(temp))) {
LOG_WARN("failed to push back expr", K(ret));
} else if (!temp->has_flag(CNT_ONETIME)) {
// do nothing
} else if (OB_FAIL(add_subquery_filter(temp))) {
LOG_WARN("failed to add onetime filter", K(ret));
}
}
}
// make candidate plan
if (OB_SUCC(ret)) {
ObLogicalOperator *top = NULL;
if (OB_FAIL(allocate_expr_values_as_top(top, &filter_exprs))) {
LOG_WARN("failed to allocate expr values", K(ret));
} else if (OB_FAIL(make_candidate_plans(top))) {
LOG_WARN("failed to make candidate plans", K(ret));
} else {
LOG_TRACE("succeed to allocate expr values operator", K(ret));
}
}
// allocate top operator of plan tree
if (OB_SUCC(ret)) {
if (OB_FAIL(allocate_plan_top())) {
LOG_WARN("failed to allocate top operators for expr select", K(ret));
} else {
LOG_TRACE("succeed to allcoate top operators for expr select", K(ret));
}
}
}
return ret;
}
int ObSelectLogPlan::candi_allocate_subplan_filter_for_select_item()
{
int ret = OB_SUCCESS;
const ObSelectStmt *select_stmt = NULL;
ObSEArray<ObRawExpr*, 4> select_exprs;
ObSEArray<ObRawExpr*, 4> subquery_exprs;
if (OB_ISNULL(select_stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("null stmt", K(select_stmt), K(ret));
} else if (OB_FAIL(select_stmt->get_select_exprs(select_exprs))) {
LOG_WARN("failed to get select exprs", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::get_subquery_exprs(select_exprs,
subquery_exprs))) {
LOG_WARN("failed to get subquery exprs", K(ret));
} else if (subquery_exprs.empty()) {
/*do nothing*/
} else if (OB_FAIL(candi_allocate_subplan_filter(subquery_exprs))) {
LOG_WARN("failed to allocate subplan filter for select item", K(ret));
} else {
LOG_TRACE("succeed to allocate subplan filter for select item", K(select_stmt->get_stmt_id()));
}
return ret;
}
int ObSelectLogPlan::generate_child_plan_for_set(const ObDMLStmt *sub_stmt,
ObSelectLogPlan *&sub_plan,
ObIArray<ObRawExpr*> &pushdown_filters,
const uint64_t child_offset,
const bool is_set_distinct)
{
int ret = OB_SUCCESS;
sub_plan = NULL;
OPT_TRACE_BEGIN_SECTION;
OPT_TRACE_TITLE("start generate child plan for set");
if (OB_ISNULL(sub_stmt)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("sub_stmt is null", K(ret), K(sub_stmt));
} else if (OB_ISNULL(sub_plan = static_cast<ObSelectLogPlan*>
(optimizer_context_.get_log_plan_factory().create(optimizer_context_,
*sub_stmt)))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("Failed to create logcial plan", K(sub_plan), K(ret));
} else if (FALSE_IT(sub_plan->set_is_parent_set_distinct(is_set_distinct))) {
// do nothing
} else if (OB_FAIL(sub_plan->add_pushdown_filters(pushdown_filters))) {
LOG_WARN("failed to add pushdown filters", K(ret));
} else if (OB_FAIL(sub_plan->generate_raw_plan())) {
LOG_WARN("Failed to generate plan for sub_stmt", K(ret));
} else if (OB_FAIL(init_selectivity_metas_for_set(sub_plan, child_offset))) {
LOG_WARN("failed to init selectivity metas for set", K(ret));
}
OPT_TRACE_TITLE("end generate child plan for set");
OPT_TRACE_END_SECTION;
return ret;
}
int ObSelectLogPlan::decide_sort_keys_for_runion(const common::ObIArray<OrderItem> &order_items,
common::ObIArray<OrderItem> &new_order_items)
{
int ret = OB_SUCCESS;
ObLogicalOperator *best_plan = NULL;
const ObSelectStmt *select_stmt = NULL;
ObSEArray<ObRawExpr*, 8> select_exprs;
ObSEArray<OrderItem, 8> candi_order_items;
if (OB_ISNULL(select_stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("null stmt", K(select_stmt), K(ret));
} else if (OB_FAIL(select_stmt->get_select_exprs(select_exprs))) {
LOG_WARN("failed to get select exprs", K(ret));
} else if (OB_FAIL(candidates_.get_best_plan(best_plan))) {
LOG_WARN("failed to get best plan", K(ret));
} else if (OB_ISNULL(best_plan)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < order_items.count(); ++i) {
ObRawExpr* raw_expr = order_items.at(i).expr_;
if (OB_ISNULL(raw_expr) || OB_UNLIKELY(!raw_expr->is_column_ref_expr())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("expr is null", K(ret));
} else if (raw_expr->is_column_ref_expr()) {
ObColumnRefRawExpr* col_expr = static_cast<ObColumnRefRawExpr*>(raw_expr);
if (OB_UNLIKELY(!col_expr->is_cte_generated_column())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("the column is not a cte generate column", K(ret));
} else {
int64_t projector_offset = col_expr->get_cte_generate_column_projector_offset();
ObRawExpr* real_expr = select_exprs.at(projector_offset);
if (OB_ISNULL(real_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("convert recursive union all generate operator sort failed");
} else if (real_expr->is_const_expr()) {
// do nothing.
} else {
OrderItem real_item = order_items.at(i);
real_item.expr_ = real_expr;
if (OB_FAIL(candi_order_items.push_back(real_item))) {
LOG_WARN("add real sort item error");
}
}
}
}
}
if (OB_SUCC(ret) && !candi_order_items.empty()) {
if (OB_FAIL(ObOptimizerUtil::simplify_ordered_exprs(best_plan->get_fd_item_set(),
best_plan->get_output_equal_sets(),
best_plan->get_output_const_exprs(),
get_onetime_query_refs(),
candi_order_items,
new_order_items))) {
LOG_WARN("failed to simplify exprs", K(ret));
} else { /*do nothing*/ }
}
}
return ret;
}
int ObSelectLogPlan::candi_allocate_window_function()
{
int ret = OB_SUCCESS;
const ObSelectStmt *stmt = get_stmt();
ObSEArray<ObRawExpr*, 8> candi_subquery_exprs;
ObRawExpr *wf_aggr_status_expr = NULL;
if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("stmt is NULL", K(ret));
} else if (OB_FAIL(append(candi_subquery_exprs, stmt->get_window_func_exprs()))) {
LOG_WARN("failed to append exprs", K(ret));
} else if (OB_FAIL(candi_allocate_subplan_filter_for_exprs(candi_subquery_exprs))) {
LOG_WARN("failed to do allocate subplan filter", K(ret));
} else if (stmt->get_window_func_count() > 0) {
ObSEArray<CandidatePlan, 8> winfunc_plans;
if (OB_FAIL(ObRawExprUtils::build_inner_wf_aggr_status_expr(
get_optimizer_context().get_expr_factory(),
*get_optimizer_context().get_session_info(),
wf_aggr_status_expr))) {
LOG_WARN("build_inner_wf_aggr_status_expr failed", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && i < candidates_.candidate_plans_.count(); ++i) {
if (OB_FAIL(generate_window_functions_plan(stmt->get_window_func_exprs(),
static_cast<ObOpPseudoColumnRawExpr *>(wf_aggr_status_expr),
winfunc_plans,
candidates_.candidate_plans_.at(i)))) {
LOG_WARN("failed to allocate window functions", K(ret));
} else { /*do nothing*/ }
}
// choose the best plan
if (OB_SUCC(ret)) {
int64_t check_scope = OrderingCheckScope::CHECK_DISTINCT |
OrderingCheckScope::CHECK_SET |
OrderingCheckScope::CHECK_ORDERBY;
if (OB_FAIL(update_plans_interesting_order_info(winfunc_plans, check_scope))) {
LOG_WARN("failed to update plans interesting order info", K(ret));
} else if (OB_FAIL(prune_and_keep_best_plans(winfunc_plans))) {
LOG_WARN("failed to add winfunc plans", K(ret));
}
}
}
return ret;
}
int ObSelectLogPlan::generate_window_functions_plan(const ObIArray<ObWinFunRawExpr *> &winfunc_exprs,
ObOpPseudoColumnRawExpr *wf_aggr_status_expr,
common::ObIArray<CandidatePlan> &total_plans,
CandidatePlan &orig_candidate_plan)
{
int ret = OB_SUCCESS;
CandidatePlan candidate_merge_plan;
CandidatePlan candidate_hash_plan;
ObSEArray<ObWinFunRawExpr*, 8> remaining_exprs;
ObSEArray<ObWinFunRawExpr*, 8> current_exprs;
ObSEArray<OrderItem, 8> current_sort_keys;
ObSEArray<OrderItem, 8> next_sort_keys;
ObSEArray<CandidatePlan, 8> local_plans;
ObSEArray<CandidatePlan, 8> tmp_plans;
ObSEArray<WinDistAlgo, 8> dist_methods;
ObSEArray<ObWinFunRawExpr*, 8> adjusted_winfunc_exprs;
if (OB_FAIL(local_plans.push_back(orig_candidate_plan))) {
LOG_WARN("failed to push back original candidate plan", K(ret));
} else if (OB_FAIL(remaining_exprs.assign(winfunc_exprs))) {
LOG_WARN("failed to assign remaining exprs", K(ret));
} else {
const bool distributed = orig_candidate_plan.plan_tree_->is_distributed();
int64_t stmt_func_idx = 0;
while (OB_SUCC(ret) && !remaining_exprs.empty()) {
if (OB_FAIL(get_next_group_window_exprs(local_plans.at(0).plan_tree_,
remaining_exprs,
current_sort_keys,
current_exprs,
next_sort_keys))) {
LOG_WARN("failed to get next window exprs", K(ret));
} else if (!current_exprs.empty()) {
adjusted_winfunc_exprs.reuse();
// split window functions by distribute method
if (OB_FAIL(adjust_window_functions(local_plans.at(0).plan_tree_,
current_exprs,
adjusted_winfunc_exprs))) {
LOG_WARN("adjust window functions failed", K(ret));
} else {
ObSEArray<int64_t, 8> split;
ObSEArray<WinDistAlgo, 8> methods;
ObSEArray<double, 8> sort_key_ndvs;
ObSEArray<std::pair<int64_t, int64_t>, 8> pby_oby_prefixes;
bool has_non_parallel_wf = false;
if (OB_FAIL(prepare_for_split_winfuncs(
local_plans.at(0).plan_tree_, adjusted_winfunc_exprs, current_sort_keys,
sort_key_ndvs, pby_oby_prefixes))) {
LOG_WARN("prepare_for_split_winfuncs failed", K(ret));
} else if (distributed &&
OB_FAIL(split_winfuncs_by_dist_method(local_plans.at(0).plan_tree_,
adjusted_winfunc_exprs,
remaining_exprs,
stmt_func_idx,
sort_key_ndvs,
pby_oby_prefixes,
split,
methods,
has_non_parallel_wf))) {
// split the window function expressions of one group (group is detected by get_next_group_window_exprs()) into multi window function operator by distribute method (HASH or RANGE)
LOG_WARN("split window function by distribute method failed", K(ret));
} else if (!distributed || has_non_parallel_wf) {
split.reuse();
methods.reuse();
if (OB_FAIL(split.push_back(adjusted_winfunc_exprs.count()))
|| OB_FAIL(methods.push_back(WinDistAlgo::NONE))) {
LOG_WARN("array push back failed", K(ret));
}
}
int64_t start = 0;
for (int64_t si = 0; OB_SUCC(ret) && si < split.count(); si++) {
const int64_t end = split.at(si);
ObArrayHelper<ObWinFunRawExpr *> winfuncs(end - start,
&adjusted_winfunc_exprs.at(start),
end - start);
const WinDistAlgo method = methods.at(si);
bool is_pushdown = false;
ObArray<bool> pushdown_info;
ObSEArray<ObRawExpr*, 8> partition_exprs;
if (distributed) {
ObArrayHelper<std::pair<int64_t, int64_t>> splitted_pby_oby_prefixes(
end - start,
&pby_oby_prefixes.at(start),
end - start);
for (int64_t i = 0; OB_SUCC(ret) && i < winfuncs.count(); ++i) {
if (OB_FAIL(pushdown_info.push_back(false))) {
LOG_WARN("push_back to push_down_array failed", K(ret));
}
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(check_winfunc_pushdown(local_plans.at(0).plan_tree_, winfuncs, method,
sort_key_ndvs, splitted_pby_oby_prefixes, is_pushdown, pushdown_info))) {
LOG_WARN("check_winfunc_pushdown failed", K(ret));
}
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(dist_methods.push_back(methods.at(si)))) {
LOG_WARN("array push back failed", K(ret));
} else if (OB_FAIL(get_window_function_partition_exprs(winfuncs, partition_exprs))) {
LOG_WARN("failed to get partition exprs", K(ret));
}
FOREACH_X(plan, local_plans, OB_SUCC(ret)) {
CandidatePlan merge_plan = *plan;
CandidatePlan hash_plan = *plan;
ObLogicalOperator *top = plan->plan_tree_;
int64_t part_cnt = 0;
int64_t prefix_pos = 0;
bool need_sort = false;
if (NULL == top) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("got NULL plan tree", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::check_need_sort(current_sort_keys,
top->get_op_ordering(),
top->get_fd_item_set(),
top->get_output_equal_sets(),
top->get_output_const_exprs(),
get_onetime_query_refs(),
top->get_is_at_most_one_row(),
need_sort,
prefix_pos))) {
LOG_WARN("failed to check need sort", K(ret));
} else if (OB_FAIL(get_partition_count(pby_oby_prefixes,
start,
end,
partition_exprs,
prefix_pos,
part_cnt))) {
LOG_WARN("failed to get partition count", K(ret));
} else if (need_sort && part_cnt > 0 &&
OB_FAIL(ObOptimizerUtil::check_need_sort(current_sort_keys,
top->get_op_ordering(),
top->get_fd_item_set(),
top->get_output_equal_sets(),
top->get_output_const_exprs(),
get_onetime_query_refs(),
top->get_is_at_most_one_row(),
need_sort,
prefix_pos,
part_cnt))) {
LOG_WARN("failed to check need sort", K(ret));
} else if (OB_FAIL(create_merge_window_function_plan(merge_plan.plan_tree_,
winfuncs,
current_sort_keys,
partition_exprs,
method,
is_pushdown,
wf_aggr_status_expr,
pushdown_info,
need_sort,
prefix_pos,
part_cnt))) {
LOG_WARN("create merge window functions plan failed", K(ret));
} else if (OB_FAIL(tmp_plans.push_back(merge_plan))) {
LOG_WARN("array push back failed", K(ret));
} else if (WinDistAlgo::RANGE != method && WinDistAlgo::LIST != method
&& need_sort && part_cnt > 0) {
if (OB_FAIL(create_hash_window_function_plan(hash_plan.plan_tree_,
winfuncs,
current_sort_keys,
partition_exprs,
part_cnt,
is_pushdown,
wf_aggr_status_expr,
pushdown_info))) {
LOG_WARN("failed to create hash window function plan", K(ret));
} else if (OB_FAIL(tmp_plans.push_back(hash_plan))) {
LOG_WARN("array push back failed", K(ret));
}
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(local_plans.assign(tmp_plans))) {
LOG_WARN("array assign failed", K(ret));
} else {
tmp_plans.reuse();
start = end;
stmt_func_idx++;
}
}
}
}
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(current_sort_keys.assign(next_sort_keys))) {
LOG_WARN("failed to assign current sort keys", K(ret));
} else {
next_sort_keys.reuse();
current_exprs.reuse();
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(append(total_plans, local_plans))) {
LOG_WARN("failed to append local plans");
} else if (distributed) {
FOREACH_X(plan, local_plans, OB_SUCC(ret)) {
if (plan->plan_tree_->get_type() != LOG_WINDOW_FUNCTION) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("top operator is not window function", K(ret));
} else {
if (OB_FAIL(static_cast<ObLogWindowFunction *>(plan->plan_tree_)
->set_dist_hint(dist_methods))) {
LOG_WARN("set distribute hint failed", K(ret));
}
}
}
}
}
}
return ret;
}
int ObSelectLogPlan::get_partition_count(const ObSEArray<std::pair<int64_t, int64_t>, 8> pby_oby_prefixes,
const int64_t start,
const int64_t end,
const ObIArray<ObRawExpr*> &partition_exprs,
const int64_t prefix_pos,
int64_t &part_cnt)
{
int ret = OB_SUCCESS;
part_cnt = 0;
if (OB_UNLIKELY(pby_oby_prefixes.empty())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get empty pby_oby_prefixes", K(ret), K(pby_oby_prefixes.count()));
} else if (prefix_pos > 0 || partition_exprs.empty()) {
part_cnt = 0;
} else if (OB_UNLIKELY(start < 0 || end > pby_oby_prefixes.count() || start >= end)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected pby_oby_prefixes count ", K(ret), K(start), K(end), K(pby_oby_prefixes.count()));
} else {
part_cnt = pby_oby_prefixes.at(start).first;
for (int64_t i = start + 1; OB_SUCC(ret) && i < end; ++i) {
if (pby_oby_prefixes.at(i).first < part_cnt) {
part_cnt = pby_oby_prefixes.at(i).first;
}
}
}
return ret;
}
int ObSelectLogPlan::get_next_group_window_exprs(const ObLogicalOperator *top,
ObIArray<ObWinFunRawExpr*> &remaining_exprs,
ObIArray<OrderItem> &current_sort_keys,
ObIArray<ObWinFunRawExpr*> &current_exprs,
ObIArray<OrderItem> &next_sort_keys)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(top), K(ret));
} else {
bool need_sort = false;
int64_t prefix_pos = 0;
int64_t best_prefix_pos = -1;
ObWinFunRawExpr* win_expr = NULL;
ObSEArray<OrderItem, 8> win_sort_keys;
ObSEArray<ObWinFunRawExpr*, 8> rest_winfunc_exprs;
ObSEArray<ObWinFunRawExpr*, 8> no_need_order_exprs;
bool is_first = current_sort_keys.empty();
ObSEArray<OrderItem, 8> input_ordering;
if (current_sort_keys.empty()) {
ret = input_ordering.assign(top->get_op_ordering());
} else {
ret = input_ordering.assign(current_sort_keys);
}
for (int64_t i = 0; OB_SUCC(ret) && i < remaining_exprs.count(); i++) {
win_sort_keys.reuse();
prefix_pos = 0;
need_sort = false;
if (OB_ISNULL(win_expr = remaining_exprs.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(get_sort_keys_for_window_function(top,
win_expr,
input_ordering,
remaining_exprs,
win_sort_keys))) {
LOG_WARN("failed to decide sort keys", K(ret));
} else if (win_sort_keys.empty()) {
if (OB_FAIL(no_need_order_exprs.push_back(win_expr))) {
LOG_WARN("failed to push back expr", K(ret));
} else { /*do nothing*/ }
} else if (OB_FAIL(ObOptimizerUtil::check_need_sort(win_sort_keys,
input_ordering,
top->get_fd_item_set(),
top->get_output_equal_sets(),
top->get_output_const_exprs(),
get_onetime_query_refs(),
top->get_is_at_most_one_row(),
need_sort,
prefix_pos))) {
LOG_WARN("failed to check if need sort", K(ret));
} else if (!need_sort) {
if (OB_FAIL(current_exprs.push_back(win_expr))) {
LOG_WARN("failed to push back expr", K(ret));
} else if (is_first && current_sort_keys.count() < win_sort_keys.count() &&
OB_FAIL(current_sort_keys.assign(win_sort_keys))) {
LOG_WARN("failed to assign sort keys", K(ret));
} else { /*do nothing*/ }
} else {
if (OB_FAIL(rest_winfunc_exprs.push_back(win_expr))) {
LOG_WARN("failed to push back expr", K(ret));
} else if (best_prefix_pos < prefix_pos ||
(best_prefix_pos == prefix_pos && next_sort_keys.count() < win_sort_keys.count())) {
if (OB_FAIL(next_sort_keys.assign(win_sort_keys))) {
LOG_WARN("failed to assign exprs", K(ret));
} else {
best_prefix_pos = prefix_pos;
}
}
}
}
if (OB_SUCC(ret)) {
if (rest_winfunc_exprs.empty() &&
OB_FAIL(append(current_exprs, no_need_order_exprs))) {
LOG_WARN("failed to append exprs", K(ret));
} else if (!current_exprs.empty()) {
if (OB_FAIL(remaining_exprs.assign(rest_winfunc_exprs))) {
LOG_WARN("failed to assign win func exprs", K(ret));
} else if (!rest_winfunc_exprs.empty() &&
OB_FAIL(append(remaining_exprs, no_need_order_exprs))) {
LOG_WARN("failed to append exprs", K(ret));
} else {
LOG_TRACE("succeed to get current group window exprs", K(current_exprs), K(remaining_exprs),
K(current_sort_keys), K(next_sort_keys));
}
} else { /*do nothing*/ }
}
}
return ret;
}
int ObSelectLogPlan::get_sort_keys_for_window_function(const ObLogicalOperator *top,
const ObWinFunRawExpr *win_expr,
const ObIArray<OrderItem> &ordering,
const ObIArray<ObWinFunRawExpr*> &remaining_exprs,
ObIArray<OrderItem> &sort_keys,
int64_t *pby_prefix /* = NULL */)
{
int ret = OB_SUCCESS;
ObSEArray<ObRawExpr*, 8> part_exprs;
ObSEArray<ObOrderDirection, 8> part_directions;
ObSEArray<OrderItem, 8> output_sort_keys;
bool is_const = false;
int64_t prefix_count = -1;
bool input_ordering_all_used = false;
sort_keys.reuse();
if (OB_ISNULL(top) || OB_ISNULL(win_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("params have null", K(ret), K(win_expr), K(top));
} else if (OB_FAIL(part_exprs.assign(win_expr->get_partition_exprs()))) {
LOG_WARN("failed to assign partition exprs", K(ret));
} else if (OB_FAIL(set_default_sort_directions(remaining_exprs,
part_exprs,
top->get_output_equal_sets(),
part_directions))) {
LOG_WARN("failed to get default sort directions", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::adjust_exprs_by_ordering(part_exprs,
ordering,
top->get_output_equal_sets(),
top->get_output_const_exprs(),
get_onetime_query_refs(),
prefix_count,
input_ordering_all_used,
part_directions))) {
LOG_WARN("failed to adjust exprs by ordering", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::make_sort_keys(part_exprs,
part_directions,
output_sort_keys))) {
LOG_WARN("failed to make sort keys", K(ret));
} else {
if (NULL != pby_prefix) {
if (OB_FAIL(ObOptimizerUtil::simplify_ordered_exprs(top->get_fd_item_set(),
top->get_output_equal_sets(),
top->get_output_const_exprs(),
get_onetime_query_refs(),
output_sort_keys,
sort_keys))) {
LOG_WARN("failed to simplify ordered exprs", K(ret));
} else {
*pby_prefix = sort_keys.count();
sort_keys.reuse();
}
}
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(append(output_sort_keys, win_expr->get_order_items()))) {
LOG_WARN("faield to append order items", K(ret));
} else if (OB_FAIL(ObOptimizerUtil::simplify_ordered_exprs(top->get_fd_item_set(),
top->get_output_equal_sets(),
top->get_output_const_exprs(),
get_onetime_query_refs(),
output_sort_keys,
sort_keys))) {
LOG_WARN("failed to simplify ordered exprs", K(ret));
} else { /*do nothing*/ }
return ret;
}
int ObSelectLogPlan::get_winfunc_pby_oby_sort_prefix(const ObLogicalOperator *top,
const ObWinFunRawExpr *win_expr,
const ObIArray<OrderItem> &ordering,
int64_t &pby_prefix,
int64_t &pby_oby_prefix)
{
int ret = OB_SUCCESS;
ObSEArray<OrderItem, 8> sort_keys;
ObArray<ObWinFunRawExpr *> remaining;
if (NULL == top || NULL == win_expr) {
ret = OB_ERR_UNEXPECTED;
} else if (OB_FAIL(get_sort_keys_for_window_function(top,
win_expr,
ordering,
remaining,
sort_keys,
&pby_prefix))) {
LOG_WARN("get sort keys for window function failed", K(ret));
} else {
pby_oby_prefix = sort_keys.count();
if (pby_prefix > pby_oby_prefix
|| pby_oby_prefix > sort_keys.count()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected prefix count",
K(ret), K(pby_prefix), K(pby_oby_prefix), K(sort_keys), K(win_expr));
}
}
return ret;
}
int ObSelectLogPlan::set_default_sort_directions(const ObIArray<ObWinFunRawExpr*> &win_exprs,
const ObIArray<ObRawExpr *> &part_exprs,
const EqualSets &equal_sets,
ObIArray<ObOrderDirection> &directions)
{
int ret = OB_SUCCESS;
directions.reset();
if (OB_ISNULL(get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("stmt is null", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && i < part_exprs.count(); ++i) {
const ObRawExpr *expr = part_exprs.at(i);
ObOrderDirection direction = default_asc_direction();
bool found_dir = false;
for (int64_t j = 0; OB_SUCC(ret) && !found_dir && j < win_exprs.count(); ++j) {
if (OB_ISNULL(win_exprs.at(j))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("win func is null", K(ret));
} else {
found_dir = ObOptimizerUtil::find_expr_direction(win_exprs.at(j)->get_order_items(),
expr,
equal_sets,
direction);
}
}
if (OB_SUCC(ret)) {
if (!found_dir) {
found_dir = ObOptimizerUtil::find_expr_direction(get_stmt()->get_order_items(),
expr,
equal_sets,
direction);
}
if (OB_FAIL(directions.push_back(direction))) {
LOG_WARN("failed to push back directions", K(ret));
}
}
}
return ret;
}
int ObSelectLogPlan::check_winfunc_pushdown(
const ObLogicalOperator *top, const common::ObIArray<ObWinFunRawExpr *> &winfunc_exprs,
const WinDistAlgo method, const ObIArray<double> &sort_key_ndvs,
const ObIArray<std::pair<int64_t, int64_t>> &pby_oby_prefixes,
bool &is_pushdown, ObIArray<bool> &pushdown_info)
{
int ret = OB_SUCCESS;
is_pushdown = false;
if (OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("top is null", K(ret));
} else if (WinDistAlgo::HASH != method) {
is_pushdown = false;
} else {
const int64_t WF_PBY_DOP_RADIO = 16;
const int64_t WF_CARD_DOP_RADIO = 256;
const int64_t dop = top->get_parallel();
int64_t prev_method = -1;
bool prev_pushdown_supported = true;
bool all_wf_exprs_reporting = true;
// whether has wf exprs which need pushdown in one split group
bool has_pushdown_wf_exprs = false;
// use _windowfunc_optimization_settings to change win_opt.disable_reporting_wf_pushdown_
// set "_windowfunc_optimization_settings" = 0; means enable reporting_wf_pushdown
// set "_windowfunc_optimization_settings" = 2; means disable reporting_wf_pushdown
ObWinfuncOptimizationOpt win_opt;
if (OB_FAIL(optimizer_context_.get_session_info()->get_sys_variable(
share::SYS_VAR__WINDOWFUNC_OPTIMIZATION_SETTINGS, win_opt.v_))) {
LOG_WARN("get sys variable failed", K(ret));
}
for (int64_t idx = 0;
OB_SUCC(ret) && all_wf_exprs_reporting && idx < winfunc_exprs.count();
++idx) {
const int64_t pby_cnt = pby_oby_prefixes.at(idx).first;
const int64_t pby_oby_cnt = pby_oby_prefixes.at(idx).second;
bool pushdown_supported =
pby_cnt > 0 && pby_cnt == pby_oby_cnt && !win_opt.disable_reporting_wf_pushdown_;
if (OB_FAIL(check_wf_pushdown_supported(winfunc_exprs.at(idx), pushdown_supported))) {
LOG_WARN("check window function range distribute parallel supported failed", K(ret));
} else {
all_wf_exprs_reporting = all_wf_exprs_reporting && pushdown_supported;
}
if (OB_SUCC(ret) && all_wf_exprs_reporting) {
ret = OB_E(EventTable::EN_ENFORCE_PUSH_DOWN_WF) OB_SUCCESS; // 247
if (OB_SUCC(ret)) {
// caculate whether need to pushdown by NDV and CARD
const double pby_ndv = pby_cnt == 0 ? 0 : sort_key_ndvs.at(pby_cnt - 1);
if (pby_ndv < WF_PBY_DOP_RADIO * dop
&& top->get_card() > WF_CARD_DOP_RADIO * dop) {
pushdown_info.at(idx) = true;
has_pushdown_wf_exprs = true;
}
} else { // use trace point to enforce pushdown window function regardless of ndv and dop
// use trace point : alter system set_tp tp_no = 247, error_code = 4000, frequency = 1;
int64_t max_push_down_wf_idx = -ret;
if (idx < max_push_down_wf_idx) {
pushdown_info.at(idx) = true;
has_pushdown_wf_exprs = true;
}
ret = OB_SUCCESS;
}
}
}
if (OB_SUCC(ret)) {
is_pushdown = all_wf_exprs_reporting && has_pushdown_wf_exprs;
}
}
return ret;
}
int ObSelectLogPlan::prepare_for_split_winfuncs(
const ObLogicalOperator *top,
const common::ObIArray<ObWinFunRawExpr *> &winfunc_exprs,
const ObIArray<OrderItem> &sort_keys,
ObIArray<double> &sort_key_ndvs,
ObIArray<std::pair<int64_t, int64_t>> &pby_oby_prefixes)
{
int ret = OB_SUCCESS;
ObSEArray<ObRawExpr *, 8> sort_key_exprs;
if (OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("top is null", K(ret));
} else if (OB_FAIL(sort_key_ndvs.prepare_allocate(sort_keys.count()))) {
LOG_WARN("array prepare allocate failed", K(ret));
} else { // Get NDV for each sort_keys prefix first.
for (int64_t i = 0; i < sort_keys.count() && OB_SUCC(ret); i++) {
if (OB_FAIL(sort_key_exprs.push_back(sort_keys.at(i).expr_))) {
LOG_WARN("array push back failed", K(ret));
} else if (OB_FAIL(ObOptSelectivity::calculate_distinct(get_update_table_metas(),
get_selectivity_ctx(),
sort_key_exprs,
top->get_card(),
sort_key_ndvs.at(i)))) {
LOG_WARN("calculate NDV failed", K(ret));
}
}
}
if (OB_SUCC(ret)) {
// Get each window function's partition by (PBY) and order by (OBY)'s prefix of %sort_keys.
// Note %sort_keys must covert PBY + OBY, which is guaranteed by `get_next_group_window_exprs()`
for (int64_t i = 0; OB_SUCC(ret) && i < winfunc_exprs.count(); i++) {
int64_t pby_prefix = 0;
int64_t pby_oby_prefix = 0;
if (OB_FAIL(get_winfunc_pby_oby_sort_prefix(top,
winfunc_exprs.at(i),
sort_keys,
pby_prefix,
pby_oby_prefix))) {
LOG_WARN("get sort keys for window function failed", K(ret));
} else if (OB_FAIL(pby_oby_prefixes.push_back(
std::make_pair(pby_prefix, pby_oby_prefix)))) {
LOG_WARN("array push back failed", K(ret));
}
}
}
return ret;
}
int ObSelectLogPlan::split_winfuncs_by_dist_method(
const ObLogicalOperator *top,
const common::ObIArray<ObWinFunRawExpr *> &winfunc_exprs,
const common::ObIArray<ObWinFunRawExpr *> &remaining_exprs,
const int64_t stmt_func_idx,
const ObIArray<double> &sort_key_ndvs,
const ObIArray<std::pair<int64_t, int64_t>> &pby_oby_prefixes,
common::ObIArray<int64_t> &split,
common::ObIArray<WinDistAlgo> &methods,
bool &has_non_parallel_wf)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("top is null", K(ret));
}
split.reuse();
methods.reuse();
// Main split logic, get current window function's distribute method (%cur_method) by
// auto detected method and hint method. Then compare it with the previous window function's
// distribute method to check need split or not.
if (OB_SUCC(ret)) {
const int64_t WF_PBY_DOP_RADIO = 16;
const int64_t WF_CARD_DOP_RADIO = 256;
const int64_t dop = top->get_parallel();
int64_t prev_method = -1;
for (int64_t idx = 0; OB_SUCC(ret) && !has_non_parallel_wf && idx < winfunc_exprs.count(); ) {
const int64_t pby_cnt = pby_oby_prefixes.at(idx).first;
const int64_t pby_oby_cnt = pby_oby_prefixes.at(idx).second;
const double pby_ndv = pby_cnt == 0 ? 0 : sort_key_ndvs.at(pby_cnt - 1);
const double pby_oby_ndv = pby_oby_cnt == 0 ? 0 : sort_key_ndvs.at(pby_oby_cnt - 1);
bool range_dist_suppored = false;
ObWinfuncOptimizationOpt win_opt;
if (OB_FAIL(check_wf_range_dist_supported(winfunc_exprs.at(idx), range_dist_suppored))) {
LOG_WARN("check window function range distribute parallel supported failed", K(ret));
} else if (OB_FAIL(optimizer_context_.get_session_info()->get_sys_variable(
share::SYS_VAR__WINDOWFUNC_OPTIMIZATION_SETTINGS, win_opt.v_))) {
LOG_WARN("get sys variable failed", K(ret));
} else {
range_dist_suppored = range_dist_suppored && !win_opt.disable_range_distribution_;
}
if (OB_FAIL(ret)) {
break;
}
// no partition by && can not do range distribution
if (pby_oby_cnt <= 0 || (pby_cnt <= 0 && !range_dist_suppored)) {
has_non_parallel_wf = true;
break;
}
// <1> detect distribute method by NDV and CARD
WinDistAlgo auto_method = WinDistAlgo::NONE;
if (pby_cnt > 0) {
auto_method = WinDistAlgo::HASH;
}
if (range_dist_suppored
&& pby_ndv < WF_PBY_DOP_RADIO * dop
&& top->get_card() > WF_CARD_DOP_RADIO * dop) {
auto_method = WinDistAlgo::RANGE;
if (pby_oby_ndv < WF_PBY_DOP_RADIO * dop) {
auto_method = WinDistAlgo::LIST;
}
}
// <2> get hint method
auto dist_hint = get_log_plan_hint().get_window_dist();
bool use_hint = false;
WinDistAlgo hint_method = WinDistAlgo::NONE;
if (NULL != dist_hint && dist_hint->get_algos().count() > stmt_func_idx + split.count()) {
use_hint = true;
hint_method = dist_hint->get_algos().at(stmt_func_idx + split.count());
// check hint method is impossibility
switch (hint_method) {
case WinDistAlgo::NONE: {
if (auto_method != WinDistAlgo::NONE) {
use_hint = false;
}
break;
}
case WinDistAlgo::HASH: {
if (pby_cnt <= 0) {
use_hint = false;
}
break;
}
case WinDistAlgo::RANGE:
case WinDistAlgo::LIST: {
if (!range_dist_suppored) {
use_hint = false;
}
break;
}
}
}
// <3> get current distribute method and compare previous method to split winfunc_exprs
WinDistAlgo cur_method = use_hint ? hint_method : auto_method;
if (-1 != prev_method && prev_method != (int)cur_method) {
if (cur_method == WinDistAlgo::NONE || cur_method == WinDistAlgo::HASH) {
prev_method = (int)cur_method;
} else if (cur_method >= WinDistAlgo::RANGE && prev_method == (int)WinDistAlgo::HASH
&& pby_oby_prefixes.at(idx - 1).first == pby_oby_prefixes.at(idx).first) {
cur_method = WinDistAlgo::HASH;
}
}
bool need_split = false;
if (-1 == prev_method) {
// no need split
} else if (prev_method != static_cast<int64_t>(cur_method)) {
need_split = true;
} else if (WinDistAlgo::RANGE == cur_method
|| WinDistAlgo::LIST == cur_method) {
// pby_cnt or oby_cnt miss match
if (pby_oby_prefixes.at(idx - 1) != pby_oby_prefixes.at(idx)) {
need_split = true;
}
}
if (need_split) {
// do not increase %idx when split, because hint_method may change after split.
OZ(split.push_back(idx));
OZ(methods.push_back((WinDistAlgo)prev_method));
prev_method = -1;
} else {
idx++;
if (idx == winfunc_exprs.count()) {
OZ(split.push_back(idx));
OZ(methods.push_back(cur_method));
} else {
prev_method = static_cast<int64_t>(cur_method);
}
}
}
}
return ret;
}
/**
* @brief 假设 winfunc_exprs 都采用 sort_keys 进行排序
* 同一个分组中的 win_expr 应该按照一定的顺序排序 (`adjust_window_functions()`)
* 同一个ObWindowFunction中的多个 win_expr 要按照一定的顺序组织,譬如:
* w1(c) over (partition by e1, e2, e3)
* w2(c) over (partition by e1)
* w3(c) over (partition by e1 e2)
* 一定要按照 w1, w3, w2 的顺序来组织。即排在后面的win_expr的partition表达式是前面对象的子集。
* 这里窗口函数会按照分组表达式数量进行排序(只统计非常量的分组表达式数量,数量多的排在前)。
*/
int ObSelectLogPlan::create_merge_window_function_plan(ObLogicalOperator *&top,
const ObIArray<ObWinFunRawExpr *> &winfunc_exprs,
const ObIArray<OrderItem> &sort_keys,
const ObIArray<ObRawExpr*> &partition_exprs,
WinDistAlgo dist_method,
const bool is_pushdown,
ObOpPseudoColumnRawExpr *wf_aggr_status_expr,
const ObIArray<bool> &pushdown_info,
bool need_sort,
int64_t prefix_pos,
int64_t part_cnt)
{
int ret = OB_SUCCESS;
ObExchangeInfo exch_info;
bool single_part_parallel = false;
bool is_partition_wise = false;
double pby_ndv = 1.0;
int64_t range_dist_keys_cnt = 0;
int64_t range_dist_pby_prefix = 0;
bool range_distribution = false;
LOG_DEBUG("try create merge distribute function plan", K(dist_method), K(sort_keys), K(winfunc_exprs));
if (OB_ISNULL(top) || OB_UNLIKELY(winfunc_exprs.empty())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpecated error", K(top), K(winfunc_exprs.count()), K(ret));
} else {
// range distribution
if (WinDistAlgo::RANGE == dist_method
|| WinDistAlgo::LIST == dist_method) {
int64_t pby_oby_prefix = 0;
ObSEArray<OrderItem, 8> range_dist_keys;
// al range distribute window function has the same pby, pby+oby sort prefix
OZ(get_winfunc_pby_oby_sort_prefix(top,
winfunc_exprs.at(0),
sort_keys,
range_dist_pby_prefix,
pby_oby_prefix));
for (int64_t i = 0; OB_SUCC(ret) && i < pby_oby_prefix; i++) {
OZ(range_dist_keys.push_back(sort_keys.at(i)));
}
range_dist_keys_cnt = pby_oby_prefix;
need_sort = false;
prefix_pos = 0;
OZ(ObOptimizerUtil::check_need_sort(range_dist_keys,
top->get_op_ordering(),
top->get_fd_item_set(),
top->get_output_equal_sets(),
top->get_output_const_exprs(),
get_onetime_query_refs(),
top->get_is_at_most_one_row(),
need_sort,
prefix_pos,
part_cnt));
exch_info.dist_method_ = ObPQDistributeMethod::RANGE;
ObRawExpr *random_expr = NULL;
OZ(exch_info.sort_keys_.assign(range_dist_keys));
if (WinDistAlgo::RANGE != dist_method) {
OZ(ObRawExprUtils::build_pseudo_random(get_optimizer_context().get_expr_factory(),
*get_optimizer_context().get_session_info(),
random_expr));
OZ(exch_info.sort_keys_.push_back(OrderItem(random_expr)));
}
exch_info.sample_type_ = FULL_INPUT_SAMPLE;
LOG_TRACE("exchange info", K(need_sort), K(prefix_pos),
K(exch_info), K(range_dist_keys), K(top->get_op_ordering()));
OZ(allocate_sort_and_exchange_as_top(top,
exch_info,
range_dist_keys,
need_sort,
prefix_pos,
top->get_is_local_order()));
if (OB_SUCC(ret)) {
ObLogicalOperator *receive = NULL;
OZ(top->find_first_recursive(LOG_EXCHANGE, receive));
CK(NULL != receive
&& NULL != receive->get_child(0)
&& LOG_EXCHANGE == receive->get_child(0)->get_type());
if (OB_SUCC(ret)) {
auto transmit = static_cast<ObLogExchange *>(receive->get_child(0));
if (NULL != random_expr) {
transmit->set_random_expr(random_expr);
}
}
}
OZ(allocate_window_function_as_top(winfunc_exprs,
single_part_parallel,
is_partition_wise,
ObLogWindowFunction::WindowFunctionRoleType::NORMAL,
sort_keys,
range_dist_keys_cnt,
range_dist_pby_prefix,
top,
wf_aggr_status_expr,
pushdown_info));
// hash distribution
} else if (WinDistAlgo::HASH == dist_method) {
OZ(top->check_sharding_compatible_with_reduce_expr(partition_exprs, is_partition_wise));
if (is_partition_wise) {
OZ(try_allocate_sort_as_top(top, sort_keys, need_sort, prefix_pos));
OZ(allocate_window_function_as_top(winfunc_exprs,
single_part_parallel,
is_partition_wise,
ObLogWindowFunction::WindowFunctionRoleType::NORMAL,
sort_keys,
range_dist_keys_cnt,
range_dist_pby_prefix,
top,
wf_aggr_status_expr,
pushdown_info));
} else if (!is_pushdown) {
OZ(get_grouping_style_exchange_info(partition_exprs,
top->get_output_equal_sets(),
exch_info));
OZ(allocate_sort_and_exchange_as_top(top,
exch_info,
sort_keys,
need_sort,
prefix_pos,
top->get_is_local_order()));
OZ(allocate_window_function_as_top(winfunc_exprs,
single_part_parallel,
is_partition_wise,
ObLogWindowFunction::WindowFunctionRoleType::NORMAL,
sort_keys,
range_dist_keys_cnt,
range_dist_pby_prefix,
top,
wf_aggr_status_expr,
pushdown_info));
} else { // is_pushdown_supported
bool top_is_local_order = top->get_is_local_order();
// sort key + aggr_status desc, for EXCHANGE IN MERGE SORT DISTR
ObSEArray<OrderItem, 8> tmp_sort_keys;
for (int64_t i = 0; OB_SUCC(ret) && i < winfunc_exprs.count(); ++i) {
// wf_hybrid_pby_exprs_cnt_array_ is used for hybrid dist
// take the value of aggr_status to decide to use which member of wf_hybrid_pby_exprs_cnt_array_ as nkey of hash dist
if (OB_FAIL(exch_info.wf_hybrid_pby_exprs_cnt_array_.push_back(
winfunc_exprs.at(i)->get_partition_exprs().count()))) {
LOG_WARN("failed to push_back",
K(ret), K(winfunc_exprs.at(i)->get_partition_exprs().count()));
}
}
if (OB_FAIL(ret)) {
} else if ((need_sort || top_is_local_order)
&& !sort_keys.empty()
&& OB_FAIL(allocate_sort_as_top(top,
sort_keys,
need_sort && !top_is_local_order ? prefix_pos : 0,
need_sort ? false : top_is_local_order))) {
LOG_WARN("failed to allcoate sort as top", K(ret));
} else if (OB_FAIL(allocate_window_function_as_top(winfunc_exprs,
single_part_parallel,
is_partition_wise,
ObLogWindowFunction::WindowFunctionRoleType::PARTICIPATOR,
sort_keys,
range_dist_keys_cnt,
range_dist_pby_prefix,
top,
wf_aggr_status_expr,
pushdown_info))) {
LOG_WARN("failed to allocate_window_function_as_top", K(ret));
} /*else if (FALSE_IT(wf_aggr_status_expr =
static_cast<ObLogWindowFunction*>(top)->get_aggr_status_expr())) {
} */else if (OB_FAIL(get_pushdown_window_function_exchange_info(winfunc_exprs,
top->get_output_equal_sets(),
wf_aggr_status_expr,
exch_info))) {
LOG_WARN("failed to get pushdown window function exchange info", K(ret));
} else if (OB_FAIL(tmp_sort_keys.assign(sort_keys))) {
LOG_WARN("failed to assign sort_keys to tmp_sort_keys", K(ret));
} else if (OB_FAIL(tmp_sort_keys.push_back(
OrderItem(wf_aggr_status_expr, default_desc_direction())))) {
LOG_WARN("failed to push_back extra sort key of aggr status to tmp_sort_keys", K(ret));
} else if (OB_FAIL(allocate_sort_and_exchange_as_top(top,
exch_info,
tmp_sort_keys,
false,
prefix_pos,
top->get_is_local_order()))) {
LOG_WARN("failed to allocate sort as top", K(ret));
} else if (OB_FAIL(allocate_window_function_as_top(winfunc_exprs,
single_part_parallel,
is_partition_wise,
ObLogWindowFunction::WindowFunctionRoleType::CONSOLIDATOR,
sort_keys,
range_dist_keys_cnt,
range_dist_pby_prefix,
top,
wf_aggr_status_expr,
pushdown_info))) {
LOG_WARN("failed to allocate_window_function_as_top", K(ret));
} else if (FALSE_IT(static_cast<ObLogWindowFunction*>(top)->set_aggr_status_expr(
wf_aggr_status_expr))) {
// use the value of wf_aggr_status_expr to decide how to compute in consilidator wf op
}
}
} else { // single partition parallel or serialize
if (top->is_distributed()) {
OZ(match_window_function_parallel(winfunc_exprs, single_part_parallel));
}
if (top->is_distributed() && !single_part_parallel) {
OZ(allocate_sort_and_exchange_as_top(top,
exch_info,
sort_keys,
need_sort,
prefix_pos,
top->get_is_local_order()));
} else {
OZ(try_allocate_sort_as_top(top, sort_keys, need_sort, prefix_pos));
}
OZ(allocate_window_function_as_top(winfunc_exprs,
single_part_parallel,
is_partition_wise,
ObLogWindowFunction::WindowFunctionRoleType::NORMAL,
sort_keys,
range_dist_keys_cnt,
range_dist_pby_prefix,
top,
wf_aggr_status_expr,
pushdown_info));
}
}
return ret;
}
/**
* @brief 假设 winfunc_exprs 都采用 sort_keys 进行排序
* 同一个分组中的 win_expr 应该按照一定的顺序排序。
* 同一个ObWindowFunction中的多个 win_expr 要按照一定的顺序组织,譬如:
* w1(c) over (partition by e1, e2, e3)
* w2(c) over (partition by e2)
* w3(c) over (partition by e2 e3)
* 一定要按照 w1, w3, w2 的顺序来组织。即排在后面的win_expr的partition表达式是前面对象的子集。
* 这里窗口函数会按照分组表达式数量进行排序(只统计非常量的分组表达式数量,数量多的排在前)。
*/
int ObSelectLogPlan::create_hash_window_function_plan(ObLogicalOperator *&top,
const ObIArray<ObWinFunRawExpr*> &adjusted_winfunc_exprs,
const ObIArray<OrderItem> &sort_keys,
const ObIArray<ObRawExpr*> &partition_exprs,
const int64_t part_cnt,
const bool is_pushdown,
ObOpPseudoColumnRawExpr *wf_aggr_status_expr,
const ObIArray<bool> &pushdown_info)
{
int ret = OB_SUCCESS;
ObExchangeInfo exch_info;
OrderItem hash_sortkey;
bool is_partition_wise = false;
const int64_t range_dist_keys_cnt = 0;
const int64_t range_dist_pby_prefix = 0;
LOG_DEBUG("create hash window function plan", K(part_cnt), K(sort_keys), K(adjusted_winfunc_exprs));
if (OB_ISNULL(top) || OB_UNLIKELY(partition_exprs.empty())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected error", K(ret), K(top), K(partition_exprs.empty()));
} else if (top->is_distributed() &&
OB_FAIL(top->check_sharding_compatible_with_reduce_expr(partition_exprs,
is_partition_wise))) {
LOG_WARN("failed to check if sharding compatible", K(ret));
} else if (part_cnt > 0 && OB_FAIL(create_hash_sortkey(part_cnt, sort_keys, hash_sortkey))) {
LOG_WARN("failed to create hash sort key", K(ret), K(part_cnt), K(sort_keys));
} else if (!top->is_distributed() || is_partition_wise) {
exch_info.dist_method_ = ObPQDistributeMethod::NONE;
if (OB_FAIL(allocate_sort_and_exchange_as_top(top,
exch_info,
sort_keys,
true, /* need_sort */
0, /* prefix_pos */
false, /* is_local_order */
NULL, /* topn_expr */
false, /* is_fetch_with_ties */
part_cnt > 0 ? &hash_sortkey : NULL))) {
LOG_WARN("failed to allocate sort as top", K(ret));
} else if (OB_FAIL(allocate_window_function_as_top(adjusted_winfunc_exprs,
false, /* match_parallel */
is_partition_wise,
ObLogWindowFunction::WindowFunctionRoleType::NORMAL,
sort_keys,
range_dist_keys_cnt,
range_dist_pby_prefix,
top,
wf_aggr_status_expr,
pushdown_info))) {
LOG_WARN("failed to allocate window function above top", K(ret));
}
} else if (!is_pushdown) {
if (OB_FAIL(get_grouping_style_exchange_info(partition_exprs,
top->get_output_equal_sets(),
exch_info))) {
LOG_WARN("failed to get grouping style exchange info", K(ret));
} else if (OB_FAIL(allocate_sort_and_exchange_as_top(top,
exch_info,
sort_keys,
true, /* need_sort */
0, /* prefix_pos */
top->get_is_local_order(),
NULL, /* topn_expr */
false, /* is_fetch_with_ties */
part_cnt > 0 ? &hash_sortkey : NULL))) {
LOG_WARN("failed to allocate sort as top", K(ret));
} else if (OB_FAIL(allocate_window_function_as_top(adjusted_winfunc_exprs,
false, /* match_parallel */
is_partition_wise,
ObLogWindowFunction::WindowFunctionRoleType::NORMAL,
sort_keys,
range_dist_keys_cnt,
range_dist_pby_prefix,
top,
wf_aggr_status_expr,
pushdown_info))) {
LOG_WARN("failed to allocate window function as top", K(ret));
}
} else { // need_pushdown
bool top_is_local_order = top->get_is_local_order();
// sort key + aggr_status desc, for EXCHANGE IN MERGE SORT DISTR
ObSEArray<OrderItem, 8> tmp_sort_keys;
for (int64_t i = 0; OB_SUCC(ret) && i < adjusted_winfunc_exprs.count(); ++i) {
// wf_hybrid_pby_exprs_cnt_array_ is used for hybrid dist
// take the value of aggr_status to decide to use which member of wf_hybrid_pby_exprs_cnt_array_ as nkey of hash dist
if (OB_FAIL(exch_info.wf_hybrid_pby_exprs_cnt_array_.push_back(
adjusted_winfunc_exprs.at(i)->get_partition_exprs().count()))) {
LOG_WARN("failed to push_back",
K(ret), K(adjusted_winfunc_exprs.at(i)->get_partition_exprs().count()));
}
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(allocate_sort_as_top(top,
sort_keys,
0,
false,
NULL,
false, // is_fetch_with_ties
part_cnt > 0 ? &hash_sortkey : NULL))) {
LOG_WARN("failed to allcoate sort as top", K(ret));
} else {
const OrderItem &hash_sortkey = static_cast<ObLogSort*>(top)->get_hash_sortkey();
if (OB_FAIL(allocate_window_function_as_top(adjusted_winfunc_exprs,
false, /* match_parallel */
is_partition_wise,
ObLogWindowFunction::WindowFunctionRoleType::PARTICIPATOR,
sort_keys,
range_dist_keys_cnt,
range_dist_pby_prefix,
top,
wf_aggr_status_expr,
pushdown_info))) {
LOG_WARN("failed to allocate_window_function_as_top", K(ret));
} else if (OB_FAIL(get_pushdown_window_function_exchange_info(adjusted_winfunc_exprs,
top->get_output_equal_sets(),
wf_aggr_status_expr,
exch_info))) {
LOG_WARN("failed to get pushdown window function exchange info", K(ret));
} else if (OB_FAIL(tmp_sort_keys.push_back(hash_sortkey))) {
LOG_WARN("failed to push_back extra sort key hash_sortkey of aggr status to tmp_sort_keys",
K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < sort_keys.count(); ++i) {
if (OB_FAIL(tmp_sort_keys.push_back(sort_keys.at(i)))) {
LOG_WARN("failed to assign sort_keys to tmp_sort_keys", K(ret));
}
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(tmp_sort_keys.push_back(
OrderItem(wf_aggr_status_expr, default_desc_direction())))) {
LOG_WARN("failed to push_back extra sort key of aggr status to tmp_sort_keys", K(ret));
}
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(allocate_sort_and_exchange_as_top(top,
exch_info,
tmp_sort_keys,
false,
0,
top->get_is_local_order()))) {
LOG_WARN("failed to allocate sort as top", K(ret));
} else if (OB_FAIL(allocate_window_function_as_top(adjusted_winfunc_exprs,
false, /* match_parallel */
is_partition_wise,
ObLogWindowFunction::WindowFunctionRoleType::CONSOLIDATOR,
sort_keys,
range_dist_keys_cnt,
range_dist_pby_prefix,
top,
wf_aggr_status_expr,
pushdown_info))) {
LOG_WARN("failed to allocate_window_function_as_top", K(ret));
} else if (FALSE_IT(static_cast<ObLogWindowFunction*>(top)->set_aggr_status_expr(
wf_aggr_status_expr))) {
// use the value of wf_aggr_status_expr to decide how to compute in consilidator wf op
}
}
}
return ret;
}
/**
* @brief 假设 winfunc_exprs 都采用 sort_keys 进行排序
* 同一个分组中的 win_expr 应该按照一定的顺序排序。
* 同一个ObWindowFunction中的多个 win_expr 要按照一定的顺序组织,譬如:
* w1(c) over (partition by e1, e2, e3)
* w2(c) over (partition by e1)
* w3(c) over (partition by e1 e2)
* 一定要按照 w1, w3, w2 的顺序来组织。即排在后面的win_expr的partition表达式是前面对象的子集。
* 这里窗口函数会按照分组表达式数量进行排序(只统计非常量的分组表达式数量,数量多的排在前)。
*/
int ObSelectLogPlan::adjust_window_functions(const ObLogicalOperator *top,
const ObIArray<ObWinFunRawExpr *> &winfunc_exprs,
ObIArray<ObWinFunRawExpr *> &adjusted_winfunc_exprs)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else {
const EqualSets &equal_sets = top->get_output_equal_sets();
ObSEArray<std::pair<int64_t, int64_t>, 8> expr_entries;
for (int64_t i = 0; OB_SUCC(ret) && i < winfunc_exprs.count(); ++i) {
int64_t non_const_exprs = 0;
if (OB_FAIL(ObOptimizerUtil::get_non_const_expr_size(winfunc_exprs.at(i)->get_partition_exprs(),
equal_sets,
top->get_output_const_exprs(),
get_onetime_query_refs(),
non_const_exprs))) {
LOG_WARN("failed to get non const expr size", K(ret));
} else if (OB_FAIL(expr_entries.push_back(std::pair<int64_t, int64_t>(-non_const_exprs, i)))) {
LOG_WARN("faield to push back expr entry", K(ret));
}
}
if (OB_SUCC(ret)) {
std::pair<int64_t, int64_t> *first = &expr_entries.at(0);
std::sort(first, first + expr_entries.count());
for (int64_t i = 0; OB_SUCC(ret) && i < expr_entries.count(); ++i) {
if (OB_FAIL(adjusted_winfunc_exprs.push_back(winfunc_exprs.at(expr_entries.at(i).second)))) {
LOG_WARN("failed to push back window function expr", K(ret));
}
}
}
}
return ret;
}
int ObSelectLogPlan::match_window_function_parallel(const ObIArray<ObWinFunRawExpr *> &win_exprs,
bool &can_parallel)
{
int ret = OB_SUCCESS;
can_parallel = true;
for (int64_t i = 0; OB_SUCC(ret) && can_parallel && i < win_exprs.count(); ++i) {
ObWinFunRawExpr *win_expr = win_exprs.at(i);
switch(win_expr->get_func_type()) {
case T_FUN_COUNT:
case T_FUN_SUM:
case T_FUN_MAX:
case T_FUN_MIN: {
if (OB_ISNULL(win_expr->get_agg_expr())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null agg expr", K(ret));
} else if (win_expr->get_agg_expr()->is_param_distinct()) {
can_parallel = false;
}
break;
}
default: {
can_parallel = false;
break;
}
}
if (win_expr->get_upper().type_ != BoundType::BOUND_UNBOUNDED ||
win_expr->get_lower().type_ != BoundType::BOUND_UNBOUNDED ||
!win_expr->get_partition_exprs().empty()) {
can_parallel = false;
}
}
return ret;
}
int ObSelectLogPlan::check_wf_range_dist_supported(ObWinFunRawExpr *win_expr,
bool &can_rd_parallel)
{
int ret = OB_SUCCESS;
can_rd_parallel = false;
if (NULL == win_expr) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("NULL expr", K(ret));
} else {
can_rd_parallel = win_expr->get_upper().type_ == BoundType::BOUND_UNBOUNDED
&& win_expr->get_lower().type_ == BoundType::BOUND_CURRENT_ROW;
switch(win_expr->get_func_type()) {
case T_WIN_FUN_RANK:
case T_WIN_FUN_DENSE_RANK: {
can_rd_parallel = true;
break;
}
case T_FUN_COUNT:
case T_FUN_SUM:
case T_FUN_MAX:
case T_FUN_MIN: {
break;
}
default: {
can_rd_parallel = false;
}
}
}
return ret;
}
int ObSelectLogPlan::check_wf_pushdown_supported(ObWinFunRawExpr *win_expr,
bool &can_wf_pushdown)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(win_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else {
// only reporting window function can pushdown
// If win_expr->get_partition_exprs().empty(), use window_function_parallel in single partition by yishen instead
can_wf_pushdown = can_wf_pushdown
&& !win_expr->get_partition_exprs().empty()
&& win_expr->get_upper().type_ == BoundType::BOUND_UNBOUNDED
&& win_expr->get_lower().type_ == BoundType::BOUND_UNBOUNDED;
switch(win_expr->get_func_type()) {
case T_FUN_SYS_BIT_AND:
case T_FUN_SYS_BIT_OR:
case T_FUN_SYS_BIT_XOR:
case T_FUN_MIN:
case T_FUN_MAX: {
break;
}
case T_FUN_COUNT:
case T_FUN_SUM: {
if (OB_ISNULL(win_expr->get_agg_expr())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), KPC(win_expr));
} else if (win_expr->get_agg_expr()->is_param_distinct()) {
// If aggr expr is count distinct or sum distinct, wf can't be parallel.
can_wf_pushdown = false;
}
break;
}
default: {
can_wf_pushdown = false;
}
}
}
return ret;
}
int ObSelectLogPlan::get_pushdown_window_function_exchange_info(
const ObIArray<ObWinFunRawExpr *> &win_exprs,
const EqualSets & equal_sets,
ObOpPseudoColumnRawExpr *wf_aggr_status_expr,
ObExchangeInfo &exch_info)
{
int ret = OB_SUCCESS;
if (0 == win_exprs.count()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("count of win_exprs is zero", K(ret));
} else if (OB_ISNULL(win_exprs.at(0))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
// get the pby expr of the first win_expr, the pby col count of first pby expr is the most
} else if (OB_FAIL(get_grouping_style_exchange_info(win_exprs.at(0)->get_partition_exprs(),
equal_sets,
exch_info))) {
LOG_WARN("failed to get grouping style exchange info", K(ret));
} else {
exch_info.is_wf_hybrid_ = true;
// use the value of wf_aggr_status_expr to decide how to distribute in ex op
exch_info.wf_hybrid_aggr_status_expr_ = wf_aggr_status_expr;
}
return ret;
}
int ObSelectLogPlan::get_window_function_partition_exprs(const ObIArray<ObWinFunRawExpr *> &win_exprs,
ObIArray<ObRawExpr*> &partition_exprs)
{
int ret = OB_SUCCESS;
for (int64_t i = 0; OB_SUCC(ret) && i < win_exprs.count(); i++) {
if (OB_ISNULL(win_exprs.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (i == 0) {
ret = partition_exprs.assign(win_exprs.at(i)->get_partition_exprs());
} else {
ret = ObOptimizerUtil::intersect(partition_exprs,
win_exprs.at(i)->get_partition_exprs(),
partition_exprs);
}
}
return ret;
}
int ObSelectLogPlan::candi_allocate_late_materialization()
{
int ret = OB_SUCCESS;
bool need_late_mat = false;
if (OB_FAIL(if_stmt_need_late_materialization(need_late_mat))) {
LOG_WARN("failed to check if stmt need late materialization", K(ret));
} else if (need_late_mat) {
for (int64_t i = 0; OB_SUCC(ret) && i < candidates_.candidate_plans_.count(); ++i) {
bool need = false;
ObLogTableScan *index_scan = NULL;
CandidatePlan &plain_plan = candidates_.candidate_plans_.at(i);
double cost = 0.0;
if (OB_FAIL(if_plan_need_late_materialization(plain_plan.plan_tree_,
index_scan,
cost,
need))) {
LOG_WARN("failed to check if need late materialization", K(ret));
} else if (need) {
plain_plan.plan_tree_->set_late_materialization(true);
plain_plan.plan_tree_->set_cost(cost);
}
}
}
return ret;
}
int ObSelectLogPlan::get_late_materialization_operator(ObLogicalOperator *top,
ObLogSort *&sort_op,
ObLogTableScan *&table_scan)
{
int ret = OB_SUCCESS;
ObLogicalOperator *child_sort = NULL;
ObLogicalOperator *child_scan = NULL;
if (OB_ISNULL(top)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("operator is null", K(ret));
} else if (log_op_def::LOG_LIMIT == top->get_type()) {
child_sort = top->get_child(ObLogicalOperator::first_child);
} else if (log_op_def::LOG_SORT == top->get_type()) {
child_sort = top;
} else { /*do nothing*/ }
if (NULL == child_sort) {
/*do nothing*/
} else if (log_op_def::LOG_SORT != child_sort->get_type()) {
/*do nothing*/
} else if (OB_ISNULL(child_scan = child_sort->get_child(ObLogicalOperator::first_child))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (log_op_def::LOG_TABLE_SCAN != child_scan->get_type()) {
/*do nothing*/
} else if (OB_FALSE_IT(table_scan = static_cast<ObLogTableScan *>(child_scan))) {
} else if (NULL != table_scan->get_limit_expr() || NULL != table_scan->get_offset_expr()) {
table_scan = NULL;
} else {
sort_op = static_cast<ObLogSort *>(child_sort);
}
return ret;
}
int ObSelectLogPlan::perform_late_materialization(ObSelectStmt *stmt,
ObLogicalOperator *&op)
{
int ret = OB_SUCCESS;
ObLogSort *sort = NULL;
ObLogTableScan *index_scan = NULL;
TableItem *nl_table_item = NULL;
ObLogTableScan *nl_table_get = NULL;
ObLogicalOperator *join_op = NULL;
if (OB_ISNULL(stmt) || OB_ISNULL(op)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("params have null", K(ret), K(op), K(stmt));
} else if (OB_FAIL(get_late_materialization_operator(op,
sort,
index_scan))) {
LOG_WARN("failed to get late materialization operator", K(ret));
} else if (OB_FAIL(generate_late_materialization_info(stmt,
index_scan,
nl_table_get,
nl_table_item))) {
LOG_WARN("failed to generate later materialization table get", K(ret));
} else if (OB_FAIL(allocate_late_materialization_join_as_top(op,
nl_table_get,
join_op))) {
LOG_WARN("failed to generate late materialization join", K(ret));
} else if (OB_FAIL(adjust_late_materialization_structure(stmt,
join_op,
index_scan,
nl_table_get,
nl_table_item))) {
LOG_WARN("failed to adjust late materialization structure", K(ret));
} else {
op = join_op;
}
return ret;
}
int ObSelectLogPlan::adjust_late_materialization_structure(ObSelectStmt *stmt,
ObLogicalOperator *join,
ObLogTableScan *index_scan,
ObLogTableScan *table_scan,
TableItem *table_item)
{
int ret = OB_SUCCESS;
if (OB_FAIL(adjust_late_materialization_stmt_structure(stmt,
index_scan,
table_scan,
table_item))) {
LOG_WARN("failed to adjust late materialization stmt structure", K(ret));
} else if (OB_FAIL(adjust_late_materialization_plan_structure(join,
index_scan,
table_scan))) {
LOG_WARN("failed to adjust latematerialization plan structure", K(ret));
} else { /*do nothing*/ }
return ret;
}
int ObSelectLogPlan::convert_project_columns(ObSelectStmt *stmt,
ObIRawExprCopier &expr_copier,
uint64_t table_id,
TableItem *project_table_item,
ObIArray<uint64_t> &index_columns)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get_stmt() returns null", K(ret));
} else {
ColumnItem *item = NULL;
ObColumnRefRawExpr *expr = NULL;
common::ObSEArray<ColumnItem, 16> new_col_items;
common::ObSEArray<ObRawExpr *, 4> gen_exprs;
common::ObSEArray<ObRawExpr *, 4> dependant_exprs;
for (int64_t i = 0; OB_SUCC(ret) && i < stmt->get_column_size(); ++i) {
if (OB_ISNULL(item = stmt->get_column_item(i)) || OB_ISNULL(expr = item->get_expr())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("Unexpected NULL pointer", K(item), K(ret));
} else if (item->table_id_ == table_id &&
!ObOptimizerUtil::find_item(index_columns, item->column_id_)) {
item->set_ref_id(project_table_item->table_id_, item->column_id_);
expr->set_ref_id(project_table_item->table_id_, expr->get_column_id());
expr->set_table_name(project_table_item->get_table_name());
if (expr->is_virtual_generated_column()) {
if (item->is_geo_ == true && expr->get_srs_id() != SPATIAL_COLUMN_SRID_MASK) {
// spatial index generated column, cannot projet from main table
if (OB_FAIL(new_col_items.push_back(*item))) {
LOG_WARN("failed to push back column item", K(ret));
}
} else if (OB_FAIL(gen_exprs.push_back(expr))) {
LOG_WARN("failed to push back", K(ret));
} else if (OB_FAIL(dependant_exprs.push_back(expr->get_dependant_expr()))) {
LOG_WARN("failed to push back", K(ret));
}
} else {
if (OB_FAIL(new_col_items.push_back(*item))) {
LOG_WARN("failed to push back column item", K(ret));
}
}
} else if (OB_FAIL(new_col_items.push_back(*item))){
LOG_WARN("failed to push back column item", K(ret));
}
}
common::ObSEArray<ObRawExprPointer, 16> relation_exprs;
if (OB_FAIL(ret) || new_col_items.count() == stmt->get_column_size()) {
} else if (OB_FAIL(stmt->get_column_items().assign(new_col_items))) {
LOG_WARN("failed to assign", K(ret));
} else if (OB_FAIL(stmt->get_relation_exprs(relation_exprs))) {
LOG_WARN("failed to get exprs", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && i < relation_exprs.count(); i++) {
ObRawExpr *replace_expr = NULL;
if (OB_FAIL(relation_exprs.at(i).get(replace_expr))) {
LOG_WARN("failed to get expr", K(ret));
} else if (OB_FAIL(ObTransformUtils::replace_expr(gen_exprs, dependant_exprs, replace_expr))) {
LOG_WARN("failed to replace", K(ret));
} else if (OB_FAIL(relation_exprs.at(i).set(replace_expr))) {
LOG_WARN("failed to set expr", K(ret));
}
}
}
return ret;
}
int ObSelectLogPlan::adjust_late_materialization_stmt_structure(ObSelectStmt *stmt,
ObLogTableScan *index_scan,
ObLogTableScan *table_scan,
TableItem *table_item)
{
int ret = OB_SUCCESS;
ObSEArray<uint64_t, 4> old_column_ids;
ObSEArray<ObRawExpr*, 4> temp_exprs;
ObSEArray<ObRawExpr*, 8> rowkeys;
ObRawExprCopier expr_copier(get_optimizer_context().get_expr_factory());
if (OB_ISNULL(stmt) ||
OB_ISNULL(index_scan) ||
OB_ISNULL(index_scan->get_est_cost_info()) ||
OB_ISNULL(table_scan) || OB_ISNULL(table_item)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(stmt), K(index_scan), K(table_scan), K(table_item), K(ret));
} else if (OB_FAIL(stmt->get_table_items().push_back(table_item))) {
LOG_WARN("failed to push back table item", K(ret));
} else if (OB_FAIL(stmt->set_table_bit_index(table_item->table_id_))) {
LOG_WARN("failed to set table bit index", K(ret));
} else if (OB_FAIL(old_column_ids.assign(index_scan->get_est_cost_info()->access_columns_))) {
LOG_WARN("failed to assign column ids", K(ret));
} else if (OB_FAIL(get_rowkey_exprs(index_scan->get_table_id(),
index_scan->get_ref_table_id(),
rowkeys))) {
LOG_WARN("failed to generate rowkey exprs", K(ret));
} else if (OB_FAIL(stmt->get_select_exprs(temp_exprs))) {
LOG_WARN("failed to get select exprs", K(ret));
} else if (OB_FAIL(append(temp_exprs, rowkeys))) {
LOG_WARN("failed to append exprs", K(ret));
} else {
// mark rowkeys as referenced
for (int64_t i = 0; OB_SUCC(ret) && i < rowkeys.count(); i++) {
if (OB_ISNULL(rowkeys.at(i)) || OB_UNLIKELY(!rowkeys.at(i)->is_column_ref_expr())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (is_shadow_column(static_cast<ObColumnRefRawExpr *>(rowkeys.at(i))->get_column_id())) {
/*do nothing*/
} else {
rowkeys.at(i)->set_explicited_reference();
}
}
ObIArray<ColumnItem> &range_columns = index_scan->get_est_cost_info()->range_columns_;
for (int64_t i = 0; OB_SUCC(ret) && i < range_columns.count(); ++i) {
ObRawExpr *new_col_expr = NULL;
ObColumnRefRawExpr *col_expr = range_columns.at(i).expr_;
if (OB_FAIL(expr_copier.copy(col_expr, new_col_expr))) {
LOG_WARN("failed to copy expr", K(ret));
} else if (OB_ISNULL(new_col_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("copy expr failed", K(ret));
} else if (!new_col_expr->is_column_ref_expr()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("expect column ref expr", K(ret));
} else {
range_columns.at(i).expr_ = static_cast<ObColumnRefRawExpr *>(new_col_expr);
}
}
//索引列、rowkey列由原表投影,剩下需要回表的列由复制表投影
if (OB_FAIL(ret)) {
} else if (OB_FAIL(index_scan->set_range_columns(range_columns))) {
LOG_WARN("failed to set range columns", K(ret));
} else if (OB_FAIL(convert_project_columns(stmt,
expr_copier,
index_scan->get_table_id(),
table_item,
old_column_ids))) {
LOG_WARN("failed to convert project columns", K(ret));
} else if (OB_FAIL(get_rowkey_exprs(table_scan->get_table_id(),
table_scan->get_ref_table_id(),
rowkeys))) {
LOG_WARN("failed to generate rowkeys", K(table_scan->get_table_id()), K(ret));
} else { /*do nothing*/ }
}
return ret;
}
int ObSelectLogPlan::adjust_late_materialization_plan_structure(ObLogicalOperator *join,
ObLogTableScan *index_scan,
ObLogTableScan *table_scan)
{
int ret = OB_SUCCESS;
const ObSelectStmt *stmt = NULL;
const ObTableSchema *table_schema = NULL;
ObSqlSchemaGuard *schema_guard = NULL;
ObSEArray<uint64_t, 8> rowkey_ids;
if (OB_ISNULL(stmt = get_stmt()) || OB_ISNULL(join) ||
OB_ISNULL(index_scan) || OB_ISNULL(table_scan)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(stmt), K(join), K(index_scan), K(table_scan), K(ret));
} else if (OB_UNLIKELY(log_op_def::LOG_JOIN != join->get_type())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected join type", K(join->get_type()), K(ret));
} else if (OB_ISNULL(schema_guard = get_optimizer_context().get_sql_schema_guard())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(schema_guard->get_table_schema(
index_scan->get_table_id(),
index_scan->get_ref_table_id(),
stmt,
table_schema))) {
LOG_WARN("failed to get table schema", K(ret));
} else if (OB_ISNULL(table_schema)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(table_schema), K(ret));
} else if (OB_FAIL(table_schema->get_rowkey_column_ids(rowkey_ids))) {
LOG_WARN("failed to get rowkey ids", K(ret));
} else {
ObLogJoin *log_join = static_cast<ObLogJoin*>(join);
ObSEArray<ColumnItem, 4> range_columns;
ObSEArray<ObRawExpr*, 4> join_conditions;
ObSEArray<ObExecParamRawExpr *, 4> scan_params;
for (int64_t i = 0; OB_SUCC(ret) && i < rowkey_ids.count(); i++) {
const ColumnItem *col_item = NULL;
ObRawExpr *equal_expr = NULL;
ObRawExpr *left_expr = NULL;
ObRawExpr *right_expr = NULL;
if (OB_ISNULL(left_expr = get_column_expr_by_id(index_scan->get_table_id(),
rowkey_ids.at(i))) ||
OB_ISNULL(right_expr = get_column_expr_by_id(table_scan->get_table_id(),
rowkey_ids.at(i)))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(left_expr), K(right_expr), K(ret));
} else if (OB_FAIL(ObRawExprUtils::create_new_exec_param(optimizer_context_.get_query_ctx(),
optimizer_context_.get_expr_factory(),
left_expr))) {
LOG_WARN("create param for stmt error in extract_params_for_nl", K(ret));
} else if (OB_FAIL(ObRawExprUtils::create_equal_expr(optimizer_context_.get_expr_factory(),
optimizer_context_.get_session_info(),
right_expr,
left_expr,
equal_expr))) {
LOG_WARN("failed to crerate equal expr", K(ret));
} else if (OB_ISNULL(equal_expr)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(scan_params.push_back(static_cast<ObExecParamRawExpr *>(left_expr)))) {
LOG_WARN("failed to push back scan param", K(ret));
} else if (OB_FAIL(join_conditions.push_back(equal_expr))) {
LOG_WARN("failed to push back equal expr", K(ret));
} else if (OB_ISNULL(col_item = get_column_item_by_id(table_scan->get_table_id(),
rowkey_ids.at(i)))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(table_scan->get_table_id()),
K(rowkey_ids.at(i)), K(col_item), K(ret));
} else if (OB_FAIL(range_columns.push_back(*col_item))) {
LOG_WARN("failed to push back column item", K(ret));
} else { /*do nothing*/ }
}
//生成pre-query-range
if (OB_SUCC(ret)) {
const ObDataTypeCastParams dtc_params =
ObBasicSessionInfo::create_dtc_params(optimizer_context_.get_session_info());
ObQueryRange *query_range = static_cast<ObQueryRange*>(get_allocator().alloc(sizeof(ObQueryRange)));
const ParamStore *params = get_optimizer_context().get_params();
if (OB_ISNULL(query_range)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("failed to allocate memory for query range", K(ret));
} else if (OB_ISNULL(params)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else {
query_range = new(query_range)ObQueryRange(get_allocator());
if (OB_FAIL(query_range->preliminary_extract_query_range(range_columns,
join_conditions,
dtc_params,
optimizer_context_.get_exec_ctx(),
NULL,
params))) {
LOG_WARN("failed to preliminary extract query range", K(ret));
} else if (OB_FAIL(table_scan->set_range_columns(range_columns))) {
LOG_WARN("failed to set range columns", K(ret));
} else {
table_scan->set_pre_query_range(query_range);
}
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(table_scan->set_table_scan_filters(join_conditions))) {
LOG_WARN("failed to set filters", K(ret));
} else if (OB_FAIL(log_join->set_nl_params(scan_params))) {
LOG_WARN("failed to set nl params", K(ret));
} else {
// set index scan need late materialization, used to print outline.
index_scan->set_late_materialization(true);
}
}
}
return ret;
}
int ObSelectLogPlan::generate_late_materialization_info(ObSelectStmt *stmt,
ObLogTableScan *index_scan,
ObLogTableScan *&table_get,
TableItem *&table_item)
{
int ret = OB_SUCCESS;
table_get = NULL;
table_item = NULL;
if (OB_ISNULL(stmt) || OB_ISNULL(optimizer_context_.get_query_ctx())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(stmt), K(ret));
} else {
uint64_t new_table_id = optimizer_context_.get_query_ctx()->available_tb_id_--;
if (OB_FAIL(generate_late_materialization_table_item(stmt,
index_scan->get_table_id(),
new_table_id,
table_item))) {
LOG_WARN("failed to generate late materialization table item", K(ret));
} else if (OB_FAIL(generate_late_materialization_table_get(index_scan,
table_item,
new_table_id,
table_get))) {
LOG_WARN("failed to generate late materialization table get", K(ret));
} else { /*do nothing*/}
}
return ret;
}
int ObSelectLogPlan::generate_late_materialization_table_get(ObLogTableScan *index_scan,
TableItem *table_item,
uint64_t table_id,
ObLogTableScan *&table_get)
{
int ret = OB_SUCCESS;
const ObDMLStmt *stmt = NULL;
ObLogTableScan *table_scan = NULL;
ObTablePartitionInfo *table_scan_part_info = NULL;
const ObTablePartitionInfo *index_scan_part_info = NULL;
ObIAllocator &allocator = get_allocator();
table_get = NULL;
if (OB_ISNULL(index_scan) || OB_ISNULL(index_scan->get_sharding()) || OB_ISNULL(table_item) ||
OB_ISNULL(stmt = get_stmt()) || OB_ISNULL(optimizer_context_.get_query_ctx()) ||
OB_ISNULL(index_scan_part_info = index_scan->get_table_partition_info())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(index_scan), K(table_item), K(stmt),
K(index_scan_part_info), K(ret));
} else if (OB_ISNULL(table_scan = static_cast<ObLogTableScan*>(get_log_op_factory().allocate(
*this, LOG_TABLE_SCAN)))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("failed to allocate table scan operator", K(ret));
} else if (OB_ISNULL(table_scan_part_info = static_cast<ObTablePartitionInfo*>(allocator.alloc(
sizeof(ObTablePartitionInfo))))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("failed to allocate table partition info", K(ret));
} else if (FALSE_IT(table_scan_part_info = new (table_scan_part_info) ObTablePartitionInfo(allocator))) {
} else if (OB_FAIL(table_scan_part_info->assign(*index_scan_part_info))) {
LOG_WARN("failed to assigin table partition info", K(ret));
} else {
table_scan->set_index_back(false);
table_scan->set_table_id(table_id);
table_scan->set_ref_table_id(index_scan->get_ref_table_id());
table_scan->set_index_table_id(index_scan->get_ref_table_id());
table_scan->set_scan_direction(index_scan->get_scan_direction());
// 改掉project table partition info的table_id
table_scan_part_info->get_table_location().set_table_id(table_scan->get_table_id());
table_scan_part_info->get_phy_tbl_location_info_for_update().set_table_location_key(
table_scan->get_table_id(), table_scan->get_ref_table_id());
table_scan->set_table_partition_info(table_scan_part_info);
table_scan->set_strong_sharding(index_scan->get_strong_sharding());
table_scan->set_phy_plan_type(index_scan->get_phy_plan_type());
table_scan->set_location_type(index_scan->get_location_type());
table_scan->set_flashback_query_expr(index_scan->get_flashback_query_expr());
table_scan->set_flashback_query_type(index_scan->get_flashback_query_type());
table_scan->set_fq_read_tx_uncommitted(index_scan->get_fq_read_tx_uncommitted());
table_scan->set_part_ids(index_scan->get_part_ids());
table_scan->get_table_name() = table_item->alias_name_.length() > 0 ?
table_item->alias_name_ : table_item->table_name_;
// set card and cost
table_scan->set_card(1.0);
table_scan->set_op_cost(ObOptEstCost::cost_late_materialization_table_get(stmt->get_column_size(),
get_optimizer_context().get_cost_model_type()));
table_scan->set_cost(table_scan->get_op_cost());
table_scan->set_table_row_count(index_scan->get_table_row_count());
table_scan->set_output_row_count(1.0);
table_scan->set_phy_query_range_row_count(1.0);
table_scan->set_query_range_row_count(1.0);
table_get = table_scan;
}
return ret;
}
int ObSelectLogPlan::generate_late_materialization_table_item(ObSelectStmt *stmt,
uint64_t old_table_id,
uint64_t new_table_id,
TableItem *&new_table_item)
{
int ret = OB_SUCCESS;
TableItem *temp_item = NULL;
TableItem *old_table_item = NULL;
new_table_item = NULL;
if (OB_ISNULL(stmt) ||
OB_ISNULL(old_table_item = stmt->get_table_item_by_id(old_table_id))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_ISNULL(temp_item = stmt->create_table_item(get_allocator()))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("failed to create temp table item", K(ret));
} else {
temp_item->type_ = TableItem::ALIAS_TABLE;
temp_item->database_name_ = old_table_item->database_name_;
temp_item->table_name_ = old_table_item->table_name_;
temp_item->is_system_table_ = old_table_item->is_system_table_;
temp_item->is_view_table_ = old_table_item->is_view_table_;
temp_item->table_id_ = new_table_id;
temp_item->ref_id_ = old_table_item->ref_id_;
temp_item->table_type_ = old_table_item->table_type_;
const char *str = "_alias";
char *buf = static_cast<char*>(get_allocator().alloc(
old_table_item->table_name_.length() + strlen(str)));
if (OB_UNLIKELY(NULL == buf)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("failed to allocate string buffer", K(ret));
} else {
MEMCPY(buf, old_table_item->table_name_.ptr(), old_table_item->table_name_.length());
MEMCPY(buf + old_table_item->table_name_.length(), str, strlen(str));
temp_item->alias_name_.assign_ptr(buf, old_table_item->table_name_.length() + strlen(str));
}
if (OB_SUCC(ret)) {
new_table_item = temp_item;
}
}
return ret;
}
int ObSelectLogPlan::allocate_late_materialization_join_as_top(ObLogicalOperator *left_child,
ObLogicalOperator *right_child,
ObLogicalOperator *&join_op)
{
int ret = OB_SUCCESS;
ObLogJoin *join = NULL;
ObLogicalOperator *parent = NULL;
if (OB_ISNULL(left_child) || OB_ISNULL(right_child)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_ISNULL(join = static_cast<ObLogJoin *>(get_log_op_factory().allocate(*this, LOG_JOIN)))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("failed to allocate join_op operator", K(ret));
} else if (OB_ISNULL(join)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(left_child->est_cost())) {
LOG_WARN("failed to estimate cost for left child", K(ret));
} else {
parent = left_child->get_parent();
join->set_left_child(left_child);
join->set_right_child(right_child);
if (NULL != parent) {
for (int64_t i = 0; i < parent->get_num_of_child(); i++) {
if (left_child == parent->get_child(i)) {
parent->set_child(i, join);
join->set_parent(parent);
break;
}
}
}
left_child->set_parent(join);
right_child->set_parent(join);
if (left_child->is_plan_root()) {
join->mark_is_plan_root();
left_child->set_is_plan_root(false);
set_plan_root(join);
}
join->set_join_type(INNER_JOIN);
join->set_join_algo(NESTED_LOOP_JOIN);
join->set_late_mat(true);
join->set_card(left_child->get_card());
join->set_width(left_child->get_width());
ObOptEstCost::cost_late_materialization_table_join(left_child->get_card(),
left_child->get_cost(),
right_child->get_card(),
right_child->get_cost(),
join->get_op_cost(),
join->get_cost(),
get_optimizer_context().get_cost_model_type());
if (OB_FAIL(join->set_op_ordering(left_child->get_op_ordering()))) {
LOG_WARN("failed to set op ordering", K(ret));
} else {
join->set_location_type(left_child->get_location_type());
join->set_phy_plan_type(left_child->get_phy_plan_type());
join->set_strong_sharding(left_child->get_sharding());
join->set_interesting_order_info(left_child->get_interesting_order_info());
join->set_fd_item_set(&left_child->get_fd_item_set());
join_op = join;
}
}
return ret;
}
int ObSelectLogPlan::if_plan_need_late_materialization(ObLogicalOperator *top,
ObLogTableScan *&index_scan,
double &late_mater_cost,
bool &need)
{
int ret = OB_SUCCESS;
const ObDMLStmt *stmt = NULL;
need = false;
index_scan = NULL;
ObLogSort *child_sort = NULL;
ObLogTableScan *table_scan = NULL;
if (OB_ISNULL(top) || OB_ISNULL(stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(top), K(get_stmt()), K(ret));
} else if (OB_FAIL(get_late_materialization_operator(top,
child_sort,
table_scan))) {
LOG_WARN("failed to get late materialization operator", K(ret));
} else if (NULL != table_scan && NULL != table_scan->get_plan() && NULL != child_sort) {
ObSEArray<ObRawExpr*, 4> temp_exprs;
ObSEArray<ObRawExpr*, 4> table_keys;
ObSEArray<uint64_t, 4> index_column_ids;
ObSEArray<uint64_t, 4> used_column_ids;
const ObTableSchema *index_schema = NULL;
ObSqlSchemaGuard *schema_guard = NULL;
// check whether index key cover filter exprs, sort exprs and part exprs
if (table_scan->is_index_scan() && table_scan->get_index_back() &&
(table_scan->is_local() || table_scan->is_remote())) {
if (OB_FAIL(get_rowkey_exprs(table_scan->get_table_id(),
table_scan->get_ref_table_id(),
table_keys))) {
LOG_WARN("failed to generate rowkey exprs", K(ret));
} else if (OB_FAIL(child_sort->get_sort_exprs(temp_exprs))) {
LOG_WARN("failed to get sort exprs", K(ret));
} else if (OB_FAIL(append(temp_exprs, table_scan->get_filter_exprs())) ||
OB_FAIL(append(temp_exprs, table_keys))) {
LOG_WARN("failed to append exprs", K(ret));
} else if (NULL != table_scan->get_pre_query_range() &&
OB_FAIL(append(temp_exprs, table_scan->get_pre_query_range()->get_range_exprs()))) {
LOG_WARN("failed to append exprs", K(ret));
} else if (OB_ISNULL(schema_guard = get_optimizer_context().get_sql_schema_guard())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected nul", K(ret));
} else if (OB_FAIL(schema_guard->get_table_schema(
table_scan->get_table_id(),
table_scan->get_index_table_id(),
stmt,
index_schema))) {
LOG_WARN("failed to get table schema", K(ret));
} else if (OB_ISNULL(index_schema)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(index_schema->get_column_ids(index_column_ids))) {
LOG_WARN("failed to get column ids", K(ret));
} else if (OB_FAIL(ObRawExprUtils::extract_column_ids(temp_exprs, used_column_ids))) {
LOG_WARN("failed to extract column ids", K(ret));
} else if (ObOptimizerUtil::is_subset(used_column_ids, index_column_ids)){
bool has_other_col = false;
for (int64_t i = 0; OB_SUCC(ret) && !has_other_col && i < stmt->get_column_size(); i++) {
const ColumnItem *item = stmt->get_column_item(i);
if (OB_ISNULL(item)) {
ret = OB_ERR_UNEXPECTED;
} else if (item->get_expr()->is_virtual_generated_column()) {
// do nothing
} else if (!ObOptimizerUtil::find_item(index_column_ids, item->base_cid_)) {
has_other_col = true;
}
}
need = has_other_col;
}
}
// update cost for late materialization
if (OB_SUCC(ret) && need) {
OPT_TRACE("try late materialization plan, normal plan cost:", top->get_cost());
if (OB_ISNULL(table_scan->get_est_cost_info())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(table_scan->get_est_cost_info()->access_columns_.assign(used_column_ids))) {
LOG_WARN("failed to assign column ids", K(ret));
} else {
table_scan->get_est_cost_info()->index_meta_info_.is_index_back_ = false;
table_scan->set_index_back(false);
table_scan->set_index_back_row_count(0.0);
double query_range_row_count = table_scan->get_query_range_row_count();
double phy_query_range_row_count = table_scan->get_phy_query_range_row_count();
double op_cost = 0.0;
double index_back_cost = 0.0;
// estimate cost
if (OB_FAIL(ObOptEstCost::cost_table(*table_scan->get_est_cost_info(),
table_scan->get_parallel(),
query_range_row_count,
phy_query_range_row_count,
op_cost,
index_back_cost,
get_optimizer_context().get_cost_model_type()))) {
LOG_WARN("failed to get index access info", K(ret));
} else {
table_scan->set_cost(op_cost);
table_scan->set_op_cost(op_cost);
}
// estimate width
if (OB_FAIL(ret)) {
/*do nothing*/
} else {
double width = 0.0;
ObSEArray<ObRawExpr*, 8> column_exprs;
for (int64_t i = 0; OB_SUCC(ret) && i < used_column_ids.count(); i++) {
const ColumnItem *col_item = NULL;
if (OB_ISNULL(col_item = get_column_item_by_id(table_scan->get_table_id(),
used_column_ids.at(i))) ||
OB_ISNULL(col_item->expr_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(col_item), K(ret));
} else if (OB_FAIL(column_exprs.push_back(col_item->expr_))) {
LOG_WARN("failed to push back column expr", K(ret));
} else { /*do nothing*/ }
}
if (OB_FAIL(ret)) {
/*do nothing*/
} else if (OB_FAIL(ObOptEstCost::estimate_width_for_exprs(table_scan->get_plan()->get_basic_table_metas(),
table_scan->get_plan()->get_selectivity_ctx(),
column_exprs,
width))) {
LOG_WARN("failed to estimate width for columns", K(ret));
} else {
table_scan->set_width(width);
}
}
if (OB_FAIL(ret)) {
/*do nothing*/
} else if (OB_FAIL(child_sort->est_cost())) {
LOG_WARN("failed to compute property", K(ret));
} else if (OB_FAIL(top->est_cost())) {
LOG_WARN("failed to compute property", K(ret));
} else {
index_scan = table_scan;
ObOptEstCost::cost_late_materialization(top->get_card(),
top->get_cost(),
stmt->get_column_size(),
late_mater_cost,
get_optimizer_context().get_cost_model_type());
OPT_TRACE("late materialization plan cost:", late_mater_cost);
}
}
}
}
return ret;
}
int ObSelectLogPlan::if_stmt_need_late_materialization(bool &need)
{
int ret = OB_SUCCESS;
const ObSelectStmt *select_stmt = NULL;
int64_t child_stmt_size = 0;
need = false;
if (OB_ISNULL(select_stmt = get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(select_stmt->get_child_stmt_size(child_stmt_size))) {
LOG_WARN("failed to get child stmt size", K(ret));
} else {
need = !get_log_plan_hint().no_use_late_material()
&& select_stmt->has_limit()
&& select_stmt->has_order_by()
&& !select_stmt->has_hierarchical_query()
&& !select_stmt->has_group_by()
&& !select_stmt->has_rollup()
&& !select_stmt->has_having()
&& !select_stmt->has_window_function()
&& !select_stmt->has_distinct()
&& !select_stmt->is_unpivot_select()
&& 1 == select_stmt->get_from_item_size()
&& 1 == select_stmt->get_table_size()
&& NULL != select_stmt->get_table_item(0)
&& select_stmt->get_table_item(0)->is_basic_table()
&& !select_stmt->get_table_item(0)->is_system_table_ //不允许系统表
&& NULL == get_stmt()->get_part_expr(select_stmt->get_table_item(0)->table_id_,
select_stmt->get_table_item(0)->ref_id_) //不允许分区表
&& 0 == child_stmt_size //不允许有子查询
&& !select_stmt->is_calc_found_rows();
}
return ret;
}
int ObSelectLogPlan::candi_allocate_unpivot()
{
int ret = OB_SUCCESS;
CandidatePlan candidate_plan;
ObSEArray<CandidatePlan, 16> unpivot_plans;
for (int64_t i = 0; OB_SUCC(ret) && i < candidates_.candidate_plans_.count(); ++i) {
candidate_plan = candidates_.candidate_plans_.at(i);
if (OB_FAIL(allocate_unpivot_as_top(candidate_plan.plan_tree_))) {
LOG_WARN("failed to allocate unpovit as top", K(i), K(ret));
} else if (OB_FAIL(unpivot_plans.push_back(candidate_plan))) {
LOG_WARN("failed to push back candidate plan", K(ret));
} else { /*do nothing*/ }
}
if (OB_SUCC(ret)) {
if (OB_FAIL(prune_and_keep_best_plans(unpivot_plans))) {
LOG_WARN("failed to prune and keep best plans", K(ret));
} else { /*do nothing*/ }
}
return ret;
}
int ObSelectLogPlan::allocate_unpivot_as_top(ObLogicalOperator *&old_top)
{
int ret = OB_SUCCESS;
const ObDMLStmt *stmt = static_cast<const ObDMLStmt *>(get_stmt());
ObLogUnpivot *unpivot = NULL;
if (OB_ISNULL(old_top)
|| OB_ISNULL(stmt)
|| OB_UNLIKELY(!stmt->is_unpivot_select())
|| OB_UNLIKELY(stmt->get_table_items().empty())
|| OB_ISNULL(stmt->get_table_item(0))) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("Get unexpected null", K(ret), K(old_top), KPC(stmt));
} else if (OB_ISNULL(unpivot = static_cast<ObLogUnpivot*>
(get_log_op_factory().allocate(*this, LOG_UNPIVOT)))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("failed to allocate subquery operator", K(ret));
} else {
const TableItem *table_item = stmt->get_table_item(0);
unpivot->unpivot_info_ = stmt->get_unpivot_info();
unpivot->set_subquery_id(table_item->table_id_);
unpivot->get_subquery_name().assign_ptr(table_item->table_name_.ptr(),
table_item->table_name_.length());
unpivot->set_child(ObLogicalOperator::first_child, old_top);
if (OB_FAIL(unpivot->compute_property())) {
LOG_WARN("failed to compute property", K(ret));
} else {
old_top = unpivot;
}
}
return ret;
}
int ObSelectLogPlan::init_selectivity_metas_for_set(ObSelectLogPlan *sub_plan,
const uint64_t child_offset)
{
int ret = OB_SUCCESS;
const ObSelectStmt *sub_stmt = NULL;
ObLogicalOperator *best_plan = NULL;
if (OB_ISNULL(sub_plan) || OB_ISNULL(sub_stmt = sub_plan->get_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(sub_plan), K(sub_stmt));
} else if (OB_INVALID_ID != sub_stmt->get_dblink_id()) {
// skip
} else if (OB_FAIL(sub_plan->get_candidate_plans().get_best_plan(best_plan))) {
LOG_WARN("failed to get best plan", K(ret));
} else if (OB_ISNULL(best_plan)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_FAIL(get_basic_table_metas().add_set_child_stmt_meta_info(
get_stmt(), sub_stmt, child_offset,
sub_plan->get_update_table_metas(),
sub_plan->get_selectivity_ctx(),
best_plan->get_card()))) {
LOG_WARN("failed to add set child stmt meta info", K(ret));
} else if (OB_FAIL(get_update_table_metas().copy_table_meta_info(get_basic_table_metas(),
child_offset))) {
LOG_WARN("failed to copy table meta info", K(ret));
}
return ret;
}
int ObSelectLogPlan::check_external_table_scan(ObSelectStmt *stmt, bool &has_external_table)
{
int ret = OB_SUCCESS;
ObArray<ObSelectStmt*> child_stmts;
if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("stmt is null", K(ret));
} else if (stmt->has_external_table()) {
has_external_table = true;
} else {
if (OB_FAIL(stmt->get_child_stmts(child_stmts))) {
LOG_WARN("fail to get child stmt", K(ret));
}
for (int i = 0; OB_SUCC(ret) && !has_external_table && i < child_stmts.count(); i++) {
ret = SMART_CALL(check_external_table_scan(child_stmts.at(i), has_external_table));
}
}
return ret;
}
}//sql
}//oceanbase