diff --git a/benches/engine.rs b/benches/engine.rs index 96fc6f96e29de28fbfe08a50ff466b8869a7e986..e9f38b716454c74c5eece12ba72fc260af338110 100644 --- a/benches/engine.rs +++ b/benches/engine.rs @@ -221,6 +221,10 @@ impl Engine for EngineMock { self.metadata.tables.clear(); } + fn is_metadata_empty(&self) -> bool { + self.metadata.tables.is_empty() + } + fn get_schema(&self) -> Result<Option<String>, QueryPlannerError> { Ok(Some("".to_string())) } diff --git a/benches/parse.rs b/benches/parse.rs index d3b863c61f81d0166fa6e582f1b5dc915510ce93..557914c503954b706892c12b8b17d916c10c1d55 100644 --- a/benches/parse.rs +++ b/benches/parse.rs @@ -227,8 +227,8 @@ fn query1() { ) AS "t3" WHERE "reestrid" = 452842574"#; - let mut engine = EngineMock::new(); - let mut query = Query::new(&mut engine, sql).unwrap(); + let engine = EngineMock::new(); + let mut query = Query::new(&engine, sql).unwrap(); let top_id = query.get_exec_plan().get_ir_plan().get_top().unwrap(); query.bucket_discovery(top_id).unwrap(); query.get_exec_plan().subtree_as_sql(top_id).unwrap(); diff --git a/src/executor.rs b/src/executor.rs index b2644c0b7230d898071a9a029f0dda81dc5f2546..0b9bb35b5d1a520419d8b87e26f211de40245f3e 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -62,7 +62,7 @@ where /// Execution plan exec_plan: ExecutionPlan, /// Execution engine - engine: &'a mut T, + engine: &'a T, /// Bucket map bucket_map: HashMap<usize, Buckets>, } @@ -78,7 +78,7 @@ where /// - Failed to build AST. /// - Failed to build IR plan. /// - Failed to apply optimizing transformations to IR plan. - pub fn new(engine: &'a mut T, sql: &str) -> Result<Self, QueryPlannerError> + pub fn new(engine: &'a T, sql: &str) -> Result<Self, QueryPlannerError> where T::Metadata: Metadata, { diff --git a/src/executor/bucket/tests.rs b/src/executor/bucket/tests.rs index 8166cd2e2076945870934b812a144d89e12aa9f1..53d5a1c9af138d0ef5b53838214f0ed8fc4311b5 100644 --- a/src/executor/bucket/tests.rs +++ b/src/executor/bucket/tests.rs @@ -14,8 +14,8 @@ fn simple_union_query() { ) as "t3" WHERE "id" = 1"#; - let mut engine = EngineMock::new(); - let mut query = Query::new(&mut engine, query).unwrap(); + let engine = EngineMock::new(); + let mut query = Query::new(&engine, query).unwrap(); let plan = query.exec_plan.get_ir_plan(); let top = plan.get_top().unwrap(); let buckets = query.bucket_discovery(top).unwrap(); @@ -35,8 +35,8 @@ fn simple_disjunction_in_union_query() { ) as "t3" WHERE ("id" = 1) OR ("id" = 100)"#; - let mut engine = EngineMock::new(); - let mut query = Query::new(&mut engine, query).unwrap(); + let engine = EngineMock::new(); + let mut query = Query::new(&engine, query).unwrap(); let plan = query.exec_plan.get_ir_plan(); let top = plan.get_top().unwrap(); let buckets = query.bucket_discovery(top).unwrap(); @@ -61,8 +61,8 @@ fn complex_shard_key_union_query() { WHERE "sys_op" > 1) AS "t3" WHERE "identification_number" = 1 AND "product_code" = '222'"#; - let mut engine = EngineMock::new(); - let mut query = Query::new(&mut engine, query).unwrap(); + let engine = EngineMock::new(); + let mut query = Query::new(&engine, query).unwrap(); let plan = query.exec_plan.get_ir_plan(); let top = plan.get_top().unwrap(); let buckets = query.bucket_discovery(top).unwrap(); @@ -90,8 +90,8 @@ fn union_complex_cond_query() { AND ("product_code" = '222' OR "product_code" = '111')"#; - let mut engine = EngineMock::new(); - let mut query = Query::new(&mut engine, query).unwrap(); + let engine = EngineMock::new(); + let mut query = Query::new(&engine, query).unwrap(); let plan = query.exec_plan.get_ir_plan(); let top = plan.get_top().unwrap(); let buckets = query.bucket_discovery(top).unwrap(); @@ -123,8 +123,8 @@ fn union_query_conjunction() { UNION ALL SELECT * FROM "test_space_hist" WHERE "id" = 2"#; - let mut engine = EngineMock::new(); - let mut query = Query::new(&mut engine, query).unwrap(); + let engine = EngineMock::new(); + let mut query = Query::new(&engine, query).unwrap(); let plan = query.exec_plan.get_ir_plan(); let top = plan.get_top().unwrap(); let buckets = query.bucket_discovery(top).unwrap(); diff --git a/src/executor/engine.rs b/src/executor/engine.rs index d045c7646d56171615a1894968adc9128a9e3071..69ff881970ba3c3a9bfac18b2880f05545c58927 100644 --- a/src/executor/engine.rs +++ b/src/executor/engine.rs @@ -57,6 +57,9 @@ pub trait Engine { /// Clear metadata information fn clear_metadata(&mut self); + /// Check if the cache is empty. + fn is_metadata_empty(&self) -> bool; + /// Retrieve cluster schema. /// /// # Errors diff --git a/src/executor/engine/cartridge.rs b/src/executor/engine/cartridge.rs index b4f3cd30057997564e0f7fe47bc3260803e6a571..badfa234a5a6e67711be258bd2f075f16aba54b3 100644 --- a/src/executor/engine/cartridge.rs +++ b/src/executor/engine/cartridge.rs @@ -40,6 +40,10 @@ impl Engine for Runtime { self.metadata = ClusterAppConfig::new(); } + fn is_metadata_empty(&self) -> bool { + self.metadata.is_empty() + } + fn get_schema(&self) -> Result<Option<String>, QueryPlannerError> { if self.metadata.is_empty() { let lua = tarantool::lua_state(); @@ -88,9 +92,6 @@ impl Engine for Runtime { } fn update_metadata(&mut self, schema: String, timeout: u64) -> Result<(), QueryPlannerError> { - if !&self.metadata.is_empty() { - return Ok(()); - } self.metadata.load_schema(&schema)?; self.metadata.set_exec_waiting_timeout(timeout); Ok(()) diff --git a/src/executor/engine/cartridge/cache/lru/tests.rs b/src/executor/engine/cartridge/cache/lru/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/executor/engine/mock.rs b/src/executor/engine/mock.rs index cccf7db143fc6ef01e48e407d901bb0d7e9d0580..7578566e08d459884e52f92c1e2eba78b33fca94 100644 --- a/src/executor/engine/mock.rs +++ b/src/executor/engine/mock.rs @@ -1,3 +1,4 @@ +use std::cell::RefCell; use std::collections::HashMap; use crate::errors::QueryPlannerError; @@ -144,7 +145,7 @@ impl MetadataMock { #[derive(Debug, Clone)] pub struct EngineMock { metadata: MetadataMock, - virtual_tables: HashMap<usize, VirtualTable>, + virtual_tables: RefCell<HashMap<usize, VirtualTable>>, } impl Engine for EngineMock { @@ -161,6 +162,10 @@ impl Engine for EngineMock { self.metadata.tables.clear(); } + fn is_metadata_empty(&self) -> bool { + self.metadata.tables.is_empty() + } + fn get_schema(&self) -> Result<Option<String>, QueryPlannerError> { Ok(Some("".to_string())) } @@ -180,7 +185,7 @@ impl Engine for EngineMock { motion_node_id: usize, _buckets: &Buckets, ) -> Result<VirtualTable, QueryPlannerError> { - if let Some(virtual_table) = self.virtual_tables.get(&motion_node_id) { + if let Some(virtual_table) = self.virtual_tables.borrow().get(&motion_node_id) { Ok(virtual_table.clone()) } else { Err(QueryPlannerError::CustomError( @@ -248,13 +253,13 @@ impl EngineMock { pub fn new() -> Self { EngineMock { metadata: MetadataMock::new(), - virtual_tables: HashMap::new(), + virtual_tables: RefCell::new(HashMap::new()), } } #[allow(dead_code)] - pub fn add_virtual_table(&mut self, id: usize, table: VirtualTable) { - self.virtual_tables.insert(id, table); + pub fn add_virtual_table(&self, id: usize, table: VirtualTable) { + self.virtual_tables.borrow_mut().insert(id, table); } } diff --git a/src/executor/tests.rs b/src/executor/tests.rs index 89cac9cedf89e58df9e5741279b919ae5d4192e8..4bb20345def1004ed73941fe26f3453f60041198 100644 --- a/src/executor/tests.rs +++ b/src/executor/tests.rs @@ -11,9 +11,9 @@ use super::*; #[test] fn shard_query() { let sql = r#"SELECT "FIRST_NAME" FROM "test_space" where "id" = 1"#; - let mut engine = EngineMock::new(); + let engine = EngineMock::new(); - let mut query = Query::new(&mut engine, sql).unwrap(); + let mut query = Query::new(&engine, sql).unwrap(); let mut expected = BoxExecuteFormat::new(); let bucket = query.engine.determine_bucket_id("1"); @@ -39,9 +39,9 @@ fn shard_union_query() { WHERE "sys_op" > 1) AS "t3" WHERE "id" = 1"#; - let mut engine = EngineMock::new(); + let engine = EngineMock::new(); - let mut query = Query::new(&mut engine, sql).unwrap(); + let mut query = Query::new(&engine, sql).unwrap(); let mut expected = BoxExecuteFormat::new(); let bucket = query.engine.determine_bucket_id("1"); @@ -69,9 +69,9 @@ fn shard_union_query() { #[test] fn map_reduce_query() { let sql = r#"SELECT "product_code" FROM "hash_testing" where "identification_number" = 1 and "product_code" = '457'"#; - let mut engine = EngineMock::new(); + let engine = EngineMock::new(); - let mut query = Query::new(&mut engine, sql).unwrap(); + let mut query = Query::new(&engine, sql).unwrap(); let mut expected = BoxExecuteFormat::new(); let bucket = query.engine.determine_bucket_id(&["1", "457"].join("")); @@ -94,9 +94,9 @@ fn map_reduce_query() { fn linker_test() { let sql = r#"SELECT "FIRST_NAME" FROM "test_space" where "id" in (SELECT "identification_number" FROM "hash_testing" where "identification_number" > 1)"#; - let mut engine = EngineMock::new(); + let engine = EngineMock::new(); - let mut query = Query::new(&mut engine, sql).unwrap(); + let mut query = Query::new(&engine, sql).unwrap(); let motion_id = query.exec_plan.get_ir_plan().get_slices().unwrap()[0][0]; let virtual_table = virtual_table_23(); query.engine.add_virtual_table(motion_id, virtual_table); @@ -144,9 +144,9 @@ fn union_linker_test() { ) as "t2" WHERE "product_code" = '123')"#; - let mut engine = EngineMock::new(); + let engine = EngineMock::new(); - let mut query = Query::new(&mut engine, sql).unwrap(); + let mut query = Query::new(&engine, sql).unwrap(); let motion_id = query.exec_plan.get_ir_plan().get_slices().unwrap()[0][0]; let virtual_table = virtual_table_23(); query.engine.add_virtual_table(motion_id, virtual_table); @@ -223,9 +223,9 @@ INNER JOIN ON "t3"."id" = "t8"."identification_number" WHERE "t3"."id" = 2 AND "t8"."identification_number" = 2"#; - let mut engine = EngineMock::new(); + let engine = EngineMock::new(); - let mut query = Query::new(&mut engine, sql).unwrap(); + let mut query = Query::new(&engine, sql).unwrap(); let motion_id = query.exec_plan.get_ir_plan().get_slices().unwrap()[0][0]; let mut virtual_table = virtual_table_23(); virtual_table.set_alias("\"t8\"").unwrap(); @@ -273,9 +273,9 @@ fn join_linker2_test() { select "id" as "id1", "id" as "id2" from "test_space_hist" ) as "t2" on "t1"."id" = 1"#; - let mut engine = EngineMock::new(); + let engine = EngineMock::new(); - let mut query = Query::new(&mut engine, sql).unwrap(); + let mut query = Query::new(&engine, sql).unwrap(); let motion_id = query.exec_plan.get_ir_plan().get_slices().unwrap()[0][0]; let mut virtual_table = VirtualTable::new(); @@ -325,9 +325,9 @@ fn join_linker3_test() { (SELECT "id" as "id1", "FIRST_NAME" FROM "test_space") AS "t2" ON "t2"."id1" = 1"#; - let mut engine = EngineMock::new(); + let engine = EngineMock::new(); - let mut query = Query::new(&mut engine, sql).unwrap(); + let mut query = Query::new(&engine, sql).unwrap(); let motion_id = query.exec_plan.get_ir_plan().get_slices().unwrap()[0][0]; let mut virtual_table = VirtualTable::new(); @@ -377,9 +377,9 @@ fn join_linker4_test() { on t1."id" = t2."r_id" and t1."FIRST_NAME" = (SELECT "FIRST_NAME" as "fn" FROM "test_space" WHERE "id" = 1)"#; - let mut engine = EngineMock::new(); + let engine = EngineMock::new(); - let mut query = Query::new(&mut engine, sql).unwrap(); + let mut query = Query::new(&engine, sql).unwrap(); let motion_t2_id = query.exec_plan.get_ir_plan().get_slices().unwrap()[0][0]; let mut virtual_t2 = VirtualTable::new(); @@ -442,9 +442,9 @@ fn anonymous_col_index_test() { WHERE "id" in (SELECT "identification_number" FROM "hash_testing" WHERE "product_units" < 3) OR "id" in (SELECT "identification_number" FROM "hash_testing" WHERE "product_units" > 5)"#; - let mut engine = EngineMock::new(); + let engine = EngineMock::new(); - let mut query = Query::new(&mut engine, sql).unwrap(); + let mut query = Query::new(&engine, sql).unwrap(); let motion1_id = query.exec_plan.get_ir_plan().get_slices().unwrap()[0][0]; query .engine diff --git a/src/parser.rs b/src/parser.rs index 1b84a86229b680bb618be892fda5310a958219bb..944599e71625e8cb27476c0c3a25e7242ec4eff2 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -72,7 +72,7 @@ pub extern "C" fn calculate_bucket_id_by_dict(ctx: FunctionCtx, args: FunctionAr return ret_code; } QUERY_ENGINE.with(|e| { - let engine = &mut *e.borrow_mut(); + let engine = &*e.borrow(); // Closure for more concise error propagation from calls nested in the bucket calculation let propagate_err = || -> Result<u64, QueryPlannerError> { @@ -117,7 +117,7 @@ pub extern "C" fn execute_query(ctx: FunctionCtx, args: FunctionArgs) -> c_int { return ret_code; } QUERY_ENGINE.with(|e| { - let engine = &mut *e.borrow_mut(); + let engine = &*e.borrow(); let mut query = match Query::new(engine, args.query.as_str()) { Ok(q) => q, Err(e) => { @@ -162,16 +162,25 @@ fn load_metadata() -> c_int { } }); + let mut is_metadata_empty = false; + QUERY_ENGINE.with(|e| { + let engine = &*e.borrow(); + if engine.is_metadata_empty() { + is_metadata_empty = true; + } + }); // Tarantool never yields here, so it is possible to hold // a mutable reference to the engine. - if let (Some(schema), Some(timeout)) = (schema, timeout) { - QUERY_ENGINE.with(|e| { - let engine = &mut *e.borrow_mut(); - if let Err(e) = engine.update_metadata(schema, timeout) { - return tarantool::set_error!(TarantoolErrorCode::ProcC, "{}", e.to_string()); - } - 0 - }); + if is_metadata_empty { + if let (Some(schema), Some(timeout)) = (schema, timeout) { + QUERY_ENGINE.with(|e| { + let engine = &mut *e.borrow_mut(); + if let Err(e) = engine.update_metadata(schema, timeout) { + return tarantool::set_error!(TarantoolErrorCode::ProcC, "{}", e.to_string()); + } + 0 + }); + } } 0