/** * 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_RESV #include "sql/resolver/dml/ob_sql_hint.h" #include "sql/resolver/dml/ob_dml_stmt.h" #include "sql/resolver/dml/ob_select_stmt.h" #include "sql/optimizer/ob_log_plan.h" #include "sql/rewrite/ob_transform_utils.h" #include "common/ob_smart_call.h" #include "sql/monitor/ob_sql_plan.h" namespace oceanbase { using namespace common; namespace sql { void ObQueryHint::reset() { cs_type_ = CS_TYPE_INVALID; global_hint_.reset(); is_valid_outline_ = false; user_def_outline_ = false; outline_stmt_id_ = OB_INVALID_STMT_ID; qb_hints_.reuse(); stmt_id_hints_.reuse(); trans_list_.reuse(); outline_trans_hints_.reuse(); used_trans_hints_.reuse(); qb_name_map_.reuse(); stmt_id_map_.reuse(); } int ObQueryHint::create_hint_table(ObIAllocator *allocator, ObTableInHint *&table) { int ret = common::OB_SUCCESS; table = NULL; void *ptr = NULL; if (OB_ISNULL(allocator)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected params", K(ret), K(allocator)); } else if (OB_ISNULL(ptr = allocator->alloc(sizeof(ObTableInHint)))) { ret = common::OB_ALLOCATE_MEMORY_FAILED; SQL_RESV_LOG(ERROR, "no more memory to create hint table"); } else { table = new(ptr) ObTableInHint(); } return ret; } int ObQueryHint::create_leading_table(ObIAllocator *allocator, ObLeadingTable *&table) { int ret = common::OB_SUCCESS; table = NULL; void *ptr = NULL; if (OB_ISNULL(allocator)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected params", K(ret), K(allocator)); } else if (OB_ISNULL(ptr = allocator->alloc(sizeof(ObLeadingTable)))) { ret = common::OB_ALLOCATE_MEMORY_FAILED; SQL_RESV_LOG(ERROR, "no more memory to create leading table"); } else { table = new(ptr) ObLeadingTable(); } return ret; } int ObQueryHint::get_qb_name_source_hash_value(const ObString &src_qb_name, const ObIArray &src_hash_val, uint32_t &hash_val) { int ret = OB_SUCCESS; hash_val = 0; if (OB_UNLIKELY(src_hash_val.empty())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected src_hash_val", K(ret), K(src_hash_val)); } else { hash_val = src_hash_val.at(src_hash_val.count() - 1); hash_val = fnv_hash2(src_qb_name.ptr(), src_qb_name.length(), hash_val); } return ret; } int ObQueryHint::set_outline_data_hints(const ObGlobalHint &global_hint, const int64_t stmt_id, const ObIArray &hints) { int ret = OB_SUCCESS; qb_hints_.reuse(); stmt_id_hints_.reuse(); trans_list_.reuse(); if (OB_FAIL(global_hint_.assign(global_hint))) { LOG_WARN("failed to assign global hint.", K(ret)); } else if (OB_FAIL(append_hints(stmt_id, hints))) { LOG_WARN("failed to assign global hint.", K(ret)); } else { if (global_hint_.has_valid_opt_features_version()) { is_valid_outline_ = true; outline_stmt_id_ = stmt_id; } else { user_def_outline_ = true; } ObHint *cur_hint = NULL; for (int64_t i = 0; OB_SUCC(ret) && i < hints.count(); ++i) { if (OB_ISNULL(cur_hint = hints.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected NULL.", K(ret), K(cur_hint)); } else if (cur_hint->is_transform_outline_hint() && OB_FAIL(trans_list_.push_back(cur_hint))) { LOG_WARN("faild to push back hint.", K(ret)); } } LOG_DEBUG("finish add outline data hints"); } return ret; } int ObQueryHint::append_hints(int64_t stmt_id, const ObIArray &hints) { int ret = OB_SUCCESS; const ObHints *stmt_id_hints = get_stmt_id_hints(stmt_id); ObHints *qb_hints = NULL; ObHint *cur_hint = NULL; for (int64_t i = 0; OB_SUCC(ret) && i < hints.count(); ++i) { if (OB_ISNULL(cur_hint = hints.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected NULL.", K(ret), K(cur_hint)); } else if (cur_hint->get_qb_name().empty()) { if (NULL == (qb_hints = const_cast(stmt_id_hints))) { if (OB_ISNULL(qb_hints = stmt_id_hints_.alloc_place_holder())) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_ERROR("Allocate ObHints from array error", K(ret)); } else { qb_hints->stmt_id_ = stmt_id; stmt_id_hints = qb_hints; } } } else if (NULL != (qb_hints = const_cast(get_qb_hints(cur_hint->get_qb_name())))) { /* do nothing */ } else if (OB_ISNULL(qb_hints = qb_hints_.alloc_place_holder())) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_ERROR("Allocate ObHints from array error", K(ret)); } else { qb_hints->qb_name_ = cur_hint->get_qb_name(); } if (OB_SUCC(ret) && OB_FAIL(qb_hints->hints_.push_back(cur_hint))) { LOG_WARN("faild to push back hint.", K(ret)); } } return ret; } const ObHints *ObQueryHint::get_qb_hints(const ObString &qb_name) const { const ObHints *qb_hints = NULL; for (int64_t i = 0; NULL == qb_hints && i < qb_hints_.count(); ++i) { if (0 == qb_name.case_compare(qb_hints_.at(i).qb_name_)) { qb_hints = &qb_hints_.at(i); } } return qb_hints; } const ObHints *ObQueryHint::get_stmt_id_hints(int64_t stmt_id) const { const ObHints *qb_hints = NULL; for (int64_t i = 0; NULL == qb_hints && i < stmt_id_hints_.count(); ++i) { if (stmt_id_hints_.at(i).stmt_id_ == stmt_id) { qb_hints = &stmt_id_hints_.at(i); } } return qb_hints; } int ObQueryHint::add_stmt_id_map(const int64_t stmt_id, stmt::StmtType stmt_type) { int ret = OB_SUCCESS; QbNames *qb_names = NULL; if (OB_INVALID_STMT_ID == stmt_id) { /* do nothing */ } else if (OB_UNLIKELY(stmt_id_map_.count() != stmt_id)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("exists valid stmt id not added before", K(ret), K(stmt_id), K(stmt_type), K(stmt_id_map_.count())); } else if (OB_ISNULL(qb_names = stmt_id_map_.alloc_place_holder())) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_ERROR("allocate QbNames from array error", K(ret)); } else { qb_names->stmt_type_ = stmt_type; } return ret; } int ObQueryHint::set_stmt_id_map_info(const ObDMLStmt &stmt, ObString &qb_name) { int ret = OB_SUCCESS; const int64_t stmt_id = stmt.get_stmt_id(); if (OB_UNLIKELY(0 > stmt_id || stmt_id_map_.count() <= stmt_id)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected stmt id for dml stmt", K(ret), K(stmt_id), K(stmt_id_map_.count())); } else { QbNames &qb_names = stmt_id_map_.at(stmt_id); qb_names.stmt_type_ = stmt.get_stmt_type(); qb_names.is_set_stmt_ = stmt.is_set_stmt(); qb_names.is_from_hint_ = !qb_name.empty(); qb_names.qb_names_.reuse(); if (!qb_name.empty() && OB_FAIL(qb_names.qb_names_.push_back(qb_name))) { LOG_WARN("fail to push back qb name", K(ret), K(qb_name)); } } return ret; } int ObQueryHint::check_and_set_params_from_hint(const ObResolverParams ¶ms, const ObDMLStmt &stmt) const { int ret = OB_SUCCESS; const ObSQLSessionInfo *session_info = NULL; bool has_enable_param = false; ObQueryCtx *query_ctx = NULL; if (OB_ISNULL(session_info = params.session_info_) || OB_ISNULL(query_ctx = params.query_ctx_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(session_info), K(query_ctx)); } else if (T_NONE_SCOPE != params.hidden_column_scope_ && OB_FAIL(global_hint_.opt_params_.has_enable_opt_param(ObOptParamHint::OptParamType::HIDDEN_COLUMN_VISIBLE, has_enable_param))) { LOG_WARN("failed to check has enable opt param", K(ret)); } else if (OB_UNLIKELY(T_NONE_SCOPE != params.hidden_column_scope_ && !has_enable_param)) { ret = OB_ERR_BAD_FIELD_ERROR; LOG_WARN("hidden columns not allowed", K(ret)); ObString column_name(OB_HIDDEN_PK_INCREMENT_COLUMN_NAME); ObString scope_name = ObString::make_string(get_scope_name(params.hidden_column_scope_)); LOG_USER_ERROR(OB_ERR_BAD_FIELD_ERROR, column_name.length(), column_name.ptr(), scope_name.length(), scope_name.ptr()); } else if (OB_FAIL(check_ddl_schema_version_from_hint(stmt))) { LOG_WARN("failed to check ddl schema version", K(ret)); } else { if (global_hint_.query_timeout_ > 0) { THIS_WORKER.set_timeout_ts(session_info->get_query_start_time() + global_hint_.query_timeout_); } if (global_hint_.has_valid_opt_features_version()) { query_ctx->optimizer_features_enable_version_ = global_hint_.opt_features_version_; } else if (OB_FAIL(session_info->get_optimizer_features_enable_version(query_ctx->optimizer_features_enable_version_))) { LOG_WARN("failed to check ddl schema version", K(ret)); } } return ret; } int ObQueryHint::check_ddl_schema_version_from_hint(const ObDMLStmt &stmt, const ObDDLSchemaVersionHint& ddlSchemaVersionHint) const { int ret = OB_SUCCESS; TableItem* item = NULL; if (OB_FAIL(get_basic_table_without_index_by_hint_table(stmt, ddlSchemaVersionHint.table_, item))) { LOG_WARN("failed to get table item by hint table", K(ret)); } else if (OB_ISNULL(item)) { ObSEArray child_stmts; if (OB_FAIL(stmt.get_child_stmts(child_stmts))) { LOG_WARN("failed to get child stmts", K(ret)); } for (int64_t index = 0; OB_SUCC(ret) && index < child_stmts.count(); ++index) { if (OB_FAIL(SMART_CALL(check_ddl_schema_version_from_hint(*child_stmts.at(index), ddlSchemaVersionHint)))) { LOG_WARN("failed to check ddl schema version from hint", K(ret)); } } } else if (OB_LIKELY(item->ddl_schema_version_ > 0) && OB_UNLIKELY(ddlSchemaVersionHint.schema_version_ != item->ddl_schema_version_)) { ret = OB_DDL_SCHEMA_VERSION_NOT_MATCH; LOG_USER_ERROR(OB_DDL_SCHEMA_VERSION_NOT_MATCH); LOG_WARN("failed to check ddl schema version", K(ret), K(item->ddl_schema_version_), K(ddlSchemaVersionHint.schema_version_)); } return ret; } int ObQueryHint::check_ddl_schema_version_from_hint(const ObDMLStmt &stmt) const { int ret = OB_SUCCESS; for (int64_t i = 0; OB_SUCC(ret) && i < global_hint_.ob_ddl_schema_versions_.count(); i++) { if (OB_FAIL(check_ddl_schema_version_from_hint(stmt, global_hint_.ob_ddl_schema_versions_.at(i)))) { LOG_WARN("failed to check ddl schema version from hint", K(ret)); } } return ret; } // init query hint after resolve all stmt. int ObQueryHint::init_query_hint(ObIAllocator *allocator, ObSQLSessionInfo *session_info, ObDMLStmt *stmt) { int ret = OB_SUCCESS; if (OB_ISNULL(allocator) || OB_ISNULL(session_info) || OB_ISNULL(stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(allocator), K(session_info), K(stmt)); } else if (OB_FAIL(session_info->get_collation_type_of_names(OB_TABLE_NAME_CLASS, cs_type_))) { LOG_WARN("fail to get collation type of name", K(OB_TABLE_NAME_CLASS), K(ret)); } else if (OB_LIKELY(!qb_name_map_.created()) && OB_FAIL(qb_name_map_.create(64, ObModIds::OB_SQL_COMPILE))) { LOG_WARN("failed to create qb name map", K(ret)); } else if (OB_FAIL(reset_duplicate_qb_name())) { LOG_WARN("failed to reset duplicate qb name", K(ret)); } else if (OB_FAIL(generate_orig_stmt_qb_name(*allocator))) { LOG_WARN("failed to generate stmt name after resolve", K(ret)); } else if (OB_FAIL(distribute_hint_to_orig_stmt(stmt))) { LOG_WARN("faild to distribute hint to orig stmt", K(ret)); } else { LOG_TRACE("finish init query hint", K(*this)); } return ret; } int ObQueryHint::distribute_hint_to_orig_stmt(ObDMLStmt *stmt) { int ret = OB_SUCCESS; ObDMLStmt *root_stmt = stmt; ObSEArray all_child_stmts; if (OB_ISNULL(stmt)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null stmt", K(ret), K(stmt)); } else if (stmt->is_explain_stmt() && OB_FALSE_IT(root_stmt = static_cast(stmt)->get_explain_query_stmt())) { } else if (OB_FAIL(ObTransformUtils::get_all_child_stmts(root_stmt, all_child_stmts))) { LOG_WARN("failed to get all child stmt", K(ret)); } else { ObDMLStmt *cur_stmt = NULL; for (int64_t i = -1; OB_SUCC(ret) && i < all_child_stmts.count(); ++i) { if (OB_ISNULL(cur_stmt = -1 == i ? root_stmt : all_child_stmts.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null stmt", K(ret), K(cur_stmt)); } else if (OB_FAIL(cur_stmt->set_table_item_qb_name())) { LOG_WARN("failed to update table item qb name", K(ret)); } else if (OB_FAIL(cur_stmt->get_stmt_hint().init_stmt_hint(*cur_stmt, *this, true))) { LOG_WARN("failed to init stmt hint", K(ret)); } } } return ret; } //after generate a new stmt_id for stmt, need generate new qb_name and add hint from qb_hints // 1. generate new qb name for stmt // 2. add hint from qb_hints use new qb name int ObQueryHint::adjust_qb_name_for_stmt(ObIAllocator &allocator, ObDMLStmt &stmt, const ObString &src_qb_name, const ObIArray &src_hash_val, int64_t *sub_num /* default = NULL */) { int ret = OB_SUCCESS; ObString qb_name; const ObHints *qb_hints = NULL; stmt.get_stmt_hint().set_query_hint(this); if (OB_FAIL(generate_qb_name_for_stmt(allocator, stmt, src_qb_name, src_hash_val, qb_name, sub_num))) { LOG_WARN("failed to generate qb name by str", K(ret)); } else if (has_outline_data() || !stmt.get_stmt_hint().inited()) { // for outline data, stmts generated by transformer only use hints matched by qb_name if (OB_FAIL(stmt.get_stmt_hint().init_stmt_hint(stmt, *this, false))) { LOG_WARN("failed to init stmt hint", K(ret)); } } else { ObStmtHint stmt_hint; if (OB_FAIL(stmt_hint.init_stmt_hint(stmt, *this, false))) { LOG_WARN("failed to init stmt hint", K(ret)); } else if (OB_FAIL(stmt.get_stmt_hint().merge_stmt_hint(stmt_hint, RIGHT_HINT_DOMINATED))) { LOG_WARN("failed to merge stmt hint", K(ret)); } } // set stmt hint // if (OB_SUCC(ret) && stmt.is_set_stmt() && OB_FAIL(stmt.get_stmt_hint().set_set_stmt_hint())) { // LOG_WARN("failed to set set stmt hint", K(ret)); // } return ret; } int ObQueryHint::generate_orig_stmt_qb_name(ObIAllocator &allocator) { int ret = OB_SUCCESS; int64_t sel_start_id = 1; int64_t set_start_id = 1; int64_t other_start_id = 1; char buf[OB_MAX_QB_NAME_LENGTH]; int64_t buf_len = OB_MAX_QB_NAME_LENGTH; ObString qb_name; qb_name_map_.reuse(); for (int64_t idx = 0; OB_SUCC(ret) && idx < stmt_id_map_.count(); ++idx) { QbNames &qb_names = stmt_id_map_.at(idx); const char *stmt_name = get_dml_stmt_name(qb_names.stmt_type_, qb_names.is_set_stmt_); int64_t pos = 0; if (qb_names.is_from_hint_) { /* add qb name from hint */ if (OB_UNLIKELY(1 != qb_names.qb_names_.count() || qb_names.qb_names_.at(0).empty())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected qb names", K(ret), K(qb_names.qb_names_)); } else if (OB_FAIL(qb_name_map_.set_refactored(qb_names.qb_names_.at(0), idx))) { LOG_WARN("failed to set refactored", K(ret)); } } else if (NULL == stmt_name) { qb_names.qb_names_.reuse(); } else if (OB_FAIL(BUF_PRINTF("%s", stmt_name))) { LOG_WARN("failed print buf stmt_name", K(ret)); } else { int64_t &id_start = stmt::T_SELECT == qb_names.stmt_type_ ? (qb_names.is_set_stmt_ ? set_start_id : sel_start_id) : other_start_id; int64_t old_pos = pos; int64_t cnt = 0; qb_name.reset(); qb_names.qb_names_.reuse(); while(qb_name.empty() && OB_SUCC(ret)) { pos = old_pos; if (OB_FAIL(BUF_PRINTF("%ld", id_start))) { LOG_WARN("failed to print buff", K(ret)); } else if (OB_FAIL(try_add_new_qb_name(allocator, idx, buf, pos, cnt, qb_name))) { LOG_WARN("failed to try add new qb name", K(ret)); } else { ++id_start; } } if (OB_SUCC(ret) && OB_FAIL(qb_names.qb_names_.push_back(qb_name))) { LOG_WARN("failed to push back", K(ret)); } } } return ret; } // generate new qb name when transform stmt int ObQueryHint::generate_qb_name_for_stmt(ObIAllocator &allocator, const ObDMLStmt &stmt, const ObString &src_qb_name, const ObIArray &src_hash_val, ObString &qb_name, int64_t *sub_num /* default = NULL */) { int ret = OB_SUCCESS; qb_name.reset(); char buf[OB_MAX_QB_NAME_LENGTH]; int64_t buf_len = OB_MAX_QB_NAME_LENGTH; int64_t pos = 0; const char *stmt_name = get_dml_stmt_name(stmt.get_stmt_type(), stmt.is_set_stmt()); const int64_t stmt_id = stmt.get_stmt_id(); uint32_t hash_val = 0; if (OB_ISNULL(stmt.get_query_ctx()) || OB_ISNULL(stmt_name) || OB_UNLIKELY(0 > stmt_id || stmt_id_map_.count() <= stmt_id)) { ret = OB_ERR_UNEXPECTED; LOG_TRACE("unexpected params", K(ret), K(stmt.get_query_ctx()), K(stmt.get_stmt_type()), K(stmt_id), K(stmt_id_map_.count())); } else if (OB_FAIL(BUF_PRINTF("%s", stmt_name))) { LOG_WARN("failed print buf stmt_name", K(ret)); } else if (OB_FAIL(get_qb_name_source_hash_value(src_qb_name, src_hash_val, hash_val))) { LOG_WARN("failed get qb name source hash value", K(ret)); } else { int64_t old_pos = pos; int64_t cnt = 0; QbNames &qb_names = stmt_id_map_.at(stmt_id); while(qb_name.empty() && OB_SUCC(ret)) { pos = old_pos; if (OB_FAIL(BUF_PRINTF("%08X", hash_val))) { LOG_WARN("failed to print buff", K(ret)); } else if (NULL != sub_num && OB_FAIL(BUF_PRINTF("_%ld", *sub_num))) { LOG_WARN("failed to print buff", K(ret)); } else if (OB_FAIL(try_add_new_qb_name(allocator, stmt.get_stmt_id(), buf, pos, cnt, qb_name))) { LOG_WARN("failed to try add new qb name", K(ret)); } else if (NULL != sub_num) { ++*sub_num; } else { ++hash_val; } } if (OB_SUCC(ret)) { if (qb_names.qb_names_.empty()) { qb_names.reset(); qb_names.stmt_type_ = stmt.get_stmt_type(); qb_names.is_set_stmt_ = stmt.is_set_stmt(); qb_names.parent_name_ = src_qb_name; } if (OB_FAIL(qb_names.qb_names_.push_back(qb_name))) { LOG_WARN("failed to push back", K(ret)); } } } return ret; } int ObQueryHint::try_add_new_qb_name(ObIAllocator &allocator, int64_t stmt_id, const char *ptr, int64_t length, int64_t &cnt, ObString &qb_name) { int ret = OB_SUCCESS; qb_name.reset(); cnt = cnt < 0 ? 0 : cnt; int64_t tmp_stmt_id = OB_INVALID_STMT_ID; ObString tmp_name; tmp_name.assign_ptr(ptr, length); if (OB_FAIL(qb_name_map_.get_refactored(tmp_name, tmp_stmt_id)) && OB_UNLIKELY(OB_HASH_NOT_EXIST != ret)) { LOG_WARN("failed to get refactored", K(ret)); } else if (OB_LIKELY(OB_HASH_NOT_EXIST == ret)) { if (OB_FAIL(ob_write_string(allocator, tmp_name, qb_name))) { LOG_WARN("Write string error", K(ret)); } else if (OB_FAIL(qb_name_map_.set_refactored(qb_name, stmt_id))) { LOG_WARN("failed to set refactored", K(ret)); } } else { ++cnt; if (cnt%50 == 0) { LOG_TRACE("try generate qb_name by str too hard", K(cnt)); } if (OB_UNLIKELY(cnt > 1000)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("failed to generate a qb_name by str", K(ret), K(cnt)); } } return ret; } // reset duplicate qb name from qb_name hint int ObQueryHint::reset_duplicate_qb_name() { int ret = OB_SUCCESS; int64_t idx = 0; qb_name_map_.reuse(); for (int64_t i = 0; OB_SUCC(ret) && i < stmt_id_map_.count(); ++i) { QbNames &qb_names = stmt_id_map_.at(i); if (!qb_names.is_from_hint_) { qb_names.qb_names_.reuse(); } else if (qb_names.qb_names_.empty()) { /* do nothing */ } else if (OB_UNLIKELY(1 != qb_names.qb_names_.count() || qb_names.qb_names_.at(0).empty())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected qb names", K(ret), K(qb_names.qb_names_)); } else if (OB_FAIL(qb_name_map_.get_refactored(qb_names.qb_names_.at(0), idx))) { if (OB_UNLIKELY(OB_HASH_NOT_EXIST != ret)) { LOG_WARN("get stmt id from hash map failed", K(ret)); } else if (OB_FAIL(qb_name_map_.set_refactored(qb_names.qb_names_.at(0), i))) { LOG_WARN("failed to add name map", K(ret)); } } else if (OB_UNLIKELY(idx < 0 || idx >= i)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected idx.", K(ret), K(idx), K(i)); } else { qb_names.qb_names_.reuse(); qb_names.is_from_hint_ = false; stmt_id_map_.at(idx).qb_names_.reuse(); stmt_id_map_.at(idx).is_from_hint_ = false; } } qb_name_map_.reuse(); return ret; } const char *ObQueryHint::get_dml_stmt_name(stmt::StmtType stmt_type, bool is_set_stmt) const { switch (stmt_type) { case stmt::T_SELECT: if (is_set_stmt) { return "SET$"; } else { return "SEL$"; } case stmt::T_INSERT_ALL: return "INS_ALL$"; case stmt::T_INSERT: return "INS$"; case stmt::T_REPLACE: return "REP$"; case stmt::T_UPDATE: return "UPD$"; case stmt::T_DELETE: return "DEL$"; case stmt::T_MERGE: return "MRG$"; default: return NULL; } } int ObQueryHint::get_qb_name_counts(const int64_t stmt_count, ObIArray &qb_name_counts) const { int ret = OB_SUCCESS; if (OB_UNLIKELY(stmt_id_map_.count() != stmt_count)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected stmt id map count", K(stmt_id_map_.count()), K(stmt_count)); } else if (OB_FAIL(qb_name_counts.prepare_allocate(stmt_count))) { LOG_WARN("fail to prepare allocate", K(ret), K(stmt_count)); } else { for (int i = 0; OB_SUCC(ret) && i < stmt_count; ++i) { if (OB_UNLIKELY(stmt_id_map_.at(i).qb_names_.empty() && NULL != get_dml_stmt_name(stmt_id_map_.at(i).stmt_type_, false))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected qb names for stmt id", K(ret), K(i), K(stmt_id_map_.at(i).qb_names_)); } else { qb_name_counts.at(i) = stmt_id_map_.at(i).qb_names_.count(); } } } return ret; } int ObQueryHint::recover_qb_names(const ObIArray &qb_name_counts, int64_t &stmt_count) { int ret = OB_SUCCESS; if (OB_UNLIKELY(stmt_id_map_.count() != stmt_count)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected stmt id map count", K(stmt_id_map_.count()), K(stmt_count)); } else { int64_t pre_qb_name_count = 0; for (int i = stmt_id_map_.count() - 1; OB_SUCC(ret) && i >= 0; --i) { pre_qb_name_count = i < qb_name_counts.count() ? qb_name_counts.at(i) : 0; ObIArray &qb_names = stmt_id_map_.at(i).qb_names_; for (int j = qb_names.count() - 1; OB_SUCC(ret) && j >= pre_qb_name_count; --j) { if (OB_FAIL(qb_name_map_.erase_refactored(qb_names.at(j)))) { LOG_WARN("unexpected qb names for stmt id", K(ret), K(i), K(qb_names.at(j))); } else { qb_names.pop_back(); } } if (OB_SUCC(ret) && i >= qb_name_counts.count()) { stmt_id_map_.pop_back(); --stmt_count; } } } return ret; } int ObQueryHint::print_qb_name_hints(PlanText &plan_text) const { int ret = OB_SUCCESS; char *buf = plan_text.buf_; int64_t &buf_len = plan_text.buf_len_; int64_t &pos = plan_text.pos_; for (int64_t i = 0; OB_SUCC(ret) && i < stmt_id_map_.count(); ++i) { if (stmt_id_map_.at(i).is_from_hint_) { const ObIArray &qb_names = stmt_id_map_.at(i).qb_names_; if (OB_UNLIKELY(qb_names.empty() || qb_names.at(0).empty())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected qb name hint", K(ret), K(i), K(stmt_id_map_.at(i))); } else if (OB_FAIL(BUF_PRINTF("%sQB_NAME(%.*s)", get_outline_indent(plan_text.is_oneline_), qb_names.at(0).length(), qb_names.at(0).ptr()))) { LOG_WARN("failed to print qb_name hint", K(ret)); } } } return ret; } // Used for stmt printer // If outline_stmt_id_ is invalid stmt id and has_outline_data(), do not print hint. // This may happened for outline data from SPM. int ObQueryHint::print_stmt_hint(PlanText &plan_text, const ObDMLStmt &stmt, const bool is_first_stmt_for_hint) const { int ret = OB_SUCCESS; const int64_t stmt_id = stmt.get_stmt_id(); if (OB_FAIL(print_qb_name_hint(plan_text, stmt_id))) { LOG_WARN("failed to print qb_name hint", K(ret)); } else if (OB_INVALID_STMT_ID != stmt_id && stmt_id == outline_stmt_id_) { // Outline data resolved from this stmt, print outline data here. if (OB_FAIL(print_outline_data(plan_text))) { LOG_WARN("failed to print outline data", K(ret)); } } else if (!has_outline_data()) { // Not outline data, print current stmt hint here. // If stmt is the first stmt can add hint, print global hint and hint with qb name. // For query "select_1 union all select_2", root stmt is "union all" and the first stmt to print hint is select_1 if (is_first_stmt_for_hint && OB_FAIL(get_global_hint().print_global_hint(plan_text))) { LOG_WARN("failed to print global hint", K(ret)); } else if (OB_FAIL(stmt.get_stmt_hint().print_stmt_hint(plan_text))) { LOG_WARN("failed to print stmt hint", K(ret)); } else if (is_first_stmt_for_hint) { int tmp = OB_SUCCESS; int64_t tmp_stmt_id = OB_INVALID_STMT_ID; for (int64_t i = 0; OB_SUCC(ret) && i < qb_hints_.count(); ++i) { if (OB_FAIL(qb_name_map_.get_refactored(qb_hints_.at(i).qb_name_, tmp_stmt_id))) { if (OB_UNLIKELY(OB_HASH_NOT_EXIST != ret)) { LOG_WARN("failed to check hash map exists", K(ret)); } else if (OB_FAIL(qb_hints_.at(i).print_hints(plan_text))) { LOG_WARN("failed to print hint", K(ret)); } } } } }/* */ return ret; } // used for stmt printer int ObQueryHint::print_outline_data(PlanText &plan_text) const { int ret = OB_SUCCESS; char *buf = plan_text.buf_; int64_t &buf_len = plan_text.buf_len_; int64_t &pos = plan_text.pos_; bool is_oneline = plan_text.is_oneline_; if (OB_UNLIKELY(1 < stmt_id_hints_.count())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected stmt id hints for outline data", K(ret), K(stmt_id_hints_)); } else if (OB_FAIL(BUF_PRINTF("%sBEGIN_OUTLINE_DATA", ObQueryHint::get_outline_indent(is_oneline)))) { } else if (1 == stmt_id_hints_.count() && OB_FAIL(stmt_id_hints_.at(0).print_hints(plan_text, true))) { LOG_WARN("failed to print hint", K(ret)); } else { const ObHint *hint = NULL; for (int64_t i = 0; OB_SUCC(ret) && i < qb_hints_.count(); ++i) { if (OB_FAIL(qb_hints_.at(i).print_hints(plan_text, true))) { LOG_WARN("failed to print hint", K(ret)); } } for (int64_t i = 0; OB_SUCC(ret) && i < trans_list_.count(); ++i) { if (OB_ISNULL(hint = trans_list_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(i), K(trans_list_)); } else if (OB_FAIL(hint->print_hint(plan_text))) { LOG_WARN("failed to print hint", K(ret)); } } if (OB_FAIL(ret)) { } else if (OB_FAIL(get_global_hint().print_global_hint(plan_text))) { LOG_WARN("failed to print global hint", K(ret)); } else if (OB_FAIL(BUF_PRINTF("%sEND_OUTLINE_DATA", ObQueryHint::get_outline_indent(is_oneline)))) { LOG_WARN("fail to print buf", K(ret)); } } return ret; } int ObQueryHint::print_qb_name_hint(PlanText &plan_text, int64_t stmt_id) const { int ret = OB_SUCCESS; if (OB_INVALID_STMT_ID == stmt_id) { /* do nothing, this stmt is create for print stmt for mv */ } else if (OB_UNLIKELY(stmt_id < 0 || stmt_id >= stmt_id_map_.count())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected stmt id", K(ret), K(stmt_id), K(stmt_id_map_.count())); } else if (stmt_id_map_.at(stmt_id).is_from_hint_) { const ObIArray &qb_names = stmt_id_map_.at(stmt_id).qb_names_; char *buf = plan_text.buf_; int64_t &buf_len = plan_text.buf_len_; int64_t &pos = plan_text.pos_; if (OB_UNLIKELY(qb_names.empty() || qb_names.at(0).empty())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected qb name hint", K(ret), K(stmt_id), K(stmt_id_map_.at(stmt_id))); } else if (OB_FAIL(BUF_PRINTF("%sQB_NAME(%.*s)", get_outline_indent(plan_text.is_oneline_), qb_names.at(0).length(), qb_names.at(0).ptr()))) { LOG_WARN("failed to print qb_name hint", K(ret)); } } return ret; } int ObQueryHint::print_transform_hints(PlanText &plan_text) const { int ret = OB_SUCCESS; const ObIArray *hints = NULL; if (plan_text.is_used_hint_) { hints = &used_trans_hints_; } else { hints = &outline_trans_hints_; } for (int64_t i = 0; OB_SUCC(ret) && i < hints->count(); ++i) { if (OB_ISNULL(hints->at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected NULL", K(ret), K(hints->at(i))); } else if (OB_FAIL(hints->at(i)->print_hint(plan_text))) { LOG_WARN("failed to print transform hint", K(ret), K(*hints->at(i))); } } return ret; } int ObQueryHint::get_relids_from_hint_tables(const ObDMLStmt &stmt, const ObIArray &tables, ObRelIds &rel_ids) const { int ret = OB_SUCCESS; rel_ids.reuse(); bool is_valid = true; int32_t index = OB_INVALID_INDEX; for (int64_t i = 0; is_valid && OB_SUCC(ret) && i < tables.count(); ++i) { if (OB_FAIL(get_table_bit_index_by_hint_table(stmt, tables.at(i), index))) { LOG_WARN("failed to get table bit index by hint table.", K(ret)); } else if (OB_INVALID_INDEX == index) { is_valid = false; } else if (OB_FAIL(rel_ids.add_member(index))) { LOG_WARN("failed to add members", K(ret)); } } if (OB_SUCC(ret) && !is_valid) { rel_ids.reuse(); } return ret; } int ObQueryHint::get_table_bit_index_by_hint_table(const ObDMLStmt &stmt, const ObTableInHint &table, int32_t &idx) const { int ret = OB_SUCCESS; idx = OB_INVALID_INDEX; TableItem *table_item = NULL; if (OB_FAIL(get_table_item_by_hint_table(stmt, table, table_item))) { LOG_WARN("failed to get table bit index by hint table.", K(ret)); } else if (NULL == table_item) { /* do nothing */ } else { idx = stmt.get_table_bit_index(table_item->table_id_); } return ret; } int ObQueryHint::get_table_item_by_hint_table(const ObDMLStmt &stmt, const ObTableInHint &table, TableItem *&table_item) const { int ret = OB_SUCCESS; table_item = NULL; bool implicit_match_allowed = table.qb_name_.empty(); const ObIArray &table_items = stmt.get_table_items(); TableItem *cur_table_item = NULL; TableItem *explicit_matched = NULL; TableItem *implicit_matched = NULL; for (int64_t i = 0; OB_SUCC(ret) && NULL == explicit_matched && i < table_items.count(); ++i) { if (OB_ISNULL(cur_table_item = table_items.at(i)) || OB_UNLIKELY(cur_table_item->qb_name_.empty())) { ret = OB_ERR_UNEXPECTED; // qb_name_ should not be empty LOG_WARN("unexpected table item.", K(ret), K(cur_table_item)); } else if (!table.is_match_table_item(cs_type_, *cur_table_item)) { /* do nothing */ } else if (0 == cur_table_item->qb_name_.case_compare(table.qb_name_)) { explicit_matched = cur_table_item; } else if (!implicit_match_allowed) { /* do nothing */ } else if (NULL == implicit_matched) { implicit_matched = cur_table_item; } else { implicit_match_allowed = false; implicit_matched = NULL; } } if (OB_SUCC(ret)) { table_item = NULL != explicit_matched ? explicit_matched : implicit_matched; if (NULL == table_item) { LOG_TRACE("no table item matched hint table", K(table), K(table_items)); } } return ret; } int ObQueryHint::get_basic_table_without_index_by_hint_table(const ObDMLStmt &stmt, const ObTableInHint &table, TableItem *&table_item) const { int ret = OB_SUCCESS; table_item = NULL; const ObIArray &table_items = stmt.get_table_items(); int64_t num = table_items.count(); TableItem *item = NULL; table_item = NULL; for (int64_t i = 0; OB_SUCC(ret) && NULL == table_item && i < num; ++i) { if (OB_ISNULL(item = table_items.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected table item.", K(ret), K(item)); } else if (!item->is_basic_table()) { /* do nothing */ } else if (OB_LIKELY(item->ddl_table_id_ != OB_INVALID_ID) && OB_UNLIKELY(item->ddl_table_id_ != item->ref_id_)) { // do nothing } else if (!table.is_match_physical_table_item(cs_type_, *item)) { /* do nothing */ } else { table_item = item; } } return ret; } int ObQueryHint::get_qb_name(int64_t stmt_id, ObString &qb_name) const { int ret = OB_SUCCESS; qb_name.reset(); if (OB_UNLIKELY(stmt_id < 0 || stmt_id >= stmt_id_map_.count())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected stmt id", K(ret), K(stmt_id), K(stmt_id_map_.count()), K(stmt_id_map_)); } else { const ObIArray &qb_names = stmt_id_map_.at(stmt_id).qb_names_; if (OB_UNLIKELY(qb_names.empty() || qb_names.at(qb_names.count() - 1).empty())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected qb names", K(ret), K(stmt_id), K(stmt_id_map_.at(stmt_id))); } else { qb_name = qb_names.at(qb_names.count() - 1); } } return ret; } int ObQueryHint::fill_tables(const TableItem &table, ObIArray &hint_tables) const { int ret = OB_SUCCESS; hint_tables.reuse(); ObSEArray tables; if (OB_FAIL(tables.push_back(&table))) { LOG_WARN("failed to push back", K(ret)); } else { const TableItem *cur_table = NULL; for (int64_t i = 0; OB_SUCC(ret) && i < tables.count(); ++i) { if (OB_ISNULL(cur_table = tables.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected NULL", K(ret), K(i), K(tables)); } else if (cur_table->is_joined_table()) { const JoinedTable *join_table = static_cast(cur_table); if (OB_FAIL(tables.push_back(join_table->left_table_)) || OB_FAIL(tables.push_back(join_table->right_table_))) { LOG_WARN("failed to push back", K(ret)); } } else if (OB_FAIL(hint_tables.push_back(ObTableInHint(cur_table->qb_name_, cur_table->database_name_, cur_table->get_object_name())))) { LOG_WARN("failed to push back", K(ret)); } } } return ret; } bool ObQueryHint::is_valid_outline_transform(int64_t trans_list_loc, const ObHint *cur_hint) const { bool bret = false; const ObHint *trans_hint = NULL; if (NULL == cur_hint || NULL == (trans_hint = get_outline_trans_hint(trans_list_loc)) || trans_hint->get_orig_hint() != cur_hint->get_orig_hint()) { /* do nothing */ } else { bret = true; } return bret; } void QbNames::reset() { stmt_type_ = stmt::StmtType::T_NONE; is_set_stmt_ = false; is_from_hint_ = false; parent_name_.reset(); qb_names_.reset(); } int QbNames::print_qb_names(PlanText &plan_text) const { int ret = OB_SUCCESS; char *buf = plan_text.buf_; int64_t &buf_len = plan_text.buf_len_; int64_t &pos = plan_text.pos_; int64_t idx = is_from_hint_ ? 1 : 0; if (OB_UNLIKELY(qb_names_.empty())) { ObString stmt_type = ObResolverUtils::get_stmt_type_string(stmt_type_); if (OB_FAIL(BUF_PRINTF("stmt_type:%.*s ", stmt_type.length(), stmt_type.ptr()))) { LOG_WARN("failed to print parent stmt type", K(ret)); } } else if (is_from_hint_) { if (OB_UNLIKELY(qb_names_.empty() || qb_names_.at(0).empty())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected qb name hint", K(ret), K(*this)); } else if (OB_FAIL(BUF_PRINTF("qb_name(%.*s) ", qb_names_.at(0).length(), qb_names_.at(0).ptr()))) { LOG_WARN("failed to print qb_name from hint", K(ret)); } } else if (!parent_name_.empty()) { if (OB_FAIL(BUF_PRINTF("parent:%.*s ", parent_name_.length(), parent_name_.ptr()))) { LOG_WARN("failed to print parent qb_name", K(ret)); } } if (OB_SUCC(ret) && qb_names_.count() > idx) { if ((is_from_hint_ || !parent_name_.empty()) && OB_FAIL(BUF_PRINTF(" > "))) { LOG_WARN("failed to print parent qb_name", K(ret)); } for (int64_t i = idx; OB_SUCC(ret) && i < qb_names_.count(); ++i) { if (OB_FAIL(BUF_PRINTF("%.*s", qb_names_.at(i).length(), qb_names_.at(i).ptr()))) { LOG_WARN("failed to print qb_name hint", K(ret)); } else if ((i < qb_names_.count() - 1) && OB_FAIL(BUF_PRINTF(" > "))) { LOG_WARN("failed to print parent qb_name", K(ret)); } } } return ret; } int ObHints::assign(const ObHints &other) { int ret = OB_SUCCESS; stmt_id_ = other.stmt_id_; qb_name_ = other.qb_name_; if (OB_FAIL(hints_.assign(other.hints_))) { LOG_WARN("failed to assign hints", K(ret)); } return ret; } int ObHints::print_hints(PlanText &plan_text, bool ignore_trans_hint /* default false */ ) const { int ret = OB_SUCCESS; const ObHint *hint = NULL; for (int64_t i = 0; OB_SUCC(ret) && i < hints_.count(); ++i) { if (OB_ISNULL(hint = hints_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected NULL", K(ret), K(hint)); } else if (ignore_trans_hint && hint->is_transform_hint()) { /* do nothing */ } else if (OB_FAIL(hint->print_hint(plan_text))) { LOG_WARN("failed to print transform hint", K(ret), K(*hint)); } } return ret; } void ObStmtHint::reset() { query_hint_ = NULL; normal_hints_.reuse(); other_opt_hints_.reuse(); } int ObStmtHint::assign(const ObStmtHint &other) { int ret = OB_SUCCESS; query_hint_ = other.query_hint_; if (OB_FAIL(normal_hints_.assign(other.normal_hints_))) { LOG_WARN("failed to assign normal hints", K(ret)); } else if (OB_FAIL(other_opt_hints_.assign(other.other_opt_hints_))) { LOG_WARN("failed to assign other opt hints", K(ret)); } return ret; } DEF_TO_STRING(ObStmtHint) { int64_t pos = 0; J_OBJ_START(); const ObHint *hint = NULL; const int64_t N = get_hint_count(); for (int64_t i = 0; i < N; ++i) { hint = get_hint_by_idx(i); if (NULL != hint) { J_KV(K(hint)); J_COMMA(); } } J_OBJ_END(); return pos; } int ObStmtHint::print_stmt_hint(PlanText &plan_text) const { int ret = OB_SUCCESS; const ObHint *hint = NULL; const int64_t N = get_hint_count(); const bool ignore_parallel = EXPLAIN_DBLINK_STMT == plan_text.type_; for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) { if (OB_ISNULL(hint = get_hint_by_idx(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected NULL", K(ret), K(hint)); } else if (ignore_parallel && hint->is_table_parallel_hint()) { // do nothing } else if (OB_FAIL(hint->print_hint(plan_text))) { LOG_WARN("failed to print hint", K(ret)); } } return ret; } int ObStmtHint::init_stmt_hint(const ObDMLStmt &stmt, const ObQueryHint &query_hint, bool use_stmt_id_hints) { int ret = OB_SUCCESS; reset(); set_query_hint(&query_hint); ObString qb_name; const ObHints *qb_hints = NULL; ObSEArray all_hints; if (OB_FAIL(stmt.get_qb_name(qb_name))) { LOG_WARN("failed to get qb name", K(ret)); } else if (NULL != (qb_hints = query_hint.get_qb_hints(qb_name)) && OB_FAIL(all_hints.assign(qb_hints->hints_))) { LOG_WARN("failed to assign hints", K(ret)); } else if (use_stmt_id_hints && NULL != (qb_hints = query_hint.get_stmt_id_hints(stmt.get_stmt_id())) && OB_FAIL(append(all_hints, qb_hints->hints_))) { LOG_WARN("failed to append hints", K(ret)); } else { ObSEArray conflict_hints; for (int64_t i = 0; OB_SUCC(ret) && i < all_hints.count(); ++i) { if (OB_ISNULL(all_hints.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(i), K(all_hints)); } else if (OB_FAIL(merge_hint(*all_hints.at(i), HINT_DOMINATED_EQUAL, conflict_hints))) { LOG_WARN("failed to merge hint", K(ret)); } } LOG_TRACE("finish init stmt hint", K(stmt.get_stmt_id()), K(qb_name), K(*this)); } return ret; } int ObStmtHint::merge_stmt_hint(const ObStmtHint &other, ObHintMergePolicy policy /* default HINT_DOMINATED_EQUAL */) { int ret = OB_SUCCESS; ObSEArray conflict_hints; for (int64_t i = 0; OB_SUCC(ret) && i < other.normal_hints_.count(); ++i) { if (OB_ISNULL(other.normal_hints_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(other.normal_hints_)); } else if (OB_FAIL(merge_normal_hint(*other.normal_hints_.at(i), policy, conflict_hints))) { LOG_WARN("failed to merge normal hint", K(ret)); } } if (OB_SUCC(ret) && OB_FAIL(append_array_no_dup(other_opt_hints_, other.other_opt_hints_))) { LOG_WARN("failed to append other opt hint", K(ret)); } return ret; } const ObHint *ObStmtHint::get_normal_hint(ObItemType hint_type, int64_t *idx /* default NULL */) const { const ObHint *hint = NULL; if (NULL != idx) { *idx = -1; } for (int64_t i = 0; NULL == hint && i < normal_hints_.count(); ++i) { if (OB_ISNULL(normal_hints_.at(i))) { /* do nothing */ } else if (normal_hints_.at(i)->get_hint_type() == hint_type) { hint = normal_hints_.at(i); if (NULL != idx) { *idx = i; } } } return hint; } ObHint *ObStmtHint::get_normal_hint(ObItemType hint_type, int64_t *idx /* default NULL */) { ObHint *hint = NULL; if (NULL != idx) { *idx = -1; } for (int64_t i = 0; NULL == hint && i < normal_hints_.count(); ++i) { if (OB_ISNULL(normal_hints_.at(i))) { /* do nothing */ } else if (normal_hints_.at(i)->get_hint_type() == hint_type) { hint = normal_hints_.at(i); if (NULL != idx) { *idx = i; } } } return hint; } const ObHint *ObStmtHint::get_hint_by_idx(int64_t idx) const { const ObHint *hint = NULL; int64_t cur_idx = idx; if (cur_idx < normal_hints_.count()) { hint = normal_hints_.at(cur_idx); } else if ((cur_idx -= normal_hints_.count()) < other_opt_hints_.count()) { hint = other_opt_hints_.at(cur_idx); } else { hint = NULL; } return hint; } int ObStmtHint::set_hint(int64_t idx, ObHint *hint) { int ret = OB_SUCCESS; int64_t cur_idx = idx; if (OB_UNLIKELY(idx < 0 || idx >= get_hint_count())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected idx", K(ret), K(idx), K(get_hint_count())); } else if (cur_idx < normal_hints_.count()) { normal_hints_.at(cur_idx) = hint; } else if ((cur_idx -= normal_hints_.count()) < other_opt_hints_.count()) { other_opt_hints_.at(cur_idx) = hint; } return ret; } int ObStmtHint::set_set_stmt_hint() { int ret = OB_SUCCESS; static const int64_t VALID_HINT_SIZE = 6; ObItemType valid_hint[VALID_HINT_SIZE] = { T_NO_REWRITE, T_SIMPLIFY_SET, T_PQ_SET, T_USE_HASH_SET, T_USE_HASH_DISTINCT, T_DISTINCT_PUSHDOWN}; const ObQueryHint *query_hint = query_hint_; ObSEArray hints; for (int64_t i = 0; OB_SUCC(ret) && i < normal_hints_.count(); ++i) { if (OB_ISNULL(normal_hints_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(i), K(normal_hints_.at(i))); } else if (!has_exist_in_array(valid_hint, VALID_HINT_SIZE, normal_hints_.at(i)->get_hint_type())) { /* do nothing */ } else if (OB_FAIL(hints.push_back(normal_hints_.at(i)))) { LOG_WARN("failed to push back", K(ret)); } } if (OB_SUCC(ret)) { reset(); query_hint_ = query_hint; if (OB_FAIL(normal_hints_.assign(hints))) { LOG_WARN("failed to assign hints", K(ret)); } } return ret; } int ObStmtHint::set_simple_view_hint(const ObStmtHint *other /* default NULL */ ) { int ret = OB_SUCCESS; static const int64_t RESET_HINT_SIZE = 3; ObItemType reset_hint[RESET_HINT_SIZE] = { T_MERGE_HINT, T_USE_HASH_AGGREGATE, T_PLACE_GROUP_BY }; if (NULL != other && OB_FAIL(assign(*other))) { LOG_WARN("failed to assign stmt hint", K(ret)); } else if (OB_FAIL(remove_normal_hints(reset_hint, RESET_HINT_SIZE))) { LOG_WARN("failed to remove hints", K(ret)); } return ret; } int ObStmtHint::remove_normal_hints(const ObItemType *hint_array, const int64_t num) { int ret = OB_SUCCESS; ObSEArray hints; for (int64_t i = 0; OB_SUCC(ret) && i < normal_hints_.count(); ++i) { if (OB_ISNULL(normal_hints_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(i), K(normal_hints_.at(i))); } else if (has_exist_in_array(hint_array, num, normal_hints_.at(i)->get_hint_type())) { /* do nothing */ } else if (OB_FAIL(hints.push_back(normal_hints_.at(i)))) { LOG_WARN("failed to push back", K(ret)); } } if (OB_SUCC(ret) && hints.count() != normal_hints_.count() && OB_FAIL(normal_hints_.assign(hints))) { LOG_WARN("failed to assign hints", K(ret)); } return ret; } int ObStmtHint::replace_name_for_single_table_view(ObIAllocator *allocator, const ObDMLStmt &stmt, const TableItem &view_table) { int ret = OB_SUCCESS; const TableItem *target_table = NULL; const ObDMLStmt *child_stmt = NULL; if (OB_ISNULL(query_hint_) || OB_ISNULL(stmt.get_table_item_by_id(view_table.table_id_)) || OB_UNLIKELY(!view_table.is_generated_table()) || OB_ISNULL(child_stmt = view_table.ref_query_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected params", K(ret), K(query_hint_), K(target_table), K(child_stmt)); } else if (child_stmt->get_from_item_size() != 1 || child_stmt->get_from_item(0).is_joined_) { /* do nothing */ } else if (OB_ISNULL(target_table = child_stmt->get_table_item(0))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected NULL", K(ret), K(target_table)); } else if (0 == ObCharset::strcmp(query_hint_->cs_type_, view_table.get_object_name(), target_table->get_object_name())) { /* need not replace name */ } else { LOG_DEBUG("replace name for single table view in hint", K(view_table), K(*target_table)); const int64_t N = get_hint_count(); ObHint *hint = NULL; ObHint *new_hint = NULL; for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) { if (OB_ISNULL(hint = const_cast(get_hint_by_idx(i)))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(i)); } else if (OB_FAIL(hint->create_push_down_hint(allocator, query_hint_->cs_type_, view_table, *target_table, new_hint))) { LOG_WARN("failed to create push down hint", K(ret), K(i), K(*hint)); } else if (OB_FAIL(set_hint(i, new_hint))) { LOG_WARN("failed to set hint", K(ret), K(i), K(new_hint)); } } } return ret; } // deal hint conflict and add hint int ObStmtHint::merge_hint(ObHint &hint, ObHintMergePolicy policy, ObIArray &conflict_hints) { int ret = OB_SUCCESS; if (hint.is_access_path_hint() || hint.is_join_hint() || hint.is_join_filter_hint() || hint.is_table_parallel_hint() || hint.is_table_dynamic_sampling_hint() || hint.is_pq_subquery_hint()) { if (OB_FAIL(add_var_to_array_no_dup(other_opt_hints_, &hint))) { LOG_WARN("failed to add var to array", K(ret)); } } else if (OB_FAIL(merge_normal_hint(hint, policy, conflict_hints))) { LOG_WARN("failed to merge normal hint", K(ret)); } return ret; } int ObStmtHint::merge_normal_hint(ObHint &hint, ObHintMergePolicy policy, ObIArray &conflict_hints) { int ret = OB_SUCCESS; const ObHint *final_hint = NULL; int64_t idx = -1; const ObHint *cur_hint = get_normal_hint(hint.get_hint_type(), &idx); if (OB_FAIL(hint.merge_hint(cur_hint, &hint, policy, conflict_hints, final_hint))) { LOG_WARN("failed to merge hint", K(ret)); } else if (cur_hint == final_hint) { /* do nothing */ } else if (NULL != cur_hint && NULL == final_hint) { if (OB_FAIL(normal_hints_.remove(idx))) { LOG_WARN("failed to remove hint", K(ret)); } } else if (NULL == cur_hint && NULL != final_hint) { if (OB_FAIL(normal_hints_.push_back(const_cast(final_hint)))) { LOG_WARN("failed to push back", K(ret)); } } else if (NULL != cur_hint && NULL != final_hint) { if (OB_FAIL(set_hint(idx, const_cast(final_hint)))) { LOG_WARN("failed to set single hint", K(ret)); } } return ret; } int ObStmtHint::reset_explicit_trans_hint(ObItemType hint_type) { int ret = OB_SUCCESS; const ObHint *cur_hint = get_normal_hint(hint_type); if (NULL == cur_hint) { /* do nothing */ } else if (OB_UNLIKELY(!cur_hint->is_transform_hint())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected hint type", K(ret), K(*cur_hint)); } else if (!static_cast(cur_hint)->is_explicit()) { /* do nothing */ } else if (OB_FAIL(remove_normal_hints(&hint_type, 1))) { LOG_WARN("failed to remove hints", K(ret)); } return ret; } bool ObStmtHint::has_enable_hint(ObItemType hint_type) const { const ObHint *cur_hint = get_normal_hint(hint_type); return NULL != cur_hint && cur_hint->is_enable_hint(); } bool ObStmtHint::has_disable_hint(ObItemType hint_type) const { const ObHint *cur_hint = get_normal_hint(hint_type); return NULL != cur_hint && cur_hint->is_disable_hint(); } void ObLogPlanHint::reset() { is_outline_data_ = false; #ifdef OB_BUILD_SPM is_spm_evolution_ = false; #endif join_order_.reset(); table_hints_.reuse(); join_hints_.reuse(); normal_hints_.reuse(); } #ifndef OB_BUILD_SPM int ObLogPlanHint::init_log_plan_hint(ObSqlSchemaGuard &schema_guard, const ObDMLStmt &stmt, const ObQueryHint &query_hint) #else int ObLogPlanHint::init_log_plan_hint(ObSqlSchemaGuard &schema_guard, const ObDMLStmt &stmt, const ObQueryHint &query_hint, const bool is_spm_evolution) #endif { int ret = OB_SUCCESS; reset(); is_outline_data_ = query_hint.has_outline_data(); #ifdef OB_BUILD_SPM is_spm_evolution_ = is_spm_evolution; #endif const ObStmtHint &stmt_hint = stmt.get_stmt_hint(); if (OB_FAIL(join_order_.init_leading_info(stmt, query_hint, stmt_hint.get_normal_hint(T_LEADING)))) { LOG_WARN("failed to get leading hint info", K(ret)); } else if (OB_FAIL(init_normal_hints(stmt_hint.normal_hints_))) { LOG_WARN("failed to init normal hints", K(ret)); } else if (OB_FAIL(init_other_opt_hints(schema_guard, stmt, query_hint, stmt_hint.other_opt_hints_))) { LOG_WARN("failed to init other opt hints", K(ret)); } else { LOG_TRACE("finish init log plan hint", K(stmt.get_stmt_id()), K(*this), K(stmt_hint.normal_hints_), K(stmt_hint.other_opt_hints_)); } return ret; } int ObLogPlanHint::init_normal_hints(const ObIArray &normal_hints) { int ret = OB_SUCCESS; const ObHint *hint = NULL; for (int64_t i = 0; OB_SUCC(ret) && i < normal_hints.count(); ++i) { if (OB_ISNULL(hint = normal_hints.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(i), K(normal_hints)); } else if (hint->is_transform_hint() || hint->is_join_order_hint()) { /* do nothing */ } else if (OB_FAIL(normal_hints_.push_back(hint))) { LOG_WARN("failed to push back", K(ret)); } } return ret; } int ObLogPlanHint::init_other_opt_hints(ObSqlSchemaGuard &schema_guard, const ObDMLStmt &stmt, const ObQueryHint &query_hint, const ObIArray &hints) { int ret = OB_SUCCESS; const ObHint *hint = NULL; for (int64_t i = 0; OB_SUCC(ret) && i < hints.count(); ++i) { if (OB_ISNULL(hint = hints.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(i)); } else if (hint->is_access_path_hint()) { if (OB_FAIL(add_index_hint(stmt, query_hint, *static_cast(hint)))) { LOG_WARN("failed to add index hint", K(ret)); } } else if (hint->is_table_parallel_hint()) { if (OB_FAIL(add_table_parallel_hint(stmt, query_hint, *static_cast(hint)))) { LOG_WARN("failed to add table parallel hint", K(ret)); } } else if (hint->is_join_filter_hint()) { if (OB_FAIL(add_join_filter_hint(stmt, query_hint, *static_cast(hint)))) { LOG_WARN("failed to add join filter hint", K(ret)); } } else if (hint->is_join_hint()) { if (OB_FAIL(add_join_hint(stmt, query_hint, *static_cast(hint)))) { LOG_WARN("failed to add join hint", K(ret)); } } else if (hint->is_table_dynamic_sampling_hint()) { if (OB_FAIL(add_table_dynamic_sampling_hint(stmt, query_hint, *static_cast(hint)))) { LOG_WARN("failed to add dynamic sampling hint", K(ret)); } } else if (hint->is_pq_subquery_hint()) { if (OB_FAIL(normal_hints_.push_back(hint))) { LOG_WARN("failed to push back", K(ret)); } } else { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected hint type in other_opt_hints_", K(ret), K(*hint)); } } if (OB_FAIL(ret)) { } else if (OB_FAIL(init_log_table_hints(schema_guard))) { LOG_WARN("failed to init log table hints", K(ret)); } else if (OB_FAIL(init_log_join_hints())) { LOG_WARN("failed to init log join hints", K(ret)); } return ret; } // init single table hints: // 1. index hints; // 2. imc hints; // 3. table parallel hints; // 4. dynamic sampling hint; int ObLogPlanHint::init_log_table_hints(ObSqlSchemaGuard &schema_guard) { int ret = OB_SUCCESS; int64_t valid_cnt = 0; for (int64_t i = 0; OB_SUCC(ret) && i < table_hints_.count(); ++i) { if (OB_FAIL(table_hints_.at(i).init_index_hints(schema_guard))) { LOG_WARN("failed to init index hint for table.", K(ret)); } else if (!table_hints_.at(i).is_valid()) { /* do nothing */ } else if (valid_cnt != i && OB_FAIL(table_hints_.at(valid_cnt).assign(table_hints_.at(i)))) { LOG_WARN("failed to assign log index hint.", K(ret)); } else { ++valid_cnt; } } if (OB_SUCC(ret)) { ObOptimizerUtil::revert_items(table_hints_, valid_cnt); } return ret; } int ObLogPlanHint::add_index_hint(const ObDMLStmt &stmt, const ObQueryHint &query_hint, const ObIndexHint &index_hint) { int ret = OB_SUCCESS; LogTableHint *log_table_hint = NULL; if (OB_FAIL(get_log_table_hint_for_update(stmt, query_hint, index_hint.get_table(), true, log_table_hint))) { LOG_WARN("failed to get log table hint by hint", K(ret)); } else if (NULL == log_table_hint) { /* do nothing */ } else if (T_USE_DAS_HINT == index_hint.get_hint_type()) { if (NULL == log_table_hint->use_das_hint_ || index_hint.is_enable_hint()) { log_table_hint->use_das_hint_ = &index_hint; } } else if (T_USE_COLUMN_STORE_HINT == index_hint.get_hint_type()) { if (NULL == log_table_hint->use_column_store_hint_ || index_hint.is_enable_hint()) { log_table_hint->use_column_store_hint_ = &index_hint; } } else if (OB_FAIL(log_table_hint->index_hints_.push_back(&index_hint))) { LOG_WARN("failed to push back", K(ret)); } return ret; } int ObLogPlanHint::add_table_parallel_hint(const ObDMLStmt &stmt, const ObQueryHint &query_hint, const ObTableParallelHint &table_parallel_hint) { int ret = OB_SUCCESS; LogTableHint *log_table_hint = NULL; if (OB_FAIL(get_log_table_hint_for_update(stmt, query_hint, table_parallel_hint.get_table(), true, log_table_hint))) { LOG_WARN("failed to get log table hint by hint", K(ret)); } else if (NULL == log_table_hint) { /* do nothing */ } else if (NULL == log_table_hint->parallel_hint_ || table_parallel_hint.get_parallel() > log_table_hint->parallel_hint_->get_parallel()) { log_table_hint->parallel_hint_ = &table_parallel_hint; } return ret; } int ObLogPlanHint::add_table_dynamic_sampling_hint(const ObDMLStmt &stmt, const ObQueryHint &query_hint, const ObTableDynamicSamplingHint &table_ds_hint) { int ret = OB_SUCCESS; LogTableHint *log_table_hint = NULL; if (OB_FAIL(get_log_table_hint_for_update(stmt, query_hint, table_ds_hint.get_table(), true, log_table_hint))) { LOG_WARN("failed to get log table hint by hint", K(ret)); } else if (NULL == log_table_hint) { /* do nothing */ } else if (NULL == log_table_hint->dynamic_sampling_hint_ && !log_table_hint->is_ds_hint_conflict_) { log_table_hint->dynamic_sampling_hint_ = &table_ds_hint; } else if (log_table_hint->dynamic_sampling_hint_->get_dynamic_sampling() != table_ds_hint.get_dynamic_sampling() || log_table_hint->dynamic_sampling_hint_->get_sample_block_cnt() != table_ds_hint.get_sample_block_cnt()) { //conflict will cause reset origin state compatible Oracle. log_table_hint->dynamic_sampling_hint_ = NULL; log_table_hint->is_ds_hint_conflict_ = true; } return ret; } int ObLogPlanHint::add_join_filter_hint(const ObDMLStmt &stmt, const ObQueryHint &query_hint, const ObJoinFilterHint &join_filter_hint) { int ret = OB_SUCCESS; LogTableHint *log_table_hint = NULL; if (OB_FAIL(get_log_table_hint_for_update(stmt, query_hint, join_filter_hint.get_filter_table(), false, log_table_hint))) { LOG_WARN("failed to get log table hint by hint", K(ret)); } else if (NULL == log_table_hint) { /* do nothing */ } else if (OB_FAIL(log_table_hint->add_join_filter_hint(stmt, query_hint, join_filter_hint))) { LOG_WARN("failed to add join filter hint", K(ret)); } return ret; } int ObLogPlanHint::get_log_table_hint_for_update(const ObDMLStmt &stmt, const ObQueryHint &query_hint, const ObTableInHint &table, const bool basic_table_only, LogTableHint *&log_table_hint) { int ret = OB_SUCCESS; TableItem *table_item = NULL; log_table_hint = NULL; if (OB_FAIL(query_hint.get_table_item_by_hint_table(stmt, table, table_item))) { LOG_WARN("failed to get table item by hint table", K(ret)); } else if (NULL != table_item && (table_item->is_basic_table() || !basic_table_only)) { for (int64_t i = 0; NULL == log_table_hint && i < table_hints_.count(); ++i) { if (OB_ISNULL(table_hints_.at(i).table_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(i), K(table_hints_)); } else if (table_hints_.at(i).table_->table_id_ == table_item->table_id_) { log_table_hint = &table_hints_.at(i); } } if (NULL == log_table_hint) { if (OB_ISNULL(log_table_hint = table_hints_.alloc_place_holder())) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_ERROR("Allocate log index hint from array error", K(ret)); } else { log_table_hint->table_ = table_item; } } } return ret; } int ObLogPlanHint::add_join_hint(const ObDMLStmt &stmt, const ObQueryHint &query_hint, const ObJoinHint &join_hint) { int ret = OB_SUCCESS; LogJoinHint *log_join_hint = NULL; ObRelIds join_tables; if (OB_FAIL(query_hint.get_relids_from_hint_tables(stmt, join_hint.get_tables(), join_tables))) { LOG_WARN("failed to get relids from hint tables", K(ret), K(join_hint.get_tables())); } else if (join_tables.is_empty()) { LOG_TRACE("get invalid join hint", K(ret), K(join_tables), K(join_hint)); } else { LOG_TRACE("get valid join hint", K(join_hint)); log_join_hint = NULL; for (int64_t i = 0; NULL == log_join_hint && i < join_hints_.count(); ++i) { if (join_tables.equal(join_hints_.at(i).join_tables_)) { log_join_hint = &join_hints_.at(i); } } if (NULL == log_join_hint) { if (OB_ISNULL(log_join_hint = join_hints_.alloc_place_holder())) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_ERROR("Allocate log join hint from array error", K(ret)); } else if (log_join_hint->join_tables_.add_members(join_tables)) { LOG_WARN("failed to add members", K(ret)); } } if (OB_SUCC(ret) && OB_FAIL(log_join_hint->add_join_hint(join_hint))) { LOG_WARN("failed to add join hint", K(ret), K(join_hint)); } } return ret; } // init log join hint for use join hint int ObLogPlanHint::init_log_join_hints() { int ret = OB_SUCCESS; for (int64_t i = 0; OB_SUCC(ret) && i < join_hints_.count(); ++i) { if (OB_FAIL(join_hints_.at(i).init_log_join_hint())) { LOG_WARN("failed to init log join hint", K(ret)); } } return ret; } const ObHint *ObLogPlanHint::get_normal_hint(ObItemType hint_type) const { const ObHint *hint = NULL; for (int64_t i = 0; NULL == hint && i < normal_hints_.count(); ++i) { if (OB_ISNULL(normal_hints_.at(i))) { /* do nothing */ } else if (normal_hints_.at(i)->get_hint_type() == hint_type) { hint = normal_hints_.at(i); } } return hint; } bool ObLogPlanHint::has_enable_hint(ObItemType hint_type) const { const ObHint *cur_hint = get_normal_hint(hint_type); return NULL != cur_hint && cur_hint->is_enable_hint(); } bool ObLogPlanHint::has_disable_hint(ObItemType hint_type) const { const ObHint *cur_hint = get_normal_hint(hint_type); return NULL != cur_hint ? cur_hint->is_disable_hint() : is_outline_data_; } int ObLogPlanHint::get_aggregation_info(bool &force_use_hash, bool &force_use_merge, bool &force_part_sort, bool &force_normal_sort) const { int ret = OB_SUCCESS; force_use_hash = false; force_use_merge = false; force_part_sort = false; force_normal_sort = false; const ObAggHint *agg_hint = static_cast(get_normal_hint(T_USE_HASH_AGGREGATE)); if (NULL != agg_hint) { force_use_hash = agg_hint->is_enable_hint(); force_use_merge = agg_hint->is_disable_hint(); force_part_sort = agg_hint->force_partition_sort(); force_normal_sort = agg_hint->force_normal_sort(); if (force_use_merge && !force_part_sort && !force_normal_sort && is_outline_data_) { force_normal_sort = true; } } else if (is_outline_data_) { force_use_merge = true; force_normal_sort = true; } return ret; } const ObWindowDistHint *ObLogPlanHint::get_window_dist() const { return static_cast( get_normal_hint(T_PQ_DISTRIBUTE_WINDOW)); } const LogTableHint *ObLogPlanHint::get_log_table_hint(uint64_t table_id) const { const LogTableHint *log_table_hint = NULL; for (int64_t i = 0; NULL == log_table_hint && i < table_hints_.count(); ++i) { if (NULL != table_hints_.at(i).table_ && table_hints_.at(i).table_->table_id_ == table_id) { log_table_hint = &table_hints_.at(i); } } return log_table_hint; } const LogTableHint* ObLogPlanHint::get_index_hint(uint64_t table_id) const { const LogTableHint *log_table_hint = get_log_table_hint(table_id); return NULL != log_table_hint && !log_table_hint->index_hints_.empty() ? log_table_hint : NULL; } int64_t ObLogPlanHint::get_parallel(uint64_t table_id) const { int64_t table_parallel = ObGlobalHint::UNSET_PARALLEL; const LogTableHint *log_table_hint = get_log_table_hint(table_id); if (NULL == log_table_hint || NULL == log_table_hint->parallel_hint_) { table_parallel = is_outline_data_ ? ObGlobalHint::DEFAULT_PARALLEL : ObGlobalHint::UNSET_PARALLEL; } else { table_parallel = log_table_hint->parallel_hint_->get_parallel(); } return table_parallel; } int ObLogPlanHint::check_use_das(uint64_t table_id, bool &force_das, bool &force_no_das) const { int ret = OB_SUCCESS; force_das = false; force_no_das = false; const LogTableHint *log_table_hint = get_log_table_hint(table_id); const ObHint *hint = NULL == log_table_hint ? NULL : log_table_hint->use_das_hint_; if (NULL != hint) { force_das = hint->is_enable_hint(); force_no_das = hint->is_disable_hint(); } else if (is_outline_data_) { force_no_das = true; } return ret; } int ObLogPlanHint::check_use_column_store(uint64_t table_id, bool &force_column_store, bool &force_no_column_store) const { int ret = OB_SUCCESS; force_column_store = false; force_no_column_store = false; const LogTableHint *log_table_hint = get_log_table_hint(table_id); const ObHint *hint = NULL == log_table_hint ? NULL : log_table_hint->use_column_store_hint_; if (NULL != hint) { force_column_store = hint->is_enable_hint(); force_no_column_store = hint->is_disable_hint(); } else if (is_outline_data_) { force_no_column_store = true; } return ret; } int ObLogPlanHint::check_use_skip_scan(uint64_t table_id, uint64_t index_id, bool &force_skip_scan, bool &force_no_skip_scan) const { int ret = OB_SUCCESS; force_skip_scan = false; force_no_skip_scan = false; const LogTableHint *log_table_hint = get_log_table_hint(table_id); int64_t pos = OB_INVALID_INDEX; if (NULL != log_table_hint && ObOptimizerUtil::find_item(log_table_hint->index_list_, index_id, &pos)) { const ObIndexHint *hint = NULL; if (OB_UNLIKELY(pos >= log_table_hint->index_hints_.count() || pos < 0) || OB_ISNULL(hint = log_table_hint->index_hints_.at(pos))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected pos", K(ret), K(pos), K(log_table_hint->index_hints_.count()), K(hint)); } else { force_skip_scan = hint->use_skip_scan(); force_no_skip_scan = !force_skip_scan && hint->is_use_index_hint(); } } if (OB_SUCC(ret) && !force_skip_scan && !force_no_skip_scan && is_outline_data_) { force_no_skip_scan = true; } return ret; } const ObTableDynamicSamplingHint *ObLogPlanHint::get_dynamic_sampling_hint(uint64_t table_id) const { const LogTableHint *log_table_hint = get_log_table_hint(table_id); return NULL == log_table_hint ? NULL : log_table_hint->dynamic_sampling_hint_; } int ObLogPlanHint::check_use_join_filter(uint64_t filter_table_id, const ObRelIds &left_tables, bool part_join_filter, bool config_disable, bool &can_use, const ObJoinFilterHint *&force_hint) const { int ret = OB_SUCCESS; const LogTableHint *log_table_hint = get_log_table_hint(filter_table_id); const ObJoinFilterHint *hint = NULL; if (NULL != log_table_hint && OB_FAIL(log_table_hint->get_join_filter_hint(left_tables, part_join_filter, hint))) { LOG_WARN("failed to get join filter hint", K(ret)); } else if (NULL != hint) { can_use = hint->is_enable_hint(); force_hint = can_use ? hint : NULL; } else if (is_outline_data_) { can_use = false; force_hint = NULL; } else { can_use = !config_disable; force_hint = NULL; } return ret; } int ObLogPlanHint::get_pushdown_join_filter_hints(uint64_t filter_table_id, const ObRelIds &left_tables, bool config_disable, JoinFilterPushdownHintInfo& info) const { int ret = OB_SUCCESS; const LogTableHint *log_table_hint = get_log_table_hint(filter_table_id); info.filter_table_id_ = filter_table_id; info.config_disable_ = config_disable; if (NULL == log_table_hint) { } else if(OB_FAIL(log_table_hint->get_join_filter_hints(left_tables, false, info.join_filter_hints_))) { LOG_WARN("failed to get join filter hints", K(ret)); } else if (OB_FAIL(log_table_hint->get_join_filter_hints(left_tables, true, info.part_join_filter_hints_))) { LOG_WARN("failed to get join filter hints", K(ret)); } return ret; } const LogJoinHint *ObLogPlanHint::get_join_hint(const ObRelIds &join_tables) const { const LogJoinHint *log_join_hint = NULL; for (int64_t i = 0; NULL == log_join_hint && i < join_hints_.count(); ++i) { if (join_tables.equal(join_hints_.at(i).join_tables_)) { log_join_hint = &join_hints_.at(i); } } return log_join_hint; } SetAlgo ObLogPlanHint::get_valid_set_algo() const { SetAlgo set_algo = INVALID_SET_ALGO; // default valid, can use MERGE_SET and HASH_SET const ObHint *cur_hint = get_normal_hint(T_USE_HASH_SET); if (NULL == cur_hint) { set_algo = is_outline_data_ ? MERGE_SET : INVALID_SET_ALGO; } else if (cur_hint->is_enable_hint()) { set_algo = HASH_SET; } else if (cur_hint->is_disable_hint()) { set_algo = MERGE_SET; } return set_algo; } DistAlgo ObLogPlanHint::get_valid_set_dist_algo(int64_t *random_none_idx /* default NULL */ ) const { DistAlgo set_dist_algo = DistAlgo::DIST_INVALID_METHOD; const ObPQSetHint *pq_set_hint = static_cast(get_normal_hint(T_PQ_SET)); if (NULL == pq_set_hint) { set_dist_algo = is_outline_data_ ? DistAlgo::DIST_BASIC_METHOD : DistAlgo::DIST_INVALID_METHOD; } else { int64_t idx = OB_INVALID_INDEX; set_dist_algo = pq_set_hint->get_dist_algo(idx); if (NULL != random_none_idx) { *random_none_idx = idx; } } return set_dist_algo; } int ObLogPlanHint::check_valid_set_left_branch(const ObSelectStmt *select_stmt, bool &hint_valid, bool &need_swap) const { int ret = OB_SUCCESS; hint_valid = false; need_swap = false; const ObPQSetHint *pq_set_hint = NULL; ObString left_child; ObString right_child; if (OB_UNLIKELY(!select_stmt->is_set_stmt() || !select_stmt->is_set_distinct() || 2 > select_stmt->get_set_query().count()) || OB_ISNULL(select_stmt->get_set_query(0)) || OB_ISNULL(select_stmt->get_set_query(1))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected set_op stmt", K(ret), K(select_stmt)); } else if (NULL == (pq_set_hint = static_cast(get_normal_hint(T_PQ_SET)))) { hint_valid = is_outline_data_; need_swap = false; } else if (pq_set_hint->get_left_branch().empty()) { hint_valid = true; need_swap = false; } else if (OB_FAIL(select_stmt->get_set_query(0)->get_qb_name(left_child)) || OB_FAIL(select_stmt->get_set_query(1)->get_qb_name(right_child))) { LOG_WARN("failed to get qb name", K(ret)); } else if (0 == pq_set_hint->get_left_branch().case_compare(left_child)) { hint_valid = true; need_swap = false; } else if (0 == pq_set_hint->get_left_branch().case_compare(right_child)) { hint_valid = true; need_swap = true; } else { hint_valid = false; need_swap = false; } return ret; } int ObLogPlanHint::get_valid_pq_subquery_hint(const ObIArray &sub_qb_names, const ObPQSubqueryHint *&explicit_hint, const ObPQSubqueryHint *&implicit_hint) const { int ret = OB_SUCCESS; for (int64_t i = 0; NULL == explicit_hint && i < normal_hints_.count(); ++i) { if (NULL != normal_hints_.at(i) && normal_hints_.at(i)->get_hint_type() == T_PQ_SUBQUERY) { const ObPQSubqueryHint *cur_hint = static_cast(normal_hints_.at(i)); if (NULL == explicit_hint && cur_hint->is_match_subplans(sub_qb_names)) { explicit_hint = cur_hint; } else if (NULL == implicit_hint && cur_hint->get_sub_qb_names().empty()) { implicit_hint = cur_hint; } } } return ret; } DistAlgo ObLogPlanHint::get_valid_pq_subquery_dist_algo(const ObIArray &sub_qb_names, const bool implicit_allowed) const { int ret = OB_SUCCESS; // ignore this ret DistAlgo dist_algo = DistAlgo::DIST_INVALID_METHOD; const ObPQSubqueryHint *explicit_hint = NULL; const ObPQSubqueryHint *implicit_hint = NULL; if (OB_FAIL(get_valid_pq_subquery_hint(sub_qb_names, explicit_hint, implicit_hint))) { LOG_WARN("failed to get valid subplan filter hint", K(ret)); } else if (NULL != explicit_hint) { dist_algo = explicit_hint->get_dist_algo(); } else if (is_outline_data_) { dist_algo = DistAlgo::DIST_BASIC_METHOD; } else if (NULL != implicit_hint && implicit_allowed) { dist_algo = implicit_hint->get_dist_algo(); } return dist_algo; } // generate spm evolution plan, throw a error code when can not get valid hint int ObLogPlanHint::check_status() const { int ret = OB_SUCCESS; #ifdef OB_BUILD_SPM if (is_spm_evolution_) { ret = OB_OUTLINE_NOT_REPRODUCIBLE; LOG_WARN("failed to generate plan use outline data for spm evolution", K(ret)); } #endif return ret; } bool ObLogPlanHint::is_spm_evolution() const { bool bret = false; #ifdef OB_BUILD_SPM bret = is_spm_evolution_; #endif return bret; } int LogLeadingHint::init_leading_info(const ObDMLStmt &stmt, const ObQueryHint &query_hint, const ObHint *hint) { int ret = OB_SUCCESS; reset(); if (NULL == hint) { /* do nothing */ } else if (OB_UNLIKELY(!hint->is_join_order_hint())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpect hint type", K(ret), K(*hint)); } else { ObRelIds table_set; hint_ = static_cast(hint); if (hint_->is_ordered_hint() && OB_FAIL(init_leading_info_from_ordered_hint(stmt))) { LOG_TRACE("failed to init leading info from ordered hint.", K(ret)); } else if (!hint_->is_ordered_hint() && OB_FAIL(init_leading_info_from_leading_hint(stmt, query_hint, hint_->get_table(), table_set))) { LOG_TRACE("failed to init leading info from leading hint.", K(ret)); } else if (NULL == hint_) { leading_tables_.reuse(); leading_infos_.reuse(); } else { LOG_TRACE("succeed to get leading infos", K(*this)); } } return ret; } int LogLeadingHint::init_leading_info_from_leading_hint(const ObDMLStmt &stmt, const ObQueryHint &query_hint, const ObLeadingTable &cur_table, ObRelIds& table_set) { int ret = OB_SUCCESS; LeadingInfo leading_info; if (NULL != cur_table.table_) { int32_t index = OB_INVALID_INDEX; if (OB_FAIL(query_hint.get_table_bit_index_by_hint_table(stmt, *cur_table.table_, index))) { LOG_WARN("failed to get table id", K(ret)); } else if (OB_INVALID_INDEX == index || leading_tables_.has_member(index)) { hint_ = NULL; LOG_WARN("failed to get table bit index by hint table", K(index), K(*cur_table.table_), K(stmt.get_table_items())); } else if (OB_FAIL(leading_tables_.add_member(index)) || OB_FAIL(table_set.add_member(index))) { LOG_WARN("failed to add members", K(ret)); } } else if (OB_ISNULL(cur_table.left_table_) || OB_ISNULL(cur_table.right_table_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpect null.", K(ret), K(cur_table.left_table_), K(cur_table.right_table_)); } else if (OB_FAIL(SMART_CALL(init_leading_info_from_leading_hint(stmt, query_hint, *cur_table.left_table_, leading_info.left_table_set_)))) { LOG_WARN("failed to init leading info from leading", K(ret)); } else if (NULL == hint_) { /* do nothing */ } else if (OB_FAIL(SMART_CALL(init_leading_info_from_leading_hint(stmt, query_hint, *cur_table.right_table_, leading_info.right_table_set_)))) { LOG_WARN("failed to init leading info from leading", K(ret)); } else if (NULL == hint_) { /* do nothing */ } else if (OB_FAIL(leading_info.table_set_.add_members(leading_info.left_table_set_)) || OB_FAIL(leading_info.table_set_.add_members(leading_info.right_table_set_))) { LOG_WARN("failed to add table ids", K(ret)); } else if (OB_FAIL(leading_infos_.push_back(leading_info))) { LOG_WARN("failed to push back hint info", K(ret)); } else if (OB_FAIL(table_set.add_members(leading_info.table_set_))) { LOG_WARN("failed to add table ids", K(ret)); } return ret; } /** * for ordered hint, init leading hint info by the ordering of tables in stmt */ int LogLeadingHint::init_leading_info_from_ordered_hint(const ObDMLStmt &stmt) { int ret = OB_SUCCESS; const ObIArray &from_items = stmt.get_from_items(); const ObIArray &semi_infos = stmt.get_semi_infos(); SemiInfo *semi_info = NULL; ObRelIds leading_tables; const int64_t N = from_items.count() + semi_infos.count(); for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) { LeadingInfo leading_info; if (i < from_items.count()) { const FromItem &from_item = from_items.at(i); TableItem *table_item = from_item.is_joined_ ? stmt.get_joined_table(from_item.table_id_) : stmt.get_table_item_by_id(from_item.table_id_); if (OB_FAIL(init_leading_info_from_table(stmt, leading_infos_, table_item, leading_info.right_table_set_))) { LOG_WARN("failed to init leading infos from table.", K(ret)); } } else if (OB_ISNULL(semi_info = semi_infos.at(i - from_items.count()))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpect null semi info", K(ret)); } else if (OB_FAIL(leading_info.right_table_set_.add_member( stmt.get_table_bit_index(semi_info->right_table_id_)))) { LOG_WARN("failed to add members", K(ret)); } if (i == 0) { ret = leading_tables.add_members(leading_info.right_table_set_); } else if (OB_FAIL(leading_info.left_table_set_.add_members(leading_tables))) { LOG_WARN("failed to add table ids", K(ret)); } else if (OB_FAIL(leading_tables.add_members(leading_info.right_table_set_))) { LOG_WARN("failed to get table ids", K(ret)); } else if (OB_FAIL(leading_info.table_set_.add_members(leading_tables))) { LOG_WARN("failed to add table ids", K(ret)); } else if (OB_FAIL(leading_infos_.push_back(leading_info))) { LOG_WARN("failed to push back hint info", K(ret)); } } if (OB_SUCC(ret) && OB_FAIL(leading_tables_.add_members(leading_tables))) { LOG_WARN("failed to get table ids", K(ret)); } return ret; } /** * init leading hint info for table item */ int LogLeadingHint::init_leading_info_from_table(const ObDMLStmt &stmt, ObIArray &leading_infos, TableItem *table, ObRelIds &table_set) { int ret = OB_SUCCESS; if (OB_ISNULL(table)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpect null table item", K(ret)); } else if (!table->is_joined_table()) { if (OB_FAIL(table_set.add_member(stmt.get_table_bit_index(table->table_id_)))) { LOG_WARN("failed to add members", K(ret)); } } else { LeadingInfo leading_info; JoinedTable *joined_table = static_cast(table); if (OB_FAIL(SMART_CALL(init_leading_info_from_table(stmt, leading_infos, joined_table->left_table_, leading_info.left_table_set_)))) { LOG_WARN("failed to get leading hint info", K(ret)); } else if (OB_FAIL(SMART_CALL(init_leading_info_from_table(stmt, leading_infos, joined_table->right_table_, leading_info.right_table_set_)))) { LOG_WARN("failed to get leading hint info", K(ret)); } else if (OB_FAIL(leading_info.table_set_.add_members(leading_info.left_table_set_))) { LOG_WARN("failed to add members", K(ret)); } else if (OB_FAIL(leading_info.table_set_.add_members(leading_info.right_table_set_))) { LOG_WARN("failed to add members", K(ret)); } else if (OB_FAIL(table_set.add_members(leading_info.table_set_))) { LOG_WARN("failed to add members", K(ret)); } else if (OB_FAIL(leading_infos.push_back(leading_info))) { LOG_WARN("failed to push back hint info", K(ret)); } } return ret; } int LogJoinHint::assign(const LogJoinHint &other) { int ret = OB_SUCCESS; local_methods_ = other.local_methods_; dist_methods_ = other.dist_methods_; slave_mapping_ = other.slave_mapping_; nl_material_ = other.nl_material_; join_tables_ = other.join_tables_; if (OB_FAIL(local_method_hints_.assign(other.local_method_hints_))) { LOG_WARN("fail to assign local method hints", K(ret)); } else if (OB_FAIL(dist_method_hints_.assign(other.dist_method_hints_))) { LOG_WARN("fail to assign dist method hints", K(ret)); } return ret; } int LogJoinHint::add_join_hint(const ObJoinHint &join_hint) { int ret = OB_SUCCESS; switch(join_hint.get_hint_type()) { case T_USE_NL: case T_USE_MERGE: case T_USE_HASH: { if (OB_FAIL(local_method_hints_.push_back(&join_hint))) { LOG_WARN("fail to push back join hint", K(ret), K(join_hint)); } break; } case T_USE_NL_MATERIALIZATION: { //if both use_material and no_use_material, no_use_material worked. if (NULL == nl_material_ || (join_hint.is_disable_hint() && nl_material_->is_enable_hint())) { nl_material_ = &join_hint; } break; } case T_PQ_DISTRIBUTE: { if (dist_methods_ & join_hint.get_dist_algo()) { /* do nothing */ } else if (OB_FAIL(dist_method_hints_.push_back(&join_hint))) { LOG_WARN("fail to push back join hint", K(ret), K(join_hint)); } else { dist_methods_ |= join_hint.get_dist_algo(); } break; } case T_PQ_MAP: { if (NULL == slave_mapping_) { slave_mapping_ = &join_hint; } break; } default: { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected join hint type", K(ret), K(join_hint)); } } return ret; } int LogJoinHint::init_log_join_hint() { int ret = OB_SUCCESS; local_methods_ = JoinAlgo::INVALID_JOIN_ALGO; ObSEArray all_hints; if (OB_FAIL(all_hints.assign(local_method_hints_))) { LOG_WARN("fail to assign local method join hints", K(ret)); } else { #define ADD_USE_JOIN_HINT(join_algo) { \ int64_t &cur_methods = join_hint->is_enable_hint() ? use_methods : no_use_methods; \ if (!(cur_methods & join_algo) && OB_FAIL(local_method_hints_.push_back(join_hint))) { \ LOG_WARN("fail to push back join hint", K(ret), K(*join_hint)); \ } else { cur_methods |= join_algo; }} \ local_method_hints_.reuse(); int64_t use_methods = JoinAlgo::INVALID_JOIN_ALGO; int64_t no_use_methods = JoinAlgo::INVALID_JOIN_ALGO; const ObJoinHint *join_hint = NULL; for (int64_t i = 0; OB_SUCC(ret) && local_method_hints_.count() < 6 && i < all_hints.count(); ++i) { if (OB_ISNULL(join_hint = all_hints.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected NULL", K(ret), K(all_hints)); } else { switch(join_hint->get_hint_type()) { case T_USE_NL: ADD_USE_JOIN_HINT(NESTED_LOOP_JOIN); break; case T_USE_HASH: ADD_USE_JOIN_HINT(HASH_JOIN); break; case T_USE_MERGE: ADD_USE_JOIN_HINT(MERGE_JOIN); break; default: { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected join hint type", K(ret), K(join_hint)); } } } } if (OB_SUCC(ret)) { use_methods = JoinAlgo::INVALID_JOIN_ALGO == use_methods ? NESTED_LOOP_JOIN | HASH_JOIN | MERGE_JOIN : use_methods; local_methods_ = use_methods & ~no_use_methods; LOG_DEBUG("finish init local methods", K(local_methods_), K(all_hints), K(local_method_hints_)); } } return ret; } int LogTableHint::assign(const LogTableHint &other) { int ret = OB_SUCCESS; table_ = other.table_; parallel_hint_ = other.parallel_hint_; use_das_hint_ = other.use_das_hint_; use_column_store_hint_ = other.use_column_store_hint_; if (OB_FAIL(index_list_.assign(other.index_list_))) { LOG_WARN("failed to assign index list", K(ret)); } else if (OB_FAIL(index_hints_.assign(other.index_hints_))) { LOG_WARN("failed to assign hints", K(ret)); } else if (OB_FAIL(join_filter_hints_.assign(other.join_filter_hints_))) { LOG_WARN("failed to assign index list", K(ret)); } else if (OB_FAIL(left_tables_.assign(other.left_tables_))) { LOG_WARN("failed to assign hints", K(ret)); } return ret; } int LogTableHint::init_index_hints(ObSqlSchemaGuard &schema_guard) { int ret = OB_SUCCESS; uint64_t tids[OB_MAX_INDEX_PER_TABLE + 1]; int64_t table_index_count = OB_MAX_INDEX_PER_TABLE + 1; if (OB_ISNULL(table_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected log index hint", K(ret), K(this)); } else if (index_hints_.empty()) { /* do nothing */ } else if (OB_FAIL(schema_guard.get_can_read_index_array(table_->ref_id_, tids, table_index_count, false, table_->access_all_part(), false /*domain index*/, false /*spatial index*/))) { LOG_WARN("failed to get can read index", K(ret)); } else if (table_index_count > OB_MAX_INDEX_PER_TABLE) { ret = OB_ERR_UNEXPECTED; LOG_WARN("Table index count is bigger than OB_MAX_INDEX_PER_TABLE", K(ret), K(table_index_count)); } else { LOG_TRACE("get readable index", K(table_index_count)); const share::schema::ObTableSchema *index_schema = NULL; ObSEArray index_list; ObSEArray no_index_list; ObSEArray index_hints; ObSEArray no_index_hints; for (int64_t i = -1; OB_SUCC(ret) && i < table_index_count; ++i) { uint64_t index_id = -1 == i ? table_->ref_id_ : tids[i]; ObString index_name; bool is_primary_key = false; if (-1 == i) { is_primary_key = true; index_name = ObIndexHint::PRIMARY_KEY; } else if (OB_FAIL(schema_guard.get_table_schema(index_id, index_schema)) || OB_ISNULL(index_schema)) { ret = OB_SCHEMA_ERROR; LOG_WARN("fail to get table schema", K(index_id), K(ret)); } else if (index_schema->is_domain_index()) { // just ignore domain index } else if (OB_FAIL(index_schema->get_index_name(index_name))) { LOG_WARN("fail to get index name", K(index_name), K(ret)); } if (OB_SUCC(ret) && (!index_name.empty())) { int64_t no_index_hint_pos = OB_INVALID_INDEX; int64_t index_hint_pos = OB_INVALID_INDEX; int64_t index_ss_hint_pos = OB_INVALID_INDEX; const uint64_t N = index_hints_.count(); const ObIndexHint *index_hint = NULL; for (int64_t hint_i = 0; OB_SUCC(ret) && hint_i < N; ++hint_i) { if (OB_ISNULL(index_hint = index_hints_.at(hint_i)) || OB_UNLIKELY(!index_hint->is_access_path_hint())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null index hint", K(ret), K(index_hint)); } else if (is_primary_key && T_FULL_HINT == index_hint->get_hint_type()) { index_hint_pos = hint_i; } else if (0 != index_hint->get_index_name().case_compare(index_name)) { /* do nothing */ } else if (T_NO_INDEX_HINT == index_hint->get_hint_type()) { no_index_hint_pos = hint_i; } else if (T_INDEX_SS_HINT == index_hint->get_hint_type()) { index_ss_hint_pos = hint_i; } else { index_hint_pos = hint_i; } } if (OB_FAIL(ret)) { } else if (OB_INVALID_INDEX != no_index_hint_pos && (OB_INVALID_INDEX != index_ss_hint_pos || OB_INVALID_INDEX != index_hint_pos)) { /* conflict full/index/index_ss and no_index hint*/ } else if (OB_INVALID_INDEX != no_index_hint_pos) { if (OB_FAIL(no_index_list.push_back(index_id))) { LOG_WARN("fail to push back", K(ret), K(index_id)); } else if (OB_FAIL(no_index_hints.push_back(index_hints_.at(no_index_hint_pos)))) { LOG_WARN("fail to push back", K(ret), K(no_index_hint_pos)); } } else if (OB_INVALID_INDEX != index_ss_hint_pos || OB_INVALID_INDEX != index_hint_pos) { int64_t hint_pos = OB_INVALID_INDEX != index_ss_hint_pos ? index_ss_hint_pos : index_hint_pos; if (OB_FAIL(index_list.push_back(index_id))) { LOG_WARN("fail to push back", K(ret), K(index_id)); } else if (OB_FAIL(index_hints.push_back(index_hints_.at(hint_pos)))) { LOG_WARN("fail to push back", K(ret), K(hint_pos)); } } } } if (OB_SUCC(ret)) { if (!index_list.empty()) { if (OB_FAIL(index_list_.assign(index_list))) { LOG_WARN("failed to assign array", K(ret)); } else if (OB_FAIL(index_hints_.assign(index_hints))) { LOG_WARN("failed to assign array", K(ret)); } } else if (OB_FAIL(index_list_.assign(no_index_list))) { LOG_WARN("failed to assign array", K(ret)); } else if (OB_FAIL(index_hints_.assign(no_index_hints))) { LOG_WARN("failed to assign array", K(ret)); } } } return ret; } int LogTableHint::get_join_filter_hint(const ObRelIds &left_tables, bool part_join_filter, const ObJoinFilterHint *&hint) const { int ret = OB_SUCCESS; hint = NULL; if (OB_UNLIKELY(left_tables.is_empty()) || OB_UNLIKELY(left_tables_.count() != join_filter_hints_.count())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected params", K(ret), K(left_tables), K(left_tables_), K(join_filter_hints_)); } else { const ObJoinFilterHint *cur_hint = NULL; bool finish = false; for (int64_t i = 0; OB_SUCC(ret) && !finish && i < left_tables_.count(); ++i) { if (OB_ISNULL(cur_hint = join_filter_hints_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret)); } else if (cur_hint->is_part_join_filter_hint() != part_join_filter) { /* do nothing */ } else if (NULL == hint && left_tables_.at(i).is_empty()) { hint = cur_hint; } else if (left_tables.equal(left_tables_.at(i))) { hint = cur_hint; finish = true; } } } return ret; } int LogTableHint::get_join_filter_hints(const ObRelIds &left_tables, bool part_join_filter, ObIArray &hints) const { int ret = OB_SUCCESS; if (OB_UNLIKELY(left_tables.is_empty()) || OB_UNLIKELY(left_tables_.count() != join_filter_hints_.count())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected params", K(ret), K(left_tables), K(left_tables_), K(join_filter_hints_)); } else { const ObJoinFilterHint *cur_hint = NULL; for (int64_t i = 0; OB_SUCC(ret) && i < left_tables_.count(); ++i) { if (OB_ISNULL(cur_hint = join_filter_hints_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret)); } else if (cur_hint->is_part_join_filter_hint() != part_join_filter) { /* do nothing */ } else if (!left_tables.equal(left_tables_.at(i))) { /* do nothing */ } else if (OB_FAIL(hints.push_back(cur_hint))) { LOG_WARN("failed to push back hints", K(ret)); } } } return ret; } int LogTableHint::add_join_filter_hint(const ObDMLStmt &stmt, const ObQueryHint &query_hint, const ObJoinFilterHint &hint) { int ret = OB_SUCCESS; ObRelIds left_tables; const bool has_left_tables = hint.has_left_tables(); if (OB_UNLIKELY(left_tables_.count() != join_filter_hints_.count())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected params", K(ret), K(left_tables_), K(join_filter_hints_)); } else if (has_left_tables && OB_FAIL(query_hint.get_relids_from_hint_tables(stmt, hint.get_left_tables(), left_tables))) { LOG_WARN("failed to get relids from hint tables", K(ret), K(hint.get_left_tables())); } else if (has_left_tables && left_tables.is_empty()) { LOG_TRACE("get invalid join hint", K(ret), K(left_tables), K(hint)); } else { bool added = false; for (int64_t i = 0; OB_SUCC(ret) && !added && i < left_tables_.count(); ++i) { if (OB_ISNULL(join_filter_hints_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected params", K(ret), K(join_filter_hints_.at(i))); } else if (join_filter_hints_.at(i)->get_hint_type() == hint.get_hint_type() && has_left_tables == join_filter_hints_.at(i)->has_left_tables() && left_tables.equal(left_tables_.at(i)) && join_filter_hints_.at(i)->get_pushdown_filter_table().equal( hint.get_pushdown_filter_table())) { added = true; if (hint.is_disable_hint()) { join_filter_hints_.at(i) = &hint; } } } if (OB_FAIL(ret) || added) { } else if (OB_FAIL(left_tables_.push_back(left_tables))) { LOG_TRACE("failed to push back", K(ret), K(hint), K(left_tables)); } else if (OB_FAIL(join_filter_hints_.push_back(&hint))) { LOG_TRACE("failed to push back", K(ret), K(hint)); } } return ret; } int LogTableHint::allowed_skip_scan(const uint64_t index_id, bool &allowed) const { int ret = OB_SUCCESS; allowed = false; if (!is_use_index_hint()) { /* do nothing */ } else if (OB_UNLIKELY(index_list_.count() != index_hints_.count())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected count", K(ret), K(index_list_.count()), K(index_hints_.count())); } else { bool find = false; for (int64_t i = 0; OB_SUCC(ret) && !find && i < index_list_.count(); ++i) { if (index_list_.at(i) != index_id) { /* do nothing */ } else if (OB_ISNULL(index_hints_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected null", K(ret), K(i), K(index_hints_)); } else { allowed = T_INDEX_SS_HINT == index_hints_.at(i)->get_hint_type(); find = true; } } } return ret; } int JoinFilterPushdownHintInfo::check_use_join_filter(const ObDMLStmt &stmt, const ObQueryHint &query_hint, uint64_t filter_table_id, bool part_join_filter, bool &can_use, const ObJoinFilterHint *&force_hint) const { int ret = OB_SUCCESS; const ObIArray &join_filters = part_join_filter ? part_join_filter_hints_ : join_filter_hints_; const TableItem* table_item; ObString qb_name; const ObJoinFilterHint* current_hint = NULL; bool found = false; if (OB_ISNULL(table_item = stmt.get_table_item_by_id(filter_table_id))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("failed to get filter table from stmt", K(ret)); } else if(OB_FAIL(stmt.get_qb_name(qb_name))) { LOG_WARN("failed to get qb name", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && !found && i < join_filters.count(); ++i) { const ObJoinFilterHint *hint; if (OB_ISNULL(hint = join_filters.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (0 != hint->get_pushdown_filter_table().qb_name_.case_compare(qb_name)) { } else if (hint->get_pushdown_filter_table().is_match_table_item(query_hint.cs_type_, *table_item)) { current_hint = hint; if (hint->is_disable_hint()) { found = true; } } } if (OB_FAIL(ret)) { } else if (NULL != current_hint) { can_use = current_hint->is_enable_hint(); force_hint = can_use ? current_hint : NULL; } else if (query_hint.has_outline_data()) { can_use = false; force_hint = NULL; } else { can_use = !config_disable_; force_hint = NULL; } return ret; } }//end of namespace sql }//end of namespace oceanbase