Files
oceanbase/src/sql/optimizer/ob_explain_log_plan.cpp
zzg19950727 3cada22bdc [FEAT MERGE]4_2_sql_feature
Co-authored-by: yinyj17 <yinyijun92@gmail.com>
Co-authored-by: xianyu-w <707512433@qq.com>
Co-authored-by: jingtaoye35 <1255153887@qq.com>
2023-04-28 11:12:11 +00:00

221 lines
9.9 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 "lib/json/ob_json.h"
#include "sql/optimizer/ob_explain_log_plan.h"
#include "sql/optimizer/ob_log_operator_factory.h"
#include "sql/optimizer/ob_log_plan_factory.h"
#include "sql/optimizer/ob_log_values.h"
#include "sql/code_generator/ob_code_generator.h"
#include "sql/monitor/ob_sql_plan.h"
using namespace oceanbase;
using namespace sql;
using namespace oceanbase::common;
using namespace oceanbase::json;
using namespace oceanbase::sql::log_op_def;
/**
* This function does two things:
*
* 1. generate a logical plan for the 'real' query that's being 'explained'
* 2. generate the plan text from the logical plan and put the text in the buffer
* and remember the logical plan as well.
*/
int ObExplainLogPlan::generate_normal_raw_plan()
{
int ret = OB_SUCCESS;
if (OB_ISNULL(get_stmt()) || OB_UNLIKELY(!get_stmt()->is_explain_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected error", K(get_stmt()), K(ret));
} else {
ObLogPlan *child_plan = NULL;
const ObDMLStmt *child_stmt = NULL;
ObLogicalOperator *top = NULL;
ObLogValues *values_op = NULL;
int64_t batch_size = 0;
const ObExplainStmt *explain_stmt = static_cast<const ObExplainStmt*>(get_stmt());
if (OB_ISNULL(child_stmt = explain_stmt->get_explain_query_stmt())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret));
} else if (OB_ISNULL(child_plan = optimizer_context_.get_log_plan_factory().
create(optimizer_context_, *child_stmt))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("failed to create log plan for explain stmt");
} else if (OB_FAIL(child_plan->generate_plan())) {
LOG_WARN("failed to generate plan tree for explain", K(ret));
} else if (OB_FAIL(check_explain_generate_plan_with_outline(child_plan))) {
LOG_WARN("failed to check generate plan with outline for explain", K(ret));
} else if (OB_FAIL(ObCodeGenerator::detect_batch_size(*child_plan, batch_size))) {
LOG_WARN("detect batch size failed", K(ret));
} else if (OB_FAIL(allocate_values_as_top(top))) {
LOG_WARN("failed to allocate expr values_op as top", K(ret));
} else if (OB_ISNULL(top) || OB_UNLIKELY(LOG_VALUES != top->get_type())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected error", K(top), K(ret));
} else {
set_plan_root(top);
top->mark_is_plan_root();
child_plan->get_optimizer_context().set_batch_size(batch_size);
values_op = static_cast<ObLogValues*>(top);
values_op->set_explain_plan(child_plan);
ObSqlPlan sql_plan(get_allocator());
ObExplainLogPlan *explain_plan = static_cast<ObExplainLogPlan*>(child_plan);
ObSEArray<common::ObString, 64> plan_strs;
const ObString& into_table = explain_stmt->get_into_table();
const ObString& statement_id = explain_stmt->get_statement_id();
if (OB_FAIL(sql_plan.store_sql_plan_for_explain(get_optimizer_context().get_exec_ctx(),
child_plan,
explain_stmt->get_explain_type(),
0 == into_table.length() ? "PLAN_TABLE" : into_table,
0 == statement_id.length() ? "" : statement_id,
explain_stmt->get_display_opt(),
plan_strs))) {
LOG_WARN("failed to store sql plan", K(ret));
} else {
//For explain stmt, we can do pack at the stage of expr alloc,
//But we need to use the FALSE flag to tell the driver
//to use normal encoding instead of memcopy
optimizer_context_.set_packed(false);
}
for (int64_t i = 0; OB_SUCC(ret) && i < plan_strs.count(); ++i) {
ObObj obj;
obj.set_varchar(plan_strs.at(i));
ObNewRow row;
row.cells_ = &obj;
row.count_ = 1;
if (OB_FAIL(values_op->add_row(row))) {
LOG_WARN("failed to add row", K(ret));
}
}
if (OB_SUCC(ret)) {
// set values_op operator id && set max operator id for LogPlan
values_op->set_op_id(0);
set_max_op_id(1);
}
}
get_optimizer_context().get_all_exprs().reuse();
if (OB_FAIL(ret)) {
} else if (OB_FAIL(allocate_output_expr_for_values_op(*values_op))) {
LOG_WARN("failed ot allocate output expr", K(ret));
} else {
get_optimizer_context().set_plan_type(ObPhyPlanType::OB_PHY_PLAN_LOCAL,
ObPhyPlanType::OB_PHY_PLAN_LOCAL,
false);
}
}
return ret;
}
int ObExplainLogPlan::check_explain_generate_plan_with_outline(ObLogPlan *real_plan)
{
int ret = OB_SUCCESS;
ObExecContext *exec_ctx = NULL;
ObSqlCtx *sql_ctx = NULL;
const ObExplainStmt *explain_stmt = static_cast<const ObExplainStmt*>(get_stmt());
if (OB_ISNULL(real_plan) || OB_ISNULL(explain_stmt)
|| OB_ISNULL(exec_ctx = get_optimizer_context().get_exec_ctx())
|| OB_ISNULL(sql_ctx = exec_ctx->get_sql_ctx())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(ret), K(real_plan), K(explain_stmt), K(exec_ctx), K(sql_ctx));
} else if (sql_ctx->multi_stmt_item_.is_part_of_multi_stmt()
&& sql_ctx->multi_stmt_item_.get_seq_num() > 0) {
/* generate plan call by ObMPQuery::process_with_tmp_context use tmp context, do not check */
} else if (EXPLAIN_UNINITIALIZED != explain_stmt->get_explain_type()
&& EXPLAIN_BASIC != explain_stmt->get_explain_type()
&& EXPLAIN_OUTLINE != explain_stmt->get_explain_type()
&& EXPLAIN_EXTENDED_NOADDR != explain_stmt->get_explain_type()) {
/* generate plan again for explain/explain basic/explain outline/explain extended_noaddr,
do not check explain extended */
} else if (0 == sql_ctx->first_plan_hash_) { /* generate plan first time */
void *tmp_ptr = NULL;
sql_ctx->first_outline_data_.reset();
bool has_win_func = false;
if (OB_UNLIKELY(0 == real_plan->get_signature()) || 0 < sql_ctx->retry_times_) {
/* do nothing */
} else if (OB_SUCC(OB_E(EventTable::EN_EXPLAIN_GENERATE_PLAN_WITH_OUTLINE) OB_SUCCESS)) {
/* do nothing */
} else if (OB_FAIL(check_has_win_func(explain_stmt->get_explain_query_stmt(), has_win_func))) {
LOG_WARN("failed to check has window function", K(ret));
} else if (has_win_func) {
/* outline is usually invalid for plan contain window functions, remove this after some hint is added. */
} else if (OB_UNLIKELY(NULL == (tmp_ptr = get_optimizer_context().get_allocator().alloc(OB_MAX_SQL_LENGTH)))) {
/* allocator in optimizer context is from ObResultSet */
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("fail to alloc memory", K(ret));
} else {
PlanText plan_text;
plan_text.buf_ = static_cast<char *>(tmp_ptr);
plan_text.buf_len_ = OB_MAX_SQL_LENGTH;
if (OB_FAIL(ObSqlPlan::get_plan_outline_info_one_line(plan_text, real_plan))) {
LOG_WARN("failed to get plan outline info", K(ret));
} else {
sql_ctx->first_outline_data_.assign_ptr(plan_text.buf_, static_cast<ObString::obstr_size_t>(plan_text.pos_));
sql_ctx->first_plan_hash_ = real_plan->get_signature();
ret = OB_SQL_RETRY_SPM;
LOG_WARN("generate plan again for explain use outline", K(ret));
}
}
} else { /* check generate plan again use outline data */
SMART_VAR(char[OB_MAX_SQL_LENGTH], buf) {
PlanText plan_text;
plan_text.buf_ = buf;
plan_text.buf_len_ = OB_MAX_SQL_LENGTH;
if (OB_FAIL(ObSqlPlan::get_plan_outline_info_one_line(plan_text, real_plan))) {
LOG_WARN("failed to get plan outline info", K(ret));
} else {
ObString cur_outline_data;
const uint64_t cur_plan_hash = real_plan->get_signature();
cur_outline_data.assign_ptr(plan_text.buf_, static_cast<ObString::obstr_size_t>(plan_text.pos_));
if (cur_plan_hash != sql_ctx->first_plan_hash_) {
ret = OB_OUTLINE_NOT_REPRODUCIBLE;
LOG_WARN("failed to generate plan use outline", K(sql_ctx->first_plan_hash_));
}
if (0 != (cur_outline_data.case_compare(sql_ctx->first_outline_data_))) {
ret = OB_OUTLINE_NOT_REPRODUCIBLE;
LOG_WARN("failed to generate plan use outline", K(sql_ctx->first_outline_data_));
}
if (OB_FAIL(ret)) {
LOG_WARN("failed to generate plan use outline", K(cur_plan_hash), K(cur_outline_data));
}
}
sql_ctx->first_plan_hash_ = 0;
sql_ctx->first_outline_data_.reset();
}
}
return ret;
}
int ObExplainLogPlan::check_has_win_func(const ObDMLStmt *stmt, bool &has_win_func)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("stmt is null", K(ret), K(stmt));
} else if (stmt->is_select_stmt() && static_cast<const ObSelectStmt*>(stmt)->has_window_function()) {
has_win_func = true;
} else {
ObSEArray<ObSelectStmt*, 8> child_stmts;
if (OB_FAIL(stmt->get_child_stmts(child_stmts))) {
LOG_WARN("get child stmt failed", K(ret));
}
for (int64_t i = 0; !has_win_func && OB_SUCC(ret) && i < child_stmts.count(); ++i) {
if (OB_FAIL(SMART_CALL(check_has_win_func(child_stmts.at(i), has_win_func)))) {
LOG_WARN("failed to check has window function", K(ret));
}
}
}
return ret;
}