Implement LEFT JOIN!

Partially addresses issue #2
This commit is contained in:
Dan Spencer 2015-04-23 00:00:36 -06:00
parent c7baa0b0f7
commit 0c02fcd5b3
3 changed files with 117 additions and 6 deletions

View File

@ -102,6 +102,39 @@ where <Storage::Info as DatabaseInfo>::Table: 'a
Ok(())
},
&SExpression::LeftJoin { source_id, ref yield_in_fn, ref predicate, ref yield_out_fn, ref right_rows_if_none } => {
let mut one_or_more_rows = false;
try!(self.execute(yield_in_fn, &mut |row| {
let new_source = Source {
parent: source,
source_id: source_id,
source_type: SourceType::Row(row)
};
let pred_result = try!(self.resolve_value(predicate, Some(&new_source)));
if pred_result.tests_true() {
one_or_more_rows = true;
self.execute(yield_out_fn, result_cb, Some(&new_source))
} else {
Ok(())
}
}, source));
if !one_or_more_rows {
// no rows were matched
let new_source = Source {
parent: source,
source_id: source_id,
source_type: SourceType::Row(right_rows_if_none)
};
self.execute(yield_out_fn, result_cb, Some(&new_source))
} else {
Ok(())
}
},
&SExpression::Map { source_id, ref yield_in_fn, ref yield_out_fn } => {
self.execute(yield_in_fn, &mut |row| {
let new_source = Source {
@ -303,6 +336,7 @@ where <Storage::Info as DatabaseInfo>::Table: 'a
}
},
&SExpression::Scan { .. } |
&SExpression::LeftJoin { .. } |
&SExpression::TempGroupBy { .. } |
&SExpression::Yield { .. } |
&SExpression::If { .. } => {

View File

@ -193,6 +193,12 @@ where <DB as DatabaseInfo>::Table: 'a
table: FromWhereTableOrSubquery<'a, DB>,
on: SExpression<'a, DB>
},
Left {
source_id: u32,
table: SExpression<'a, DB>,
on: SExpression<'a, DB>,
right_rows_if_none: Vec<<DB as DatabaseInfo>::ColumnValue>
}
}
enum FromWhereTableOrSubquery<'a, DB: DatabaseInfo>
@ -243,6 +249,15 @@ where <DB as DatabaseInfo>::Table: 'a
}],
else_: None
})
},
FromWhereJoin::Left { source_id, table, on, right_rows_if_none } => {
SExpression::LeftJoin {
source_id: source_id,
yield_in_fn: Box::new(table),
predicate: Box::new(on),
yield_out_fn: Box::new(nested_expr),
right_rows_if_none: right_rows_if_none
}
}
}
});
@ -274,6 +289,27 @@ where <DB as DatabaseInfo>::Table: 'a
}
}
}
fn source_id(&self) -> u32 {
match self {
&FromWhereTableOrSubquery::Table { source_id, .. } => source_id,
&FromWhereTableOrSubquery::Subquery { source_id, .. } => source_id,
}
}
fn yield_all_columns(self, column_count: u32) -> SExpression<'a, DB> {
// TODO: remove column_count parameter, put that information in the type
let source_id = self.source_id();
self.into_sexpr(SExpression::Yield {
fields: (0..column_count).map(|column_offset| {
SExpression::ColumnField {
source_id: source_id,
column_offset: column_offset
}
}).collect()
})
}
}
impl<'a, 'z, DB: DatabaseInfo> QueryCompiler<'a, 'z, DB>
@ -523,19 +559,38 @@ where DB: 'a, <DB as DatabaseInfo>::Table: 'a
let j = try!(joins.into_iter().map(|join| {
let ((source_table, fromwhere_table), alias) = try!(self.ast_table_or_subquery_to(join.table, scope, groups_info));
match join.operator {
ast::JoinOperator::Inner => {
new_scope.tables.push(source_table);
new_scope.table_aliases.push(alias);
let on = try!(self.ast_expression_to_sexpression(join.on, &new_scope, groups_info));
match join.operator {
ast::JoinOperator::Inner => {
Ok(FromWhereJoin::Inner {
table: fromwhere_table,
on: on
})
},
ast::JoinOperator::Left => unimplemented!()
ast::JoinOperator::Left => {
let source_id = self.new_source_id();
let left_join_source_table = TableOrSubquery {
source_id: source_id,
out_column_names: source_table.out_column_names
};
let column_count = left_join_source_table.out_column_names.len() as u32;
new_scope.tables.push(left_join_source_table);
new_scope.table_aliases.push(alias);
let on = try!(self.ast_expression_to_sexpression(join.on, &new_scope, groups_info));
Ok(FromWhereJoin::Left {
source_id: source_id,
table: fromwhere_table.yield_all_columns(column_count),
on: on,
right_rows_if_none: (0..column_count).map(|_| ColumnValueOpsExt::null()).collect()
})
}
}
}).collect());

View File

@ -19,6 +19,13 @@ where <DB as DatabaseInfo>::Table: 'a
source_id: u32,
yield_fn: Box<SExpression<'a, DB>>
},
LeftJoin {
source_id: u32,
yield_in_fn: Box<SExpression<'a, DB>>,
predicate: Box<SExpression<'a, DB>>,
yield_out_fn: Box<SExpression<'a, DB>>,
right_rows_if_none: Vec<<DB as DatabaseInfo>::ColumnValue>
},
Map {
source_id: u32,
yield_in_fn: Box<SExpression<'a, DB>>,
@ -90,6 +97,21 @@ where <DB as DatabaseInfo>::Table: 'a
try!(yield_fn.format(f, indent + 1));
write!(f, ")")
},
&SExpression::LeftJoin { source_id, ref yield_in_fn, ref predicate, ref yield_out_fn, ref right_rows_if_none } => {
try!(writeln!(f, "(left-join :source-id {}", source_id));
try!(yield_in_fn.format(f, indent + 1));
try!(writeln!(f, ""));
try!(predicate.format(f, indent + 1));
try!(writeln!(f, ""));
try!(yield_out_fn.format(f, indent + 1));
try!(writeln!(f, ""));
write_indent!(indent + 1);
try!(write!(f, "(right-rows-if-none "));
for value in right_rows_if_none {
try!(write!(f, "{} ", value));
}
write!(f, "))")
},
&SExpression::Map { source_id, ref yield_in_fn, ref yield_out_fn } => {
try!(writeln!(f, "(map :source-id {}", source_id));
try!(yield_in_fn.format(f, indent + 1));