From 2424497c5eef6ce695d81b44818bdcaeb422986a Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Thu, 9 Nov 2023 00:53:05 +0800 Subject: [PATCH 01/10] refactor: remove InputRef expression --- src/binder/aggregate.rs | 91 ++----------------- .../executor/dql/aggregate/hash_agg.rs | 2 +- .../executor/dql/aggregate/simple_agg.rs | 2 +- src/execution/executor/dql/projection.rs | 2 +- src/expression/evaluator.rs | 13 ++- src/expression/mod.rs | 49 +++++----- src/optimizer/rule/column_pruning.rs | 3 + src/planner/operator/mod.rs | 37 -------- src/storage/mod.rs | 2 +- 9 files changed, 51 insertions(+), 150 deletions(-) diff --git a/src/binder/aggregate.rs b/src/binder/aggregate.rs index b8523ac7..cf770626 100644 --- a/src/binder/aggregate.rs +++ b/src/binder/aggregate.rs @@ -3,7 +3,7 @@ use itertools::Itertools; use sqlparser::ast::{Expr, OrderByExpr}; use std::collections::HashSet; -use crate::binder::{BindError, InputRefType}; +use crate::binder::BindError; use crate::planner::LogicalPlan; use crate::storage::Transaction; use crate::{ @@ -92,55 +92,10 @@ impl<'a, T: Transaction> Binder<'a, T> { expr: &mut ScalarExpression, is_select: bool, ) -> Result<(), BindError> { - let ref_columns = expr.referenced_columns(); - match expr { - ScalarExpression::AggCall { - ty: return_type, .. - } => { - let ty = return_type.clone(); - if is_select { - let index = self.context.input_ref_index(InputRefType::AggCall); - let input_ref = ScalarExpression::InputRef { - index, - ty, - ref_columns, - }; - match std::mem::replace(expr, input_ref) { - ScalarExpression::AggCall { - kind, - args, - ty, - distinct, - } => { - self.context.agg_calls.push(ScalarExpression::AggCall { - distinct, - kind, - args, - ty, - }); - } - _ => unreachable!(), - } - } else { - let (index, _) = self - .context - .agg_calls - .iter() - .find_position(|agg_expr| agg_expr == &expr) - .ok_or_else(|| BindError::AggMiss(format!("{:?}", expr)))?; - - let _ = std::mem::replace( - expr, - ScalarExpression::InputRef { - index, - ty, - ref_columns, - }, - ); - } + ScalarExpression::AggCall { .. } => { + self.context.agg_calls.push(expr.clone()); } - ScalarExpression::TypeCast { expr, .. } => { self.visit_column_agg_expr(expr, is_select)? } @@ -156,8 +111,7 @@ impl<'a, T: Transaction> Binder<'a, T> { self.visit_column_agg_expr(right_expr, is_select)?; } ScalarExpression::Constant(_) - | ScalarExpression::ColumnRef { .. } - | ScalarExpression::InputRef { .. } => {} + | ScalarExpression::ColumnRef { .. } => {} } Ok(()) @@ -239,44 +193,13 @@ impl<'a, T: Transaction> Binder<'a, T> { false } }) { - let index = self.context.input_ref_index(InputRefType::GroupBy); - let mut select_item = &mut select_list[i]; - let ref_columns = select_item.referenced_columns(); - let return_type = select_item.return_type(); - - self.context.group_by_exprs.push(std::mem::replace( - &mut select_item, - ScalarExpression::InputRef { - index, - ty: return_type, - ref_columns, - }, - )); + self.context.group_by_exprs.push(select_list[i].clone()); return; } } if let Some(i) = select_list.iter().position(|column| column == expr) { - let expr = &mut select_list[i]; - let ref_columns = expr.referenced_columns(); - - match expr { - ScalarExpression::Constant(_) | ScalarExpression::ColumnRef { .. } => { - self.context.group_by_exprs.push(expr.clone()) - } - _ => { - let index = self.context.input_ref_index(InputRefType::GroupBy); - - self.context.group_by_exprs.push(std::mem::replace( - expr, - ScalarExpression::InputRef { - index, - ty: expr.return_type(), - ref_columns, - }, - )) - } - } + self.context.group_by_exprs.push(select_list[i].clone()) } } @@ -330,7 +253,7 @@ impl<'a, T: Transaction> Binder<'a, T> { Ok(()) } - ScalarExpression::Constant(_) | ScalarExpression::InputRef { .. } => Ok(()), + ScalarExpression::Constant(_) => Ok(()), } } } diff --git a/src/execution/executor/dql/aggregate/hash_agg.rs b/src/execution/executor/dql/aggregate/hash_agg.rs index 968d2c05..fa4052b0 100644 --- a/src/execution/executor/dql/aggregate/hash_agg.rs +++ b/src/execution/executor/dql/aggregate/hash_agg.rs @@ -57,7 +57,7 @@ impl HashAggExecutor { self.agg_calls .iter() .chain(self.groupby_exprs.iter()) - .map(|expr| expr.output_columns(&tuple)) + .map(|expr| expr.output_columns()) .collect_vec() }); diff --git a/src/execution/executor/dql/aggregate/simple_agg.rs b/src/execution/executor/dql/aggregate/simple_agg.rs index b1d39ee3..96c1589b 100644 --- a/src/execution/executor/dql/aggregate/simple_agg.rs +++ b/src/execution/executor/dql/aggregate/simple_agg.rs @@ -42,7 +42,7 @@ impl SimpleAggExecutor { columns_option.get_or_insert_with(|| { self.agg_calls .iter() - .map(|expr| expr.output_columns(&tuple)) + .map(|expr| expr.output_columns()) .collect_vec() }); diff --git a/src/execution/executor/dql/projection.rs b/src/execution/executor/dql/projection.rs index d6c21b92..de993900 100644 --- a/src/execution/executor/dql/projection.rs +++ b/src/execution/executor/dql/projection.rs @@ -38,7 +38,7 @@ impl Projection { for expr in exprs.iter() { values.push(expr.eval_column(&tuple)?); - columns.push(expr.output_columns(&tuple)); + columns.push(expr.output_columns()); } yield Tuple { diff --git a/src/expression/evaluator.rs b/src/expression/evaluator.rs index 9288ec43..392463a2 100644 --- a/src/expression/evaluator.rs +++ b/src/expression/evaluator.rs @@ -13,6 +13,10 @@ lazy_static! { impl ScalarExpression { pub fn eval_column(&self, tuple: &Tuple) -> Result { + if let Some(value) = Self::eval_with_name(&tuple, self.output_columns().name()) { + return Ok(value.clone()); + } + match &self { ScalarExpression::Constant(val) => Ok(val.clone()), ScalarExpression::ColumnRef(col) => { @@ -22,7 +26,6 @@ impl ScalarExpression { Ok(value) } - ScalarExpression::InputRef { index, .. } => Ok(tuple.values[*index].clone()), ScalarExpression::Alias { expr, alias } => { if let Some(value) = Self::eval_with_name(&tuple, alias) { return Ok(value.clone()); @@ -59,7 +62,13 @@ impl ScalarExpression { Ok(Arc::new(unary_op(&value, op)?)) } - ScalarExpression::AggCall { .. } => todo!(), + ScalarExpression::AggCall { .. } => { + let value = Self::eval_with_name(&tuple, self.output_columns().name()) + .unwrap_or(&NULL_VALUE) + .clone(); + + Ok(value) + }, } } diff --git a/src/expression/mod.rs b/src/expression/mod.rs index 282649af..a0ae584f 100644 --- a/src/expression/mod.rs +++ b/src/expression/mod.rs @@ -10,7 +10,6 @@ use sqlparser::ast::{BinaryOperator as SqlBinaryOperator, UnaryOperator as SqlUn use self::agg::AggKind; use crate::catalog::{ColumnCatalog, ColumnDesc, ColumnRef}; use crate::storage::Transaction; -use crate::types::tuple::Tuple; use crate::types::value::ValueRef; use crate::types::LogicalType; @@ -27,11 +26,6 @@ pub mod value_compute; pub enum ScalarExpression { Constant(ValueRef), ColumnRef(ColumnRef), - InputRef { - index: usize, - ty: LogicalType, - ref_columns: Vec, - }, Alias { expr: Box, alias: String, @@ -74,7 +68,6 @@ impl ScalarExpression { pub fn has_count_star(&self) -> bool { match self { - ScalarExpression::InputRef { ref_columns, .. } => ref_columns.is_empty(), ScalarExpression::Alias { expr, .. } => expr.has_count_star(), ScalarExpression::TypeCast { expr, .. } => expr.has_count_star(), ScalarExpression::IsNull { expr, .. } => expr.has_count_star(), @@ -93,7 +86,6 @@ impl ScalarExpression { match self { ScalarExpression::Constant(_) => false, ScalarExpression::ColumnRef(col) => col.nullable, - ScalarExpression::InputRef { .. } => unreachable!(), ScalarExpression::Alias { expr, .. } => expr.nullable(), ScalarExpression::TypeCast { expr, .. } => expr.nullable(), ScalarExpression::IsNull { expr, .. } => expr.nullable(), @@ -123,9 +115,6 @@ impl ScalarExpression { Self::AggCall { ty: return_type, .. } => return_type.clone(), - Self::InputRef { - ty: return_type, .. - } => return_type.clone(), Self::IsNull { .. } => LogicalType::Boolean, Self::Alias { expr, .. } => expr.return_type(), } @@ -133,6 +122,9 @@ impl ScalarExpression { pub fn referenced_columns(&self) -> Vec { fn columns_collect(expr: &ScalarExpression, vec: &mut Vec) { + // When `ScalarExpression` is a complex type, it itself is also a special Column + vec.push(expr.output_columns()); + match expr { ScalarExpression::ColumnRef(col) => { vec.push(col.clone()); @@ -154,9 +146,6 @@ impl ScalarExpression { columns_collect(expr, vec) } } - ScalarExpression::InputRef { ref_columns, .. } => { - vec.extend_from_slice(ref_columns); - } _ => (), } } @@ -170,8 +159,7 @@ impl ScalarExpression { pub fn has_agg_call(&self, context: &BinderContext<'_, T>) -> bool { match self { - ScalarExpression::InputRef { index, .. } => context.agg_calls.get(*index).is_some(), - ScalarExpression::AggCall { .. } => unreachable!(), + ScalarExpression::AggCall { .. } => true, ScalarExpression::Constant(_) => false, ScalarExpression::ColumnRef(_) => false, ScalarExpression::Alias { expr, .. } => expr.has_agg_call(context), @@ -186,7 +174,7 @@ impl ScalarExpression { } } - pub fn output_columns(&self, tuple: &Tuple) -> ColumnRef { + pub fn output_columns(&self) -> ColumnRef { match self { ScalarExpression::ColumnRef(col) => col.clone(), ScalarExpression::Constant(value) => Arc::new(ColumnCatalog::new( @@ -209,7 +197,7 @@ impl ScalarExpression { } => { let args_str = args .iter() - .map(|expr| expr.output_columns(tuple).name().to_string()) + .map(|expr| expr.output_columns().name().to_string()) .join(", "); let op = |allow_distinct, distinct| { if allow_distinct && distinct { @@ -232,7 +220,6 @@ impl ScalarExpression { Some(self.clone()), )) } - ScalarExpression::InputRef { index, .. } => tuple.columns[*index].clone(), ScalarExpression::Binary { left_expr, right_expr, @@ -241,9 +228,9 @@ impl ScalarExpression { } => { let column_name = format!( "({} {} {})", - left_expr.output_columns(tuple).name(), + left_expr.output_columns().name(), op, - right_expr.output_columns(tuple).name(), + right_expr.output_columns().name(), ); Arc::new(ColumnCatalog::new( @@ -254,7 +241,7 @@ impl ScalarExpression { )) } ScalarExpression::Unary { expr, op, ty } => { - let column_name = format!("{} {}", op, expr.output_columns(tuple).name()); + let column_name = format!("{}{}", op, expr.output_columns().name()); Arc::new(ColumnCatalog::new( column_name, true, @@ -262,7 +249,23 @@ impl ScalarExpression { Some(self.clone()), )) } - _ => unreachable!(), + ScalarExpression::IsNull { negated, expr } => { + let suffix = if *negated { + "is not null" + } else { + "is null" + }; + let column_name = format!("{} {}", expr.output_columns().name(), suffix); + Arc::new(ColumnCatalog::new( + column_name, + true, + ColumnDesc::new(LogicalType::Boolean, false, false), + Some(self.clone()), + )) + } + _ => { + todo!() + } } } } diff --git a/src/optimizer/rule/column_pruning.rs b/src/optimizer/rule/column_pruning.rs index 0ae347dc..ca696ef8 100644 --- a/src/optimizer/rule/column_pruning.rs +++ b/src/optimizer/rule/column_pruning.rs @@ -30,6 +30,9 @@ impl ColumnPruning { exprs: &mut Vec, ) { exprs.retain(|expr| { + if column_references.contains(expr.output_columns().summary()) { + return true; + } expr.referenced_columns() .iter() .any(|column| column_references.contains(column.summary())) diff --git a/src/planner/operator/mod.rs b/src/planner/operator/mod.rs index 633012db..53a9cb0a 100644 --- a/src/planner/operator/mod.rs +++ b/src/planner/operator/mod.rs @@ -17,7 +17,6 @@ pub mod update; pub mod values; use crate::catalog::ColumnRef; -use crate::expression::ScalarExpression; use crate::planner::operator::copy_from_file::CopyFromFileOperator; use crate::planner::operator::copy_to_file::CopyToFileOperator; use crate::planner::operator::create_table::CreateTableOperator; @@ -64,41 +63,6 @@ pub enum Operator { } impl Operator { - pub fn project_input_refs(&self) -> Vec { - match self { - Operator::Project(op) => op - .exprs - .iter() - .map(ScalarExpression::unpack_alias) - .filter(|expr| matches!(expr, ScalarExpression::InputRef { .. })) - .sorted_by_key(|expr| match expr { - ScalarExpression::InputRef { index, .. } => index, - _ => unreachable!(), - }) - .cloned() - .collect_vec(), - _ => vec![], - } - } - - pub fn agg_mapping_col_refs(&self, input_refs: &[ScalarExpression]) -> Vec { - match self { - Operator::Aggregate(AggregateOperator { agg_calls, .. }) => input_refs - .iter() - .filter_map(|expr| { - if let ScalarExpression::InputRef { index, .. } = expr { - Some(agg_calls[*index].clone()) - } else { - None - } - }) - .map(|expr| expr.referenced_columns()) - .flatten() - .collect_vec(), - _ => vec![], - } - } - pub fn referenced_columns(&self) -> Vec { match self { Operator::Aggregate(op) => op @@ -121,7 +85,6 @@ impl Operator { exprs.append(&mut filter_expr.referenced_columns()); } } - exprs } Operator::Project(op) => op diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 6e4abc4d..d58e8733 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -125,7 +125,7 @@ pub(crate) fn tuple_projection( for expr in projections.iter() { values.push(expr.eval_column(&tuple)?); - columns.push(expr.output_columns(&tuple)); + columns.push(expr.output_columns()); } if let Some(num) = limit { From a787c6715b3225bf68b5d2ab586129bcf8e81209 Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Sat, 11 Nov 2023 13:30:20 +0800 Subject: [PATCH 02/10] fix: pushdown_predicates not work --- src/binder/aggregate.rs | 3 +- src/expression/evaluator.rs | 2 +- src/expression/mod.rs | 44 +++++++++++++---------- src/optimizer/rule/column_pruning.rs | 10 +++--- src/optimizer/rule/pushdown_predicates.rs | 8 ++--- src/planner/operator/mod.rs | 18 +++++----- src/storage/kip.rs | 8 ++--- 7 files changed, 48 insertions(+), 45 deletions(-) diff --git a/src/binder/aggregate.rs b/src/binder/aggregate.rs index cf770626..3c6539b3 100644 --- a/src/binder/aggregate.rs +++ b/src/binder/aggregate.rs @@ -110,8 +110,7 @@ impl<'a, T: Transaction> Binder<'a, T> { self.visit_column_agg_expr(left_expr, is_select)?; self.visit_column_agg_expr(right_expr, is_select)?; } - ScalarExpression::Constant(_) - | ScalarExpression::ColumnRef { .. } => {} + ScalarExpression::Constant(_) | ScalarExpression::ColumnRef { .. } => {} } Ok(()) diff --git a/src/expression/evaluator.rs b/src/expression/evaluator.rs index 392463a2..de35803b 100644 --- a/src/expression/evaluator.rs +++ b/src/expression/evaluator.rs @@ -68,7 +68,7 @@ impl ScalarExpression { .clone(); Ok(value) - }, + } } } diff --git a/src/expression/mod.rs b/src/expression/mod.rs index a0ae584f..cae6b0ae 100644 --- a/src/expression/mod.rs +++ b/src/expression/mod.rs @@ -120,39 +120,51 @@ impl ScalarExpression { } } - pub fn referenced_columns(&self) -> Vec { - fn columns_collect(expr: &ScalarExpression, vec: &mut Vec) { + pub fn referenced_columns(&self, only_column_ref: bool) -> Vec { + fn columns_collect( + expr: &ScalarExpression, + vec: &mut Vec, + only_column_ref: bool, + ) { // When `ScalarExpression` is a complex type, it itself is also a special Column - vec.push(expr.output_columns()); - + if !only_column_ref { + vec.push(expr.output_columns()); + } match expr { ScalarExpression::ColumnRef(col) => { vec.push(col.clone()); } - ScalarExpression::Alias { expr, .. } => columns_collect(&expr, vec), - ScalarExpression::TypeCast { expr, .. } => columns_collect(&expr, vec), - ScalarExpression::IsNull { expr, .. } => columns_collect(&expr, vec), - ScalarExpression::Unary { expr, .. } => columns_collect(&expr, vec), + ScalarExpression::Alias { expr, .. } => { + columns_collect(&expr, vec, only_column_ref) + } + ScalarExpression::TypeCast { expr, .. } => { + columns_collect(&expr, vec, only_column_ref) + } + ScalarExpression::IsNull { expr, .. } => { + columns_collect(&expr, vec, only_column_ref) + } + ScalarExpression::Unary { expr, .. } => { + columns_collect(&expr, vec, only_column_ref) + } ScalarExpression::Binary { left_expr, right_expr, .. } => { - columns_collect(left_expr, vec); - columns_collect(right_expr, vec); + columns_collect(left_expr, vec, only_column_ref); + columns_collect(right_expr, vec, only_column_ref); } ScalarExpression::AggCall { args, .. } => { for expr in args { - columns_collect(expr, vec) + columns_collect(expr, vec, only_column_ref) } } _ => (), } } - let mut exprs = Vec::new(); - columns_collect(self, &mut exprs); + columns_collect(self, &mut exprs, only_column_ref); exprs } @@ -250,11 +262,7 @@ impl ScalarExpression { )) } ScalarExpression::IsNull { negated, expr } => { - let suffix = if *negated { - "is not null" - } else { - "is null" - }; + let suffix = if *negated { "is not null" } else { "is null" }; let column_name = format!("{} {}", expr.output_columns().name(), suffix); Arc::new(ColumnCatalog::new( column_name, diff --git a/src/optimizer/rule/column_pruning.rs b/src/optimizer/rule/column_pruning.rs index ca696ef8..86984530 100644 --- a/src/optimizer/rule/column_pruning.rs +++ b/src/optimizer/rule/column_pruning.rs @@ -33,7 +33,7 @@ impl ColumnPruning { if column_references.contains(expr.output_columns().summary()) { return true; } - expr.referenced_columns() + expr.referenced_columns(false) .iter() .any(|column| column_references.contains(column.summary())) }) @@ -64,7 +64,7 @@ impl ColumnPruning { }) } } - let op_ref_columns = operator.referenced_columns(); + let op_ref_columns = operator.referenced_columns(false); Self::recollect_apply(op_ref_columns, false, node_id, graph); } @@ -74,7 +74,7 @@ impl ColumnPruning { if !all_referenced { Self::clear_exprs(column_references, &mut op.exprs); } - let op_ref_columns = operator.referenced_columns(); + let op_ref_columns = operator.referenced_columns(false); Self::recollect_apply(op_ref_columns, false, node_id, graph); } @@ -94,7 +94,7 @@ impl ColumnPruning { } } Operator::Limit(_) | Operator::Join(_) | Operator::Filter(_) => { - for column in operator.referenced_columns() { + for column in operator.referenced_columns(false) { column_references.insert(column.summary().clone()); } for child_id in graph.children_at(node_id) { @@ -105,7 +105,7 @@ impl ColumnPruning { Operator::Dummy | Operator::Values(_) => (), // DDL Based on Other Plan Operator::Insert(_) | Operator::Update(_) | Operator::Delete(_) => { - let op_ref_columns = operator.referenced_columns(); + let op_ref_columns = operator.referenced_columns(false); Self::recollect_apply(op_ref_columns, true, graph.children_at(node_id)[0], graph); } diff --git a/src/optimizer/rule/pushdown_predicates.rs b/src/optimizer/rule/pushdown_predicates.rs index d5132148..ac0f1e1a 100644 --- a/src/optimizer/rule/pushdown_predicates.rs +++ b/src/optimizer/rule/pushdown_predicates.rs @@ -111,8 +111,8 @@ impl Rule for PushPredicateThroughJoin { } let join_childs = graph.children_at(child_id); - let left_columns = graph.operator(join_childs[0]).referenced_columns(); - let right_columns = graph.operator(join_childs[1]).referenced_columns(); + let left_columns = graph.operator(join_childs[0]).referenced_columns(true); + let right_columns = graph.operator(join_childs[1]).referenced_columns(true); let mut new_ops = (None, None, None); @@ -121,10 +121,10 @@ impl Rule for PushPredicateThroughJoin { let (left_filters, rest): (Vec<_>, Vec<_>) = filter_exprs .into_iter() - .partition(|f| is_subset_cols(&f.referenced_columns(), &left_columns)); + .partition(|f| is_subset_cols(&f.referenced_columns(true), &left_columns)); let (right_filters, common_filters): (Vec<_>, Vec<_>) = rest .into_iter() - .partition(|f| is_subset_cols(&f.referenced_columns(), &right_columns)); + .partition(|f| is_subset_cols(&f.referenced_columns(true), &right_columns)); let replace_filters = match child_op.join_type { JoinType::Inner => { diff --git a/src/planner/operator/mod.rs b/src/planner/operator/mod.rs index 53a9cb0a..e1d11aab 100644 --- a/src/planner/operator/mod.rs +++ b/src/planner/operator/mod.rs @@ -63,26 +63,26 @@ pub enum Operator { } impl Operator { - pub fn referenced_columns(&self) -> Vec { + pub fn referenced_columns(&self, only_column_ref: bool) -> Vec { match self { Operator::Aggregate(op) => op .agg_calls .iter() .chain(op.groupby_exprs.iter()) - .flat_map(|expr| expr.referenced_columns()) + .flat_map(|expr| expr.referenced_columns(only_column_ref)) .collect_vec(), - Operator::Filter(op) => op.predicate.referenced_columns(), + Operator::Filter(op) => op.predicate.referenced_columns(only_column_ref), Operator::Join(op) => { let mut exprs = Vec::new(); if let JoinCondition::On { on, filter } = &op.on { for (left_expr, right_expr) in on { - exprs.append(&mut left_expr.referenced_columns()); - exprs.append(&mut right_expr.referenced_columns()); + exprs.append(&mut left_expr.referenced_columns(only_column_ref)); + exprs.append(&mut right_expr.referenced_columns(only_column_ref)); } if let Some(filter_expr) = filter { - exprs.append(&mut filter_expr.referenced_columns()); + exprs.append(&mut filter_expr.referenced_columns(only_column_ref)); } } exprs @@ -90,18 +90,18 @@ impl Operator { Operator::Project(op) => op .exprs .iter() - .flat_map(|expr| expr.referenced_columns()) + .flat_map(|expr| expr.referenced_columns(only_column_ref)) .collect_vec(), Operator::Scan(op) => op .columns .iter() - .flat_map(|expr| expr.referenced_columns()) + .flat_map(|expr| expr.referenced_columns(only_column_ref)) .collect_vec(), Operator::Sort(op) => op .sort_fields .iter() .map(|field| &field.expr) - .flat_map(|expr| expr.referenced_columns()) + .flat_map(|expr| expr.referenced_columns(only_column_ref)) .collect_vec(), Operator::Values(op) => op.columns.clone(), _ => vec![], diff --git a/src/storage/kip.rs b/src/storage/kip.rs index 73b61379..f80064e3 100644 --- a/src/storage/kip.rs +++ b/src/storage/kip.rs @@ -569,7 +569,7 @@ mod test { &"test".to_string(), Tuple { id: Some(Arc::new(DataValue::Int32(Some(2)))), - columns, + columns: columns.clone(), values: vec![ Arc::new(DataValue::Int32(Some(2))), Arc::new(DataValue::Boolean(Some(false))), @@ -581,11 +581,7 @@ mod test { let mut iter = transaction.read( &"test".to_string(), (Some(1), Some(1)), - vec![ScalarExpression::InputRef { - index: 0, - ty: LogicalType::Integer, - ref_columns: vec![], - }], + vec![ScalarExpression::ColumnRef(columns[0].clone())], )?; let option_1 = iter.next_tuple()?; From 1d2f831a7b1474071fe7d8ce297b1383f5ab096b Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Sat, 11 Nov 2023 01:26:18 +0800 Subject: [PATCH 03/10] feat: support `in` expression --- src/binder/aggregate.rs | 36 ++++++++++----- src/binder/expr.rs | 13 ++++++ src/db.rs | 8 ++++ .../executor/dql/aggregate/hash_agg.rs | 4 +- .../executor/dql/aggregate/simple_agg.rs | 2 +- src/execution/executor/dql/filter.rs | 2 +- src/execution/executor/dql/join/hash_join.rs | 4 +- src/execution/executor/dql/projection.rs | 2 +- src/execution/executor/dql/sort.rs | 4 +- src/expression/evaluator.rs | 33 +++++++++----- src/expression/mod.rs | 44 ++++++++++++++++--- src/storage/mod.rs | 2 +- tests/slt/filter.slt | 12 +++++ 13 files changed, 131 insertions(+), 35 deletions(-) diff --git a/src/binder/aggregate.rs b/src/binder/aggregate.rs index 3c6539b3..8027e62b 100644 --- a/src/binder/aggregate.rs +++ b/src/binder/aggregate.rs @@ -28,7 +28,7 @@ impl<'a, T: Transaction> Binder<'a, T> { select_items: &mut [ScalarExpression], ) -> Result<(), BindError> { for column in select_items { - self.visit_column_agg_expr(column, true)?; + self.visit_column_agg_expr(column)?; } Ok(()) } @@ -55,7 +55,7 @@ impl<'a, T: Transaction> Binder<'a, T> { // Extract having expression. let return_having = if let Some(having) = having { let mut having = self.bind_expr(having)?; - self.visit_column_agg_expr(&mut having, false)?; + self.visit_column_agg_expr(&mut having)?; Some(having) } else { @@ -72,7 +72,7 @@ impl<'a, T: Transaction> Binder<'a, T> { nulls_first, } = orderby; let mut expr = self.bind_expr(expr)?; - self.visit_column_agg_expr(&mut expr, false)?; + self.visit_column_agg_expr(&mut expr)?; return_orderby.push(SortField::new( expr, @@ -90,25 +90,34 @@ impl<'a, T: Transaction> Binder<'a, T> { fn visit_column_agg_expr( &mut self, expr: &mut ScalarExpression, - is_select: bool, ) -> Result<(), BindError> { match expr { ScalarExpression::AggCall { .. } => { self.context.agg_calls.push(expr.clone()); } ScalarExpression::TypeCast { expr, .. } => { - self.visit_column_agg_expr(expr, is_select)? + self.visit_column_agg_expr(expr)? } - ScalarExpression::IsNull { expr, .. } => self.visit_column_agg_expr(expr, is_select)?, - ScalarExpression::Unary { expr, .. } => self.visit_column_agg_expr(expr, is_select)?, - ScalarExpression::Alias { expr, .. } => self.visit_column_agg_expr(expr, is_select)?, + ScalarExpression::IsNull { expr, .. } => self.visit_column_agg_expr(expr)?, + ScalarExpression::Unary { expr, .. } => self.visit_column_agg_expr(expr)?, + ScalarExpression::Alias { expr, .. } => self.visit_column_agg_expr(expr)?, ScalarExpression::Binary { left_expr, right_expr, .. } => { - self.visit_column_agg_expr(left_expr, is_select)?; - self.visit_column_agg_expr(right_expr, is_select)?; + self.visit_column_agg_expr(left_expr)?; + self.visit_column_agg_expr(right_expr)?; + } + ScalarExpression::In { + expr, + args, + .. + } => { + self.visit_column_agg_expr(expr)?; + for arg in args { + self.visit_column_agg_expr(arg)?; + } } ScalarExpression::Constant(_) | ScalarExpression::ColumnRef { .. } => {} } @@ -242,6 +251,13 @@ impl<'a, T: Transaction> Binder<'a, T> { ScalarExpression::TypeCast { expr, .. } => self.validate_having_orderby(expr), ScalarExpression::IsNull { expr, .. } => self.validate_having_orderby(expr), ScalarExpression::Unary { expr, .. } => self.validate_having_orderby(expr), + ScalarExpression::In { expr, args, .. } => { + self.validate_having_orderby(expr)?; + for arg in args { + self.validate_having_orderby(arg)?; + } + Ok(()) + } ScalarExpression::Binary { left_expr, right_expr, diff --git a/src/binder/expr.rs b/src/binder/expr.rs index 01bd21c2..ed3f6ccb 100644 --- a/src/binder/expr.rs +++ b/src/binder/expr.rs @@ -34,6 +34,7 @@ impl<'a, T: Transaction> Binder<'a, T> { } => self.bind_like(*negated, expr, pattern), Expr::IsNull(expr) => self.bind_is_null(expr, false), Expr::IsNotNull(expr) => self.bind_is_null(expr, true), + Expr::InList { expr, list, negated } => self.bind_is_in(expr, list, *negated), _ => { todo!() } @@ -235,6 +236,18 @@ impl<'a, T: Transaction> Binder<'a, T> { }) } + fn bind_is_in(&mut self, expr: &Expr, list: &[Expr], negated: bool) -> Result { + let args = list.iter() + .map(|expr| self.bind_expr(expr)) + .try_collect()?; + + Ok(ScalarExpression::In { + negated, + expr: Box::new(self.bind_expr(expr)?), + args, + }) + } + fn wildcard_expr() -> ScalarExpression { ScalarExpression::Constant(Arc::new(DataValue::Utf8(Some("*".to_string())))) } diff --git a/src/db.rs b/src/db.rs index 1b94a885..9a5e4bcc 100644 --- a/src/db.rs +++ b/src/db.rs @@ -239,6 +239,14 @@ mod test { let tuples_not_like_t1 = kipsql.run("select * from t1 where z not like '_b'").await?; println!("{}", create_table(&tuples_not_like_t1)); + println!("in t1:"); + let tuples_in_t1 = kipsql.run("select * from t1 where a in (5, 29)").await?; + println!("{}", create_table(&tuples_in_t1)); + + println!("not in t1:"); + let tuples_not_in_t1 = kipsql.run("select * from t1 where a not in (5, 29)").await?; + println!("{}", create_table(&tuples_not_in_t1)); + println!("limit:"); let tuples_limit = kipsql.run("select * from t1 limit 1 offset 1").await?; println!("{}", create_table(&tuples_limit)); diff --git a/src/execution/executor/dql/aggregate/hash_agg.rs b/src/execution/executor/dql/aggregate/hash_agg.rs index fa4052b0..47ff3aea 100644 --- a/src/execution/executor/dql/aggregate/hash_agg.rs +++ b/src/execution/executor/dql/aggregate/hash_agg.rs @@ -67,7 +67,7 @@ impl HashAggExecutor { .iter() .map(|expr| { if let ScalarExpression::AggCall { args, .. } = expr { - args[0].eval_column(&tuple) + args[0].eval(&tuple) } else { unreachable!() } @@ -77,7 +77,7 @@ impl HashAggExecutor { let group_keys: Vec = self .groupby_exprs .iter() - .map(|expr| expr.eval_column(&tuple)) + .map(|expr| expr.eval(&tuple)) .try_collect()?; for (acc, value) in group_hash_accs diff --git a/src/execution/executor/dql/aggregate/simple_agg.rs b/src/execution/executor/dql/aggregate/simple_agg.rs index 96c1589b..81454d94 100644 --- a/src/execution/executor/dql/aggregate/simple_agg.rs +++ b/src/execution/executor/dql/aggregate/simple_agg.rs @@ -50,7 +50,7 @@ impl SimpleAggExecutor { .agg_calls .iter() .map(|expr| match expr { - ScalarExpression::AggCall { args, .. } => args[0].eval_column(&tuple), + ScalarExpression::AggCall { args, .. } => args[0].eval(&tuple), _ => unreachable!(), }) .try_collect()?; diff --git a/src/execution/executor/dql/filter.rs b/src/execution/executor/dql/filter.rs index 53ff1d09..b86da170 100644 --- a/src/execution/executor/dql/filter.rs +++ b/src/execution/executor/dql/filter.rs @@ -33,7 +33,7 @@ impl Filter { #[for_await] for tuple in input { let tuple = tuple?; - if let DataValue::Boolean(option) = predicate.eval_column(&tuple)?.as_ref() { + if let DataValue::Boolean(option) = predicate.eval(&tuple)?.as_ref() { if let Some(true) = option { yield tuple; } else { diff --git a/src/execution/executor/dql/join/hash_join.rs b/src/execution/executor/dql/join/hash_join.rs index c99fee67..1676b558 100644 --- a/src/execution/executor/dql/join/hash_join.rs +++ b/src/execution/executor/dql/join/hash_join.rs @@ -146,7 +146,7 @@ impl HashJoin { let mut filter_tuples = Vec::with_capacity(join_tuples.len()); for mut tuple in join_tuples { - if let DataValue::Boolean(option) = expr.eval_column(&tuple)?.as_ref() { + if let DataValue::Boolean(option) = expr.eval(&tuple)?.as_ref() { if let Some(false) | None = option { let full_cols_len = tuple.columns.len(); let left_cols_len = full_cols_len - right_cols_len; @@ -239,7 +239,7 @@ impl HashJoin { let mut values = Vec::with_capacity(on_keys.len()); for expr in on_keys { - values.push(expr.eval_column(tuple)?); + values.push(expr.eval(tuple)?); } Ok(hash_random_state.hash_one(values)) diff --git a/src/execution/executor/dql/projection.rs b/src/execution/executor/dql/projection.rs index de993900..d3a0a218 100644 --- a/src/execution/executor/dql/projection.rs +++ b/src/execution/executor/dql/projection.rs @@ -37,7 +37,7 @@ impl Projection { let mut values = Vec::with_capacity(exprs.len()); for expr in exprs.iter() { - values.push(expr.eval_column(&tuple)?); + values.push(expr.eval(&tuple)?); columns.push(expr.output_columns()); } diff --git a/src/execution/executor/dql/sort.rs b/src/execution/executor/dql/sort.rs index e1d25fd2..25e3c503 100644 --- a/src/execution/executor/dql/sort.rs +++ b/src/execution/executor/dql/sort.rs @@ -53,8 +53,8 @@ impl Sort { nulls_first, } in &sort_fields { - let value_1 = expr.eval_column(tuple_1).unwrap(); - let value_2 = expr.eval_column(tuple_2).unwrap(); + let value_1 = expr.eval(tuple_1).unwrap(); + let value_2 = expr.eval(tuple_2).unwrap(); ordering = value_1.partial_cmp(&value_2).unwrap_or_else(|| { match (value_1.is_null(), value_2.is_null()) { diff --git a/src/expression/evaluator.rs b/src/expression/evaluator.rs index de35803b..b23cd313 100644 --- a/src/expression/evaluator.rs +++ b/src/expression/evaluator.rs @@ -12,7 +12,7 @@ lazy_static! { } impl ScalarExpression { - pub fn eval_column(&self, tuple: &Tuple) -> Result { + pub fn eval(&self, tuple: &Tuple) -> Result { if let Some(value) = Self::eval_with_name(&tuple, self.output_columns().name()) { return Ok(value.clone()); } @@ -31,10 +31,10 @@ impl ScalarExpression { return Ok(value.clone()); } - expr.eval_column(tuple) + expr.eval(tuple) } ScalarExpression::TypeCast { expr, ty, .. } => { - let value = expr.eval_column(tuple)?; + let value = expr.eval(tuple)?; Ok(Arc::new(DataValue::clone(&value).cast(ty)?)) } @@ -44,21 +44,34 @@ impl ScalarExpression { op, .. } => { - let left = left_expr.eval_column(tuple)?; - let right = right_expr.eval_column(tuple)?; + let left = left_expr.eval(tuple)?; + let right = right_expr.eval(tuple)?; Ok(Arc::new(binary_op(&left, &right, op)?)) } ScalarExpression::IsNull { expr, negated } => { - let mut value = expr.eval_column(tuple)?.is_null(); - + let mut is_null = expr.eval(tuple)?.is_null(); + if *negated { + is_null = !is_null; + } + Ok(Arc::new(DataValue::Boolean(Some(is_null)))) + } + ScalarExpression::In { expr, args, negated } => { + let value = expr.eval(tuple)?; + let mut is_in = false; + for arg in args { + if arg.eval(tuple)? == value { + is_in = true; + break + } + } if *negated { - value = !value; + is_in = !is_in; } - Ok(Arc::new(DataValue::Boolean(Some(value)))) + Ok(Arc::new(DataValue::Boolean(Some(is_in)))) } ScalarExpression::Unary { expr, op, .. } => { - let value = expr.eval_column(tuple)?; + let value = expr.eval(tuple)?; Ok(Arc::new(unary_op(&value, op)?)) } diff --git a/src/expression/mod.rs b/src/expression/mod.rs index cae6b0ae..c19a01bb 100644 --- a/src/expression/mod.rs +++ b/src/expression/mod.rs @@ -55,6 +55,11 @@ pub enum ScalarExpression { args: Vec, ty: LogicalType, }, + In { + negated: bool, + expr: Box, + args: Vec + } } impl ScalarExpression { @@ -95,7 +100,12 @@ impl ScalarExpression { right_expr, .. } => left_expr.nullable() && right_expr.nullable(), - ScalarExpression::AggCall { args, .. } => args[0].nullable(), + ScalarExpression::In { expr, args, .. } => { + args.iter().all(ScalarExpression::nullable) && expr.nullable() + } + ScalarExpression::AggCall { args, .. } => { + args.iter().all(ScalarExpression::nullable) + }, } } @@ -115,7 +125,7 @@ impl ScalarExpression { Self::AggCall { ty: return_type, .. } => return_type.clone(), - Self::IsNull { .. } => LogicalType::Boolean, + Self::IsNull { .. } | Self::In { .. } => LogicalType::Boolean, Self::Alias { expr, .. } => expr.return_type(), } } @@ -159,6 +169,12 @@ impl ScalarExpression { columns_collect(expr, vec, only_column_ref) } } + ScalarExpression::In { expr, args, .. } => { + columns_collect(expr, vec, only_column_ref); + for arg in args { + columns_collect(arg, vec, only_column_ref) + } + } _ => (), } } @@ -183,6 +199,9 @@ impl ScalarExpression { right_expr, .. } => left_expr.has_agg_call(context) || right_expr.has_agg_call(context), + ScalarExpression::In { expr, args, .. } => { + expr.has_agg_call(context) || args.iter().any(|arg| arg.has_agg_call(context)) + } } } @@ -263,9 +282,24 @@ impl ScalarExpression { } ScalarExpression::IsNull { negated, expr } => { let suffix = if *negated { "is not null" } else { "is null" }; - let column_name = format!("{} {}", expr.output_columns().name(), suffix); Arc::new(ColumnCatalog::new( - column_name, + format!("{} {}", expr.output_columns().name(), suffix), + true, + ColumnDesc::new(LogicalType::Boolean, false, false), + Some(self.clone()), + )) + } + ScalarExpression::In { negated, expr, args } => { + let args_string = args.iter() + .map(|arg| arg.output_columns().name().to_string()) + .join(", "); + let op_string = if *negated { + "not in" + } else { + "in" + }; + Arc::new(ColumnCatalog::new( + format!("{} {} ({})", expr.output_columns().name(), op_string, args_string), true, ColumnDesc::new(LogicalType::Boolean, false, false), Some(self.clone()), @@ -351,7 +385,7 @@ impl fmt::Display for UnaryOperator { match self { UnaryOperator::Plus => write!(f, "+"), UnaryOperator::Minus => write!(f, "-"), - UnaryOperator::Not => write!(f, "not"), + UnaryOperator::Not => write!(f, "!"), } } } diff --git a/src/storage/mod.rs b/src/storage/mod.rs index d58e8733..b27feecc 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -124,7 +124,7 @@ pub(crate) fn tuple_projection( let mut values = Vec::with_capacity(projection_len); for expr in projections.iter() { - values.push(expr.eval_column(&tuple)?); + values.push(expr.eval(&tuple)?); columns.push(expr.output_columns()); } diff --git a/tests/slt/filter.slt b/tests/slt/filter.slt index 14646fe2..45cb7fd9 100644 --- a/tests/slt/filter.slt +++ b/tests/slt/filter.slt @@ -100,6 +100,18 @@ select * from t1 where v1 like 'K%L' ---- 0 KipSQL +query II +select * from t1 where id in (1, 2) +---- +1 KipDB +2 KipBlog + +query II +select * from t1 where id not in (1, 2) +---- +0 KipSQL +3 Cool! + statement ok drop table t From 7c380f3f492f1950ee62b98ea56c9bf2a1954013 Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Sat, 11 Nov 2023 11:17:28 +0800 Subject: [PATCH 04/10] ci: config codecov --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7556249..3a9427e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,10 @@ jobs: toolchain: nightly override: true components: rustfmt, clippy + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} # `cargo check` command here will use installed `nightly` # as it is set as an "override" for current directory From 63c43e8d214aea2d2bfc7a6790214d7ec0ab55b7 Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Sat, 11 Nov 2023 13:33:15 +0800 Subject: [PATCH 05/10] code fmt --- src/binder/aggregate.rs | 15 +++------------ src/binder/expr.rs | 17 ++++++++++++----- src/db.rs | 4 +++- src/expression/evaluator.rs | 8 ++++++-- src/expression/mod.rs | 30 +++++++++++++++++------------- 5 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/binder/aggregate.rs b/src/binder/aggregate.rs index 8027e62b..1d9a0778 100644 --- a/src/binder/aggregate.rs +++ b/src/binder/aggregate.rs @@ -87,17 +87,12 @@ impl<'a, T: Transaction> Binder<'a, T> { Ok((return_having, return_orderby)) } - fn visit_column_agg_expr( - &mut self, - expr: &mut ScalarExpression, - ) -> Result<(), BindError> { + fn visit_column_agg_expr(&mut self, expr: &mut ScalarExpression) -> Result<(), BindError> { match expr { ScalarExpression::AggCall { .. } => { self.context.agg_calls.push(expr.clone()); } - ScalarExpression::TypeCast { expr, .. } => { - self.visit_column_agg_expr(expr)? - } + ScalarExpression::TypeCast { expr, .. } => self.visit_column_agg_expr(expr)?, ScalarExpression::IsNull { expr, .. } => self.visit_column_agg_expr(expr)?, ScalarExpression::Unary { expr, .. } => self.visit_column_agg_expr(expr)?, ScalarExpression::Alias { expr, .. } => self.visit_column_agg_expr(expr)?, @@ -109,11 +104,7 @@ impl<'a, T: Transaction> Binder<'a, T> { self.visit_column_agg_expr(left_expr)?; self.visit_column_agg_expr(right_expr)?; } - ScalarExpression::In { - expr, - args, - .. - } => { + ScalarExpression::In { expr, args, .. } => { self.visit_column_agg_expr(expr)?; for arg in args { self.visit_column_agg_expr(arg)?; diff --git a/src/binder/expr.rs b/src/binder/expr.rs index ed3f6ccb..eefe09d8 100644 --- a/src/binder/expr.rs +++ b/src/binder/expr.rs @@ -34,7 +34,11 @@ impl<'a, T: Transaction> Binder<'a, T> { } => self.bind_like(*negated, expr, pattern), Expr::IsNull(expr) => self.bind_is_null(expr, false), Expr::IsNotNull(expr) => self.bind_is_null(expr, true), - Expr::InList { expr, list, negated } => self.bind_is_in(expr, list, *negated), + Expr::InList { + expr, + list, + negated, + } => self.bind_is_in(expr, list, *negated), _ => { todo!() } @@ -236,10 +240,13 @@ impl<'a, T: Transaction> Binder<'a, T> { }) } - fn bind_is_in(&mut self, expr: &Expr, list: &[Expr], negated: bool) -> Result { - let args = list.iter() - .map(|expr| self.bind_expr(expr)) - .try_collect()?; + fn bind_is_in( + &mut self, + expr: &Expr, + list: &[Expr], + negated: bool, + ) -> Result { + let args = list.iter().map(|expr| self.bind_expr(expr)).try_collect()?; Ok(ScalarExpression::In { negated, diff --git a/src/db.rs b/src/db.rs index 9a5e4bcc..9c05b919 100644 --- a/src/db.rs +++ b/src/db.rs @@ -244,7 +244,9 @@ mod test { println!("{}", create_table(&tuples_in_t1)); println!("not in t1:"); - let tuples_not_in_t1 = kipsql.run("select * from t1 where a not in (5, 29)").await?; + let tuples_not_in_t1 = kipsql + .run("select * from t1 where a not in (5, 29)") + .await?; println!("{}", create_table(&tuples_not_in_t1)); println!("limit:"); diff --git a/src/expression/evaluator.rs b/src/expression/evaluator.rs index b23cd313..8b9201e8 100644 --- a/src/expression/evaluator.rs +++ b/src/expression/evaluator.rs @@ -56,13 +56,17 @@ impl ScalarExpression { } Ok(Arc::new(DataValue::Boolean(Some(is_null)))) } - ScalarExpression::In { expr, args, negated } => { + ScalarExpression::In { + expr, + args, + negated, + } => { let value = expr.eval(tuple)?; let mut is_in = false; for arg in args { if arg.eval(tuple)? == value { is_in = true; - break + break; } } if *negated { diff --git a/src/expression/mod.rs b/src/expression/mod.rs index c19a01bb..b3d08678 100644 --- a/src/expression/mod.rs +++ b/src/expression/mod.rs @@ -58,8 +58,8 @@ pub enum ScalarExpression { In { negated: bool, expr: Box, - args: Vec - } + args: Vec, + }, } impl ScalarExpression { @@ -103,9 +103,7 @@ impl ScalarExpression { ScalarExpression::In { expr, args, .. } => { args.iter().all(ScalarExpression::nullable) && expr.nullable() } - ScalarExpression::AggCall { args, .. } => { - args.iter().all(ScalarExpression::nullable) - }, + ScalarExpression::AggCall { args, .. } => args.iter().all(ScalarExpression::nullable), } } @@ -289,17 +287,23 @@ impl ScalarExpression { Some(self.clone()), )) } - ScalarExpression::In { negated, expr, args } => { - let args_string = args.iter() + ScalarExpression::In { + negated, + expr, + args, + } => { + let args_string = args + .iter() .map(|arg| arg.output_columns().name().to_string()) .join(", "); - let op_string = if *negated { - "not in" - } else { - "in" - }; + let op_string = if *negated { "not in" } else { "in" }; Arc::new(ColumnCatalog::new( - format!("{} {} ({})", expr.output_columns().name(), op_string, args_string), + format!( + "{} {} ({})", + expr.output_columns().name(), + op_string, + args_string + ), true, ColumnDesc::new(LogicalType::Boolean, false, false), Some(self.clone()), From fed21768710695d991136fa5a50377d64a3f64ef Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Sat, 11 Nov 2023 13:56:45 +0800 Subject: [PATCH 06/10] ci: config codecov --- .github/workflows/ci.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a9427e3..d97fb0a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,10 +21,6 @@ jobs: toolchain: nightly override: true components: rustfmt, clippy - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} # `cargo check` command here will use installed `nightly` # as it is set as an "override" for current directory @@ -86,4 +82,12 @@ jobs: uses: actions-rs/cargo@v1 with: command: run - args: --bin sqllogictest-test --manifest-path ./tests/sqllogictest/Cargo.toml \ No newline at end of file + args: --bin sqllogictest-test --manifest-path ./tests/sqllogictest/Cargo.toml + codecov: + name: Upload coverage reports to Codecov + runs-on: ubuntu-latest + steps: + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file From d3d779b82481117320671a543a31191d8833d795 Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Sat, 11 Nov 2023 14:15:37 +0800 Subject: [PATCH 07/10] ci: config codecov --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d97fb0a5..256641f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,5 +89,8 @@ jobs: steps: - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 + with: + files: ./lcov.info + flags: rust env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file From 5fd944a8aa0cd2bc748ecae82feaf5ca099ef6fc Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Sat, 11 Nov 2023 14:19:33 +0800 Subject: [PATCH 08/10] ci: config codecov --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 256641f9..8bb5716d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,7 +90,6 @@ jobs: - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 with: - files: ./lcov.info flags: rust env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file From b289af09b580260cc514ff3b24c32b8785c3c4d1 Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Sat, 11 Nov 2023 15:39:41 +0800 Subject: [PATCH 09/10] docs: README.md --- .github/workflows/ci.yml | 21 +++++----- Cargo.toml | 1 + README.md | 89 ++++++++++++++++++++++++++-------------- 3 files changed, 71 insertions(+), 40 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8bb5716d..ab93efad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,13 +83,14 @@ jobs: with: command: run args: --bin sqllogictest-test --manifest-path ./tests/sqllogictest/Cargo.toml - codecov: - name: Upload coverage reports to Codecov - runs-on: ubuntu-latest - steps: - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3 - with: - flags: rust - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file +# codecov: +# name: Upload coverage reports to Codecov +# runs-on: ubuntu-latest +# steps: +# - name: Upload coverage reports to Codecov +# uses: codecov/codecov-action@v3 +# with: +# files: ./lcov.info +# flags: rust +# env: +# CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 5351475a..c3f5c1fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ csv = "1" regex = "1.10.2" [dev-dependencies] +cargo-tarpaulin = "0.27.1" tokio-test = "0.4.2" ctor = "0.2.0" env_logger = "0.10" diff --git a/README.md b/README.md index 501a2494..35cabf78 100755 --- a/README.md +++ b/README.md @@ -10,45 +10,67 @@ Built by @KipData ----------------------------------- Embedded SQL DBMS -
- -### Architecture -Welcome to our WebSite, Power By KipSQL: -**http://www.kipdata.site/** - -> Lightweight SQL calculation engine, as the SQL layer of KipDB, implemented with TalentPlan's TinySQL as the reference standard - - -![architecture](./static/images/architecture.png) - -### Get Started -``` toml -kip-sql = "0.0.1-alpha.0" +

+ The Lightweight Embedded OLTP Open-source Database +

+ +

+   + CI +   + +   + +

+

+ + github star + github fork + +

+ +### What is KipSQL + +KipSQL is designed to allow small Rust projects to reduce external dependencies and get rid of heavy database maintenance, +so that the Rust application itself can provide SQL storage capabilities. + + +If you are a developer of the following applications, we very much welcome you to try using KipSQL +and provide your experience and opinions on using it. +- personal website +- desktop/mobile application +- learning database +- platform bot + +Welcome to our WebSite, Power By KipSQL: **http://www.kipdata.site/** + +### Quick Started +Clone the repository +``` shell +git clone https://github.com/KipData/KipSQL.git ``` Install rust toolchain first. ``` cargo run ``` -test command +Example ```sql -create table t1 (a int primary key, b int); +create table blog (id int primary key, title int unique); -insert into t1 (a, b) values (1, 1), (5, 3), (6, 2); +insert into blog (id, title) values (0, 'KipSQL'), (1, 'KipDB'), (2, 'KipBlog'); -update t1 set a = 0 where b > 1; +update blog set title = 'KipData' where id = 2; -delete from t1 where b > 1; +select * from blog order by a asc nulls first -select * from t1; +select count(distinct id) from blog; -select * from t1 order by a asc nulls first +delete from blog where title like 'Kip%'; -select count(distinct a) from t1; +truncate table blog; -truncate table t1; - -drop table t1; +drop table blog; ``` Using KipSQL in code ```rust @@ -58,9 +80,6 @@ let tupes = db.run("select * from t1").await?; ``` Storage Support: - KipDB -- Memory - -![demo](./static/images/demo.png) ### Features - ORM Mapping @@ -100,6 +119,8 @@ implement_from_tuple!(Post, ( - is not null - like - not like + - in + - not in - Supports index type - Unique Index - Supports multiple primary key types @@ -128,7 +149,7 @@ implement_from_tuple!(Post, ( - [x] Distinct - [x] Alias - [x] Aggregation: count()/sum()/avg()/min()/max() - - [ ] Subquery + - [x] SubQuery(from) - [x] Join: Inner/Left/Right/Full Cross(x) - [x] Group By - [x] Having @@ -165,6 +186,14 @@ implement_from_tuple!(Post, ( - Column Pruning - Collapse Project +## License + +KipSQL uses the [Apache 2.0 license][1] to strike a balance between +open contributions and allowing you to use the software however you want. + +[1]: + ### Thanks For -- [Fedomn/sqlrs](https://github.com/Fedomn/sqlrs): 主要参考资料,Optimizer、Executor均参考自sqlrs的设计 +- [Fedomn/sqlrs](https://github.com/Fedomn/sqlrs): Main reference materials, Optimizer and Executor all refer to the design of sqlrs - [systemxlabs/bustubx](https://github.com/systemxlabs/bustubx) +- [duckdb/duckdb](https://github.com/duckdb/duckdb) From a1cd0daaffe73045e6e364b76761d3308020e1a8 Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Sat, 11 Nov 2023 15:45:21 +0800 Subject: [PATCH 10/10] docs: README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 35cabf78..a017ec0c 100755 --- a/README.md +++ b/README.md @@ -56,13 +56,13 @@ cargo run ``` Example ```sql -create table blog (id int primary key, title int unique); +create table blog (id int primary key, title varchar unique); insert into blog (id, title) values (0, 'KipSQL'), (1, 'KipDB'), (2, 'KipBlog'); update blog set title = 'KipData' where id = 2; -select * from blog order by a asc nulls first +select * from blog order by title desc nulls first select count(distinct id) from blog;