Skip to content

Commit e32ef34

Browse files
authored
feat: Implement EXISTS/NOT EXISTS subquery support (#269)
* feat: Implement EXISTS/NOT EXISTS subquery support * feat: fmt
1 parent 2aa5642 commit e32ef34

File tree

7 files changed

+135
-27
lines changed

7 files changed

+135
-27
lines changed

src/binder/expr.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,17 @@ impl<'a, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<'a, '_, T
142142
trim_where: *trim_where,
143143
})
144144
}
145+
Expr::Exists { subquery, negated } => {
146+
let (sub_query, column) = self.bind_subquery(subquery)?;
147+
let (_, sub_query) = if !self.context.is_step(&QueryBindStep::Where) {
148+
self.bind_temp_table(column, sub_query)?
149+
} else {
150+
(ScalarExpression::ColumnRef(column), sub_query)
151+
};
152+
self.context
153+
.sub_query(SubQueryType::ExistsSubQuery(*negated, sub_query));
154+
Ok(ScalarExpression::Constant(DataValue::Boolean(true)))
155+
}
145156
Expr::Subquery(subquery) => {
146157
let (sub_query, column) = self.bind_subquery(subquery)?;
147158
let (expr, sub_query) = if !self.context.is_step(&QueryBindStep::Where) {

src/binder/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ pub enum QueryBindStep {
8585
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
8686
pub enum SubQueryType {
8787
SubQuery(LogicalPlan),
88+
ExistsSubQuery(bool, LogicalPlan),
8889
InSubQuery(bool, LogicalPlan),
8990
}
9091

src/binder/select.rs

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ use super::{
2121
use crate::catalog::{ColumnCatalog, ColumnRef, ColumnSummary, TableName};
2222
use crate::errors::DatabaseError;
2323
use crate::execution::dql::join::joins_nullable;
24+
use crate::expression::agg::AggKind;
2425
use crate::expression::{AliasType, BinaryOperator};
26+
use crate::planner::operator::aggregate::AggregateOperator;
2527
use crate::planner::operator::function_scan::FunctionScanOperator;
2628
use crate::planner::operator::insert::InsertOperator;
2729
use crate::planner::operator::join::JoinCondition;
@@ -30,12 +32,14 @@ use crate::planner::operator::union::UnionOperator;
3032
use crate::planner::{Childrens, LogicalPlan, SchemaOutput};
3133
use crate::storage::Transaction;
3234
use crate::types::tuple::{Schema, SchemaRef};
35+
use crate::types::value::Utf8Type;
36+
use crate::types::LogicalType::Char;
3337
use crate::types::{ColumnId, LogicalType};
3438
use itertools::Itertools;
3539
use sqlparser::ast::{
36-
Distinct, Expr, Ident, Join, JoinConstraint, JoinOperator, Offset, OrderByExpr, Query, Select,
37-
SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, TableAlias, TableFactor,
38-
TableWithJoins,
40+
CharLengthUnits, Distinct, Expr, Ident, Join, JoinConstraint, JoinOperator, Offset,
41+
OrderByExpr, Query, Select, SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier,
42+
TableAlias, TableFactor, TableWithJoins,
3943
};
4044

4145
impl<'a: 'b, 'b, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<'a, 'b, T, A> {
@@ -599,6 +603,57 @@ impl<'a: 'b, 'b, T: Transaction, A: AsRef<[(&'static str, DataValue)]>> Binder<'
599603

600604
let (mut plan, join_ty) = match sub_query {
601605
SubQueryType::SubQuery(plan) => (plan, JoinType::Inner),
606+
SubQueryType::ExistsSubQuery(is_not, plan) => {
607+
let limit = LimitOperator::build(None, Some(1), plan);
608+
let mut agg = AggregateOperator::build(
609+
limit,
610+
vec![ScalarExpression::AggCall {
611+
distinct: false,
612+
kind: AggKind::Count,
613+
args: vec![ScalarExpression::Constant(DataValue::Utf8 {
614+
value: "*".to_string(),
615+
ty: Utf8Type::Fixed(1),
616+
unit: CharLengthUnits::Characters,
617+
})],
618+
ty: LogicalType::Integer,
619+
}],
620+
vec![],
621+
false,
622+
);
623+
let filter = FilterOperator::build(
624+
ScalarExpression::Binary {
625+
op: if is_not {
626+
BinaryOperator::NotEq
627+
} else {
628+
BinaryOperator::Eq
629+
},
630+
left_expr: Box::new(ScalarExpression::ColumnRef(
631+
agg.output_schema()[0].clone(),
632+
)),
633+
right_expr: Box::new(ScalarExpression::Constant(DataValue::Int32(
634+
1,
635+
))),
636+
evaluator: None,
637+
ty: LogicalType::Boolean,
638+
},
639+
agg,
640+
false,
641+
);
642+
let projection = ProjectOperator {
643+
exprs: vec![ScalarExpression::Constant(DataValue::Int32(1))],
644+
};
645+
let plan = LogicalPlan::new(
646+
Operator::Project(projection),
647+
Childrens::Only(filter),
648+
);
649+
children = LJoinOperator::build(
650+
children,
651+
plan,
652+
JoinCondition::None,
653+
JoinType::Cross,
654+
);
655+
continue;
656+
}
602657
SubQueryType::InSubQuery(is_not, plan) => {
603658
let join_ty = if is_not {
604659
JoinType::LeftAnti

src/expression/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -479,8 +479,15 @@ impl ScalarExpression {
479479
right_expr,
480480
..
481481
} => left_expr.has_count_star() || right_expr.has_count_star(),
482-
ScalarExpression::AggCall { args, .. }
483-
| ScalarExpression::ScalaFunction(ScalarFunction { args, .. })
482+
ScalarExpression::AggCall { args, .. } => {
483+
if args.len() == 1 {
484+
if let ScalarExpression::Constant(value) = &args[0] {
485+
return matches!(value.utf8(), Some("*"));
486+
}
487+
}
488+
args.iter().any(Self::has_count_star)
489+
}
490+
ScalarExpression::ScalaFunction(ScalarFunction { args, .. })
484491
| ScalarExpression::Coalesce { exprs: args, .. } => {
485492
args.iter().any(Self::has_count_star)
486493
}

tests/slt/exists.slt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
statement ok
2+
CREATE TABLE t1 (id INT PRIMARY KEY, val INT);
3+
4+
statement ok
5+
CREATE TABLE t2 (id INT PRIMARY KEY);
6+
7+
statement ok
8+
INSERT INTO t1 VALUES (1, 10), (2, 20), (3, 30);
9+
10+
statement ok
11+
INSERT INTO t2 VALUES (1), (3);
12+
13+
query II
14+
SELECT id, val FROM t1 WHERE EXISTS (SELECT 1 FROM t2)
15+
----
16+
1 10
17+
2 20
18+
3 30
19+
20+
query II
21+
SELECT id, val FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE 1 = 0)
22+
----
23+
24+
query II
25+
SELECT id, val FROM t1 WHERE NOT EXISTS (SELECT 1 FROM t2)
26+
----
27+
28+
29+
query II
30+
SELECT id, val FROM t1 WHERE NOT EXISTS (SELECT 1 FROM t2 WHERE 1 = 0)
31+
----
32+
1 10
33+
2 20
34+
3 30

tests/slt/sql_2016/E061_08.slt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
# E061-08: EXISTS predicate
22

3-
# TODO: Support `EXISTS` on `WHERE`
3+
statement ok
4+
CREATE TABLE TABLE_E061_08_01_01 ( ID INT PRIMARY KEY, A INT );
45

5-
# statement ok
6-
# CREATE TABLE TABLE_E061_08_01_01 ( ID INT PRIMARY KEY, A INT );
7-
8-
# SELECT A FROM TABLE_E061_08_01_01 WHERE EXISTS ( SELECT 1 )
6+
query I
7+
SELECT A FROM TABLE_E061_08_01_01 WHERE EXISTS ( SELECT 1 )

tests/slt/sql_2016/E071_06.slt

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
11
# E071-06: Table operators in subqueries
22

3-
# TODO: Table operators in subqueries
3+
statement ok
4+
CREATE TABLE TABLE_E071_06_01_011 ( ID INT PRIMARY KEY, A INT );
45

5-
# statement ok
6-
# CREATE TABLE TABLE_E071_06_01_011 ( ID INT PRIMARY KEY, A INT );
6+
statement ok
7+
CREATE TABLE TABLE_E071_06_01_012 ( ID INT PRIMARY KEY, B INT );
78

8-
# statement ok
9-
# CREATE TABLE TABLE_E071_06_01_012 ( ID INT PRIMARY KEY, B FLOAT );
9+
query I
10+
SELECT A FROM TABLE_E071_06_01_011 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_01_011 UNION ALL SELECT B FROM TABLE_E071_06_01_012 )
1011

11-
# SELECT A FROM TABLE_E071_06_01_011 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_01_011 UNION ALL SELECT B FROM TABLE_E071_06_01_012 )
12+
statement ok
13+
CREATE TABLE TABLE_E071_06_01_021 ( ID INT PRIMARY KEY, A INT );
1214

13-
# statement ok
14-
# CREATE TABLE TABLE_E071_06_01_021 ( ID INT PRIMARY KEY, A INT );
15+
statement ok
16+
CREATE TABLE TABLE_E071_06_01_022 ( ID INT PRIMARY KEY, B INT );
1517

16-
# statement ok
17-
# CREATE TABLE TABLE_E071_06_01_022 ( ID INT PRIMARY KEY, B FLOAT );
18+
query I
19+
SELECT A FROM TABLE_E071_06_01_021 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_01_021 UNION DISTINCT SELECT B FROM TABLE_E071_06_01_022 )
1820

19-
# SELECT A FROM TABLE_E071_06_01_021 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_01_021 UNION DISTINCT SELECT B FROM TABLE_E071_06_01_022 )
21+
statement ok
22+
CREATE TABLE TABLE_E071_06_01_031 ( ID INT PRIMARY KEY, A INT );
2023

21-
# statement ok
22-
# CREATE TABLE TABLE_E071_06_01_031 ( ID INT PRIMARY KEY, A INT );
23-
24-
# statement ok
25-
# CREATE TABLE TABLE_E071_06_01_032 ( ID INT PRIMARY KEY, B FLOAT );
24+
statement ok
25+
CREATE TABLE TABLE_E071_06_01_032 ( ID INT PRIMARY KEY, B INT );
2626

27-
# SELECT A FROM TABLE_E071_06_01_031 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_01_031 UNION SELECT B FROM TABLE_E071_06_01_032 )
27+
query I
28+
SELECT A FROM TABLE_E071_06_01_031 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_01_031 UNION SELECT B FROM TABLE_E071_06_01_032 )
2829

2930
# statement ok
3031
# CREATE TABLE TABLE_E071_06_02_011 ( ID INT PRIMARY KEY, A INT );

0 commit comments

Comments
 (0)