/** * Copyright (c) 2021 OceanBase * OceanBase CE is licensed under Mulan PubL v2. * You can use this software according to the terms and conditions of the Mulan PubL v2. * You may obtain a copy of Mulan PubL v2 at: * http://license.coscl.org.cn/MulanPubL-2.0 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PubL v2 for more details. */ #define USING_LOG_PREFIX SQL_OPT #include #include #include #include "lib/json/ob_json.h" #define private public #define protected public #include "sql/resolver/expr/ob_raw_expr_util.h" #include "sql/optimizer/ob_select_log_plan.h" #include "sql/optimizer/ob_opt_selectivity.h" #include "sql/optimizer/test_optimizer_utils.h" #include "observer/ob_req_time_service.h" using namespace oceanbase::common; namespace test { class TestOptEstSel: public TestOptimizerUtils { enum StatMode { NORMAL_MODE, DEFAULT_STAT_MODE, ZERO_DISTINCT_MODE, ONE_DISTINCT_MODE, MUTEX_MODE, HISTOGRAM_MODE, }; static const uint8_t YEAR_MAX_VALUE = 10; public: TestOptEstSel(); virtual ~TestOptEstSel(); virtual void SetUp(); virtual void TearDown(); void before_process(const char *sql_str, ObDMLStmt *&dml_stmt, ObLogPlan *&logical_plan, StatMode mode, bool no_predicate); void process_basic(const char *expr_str, std::ofstream &of_result); void process_special(const char *expr_str, std::ofstream &of_result); void process_string(const char *expr_str, std::ofstream &of_result); void process_date(const char *expr_str, std::ofstream &of_result); void process_default_stat(const char *sql_str, std::ofstream &of_result); void process_zero_distinct(const char *sql_str, std::ofstream &of_result); void process_one_distinct(const char *sql_str, std::ofstream &of_result); void process_mutex(const char *sql_str, std::ofstream &of_result); void process_join(const char *sql_str, std::ofstream &of_result); void process_hist(const char *sql_str, std::ofstream &of_result); void get_log_plan(ObStmt &stmt, ObLogPlan *&plan); void run_test(const char *test_file, const char *result_file, const char *tmp_file, int64_t flag); protected: ObOptimizerContext *optctx_; ObAddr addr_; ObGlobalHint global_hint_; bool is_datetime_; oceanbase::sql::ObSchemaChecker schema_checker_; private: // disallow copy DISALLOW_COPY_AND_ASSIGN(TestOptEstSel); }; TestOptEstSel::TestOptEstSel() : optctx_(NULL) { is_datetime_ = false; memcpy(schema_file_path_, "./opt_est/test_opt_est_sel.schema", sizeof("./opt_est/test_opt_est_sel.schema")); } TestOptEstSel::~TestOptEstSel() { } void TestOptEstSel::SetUp() { int ret = OB_SUCCESS; TestOptimizerUtils::SetUp(); exec_ctx_.get_sql_ctx()->session_info_ = &session_info_; optctx_ = new ObOptimizerContext(&session_info_, &exec_ctx_, //schema_mgr_, &sql_schema_guard_, &stat_manager_, NULL, allocator_, &part_cache_, ¶m_list_, addr_, NULL, global_hint_, expr_factory_, NULL, false, stmt_factory_.get_query_ctx()); ObTableLocation table_location; if (OB_FAIL(optctx_->get_table_location_list().push_back(table_location))) { LOG_WARN("failed to set table location", K(ret)); } } void TestOptEstSel::TearDown() { destroy(); } void TestOptEstSel::run_test(const char *test_file, const char *result_file, const char *tmp_file, int64_t flag) { std::ofstream of_result(tmp_file); ASSERT_TRUE(of_result.is_open()); std::ifstream if_test(test_file); ASSERT_TRUE(if_test.is_open()); std::string line; std::string total_line; while(std::getline(if_test, line)) { if (line.size() <= 0) continue; std::size_t begin = line.find_first_not_of('\t'); if (line.at(begin) == '#') continue; std::size_t end = line.find_last_not_of('\t'); std::string cur_line = line.substr(begin, end - begin + 1); switch (flag) { case 1: process_basic(cur_line.c_str(), of_result); break; case 2: process_special(cur_line.c_str(), of_result); break; case 3: process_string(cur_line.c_str(), of_result); break; case 4: process_date(cur_line.c_str(), of_result); break; case 5: process_default_stat(cur_line.c_str(), of_result); break; case 6: process_zero_distinct(cur_line.c_str(), of_result); break; case 7: process_one_distinct(cur_line.c_str(), of_result); break; case 8: process_mutex(cur_line.c_str(), of_result); break; case 9: process_join(cur_line.c_str(), of_result); break; case 10: process_hist(cur_line.c_str(), of_result); break; default: break; } cur_line = ""; } if_test.close(); of_result.close(); std::cout << "diff -u " << tmp_file << " " << result_file << std::endl; TestSqlUtils::is_equal_content(tmp_file, result_file); } void TestOptEstSel::get_log_plan(ObStmt &stmt, ObLogPlan *&plan) { ASSERT_TRUE(NULL != stmt.get_query_ctx()); global_hint_.merge_global_hint(stmt.get_query_ctx()->get_global_hint()); optctx_->set_root_stmt(dynamic_cast(&stmt)); // local address optctx_->set_local_server_addr("1.1.1.1", 8888); optctx_->get_all_exprs().reuse(); ObOptimizer optimizer(*optctx_); optctx_->get_loc_rel_infos().reuse(); OK(optimizer.optimize(dynamic_cast(stmt), plan)); } void TestOptEstSel::before_process(const char *sql_str, ObDMLStmt *&dml_stmt, ObLogPlan *&logical_plan, StatMode mode, bool no_predicate) { MockStatManager *stat_manager = static_cast(optctx_->get_stat_manager()); ObObj min; ObObj max; opt_stat_.no_use_histogram(); optctx_->set_opt_stat_manager(NULL); if (DEFAULT_STAT_MODE == mode) { min.set_min_value(); max.set_max_value(); stat_manager->set_column_stat(0, min, max, 0); stat_manager->set_table_stat(0, 0); } else if (ZERO_DISTINCT_MODE == mode) { min.set_min_value(); max.set_max_value(); stat_manager->set_column_stat(0, min, max, 10000); stat_manager->set_table_stat(10000, 10000); } else if (ONE_DISTINCT_MODE == mode) { min.set_int(1000); max.set_int(1000); stat_manager->set_column_stat(1, min, max, 3000); stat_manager->set_table_stat(10000, 10000); } else if (MUTEX_MODE == mode) { min.set_int(1); max.set_int(5); stat_manager->set_column_stat(5, min, max, 5); stat_manager->set_table_stat(10, 1000); } else if (HISTOGRAM_MODE == mode) { min.set_int(1); max.set_int(20); stat_manager->set_column_stat(20, min, max, 50); stat_manager->set_table_stat(250, 250); opt_stat_.use_histogram(); optctx_->set_opt_stat_manager(&opt_stat_manager_); } else { min.set_int(1001); max.set_int(10000); stat_manager->set_column_stat(1000, min, max, 500); stat_manager->set_table_stat(10000, 2000); } param_list_.reset(); ObStmt *stmt = NULL; // resolver does not generate questionmark expr, selectivity changes. // ONLY affect UT, observer is okay. Tracked by separated isssue // do_resolve(sql_str, stmt, false, JSON_FORMAT, OB_SUCCESS, false); dml_stmt = static_cast(stmt); OB_LOG(INFO, "process stmt", "sql", sql_str); ObArray filter; if (no_predicate) { append(filter, dml_stmt->get_condition_exprs()); dml_stmt->get_condition_exprs().reset(); } get_log_plan(*stmt, logical_plan); schema_checker_.init(sql_schema_guard_); stat_manager->set_schema_checker(&schema_checker_); ASSERT_TRUE(NULL != (logical_plan)); if (no_predicate) { append(dml_stmt->get_condition_exprs(), filter); } } void TestOptEstSel::process_basic(const char *sql_str, std::ofstream &of_result) { if (++case_id_ % 2) { of_result << "******************CASE" < &condition_exprs = dml_stmt->get_condition_exprs(); ObOptSelectivity::calculate_selectivity(logical_plan->get_basic_table_metas(), logical_plan->get_selectivity_ctx(), condition_exprs, selectivity, logical_plan->get_predicate_selectivities()); of_result << "SELECTIVITY = "<< selectivity << std::endl; of_result < &condition_exprs = dml_stmt->get_condition_exprs(); double t1_sel = 1.0; double t2_sel = 1.0; ObOptSelectivity::calculate_selectivity(logical_plan->get_basic_table_metas(), logical_plan->get_selectivity_ctx(), condition_exprs, t1_sel, logical_plan->get_predicate_selectivities()); ObOptSelectivity::calculate_selectivity(logical_plan->get_basic_table_metas(), logical_plan->get_selectivity_ctx(), condition_exprs, t2_sel, logical_plan->get_predicate_selectivities()); of_result << "table1 SELECTIVITY = "<< t1_sel << std::endl; of_result << "table2 SELECTIVITY = "<< t2_sel << std::endl; of_result <(optctx_->get_stat_manager()); ObObj min; ObObj max; if (is_datetime_) { min.set_varchar("10000101000000111"); max.set_varchar("99991231235959999"); } else { min.set_varchar(""); max.set_varchar("||||||||||||||||||||"); } stat_manager->set_column_stat(1000, min, max, 500); ASSERT_TRUE(NULL != (dml_stmt)); ASSERT_TRUE(NULL != (logical_plan)); ObIArray &condition_exprs = dml_stmt->get_condition_exprs(); ObOptSelectivity::calculate_selectivity(logical_plan->get_basic_table_metas(), logical_plan->get_selectivity_ctx(), condition_exprs, selectivity, logical_plan->get_predicate_selectivities()); of_result << "SELECTIVITY = "<< selectivity << std::endl; of_result < &condition_exprs = dml_stmt->get_condition_exprs(); ObOptSelectivity::calculate_selectivity(logical_plan->get_basic_table_metas(), logical_plan->get_selectivity_ctx(), condition_exprs, selectivity, logical_plan->get_predicate_selectivities()); of_result << "SELECTIVITY = "<< selectivity << std::endl; of_result < &condition_exprs = dml_stmt->get_condition_exprs(); ObOptSelectivity::calculate_selectivity(logical_plan->get_basic_table_metas(), logical_plan->get_selectivity_ctx(), condition_exprs, selectivity, logical_plan->get_predicate_selectivities()); of_result << "SELECTIVITY = "<< selectivity << std::endl; of_result < &condition_exprs = dml_stmt->get_condition_exprs(); ObOptSelectivity::calculate_selectivity(logical_plan->get_basic_table_metas(), logical_plan->get_selectivity_ctx(), condition_exprs, selectivity, logical_plan->get_predicate_selectivities()); of_result << "SELECTIVITY = "<< selectivity << std::endl; of_result < &condition_exprs = dml_stmt->get_condition_exprs(); ObOptSelectivity::calculate_selectivity(logical_plan->get_basic_table_metas(), logical_plan->get_selectivity_ctx(), condition_exprs, selectivity, logical_plan->get_predicate_selectivities()); of_result << "SELECTIVITY = "<< selectivity << std::endl; of_result < &condition_exprs = dml_stmt->get_condition_exprs(); ObOptSelectivity::calculate_selectivity(logical_plan->get_basic_table_metas(), logical_plan->get_selectivity_ctx(), condition_exprs, selectivity, logical_plan->get_predicate_selectivities()); of_result << "SELECTIVITY = "<< selectivity << std::endl; of_result <to_string(buf, BUF_LEN, explain_type_); //printf("%s\n", buf); of_result << buf << std::endl; of_result << "*************** Case "<< case_id_ << "(end) ************** " << std::endl; of_result << std::endl; // deconstruct expr_factory_.destory(); stmt_factory_.destory(); // log_plan_factory_.destroy(); } //TEST_F(TestOptEstSel, basic_test) //{ // const char* test_file = "./opt_est/test_sel_basic.sql"; // const char* result_file = "./opt_est/test_sel_basic.result"; // const char* tmp_file = "./opt_est/test_sel_basic.tmp"; // run_test(test_file, result_file, tmp_file, 1); //} //TEST_F(TestOptEstSel, special_test) //{ // const char* test_file = "./opt_est/test_sel_special.sql"; // const char* result_file = "./opt_est/test_sel_special.result"; // const char* tmp_file = "./opt_est/test_sel_special.tmp"; // run_test(test_file, result_file, tmp_file, 2); //} void TestOptEstSel::process_hist(const char *sql_str, std::ofstream &of_result) { if (++case_id_ % 2) { of_result << "******************CASE" < &condition_exprs = dml_stmt->get_condition_exprs(); ObOptSelectivity::calculate_selectivity(logical_plan->get_basic_table_metas(), logical_plan->get_selectivity_ctx(), condition_exprs, selectivity, logical_plan->get_predicate_selectivities()); of_result << "SELECTIVITY = "<< selectivity << std::endl; of_result < repeat_count; common::ObSEArray value; common::ObSEArray num_elements; for (int64_t i = 0; i < ARRAYSIZEOF(infos); i++) { repeat_count.push_back(infos[i][0]); value.push_back(infos[i][1]); num_elements.push_back(infos[i][2]); } init_histogram(allocator_, ObHistType::FREQUENCY, 100, 0.0025, repeat_count, value, num_elements, opt_stat_.get_histogram()); run_test(test_file, result_file, tmp_file, 10); } } // namespace test int main(int argc, char **argv) { system("rm -rf test_opt_est_sel.log"); observer::ObReqTimeGuard req_timeinfo_guard; OB_LOGGER.set_log_level("INFO"); OB_LOGGER.set_file_name("test_opt_est_sel.log", true); init_sql_factories(); ::testing::InitGoogleTest(&argc,argv); if(argc >= 2) { if (strcmp("DEBUG", argv[1]) == 0 || strcmp("WARN", argv[1]) == 0) OB_LOGGER.set_log_level(argv[1]); } return RUN_ALL_TESTS(); }