Skip to content

Commit

Permalink
Support In expression (#95)
Browse files Browse the repository at this point in the history
* refactor: remove InputRef expression

* fix: pushdown_predicates not work

* feat: support `in` expression

* ci: config codecov

* code fmt

* ci: config codecov

* ci: config codecov

* ci: config codecov

* docs: README.md

* docs: README.md
  • Loading branch information
KKould authored Nov 11, 2023
1 parent 09c1042 commit 43787a7
Show file tree
Hide file tree
Showing 20 changed files with 304 additions and 255 deletions.
13 changes: 12 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,15 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: run
args: --bin sqllogictest-test --manifest-path ./tests/sqllogictest/Cargo.toml
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:
# files: ./lcov.info
# flags: rust
# env:
# CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
89 changes: 59 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,45 +10,67 @@ Built by @KipData
-----------------------------------
Embedded SQL DBMS
</pre>
<br/>

### 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"
<h3 align="center">
The Lightweight Embedded OLTP Open-source Database
</h3>

<p align="center">
&nbsp;
<a href="https://github.com/KipData/KipSQL/actions/workflows/ci.yml"><img src="https://github.com/KipData/KipSQL/actions/workflows/ci.yml/badge.svg" alt="CI"></img></a>
&nbsp;
<a href="https://github.com/KipData/KipSQL/blob/main/LICENSE"><img src="https://img.shields.io/github/license/KipData/KipSQL"></a>
&nbsp;
<a href="https://www.rust-lang.org/community"><img src="https://img.shields.io/badge/Rust_Community%20-Join_us-brightgreen?style=plastic&logo=rust"></a>
</p>
<p align="center">
<a href="https://github.com/KipData/KipSQL" target="_blank">
<img src="https://img.shields.io/github/stars/KipData/KipSQL.svg?style=social" alt="github star"/>
<img src="https://img.shields.io/github/forks/KipData/KipSQL.svg?style=social" alt="github fork"/>
</a>
</p>

### 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 varchar 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 title desc 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
Expand All @@ -58,9 +80,6 @@ let tupes = db.run("select * from t1").await?;
```
Storage Support:
- KipDB
- Memory

![demo](./static/images/demo.png)

### Features
- ORM Mapping
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]: <https://github.com/KipData/KipSQL/blob/main/LICENSE>

### 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)
131 changes: 30 additions & 101 deletions src/binder/aggregate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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(())
}
Expand All @@ -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 {
Expand All @@ -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,
Expand All @@ -87,77 +87,30 @@ impl<'a, T: Transaction> Binder<'a, T> {
Ok((return_having, return_orderby))
}

fn visit_column_agg_expr(
&mut self,
expr: &mut ScalarExpression,
is_select: bool,
) -> Result<(), BindError> {
let ref_columns = expr.referenced_columns();

fn visit_column_agg_expr(&mut self, expr: &mut ScalarExpression) -> Result<(), BindError> {
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::TypeCast { expr, .. } => {
self.visit_column_agg_expr(expr, is_select)?
ScalarExpression::AggCall { .. } => {
self.context.agg_calls.push(expr.clone());
}
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::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)?,
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::Constant(_)
| ScalarExpression::ColumnRef { .. }
| ScalarExpression::InputRef { .. } => {}
ScalarExpression::In { expr, args, .. } => {
self.visit_column_agg_expr(expr)?;
for arg in args {
self.visit_column_agg_expr(arg)?;
}
}
ScalarExpression::Constant(_) | ScalarExpression::ColumnRef { .. } => {}
}

Ok(())
Expand Down Expand Up @@ -239,44 +192,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())
}
}

Expand Down Expand Up @@ -320,6 +242,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,
Expand All @@ -330,7 +259,7 @@ impl<'a, T: Transaction> Binder<'a, T> {
Ok(())
}

ScalarExpression::Constant(_) | ScalarExpression::InputRef { .. } => Ok(()),
ScalarExpression::Constant(_) => Ok(()),
}
}
}
20 changes: 20 additions & 0 deletions src/binder/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +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),
_ => {
todo!()
}
Expand Down Expand Up @@ -235,6 +240,21 @@ impl<'a, T: Transaction> Binder<'a, T> {
})
}

fn bind_is_in(
&mut self,
expr: &Expr,
list: &[Expr],
negated: bool,
) -> Result<ScalarExpression, BindError> {
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()))))
}
Expand Down
Loading

0 comments on commit 43787a7

Please sign in to comment.