Skip to content

Commit

Permalink
Fix left join.
Browse files Browse the repository at this point in the history
  • Loading branch information
gtnao committed Jan 15, 2024
1 parent f6aaf77 commit 7ad2a1b
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 31 deletions.
54 changes: 51 additions & 3 deletions src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ impl ExecutorEngine {
children,
tuples: vec![],
executor_context: &self.context,
internal_left_join_statuses: vec![false; plan.children.len() - 1],
matched_statuses: vec![false; plan.children.len() - 1],
in_guard_statuses: vec![false; plan.children.len() - 1],
})
}
Plan::Insert(plan) => Executor::Insert(InsertExecutor {
Expand Down Expand Up @@ -319,8 +320,6 @@ mod tests {

let sql = "SELECT * FROM t1 LEFT JOIN t2 ON t1.c1 = t2.t1_c1";
let (rows, _) = execute(sql, &instance, txn_id)?;
println!("{:?}", rows);
println!("{:?}", rows.len());
assert_eq!(
rows,
vec![
Expand Down Expand Up @@ -355,6 +354,55 @@ mod tests {
]
);

let sql =
"SELECT * FROM t1 LEFT JOIN t2 ON t1.c1 = t2.t1_c1 LEFT JOIN t3 ON t2.c1 = t3.t2_c1";
let (rows, _) = execute(sql, &instance, txn_id)?;
assert_eq!(
rows,
vec![
vec![
Value::Integer(IntegerValue(1)),
Value::Varchar(VarcharValue("foo".to_string())),
Value::Integer(IntegerValue(1)),
Value::Integer(IntegerValue(1)),
Value::Varchar(VarcharValue("hoge".to_string())),
Value::Null,
Value::Null,
Value::Null,
],
vec![
Value::Integer(IntegerValue(1)),
Value::Varchar(VarcharValue("foo".to_string())),
Value::Integer(IntegerValue(1)),
Value::Integer(IntegerValue(2)),
Value::Varchar(VarcharValue("fuga".to_string())),
Value::Null,
Value::Null,
Value::Null,
],
vec![
Value::Integer(IntegerValue(2)),
Value::Varchar(VarcharValue("bar".to_string())),
Value::Null,
Value::Null,
Value::Null,
Value::Null,
Value::Null,
Value::Null,
],
vec![
Value::Integer(IntegerValue(3)),
Value::Varchar(VarcharValue("baz".to_string())),
Value::Integer(IntegerValue(3)),
Value::Integer(IntegerValue(3)),
Value::Varchar(VarcharValue("piyo".to_string())),
Value::Integer(IntegerValue(3)),
Value::Integer(IntegerValue(1)),
Value::Varchar(VarcharValue("aaaa".to_string())),
],
]
);

Ok(())
}
}
91 changes: 63 additions & 28 deletions src/executor/nested_loop_join_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ pub struct NestedLoopJoinExecutor<'a> {
pub tuples: Vec<Option<Tuple>>,
pub executor_context: &'a ExecutorContext,
// TODO: other implementation
pub internal_left_join_statuses: Vec<bool>,
pub matched_statuses: Vec<bool>,
pub in_guard_statuses: Vec<bool>,
}

impl NestedLoopJoinExecutor<'_> {
Expand Down Expand Up @@ -50,10 +51,10 @@ impl NestedLoopJoinExecutor<'_> {
if depth == max_depth {
// left join check
if self.plan.join_types[depth - 1] == JoinType::Left
&& self.internal_left_join_statuses[depth - 1]
&& self.in_guard_statuses[depth - 1]
{
let v = replace(&mut self.tuples[depth], None);
self.internal_left_join_statuses[depth - 1] = false;
self.in_guard_statuses[depth - 1] = false;
return Ok(Some(vec![v.unwrap()]));
}

Expand All @@ -79,22 +80,24 @@ impl NestedLoopJoinExecutor<'_> {
self.tuples[depth] = self.children[depth].next()?;

// left join
if self.tuples[depth].is_none() {
if self.plan.join_types[depth - 1] == JoinType::Left {
self.internal_left_join_statuses[depth - 1] = true;
let dummy = Tuple::temp_tuple(&vec![
Value::Null;
self.plan.children[depth]
.schema()
.columns
.len()
]);
self.tuples[depth] = Some(dummy);
}
if self.tuples[depth].is_none()
&& self.plan.join_types[depth - 1] == JoinType::Left
&& !self.matched_statuses[depth - 1]
{
self.in_guard_statuses[depth - 1] = true;
let dummy = Tuple::temp_tuple(&vec![
Value::Null;
self.plan.children[depth]
.schema()
.columns
.len()
]);
self.tuples[depth] = Some(dummy);
}

continue;
}
self.matched_statuses[depth - 1] = true;
// get and update
let v = replace(&mut self.tuples[depth], self.children[depth].next()?);
if let Some(v) = v {
Expand All @@ -105,26 +108,39 @@ impl NestedLoopJoinExecutor<'_> {
}

// root and internal depth

// none check(for left join)
let none_exist = self.tuples.iter().any(|v| v.is_none());
if none_exist {
// reset child iterator
self.children[depth + 1].init()?;
self.tuples[depth + 1] = self.children[depth + 1].next()?;
// update self iterator
self.tuples[depth] = self.children[depth].next()?;
continue;
}
// condition check(except root)
if depth != 0 {
// left join check
if self.plan.join_types[depth - 1] == JoinType::Left
&& self.in_guard_statuses[depth - 1]
{
let res = self.internal_next(depth + 1)?;
if let Some(mut res) = res {
// child iterator has result
let v = self.tuples[depth].as_ref().unwrap();
res.push(v.clone());
return Ok(Some(res));
} else {
// child iterator has no result
// reset child iterator
self.children[depth + 1].init()?;
self.matched_statuses[depth] = false;
self.tuples[depth + 1] = self.children[depth + 1].next()?;
// update self iterator
self.tuples[depth] = None;
self.in_guard_statuses[depth - 1] = false;
continue;
}
}

let condition_result = self.plan.conditions[depth - 1].as_ref().map_or_else(
|| true,
|condition| {
// for left join dummy tuple
let dummy = Tuple::temp_tuple(&vec![]);
let tuples = self
.tuples
.iter()
.map(|tuple| tuple.as_ref().unwrap())
.map(|tuple| tuple.as_ref().unwrap_or(&dummy))
.collect::<Vec<_>>();
let schemas = self
.plan
Expand All @@ -137,8 +153,26 @@ impl NestedLoopJoinExecutor<'_> {
);
if !condition_result {
self.tuples[depth] = self.children[depth].next()?;

// left join
if self.tuples[depth].is_none()
&& self.plan.join_types[depth - 1] == JoinType::Left
&& !self.matched_statuses[depth - 1]
{
self.in_guard_statuses[depth - 1] = true;
let dummy = Tuple::temp_tuple(&vec![
Value::Null;
self.plan.children[depth]
.schema()
.columns
.len()
]);
self.tuples[depth] = Some(dummy);
}

continue;
}
self.matched_statuses[depth - 1] = true;
}
let res = self.internal_next(depth + 1)?;
if let Some(mut res) = res {
Expand All @@ -150,6 +184,7 @@ impl NestedLoopJoinExecutor<'_> {
// child iterator has no result
// reset child iterator
self.children[depth + 1].init()?;
self.matched_statuses[depth] = false;
self.tuples[depth + 1] = self.children[depth + 1].next()?;
// update self iterator
self.tuples[depth] = self.children[depth].next()?;
Expand Down

0 comments on commit 7ad2a1b

Please sign in to comment.