/** * 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 private public #define protected public #define USING_LOG_PREFIX SQL #include "sql/test_sql_utils.h" #include #include "lib/json/ob_json.h" #include "test_sql.h" #include "sql/plan_cache/ob_cache_object_factory.h" using namespace oceanbase::common; using namespace oceanbase::json; using namespace oceanbase::share::schema; namespace test { static int MAX_THREAD_COUNT = 1; static int LOOP_PER_CASE = 10; static bool PREPARE_PLAN_CACHE = false; static bool WITH_SCHEMA_ALTER = false; static bool WITH_CACHE_EVICT = true; static bool WITH_PLAN_VERIFY = false; static bool OUTPUT_EXEC_TIME = false; static bool OUTPUT_LOGIC_PLAN = false; static bool OUTPUT_PHY_PLAN = false; static const int64_t BUKET_NUM = 102400; static const int64_t MAX_FILE_NAME_LEN = 1024; static const int64_t HIGH_WATER_MARK = 500 * 1024; // 10 plans static const int64_t LOW_WATER_MARK = 500 * 1024; // 5 plans static TestSQL *test_sql = NULL; static ObPlanCache *plan_cache = NULL; void init_pc() { if (NULL == test_sql) { test_sql = new TestSQL(ObString("test_schema.sql")); ASSERT_TRUE(test_sql); test_sql->init(); } if (NULL == plan_cache) { plan_cache = new ObPlanCache(); plan_cache->init(BUKET_NUM, test_sql->get_addr(), test_sql->get_part_cache(), 1); plan_cache->inc_ref_count(); } } class TestPlanCache : public ::testing::Test { public: TestPlanCache() {} virtual ~TestPlanCache() {} void SetUp() {} void TearDown() {} private: // disallow copy TestPlanCache(const TestPlanCache &other); TestPlanCache& operator=(const TestPlanCache &other); }; void do_operation(const char *query, std::ofstream &of_result) { of_result << "*************** Case Start"<< test_sql->next_case_id() << " ***************" << std::endl; of_result << std::endl; of_result << "SQL: " << query << std::endl; of_result << std::endl; ObArenaAllocator allocator(ObModIds::TEST); ObRawExprFactory expr_factory(allocator); ObStmtFactory stmt_factory(allocator); ObLogPlanFactory log_plan_factory(allocator); ObExecContext exec_ctx; ParseResult parse_result; ObStmt *stmt = NULL; ObLogPlan *logical_plan = NULL; ObPhysicalPlan *phy_plan = NULL; int64_t time_1 = 0; int64_t time_2 = 0; int64_t time_3 = 0; int64_t time_4 = 0; int64_t time_5 = 0; int64_t time_6 = 0; int64_t time_7 = 0; int64_t time_8 = 0; int64_t time_9 = 0; // parse time_1 = TestSQL::get_usec(); time_2 = TestSQL::get_usec(); // plan cache ObArray params; int ret = OB_SUCCESS; ObSQLSessionInfo session; ObSqlCtx context; //context.schema_manager_ = test_sql->get_schema_manager(); context.schema_guard_ = &(test_sql->get_schema_guard()); //test error ObPlanCacheKey key; exec_ctx.reset(); exec_ctx.get_task_executor_ctx()->set_min_cluster_version(CLUSTER_VERSION_1500); context.session_info_ = &session; ret = plan_cache->get_plan(allocator, context, ObString::make_string(query), exec_ctx, phy_plan, key); TestSqlCtx test_sql_ctx; test_sql_ctx.allocator_ = &allocator; test_sql_ctx.expr_factory_ = &expr_factory; test_sql_ctx.stmt_factory_ = &stmt_factory; test_sql_ctx.log_plan_factory_ = &log_plan_factory; time_3 = TestSQL::get_usec(); if (OB_NOT_SUPPORTED == ret || OB_REACH_MEMORY_LIMIT == ret) { SQL_PC_LOG(DEBUG, "memory limit or distribute plan not support", K(ret)); } else if (OB_SUCCESS != ret || NULL == phy_plan) { // parse test_sql->do_parse(allocator, query, parse_result); // resolve ret = test_sql->do_resolve(test_sql_ctx, parse_result, stmt, ¶ms); EXPECT_EQ(OB_SUCCESS, ret); SQL_PC_LOG(INFO, "after do_resolve","parse_result.result_tree_", CSJ(ObParserResultPrintWrapper(*parse_result.result_tree_))); time_4 = TestSQL::get_usec(); // logical plan test_sql->generate_logical_plan(test_sql_ctx, ¶ms, stmt, logical_plan); time_5 = TestSQL::get_usec(); if (OUTPUT_LOGIC_PLAN) { char buf[BUF_LEN]; logical_plan->to_string(buf, BUF_LEN); of_result << buf << std::endl; } // physical plan test_sql->generate_physical_plan(logical_plan, phy_plan); phy_plan->set_plan_id(plan_cache->allocate_plan_id()); time_6 = TestSQL::get_usec(); SQL_PC_LOG(INFO, "phy_plan size", K(phy_plan->get_mem_size()), K(sizeof(ObPhysicalPlan))/*K(phy_plan->total_mem_size_)*/); //add_plan error ret = plan_cache->add_plan(key, ObString::make_string(query), NULL, logical_plan->get_table_partition_info()); EXPECT_TRUE(OB_FAIL(ret)); ret = plan_cache->add_plan(key, ObString::make_string(query), phy_plan, logical_plan->get_table_partition_info()); EXPECT_TRUE(OB_SUCCESS == ret || OB_SQL_PC_PLAN_DUPLICATE == ret || OB_NOT_SUPPORTED == ret || OB_REACH_MEMORY_LIMIT == ret); time_7 = TestSQL::get_usec(); if (OB_FAIL(ret)) { SQL_PC_LOG(WARN, "failed to add plan to plan cache", K(ret)); } else { SQL_PC_LOG(INFO, "succeed to add physical plan", K(ret)); SQL_PC_LOG(INFO, "memory usage", "mem used", plan_cache->get_mem_used(), "hwm", plan_cache->get_mem_hwm(), "lwm", plan_cache->get_mem_lwm()); if (OB_FAIL(ret)) { SQL_PC_LOG(WARN, "failed to add plan statistic to plan cache", K(ret)); } ret = plan_cache->remove_plan_stat_entry(phy_plan->get_plan_id()); EXPECT_TRUE(OB_SUCC(ret)); ret = plan_cache->add_plan_stat(test_sql->get_database_id(), ObString::make_string(query), phy_plan); EXPECT_TRUE(OB_SUCC(ret)); } } else {//get plan from plan cache success SQL_PC_LOG(INFO, "get plan from pc", K(*phy_plan), "ref_count", phy_plan->get_ref_count()); //plan_id->stat may_be erase by other thread plan_cache->update_plan_stat(phy_plan->get_plan_id(), 100 * 1000); ObPlanStat plan_stat; if (OB_SUCCESS != (ret = plan_cache->get_plan_stat(phy_plan->get_plan_id(), plan_stat))) { SQL_PC_LOG(WARN, "failed to get plan statistic in plan cache", K(ret)); } } if (OUTPUT_PHY_PLAN) { char buf[BUF_LEN]; phy_plan->to_string(buf, BUF_LEN); of_result << buf << std::endl; } time_8 = TestSQL::get_usec(); ObCacheObjectFactory::free(phy_plan); //ObPhysicalPlan::free(phy_plan); time_9 = TestSQL::get_usec(); if (OUTPUT_EXEC_TIME) { //of_result << "<<<<<<<<<<<<<<<<<<<<<<<<<<<<" << std::endl; of_result << "parse: " << time_2 - time_1 << std::endl; of_result << "pc_get: " << time_3 - time_2 << std::endl; if (time_4 != 0) { of_result << "resolve: " << time_4 - time_3 << std::endl; of_result << "optimize: " << time_5 - time_4 << std::endl; of_result << "cg: " << time_6 - time_5 << std::endl; of_result << "pc_add: " << time_7 - time_6 << std::endl; } of_result << "plan_free: " << time_9 - time_8 << std::endl; //of_result << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>" << std::endl; } of_result << "*************** Case END ************** " << std::endl; of_result << "*************** Case END"<< test_sql->get_case_id() << " ***************" << std::endl; of_result << std::endl; } void test_plan_cache(obsys::CThread *thread) { const char *test_file = "test_plan_cache.sql"; const char *result_file = "test_plan_cache.result"; const char *tmp_file = "test_plan_cache.temp"; if (NULL != thread) { char temp_buffer[MAX_FILE_NAME_LEN]; sprintf(temp_buffer, "test_plan_cache.temp.%d", thread->getpid()); tmp_file = temp_buffer; } std::ofstream of_result(tmp_file); ASSERT_TRUE(of_result.is_open()); std::ifstream if_tests(test_file); ASSERT_TRUE(if_tests.is_open()); std::string line; std::string total_line; while (std::getline(if_tests, line)) { // allow human readable formatting 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 exact_line = line.substr(begin, end - begin + 1); total_line += exact_line; total_line += " "; if (exact_line.at(exact_line.length() - 1) != ';') continue; else { do_operation(total_line.c_str(), of_result); total_line = ""; } } if_tests.close(); of_result.close(); if (WITH_PLAN_VERIFY) { std::ifstream if_result(tmp_file); ASSERT_TRUE(if_result.is_open()); std::istream_iterator it_result(if_result); std::ifstream if_expected(result_file); ASSERT_TRUE(if_expected.is_open()); std::istream_iterator it_expected(if_expected); ASSERT_TRUE(std::equal(it_result, std::istream_iterator(), it_expected)); std::remove(tmp_file); } } void alter_schema_mgr_version() { // schema mgr's version //ObSchemaManager *schema_mgr = test_sql->get_schema_manager(); //int64_t schema_version = schema_mgr->get_schema_version(); //schema_mgr->set_schema_version(schema_version + 1); } void alter_table_schema_version() { // schema mgr's version ObSchemaGetterGuard &schema_guard = test_sql->get_schema_guard(); // table's version; table t1's id is 1 int64_t tid = combine_id(OB_SYS_TENANT_ID, 50000); const ObTableSchema *table_schema = NULL; OK(schema_guard.get_table_schema(tid, table_schema)); ObTableSchema *table_schema1 = const_cast(table_schema); OK(test_sql->add_table_schema(*table_schema1)); //if (table_schema) { // int64_t schema_version = table_schema->get_schema_version(); // table_schema->set_schema_version(schema_version + 1); //} } class PlanCacheRunnable : public share::ObThreadPool { public: void run1() { UNUSED(arg); //TestPlanCache *test_pc = reinterpret_cast(arg); LOG_INFO("start thread", K(thread)); // alter schema if (WITH_SCHEMA_ALTER) { //alter_schema_mgr_version(); alter_table_schema_version(); } int64_t count = LOOP_PER_CASE; while (count--) { test_plan_cache(thread); plan_cache->cache_evict(); plan_cache->cache_evict_all(); } } }; void run_test() { // prepare test_plan_cache(NULL); // test PlanCacheRunnable pc_runner; obsys::CThread pc_threads[MAX_THREAD_COUNT]; for (int i = 0; i < MAX_THREAD_COUNT; ++i) { pc_threads[i].start(&pc_runner, NULL); } for (int i = 0; i < MAX_THREAD_COUNT; ++i) { pc_threads[i].join(); } } TEST_F(TestPlanCache, basic) { int64_t time_1 = 0; int64_t time_2 = 0; // prepare if (PREPARE_PLAN_CACHE) { test_plan_cache(NULL); } time_1 = ::test::TestSQL::get_usec(); // test PlanCacheRunnable pc_runner; obsys::CThread pc_threads[MAX_THREAD_COUNT]; for (int i = 0; i < MAX_THREAD_COUNT; ++i) { pc_threads[i].start(&pc_runner, NULL); } for (int i = 0; i < MAX_THREAD_COUNT; ++i) { pc_threads[i].join(); } // cache evict if (WITH_CACHE_EVICT) { plan_cache->cache_evict_all(); } LOG_INFO("ref_count:", K(plan_cache->ref_count_)); EXPECT_TRUE(plan_cache->get_ref_count() == 1); plan_cache->dec_ref_count(); time_2 = ::test::TestSQL::get_usec(); std::cout<<"total time: "<