Skip to content

Commit 43787a7

Browse files
authored
Support In expression (#95)
* 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
1 parent 09c1042 commit 43787a7

File tree

20 files changed

+304
-255
lines changed

20 files changed

+304
-255
lines changed

.github/workflows/ci.yml

+12-1
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,15 @@ jobs:
8282
uses: actions-rs/cargo@v1
8383
with:
8484
command: run
85-
args: --bin sqllogictest-test --manifest-path ./tests/sqllogictest/Cargo.toml
85+
args: --bin sqllogictest-test --manifest-path ./tests/sqllogictest/Cargo.toml
86+
# codecov:
87+
# name: Upload coverage reports to Codecov
88+
# runs-on: ubuntu-latest
89+
# steps:
90+
# - name: Upload coverage reports to Codecov
91+
# uses: codecov/codecov-action@v3
92+
# with:
93+
# files: ./lcov.info
94+
# flags: rust
95+
# env:
96+
# CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ csv = "1"
4242
regex = "1.10.2"
4343

4444
[dev-dependencies]
45+
cargo-tarpaulin = "0.27.1"
4546
tokio-test = "0.4.2"
4647
ctor = "0.2.0"
4748
env_logger = "0.10"

README.md

+59-30
Original file line numberDiff line numberDiff line change
@@ -10,45 +10,67 @@ Built by @KipData
1010
-----------------------------------
1111
Embedded SQL DBMS
1212
</pre>
13-
<br/>
14-
15-
### Architecture
16-
Welcome to our WebSite, Power By KipSQL:
17-
**http://www.kipdata.site/**
18-
19-
> Lightweight SQL calculation engine, as the SQL layer of KipDB, implemented with TalentPlan's TinySQL as the reference standard
20-
21-
22-
![architecture](./static/images/architecture.png)
23-
24-
### Get Started
25-
``` toml
26-
kip-sql = "0.0.1-alpha.0"
13+
<h3 align="center">
14+
The Lightweight Embedded OLTP Open-source Database
15+
</h3>
16+
17+
<p align="center">
18+
&nbsp;
19+
<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>
20+
&nbsp;
21+
<a href="https://github.com/KipData/KipSQL/blob/main/LICENSE"><img src="https://img.shields.io/github/license/KipData/KipSQL"></a>
22+
&nbsp;
23+
<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>
24+
</p>
25+
<p align="center">
26+
<a href="https://github.com/KipData/KipSQL" target="_blank">
27+
<img src="https://img.shields.io/github/stars/KipData/KipSQL.svg?style=social" alt="github star"/>
28+
<img src="https://img.shields.io/github/forks/KipData/KipSQL.svg?style=social" alt="github fork"/>
29+
</a>
30+
</p>
31+
32+
### What is KipSQL
33+
34+
KipSQL is designed to allow small Rust projects to reduce external dependencies and get rid of heavy database maintenance,
35+
so that the Rust application itself can provide SQL storage capabilities.
36+
37+
38+
If you are a developer of the following applications, we very much welcome you to try using KipSQL
39+
and provide your experience and opinions on using it.
40+
- personal website
41+
- desktop/mobile application
42+
- learning database
43+
- platform bot
44+
45+
Welcome to our WebSite, Power By KipSQL: **http://www.kipdata.site/**
46+
47+
### Quick Started
48+
Clone the repository
49+
``` shell
50+
git clone https://github.com/KipData/KipSQL.git
2751
```
2852

2953
Install rust toolchain first.
3054
```
3155
cargo run
3256
```
33-
test command
57+
Example
3458
```sql
35-
create table t1 (a int primary key, b int);
59+
create table blog (id int primary key, title varchar unique);
3660

37-
insert into t1 (a, b) values (1, 1), (5, 3), (6, 2);
61+
insert into blog (id, title) values (0, 'KipSQL'), (1, 'KipDB'), (2, 'KipBlog');
3862

39-
update t1 set a = 0 where b > 1;
63+
update blog set title = 'KipData' where id = 2;
4064

41-
delete from t1 where b > 1;
65+
select * from blog order by title desc nulls first
4266

43-
select * from t1;
67+
select count(distinct id) from blog;
4468

45-
select * from t1 order by a asc nulls first
69+
delete from blog where title like 'Kip%';
4670

47-
select count(distinct a) from t1;
71+
truncate table blog;
4872

49-
truncate table t1;
50-
51-
drop table t1;
73+
drop table blog;
5274
```
5375
Using KipSQL in code
5476
```rust
@@ -58,9 +80,6 @@ let tupes = db.run("select * from t1").await?;
5880
```
5981
Storage Support:
6082
- KipDB
61-
- Memory
62-
63-
![demo](./static/images/demo.png)
6483

6584
### Features
6685
- ORM Mapping
@@ -100,6 +119,8 @@ implement_from_tuple!(Post, (
100119
- is not null
101120
- like
102121
- not like
122+
- in
123+
- not in
103124
- Supports index type
104125
- Unique Index
105126
- Supports multiple primary key types
@@ -128,7 +149,7 @@ implement_from_tuple!(Post, (
128149
- [x] Distinct
129150
- [x] Alias
130151
- [x] Aggregation: count()/sum()/avg()/min()/max()
131-
- [ ] Subquery
152+
- [x] SubQuery(from)
132153
- [x] Join: Inner/Left/Right/Full Cross(x)
133154
- [x] Group By
134155
- [x] Having
@@ -165,6 +186,14 @@ implement_from_tuple!(Post, (
165186
- Column Pruning
166187
- Collapse Project
167188

189+
## License
190+
191+
KipSQL uses the [Apache 2.0 license][1] to strike a balance between
192+
open contributions and allowing you to use the software however you want.
193+
194+
[1]: <https://github.com/KipData/KipSQL/blob/main/LICENSE>
195+
168196
### Thanks For
169-
- [Fedomn/sqlrs](https://github.com/Fedomn/sqlrs): 主要参考资料,Optimizer、Executor均参考自sqlrs的设计
197+
- [Fedomn/sqlrs](https://github.com/Fedomn/sqlrs): Main reference materials, Optimizer and Executor all refer to the design of sqlrs
170198
- [systemxlabs/bustubx](https://github.com/systemxlabs/bustubx)
199+
- [duckdb/duckdb](https://github.com/duckdb/duckdb)

src/binder/aggregate.rs

+30-101
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use itertools::Itertools;
33
use sqlparser::ast::{Expr, OrderByExpr};
44
use std::collections::HashSet;
55

6-
use crate::binder::{BindError, InputRefType};
6+
use crate::binder::BindError;
77
use crate::planner::LogicalPlan;
88
use crate::storage::Transaction;
99
use crate::{
@@ -28,7 +28,7 @@ impl<'a, T: Transaction> Binder<'a, T> {
2828
select_items: &mut [ScalarExpression],
2929
) -> Result<(), BindError> {
3030
for column in select_items {
31-
self.visit_column_agg_expr(column, true)?;
31+
self.visit_column_agg_expr(column)?;
3232
}
3333
Ok(())
3434
}
@@ -55,7 +55,7 @@ impl<'a, T: Transaction> Binder<'a, T> {
5555
// Extract having expression.
5656
let return_having = if let Some(having) = having {
5757
let mut having = self.bind_expr(having)?;
58-
self.visit_column_agg_expr(&mut having, false)?;
58+
self.visit_column_agg_expr(&mut having)?;
5959

6060
Some(having)
6161
} else {
@@ -72,7 +72,7 @@ impl<'a, T: Transaction> Binder<'a, T> {
7272
nulls_first,
7373
} = orderby;
7474
let mut expr = self.bind_expr(expr)?;
75-
self.visit_column_agg_expr(&mut expr, false)?;
75+
self.visit_column_agg_expr(&mut expr)?;
7676

7777
return_orderby.push(SortField::new(
7878
expr,
@@ -87,77 +87,30 @@ impl<'a, T: Transaction> Binder<'a, T> {
8787
Ok((return_having, return_orderby))
8888
}
8989

90-
fn visit_column_agg_expr(
91-
&mut self,
92-
expr: &mut ScalarExpression,
93-
is_select: bool,
94-
) -> Result<(), BindError> {
95-
let ref_columns = expr.referenced_columns();
96-
90+
fn visit_column_agg_expr(&mut self, expr: &mut ScalarExpression) -> Result<(), BindError> {
9791
match expr {
98-
ScalarExpression::AggCall {
99-
ty: return_type, ..
100-
} => {
101-
let ty = return_type.clone();
102-
if is_select {
103-
let index = self.context.input_ref_index(InputRefType::AggCall);
104-
let input_ref = ScalarExpression::InputRef {
105-
index,
106-
ty,
107-
ref_columns,
108-
};
109-
match std::mem::replace(expr, input_ref) {
110-
ScalarExpression::AggCall {
111-
kind,
112-
args,
113-
ty,
114-
distinct,
115-
} => {
116-
self.context.agg_calls.push(ScalarExpression::AggCall {
117-
distinct,
118-
kind,
119-
args,
120-
ty,
121-
});
122-
}
123-
_ => unreachable!(),
124-
}
125-
} else {
126-
let (index, _) = self
127-
.context
128-
.agg_calls
129-
.iter()
130-
.find_position(|agg_expr| agg_expr == &expr)
131-
.ok_or_else(|| BindError::AggMiss(format!("{:?}", expr)))?;
132-
133-
let _ = std::mem::replace(
134-
expr,
135-
ScalarExpression::InputRef {
136-
index,
137-
ty,
138-
ref_columns,
139-
},
140-
);
141-
}
142-
}
143-
144-
ScalarExpression::TypeCast { expr, .. } => {
145-
self.visit_column_agg_expr(expr, is_select)?
92+
ScalarExpression::AggCall { .. } => {
93+
self.context.agg_calls.push(expr.clone());
14694
}
147-
ScalarExpression::IsNull { expr, .. } => self.visit_column_agg_expr(expr, is_select)?,
148-
ScalarExpression::Unary { expr, .. } => self.visit_column_agg_expr(expr, is_select)?,
149-
ScalarExpression::Alias { expr, .. } => self.visit_column_agg_expr(expr, is_select)?,
95+
ScalarExpression::TypeCast { expr, .. } => self.visit_column_agg_expr(expr)?,
96+
ScalarExpression::IsNull { expr, .. } => self.visit_column_agg_expr(expr)?,
97+
ScalarExpression::Unary { expr, .. } => self.visit_column_agg_expr(expr)?,
98+
ScalarExpression::Alias { expr, .. } => self.visit_column_agg_expr(expr)?,
15099
ScalarExpression::Binary {
151100
left_expr,
152101
right_expr,
153102
..
154103
} => {
155-
self.visit_column_agg_expr(left_expr, is_select)?;
156-
self.visit_column_agg_expr(right_expr, is_select)?;
104+
self.visit_column_agg_expr(left_expr)?;
105+
self.visit_column_agg_expr(right_expr)?;
157106
}
158-
ScalarExpression::Constant(_)
159-
| ScalarExpression::ColumnRef { .. }
160-
| ScalarExpression::InputRef { .. } => {}
107+
ScalarExpression::In { expr, args, .. } => {
108+
self.visit_column_agg_expr(expr)?;
109+
for arg in args {
110+
self.visit_column_agg_expr(arg)?;
111+
}
112+
}
113+
ScalarExpression::Constant(_) | ScalarExpression::ColumnRef { .. } => {}
161114
}
162115

163116
Ok(())
@@ -239,44 +192,13 @@ impl<'a, T: Transaction> Binder<'a, T> {
239192
false
240193
}
241194
}) {
242-
let index = self.context.input_ref_index(InputRefType::GroupBy);
243-
let mut select_item = &mut select_list[i];
244-
let ref_columns = select_item.referenced_columns();
245-
let return_type = select_item.return_type();
246-
247-
self.context.group_by_exprs.push(std::mem::replace(
248-
&mut select_item,
249-
ScalarExpression::InputRef {
250-
index,
251-
ty: return_type,
252-
ref_columns,
253-
},
254-
));
195+
self.context.group_by_exprs.push(select_list[i].clone());
255196
return;
256197
}
257198
}
258199

259200
if let Some(i) = select_list.iter().position(|column| column == expr) {
260-
let expr = &mut select_list[i];
261-
let ref_columns = expr.referenced_columns();
262-
263-
match expr {
264-
ScalarExpression::Constant(_) | ScalarExpression::ColumnRef { .. } => {
265-
self.context.group_by_exprs.push(expr.clone())
266-
}
267-
_ => {
268-
let index = self.context.input_ref_index(InputRefType::GroupBy);
269-
270-
self.context.group_by_exprs.push(std::mem::replace(
271-
expr,
272-
ScalarExpression::InputRef {
273-
index,
274-
ty: expr.return_type(),
275-
ref_columns,
276-
},
277-
))
278-
}
279-
}
201+
self.context.group_by_exprs.push(select_list[i].clone())
280202
}
281203
}
282204

@@ -320,6 +242,13 @@ impl<'a, T: Transaction> Binder<'a, T> {
320242
ScalarExpression::TypeCast { expr, .. } => self.validate_having_orderby(expr),
321243
ScalarExpression::IsNull { expr, .. } => self.validate_having_orderby(expr),
322244
ScalarExpression::Unary { expr, .. } => self.validate_having_orderby(expr),
245+
ScalarExpression::In { expr, args, .. } => {
246+
self.validate_having_orderby(expr)?;
247+
for arg in args {
248+
self.validate_having_orderby(arg)?;
249+
}
250+
Ok(())
251+
}
323252
ScalarExpression::Binary {
324253
left_expr,
325254
right_expr,
@@ -330,7 +259,7 @@ impl<'a, T: Transaction> Binder<'a, T> {
330259
Ok(())
331260
}
332261

333-
ScalarExpression::Constant(_) | ScalarExpression::InputRef { .. } => Ok(()),
262+
ScalarExpression::Constant(_) => Ok(()),
334263
}
335264
}
336265
}

src/binder/expr.rs

+20
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ impl<'a, T: Transaction> Binder<'a, T> {
3434
} => self.bind_like(*negated, expr, pattern),
3535
Expr::IsNull(expr) => self.bind_is_null(expr, false),
3636
Expr::IsNotNull(expr) => self.bind_is_null(expr, true),
37+
Expr::InList {
38+
expr,
39+
list,
40+
negated,
41+
} => self.bind_is_in(expr, list, *negated),
3742
_ => {
3843
todo!()
3944
}
@@ -235,6 +240,21 @@ impl<'a, T: Transaction> Binder<'a, T> {
235240
})
236241
}
237242

243+
fn bind_is_in(
244+
&mut self,
245+
expr: &Expr,
246+
list: &[Expr],
247+
negated: bool,
248+
) -> Result<ScalarExpression, BindError> {
249+
let args = list.iter().map(|expr| self.bind_expr(expr)).try_collect()?;
250+
251+
Ok(ScalarExpression::In {
252+
negated,
253+
expr: Box::new(self.bind_expr(expr)?),
254+
args,
255+
})
256+
}
257+
238258
fn wildcard_expr() -> ScalarExpression {
239259
ScalarExpression::Constant(Arc::new(DataValue::Utf8(Some("*".to_string()))))
240260
}

0 commit comments

Comments
 (0)