From 487548988fde3ec637244ecc3ca614cccb0ac282 Mon Sep 17 00:00:00 2001 From: kould Date: Thu, 13 Nov 2025 23:40:47 +0800 Subject: [PATCH 1/2] refactor: stable kitesql --- Cargo.lock | 77 ++- Cargo.toml | 4 +- .../src/reference_serialization.rs | 4 +- rust-toolchain | 2 +- src/binder/aggregate.rs | 25 +- src/binder/alter_table.rs | 7 +- src/binder/analyze.rs | 2 +- src/binder/copy.rs | 7 +- src/binder/create_index.rs | 5 +- src/binder/create_table.rs | 6 +- src/binder/delete.rs | 2 +- src/binder/explain.rs | 5 +- src/binder/expr.rs | 5 +- src/binder/insert.rs | 2 +- src/binder/mod.rs | 3 +- src/binder/select.rs | 28 +- src/binder/update.rs | 2 +- src/db.rs | 19 +- src/execution/ddl/add_column.rs | 124 ++-- src/execution/ddl/create_index.rs | 129 ++-- src/execution/ddl/create_table.rs | 29 +- src/execution/ddl/create_view.rs | 20 +- src/execution/ddl/drop_column.rs | 109 ++-- src/execution/ddl/drop_index.rs | 29 +- src/execution/ddl/drop_table.rs | 27 +- src/execution/ddl/drop_view.rs | 27 +- src/execution/ddl/truncate.rs | 16 +- src/execution/dml/analyze.rs | 183 +++--- src/execution/dml/copy_from_file.rs | 82 ++- src/execution/dml/copy_to_file.rs | 46 +- src/execution/dml/delete.rs | 140 +++-- src/execution/dml/insert.rs | 186 +++--- src/execution/dml/update.rs | 182 +++--- src/execution/dql/aggregate/hash_agg.rs | 103 ++-- src/execution/dql/aggregate/simple_agg.rs | 50 +- src/execution/dql/describe.rs | 117 ++-- src/execution/dql/dummy.rs | 11 +- src/execution/dql/except.rs | 45 +- src/execution/dql/explain.rs | 21 +- src/execution/dql/filter.rs | 38 +- src/execution/dql/function_scan.rs | 17 +- src/execution/dql/index_scan.rs | 38 +- src/execution/dql/join/hash/full_join.rs | 92 ++- src/execution/dql/join/hash/inner_join.rs | 49 +- src/execution/dql/join/hash/left_anti_join.rs | 67 +-- src/execution/dql/join/hash/left_join.rs | 88 ++- src/execution/dql/join/hash/left_semi_join.rs | 100 ++-- src/execution/dql/join/hash/right_join.rs | 68 +-- src/execution/dql/join/hash_join.rs | 249 ++++---- src/execution/dql/join/nested_loop_join.rs | 558 ++++++------------ src/execution/dql/limit.rs | 61 +- src/execution/dql/projection.rs | 29 +- src/execution/dql/seq_scan.rs | 36 +- src/execution/dql/show_table.rs | 27 +- src/execution/dql/show_view.rs | 27 +- src/execution/dql/sort.rs | 75 ++- src/execution/dql/top_k.rs | 69 ++- src/execution/dql/union.rs | 37 +- src/execution/dql/values.rs | 27 +- src/execution/execute_macro.rs | 4 +- src/execution/mod.rs | 25 +- src/expression/mod.rs | 28 +- src/expression/range_detacher.rs | 14 +- src/lib.rs | 8 - src/optimizer/core/cm_sketch.rs | 2 +- src/optimizer/heuristic/graph.rs | 12 +- src/optimizer/heuristic/matcher.rs | 12 +- .../rule/normalization/pushdown_limit.rs | 2 +- src/planner/mod.rs | 18 +- src/planner/operator/aggregate.rs | 10 +- src/planner/operator/except.rs | 6 +- src/planner/operator/filter.rs | 2 +- src/planner/operator/join.rs | 11 +- src/planner/operator/limit.rs | 6 +- src/planner/operator/mod.rs | 58 +- src/planner/operator/project.rs | 4 +- src/planner/operator/sort.rs | 6 +- src/planner/operator/table_scan.rs | 4 +- src/planner/operator/top_k.rs | 8 +- src/planner/operator/union.rs | 6 +- src/serdes/column.rs | 3 +- src/types/index.rs | 2 +- src/types/mod.rs | 12 +- src/types/serialize.rs | 2 +- src/types/value.rs | 68 +-- 85 files changed, 1895 insertions(+), 2071 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac6a61fa..de6cb2a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1004,6 +1004,36 @@ dependencies = [ "slab", ] +[[package]] +name = "genawaiter" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0" +dependencies = [ + "genawaiter-macro", + "genawaiter-proc-macro", + "proc-macro-hack", +] + +[[package]] +name = "genawaiter-macro" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc" + +[[package]] +name = "genawaiter-proc-macro" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784f84eebc366e15251c4a8c3acee82a6a6f427949776ecb88377362a9621738" +dependencies = [ + "proc-macro-error", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1303,7 +1333,7 @@ dependencies = [ [[package]] name = "kite_sql" -version = "0.1.2" +version = "0.1.3" dependencies = [ "ahash 0.8.12", "async-trait", @@ -1319,6 +1349,7 @@ dependencies = [ "env_logger", "fixedbitset", "futures", + "genawaiter", "indicatif", "itertools 0.12.1", "kite_sql_serde_macros", @@ -1329,6 +1360,7 @@ dependencies = [ "petgraph", "pgwire", "pprof", + "rand 0.8.5", "recursive", "regex", "rocksdb", @@ -1888,6 +1920,38 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-error" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "syn-mid", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro2" version = "1.0.95" @@ -2581,6 +2645,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-mid" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea305d57546cc8cd04feb14b62ec84bf17f50e3f7b12560d7bfa9265f39d9ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "tap" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index bc8b854b..ce175e43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "kite_sql" -version = "0.1.2" +version = "0.1.3" edition = "2021" authors = ["Kould ", "Xwg "] description = "SQL as a Function for Rust" @@ -60,6 +60,8 @@ sqlparser = { version = "0.34", features = ["serde"] } thiserror = { version = "1" } typetag = { version = "0.2" } ulid = { version = "1", features = ["serde"] } +genawaiter = { version = "0.99" } +rand = { version = "0.8" } # Feature: net async-trait = { version = "0.1", optional = true } diff --git a/kite_sql_serde_macros/src/reference_serialization.rs b/kite_sql_serde_macros/src/reference_serialization.rs index a016c0e3..1da46649 100644 --- a/kite_sql_serde_macros/src/reference_serialization.rs +++ b/kite_sql_serde_macros/src/reference_serialization.rs @@ -92,7 +92,7 @@ pub(crate) fn handle(ast: DeriveInput) -> Result { let field_name = field_opts .ident - .unwrap_or_else(|| Ident::new(&format!("field_{}", i), Span::call_site())); + .unwrap_or_else(|| Ident::new(&format!("field_{i}"), Span::call_site())); let ty = process_type(&field_opts.ty); encode_fields.push(quote! { @@ -155,7 +155,7 @@ pub(crate) fn handle(ast: DeriveInput) -> Result { let field_name = field_opts .ident - .unwrap_or_else(|| Ident::new(&format!("field_{}", i), Span::call_site())); + .unwrap_or_else(|| Ident::new(&format!("field_{i}"), Span::call_site())); let ty = process_type(&field_opts.ty); encode_fields.push(quote! { diff --git a/rust-toolchain b/rust-toolchain index 4e42420d..2bf5ad04 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2024-10-10 \ No newline at end of file +stable diff --git a/src/binder/aggregate.rs b/src/binder/aggregate.rs index a1c6db53..b619bfd2 100644 --- a/src/binder/aggregate.rs +++ b/src/binder/aggregate.rs @@ -83,8 +83,8 @@ impl> Binder<'_, '_, T, A> return_orderby.push(SortField::new( expr, - asc.map_or(true, |asc| asc), - nulls_first.map_or(false, |first| first), + asc.is_none_or(|asc| asc), + nulls_first.is_some_and(|first| first), )); } Some(return_orderby) @@ -251,8 +251,7 @@ impl> Binder<'_, '_, T, A> if !group_raw_exprs.iter().contains(&expr) { return Err(DatabaseError::AggMiss(format!( - "`{}` must appear in the GROUP BY clause or be used in an aggregate function", - expr + "`{expr}` must appear in the GROUP BY clause or be used in an aggregate function" ))); } } @@ -306,12 +305,9 @@ impl> Binder<'_, '_, T, A> return Ok(()); } - Err(DatabaseError::AggMiss( - format!( - "expression '{}' must appear in the GROUP BY clause or be used in an aggregate function", - expr - ) - )) + Err(DatabaseError::AggMiss(format!( + "expression '{expr}' must appear in the GROUP BY clause or be used in an aggregate function" + ))) } ScalarExpression::ColumnRef { .. } | ScalarExpression::Alias { .. } => { if self.context.group_by_exprs.contains(expr) { @@ -321,12 +317,9 @@ impl> Binder<'_, '_, T, A> return self.validate_having_orderby(expr.unpack_alias_ref()); } - Err(DatabaseError::AggMiss( - format!( - "expression '{}' must appear in the GROUP BY clause or be used in an aggregate function", - expr - ) - )) + Err(DatabaseError::AggMiss(format!( + "expression '{expr}' must appear in the GROUP BY clause or be used in an aggregate function" + ))) } ScalarExpression::TypeCast { expr, .. } => self.validate_having_orderby(expr), diff --git a/src/binder/alter_table.rs b/src/binder/alter_table.rs index ff615e3c..3f829700 100644 --- a/src/binder/alter_table.rs +++ b/src/binder/alter_table.rs @@ -44,7 +44,7 @@ impl> Binder<'_, '_, T, A> if_not_exists: *if_not_exists, column, }), - Childrens::Only(plan), + Childrens::Only(Box::new(plan)), ) } AlterTableOperation::DropColumn { @@ -61,13 +61,12 @@ impl> Binder<'_, '_, T, A> if_exists: *if_exists, column_name, }), - Childrens::Only(plan), + Childrens::Only(Box::new(plan)), ) } op => { return Err(DatabaseError::UnsupportedStmt(format!( - "AlertOperation: {:?}", - op + "AlertOperation: {op:?}" ))) } }; diff --git a/src/binder/analyze.rs b/src/binder/analyze.rs index 6ade3864..727fdab3 100644 --- a/src/binder/analyze.rs +++ b/src/binder/analyze.rs @@ -32,7 +32,7 @@ impl> Binder<'_, '_, T, A> table_name, index_metas, }), - Childrens::Only(scan_op), + Childrens::Only(Box::new(scan_op)), )) } } diff --git a/src/binder/copy.rs b/src/binder/copy.rs index 4cd29392..9cac0fd4 100644 --- a/src/binder/copy.rs +++ b/src/binder/copy.rs @@ -90,8 +90,7 @@ impl> Binder<'_, '_, T, A> CopyTarget::File { filename } => filename.into(), t => { return Err(DatabaseError::UnsupportedStmt(format!( - "copy target: {:?}", - t + "copy target: {t:?}" ))) } }, @@ -105,7 +104,7 @@ impl> Binder<'_, '_, T, A> target: ext_source, schema_ref, }), - Childrens::Only(TableScanOperator::build(table_name, table, false)), + Childrens::Only(Box::new(TableScanOperator::build(table_name, table, false))), )) } else { // COPY FROM @@ -140,7 +139,7 @@ impl FileFormat { CopyOption::Header(b) => header = *b, CopyOption::Quote(c) => quote = *c, CopyOption::Escape(c) => escape = Some(*c), - o => panic!("unsupported copy option: {:?}", o), + o => panic!("unsupported copy option: {o:?}"), } } FileFormat::Csv { diff --git a/src/binder/create_index.rs b/src/binder/create_index.rs index 535c9748..f5a10df3 100644 --- a/src/binder/create_index.rs +++ b/src/binder/create_index.rs @@ -46,8 +46,7 @@ impl> Binder<'_, '_, T, A> ScalarExpression::ColumnRef { column, .. } => columns.push(column), expr => { return Err(DatabaseError::UnsupportedStmt(format!( - "'CREATE INDEX' by {}", - expr + "'CREATE INDEX' by {expr}" ))) } } @@ -61,7 +60,7 @@ impl> Binder<'_, '_, T, A> if_not_exists, ty, }), - Childrens::Only(plan), + Childrens::Only(Box::new(plan)), )) } } diff --git a/src/binder/create_table.rs b/src/binder/create_table.rs index ec0971c0..14c79b85 100644 --- a/src/binder/create_table.rs +++ b/src/binder/create_table.rs @@ -76,8 +76,7 @@ impl> Binder<'_, '_, T, A> } constraint => { return Err(DatabaseError::UnsupportedStmt(format!( - "`CreateTable` does not currently support this constraint: {:?}", - constraint + "`CreateTable` does not currently support this constraint: {constraint:?}" )))? } } @@ -145,8 +144,7 @@ impl> Binder<'_, '_, T, A> } option => { return Err(DatabaseError::UnsupportedStmt(format!( - "`Column` does not currently support this option: {:?}", - option + "`Column` does not currently support this option: {option:?}" ))) } } diff --git a/src/binder/delete.rs b/src/binder/delete.rs index 94fc8118..05bad2c6 100644 --- a/src/binder/delete.rs +++ b/src/binder/delete.rs @@ -53,7 +53,7 @@ impl> Binder<'_, '_, T, A> table_name, primary_keys, }), - Childrens::Only(plan), + Childrens::Only(Box::new(plan)), )) } else { unreachable!("only table") diff --git a/src/binder/explain.rs b/src/binder/explain.rs index 48a9067c..1a0df211 100644 --- a/src/binder/explain.rs +++ b/src/binder/explain.rs @@ -7,6 +7,9 @@ use crate::types::value::DataValue; impl> Binder<'_, '_, T, A> { pub(crate) fn bind_explain(&mut self, plan: LogicalPlan) -> Result { - Ok(LogicalPlan::new(Operator::Explain, Childrens::Only(plan))) + Ok(LogicalPlan::new( + Operator::Explain, + Childrens::Only(Box::new(plan)), + )) } } diff --git a/src/binder/expr.rs b/src/binder/expr.rs index 60aa5f70..50e9c479 100644 --- a/src/binder/expr.rs +++ b/src/binder/expr.rs @@ -454,7 +454,7 @@ impl<'a, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<'a, '_, T | BinaryOperator::Or | BinaryOperator::Xor => LogicalType::Boolean, BinaryOperator::StringConcat => LogicalType::Varchar(None, CharLengthUnits::Characters), - op => return Err(DatabaseError::UnsupportedStmt(format!("{}", op))), + op => return Err(DatabaseError::UnsupportedStmt(format!("{op}"))), }; Ok(ScalarExpression::Binary { @@ -504,8 +504,7 @@ impl<'a, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<'a, '_, T FunctionArgExpr::Wildcard => args.push(Self::wildcard_expr()), expr => { return Err(DatabaseError::UnsupportedStmt(format!( - "function arg: {:#?}", - expr + "function arg: {expr:#?}" ))) } } diff --git a/src/binder/insert.rs b/src/binder/insert.rs index 283f278b..d548de0f 100644 --- a/src/binder/insert.rs +++ b/src/binder/insert.rs @@ -105,7 +105,7 @@ impl> Binder<'_, '_, T, A> is_overwrite, is_mapping_by_name, }), - Childrens::Only(values_plan), + Childrens::Only(Box::new(values_plan)), )) } diff --git a/src/binder/mod.rs b/src/binder/mod.rs index 54bee538..ef2cb20f 100644 --- a/src/binder/mod.rs +++ b/src/binder/mod.rs @@ -487,8 +487,7 @@ impl<'a, 'b, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<'a, ' right, } => self.bind_set_operation(op, set_quantifier, left, right), expr => Err(DatabaseError::UnsupportedStmt(format!( - "set expression: {:?}", - expr + "set expression: {expr:?}" ))), } } diff --git a/src/binder/select.rs b/src/binder/select.rs index 56dcf695..b49ab8d5 100644 --- a/src/binder/select.rs +++ b/src/binder/select.rs @@ -63,8 +63,7 @@ impl<'a: 'b, 'b, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<' SetExpr::Values(values) => self.bind_temp_values(&values.rows), expr => { return Err(DatabaseError::UnsupportedStmt(format!( - "query body: {:?}", - expr + "query body: {expr:?}" ))) } }?; @@ -151,7 +150,7 @@ impl<'a: 'b, 'b, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<' is_overwrite: false, is_mapping_by_name: true, }), - Childrens::Only(plan), + Childrens::Only(Box::new(plan)), ) } @@ -249,14 +248,14 @@ impl<'a: 'b, 'b, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<' if !left_cast.is_empty() { left_plan = LogicalPlan::new( Operator::Project(ProjectOperator { exprs: left_cast }), - Childrens::Only(left_plan), + Childrens::Only(Box::new(left_plan)), ); } if !right_cast.is_empty() { right_plan = LogicalPlan::new( Operator::Project(ProjectOperator { exprs: right_cast }), - Childrens::Only(right_plan), + Childrens::Only(Box::new(right_plan)), ); } @@ -324,8 +323,8 @@ impl<'a: 'b, 'b, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<' LogicalPlan::new( union_op, Childrens::Twins { - left: left_plan, - right: right_plan, + left: Box::new(left_plan), + right: Box::new(right_plan), }, ), distinct_exprs, @@ -356,8 +355,8 @@ impl<'a: 'b, 'b, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<' LogicalPlan::new( except_op, Childrens::Twins { - left: left_plan, - right: right_plan, + left: Box::new(left_plan), + right: Box::new(right_plan), }, ), distinct_exprs, @@ -365,8 +364,7 @@ impl<'a: 'b, 'b, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<' } } set_operator => Err(DatabaseError::UnsupportedStmt(format!( - "set operator: {:?}", - set_operator + "set operator: {set_operator:?}" ))), } } @@ -450,7 +448,7 @@ impl<'a: 'b, 'b, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<' unreachable!() } } - table => return Err(DatabaseError::UnsupportedStmt(format!("{:#?}", table))), + table => return Err(DatabaseError::UnsupportedStmt(format!("{table:#?}"))), }; Ok(plan) @@ -768,7 +766,7 @@ impl<'a: 'b, 'b, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<' }; let plan = LogicalPlan::new( Operator::Project(projection), - Childrens::Only(filter), + Childrens::Only(Box::new(filter)), ); children = LJoinOperator::build( children, @@ -842,7 +840,7 @@ impl<'a: 'b, 'b, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<' Ok(LogicalPlan::new( Operator::Project(ProjectOperator { exprs: select_list }), - Childrens::Only(children), + Childrens::Only(Box::new(children)), )) } @@ -854,7 +852,7 @@ impl<'a: 'b, 'b, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<' sort_fields, limit: None, }), - Childrens::Only(children), + Childrens::Only(Box::new(children)), ) } diff --git a/src/binder/update.rs b/src/binder/update.rs index 23c8f3e2..90c82639 100644 --- a/src/binder/update.rs +++ b/src/binder/update.rs @@ -68,7 +68,7 @@ impl> Binder<'_, '_, T, A> table_name, value_exprs, }), - Childrens::Only(plan), + Childrens::Only(Box::new(plan)), )) } else { unreachable!("only table") diff --git a/src/db.rs b/src/db.rs index 4f1c3293..9c05bc7c 100644 --- a/src/db.rs +++ b/src/db.rs @@ -28,9 +28,7 @@ use parking_lot::{RawRwLock, RwLock}; use std::hash::RandomState; use std::marker::PhantomData; use std::mem; -use std::ops::{Coroutine, CoroutineState}; use std::path::PathBuf; -use std::pin::Pin; use std::sync::atomic::AtomicUsize; use std::sync::Arc; @@ -440,16 +438,11 @@ impl DBTransaction<'_, S> { pub struct TransactionIter<'a> { executor: Executor<'a>, schema: SchemaRef, - is_over: bool, } impl<'a> TransactionIter<'a> { fn new(schema: SchemaRef, executor: Executor<'a>) -> Self { - Self { - executor, - schema, - is_over: false, - } + Self { executor, schema } } } @@ -457,15 +450,7 @@ impl Iterator for TransactionIter<'_> { type Item = Result; fn next(&mut self) -> Option { - if self.is_over { - return None; - } - if let CoroutineState::Yielded(tuple) = Pin::new(&mut self.executor).resume(()) { - Some(tuple) - } else { - self.is_over = true; - None - } + self.executor.next() } } diff --git a/src/execution/ddl/add_column.rs b/src/execution/ddl/add_column.rs index 9e8c5de9..6de3e681 100644 --- a/src/execution/ddl/add_column.rs +++ b/src/execution/ddl/add_column.rs @@ -1,5 +1,5 @@ use crate::errors::DatabaseError; -use crate::execution::{build_read, Executor, WriteExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, WriteExecutor}; use crate::planner::LogicalPlan; use crate::storage::{StatisticsMetaCache, TableCache, ViewCache}; use crate::types::index::{Index, IndexType}; @@ -10,9 +10,6 @@ use crate::{ planner::operator::alter_table::add_column::AddColumnOperator, storage::Transaction, throw, }; use itertools::Itertools; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; pub struct AddColumn { op: AddColumnOperator, @@ -31,78 +28,89 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for AddColumn { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let AddColumnOperator { - table_name, - column, - if_not_exists, - } = &self.op; + spawn_executor(move |co| async move { + let AddColumnOperator { + table_name, + column, + if_not_exists, + } = &self.op; - let mut unique_values = column.desc().is_unique().then(Vec::new); - let mut tuples = Vec::new(); - let schema = self.input.output_schema(); - let mut types = Vec::with_capacity(schema.len() + 1); + let mut unique_values = column.desc().is_unique().then(Vec::new); + let mut tuples = Vec::new(); + let schema = self.input.output_schema(); + let mut types = Vec::with_capacity(schema.len() + 1); - for column_ref in schema.iter() { - types.push(column_ref.datatype().clone()); - } - types.push(column.datatype().clone()); + for column_ref in schema.iter() { + types.push(column_ref.datatype().clone()); + } + types.push(column.datatype().clone()); - let mut coroutine = build_read(self.input, cache, transaction); + let mut coroutine = build_read(self.input, cache, transaction); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let mut tuple: Tuple = throw!(tuple); + for tuple in coroutine.by_ref() { + let mut tuple: Tuple = throw!(co, tuple); - if let Some(value) = throw!(column.default_value()) { - if let Some(unique_values) = &mut unique_values { - unique_values.push(( - throw!(tuple.pk.clone().ok_or(DatabaseError::PrimaryKeyNotFound)), - value.clone(), - )); - } - tuple.values.push(value); - } else { - tuple.values.push(DataValue::Null); + if let Some(value) = throw!(co, column.default_value()) { + if let Some(unique_values) = &mut unique_values { + unique_values.push(( + throw!( + co, + tuple.pk.clone().ok_or(DatabaseError::PrimaryKeyNotFound) + ), + value.clone(), + )); } - tuples.push(tuple); + tuple.values.push(value); + } else { + tuple.values.push(DataValue::Null); } - drop(coroutine); + tuples.push(tuple); + } + drop(coroutine); - let serializers = types.iter().map(|ty| ty.serializable()).collect_vec(); - for tuple in tuples { - throw!(unsafe { &mut (*transaction) }.append_tuple( + let serializers = types.iter().map(|ty| ty.serializable()).collect_vec(); + for tuple in tuples { + throw!( + co, + unsafe { &mut (*transaction) }.append_tuple( table_name, tuple, &serializers, true - )); - } - let col_id = throw!(unsafe { &mut (*transaction) }.add_column( + ) + ); + } + let col_id = throw!( + co, + unsafe { &mut (*transaction) }.add_column( cache.0, table_name, column, *if_not_exists - )); + ) + ); - // Unique Index - if let (Some(unique_values), Some(unique_meta)) = ( - unique_values, - throw!(unsafe { &mut (*transaction) }.table(cache.0, table_name.clone())) - .and_then(|table| table.get_unique_index(&col_id)) - .cloned(), - ) { - for (tuple_id, value) in unique_values { - let index = Index::new(unique_meta.id, &value, IndexType::Unique); - throw!( - unsafe { &mut (*transaction) }.add_index(table_name, index, &tuple_id) - ); - } + // Unique Index + if let (Some(unique_values), Some(unique_meta)) = ( + unique_values, + throw!( + co, + unsafe { &mut (*transaction) }.table(cache.0, table_name.clone()) + ) + .and_then(|table| table.get_unique_index(&col_id)) + .cloned(), + ) { + for (tuple_id, value) in unique_values { + let index = Index::new(unique_meta.id, &value, IndexType::Unique); + throw!( + co, + unsafe { &mut (*transaction) }.add_index(table_name, index, &tuple_id) + ); } + } - yield Ok(TupleBuilder::build_result("1".to_string())); - }, - ) + co.yield_(Ok(TupleBuilder::build_result("1".to_string()))) + .await; + }) } } diff --git a/src/execution/ddl/create_index.rs b/src/execution/ddl/create_index.rs index 6fb1e315..f9f1a100 100644 --- a/src/execution/ddl/create_index.rs +++ b/src/execution/ddl/create_index.rs @@ -1,6 +1,6 @@ use crate::execution::dql::projection::Projection; use crate::execution::DatabaseError; -use crate::execution::{build_read, Executor, WriteExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, WriteExecutor}; use crate::expression::{BindPosition, ScalarExpression}; use crate::planner::operator::create_index::CreateIndexOperator; use crate::planner::LogicalPlan; @@ -12,9 +12,6 @@ use crate::types::tuple_builder::TupleBuilder; use crate::types::value::DataValue; use crate::types::ColumnId; use std::borrow::Cow; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; pub struct CreateIndex { op: CreateIndexOperator, @@ -33,75 +30,73 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for CreateIndex { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let CreateIndexOperator { - table_name, - index_name, - columns, - if_not_exists, - ty, - } = self.op; + spawn_executor(move |co| async move { + let CreateIndexOperator { + table_name, + index_name, + columns, + if_not_exists, + ty, + } = self.op; - let (column_ids, mut column_exprs): (Vec, Vec) = - columns - .into_iter() - .filter_map(|column| { - column - .id() - .map(|id| (id, ScalarExpression::column_expr(column))) - }) - .unzip(); - let schema = self.input.output_schema().clone(); - throw!(BindPosition::bind_exprs( + let (column_ids, mut column_exprs): (Vec, Vec) = columns + .into_iter() + .filter_map(|column| { + column + .id() + .map(|id| (id, ScalarExpression::column_expr(column))) + }) + .unzip(); + let schema = self.input.output_schema().clone(); + throw!( + co, + BindPosition::bind_exprs( column_exprs.iter_mut(), || schema.iter().map(Cow::Borrowed), |a, b| a == b - )); - let index_id = match unsafe { &mut (*transaction) }.add_index_meta( - cache.0, - &table_name, - index_name, - column_ids, - ty, - ) { - Ok(index_id) => index_id, - Err(DatabaseError::DuplicateIndex(index_name)) => { - if if_not_exists { - return; - } else { - throw!(Err(DatabaseError::DuplicateIndex(index_name))) - } + ) + ); + let index_id = match unsafe { &mut (*transaction) }.add_index_meta( + cache.0, + &table_name, + index_name, + column_ids, + ty, + ) { + Ok(index_id) => index_id, + Err(DatabaseError::DuplicateIndex(index_name)) => { + if if_not_exists { + return; + } else { + throw!(co, Err(DatabaseError::DuplicateIndex(index_name))) } - err => throw!(err), - }; - let mut coroutine = build_read(self.input, cache, transaction); + } + err => throw!(co, err), + }; + let mut coroutine = build_read(self.input, cache, transaction); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let tuple: Tuple = throw!(tuple); + for tuple in coroutine.by_ref() { + let tuple: Tuple = throw!(co, tuple); - let Some(value) = DataValue::values_to_tuple(throw!(Projection::projection( - &tuple, - &column_exprs, - &schema - ))) else { - continue; - }; - let tuple_id = if let Some(tuple_id) = tuple.pk.as_ref() { - tuple_id - } else { - continue; - }; - let index = Index::new(index_id, &value, ty); - throw!(unsafe { &mut (*transaction) }.add_index( - table_name.as_str(), - index, - tuple_id - )); - } - yield Ok(TupleBuilder::build_result("1".to_string())); - }, - ) + let Some(value) = DataValue::values_to_tuple(throw!( + co, + Projection::projection(&tuple, &column_exprs, &schema) + )) else { + continue; + }; + let tuple_id = if let Some(tuple_id) = tuple.pk.as_ref() { + tuple_id + } else { + continue; + }; + let index = Index::new(index_id, &value, ty); + throw!( + co, + unsafe { &mut (*transaction) }.add_index(table_name.as_str(), index, tuple_id) + ); + } + co.yield_(Ok(TupleBuilder::build_result("1".to_string()))) + .await; + }) } } diff --git a/src/execution/ddl/create_table.rs b/src/execution/ddl/create_table.rs index c55035fe..596e5d50 100644 --- a/src/execution/ddl/create_table.rs +++ b/src/execution/ddl/create_table.rs @@ -1,4 +1,4 @@ -use crate::execution::{Executor, WriteExecutor}; +use crate::execution::{spawn_executor, Executor, WriteExecutor}; use crate::planner::operator::create_table::CreateTableOperator; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; @@ -20,24 +20,25 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for CreateTable { (table_cache, _, _): (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let CreateTableOperator { - table_name, - columns, - if_not_exists, - } = self.op; + spawn_executor(move |co| async move { + let CreateTableOperator { + table_name, + columns, + if_not_exists, + } = self.op; - let _ = throw!(unsafe { &mut (*transaction) }.create_table( + let _ = throw!( + co, + unsafe { &mut (*transaction) }.create_table( table_cache, table_name.clone(), columns, if_not_exists - )); + ) + ); - yield Ok(TupleBuilder::build_result(format!("{}", table_name))); - }, - ) + co.yield_(Ok(TupleBuilder::build_result(format!("{table_name}")))) + .await; + }) } } diff --git a/src/execution/ddl/create_view.rs b/src/execution/ddl/create_view.rs index d153f2df..52afdcf7 100644 --- a/src/execution/ddl/create_view.rs +++ b/src/execution/ddl/create_view.rs @@ -1,4 +1,4 @@ -use crate::execution::{Executor, WriteExecutor}; +use crate::execution::{spawn_executor, Executor, WriteExecutor}; use crate::planner::operator::create_view::CreateViewOperator; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; @@ -20,16 +20,16 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for CreateView { (_, view_cache, _): (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let CreateViewOperator { view, or_replace } = self.op; + spawn_executor(move |co| async move { + let CreateViewOperator { view, or_replace } = self.op; - let result_tuple = TupleBuilder::build_result(format!("{}", view.name)); - throw!(unsafe { &mut (*transaction) }.create_view(view_cache, view, or_replace)); + let result_tuple = TupleBuilder::build_result(format!("{}", view.name)); + throw!( + co, + unsafe { &mut (*transaction) }.create_view(view_cache, view, or_replace) + ); - yield Ok(result_tuple); - }, - ) + co.yield_(Ok(result_tuple)).await; + }) } } diff --git a/src/execution/ddl/drop_column.rs b/src/execution/ddl/drop_column.rs index 0c5d5b56..a44ef09b 100644 --- a/src/execution/ddl/drop_column.rs +++ b/src/execution/ddl/drop_column.rs @@ -1,5 +1,5 @@ use crate::errors::DatabaseError; -use crate::execution::{build_read, Executor, WriteExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, WriteExecutor}; use crate::planner::operator::alter_table::drop_column::DropColumnOperator; use crate::planner::LogicalPlan; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; @@ -7,9 +7,6 @@ use crate::throw; use crate::types::tuple::Tuple; use crate::types::tuple_builder::TupleBuilder; use itertools::Itertools; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; pub struct DropColumn { op: DropColumnOperator, @@ -28,68 +25,76 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for DropColumn { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let DropColumnOperator { - table_name, - column_name, - if_exists, - } = self.op; + spawn_executor(move |co| async move { + let DropColumnOperator { + table_name, + column_name, + if_exists, + } = self.op; - let tuple_columns = self.input.output_schema(); - if let Some((column_index, is_primary)) = tuple_columns - .iter() - .enumerate() - .find(|(_, column)| column.name() == column_name) - .map(|(i, column)| (i, column.desc().is_primary())) - { - if is_primary { - throw!(Err(DatabaseError::InvalidColumn( + let tuple_columns = self.input.output_schema(); + if let Some((column_index, is_primary)) = tuple_columns + .iter() + .enumerate() + .find(|(_, column)| column.name() == column_name) + .map(|(i, column)| (i, column.desc().is_primary())) + { + if is_primary { + throw!( + co, + Err(DatabaseError::InvalidColumn( "drop of primary key column is not allowed.".to_owned(), - ))); - } - let mut tuples = Vec::new(); - let mut types = Vec::with_capacity(tuple_columns.len() - 1); + )) + ); + } + let mut tuples = Vec::new(); + let mut types = Vec::with_capacity(tuple_columns.len() - 1); - for (i, column_ref) in tuple_columns.iter().enumerate() { - if i == column_index { - continue; - } - types.push(column_ref.datatype().clone()); + for (i, column_ref) in tuple_columns.iter().enumerate() { + if i == column_index { + continue; } - let mut coroutine = build_read(self.input, cache, transaction); + types.push(column_ref.datatype().clone()); + } + let mut coroutine = build_read(self.input, cache, transaction); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let mut tuple: Tuple = throw!(tuple); - let _ = tuple.values.remove(column_index); + for tuple in coroutine.by_ref() { + let mut tuple: Tuple = throw!(co, tuple); + let _ = tuple.values.remove(column_index); - tuples.push(tuple); - } - drop(coroutine); - let serializers = types.iter().map(|ty| ty.serializable()).collect_vec(); - for tuple in tuples { - throw!(unsafe { &mut (*transaction) }.append_tuple( + tuples.push(tuple); + } + drop(coroutine); + let serializers = types.iter().map(|ty| ty.serializable()).collect_vec(); + for tuple in tuples { + throw!( + co, + unsafe { &mut (*transaction) }.append_tuple( &table_name, tuple, &serializers, true - )); - } - throw!(unsafe { &mut (*transaction) }.drop_column( + ) + ); + } + throw!( + co, + unsafe { &mut (*transaction) }.drop_column( cache.0, cache.2, &table_name, &column_name - )); + ) + ); - yield Ok(TupleBuilder::build_result("1".to_string())); - } else if if_exists { - return; - } else { - yield Err(DatabaseError::ColumnNotFound(column_name)); - } - }, - ) + co.yield_(Ok(TupleBuilder::build_result("1".to_string()))) + .await; + } else if if_exists { + return; + } else { + co.yield_(Err(DatabaseError::ColumnNotFound(column_name))) + .await; + } + }) } } diff --git a/src/execution/ddl/drop_index.rs b/src/execution/ddl/drop_index.rs index f0d5f316..826578ab 100644 --- a/src/execution/ddl/drop_index.rs +++ b/src/execution/ddl/drop_index.rs @@ -1,4 +1,4 @@ -use crate::execution::{Executor, WriteExecutor}; +use crate::execution::{spawn_executor, Executor, WriteExecutor}; use crate::planner::operator::drop_index::DropIndexOperator; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; @@ -20,24 +20,25 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for DropIndex { (table_cache, _, _): (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let DropIndexOperator { - table_name, - index_name, - if_exists, - } = self.op; + spawn_executor(move |co| async move { + let DropIndexOperator { + table_name, + index_name, + if_exists, + } = self.op; - throw!(unsafe { &mut (*transaction) }.drop_index( + throw!( + co, + unsafe { &mut (*transaction) }.drop_index( table_cache, table_name, &index_name, if_exists - )); + ) + ); - yield Ok(TupleBuilder::build_result(index_name.to_string())); - }, - ) + co.yield_(Ok(TupleBuilder::build_result(index_name.to_string()))) + .await; + }) } } diff --git a/src/execution/ddl/drop_table.rs b/src/execution/ddl/drop_table.rs index 8541ce16..524f1a44 100644 --- a/src/execution/ddl/drop_table.rs +++ b/src/execution/ddl/drop_table.rs @@ -1,4 +1,4 @@ -use crate::execution::{Executor, WriteExecutor}; +use crate::execution::{spawn_executor, Executor, WriteExecutor}; use crate::planner::operator::drop_table::DropTableOperator; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; @@ -20,22 +20,23 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for DropTable { (table_cache, _, _): (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let DropTableOperator { - table_name, - if_exists, - } = self.op; + spawn_executor(move |co| async move { + let DropTableOperator { + table_name, + if_exists, + } = self.op; - throw!(unsafe { &mut (*transaction) }.drop_table( + throw!( + co, + unsafe { &mut (*transaction) }.drop_table( table_cache, table_name.clone(), if_exists - )); + ) + ); - yield Ok(TupleBuilder::build_result(format!("{}", table_name))); - }, - ) + co.yield_(Ok(TupleBuilder::build_result(format!("{table_name}")))) + .await; + }) } } diff --git a/src/execution/ddl/drop_view.rs b/src/execution/ddl/drop_view.rs index 13e78174..7932bc70 100644 --- a/src/execution/ddl/drop_view.rs +++ b/src/execution/ddl/drop_view.rs @@ -1,4 +1,4 @@ -use crate::execution::{Executor, WriteExecutor}; +use crate::execution::{spawn_executor, Executor, WriteExecutor}; use crate::planner::operator::drop_view::DropViewOperator; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; @@ -20,23 +20,24 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for DropView { (table_cache, view_cache, _): (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let DropViewOperator { - view_name, - if_exists, - } = self.op; + spawn_executor(move |co| async move { + let DropViewOperator { + view_name, + if_exists, + } = self.op; - throw!(unsafe { &mut (*transaction) }.drop_view( + throw!( + co, + unsafe { &mut (*transaction) }.drop_view( view_cache, table_cache, view_name.clone(), if_exists - )); + ) + ); - yield Ok(TupleBuilder::build_result(format!("{}", view_name))); - }, - ) + co.yield_(Ok(TupleBuilder::build_result(format!("{view_name}")))) + .await; + }) } } diff --git a/src/execution/ddl/truncate.rs b/src/execution/ddl/truncate.rs index 57558e5a..02c942fc 100644 --- a/src/execution/ddl/truncate.rs +++ b/src/execution/ddl/truncate.rs @@ -1,4 +1,4 @@ -use crate::execution::{Executor, WriteExecutor}; +use crate::execution::{spawn_executor, Executor, WriteExecutor}; use crate::planner::operator::truncate::TruncateOperator; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; @@ -20,15 +20,13 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Truncate { _: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let TruncateOperator { table_name } = self.op; + spawn_executor(move |co| async move { + let TruncateOperator { table_name } = self.op; - throw!(unsafe { &mut (*transaction) }.drop_data(&table_name)); + throw!(co, unsafe { &mut (*transaction) }.drop_data(&table_name)); - yield Ok(TupleBuilder::build_result(format!("{}", table_name))); - }, - ) + co.yield_(Ok(TupleBuilder::build_result(format!("{table_name}")))) + .await; + }) } } diff --git a/src/execution/dml/analyze.rs b/src/execution/dml/analyze.rs index 17730bfe..cdcabc95 100644 --- a/src/execution/dml/analyze.rs +++ b/src/execution/dml/analyze.rs @@ -1,7 +1,7 @@ use crate::catalog::TableName; use crate::errors::DatabaseError; use crate::execution::dql::projection::Projection; -use crate::execution::{build_read, Executor, WriteExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, WriteExecutor}; use crate::expression::{BindPosition, ScalarExpression}; use crate::optimizer::core::histogram::HistogramBuilder; use crate::optimizer::core::statistics_meta::StatisticsMeta; @@ -19,10 +19,7 @@ use std::collections::HashSet; use std::ffi::OsStr; use std::fmt::Formatter; use std::fs::DirEntry; -use std::ops::Coroutine; -use std::ops::CoroutineState; use std::path::PathBuf; -use std::pin::Pin; use std::sync::Arc; use std::{fmt, fs}; @@ -59,111 +56,119 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Analyze { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let Analyze { - table_name, - mut input, - index_metas, - } = self; - - let schema = input.output_schema().clone(); - let mut builders = Vec::with_capacity(index_metas.len()); - let table = throw!(throw!( + spawn_executor(move |co| async move { + let Analyze { + table_name, + mut input, + index_metas, + } = self; + + let schema = input.output_schema().clone(); + let mut builders = Vec::with_capacity(index_metas.len()); + let table = throw!( + co, + throw!( + co, unsafe { &mut (*transaction) }.table(cache.0, table_name.clone()) ) .cloned() - .ok_or(DatabaseError::TableNotFound)); - - for index in table.indexes() { - builders.push(State { - is_bound_position: false, - index_id: index.id, - exprs: throw!(index.column_exprs(&table)), - builder: HistogramBuilder::new(index, None), - }); - } + .ok_or(DatabaseError::TableNotFound) + ); + + for index in table.indexes() { + builders.push(State { + is_bound_position: false, + index_id: index.id, + exprs: throw!(co, index.column_exprs(&table)), + builder: HistogramBuilder::new(index, None), + }); + } - let mut coroutine = build_read(input, cache, transaction); + let mut coroutine = build_read(input, cache, transaction); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let tuple = throw!(tuple); + for tuple in coroutine.by_ref() { + let tuple = throw!(co, tuple); - for State { - is_bound_position, - exprs, - builder, - .. - } in builders.iter_mut() - { - if !*is_bound_position { - throw!(BindPosition::bind_exprs( + for State { + is_bound_position, + exprs, + builder, + .. + } in builders.iter_mut() + { + if !*is_bound_position { + throw!( + co, + BindPosition::bind_exprs( exprs.iter_mut(), || schema.iter().map(Cow::Borrowed), |a, b| a == b - )); - *is_bound_position = true; - } - let values = throw!(Projection::projection(&tuple, exprs, &schema)); - - if values.len() == 1 { - throw!(builder.append(&values[0])); - } else { - throw!(builder.append(&Arc::new(DataValue::Tuple(values, false)))); - } + ) + ); + *is_bound_position = true; + } + let values = throw!(co, Projection::projection(&tuple, exprs, &schema)); + + if values.len() == 1 { + throw!(co, builder.append(&values[0])); + } else { + throw!( + co, + builder.append(&Arc::new(DataValue::Tuple(values, false))) + ); } } - drop(coroutine); - let mut values = Vec::with_capacity(builders.len()); - let dir_path = Self::build_statistics_meta_path(&table_name); - // For DEBUG - // println!("Statistics Path: {:#?}", dir_path); - throw!(fs::create_dir_all(&dir_path).map_err(DatabaseError::IO)); - - let mut active_index_paths = HashSet::new(); - - for State { - index_id, builder, .. - } in builders - { - let index_file = OsStr::new(&index_id.to_string()).to_os_string(); - let path = dir_path.join(&index_file); - let temp_path = path.with_extension("tmp"); - let path_str: String = path.to_string_lossy().into(); - - let (histogram, sketch) = throw!(builder.build(DEFAULT_NUM_OF_BUCKETS)); - let meta = StatisticsMeta::new(histogram, sketch); - - throw!(meta.to_file(&temp_path)); - values.push(DataValue::Utf8 { - value: path_str.clone(), - ty: Utf8Type::Variable(None), - unit: CharLengthUnits::Characters, - }); - throw!(unsafe { &mut (*transaction) }.save_table_meta( + } + drop(coroutine); + let mut values = Vec::with_capacity(builders.len()); + let dir_path = Self::build_statistics_meta_path(&table_name); + throw!(co, fs::create_dir_all(&dir_path).map_err(DatabaseError::IO)); + + let mut active_index_paths = HashSet::new(); + + for State { + index_id, builder, .. + } in builders + { + let index_file = OsStr::new(&index_id.to_string()).to_os_string(); + let path = dir_path.join(&index_file); + let temp_path = path.with_extension("tmp"); + let path_str: String = path.to_string_lossy().into(); + + let (histogram, sketch) = throw!(co, builder.build(DEFAULT_NUM_OF_BUCKETS)); + let meta = StatisticsMeta::new(histogram, sketch); + + throw!(co, meta.to_file(&temp_path)); + values.push(DataValue::Utf8 { + value: path_str.clone(), + ty: Utf8Type::Variable(None), + unit: CharLengthUnits::Characters, + }); + throw!( + co, + unsafe { &mut (*transaction) }.save_table_meta( cache.2, &table_name, path_str, meta - )); - throw!(fs::rename(&temp_path, &path).map_err(DatabaseError::IO)); + ) + ); + throw!(co, fs::rename(&temp_path, &path).map_err(DatabaseError::IO)); - active_index_paths.insert(index_file); - } + active_index_paths.insert(index_file); + } - // clean expired index - for entry in throw!(fs::read_dir(dir_path).map_err(DatabaseError::IO)) { - let entry: DirEntry = throw!(entry.map_err(DatabaseError::IO)); + // clean expired index + for entry in throw!(co, fs::read_dir(dir_path).map_err(DatabaseError::IO)) { + let entry: DirEntry = throw!(co, entry.map_err(DatabaseError::IO)); - if !active_index_paths.remove(&entry.file_name()) { - throw!(fs::remove_file(entry.path()).map_err(DatabaseError::IO)); - } + if !active_index_paths.remove(&entry.file_name()) { + throw!(co, fs::remove_file(entry.path()).map_err(DatabaseError::IO)); } + } - yield Ok(Tuple::new(None, values)); - }, - ) + co.yield_(Ok(Tuple::new(None, values))).await; + }) } } diff --git a/src/execution/dml/copy_from_file.rs b/src/execution/dml/copy_from_file.rs index 8833581e..1e9fd27f 100644 --- a/src/execution/dml/copy_from_file.rs +++ b/src/execution/dml/copy_from_file.rs @@ -1,7 +1,7 @@ use crate::binder::copy::FileFormat; use crate::catalog::PrimaryKeyIndices; use crate::errors::DatabaseError; -use crate::execution::{Executor, WriteExecutor}; +use crate::execution::{spawn_executor, Executor, WriteExecutor}; use crate::planner::operator::copy_from_file::CopyFromFileOperator; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; @@ -31,45 +31,46 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for CopyFromFile { (table_cache, _, _): (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let serializers = self - .op - .schema_ref - .iter() - .map(|column| column.datatype().serializable()) - .collect_vec(); - let (tx, rx) = mpsc::channel(); - let (tx1, rx1) = mpsc::channel(); - // # Cancellation - // When this stream is dropped, the `rx` is dropped, the spawned task will fail to send to - // `tx`, then the task will finish. - let table = throw!(throw!( + spawn_executor(move |co| async move { + let serializers = self + .op + .schema_ref + .iter() + .map(|column| column.datatype().serializable()) + .collect_vec(); + let (tx, rx) = mpsc::channel(); + let (tx1, rx1) = mpsc::channel(); + let table = throw!( + co, + throw!( + co, unsafe { &mut (*transaction) }.table(table_cache, self.op.table.clone()) ) - .ok_or(DatabaseError::TableNotFound)); - let primary_keys_indices = table.primary_keys_indices().clone(); - let handle = thread::spawn(|| self.read_file_blocking(tx, primary_keys_indices)); - let mut size = 0_usize; - while let Ok(chunk) = rx.recv() { - throw!(unsafe { &mut (*transaction) }.append_tuple( + .ok_or(DatabaseError::TableNotFound) + ); + let primary_keys_indices = table.primary_keys_indices().clone(); + let handle = thread::spawn(|| self.read_file_blocking(tx, primary_keys_indices)); + let mut size = 0_usize; + while let Ok(chunk) = rx.recv() { + throw!( + co, + unsafe { &mut (*transaction) }.append_tuple( table.name(), chunk, &serializers, false - )); - size += 1; - } - throw!(handle.join().unwrap()); - - let handle = thread::spawn(move || return_result(size, tx1)); - while let Ok(chunk) = rx1.recv() { - yield Ok(chunk); - } - throw!(handle.join().unwrap()) - }, - ) + ) + ); + size += 1; + } + throw!(co, handle.join().unwrap()); + + let handle = thread::spawn(move || return_result(size, tx1)); + while let Ok(chunk) = rx1.recv() { + co.yield_(Ok(chunk)).await; + } + throw!(co, handle.join().unwrap()) + }) } } @@ -137,8 +138,6 @@ mod tests { use crate::types::LogicalType; use sqlparser::ast::CharLengthUnits; use std::io::Write; - use std::ops::{Coroutine, CoroutineState}; - use std::pin::Pin; use std::sync::Arc; use tempfile::TempDir; use ulid::Ulid; @@ -222,7 +221,7 @@ mod tests { let storage = db.storage; let mut transaction = storage.transaction()?; - let mut coroutine = executor.execute_mut( + let mut executor_iter = executor.execute_mut( ( db.state.table_cache(), db.state.view_cache(), @@ -230,11 +229,10 @@ mod tests { ), &mut transaction, ); - let tuple = match Pin::new(&mut coroutine).resume(()) { - CoroutineState::Yielded(tuple) => tuple, - CoroutineState::Complete(()) => unreachable!(), - } - .unwrap(); + let tuple = executor_iter + .next() + .expect("executor should yield once") + .unwrap(); assert_eq!(tuple, TupleBuilder::build_result(2.to_string())); Ok(()) diff --git a/src/execution/dml/copy_to_file.rs b/src/execution/dml/copy_to_file.rs index 562c810a..5fed5e7e 100644 --- a/src/execution/dml/copy_to_file.rs +++ b/src/execution/dml/copy_to_file.rs @@ -1,14 +1,11 @@ use crate::binder::copy::FileFormat; use crate::errors::DatabaseError; -use crate::execution::{build_read, Executor, ReadExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, ReadExecutor}; use crate::planner::operator::copy_to_file::CopyToFileOperator; use crate::planner::LogicalPlan; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; use crate::types::tuple_builder::TupleBuilder; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; pub struct CopyToFile { op: CopyToFileOperator, @@ -27,18 +24,19 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for CopyToFile { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let mut writer = throw!(self.create_writer()); - let CopyToFile { input, .. } = self; + spawn_executor(move |co| async move { + let this = self; + let mut writer = throw!(co, this.create_writer()); + let CopyToFile { input, op } = this; - let mut coroutine = build_read(input, cache, transaction); + let coroutine = build_read(input, cache, transaction); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let tuple = throw!(tuple); + for tuple in coroutine { + let tuple = throw!(co, tuple); - throw!(writer + throw!( + co, + writer .write_record( tuple .values @@ -46,14 +44,15 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for CopyToFile { .map(|v| v.to_string()) .collect::>() ) - .map_err(DatabaseError::from)); - } + .map_err(DatabaseError::from) + ); + } - throw!(writer.flush().map_err(DatabaseError::from)); + throw!(co, writer.flush().map_err(DatabaseError::from)); - yield Ok(TupleBuilder::build_result(format!("{}", self.op))); - }, - ) + co.yield_(Ok(TupleBuilder::build_result(format!("{op}")))) + .await; + }) } } @@ -97,8 +96,6 @@ mod tests { use crate::storage::Storage; use crate::types::LogicalType; use sqlparser::ast::CharLengthUnits; - use std::ops::{Coroutine, CoroutineState}; - use std::pin::Pin; use std::sync::Arc; use tempfile::TempDir; use ulid::Ulid; @@ -186,7 +183,7 @@ mod tests { op: op.clone(), input: TableScanOperator::build(Arc::new("t1".to_string()), table, true), }; - let mut coroutine = executor.execute( + let mut executor = executor.execute( ( db.state.table_cache(), db.state.view_cache(), @@ -195,10 +192,7 @@ mod tests { &mut transaction, ); - let tuple = match Pin::new(&mut coroutine).resume(()) { - CoroutineState::Yielded(tuple) => tuple, - CoroutineState::Complete(()) => unreachable!(), - }?; + let tuple = executor.next().expect("executor should yield once")?; let mut rdr = csv::Reader::from_path(file_path)?; let headers = rdr.headers()?.clone(); diff --git a/src/execution/dml/delete.rs b/src/execution/dml/delete.rs index 788b218f..210a2247 100644 --- a/src/execution/dml/delete.rs +++ b/src/execution/dml/delete.rs @@ -1,7 +1,7 @@ use crate::catalog::TableName; use crate::errors::DatabaseError; use crate::execution::dql::projection::Projection; -use crate::execution::{build_read, Executor, WriteExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, WriteExecutor}; use crate::expression::{BindPosition, ScalarExpression}; use crate::planner::operator::delete::DeleteOperator; use crate::planner::LogicalPlan; @@ -13,9 +13,6 @@ use crate::types::tuple_builder::TupleBuilder; use crate::types::value::DataValue; use std::borrow::Cow; use std::collections::HashMap; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; pub struct Delete { table_name: TableName, @@ -34,85 +31,98 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Delete { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let Delete { - table_name, - mut input, - } = self; + spawn_executor(move |co| async move { + let Delete { + table_name, + mut input, + } = self; - let schema = input.output_schema().clone(); - let table = throw!(throw!( + let schema = input.output_schema().clone(); + let table = throw!( + co, + throw!( + co, unsafe { &mut (*transaction) }.table(cache.0, table_name.clone()) ) - .ok_or(DatabaseError::TableNotFound)); - let mut indexes: HashMap = HashMap::new(); + .ok_or(DatabaseError::TableNotFound) + ); + let mut indexes: HashMap = HashMap::new(); - let mut deleted_count = 0; - let mut coroutine = build_read(input, cache, transaction); + let mut deleted_count = 0; + let mut coroutine = build_read(input, cache, transaction); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let tuple: Tuple = throw!(tuple); + for tuple in coroutine.by_ref() { + let tuple: Tuple = throw!(co, tuple); - for index_meta in table.indexes() { - if let Some(Value { exprs, values, .. }) = indexes.get_mut(&index_meta.id) { - let Some(data_value) = DataValue::values_to_tuple(throw!( - Projection::projection(&tuple, exprs, &schema) - )) else { - continue; - }; - values.push(data_value); - } else { - let mut values = Vec::with_capacity(table.indexes().len()); - let mut exprs = throw!(index_meta.column_exprs(table)); - throw!(BindPosition::bind_exprs( + for index_meta in table.indexes() { + if let Some(Value { exprs, values, .. }) = indexes.get_mut(&index_meta.id) { + let Some(data_value) = DataValue::values_to_tuple(throw!( + co, + Projection::projection(&tuple, exprs, &schema) + )) else { + continue; + }; + values.push(data_value); + } else { + let mut values = Vec::with_capacity(table.indexes().len()); + let mut exprs = throw!(co, index_meta.column_exprs(table)); + throw!( + co, + BindPosition::bind_exprs( exprs.iter_mut(), || schema.iter().map(Cow::Borrowed), |a, b| a == b - )); - let Some(data_value) = DataValue::values_to_tuple(throw!( - Projection::projection(&tuple, &exprs, &schema) - )) else { - continue; - }; - values.push(data_value); + ) + ); + let Some(data_value) = DataValue::values_to_tuple(throw!( + co, + Projection::projection(&tuple, &exprs, &schema) + )) else { + continue; + }; + values.push(data_value); - indexes.insert( - index_meta.id, - Value { - exprs, - values, - index_ty: index_meta.ty, - }, - ); - } - } - if let Some(tuple_id) = &tuple.pk { - for ( - index_id, + indexes.insert( + index_meta.id, Value { - values, index_ty, .. + exprs, + values, + index_ty: index_meta.ty, }, - ) in indexes.iter_mut() - { - for value in values { - throw!(unsafe { &mut (*transaction) }.del_index( + ); + } + } + if let Some(tuple_id) = &tuple.pk { + for ( + index_id, + Value { + values, index_ty, .. + }, + ) in indexes.iter_mut() + { + for value in values { + throw!( + co, + unsafe { &mut (*transaction) }.del_index( &table_name, &Index::new(*index_id, value, *index_ty), tuple_id, - )); - } + ) + ); } - - throw!(unsafe { &mut (*transaction) }.remove_tuple(&table_name, tuple_id)); - deleted_count += 1; } + + throw!( + co, + unsafe { &mut (*transaction) }.remove_tuple(&table_name, tuple_id) + ); + deleted_count += 1; } - drop(coroutine); - yield Ok(TupleBuilder::build_result(deleted_count.to_string())); - }, - ) + } + drop(coroutine); + co.yield_(Ok(TupleBuilder::build_result(deleted_count.to_string()))) + .await; + }) } } diff --git a/src/execution/dml/insert.rs b/src/execution/dml/insert.rs index 404334b5..3ea405bf 100644 --- a/src/execution/dml/insert.rs +++ b/src/execution/dml/insert.rs @@ -1,7 +1,7 @@ use crate::catalog::{ColumnCatalog, TableName}; use crate::errors::DatabaseError; use crate::execution::dql::projection::Projection; -use crate::execution::{build_read, Executor, WriteExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, WriteExecutor}; use crate::expression::BindPosition; use crate::planner::operator::insert::InsertOperator; use crate::planner::LogicalPlan; @@ -15,9 +15,6 @@ use crate::types::ColumnId; use itertools::Itertools; use std::borrow::Cow; use std::collections::HashMap; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; pub struct Insert { table_name: TableName, @@ -68,108 +65,121 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Insert { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let Insert { - table_name, - mut input, - is_overwrite, - is_mapping_by_name, - } = self; + spawn_executor(move |co| async move { + let Insert { + table_name, + mut input, + is_overwrite, + is_mapping_by_name, + } = self; - let schema = input.output_schema().clone(); + let schema = input.output_schema().clone(); - let primary_keys = schema - .iter() - .filter_map(|column| column.desc().primary().map(|i| (i, column))) - .sorted_by_key(|(i, _)| *i) - .map(|(_, col)| col.key(is_mapping_by_name)) - .collect_vec(); - if primary_keys.is_empty() { - throw!(Err(DatabaseError::NotNull)) - } + let primary_keys = schema + .iter() + .filter_map(|column| column.desc().primary().map(|i| (i, column))) + .sorted_by_key(|(i, _)| *i) + .map(|(_, col)| col.key(is_mapping_by_name)) + .collect_vec(); + if primary_keys.is_empty() { + throw!(co, Err(DatabaseError::NotNull)) + } - let mut inserted_count = 0; - if let Some(table_catalog) = - throw!(unsafe { &mut (*transaction) }.table(cache.0, table_name.clone())) - .cloned() - { - let mut index_metas = Vec::new(); - for index_meta in table_catalog.indexes() { - let mut exprs = throw!(index_meta.column_exprs(&table_catalog)); - throw!(BindPosition::bind_exprs( + if let Some(table_catalog) = throw!( + co, + unsafe { &mut (*transaction) }.table(cache.0, table_name.clone()) + ) + .cloned() + { + let mut index_metas = Vec::new(); + for index_meta in table_catalog.indexes() { + let mut exprs = throw!(co, index_meta.column_exprs(&table_catalog)); + throw!( + co, + BindPosition::bind_exprs( exprs.iter_mut(), || schema.iter().map(Cow::Borrowed), - |a, b| if self.is_mapping_by_name { - a.name == b.name - } else { - a == b + |a, b| { + if is_mapping_by_name { + a.name == b.name + } else { + a == b + } } - )); - index_metas.push((index_meta, exprs)); - } + ) + ); + index_metas.push((index_meta, exprs)); + } - let serializers = table_catalog - .columns() - .map(|column| column.datatype().serializable()) - .collect_vec(); - let pk_indices = table_catalog.primary_keys_indices(); - let mut coroutine = build_read(input, cache, transaction); + let serializers = table_catalog + .columns() + .map(|column| column.datatype().serializable()) + .collect_vec(); + let pk_indices = table_catalog.primary_keys_indices(); + let mut coroutine = build_read(input, cache, transaction); + let mut inserted_count = 0; - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let Tuple { values, .. } = throw!(tuple); + for tuple in coroutine.by_ref() { + let Tuple { values, .. } = throw!(co, tuple); - let mut tuple_map = HashMap::new(); - for (i, value) in values.into_iter().enumerate() { - tuple_map.insert(schema[i].key(is_mapping_by_name), value); - } - let mut values = Vec::with_capacity(table_catalog.columns_len()); + let mut tuple_map = HashMap::new(); + for (i, value) in values.into_iter().enumerate() { + tuple_map.insert(schema[i].key(is_mapping_by_name), value); + } + let mut values = Vec::with_capacity(table_catalog.columns_len()); - for col in table_catalog.columns() { - let value = { - let mut value = tuple_map.remove(&col.key(is_mapping_by_name)); + for col in table_catalog.columns() { + let value = { + let mut value = tuple_map.remove(&col.key(is_mapping_by_name)); - if value.is_none() { - value = throw!(col.default_value()); - } - value.unwrap_or(DataValue::Null) - }; - if value.is_null() && !col.nullable() { - yield Err(DatabaseError::NotNull); - return; + if value.is_none() { + value = throw!(co, col.default_value()); } - values.push(value) + value.unwrap_or(DataValue::Null) + }; + if value.is_null() && !col.nullable() { + co.yield_(Err(DatabaseError::NotNull)).await; + return; } - let pk = Tuple::primary_projection(pk_indices, &values); - let tuple = Tuple::new(Some(pk), values); + values.push(value) + } + let pk = Tuple::primary_projection(pk_indices, &values); + let tuple = Tuple::new(Some(pk), values); - for (index_meta, exprs) in index_metas.iter() { - let values = throw!(Projection::projection(&tuple, exprs, &schema)); - let Some(value) = DataValue::values_to_tuple(values) else { - continue; - }; - let tuple_id = - throw!(tuple.pk.as_ref().ok_or(DatabaseError::PrimaryKeyNotFound)); - let index = Index::new(index_meta.id, &value, index_meta.ty); - throw!(unsafe { &mut (*transaction) }.add_index( - &table_name, - index, - tuple_id - )); - } - throw!(unsafe { &mut (*transaction) }.append_tuple( + for (index_meta, exprs) in index_metas.iter() { + let values = throw!(co, Projection::projection(&tuple, exprs, &schema)); + let Some(value) = DataValue::values_to_tuple(values) else { + continue; + }; + let tuple_id = throw!( + co, + tuple.pk.as_ref().ok_or(DatabaseError::PrimaryKeyNotFound) + ); + let index = Index::new(index_meta.id, &value, index_meta.ty); + throw!( + co, + unsafe { &mut (*transaction) }.add_index(&table_name, index, tuple_id) + ); + } + throw!( + co, + unsafe { &mut (*transaction) }.append_tuple( &table_name, tuple, &serializers, is_overwrite - )); - inserted_count += 1; - } - drop(coroutine); + ) + ); + inserted_count += 1; } - yield Ok(TupleBuilder::build_result(inserted_count.to_string())); - }, - ) + drop(coroutine); + + co.yield_(Ok(TupleBuilder::build_result(inserted_count.to_string()))) + .await; + } else { + co.yield_(Ok(TupleBuilder::build_result("0".to_string()))) + .await; + } + }) } } diff --git a/src/execution/dml/update.rs b/src/execution/dml/update.rs index d4c6235c..8c945779 100644 --- a/src/execution/dml/update.rs +++ b/src/execution/dml/update.rs @@ -1,7 +1,7 @@ use crate::catalog::{ColumnRef, TableName}; use crate::errors::DatabaseError; use crate::execution::dql::projection::Projection; -use crate::execution::{build_read, Executor, WriteExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, WriteExecutor}; use crate::expression::{BindPosition, ScalarExpression}; use crate::planner::operator::update::UpdateOperator; use crate::planner::LogicalPlan; @@ -14,9 +14,6 @@ use crate::types::value::DataValue; use itertools::Itertools; use std::borrow::Cow; use std::collections::HashMap; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; pub struct Update { table_name: TableName, @@ -48,109 +45,122 @@ impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Update { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let Update { - table_name, - value_exprs, - mut input, - } = self; + spawn_executor(move |co| async move { + let Update { + table_name, + value_exprs, + mut input, + } = self; - let mut exprs_map = HashMap::with_capacity(value_exprs.len()); - for (column, expr) in value_exprs { - exprs_map.insert(column.id(), expr); - } + let mut exprs_map = HashMap::with_capacity(value_exprs.len()); + for (column, expr) in value_exprs { + exprs_map.insert(column.id(), expr); + } - let input_schema = input.output_schema().clone(); + let input_schema = input.output_schema().clone(); + + if let Some(table_catalog) = throw!( + co, + unsafe { &mut (*transaction) }.table(cache.0, table_name.clone()) + ) + .cloned() + { let serializers = input_schema .iter() .map(|column| column.datatype().serializable()) .collect_vec(); - let mut updated_count = 0; - - if let Some(table_catalog) = - throw!(unsafe { &mut (*transaction) }.table(cache.0, table_name.clone())) - .cloned() - { - let mut index_metas = Vec::new(); - for index_meta in table_catalog.indexes() { - let mut exprs = throw!(index_meta.column_exprs(&table_catalog)); - throw!(BindPosition::bind_exprs( + let mut index_metas = Vec::new(); + for index_meta in table_catalog.indexes() { + let mut exprs = throw!(co, index_meta.column_exprs(&table_catalog)); + throw!( + co, + BindPosition::bind_exprs( exprs.iter_mut(), || input_schema.iter().map(Cow::Borrowed), |a, b| a == b - )); - index_metas.push((index_meta, exprs)); - } + ) + ); + index_metas.push((index_meta, exprs)); + } - let mut coroutine = build_read(input, cache, transaction); + let mut coroutine = build_read(input, cache, transaction); + let mut updated_count = 0; - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let mut tuple: Tuple = throw!(tuple); + for tuple in coroutine.by_ref() { + let mut tuple: Tuple = throw!(co, tuple); - let mut is_overwrite = true; + let mut is_overwrite = true; - let old_pk = - throw!(tuple.pk.clone().ok_or(DatabaseError::PrimaryKeyNotFound)); - for (index_meta, exprs) in index_metas.iter() { - let values = - throw!(Projection::projection(&tuple, exprs, &input_schema)); - let Some(value) = DataValue::values_to_tuple(values) else { - continue; - }; - let index = Index::new(index_meta.id, &value, index_meta.ty); - throw!(unsafe { &mut (*transaction) }.del_index( - &table_name, - &index, - &old_pk - )); - } - for (i, column) in input_schema.iter().enumerate() { - if let Some(expr) = exprs_map.get(&column.id()) { - tuple.values[i] = throw!(expr.eval(Some((&tuple, &input_schema)))); - } + let old_pk = throw!( + co, + tuple.pk.clone().ok_or(DatabaseError::PrimaryKeyNotFound) + ); + for (index_meta, exprs) in index_metas.iter() { + let values = + throw!(co, Projection::projection(&tuple, exprs, &input_schema)); + let Some(value) = DataValue::values_to_tuple(values) else { + continue; + }; + let index = Index::new(index_meta.id, &value, index_meta.ty); + throw!( + co, + unsafe { &mut (*transaction) }.del_index(&table_name, &index, &old_pk) + ); + } + for (i, column) in input_schema.iter().enumerate() { + if let Some(expr) = exprs_map.get(&column.id()) { + tuple.values[i] = throw!(co, expr.eval(Some((&tuple, &input_schema)))); } + } - tuple.pk = Some(Tuple::primary_projection( - table_catalog.primary_keys_indices(), - &tuple.values, - )); - let new_pk = - throw!(tuple.pk.as_ref().ok_or(DatabaseError::PrimaryKeyNotFound)); + tuple.pk = Some(Tuple::primary_projection( + table_catalog.primary_keys_indices(), + &tuple.values, + )); + let new_pk = throw!( + co, + tuple.pk.as_ref().ok_or(DatabaseError::PrimaryKeyNotFound) + ); - if new_pk != &old_pk { - throw!( - unsafe { &mut (*transaction) }.remove_tuple(&table_name, &old_pk) - ); - is_overwrite = false; - } - for (index_meta, exprs) in index_metas.iter() { - let values = - throw!(Projection::projection(&tuple, exprs, &input_schema)); - let Some(value) = DataValue::values_to_tuple(values) else { - continue; - }; - let index = Index::new(index_meta.id, &value, index_meta.ty); - throw!(unsafe { &mut (*transaction) }.add_index( - &table_name, - index, - new_pk - )); - } + if new_pk != &old_pk { + throw!( + co, + unsafe { &mut (*transaction) }.remove_tuple(&table_name, &old_pk) + ); + is_overwrite = false; + } + for (index_meta, exprs) in index_metas.iter() { + let values = + throw!(co, Projection::projection(&tuple, exprs, &input_schema)); + let Some(value) = DataValue::values_to_tuple(values) else { + continue; + }; + let index = Index::new(index_meta.id, &value, index_meta.ty); + throw!( + co, + unsafe { &mut (*transaction) }.add_index(&table_name, index, new_pk) + ); + } - throw!(unsafe { &mut (*transaction) }.append_tuple( + throw!( + co, + unsafe { &mut (*transaction) }.append_tuple( &table_name, tuple, &serializers, is_overwrite - )); - updated_count += 1; - } - drop(coroutine); + ) + ); + updated_count += 1; } - yield Ok(TupleBuilder::build_result(updated_count.to_string())); - }, - ) + drop(coroutine); + + co.yield_(Ok(TupleBuilder::build_result(updated_count.to_string()))) + .await; + } else { + co.yield_(Ok(TupleBuilder::build_result("0".to_string()))) + .await; + } + }) } } diff --git a/src/execution/dql/aggregate/hash_agg.rs b/src/execution/dql/aggregate/hash_agg.rs index 7dd445cd..1ce7b8c4 100644 --- a/src/execution/dql/aggregate/hash_agg.rs +++ b/src/execution/dql/aggregate/hash_agg.rs @@ -1,6 +1,6 @@ use crate::errors::DatabaseError; use crate::execution::dql::aggregate::{create_accumulators, Accumulator}; -use crate::execution::{build_read, Executor, ReadExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, ReadExecutor}; use crate::expression::ScalarExpression; use crate::planner::operator::aggregate::AggregateOperator; use crate::planner::LogicalPlan; @@ -11,8 +11,6 @@ use crate::types::value::DataValue; use ahash::{HashMap, HashMapExt}; use itertools::Itertools; use std::collections::hash_map::Entry; -use std::ops::{Coroutine, CoroutineState}; -use std::pin::Pin; pub struct HashAggExecutor { agg_calls: Vec, @@ -45,62 +43,67 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for HashAggExecutor { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let HashAggExecutor { - agg_calls, - groupby_exprs, - mut input, - } = self; - - let schema_ref = input.output_schema().clone(); - let mut group_hash_accs: HashMap, Vec>> = - HashMap::new(); - - let mut coroutine = build_read(input, cache, transaction); - - while let CoroutineState::Yielded(result) = Pin::new(&mut coroutine).resume(()) { - let tuple = throw!(result); - let mut values = Vec::with_capacity(agg_calls.len()); - - for expr in agg_calls.iter() { - if let ScalarExpression::AggCall { args, .. } = expr { - if args.len() > 1 { - throw!(Err(DatabaseError::UnsupportedStmt("currently aggregate functions only support a single Column as a parameter".to_string()))) - } - values.push(throw!(args[0].eval(Some((&tuple, &schema_ref))))); - } else { - unreachable!() + spawn_executor(move |co| async move { + let HashAggExecutor { + agg_calls, + groupby_exprs, + mut input, + } = self; + + let schema_ref = input.output_schema().clone(); + let mut group_hash_accs: HashMap, Vec>> = + HashMap::new(); + + let mut executor = build_read(input, cache, transaction); + + for result in executor.by_ref() { + let tuple = throw!(co, result); + let mut values = Vec::with_capacity(agg_calls.len()); + + for expr in agg_calls.iter() { + if let ScalarExpression::AggCall { args, .. } = expr { + if args.len() > 1 { + throw!(co, Err(DatabaseError::UnsupportedStmt( + "currently aggregate functions only support a single Column as a parameter" + .to_string() + ))) } + values.push(throw!(co, args[0].eval(Some((&tuple, &schema_ref))))); + } else { + unreachable!() } - let group_keys: Vec = throw!(groupby_exprs + } + let group_keys: Vec = throw!( + co, + groupby_exprs .iter() .map(|expr| expr.eval(Some((&tuple, &schema_ref)))) - .try_collect()); + .try_collect() + ); - let entry = match group_hash_accs.entry(group_keys) { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => { - entry.insert(throw!(create_accumulators(&agg_calls))) - } - }; - for (acc, value) in entry.iter_mut().zip_eq(values.iter()) { - throw!(acc.update_value(value)); + let entry = match group_hash_accs.entry(group_keys) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + entry.insert(throw!(co, create_accumulators(&agg_calls))) } + }; + for (acc, value) in entry.iter_mut().zip_eq(values.iter()) { + throw!(co, acc.update_value(value)); } + } - for (group_keys, accs) in group_hash_accs { - // Tips: Accumulator First - let values: Vec = throw!(accs - .iter() + for (group_keys, accs) in group_hash_accs { + // Tips: Accumulator First + let values: Vec = throw!( + co, + accs.iter() .map(|acc| acc.evaluate()) .chain(group_keys.into_iter().map(Ok)) - .try_collect()); - yield Ok(Tuple::new(None, values)); - } - }, - ) + .try_collect() + ); + co.yield_(Ok(Tuple::new(None, values))).await; + } + }) } } @@ -188,7 +191,7 @@ mod test { }], is_distinct: false, }), - Childrens::Only(input), + Childrens::Only(Box::new(input)), ); let plan = HepOptimizer::new(plan) diff --git a/src/execution/dql/aggregate/simple_agg.rs b/src/execution/dql/aggregate/simple_agg.rs index c8f268f9..e9eb023c 100644 --- a/src/execution/dql/aggregate/simple_agg.rs +++ b/src/execution/dql/aggregate/simple_agg.rs @@ -1,5 +1,5 @@ use crate::execution::dql::aggregate::create_accumulators; -use crate::execution::{build_read, Executor, ReadExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, ReadExecutor}; use crate::expression::ScalarExpression; use crate::planner::operator::aggregate::AggregateOperator; use crate::planner::LogicalPlan; @@ -8,10 +8,6 @@ use crate::throw; use crate::types::tuple::Tuple; use crate::types::value::DataValue; use itertools::Itertools; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; - pub struct SimpleAggExecutor { agg_calls: Vec, input: LogicalPlan, @@ -31,23 +27,23 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for SimpleAggExecutor { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let SimpleAggExecutor { - agg_calls, - mut input, - } = self; + spawn_executor(move |co| async move { + let SimpleAggExecutor { + agg_calls, + mut input, + } = self; - let mut accs = throw!(create_accumulators(&agg_calls)); - let schema = input.output_schema().clone(); + let mut accs = throw!(co, create_accumulators(&agg_calls)); + let schema = input.output_schema().clone(); - let mut coroutine = build_read(input, cache, transaction); + let mut executor = build_read(input, cache, transaction); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let tuple = throw!(tuple); + for tuple in executor.by_ref() { + let tuple = throw!(co, tuple); - let values: Vec = throw!(agg_calls + let values: Vec = throw!( + co, + agg_calls .iter() .map(|expr| match expr { ScalarExpression::AggCall { args, .. } => { @@ -55,17 +51,17 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for SimpleAggExecutor { } _ => unreachable!(), }) - .try_collect()); + .try_collect() + ); - for (acc, value) in accs.iter_mut().zip_eq(values.iter()) { - throw!(acc.update_value(value)); - } + for (acc, value) in accs.iter_mut().zip_eq(values.iter()) { + throw!(co, acc.update_value(value)); } - let values: Vec = - throw!(accs.into_iter().map(|acc| acc.evaluate()).try_collect()); + } + let values: Vec = + throw!(co, accs.into_iter().map(|acc| acc.evaluate()).try_collect()); - yield Ok(Tuple::new(None, values)); - }, - ) + co.yield_(Ok(Tuple::new(None, values))).await; + }) } } diff --git a/src/execution/dql/describe.rs b/src/execution/dql/describe.rs index 14f14bb5..22ff4d6d 100644 --- a/src/execution/dql/describe.rs +++ b/src/execution/dql/describe.rs @@ -1,6 +1,6 @@ use crate::catalog::{ColumnCatalog, TableName}; use crate::execution::DatabaseError; -use crate::execution::{Executor, ReadExecutor}; +use crate::execution::{spawn_executor, Executor, ReadExecutor}; use crate::planner::operator::describe::DescribeOperator; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; @@ -45,65 +45,66 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Describe { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let table = throw!(throw!( + spawn_executor(move |co| async move { + let table = throw!( + co, + throw!( + co, unsafe { &mut (*transaction) }.table(cache.0, self.table_name.clone()) ) - .ok_or(DatabaseError::TableNotFound)); - let key_fn = |column: &ColumnCatalog| { - if column.desc().is_primary() { - PRIMARY_KEY_TYPE.clone() - } else if column.desc().is_unique() { - UNIQUE_KEY_TYPE.clone() - } else { - EMPTY_KEY_TYPE.clone() - } - }; - - for column in table.columns() { - let datatype = column.datatype(); - let default = column - .desc() - .default - .as_ref() - .map(|expr| format!("{}", expr)) - .unwrap_or_else(|| "null".to_string()); - let values = vec![ - DataValue::Utf8 { - value: column.name().to_string(), - ty: Utf8Type::Variable(None), - unit: CharLengthUnits::Characters, - }, - DataValue::Utf8 { - value: datatype.to_string(), - ty: Utf8Type::Variable(None), - unit: CharLengthUnits::Characters, - }, - DataValue::Utf8 { - value: datatype - .raw_len() - .map(|len| len.to_string()) - .unwrap_or_else(|| "variable".to_string()), - ty: Utf8Type::Variable(None), - unit: CharLengthUnits::Characters, - }, - DataValue::Utf8 { - value: column.nullable().to_string(), - ty: Utf8Type::Variable(None), - unit: CharLengthUnits::Characters, - }, - key_fn(column), - DataValue::Utf8 { - value: default, - ty: Utf8Type::Variable(None), - unit: CharLengthUnits::Characters, - }, - ]; - yield Ok(Tuple::new(None, values)); + .ok_or(DatabaseError::TableNotFound) + ); + let key_fn = |column: &ColumnCatalog| { + if column.desc().is_primary() { + PRIMARY_KEY_TYPE.clone() + } else if column.desc().is_unique() { + UNIQUE_KEY_TYPE.clone() + } else { + EMPTY_KEY_TYPE.clone() } - }, - ) + }; + + for column in table.columns() { + let datatype = column.datatype(); + let default = column + .desc() + .default + .as_ref() + .map(|expr| format!("{expr}")) + .unwrap_or_else(|| "null".to_string()); + let values = vec![ + DataValue::Utf8 { + value: column.name().to_string(), + ty: Utf8Type::Variable(None), + unit: CharLengthUnits::Characters, + }, + DataValue::Utf8 { + value: datatype.to_string(), + ty: Utf8Type::Variable(None), + unit: CharLengthUnits::Characters, + }, + DataValue::Utf8 { + value: datatype + .raw_len() + .map(|len| len.to_string()) + .unwrap_or_else(|| "variable".to_string()), + ty: Utf8Type::Variable(None), + unit: CharLengthUnits::Characters, + }, + DataValue::Utf8 { + value: column.nullable().to_string(), + ty: Utf8Type::Variable(None), + unit: CharLengthUnits::Characters, + }, + key_fn(column), + DataValue::Utf8 { + value: default, + ty: Utf8Type::Variable(None), + unit: CharLengthUnits::Characters, + }, + ]; + co.yield_(Ok(Tuple::new(None, values))).await; + } + }) } } diff --git a/src/execution/dql/dummy.rs b/src/execution/dql/dummy.rs index 6ea7b669..59733c0f 100644 --- a/src/execution/dql/dummy.rs +++ b/src/execution/dql/dummy.rs @@ -1,4 +1,4 @@ -use crate::execution::{Executor, ReadExecutor}; +use crate::execution::{spawn_executor, Executor, ReadExecutor}; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::types::tuple::Tuple; @@ -10,11 +10,8 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Dummy { _: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), _: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - yield Ok(Tuple::new(None, Vec::new())); - }, - ) + spawn_executor(move |co| async move { + co.yield_(Ok(Tuple::new(None, Vec::new()))).await; + }) } } diff --git a/src/execution/dql/except.rs b/src/execution/dql/except.rs index f38b70d8..7c3bd044 100644 --- a/src/execution/dql/except.rs +++ b/src/execution/dql/except.rs @@ -1,12 +1,8 @@ -use crate::execution::{build_read, Executor, ReadExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, ReadExecutor}; use crate::planner::LogicalPlan; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; use ahash::{HashSet, HashSetExt}; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; - pub struct Except { left_input: LogicalPlan, right_input: LogicalPlan, @@ -27,32 +23,29 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Except { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let Except { - left_input, - right_input, - } = self; + spawn_executor(move |co| async move { + let Except { + left_input, + right_input, + } = self; - let mut coroutine = build_read(right_input, cache, transaction); + let mut coroutine = build_read(right_input, cache, transaction); - let mut except_col = HashSet::new(); + let mut except_col = HashSet::new(); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let tuple = throw!(tuple); - except_col.insert(tuple); - } + for tuple in coroutine.by_ref() { + let tuple = throw!(co, tuple); + except_col.insert(tuple); + } - let mut coroutine = build_read(left_input, cache, transaction); + let coroutine = build_read(left_input, cache, transaction); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let tuple = throw!(tuple); - if !except_col.contains(&tuple) { - yield Ok(tuple); - } + for tuple in coroutine { + let tuple = throw!(co, tuple); + if !except_col.contains(&tuple) { + co.yield_(Ok(tuple)).await; } - }, - ) + } + }) } } diff --git a/src/execution/dql/explain.rs b/src/execution/dql/explain.rs index 2ca71d63..c8dfd26e 100644 --- a/src/execution/dql/explain.rs +++ b/src/execution/dql/explain.rs @@ -1,4 +1,4 @@ -use crate::execution::{Executor, ReadExecutor}; +use crate::execution::{spawn_executor, Executor, ReadExecutor}; use crate::planner::LogicalPlan; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::types::tuple::Tuple; @@ -21,17 +21,14 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Explain { _: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), _: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let values = vec![DataValue::Utf8 { - value: self.plan.explain(0), - ty: Utf8Type::Variable(None), - unit: CharLengthUnits::Characters, - }]; + spawn_executor(move |co| async move { + let values = vec![DataValue::Utf8 { + value: self.plan.explain(0), + ty: Utf8Type::Variable(None), + unit: CharLengthUnits::Characters, + }]; - yield Ok(Tuple::new(None, values)); - }, - ) + co.yield_(Ok(Tuple::new(None, values))).await; + }) } } diff --git a/src/execution/dql/filter.rs b/src/execution/dql/filter.rs index 21ce815a..e235e5d1 100644 --- a/src/execution/dql/filter.rs +++ b/src/execution/dql/filter.rs @@ -1,13 +1,9 @@ -use crate::execution::{build_read, Executor, ReadExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, ReadExecutor}; use crate::expression::ScalarExpression; use crate::planner::operator::filter::FilterOperator; use crate::planner::LogicalPlan; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; - pub struct Filter { predicate: ScalarExpression, input: LogicalPlan, @@ -25,26 +21,26 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Filter { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let Filter { - predicate, - mut input, - } = self; + spawn_executor(move |co| async move { + let Filter { + predicate, + mut input, + } = self; - let schema = input.output_schema().clone(); + let schema = input.output_schema().clone(); - let mut coroutine = build_read(input, cache, transaction); + let executor = build_read(input, cache, transaction); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let tuple = throw!(tuple); + for tuple in executor { + let tuple = throw!(co, tuple); - if throw!(throw!(predicate.eval(Some((&tuple, &schema)))).is_true()) { - yield Ok(tuple); - } + if throw!( + co, + throw!(co, predicate.eval(Some((&tuple, &schema)))).is_true() + ) { + co.yield_(Ok(tuple)).await; } - }, - ) + } + }) } } diff --git a/src/execution/dql/function_scan.rs b/src/execution/dql/function_scan.rs index e7936c31..bc4db579 100644 --- a/src/execution/dql/function_scan.rs +++ b/src/execution/dql/function_scan.rs @@ -1,4 +1,4 @@ -use crate::execution::{Executor, ReadExecutor}; +use crate::execution::{spawn_executor, Executor, ReadExecutor}; use crate::expression::function::table::TableFunction; use crate::planner::operator::function_scan::FunctionScanOperator; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; @@ -22,14 +22,11 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for FunctionScan { _: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), _: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let TableFunction { args, inner } = self.table_function; - for tuple in throw!(inner.eval(&args)) { - yield tuple; - } - }, - ) + spawn_executor(move |co| async move { + let TableFunction { args, inner } = self.table_function; + for tuple in throw!(co, inner.eval(&args)) { + co.yield_(tuple).await; + } + }) } } diff --git a/src/execution/dql/index_scan.rs b/src/execution/dql/index_scan.rs index 607302ef..773e6ece 100644 --- a/src/execution/dql/index_scan.rs +++ b/src/execution/dql/index_scan.rs @@ -1,4 +1,4 @@ -use crate::execution::{Executor, ReadExecutor}; +use crate::execution::{spawn_executor, Executor, ReadExecutor}; use crate::expression::range_detacher::Range; use crate::planner::operator::table_scan::TableScanOperator; use crate::storage::{Iter, StatisticsMetaCache, TableCache, Transaction, ViewCache}; @@ -32,31 +32,31 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for IndexScan { (table_cache, _, _): (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let TableScanOperator { - table_name, - columns, - limit, - with_pk, - .. - } = self.op; + spawn_executor(move |co| async move { + let TableScanOperator { + table_name, + columns, + limit, + with_pk, + .. + } = self.op; - let mut iter = throw!(unsafe { &(*transaction) }.read_by_index( + let mut iter = throw!( + co, + unsafe { &(*transaction) }.read_by_index( table_cache, table_name, limit, columns, self.index_by, self.ranges, - with_pk - )); + with_pk, + ) + ); - while let Some(tuple) = throw!(iter.next_tuple()) { - yield Ok(tuple); - } - }, - ) + while let Some(tuple) = throw!(co, iter.next_tuple()) { + co.yield_(Ok(tuple)).await; + } + }) } } diff --git a/src/execution/dql/join/hash/full_join.rs b/src/execution/dql/join/hash/full_join.rs index 8c8530f7..da5d1632 100644 --- a/src/execution/dql/join/hash/full_join.rs +++ b/src/execution/dql/join/hash/full_join.rs @@ -1,7 +1,7 @@ use crate::execution::dql::join::hash::{filter, FilterArgs, JoinProbeState, ProbeArgs}; use crate::execution::dql::join::hash_join::BuildState; use crate::execution::dql::sort::BumpVec; -use crate::execution::Executor; +use crate::execution::{spawn_executor, Executor}; use crate::throw; use crate::types::tuple::Tuple; use crate::types::value::DataValue; @@ -23,45 +23,44 @@ impl<'a> JoinProbeState<'a> for FullJoinState { let left_schema_len = self.left_schema_len; let bits_ptr: *mut FixedBitSet = &mut self.bits; - Box::new( - #[coroutine] - move || { - let ProbeArgs { probe_tuple, .. } = probe_args; + spawn_executor(move |co| async move { + let ProbeArgs { probe_tuple, .. } = probe_args; - if let ProbeArgs { - is_keys_has_null: false, - build_state: Some(build_state), - .. - } = probe_args - { - let mut has_filtered = false; - for (i, Tuple { values, pk }) in build_state.tuples.iter() { - let full_values = - Vec::from_iter(values.iter().chain(probe_tuple.values.iter()).cloned()); + if let ProbeArgs { + is_keys_has_null: false, + build_state: Some(build_state), + .. + } = probe_args + { + let mut has_filtered = false; + for (i, Tuple { values, pk }) in build_state.tuples.iter() { + let full_values = + Vec::from_iter(values.iter().chain(probe_tuple.values.iter()).cloned()); - match &filter_args { - None => (), - Some(filter_args) => { - if !throw!(filter(&full_values, filter_args)) { - has_filtered = true; - unsafe { - (*bits_ptr).set(*i, true); - } - yield Ok(Self::full_right_row(left_schema_len, &probe_tuple)); - continue; + match &filter_args { + None => (), + Some(filter_args) => { + if !throw!(co, filter(&full_values, filter_args)) { + has_filtered = true; + unsafe { + (*bits_ptr).set(*i, true); } + co.yield_(Ok(Self::full_right_row(left_schema_len, &probe_tuple))) + .await; + continue; } } - yield Ok(Tuple::new(pk.clone(), full_values)); } - build_state.is_used = !has_filtered; - build_state.has_filted = has_filtered; - return; + co.yield_(Ok(Tuple::new(pk.clone(), full_values))).await; } + build_state.is_used = !has_filtered; + build_state.has_filted = has_filtered; + return; + } - yield Ok(Self::full_right_row(left_schema_len, &probe_tuple)); - }, - ) + co.yield_(Ok(Self::full_right_row(left_schema_len, &probe_tuple))) + .await; + }) } fn left_drop( @@ -72,25 +71,22 @@ impl<'a> JoinProbeState<'a> for FullJoinState { let full_schema_len = self.right_schema_len + self.left_schema_len; let bits_ptr: *mut FixedBitSet = &mut self.bits; - Some(Box::new( - #[coroutine] - move || { - for (_, state) in _build_map { - if state.is_used { - continue; - } - for (i, mut left_tuple) in state.tuples { - unsafe { - if !(*bits_ptr).contains(i) && state.has_filted { - continue; - } + Some(spawn_executor(move |co| async move { + for (_, state) in _build_map { + if state.is_used { + continue; + } + for (i, mut left_tuple) in state.tuples { + unsafe { + if !(*bits_ptr).contains(i) && state.has_filted { + continue; } - left_tuple.values.resize(full_schema_len, DataValue::Null); - yield Ok(left_tuple); } + left_tuple.values.resize(full_schema_len, DataValue::Null); + co.yield_(Ok(left_tuple)).await; } - }, - )) + } + })) } } diff --git a/src/execution/dql/join/hash/inner_join.rs b/src/execution/dql/join/hash/inner_join.rs index 2b0f3298..5b12b7b6 100644 --- a/src/execution/dql/join/hash/inner_join.rs +++ b/src/execution/dql/join/hash/inner_join.rs @@ -1,5 +1,5 @@ use crate::execution::dql::join::hash::{filter, FilterArgs, JoinProbeState, ProbeArgs}; -use crate::execution::Executor; +use crate::execution::{spawn_executor, Executor}; use crate::throw; use crate::types::tuple::Tuple; @@ -11,35 +11,32 @@ impl<'a> JoinProbeState<'a> for InnerJoinState { probe_args: ProbeArgs<'a>, filter_args: Option<&'a FilterArgs>, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let ProbeArgs { - is_keys_has_null: false, - probe_tuple, - build_state: Some(build_state), - .. - } = probe_args - else { - return; - }; + spawn_executor(move |co| async move { + let ProbeArgs { + is_keys_has_null: false, + probe_tuple, + build_state: Some(build_state), + .. + } = probe_args + else { + return; + }; - build_state.is_used = true; - for (_, Tuple { values, pk }) in build_state.tuples.iter() { - let full_values = - Vec::from_iter(values.iter().chain(probe_tuple.values.iter()).cloned()); + build_state.is_used = true; + for (_, Tuple { values, pk }) in build_state.tuples.iter() { + let full_values = + Vec::from_iter(values.iter().chain(probe_tuple.values.iter()).cloned()); - match &filter_args { - None => (), - Some(filter_args) => { - if !throw!(filter(&full_values, filter_args)) { - continue; - } + match &filter_args { + None => (), + Some(filter_args) => { + if !throw!(co, filter(&full_values, filter_args)) { + continue; } } - yield Ok(Tuple::new(pk.clone(), full_values)); } - }, - ) + co.yield_(Ok(Tuple::new(pk.clone(), full_values))).await; + } + }) } } diff --git a/src/execution/dql/join/hash/left_anti_join.rs b/src/execution/dql/join/hash/left_anti_join.rs index fe1d5b1b..1131d688 100644 --- a/src/execution/dql/join/hash/left_anti_join.rs +++ b/src/execution/dql/join/hash/left_anti_join.rs @@ -2,7 +2,7 @@ use crate::execution::dql::join::hash::left_semi_join::LeftSemiJoinState; use crate::execution::dql::join::hash::{filter, FilterArgs, JoinProbeState, ProbeArgs}; use crate::execution::dql::join::hash_join::BuildState; use crate::execution::dql::sort::BumpVec; -use crate::execution::Executor; +use crate::execution::{spawn_executor, Executor}; use crate::throw; use crate::types::value::DataValue; use ahash::HashMap; @@ -29,43 +29,40 @@ impl<'a> JoinProbeState<'a> for LeftAntiJoinState { ) -> Option> { let bits_ptr: *mut FixedBitSet = &mut self.inner.bits; let right_schema_len = self.right_schema_len; - Some(Box::new( - #[coroutine] - move || { - for ( - _, - BuildState { - tuples, - is_used, - has_filted, - }, - ) in _build_map - { - if is_used { - continue; - } - for (i, tuple) in tuples { - unsafe { - if (*bits_ptr).contains(i) && has_filted { - continue; - } + Some(spawn_executor(move |co| async move { + for ( + _, + BuildState { + tuples, + is_used, + has_filted, + }, + ) in _build_map + { + if is_used { + continue; + } + for (i, tuple) in tuples { + unsafe { + if (*bits_ptr).contains(i) && has_filted { + continue; } - if let Some(filter_args) = filter_args { - let full_values = Vec::from_iter( - tuple - .values - .iter() - .cloned() - .chain((0..right_schema_len).map(|_| DataValue::Null)), - ); - if !throw!(filter(&full_values, filter_args)) { - continue; - } + } + if let Some(filter_args) = filter_args { + let full_values = Vec::from_iter( + tuple + .values + .iter() + .cloned() + .chain((0..right_schema_len).map(|_| DataValue::Null)), + ); + if !throw!(co, filter(&full_values, filter_args)) { + continue; } - yield Ok(tuple); } + co.yield_(Ok(tuple)).await; } - }, - )) + } + })) } } diff --git a/src/execution/dql/join/hash/left_join.rs b/src/execution/dql/join/hash/left_join.rs index 3e727508..5bb7f02f 100644 --- a/src/execution/dql/join/hash/left_join.rs +++ b/src/execution/dql/join/hash/left_join.rs @@ -1,7 +1,7 @@ use crate::execution::dql::join::hash::{filter, FilterArgs, JoinProbeState, ProbeArgs}; use crate::execution::dql::join::hash_join::BuildState; use crate::execution::dql::sort::BumpVec; -use crate::execution::Executor; +use crate::execution::{spawn_executor, Executor}; use crate::throw; use crate::types::tuple::Tuple; use crate::types::value::DataValue; @@ -21,42 +21,39 @@ impl<'a> JoinProbeState<'a> for LeftJoinState { filter_args: Option<&'a FilterArgs>, ) -> Executor<'a> { let bits_ptr: *mut FixedBitSet = &mut self.bits; - Box::new( - #[coroutine] - move || { - let ProbeArgs { - is_keys_has_null: false, - probe_tuple, - build_state: Some(build_state), - .. - } = probe_args - else { - return; - }; + spawn_executor(move |co| async move { + let ProbeArgs { + is_keys_has_null: false, + probe_tuple, + build_state: Some(build_state), + .. + } = probe_args + else { + return; + }; - let mut has_filted = false; - for (i, Tuple { values, pk }) in build_state.tuples.iter() { - let full_values = - Vec::from_iter(values.iter().chain(probe_tuple.values.iter()).cloned()); + let mut has_filted = false; + for (i, Tuple { values, pk }) in build_state.tuples.iter() { + let full_values = + Vec::from_iter(values.iter().chain(probe_tuple.values.iter()).cloned()); - match &filter_args { - None => (), - Some(filter_args) => { - if !throw!(filter(&full_values, filter_args)) { - has_filted = true; - unsafe { - (*bits_ptr).set(*i, true); - } - continue; + match &filter_args { + None => (), + Some(filter_args) => { + if !throw!(co, filter(&full_values, filter_args)) { + has_filted = true; + unsafe { + (*bits_ptr).set(*i, true); } + continue; } } - yield Ok(Tuple::new(pk.clone(), full_values)); } - build_state.is_used = !has_filted; - build_state.has_filted = has_filted; - }, - ) + co.yield_(Ok(Tuple::new(pk.clone(), full_values))).await; + } + build_state.is_used = !has_filted; + build_state.has_filted = has_filted; + }) } fn left_drop( @@ -67,24 +64,21 @@ impl<'a> JoinProbeState<'a> for LeftJoinState { let full_schema_len = self.right_schema_len + self.left_schema_len; let bits_ptr: *mut FixedBitSet = &mut self.bits; - Some(Box::new( - #[coroutine] - move || { - for (_, state) in _build_map { - if state.is_used { - continue; - } - for (i, mut left_tuple) in state.tuples { - unsafe { - if !(*bits_ptr).contains(i) && state.has_filted { - continue; - } + Some(spawn_executor(move |co| async move { + for (_, state) in _build_map { + if state.is_used { + continue; + } + for (i, mut left_tuple) in state.tuples { + unsafe { + if !(*bits_ptr).contains(i) && state.has_filted { + continue; } - left_tuple.values.resize(full_schema_len, DataValue::Null); - yield Ok(left_tuple); } + left_tuple.values.resize(full_schema_len, DataValue::Null); + co.yield_(Ok(left_tuple)).await; } - }, - )) + } + })) } } diff --git a/src/execution/dql/join/hash/left_semi_join.rs b/src/execution/dql/join/hash/left_semi_join.rs index 6a98a249..2c7be2cf 100644 --- a/src/execution/dql/join/hash/left_semi_join.rs +++ b/src/execution/dql/join/hash/left_semi_join.rs @@ -1,7 +1,7 @@ use crate::execution::dql::join::hash::{filter, FilterArgs, JoinProbeState, ProbeArgs}; use crate::execution::dql::join::hash_join::BuildState; use crate::execution::dql::sort::BumpVec; -use crate::execution::Executor; +use crate::execution::{spawn_executor, Executor}; use crate::throw; use crate::types::tuple::Tuple; use crate::types::value::DataValue; @@ -19,41 +19,38 @@ impl<'a> JoinProbeState<'a> for LeftSemiJoinState { filter_args: Option<&'a FilterArgs>, ) -> Executor<'a> { let bits_ptr: *mut FixedBitSet = &mut self.bits; - Box::new( - #[coroutine] - move || { - let ProbeArgs { - is_keys_has_null: false, - probe_tuple, - build_state: Some(build_state), - .. - } = probe_args - else { - return; - }; + spawn_executor(move |co| async move { + let ProbeArgs { + is_keys_has_null: false, + probe_tuple, + build_state: Some(build_state), + .. + } = probe_args + else { + return; + }; - let mut has_filted = false; - for (i, Tuple { values, .. }) in build_state.tuples.iter() { - let full_values = - Vec::from_iter(values.iter().chain(probe_tuple.values.iter()).cloned()); + let mut has_filted = false; + for (i, Tuple { values, .. }) in build_state.tuples.iter() { + let full_values = + Vec::from_iter(values.iter().chain(probe_tuple.values.iter()).cloned()); - match &filter_args { - None => (), - Some(filter_args) => { - if !throw!(filter(&full_values, filter_args)) { - has_filted = true; - unsafe { - (*bits_ptr).set(*i, true); - } - continue; + match &filter_args { + None => (), + Some(filter_args) => { + if !throw!(co, filter(&full_values, filter_args)) { + has_filted = true; + unsafe { + (*bits_ptr).set(*i, true); } + continue; } } } - build_state.is_used = true; - build_state.has_filted = has_filted; - }, - ) + } + build_state.is_used = true; + build_state.has_filted = has_filted; + }) } fn left_drop( @@ -62,31 +59,28 @@ impl<'a> JoinProbeState<'a> for LeftSemiJoinState { _filter_args: Option<&'a FilterArgs>, ) -> Option> { let bits_ptr: *mut FixedBitSet = &mut self.bits; - Some(Box::new( - #[coroutine] - move || { - for ( - _, - BuildState { - tuples, - is_used, - has_filted, - }, - ) in _build_map - { - if !is_used { - continue; - } - for (i, tuple) in tuples { - unsafe { - if (*bits_ptr).contains(i) && has_filted { - continue; - } + Some(spawn_executor(move |co| async move { + for ( + _, + BuildState { + tuples, + is_used, + has_filted, + }, + ) in _build_map + { + if !is_used { + continue; + } + for (i, tuple) in tuples { + unsafe { + if (*bits_ptr).contains(i) && has_filted { + continue; } - yield Ok(tuple); } + co.yield_(Ok(tuple)).await; } - }, - )) + } + })) } } diff --git a/src/execution/dql/join/hash/right_join.rs b/src/execution/dql/join/hash/right_join.rs index f84cab97..58048d06 100644 --- a/src/execution/dql/join/hash/right_join.rs +++ b/src/execution/dql/join/hash/right_join.rs @@ -1,6 +1,6 @@ use crate::execution::dql::join::hash::full_join::FullJoinState; use crate::execution::dql::join::hash::{filter, FilterArgs, JoinProbeState, ProbeArgs}; -use crate::execution::Executor; +use crate::execution::{spawn_executor, Executor}; use crate::throw; use crate::types::tuple::Tuple; @@ -16,44 +16,46 @@ impl<'a> JoinProbeState<'a> for RightJoinState { ) -> Executor<'a> { let left_schema_len = self.left_schema_len; - Box::new( - #[coroutine] - move || { - let ProbeArgs { probe_tuple, .. } = probe_args; + spawn_executor(move |co| async move { + let ProbeArgs { probe_tuple, .. } = probe_args; - if let ProbeArgs { - is_keys_has_null: false, - build_state: Some(build_state), - .. - } = probe_args - { - let mut has_filtered = false; - for (_, Tuple { values, pk }) in build_state.tuples.iter() { - let full_values = - Vec::from_iter(values.iter().chain(probe_tuple.values.iter()).cloned()); + if let ProbeArgs { + is_keys_has_null: false, + build_state: Some(build_state), + .. + } = probe_args + { + let mut has_filtered = false; + for (_, Tuple { values, pk }) in build_state.tuples.iter() { + let full_values = + Vec::from_iter(values.iter().chain(probe_tuple.values.iter()).cloned()); - match &filter_args { - None => (), - Some(filter_args) => { - if !throw!(filter(&full_values, filter_args)) { - has_filtered = true; - yield Ok(FullJoinState::full_right_row( - left_schema_len, - &probe_tuple, - )); - continue; - } + match &filter_args { + None => (), + Some(filter_args) => { + if !throw!(co, filter(&full_values, filter_args)) { + has_filtered = true; + co.yield_(Ok(FullJoinState::full_right_row( + left_schema_len, + &probe_tuple, + ))) + .await; + continue; } } - yield Ok(Tuple::new(pk.clone(), full_values)); } - build_state.is_used = !has_filtered; - build_state.has_filted = has_filtered; - return; + co.yield_(Ok(Tuple::new(pk.clone(), full_values))).await; } + build_state.is_used = !has_filtered; + build_state.has_filted = has_filtered; + return; + } - yield Ok(FullJoinState::full_right_row(left_schema_len, &probe_tuple)); - }, - ) + co.yield_(Ok(FullJoinState::full_right_row( + left_schema_len, + &probe_tuple, + ))) + .await; + }) } } diff --git a/src/execution/dql/join/hash_join.rs b/src/execution/dql/join/hash_join.rs index d2cb9419..3e4db1d3 100644 --- a/src/execution/dql/join/hash_join.rs +++ b/src/execution/dql/join/hash_join.rs @@ -11,7 +11,7 @@ use crate::execution::dql::join::hash::{ }; use crate::execution::dql::join::joins_nullable; use crate::execution::dql::sort::BumpVec; -use crate::execution::{build_read, Executor, ReadExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, ReadExecutor}; use crate::expression::ScalarExpression; use crate::planner::operator::join::{JoinCondition, JoinOperator, JoinType}; use crate::planner::LogicalPlan; @@ -22,9 +22,6 @@ use crate::types::value::DataValue; use ahash::{HashMap, HashMapExt}; use bumpalo::Bump; use fixedbitset::FixedBitSet; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; use std::sync::Arc; pub struct HashJoin { @@ -82,136 +79,142 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for HashJoin { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let HashJoin { - on, - ty, - mut left_input, - mut right_input, - mut bump, - } = self; - - if ty == JoinType::Cross { - unreachable!("Cross join should not be in HashJoinExecutor"); - } - let ((on_left_keys, on_right_keys), filter): ( - (Vec, Vec), - _, - ) = match on { - JoinCondition::On { on, filter } => (on.into_iter().unzip(), filter), - JoinCondition::None => unreachable!("HashJoin must has on condition"), - }; - if on_left_keys.is_empty() || on_right_keys.is_empty() { - throw!(Err(DatabaseError::UnsupportedStmt( + spawn_executor(move |co| async move { + let HashJoin { + on, + ty, + mut left_input, + mut right_input, + mut bump, + } = self; + + if ty == JoinType::Cross { + unreachable!("Cross join should not be in HashJoinExecutor"); + } + let ((on_left_keys, on_right_keys), filter): ( + (Vec, Vec), + _, + ) = match on { + JoinCondition::On { on, filter } => (on.into_iter().unzip(), filter), + JoinCondition::None => unreachable!("HashJoin must has on condition"), + }; + if on_left_keys.is_empty() || on_right_keys.is_empty() { + throw!( + co, + Err(DatabaseError::UnsupportedStmt( "`NestLoopJoin` should be used when there is no equivalent condition" .to_string() - ))) - } - debug_assert!(!on_left_keys.is_empty()); - debug_assert!(!on_right_keys.is_empty()); - - let fn_process = |schema: &mut [ColumnRef], force_nullable| { - for column in schema.iter_mut() { - if let Some(new_column) = column.nullable_for_join(force_nullable) { - *column = new_column; - } + )) + ) + } + debug_assert!(!on_left_keys.is_empty()); + debug_assert!(!on_right_keys.is_empty()); + + let fn_process = |schema: &mut [ColumnRef], force_nullable| { + for column in schema.iter_mut() { + if let Some(new_column) = column.nullable_for_join(force_nullable) { + *column = new_column; } - }; + } + }; - let (left_force_nullable, right_force_nullable) = joins_nullable(&ty); + let (left_force_nullable, right_force_nullable) = joins_nullable(&ty); - let mut full_schema_ref = Vec::clone(left_input.output_schema()); - let left_schema_len = full_schema_ref.len(); + let mut full_schema_ref = Vec::clone(left_input.output_schema()); + let left_schema_len = full_schema_ref.len(); - fn_process(&mut full_schema_ref, left_force_nullable); - full_schema_ref.extend_from_slice(right_input.output_schema()); - fn_process( - &mut full_schema_ref[left_schema_len..], - right_force_nullable, - ); - let right_schema_len = full_schema_ref.len() - left_schema_len; - let full_schema_ref = Arc::new(full_schema_ref); - - // build phase: - // 1.construct hashtable, one hash key may contains multiple rows indices. - // 2.merged all left tuples. - let mut coroutine = build_read(left_input, cache, transaction); - let mut build_map = HashMap::new(); - let bump_ptr: *mut Bump = &mut bump; - let build_map_ptr: *mut HashMap, BuildState> = &mut build_map; - - let mut buf_row = - BumpVec::with_capacity_in(on_left_keys.len(), unsafe { &mut (*bump_ptr) }); - - let mut build_count = 0; - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let tuple: Tuple = throw!(tuple); - throw!(Self::eval_keys( + fn_process(&mut full_schema_ref, left_force_nullable); + full_schema_ref.extend_from_slice(right_input.output_schema()); + fn_process( + &mut full_schema_ref[left_schema_len..], + right_force_nullable, + ); + let right_schema_len = full_schema_ref.len() - left_schema_len; + let full_schema_ref = Arc::new(full_schema_ref); + + // build phase: + // 1.construct hashtable, one hash key may contains multiple rows indices. + // 2.merged all left tuples. + let mut coroutine = build_read(left_input, cache, transaction); + let mut build_map = HashMap::new(); + let bump_ptr: *mut Bump = &mut bump; + let build_map_ptr: *mut HashMap, BuildState> = &mut build_map; + + let mut buf_row = + BumpVec::with_capacity_in(on_left_keys.len(), unsafe { &mut (*bump_ptr) }); + + let mut build_count = 0; + for tuple in coroutine.by_ref() { + let tuple: Tuple = throw!(co, tuple); + throw!( + co, + Self::eval_keys( &on_left_keys, &tuple, &full_schema_ref[0..left_schema_len], &mut buf_row, - )); - - let build_map_ref = unsafe { &mut (*build_map_ptr) }; - match build_map_ref.get_mut(&buf_row) { - None => { - build_map_ref.insert( - buf_row.clone(), - BuildState { - tuples: vec![(build_count, tuple)], - ..Default::default() - }, - ); - } - Some(BuildState { tuples, .. }) => tuples.push((build_count, tuple)), + ) + ); + + let build_map_ref = unsafe { &mut (*build_map_ptr) }; + match build_map_ref.get_mut(&buf_row) { + None => { + build_map_ref.insert( + buf_row.clone(), + BuildState { + tuples: vec![(build_count, tuple)], + ..Default::default() + }, + ); } - build_count += 1; + Some(BuildState { tuples, .. }) => tuples.push((build_count, tuple)), } - let mut join_impl = - Self::create_join_impl(self.ty, left_schema_len, right_schema_len, build_count); - let mut filter_arg = filter.map(|expr| FilterArgs { - full_schema: full_schema_ref.clone(), - filter_expr: expr, - }); - let filter_arg_ptr: *mut Option = &mut filter_arg; + build_count += 1; + } + let mut join_impl = + Self::create_join_impl(self.ty, left_schema_len, right_schema_len, build_count); + let mut filter_arg = filter.map(|expr| FilterArgs { + full_schema: full_schema_ref.clone(), + filter_expr: expr, + }); + let filter_arg_ptr: *mut Option = &mut filter_arg; - // probe phase - let mut coroutine = build_read(right_input, cache, transaction); + // probe phase + let mut coroutine = build_read(right_input, cache, transaction); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let tuple: Tuple = throw!(tuple); + for tuple in coroutine.by_ref() { + let tuple: Tuple = throw!(co, tuple); - throw!(Self::eval_keys( + throw!( + co, + Self::eval_keys( &on_right_keys, &tuple, &full_schema_ref[left_schema_len..], &mut buf_row - )); - let build_value = unsafe { (*build_map_ptr).get_mut(&buf_row) }; - - let probe_args = ProbeArgs { - is_keys_has_null: buf_row.iter().any(|value| value.is_null()), - probe_tuple: tuple, - build_state: build_value, - }; - let mut executor = - join_impl.probe(probe_args, unsafe { &mut (*filter_arg_ptr) }.as_ref()); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut executor).resume(()) { - yield tuple; - } - } + ) + ); + let build_value = unsafe { (*build_map_ptr).get_mut(&buf_row) }; + + let probe_args = ProbeArgs { + is_keys_has_null: buf_row.iter().any(|value| value.is_null()), + probe_tuple: tuple, + build_state: build_value, + }; let executor = - join_impl.left_drop(build_map, unsafe { &mut (*filter_arg_ptr) }.as_ref()); - if let Some(mut executor) = executor { - while let CoroutineState::Yielded(tuple) = Pin::new(&mut executor).resume(()) { - yield tuple; - } + join_impl.probe(probe_args, unsafe { &mut (*filter_arg_ptr) }.as_ref()); + for tuple in executor { + co.yield_(tuple).await; } - }, - ) + } + if let Some(executor) = + join_impl.left_drop(build_map, unsafe { &mut (*filter_arg_ptr) }.as_ref()) + { + for tuple in executor { + co.yield_(tuple).await; + } + } + }) } } @@ -377,7 +380,10 @@ mod test { }, join_type: JoinType::Inner, }), - Childrens::Twins { left, right }, + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), + }, ); let plan = HepOptimizer::new(plan) .batch( @@ -435,7 +441,10 @@ mod test { }, join_type: JoinType::LeftOuter, }), - Childrens::Twins { left, right }, + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), + }, ); let plan = HepOptimizer::new(plan) .batch( @@ -540,7 +549,10 @@ mod test { }, join_type: JoinType::RightOuter, }), - Childrens::Twins { left, right }, + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), + }, ); let plan = HepOptimizer::new(plan) .batch( @@ -602,7 +614,10 @@ mod test { }, join_type: JoinType::Full, }), - Childrens::Twins { left, right }, + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), + }, ); let plan = HepOptimizer::new(plan) .batch( diff --git a/src/execution/dql/join/nested_loop_join.rs b/src/execution/dql/join/nested_loop_join.rs index 1da1b466..e15a19f7 100644 --- a/src/execution/dql/join/nested_loop_join.rs +++ b/src/execution/dql/join/nested_loop_join.rs @@ -5,8 +5,8 @@ use super::joins_nullable; use crate::catalog::ColumnRef; use crate::errors::DatabaseError; use crate::execution::dql::projection::Projection; -use crate::execution::{build_read, Executor, ReadExecutor}; -use crate::expression::{BindPosition, ScalarExpression}; +use crate::execution::{build_read, spawn_executor, Executor, ReadExecutor}; +use crate::expression::ScalarExpression; use crate::planner::operator::join::{JoinCondition, JoinOperator, JoinType}; use crate::planner::LogicalPlan; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; @@ -15,10 +15,6 @@ use crate::types::tuple::{Schema, SchemaRef, Tuple}; use crate::types::value::DataValue; use fixedbitset::FixedBitSet; use itertools::Itertools; -use std::borrow::Cow; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; use std::sync::Arc; /// Equivalent condition @@ -132,163 +128,132 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for NestedLoopJoin { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let NestedLoopJoin { - ty, - left_input, - right_input, - output_schema_ref, - filter, - mut eq_cond, - .. - } = self; - - throw!(BindPosition::bind_exprs( - eq_cond.on_left_keys.iter_mut(), - || eq_cond.left_schema.iter().map(Cow::Borrowed), - |a, b| a == b - )); - throw!(BindPosition::bind_exprs( - eq_cond.on_right_keys.iter_mut(), - || eq_cond.right_schema.iter().map(Cow::Borrowed), - |a, b| a == b - )); - - let right_schema_len = eq_cond.right_schema.len(); - let mut left_coroutine = build_read(left_input, cache, transaction); - let mut bitmap: Option = None; - let mut first_matches = Vec::new(); - - while let CoroutineState::Yielded(left_tuple) = - Pin::new(&mut left_coroutine).resume(()) - { - let left_tuple: Tuple = throw!(left_tuple); - let mut has_matched = false; - - let mut right_coroutine = build_read(right_input.clone(), cache, transaction); - let mut right_idx = 0; - - while let CoroutineState::Yielded(right_tuple) = - Pin::new(&mut right_coroutine).resume(()) - { - let right_tuple: Tuple = throw!(right_tuple); - - let tuple = match ( - filter.as_ref(), - throw!(eq_cond.equals(&left_tuple, &right_tuple)), - ) { - (None, true) if matches!(ty, JoinType::RightOuter) => { - has_matched = true; - Self::emit_tuple(&right_tuple, &left_tuple, ty, true) - } - (None, true) => { - has_matched = true; - Self::emit_tuple(&left_tuple, &right_tuple, ty, true) - } - (Some(filter), true) => { - let new_tuple = Self::merge_tuple(&left_tuple, &right_tuple, &ty); - let value = - throw!(filter.eval(Some((&new_tuple, &output_schema_ref)))); - match &value { - DataValue::Boolean(true) => { - let tuple = match ty { - JoinType::LeftAnti => None, - JoinType::LeftSemi if has_matched => None, - JoinType::RightOuter => Self::emit_tuple( - &right_tuple, - &left_tuple, - ty, - true, - ), - _ => Self::emit_tuple( - &left_tuple, - &right_tuple, - ty, - true, - ), - }; - has_matched = true; - tuple - } - DataValue::Boolean(false) | DataValue::Null => None, - _ => { - yield Err(DatabaseError::InvalidType); - return; - } + spawn_executor(move |co| async move { + let NestedLoopJoin { + ty, + left_input, + right_input, + output_schema_ref, + filter, + eq_cond, + .. + } = self; + + let right_schema_len = eq_cond.right_schema.len(); + let mut left_coroutine = build_read(left_input, cache, transaction); + let mut bitmap: Option = None; + let mut first_matches = Vec::new(); + + for left_tuple in left_coroutine.by_ref() { + let left_tuple: Tuple = throw!(co, left_tuple); + let mut has_matched = false; + + let mut right_coroutine = build_read(right_input.clone(), cache, transaction); + let mut right_idx = 0; + + for right_tuple in right_coroutine.by_ref() { + let right_tuple: Tuple = throw!(co, right_tuple); + + let tuple = match ( + filter.as_ref(), + throw!(co, eq_cond.equals(&left_tuple, &right_tuple)), + ) { + (None, true) if matches!(ty, JoinType::RightOuter) => { + has_matched = true; + Self::emit_tuple(&right_tuple, &left_tuple, ty, true) + } + (None, true) => { + has_matched = true; + Self::emit_tuple(&left_tuple, &right_tuple, ty, true) + } + (Some(filter), true) => { + let new_tuple = Self::merge_tuple(&left_tuple, &right_tuple, &ty); + let value = + throw!(co, filter.eval(Some((&new_tuple, &output_schema_ref)))); + match &value { + DataValue::Boolean(true) => { + let tuple = match ty { + JoinType::LeftAnti => None, + JoinType::LeftSemi if has_matched => None, + JoinType::RightOuter => { + Self::emit_tuple(&right_tuple, &left_tuple, ty, true) + } + _ => Self::emit_tuple(&left_tuple, &right_tuple, ty, true), + }; + has_matched = true; + tuple + } + DataValue::Boolean(false) | DataValue::Null => None, + _ => { + co.yield_(Err(DatabaseError::InvalidType)).await; + return; } - } - _ => None, - }; - - if let Some(tuple) = tuple { - yield Ok(tuple); - if matches!(ty, JoinType::LeftSemi) { - break; - } - if let Some(bits) = bitmap.as_mut() { - bits.insert(right_idx); - } else if matches!(ty, JoinType::Full) { - first_matches.push(right_idx); } } - if matches!(ty, JoinType::LeftAnti) && has_matched { + _ => None, + }; + + if let Some(tuple) = tuple { + co.yield_(Ok(tuple)).await; + if matches!(ty, JoinType::LeftSemi) { break; } - right_idx += 1; + if let Some(bits) = bitmap.as_mut() { + bits.insert(right_idx); + } else if matches!(ty, JoinType::Full) { + first_matches.push(right_idx); + } } - - if matches!(self.ty, JoinType::Full) && bitmap.is_none() { - bitmap = Some(FixedBitSet::with_capacity(right_idx)); + if matches!(ty, JoinType::LeftAnti) && has_matched { + break; } + right_idx += 1; + } - // handle no matched tuple case - let tuple = match ty { - JoinType::LeftAnti if !has_matched => Some(left_tuple.clone()), - JoinType::LeftOuter - | JoinType::LeftSemi - | JoinType::RightOuter - | JoinType::Full - if !has_matched => - { - let right_tuple = - Tuple::new(None, vec![DataValue::Null; right_schema_len]); - if matches!(ty, JoinType::RightOuter) { - Self::emit_tuple(&right_tuple, &left_tuple, ty, false) - } else { - Self::emit_tuple(&left_tuple, &right_tuple, ty, false) - } + if matches!(ty, JoinType::Full) && bitmap.is_none() { + bitmap = Some(FixedBitSet::with_capacity(right_idx)); + } + + // handle no matched tuple case + let tuple = match ty { + JoinType::LeftAnti if !has_matched => Some(left_tuple.clone()), + JoinType::LeftOuter + | JoinType::LeftSemi + | JoinType::RightOuter + | JoinType::Full + if !has_matched => + { + let right_tuple = Tuple::new(None, vec![DataValue::Null; right_schema_len]); + if matches!(ty, JoinType::RightOuter) { + Self::emit_tuple(&right_tuple, &left_tuple, ty, false) + } else { + Self::emit_tuple(&left_tuple, &right_tuple, ty, false) } - _ => None, - }; - if let Some(tuple) = tuple { - yield Ok(tuple) } + _ => None, + }; + if let Some(tuple) = tuple { + co.yield_(Ok(tuple)).await; } + } - if matches!(ty, JoinType::Full) { - for idx in first_matches.into_iter() { - bitmap.as_mut().unwrap().insert(idx); - } + if matches!(ty, JoinType::Full) { + for idx in first_matches.into_iter() { + bitmap.as_mut().unwrap().insert(idx); + } - let mut right_coroutine = build_read(right_input.clone(), cache, transaction); - let mut idx = 0; - while let CoroutineState::Yielded(right_tuple) = - Pin::new(&mut right_coroutine).resume(()) - { - if !bitmap.as_ref().unwrap().contains(idx) { - let mut right_tuple: Tuple = throw!(right_tuple); - let mut values = vec![DataValue::Null; right_schema_len]; - values.append(&mut right_tuple.values); + let mut right_coroutine = build_read(right_input.clone(), cache, transaction); + for (idx, right_tuple) in right_coroutine.by_ref().enumerate() { + if !bitmap.as_ref().unwrap().contains(idx) { + let mut right_tuple: Tuple = throw!(co, right_tuple); + let mut values = vec![DataValue::Null; right_schema_len]; + values.append(&mut right_tuple.values); - yield Ok(Tuple::new(right_tuple.pk, values)) - } - idx += 1; + co.yield_(Ok(Tuple::new(right_tuple.pk, values))).await; } } - }, - ) + } + }) } } @@ -407,13 +372,10 @@ mod test { use crate::execution::dql::test::build_integers; use crate::execution::{try_collect, ReadExecutor}; use crate::expression::ScalarExpression; - use crate::optimizer::heuristic::batch::HepBatchStrategy; - use crate::optimizer::heuristic::optimizer::HepOptimizer; - use crate::optimizer::rule::normalization::NormalizationRuleImpl; use crate::planner::operator::values::ValuesOperator; use crate::planner::operator::Operator; use crate::planner::Childrens; - use crate::storage::rocksdb::{RocksStorage, RocksTransaction}; + use crate::storage::rocksdb::RocksStorage; use crate::storage::Storage; use crate::types::evaluator::int32::Int32GtBinaryEvaluator; use crate::types::evaluator::BinaryEvaluatorBox; @@ -449,8 +411,14 @@ mod test { let on_keys = if eq { vec![( - ScalarExpression::column_expr(t1_columns[1].clone()), - ScalarExpression::column_expr(t2_columns[1].clone()), + ScalarExpression::ColumnRef { + column: t1_columns[1].clone(), + position: None, + }, + ScalarExpression::ColumnRef { + column: t2_columns[1].clone(), + position: None, + }, )] } else { vec![] @@ -520,12 +488,14 @@ mod test { let filter = ScalarExpression::Binary { op: crate::expression::BinaryOperator::Gt, - left_expr: Box::new(ScalarExpression::column_expr(ColumnRef::from( - ColumnCatalog::new("c1".to_owned(), true, desc.clone()), - ))), - right_expr: Box::new(ScalarExpression::column_expr(ColumnRef::from( - ColumnCatalog::new("c4".to_owned(), true, desc.clone()), - ))), + left_expr: Box::new(ScalarExpression::ColumnRef { + column: ColumnRef::from(ColumnCatalog::new("c1".to_owned(), true, desc.clone())), + position: None, + }), + right_expr: Box::new(ScalarExpression::ColumnRef { + column: ColumnRef::from(ColumnCatalog::new("c4".to_owned(), true, desc.clone())), + position: None, + }), evaluator: Some(BinaryEvaluatorBox(Arc::new(Int32GtBinaryEvaluator))), ty: LogicalType::Boolean, }; @@ -563,31 +533,13 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let plan = LogicalPlan::new( - Operator::Join(JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), - }, - join_type: JoinType::Inner, - }), - Childrens::Twins { left, right }, - ); - let plan = HepOptimizer::new(plan) - .batch( - "Expression Remapper".to_string(), - HepBatchStrategy::once_topdown(), - vec![ - NormalizationRuleImpl::BindExpressionPosition, - // TIPS: This rule is necessary - NormalizationRuleImpl::EvaluatorBind, - ], - ) - .find_best::(None)?; - let Operator::Join(op) = plan.operator else { - unreachable!() + let op = JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::Inner, }; - let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -610,31 +562,13 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let plan = LogicalPlan::new( - Operator::Join(JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), - }, - join_type: JoinType::LeftOuter, - }), - Childrens::Twins { left, right }, - ); - let plan = HepOptimizer::new(plan) - .batch( - "Expression Remapper".to_string(), - HepBatchStrategy::once_topdown(), - vec![ - NormalizationRuleImpl::BindExpressionPosition, - // TIPS: This rule is necessary - NormalizationRuleImpl::EvaluatorBind, - ], - ) - .find_best::(None)?; - let Operator::Join(op) = plan.operator else { - unreachable!() + let op = JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::LeftOuter, }; - let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -669,31 +603,13 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let plan = LogicalPlan::new( - Operator::Join(JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), - }, - join_type: JoinType::Cross, - }), - Childrens::Twins { left, right }, - ); - let plan = HepOptimizer::new(plan) - .batch( - "Expression Remapper".to_string(), - HepBatchStrategy::once_topdown(), - vec![ - NormalizationRuleImpl::BindExpressionPosition, - // TIPS: This rule is necessary - NormalizationRuleImpl::EvaluatorBind, - ], - ) - .find_best::(None)?; - let Operator::Join(op) = plan.operator else { - unreachable!() + let op = JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::Cross, }; - let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -717,31 +633,13 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, _) = build_join_values(true); - let plan = LogicalPlan::new( - Operator::Join(JoinOperator { - on: JoinCondition::On { - on: keys, - filter: None, - }, - join_type: JoinType::Cross, - }), - Childrens::Twins { left, right }, - ); - let plan = HepOptimizer::new(plan) - .batch( - "Expression Remapper".to_string(), - HepBatchStrategy::once_topdown(), - vec![ - NormalizationRuleImpl::BindExpressionPosition, - // TIPS: This rule is necessary - NormalizationRuleImpl::EvaluatorBind, - ], - ) - .find_best::(None)?; - let Operator::Join(op) = plan.operator else { - unreachable!() + let op = JoinOperator { + on: JoinCondition::On { + on: keys, + filter: None, + }, + join_type: JoinType::Cross, }; - let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -768,31 +666,13 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, _) = build_join_values(false); - let plan = LogicalPlan::new( - Operator::Join(JoinOperator { - on: JoinCondition::On { - on: keys, - filter: None, - }, - join_type: JoinType::Cross, - }), - Childrens::Twins { left, right }, - ); - let plan = HepOptimizer::new(plan) - .batch( - "Expression Remapper".to_string(), - HepBatchStrategy::once_topdown(), - vec![ - NormalizationRuleImpl::BindExpressionPosition, - // TIPS: This rule is necessary - NormalizationRuleImpl::EvaluatorBind, - ], - ) - .find_best::(None)?; - let Operator::Join(op) = plan.operator else { - unreachable!() + let op = JoinOperator { + on: JoinCondition::On { + on: keys, + filter: None, + }, + join_type: JoinType::Cross, }; - let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -811,31 +691,13 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let plan = LogicalPlan::new( - Operator::Join(JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), - }, - join_type: JoinType::LeftSemi, - }), - Childrens::Twins { left, right }, - ); - let plan = HepOptimizer::new(plan) - .batch( - "Expression Remapper".to_string(), - HepBatchStrategy::once_topdown(), - vec![ - NormalizationRuleImpl::BindExpressionPosition, - // TIPS: This rule is necessary - NormalizationRuleImpl::EvaluatorBind, - ], - ) - .find_best::(None)?; - let Operator::Join(op) = plan.operator else { - unreachable!() + let op = JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::LeftSemi, }; - let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -857,31 +719,13 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let plan = LogicalPlan::new( - Operator::Join(JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), - }, - join_type: JoinType::LeftAnti, - }), - Childrens::Twins { left, right }, - ); - let plan = HepOptimizer::new(plan) - .batch( - "Expression Remapper".to_string(), - HepBatchStrategy::once_topdown(), - vec![ - NormalizationRuleImpl::BindExpressionPosition, - // TIPS: This rule is necessary - NormalizationRuleImpl::EvaluatorBind, - ], - ) - .find_best::(None)?; - let Operator::Join(op) = plan.operator else { - unreachable!() + let op = JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::LeftAnti, }; - let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -905,31 +749,13 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let plan = LogicalPlan::new( - Operator::Join(JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), - }, - join_type: JoinType::RightOuter, - }), - Childrens::Twins { left, right }, - ); - let plan = HepOptimizer::new(plan) - .batch( - "Expression Remapper".to_string(), - HepBatchStrategy::once_topdown(), - vec![ - NormalizationRuleImpl::BindExpressionPosition, - // TIPS: This rule is necessary - NormalizationRuleImpl::EvaluatorBind, - ], - ) - .find_best::(None)?; - let Operator::Join(op) = plan.operator else { - unreachable!() + let op = JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::RightOuter, }; - let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -958,31 +784,13 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let plan = LogicalPlan::new( - Operator::Join(JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), - }, - join_type: JoinType::Full, - }), - Childrens::Twins { left, right }, - ); - let plan = HepOptimizer::new(plan) - .batch( - "Expression Remapper".to_string(), - HepBatchStrategy::once_topdown(), - vec![ - NormalizationRuleImpl::BindExpressionPosition, - // TIPS: This rule is necessary - NormalizationRuleImpl::EvaluatorBind, - ], - ) - .find_best::(None)?; - let Operator::Join(op) = plan.operator else { - unreachable!() + let op = JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::Full, }; - let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; diff --git a/src/execution/dql/limit.rs b/src/execution/dql/limit.rs index a9050be1..f08b1702 100644 --- a/src/execution/dql/limit.rs +++ b/src/execution/dql/limit.rs @@ -1,11 +1,7 @@ -use crate::execution::{build_read, Executor, ReadExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, ReadExecutor}; use crate::planner::operator::limit::LimitOperator; use crate::planner::LogicalPlan; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; - pub struct Limit { offset: Option, limit: Option, @@ -28,36 +24,33 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Limit { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let Limit { - offset, - limit, - input, - } = self; - - if limit.is_some() && limit.unwrap() == 0 { - return; + spawn_executor(move |co| async move { + let Limit { + offset, + limit, + input, + } = self; + + if limit.is_some() && limit.unwrap() == 0 { + return; + } + + let offset_val = offset.unwrap_or(0); + let offset_limit = offset_val.saturating_add(limit.unwrap_or(usize::MAX)) - 1; + + let mut i = 0; + let executor = build_read(input, cache, transaction); + + for tuple in executor { + i += 1; + if i - 1 < offset_val { + continue; + } else if i - 1 > offset_limit { + break; } - let offset_val = offset.unwrap_or(0); - let offset_limit = offset_val.saturating_add(limit.unwrap_or(usize::MAX)) - 1; - - let mut i = 0; - let mut coroutine = build_read(input, cache, transaction); - - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - i += 1; - if i - 1 < offset_val { - continue; - } else if i - 1 > offset_limit { - break; - } - - yield tuple; - } - }, - ) + co.yield_(tuple).await; + } + }) } } diff --git a/src/execution/dql/projection.rs b/src/execution/dql/projection.rs index 4f11e60e..511ba8db 100644 --- a/src/execution/dql/projection.rs +++ b/src/execution/dql/projection.rs @@ -1,6 +1,6 @@ use crate::catalog::ColumnRef; use crate::errors::DatabaseError; -use crate::execution::{build_read, Executor, ReadExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, ReadExecutor}; use crate::expression::ScalarExpression; use crate::planner::operator::project::ProjectOperator; use crate::planner::LogicalPlan; @@ -8,10 +8,6 @@ use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; use crate::types::tuple::Tuple; use crate::types::value::DataValue; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; - pub struct Projection { exprs: Vec, input: LogicalPlan, @@ -29,20 +25,17 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Projection { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let Projection { exprs, mut input } = self; - let schema = input.output_schema().clone(); - let mut coroutine = build_read(input, cache, transaction); + spawn_executor(move |co| async move { + let Projection { exprs, mut input } = self; + let schema = input.output_schema().clone(); + let executor = build_read(input, cache, transaction); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - let tuple = throw!(tuple); - let values = throw!(Self::projection(&tuple, &exprs, &schema)); - yield Ok(Tuple::new(tuple.pk, values)); - } - }, - ) + for tuple in executor { + let tuple = throw!(co, tuple); + let values = throw!(co, Self::projection(&tuple, &exprs, &schema)); + co.yield_(Ok(Tuple::new(tuple.pk, values))).await; + } + }) } } diff --git a/src/execution/dql/seq_scan.rs b/src/execution/dql/seq_scan.rs index cf675e4f..2158c69c 100644 --- a/src/execution/dql/seq_scan.rs +++ b/src/execution/dql/seq_scan.rs @@ -1,4 +1,4 @@ -use crate::execution::{Executor, ReadExecutor}; +use crate::execution::{spawn_executor, Executor, ReadExecutor}; use crate::planner::operator::table_scan::TableScanOperator; use crate::storage::{Iter, StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; @@ -19,29 +19,29 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for SeqScan { (table_cache, _, _): (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let TableScanOperator { - table_name, - columns, - limit, - with_pk, - .. - } = self.op; + spawn_executor(move |co| async move { + let TableScanOperator { + table_name, + columns, + limit, + with_pk, + .. + } = self.op; - let mut iter = throw!(unsafe { &mut (*transaction) }.read( + let mut iter = throw!( + co, + unsafe { &mut (*transaction) }.read( table_cache, table_name, limit, columns, with_pk - )); + ) + ); - while let Some(tuple) = throw!(iter.next_tuple()) { - yield Ok(tuple); - } - }, - ) + while let Some(tuple) = throw!(co, iter.next_tuple()) { + co.yield_(Ok(tuple)).await; + } + }) } } diff --git a/src/execution/dql/show_table.rs b/src/execution/dql/show_table.rs index 8ba65ef5..fcc3adbb 100644 --- a/src/execution/dql/show_table.rs +++ b/src/execution/dql/show_table.rs @@ -1,5 +1,5 @@ use crate::catalog::TableMeta; -use crate::execution::{Executor, ReadExecutor}; +use crate::execution::{spawn_executor, Executor, ReadExecutor}; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; use crate::types::tuple::Tuple; @@ -14,21 +14,18 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for ShowTables { _: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let metas = throw!(unsafe { &mut (*transaction) }.table_metas()); + spawn_executor(move |co| async move { + let metas = throw!(co, unsafe { &mut (*transaction) }.table_metas()); - for TableMeta { table_name } in metas { - let values = vec![DataValue::Utf8 { - value: table_name.to_string(), - ty: Utf8Type::Variable(None), - unit: CharLengthUnits::Characters, - }]; + for TableMeta { table_name } in metas { + let values = vec![DataValue::Utf8 { + value: table_name.to_string(), + ty: Utf8Type::Variable(None), + unit: CharLengthUnits::Characters, + }]; - yield Ok(Tuple::new(None, values)); - } - }, - ) + co.yield_(Ok(Tuple::new(None, values))).await; + } + }) } } diff --git a/src/execution/dql/show_view.rs b/src/execution/dql/show_view.rs index 9f11dba0..4442c0a5 100644 --- a/src/execution/dql/show_view.rs +++ b/src/execution/dql/show_view.rs @@ -1,5 +1,5 @@ use crate::catalog::view::View; -use crate::execution::{Executor, ReadExecutor}; +use crate::execution::{spawn_executor, Executor, ReadExecutor}; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; use crate::types::tuple::Tuple; @@ -14,21 +14,18 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for ShowViews { (table_cache, _, _): (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let metas = throw!(unsafe { &mut (*transaction) }.views(table_cache)); + spawn_executor(move |co| async move { + let metas = throw!(co, unsafe { &mut (*transaction) }.views(table_cache)); - for View { name, .. } in metas { - let values = vec![DataValue::Utf8 { - value: name.to_string(), - ty: Utf8Type::Variable(None), - unit: CharLengthUnits::Characters, - }]; + for View { name, .. } in metas { + let values = vec![DataValue::Utf8 { + value: name.to_string(), + ty: Utf8Type::Variable(None), + unit: CharLengthUnits::Characters, + }]; - yield Ok(Tuple::new(None, values)); - } - }, - ) + co.yield_(Ok(Tuple::new(None, values))).await; + } + }) } } diff --git a/src/execution/dql/sort.rs b/src/execution/dql/sort.rs index b2c82412..01cef3cd 100644 --- a/src/execution/dql/sort.rs +++ b/src/execution/dql/sort.rs @@ -1,5 +1,5 @@ use crate::errors::DatabaseError; -use crate::execution::{build_read, Executor, ReadExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, ReadExecutor}; use crate::planner::operator::sort::{SortField, SortOperator}; use crate::planner::LogicalPlan; use crate::storage::table_codec::BumpBytes; @@ -9,9 +9,6 @@ use crate::types::tuple::{Schema, Tuple}; use bumpalo::Bump; use std::cmp::Ordering; use std::mem::MaybeUninit; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; pub(crate) type BumpVec<'bump, T> = bumpalo::collections::Vec<'bump, T>; @@ -288,45 +285,41 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Sort { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let Sort { - arena, - sort_fields, - limit, - mut input, - } = self; - - let arena: *const Bump = &arena; - let schema = input.output_schema().clone(); - let mut tuples = NullableVec::new(unsafe { &*arena }); - let mut offset = 0; - - let mut coroutine = build_read(input, cache, transaction); - - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - tuples.put((offset, throw!(tuple))); - offset += 1; - } + spawn_executor(move |co| async move { + let Sort { + arena, + sort_fields, + limit, + mut input, + } = self; + + let arena: *const Bump = &arena; + let schema = input.output_schema().clone(); + let mut tuples = NullableVec::new(unsafe { &*arena }); + + let mut coroutine = build_read(input, cache, transaction); + + for (offset, tuple) in coroutine.by_ref().enumerate() { + tuples.put((offset, throw!(co, tuple))); + } - let sort_by = if tuples.len() > 256 { - SortBy::Radix - } else { - SortBy::Fast - }; - let mut limit = limit.unwrap_or(tuples.len()); - - for tuple in - throw!(sort_by.sorted_tuples(unsafe { &*arena }, &schema, &sort_fields, tuples)) - { - if limit != 0 { - yield Ok(tuple); - limit -= 1; - } + let sort_by = if tuples.len() > 256 { + SortBy::Radix + } else { + SortBy::Fast + }; + let mut limit = limit.unwrap_or(tuples.len()); + + for tuple in throw!( + co, + sort_by.sorted_tuples(unsafe { &*arena }, &schema, &sort_fields, tuples) + ) { + if limit != 0 { + co.yield_(Ok(tuple)).await; + limit -= 1; } - }, - ) + } + }) } } diff --git a/src/execution/dql/top_k.rs b/src/execution/dql/top_k.rs index b495791d..3f5f2ae1 100644 --- a/src/execution/dql/top_k.rs +++ b/src/execution/dql/top_k.rs @@ -1,6 +1,6 @@ use crate::errors::DatabaseError; use crate::execution::dql::sort::BumpVec; -use crate::execution::{build_read, Executor, ReadExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, ReadExecutor}; use crate::planner::operator::sort::SortField; use crate::planner::operator::top_k::TopKOperator; use crate::planner::LogicalPlan; @@ -11,9 +11,6 @@ use crate::types::tuple::{Schema, Tuple}; use bumpalo::Bump; use std::cmp::Ordering; use std::collections::BTreeSet; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; #[derive(Eq, PartialEq, Debug)] struct CmpItem<'a> { @@ -114,46 +111,46 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for TopK { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let TopK { - arena, - sort_fields, - limit, - offset, - mut input, - } = self; - - let arena: *const Bump = &arena; - - let schema = input.output_schema().clone(); - let keep_count = offset.unwrap_or(0) + limit; - let mut set = BTreeSet::new(); - let mut coroutine = build_read(input, cache, transaction); - - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - throw!(top_sort( + spawn_executor(move |co| async move { + let TopK { + arena, + sort_fields, + limit, + offset, + mut input, + } = self; + + let arena: *const Bump = &arena; + + let schema = input.output_schema().clone(); + let keep_count = offset.unwrap_or(0) + limit; + let mut set = BTreeSet::new(); + let coroutine = build_read(input, cache, transaction); + + for tuple in coroutine { + throw!( + co, + top_sort( unsafe { &*arena }, &schema, &sort_fields, &mut set, - throw!(tuple), + throw!(co, tuple), keep_count, - )); - } + ) + ); + } - let mut i: usize = 0; + let mut i: usize = 0; - while let Some(item) = set.pop_first() { - i += 1; - if i - 1 < offset.unwrap_or(0) { - continue; - } - yield Ok(item.tuple); + while let Some(item) = set.pop_first() { + i += 1; + if i - 1 < offset.unwrap_or(0) { + continue; } - }, - ) + co.yield_(Ok(item.tuple)).await; + } + }) } } diff --git a/src/execution/dql/union.rs b/src/execution/dql/union.rs index 95762985..928b7da5 100644 --- a/src/execution/dql/union.rs +++ b/src/execution/dql/union.rs @@ -1,10 +1,6 @@ -use crate::execution::{build_read, Executor, ReadExecutor}; +use crate::execution::{build_read, spawn_executor, Executor, ReadExecutor}; use crate::planner::LogicalPlan; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; -use std::ops::Coroutine; -use std::ops::CoroutineState; -use std::pin::Pin; - pub struct Union { left_input: LogicalPlan, right_input: LogicalPlan, @@ -25,24 +21,21 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Union { cache: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), transaction: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let Union { - left_input, - right_input, - } = self; - let mut coroutine = build_read(left_input, cache, transaction); + spawn_executor(move |co| async move { + let Union { + left_input, + right_input, + } = self; + let mut left = build_read(left_input, cache, transaction); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - yield tuple; - } - let mut coroutine = build_read(right_input, cache, transaction); + for tuple in left.by_ref() { + co.yield_(tuple).await; + } + let right = build_read(right_input, cache, transaction); - while let CoroutineState::Yielded(tuple) = Pin::new(&mut coroutine).resume(()) { - yield tuple; - } - }, - ) + for tuple in right { + co.yield_(tuple).await; + } + }) } } diff --git a/src/execution/dql/values.rs b/src/execution/dql/values.rs index bfaeeba0..5049ac10 100644 --- a/src/execution/dql/values.rs +++ b/src/execution/dql/values.rs @@ -1,4 +1,4 @@ -use crate::execution::{Executor, ReadExecutor}; +use crate::execution::{spawn_executor, Executor, ReadExecutor}; use crate::planner::operator::values::ValuesOperator; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::throw; @@ -22,23 +22,20 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Values { _: (&'a TableCache, &'a ViewCache, &'a StatisticsMetaCache), _: *mut T, ) -> Executor<'a> { - Box::new( - #[coroutine] - move || { - let ValuesOperator { rows, schema_ref } = self.op; + spawn_executor(move |co| async move { + let ValuesOperator { rows, schema_ref } = self.op; - for mut values in rows { - for (i, value) in values.iter_mut().enumerate() { - let ty = schema_ref[i].datatype().clone(); + for mut values in rows { + for (i, value) in values.iter_mut().enumerate() { + let ty = schema_ref[i].datatype().clone(); - if value.logical_type() != ty { - *value = throw!(mem::replace(value, DataValue::Null).cast(&ty)); - } + if value.logical_type() != ty { + *value = throw!(co, mem::replace(value, DataValue::Null).cast(&ty)); } - - yield Ok(Tuple::new(None, values)); } - }, - ) + + co.yield_(Ok(Tuple::new(None, values))).await; + } + }) } } diff --git a/src/execution/execute_macro.rs b/src/execution/execute_macro.rs index 97ba1586..03851315 100644 --- a/src/execution/execute_macro.rs +++ b/src/execution/execute_macro.rs @@ -1,10 +1,10 @@ #[macro_export] macro_rules! throw { - ($code:expr) => { + ($co:expr, $code:expr) => { match $code { Ok(item) => item, Err(err) => { - yield Err(err); + $co.yield_(Err(err)).await; return; } } diff --git a/src/execution/mod.rs b/src/execution/mod.rs index 0685d9d7..6586ec1e 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -45,10 +45,18 @@ use crate::planner::LogicalPlan; use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache}; use crate::types::index::IndexInfo; use crate::types::tuple::Tuple; -use std::ops::Coroutine; +use genawaiter::sync::{Co, Gen}; +use std::future::Future; -pub type Executor<'a> = - Box, Return = ()> + 'a + Unpin>; +pub type Executor<'a> = Box> + 'a>; + +pub(crate) fn spawn_executor<'a, F, Fut>(producer: F) -> Executor<'a> +where + F: FnOnce(Co>) -> Fut + 'a, + Fut: Future + 'a, +{ + Box::new(Gen::new(producer).into_iter().fuse()) +} pub trait ReadExecutor<'a, T: Transaction + 'a> { fn execute( @@ -235,13 +243,6 @@ pub fn build_write<'a, T: Transaction + 'a>( } #[cfg(test)] -pub fn try_collect(mut executor: Executor) -> Result, DatabaseError> { - let mut output = Vec::new(); - - while let std::ops::CoroutineState::Yielded(tuple) = - std::pin::Pin::new(&mut executor).resume(()) - { - output.push(tuple?); - } - Ok(output) +pub fn try_collect(executor: Executor) -> Result, DatabaseError> { + executor.collect() } diff --git a/src/expression/mod.rs b/src/expression/mod.rs index f2c66b56..488a9bcf 100644 --- a/src/expression/mod.rs +++ b/src/expression/mod.rs @@ -461,7 +461,7 @@ impl ScalarExpression { pub fn output_name(&self) -> String { match self { - ScalarExpression::Constant(value) => format!("{}", value), + ScalarExpression::Constant(value) => format!("{value}"), ScalarExpression::ColumnRef { column, .. } => column.full_name(), ScalarExpression::Alias { alias, expr } => match alias { AliasType::Name(alias) => alias.to_string(), @@ -572,14 +572,14 @@ impl ScalarExpression { .unwrap_or(" ".to_string()) }; let trim_where_str = match trim_where { - Some(TrimWhereField::Both) => format!("both '{}' from", trim_what_str), - Some(TrimWhereField::Leading) => format!("leading '{}' from", trim_what_str), - Some(TrimWhereField::Trailing) => format!("trailing '{}' from", trim_what_str), + Some(TrimWhereField::Both) => format!("both '{trim_what_str}' from"), + Some(TrimWhereField::Leading) => format!("leading '{trim_what_str}' from"), + Some(TrimWhereField::Trailing) => format!("trailing '{trim_what_str}' from"), None => { if trim_what_str.is_empty() { String::new() } else { - format!("'{}' from", trim_what_str) + format!("'{trim_what_str}' from") } } }; @@ -588,7 +588,7 @@ impl ScalarExpression { ScalarExpression::Empty => unreachable!(), ScalarExpression::Tuple(args) => { let args_str = args.iter().map(|expr| expr.output_name()).join(", "); - format!("({})", args_str) + format!("({args_str})") } ScalarExpression::ScalaFunction(ScalarFunction { args, inner }) => { let args_str = args.iter().map(|expr| expr.output_name()).join(", "); @@ -604,25 +604,25 @@ impl ScalarExpression { right_expr, .. } => { - format!("if {} ({}, {})", condition, left_expr, right_expr) + format!("if {condition} ({left_expr}, {right_expr})") } ScalarExpression::IfNull { left_expr, right_expr, .. } => { - format!("ifnull({}, {})", left_expr, right_expr) + format!("ifnull({left_expr}, {right_expr})") } ScalarExpression::NullIf { left_expr, right_expr, .. } => { - format!("ifnull({}, {})", left_expr, right_expr) + format!("ifnull({left_expr}, {right_expr})") } ScalarExpression::Coalesce { exprs, .. } => { let exprs_str = exprs.iter().map(|expr| expr.output_name()).join(", "); - format!("coalesce({})", exprs_str) + format!("coalesce({exprs_str})") } ScalarExpression::CaseWhen { operand_expr, @@ -637,7 +637,7 @@ impl ScalarExpression { }; let expr_pairs_str = expr_pairs .iter() - .map(|(when_expr, then_expr)| format!("when {} then {}", when_expr, then_expr)) + .map(|(when_expr, then_expr)| format!("when {when_expr} then {then_expr}")) .join(" "); format!( @@ -682,7 +682,7 @@ impl TryFrom for UnaryOperator { SqlUnaryOperator::Plus => Ok(UnaryOperator::Plus), SqlUnaryOperator::Minus => Ok(UnaryOperator::Minus), SqlUnaryOperator::Not => Ok(UnaryOperator::Not), - op => Err(DatabaseError::UnsupportedStmt(format!("{}", op))), + op => Err(DatabaseError::UnsupportedStmt(format!("{op}"))), } } } @@ -721,7 +721,7 @@ impl fmt::Display for BinaryOperator { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let like_op = |f: &mut Formatter, escape_char: &Option| { if let Some(escape_char) = escape_char { - write!(f, "(escape: {})", escape_char)?; + write!(f, "(escape: {escape_char})")?; } Ok(()) }; @@ -784,7 +784,7 @@ impl TryFrom for BinaryOperator { SqlBinaryOperator::NotEq => Ok(BinaryOperator::NotEq), SqlBinaryOperator::And => Ok(BinaryOperator::And), SqlBinaryOperator::Or => Ok(BinaryOperator::Or), - op => Err(DatabaseError::UnsupportedStmt(format!("{}", op))), + op => Err(DatabaseError::UnsupportedStmt(format!("{op}"))), } } } diff --git a/src/expression/range_detacher.rs b/src/expression/range_detacher.rs index bd475837..e93a8594 100644 --- a/src/expression/range_detacher.rs +++ b/src/expression/range_detacher.rs @@ -762,25 +762,25 @@ impl fmt::Display for Range { Range::Scope { min, max } => { match min { Bound::Unbounded => write!(f, "(-inf")?, - Bound::Included(value) => write!(f, "[{}", value)?, - Bound::Excluded(value) => write!(f, "({}", value)?, + Bound::Included(value) => write!(f, "[{value}")?, + Bound::Excluded(value) => write!(f, "({value}")?, } write!(f, ", ")?; match max { Bound::Unbounded => write!(f, "+inf)")?, - Bound::Included(value) => write!(f, "{}]", value)?, - Bound::Excluded(value) => write!(f, "{})", value)?, + Bound::Included(value) => write!(f, "{value}]")?, + Bound::Excluded(value) => write!(f, "{value})")?, } Ok(()) } - Range::Eq(value) => write!(f, "{}", value), + Range::Eq(value) => write!(f, "{value}"), Range::Dummy => write!(f, "Dummy"), Range::SortedRanges(ranges) => { - let ranges_str = ranges.iter().map(|range| format!("{}", range)).join(", "); - write!(f, "{}", ranges_str) + let ranges_str = ranges.iter().map(|range| format!("{range}")).join(", "); + write!(f, "{ranges_str}") } } } diff --git a/src/lib.rs b/src/lib.rs index cc4a28f6..4ddf15eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,15 +85,7 @@ //! Ok(()) //! } //! ``` -#![feature(error_generic_member_access)] #![allow(unused_doc_comments)] -#![feature(result_flattening)] -#![feature(coroutines)] -#![feature(coroutine_trait)] -#![feature(iterator_try_collect)] -#![feature(slice_pattern)] -#![feature(stmt_expr_attributes)] -#![feature(random)] extern crate core; pub mod binder; diff --git a/src/optimizer/core/cm_sketch.rs b/src/optimizer/core/cm_sketch.rs index 2dc78435..0d2bfa86 100644 --- a/src/optimizer/core/cm_sketch.rs +++ b/src/optimizer/core/cm_sketch.rs @@ -3,12 +3,12 @@ use crate::expression::range_detacher::Range; use crate::serdes::{ReferenceSerialization, ReferenceTables}; use crate::storage::{TableCache, Transaction}; use crate::types::value::DataValue; +use rand::random; use siphasher::sip::SipHasher13; use std::borrow::Borrow; use std::hash::{Hash, Hasher}; use std::io::{Read, Write}; use std::marker::PhantomData; -use std::random::random; use std::{cmp, mem}; pub(crate) type FastHasher = SipHasher13; diff --git a/src/optimizer/heuristic/graph.rs b/src/optimizer/heuristic/graph.rs index f6de8c61..9aab5f47 100644 --- a/src/optimizer/heuristic/graph.rs +++ b/src/optimizer/heuristic/graph.rs @@ -32,13 +32,13 @@ impl HepGraph { match *childrens { Childrens::None => (), Childrens::Only(child) => { - let child_index = graph_filling(graph, child); + let child_index = graph_filling(graph, *child); let _ = graph.add_edge(index, child_index, 0); } Childrens::Twins { left, right } => { - let child_index = graph_filling(graph, left); + let child_index = graph_filling(graph, *left); let _ = graph.add_edge(index, child_index, 0); - let child_index = graph_filling(graph, right); + let child_index = graph_filling(graph, *right); let _ = graph.add_edge(index, child_index, 1); } } @@ -200,10 +200,10 @@ impl HepGraph { let childrens = match (child_0, child_1) { (Some(child_0), Some(child_1)) => Childrens::Twins { - left: child_0, - right: child_1, + left: Box::new(child_0), + right: Box::new(child_1), }, - (Some(child), None) | (None, Some(child)) => Childrens::Only(child), + (Some(child), None) | (None, Some(child)) => Childrens::Only(Box::new(child)), (None, None) => Childrens::None, }; diff --git a/src/optimizer/heuristic/matcher.rs b/src/optimizer/heuristic/matcher.rs index c8195dd7..9779178f 100644 --- a/src/optimizer/heuristic/matcher.rs +++ b/src/optimizer/heuristic/matcher.rs @@ -94,23 +94,23 @@ mod tests { let all_dummy_plan = LogicalPlan { operator: Operator::Dummy, childrens: Box::new(Childrens::Twins { - left: LogicalPlan { + left: Box::new(LogicalPlan { operator: Operator::Dummy, - childrens: Box::new(Childrens::Only(LogicalPlan { + childrens: Box::new(Childrens::Only(Box::new(LogicalPlan { operator: Operator::Dummy, childrens: Box::new(Childrens::None), physical_option: None, _output_schema_ref: None, - })), + }))), physical_option: None, _output_schema_ref: None, - }, - right: LogicalPlan { + }), + right: Box::new(LogicalPlan { operator: Operator::Dummy, childrens: Box::new(Childrens::None), physical_option: None, _output_schema_ref: None, - }, + }), }), physical_option: None, _output_schema_ref: None, diff --git a/src/optimizer/rule/normalization/pushdown_limit.rs b/src/optimizer/rule/normalization/pushdown_limit.rs index fd6497a0..452ee34e 100644 --- a/src/optimizer/rule/normalization/pushdown_limit.rs +++ b/src/optimizer/rule/normalization/pushdown_limit.rs @@ -52,7 +52,7 @@ impl NormalizationRule for LimitProjectTranspose { /// Add extra limits below JOIN: /// 1. For LEFT OUTER and RIGHT OUTER JOIN, we push limits to the left and right sides, -/// respectively. +/// respectively. /// /// TODO: 2. For INNER and CROSS JOIN, we push limits to both the left and right sides /// TODO: if join condition is empty. diff --git a/src/planner/mod.rs b/src/planner/mod.rs index c23d196b..84c81e55 100644 --- a/src/planner/mod.rs +++ b/src/planner/mod.rs @@ -20,10 +20,10 @@ pub(crate) enum SchemaOutput { #[derive(Debug, PartialEq, Eq, Clone, Hash, ReferenceSerialization)] pub enum Childrens { None, - Only(LogicalPlan), + Only(Box), Twins { - left: LogicalPlan, - right: LogicalPlan, + left: Box, + right: Box, }, } @@ -37,7 +37,7 @@ impl Childrens { pub fn pop_only(self) -> LogicalPlan { match self { - Childrens::Only(plan) => plan, + Childrens::Only(plan) => *plan, _ => { unreachable!() } @@ -46,7 +46,7 @@ impl Childrens { pub fn pop_twins(self) -> (LogicalPlan, LogicalPlan) { match self { - Childrens::Twins { left, right } => (left, right), + Childrens::Twins { left, right } => (*left, *right), _ => unreachable!(), } } @@ -67,12 +67,12 @@ impl<'a> Iterator for ChildrensIter<'a> { return None; } self.pos += 1; - Some(plan) + Some(plan.as_ref()) } Childrens::Twins { left, right } => { let option = match self.pos { - 0 => Some(left), - 1 => Some(right), + 0 => Some(left.as_ref()), + 1 => Some(right.as_ref()), _ => None, }; self.pos += 1; @@ -258,7 +258,7 @@ impl LogicalPlan { let mut result = format!("{:indent$}{}", "", self.operator, indent = indentation); if let Some(physical_option) = &self.physical_option { - result.push_str(&format!(" [{}]", physical_option)); + result.push_str(&format!(" [{physical_option}]")); } for child in self.childrens.iter() { diff --git a/src/planner/operator/aggregate.rs b/src/planner/operator/aggregate.rs index f74b7182..8024d3a8 100644 --- a/src/planner/operator/aggregate.rs +++ b/src/planner/operator/aggregate.rs @@ -25,7 +25,7 @@ impl AggregateOperator { agg_calls, is_distinct, }), - Childrens::Only(children), + Childrens::Only(Box::new(children)), ) } } @@ -35,17 +35,17 @@ impl fmt::Display for AggregateOperator { let calls = self .agg_calls .iter() - .map(|call| format!("{}", call)) + .map(|call| format!("{call}")) .join(", "); - write!(f, "Aggregate [{}]", calls)?; + write!(f, "Aggregate [{calls}]")?; if !self.groupby_exprs.is_empty() { let groupbys = self .groupby_exprs .iter() - .map(|groupby| format!("{}", groupby)) + .map(|groupby| format!("{groupby}")) .join(", "); - write!(f, " -> Group By [{}]", groupbys)?; + write!(f, " -> Group By [{groupbys}]")?; } Ok(()) diff --git a/src/planner/operator/except.rs b/src/planner/operator/except.rs index 3ed7a32b..da232a2f 100644 --- a/src/planner/operator/except.rs +++ b/src/planner/operator/except.rs @@ -26,8 +26,8 @@ impl ExceptOperator { _right_schema_ref: right_schema_ref, }), Childrens::Twins { - left: left_plan, - right: right_plan, + left: Box::new(left_plan), + right: Box::new(right_plan), }, ) } @@ -41,7 +41,7 @@ impl fmt::Display for ExceptOperator { .map(|column| column.name().to_string()) .join(", "); - write!(f, "Except: [{}]", schema)?; + write!(f, "Except: [{schema}]")?; Ok(()) } diff --git a/src/planner/operator/filter.rs b/src/planner/operator/filter.rs index 54c5a039..c78b7df9 100644 --- a/src/planner/operator/filter.rs +++ b/src/planner/operator/filter.rs @@ -21,7 +21,7 @@ impl FilterOperator { is_optimized: false, having, }), - Childrens::Only(children), + Childrens::Only(Box::new(children)), ) } } diff --git a/src/planner/operator/join.rs b/src/planner/operator/join.rs index ab431d95..0007deb4 100644 --- a/src/planner/operator/join.rs +++ b/src/planner/operator/join.rs @@ -49,7 +49,10 @@ impl JoinOperator { ) -> LogicalPlan { LogicalPlan::new( Operator::Join(JoinOperator { on, join_type }), - Childrens::Twins { left, right }, + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), + }, ) } } @@ -85,13 +88,13 @@ impl fmt::Display for JoinCondition { if !on.is_empty() { let on = on .iter() - .map(|(v1, v2)| format!("{} = {}", v1, v2)) + .map(|(v1, v2)| format!("{v1} = {v2}")) .join(" AND "); - write!(f, " On {}", on)?; + write!(f, " On {on}")?; } if let Some(filter) = filter { - write!(f, " Where {}", filter)?; + write!(f, " Where {filter}")?; } } JoinCondition::None => { diff --git a/src/planner/operator/limit.rs b/src/planner/operator/limit.rs index 299c8565..5f78f492 100644 --- a/src/planner/operator/limit.rs +++ b/src/planner/operator/limit.rs @@ -18,7 +18,7 @@ impl LimitOperator { ) -> LogicalPlan { LogicalPlan::new( Operator::Limit(LimitOperator { offset, limit }), - Childrens::Only(children), + Childrens::Only(Box::new(children)), ) } } @@ -26,13 +26,13 @@ impl LimitOperator { impl fmt::Display for LimitOperator { fn fmt(&self, f: &mut Formatter) -> fmt::Result { if let Some(limit) = self.limit { - write!(f, "Limit {}", limit)?; + write!(f, "Limit {limit}")?; } if self.limit.is_some() && self.offset.is_some() { write!(f, ", ")?; } if let Some(offset) = self.offset { - write!(f, "Offset {}", offset)?; + write!(f, "Offset {offset}")?; } Ok(()) diff --git a/src/planner/operator/mod.rs b/src/planner/operator/mod.rs index e82c48ef..90c1c7a9 100644 --- a/src/planner/operator/mod.rs +++ b/src/planner/operator/mod.rs @@ -285,37 +285,37 @@ impl fmt::Display for Operator { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Operator::Dummy => write!(f, "Dummy"), - Operator::Aggregate(op) => write!(f, "{}", op), - Operator::Filter(op) => write!(f, "{}", op), - Operator::Join(op) => write!(f, "{}", op), - Operator::Project(op) => write!(f, "{}", op), - Operator::TableScan(op) => write!(f, "{}", op), - Operator::FunctionScan(op) => write!(f, "{}", op), - Operator::Sort(op) => write!(f, "{}", op), - Operator::Limit(op) => write!(f, "{}", op), - Operator::TopK(op) => write!(f, "{}", op), - Operator::Values(op) => write!(f, "{}", op), + Operator::Aggregate(op) => write!(f, "{op}"), + Operator::Filter(op) => write!(f, "{op}"), + Operator::Join(op) => write!(f, "{op}"), + Operator::Project(op) => write!(f, "{op}"), + Operator::TableScan(op) => write!(f, "{op}"), + Operator::FunctionScan(op) => write!(f, "{op}"), + Operator::Sort(op) => write!(f, "{op}"), + Operator::Limit(op) => write!(f, "{op}"), + Operator::TopK(op) => write!(f, "{op}"), + Operator::Values(op) => write!(f, "{op}"), Operator::ShowTable => write!(f, "Show Tables"), Operator::ShowView => write!(f, "Show Views"), Operator::Explain => unreachable!(), - Operator::Describe(op) => write!(f, "{}", op), - Operator::Insert(op) => write!(f, "{}", op), - Operator::Update(op) => write!(f, "{}", op), - Operator::Delete(op) => write!(f, "{}", op), - Operator::Analyze(op) => write!(f, "{}", op), - Operator::AddColumn(op) => write!(f, "{}", op), - Operator::DropColumn(op) => write!(f, "{}", op), - Operator::CreateTable(op) => write!(f, "{}", op), - Operator::CreateIndex(op) => write!(f, "{}", op), - Operator::CreateView(op) => write!(f, "{}", op), - Operator::DropTable(op) => write!(f, "{}", op), - Operator::DropView(op) => write!(f, "{}", op), - Operator::DropIndex(op) => write!(f, "{}", op), - Operator::Truncate(op) => write!(f, "{}", op), - Operator::CopyFromFile(op) => write!(f, "{}", op), - Operator::CopyToFile(op) => write!(f, "{}", op), - Operator::Union(op) => write!(f, "{}", op), - Operator::Except(op) => write!(f, "{}", op), + Operator::Describe(op) => write!(f, "{op}"), + Operator::Insert(op) => write!(f, "{op}"), + Operator::Update(op) => write!(f, "{op}"), + Operator::Delete(op) => write!(f, "{op}"), + Operator::Analyze(op) => write!(f, "{op}"), + Operator::AddColumn(op) => write!(f, "{op}"), + Operator::DropColumn(op) => write!(f, "{op}"), + Operator::CreateTable(op) => write!(f, "{op}"), + Operator::CreateIndex(op) => write!(f, "{op}"), + Operator::CreateView(op) => write!(f, "{op}"), + Operator::DropTable(op) => write!(f, "{op}"), + Operator::DropView(op) => write!(f, "{op}"), + Operator::DropIndex(op) => write!(f, "{op}"), + Operator::Truncate(op) => write!(f, "{op}"), + Operator::CopyFromFile(op) => write!(f, "{op}"), + Operator::CopyToFile(op) => write!(f, "{op}"), + Operator::Union(op) => write!(f, "{op}"), + Operator::Except(op) => write!(f, "{op}"), } } } @@ -332,7 +332,7 @@ impl fmt::Display for PhysicalOption { PhysicalOption::Project => write!(f, "Project"), PhysicalOption::SeqScan => write!(f, "SeqScan"), PhysicalOption::FunctionScan => write!(f, "FunctionScan"), - PhysicalOption::IndexScan(index) => write!(f, "IndexScan By {}", index), + PhysicalOption::IndexScan(index) => write!(f, "IndexScan By {index}"), PhysicalOption::Sort => write!(f, "Sort"), PhysicalOption::Limit => write!(f, "Limit"), PhysicalOption::TopK => write!(f, "TopK"), diff --git a/src/planner/operator/project.rs b/src/planner/operator/project.rs index 2166b5a0..79e28a3b 100644 --- a/src/planner/operator/project.rs +++ b/src/planner/operator/project.rs @@ -11,9 +11,9 @@ pub struct ProjectOperator { impl fmt::Display for ProjectOperator { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let exprs = self.exprs.iter().map(|expr| format!("{}", expr)).join(", "); + let exprs = self.exprs.iter().map(|expr| format!("{expr}")).join(", "); - write!(f, "Projection [{}]", exprs)?; + write!(f, "Projection [{exprs}]")?; Ok(()) } diff --git a/src/planner/operator/sort.rs b/src/planner/operator/sort.rs index 13f22c1e..95774898 100644 --- a/src/planner/operator/sort.rs +++ b/src/planner/operator/sort.rs @@ -33,12 +33,12 @@ impl fmt::Display for SortOperator { let sort_fields = self .sort_fields .iter() - .map(|sort_field| format!("{}", sort_field)) + .map(|sort_field| format!("{sort_field}")) .join(", "); - write!(f, "Sort By {}", sort_fields)?; + write!(f, "Sort By {sort_fields}")?; if let Some(limit) = self.limit { - write!(f, ", Limit {}", limit)?; + write!(f, ", Limit {limit}")?; } Ok(()) diff --git a/src/planner/operator/table_scan.rs b/src/planner/operator/table_scan.rs index 18a9010d..f93eb521 100644 --- a/src/planner/operator/table_scan.rs +++ b/src/planner/operator/table_scan.rs @@ -80,10 +80,10 @@ impl fmt::Display for TableScanOperator { self.table_name, projection_columns )?; if let Some(limit) = limit { - write!(f, ", Limit: {}", limit)?; + write!(f, ", Limit: {limit}")?; } if let Some(offset) = offset { - write!(f, ", Offset: {}", offset)?; + write!(f, ", Offset: {offset}")?; } Ok(()) diff --git a/src/planner/operator/top_k.rs b/src/planner/operator/top_k.rs index 25f8e3b1..3772496f 100644 --- a/src/planner/operator/top_k.rs +++ b/src/planner/operator/top_k.rs @@ -26,7 +26,7 @@ impl TopKOperator { limit, offset, }), - Childrens::Only(children), + Childrens::Only(Box::new(children)), ) } } @@ -36,15 +36,15 @@ impl fmt::Display for TopKOperator { write!(f, "Top {}, ", self.limit)?; if let Some(offset) = self.offset { - write!(f, "Offset {}, ", offset)?; + write!(f, "Offset {offset}, ")?; } let sort_fields = self .sort_fields .iter() - .map(|sort_field| format!("{}", sort_field)) + .map(|sort_field| format!("{sort_field}")) .join(", "); - write!(f, "Sort By {}", sort_fields)?; + write!(f, "Sort By {sort_fields}")?; Ok(()) } diff --git a/src/planner/operator/union.rs b/src/planner/operator/union.rs index 67068053..9b2c36e4 100644 --- a/src/planner/operator/union.rs +++ b/src/planner/operator/union.rs @@ -26,8 +26,8 @@ impl UnionOperator { _right_schema_ref: right_schema_ref, }), Childrens::Twins { - left: left_plan, - right: right_plan, + left: Box::new(left_plan), + right: Box::new(right_plan), }, ) } @@ -41,7 +41,7 @@ impl fmt::Display for UnionOperator { .map(|column| column.name().to_string()) .join(", "); - write!(f, "Union: [{}]", schema)?; + write!(f, "Union: [{schema}]")?; Ok(()) } diff --git a/src/serdes/column.rs b/src/serdes/column.rs index 0fad7d86..134504a6 100644 --- a/src/serdes/column.rs +++ b/src/serdes/column.rs @@ -55,8 +55,7 @@ impl ReferenceSerialization for ColumnRef { let column = table .get_column_by_id(column_id) .ok_or(DatabaseError::InvalidColumn(format!( - "column id: {} not found", - column_id + "column id: {column_id} not found" )))?; Ok(nullable_for_join .and_then(|nullable| column.nullable_for_join(nullable)) diff --git a/src/types/index.rs b/src/types/index.rs index ae416d1a..a1f8e2d8 100644 --- a/src/types/index.rs +++ b/src/types/index.rs @@ -74,7 +74,7 @@ impl fmt::Display for IndexInfo { write!(f, " => ")?; if let Some(range) = &self.range { - write!(f, "{}", range)?; + write!(f, "{range}")?; } else { write!(f, "EMPTY")?; } diff --git a/src/types/mod.rs b/src/types/mod.rs index fedb69f9..1071fb82 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -497,16 +497,16 @@ impl std::fmt::Display for LogicalType { LogicalType::UBigint => write!(f, "UBigint")?, LogicalType::Float => write!(f, "Float")?, LogicalType::Double => write!(f, "Double")?, - LogicalType::Char(len, units) => write!(f, "Char({}, {})", len, units)?, - LogicalType::Varchar(len, units) => write!(f, "Varchar({:?}, {})", len, units)?, + LogicalType::Char(len, units) => write!(f, "Char({len}, {units})")?, + LogicalType::Varchar(len, units) => write!(f, "Varchar({len:?}, {units})")?, LogicalType::Date => write!(f, "Date")?, LogicalType::DateTime => write!(f, "DateTime")?, LogicalType::TimeStamp(precision, zone) => { - write!(f, "TimeStamp({:?}, {:?})", precision, zone)? + write!(f, "TimeStamp({precision:?}, {zone:?})")? } - LogicalType::Time(precision) => write!(f, "Time({:?})", precision)?, + LogicalType::Time(precision) => write!(f, "Time({precision:?})")?, LogicalType::Decimal(precision, scale) => { - write!(f, "Decimal({:?}, {:?})", precision, scale)? + write!(f, "Decimal({precision:?}, {scale:?})")? } LogicalType::Tuple(types) => { write!(f, "(")?; @@ -516,7 +516,7 @@ impl std::fmt::Display for LogicalType { write!(f, ", ")?; } first = false; - write!(f, "{}", ty)?; + write!(f, "{ty}")?; } write!(f, ")")? } diff --git a/src/types/serialize.rs b/src/types/serialize.rs index ec207b92..39948d47 100644 --- a/src/types/serialize.rs +++ b/src/types/serialize.rs @@ -362,7 +362,7 @@ impl TupleValueSerializable for CharSerializable { match unit { CharLengthUnits::Characters => { let chars_len = *len as usize; - let v = format!("{:len$}", value, len = chars_len); + let v = format!("{value:chars_len$}"); let bytes = v.as_bytes(); writer.write_u32::(bytes.len() as u32)?; diff --git a/src/types/value.rs b/src/types/value.rs index 8c95d3cc..080e88ef 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -357,7 +357,7 @@ impl DataValue { pub(crate) fn check_string_len(string: &str, len: usize, unit: CharLengthUnits) -> bool { match unit { CharLengthUnits::Characters => string.chars().count() > len, - CharLengthUnits::Octets => string.as_bytes().len() > len, + CharLengthUnits::Octets => string.len() > len, } } @@ -450,19 +450,19 @@ impl DataValue { } fn format_date(value: i32) -> Option { - Self::date_format(value).map(|fmt| format!("{}", fmt)) + Self::date_format(value).map(|fmt| format!("{fmt}")) } fn format_datetime(value: i64) -> Option { - Self::date_time_format(value).map(|fmt| format!("{}", fmt)) + Self::date_time_format(value).map(|fmt| format!("{fmt}")) } fn format_time(value: u32, precision: u64) -> Option { - Self::time_format(value, precision).map(|fmt| format!("{}", fmt)) + Self::time_format(value, precision).map(|fmt| format!("{fmt}")) } fn format_timestamp(value: i64, precision: u64) -> Option { - Self::time_stamp_format(value, precision, false).map(|fmt| format!("{}", fmt)) + Self::time_stamp_format(value, precision, false).map(|fmt| format!("{fmt}")) } #[inline] @@ -1841,7 +1841,7 @@ impl TryFrom<&sqlparser::ast::Value> for DataValue { | sqlparser::ast::Value::DoubleQuotedString(s) => s.clone().into(), sqlparser::ast::Value::Boolean(b) => (*b).into(), sqlparser::ast::Value::Null => Self::Null, - v => return Err(DatabaseError::UnsupportedStmt(format!("{:?}", v))), + v => return Err(DatabaseError::UnsupportedStmt(format!("{v:?}"))), }) } } @@ -1862,18 +1862,18 @@ macro_rules! format_float_option { impl fmt::Display for DataValue { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - DataValue::Boolean(e) => write!(f, "{}", e)?, + DataValue::Boolean(e) => write!(f, "{e}")?, DataValue::Float32(e) => format_float_option!(f, e)?, DataValue::Float64(e) => format_float_option!(f, e)?, - DataValue::Int8(e) => write!(f, "{}", e)?, - DataValue::Int16(e) => write!(f, "{}", e)?, - DataValue::Int32(e) => write!(f, "{}", e)?, - DataValue::Int64(e) => write!(f, "{}", e)?, - DataValue::UInt8(e) => write!(f, "{}", e)?, - DataValue::UInt16(e) => write!(f, "{}", e)?, - DataValue::UInt32(e) => write!(f, "{}", e)?, - DataValue::UInt64(e) => write!(f, "{}", e)?, - DataValue::Utf8 { value: e, .. } => write!(f, "{}", e)?, + DataValue::Int8(e) => write!(f, "{e}")?, + DataValue::Int16(e) => write!(f, "{e}")?, + DataValue::Int32(e) => write!(f, "{e}")?, + DataValue::Int64(e) => write!(f, "{e}")?, + DataValue::UInt8(e) => write!(f, "{e}")?, + DataValue::UInt16(e) => write!(f, "{e}")?, + DataValue::UInt32(e) => write!(f, "{e}")?, + DataValue::UInt64(e) => write!(f, "{e}")?, + DataValue::Utf8 { value: e, .. } => write!(f, "{e}")?, DataValue::Null => write!(f, "null")?, DataValue::Date32(e) => write!(f, "{}", DataValue::date_format(*e).unwrap())?, DataValue::Date64(e) => write!(f, "{}", DataValue::date_time_format(*e).unwrap())?, @@ -1906,26 +1906,26 @@ impl fmt::Display for DataValue { impl fmt::Debug for DataValue { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - DataValue::Boolean(_) => write!(f, "Boolean({})", self), - DataValue::Float32(_) => write!(f, "Float32({})", self), - DataValue::Float64(_) => write!(f, "Float64({})", self), - DataValue::Int8(_) => write!(f, "Int8({})", self), - DataValue::Int16(_) => write!(f, "Int16({})", self), - DataValue::Int32(_) => write!(f, "Int32({})", self), - DataValue::Int64(_) => write!(f, "Int64({})", self), - DataValue::UInt8(_) => write!(f, "UInt8({})", self), - DataValue::UInt16(_) => write!(f, "UInt16({})", self), - DataValue::UInt32(_) => write!(f, "UInt32({})", self), - DataValue::UInt64(_) => write!(f, "UInt64({})", self), - DataValue::Utf8 { .. } => write!(f, "Utf8(\"{}\")", self), + DataValue::Boolean(_) => write!(f, "Boolean({self})"), + DataValue::Float32(_) => write!(f, "Float32({self})"), + DataValue::Float64(_) => write!(f, "Float64({self})"), + DataValue::Int8(_) => write!(f, "Int8({self})"), + DataValue::Int16(_) => write!(f, "Int16({self})"), + DataValue::Int32(_) => write!(f, "Int32({self})"), + DataValue::Int64(_) => write!(f, "Int64({self})"), + DataValue::UInt8(_) => write!(f, "UInt8({self})"), + DataValue::UInt16(_) => write!(f, "UInt16({self})"), + DataValue::UInt32(_) => write!(f, "UInt32({self})"), + DataValue::UInt64(_) => write!(f, "UInt64({self})"), + DataValue::Utf8 { .. } => write!(f, "Utf8(\"{self}\")"), DataValue::Null => write!(f, "null"), - DataValue::Date32(_) => write!(f, "Date32({})", self), - DataValue::Date64(_) => write!(f, "Date64({})", self), - DataValue::Time32(..) => write!(f, "Time32({})", self), - DataValue::Time64(..) => write!(f, "Time64({})", self), - DataValue::Decimal(_) => write!(f, "Decimal({})", self), + DataValue::Date32(_) => write!(f, "Date32({self})"), + DataValue::Date64(_) => write!(f, "Date64({self})"), + DataValue::Time32(..) => write!(f, "Time32({self})"), + DataValue::Time64(..) => write!(f, "Time64({self})"), + DataValue::Decimal(_) => write!(f, "Decimal({self})"), DataValue::Tuple(..) => { - write!(f, "Tuple({}", self)?; + write!(f, "Tuple({self}")?; if matches!(self, DataValue::Tuple(_, true)) { write!(f, " [is upper]")?; } From 080266a8170a2c1399525c7d5f09cc547a758e89 Mon Sep 17 00:00:00 2001 From: kould Date: Thu, 13 Nov 2025 23:49:22 +0800 Subject: [PATCH 2/2] chore: fix nested_loop_join unit test --- src/execution/dql/join/nested_loop_join.rs | 308 ++++++++++++++++----- 1 file changed, 246 insertions(+), 62 deletions(-) diff --git a/src/execution/dql/join/nested_loop_join.rs b/src/execution/dql/join/nested_loop_join.rs index e15a19f7..6161db3e 100644 --- a/src/execution/dql/join/nested_loop_join.rs +++ b/src/execution/dql/join/nested_loop_join.rs @@ -372,10 +372,13 @@ mod test { use crate::execution::dql::test::build_integers; use crate::execution::{try_collect, ReadExecutor}; use crate::expression::ScalarExpression; + use crate::optimizer::heuristic::batch::HepBatchStrategy; + use crate::optimizer::heuristic::optimizer::HepOptimizer; + use crate::optimizer::rule::normalization::NormalizationRuleImpl; use crate::planner::operator::values::ValuesOperator; use crate::planner::operator::Operator; use crate::planner::Childrens; - use crate::storage::rocksdb::RocksStorage; + use crate::storage::rocksdb::{RocksStorage, RocksTransaction}; use crate::storage::Storage; use crate::types::evaluator::int32::Int32GtBinaryEvaluator; use crate::types::evaluator::BinaryEvaluatorBox; @@ -411,14 +414,8 @@ mod test { let on_keys = if eq { vec![( - ScalarExpression::ColumnRef { - column: t1_columns[1].clone(), - position: None, - }, - ScalarExpression::ColumnRef { - column: t2_columns[1].clone(), - position: None, - }, + ScalarExpression::column_expr(t1_columns[1].clone()), + ScalarExpression::column_expr(t2_columns[1].clone()), )] } else { vec![] @@ -488,14 +485,12 @@ mod test { let filter = ScalarExpression::Binary { op: crate::expression::BinaryOperator::Gt, - left_expr: Box::new(ScalarExpression::ColumnRef { - column: ColumnRef::from(ColumnCatalog::new("c1".to_owned(), true, desc.clone())), - position: None, - }), - right_expr: Box::new(ScalarExpression::ColumnRef { - column: ColumnRef::from(ColumnCatalog::new("c4".to_owned(), true, desc.clone())), - position: None, - }), + left_expr: Box::new(ScalarExpression::column_expr(ColumnRef::from( + ColumnCatalog::new("c1".to_owned(), true, desc.clone()), + ))), + right_expr: Box::new(ScalarExpression::column_expr(ColumnRef::from( + ColumnCatalog::new("c4".to_owned(), true, desc.clone()), + ))), evaluator: Some(BinaryEvaluatorBox(Arc::new(Int32GtBinaryEvaluator))), ty: LogicalType::Boolean, }; @@ -533,13 +528,34 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let op = JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), + let plan = LogicalPlan::new( + Operator::Join(JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::Inner, + }), + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), }, - join_type: JoinType::Inner, + ); + let plan = HepOptimizer::new(plan) + .batch( + "Expression Remapper".to_string(), + HepBatchStrategy::once_topdown(), + vec![ + NormalizationRuleImpl::BindExpressionPosition, + // TIPS: This rule is necessary + NormalizationRuleImpl::EvaluatorBind, + ], + ) + .find_best::(None)?; + let Operator::Join(op) = plan.operator else { + unreachable!() }; + let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -562,13 +578,34 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let op = JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), + let plan = LogicalPlan::new( + Operator::Join(JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::LeftOuter, + }), + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), }, - join_type: JoinType::LeftOuter, + ); + let plan = HepOptimizer::new(plan) + .batch( + "Expression Remapper".to_string(), + HepBatchStrategy::once_topdown(), + vec![ + NormalizationRuleImpl::BindExpressionPosition, + // TIPS: This rule is necessary + NormalizationRuleImpl::EvaluatorBind, + ], + ) + .find_best::(None)?; + let Operator::Join(op) = plan.operator else { + unreachable!() }; + let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -603,13 +640,34 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let op = JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), + let plan = LogicalPlan::new( + Operator::Join(JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::Cross, + }), + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), }, - join_type: JoinType::Cross, + ); + let plan = HepOptimizer::new(plan) + .batch( + "Expression Remapper".to_string(), + HepBatchStrategy::once_topdown(), + vec![ + NormalizationRuleImpl::BindExpressionPosition, + // TIPS: This rule is necessary + NormalizationRuleImpl::EvaluatorBind, + ], + ) + .find_best::(None)?; + let Operator::Join(op) = plan.operator else { + unreachable!() }; + let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -633,13 +691,34 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, _) = build_join_values(true); - let op = JoinOperator { - on: JoinCondition::On { - on: keys, - filter: None, + let plan = LogicalPlan::new( + Operator::Join(JoinOperator { + on: JoinCondition::On { + on: keys, + filter: None, + }, + join_type: JoinType::Cross, + }), + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), }, - join_type: JoinType::Cross, + ); + let plan = HepOptimizer::new(plan) + .batch( + "Expression Remapper".to_string(), + HepBatchStrategy::once_topdown(), + vec![ + NormalizationRuleImpl::BindExpressionPosition, + // TIPS: This rule is necessary + NormalizationRuleImpl::EvaluatorBind, + ], + ) + .find_best::(None)?; + let Operator::Join(op) = plan.operator else { + unreachable!() }; + let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -666,13 +745,34 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, _) = build_join_values(false); - let op = JoinOperator { - on: JoinCondition::On { - on: keys, - filter: None, + let plan = LogicalPlan::new( + Operator::Join(JoinOperator { + on: JoinCondition::On { + on: keys, + filter: None, + }, + join_type: JoinType::Cross, + }), + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), }, - join_type: JoinType::Cross, + ); + let plan = HepOptimizer::new(plan) + .batch( + "Expression Remapper".to_string(), + HepBatchStrategy::once_topdown(), + vec![ + NormalizationRuleImpl::BindExpressionPosition, + // TIPS: This rule is necessary + NormalizationRuleImpl::EvaluatorBind, + ], + ) + .find_best::(None)?; + let Operator::Join(op) = plan.operator else { + unreachable!() }; + let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -691,13 +791,34 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let op = JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), + let plan = LogicalPlan::new( + Operator::Join(JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::LeftSemi, + }), + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), }, - join_type: JoinType::LeftSemi, + ); + let plan = HepOptimizer::new(plan) + .batch( + "Expression Remapper".to_string(), + HepBatchStrategy::once_topdown(), + vec![ + NormalizationRuleImpl::BindExpressionPosition, + // TIPS: This rule is necessary + NormalizationRuleImpl::EvaluatorBind, + ], + ) + .find_best::(None)?; + let Operator::Join(op) = plan.operator else { + unreachable!() }; + let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -719,13 +840,34 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let op = JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), + let plan = LogicalPlan::new( + Operator::Join(JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::LeftAnti, + }), + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), }, - join_type: JoinType::LeftAnti, + ); + let plan = HepOptimizer::new(plan) + .batch( + "Expression Remapper".to_string(), + HepBatchStrategy::once_topdown(), + vec![ + NormalizationRuleImpl::BindExpressionPosition, + // TIPS: This rule is necessary + NormalizationRuleImpl::EvaluatorBind, + ], + ) + .find_best::(None)?; + let Operator::Join(op) = plan.operator else { + unreachable!() }; + let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -749,13 +891,34 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let op = JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), + let plan = LogicalPlan::new( + Operator::Join(JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::RightOuter, + }), + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), }, - join_type: JoinType::RightOuter, + ); + let plan = HepOptimizer::new(plan) + .batch( + "Expression Remapper".to_string(), + HepBatchStrategy::once_topdown(), + vec![ + NormalizationRuleImpl::BindExpressionPosition, + // TIPS: This rule is necessary + NormalizationRuleImpl::EvaluatorBind, + ], + ) + .find_best::(None)?; + let Operator::Join(op) = plan.operator else { + unreachable!() }; + let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?; @@ -784,13 +947,34 @@ mod test { let view_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let table_cache = Arc::new(SharedLruCache::new(4, 1, RandomState::new())?); let (keys, left, right, filter) = build_join_values(true); - let op = JoinOperator { - on: JoinCondition::On { - on: keys, - filter: Some(filter), + let plan = LogicalPlan::new( + Operator::Join(JoinOperator { + on: JoinCondition::On { + on: keys, + filter: Some(filter), + }, + join_type: JoinType::Full, + }), + Childrens::Twins { + left: Box::new(left), + right: Box::new(right), }, - join_type: JoinType::Full, + ); + let plan = HepOptimizer::new(plan) + .batch( + "Expression Remapper".to_string(), + HepBatchStrategy::once_topdown(), + vec![ + NormalizationRuleImpl::BindExpressionPosition, + // TIPS: This rule is necessary + NormalizationRuleImpl::EvaluatorBind, + ], + ) + .find_best::(None)?; + let Operator::Join(op) = plan.operator else { + unreachable!() }; + let (left, right) = plan.childrens.pop_twins(); let executor = NestedLoopJoin::from((op, left, right)) .execute((&table_cache, &view_cache, &meta_cache), &mut transaction); let tuples = try_collect(executor)?;