From 7c0b71f22083b20688390cb37e728751e16e4764 Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Wed, 10 Apr 2024 15:48:09 +0800 Subject: [PATCH] feat: support more join case - reconstruct the alias mechanism to ensure correctness during Join - support Natural Join - support multiple from(to Cross Join) - when Using and Natural Join, the same columns are automatically removed (Fixme: JoinType needs to be supported to decide whether to use the columns of the left table or the right table) - `RangeDetacher::detach` removes meaningless `Result` --- src/binder/analyze.rs | 4 +- src/binder/create_index.rs | 4 +- src/binder/delete.rs | 18 +- src/binder/expr.rs | 45 +- src/binder/insert.rs | 4 +- src/binder/mod.rs | 36 +- src/binder/select.rs | 228 ++-- src/binder/update.rs | 2 +- src/catalog/column.rs | 4 + src/execution/volcano/dql/join/hash_join.rs | 5 +- src/expression/range_detacher.rs | 125 +- .../rule/normalization/pushdown_predicates.rs | 4 +- .../rule/normalization/simplification.rs | 6 +- tests/slt/crdb/join.slt | 1066 +++++++++++++++++ tests/slt/join.slt | 13 +- 15 files changed, 1380 insertions(+), 184 deletions(-) create mode 100644 tests/slt/crdb/join.slt diff --git a/src/binder/analyze.rs b/src/binder/analyze.rs index 4d8f0a02..abe3c4f0 100644 --- a/src/binder/analyze.rs +++ b/src/binder/analyze.rs @@ -12,7 +12,9 @@ impl<'a, T: Transaction> Binder<'a, T> { pub(crate) fn bind_analyze(&mut self, name: &ObjectName) -> Result { let table_name = Arc::new(lower_case_name(name)?); - let table_catalog = self.context.table_and_bind(table_name.clone(), None)?; + let table_catalog = self + .context + .table_and_bind(table_name.clone(), None, None)?; let index_metas = table_catalog.indexes.clone(); let scan_op = ScanOperator::build(table_name.clone(), table_catalog); diff --git a/src/binder/create_index.rs b/src/binder/create_index.rs index e9cdefb6..3c34edf7 100644 --- a/src/binder/create_index.rs +++ b/src/binder/create_index.rs @@ -29,7 +29,9 @@ impl<'a, T: Transaction> Binder<'a, T> { IndexType::Composite }; - let table = self.context.table_and_bind(table_name.clone(), None)?; + let table = self + .context + .table_and_bind(table_name.clone(), None, None)?; let plan = ScanOperator::build(table_name.clone(), table); let mut columns = Vec::with_capacity(exprs.len()); diff --git a/src/binder/delete.rs b/src/binder/delete.rs index c5f81b85..cb8a6b5b 100644 --- a/src/binder/delete.rs +++ b/src/binder/delete.rs @@ -5,7 +5,7 @@ use crate::planner::operator::scan::ScanOperator; use crate::planner::operator::Operator; use crate::planner::LogicalPlan; use crate::storage::Transaction; -use sqlparser::ast::{Expr, TableFactor, TableWithJoins}; +use sqlparser::ast::{Expr, TableAlias, TableFactor, TableWithJoins}; use std::sync::Arc; impl<'a, T: Transaction> Binder<'a, T> { @@ -16,8 +16,16 @@ impl<'a, T: Transaction> Binder<'a, T> { ) -> Result { if let TableFactor::Table { name, alias, .. } = &from.relation { let table_name = Arc::new(lower_case_name(name)?); + let mut table_alias = None; + let mut alias_idents = None; - let table_catalog = self.context.table_and_bind(table_name.clone(), None)?; + if let Some(TableAlias { name, columns }) = alias { + table_alias = Some(Arc::new(name.value.to_lowercase())); + alias_idents = Some(columns); + } + let table_catalog = + self.context + .table_and_bind(table_name.clone(), table_alias.clone(), None)?; let primary_key_column = table_catalog .columns() .find(|column| column.desc.is_primary) @@ -25,9 +33,9 @@ impl<'a, T: Transaction> Binder<'a, T> { .unwrap(); let mut plan = ScanOperator::build(table_name.clone(), table_catalog); - if let Some(alias) = alias { - self.context - .add_table_alias(alias.to_string(), table_name.clone()); + if let Some(alias_idents) = alias_idents { + plan = + self.bind_alias(plan, alias_idents, table_alias.unwrap(), table_name.clone())?; } if let Some(predicate) = selection { diff --git a/src/binder/expr.rs b/src/binder/expr.rs index ae9e7890..31d8f069 100644 --- a/src/binder/expr.rs +++ b/src/binder/expr.rs @@ -19,11 +19,11 @@ use crate::types::value::{DataValue, Utf8Type}; use crate::types::LogicalType; macro_rules! try_alias { - ($context:expr, $column_name:expr) => { - if let Some(expr) = $context.expr_aliases.get(&$column_name) { + ($context:expr, $full_name:expr) => { + if let Some(expr) = $context.expr_aliases.get(&$full_name) { return Ok(ScalarExpression::Alias { expr: Box::new(expr.clone()), - alias: AliasType::Name($column_name), + alias: AliasType::Name($full_name.1), }); } }; @@ -283,7 +283,7 @@ impl<'a, T: Transaction> Binder<'a, T> { idents: &[Ident], bind_table_name: Option, ) -> Result { - let (table_name, column_name) = match idents { + let full_name = match idents { [column] => (None, lower_ident(column)), [table, column] => (Some(lower_ident(table)), lower_ident(column)), _ => { @@ -296,25 +296,40 @@ impl<'a, T: Transaction> Binder<'a, T> { )) } }; - try_alias!(self.context, column_name); + try_alias!(self.context, full_name); if self.context.allow_default { - try_default!(&table_name, column_name); + try_default!(&full_name.0, full_name.1); } - if let Some(table) = table_name.or(bind_table_name) { + if let Some(table) = full_name.0.or(bind_table_name) { let table_catalog = self.context.bind_table(&table, self.parent)?; let column_catalog = table_catalog - .get_column_by_name(&column_name) - .ok_or_else(|| DatabaseError::NotFound("column", column_name))?; + .get_column_by_name(&full_name.1) + .ok_or_else(|| DatabaseError::NotFound("column", full_name.1))?; Ok(ScalarExpression::ColumnRef(column_catalog.clone())) } else { - let op = |got_column: &mut Option<&'a ColumnRef>, context: &BinderContext<'a, T>| { - for table_catalog in context.bind_table.values() { + let op = |got_column: &mut Option, context: &BinderContext<'a, T>| { + for ((_, alias, _), table_catalog) in context.bind_table.iter() { if got_column.is_some() { break; } - if let Some(column_catalog) = table_catalog.get_column_by_name(&column_name) { - *got_column = Some(column_catalog); + if let Some(alias) = alias { + *got_column = self.context.expr_aliases.iter().find_map( + |((alias_table, alias_column), expr)| { + matches!( + alias_table + .as_ref() + .map(|table_name| table_name == alias.as_ref() + && alias_column == &full_name.1), + Some(true) + ) + .then(|| expr.clone()) + }, + ); + } else if let Some(column_catalog) = + table_catalog.get_column_by_name(&full_name.1) + { + *got_column = Some(ScalarExpression::ColumnRef(column_catalog.clone())); } } }; @@ -325,9 +340,7 @@ impl<'a, T: Transaction> Binder<'a, T> { if let Some(parent) = self.parent { op(&mut got_column, &parent.context); } - let column_catalog = - got_column.ok_or_else(|| DatabaseError::NotFound("column", column_name))?; - Ok(ScalarExpression::ColumnRef(column_catalog.clone())) + Ok(got_column.ok_or_else(|| DatabaseError::NotFound("column", full_name.1))?) } } diff --git a/src/binder/insert.rs b/src/binder/insert.rs index 699a2aa5..22d603a3 100644 --- a/src/binder/insert.rs +++ b/src/binder/insert.rs @@ -24,7 +24,9 @@ impl<'a, T: Transaction> Binder<'a, T> { self.context.allow_default = true; let table_name = Arc::new(lower_case_name(name)?); - let table = self.context.table_and_bind(table_name.clone(), None)?; + let table = self + .context + .table_and_bind(table_name.clone(), None, None)?; let mut _schema_ref = None; let values_len = expr_rows[0].len(); diff --git a/src/binder/mod.rs b/src/binder/mod.rs index d3221421..2741c1e3 100644 --- a/src/binder/mod.rs +++ b/src/binder/mod.rs @@ -16,6 +16,7 @@ mod show; mod truncate; mod update; +use ahash::HashSet; use sqlparser::ast::{Ident, ObjectName, ObjectType, SetExpr, Statement}; use std::collections::{BTreeMap, HashMap}; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -85,13 +86,16 @@ pub struct BinderContext<'a, T: Transaction> { pub(crate) functions: &'a Functions, pub(crate) transaction: &'a T, // Tips: When there are multiple tables and Wildcard, use BTreeMap to ensure that the order of the output tables is certain. - pub(crate) bind_table: BTreeMap<(TableName, Option), &'a TableCatalog>, + pub(crate) bind_table: + BTreeMap<(TableName, Option, Option), &'a TableCatalog>, // alias - expr_aliases: HashMap, - table_aliases: HashMap, + expr_aliases: BTreeMap<(Option, String), ScalarExpression>, + table_aliases: HashMap, // agg group_by_exprs: Vec, pub(crate) agg_calls: Vec, + // join + using: HashSet, bind_step: QueryBindStep, sub_queries: HashMap>, @@ -114,6 +118,7 @@ impl<'a, T: Transaction> BinderContext<'a, T> { table_aliases: Default::default(), group_by_exprs: vec![], agg_calls: Default::default(), + using: Default::default(), bind_step: QueryBindStep::From, sub_queries: Default::default(), temp_table_id, @@ -162,6 +167,7 @@ impl<'a, T: Transaction> BinderContext<'a, T> { pub fn table_and_bind( &mut self, table_name: TableName, + alias: Option, join_type: Option, ) -> Result<&TableCatalog, DatabaseError> { let table = if let Some(real_name) = self.table_aliases.get(table_name.as_ref()) { @@ -172,7 +178,7 @@ impl<'a, T: Transaction> BinderContext<'a, T> { .ok_or(DatabaseError::TableNotFound)?; self.bind_table - .insert((table_name.clone(), join_type), table); + .insert((table_name.clone(), alias, join_type), table); Ok(table) } @@ -183,9 +189,10 @@ impl<'a, T: Transaction> BinderContext<'a, T> { table_name: &str, parent: Option<&'a Binder<'a, T>>, ) -> Result<&TableCatalog, DatabaseError> { - let default_name = Arc::new(table_name.to_owned()); - let real_name = self.table_aliases.get(table_name).unwrap_or(&default_name); - if let Some(table_catalog) = self.bind_table.iter().find(|((t, _), _)| t == real_name) { + if let Some(table_catalog) = self.bind_table.iter().find(|((t, alias, _), _)| { + t.as_str() == table_name + || matches!(alias.as_ref().map(|a| a.as_str() == table_name), Some(true)) + }) { Ok(table_catalog.1) } else if let Some(binder) = parent { binder.context.bind_table(table_name, binder.parent) @@ -202,11 +209,20 @@ impl<'a, T: Transaction> BinderContext<'a, T> { } } - pub fn add_alias(&mut self, alias: String, expr: ScalarExpression) { - self.expr_aliases.insert(alias, expr); + pub fn add_using(&mut self, name: String) { + self.using.insert(name); } - pub fn add_table_alias(&mut self, alias: String, table: TableName) { + pub fn add_alias( + &mut self, + alias_table: Option, + alias_column: String, + expr: ScalarExpression, + ) { + self.expr_aliases.insert((alias_table, alias_column), expr); + } + + pub fn add_table_alias(&mut self, alias: TableName, table: TableName) { self.table_aliases.insert(alias.clone(), table.clone()); } diff --git a/src/binder/select.rs b/src/binder/select.rs index d794bb03..3c3a1d3e 100644 --- a/src/binder/select.rs +++ b/src/binder/select.rs @@ -1,5 +1,5 @@ use std::borrow::Borrow; -use std::collections::HashMap; +use std::collections::HashSet; use std::sync::Arc; use crate::{ @@ -14,7 +14,7 @@ use crate::{ types::value::DataValue, }; -use super::{lower_case_name, lower_ident, Binder, QueryBindStep, SubQueryType}; +use super::{lower_case_name, lower_ident, Binder, BinderContext, QueryBindStep, SubQueryType}; use crate::catalog::{ColumnCatalog, ColumnSummary, TableName}; use crate::errors::DatabaseError; @@ -71,8 +71,23 @@ impl<'a, T: Transaction> Binder<'a, T> { select: &Select, orderby: &[OrderByExpr], ) -> Result { - let mut plan = self.bind_table_ref(&select.from)?; - + let mut plan = if select.from.is_empty() { + LogicalPlan::new(Operator::Dummy, vec![]) + } else { + let mut plan = self.bind_table_ref(&select.from[0])?; + + if select.from.len() > 1 { + for from in select.from[1..].iter() { + plan = LJoinOperator::build( + plan, + self.bind_table_ref(from)?, + JoinCondition::None, + JoinType::Cross, + ) + } + } + plan + }; // Resolve scalar function call. // TODO support SRF(Set-Returning Function). @@ -224,16 +239,11 @@ impl<'a, T: Transaction> Binder<'a, T> { pub(crate) fn bind_table_ref( &mut self, - from: &[TableWithJoins], + from: &TableWithJoins, ) -> Result { self.context.step(QueryBindStep::From); - assert!(from.len() < 2, "not support yet."); - if from.is_empty() { - return Ok(LogicalPlan::new(Operator::Dummy, vec![])); - } - - let TableWithJoins { relation, joins } = &from[0]; + let TableWithJoins { relation, joins } = from; let mut plan = self.bind_single_table_ref(relation, None)?; for join in joins { @@ -256,7 +266,7 @@ impl<'a, T: Transaction> Binder<'a, T> { TableFactor::Derived { subquery, alias, .. } => { - let plan = self.bind_query(subquery)?; + let mut plan = self.bind_query(subquery)?; let mut tables = plan.referenced_table(); if let Some(TableAlias { @@ -269,11 +279,8 @@ impl<'a, T: Transaction> Binder<'a, T> { } let table_alias = Arc::new(name.value.to_lowercase()); - self.register_alias( - alias_column, - table_alias.to_string(), - tables.pop().unwrap(), - )?; + plan = + self.bind_alias(plan, alias_column, table_alias, tables.pop().unwrap())?; } plan } @@ -283,35 +290,52 @@ impl<'a, T: Transaction> Binder<'a, T> { Ok(plan) } - fn register_alias( + pub(crate) fn bind_alias( &mut self, + mut plan: LogicalPlan, alias_column: &[Ident], - table_alias: String, + table_alias: TableName, table_name: TableName, - ) -> Result<(), DatabaseError> { - if !alias_column.is_empty() { - let table = self - .context - .table(table_name.clone()) - .ok_or(DatabaseError::TableNotFound)?; - - if alias_column.len() != table.columns_len() { - return Err(DatabaseError::MisMatch("alias", "columns")); - } - let aliases_with_columns = alias_column + ) -> Result { + let input_schema = plan.output_schema(); + if !alias_column.is_empty() && alias_column.len() != input_schema.len() { + return Err(DatabaseError::MisMatch("alias", "columns")); + } + let aliases_with_columns = if alias_column.is_empty() { + input_schema + .iter() + .cloned() + .map(|column| (column.name().to_string(), column)) + .collect_vec() + } else { + alias_column .iter() .map(lower_ident) - .zip(table.columns().cloned()) - .collect_vec(); - - for (alias, column) in aliases_with_columns { - self.context - .add_alias(alias, ScalarExpression::ColumnRef(column)); - } + .zip(input_schema.iter().cloned()) + .collect_vec() + }; + let mut alias_exprs = Vec::with_capacity(aliases_with_columns.len()); + + for (alias, column) in aliases_with_columns { + let mut alias_column = ColumnCatalog::clone(&column); + alias_column.set_name(alias.clone()); + alias_column.set_table_name(table_alias.clone()); + + let alias_column_expr = ScalarExpression::Alias { + expr: Box::new(ScalarExpression::ColumnRef(column)), + alias: AliasType::Expr(Box::new(ScalarExpression::ColumnRef(Arc::new( + alias_column, + )))), + }; + self.context.add_alias( + Some(table_alias.to_string()), + alias, + alias_column_expr.clone(), + ); + alias_exprs.push(alias_column_expr); } self.context.add_table_alias(table_alias, table_name); - - Ok(()) + self.bind_project(plan, alias_exprs) } pub(crate) fn _bind_single_table_ref( @@ -321,12 +345,21 @@ impl<'a, T: Transaction> Binder<'a, T> { alias: Option<&TableAlias>, ) -> Result { let table_name = Arc::new(table.to_string()); - - let table_catalog = self.context.table_and_bind(table_name.clone(), join_type)?; - let scan_op = ScanOperator::build(table_name.clone(), table_catalog); + let mut table_alias = None; + let mut alias_idents = None; if let Some(TableAlias { name, columns }) = alias { - self.register_alias(columns, lower_ident(name), table_name.clone())?; + table_alias = Some(Arc::new(name.value.to_lowercase())); + alias_idents = Some(columns); + } + + let table_catalog = + self.context + .table_and_bind(table_name.clone(), table_alias.clone(), join_type)?; + let mut scan_op = ScanOperator::build(table_name.clone(), table_catalog); + + if let Some(idents) = alias_idents { + scan_op = self.bind_alias(scan_op, idents, table_alias.unwrap(), table_name.clone())?; } Ok(scan_op) @@ -352,7 +385,8 @@ impl<'a, T: Transaction> Binder<'a, T> { let expr = self.bind_expr(expr)?; let alias_name = alias.to_string(); - self.context.add_alias(alias_name.clone(), expr.clone()); + self.context + .add_alias(None, alias_name.clone(), expr.clone()); select_items.push(ScalarExpression::Alias { expr: Box::new(expr), @@ -363,14 +397,21 @@ impl<'a, T: Transaction> Binder<'a, T> { if let Operator::Project(op) = &plan.operator { return Ok(op.exprs.clone()); } - for (table_name, _) in self.context.bind_table.keys() { - self.bind_table_column_refs(&mut select_items, table_name.clone())?; + let mut join_used = HashSet::with_capacity(self.context.using.len()); + + for (table_name, alias, _) in self.context.bind_table.keys() { + self.bind_table_column_refs( + &mut select_items, + alias.as_ref().unwrap_or(table_name).clone(), + &mut join_used, + )?; } } SelectItem::QualifiedWildcard(table_name, _) => { self.bind_table_column_refs( &mut select_items, Arc::new(lower_case_name(table_name)?), + &mut HashSet::with_capacity(self.context.using.len()), )?; } }; @@ -383,32 +424,46 @@ impl<'a, T: Transaction> Binder<'a, T> { &self, exprs: &mut Vec, table_name: TableName, + join_used: &mut HashSet, ) -> Result<(), DatabaseError> { + let mut is_bound_alias = false; + + let fn_used = |column_name: &str, context: &BinderContext, join_used: &HashSet<_>| { + context.using.contains(column_name) && join_used.contains(column_name) + }; + for (_, alias_expr) in self.context.expr_aliases.iter().filter(|(_, expr)| { + if let ScalarExpression::ColumnRef(col) = expr.unpack_alias_ref() { + let column_name = col.name(); + + if Some(&table_name) == col.table_name() + && !fn_used(column_name, &self.context, join_used) + { + join_used.insert(column_name.to_string()); + return true; + } + } + false + }) { + is_bound_alias = true; + exprs.push(alias_expr.clone()); + } + if is_bound_alias { + return Ok(()); + } + let table = self .context .table(table_name.clone()) .ok_or(DatabaseError::TableNotFound)?; - let alias_map: HashMap<&ScalarExpression, &String> = self - .context - .expr_aliases - .iter() - .filter(|(_, expr)| { - if let ScalarExpression::ColumnRef(col) = expr { - return Some(&table_name) == col.table_name(); - } - false - }) - .map(|(alias, expr)| (expr, alias)) - .collect(); for column in table.columns() { - let mut expr = ScalarExpression::ColumnRef(column.clone()); + let column_name = column.name(); - if let Some(alias_expr) = alias_map.get(&expr) { - expr = ScalarExpression::Alias { - expr: Box::new(expr), - alias: AliasType::Name(alias_expr.to_string()), - } + if fn_used(column_name, &self.context, join_used) { + continue; } + let expr = ScalarExpression::ColumnRef(column.clone()); + + join_used.insert(column_name.to_string()); exprs.push(expr); } Ok(()) @@ -597,7 +652,7 @@ impl<'a, T: Transaction> Binder<'a, T> { let mut left_table_force_nullable = false; let mut left_table = None; - for ((_, join_option), table) in bind_tables { + for ((_, _, join_option), table) in bind_tables { if let Some(join_type) = join_option { let (left_force_nullable, right_force_nullable) = joins_nullable(join_type); table_force_nullable.push((table, right_force_nullable)); @@ -626,10 +681,10 @@ impl<'a, T: Transaction> Binder<'a, T> { } } - fn bind_join_constraint( + fn bind_join_constraint<'b>( &mut self, - left_schema: &SchemaRef, - right_schema: &SchemaRef, + left_schema: &'b SchemaRef, + right_schema: &'b SchemaRef, constraint: &JoinConstraint, ) -> Result { match constraint { @@ -665,23 +720,24 @@ impl<'a, T: Transaction> Binder<'a, T> { }) } JoinConstraint::Using(idents) => { - let mut on_keys: Vec<(ScalarExpression, ScalarExpression)> = vec![]; - let fn_column = |schema: &Schema, ident: &Ident| { + let mut on_keys: Vec<(ScalarExpression, ScalarExpression)> = Vec::new(); + let fn_column = |schema: &Schema, name: &str| { schema .iter() - .find(|column| column.name() == lower_ident(ident)) + .find(|column| column.name() == name) .map(|column| ScalarExpression::ColumnRef(column.clone())) }; - for ident in idents { + let name = lower_ident(ident); if let (Some(left_column), Some(right_column)) = ( - fn_column(left_schema, ident), - fn_column(right_schema, ident), + fn_column(left_schema, &name), + fn_column(right_schema, &name), ) { on_keys.push((left_column, right_column)); } else { return Err(DatabaseError::InvalidColumn("not found column".to_string()))?; } + self.context.add_using(name); } Ok(JoinCondition::On { on: on_keys, @@ -689,7 +745,29 @@ impl<'a, T: Transaction> Binder<'a, T> { }) } JoinConstraint::None => Ok(JoinCondition::None), - _ => unimplemented!("not supported join constraint {:?}", constraint), + JoinConstraint::Natural => { + let fn_names = |schema: &'b Schema| -> HashSet<&'b str> { + schema.iter().map(|column| column.name()).collect() + }; + let mut on_keys: Vec<(ScalarExpression, ScalarExpression)> = Vec::new(); + + for name in fn_names(left_schema).intersection(&fn_names(right_schema)) { + self.context.add_using(name.to_string()); + if let (Some(left_column), Some(right_column)) = ( + left_schema.iter().find(|column| column.name() == *name), + right_schema.iter().find(|column| column.name() == *name), + ) { + on_keys.push(( + ScalarExpression::ColumnRef(left_column.clone()), + ScalarExpression::ColumnRef(right_column.clone()), + )); + } + } + Ok(JoinCondition::On { + on: on_keys, + filter: None, + }) + } } } diff --git a/src/binder/update.rs b/src/binder/update.rs index 33253d1a..4b8a2ec4 100644 --- a/src/binder/update.rs +++ b/src/binder/update.rs @@ -22,7 +22,7 @@ impl<'a, T: Transaction> Binder<'a, T> { if let TableFactor::Table { name, .. } = &to.relation { let table_name = Arc::new(lower_case_name(name)?); - let mut plan = self.bind_table_ref(slice::from_ref(to))?; + let mut plan = self.bind_table_ref(to)?; if let Some(predicate) = selection { plan = self.bind_where(plan, predicate)?; diff --git a/src/catalog/column.rs b/src/catalog/column.rs index 258536fd..fe3e4d68 100644 --- a/src/catalog/column.rs +++ b/src/catalog/column.rs @@ -83,6 +83,10 @@ impl ColumnCatalog { self.summary.table_name.as_ref() } + pub fn set_name(&mut self, name: String) { + self.summary.name = name; + } + pub fn set_table_name(&mut self, table_name: TableName) { self.summary.table_name = Some(table_name); } diff --git a/src/execution/volcano/dql/join/hash_join.rs b/src/execution/volcano/dql/join/hash_join.rs index 1d5b1435..46d52808 100644 --- a/src/execution/volcano/dql/join/hash_join.rs +++ b/src/execution/volcano/dql/join/hash_join.rs @@ -142,8 +142,11 @@ impl HashJoinStatus { let right_cols_len = tuple.values.len(); let values = Self::eval_keys(on_right_keys, &tuple, &full_schema_ref[*left_schema_len..])?; + let has_null = values.iter().any(|value| value.is_null()); - if let Some((tuples, is_used, is_filtered)) = build_map.get_mut(&values) { + if let (false, Some((tuples, is_used, is_filtered))) = + (has_null, build_map.get_mut(&values)) + { let mut bits_option = None; *is_used = true; diff --git a/src/expression/range_detacher.rs b/src/expression/range_detacher.rs index 10039e0a..dd23acf7 100644 --- a/src/expression/range_detacher.rs +++ b/src/expression/range_detacher.rs @@ -1,5 +1,4 @@ use crate::catalog::ColumnRef; -use crate::errors::DatabaseError; use crate::expression::{BinaryOperator, ScalarExpression}; use crate::types::value::{DataValue, ValueRef, NULL_VALUE}; use crate::types::ColumnId; @@ -187,35 +186,31 @@ impl<'a> RangeDetacher<'a> { } } - pub(crate) fn detach( - &mut self, - expr: &ScalarExpression, - ) -> Result, DatabaseError> { + pub(crate) fn detach(&mut self, expr: &ScalarExpression) -> Option { match expr { ScalarExpression::Binary { left_expr, right_expr, op, .. - } => match (self.detach(left_expr)?, self.detach(right_expr)?) { + } => match (self.detach(left_expr), self.detach(right_expr)) { (Some(left_binary), Some(right_binary)) => { - Ok(Self::merge_binary(*op, left_binary, right_binary)) + Self::merge_binary(*op, left_binary, right_binary) } (None, None) => { if let (Some(col), Some(val)) = (left_expr.unpack_col(false), right_expr.unpack_val()) { - return Ok(self.new_range(*op, col, val, false)); + return self.new_range(*op, col, val, false); } else if let (Some(val), Some(col)) = (left_expr.unpack_val(), right_expr.unpack_col(false)) { - return Ok(self.new_range(*op, col, val, true)); + return self.new_range(*op, col, val, true); } - Ok(None) + None } - (Some(binary), None) => Ok(self.check_or(op, binary)), - (None, Some(binary)) => Ok(self.check_or(op, binary)), + (Some(binary), None) | (None, Some(binary)) => self.check_or(op, binary), }, ScalarExpression::Alias { expr, .. } | ScalarExpression::TypeCast { expr, .. } @@ -228,16 +223,16 @@ impl<'a> RangeDetacher<'a> { ScalarExpression::ColumnRef(column) => { if let (Some(col_id), Some(col_table)) = (column.id(), column.table_name()) { if &col_id == self.column_id && col_table.as_str() == self.table_name { - return Ok(if *negated { + return if *negated { // Range::NotEq(NULL_VALUE.clone()) None } else { Some(Range::Eq(NULL_VALUE.clone())) - }); + }; } } - Ok(None) + None } ScalarExpression::Constant(_) | ScalarExpression::Alias { .. } @@ -260,7 +255,7 @@ impl<'a> RangeDetacher<'a> { | ScalarExpression::Reference { .. } | ScalarExpression::Empty => unreachable!(), }, - ScalarExpression::Constant(_) | ScalarExpression::ColumnRef(_) => Ok(None), + ScalarExpression::Constant(_) | ScalarExpression::ColumnRef(_) => None, // FIXME: support [RangeDetacher::_detach] ScalarExpression::Tuple(_) | ScalarExpression::AggCall { .. } @@ -269,7 +264,7 @@ impl<'a> RangeDetacher<'a> { | ScalarExpression::IfNull { .. } | ScalarExpression::NullIf { .. } | ScalarExpression::Coalesce { .. } - | ScalarExpression::CaseWhen { .. } => Ok(None), + | ScalarExpression::CaseWhen { .. } => None, ScalarExpression::Reference { .. } | ScalarExpression::Empty => unreachable!(), } } @@ -817,21 +812,21 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 = 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 = 1 => {}", range); assert_eq!(range, Range::Eq(Arc::new(DataValue::Int32(Some(1))))) } { let plan = select_sql_run("select * from t1 where c1 != 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?; + let range = RangeDetacher::new("t1", &0).detach(&op.predicate); println!("c1 != 1 => {:#?}", range); assert_eq!(range, None) } { let plan = select_sql_run("select * from t1 where c1 > 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 > 1 => c1: {}", range); assert_eq!( range, @@ -844,7 +839,7 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 >= 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 >= 1 => c1: {}", range); assert_eq!( range, @@ -857,7 +852,7 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 < 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 < 1 => c1: {}", range); assert_eq!( range, @@ -870,7 +865,7 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 <= 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 <= 1 => c1: {}", range); assert_eq!( range, @@ -883,7 +878,7 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 < 1 and c1 >= 0").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 < 1 and c1 >= 0 => c1: {}", range); assert_eq!( range, @@ -896,7 +891,7 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 < 1 or c1 >= 0").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 < 1 or c1 >= 0 => c1: {}", range); assert_eq!( range, @@ -910,14 +905,14 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 = 1 and c1 = 0").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 = 1 and c1 = 0 => c1: {}", range); assert_eq!(range, Range::Dummy) } { let plan = select_sql_run("select * from t1 where c1 = 1 or c1 = 0").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 = 1 or c1 = 0 => c1: {}", range); assert_eq!( range, @@ -930,14 +925,14 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 = 1 and c1 = 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 = 1 and c1 = 1 => c1: {}", range); assert_eq!(range, Range::Eq(Arc::new(DataValue::Int32(Some(1))))) } { let plan = select_sql_run("select * from t1 where c1 = 1 or c1 = 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 = 1 or c1 = 1 => c1: {}", range); assert_eq!(range, Range::Eq(Arc::new(DataValue::Int32(Some(1))))) } @@ -945,21 +940,21 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 > 1 and c1 = 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 > 1 and c1 = 1 => c1: {}", range); assert_eq!(range, Range::Dummy) } { let plan = select_sql_run("select * from t1 where c1 >= 1 and c1 = 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 >= 1 and c1 = 1 => c1: {}", range); assert_eq!(range, Range::Eq(Arc::new(DataValue::Int32(Some(1))))) } { let plan = select_sql_run("select * from t1 where c1 > 1 or c1 = 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 > 1 or c1 = 1 => c1: {}", range); assert_eq!( range, @@ -972,7 +967,7 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 >= 1 or c1 = 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 >= 1 or c1 = 1 => c1: {}", range); assert_eq!( range, @@ -989,7 +984,7 @@ mod test { ) .await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!( "(c1 > 0 and c1 < 3) and (c1 > 1 and c1 < 4) => c1: {}", range @@ -1007,7 +1002,7 @@ mod test { select_sql_run("select * from t1 where (c1 > 0 and c1 < 3) or (c1 > 1 and c1 < 4)") .await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!( "(c1 > 0 and c1 < 3) or (c1 > 1 and c1 < 4) => c1: {}", range @@ -1027,7 +1022,7 @@ mod test { ) .await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!( "((c1 > 0 and c1 < 3) and (c1 > 1 and c1 < 4)) and c1 = 0 => c1: {}", range @@ -1040,7 +1035,7 @@ mod test { ) .await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!( "((c1 > 0 and c1 < 3) or (c1 > 1 and c1 < 4)) and c1 = 0 => c1: {}", range @@ -1053,7 +1048,7 @@ mod test { ) .await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!( "((c1 > 0 and c1 < 3) and (c1 > 1 and c1 < 4)) or c1 = 0 => c1: {}", range @@ -1075,7 +1070,7 @@ mod test { ) .await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!( "((c1 > 0 and c1 < 3) or (c1 > 1 and c1 < 4)) or c1 = 0 => c1: {}", range @@ -1092,14 +1087,14 @@ mod test { { let plan = select_sql_run("select * from t1 where (((c1 > 0 and c1 < 3) and (c1 > 1 and c1 < 4)) and c1 = 0) and (c1 >= 0 and c1 <= 2)").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("(((c1 > 0 and c1 < 3) and (c1 > 1 and c1 < 4)) and c1 = 0) and (c1 >= 0 and c1 <= 2) => c1: {}", range); assert_eq!(range, Range::Dummy) } { let plan = select_sql_run("select * from t1 where (((c1 > 0 and c1 < 3) and (c1 > 1 and c1 < 4)) and c1 = 0) or (c1 >= 0 and c1 <= 2)").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("(((c1 > 0 and c1 < 3) and (c1 > 1 and c1 < 4)) and c1 = 0) or (c1 >= 0 and c1 <= 2) => c1: {}", range); assert_eq!( range, @@ -1113,7 +1108,7 @@ mod test { { let plan = select_sql_run("select * from t1 where ((c1 < 2 and c1 > 0) or (c1 < 6 and c1 > 4)) and ((c1 < 3 and c1 > 1) or (c1 < 7 and c1 > 5))").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("((c1 < 2 and c1 > 0) or (c1 < 6 and c1 > 4)) and ((c1 < 3 and c1 > 1) or (c1 < 7 and c1 > 5)) => c1: {}", range); assert_eq!( range, @@ -1132,7 +1127,7 @@ mod test { { let plan = select_sql_run("select * from t1 where ((c1 < 2 and c1 > 0) or (c1 < 6 and c1 > 4)) or ((c1 < 3 and c1 > 1) or (c1 < 7 and c1 > 5))").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("((c1 < 2 and c1 > 0) or (c1 < 6 and c1 > 4)) or ((c1 < 3 and c1 > 1) or (c1 < 7 and c1 > 5)) => c1: {}", range); assert_eq!( range, @@ -1152,7 +1147,7 @@ mod test { { let plan = select_sql_run("select * from t1 where true").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?; + let range = RangeDetacher::new("t1", &0).detach(&op.predicate); println!("empty => c1: {:#?}", range); assert_eq!(range, None) } @@ -1160,21 +1155,21 @@ mod test { { let plan = select_sql_run("select * from t1 where c2 = 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?; + let range = RangeDetacher::new("t1", &0).detach(&op.predicate); println!("c2 = 1 => c1: {:#?}", range); assert_eq!(range, None) } { let plan = select_sql_run("select * from t1 where c1 > 1 or c2 > 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?; + let range = RangeDetacher::new("t1", &0).detach(&op.predicate); println!("c1 > 1 or c2 > 1 => c1: {:#?}", range); assert_eq!(range, None) } { let plan = select_sql_run("select * from t1 where c1 > c2 or c2 > 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?; + let range = RangeDetacher::new("t1", &0).detach(&op.predicate); println!("c1 > c2 or c2 > 1 => c1: {:#?}", range); assert_eq!(range, None) } @@ -1185,7 +1180,7 @@ mod test { ) .await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!( "c1 = 5 or (c1 > 5 and (c1 > 6 or c1 < 8) and c1 < 12) => c1: {}", range @@ -1205,7 +1200,7 @@ mod test { ) .await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!( "((c2 >= -8 and -4 >= c1) or (c1 >= 0 and 5 > c2)) and ((c2 > 0 and c1 <= 1) or (c1 > -8 and c2 < -6)) => c1: {}", range @@ -1235,14 +1230,14 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 = null").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 = null => c1: {}", range); assert_eq!(range, Range::Eq(Arc::new(DataValue::Int32(None)))) } { let plan = select_sql_run("select * from t1 where c1 = null or c1 = 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 = null or c1 = 1 => c1: {}", range); assert_eq!( range, @@ -1255,7 +1250,7 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 = null or c1 < 5").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 = null or c1 < 5 => c1: {}", range); assert_eq!( range, @@ -1269,7 +1264,7 @@ mod test { let plan = select_sql_run("select * from t1 where c1 = null or (c1 > 1 and c1 < 5)").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 = null or (c1 > 1 and c1 < 5) => c1: {}", range); assert_eq!( range, @@ -1285,7 +1280,7 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 = null and c1 < 5").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 = null and c1 < 5 => c1: {}", range); assert_eq!(range, Range::Eq(Arc::new(DataValue::Int32(None)))) } @@ -1293,7 +1288,7 @@ mod test { let plan = select_sql_run("select * from t1 where c1 = null and (c1 > 1 and c1 < 5)").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 = null and (c1 > 1 and c1 < 5) => c1: {}", range); assert_eq!(range, Range::Dummy) } @@ -1301,21 +1296,21 @@ mod test { { let plan = select_sql_run("select * from t1 where c1 != null").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?; + let range = RangeDetacher::new("t1", &0).detach(&op.predicate); println!("c1 != null => c1: {:#?}", range); assert_eq!(range, None) } { let plan = select_sql_run("select * from t1 where c1 = null or c1 != 1").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?; + let range = RangeDetacher::new("t1", &0).detach(&op.predicate); println!("c1 = null or c1 != 1 => c1: {:#?}", range); assert_eq!(range, None) } { let plan = select_sql_run("select * from t1 where c1 != null or c1 < 5").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?; + let range = RangeDetacher::new("t1", &0).detach(&op.predicate); println!("c1 != null or c1 < 5 => c1: {:#?}", range); assert_eq!(range, None) } @@ -1323,14 +1318,14 @@ mod test { let plan = select_sql_run("select * from t1 where c1 != null or (c1 > 1 and c1 < 5)").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?; + let range = RangeDetacher::new("t1", &0).detach(&op.predicate); println!("c1 != null or (c1 > 1 and c1 < 5) => c1: {:#?}", range); assert_eq!(range, None) } { let plan = select_sql_run("select * from t1 where c1 != null and c1 < 5").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 != null and c1 < 5 => c1: {}", range); assert_eq!( range, @@ -1344,7 +1339,7 @@ mod test { let plan = select_sql_run("select * from t1 where c1 != null and (c1 > 1 and c1 < 5)").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("c1 != null and (c1 > 1 and c1 < 5) => c1: {}", range); assert_eq!( range, @@ -1357,7 +1352,7 @@ mod test { { let plan = select_sql_run("select * from t1 where (c1 = null or (c1 < 2 and c1 > 0) or (c1 < 6 and c1 > 4)) or ((c1 < 3 and c1 > 1) or (c1 < 7 and c1 > 5))").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("(c1 = null or (c1 < 2 and c1 > 0) or (c1 < 6 and c1 > 4)) or ((c1 < 3 and c1 > 1) or (c1 < 7 and c1 > 5)) => c1: {}", range); assert_eq!( range, @@ -1377,7 +1372,7 @@ mod test { { let plan = select_sql_run("select * from t1 where ((c1 < 2 and c1 > 0) or (c1 < 6 and c1 > 4)) or (c1 = null or (c1 < 3 and c1 > 1) or (c1 < 7 and c1 > 5))").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("((c1 < 2 and c1 > 0) or (c1 < 6 and c1 > 4)) or (c1 = null or (c1 < 3 and c1 > 1) or (c1 < 7 and c1 > 5)) => c1: {}", range); assert_eq!( range, @@ -1397,7 +1392,7 @@ mod test { { let plan = select_sql_run("select * from t1 where (c1 = null or (c1 < 2 and c1 > 0) or (c1 < 6 and c1 > 4)) and ((c1 < 3 and c1 > 1) or (c1 < 7 and c1 > 5))").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("(c1 = null or (c1 < 2 and c1 > 0) or (c1 < 6 and c1 > 4)) and ((c1 < 3 and c1 > 1) or (c1 < 7 and c1 > 5)) => c1: {}", range); assert_eq!( range, @@ -1416,7 +1411,7 @@ mod test { { let plan = select_sql_run("select * from t1 where ((c1 < 2 and c1 > 0) or (c1 < 6 and c1 > 4)) and (c1 = null or (c1 < 3 and c1 > 1) or (c1 < 7 and c1 > 5))").await?; let op = plan_filter(plan)?.unwrap(); - let range = RangeDetacher::new("t1", &0).detach(&op.predicate)?.unwrap(); + let range = RangeDetacher::new("t1", &0).detach(&op.predicate).unwrap(); println!("((c1 < 2 and c1 > 0) or (c1 < 6 and c1 > 4)) and (c1 = null or (c1 < 3 and c1 > 1) or (c1 < 7 and c1 > 5)) => c1: {}", range); assert_eq!( range, diff --git a/src/optimizer/rule/normalization/pushdown_predicates.rs b/src/optimizer/rule/normalization/pushdown_predicates.rs index f55b07f0..10651938 100644 --- a/src/optimizer/rule/normalization/pushdown_predicates.rs +++ b/src/optimizer/rule/normalization/pushdown_predicates.rs @@ -227,7 +227,7 @@ impl NormalizationRule for PushPredicateIntoScan { *range = match meta.ty { IndexType::PrimaryKey | IndexType::Unique | IndexType::Normal => { RangeDetacher::new(meta.table_name.as_str(), &meta.column_ids[0]) - .detach(&op.predicate)? + .detach(&op.predicate) } IndexType::Composite => { let mut res = None; @@ -236,7 +236,7 @@ impl NormalizationRule for PushPredicateIntoScan { for column_id in meta.column_ids.iter() { if let Some(range) = RangeDetacher::new(meta.table_name.as_str(), column_id) - .detach(&op.predicate)? + .detach(&op.predicate) { if range.only_eq() { eq_ranges.push(range); diff --git a/src/optimizer/rule/normalization/simplification.rs b/src/optimizer/rule/normalization/simplification.rs index 67ed9f7a..1c4d431f 100644 --- a/src/optimizer/rule/normalization/simplification.rs +++ b/src/optimizer/rule/normalization/simplification.rs @@ -157,7 +157,7 @@ mod test { } if let Operator::Filter(filter_op) = best_plan.childrens[0].clone().operator { let range = RangeDetacher::new("t1", &0) - .detach(&filter_op.predicate)? + .detach(&filter_op.predicate) .unwrap(); assert_eq!( range, @@ -206,7 +206,7 @@ mod test { ) .find_best::(None)?; if let Operator::Filter(filter_op) = best_plan.childrens[0].clone().operator { - Ok(RangeDetacher::new("t1", &0).detach(&filter_op.predicate)?) + Ok(RangeDetacher::new("t1", &0).detach(&filter_op.predicate)) } else { Ok(None) } @@ -317,7 +317,7 @@ mod test { ) .find_best::(None)?; if let Operator::Filter(filter_op) = best_plan.childrens[0].clone().operator { - Ok(RangeDetacher::new("t1", &column_id).detach(&filter_op.predicate)?) + Ok(RangeDetacher::new("t1", &column_id).detach(&filter_op.predicate)) } else { Ok(None) } diff --git a/tests/slt/crdb/join.slt b/tests/slt/crdb/join.slt new file mode 100644 index 00000000..8eb22c60 --- /dev/null +++ b/tests/slt/crdb/join.slt @@ -0,0 +1,1066 @@ +statement ok +drop table if exists onecolumn + +statement ok +CREATE TABLE onecolumn (id INT PRIMARY KEY, x INT NULL) + +statement ok +INSERT INTO onecolumn(id, x) VALUES (0, 44), (1, NULL), (2, 42) + +query II +SELECT * FROM onecolumn AS a(aid, x) CROSS JOIN onecolumn AS b(bid, y) order by x +---- +1 null 0 44 +1 null 1 null +1 null 2 42 +2 42 0 44 +2 42 1 null +2 42 2 42 +0 44 0 44 +0 44 1 null +0 44 2 42 + +# FIXME +# statement error 1065 +# SELECT x FROM onecolumn AS a, onecolumn AS b; + +query II +SELECT * FROM onecolumn AS a(aid, x) JOIN onecolumn AS b(bid, y) ON a.x = b.y order by a.x desc +---- +0 44 0 44 +2 42 2 42 + +query I +SELECT * FROM onecolumn AS a JOIN onecolumn as b USING(x) ORDER BY x desc +---- +0 44 0 +2 42 2 + +query I +SELECT * FROM onecolumn AS a NATURAL JOIN onecolumn as b order by a.x desc +---- +0 44 +2 42 + +query II +SELECT * FROM onecolumn AS a(aid, x) LEFT OUTER JOIN onecolumn AS b(bid, y) ON a.x = b.y order by a.x +---- +1 null null null +2 42 2 42 +0 44 0 44 + +query I +SELECT * FROM onecolumn AS a LEFT OUTER JOIN onecolumn AS b USING(x) ORDER BY x +---- +1 null null +2 42 2 +0 44 0 + +# FIXME +# statement error 1065 +# SELECT * FROM onecolumn AS a, onecolumn AS b ORDER BY x + +query I +SELECT * FROM onecolumn AS a NATURAL LEFT OUTER JOIN onecolumn AS b order by a.x +---- +1 null +2 42 +0 44 + +query II +SELECT * FROM onecolumn AS a(aid, x) RIGHT OUTER JOIN onecolumn AS b(bid, y) ON a.x = b.y order by x +---- +null null 1 null +2 42 2 42 +0 44 0 44 + +query I +SELECT * FROM onecolumn AS a RIGHT OUTER JOIN onecolumn AS b USING(x) ORDER BY x +---- +null null 1 +2 42 2 +0 44 0 + +# FIXME: The fields output by Using are determined by JoinType. At this time, because it is a Right Outer Join, the first row of results should be (1 null). +query I +SELECT * FROM onecolumn AS a NATURAL RIGHT OUTER JOIN onecolumn AS b order by x +---- +null null +2 42 +0 44 + +statement ok +drop table if exists onecolumn_w + +statement ok +CREATE TABLE onecolumn_w(w INT) + +statement ok +INSERT INTO onecolumn_w(w) VALUES (42),(43) + +query II +SELECT * FROM onecolumn AS a NATURAL JOIN onecolumn_w as b +---- +42 42 +42 43 +44 42 +44 43 +NULL 42 +NULL 43 + +statement ok +drop table if exists othercolumn + +statement ok +CREATE TABLE othercolumn (x INT) + +statement ok +INSERT INTO othercolumn(x) VALUES (43),(42),(16) + +query II +SELECT * FROM onecolumn AS a FULL OUTER JOIN othercolumn AS b ON a.x = b.x ORDER BY a.x,b.x +---- +42 42 +44 NULL +NULL 16 +NULL 43 +NULL NULL + +query II +SELECT * FROM onecolumn AS a full OUTER JOIN othercolumn AS b ON a.x = b.x and a.x > 16 order by a.x +---- +42 42 +44 NULL +NULL 16 +NULL 43 +NULL NULL + +query II +SELECT * FROM onecolumn AS a full OUTER JOIN othercolumn AS b ON a.x = b.x and b.x > 16 order by b.x +---- +42 42 +44 NULL +NULL 16 +NULL 43 +NULL NULL + +query II +SELECT * FROM onecolumn AS a full OUTER JOIN othercolumn AS b ON false order by b.x +---- +42 NULL +44 NULL +NULL 16 +NULL 42 +NULL 43 +NULL NULL + +query II +SELECT * FROM onecolumn AS a full OUTER JOIN othercolumn AS b ON true order by b.x +---- +42 16 +42 42 +42 43 +44 16 +44 42 +44 43 +NULL 16 +NULL 42 +NULL 43 + +# query +# SELECT * FROM onecolumn AS a FULL OUTER JOIN othercolumn AS b USING(x) ORDER BY x + +# query +# SELECT x AS s, a.x, b.x FROM onecolumn AS a FULL OUTER JOIN othercolumn AS b USING(x) ORDER BY s + +# query +# SELECT * FROM onecolumn AS a NATURAL FULL OUTER JOIN othercolumn AS b ORDER BY x + +# query +# SELECT * FROM (SELECT x FROM onecolumn ORDER BY x DESC) NATURAL JOIN (VALUES (42)) AS v(x) LIMIT 1 + +statement ok +drop table if exists empty + +statement ok +CREATE TABLE empty (x INT) + +# bug(#7149) fix by https://github.com/datafuselabs/databend/pull/7150 +statement ok +SELECT * FROM onecolumn AS a(x) CROSS JOIN empty AS b(y) + +statement ok +SELECT * FROM empty AS a CROSS JOIN onecolumn AS b + +statement ok +SELECT * FROM onecolumn AS a(x) JOIN empty AS b(y) ON a.x = b.y + +statement ok +SELECT * FROM onecolumn AS a JOIN empty AS b USING(x) + +statement ok +SELECT * FROM empty AS a(x) JOIN onecolumn AS b(y) ON a.x = b.y + +statement ok +SELECT * FROM empty AS a JOIN onecolumn AS b USING(x) + +query IT +SELECT * FROM onecolumn AS a(x) LEFT OUTER JOIN empty AS b(y) ON a.x = b.y ORDER BY a.x +---- +42 NULL +44 NULL +NULL NULL + +query I +SELECT * FROM onecolumn AS a LEFT OUTER JOIN empty AS b USING(x) ORDER BY x +---- +42 +44 +NULL + +statement ok +SELECT * FROM empty AS a(x) LEFT OUTER JOIN onecolumn AS b(y) ON a.x = b.y + +statement ok +SELECT * FROM empty AS a LEFT OUTER JOIN onecolumn AS b USING(x) + +statement ok +SELECT * FROM onecolumn AS a(x) RIGHT OUTER JOIN empty AS b(y) ON a.x = b.y + +statement ok +SELECT * FROM onecolumn AS a RIGHT OUTER JOIN empty AS b USING(x) + +query II +SELECT * FROM empty AS a(x) FULL OUTER JOIN onecolumn AS b(y) ON a.x = b.y ORDER BY b.y +---- +NULL 42 +NULL 44 +NULL NULL + +statement ok +SELECT * FROM empty AS a FULL OUTER JOIN onecolumn AS b USING(x) ORDER BY x + +query II +SELECT * FROM onecolumn AS a(x) FULL OUTER JOIN empty AS b(y) ON a.x = b.y ORDER BY a.x +---- +42 NULL +44 NULL +NULL NULL + +# query +# SELECT * FROM onecolumn AS a FULL OUTER JOIN empty AS b USING(x) ORDER BY x + +query II +SELECT * FROM empty AS a(x) FULL OUTER JOIN onecolumn AS b(y) ON a.x = b.y ORDER BY b.y +---- +NULL 42 +NULL 44 +NULL NULL + +# query +# SELECT * FROM empty AS a FULL OUTER JOIN onecolumn AS b USING(x) ORDER BY x + +statement ok +drop table if exists twocolumn + +statement ok +CREATE TABLE twocolumn (x INT NULL, y INT NULL) + +statement ok +INSERT INTO twocolumn(x, y) VALUES (44,51), (NULL,52), (42,53), (45,45) + +query II +SELECT * FROM onecolumn NATURAL JOIN twocolumn +---- +42 53 +44 51 + +query IIII +SELECT * FROM twocolumn AS a JOIN twocolumn AS b ON a.x = a.y order by a.x +---- +45 45 42 53 +45 45 44 51 +45 45 45 45 +45 45 NULL 52 + +query II +SELECT o.x, t.y FROM onecolumn o INNER JOIN twocolumn t ON (o.x=t.x AND t.y=53) +---- +42 53 + +query IT +SELECT o.x, t.y FROM onecolumn o LEFT OUTER JOIN twocolumn t ON (o.x=t.x AND t.y=53) order by o.x +---- +42 53 +44 NULL +NULL NULL + +query II +SELECT o.x, t.y FROM onecolumn o LEFT OUTER JOIN twocolumn t ON (o.x=t.x AND o.x=44) order by o.x +---- +42 NULL +44 51 +NULL NULL + +query II +SELECT o.x, t.y FROM onecolumn o LEFT OUTER JOIN twocolumn t ON (o.x=t.x AND t.x=44) order by o.x +---- +42 NULL +44 51 +NULL NULL + +# query +# SELECT * FROM (SELECT x, 2 two FROM onecolumn) NATURAL FULL JOIN (SELECT x, y+1 plus1 FROM twocolumn) + +statement ok +drop table if exists a + +statement ok +drop table if exists b + +statement ok +CREATE TABLE a (i int) + +statement ok +INSERT INTO a VALUES (1), (2), (3) + +statement ok +CREATE TABLE b (i int, b bool) + +statement ok +INSERT INTO b VALUES (2, true), (3, true), (4, false) + +query III +SELECT * FROM a INNER JOIN b ON a.i = b.i +---- +2 2 1 +3 3 1 + +query ITT +SELECT * FROM a LEFT OUTER JOIN b ON a.i = b.i +---- +1 NULL NULL +2 2 1 +3 3 1 + +query III +SELECT * FROM a RIGHT OUTER JOIN b ON a.i = b.i order by b +---- +2 2 1 +3 3 1 +NULL 4 0 + +query III +SELECT * FROM a FULL OUTER JOIN b ON a.i = b.i order by b +---- +1 NULL NULL +2 2 1 +3 3 1 +NULL 4 0 + +query III +SELECT * FROM a FULL OUTER JOIN b ON (a.i = b.i and a.i>2) ORDER BY a.i, b.i +---- +1 NULL NULL +2 NULL NULL +3 3 1 +NULL 2 1 +NULL 4 0 + + +statement ok +INSERT INTO b VALUES (3, false) + +query III +SELECT * FROM a RIGHT OUTER JOIN b ON a.i=b.i ORDER BY b.i, b.b +---- +2 2 1 +3 3 0 +3 3 1 +NULL 4 0 + +query III +SELECT * FROM a FULL OUTER JOIN b ON a.i=b.i ORDER BY b.i, b.b +---- +1 NULL NULL +2 2 1 +3 3 0 +3 3 1 +NULL 4 0 + + +query IIIIII +SELECT * FROM (onecolumn CROSS JOIN twocolumn JOIN onecolumn AS a(b) ON a.b=twocolumn.x JOIN twocolumn AS c(d,e) ON a.b=c.d AND c.d=onecolumn.x) ORDER BY 1 LIMIT 1 +---- +42 42 53 42 42 53 + +# query I +# SELECT * FROM onecolumn JOIN twocolumn ON twocolumn.x = onecolumn.x AND onecolumn.x IN (SELECT x FROM twocolumn WHERE y >= 52) + +# ---- +# 42 42 53 + +# query I +# SELECT * FROM onecolumn JOIN (VALUES (41),(42),(43)) AS a(x) USING(x) + +# ---- +# 42 + +query I +SELECT * FROM onecolumn JOIN (SELECT x + 2 AS x FROM onecolumn) USING(x) +---- +44 + +query IIIII +SELECT * FROM (twocolumn AS a JOIN twocolumn AS b USING(x) JOIN twocolumn AS c USING(x)) ORDER BY x LIMIT 1 +---- +42 53 53 53 + +query IIIIII +SELECT a.x AS s, b.x, c.x, a.y, b.y, c.y FROM (twocolumn AS a JOIN twocolumn AS b USING(x) JOIN twocolumn AS c USING(x)) ORDER BY s +---- +42 42 42 53 53 53 +44 44 44 51 51 51 +45 45 45 45 45 45 + +statement error 1065 +SELECT * FROM (onecolumn AS a JOIN onecolumn AS b USING(y)) + + +query I +SELECT * FROM (onecolumn AS a JOIN onecolumn AS b USING(x, x)) +---- +42 +44 + +statement ok +drop table if exists othertype + +statement ok +CREATE TABLE othertype (x TEXT) + +statement error 1065 +SELECT * FROM (onecolumn JOIN onecolumn USING(x)) + +statement error 1065 +SELECT * FROM (onecolumn JOIN twocolumn USING(x) JOIN onecolumn USING(x)) + +# query II +# SELECT * FROM (SELECT * FROM onecolumn), (SELECT * FROM onecolumn) + +# query I +# SELECT x FROM (onecolumn JOIN othercolumn USING (x)) JOIN (onecolumn AS a JOIN othercolumn AS b USING(x)) USING(x) + +statement error 1065 +SELECT x FROM (SELECT * FROM onecolumn), (SELECT * FROM onecolumn) + +statement error 1065 +SELECT * FROM (onecolumn AS a JOIN onecolumn AS b ON x > 32) + +statement error 1065 +SELECT * FROM (onecolumn AS a JOIN onecolumn AS b ON a.y > y) + +statement ok +drop table if exists s + +statement ok +CREATE TABLE s(x INT) + +statement ok +INSERT INTO s(x) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10) + +statement ok +drop table if exists pairs + +statement ok +drop table if exists square + +statement ok +CREATE TABLE square (n INT, sq INT) + +statement ok +INSERT INTO square VALUES (1,1), (2,4), (3,9), (4,16), (5,25), (6,36) + +statement ok +CREATE TABLE pairs (a INT, b INT) + +statement ok +INSERT INTO pairs VALUES (1,1), (1,2), (1,3), (1,4), (1,5), (1,6), (2,3), (2,4), (2,5), (2,6), (3,4), (3,5), (3,6), (4,5), (4,6) + +query IIII +SELECT * FROM pairs, square WHERE pairs.b = square.n order by a +---- +1 1 1 1 +1 2 2 4 +1 3 3 9 +1 4 4 16 +1 5 5 25 +1 6 6 36 +2 3 3 9 +2 4 4 16 +2 5 5 25 +2 6 6 36 +3 4 4 16 +3 5 5 25 +3 6 6 36 +4 5 5 25 +4 6 6 36 + +query IIII +SELECT * FROM pairs, square WHERE pairs.a + pairs.b = square.sq +---- +1 3 2 4 +3 6 3 9 +4 5 3 9 + +# query +# SELECT a, b, n, sq FROM (SELECT a, b, a * b / 2 AS div, n, sq FROM pairs, square) WHERE div = sq + +query IIII +SELECT * FROM pairs FULL OUTER JOIN square ON pairs.a + pairs.b = square.sq order by a +---- +1 1 NULL NULL +1 2 NULL NULL +1 3 2 4 +1 4 NULL NULL +1 5 NULL NULL +1 6 NULL NULL +2 3 NULL NULL +2 4 NULL NULL +2 5 NULL NULL +2 6 NULL NULL +3 4 NULL NULL +3 5 NULL NULL +3 6 3 9 +4 5 3 9 +4 6 NULL NULL +NULL NULL 1 1 +NULL NULL 4 16 +NULL NULL 5 25 +NULL NULL 6 36 + +query IIII +SELECT * FROM pairs FULL OUTER JOIN square ON pairs.a + pairs.b = square.sq WHERE pairs.b%2 <> square.sq%2 order by a +---- +1 3 2 4 +3 6 3 9 + +query IITT +SELECT * FROM (SELECT * FROM pairs LEFT JOIN square ON b = sq AND a > 1 AND n < 6) WHERE b > 1 AND (n IS NULL OR n > 1) AND (n IS NULL OR a < sq) +---- +1 2 NULL NULL +1 3 NULL NULL +1 4 NULL NULL +1 5 NULL NULL +1 6 NULL NULL +2 3 NULL NULL +2 4 2 4 +2 5 NULL NULL +2 6 NULL NULL +3 4 2 4 +3 5 NULL NULL +3 6 NULL NULL +4 5 NULL NULL +4 6 NULL NULL + +onlyif todo +query IIII +SELECT * FROM (SELECT * FROM pairs RIGHT JOIN square ON b = sq AND a > 1 AND n < 6) WHERE (a IS NULL OR a > 2) AND n > 1 AND (a IS NULL OR a < sq) order by n +---- +3 4 2 4 +NULL NULL 3 9 +NULL NULL 4 16 +NULL NULL 5 25 +NULL NULL 6 36 + +statement ok +drop table if exists t1 + +statement ok +drop table if exists t2 + +statement ok +CREATE TABLE t1 (col1 INT, x INT, col2 INT, y INT) + +statement ok +CREATE TABLE t2 (col3 INT, y INT, x INT, col4 INT) + +statement ok +INSERT INTO t1 VALUES (10, 1, 11, 1), (20, 2, 21, 1), (30, 3, 31, 1) + +statement ok +INSERT INTO t2 VALUES (100, 1, 1, 101), (200, 1, 201, 2), (400, 1, 401, 4) + +query IIIIIII +SELECT * FROM t1 JOIN t2 USING(x) +---- +10 1 11 1 100 1 101 + +query IIIIII +SELECT * FROM t1 NATURAL JOIN t2 +---- +10 1 11 1 100 101 + +query IIIIIIII +SELECT * FROM t1 JOIN t2 ON t2.x=t1.x +---- +10 1 11 1 100 1 1 101 + +# query +# SELECT * FROM t1 FULL OUTER JOIN t2 USING(x) + +# query +# SELECT * FROM t1 NATURAL FULL OUTER JOIN t2 + +query III +SELECT t2.x, t1.x, x FROM t1 JOIN t2 USING(x) +---- +1 1 1 + +# query +# SELECT t2.x, t1.x, x FROM t1 FULL OUTER JOIN t2 USING(x) + +query I +SELECT x FROM t1 NATURAL JOIN (SELECT * FROM t2) +---- +1 + +statement ok +drop table if exists pkBA + +statement ok +drop table if exists pkBC + +statement ok +drop table if exists pkBAC + +statement ok +drop table if exists pkBAD + +statement ok +CREATE TABLE pkBA (a INT, b INT, c INT, d INT) + +statement ok +CREATE TABLE pkBC (a INT, b INT, c INT, d INT) + +statement ok +CREATE TABLE pkBAC (a INT, b INT, c INT, d INT) + +statement ok +CREATE TABLE pkBAD (a INT, b INT, c INT, d INT) + +statement ok +drop table if exists str1 + +statement ok +drop table if exists str2 + +statement ok +CREATE TABLE str1 (a INT, s STRING) + +statement ok +INSERT INTO str1 VALUES (1, 'a' ), (2, 'A'), (3, 'c'), (4, 'D') + +statement ok +CREATE TABLE str2 (a INT, s STRING) + +statement ok +INSERT INTO str2 VALUES (1, 'A'), (2, 'B'), (3, 'C'), (4, 'E') + +query TTT +SELECT s, str1.s, str2.s FROM str1 INNER JOIN str2 USING(s) +---- +A A A + +query TTT +SELECT s, str1.s, str2.s FROM str1 LEFT OUTER JOIN str2 USING(s) order by str1.s +---- +A A A +D D NULL +a a NULL +c c NULL + +query TTT +SELECT s, str1.s, str2.s FROM str1 RIGHT OUTER JOIN str2 USING(s) order by str2.s +---- +A A A +B NULL B +C NULL C +E NULL E + +query ITIT +SELECT * FROM str1 LEFT OUTER JOIN str2 ON str1.s = str2.s order by str1.a +---- +1 a NULL NULL +2 A 1 A +3 c NULL NULL +4 D NULL NULL + +statement ok +INSERT INTO str1 VALUES (1, 'a' ), (2, 'A'), (3, 'c'), (4, 'D') + +query ITIT +select * from str1 right join str2 on str1.s = str2.s order by str2.a +---- +2 A 1 A +2 A 1 A +NULL NULL 2 B +NULL NULL 3 C +NULL NULL 4 E + +query ITIT +select * from str1 right join str2 on false order by str2.a +---- +NULL NULL 1 A +NULL NULL 2 B +NULL NULL 3 C +NULL NULL 4 E + +# query +# SELECT s, str1.s, str2.s FROM str1 FULL OUTER JOIN str2 USING(s) + +statement ok +drop table if exists xyu + +statement ok +drop table if exists xyv + +statement ok +CREATE TABLE xyu (x INT, y INT, u INT) + +statement ok +INSERT INTO xyu VALUES (0, 0, 0), (1, 1, 1), (3, 1, 31), (3, 2, 32), (4, 4, 44) + +statement ok +CREATE TABLE xyv (x INT, y INT, v INT) + +statement ok +INSERT INTO xyv VALUES (1, 1, 1), (2, 2, 2), (3, 1, 31), (3, 3, 33), (5, 5, 55) + +query IIII +SELECT * FROM xyu INNER JOIN xyv USING(x, y) WHERE x > 2 +---- +3 1 31 31 + +query IIII +SELECT * FROM xyu LEFT OUTER JOIN xyv USING(x, y) WHERE x > 2 +---- +3 1 31 31 +3 2 32 NULL +4 4 44 NULL + +query IIII +SELECT * FROM xyu RIGHT OUTER JOIN xyv USING(x, y) WHERE x > 2 order by y +---- +31 3 1 31 +NULL 3 3 33 +NULL 5 5 55 + +# statement error 1065 +# SELECT * FROM xyu FULL OUTER JOIN xyv USING(x, y) WHERE x > 2 + +query IIIIII +SELECT * FROM xyu INNER JOIN xyv ON xyu.x = xyv.x AND xyu.y = xyv.y WHERE xyu.x = 1 AND xyu.y < 10 +---- +1 1 1 1 1 1 + +query IIIIII +SELECT * FROM xyu INNER JOIN xyv ON xyu.x = xyv.x AND xyu.y = xyv.y AND xyu.x = 1 AND xyu.y < 10 +---- +1 1 1 1 1 1 + +query IIITTT +SELECT * FROM xyu LEFT OUTER JOIN xyv ON xyu.x = xyv.x AND xyu.y = xyv.y AND xyu.x = 1 AND xyu.y < 10 +---- +0 0 0 NULL NULL NULL +1 1 1 1 1 1 +3 1 31 NULL NULL NULL +3 2 32 NULL NULL NULL +4 4 44 NULL NULL NULL + +query IIIIII +SELECT * FROM xyu RIGHT OUTER JOIN xyv ON xyu.x = xyv.x AND xyu.y = xyv.y AND xyu.x = 1 AND xyu.y < 10 order by v +---- +1 1 1 1 1 1 +NULL NULL NULL 2 2 2 +NULL NULL NULL 3 1 31 +NULL NULL NULL 3 3 33 +NULL NULL NULL 5 5 55 + +query IIII +SELECT * FROM (SELECT * FROM xyu ORDER BY x, y) AS xyu LEFT OUTER JOIN (SELECT * FROM xyv ORDER BY x, y) AS xyv USING(x, y) WHERE x > 2 +---- +3 1 31 31 +3 2 32 NULL +4 4 44 NULL + +query IIII +SELECT * FROM (SELECT * FROM xyu ORDER BY x, y) AS xyu RIGHT OUTER JOIN (SELECT * FROM xyv ORDER BY x, y) AS xyv USING(x, y) WHERE x > 2 order by v +---- +31 3 1 31 +NULL 3 3 33 +NULL 5 5 55 + +# query +# SELECT * FROM (SELECT * FROM xyu ORDER BY x, y) AS xyu FULL OUTER JOIN (SELECT * FROM xyv ORDER BY x, y) AS xyv USING(x, y) WHERE x > 2 + +query IIITTT +SELECT * FROM (SELECT * FROM xyu ORDER BY x, y) AS xyu LEFT OUTER JOIN (SELECT * FROM xyv ORDER BY x, y) AS xyv ON xyu.x = xyv.x AND xyu.y = xyv.y AND xyu.x = 1 AND xyu.y < 10 +---- +0 0 0 NULL NULL NULL +1 1 1 1 1 1 +3 1 31 NULL NULL NULL +3 2 32 NULL NULL NULL +4 4 44 NULL NULL NULL + +query IIIIII +SELECT * FROM xyu RIGHT OUTER JOIN (SELECT * FROM xyv ORDER BY x, y) AS xyv ON xyu.x = xyv.x AND xyu.y = xyv.y AND xyu.x = 1 AND xyu.y < 10 ORDER BY v +---- +1 1 1 1 1 1 +NULL NULL NULL 2 2 2 +NULL NULL NULL 3 1 31 +NULL NULL NULL 3 3 33 +NULL NULL NULL 5 5 55 + +statement ok +drop table if exists l + +statement ok +drop table if exists r + +statement ok +CREATE TABLE l (a INT, b1 INT) + +statement ok +CREATE TABLE r (a INT, b2 INT) + +statement ok +INSERT INTO l VALUES (1, 1), (2, 1), (3, 1) + +statement ok +INSERT INTO r VALUES (2, 1), (3, 1), (4, 1) + +query III +SELECT * FROM l LEFT OUTER JOIN r USING(a) WHERE a = 1 +---- +1 1 NULL + +query III +SELECT * FROM l LEFT OUTER JOIN r USING(a) WHERE a = 2 +---- +2 1 1 + +query III +SELECT * FROM l RIGHT OUTER JOIN r USING(a) WHERE a = 3 +---- +1 3 1 + +query III +SELECT * FROM l RIGHT OUTER JOIN r USING(a) WHERE a = 4 +---- +NULL 4 1 + +statement ok +drop table if exists foo + +statement ok +drop table if exists bar + +statement ok +CREATE TABLE foo ( a INT, b INT, c FLOAT, d FLOAT) + +statement ok +INSERT INTO foo VALUES (1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3) + +statement ok +CREATE TABLE bar ( a INT, b FLOAT, c FLOAT, d INT) + +statement ok +INSERT INTO bar VALUES (1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3) + +query II?? +SELECT * FROM foo NATURAL JOIN bar +---- +1 1 1.0 1.0 +2 2 2.0 2.0 +3 3 3.0 3.0 + +query II??I?I +SELECT * FROM foo JOIN bar USING (b) +---- +1 1 1.0 1.0 1 1.0 1 +2 2 2.0 2.0 2 2.0 2 +3 3 3.0 3.0 3 3.0 3 + +query II???I +SELECT * FROM foo JOIN bar USING (a, b) +---- +1 1 1.0 1.0 1.0 1 +2 2 2.0 2.0 2.0 2 +3 3 3.0 3.0 3.0 3 + +query II??I +SELECT * FROM foo JOIN bar USING (a, b, c) +---- +1 1 1.0 1.0 1 +2 2 2.0 2.0 2 +3 3 3.0 3.0 3 + +query II??I??I +SELECT * FROM foo JOIN bar ON foo.b = bar.b +---- +1 1 1.0 1.0 1 1.0 1.0 1 +2 2 2.0 2.0 2 2.0 2.0 2 +3 3 3.0 3.0 3 3.0 3.0 3 + +query II??I??I +SELECT * FROM foo JOIN bar ON foo.a = bar.a AND foo.b = bar.b +---- +1 1 1.0 1.0 1 1.0 1.0 1 +2 2 2.0 2.0 2 2.0 2.0 2 +3 3 3.0 3.0 3 3.0 3.0 3 + +query II??I??I +SELECT * FROM foo, bar WHERE foo.b = bar.b +---- +1 1 1.0 1.0 1 1.0 1.0 1 +2 2 2.0 2.0 2 2.0 2.0 2 +3 3 3.0 3.0 3 3.0 3.0 3 + +query II??I??I +SELECT * FROM foo, bar WHERE foo.a = bar.a AND foo.b = bar.b +---- +1 1 1.0 1.0 1 1.0 1.0 1 +2 2 2.0 2.0 2 2.0 2.0 2 +3 3 3.0 3.0 3 3.0 3.0 3 + +query II???I +SELECT * FROM foo JOIN bar USING (a, b) WHERE foo.c = bar.c AND foo.d = bar.d +---- +1 1 1.0 1.0 1.0 1 +2 2 2.0 2.0 2.0 2 +3 3 3.0 3.0 3.0 3 + +query TII +SELECT * FROM onecolumn AS a(x) RIGHT JOIN twocolumn ON false order by y +---- +NULL 42 53 +NULL 44 51 +NULL 45 45 +NULL NULL 52 + +statement ok +SELECT * FROM onecolumn AS a(x) RIGHT JOIN twocolumn ON true where false order by y + +statement ok +SELECT * FROM onecolumn AS a(x) LEFT JOIN twocolumn ON true where twocolumn.x > 50 order by y + +statement ok +insert into onecolumn values(42) + +query II +select * from onecolumn as a right semi join twocolumn as b on a.x = b.x order by b.x +---- +42 53 +44 51 + +query II +select * from onecolumn as a right anti join twocolumn as b on a.x = b.x order by b.x +---- +45 45 +NULL 52 + +query II +select * from onecolumn as a right semi join twocolumn as b on a.x = b.x and a.x > 42 order by b.x +---- +44 51 + +query II +select * from onecolumn as a right anti join twocolumn as b on a.x = b.x and a.x > 42 order by b.x +---- +42 53 +45 45 +NULL 52 + +query II +select * from onecolumn as a right semi join twocolumn as b on a.x = b.x and b.x > 42 order by b.x +---- +44 51 + +query II +select * from onecolumn as a right anti join twocolumn as b on a.x = b.x and b.x > 42 order by b.x +---- +42 53 +45 45 +NULL 52 + +query II +select * from onecolumn as a right semi join twocolumn as b on true order by b.x +---- +42 53 +44 51 +45 45 +NULL 52 + +statement ok +select * from onecolumn as a right anti join twocolumn as b on true order by b.x + +statement ok +select * from onecolumn as a right semi join twocolumn as b on false order by b.x + +query II +select * from onecolumn as a right anti join twocolumn as b on false order by b.x +---- +42 53 +44 51 +45 45 +NULL 52 + +query III +select * from onecolumn as a left join twocolumn as b on a.x = b.x where b.x > 42 +---- +44 44 51 + +query III +select * from onecolumn as a left join twocolumn as b on a.x = b.x where b.x > 44 or b.x < 43 +---- +42 42 53 +42 42 53 + +query III +select * from onecolumn as a left join twocolumn as b on a.x = b.x where b.x > 42 and b.x < 45 +---- +44 44 51 + +# query +# SELECT column1, column1+1FROM (SELECT * FROM (VALUES (NULL, NULL)) AS t NATURAL FULL OUTER JOIN (VALUES (1, 1)) AS u) + +# query +# SELECT * FROM (VALUES (1, 2)) a(a1,a2) FULL JOIN (VALUES (3, 4)) b(b1,b2) ON a1=b1 ORDER BY a2 + +# statement ok +# drop table if exists abcd + +# statement ok +# drop table if exists dxby + +# statement ok +# CREATE TABLE abcd (a INT, b INT, c INT, d INT) + +# statement ok +# INSERT INTO abcd VALUES (1, 1, 1, 1), (2, 2, 2, 2) + +# statement ok +# CREATE TABLE dxby (d INT, x INT, b INT, y INT) + +# statement ok +# INSERT INTO dxby VALUES (2, 2, 2, 2), (3, 3, 3, 3) + +# query +# SELECT * FROM abcd NATURAL FULL OUTER JOIN dxby + +# query +# SELECT abcd.*, dxby.* FROM abcd NATURAL FULL OUTER JOIN dxby + +# query +# SELECT abcd.*, dxby.* FROM abcd INNER JOIN dxby USING (d, b) \ No newline at end of file diff --git a/tests/slt/join.slt b/tests/slt/join.slt index 06bc9781..7dbfed87 100644 --- a/tests/slt/join.slt +++ b/tests/slt/join.slt @@ -118,9 +118,16 @@ select v1, v2, v3, v4, v5 from a join b on v1 = v3 and v2 = v4 and v1 < v5; query IIIII rowsort select * from a join b using (id) ---- -0 1 1 0 1 1 1 -1 2 2 1 2 2 2 -2 3 3 2 3 3 4 +0 1 1 1 1 1 +1 2 2 2 2 2 +2 3 3 3 3 4 + +query IIIII rowsort +select * from a natural join b +---- +0 1 1 1 1 1 +1 2 2 2 2 2 +2 3 3 3 3 4 query IIIIII rowsort select a.*, c.* from a inner join a as c using (id)