From 5ac7795812c9794a06754b283829831817afecb5 Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Sat, 7 Dec 2024 00:30:43 +0800 Subject: [PATCH] Refactor/result iter (#251) * refactor: use `ResultIter`(`DatabaseIter`&`TransactionIter`) for `DataBase` result return * refactor: enumerate the childrens attribute of `LogicalPlan` * chore: fix word (`macros`) --- Cargo.toml | 6 +- benchmarks/query_benchmark.rs | 29 +- examples/hello_world.rs | 26 +- examples/transaction.rs | 16 +- src/bin/server.rs | 15 +- src/binder/alter_table.rs | 6 +- src/binder/analyze.rs | 4 +- src/binder/copy.rs | 5 +- src/binder/create_index.rs | 4 +- src/binder/create_table.rs | 4 +- src/binder/create_view.rs | 4 +- src/binder/delete.rs | 4 +- src/binder/describe.rs | 4 +- src/binder/drop_table.rs | 4 +- src/binder/drop_view.rs | 4 +- src/binder/explain.rs | 4 +- src/binder/insert.rs | 6 +- src/binder/select.rs | 18 +- src/binder/show.rs | 4 +- src/binder/truncate.rs | 4 +- src/binder/update.rs | 4 +- src/db.rs | 238 ++++++++++++----- src/execution/dml/analyze.rs | 34 ++- src/execution/dml/copy_from_file.rs | 5 +- src/execution/dml/copy_to_file.rs | 11 +- src/execution/dql/aggregate/hash_agg.rs | 13 +- src/execution/dql/join/hash_join.rs | 6 +- src/execution/dql/join/nested_loop_join.rs | 5 +- src/execution/mod.rs | 44 +-- src/expression/range_detacher.rs | 20 +- src/lib.rs | 28 +- src/optimizer/core/memo.rs | 27 +- src/optimizer/core/mod.rs | 1 - src/optimizer/core/opt_expr.rs | 45 ---- src/optimizer/heuristic/graph.rs | 59 +++-- src/optimizer/heuristic/matcher.rs | 18 +- .../implementation/{marcos.rs => macros.rs} | 0 src/optimizer/rule/implementation/mod.rs | 2 +- .../rule/normalization/column_pruning.rs | 14 +- .../rule/normalization/combine_operators.rs | 15 +- .../rule/normalization/pushdown_limit.rs | 12 +- .../rule/normalization/pushdown_predicates.rs | 23 +- .../rule/normalization/simplification.rs | 15 +- src/planner/mod.rs | 92 +++++-- src/planner/operator/aggregate.rs | 4 +- src/planner/operator/filter.rs | 6 +- src/planner/operator/function_scan.rs | 4 +- src/planner/operator/join.rs | 4 +- src/planner/operator/limit.rs | 4 +- src/planner/operator/table_scan.rs | 4 +- src/planner/operator/union.rs | 7 +- src/storage/rocksdb.rs | 18 +- src/types/tuple.rs | 15 +- tests/sqllogictest/src/lib.rs | 30 +-- tpcc/Cargo.toml | 2 +- tpcc/src/delivery.rs | 83 +++--- tpcc/src/load.rs | 135 ++++++---- tpcc/src/main.rs | 250 +++++++++++------- tpcc/src/new_ord.rs | 177 +++++++------ tpcc/src/order_stat.rs | 124 +++++---- tpcc/src/payment.rs | 180 +++++++------ tpcc/src/slev.rs | 63 +++-- 62 files changed, 1183 insertions(+), 829 deletions(-) delete mode 100644 src/optimizer/core/opt_expr.rs rename src/optimizer/rule/implementation/{marcos.rs => macros.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index 406e3290..3f3f8118 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "fnck_sql" -version = "0.0.6" +version = "0.0.7" edition = "2021" authors = ["Kould ", "Xwg "] description = "SQL as a Function for Rust" @@ -23,8 +23,8 @@ required-features = ["net"] doctest = false [features] -default = ["marcos"] -marcos = [] +default = ["macros"] +macros = [] net = ["dep:pgwire", "dep:async-trait", "dep:clap", "dep:env_logger", "dep:futures", "dep:log", "dep:tokio"] [[bench]] diff --git a/benchmarks/query_benchmark.rs b/benchmarks/query_benchmark.rs index 2a943fc8..605edd54 100644 --- a/benchmarks/query_benchmark.rs +++ b/benchmarks/query_benchmark.rs @@ -1,5 +1,5 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use fnck_sql::db::DataBaseBuilder; +use fnck_sql::db::{DataBaseBuilder, ResultIter}; use fnck_sql::errors::DatabaseError; use indicatif::{ProgressBar, ProgressStyle}; use itertools::Itertools; @@ -25,10 +25,10 @@ fn query_cases() -> Vec<(&'static str, &'static str)> { } fn init_fncksql_query_bench() -> Result<(), DatabaseError> { - let database = DataBaseBuilder::path(QUERY_BENCH_FNCK_SQL_PATH) - .build() - .unwrap(); - database.run("create table t1 (c1 int primary key, c2 int)")?; + let database = DataBaseBuilder::path(QUERY_BENCH_FNCK_SQL_PATH).build()?; + database + .run("create table t1 (c1 int primary key, c2 int)")? + .done()?; let pb = ProgressBar::new(TABLE_ROW_NUM); pb.set_style( ProgressStyle::default_bar() @@ -36,12 +36,14 @@ fn init_fncksql_query_bench() -> Result<(), DatabaseError> { .unwrap(), ); for i in 0..TABLE_ROW_NUM { - let _ = database.run(format!("insert into t1 values({}, {})", i, i + 1).as_str())?; + database + .run(format!("insert into t1 values({}, {})", i, i + 1).as_str())? + .done()?; pb.set_position(i + 1); } pb.finish_with_message("Insert completed!"); - let _ = database.run("analyze table t1")?; + database.run("analyze table t1")?.done()?; Ok(()) } @@ -98,19 +100,18 @@ fn query_on_execute(c: &mut Criterion) { for (name, case) in query_cases() { c.bench_function(format!("FnckSQL: {} by '{}'", name, case).as_str(), |b| { b.iter(|| { - let _tuples = database.run(case).unwrap(); + for tuple in database.run(case).unwrap() { + let _ = tuple.unwrap(); + } }) }); let connection = sqlite::open(QUERY_BENCH_SQLITE_PATH.to_owned()).unwrap(); c.bench_function(format!("SQLite: {} by '{}'", name, case).as_str(), |b| { b.iter(|| { - let _tuples = connection - .prepare(case) - .unwrap() - .into_iter() - .map(|row| row.unwrap()) - .collect_vec(); + for row in connection.prepare(case).unwrap() { + let _ = row.unwrap(); + } }) }); } diff --git a/examples/hello_world.rs b/examples/hello_world.rs index a0f36b6c..4237ab65 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -1,8 +1,7 @@ -use fnck_sql::db::DataBaseBuilder; +use fnck_sql::db::{DataBaseBuilder, ResultIter}; use fnck_sql::errors::DatabaseError; use fnck_sql::implement_from_tuple; use fnck_sql::types::value::DataValue; -use itertools::Itertools; #[derive(Default, Debug, PartialEq)] struct MyStruct { @@ -25,21 +24,24 @@ implement_from_tuple!( ) ); -#[cfg(feature = "marcos")] +#[cfg(feature = "macros")] fn main() -> Result<(), DatabaseError> { let database = DataBaseBuilder::path("./hello_world").build()?; - let _ = database.run("create table if not exists my_struct (c1 int primary key, c2 int)")?; - let _ = database.run("insert into my_struct values(0, 0), (1, 1)")?; - let (schema, tuples) = database.run("select * from my_struct")?; - let tuples = tuples - .into_iter() - .map(|tuple| MyStruct::from((&schema, tuple))) - .collect_vec(); + database + .run("create table if not exists my_struct (c1 int primary key, c2 int)")? + .done()?; + database + .run("insert into my_struct values(0, 0), (1, 1)")? + .done()?; - println!("{:#?}", tuples); + let iter = database.run("select * from my_struct")?; + let schema = iter.schema().clone(); - let _ = database.run("drop table my_struct")?; + for tuple in iter { + println!("{:?}", MyStruct::from((&schema, tuple?))); + } + database.run("drop table my_struct")?.done()?; Ok(()) } diff --git a/examples/transaction.rs b/examples/transaction.rs index 97d6df4a..9d4d0baf 100644 --- a/examples/transaction.rs +++ b/examples/transaction.rs @@ -1,20 +1,24 @@ -use fnck_sql::db::DataBaseBuilder; +use fnck_sql::db::{DataBaseBuilder, ResultIter}; use fnck_sql::errors::DatabaseError; fn main() -> Result<(), DatabaseError> { let database = DataBaseBuilder::path("./transaction").build()?; - let mut tx_1 = database.new_transaction()?; + let mut transaction = database.new_transaction()?; - let _ = tx_1.run("create table if not exists t1 (c1 int primary key, c2 int)")?; - let _ = tx_1.run("insert into t1 values(0, 0), (1, 1)")?; + transaction + .run("create table if not exists t1 (c1 int primary key, c2 int)")? + .done()?; + transaction + .run("insert into t1 values(0, 0), (1, 1)")? + .done()?; assert!(database.run("select * from t1").is_err()); - tx_1.commit()?; + transaction.commit()?; assert!(database.run("select * from t1").is_ok()); - let _ = database.run("drop table t1")?; + database.run("drop table t1")?.done()?; Ok(()) } diff --git a/src/bin/server.rs b/src/bin/server.rs index 2095beae..8a4afc15 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -1,6 +1,6 @@ use async_trait::async_trait; use clap::Parser; -use fnck_sql::db::{DBTransaction, DataBaseBuilder, Database}; +use fnck_sql::db::{DBTransaction, DataBaseBuilder, Database, ResultIter}; use fnck_sql::errors::DatabaseError; use fnck_sql::storage::rocksdb::RocksStorage; use fnck_sql::types::tuple::{Schema, Tuple}; @@ -172,14 +172,19 @@ impl SimpleQueryHandler for SessionBackend { _ => { let mut guard = self.tx.lock(); - let (schema, tuples) = if let Some(transaction) = guard.as_mut() { - unsafe { transaction.as_mut().run(query) } + let iter = if let Some(transaction) = guard.as_mut() { + unsafe { transaction.as_mut().run(query) }.map(Box::new) + as Result, _> } else { - self.inner.run(query) + self.inner.run(query).map(Box::new) } .map_err(|e| PgWireError::ApiError(Box::new(e)))?; - Ok(vec![Response::Query(encode_tuples(&schema, tuples)?)]) + let mut tuples = Vec::new(); + for tuple in iter { + tuples.push(tuple.map_err(|e| PgWireError::ApiError(Box::new(e)))?); + } + Ok(vec![Response::Query(encode_tuples(iter.schema(), tuples)?)]) } } } diff --git a/src/binder/alter_table.rs b/src/binder/alter_table.rs index bf73e5cf..8d21a3b3 100644 --- a/src/binder/alter_table.rs +++ b/src/binder/alter_table.rs @@ -9,7 +9,7 @@ use crate::planner::operator::alter_table::add_column::AddColumnOperator; use crate::planner::operator::alter_table::drop_column::DropColumnOperator; use crate::planner::operator::table_scan::TableScanOperator; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; impl Binder<'_, '_, T> { @@ -43,7 +43,7 @@ impl Binder<'_, '_, T> { if_not_exists: *if_not_exists, column, }), - vec![plan], + Childrens::Only(plan), ) } AlterTableOperation::DropColumn { @@ -60,7 +60,7 @@ impl Binder<'_, '_, T> { if_exists: *if_exists, column_name, }), - vec![plan], + Childrens::Only(plan), ) } AlterTableOperation::DropPrimaryKey => todo!(), diff --git a/src/binder/analyze.rs b/src/binder/analyze.rs index 3511fa28..ce9d2694 100644 --- a/src/binder/analyze.rs +++ b/src/binder/analyze.rs @@ -3,7 +3,7 @@ use crate::errors::DatabaseError; use crate::planner::operator::analyze::AnalyzeOperator; use crate::planner::operator::table_scan::TableScanOperator; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; use sqlparser::ast::ObjectName; use std::sync::Arc; @@ -31,7 +31,7 @@ impl Binder<'_, '_, T> { table_name, index_metas, }), - vec![scan_op], + Childrens::Only(scan_op), )) } } diff --git a/src/binder/copy.rs b/src/binder/copy.rs index eae6f3c7..398d1761 100644 --- a/src/binder/copy.rs +++ b/src/binder/copy.rs @@ -7,6 +7,7 @@ use crate::errors::DatabaseError; use crate::planner::operator::copy_from_file::CopyFromFileOperator; use crate::planner::operator::copy_to_file::CopyToFileOperator; use crate::planner::operator::Operator; +use crate::planner::Childrens; use fnck_sql_serde_macros::ReferenceSerialization; use serde::{Deserialize, Serialize}; use sqlparser::ast::{CopyOption, CopySource, CopyTarget}; @@ -98,7 +99,7 @@ impl Binder<'_, '_, T> { target: ext_source, schema_ref, }), - vec![], + Childrens::None, )) } else { // COPY FROM @@ -108,7 +109,7 @@ impl Binder<'_, '_, T> { schema_ref, table: table_name.to_string(), }), - vec![], + Childrens::None, )) } } else { diff --git a/src/binder/create_index.rs b/src/binder/create_index.rs index 65a04b32..06660aa1 100644 --- a/src/binder/create_index.rs +++ b/src/binder/create_index.rs @@ -4,7 +4,7 @@ use crate::expression::ScalarExpression; use crate::planner::operator::create_index::CreateIndexOperator; use crate::planner::operator::table_scan::TableScanOperator; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; use crate::types::index::IndexType; use sqlparser::ast::{ObjectName, OrderByExpr}; @@ -60,7 +60,7 @@ impl Binder<'_, '_, T> { if_not_exists, ty, }), - vec![plan], + Childrens::Only(plan), )) } } diff --git a/src/binder/create_table.rs b/src/binder/create_table.rs index abdf0128..b8bcd9d9 100644 --- a/src/binder/create_table.rs +++ b/src/binder/create_table.rs @@ -10,7 +10,7 @@ use crate::errors::DatabaseError; use crate::expression::ScalarExpression; use crate::planner::operator::create_table::CreateTableOperator; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; use crate::types::LogicalType; @@ -90,7 +90,7 @@ impl Binder<'_, '_, T> { columns, if_not_exists, }), - vec![], + Childrens::None, )) } diff --git a/src/binder/create_view.rs b/src/binder/create_view.rs index 1531d1fa..c48d1db0 100644 --- a/src/binder/create_view.rs +++ b/src/binder/create_view.rs @@ -5,7 +5,7 @@ use crate::errors::DatabaseError; use crate::expression::{AliasType, ScalarExpression}; use crate::planner::operator::create_view::CreateViewOperator; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; use itertools::Itertools; use sqlparser::ast::{Ident, ObjectName, Query}; @@ -56,7 +56,7 @@ impl Binder<'_, '_, T> { }, or_replace: *or_replace, }), - vec![], + Childrens::None, )) } } diff --git a/src/binder/delete.rs b/src/binder/delete.rs index b166c943..026c0844 100644 --- a/src/binder/delete.rs +++ b/src/binder/delete.rs @@ -3,7 +3,7 @@ use crate::errors::DatabaseError; use crate::planner::operator::delete::DeleteOperator; use crate::planner::operator::table_scan::TableScanOperator; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; use itertools::Itertools; use sqlparser::ast::{Expr, TableAlias, TableFactor, TableWithJoins}; @@ -52,7 +52,7 @@ impl Binder<'_, '_, T> { table_name, primary_keys, }), - vec![plan], + Childrens::Only(plan), )) } else { unreachable!("only table") diff --git a/src/binder/describe.rs b/src/binder/describe.rs index 2a99791b..d77ecf2b 100644 --- a/src/binder/describe.rs +++ b/src/binder/describe.rs @@ -2,7 +2,7 @@ use crate::binder::{lower_case_name, Binder}; use crate::errors::DatabaseError; use crate::planner::operator::describe::DescribeOperator; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; use sqlparser::ast::ObjectName; use std::sync::Arc; @@ -16,7 +16,7 @@ impl Binder<'_, '_, T> { Ok(LogicalPlan::new( Operator::Describe(DescribeOperator { table_name }), - vec![], + Childrens::None, )) } } diff --git a/src/binder/drop_table.rs b/src/binder/drop_table.rs index ec31dfce..30bc13f0 100644 --- a/src/binder/drop_table.rs +++ b/src/binder/drop_table.rs @@ -2,7 +2,7 @@ use crate::binder::{lower_case_name, Binder}; use crate::errors::DatabaseError; use crate::planner::operator::drop_table::DropTableOperator; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; use sqlparser::ast::ObjectName; use std::sync::Arc; @@ -20,7 +20,7 @@ impl Binder<'_, '_, T> { table_name, if_exists: *if_exists, }), - vec![], + Childrens::None, )) } } diff --git a/src/binder/drop_view.rs b/src/binder/drop_view.rs index 9707f139..4e635b7c 100644 --- a/src/binder/drop_view.rs +++ b/src/binder/drop_view.rs @@ -2,7 +2,7 @@ use crate::binder::{lower_case_name, Binder}; use crate::errors::DatabaseError; use crate::planner::operator::drop_view::DropViewOperator; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; use sqlparser::ast::ObjectName; use std::sync::Arc; @@ -20,7 +20,7 @@ impl Binder<'_, '_, T> { view_name, if_exists: *if_exists, }), - vec![], + Childrens::None, )) } } diff --git a/src/binder/explain.rs b/src/binder/explain.rs index 8620e1cd..9119feb9 100644 --- a/src/binder/explain.rs +++ b/src/binder/explain.rs @@ -1,11 +1,11 @@ use crate::binder::Binder; use crate::errors::DatabaseError; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; impl Binder<'_, '_, T> { pub(crate) fn bind_explain(&mut self, plan: LogicalPlan) -> Result { - Ok(LogicalPlan::new(Operator::Explain, vec![plan])) + Ok(LogicalPlan::new(Operator::Explain, Childrens::Only(plan))) } } diff --git a/src/binder/insert.rs b/src/binder/insert.rs index 810037b0..56f4dce3 100644 --- a/src/binder/insert.rs +++ b/src/binder/insert.rs @@ -4,7 +4,7 @@ use crate::expression::ScalarExpression; use crate::planner::operator::insert::InsertOperator; use crate::planner::operator::values::ValuesOperator; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; use crate::types::tuple::SchemaRef; use crate::types::value::DataValue; @@ -99,7 +99,7 @@ impl Binder<'_, '_, T> { is_overwrite, is_mapping_by_name, }), - vec![values_plan], + Childrens::Only(values_plan), )) } @@ -110,7 +110,7 @@ impl Binder<'_, '_, T> { ) -> LogicalPlan { LogicalPlan::new( Operator::Values(ValuesOperator { rows, schema_ref }), - vec![], + Childrens::None, ) } } diff --git a/src/binder/select.rs b/src/binder/select.rs index eb23b3dd..b079eb6e 100644 --- a/src/binder/select.rs +++ b/src/binder/select.rs @@ -27,7 +27,7 @@ use crate::planner::operator::insert::InsertOperator; use crate::planner::operator::join::JoinCondition; use crate::planner::operator::sort::{SortField, SortOperator}; use crate::planner::operator::union::UnionOperator; -use crate::planner::{LogicalPlan, SchemaOutput}; +use crate::planner::{Childrens, LogicalPlan, SchemaOutput}; use crate::storage::Transaction; use crate::types::tuple::{Schema, SchemaRef}; use crate::types::{ColumnId, LogicalType}; @@ -75,7 +75,7 @@ impl<'a: 'b, 'b, T: Transaction> Binder<'a, 'b, T> { orderby: &[OrderByExpr], ) -> Result { let mut plan = if select.from.is_empty() { - LogicalPlan::new(Operator::Dummy, vec![]) + LogicalPlan::new(Operator::Dummy, Childrens::None) } else { let mut plan = self.bind_table_ref(&select.from[0])?; @@ -152,7 +152,7 @@ impl<'a: 'b, 'b, T: Transaction> Binder<'a, 'b, T> { is_overwrite: false, is_mapping_by_name: true, }), - vec![plan], + Childrens::Only(plan), ) } @@ -224,7 +224,13 @@ impl<'a: 'b, 'b, T: Transaction> Binder<'a, 'b, T> { .collect_vec(); Ok(self.bind_distinct( - LogicalPlan::new(union_op, vec![left_plan, right_plan]), + LogicalPlan::new( + union_op, + Childrens::Twins { + left: left_plan, + right: right_plan, + }, + ), distinct_exprs, )) } @@ -669,7 +675,7 @@ impl<'a: 'b, 'b, T: Transaction> Binder<'a, 'b, T> { Ok(LogicalPlan::new( Operator::Project(ProjectOperator { exprs: select_list }), - vec![children], + Childrens::Only(children), )) } @@ -681,7 +687,7 @@ impl<'a: 'b, 'b, T: Transaction> Binder<'a, 'b, T> { sort_fields, limit: None, }), - vec![children], + Childrens::Only(children), ) } diff --git a/src/binder/show.rs b/src/binder/show.rs index 9d9b0536..2c49f1fc 100644 --- a/src/binder/show.rs +++ b/src/binder/show.rs @@ -1,11 +1,11 @@ use crate::binder::Binder; use crate::errors::DatabaseError; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; impl Binder<'_, '_, T> { pub(crate) fn bind_show_tables(&mut self) -> Result { - Ok(LogicalPlan::new(Operator::Show, vec![])) + Ok(LogicalPlan::new(Operator::Show, Childrens::None)) } } diff --git a/src/binder/truncate.rs b/src/binder/truncate.rs index a1a0ee98..39720670 100644 --- a/src/binder/truncate.rs +++ b/src/binder/truncate.rs @@ -2,7 +2,7 @@ use crate::binder::{lower_case_name, Binder}; use crate::errors::DatabaseError; use crate::planner::operator::truncate::TruncateOperator; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; use sqlparser::ast::ObjectName; use std::sync::Arc; @@ -16,7 +16,7 @@ impl Binder<'_, '_, T> { Ok(LogicalPlan::new( Operator::Truncate(TruncateOperator { table_name }), - vec![], + Childrens::None, )) } } diff --git a/src/binder/update.rs b/src/binder/update.rs index b2cc49f6..d33bd50b 100644 --- a/src/binder/update.rs +++ b/src/binder/update.rs @@ -3,7 +3,7 @@ use crate::errors::DatabaseError; use crate::expression::ScalarExpression; use crate::planner::operator::update::UpdateOperator; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Transaction; use sqlparser::ast::{Assignment, Expr, TableFactor, TableWithJoins}; use std::slice; @@ -66,7 +66,7 @@ impl Binder<'_, '_, T> { table_name, value_exprs, }), - vec![plan], + Childrens::Only(plan), )) } else { unreachable!("only table") diff --git a/src/db.rs b/src/db.rs index 79920274..61a518a1 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,6 +1,6 @@ use crate::binder::{command_type, Binder, BinderContext, CommandType}; use crate::errors::DatabaseError; -use crate::execution::{build_write, try_collect}; +use crate::execution::{build_write, Executor}; use crate::expression::function::scala::ScalarFunctionImpl; use crate::expression::function::table::TableFunctionImpl; use crate::expression::function::FunctionSummary; @@ -26,7 +26,10 @@ use parking_lot::{RawRwLock, RwLock}; use std::cell::RefCell; 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; @@ -251,12 +254,12 @@ impl State { stmts.pop().ok_or(DatabaseError::EmptyStatement) } - fn execute( - &self, - transaction: &mut S::TransactionType<'_>, + fn execute<'a>( + &'a self, + transaction: &'a mut S::TransactionType<'_>, stmt: &Statement, args: Args, - ) -> Result<(SchemaRef, Vec), DatabaseError> { + ) -> Result<(SchemaRef, Executor<'a>), DatabaseError> { let args = RefCell::new(args); let mut plan = Self::build_plan( @@ -270,14 +273,13 @@ impl State { self.table_functions(), )?; let schema = plan.output_schema().clone(); - let iterator = build_write( + let executor = build_write( plan, (&self.table_cache, &self.view_cache, &self.meta_cache), transaction, ); - let tuples = try_collect(iterator)?; - Ok((schema, tuples)) + Ok((schema, executor)) } } @@ -289,7 +291,7 @@ pub struct Database { impl Database { /// Run SQL queries. - pub fn run>(&self, sql: T) -> Result<(SchemaRef, Vec), DatabaseError> { + pub fn run>(&self, sql: T) -> Result, DatabaseError> { let statement = self.prepare(sql)?; self.execute(&statement, vec![]) @@ -303,17 +305,18 @@ impl Database { &self, statement: &Statement, args: Args, - ) -> Result<(SchemaRef, Vec), DatabaseError> { + ) -> Result, DatabaseError> { let _guard = if matches!(command_type(statement)?, CommandType::DDL) { MetaDataLock::Write(self.mdl.write_arc()) } else { MetaDataLock::Read(self.mdl.read_arc()) }; - let mut transaction = self.storage.transaction()?; - let (schema, tuples) = self.state.execute(&mut transaction, statement, args)?; - transaction.commit()?; - - Ok((schema, tuples)) + let transaction = Box::into_raw(Box::new(self.storage.transaction()?)); + let (schema, executor) = + self.state + .execute(unsafe { &mut (*transaction) }, statement, args)?; + let inner = Box::into_raw(Box::new(TransactionIter::new(schema, executor))); + Ok(DatabaseIter { transaction, inner }) } pub fn new_transaction(&self) -> Result, DatabaseError> { @@ -329,6 +332,52 @@ impl Database { } } +pub trait ResultIter: Iterator> + Sized { + fn schema(&self) -> &SchemaRef; + + fn done(self) -> Result<(), DatabaseError>; +} + +pub struct DatabaseIter<'a, S: Storage + 'a> { + transaction: *mut S::TransactionType<'a>, + inner: *mut TransactionIter<'a>, +} + +impl Drop for DatabaseIter<'_, S> { + fn drop(&mut self) { + if !self.transaction.is_null() { + unsafe { drop(Box::from_raw(self.transaction)) } + } + if !self.inner.is_null() { + unsafe { drop(Box::from_raw(self.inner)) } + } + } +} + +impl Iterator for DatabaseIter<'_, S> { + type Item = Result; + + fn next(&mut self) -> Option { + unsafe { (*self.inner).next() } + } +} + +impl ResultIter for DatabaseIter<'_, S> { + fn schema(&self) -> &SchemaRef { + unsafe { (*self.inner).schema() } + } + + fn done(mut self) -> Result<(), DatabaseError> { + unsafe { + Box::from_raw(mem::replace(&mut self.inner, std::ptr::null_mut())).done()?; + } + unsafe { + Box::from_raw(mem::replace(&mut self.transaction, std::ptr::null_mut())).commit()?; + } + Ok(()) + } +} + pub struct DBTransaction<'a, S: Storage + 'a> { inner: S::TransactionType<'a>, _guard: ArcRwLockReadGuard, @@ -336,7 +385,7 @@ pub struct DBTransaction<'a, S: Storage + 'a> { } impl DBTransaction<'_, S> { - pub fn run>(&mut self, sql: T) -> Result<(SchemaRef, Vec), DatabaseError> { + pub fn run>(&mut self, sql: T) -> Result, DatabaseError> { let statement = self.state.prepare(sql)?; self.execute(&statement, vec![]) @@ -350,13 +399,14 @@ impl DBTransaction<'_, S> { &mut self, statement: &Statement, args: Args, - ) -> Result<(SchemaRef, Vec), DatabaseError> { + ) -> Result { if matches!(command_type(statement)?, CommandType::DDL) { return Err(DatabaseError::UnsupportedStmt( "`DDL` is not allowed to execute within a transaction".to_string(), )); } - self.state.execute(&mut self.inner, statement, args) + let (schema, executor) = self.state.execute(&mut self.inner, statement, args)?; + Ok(TransactionIter::new(schema, executor)) } pub fn commit(self) -> Result<(), DatabaseError> { @@ -366,12 +416,57 @@ 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, + } + } +} + +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 + } + } +} + +impl ResultIter for TransactionIter<'_> { + fn schema(&self) -> &SchemaRef { + &self.schema + } + + fn done(mut self) -> Result<(), DatabaseError> { + for result in self.by_ref() { + let _ = result?; + } + Ok(()) + } +} + #[cfg(test)] pub(crate) mod test { use crate::catalog::{ColumnCatalog, ColumnDesc, ColumnRef}; - use crate::db::{DataBaseBuilder, DatabaseError}; + use crate::db::{DataBaseBuilder, DatabaseError, ResultIter}; use crate::storage::{Storage, TableCache, Transaction}; - use crate::types::tuple::{create_table, Tuple}; + use crate::types::tuple::Tuple; use crate::types::value::DataValue; use crate::types::LogicalType; use chrono::{Datelike, Local}; @@ -414,9 +509,9 @@ pub(crate) mod test { build_table(&database.state.table_cache(), &mut transaction)?; transaction.commit()?; - let batch = database.run("select * from t1")?; - - println!("{:#?}", batch); + for result in database.run("select * from t1")? { + println!("{:#?}", result?); + } Ok(()) } @@ -425,24 +520,25 @@ pub(crate) mod test { fn test_udf() -> Result<(), DatabaseError> { let temp_dir = TempDir::new().expect("unable to create temporary working directory"); let fnck_sql = DataBaseBuilder::path(temp_dir.path()).build()?; - let (schema, tuples) = fnck_sql.run("select current_date()")?; - println!("{}", create_table(&schema, &tuples)); + let mut iter = fnck_sql.run("select current_date()")?; assert_eq!( - schema, - Arc::new(vec![ColumnRef::from(ColumnCatalog::new( + iter.schema(), + &Arc::new(vec![ColumnRef::from(ColumnCatalog::new( "current_date()".to_string(), true, ColumnDesc::new(LogicalType::Date, None, false, None).unwrap() ))]) ); assert_eq!( - tuples, - vec![Tuple { + iter.next().unwrap()?, + Tuple { id: None, values: vec![DataValue::Date32(Some(Local::now().num_days_from_ce()))], - }] + } ); + assert!(iter.next().is_none()); + Ok(()) } @@ -451,32 +547,32 @@ pub(crate) mod test { fn test_udtf() -> Result<(), DatabaseError> { let temp_dir = TempDir::new().expect("unable to create temporary working directory"); let fnck_sql = DataBaseBuilder::path(temp_dir.path()).build()?; - let (schema, tuples) = fnck_sql.run( + let mut iter = fnck_sql.run( "SELECT * FROM (select * from table(numbers(10)) a ORDER BY number LIMIT 5) OFFSET 3", )?; - println!("{}", create_table(&schema, &tuples)); let mut column = ColumnCatalog::new( "number".to_string(), true, ColumnDesc::new(LogicalType::Integer, None, false, None).unwrap(), ); - let number_column_id = schema[0].id().unwrap(); + let number_column_id = iter.schema()[0].id().unwrap(); column.set_ref_table(Arc::new("a".to_string()), number_column_id, false); - assert_eq!(schema, Arc::new(vec![ColumnRef::from(column)])); + assert_eq!(iter.schema(), &Arc::new(vec![ColumnRef::from(column)])); assert_eq!( - tuples, - vec![ - Tuple { - id: None, - values: vec![DataValue::Int32(Some(3))], - }, - Tuple { - id: None, - values: vec![DataValue::Int32(Some(4))], - }, - ] + iter.next().unwrap()?, + Tuple { + id: None, + values: vec![DataValue::Int32(Some(3))], + } + ); + assert_eq!( + iter.next().unwrap()?, + Tuple { + id: None, + values: vec![DataValue::Int32(Some(4))], + } ); Ok(()) } @@ -486,20 +582,21 @@ pub(crate) mod test { let temp_dir = TempDir::new().expect("unable to create temporary working directory"); let fnck_sql = DataBaseBuilder::path(temp_dir.path()).build()?; - let _ = fnck_sql.run("create table t1 (a int primary key, b int)")?; - let _ = fnck_sql.run("insert into t1 values(0, 0)")?; - let _ = fnck_sql.run("insert into t1 values(1, 1)")?; - let _ = fnck_sql.run("insert into t1 values(2, 2)")?; + fnck_sql + .run("create table t1 (a int primary key, b int)")? + .done()?; + fnck_sql.run("insert into t1 values(0, 0)")?.done()?; + fnck_sql.run("insert into t1 values(1, 1)")?.done()?; + fnck_sql.run("insert into t1 values(2, 2)")?.done()?; // Filter { let statement = fnck_sql.prepare("explain select * from t1 where b > ?1")?; - let (_, tuples) = - fnck_sql.execute(&statement, vec![("?1", DataValue::Int32(Some(0)))])?; + let mut iter = fnck_sql.execute(&statement, vec![("?1", DataValue::Int32(Some(0)))])?; assert_eq!( - tuples[0].values[0].utf8().unwrap(), + iter.next().unwrap()?.values[0].utf8().unwrap(), "Projection [t1.a, t1.b] [Project] Filter (t1.b > 0), Is Having: false [Filter] TableScan t1 -> [a, b] [SeqScan]" @@ -511,7 +608,7 @@ pub(crate) mod test { "explain select a + ?1, max(b + ?2) from t1 where b > ?3 group by a + ?4", )?; - let (_, tuples) = fnck_sql.execute( + let mut iter = fnck_sql.execute( &statement, vec![ ("?1", DataValue::Int32(Some(0))), @@ -521,7 +618,7 @@ pub(crate) mod test { ], )?; assert_eq!( - tuples[0].values[0].utf8().unwrap(), + iter.next().unwrap()?.values[0].utf8().unwrap(), "Projection [(t1.a + 0), Max((t1.b + 0))] [Project] Aggregate [Max((t1.b + 0))] -> Group By [(t1.a + 0)] [HashAggregate] Filter (t1.b > 1), Is Having: false [Filter] @@ -531,7 +628,7 @@ pub(crate) mod test { { let statement = fnck_sql.prepare("explain select *, ?1 from (select * from t1 where b > ?2) left join (select * from t1 where a > ?3) on a > ?4")?; - let (_, tuples) = fnck_sql.execute( + let mut iter = fnck_sql.execute( &statement, vec![ ("?1", DataValue::Int32(Some(9))), @@ -541,7 +638,7 @@ pub(crate) mod test { ], )?; assert_eq!( - tuples[0].values[0].utf8().unwrap(), + iter.next().unwrap()?.values[0].utf8().unwrap(), "Projection [t1.a, t1.b, 9] [Project] LeftOuter Join Where (t1.a > 0) [NestLoopJoin] Projection [t1.a, t1.b] [Project] @@ -561,40 +658,41 @@ pub(crate) mod test { let temp_dir = TempDir::new().expect("unable to create temporary working directory"); let fnck_sql = DataBaseBuilder::path(temp_dir.path()).build()?; - let _ = fnck_sql.run("create table t1 (a int primary key, b int)")?; + fnck_sql + .run("create table t1 (a int primary key, b int)")? + .done()?; let mut tx_1 = fnck_sql.new_transaction()?; let mut tx_2 = fnck_sql.new_transaction()?; - let _ = tx_1.run("insert into t1 values(0, 0)")?; - let _ = tx_1.run("insert into t1 values(1, 1)")?; - - let _ = tx_2.run("insert into t1 values(0, 0)")?; - let _ = tx_2.run("insert into t1 values(3, 3)")?; + tx_1.run("insert into t1 values(0, 0)")?.done()?; + tx_1.run("insert into t1 values(1, 1)")?.done()?; - let (_, tuples_1) = tx_1.run("select * from t1")?; - let (_, tuples_2) = tx_2.run("select * from t1")?; + tx_2.run("insert into t1 values(0, 0)")?.done()?; + tx_2.run("insert into t1 values(3, 3)")?.done()?; - assert_eq!(tuples_1.len(), 2); - assert_eq!(tuples_2.len(), 2); + let mut iter_1 = tx_1.run("select * from t1")?; + let mut iter_2 = tx_2.run("select * from t1")?; assert_eq!( - tuples_1[0].values, + iter_1.next().unwrap()?.values, vec![DataValue::Int32(Some(0)), DataValue::Int32(Some(0))] ); assert_eq!( - tuples_1[1].values, + iter_1.next().unwrap()?.values, vec![DataValue::Int32(Some(1)), DataValue::Int32(Some(1))] ); assert_eq!( - tuples_2[0].values, + iter_2.next().unwrap()?.values, vec![DataValue::Int32(Some(0)), DataValue::Int32(Some(0))] ); assert_eq!( - tuples_2[1].values, + iter_2.next().unwrap()?.values, vec![DataValue::Int32(Some(3)), DataValue::Int32(Some(3))] ); + drop(iter_1); + drop(iter_2); tx_1.commit()?; diff --git a/src/execution/dml/analyze.rs b/src/execution/dml/analyze.rs index da615650..62341061 100644 --- a/src/execution/dml/analyze.rs +++ b/src/execution/dml/analyze.rs @@ -156,7 +156,7 @@ impl fmt::Display for AnalyzeOperator { #[cfg(test)] mod test { - use crate::db::DataBaseBuilder; + use crate::db::{DataBaseBuilder, ResultIter}; use crate::errors::DatabaseError; use crate::execution::dml::analyze::{DEFAULT_NUM_OF_BUCKETS, DEFAULT_STATISTICS_META_PATH}; use crate::optimizer::core::statistics_meta::StatisticsMeta; @@ -177,14 +177,18 @@ mod test { let temp_dir = TempDir::new().expect("unable to create temporary working directory"); let fnck_sql = DataBaseBuilder::path(temp_dir.path()).build()?; - let _ = fnck_sql.run("create table t1 (a int primary key, b int)")?; - let _ = fnck_sql.run("create index b_index on t1 (b)")?; - let _ = fnck_sql.run("create index p_index on t1 (a, b)")?; + fnck_sql + .run("create table t1 (a int primary key, b int)")? + .done()?; + fnck_sql.run("create index b_index on t1 (b)")?.done()?; + fnck_sql.run("create index p_index on t1 (a, b)")?.done()?; for i in 0..DEFAULT_NUM_OF_BUCKETS + 1 { - let _ = fnck_sql.run(format!("insert into t1 values({i}, {})", i % 20))?; + fnck_sql + .run(format!("insert into t1 values({i}, {})", i % 20))? + .done()?; } - let _ = fnck_sql.run("analyze table t1")?; + fnck_sql.run("analyze table t1")?.done()?; let dir_path = dirs::home_dir() .expect("Your system does not have a Config directory!") @@ -220,14 +224,18 @@ mod test { let temp_dir = TempDir::new().expect("unable to create temporary working directory"); let fnck_sql = DataBaseBuilder::path(temp_dir.path()).build()?; - let _ = fnck_sql.run("create table t1 (a int primary key, b int)")?; - let _ = fnck_sql.run("create index b_index on t1 (b)")?; - let _ = fnck_sql.run("create index p_index on t1 (a, b)")?; + fnck_sql + .run("create table t1 (a int primary key, b int)")? + .done()?; + fnck_sql.run("create index b_index on t1 (b)")?.done()?; + fnck_sql.run("create index p_index on t1 (a, b)")?.done()?; for i in 0..DEFAULT_NUM_OF_BUCKETS + 1 { - let _ = fnck_sql.run(format!("insert into t1 values({i}, {i})"))?; + fnck_sql + .run(format!("insert into t1 values({i}, {i})"))? + .done()?; } - let _ = fnck_sql.run("analyze table t1")?; + fnck_sql.run("analyze table t1")?.done()?; let dir_path = dirs::home_dir() .expect("Your system does not have a Config directory!") @@ -246,8 +254,8 @@ mod test { assert_eq!(file_names[1], OsStr::new("1")); assert_eq!(file_names[2], OsStr::new("2")); - let _ = fnck_sql.run("alter table t1 drop column b")?; - let _ = fnck_sql.run("analyze table t1")?; + fnck_sql.run("alter table t1 drop column b")?.done()?; + fnck_sql.run("analyze table t1")?.done()?; let mut entries = fs::read_dir(&dir_path)?; diff --git a/src/execution/dml/copy_from_file.rs b/src/execution/dml/copy_from_file.rs index 0e308c45..1d29200a 100644 --- a/src/execution/dml/copy_from_file.rs +++ b/src/execution/dml/copy_from_file.rs @@ -111,7 +111,7 @@ mod tests { use super::*; use crate::binder::copy::ExtSource; use crate::catalog::{ColumnCatalog, ColumnDesc, ColumnRef, ColumnRelation, ColumnSummary}; - use crate::db::DataBaseBuilder; + use crate::db::{DataBaseBuilder, ResultIter}; use crate::errors::DatabaseError; use crate::storage::Storage; use crate::types::LogicalType; @@ -197,7 +197,8 @@ mod tests { let temp_dir = TempDir::new().unwrap(); let db = DataBaseBuilder::path(temp_dir.path()).build()?; - let _ = db.run("create table test_copy (a int primary key, b float, c varchar(10))"); + db.run("create table test_copy (a int primary key, b float, c varchar(10))")? + .done()?; let storage = db.storage; let mut transaction = storage.transaction()?; diff --git a/src/execution/dml/copy_to_file.rs b/src/execution/dml/copy_to_file.rs index c5c100b6..e5ce481e 100644 --- a/src/execution/dml/copy_to_file.rs +++ b/src/execution/dml/copy_to_file.rs @@ -94,7 +94,7 @@ mod tests { use super::*; use crate::binder::copy::ExtSource; use crate::catalog::{ColumnCatalog, ColumnDesc, ColumnRef, ColumnRelation, ColumnSummary}; - use crate::db::DataBaseBuilder; + use crate::db::{DataBaseBuilder, ResultIter}; use crate::errors::DatabaseError; use crate::storage::Storage; use crate::types::LogicalType; @@ -173,10 +173,11 @@ mod tests { let temp_dir = TempDir::new().unwrap(); let db = DataBaseBuilder::path(temp_dir.path()).build()?; - let _ = db.run("create table t1 (a int primary key, b float, c varchar(10))"); - let _ = db.run("insert into t1 values (1, 1.1, 'foo')"); - let _ = db.run("insert into t1 values (2, 2.0, 'fooo')"); - let _ = db.run("insert into t1 values (3, 2.1, 'fnck')"); + db.run("create table t1 (a int primary key, b float, c varchar(10))")? + .done()?; + db.run("insert into t1 values (1, 1.1, 'foo')")?.done()?; + db.run("insert into t1 values (2, 2.0, 'fooo')")?.done()?; + db.run("insert into t1 values (3, 2.1, 'fnck')")?.done()?; let storage = db.storage; let mut transaction = storage.transaction()?; diff --git a/src/execution/dql/aggregate/hash_agg.rs b/src/execution/dql/aggregate/hash_agg.rs index 1918d7c7..656db0ba 100644 --- a/src/execution/dql/aggregate/hash_agg.rs +++ b/src/execution/dql/aggregate/hash_agg.rs @@ -169,10 +169,9 @@ mod test { use crate::planner::operator::aggregate::AggregateOperator; use crate::planner::operator::values::ValuesOperator; use crate::planner::operator::Operator; - use crate::planner::LogicalPlan; + use crate::planner::{Childrens, LogicalPlan}; use crate::storage::rocksdb::RocksStorage; use crate::storage::Storage; - use crate::types::tuple::create_table; use crate::types::value::DataValue; use crate::types::LogicalType; use crate::utils::lru::SharedLruCache; @@ -235,7 +234,7 @@ mod test { ], schema_ref: t1_schema.clone(), }), - childrens: vec![], + childrens: Box::new(Childrens::None), physical_option: None, _output_schema_ref: None, }; @@ -245,14 +244,6 @@ mod test { .execute((&table_cache, &view_cache, &meta_cache), &transaction), )?; - println!( - "hash_agg_test: \n{}", - create_table( - &Arc::new(vec![t1_schema[0].clone(), t1_schema[1].clone()]), - &tuples - ) - ); - assert_eq!(tuples.len(), 2); let vec_values = tuples.into_iter().map(|tuple| tuple.values).collect_vec(); diff --git a/src/execution/dql/join/hash_join.rs b/src/execution/dql/join/hash_join.rs index 833dad64..1fb1590c 100644 --- a/src/execution/dql/join/hash_join.rs +++ b/src/execution/dql/join/hash_join.rs @@ -428,7 +428,7 @@ mod test { use crate::planner::operator::join::{JoinCondition, JoinOperator, JoinType}; use crate::planner::operator::values::ValuesOperator; use crate::planner::operator::Operator; - use crate::planner::LogicalPlan; + use crate::planner::{Childrens, LogicalPlan}; use crate::storage::rocksdb::RocksStorage; use crate::storage::Storage; use crate::types::value::DataValue; @@ -483,7 +483,7 @@ mod test { ], schema_ref: Arc::new(t1_columns), }), - childrens: vec![], + childrens: Box::new(Childrens::None), physical_option: None, _output_schema_ref: None, }; @@ -514,7 +514,7 @@ mod test { ], schema_ref: Arc::new(t2_columns), }), - childrens: vec![], + childrens: Box::new(Childrens::None), physical_option: None, _output_schema_ref: None, }; diff --git a/src/execution/dql/join/nested_loop_join.rs b/src/execution/dql/join/nested_loop_join.rs index 508b3679..c0755363 100644 --- a/src/execution/dql/join/nested_loop_join.rs +++ b/src/execution/dql/join/nested_loop_join.rs @@ -394,6 +394,7 @@ mod test { use crate::expression::ScalarExpression; use crate::planner::operator::values::ValuesOperator; use crate::planner::operator::Operator; + use crate::planner::Childrens; use crate::storage::rocksdb::RocksStorage; use crate::storage::Storage; use crate::types::evaluator::int32::Int32GtBinaryEvaluator; @@ -463,7 +464,7 @@ mod test { ], schema_ref: Arc::new(t1_columns), }), - childrens: vec![], + childrens: Box::new(Childrens::None), physical_option: None, _output_schema_ref: None, }; @@ -494,7 +495,7 @@ mod test { ], schema_ref: Arc::new(t2_columns), }), - childrens: vec![], + childrens: Box::new(Childrens::None), physical_option: None, _output_schema_ref: None, }; diff --git a/src/execution/mod.rs b/src/execution/mod.rs index 505953fa..c63c9eb2 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -41,8 +41,7 @@ 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, CoroutineState}; -use std::pin::Pin; +use std::ops::Coroutine; pub type Executor<'a> = Box, Return = ()> + 'a + Unpin>; @@ -70,14 +69,14 @@ pub fn build_read<'a, T: Transaction + 'a>( ) -> Executor<'a> { let LogicalPlan { operator, - mut childrens, + childrens, .. } = plan; match operator { Operator::Dummy => Dummy {}.execute(cache, transaction), Operator::Aggregate(op) => { - let input = childrens.pop().unwrap(); + let input = childrens.pop_only(); if op.groupby_exprs.is_empty() { SimpleAggExecutor::from((op, input)).execute(cache, transaction) @@ -86,13 +85,12 @@ pub fn build_read<'a, T: Transaction + 'a>( } } Operator::Filter(op) => { - let input = childrens.pop().unwrap(); + let input = childrens.pop_only(); Filter::from((op, input)).execute(cache, transaction) } Operator::Join(op) => { - let right_input = childrens.pop().unwrap(); - let left_input = childrens.pop().unwrap(); + let (left_input, right_input) = childrens.pop_twins(); match &op.on { JoinCondition::On { on, .. } @@ -106,7 +104,7 @@ pub fn build_read<'a, T: Transaction + 'a>( } } Operator::Project(op) => { - let input = childrens.pop().unwrap(); + let input = childrens.pop_only(); Projection::from((op, input)).execute(cache, transaction) } @@ -123,26 +121,25 @@ pub fn build_read<'a, T: Transaction + 'a>( } Operator::FunctionScan(op) => FunctionScan::from(op).execute(cache, transaction), Operator::Sort(op) => { - let input = childrens.pop().unwrap(); + let input = childrens.pop_only(); Sort::from((op, input)).execute(cache, transaction) } Operator::Limit(op) => { - let input = childrens.pop().unwrap(); + let input = childrens.pop_only(); Limit::from((op, input)).execute(cache, transaction) } Operator::Values(op) => Values::from(op).execute(cache, transaction), Operator::Show => ShowTables.execute(cache, transaction), Operator::Explain => { - let input = childrens.pop().unwrap(); + let input = childrens.pop_only(); Explain::from(input).execute(cache, transaction) } Operator::Describe(op) => Describe::from(op).execute(cache, transaction), Operator::Union(_) => { - let right_input = childrens.pop().unwrap(); - let left_input = childrens.pop().unwrap(); + let (left_input, right_input) = childrens.pop_twins(); Union::from((left_input, right_input)).execute(cache, transaction) } @@ -157,38 +154,38 @@ pub fn build_write<'a, T: Transaction + 'a>( ) -> Executor<'a> { let LogicalPlan { operator, - mut childrens, + childrens, physical_option, _output_schema_ref, } = plan; match operator { Operator::Insert(op) => { - let input = childrens.pop().unwrap(); + let input = childrens.pop_only(); Insert::from((op, input)).execute_mut(cache, transaction) } Operator::Update(op) => { - let input = childrens.pop().unwrap(); + let input = childrens.pop_only(); Update::from((op, input)).execute_mut(cache, transaction) } Operator::Delete(op) => { - let input = childrens.pop().unwrap(); + let input = childrens.pop_only(); Delete::from((op, input)).execute_mut(cache, transaction) } Operator::AddColumn(op) => { - let input = childrens.pop().unwrap(); + let input = childrens.pop_only(); AddColumn::from((op, input)).execute_mut(cache, transaction) } Operator::DropColumn(op) => { - let input = childrens.pop().unwrap(); + let input = childrens.pop_only(); DropColumn::from((op, input)).execute_mut(cache, transaction) } Operator::CreateTable(op) => CreateTable::from(op).execute_mut(cache, transaction), Operator::CreateIndex(op) => { - let input = childrens.pop().unwrap(); + let input = childrens.pop_only(); CreateIndex::from((op, input)).execute_mut(cache, transaction) } @@ -200,7 +197,7 @@ pub fn build_write<'a, T: Transaction + 'a>( Operator::CopyToFile(op) => CopyToFile::from(op).execute(cache, transaction), Operator::Analyze(op) => { - let input = childrens.pop().unwrap(); + let input = childrens.pop_only(); Analyze::from((op, input)).execute_mut(cache, transaction) } @@ -217,10 +214,13 @@ 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 CoroutineState::Yielded(tuple) = Pin::new(&mut executor).resume(()) { + while let std::ops::CoroutineState::Yielded(tuple) = + std::pin::Pin::new(&mut executor).resume(()) + { output.push(tuple?); } Ok(output) diff --git a/src/expression/range_detacher.rs b/src/expression/range_detacher.rs index 9c77c719..2f15d9c2 100644 --- a/src/expression/range_detacher.rs +++ b/src/expression/range_detacher.rs @@ -116,7 +116,7 @@ impl Range { DataValue::Tuple(Some((merge_tuple, is_upper))) } - fn _to_tuple_range(tuple: &[&DataValue], range: Range) -> Range { + fn collect_tuple_range(result_ranges: &mut Vec, tuple: &[&DataValue], range: Range) { fn merge_value_on_bound( tuple: &[&DataValue], is_upper: bool, @@ -136,17 +136,16 @@ impl Range { } match range { - Range::Scope { min, max } => Range::Scope { + Range::Scope { min, max } => result_ranges.push(Range::Scope { min: merge_value_on_bound(tuple, false, min), max: merge_value_on_bound(tuple, true, max), - }, - Range::Eq(v) => Range::Eq(merge_value(tuple, false, v)), - Range::Dummy => Range::Dummy, + }), + Range::Eq(v) => result_ranges.push(Range::Eq(merge_value(tuple, false, v))), + Range::Dummy => result_ranges.push(Range::Dummy), Range::SortedRanges(mut ranges) => { for range in &mut ranges { - *range = _to_tuple_range(tuple, mem::replace(range, Range::Dummy)); + collect_tuple_range(result_ranges, tuple, mem::replace(range, Range::Dummy)) } - Range::SortedRanges(ranges) } } } @@ -159,10 +158,7 @@ impl Range { let mut ranges = Vec::new(); for tuple in combinations { - match _to_tuple_range(&tuple, self.clone()) { - Range::SortedRanges(mut res_ranges) => ranges.append(&mut res_ranges), - range => ranges.push(range), - } + collect_tuple_range(&mut ranges, &tuple, self.clone()) } Some(RangeDetacher::ranges2range(ranges)) } @@ -801,7 +797,7 @@ mod test { vec![NormalizationRuleImpl::SimplifyFilter], ) .find_best::(None)?; - if let Operator::Filter(filter_op) = best_plan.childrens[0].clone().operator { + if let Operator::Filter(filter_op) = best_plan.childrens.pop_only().operator { Ok(Some(filter_op)) } else { Ok(None) diff --git a/src/lib.rs b/src/lib.rs index 513358a4..c6f47c07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,13 +37,10 @@ //! # Examples //! //! ```ignore -//! use fnck_sql::db::DataBaseBuilder; +//! use fnck_sql::db::{DataBaseBuilder, ResultIter}; //! use fnck_sql::errors::DatabaseError; //! use fnck_sql::implement_from_tuple; -//! use fnck_sql::types::tuple::{SchemaRef, Tuple}; //! use fnck_sql::types::value::DataValue; -//! use fnck_sql::types::LogicalType; -//! use itertools::Itertools; //! //! #[derive(Default, Debug, PartialEq)] //! struct MyStruct { @@ -70,17 +67,20 @@ //! fn main() -> Result<(), DatabaseError> { //! let database = DataBaseBuilder::path("./hello_world").build()?; //! -//! let _ = database.run("create table if not exists my_struct (c1 int primary key, c2 int)")?; -//! let _ = database.run("insert into my_struct values(0, 0), (1, 1)")?; -//! let (schema, tuples) = database.run("select * from my_struct")?; -//! let tuples = tuples -//! .into_iter() -//! .map(|tuple| MyStruct::from((&schema, tuple))) -//! .collect_vec(); +//! database +//! .run("create table if not exists my_struct (c1 int primary key, c2 int)")? +//! .done()?; +//! database +//! .run("insert into my_struct values(0, 0), (1, 1)")? +//! .done()?; //! -//! println!("{:#?}", tuples); +//! let iter = database.run("select * from my_struct")?; +//! let schema = iter.schema().clone(); //! -//! let _ = database.run("drop table my_struct")?; +//! for tuple in iter { +//! println!("{:?}", MyStruct::from((&schema, tuple?))); +//! } +//! database.run("drop table my_struct")?.done()?; //! //! Ok(()) //! } @@ -103,7 +103,7 @@ pub mod errors; pub mod execution; pub mod expression; mod function; -#[cfg(feature = "marcos")] +#[cfg(feature = "macros")] pub mod macros; mod optimizer; pub mod parser; diff --git a/src/optimizer/core/memo.rs b/src/optimizer/core/memo.rs index 1b378cad..d06dde5c 100644 --- a/src/optimizer/core/memo.rs +++ b/src/optimizer/core/memo.rs @@ -82,7 +82,7 @@ impl Memo { #[cfg(test)] mod tests { use crate::binder::{Binder, BinderContext}; - use crate::db::DataBaseBuilder; + use crate::db::{DataBaseBuilder, ResultIter}; use crate::errors::DatabaseError; use crate::expression::range_detacher::Range; use crate::optimizer::core::memo::Memo; @@ -108,13 +108,19 @@ mod tests { fn test_build_memo() -> Result<(), DatabaseError> { let temp_dir = TempDir::new().expect("unable to create temporary working directory"); let database = DataBaseBuilder::path(temp_dir.path()).build()?; - database.run("create table t1 (c1 int primary key, c2 int)")?; - database.run("create table t2 (c3 int primary key, c4 int)")?; + database + .run("create table t1 (c1 int primary key, c2 int)")? + .done()?; + database + .run("create table t2 (c3 int primary key, c4 int)")? + .done()?; for i in 0..1000 { - let _ = database.run(format!("insert into t1 values({}, {})", i, i + 1).as_str())?; + let _ = database + .run(format!("insert into t1 values({}, {})", i, i + 1).as_str())? + .done()?; } - database.run("analyze table t1")?; + database.run("analyze table t1")?.done()?; let transaction = database.storage.transaction()?; let c1_column_id = { @@ -182,7 +188,16 @@ mod tests { assert!(exprs.exprs[1].cost.unwrap() >= 960); assert!(matches!(exprs.exprs[1].op, PhysicalOption::IndexScan(_))); assert_eq!( - best_plan.as_ref().unwrap().childrens[0].childrens[0].childrens[0].physical_option, + best_plan + .unwrap() + .childrens + .pop_only() + .childrens + .pop_twins() + .0 + .childrens + .pop_only() + .physical_option, Some(PhysicalOption::IndexScan(IndexInfo { meta: Arc::new(IndexMeta { id: 0, diff --git a/src/optimizer/core/mod.rs b/src/optimizer/core/mod.rs index 8865a8ed..c2f41b09 100644 --- a/src/optimizer/core/mod.rs +++ b/src/optimizer/core/mod.rs @@ -1,7 +1,6 @@ pub(crate) mod cm_sketch; pub(crate) mod histogram; pub(crate) mod memo; -pub(crate) mod opt_expr; pub(crate) mod pattern; pub(crate) mod rule; pub(crate) mod statistics_meta; diff --git a/src/optimizer/core/opt_expr.rs b/src/optimizer/core/opt_expr.rs deleted file mode 100644 index a1692758..00000000 --- a/src/optimizer/core/opt_expr.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; -use std::fmt::Debug; - -pub type OptExprNodeId = usize; -#[derive(Clone, Debug)] -pub struct OptExpr { - /// The root of the tree. - pub root: Operator, - /// The root's children expressions. - pub childrens: Vec, -} - -impl OptExpr { - #[allow(dead_code)] - pub fn new(root: Operator, childrens: Vec) -> Self { - Self { root, childrens } - } - - #[allow(dead_code)] - pub fn new_from_op_ref(plan: &LogicalPlan) -> Self { - OptExpr::build_opt_expr_internal(plan) - } - - #[allow(dead_code)] - fn build_opt_expr_internal(input: &LogicalPlan) -> OptExpr { - let root = input.operator.clone(); - let childrens = input - .childrens - .iter() - .map(OptExpr::build_opt_expr_internal) - .collect::>(); - OptExpr { root, childrens } - } - - #[allow(dead_code)] - pub fn to_plan_ref(&self) -> LogicalPlan { - let childrens = self - .childrens - .iter() - .map(|c| c.to_plan_ref()) - .collect::>(); - LogicalPlan::new(self.root.clone(), childrens) - } -} diff --git a/src/optimizer/heuristic/graph.rs b/src/optimizer/heuristic/graph.rs index 047e9c0f..841d6d74 100644 --- a/src/optimizer/heuristic/graph.rs +++ b/src/optimizer/heuristic/graph.rs @@ -1,15 +1,14 @@ use crate::optimizer::core::memo::Memo; -use crate::optimizer::core::opt_expr::OptExprNodeId; use crate::optimizer::heuristic::batch::HepMatchOrder; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use itertools::Itertools; use petgraph::stable_graph::{NodeIndex, StableDiGraph}; use petgraph::visit::{Bfs, EdgeRef}; use std::mem; /// HepNodeId is used in optimizer to identify a node. -pub type HepNodeId = NodeIndex; +pub type HepNodeId = NodeIndex; #[derive(Debug, Clone)] pub struct HepGraph { @@ -30,11 +29,19 @@ impl HepGraph { ) -> HepNodeId { let index = graph.add_node(operator); - for (order, child) in childrens.into_iter().enumerate() { - let child_index = graph_filling(graph, child); - let _ = graph.add_edge(index, child_index, order); + match *childrens { + Childrens::None => (), + Childrens::Only(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 _ = graph.add_edge(index, child_index, 0); + let child_index = graph_filling(graph, right); + let _ = graph.add_edge(index, child_index, 1); + } } - index } @@ -194,18 +201,29 @@ impl HepGraph { } fn build_childrens(&mut self, start: HepNodeId, memo: Option<&Memo>) -> Option { - let mut childrens = Vec::with_capacity(2); let physical_option = memo.and_then(|memo| memo.cheapest_physical_option(&start)); - for child_id in self.children_at(start).collect_vec() { - if let Some(child_plan) = self.build_childrens(child_id, memo) { - childrens.push(child_plan); - } - } + let mut iter = self.children_at(start); + + let child_0 = iter.next(); + let child_1 = iter.next(); + drop(iter); + + let child_0 = child_0.and_then(|id| self.build_childrens(id, memo)); + let child_1 = child_1.and_then(|id| self.build_childrens(id, memo)); + + let childrens = match (child_0, child_1) { + (Some(child_0), Some(child_1)) => Childrens::Twins { + left: child_0, + right: child_1, + }, + (Some(child), None) | (None, Some(child)) => Childrens::Only(child), + (None, None) => Childrens::None, + }; self.graph.remove_node(start).map(|operator| LogicalPlan { operator, - childrens, + childrens: Box::new(childrens), physical_option, _output_schema_ref: None, }) @@ -218,7 +236,7 @@ mod tests { use crate::errors::DatabaseError; use crate::optimizer::heuristic::graph::{HepGraph, HepNodeId}; use crate::planner::operator::Operator; - use crate::planner::LogicalPlan; + use crate::planner::{Childrens, LogicalPlan}; use petgraph::stable_graph::{EdgeIndex, NodeIndex}; #[test] @@ -361,8 +379,15 @@ mod tests { fn clear_output_schema_buf(plan: &mut LogicalPlan) { plan._output_schema_ref = None; - for child in plan.childrens.iter_mut() { - clear_output_schema_buf(child); + match plan.childrens.as_mut() { + Childrens::Only(child) => { + clear_output_schema_buf(child); + } + Childrens::Twins { left, right } => { + clear_output_schema_buf(left); + clear_output_schema_buf(right); + } + Childrens::None => (), } } diff --git a/src/optimizer/heuristic/matcher.rs b/src/optimizer/heuristic/matcher.rs index 57de0023..1e3d0493 100644 --- a/src/optimizer/heuristic/matcher.rs +++ b/src/optimizer/heuristic/matcher.rs @@ -63,7 +63,7 @@ mod tests { use crate::optimizer::heuristic::graph::{HepGraph, HepNodeId}; use crate::optimizer::heuristic::matcher::HepMatcher; use crate::planner::operator::Operator; - use crate::planner::LogicalPlan; + use crate::planner::{Childrens, LogicalPlan}; #[test] fn test_predicate() -> Result<(), DatabaseError> { @@ -97,25 +97,25 @@ mod tests { fn test_recursive() { let all_dummy_plan = LogicalPlan { operator: Operator::Dummy, - childrens: vec![ - LogicalPlan { + childrens: Box::new(Childrens::Twins { + left: LogicalPlan { operator: Operator::Dummy, - childrens: vec![LogicalPlan { + childrens: Box::new(Childrens::Only(LogicalPlan { operator: Operator::Dummy, - childrens: vec![], + childrens: Box::new(Childrens::None), physical_option: None, _output_schema_ref: None, - }], + })), physical_option: None, _output_schema_ref: None, }, - LogicalPlan { + right: LogicalPlan { operator: Operator::Dummy, - childrens: vec![], + 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/implementation/marcos.rs b/src/optimizer/rule/implementation/macros.rs similarity index 100% rename from src/optimizer/rule/implementation/marcos.rs rename to src/optimizer/rule/implementation/macros.rs diff --git a/src/optimizer/rule/implementation/mod.rs b/src/optimizer/rule/implementation/mod.rs index c4f14e1a..a511fe82 100644 --- a/src/optimizer/rule/implementation/mod.rs +++ b/src/optimizer/rule/implementation/mod.rs @@ -1,7 +1,7 @@ pub(crate) mod ddl; pub(crate) mod dml; pub(crate) mod dql; -pub(crate) mod marcos; +pub(crate) mod macros; use crate::errors::DatabaseError; use crate::optimizer::core::memo::GroupExpression; diff --git a/src/optimizer/rule/normalization/column_pruning.rs b/src/optimizer/rule/normalization/column_pruning.rs index cbdb5d25..d573e0bb 100644 --- a/src/optimizer/rule/normalization/column_pruning.rs +++ b/src/optimizer/rule/normalization/column_pruning.rs @@ -197,6 +197,7 @@ mod tests { use crate::optimizer::rule::normalization::NormalizationRuleImpl; use crate::planner::operator::join::JoinCondition; use crate::planner::operator::Operator; + use crate::planner::Childrens; use crate::storage::rocksdb::RocksTransaction; #[test] @@ -212,14 +213,15 @@ mod tests { ) .find_best::(None)?; - assert_eq!(best_plan.childrens.len(), 1); + assert!(matches!(best_plan.childrens.as_ref(), Childrens::Only(_))); match best_plan.operator { Operator::Project(op) => { assert_eq!(op.exprs.len(), 2); } _ => unreachable!("Should be a project operator"), } - match &best_plan.childrens[0].operator { + let join_op = best_plan.childrens.pop_only(); + match &join_op.operator { Operator::Join(op) => match &op.on { JoinCondition::On { on, filter } => { assert_eq!(on.len(), 1); @@ -229,10 +231,12 @@ mod tests { }, _ => unreachable!("Should be a join operator"), } + assert!(matches!( + join_op.childrens.as_ref(), + Childrens::Twins { .. } + )); - assert_eq!(best_plan.childrens[0].childrens.len(), 2); - - for grandson_plan in &best_plan.childrens[0].childrens { + for grandson_plan in join_op.childrens.iter() { match &grandson_plan.operator { Operator::TableScan(op) => { assert_eq!(op.columns.len(), 1); diff --git a/src/optimizer/rule/normalization/combine_operators.rs b/src/optimizer/rule/normalization/combine_operators.rs index c89e1436..56db7d12 100644 --- a/src/optimizer/rule/normalization/combine_operators.rs +++ b/src/optimizer/rule/normalization/combine_operators.rs @@ -148,6 +148,7 @@ mod tests { use crate::optimizer::heuristic::optimizer::HepOptimizer; use crate::optimizer::rule::normalization::NormalizationRuleImpl; use crate::planner::operator::Operator; + use crate::planner::Childrens; use crate::storage::rocksdb::RocksTransaction; use crate::types::value::DataValue; use crate::types::LogicalType; @@ -181,8 +182,9 @@ mod tests { unreachable!("Should be a project operator") } - if let Operator::TableScan(_) = &best_plan.childrens[0].operator { - assert_eq!(best_plan.childrens[0].childrens.len(), 0) + let scan_op = best_plan.childrens.pop_only(); + if let Operator::TableScan(_) = &scan_op.operator { + assert!(matches!(scan_op.childrens.as_ref(), Childrens::None)); } else { unreachable!("Should be a scan operator") } @@ -221,7 +223,8 @@ mod tests { let best_plan = optimizer.find_best::(None)?; - if let Operator::Filter(op) = &best_plan.childrens[0].operator { + let filter_op = best_plan.childrens.pop_only(); + if let Operator::Filter(op) = &filter_op.operator { if let ScalarExpression::Binary { op, .. } = &op.predicate { assert_eq!(op, &BinaryOperator::And); } else { @@ -247,8 +250,10 @@ mod tests { let best_plan = optimizer.find_best::(None)?; - if let Operator::Aggregate(_) = &best_plan.childrens[0].operator { - if let Operator::Aggregate(_) = &best_plan.childrens[0].childrens[0].operator { + let agg_op = best_plan.childrens.pop_only(); + if let Operator::Aggregate(_) = &agg_op.operator { + let inner_agg_op = agg_op.childrens.pop_only(); + if let Operator::Aggregate(_) = &inner_agg_op.operator { unreachable!("Should not be a agg operator") } else { return Ok(()); diff --git a/src/optimizer/rule/normalization/pushdown_limit.rs b/src/optimizer/rule/normalization/pushdown_limit.rs index 07f01e84..a32efd61 100644 --- a/src/optimizer/rule/normalization/pushdown_limit.rs +++ b/src/optimizer/rule/normalization/pushdown_limit.rs @@ -150,7 +150,8 @@ mod tests { unreachable!("Should be a project operator") } - if let Operator::Limit(_) = &best_plan.childrens[0].operator { + let limit_op = best_plan.childrens.pop_only(); + if let Operator::Limit(_) = &limit_op.operator { } else { unreachable!("Should be a limit operator") } @@ -174,12 +175,14 @@ mod tests { ) .find_best::(None)?; - if let Operator::Join(_) = &best_plan.childrens[0].childrens[0].operator { + let join_op = best_plan.childrens.pop_only().childrens.pop_only(); + if let Operator::Join(_) = &join_op.operator { } else { unreachable!("Should be a join operator") } - if let Operator::Limit(op) = &best_plan.childrens[0].childrens[0].childrens[0].operator { + let limit_op = join_op.childrens.pop_twins().0; + if let Operator::Limit(op) = &limit_op.operator { assert_eq!(op.limit, Some(1)); } else { unreachable!("Should be a limit operator") @@ -204,7 +207,8 @@ mod tests { ) .find_best::(None)?; - if let Operator::TableScan(op) = &best_plan.childrens[0].operator { + let scan_op = best_plan.childrens.pop_only(); + if let Operator::TableScan(op) = &scan_op.operator { assert_eq!(op.limit, (Some(1), Some(1))) } else { unreachable!("Should be a project operator") diff --git a/src/optimizer/rule/normalization/pushdown_predicates.rs b/src/optimizer/rule/normalization/pushdown_predicates.rs index 1fcef798..6778d817 100644 --- a/src/optimizer/rule/normalization/pushdown_predicates.rs +++ b/src/optimizer/rule/normalization/pushdown_predicates.rs @@ -327,7 +327,8 @@ mod tests { ) .find_best::(None)?; - if let Operator::TableScan(op) = &best_plan.childrens[0].childrens[0].operator { + let scan_op = best_plan.childrens.pop_only().childrens.pop_only(); + if let Operator::TableScan(op) = &scan_op.operator { let mock_range = Range::Scope { min: Bound::Excluded(DataValue::Int32(Some(1))), max: Bound::Unbounded, @@ -355,7 +356,8 @@ mod tests { ) .find_best::(None)?; - if let Operator::Filter(op) = &best_plan.childrens[0].operator { + let filter_op = best_plan.childrens.pop_only(); + if let Operator::Filter(op) = &filter_op.operator { match op.predicate { ScalarExpression::Binary { op: BinaryOperator::Lt, @@ -368,7 +370,8 @@ mod tests { unreachable!("Should be a filter operator") } - if let Operator::Filter(op) = &best_plan.childrens[0].childrens[0].childrens[0].operator { + let filter_op = filter_op.childrens.pop_only().childrens.pop_twins().0; + if let Operator::Filter(op) = &filter_op.operator { match op.predicate { ScalarExpression::Binary { op: BinaryOperator::Gt, @@ -398,7 +401,8 @@ mod tests { ) .find_best::(None)?; - if let Operator::Filter(op) = &best_plan.childrens[0].operator { + let filter_op = best_plan.childrens.pop_only(); + if let Operator::Filter(op) = &filter_op.operator { match op.predicate { ScalarExpression::Binary { op: BinaryOperator::Gt, @@ -411,7 +415,8 @@ mod tests { unreachable!("Should be a filter operator") } - if let Operator::Filter(op) = &best_plan.childrens[0].childrens[0].childrens[1].operator { + let filter_op = filter_op.childrens.pop_only().childrens.pop_twins().1; + if let Operator::Filter(op) = &filter_op.operator { match op.predicate { ScalarExpression::Binary { op: BinaryOperator::Lt, @@ -441,12 +446,14 @@ mod tests { ) .find_best::(None)?; - if let Operator::Join(_) = &best_plan.childrens[0].operator { + let join_op = best_plan.childrens.pop_only(); + if let Operator::Join(_) = &join_op.operator { } else { unreachable!("Should be a filter operator") } - if let Operator::Filter(op) = &best_plan.childrens[0].childrens[0].operator { + let (left_filter_op, right_filter_op) = join_op.childrens.pop_twins(); + if let Operator::Filter(op) = &left_filter_op.operator { match op.predicate { ScalarExpression::Binary { op: BinaryOperator::Gt, @@ -459,7 +466,7 @@ mod tests { unreachable!("Should be a filter operator") } - if let Operator::Filter(op) = &best_plan.childrens[0].childrens[1].operator { + if let Operator::Filter(op) = &right_filter_op.operator { match op.predicate { ScalarExpression::Binary { op: BinaryOperator::Lt, diff --git a/src/optimizer/rule/normalization/simplification.rs b/src/optimizer/rule/normalization/simplification.rs index 227f75ff..e244e01f 100644 --- a/src/optimizer/rule/normalization/simplification.rs +++ b/src/optimizer/rule/normalization/simplification.rs @@ -151,7 +151,8 @@ mod test { } else { unreachable!(); } - if let Operator::Filter(filter_op) = best_plan.childrens[0].clone().operator { + let filter_op = best_plan.childrens.pop_only(); + if let Operator::Filter(filter_op) = filter_op.operator { let range = RangeDetacher::new("t1", table_state.column_id_by_name("c1")) .detach(&filter_op.predicate) .unwrap(); @@ -202,7 +203,9 @@ mod test { vec![NormalizationRuleImpl::SimplifyFilter], ) .find_best::(None)?; - if let Operator::Filter(filter_op) = best_plan.childrens[0].clone().operator { + + let filter_op = best_plan.childrens.pop_only(); + if let Operator::Filter(filter_op) = filter_op.operator { Ok( RangeDetacher::new("t1", table_state.column_id_by_name("c1")) .detach(&filter_op.predicate), @@ -245,7 +248,9 @@ mod test { vec![NormalizationRuleImpl::SimplifyFilter], ) .find_best::(None)?; - if let Operator::Filter(filter_op) = best_plan.childrens[0].clone().operator { + + let filter_op = best_plan.childrens.pop_only(); + if let Operator::Filter(filter_op) = filter_op.operator { let c1_col = ColumnCatalog::direct_new( ColumnSummary { name: "c1".to_string(), @@ -317,7 +322,9 @@ mod test { vec![NormalizationRuleImpl::SimplifyFilter], ) .find_best::(None)?; - if let Operator::Filter(filter_op) = best_plan.childrens[0].clone().operator { + + let filter_op = best_plan.childrens.pop_only(); + if let Operator::Filter(filter_op) = filter_op.operator { Ok(RangeDetacher::new("t1", &column_id).detach(&filter_op.predicate)) } else { Ok(None) diff --git a/src/planner/mod.rs b/src/planner/mod.rs index 94a4bb96..aa81125e 100644 --- a/src/planner/mod.rs +++ b/src/planner/mod.rs @@ -16,10 +16,76 @@ pub(crate) enum SchemaOutput { SchemaRef(SchemaRef), } +#[derive(Debug, PartialEq, Eq, Clone, Hash, ReferenceSerialization)] +pub enum Childrens { + None, + Only(LogicalPlan), + Twins { + left: LogicalPlan, + right: LogicalPlan, + }, +} + +impl Childrens { + pub fn iter(&self) -> ChildrensIter { + ChildrensIter { + inner: self, + pos: 0, + } + } + + pub fn pop_only(self) -> LogicalPlan { + match self { + Childrens::Only(plan) => plan, + _ => { + unreachable!() + } + } + } + + pub fn pop_twins(self) -> (LogicalPlan, LogicalPlan) { + match self { + Childrens::Twins { left, right } => (left, right), + _ => unreachable!(), + } + } +} + +pub struct ChildrensIter<'a> { + inner: &'a Childrens, + pos: usize, +} + +impl<'a> Iterator for ChildrensIter<'a> { + type Item = &'a LogicalPlan; + + fn next(&mut self) -> Option { + match self.inner { + Childrens::Only(plan) => { + if self.pos > 0 { + return None; + } + self.pos += 1; + Some(plan) + } + Childrens::Twins { left, right } => { + let option = match self.pos { + 0 => Some(left), + 1 => Some(right), + _ => None, + }; + self.pos += 1; + option + } + Childrens::None => None, + } + } +} + #[derive(Debug, PartialEq, Eq, Clone, Hash, ReferenceSerialization)] pub struct LogicalPlan { pub(crate) operator: Operator, - pub(crate) childrens: Vec, + pub(crate) childrens: Box, pub(crate) physical_option: Option, pub(crate) _output_schema_ref: Option, @@ -35,25 +101,21 @@ impl SchemaOutput { } impl LogicalPlan { - pub fn new(operator: Operator, childrens: Vec) -> Self { + pub fn new(operator: Operator, childrens: Childrens) -> Self { Self { operator, - childrens, + childrens: Box::new(childrens), physical_option: None, _output_schema_ref: None, } } - pub fn child(&self, index: usize) -> Option<&LogicalPlan> { - self.childrens.get(index) - } - pub fn referenced_table(&self) -> Vec { fn collect_table(plan: &LogicalPlan, results: &mut Vec) { if let Operator::TableScan(op) = &plan.operator { results.push(op.table_name.clone()); } - for child in &plan.childrens { + for child in plan.childrens.iter() { collect_table(child, results); } } @@ -65,11 +127,11 @@ impl LogicalPlan { pub(crate) fn _output_schema_direct( operator: &Operator, - childrens: &[LogicalPlan], + mut childrens_iter: ChildrensIter, ) -> SchemaOutput { match operator { Operator::Filter(_) | Operator::Sort(_) | Operator::Limit(_) => { - childrens[0].output_schema_direct() + childrens_iter.next().unwrap().output_schema_direct() } Operator::Aggregate(op) => SchemaOutput::Schema( op.agg_calls @@ -80,11 +142,11 @@ impl LogicalPlan { ), Operator::Join(op) => { if matches!(op.join_type, JoinType::LeftSemi | JoinType::LeftAnti) { - return childrens[0].output_schema_direct(); + return childrens_iter.next().unwrap().output_schema_direct(); } let mut columns = Vec::new(); - for plan in childrens.iter() { + for plan in childrens_iter { for column in plan.output_schema_direct().columns() { columns.push(column.clone()); } @@ -172,12 +234,12 @@ impl LogicalPlan { } pub(crate) fn output_schema_direct(&self) -> SchemaOutput { - Self::_output_schema_direct(&self.operator, &self.childrens) + Self::_output_schema_direct(&self.operator, self.childrens.iter()) } pub fn output_schema(&mut self) -> &SchemaRef { self._output_schema_ref.get_or_insert_with(|| { - match Self::_output_schema_direct(&self.operator, &self.childrens) { + match Self::_output_schema_direct(&self.operator, self.childrens.iter()) { SchemaOutput::Schema(schema) => Arc::new(schema), SchemaOutput::SchemaRef(schema_ref) => schema_ref.clone(), } @@ -191,7 +253,7 @@ impl LogicalPlan { result.push_str(&format!(" [{}]", physical_option)); } - for child in &self.childrens { + for child in self.childrens.iter() { result.push('\n'); result.push_str(&child.explain(indentation + 2)); } diff --git a/src/planner/operator/aggregate.rs b/src/planner/operator/aggregate.rs index 79d5860e..185868e0 100644 --- a/src/planner/operator/aggregate.rs +++ b/src/planner/operator/aggregate.rs @@ -1,4 +1,4 @@ -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::{expression::ScalarExpression, planner::operator::Operator}; use fnck_sql_serde_macros::ReferenceSerialization; use itertools::Itertools; @@ -25,7 +25,7 @@ impl AggregateOperator { agg_calls, is_distinct, }), - vec![children], + Childrens::Only(children), ) } } diff --git a/src/planner/operator/filter.rs b/src/planner/operator/filter.rs index a32b5080..69700a6c 100644 --- a/src/planner/operator/filter.rs +++ b/src/planner/operator/filter.rs @@ -1,8 +1,8 @@ use crate::expression::ScalarExpression; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use fnck_sql_serde_macros::ReferenceSerialization; +use std::fmt; use std::fmt::Formatter; -use std::{fmt, vec}; use super::Operator; @@ -16,7 +16,7 @@ impl FilterOperator { pub fn build(predicate: ScalarExpression, children: LogicalPlan, having: bool) -> LogicalPlan { LogicalPlan::new( Operator::Filter(FilterOperator { predicate, having }), - vec![children], + Childrens::Only(children), ) } } diff --git a/src/planner/operator/function_scan.rs b/src/planner/operator/function_scan.rs index c6652db3..d6e2eace 100644 --- a/src/planner/operator/function_scan.rs +++ b/src/planner/operator/function_scan.rs @@ -1,6 +1,6 @@ use crate::expression::function::table::TableFunction; use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use fnck_sql_serde_macros::ReferenceSerialization; use std::fmt; use std::fmt::Formatter; @@ -14,7 +14,7 @@ impl FunctionScanOperator { pub fn build(table_function: TableFunction) -> LogicalPlan { LogicalPlan::new( Operator::FunctionScan(FunctionScanOperator { table_function }), - vec![], + Childrens::None, ) } } diff --git a/src/planner/operator/join.rs b/src/planner/operator/join.rs index 73859843..ff5f2d34 100644 --- a/src/planner/operator/join.rs +++ b/src/planner/operator/join.rs @@ -1,6 +1,6 @@ use super::Operator; use crate::expression::ScalarExpression; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use fnck_sql_serde_macros::ReferenceSerialization; use itertools::Itertools; use std::fmt; @@ -42,7 +42,7 @@ impl JoinOperator { ) -> LogicalPlan { LogicalPlan::new( Operator::Join(JoinOperator { on, join_type }), - vec![left, right], + Childrens::Twins { left, right }, ) } } diff --git a/src/planner/operator/limit.rs b/src/planner/operator/limit.rs index 48bbf596..2865d611 100644 --- a/src/planner/operator/limit.rs +++ b/src/planner/operator/limit.rs @@ -1,5 +1,5 @@ use super::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use fnck_sql_serde_macros::ReferenceSerialization; use std::fmt; use std::fmt::Formatter; @@ -18,7 +18,7 @@ impl LimitOperator { ) -> LogicalPlan { LogicalPlan::new( Operator::Limit(LimitOperator { offset, limit }), - vec![children], + Childrens::Only(children), ) } } diff --git a/src/planner/operator/table_scan.rs b/src/planner/operator/table_scan.rs index af2fa59c..1e9d01a1 100644 --- a/src/planner/operator/table_scan.rs +++ b/src/planner/operator/table_scan.rs @@ -1,6 +1,6 @@ use super::Operator; use crate::catalog::{ColumnRef, TableCatalog, TableName}; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::storage::Bounds; use crate::types::index::IndexInfo; use crate::types::ColumnId; @@ -52,7 +52,7 @@ impl TableScanOperator { columns, limit: (None, None), }), - vec![], + Childrens::None, ) } } diff --git a/src/planner/operator/union.rs b/src/planner/operator/union.rs index 67eaf92e..d4e9e206 100644 --- a/src/planner/operator/union.rs +++ b/src/planner/operator/union.rs @@ -1,5 +1,5 @@ use crate::planner::operator::Operator; -use crate::planner::LogicalPlan; +use crate::planner::{Childrens, LogicalPlan}; use crate::types::tuple::SchemaRef; use fnck_sql_serde_macros::ReferenceSerialization; use itertools::Itertools; @@ -25,7 +25,10 @@ impl UnionOperator { left_schema_ref, _right_schema_ref: right_schema_ref, }), - vec![left_plan, right_plan], + Childrens::Twins { + left: left_plan, + right: right_plan, + }, ) } } diff --git a/src/storage/rocksdb.rs b/src/storage/rocksdb.rs index 552142da..38b15347 100644 --- a/src/storage/rocksdb.rs +++ b/src/storage/rocksdb.rs @@ -134,7 +134,7 @@ impl InnerIter for RocksIter<'_, '_> { #[cfg(test)] mod test { use crate::catalog::{ColumnCatalog, ColumnDesc, ColumnRef}; - use crate::db::DataBaseBuilder; + use crate::db::{DataBaseBuilder, ResultIter}; use crate::errors::DatabaseError; use crate::expression::range_detacher::Range; use crate::storage::rocksdb::RocksStorage; @@ -231,8 +231,12 @@ mod test { let temp_dir = TempDir::new().expect("unable to create temporary working directory"); let fnck_sql = DataBaseBuilder::path(temp_dir.path()).build()?; - let _ = fnck_sql.run("create table t1 (a int primary key)")?; - let _ = fnck_sql.run("insert into t1 (a) values (0), (1), (2), (3), (4)")?; + fnck_sql + .run("create table t1 (a int primary key)")? + .done()?; + fnck_sql + .run("insert into t1 (a) values (0), (1), (2), (3), (4)")? + .done()?; let transaction = fnck_sql.storage.transaction()?; let table_name = Arc::new("t1".to_string()); @@ -294,8 +298,12 @@ mod test { fn test_read_by_index() -> Result<(), DatabaseError> { let temp_dir = TempDir::new().expect("unable to create temporary working directory"); let fnck_sql = DataBaseBuilder::path(temp_dir.path()).build()?; - let _ = fnck_sql.run("create table t1 (a int primary key, b int unique)")?; - let _ = fnck_sql.run("insert into t1 (a, b) values (0, 0), (1, 1), (2, 2)")?; + fnck_sql + .run("create table t1 (a int primary key, b int unique)")? + .done()?; + fnck_sql + .run("insert into t1 (a, b) values (0, 0), (1, 1), (2, 2)")? + .done()?; let transaction = fnck_sql.storage.transaction().unwrap(); let table = transaction diff --git a/src/types/tuple.rs b/src/types/tuple.rs index 3acecb86..73c07802 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -1,4 +1,5 @@ use crate::catalog::ColumnRef; +use crate::db::ResultIter; use crate::errors::DatabaseError; use crate::types::tuple_builder::TupleIdBuilder; use crate::types::value::DataValue; @@ -131,20 +132,18 @@ impl Tuple { } } -pub fn create_table(schema: &Schema, tuples: &[Tuple]) -> Table { +pub fn create_table(iter: I) -> Result { let mut table = Table::new(); - - if tuples.is_empty() { - return table; - } - let mut header = Vec::new(); + let schema = iter.schema().clone(); + for col in schema.iter() { header.push(Cell::new(col.full_name())); } table.set_header(header); - for tuple in tuples { + for tuple in iter { + let tuple = tuple?; debug_assert_eq!(schema.len(), tuple.values.len()); let cells = tuple @@ -156,7 +155,7 @@ pub fn create_table(schema: &Schema, tuples: &[Tuple]) -> Table { table.add_row(cells); } - table + Ok(table) } #[cfg(test)] diff --git a/tests/sqllogictest/src/lib.rs b/tests/sqllogictest/src/lib.rs index c31c43b6..10591b2d 100644 --- a/tests/sqllogictest/src/lib.rs +++ b/tests/sqllogictest/src/lib.rs @@ -1,4 +1,4 @@ -use fnck_sql::db::Database; +use fnck_sql::db::{Database, ResultIter}; use fnck_sql::errors::DatabaseError; use fnck_sql::storage::rocksdb::RocksStorage; use sqllogictest::{DBOutput, DefaultColumnType, DB}; @@ -14,25 +14,25 @@ impl DB for SQLBase { fn run(&mut self, sql: &str) -> Result, Self::Error> { let start = Instant::now(); - let (schema, tuples) = self.db.run(sql)?; + let mut iter = self.db.run(sql)?; println!("|— Input SQL: {}", sql); - println!(" |— time spent: {:?}", start.elapsed()); - - if tuples.is_empty() { - return Ok(DBOutput::StatementComplete(0)); - } + let types = vec![DefaultColumnType::Any; iter.schema().len()]; + let mut rows = Vec::new(); - let types = vec![DefaultColumnType::Any; schema.len()]; - let rows = tuples - .into_iter() - .map(|tuple| { - tuple + for tuple in iter.by_ref() { + rows.push( + tuple? .values .into_iter() .map(|value| format!("{}", value)) - .collect() - }) - .collect(); + .collect(), + ) + } + iter.done()?; + println!(" |— time spent: {:?}", start.elapsed()); + if rows.is_empty() { + return Ok(DBOutput::StatementComplete(0)); + } Ok(DBOutput::Rows { types, rows }) } } diff --git a/tpcc/Cargo.toml b/tpcc/Cargo.toml index 54102107..09bff840 100644 --- a/tpcc/Cargo.toml +++ b/tpcc/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] clap = { version = "4", features = ["derive"] } chrono = { version = "0.4" } -fnck_sql = { version = "0.0.6", path = "..", package = "fnck_sql" } +fnck_sql = { version = "0.0.7", path = "..", package = "fnck_sql" } indicatif = { version = "0.17" } ordered-float = { version = "4" } rand = { version = "0.8" } diff --git a/tpcc/src/delivery.rs b/tpcc/src/delivery.rs index 50ae0751..10a1b7ec 100644 --- a/tpcc/src/delivery.rs +++ b/tpcc/src/delivery.rs @@ -1,7 +1,7 @@ use crate::load::DIST_PER_WARE; use crate::{TpccArgs, TpccError, TpccTest, TpccTransaction}; use chrono::Utc; -use fnck_sql::db::{DBTransaction, Statement}; +use fnck_sql::db::{DBTransaction, ResultIter, Statement}; use fnck_sql::storage::Storage; use fnck_sql::types::value::DataValue; use rand::prelude::ThreadRng; @@ -34,39 +34,46 @@ impl TpccTransaction for Delivery { for d_id in 1..DIST_PER_WARE + 1 { // "SELECT COALESCE(MIN(no_o_id),0) FROM new_orders WHERE no_d_id = ? AND no_w_id = ?" - let (_, tuples) = tx.execute( - &statements[0], - vec![ - ("?1", DataValue::Int8(Some(d_id as i8))), - ("?2", DataValue::Int16(Some(args.w_id as i16))), - ], - )?; - let no_o_id = tuples[0].values[0].i32().unwrap(); + let tuple = tx + .execute( + &statements[0], + vec![ + ("?1", DataValue::Int8(Some(d_id as i8))), + ("?2", DataValue::Int16(Some(args.w_id as i16))), + ], + )? + .next() + .unwrap()?; + let no_o_id = tuple.values[0].i32().unwrap(); if no_o_id == 0 { continue; } // "DELETE FROM new_orders WHERE no_o_id = ? AND no_d_id = ? AND no_w_id = ?" - let (_, tuples) = tx.execute( + tx.execute( &statements[1], vec![ ("?1", DataValue::Int32(Some(no_o_id))), ("?2", DataValue::Int8(Some(d_id as i8))), ("?3", DataValue::Int16(Some(args.w_id as i16))), ], - )?; + )? + .done()?; // "SELECT o_c_id FROM orders WHERE o_id = ? AND o_d_id = ? AND o_w_id = ?" - let (_, tuples) = tx.execute( - &statements[2], - vec![ - ("?1", DataValue::Int32(Some(no_o_id))), - ("?2", DataValue::Int8(Some(d_id as i8))), - ("?3", DataValue::Int16(Some(args.w_id as i16))), - ], - )?; - let c_id = tuples[0].values[0].i32().unwrap(); + let tuple = tx + .execute( + &statements[2], + vec![ + ("?1", DataValue::Int32(Some(no_o_id))), + ("?2", DataValue::Int8(Some(d_id as i8))), + ("?3", DataValue::Int16(Some(args.w_id as i16))), + ], + )? + .next() + .unwrap()?; + let c_id = tuple.values[0].i32().unwrap(); // "UPDATE orders SET o_carrier_id = ? WHERE o_id = ? AND o_d_id = ? AND o_w_id = ?" - let (_, tuples) = tx.execute( + tx.execute( &statements[3], vec![ ("?1", DataValue::Int8(Some(args.o_carrier_id as i8))), @@ -74,9 +81,10 @@ impl TpccTransaction for Delivery { ("?3", DataValue::Int8(Some(d_id as i8))), ("?4", DataValue::Int16(Some(args.w_id as i16))), ], - )?; + )? + .done()?; // "UPDATE order_line SET ol_delivery_d = ? WHERE ol_o_id = ? AND ol_d_id = ? AND ol_w_id = ?" - let (_, tuples) = tx.execute( + tx.execute( &statements[4], vec![ ("?1", DataValue::from(&now)), @@ -84,19 +92,23 @@ impl TpccTransaction for Delivery { ("?3", DataValue::Int8(Some(d_id as i8))), ("?4", DataValue::Int16(Some(args.w_id as i16))), ], - )?; + )? + .done()?; // "SELECT SUM(ol_amount) FROM order_line WHERE ol_o_id = ? AND ol_d_id = ? AND ol_w_id = ?" - let (_, tuples) = tx.execute( - &statements[5], - vec![ - ("?1", DataValue::Int32(Some(no_o_id))), - ("?2", DataValue::Int8(Some(d_id as i8))), - ("?3", DataValue::Int16(Some(args.w_id as i16))), - ], - )?; - let ol_total = tuples[0].values[0].decimal().unwrap(); + let tuple = tx + .execute( + &statements[5], + vec![ + ("?1", DataValue::Int32(Some(no_o_id))), + ("?2", DataValue::Int8(Some(d_id as i8))), + ("?3", DataValue::Int16(Some(args.w_id as i16))), + ], + )? + .next() + .unwrap()?; + let ol_total = tuple.values[0].decimal().unwrap(); // "UPDATE customer SET c_balance = c_balance + ? , c_delivery_cnt = c_delivery_cnt + 1 WHERE c_id = ? AND c_d_id = ? AND c_w_id = ?" - let (_, tuples) = tx.execute( + tx.execute( &statements[6], vec![ ("?1", DataValue::Decimal(Some(ol_total))), @@ -104,7 +116,8 @@ impl TpccTransaction for Delivery { ("?3", DataValue::Int8(Some(d_id as i8))), ("?4", DataValue::Int16(Some(args.w_id as i16))), ], - )?; + )? + .done()?; } Ok(()) diff --git a/tpcc/src/load.rs b/tpcc/src/load.rs index 90a44d5e..8122393c 100644 --- a/tpcc/src/load.rs +++ b/tpcc/src/load.rs @@ -1,6 +1,6 @@ use crate::TpccError; use chrono::Utc; -use fnck_sql::db::Database; +use fnck_sql::db::{Database, ResultIter}; use fnck_sql::storage::Storage; use indicatif::{ProgressBar, ProgressStyle}; use rand::rngs::ThreadRng; @@ -89,8 +89,8 @@ impl Load { /// /// primary key (i_id) pub fn load_items(rng: &mut ThreadRng, db: &Database) -> Result<(), TpccError> { - let _ = db.run("drop table if exists item;")?; - let _ = db.run( + db.run("drop table if exists item;")?.done()?; + db.run( "create table item ( i_id int not null, i_im_id int, @@ -98,7 +98,8 @@ impl Load { i_price decimal(5,2), i_data varchar(50), PRIMARY KEY(i_id) );", - )?; + )? + .done()?; let pb = ProgressBar::new(MAX_ITEMS as u64); pb.set_style( ProgressStyle::default_bar() @@ -124,14 +125,15 @@ impl Load { i_data = format!("{}original{}", prefix, remainder); } - let _ = db.run(format!( + db.run(format!( "insert into item values ({i_id}, {i_im_id}, '{i_name}', {i_price}, '{i_data}')" - ))?; + ))? + .done()?; pb.set_position(i_id as u64); } pb.finish_with_message("load completed!"); println!("[Analyze Table: item]"); - let _ = db.run("analyze table item")?; + db.run("analyze table item")?.done()?; Ok(()) } @@ -153,8 +155,8 @@ impl Load { db: &Database, num_ware: usize, ) -> Result<(), TpccError> { - let _ = db.run("drop table if exists warehouse;")?; - let _ = db.run( + db.run("drop table if exists warehouse;")?.done()?; + db.run( "create table warehouse ( w_id smallint not null, w_name varchar(10), @@ -166,9 +168,10 @@ impl Load { w_tax decimal(4,2), w_ytd decimal(12,2), PRIMARY KEY(w_id) );", - )?; - let _ = db.run("drop table if exists stock;")?; - let _ = db.run( + )? + .done()?; + db.run("drop table if exists stock;")?.done()?; + db.run( "create table stock ( s_i_id int not null, s_w_id smallint not null, @@ -188,10 +191,12 @@ impl Load { s_remote_cnt smallint, s_data varchar(50), PRIMARY KEY(s_w_id, s_i_id) );", - )?; - let _ = db.run("CREATE INDEX fkey_stock_2 ON stock (s_i_id);")?; - let _ = db.run("drop table if exists district;")?; - let _ = db.run( + )? + .done()?; + db.run("CREATE INDEX fkey_stock_2 ON stock (s_i_id);")? + .done()?; + db.run("drop table if exists district;")?.done()?; + db.run( "create table district ( d_id tinyint not null, d_w_id smallint not null, @@ -205,7 +210,8 @@ impl Load { d_ytd decimal(12,2), d_next_o_id int, primary key (d_w_id, d_id) );", - )?; + )? + .done()?; let pb = ProgressBar::new(num_ware as u64); pb.set_style( ProgressStyle::default_bar() @@ -227,10 +233,11 @@ impl Load { .round_dp(2); let w_ytd = Decimal::from_f64_retain(3000000.00).unwrap().round_dp(2); - let _ = db.run(format!( + db.run(format!( "insert into warehouse values({}, '{}', '{}', '{}', '{}', '{}', '{}', {}, {})", w_id, w_name, w_street_1, w_street_2, w_city, w_state, w_zip, w_tax, w_ytd, - ))?; + ))? + .done()?; Self::stock(rng, db, w_id)?; Self::district(rng, db, w_id)?; @@ -238,7 +245,7 @@ impl Load { } pb.finish_with_message("load completed!"); println!("[Analyze Table: stock]"); - let _ = db.run("analyze table stock")?; + db.run("analyze table stock")?.done()?; Ok(()) } @@ -247,8 +254,8 @@ impl Load { db: &Database, num_ware: usize, ) -> Result<(), TpccError> { - let _ = db.run("drop table if exists customer;")?; - let _ = db.run( + db.run("drop table if exists customer;")?.done()?; + db.run( "create table customer ( c_id int not null, c_d_id tinyint not null, @@ -272,10 +279,12 @@ impl Load { c_delivery_cnt smallint, c_data text, PRIMARY KEY(c_w_id, c_d_id, c_id) );", - )?; - let _ = db.run("CREATE INDEX idx_customer ON customer (c_w_id,c_d_id,c_last,c_first);")?; - let _ = db.run("drop table if exists history;")?; - let _ = db.run( + )? + .done()?; + db.run("CREATE INDEX idx_customer ON customer (c_w_id,c_d_id,c_last,c_first);")? + .done()?; + db.run("drop table if exists history;")?.done()?; + db.run( "create table history ( h_c_id int, h_c_d_id tinyint, @@ -286,14 +295,15 @@ impl Load { h_amount decimal(6,2), h_data varchar(24), PRIMARY KEY(h_c_id, h_c_d_id, h_c_w_id, h_d_id, h_w_id) );", - )?; + )? + .done()?; for w_id in 1..num_ware + 1 { for d_id in 1..DIST_PER_WARE + 1 { Self::load_customers(rng, db, d_id, w_id)?; } } println!("[Analyze Table: customer]"); - let _ = db.run("analyze table customer")?; + db.run("analyze table customer")?.done()?; Ok(()) } @@ -303,8 +313,8 @@ impl Load { db: &Database, num_ware: usize, ) -> Result<(), TpccError> { - let _ = db.run("drop table if exists orders;")?; - let _ = db.run( + db.run("drop table if exists orders;")?.done()?; + db.run( "create table orders ( o_id int not null, o_d_id tinyint not null, @@ -315,18 +325,21 @@ impl Load { o_ol_cnt tinyint, o_all_local tinyint, PRIMARY KEY(o_w_id, o_d_id, o_id) );", - )?; - let _ = db.run("CREATE INDEX idx_orders ON orders (o_w_id,o_d_id,o_c_id,o_id);")?; - let _ = db.run("drop table if exists new_orders;")?; - let _ = db.run( + )? + .done()?; + db.run("CREATE INDEX idx_orders ON orders (o_w_id,o_d_id,o_c_id,o_id);")? + .done()?; + db.run("drop table if exists new_orders;")?.done()?; + db.run( "create table new_orders ( no_o_id int not null, no_d_id tinyint not null, no_w_id smallint not null, PRIMARY KEY(no_w_id, no_d_id, no_o_id));", - )?; - let _ = db.run("drop table if exists order_line;")?; - let _ = db.run( + )? + .done()?; + db.run("drop table if exists order_line;")?.done()?; + db.run( "create table order_line ( ol_o_id int not null, ol_d_id tinyint not null, @@ -339,19 +352,21 @@ impl Load { ol_amount decimal(6,2), ol_dist_info char(24), PRIMARY KEY(ol_w_id, ol_d_id, ol_o_id, ol_number) );", - )?; - let _ = - db.run("CREATE INDEX fkey_order_line_1 ON order_line (ol_o_id, ol_d_id, ol_w_id);")?; - let _ = db.run("CREATE INDEX fkey_order_line_2 ON order_line (ol_supply_w_id,ol_i_id);")?; + )? + .done()?; + db.run("CREATE INDEX fkey_order_line_1 ON order_line (ol_o_id, ol_d_id, ol_w_id);")? + .done()?; + db.run("CREATE INDEX fkey_order_line_2 ON order_line (ol_supply_w_id,ol_i_id);")? + .done()?; for w_id in 1..num_ware + 1 { for d_id in 1..DIST_PER_WARE + 1 { Self::load_orders(rng, db, d_id, w_id)?; } } println!("[Analyze Table: orders & order_line & new_order]"); - let _ = db.run("analyze table orders")?; - let _ = db.run("analyze table order_line")?; - let _ = db.run("analyze table new_orders")?; + db.run("analyze table orders")?.done()?; + db.run("analyze table order_line")?.done()?; + db.run("analyze table new_orders")?.done()?; Ok(()) } @@ -407,7 +422,7 @@ impl Load { } else { generate_string(rng, 26, 50) }; - let _ = db.run(format!( + db.run(format!( "insert into stock values({}, {}, {}, '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', {}, {}, {}, '{}')", s_i_id, s_w_id, @@ -426,7 +441,7 @@ impl Load { 0, 0, s_data, - ))?; + ))?.done()?; pb.set_position(s_i_id as u64); } pb.finish_with_message("load completed!"); @@ -493,7 +508,7 @@ impl Load { .unwrap() .round_dp(2); - let _ = db.run(format!( + db.run(format!( "insert into district values({}, {}, '{}', '{}', '{}', '{}', '{}', '{}', {}, {}, {})", d_id, d_w_id, @@ -506,7 +521,7 @@ impl Load { d_tax, d_ytd, d_next_o_id, - ))?; + ))?.done()?; pb.set_position(d_id as u64); } pb.finish_with_message("load completed!"); @@ -601,7 +616,7 @@ impl Load { let c_data = generate_string(rng, 300, 500); - let _ = db.run(format!( + db.run(format!( "insert into customer values({}, {}, {}, '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', {}, {}, {}, {}, {}, {}, '{}')", c_id, c_d_id, @@ -624,16 +639,17 @@ impl Load { c_payment_cnt, c_delivery_cnt, c_data, - ))?; + ))?.done()?; let h_date = &date; let h_amount = Decimal::from_f64_retain(10.0).unwrap().round_dp(2); let h_data = generate_string(rng, 12, 24); - let _ = db.run(format!( + db.run(format!( "insert into history values({}, {}, {}, {}, {}, '{}', {}, '{}')", c_id, c_d_id, c_w_id, c_d_id, c_w_id, h_date, h_amount, h_data, - ))?; + ))? + .done()?; pb.set_position(c_id as u64); } pb.finish_with_message("load completed!"); @@ -705,18 +721,20 @@ impl Load { let date = format!("'{}'", Utc::now().format("%Y-%m-%d %H:%M:%S")); let o_carrier_id = if o_id > 2100 { - let _ = db.run(format!( + db.run(format!( "insert into new_orders values({}, {}, {})", o_id, o_d_id, o_w_id, - ))?; + ))? + .done()?; "null".to_string() } else { o_carrier_id.to_string() }; - let _ = db.run(format!( + db.run(format!( "insert into orders values({}, {}, {}, {}, {}, {}, {}, {})", o_id, o_d_id, o_w_id, o_c_id, date, o_carrier_id, o_ol_cnt, "1", - ))?; + ))? + .done()?; for ol in 1..o_ol_cnt + 1 { let ol_i_id = rng.gen_range(1..MAX_ITEMS); @@ -730,7 +748,7 @@ impl Load { } else { (date.as_str(), rng.gen_range(0.1..100.0)) }; - let _ = db.run(format!( + db.run(format!( "insert into order_line values({}, {}, {}, {}, {}, {}, {}, {}, {}, '{}')", o_id, o_d_id, @@ -742,7 +760,8 @@ impl Load { ol_quantity, ol_amount, ol_dist_info, - ))?; + ))? + .done()?; } pb.set_position((o_id - 1) as u64); } diff --git a/tpcc/src/main.rs b/tpcc/src/main.rs index 4d753364..3ab4864f 100644 --- a/tpcc/src/main.rs +++ b/tpcc/src/main.rs @@ -10,7 +10,6 @@ use clap::Parser; use fnck_sql::db::{DBTransaction, DataBaseBuilder, Statement}; use fnck_sql::errors::DatabaseError; use fnck_sql::storage::Storage; -use fnck_sql::types::tuple::create_table; use rand::prelude::ThreadRng; use rand::Rng; use std::time::{Duration, Instant}; @@ -319,93 +318,117 @@ pub enum TpccError { MaxRetry, } +#[ignore] #[test] fn explain_tpcc() -> Result<(), DatabaseError> { + use fnck_sql::db::ResultIter; + use fnck_sql::types::tuple::create_table; + let database = DataBaseBuilder::path("./fnck_sql_tpcc").build()?; let mut tx = database.new_transaction()?; - let (_, customer_tuples) = - tx.run("SELECT c_w_id, c_d_id, c_id, c_last, c_balance, c_data FROM customer limit 1")?; - let (_, district_tuples) = tx.run("SELECT d_id, d_w_id, d_next_o_id FROM district limit 1")?; - let (_, item_tuples) = tx.run("SELECT i_id FROM item limit 1")?; - let (_, stock_tuples) = tx.run("SELECT s_i_id, s_w_id, s_quantity FROM stock limit 1")?; - let (_, orders_tuples) = - tx.run("SELECT o_w_id, o_d_id, o_c_id, o_id, o_carrier_id FROM orders limit 1")?; - let (_, order_line_tuples) = - tx.run("SELECT ol_w_id, ol_d_id, ol_o_id, ol_delivery_d FROM order_line limit 1")?; - let (_, new_order_tuples) = - tx.run("SELECT no_d_id, no_w_id, no_o_id FROM new_orders limit 1")?; - - let c_w_id = customer_tuples[0].values[0].clone(); - let c_d_id = customer_tuples[0].values[1].clone(); - let c_id = customer_tuples[0].values[2].clone(); - let c_last = customer_tuples[0].values[3].clone(); - let c_balance = customer_tuples[0].values[4].clone(); - let c_data = customer_tuples[0].values[5].clone(); - - let d_id = district_tuples[0].values[0].clone(); - let d_w_id = district_tuples[0].values[1].clone(); - let d_next_o_id = district_tuples[0].values[2].clone(); - - let i_id = item_tuples[0].values[0].clone(); - - let s_i_id = stock_tuples[0].values[0].clone(); - let s_w_id = stock_tuples[0].values[1].clone(); - let s_quantity = stock_tuples[0].values[2].clone(); - - let o_w_id = orders_tuples[0].values[0].clone(); - let o_d_id = orders_tuples[0].values[1].clone(); - let o_c_id = orders_tuples[0].values[2].clone(); - let o_id = orders_tuples[0].values[3].clone(); - let o_carrier_id = orders_tuples[0].values[4].clone(); - - let ol_w_id = order_line_tuples[0].values[0].clone(); - let ol_d_id = order_line_tuples[0].values[1].clone(); - let ol_o_id = order_line_tuples[0].values[2].clone(); - let ol_delivery_d = order_line_tuples[0].values[3].clone(); - - let no_d_id = new_order_tuples[0].values[0].clone(); - let no_w_id = new_order_tuples[0].values[1].clone(); - let no_o_id = new_order_tuples[0].values[2].clone(); + let customer_tuple = tx + .run("SELECT c_w_id, c_d_id, c_id, c_last, c_balance, c_data FROM customer limit 1")? + .next() + .unwrap()?; + let district_tuple = tx + .run("SELECT d_id, d_w_id, d_next_o_id FROM district limit 1")? + .next() + .unwrap()?; + let item_tuple = tx.run("SELECT i_id FROM item limit 1")?.next().unwrap()?; + let stock_tuple = tx + .run("SELECT s_i_id, s_w_id, s_quantity FROM stock limit 1")? + .next() + .unwrap()?; + let orders_tuple = tx + .run("SELECT o_w_id, o_d_id, o_c_id, o_id, o_carrier_id FROM orders limit 1")? + .next() + .unwrap()?; + let order_line_tuple = tx + .run("SELECT ol_w_id, ol_d_id, ol_o_id, ol_delivery_d FROM order_line limit 1")? + .next() + .unwrap()?; + let new_order_tuple = tx + .run("SELECT no_d_id, no_w_id, no_o_id FROM new_orders limit 1")? + .next() + .unwrap()?; + + let c_w_id = customer_tuple.values[0].clone(); + let c_d_id = customer_tuple.values[1].clone(); + let c_id = customer_tuple.values[2].clone(); + let c_last = customer_tuple.values[3].clone(); + let c_balance = customer_tuple.values[4].clone(); + let c_data = customer_tuple.values[5].clone(); + + let d_id = district_tuple.values[0].clone(); + let d_w_id = district_tuple.values[1].clone(); + let d_next_o_id = district_tuple.values[2].clone(); + + let i_id = item_tuple.values[0].clone(); + + let s_i_id = stock_tuple.values[0].clone(); + let s_w_id = stock_tuple.values[1].clone(); + let s_quantity = stock_tuple.values[2].clone(); + + let o_w_id = orders_tuple.values[0].clone(); + let o_d_id = orders_tuple.values[1].clone(); + let o_c_id = orders_tuple.values[2].clone(); + let o_id = orders_tuple.values[3].clone(); + let o_carrier_id = orders_tuple.values[4].clone(); + + let ol_w_id = order_line_tuple.values[0].clone(); + let ol_d_id = order_line_tuple.values[1].clone(); + let ol_o_id = order_line_tuple.values[2].clone(); + let ol_delivery_d = order_line_tuple.values[3].clone(); + + let no_d_id = new_order_tuple.values[0].clone(); + let no_w_id = new_order_tuple.values[1].clone(); + let no_o_id = new_order_tuple.values[2].clone(); // ORDER { println!("========Explain on Order"); { println!("{}", format!("explain SELECT c_discount, c_last, c_credit FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_id = {}", c_w_id, c_d_id, c_id)); - let (schema, tuples) = tx.run(format!("explain SELECT c_discount, c_last, c_credit FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_id = {}", c_w_id, c_d_id, c_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT c_discount, c_last, c_credit FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_id = {}", c_w_id, c_d_id, c_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!( + let iter = tx.run(format!( "explain SELECT d_next_o_id, d_tax FROM district WHERE d_id = {} AND d_w_id = {}", d_id, d_w_id ))?; - println!("{}", create_table(&schema, &tuples)); + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!( + let iter = tx.run(format!( "explain UPDATE district SET d_next_o_id = {} + 1 WHERE d_id = {} AND d_w_id = {}", d_next_o_id, d_id, d_w_id ))?; - println!("{}", create_table(&schema, &tuples)); + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!( + let iter = tx.run(format!( "explain SELECT i_price, i_name, i_data FROM item WHERE i_id = {}", i_id ))?; - println!("{}", create_table(&schema, &tuples)); + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain SELECT s_quantity, s_data, s_dist_01, s_dist_02, s_dist_03, s_dist_04, s_dist_05, s_dist_06, s_dist_07, s_dist_08, s_dist_09, s_dist_10 FROM stock WHERE s_i_id = {} AND s_w_id = {}", s_i_id, s_w_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT s_quantity, s_data, s_dist_01, s_dist_02, s_dist_03, s_dist_04, s_dist_05, s_dist_06, s_dist_07, s_dist_08, s_dist_09, s_dist_10 FROM stock WHERE s_i_id = {} AND s_w_id = {}", s_i_id, s_w_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!( + let iter = tx.run(format!( "explain UPDATE stock SET s_quantity = {} WHERE s_i_id = {} AND s_w_id = {}", s_quantity, s_i_id, s_w_id ))?; - println!("{}", create_table(&schema, &tuples)); + + println!("{}", create_table(iter)?); } } @@ -413,39 +436,47 @@ fn explain_tpcc() -> Result<(), DatabaseError> { { println!("========Explain on Payment"); { - let (schema, tuples) = tx.run(format!( + let iter = tx.run(format!( "explain UPDATE stock SET s_quantity = {} WHERE s_i_id = {} AND s_w_id = {}", s_quantity, s_i_id, s_w_id ))?; - println!("{}", create_table(&schema, &tuples)); + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain SELECT d_street_1, d_street_2, d_city, d_state, d_zip, d_name FROM district WHERE d_w_id = {} AND d_id = {}", d_w_id, d_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT d_street_1, d_street_2, d_city, d_state, d_zip, d_name FROM district WHERE d_w_id = {} AND d_id = {}", d_w_id, d_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain SELECT count(c_id) FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_last = '{}'", c_w_id, c_d_id, c_last))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT count(c_id) FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_last = '{}'", c_w_id, c_d_id, c_last))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain SELECT c_id FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_last = '{}' ORDER BY c_first", c_w_id, c_d_id, c_last))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT c_id FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_last = '{}' ORDER BY c_first", c_w_id, c_d_id, c_last))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain SELECT c_first, c_middle, c_last, c_street_1, c_street_2, c_city, c_state, c_zip, c_phone, c_credit, c_credit_lim, c_discount, c_balance, c_since FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_id = {}", c_w_id, c_d_id, c_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT c_first, c_middle, c_last, c_street_1, c_street_2, c_city, c_state, c_zip, c_phone, c_credit, c_credit_lim, c_discount, c_balance, c_since FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_id = {}", c_w_id, c_d_id, c_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain SELECT c_data FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_id = {}", c_w_id, c_d_id, c_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT c_data FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_id = {}", c_w_id, c_d_id, c_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain UPDATE customer SET c_balance = {}, c_data = '{}' WHERE c_w_id = {} AND c_d_id = {} AND c_id = {}", c_balance, c_data, c_w_id, c_d_id, c_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain UPDATE customer SET c_balance = {}, c_data = '{}' WHERE c_w_id = {} AND c_d_id = {} AND c_id = {}", c_balance, c_data, c_w_id, c_d_id, c_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain UPDATE customer SET c_balance = {} WHERE c_w_id = {} AND c_d_id = {} AND c_id = {}", c_balance, c_w_id, c_d_id, c_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain UPDATE customer SET c_balance = {} WHERE c_w_id = {} AND c_d_id = {} AND c_id = {}", c_balance, c_w_id, c_d_id, c_id))?; + + println!("{}", create_table(iter)?); } } @@ -453,24 +484,29 @@ fn explain_tpcc() -> Result<(), DatabaseError> { { println!("========Explain on Order-Stat"); { - let (schema, tuples) = tx.run(format!("explain SELECT count(c_id) FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_last = '{}'", c_w_id, c_d_id, c_last))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT count(c_id) FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_last = '{}'", c_w_id, c_d_id, c_last))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain SELECT c_balance, c_first, c_middle, c_last FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_last = '{}' ORDER BY c_first", c_w_id, c_d_id, c_last))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT c_balance, c_first, c_middle, c_last FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_last = '{}' ORDER BY c_first", c_w_id, c_d_id, c_last))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain SELECT c_balance, c_first, c_middle, c_last FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_id = {}", c_w_id, c_d_id, c_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT c_balance, c_first, c_middle, c_last FROM customer WHERE c_w_id = {} AND c_d_id = {} AND c_id = {}", c_w_id, c_d_id, c_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain SELECT o_id, o_entry_d, COALESCE(o_carrier_id,0) FROM orders WHERE o_w_id = {} AND o_d_id = {} AND o_c_id = {} AND o_id = (SELECT MAX(o_id) FROM orders WHERE o_w_id = {} AND o_d_id = {} AND o_c_id = {})", o_w_id, o_d_id, o_c_id, o_w_id, o_d_id, o_c_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT o_id, o_entry_d, COALESCE(o_carrier_id,0) FROM orders WHERE o_w_id = {} AND o_d_id = {} AND o_c_id = {} AND o_id = (SELECT MAX(o_id) FROM orders WHERE o_w_id = {} AND o_d_id = {} AND o_c_id = {})", o_w_id, o_d_id, o_c_id, o_w_id, o_d_id, o_c_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain SELECT ol_i_id, ol_supply_w_id, ol_quantity, ol_amount, ol_delivery_d FROM order_line WHERE ol_w_id = {} AND ol_d_id = {} AND ol_o_id = {}", ol_w_id, ol_d_id, ol_o_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT ol_i_id, ol_supply_w_id, ol_quantity, ol_amount, ol_delivery_d FROM order_line WHERE ol_w_id = {} AND ol_d_id = {} AND ol_o_id = {}", ol_w_id, ol_d_id, ol_o_id))?; + + println!("{}", create_table(iter)?); } } @@ -478,35 +514,42 @@ fn explain_tpcc() -> Result<(), DatabaseError> { { println!("========Explain on Deliver"); { - let (schema, tuples) = tx.run(format!("explain SELECT COALESCE(MIN(no_o_id),0) FROM new_orders WHERE no_d_id = {} AND no_w_id = {}", no_d_id, no_w_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT COALESCE(MIN(no_o_id),0) FROM new_orders WHERE no_d_id = {} AND no_w_id = {}", no_d_id, no_w_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain DELETE FROM new_orders WHERE no_o_id = {} AND no_d_id = {} AND no_w_id = {}", no_o_id, no_d_id, no_w_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain DELETE FROM new_orders WHERE no_o_id = {} AND no_d_id = {} AND no_w_id = {}", no_o_id, no_d_id, no_w_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!( + let iter = tx.run(format!( "explain SELECT o_c_id FROM orders WHERE o_id = {} AND o_d_id = {} AND o_w_id = {}", o_id, o_d_id, o_w_id ))?; - println!("{}", create_table(&schema, &tuples)); + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain UPDATE orders SET o_carrier_id = {} WHERE o_id = {} AND o_d_id = {} AND o_w_id = {}", o_carrier_id, o_id, o_d_id, o_w_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain UPDATE orders SET o_carrier_id = {} WHERE o_id = {} AND o_d_id = {} AND o_w_id = {}", o_carrier_id, o_id, o_d_id, o_w_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain UPDATE order_line SET ol_delivery_d = '{}' WHERE ol_o_id = {} AND ol_d_id = {} AND ol_w_id = {}", ol_delivery_d, ol_o_id, ol_d_id, ol_w_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain UPDATE order_line SET ol_delivery_d = '{}' WHERE ol_o_id = {} AND ol_d_id = {} AND ol_w_id = {}", ol_delivery_d, ol_o_id, ol_d_id, ol_w_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain SELECT SUM(ol_amount) FROM order_line WHERE ol_o_id = {} AND ol_d_id = {} AND ol_w_id = {}", ol_o_id, ol_d_id, ol_w_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT SUM(ol_amount) FROM order_line WHERE ol_o_id = {} AND ol_d_id = {} AND ol_w_id = {}", ol_o_id, ol_d_id, ol_w_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain UPDATE customer SET c_balance = c_balance + 1 , c_delivery_cnt = c_delivery_cnt + 1 WHERE c_id = {} AND c_d_id = {} AND c_w_id = {}", c_id, c_d_id, c_w_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain UPDATE customer SET c_balance = c_balance + 1 , c_delivery_cnt = c_delivery_cnt + 1 WHERE c_id = {} AND c_d_id = {} AND c_w_id = {}", c_id, c_d_id, c_w_id))?; + + println!("{}", create_table(iter)?); } } @@ -514,19 +557,22 @@ fn explain_tpcc() -> Result<(), DatabaseError> { { println!("========Explain on Stock-Level"); { - let (schema, tuples) = tx.run(format!( + let iter = tx.run(format!( "explain SELECT d_next_o_id FROM district WHERE d_id = {} AND d_w_id = {}", d_id, d_w_id ))?; - println!("{}", create_table(&schema, &tuples)); + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain SELECT DISTINCT ol_i_id FROM order_line WHERE ol_w_id = {} AND ol_d_id = {} AND ol_o_id < {} AND ol_o_id >= ({} - 20)", ol_w_id, ol_d_id, ol_o_id, ol_o_id))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT DISTINCT ol_i_id FROM order_line WHERE ol_w_id = {} AND ol_d_id = {} AND ol_o_id < {} AND ol_o_id >= ({} - 20)", ol_w_id, ol_d_id, ol_o_id, ol_o_id))?; + + println!("{}", create_table(iter)?); } { - let (schema, tuples) = tx.run(format!("explain SELECT count(*) FROM stock WHERE s_w_id = {} AND s_i_id = {} AND s_quantity < {}", s_w_id, s_i_id, s_quantity))?; - println!("{}", create_table(&schema, &tuples)); + let iter = tx.run(format!("explain SELECT count(*) FROM stock WHERE s_w_id = {} AND s_i_id = {} AND s_quantity < {}", s_w_id, s_i_id, s_quantity))?; + + println!("{}", create_table(iter)?); } } diff --git a/tpcc/src/new_ord.rs b/tpcc/src/new_ord.rs index 3b1fea55..9af9c222 100644 --- a/tpcc/src/new_ord.rs +++ b/tpcc/src/new_ord.rs @@ -1,7 +1,7 @@ use crate::load::{nu_rand, CUST_PER_DIST, DIST_PER_WARE, MAX_ITEMS, MAX_NUM_ITEMS}; use crate::{other_ware, TpccArgs, TpccError, TpccTest, TpccTransaction, ALLOW_MULTI_WAREHOUSE_TX}; use chrono::Utc; -use fnck_sql::db::{DBTransaction, Statement}; +use fnck_sql::db::{DBTransaction, ResultIter, Statement}; use fnck_sql::storage::Storage; use fnck_sql::types::value::DataValue; use rand::prelude::ThreadRng; @@ -68,65 +68,78 @@ impl TpccTransaction for NewOrd { let (c_discount, c_last, c_credit, w_tax) = if args.joins { // "SELECT c_discount, c_last, c_credit, w_tax FROM customer, warehouse WHERE w_id = ? AND c_w_id = w_id AND c_d_id = ? AND c_id = ?" - let (_, tuples) = tx.execute( - &statements[0], - vec![ - ("?1", DataValue::Int16(Some(args.w_id as i16))), - ("?2", DataValue::Int16(Some(args.w_id as i16))), - ("?3", DataValue::Int8(Some(args.d_id as i8))), - ("?4", DataValue::Int64(Some(args.c_id as i64))), - ], - )?; - let c_discount = tuples[0].values[0].decimal().unwrap(); - let c_last = tuples[0].values[1].utf8().unwrap(); - let c_credit = tuples[0].values[2].utf8().unwrap(); - let w_tax = tuples[0].values[3].decimal().unwrap(); + let tuple = tx + .execute( + &statements[0], + vec![ + ("?1", DataValue::Int16(Some(args.w_id as i16))), + ("?2", DataValue::Int16(Some(args.w_id as i16))), + ("?3", DataValue::Int8(Some(args.d_id as i8))), + ("?4", DataValue::Int64(Some(args.c_id as i64))), + ], + )? + .next() + .unwrap()?; + let c_discount = tuple.values[0].decimal().unwrap(); + let c_last = tuple.values[1].utf8().unwrap(); + let c_credit = tuple.values[2].utf8().unwrap(); + let w_tax = tuple.values[3].decimal().unwrap(); (c_discount, c_last, c_credit, w_tax) } else { // "SELECT c_discount, c_last, c_credit FROM customer WHERE c_w_id = ? AND c_d_id = ? AND c_id = ?" - let (_, tuples) = tx.execute( - &statements[1], - vec![ - ("?1", DataValue::Int16(Some(args.w_id as i16))), - ("?2", DataValue::Int8(Some(args.d_id as i8))), - ("?3", DataValue::Int32(Some(args.c_id as i32))), - ], - )?; - let c_discount = tuples[0].values[0].decimal().unwrap(); - let c_last = tuples[0].values[1].utf8().unwrap(); - let c_credit = tuples[0].values[2].utf8().unwrap(); + let tuple = tx + .execute( + &statements[1], + vec![ + ("?1", DataValue::Int16(Some(args.w_id as i16))), + ("?2", DataValue::Int8(Some(args.d_id as i8))), + ("?3", DataValue::Int32(Some(args.c_id as i32))), + ], + )? + .next() + .unwrap()?; + let c_discount = tuple.values[0].decimal().unwrap(); + let c_last = tuple.values[1].utf8().unwrap(); + let c_credit = tuple.values[2].utf8().unwrap(); // "SELECT w_tax FROM warehouse WHERE w_id = ?" - let (_, tuples) = tx.execute( - &statements[2], - vec![("?1", DataValue::Int16(Some(args.w_id as i16)))], - )?; - let w_tax = tuples[0].values[0].decimal().unwrap(); + let tuple = tx + .execute( + &statements[2], + vec![("?1", DataValue::Int16(Some(args.w_id as i16)))], + )? + .next() + .unwrap()?; + let w_tax = tuple.values[0].decimal().unwrap(); (c_discount, c_last, c_credit, w_tax) }; // "SELECT d_next_o_id, d_tax FROM district WHERE d_id = ? AND d_w_id = ? FOR UPDATE" - let (_, tuples) = tx.execute( - &statements[3], - vec![ - ("?1", DataValue::Int8(Some(args.d_id as i8))), - ("?2", DataValue::Int16(Some(args.w_id as i16))), - ], - )?; - let d_next_o_id = tuples[0].values[0].i32().unwrap(); - let d_tax = tuples[0].values[1].decimal().unwrap(); + let tuple = tx + .execute( + &statements[3], + vec![ + ("?1", DataValue::Int8(Some(args.d_id as i8))), + ("?2", DataValue::Int16(Some(args.w_id as i16))), + ], + )? + .next() + .unwrap()?; + let d_next_o_id = tuple.values[0].i32().unwrap(); + let d_tax = tuple.values[1].decimal().unwrap(); // "UPDATE district SET d_next_o_id = ? + 1 WHERE d_id = ? AND d_w_id = ?" - let (_, tuples) = tx.execute( + tx.execute( &statements[4], vec![ ("?1", DataValue::Int32(Some(d_next_o_id))), ("?2", DataValue::Int8(Some(args.d_id as i8))), ("?3", DataValue::Int16(Some(args.w_id as i16))), ], - )?; + )? + .done()?; let o_id = d_next_o_id; // "INSERT INTO orders (o_id, o_d_id, o_w_id, o_c_id, o_entry_d, o_ol_cnt, o_all_local) VALUES(?, ?, ?, ?, ?, ?, ?)" - let (_, tuples) = tx.execute( + tx.execute( &statements[5], vec![ ("?1", DataValue::Int32(Some(o_id))), @@ -137,16 +150,18 @@ impl TpccTransaction for NewOrd { ("?6", DataValue::Int8(Some(args.o_ol_cnt as i8))), ("?7", DataValue::Int8(Some(args.o_all_local as i8))), ], - )?; + )? + .done()?; // "INSERT INTO new_orders (no_o_id, no_d_id, no_w_id) VALUES (?,?,?) - let (_, tuples) = tx.execute( + tx.execute( &statements[6], vec![ ("?1", DataValue::Int32(Some(o_id))), ("?2", DataValue::Int8(Some(args.d_id as i8))), ("?3", DataValue::Int16(Some(args.w_id as i16))), ], - )?; + )? + .done()?; let mut ol_num_seq = vec![0; MAX_NUM_ITEMS]; for i in 0..args.o_ol_cnt { @@ -175,40 +190,46 @@ impl TpccTransaction for NewOrd { let ol_i_id = args.item_id[ol_num_seq[ol_number - 1]]; let ol_quantity = args.qty[ol_num_seq[ol_number - 1]]; // "SELECT i_price, i_name, i_data FROM item WHERE i_id = ?" - let (_, tuples) = tx.execute( - &statements[7], - vec![("?1", DataValue::Int32(Some(ol_i_id as i32)))], - )?; - if tuples.is_empty() { + let tuple = tx + .execute( + &statements[7], + vec![("?1", DataValue::Int32(Some(ol_i_id as i32)))], + )? + .next(); + let Some(tuple) = tuple else { return Err(TpccError::EmptyTuples); - } - let i_price = tuples[0].values[0].decimal().unwrap(); - let i_name = tuples[0].values[1].utf8().unwrap(); - let i_data = tuples[0].values[2].utf8().unwrap(); + }; + let tuple = tuple?; + let i_price = tuple.values[0].decimal().unwrap(); + let i_name = tuple.values[1].utf8().unwrap(); + let i_data = tuple.values[2].utf8().unwrap(); price[ol_num_seq[ol_number - 1]] = i_price; iname[ol_num_seq[ol_number - 1]] = i_name; // "SELECT s_quantity, s_data, s_dist_01, s_dist_02, s_dist_03, s_dist_04, s_dist_05, s_dist_06, s_dist_07, s_dist_08, s_dist_09, s_dist_10 FROM stock WHERE s_i_id = ? AND s_w_id = ? FOR UPDATE" - let (_, tuples) = tx.execute( - &statements[8], - vec![ - ("?1", DataValue::Int32(Some(ol_i_id as i32))), - ("?2", DataValue::Int16(Some(ol_supply_w_id as i16))), - ], - )?; - let mut s_quantity = tuples[0].values[0].i16().unwrap(); - let s_data = tuples[0].values[1].utf8().unwrap(); - let s_dist_01 = tuples[0].values[2].utf8().unwrap(); - let s_dist_02 = tuples[0].values[3].utf8().unwrap(); - let s_dist_03 = tuples[0].values[4].utf8().unwrap(); - let s_dist_04 = tuples[0].values[5].utf8().unwrap(); - let s_dist_05 = tuples[0].values[6].utf8().unwrap(); - let s_dist_06 = tuples[0].values[7].utf8().unwrap(); - let s_dist_07 = tuples[0].values[8].utf8().unwrap(); - let s_dist_08 = tuples[0].values[9].utf8().unwrap(); - let s_dist_09 = tuples[0].values[10].utf8().unwrap(); - let s_dist_10 = tuples[0].values[11].utf8().unwrap(); + let tuple = tx + .execute( + &statements[8], + vec![ + ("?1", DataValue::Int32(Some(ol_i_id as i32))), + ("?2", DataValue::Int16(Some(ol_supply_w_id as i16))), + ], + )? + .next() + .unwrap()?; + let mut s_quantity = tuple.values[0].i16().unwrap(); + let s_data = tuple.values[1].utf8().unwrap(); + let s_dist_01 = tuple.values[2].utf8().unwrap(); + let s_dist_02 = tuple.values[3].utf8().unwrap(); + let s_dist_03 = tuple.values[4].utf8().unwrap(); + let s_dist_04 = tuple.values[5].utf8().unwrap(); + let s_dist_05 = tuple.values[6].utf8().unwrap(); + let s_dist_06 = tuple.values[7].utf8().unwrap(); + let s_dist_07 = tuple.values[8].utf8().unwrap(); + let s_dist_08 = tuple.values[9].utf8().unwrap(); + let s_dist_09 = tuple.values[10].utf8().unwrap(); + let s_dist_10 = tuple.values[11].utf8().unwrap(); let ol_dist_info = pick_dist_info( args.d_id, s_dist_01, s_dist_02, s_dist_03, s_dist_04, s_dist_05, s_dist_06, @@ -228,14 +249,15 @@ impl TpccTransaction for NewOrd { s_quantity - ol_quantity as i16 + 91 }; // "UPDATE stock SET s_quantity = ? WHERE s_i_id = ? AND s_w_id = ?" - let (_, tuples) = tx.execute( + tx.execute( &statements[9], vec![ ("?1", DataValue::Int16(Some(s_quantity))), ("?2", DataValue::Int32(Some(ol_i_id as i32))), ("?3", DataValue::Int16(Some(ol_supply_w_id as i16))), ], - )?; + )? + .done()?; // Tips: Integers always have 7 digits, so divide by 10 here let mut ol_amount = Decimal::from(ol_quantity) @@ -248,7 +270,7 @@ impl TpccTransaction for NewOrd { amt[ol_num_seq[ol_number - 1]] = ol_amount; // "INSERT INTO order_line (ol_o_id, ol_d_id, ol_w_id, ol_number, ol_i_id, ol_supply_w_id, ol_quantity, ol_amount, ol_dist_info) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" - let (_, tuples) = tx.execute( + tx.execute( &statements[10], vec![ ("?1", DataValue::Int32(Some(o_id))), @@ -261,7 +283,8 @@ impl TpccTransaction for NewOrd { ("?8", DataValue::Decimal(Some(ol_amount.round_dp(2)))), ("?9", DataValue::from(ol_dist_info)), ], - )?; + )? + .done()?; } Ok(()) diff --git a/tpcc/src/order_stat.rs b/tpcc/src/order_stat.rs index 982b7a69..5f81e078 100644 --- a/tpcc/src/order_stat.rs +++ b/tpcc/src/order_stat.rs @@ -47,17 +47,20 @@ impl TpccTransaction for OrderStat { ) -> Result<(), TpccError> { let (c_balance, c_first, c_middle, c_last) = if args.by_name { // SELECT count(c_id) FROM customer WHERE c_w_id = ? AND c_d_id = ? AND c_last = ?" - let (_, tuples) = tx.execute( - &statements[0], - vec![ - ("?1", DataValue::Int16(Some(args.w_id as i16))), - ("?2", DataValue::Int8(Some(args.d_id as i8))), - ("?3", DataValue::from(args.c_last.clone())), - ], - )?; - let mut name_cnt = tuples[0].values[0].i32().unwrap() as usize; + let tuple = tx + .execute( + &statements[0], + vec![ + ("?1", DataValue::Int16(Some(args.w_id as i16))), + ("?2", DataValue::Int8(Some(args.d_id as i8))), + ("?3", DataValue::from(args.c_last.clone())), + ], + )? + .next() + .unwrap()?; + let mut name_cnt = tuple.values[0].i32().unwrap() as usize; // SELECT c_balance, c_first, c_middle, c_last FROM customer WHERE c_w_id = ? AND c_d_id = ? AND c_last = ? ORDER BY c_first" - let (_, tuples) = tx.execute( + let mut tuple_iter = tx.execute( &statements[1], vec![ ("?1", DataValue::Int16(Some(args.w_id as i16))), @@ -74,62 +77,73 @@ impl TpccTransaction for OrderStat { let mut c_middle = String::new(); let mut c_last = String::new(); - for n in 0..name_cnt / 2 { - c_balance = tuples[n].values[0].decimal().unwrap(); - c_first = tuples[n].values[1].utf8().unwrap(); - c_middle = tuples[n].values[2].utf8().unwrap(); - c_last = tuples[n].values[3].utf8().unwrap(); + for _ in 0..name_cnt / 2 { + let tuple = tuple_iter.next().unwrap()?; + + c_balance = tuple.values[0].decimal().unwrap(); + c_first = tuple.values[1].utf8().unwrap(); + c_middle = tuple.values[2].utf8().unwrap(); + c_last = tuple.values[3].utf8().unwrap(); } (c_balance, c_first, c_middle, c_last) } else { // "SELECT c_balance, c_first, c_middle, c_last FROM customer WHERE c_w_id = ? AND c_d_id = ? AND c_id = ?" - let (_, tuples) = tx.execute( - &statements[2], + let tuple = tx + .execute( + &statements[2], + vec![ + ("?1", DataValue::Int16(Some(args.w_id as i16))), + ("?2", DataValue::Int8(Some(args.d_id as i8))), + ("?3", DataValue::Int32(Some(args.c_id as i32))), + ], + )? + .next() + .unwrap()?; + let c_balance = tuple.values[0].decimal().unwrap(); + let c_first = tuple.values[1].utf8().unwrap(); + let c_middle = tuple.values[2].utf8().unwrap(); + let c_last = tuple.values[3].utf8().unwrap(); + (c_balance, c_first, c_middle, c_last) + }; + // TODO: Join Eq + // "SELECT o_id, o_entry_d, COALESCE(o_carrier_id,0) FROM orders WHERE o_w_id = ? AND o_d_id = ? AND o_c_id = ? AND o_id = (SELECT MAX(o_id) FROM orders WHERE o_w_id = ? AND o_d_id = ? AND o_c_id = ?)" + let tuple = tx + .execute( + &statements[3], vec![ ("?1", DataValue::Int16(Some(args.w_id as i16))), ("?2", DataValue::Int8(Some(args.d_id as i8))), ("?3", DataValue::Int32(Some(args.c_id as i32))), + ("?4", DataValue::Int16(Some(args.w_id as i16))), + ("?5", DataValue::Int8(Some(args.d_id as i8))), + ("?6", DataValue::Int32(Some(args.c_id as i32))), ], - )?; - let c_balance = tuples[0].values[0].decimal().unwrap(); - let c_first = tuples[0].values[1].utf8().unwrap(); - let c_middle = tuples[0].values[2].utf8().unwrap(); - let c_last = tuples[0].values[3].utf8().unwrap(); - (c_balance, c_first, c_middle, c_last) - }; - // TODO: Join Eq - // "SELECT o_id, o_entry_d, COALESCE(o_carrier_id,0) FROM orders WHERE o_w_id = ? AND o_d_id = ? AND o_c_id = ? AND o_id = (SELECT MAX(o_id) FROM orders WHERE o_w_id = ? AND o_d_id = ? AND o_c_id = ?)" - let (_, tuples) = tx.execute( - &statements[3], - vec![ - ("?1", DataValue::Int16(Some(args.w_id as i16))), - ("?2", DataValue::Int8(Some(args.d_id as i8))), - ("?3", DataValue::Int32(Some(args.c_id as i32))), - ("?4", DataValue::Int16(Some(args.w_id as i16))), - ("?5", DataValue::Int8(Some(args.d_id as i8))), - ("?6", DataValue::Int32(Some(args.c_id as i32))), - ], - )?; - if tuples.is_empty() { + )? + .next(); + let Some(tuple) = tuple else { return Err(TpccError::EmptyTuples); - } - let o_id = tuples[0].values[0].i32().unwrap(); - // let o_entry_d = tuples[0].values[1].datetime().unwrap(); - // let o_carrier_id = tuples[0].values[2].i32().unwrap(); + }; + let tuple = tuple?; + let o_id = tuple.values[0].i32().unwrap(); + // let o_entry_d = tuple.values[1].datetime().unwrap(); + // let o_carrier_id = tuple.values[2].i32().unwrap(); // "SELECT ol_i_id, ol_supply_w_id, ol_quantity, ol_amount, ol_delivery_d FROM order_line WHERE ol_w_id = ? AND ol_d_id = ? AND ol_o_id = ?" - let (_, tuples) = tx.execute( - &statements[4], - vec![ - ("?1", DataValue::Int16(Some(args.w_id as i16))), - ("?2", DataValue::Int8(Some(args.d_id as i8))), - ("?3", DataValue::Int32(Some(o_id))), - ], - )?; - // let ol_i_id = tuples[0].values[0].i32(); - // let ol_supply_w_id = tuples[0].values[1].i16(); - // let ol_quantity = tuples[0].values[2].i8(); - // let ol_amount = tuples[0].values[3].decimal(); - // let ol_delivery_d = tuples[0].values[4].datetime(); + let tuple = tx + .execute( + &statements[4], + vec![ + ("?1", DataValue::Int16(Some(args.w_id as i16))), + ("?2", DataValue::Int8(Some(args.d_id as i8))), + ("?3", DataValue::Int32(Some(o_id))), + ], + )? + .next() + .unwrap()?; + // let ol_i_id = tuple.values[0].i32(); + // let ol_supply_w_id = tuple.values[1].i16(); + // let ol_quantity = tuple.values[2].i8(); + // let ol_amount = tuple.values[3].decimal(); + // let ol_delivery_d = tuple.values[4].datetime(); Ok(()) } diff --git a/tpcc/src/payment.rs b/tpcc/src/payment.rs index 93ba8586..449e9400 100644 --- a/tpcc/src/payment.rs +++ b/tpcc/src/payment.rs @@ -1,7 +1,7 @@ use crate::load::{last_name, nu_rand, CUST_PER_DIST, DIST_PER_WARE}; use crate::{other_ware, TpccArgs, TpccError, TpccTest, TpccTransaction, ALLOW_MULTI_WAREHOUSE_TX}; use chrono::Utc; -use fnck_sql::db::{DBTransaction, Statement}; +use fnck_sql::db::{DBTransaction, ResultIter, Statement}; use fnck_sql::storage::Storage; use fnck_sql::types::value::DataValue; use rand::prelude::ThreadRng; @@ -59,65 +59,76 @@ impl TpccTransaction for Payment { ) -> Result<(), TpccError> { let now = Utc::now(); // "UPDATE warehouse SET w_ytd = w_ytd + ? WHERE w_id = ?" - let (_, tuples) = tx.execute( + tx.execute( &statements[0], vec![ ("?1", DataValue::Decimal(Some(args.h_amount))), ("?2", DataValue::Int16(Some(args.w_id as i16))), ], - )?; + )? + .done()?; // "SELECT w_street_1, w_street_2, w_city, w_state, w_zip, w_name FROM warehouse WHERE w_id = ?" - let (_, tuples) = tx.execute( - &statements[1], - vec![("?1", DataValue::Int16(Some(args.w_id as i16)))], - )?; - let w_street_1 = tuples[0].values[0].utf8().unwrap(); - let w_street_2 = tuples[0].values[1].utf8().unwrap(); - let w_city = tuples[0].values[2].utf8().unwrap(); - let w_state = tuples[0].values[3].utf8().unwrap(); - let w_zip = tuples[0].values[4].utf8().unwrap(); - let w_name = tuples[0].values[5].utf8().unwrap(); + let tuple = tx + .execute( + &statements[1], + vec![("?1", DataValue::Int16(Some(args.w_id as i16)))], + )? + .next() + .unwrap()?; + let w_street_1 = tuple.values[0].utf8().unwrap(); + let w_street_2 = tuple.values[1].utf8().unwrap(); + let w_city = tuple.values[2].utf8().unwrap(); + let w_state = tuple.values[3].utf8().unwrap(); + let w_zip = tuple.values[4].utf8().unwrap(); + let w_name = tuple.values[5].utf8().unwrap(); // "UPDATE district SET d_ytd = d_ytd + ? WHERE d_w_id = ? AND d_id = ?" - let (_, tuples) = tx.execute( + tx.execute( &statements[2], vec![ ("?1", DataValue::Decimal(Some(args.h_amount))), ("?2", DataValue::Int16(Some(args.w_id as i16))), ("?3", DataValue::Int8(Some(args.d_id as i8))), ], - )?; + )? + .done()?; // "SELECT d_street_1, d_street_2, d_city, d_state, d_zip, d_name FROM district WHERE d_w_id = ? AND d_id = ?" - let (_, tuples) = tx.execute( - &statements[3], - vec![ - ("?1", DataValue::Int16(Some(args.w_id as i16))), - ("?2", DataValue::Int8(Some(args.d_id as i8))), - ], - )?; - let d_street_1 = tuples[0].values[0].utf8().unwrap(); - let d_street_2 = tuples[0].values[1].utf8().unwrap(); - let d_city = tuples[0].values[2].utf8().unwrap(); - let d_state = tuples[0].values[3].utf8().unwrap(); - let d_zip = tuples[0].values[4].utf8().unwrap(); - let d_name = tuples[0].values[5].utf8().unwrap(); + let tuple = tx + .execute( + &statements[3], + vec![ + ("?1", DataValue::Int16(Some(args.w_id as i16))), + ("?2", DataValue::Int8(Some(args.d_id as i8))), + ], + )? + .next() + .unwrap()?; + let d_street_1 = tuple.values[0].utf8().unwrap(); + let d_street_2 = tuple.values[1].utf8().unwrap(); + let d_city = tuple.values[2].utf8().unwrap(); + let d_state = tuple.values[3].utf8().unwrap(); + let d_zip = tuple.values[4].utf8().unwrap(); + let d_name = tuple.values[5].utf8().unwrap(); let mut c_id = args.c_id as i32; if args.by_name { // "SELECT count(c_id) FROM customer WHERE c_w_id = ? AND c_d_id = ? AND c_last = ?" - let (_, tuples) = tx.execute( - &statements[4], - vec![ - ("?1", DataValue::Int16(Some(args.c_w_id as i16))), - ("?2", DataValue::Int8(Some(args.c_d_id as i8))), - ("?3", DataValue::from(args.c_last.clone())), - ], - )?; - let mut name_cnt = tuples[0].values[0].i32().unwrap(); + let tuple = tx + .execute( + &statements[4], + vec![ + ("?1", DataValue::Int16(Some(args.c_w_id as i16))), + ("?2", DataValue::Int8(Some(args.c_d_id as i8))), + ("?3", DataValue::from(args.c_last.clone())), + ], + )? + .next() + .unwrap()?; + let mut name_cnt = tuple.values[0].i32().unwrap(); // "SELECT c_id FROM customer WHERE c_w_id = ? AND c_d_id = ? AND c_last = ? ORDER BY c_first" - let (_, tuples) = tx.execute( + let mut tuple_iter = tx.execute( &statements[5], vec![ ("?1", DataValue::Int16(Some(args.c_w_id as i16))), @@ -128,53 +139,60 @@ impl TpccTransaction for Payment { if name_cnt % 2 == 1 { name_cnt += 1; } - for n in 0..name_cnt / 2 { - c_id = tuples[n as usize].values[0].i32().unwrap(); + for _ in 0..name_cnt / 2 { + let result = tuple_iter.next().unwrap()?; + c_id = result.values[0].i32().unwrap(); } } // "SELECT c_first, c_middle, c_last, c_street_1, c_street_2, c_city, c_state, c_zip, c_phone, c_credit, c_credit_lim, c_discount, c_balance, c_since FROM customer WHERE c_w_id = ? AND c_d_id = ? AND c_id = ? FOR UPDATE" - let (_, tuples) = tx.execute( - &statements[6], - vec![ - ("?1", DataValue::Int16(Some(args.c_w_id as i16))), - ("?2", DataValue::Int8(Some(args.c_d_id as i8))), - ("?3", DataValue::Int32(Some(c_id))), - ], - )?; - let c_first = tuples[0].values[0].utf8().unwrap(); - let c_middle = tuples[0].values[1].utf8().unwrap(); - let c_last = tuples[0].values[2].utf8().unwrap(); - let c_street_1 = tuples[0].values[3].utf8().unwrap(); - let c_street_2 = tuples[0].values[4].utf8().unwrap(); - let c_city = tuples[0].values[5].utf8().unwrap(); - let c_state = tuples[0].values[6].utf8().unwrap(); - let c_zip = tuples[0].values[7].utf8().unwrap(); - let c_phone = tuples[0].values[8].utf8().unwrap(); - let c_credit = tuples[0].values[9].utf8(); - let c_credit_lim = tuples[0].values[10].i64().unwrap(); - let c_discount = tuples[0].values[11].decimal().unwrap(); - let mut c_balance = tuples[0].values[12].decimal().unwrap(); - let c_since = tuples[0].values[13].datetime().unwrap(); + let tuple = tx + .execute( + &statements[6], + vec![ + ("?1", DataValue::Int16(Some(args.c_w_id as i16))), + ("?2", DataValue::Int8(Some(args.c_d_id as i8))), + ("?3", DataValue::Int32(Some(c_id))), + ], + )? + .next() + .unwrap()?; + let c_first = tuple.values[0].utf8().unwrap(); + let c_middle = tuple.values[1].utf8().unwrap(); + let c_last = tuple.values[2].utf8().unwrap(); + let c_street_1 = tuple.values[3].utf8().unwrap(); + let c_street_2 = tuple.values[4].utf8().unwrap(); + let c_city = tuple.values[5].utf8().unwrap(); + let c_state = tuple.values[6].utf8().unwrap(); + let c_zip = tuple.values[7].utf8().unwrap(); + let c_phone = tuple.values[8].utf8().unwrap(); + let c_credit = tuple.values[9].utf8(); + let c_credit_lim = tuple.values[10].i64().unwrap(); + let c_discount = tuple.values[11].decimal().unwrap(); + let mut c_balance = tuple.values[12].decimal().unwrap(); + let c_since = tuple.values[13].datetime().unwrap(); c_balance += args.h_amount; if let Some(c_credit) = c_credit { if c_credit.contains("BC") { // "SELECT c_data FROM customer WHERE c_w_id = ? AND c_d_id = ? AND c_id = ?" - let (_, tuples) = tx.execute( - &statements[7], - vec![ - ("?1", DataValue::Int16(Some(args.c_w_id as i16))), - ("?2", DataValue::Int8(Some(args.c_d_id as i8))), - ("?3", DataValue::Int32(Some(c_id))), - ], - )?; - let c_data = tuples[0].values[0].utf8().unwrap(); + let tuple = tx + .execute( + &statements[7], + vec![ + ("?1", DataValue::Int16(Some(args.c_w_id as i16))), + ("?2", DataValue::Int8(Some(args.c_d_id as i8))), + ("?3", DataValue::Int32(Some(c_id))), + ], + )? + .next() + .unwrap()?; + let c_data = tuple.values[0].utf8().unwrap(); // https://github.com/AgilData/tpcc/blob/dfbabe1e35cc93b2bf2e107fc699eb29c2097e24/src/main/java/com/codefutures/tpcc/Payment.java#L284 // let c_new_data = format!("| {} {} {} {} {} {} {}", c_id, args.c_d_id, args.c_w_id, args.d_id, args.w_id, args.h_amount, ) // "UPDATE customer SET c_balance = ?, c_data = ? WHERE c_w_id = ? AND c_d_id = ? AND c_id = ?" - let (_, tuples) = tx.execute( + tx.execute( &statements[8], vec![ ("?1", DataValue::Decimal(Some(c_balance))), @@ -183,10 +201,11 @@ impl TpccTransaction for Payment { ("?4", DataValue::Int8(Some(args.c_d_id as i8))), ("?5", DataValue::Int32(Some(c_id))), ], - )?; + )? + .done()?; } else { // "UPDATE customer SET c_balance = ? WHERE c_w_id = ? AND c_d_id = ? AND c_id = ?" - let (_, tuples) = tx.execute( + tx.execute( &statements[9], vec![ ("?1", DataValue::Decimal(Some(c_balance))), @@ -194,11 +213,12 @@ impl TpccTransaction for Payment { ("?3", DataValue::Int8(Some(args.c_d_id as i8))), ("?4", DataValue::Int32(Some(c_id))), ], - )?; + )? + .done()?; } } else { // "UPDATE customer SET c_balance = ? WHERE c_w_id = ? AND c_d_id = ? AND c_id = ?" - let (_, tuples) = tx.execute( + tx.execute( &statements[9], vec![ ("?1", DataValue::Decimal(Some(c_balance))), @@ -206,11 +226,12 @@ impl TpccTransaction for Payment { ("?3", DataValue::Int8(Some(args.c_d_id as i8))), ("?4", DataValue::Int32(Some(c_id))), ], - )?; + )? + .done()?; } let h_data = format!("\\0{d_name} \\0"); // "INSERT INTO history(h_c_d_id, h_c_w_id, h_c_id, h_d_id, h_w_id, h_date, h_amount, h_data) VALUES(?, ?, ?, ?, ?, ?, ?, ?)" - let (_, tuples) = tx.execute( + tx.execute( &statements[10], vec![ ("?1", DataValue::Int8(Some(args.c_d_id as i8))), @@ -222,7 +243,8 @@ impl TpccTransaction for Payment { ("?7", DataValue::Decimal(Some(args.h_amount))), ("?8", DataValue::from(h_data)), ], - )?; + )? + .done()?; Ok(()) } diff --git a/tpcc/src/slev.rs b/tpcc/src/slev.rs index 042c0c53..55b7565f 100644 --- a/tpcc/src/slev.rs +++ b/tpcc/src/slev.rs @@ -31,35 +31,44 @@ impl TpccTransaction for Slev { statements: &[Statement], ) -> Result<(), TpccError> { // "SELECT d_next_o_id FROM district WHERE d_id = ? AND d_w_id = ?" - let (_, tuples) = tx.execute( - &statements[0], - vec![ - ("?1", DataValue::Int8(Some(args.d_id as i8))), - ("?2", DataValue::Int16(Some(args.w_id as i16))), - ], - )?; - let d_next_o_id = tuples[0].values[0].i32().unwrap(); + let tuple = tx + .execute( + &statements[0], + vec![ + ("?1", DataValue::Int8(Some(args.d_id as i8))), + ("?2", DataValue::Int16(Some(args.w_id as i16))), + ], + )? + .next() + .unwrap()?; + let d_next_o_id = tuple.values[0].i32().unwrap(); // "SELECT DISTINCT ol_i_id FROM order_line WHERE ol_w_id = ? AND ol_d_id = ? AND ol_o_id < ? AND ol_o_id >= (? - 20)" - let (_, tuples) = tx.execute( - &statements[1], - vec![ - ("?1", DataValue::Int16(Some(args.w_id as i16))), - ("?2", DataValue::Int8(Some(args.d_id as i8))), - ("?3", DataValue::Int32(Some(d_next_o_id))), - ("?4", DataValue::Int32(Some(d_next_o_id))), - ], - )?; - let ol_i_id = tuples[0].values[0].i32().unwrap(); + let tuple = tx + .execute( + &statements[1], + vec![ + ("?1", DataValue::Int16(Some(args.w_id as i16))), + ("?2", DataValue::Int8(Some(args.d_id as i8))), + ("?3", DataValue::Int32(Some(d_next_o_id))), + ("?4", DataValue::Int32(Some(d_next_o_id))), + ], + )? + .next() + .unwrap()?; + let ol_i_id = tuple.values[0].i32().unwrap(); // "SELECT count(*) FROM stock WHERE s_w_id = ? AND s_i_id = ? AND s_quantity < ?" - let (_, tuples) = tx.execute( - &statements[2], - vec![ - ("?1", DataValue::Int16(Some(args.w_id as i16))), - ("?2", DataValue::Int8(Some(ol_i_id as i8))), - ("?3", DataValue::Int16(Some(args.level as i16))), - ], - )?; - // let i_count = tuples[0].values[0].i32().unwrap(); + let tuple = tx + .execute( + &statements[2], + vec![ + ("?1", DataValue::Int16(Some(args.w_id as i16))), + ("?2", DataValue::Int8(Some(ol_i_id as i8))), + ("?3", DataValue::Int16(Some(args.level as i16))), + ], + )? + .next() + .unwrap()?; + // let i_count = tuple.values[0].i32().unwrap(); Ok(()) }