diff --git a/src/queryplan/execute/mod.rs b/src/queryplan/execute/mod.rs index 1978da1..76308c9 100644 --- a/src/queryplan/execute/mod.rs +++ b/src/queryplan/execute/mod.rs @@ -102,6 +102,39 @@ where ::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 ::Table: 'a } }, &SExpression::Scan { .. } | + &SExpression::LeftJoin { .. } | &SExpression::TempGroupBy { .. } | &SExpression::Yield { .. } | &SExpression::If { .. } => { diff --git a/src/queryplan/mod.rs b/src/queryplan/mod.rs index 04c7d50..84090de 100644 --- a/src/queryplan/mod.rs +++ b/src/queryplan/mod.rs @@ -193,6 +193,12 @@ where ::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<::ColumnValue> + } } enum FromWhereTableOrSubquery<'a, DB: DatabaseInfo> @@ -243,6 +249,15 @@ where ::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 ::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, ::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)); - 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 => { + 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)); 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()); diff --git a/src/queryplan/sexpression.rs b/src/queryplan/sexpression.rs index f929d4c..1721208 100644 --- a/src/queryplan/sexpression.rs +++ b/src/queryplan/sexpression.rs @@ -19,6 +19,13 @@ where ::Table: 'a source_id: u32, yield_fn: Box> }, + LeftJoin { + source_id: u32, + yield_in_fn: Box>, + predicate: Box>, + yield_out_fn: Box>, + right_rows_if_none: Vec<::ColumnValue> + }, Map { source_id: u32, yield_in_fn: Box>, @@ -90,6 +97,21 @@ where ::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));