/** * 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_PC #include "sql/plan_cache/ob_pcv_set.h" #include "sql/ob_sql_context.h" #include "sql/plan_cache/ob_pc_ref_handle.h" #include "share/schema/ob_table_schema.h" #include "share/ob_cluster_version.h" #include "share/config/ob_server_config.h" #include "lib/container/ob_bit_set.h" #include "lib/charset/ob_charset.h" using namespace oceanbase::common; using namespace oceanbase::share::schema; namespace oceanbase { namespace sql { int ObPCVSet::init(ObILibCacheCtx &ctx, const ObILibCacheObject *obj) { int ret = OB_SUCCESS; ObSQLSessionInfo* sess; #ifdef OB_BUILD_SPM bool is_spm_on = false; #endif ObPlanCacheCtx &pc_ctx = static_cast(ctx); const ObPlanCacheObject *cache_obj = static_cast(obj); if (is_inited_) { ret = OB_INIT_TWICE; LOG_WARN("init twice", K(ret)); } else if (OB_ISNULL(lib_cache_) || OB_ISNULL(cache_obj)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid argument", K(ret)); } else if (NULL == (sess = pc_ctx.sql_ctx_.session_info_)) { ret = OB_ERR_UNEXPECTED; SQL_PC_LOG(WARN, "session info is null", K(ret)); #ifdef OB_BUILD_SPM } else if (OB_FAIL(sess->get_use_plan_baseline(is_spm_on))) { LOG_WARN("fail to get spm config"); } else if (FALSE_IT(is_spm_closed_ = (!is_spm_on))) { // do nothing #endif } else if (NULL == (pc_alloc_ = lib_cache_->get_pc_allocator())) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid plan cache allocator", K_(pc_alloc), K(ret)); } else { if (OB_FAIL(pc_key_.deep_copy(allocator_, pc_ctx.fp_result_.pc_key_))) { LOG_WARN("fail to init plan cache key in pcv set", K(ret)); } else if (OB_FAIL(deep_copy_sql(pc_ctx.raw_sql_))) { LOG_WARN("fail to deep_copy_sql", K(ret), K(pc_ctx.raw_sql_)); } else { is_inited_ = true; if (pc_ctx.exec_ctx_.get_min_cluster_version() != GET_MIN_CLUSTER_VERSION()) { LOG_TRACE("Lob Debug, using remote min cluster version", K(pc_ctx.exec_ctx_.get_min_cluster_version()), K(GET_MIN_CLUSTER_VERSION())); } min_cluster_version_ = pc_ctx.exec_ctx_.get_min_cluster_version(); normal_parse_const_cnt_ = pc_ctx.normal_parse_const_cnt_; LOG_TRACE("inited pcv set", K(pc_key_), K(ObTimeUtility::current_time())); } } return ret; } void ObPCVSet::destroy() { if (is_inited_) { while (!pcv_list_.is_empty()) { ObPlanCacheValue *pcv= pcv_list_.get_first(); if (OB_ISNULL(pcv)) { //do nothing; } else { pcv_list_.remove(pcv); free_pcv(pcv); pcv = NULL; } } pc_key_.destory(allocator_); pc_key_.reset(); if (NULL != sql_.ptr()) { allocator_.free(sql_.ptr()); } // free sql_id list for (int64_t i = 0; i < sql_ids_.count(); i++) { if (NULL != sql_ids_[i].ptr()) { allocator_.free(sql_ids_[i].ptr()); } } sql_ids_.reset(); sql_.reset(); normal_parse_const_cnt_ = 0; min_cluster_version_ = 0; plan_num_ = 0; need_check_gen_tbl_col_ = false; is_inited_ = false; } } //1.检查fast parser识别常量个数是否与正常parser识别常量个数一致 //2.通过match 每个pcv中db_id, sys_vars, not params找到对应的pcv //3.在pcv中get plan int ObPCVSet::inner_get_cache_obj(ObILibCacheCtx &ctx, ObILibCacheKey *key, ObILibCacheObject *&cache_obj) { UNUSED(key); int ret = OB_SUCCESS; #ifdef OB_BUILD_SPM bool is_spm_on = false; #endif ObPlanCacheObject *plan = NULL; ObPlanCacheCtx &pc_ctx = static_cast(ctx); if (PC_PS_MODE == pc_ctx.mode_ || PC_PL_MODE == pc_ctx.mode_) { if (normal_parse_const_cnt_ != pc_ctx.fp_result_.parameterized_params_.count()) { ret = OB_ERR_UNEXPECTED; LOG_WARN("param num is not equal", K_(normal_parse_const_cnt), "parameterized_params_count", pc_ctx.fp_result_.parameterized_params_.count()); } } else { if (normal_parse_const_cnt_ != pc_ctx.fp_result_.raw_params_.count()) { ret = OB_ERR_UNEXPECTED; SQL_PC_LOG(TRACE, "const number of fast parse and normal parse is different", "fast_parse_const_num", pc_ctx.fp_result_.raw_params_.count(), K_(normal_parse_const_cnt), K(pc_ctx.fp_result_.raw_params_)); } } if (pc_ctx.exec_ctx_.get_min_cluster_version() != GET_MIN_CLUSTER_VERSION()) { LOG_TRACE("Lob Debug, using remote min cluster version", K(pc_ctx.exec_ctx_.get_min_cluster_version()), K(GET_MIN_CLUSTER_VERSION())); } if (OB_FAIL(ret)) { } else if (min_cluster_version_ != pc_ctx.exec_ctx_.get_min_cluster_version()) { ret = OB_OLD_SCHEMA_VERSION; } else if (need_check_gen_tbl_col_) { // 检查是否有generated table投影列同名的可能 bool contain_dup_col = false; if (OB_FAIL(check_raw_param_for_dup_col(pc_ctx, contain_dup_col))) { LOG_WARN("failed to check raw param for dup col", K(ret)); } else if (contain_dup_col) { ret = OB_SQL_PC_NOT_EXIST; } } if (OB_FAIL(ret)) { } else if (OB_ISNULL(pc_ctx.sql_ctx_.schema_guard_) || OB_ISNULL(pc_ctx.sql_ctx_.session_info_)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("unexpected null schema guard", K(ret), K(pc_ctx.sql_ctx_.schema_guard_), K(pc_ctx.sql_ctx_.session_info_)); #ifdef OB_BUILD_SPM } else if (OB_FAIL(pc_ctx.sql_ctx_.session_info_->get_use_plan_baseline(is_spm_on))) { LOG_WARN("failed to get spm status", K(ret)); } else if (is_spm_closed_ != (!is_spm_on)) { // spm param is altered ret = OB_OLD_SCHEMA_VERSION; #endif } else { ObSEArray schema_array; //plan cache匹配临时表应该始终使用用户创建的session才能保证语义的正确性 //远端执行的时候会创建临时的session对象,其session_id也是临时的, //所以这里必须使用get_sessid_for_table()规则去判断 pc_ctx.sql_ctx_.schema_guard_->set_session_id( pc_ctx.sql_ctx_.session_info_->get_sessid_for_table()); ObPlanCacheValue *matched_pcv = NULL; int64_t new_tenant_schema_version = OB_INVALID_VERSION; bool need_check_schema = true; DLIST_FOREACH(pcv, pcv_list_) { bool is_same = false; LOG_TRACE("get plan, pcv", K(pcv)); if (OB_FAIL(pcv->get_all_dep_schema(pc_ctx, pc_ctx.sql_ctx_.session_info_->get_database_id(), new_tenant_schema_version, need_check_schema, schema_array))) { if (OB_OLD_SCHEMA_VERSION == ret) { LOG_TRACE("failed to get all table schema", K(ret)); } else { LOG_WARN("failed to get all table schema", K(ret)); } } else if (OB_FAIL(pcv->match(pc_ctx, schema_array, is_same))) { LOG_WARN("fail to match pcv when get plan", K(ret)); } else if (false == is_same) { LOG_TRACE("failed to match param"); /*do nothing*/ } else { matched_pcv = pcv; if (OB_FAIL(pcv->choose_plan(pc_ctx, schema_array, plan))) { LOG_TRACE("failed to get plan in plan cache value", K(ret)); } break; } } //end foreach // 如果tenant schema变化了, 但table schema没有过期, 则更新tenant schema // plan必须不为NULL,因为下层可能会有覆盖错误码的行为,即使计划没有匹配上, // ret仍然为success,此时tenant schema version又会被推高,可能有正确性问题 // bug link: if (OB_SUCC(ret) && NULL != matched_pcv && NULL != plan) { if (OB_FAIL(matched_pcv->lift_tenant_schema_version(new_tenant_schema_version))) { LOG_WARN("failed to lift pcv's tenant schema version", K(ret)); } else if (!need_check_schema && OB_NOT_NULL(pc_ctx.exec_ctx_.get_physical_plan_ctx())) { pc_ctx.exec_ctx_.get_physical_plan_ctx()->set_tenant_schema_version(new_tenant_schema_version); } } } if (OB_SUCC(ret)) { cache_obj = plan; if (NULL == plan) { ret = OB_SQL_PC_NOT_EXIST; } } return ret; } //1.通过match 每个pcv中db_id, sys_vars, not params找到对应的pcv //2.如果匹配的pcv则在pcv中add plan, 否则重新生成pcv并add plan int ObPCVSet::inner_add_cache_obj(ObILibCacheCtx &ctx, ObILibCacheKey *key, ObILibCacheObject *cache_obj) { UNUSED(key); int ret = OB_SUCCESS; bool is_new = true; ObPlanCacheObject *plan = static_cast(cache_obj); ObPlanCacheCtx &pc_ctx = static_cast(ctx); ObSEArray schema_array; if (OB_ISNULL(plan) || OB_ISNULL(pc_ctx.sql_ctx_.schema_guard_) || OB_ISNULL(pc_ctx.sql_ctx_.session_info_)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid argument", K(ret), K(plan), K(pc_ctx.sql_ctx_.schema_guard_), K(pc_ctx.sql_ctx_.session_info_)); } else if (get_plan_num() >= MAX_PCV_SET_PLAN_NUM) { static const int64_t PRINT_PLAN_EXCEEDS_LOG_INTERVAL = 20 * 1000 * 1000; // 20s ret = OB_REACH_MEMORY_LIMIT; if (REACH_TIME_INTERVAL(PRINT_PLAN_EXCEEDS_LOG_INTERVAL)) { LOG_INFO("number of plans in a single pcv_set reach limit", K(ret), K(get_plan_num()), K(pc_ctx)); } } else if (OB_FAIL(ObPlanCacheValue::get_all_dep_schema(*pc_ctx.sql_ctx_.schema_guard_, plan->get_dependency_table(), schema_array))) { LOG_WARN("failed to get all dep schema", K(ret)); } else { DLIST_FOREACH(pcv, pcv_list_) { bool is_same = false; LOG_DEBUG("add plan, pcv", K(pcv)); if (OB_FAIL(pcv->match(pc_ctx, schema_array, is_same))) { LOG_WARN("fail to match pcv in pcv_set", K(ret)); } else if (is_same) { is_new = false; LOG_INFO("has identical pcv", K(is_same), K(pcv)); if (OB_FAIL(pcv->add_plan(*plan, schema_array, pc_ctx))) { if (OB_SQL_PC_PLAN_DUPLICATE == ret || is_not_supported_err(ret)) { LOG_TRACE("fail to add plan to pcv", K(ret)); } else { LOG_WARN("fail to add plan to pcv", K(ret)); } } break; } } //for end } if (OB_SUCC(ret) && is_new) { ObPlanCacheValue *pcv = NULL; if (OB_FAIL(create_pcv_and_add_plan(plan, pc_ctx, schema_array, pcv))) { if (!is_not_supported_err(ret)) { LOG_WARN("fail to create and add pcv", K(ret)); } } else if (NULL == pcv) { ret = OB_ERR_UNEXPECTED; } else if (!pcv_list_.add_last(pcv)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("fail to add pcv to pcv_list", K(ret)); free_pcv(pcv); pcv = NULL; } else { // do nothing } } //在add plan时维护一个最小的merged version, 用于后台在淘汰时进行检查; if (OB_SUCC(ret)) { if (OB_FAIL(set_raw_param_info_if_needed(plan))) { LOG_WARN("failed to set raw param info", K(ret)); } else { // inc plan num if succeeds plan_num_ += 1; } } return ret; } //生成pcv并add plan int ObPCVSet::create_pcv_and_add_plan(ObPlanCacheObject *cache_obj, ObPlanCacheCtx &pc_ctx, const ObIArray &schema_array, ObPlanCacheValue *&new_pcv) { int ret = OB_SUCCESS; new_pcv = nullptr; common::ObString sql_id; common::ObString sql_id_org(common::OB_MAX_SQL_ID_LENGTH, (const char*)&pc_ctx.sql_ctx_.sql_id_); //create pcv and init if (OB_ISNULL(cache_obj) || OB_ISNULL(pc_ctx.sql_ctx_.schema_guard_)) { ret = OB_INVALID_ARGUMENT; SQL_PC_LOG(WARN, "invalid argument", K(ret)); } else if (OB_FAIL(create_new_pcv(new_pcv))) { SQL_PC_LOG(WARN, "failed to create new plan cache value", K(ret)); } else if (OB_UNLIKELY(nullptr == new_pcv)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null for new_pcv", K(new_pcv)); } else if (OB_FAIL(new_pcv->init(this, cache_obj, pc_ctx))) { SQL_PC_LOG(WARN, "failed to init plan cache value"); } else if (OB_FAIL(ob_write_string(allocator_, sql_id_org, sql_id))) { SQL_PC_LOG(WARN, "failed to deep copy sql_id_", K(sql_id_org), K(ret)); } else if (OB_FAIL(push_sql_id(sql_id))) { SQL_PC_LOG(WARN, "failed to push sql_id_", K(ret)); } else { // do nothing } //add plan if (OB_SUCC(ret)) { if (OB_FAIL(new_pcv->add_plan(*cache_obj, schema_array, pc_ctx))) { if (!is_not_supported_err(ret)) { SQL_PC_LOG(WARN, "failed to add plan to plan cache value", K(ret)); } } } if (OB_FAIL(ret) && nullptr != new_pcv) { free_pcv(new_pcv); new_pcv = nullptr; } return ret; } int ObPCVSet::deep_copy_sql(const ObString &sql) { int ret = OB_SUCCESS; if (OB_ISNULL(sql.ptr())) { SQL_PC_LOG(TRACE, "sql is empty, ignore copy sql", K(ret), K(lbt())); } else if (OB_FAIL(ob_write_string(allocator_, sql, sql_))) { SQL_PC_LOG(WARN, "deep copy sql into pcv_set failed", K(ret), K(sql)); } return ret; } int ObPCVSet::create_new_pcv(ObPlanCacheValue *&new_pcv) { int ret = OB_SUCCESS; void *buff = nullptr; new_pcv = nullptr; if (nullptr == (buff = allocator_.alloc(sizeof(ObPlanCacheValue)))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("failed to allocate memory for ObPlanCacheValue", K(ret)); } else if (nullptr == (new_pcv = new(buff)ObPlanCacheValue())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("failed to construct ObPlanCacheValue", K(ret)); } else { // do nothing } if (OB_SUCC(ret)) { // do nothing } else if (nullptr != new_pcv) { // cleanup new_pcv->~ObPlanCacheValue(); allocator_.free(new_pcv); new_pcv = nullptr; } return ret; } void ObPCVSet::free_pcv(ObPlanCacheValue *pcv) { int ret = OB_SUCCESS; if (OB_ISNULL(pcv)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid argument", K(pcv), K(ret)); } else { pcv->~ObPlanCacheValue(); allocator_.free(pcv); pcv = nullptr; } } int ObPCVSet::set_raw_param_info_if_needed(ObPlanCacheObject *cache_obj) { int ret = OB_SUCCESS; ObPhysicalPlan *plan = NULL; col_field_arr_.reset(); col_field_arr_.set_allocator(&allocator_); ObBitSet<> visited_idx; PCColStruct col_item; if (need_check_gen_tbl_col_) { // do nothing } else if (OB_ISNULL(cache_obj)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid argument", K(cache_obj)); } else if (OB_ISNULL(plan = dynamic_cast(cache_obj))) { // do nothing } else if (!plan->contain_paramed_column_field()) { // do nothing } else if (OB_FAIL(col_field_arr_.reserve(plan->get_field_columns().count()))) { LOG_WARN("failed to init fixed array", K(ret), K(plan->get_field_columns().count())); } else { for (int64_t i = 0; i < plan->get_field_columns().count(); i++) { const ObField &cur_field = plan->get_field_columns().at(i); if (!cur_field.is_paramed_select_item_ || OB_ISNULL(cur_field.paramed_ctx_)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("Invalid argument", K(ret), K(cur_field.is_paramed_select_item_), K(cur_field.paramed_ctx_)); } else if (visited_idx.has_member(i) || !cur_field.paramed_ctx_->need_check_dup_name_) { // do nothing } else { if (OB_FAIL(visited_idx.add_member(i))) { LOG_WARN("failed to add member", K(ret), K(i)); } else if (OB_FAIL(col_item.param_idxs_.assign(cur_field.paramed_ctx_->param_idxs_))) { LOG_WARN("failed to assign array", K(ret)); } else if (OB_FAIL(col_field_arr_.push_back(col_item))) { LOG_WARN("failed to push back element", K(ret)); } else { // do nothing } col_item.reset(); for (int64_t j = i + 1; OB_SUCC(ret) && j < plan->get_field_columns().count(); j++) { const ObField &next_field = plan->get_field_columns().at(j); if (!next_field.is_paramed_select_item_ || OB_ISNULL(next_field.paramed_ctx_)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("Invalid argument", K(ret), K(next_field.is_paramed_select_item_), K(next_field.paramed_ctx_)); } else if (visited_idx.has_member(j) || !next_field.paramed_ctx_->need_check_dup_name_) { // do nothing } else if (ObCharset::case_compat_mode_equal(cur_field.paramed_ctx_->paramed_cname_, next_field.paramed_ctx_->paramed_cname_)) { if (OB_FAIL(visited_idx.add_member(j))) { LOG_WARN("failed to add member", K(ret)); } else if (OB_FAIL(col_item.param_idxs_.assign(next_field.paramed_ctx_->param_idxs_))) { LOG_WARN("failed to assign array", K(ret)); } else if (OB_FAIL(col_field_arr_.push_back(col_item))) { LOG_WARN("failed to push back element"); } else { col_item.reset(); } } else { // do nothing } } // for end if (OB_SUCC(ret)) { col_field_arr_.at(col_field_arr_.count() - 1).is_last_ = true; } } } if (OB_SUCC(ret)) { need_check_gen_tbl_col_ = true; } } return ret; } int ObPCVSet::check_raw_param_for_dup_col(ObPlanCacheCtx &pc_ctx, bool &contain_dup_col) { int ret = OB_SUCCESS; contain_dup_col = false; if (!need_check_gen_tbl_col_) { // do nothing } else { for (int64_t cur_idx = 0; OB_SUCC(ret) && !contain_dup_col && cur_idx < col_field_arr_.count(); cur_idx++) { if (col_field_arr_.at(cur_idx).is_last_) { // do nothing } else { PCColStruct &left_col_item = col_field_arr_.at(cur_idx); PCColStruct &right_col_item = col_field_arr_.at(cur_idx + 1); bool all_same = true; ObIArray &raw_params = pc_ctx.fp_result_.raw_params_; for (int64_t i = 0; OB_SUCC(ret) && all_same && i < left_col_item.param_idxs_.count(); i++) { int64_t l_idx = left_col_item.param_idxs_.at(i); int64_t r_idx = right_col_item.param_idxs_.at(i); if (OB_ISNULL(raw_params.at(l_idx)) || OB_ISNULL(raw_params.at(r_idx)) || OB_ISNULL(raw_params.at(l_idx)->node_) || OB_ISNULL(raw_params.at(r_idx)->node_)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid argument", K(ret)); } else { ObString l_tmp_str(raw_params.at(l_idx)->node_->text_len_, raw_params.at(l_idx)->node_->raw_text_); ObString r_tmp_str(raw_params.at(r_idx)->node_->text_len_, raw_params.at(r_idx)->node_->raw_text_); if (0 != l_tmp_str.compare(r_tmp_str)) { all_same = false; LOG_TRACE("raw text not matched", K(l_tmp_str), K(r_tmp_str)); } } } // for end contain_dup_col = all_same; } } } return ret; } int ObPCVSet::check_contains_table(uint64_t db_id, common::ObString tab_name, bool &contains) { int ret = OB_SUCCESS; DLIST_FOREACH(pcv, pcv_list_) { if (OB_ISNULL(pcv)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid argument", K(pcv), K(ret)); } else if (OB_FAIL(pcv->check_contains_table(db_id, tab_name, contains))) { LOG_WARN("fail to check table name", K(ret), K(db_id), K(tab_name)); } else if (!contains) { // continue find } else { break; } } return ret; } #ifdef OB_BUILD_SPM int ObPCVSet::get_evolving_evolution_task(EvolutionPlanList &evo_task_list) { int ret = OB_SUCCESS; DLIST_FOREACH(pcv, pcv_list_) { if (OB_ISNULL(pcv)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid argument", K(pcv), K(ret)); } else if (OB_FAIL(pcv->get_evolving_evolution_task(evo_task_list))) { LOG_WARN("fail to get evolving evolution task", K(ret)); } } return ret; } #endif } }