/** * 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 "share/schema/ob_part_mgr_util.h" #include "sql/resolver/ddl/ob_analyze_stmt_resolver.h" #include "sql/resolver/ddl/ob_analyze_stmt.h" #include "sql/session/ob_sql_session_info.h" #include "pl/sys_package/ob_dbms_stats.h" #include "share/stat/ob_dbms_stats_utils.h" using namespace oceanbase::common; using namespace oceanbase::share::schema; namespace oceanbase { namespace sql { ObAnalyzeStmtResolver::ObAnalyzeStmtResolver(ObResolverParams ¶ms) : ObDDLResolver(params) { // TODO Auto-generated constructor stub } ObAnalyzeStmtResolver::~ObAnalyzeStmtResolver() { // TODO Auto-generated destructor stub } int ObAnalyzeStmtResolver::resolve(const ParseNode &parse_tree) { int ret = OB_SUCCESS; ObAnalyzeStmt *analyze_stmt = NULL; uint64_t parallel_degree = 1; if (OB_ISNULL(session_info_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("session info should not be null", K(ret)); } else if (OB_ISNULL(analyze_stmt = create_stmt())) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_ERROR("failed to create analyze stmt", K(ret)); } else if (FALSE_IT(analyze_stmt->set_tenant_id(session_info_->get_effective_tenant_id()))) { /*do nothing*/ } else if (OB_FAIL(session_info_->get_force_parallel_query_dop(parallel_degree))) { LOG_WARN("failed to get force parallel query dop", K(ret)); } else if (T_ANALYZE == parse_tree.type_) { if (lib::is_mysql_mode() && !session_info_->is_enable_sql_extension()) { ret = OB_NOT_SUPPORTED; LOG_USER_ERROR(OB_NOT_SUPPORTED, "The Oracle-mode analyze syntax is used in the disable sql extension MySQL-mode"); } else if (OB_FAIL(resolve_oracle_analyze(parse_tree, *analyze_stmt))) { LOG_WARN("failed to resolve oracle analyze stmt", K(ret)); } else { /*do nothing*/ } } else if (T_MYSQL_UPDATE_HISTOGRAM == parse_tree.type_) { if (OB_FAIL(resolve_mysql_update_histogram(parse_tree, *analyze_stmt))) { LOG_WARN("failed to resolve mysql update histogram info", K(ret)); } else { /*do nothing*/ } } else if (T_MYSQL_DROP_HISTOGRAM == parse_tree.type_) { // TODO link.zt drop is not supported at the present analyze_stmt->set_is_drop(); if (OB_FAIL(resolve_mysql_delete_histogram(parse_tree, *analyze_stmt))) { LOG_WARN("failed to resolve mysql update histogram info", K(ret)); } else { /*do nothing*/ } } else { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpcted parse tree type", K(parse_tree.type_), K(ret)); } if (OB_SUCC(ret)) { analyze_stmt->set_degree(parallel_degree); stmt_ = analyze_stmt; LOG_DEBUG("analyze statement", K(*analyze_stmt)); } return ret; } int ObAnalyzeStmtResolver::resolve_oracle_analyze(const ParseNode &parse_node, ObAnalyzeStmt &analyze_stmt) { int ret = OB_SUCCESS; if (OB_UNLIKELY(3 != parse_node.num_child_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("should have 3 children", K(parse_node.num_child_), K(ret)); } else { ParseNode *table_node = parse_node.children_[0]; ParseNode *part_node = parse_node.children_[1]; ParseNode *statistic_node = parse_node.children_[2]; if (OB_ISNULL(table_node) || OB_ISNULL(statistic_node)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("null parse node", K(table_node), K(statistic_node), K(ret)); } else if (OB_FAIL(resolve_table_info(table_node, analyze_stmt))) { LOG_WARN("failed to resolve table info", K(ret)); } else if (OB_FAIL(resolve_partition_info(part_node, analyze_stmt))) { LOG_WARN("failed to resolve partition info", K(ret)); } else if (OB_FAIL(resolve_statistic_info(statistic_node, analyze_stmt))) { LOG_WARN("failed to resolve statistic info", K(ret)); } else { /*do nothing*/ } } return ret; } int ObAnalyzeStmtResolver::resolve_mysql_update_histogram(const ParseNode &parse_node, ObAnalyzeStmt &analyze_stmt) { int ret = OB_SUCCESS; if (OB_UNLIKELY(3 != parse_node.num_child_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("should have 3 children", K(parse_node.num_child_), K(ret)); } else { ParseNode *table_node = parse_node.children_[0]; ParseNode *column_node = parse_node.children_[1]; ParseNode *bucket_node = parse_node.children_[2]; if (OB_ISNULL(table_node) || OB_ISNULL(column_node) || OB_ISNULL(bucket_node)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("null parse node", K(table_node), K(column_node), K(bucket_node), K(ret)); } else if (OB_FAIL(resolve_table_info(table_node, analyze_stmt))) { LOG_WARN("failed to resolve table info", K(ret)); } else if (OB_FAIL(resolve_partition_info(NULL, analyze_stmt))) { LOG_WARN("failed to resolve partition info", K(ret)); } else if (OB_FAIL(resolve_mysql_column_bucket_info(column_node, bucket_node->value_, analyze_stmt))) { LOG_WARN("failed to resolve column bucket info", K(ret)); } else { ObAnalyzeSampleInfo sample_info; sample_info.is_sample_ = true; sample_info.sample_type_ = SampleType::RowSample; sample_info.sample_value_ = bucket_node->value_ * DEFAULT_SAMPLE_ROWCOUNT_PER_BUCKET; analyze_stmt.set_sample_info(sample_info); } } return ret; } int ObAnalyzeStmtResolver::resolve_mysql_delete_histogram(const ParseNode &parse_node, ObAnalyzeStmt &analyze_stmt) { int ret = OB_SUCCESS; if (OB_UNLIKELY(2 != parse_node.num_child_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("should have 2 children", K(parse_node.num_child_), K(ret)); } else { ParseNode *table_node = parse_node.children_[0]; ParseNode *column_node = parse_node.children_[1]; if (OB_ISNULL(table_node) || OB_ISNULL(column_node)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("null parse node", K(ret)); } else if (OB_FAIL(resolve_table_info(table_node, analyze_stmt))) { LOG_WARN("failed to resolve table info", K(ret)); } else if (OB_FAIL(resolve_partition_info(NULL, analyze_stmt))) { LOG_WARN("failed to resolve partition info", K(ret)); } else if (OB_FAIL(resolve_mysql_column_bucket_info(column_node, 0, analyze_stmt))) { LOG_WARN("failed to resolve column bucket info", K(ret)); } else { /*do nothing*/ } } return ret; } int ObAnalyzeStmtResolver::resolve_mysql_column_bucket_info(const ParseNode *column_node, const int64_t bucket_number, ObAnalyzeStmt &analyze_stmt) { int ret = OB_SUCCESS; const ObTableSchema *table_schema = NULL; if (OB_ISNULL(column_node) || OB_ISNULL(schema_checker_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid params", K(ret), K(column_node), K(schema_checker_)); } else if (OB_FAIL(schema_checker_->get_table_schema(analyze_stmt.get_tenant_id(), analyze_stmt.get_table_id(), table_schema))) { LOG_WARN("failed to get table schema", K(analyze_stmt.get_tenant_id()), K(analyze_stmt.get_table_id()), K(ret)); } else if (OB_ISNULL(table_schema)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("null table schema", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < column_node->num_child_; i++) { const ObColumnSchemaV2 *column_schema = NULL; ObColumnStatParam *col_param = NULL; ObString column_name; if (OB_ISNULL(column_node->children_[i])) { ret = OB_ERR_UNEXPECTED; LOG_WARN("null parse node", K(ret)); } else { column_name.assign_ptr(const_cast(column_node->children_[i]->str_value_), static_cast(column_node->children_[i]->str_len_)); if (OB_ISNULL(column_schema = table_schema->get_column_schema(column_name))) { ret = OB_ERR_COLUMN_NOT_FOUND; LOG_WARN("failed to get column schema", K(column_name), K(ret)); } else if (OB_ISNULL(col_param = analyze_stmt.get_column_param(column_schema->get_column_id()))) { // do nothing } else if (col_param->is_valid_opt_col()) { col_param->set_need_basic_stat(); col_param->bucket_num_ = bucket_number; col_param->set_size_manual(); } } } return ret; } int ObAnalyzeStmtResolver::resolve_table_info(const ParseNode *table_node, ObAnalyzeStmt &analyze_stmt) { int ret = OB_SUCCESS; ObString table_name; ObString database_name; const ObTableSchema *table_schema = NULL; int64_t tenant_id = analyze_stmt.get_tenant_id(); uint64_t database_id = OB_INVALID_ID; if (OB_ISNULL(table_node) || OB_ISNULL(schema_checker_)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("null point", K(table_node), K(schema_checker_), K(ret)); } else if (OB_FAIL(resolve_table_relation_node(table_node, table_name, database_name))) { LOG_WARN("failed to resolve table relation node", K(ret)); } else if (OB_FAIL(schema_checker_->get_database_id(tenant_id, database_name, database_id))) { LOG_WARN("failed to get database id", K(ret)); } else if (OB_FAIL(schema_checker_->get_table_schema(tenant_id, database_name, table_name, false, table_schema))){ LOG_WARN("failed to get table schema", K(ret)); } else if (OB_ISNULL(table_schema)) { LOG_WARN("null table schema", K(ret)); } else if (OB_FAIL(pl::ObDbmsStats::init_column_stat_params(*allocator_, *schema_checker_->get_schema_guard(), *table_schema, analyze_stmt.get_column_params()))) { LOG_WARN("failed to init column stat param", K(ret)); } else { analyze_stmt.set_table(database_name, database_id, table_name, table_schema->get_table_id(), table_schema->get_table_type()); } return ret; } int ObAnalyzeStmtResolver::resolve_partition_info(const ParseNode *part_node, ObAnalyzeStmt &analyze_stmt) { int ret = OB_SUCCESS; const ObTableSchema *table_schema = NULL; int64_t table_id = analyze_stmt.get_table_id(); ObSEArray part_infos; ObSEArray subpart_infos; ObSEArray part_ids; ObSEArray subpart_ids; bool is_subpart_name = false; ObString &partition_name = analyze_stmt.get_partition_name(); if (OB_ISNULL(schema_checker_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("null schema checker", K(schema_checker_), K(ret)); } else if (OB_FAIL(schema_checker_->get_table_schema(analyze_stmt.get_tenant_id(), table_id, table_schema))) { LOG_WARN("failed to get table schema", K(analyze_stmt.get_tenant_id()), K(table_id), K(ret)); } else if (OB_ISNULL(table_schema)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("null table schema", K(ret)); } else if (OB_FAIL(ObDbmsStatsUtils::get_part_infos(*table_schema, part_infos, subpart_infos, part_ids, subpart_ids))) { LOG_WARN("failed to get part infos", K(ret)); } else if (OB_FAIL(analyze_stmt.set_part_ids(part_ids))) { LOG_WARN("failed to set part infos", K(ret)); } else if (OB_FAIL(analyze_stmt.set_subpart_ids(subpart_ids))) { LOG_WARN("failed to set subpart infos", K(ret)); } else if (OB_FAIL(analyze_stmt.set_all_part_infos(part_infos))) { LOG_WARN("failed to set part infos", K(ret)); } else if (OB_FAIL(analyze_stmt.set_all_subpart_infos(subpart_infos))) { LOG_WARN("failed to set subpart infos", K(ret)); } else if (NULL == part_node) { analyze_stmt.set_part_level(table_schema->get_part_level()); analyze_stmt.set_part_cnt(0); if (OB_FAIL(analyze_stmt.set_part_infos(part_infos))) { LOG_WARN("failed to set part infos", K(ret)); } else if (OB_FAIL(analyze_stmt.set_subpart_infos(subpart_infos))) { LOG_WARN("failed to set subpart infos", K(ret)); } } else if (is_virtual_table(table_id) && table_schema->get_part_option().get_part_num() > 1) { ret = OB_ERR_UNEXPECTED; LOG_WARN("virtual table should not be partitioned", K(table_id), K(table_schema->get_part_option().get_part_num()), K(ret)); } else if (part_node->num_child_ != 1 || OB_ISNULL(part_node->children_[0])) { ret = OB_ERR_UNEXPECTED; LOG_WARN("part node should have only one non-null children", K(part_node->num_child_), K(part_node->children_[0]), K(ret)); } else { const ParseNode *name_list = part_node->children_[0]; analyze_stmt.set_part_level(table_schema->get_part_level()); analyze_stmt.set_part_cnt(0); for (int64_t i = 0; OB_SUCC(ret) && i < name_list->num_child_; i++) { partition_name.assign_ptr(name_list->children_[i]->str_value_, static_cast(name_list->children_[i]->str_len_)); if (ObCharset::case_insensitive_equal(partition_name, table_schema->get_table_name())) { // do nothing partition_name.reset(); } else if (OB_FAIL(pl::ObDbmsStats::find_selected_part_infos(partition_name, part_infos, subpart_infos, false, analyze_stmt.get_part_infos(), analyze_stmt.get_subpart_infos(), is_subpart_name))) { LOG_WARN("failed to find selected part infos", K(ret)); } } } return ret; } int ObAnalyzeStmtResolver::resolve_statistic_info(const ParseNode *statistic_node, ObAnalyzeStmt &analyze_stmt) { int ret = OB_SUCCESS; if (OB_ISNULL(statistic_node)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("null point", K(statistic_node), K(ret)); } else if (T_ANALYZE_STATISTICS != statistic_node->type_ || 2 != statistic_node->num_child_) { ret = OB_ERR_UNEXPECTED; LOG_WARN("not valid statistic node", K(statistic_node->type_), K(statistic_node->num_child_), K(ret)); } else { const ParseNode *for_clause_node = statistic_node->children_[0]; const ParseNode *sample_clause_node = statistic_node->children_[1]; if (OB_FAIL(resolve_for_clause_info(for_clause_node, analyze_stmt))) { LOG_WARN("failed to resolve for clause info", K(ret)); } else if (OB_FAIL(resolve_sample_clause_info(sample_clause_node, analyze_stmt))) { LOG_WARN("failed to resolve sample clause info", K(ret)); } else { /*do nothing*/ } } return ret; } int ObAnalyzeStmtResolver::resolve_for_clause_info(const ParseNode *for_clause_node, ObAnalyzeStmt &analyze_stmt) { int ret = OB_SUCCESS; const ObTableSchema *table_schema = NULL; if (OB_ISNULL(schema_checker_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("null schema checker", K(schema_checker_), K(ret)); } else if (OB_FAIL(schema_checker_->get_table_schema(analyze_stmt.get_tenant_id(), analyze_stmt.get_table_id(), table_schema))) { LOG_WARN("failed to get table schema", K(analyze_stmt.get_tenant_id()), K(analyze_stmt.get_table_id()), K(ret)); } else if (OB_ISNULL(table_schema)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("null table schema", K(ret)); } else if (NULL == for_clause_node) { if (OB_FAIL(pl::ObDbmsStats::set_default_column_params(analyze_stmt.get_column_params()))) { LOG_WARN("failed to set default column params", K(ret)); } } else { for (int64_t i = 0; OB_SUCC(ret) && i < for_clause_node->num_child_; i++) { if (OB_FAIL(resolve_for_clause_element(for_clause_node->children_[i], analyze_stmt))) { LOG_WARN("failed to resolve for clause element", K(ret)); } else { /*do nothing*/ } } } return ret; } int ObAnalyzeStmtResolver::resolve_for_clause_element(const ParseNode *for_clause_node, ObAnalyzeStmt &analyze_stmt) { int ret = OB_SUCCESS; ObSEArray all_for_col; if (OB_ISNULL(for_clause_node)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("null parse node", K(ret)); } else if (T_ANALYZE_TABLE == for_clause_node->type_) { analyze_stmt.set_statistic_scope(StatisticType::TableStatistics); ret = OB_NOT_SUPPORTED; LOG_WARN("analyze table not supported yet", K(ret)); LOG_USER_ERROR(OB_NOT_SUPPORTED, "analyze table"); } else if (T_FOR_ALL == for_clause_node->type_) { if (OB_FAIL(pl::ObDbmsStats::parser_for_all_clause(for_clause_node, analyze_stmt.get_column_params()))) { LOG_WARN("failed to resolve for all clause", K(ret)); } } else if (T_FOR_COLUMNS == for_clause_node->type_) { if (OB_FAIL(pl::ObDbmsStats::parser_for_columns_clause(for_clause_node, analyze_stmt.get_column_params(), all_for_col))) { LOG_WARN("failed to parser for columns clause", K(ret)); } } else { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected node type", K(for_clause_node->type_), K(ret)); } return ret; } int ObAnalyzeStmtResolver::resolve_sample_clause_info(const ParseNode *sample_clause_node, ObAnalyzeStmt &analyze_stmt) { int ret = OB_SUCCESS; if (NULL == sample_clause_node) { /*do nothing*/ } else if (T_ANALYZE_SAMPLE_INFO != sample_clause_node->type_ || 2 != sample_clause_node->num_child_){ ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid sample clause node", K(sample_clause_node->type_), K(sample_clause_node->num_child_), K(ret)); } else { ObAnalyzeSampleInfo sample_info; sample_info.is_sample_ = true; const ParseNode *row_node = sample_clause_node->children_[0]; const ParseNode *sample_option_node = sample_clause_node->children_[1]; if (OB_ISNULL(row_node) || OB_ISNULL(sample_option_node)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("null parse node", K(row_node), K(sample_option_node), K(ret)); } else if (sample_info.sample_type_ == SampleType::PercentSample && (sample_info.sample_value_ < 1 || sample_info.sample_value_ > 99)) { ret = OB_ERR_INVALID_PERCENTAGE; LOG_WARN("invalid percenate", K(ret), K(sample_info.sample_value_)); } else { sample_info.set_is_block_sample(false); if (0 == sample_option_node->value_) { // TODO link.zt should I use block sampling? sample_info.set_rows(row_node->value_); } else { sample_info.set_percent(row_node->value_); } analyze_stmt.set_sample_info(sample_info); } } return ret; } int ObAnalyzeStmtResolver::get_bucket_size(const ParseNode *node, int64_t &bucket_num) { int ret = OB_SUCCESS; if (NULL == node) { // do nothing } else if (T_INT != node->type_) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected node type", K(ret)); } else { bucket_num = node->value_; } return ret; } } /* namespace sql */ } /* namespace oceanbase */