diff --git a/README.md b/README.md index 5922716a..a3e7f510 100755 --- a/README.md +++ b/README.md @@ -114,8 +114,11 @@ implement_from_tuple!( - User-Defined Function: `features = ["marcos"]` ```rust function!(TestFunction::test(LogicalType::Integer, LogicalType::Integer) -> LogicalType::Integer => |v1: ValueRef, v2: ValueRef| { - let value = DataValue::binary_op(&v1, &v2, &BinaryOperator::Plus)?; - DataValue::unary_op(&value, &UnaryOperator::Minus) + let plus_binary_evaluator = EvaluatorFactory::binary_create(LogicalType::Integer, BinaryOperator::Plus)?; + let value = plus_binary_evaluator.binary_eval(&v1, &v2); + + let plus_unary_evaluator = EvaluatorFactory::unary_create(LogicalType::Integer, UnaryOperator::Minus)?; + Ok(plus_unary_evaluator.unary_eval(&value)) }); let fnck_sql = DataBaseBuilder::path("./data") @@ -178,7 +181,7 @@ let fnck_sql = DataBaseBuilder::path("./data") - [x] Alias - [x] Aggregation: count()/sum()/avg()/min()/max() - [x] SubQuery[select/from/where] - - [x] Join: Inner/Left/Right/Full/Cross + - [x] Join: Inner/Left/Right/Full/Cross (Natural\Using) - [x] Group By - [x] Having - [x] Order By diff --git a/src/binder/aggregate.rs b/src/binder/aggregate.rs index 096799c0..d240d852 100644 --- a/src/binder/aggregate.rs +++ b/src/binder/aggregate.rs @@ -14,7 +14,7 @@ use crate::{ use super::{Binder, QueryBindStep}; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub fn bind_aggregate( &mut self, children: LogicalPlan, diff --git a/src/binder/alter_table.rs b/src/binder/alter_table.rs index c40dc122..1ec03506 100644 --- a/src/binder/alter_table.rs +++ b/src/binder/alter_table.rs @@ -12,7 +12,7 @@ use crate::planner::operator::Operator; use crate::planner::LogicalPlan; use crate::storage::Transaction; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub(crate) fn bind_alter_table( &mut self, name: &ObjectName, diff --git a/src/binder/analyze.rs b/src/binder/analyze.rs index 4d8f0a02..2ef13274 100644 --- a/src/binder/analyze.rs +++ b/src/binder/analyze.rs @@ -8,11 +8,13 @@ use crate::storage::Transaction; use sqlparser::ast::ObjectName; use std::sync::Arc; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, 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/copy.rs b/src/binder/copy.rs index 64613a5c..f9a973bd 100644 --- a/src/binder/copy.rs +++ b/src/binder/copy.rs @@ -51,7 +51,7 @@ impl FromStr for ExtSource { } } -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub(super) fn bind_copy( &mut self, source: CopySource, diff --git a/src/binder/create_index.rs b/src/binder/create_index.rs index e9cdefb6..e5352a95 100644 --- a/src/binder/create_index.rs +++ b/src/binder/create_index.rs @@ -10,7 +10,7 @@ use crate::types::index::IndexType; use sqlparser::ast::{ObjectName, OrderByExpr}; use std::sync::Arc; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub(crate) fn bind_create_index( &mut self, table_name: &ObjectName, @@ -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/create_table.rs b/src/binder/create_table.rs index cd54afc0..2a2fba8e 100644 --- a/src/binder/create_table.rs +++ b/src/binder/create_table.rs @@ -14,7 +14,7 @@ use crate::planner::LogicalPlan; use crate::storage::Transaction; use crate::types::LogicalType; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { // TODO: TableConstraint pub(crate) fn bind_create_table( &mut self, diff --git a/src/binder/delete.rs b/src/binder/delete.rs index c5f81b85..d9bd97d8 100644 --- a/src/binder/delete.rs +++ b/src/binder/delete.rs @@ -5,10 +5,10 @@ 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> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub(crate) fn bind_delete( &mut self, from: &TableWithJoins, @@ -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/describe.rs b/src/binder/describe.rs index e7655c71..159d4ad1 100644 --- a/src/binder/describe.rs +++ b/src/binder/describe.rs @@ -7,7 +7,7 @@ use crate::storage::Transaction; use sqlparser::ast::ObjectName; use std::sync::Arc; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub(crate) fn bind_describe( &mut self, name: &ObjectName, diff --git a/src/binder/distinct.rs b/src/binder/distinct.rs index 8495e8f8..2d03989d 100644 --- a/src/binder/distinct.rs +++ b/src/binder/distinct.rs @@ -4,7 +4,7 @@ use crate::planner::operator::aggregate::AggregateOperator; use crate::planner::LogicalPlan; use crate::storage::Transaction; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub fn bind_distinct( &mut self, children: LogicalPlan, diff --git a/src/binder/drop_table.rs b/src/binder/drop_table.rs index 79fd9800..39dc19dd 100644 --- a/src/binder/drop_table.rs +++ b/src/binder/drop_table.rs @@ -7,7 +7,7 @@ use crate::storage::Transaction; use sqlparser::ast::ObjectName; use std::sync::Arc; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub(crate) fn bind_drop_table( &mut self, name: &ObjectName, diff --git a/src/binder/explain.rs b/src/binder/explain.rs index d5df278f..3fe29c32 100644 --- a/src/binder/explain.rs +++ b/src/binder/explain.rs @@ -4,7 +4,7 @@ use crate::planner::operator::Operator; use crate::planner::LogicalPlan; use crate::storage::Transaction; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub(crate) fn bind_explain(&mut self, plan: LogicalPlan) -> Result { Ok(LogicalPlan::new(Operator::Explain, vec![plan])) } diff --git a/src/binder/expr.rs b/src/binder/expr.rs index ae9e7890..9180aaae 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), }); } }; @@ -37,7 +37,7 @@ macro_rules! try_default { }; } -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub(crate) fn bind_expr(&mut self, expr: &Expr) -> Result { match expr { Expr::Identifier(ident) => { @@ -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..0a33d3c4 100644 --- a/src/binder/insert.rs +++ b/src/binder/insert.rs @@ -12,7 +12,7 @@ use sqlparser::ast::{Expr, Ident, ObjectName}; use std::slice; use std::sync::Arc; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub(crate) fn bind_insert( &mut self, name: &ObjectName, @@ -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..db1cea2b 100644 --- a/src/binder/mod.rs +++ b/src/binder/mod.rs @@ -17,7 +17,7 @@ mod truncate; mod update; use sqlparser::ast::{Ident, ObjectName, ObjectType, SetExpr, Statement}; -use std::collections::{BTreeMap, HashMap}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -85,13 +85,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 +117,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 +166,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,20 +177,21 @@ 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) } /// get table from bindings - pub fn bind_table( + pub fn bind_table<'b: 'a>( &self, table_name: &str, - parent: Option<&'a Binder<'a, T>>, + parent: Option<&'b Binder<'a, 'b, 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 +208,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()); } @@ -215,13 +230,13 @@ impl<'a, T: Transaction> BinderContext<'a, T> { } } -pub struct Binder<'a, T: Transaction> { +pub struct Binder<'a, 'b, T: Transaction> { context: BinderContext<'a, T>, - pub(crate) parent: Option<&'a Binder<'a, T>>, + pub(crate) parent: Option<&'b Binder<'a, 'b, T>>, } -impl<'a, T: Transaction> Binder<'a, T> { - pub fn new(context: BinderContext<'a, T>, parent: Option<&'a Binder<'a, T>>) -> Self { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { + pub fn new(context: BinderContext<'a, T>, parent: Option<&'b Binder<'a, 'b, T>>) -> Self { Binder { context, parent } } @@ -326,6 +341,18 @@ impl<'a, T: Transaction> Binder<'a, T> { _ => todo!(), } } + + fn extend(&mut self, context: BinderContext<'a, T>) { + for (key, table) in context.bind_table { + self.context.bind_table.insert(key, table); + } + for (key, expr) in context.expr_aliases { + self.context.expr_aliases.insert(key, expr); + } + for (key, table_name) in context.table_aliases { + self.context.table_aliases.insert(key, table_name); + } + } } fn lower_ident(ident: &Ident) -> String { diff --git a/src/binder/select.rs b/src/binder/select.rs index d794bb03..5aa69564 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; @@ -35,7 +35,7 @@ use sqlparser::ast::{ TableWithJoins, }; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a: 'b, 'b, T: Transaction> Binder<'a, 'b, T> { pub(crate) fn bind_query(&mut self, query: &Query) -> Result { let origin_step = self.context.step_now(); @@ -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(), + Some(&mut join_used), + )?; } } SelectItem::QualifiedWildcard(table_name, _) => { self.bind_table_column_refs( &mut select_items, Arc::new(lower_case_name(table_name)?), + None, )?; } }; @@ -383,31 +424,51 @@ impl<'a, T: Transaction> Binder<'a, T> { &self, exprs: &mut Vec, table_name: TableName, + mut join_used: Option<&mut HashSet>, ) -> Result<(), DatabaseError> { + let mut is_bound_alias = false; + + let fn_used = + |column_name: &str, context: &BinderContext, join_used: Option<&HashSet<_>>| { + context.using.contains(column_name) + && matches!(join_used.map(|used| used.contains(column_name)), Some(true)) + }; + 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.as_deref()) + { + if let Some(used) = join_used.as_mut() { + 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.as_deref()) { + continue; + } + let expr = ScalarExpression::ColumnRef(column.clone()); + + if let Some(used) = join_used.as_mut() { + used.insert(column_name.to_string()); } exprs.push(expr); } @@ -432,7 +493,18 @@ impl<'a, T: Transaction> Binder<'a, T> { JoinOperator::CrossJoin => (JoinType::Cross, None), _ => unimplemented!(), }; - let mut right = self.bind_single_table_ref(relation, Some(join_type))?; + let BinderContext { + transaction, + functions, + temp_table_id, + .. + } = &self.context; + let mut binder = Binder::new( + BinderContext::new(*transaction, functions, temp_table_id.clone()), + Some(self), + ); + let mut right = binder.bind_single_table_ref(relation, Some(join_type))?; + self.extend(binder.context); let on = match joint_condition { Some(constraint) => { @@ -597,7 +669,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 +698,10 @@ impl<'a, T: Transaction> Binder<'a, T> { } } - fn bind_join_constraint( + fn bind_join_constraint<'c>( &mut self, - left_schema: &SchemaRef, - right_schema: &SchemaRef, + left_schema: &'c SchemaRef, + right_schema: &'c SchemaRef, constraint: &JoinConstraint, ) -> Result { match constraint { @@ -665,23 +737,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 +762,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: &'c Schema| -> HashSet<&'c 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/show.rs b/src/binder/show.rs index fe35421c..b3b54b40 100644 --- a/src/binder/show.rs +++ b/src/binder/show.rs @@ -4,7 +4,7 @@ use crate::planner::operator::Operator; use crate::planner::LogicalPlan; use crate::storage::Transaction; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub(crate) fn bind_show_tables(&mut self) -> Result { Ok(LogicalPlan::new(Operator::Show, vec![])) } diff --git a/src/binder/truncate.rs b/src/binder/truncate.rs index 312b6199..2dbd9e86 100644 --- a/src/binder/truncate.rs +++ b/src/binder/truncate.rs @@ -7,7 +7,7 @@ use crate::storage::Transaction; use sqlparser::ast::ObjectName; use std::sync::Arc; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub(crate) fn bind_truncate( &mut self, name: &ObjectName, diff --git a/src/binder/update.rs b/src/binder/update.rs index 33253d1a..fe803a3f 100644 --- a/src/binder/update.rs +++ b/src/binder/update.rs @@ -10,7 +10,7 @@ use sqlparser::ast::{Assignment, Expr, TableFactor, TableWithJoins}; use std::slice; use std::sync::Arc; -impl<'a, T: Transaction> Binder<'a, T> { +impl<'a, 'b, T: Transaction> Binder<'a, 'b, T> { pub(crate) fn bind_update( &mut self, to: &TableWithJoins, @@ -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/db.rs b/src/db.rs index a3a77309..b459a5ca 100644 --- a/src/db.rs +++ b/src/db.rs @@ -310,10 +310,10 @@ mod test { function!(TestFunction::test(LogicalType::Integer, LogicalType::Integer) -> LogicalType::Integer => (|v1: ValueRef, v2: ValueRef| { let plus_binary_evaluator = EvaluatorFactory::binary_create(LogicalType::Integer, BinaryOperator::Plus)?; - let value = plus_binary_evaluator.0.binary_eval(&v1, &v2); + let value = plus_binary_evaluator.binary_eval(&v1, &v2); let plus_unary_evaluator = EvaluatorFactory::unary_create(LogicalType::Integer, UnaryOperator::Minus)?; - Ok(plus_unary_evaluator.0.unary_eval(&value)) + Ok(plus_unary_evaluator.unary_eval(&value)) })); #[tokio::test] 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/src/types/evaluator/boolean.rs b/src/types/evaluator/boolean.rs index eb671a75..900f2581 100644 --- a/src/types/evaluator/boolean.rs +++ b/src/types/evaluator/boolean.rs @@ -19,6 +19,7 @@ impl UnaryEvaluator for BooleanNotUnaryEvaluator { fn unary_eval(&self, value: &DataValue) -> DataValue { let value = match value { DataValue::Boolean(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; DataValue::Boolean(value.map(|v| !v)) @@ -29,10 +30,12 @@ impl BinaryEvaluator for BooleanAndBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Boolean(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Boolean(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = match (left, right) { @@ -49,10 +52,12 @@ impl BinaryEvaluator for BooleanOrBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Boolean(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Boolean(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = match (left, right) { @@ -69,10 +74,12 @@ impl BinaryEvaluator for BooleanEqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Boolean(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Boolean(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = match (left, right) { @@ -88,10 +95,12 @@ impl BinaryEvaluator for BooleanNotEqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Boolean(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Boolean(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = match (left, right) { diff --git a/src/types/evaluator/decimal.rs b/src/types/evaluator/decimal.rs index 9139f877..f2b47560 100644 --- a/src/types/evaluator/decimal.rs +++ b/src/types/evaluator/decimal.rs @@ -23,16 +23,20 @@ pub struct DecimalLtEqBinaryEvaluator; pub struct DecimalEqBinaryEvaluator; #[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)] pub struct DecimalNotEqBinaryEvaluator; +#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)] +pub struct DecimalModBinaryEvaluator; #[typetag::serde] impl BinaryEvaluator for DecimalPlusBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -48,10 +52,12 @@ impl BinaryEvaluator for DecimalMinusBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -67,10 +73,12 @@ impl BinaryEvaluator for DecimalMultiplyBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -86,10 +94,12 @@ impl BinaryEvaluator for DecimalDivideBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -105,10 +115,12 @@ impl BinaryEvaluator for DecimalGtBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -124,10 +136,12 @@ impl BinaryEvaluator for DecimalGtEqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -143,10 +157,12 @@ impl BinaryEvaluator for DecimalLtBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -162,10 +178,12 @@ impl BinaryEvaluator for DecimalLtEqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -181,10 +199,12 @@ impl BinaryEvaluator for DecimalEqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -200,10 +220,12 @@ impl BinaryEvaluator for DecimalNotEqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Decimal(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -214,3 +236,24 @@ impl BinaryEvaluator for DecimalNotEqBinaryEvaluator { DataValue::Boolean(value) } } +#[typetag::serde] +impl BinaryEvaluator for DecimalModBinaryEvaluator { + fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { + let left = match left { + DataValue::Decimal(value) => value, + DataValue::Null => &None, + _ => unsafe { hint::unreachable_unchecked() }, + }; + let right = match right { + DataValue::Decimal(value) => value, + DataValue::Null => &None, + _ => unsafe { hint::unreachable_unchecked() }, + }; + let value = if let (Some(v1), Some(v2)) = (left, right) { + Some(v1 % v2) + } else { + None + }; + DataValue::Decimal(value) + } +} diff --git a/src/types/evaluator/mod.rs b/src/types/evaluator/mod.rs index 1a80c77a..c86c59b2 100644 --- a/src/types/evaluator/mod.rs +++ b/src/types/evaluator/mod.rs @@ -65,9 +65,21 @@ pub trait UnaryEvaluator: Send + Sync + Debug { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct BinaryEvaluatorBox(pub Arc); +impl BinaryEvaluatorBox { + pub fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { + self.0.binary_eval(left, right) + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct UnaryEvaluatorBox(pub Arc); +impl UnaryEvaluatorBox { + pub fn unary_eval(&self, value: &DataValue) -> DataValue { + self.0.unary_eval(value) + } +} + impl PartialEq for BinaryEvaluatorBox { fn eq(&self, _: &Self) -> bool { // FIXME @@ -112,6 +124,7 @@ macro_rules! numeric_binary_evaluator { BinaryOperator::LtEq => Ok(BinaryEvaluatorBox(Arc::new([<$value_type LtEqBinaryEvaluator>]))), BinaryOperator::Eq => Ok(BinaryEvaluatorBox(Arc::new([<$value_type EqBinaryEvaluator>]))), BinaryOperator::NotEq => Ok(BinaryEvaluatorBox(Arc::new([<$value_type NotEqBinaryEvaluator>]))), + BinaryOperator::Modulo => Ok(BinaryEvaluatorBox(Arc::new([<$value_type ModBinaryEvaluator>]))), _ => { return Err(DatabaseError::UnsupportedBinaryOperator( $ty, @@ -251,6 +264,7 @@ macro_rules! numeric_unary_evaluator_definition { fn unary_eval(&self, value: &DataValue) -> DataValue { let value = match value { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; $compute_type(value.map(|v| -v)) @@ -284,16 +298,20 @@ macro_rules! numeric_binary_evaluator_definition { pub struct [<$value_type EqBinaryEvaluator>]; #[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)] pub struct [<$value_type NotEqBinaryEvaluator>]; + #[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)] + pub struct [<$value_type ModBinaryEvaluator>]; #[typetag::serde] impl BinaryEvaluator for [<$value_type PlusBinaryEvaluator>] { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -309,10 +327,12 @@ macro_rules! numeric_binary_evaluator_definition { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -328,10 +348,12 @@ macro_rules! numeric_binary_evaluator_definition { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -347,10 +369,12 @@ macro_rules! numeric_binary_evaluator_definition { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -366,10 +390,12 @@ macro_rules! numeric_binary_evaluator_definition { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -385,10 +411,12 @@ macro_rules! numeric_binary_evaluator_definition { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -404,10 +432,12 @@ macro_rules! numeric_binary_evaluator_definition { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -423,10 +453,12 @@ macro_rules! numeric_binary_evaluator_definition { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -442,10 +474,12 @@ macro_rules! numeric_binary_evaluator_definition { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -461,10 +495,12 @@ macro_rules! numeric_binary_evaluator_definition { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { $compute_type(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -475,6 +511,27 @@ macro_rules! numeric_binary_evaluator_definition { DataValue::Boolean(value) } } + #[typetag::serde] + impl BinaryEvaluator for [<$value_type ModBinaryEvaluator>] { + fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { + let left = match left { + $compute_type(value) => value, + DataValue::Null => &None, + _ => unsafe { hint::unreachable_unchecked() }, + }; + let right = match right { + $compute_type(value) => value, + DataValue::Null => &None, + _ => unsafe { hint::unreachable_unchecked() }, + }; + let value = if let (Some(v1), Some(v2)) = (left, right) { + Some(v1 % v2) + } else { + None + }; + $compute_type(value) + } + } } }; } diff --git a/src/types/evaluator/tuple.rs b/src/types/evaluator/tuple.rs index c7818c5d..2fa1943d 100644 --- a/src/types/evaluator/tuple.rs +++ b/src/types/evaluator/tuple.rs @@ -39,10 +39,12 @@ impl BinaryEvaluator for TupleEqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Tuple(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Tuple(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = match (left, right) { @@ -57,10 +59,12 @@ impl BinaryEvaluator for TupleNotEqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Tuple(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Tuple(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = match (left, right) { @@ -75,10 +79,12 @@ impl BinaryEvaluator for TupleGtBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Tuple(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Tuple(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = match (left, right) { @@ -93,10 +99,12 @@ impl BinaryEvaluator for TupleGtEqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Tuple(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Tuple(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = match (left, right) { @@ -111,10 +119,12 @@ impl BinaryEvaluator for TupleLtBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Tuple(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Tuple(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = match (left, right) { @@ -129,10 +139,12 @@ impl BinaryEvaluator for TupleLtEqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Tuple(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Tuple(value) => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = match (left, right) { diff --git a/src/types/evaluator/utf8.rs b/src/types/evaluator/utf8.rs index 61c8ba22..7ca9ce09 100644 --- a/src/types/evaluator/utf8.rs +++ b/src/types/evaluator/utf8.rs @@ -34,10 +34,12 @@ impl BinaryEvaluator for Utf8GtBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -53,10 +55,12 @@ impl BinaryEvaluator for Utf8GtEqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -72,10 +76,12 @@ impl BinaryEvaluator for Utf8LtBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -91,10 +97,12 @@ impl BinaryEvaluator for Utf8LtEqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -110,10 +118,12 @@ impl BinaryEvaluator for Utf8EqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -129,10 +139,12 @@ impl BinaryEvaluator for Utf8NotEqBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = if let (Some(v1), Some(v2)) = (left, right) { @@ -148,10 +160,12 @@ impl BinaryEvaluator for Utf8StringConcatBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let left = match left { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let right = match right { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let value = match (left, right) { @@ -170,10 +184,12 @@ impl BinaryEvaluator for Utf8LikeBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let value = match left { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let pattern = match right { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let is_match = if let (Some(value), Some(pattern)) = (value, pattern) { @@ -190,10 +206,12 @@ impl BinaryEvaluator for Utf8NotLikeBinaryEvaluator { fn binary_eval(&self, left: &DataValue, right: &DataValue) -> DataValue { let value = match left { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let pattern = match right { DataValue::Utf8 { value, .. } => value, + DataValue::Null => &None, _ => unsafe { hint::unreachable_unchecked() }, }; let is_match = if let (Some(value), Some(pattern)) = (value, pattern) { diff --git a/tests/slt/crdb/join.slt b/tests/slt/crdb/join.slt new file mode 100644 index 00000000..0bd832c4 --- /dev/null +++ b/tests/slt/crdb/join.slt @@ -0,0 +1,1091 @@ +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_id INT PRIMARY KEY, w INT) + +statement ok +INSERT INTO onecolumn_w(w_id, w) VALUES (0, 42),(1, 43) + +query II +SELECT * FROM onecolumn AS a NATURAL JOIN onecolumn_w as b +---- +0 44 42 0 +0 44 43 1 +1 null 42 0 +1 null 43 1 +2 42 42 0 +2 42 43 1 + +statement ok +drop table if exists othercolumn + +statement ok +CREATE TABLE othercolumn (o_id INT PRIMARY KEY, x INT) + +statement ok +INSERT INTO othercolumn(o_id, x) VALUES (0, 43),(1, 42),(2, 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 +---- +1 null null null +null null 2 16 +null null 0 43 +2 42 1 42 +0 44 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 +---- +null null 0 43 +null null 2 16 +1 null null null +2 42 1 42 +0 44 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,a.x +---- +1 null null null +0 44 null null +null null 2 16 +2 42 1 42 +null null 0 43 + +# TODO: Full Join on nested loop join +# 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 + +# TODO: Full Join on nested loop join +# 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 + +# TODO: Full Join on nested loop join +# query +# SELECT * FROM onecolumn AS a FULL OUTER JOIN othercolumn AS b USING(x) ORDER BY x + +# TODO: Full Join on nested loop join +# query +# SELECT x AS s, a.x, b.x FROM onecolumn AS a FULL OUTER JOIN othercolumn AS b USING(x) ORDER BY s + +# TODO: Full Join on nested loop join +# query +# SELECT * FROM onecolumn AS a NATURAL FULL OUTER JOIN othercolumn AS b ORDER BY x + +# TODO +# 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 (e_id INT PRIMARY KEY, x INT) + +statement ok +SELECT * FROM onecolumn AS a(aid, x) CROSS JOIN empty AS b(bid, y) + +statement ok +SELECT * FROM empty AS a CROSS JOIN onecolumn AS b + +statement ok +SELECT * FROM onecolumn AS a(aid, x) JOIN empty AS b(bid, 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(aid, x) JOIN onecolumn AS b(bid, 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(aid, x) LEFT OUTER JOIN empty AS b(bid, y) ON a.x = b.y ORDER BY a.x +---- +null null 1 null +null null 2 42 +null null 0 44 + +# FIXME: The fields output by Using are determined by JoinType. +# query I +# SELECT * FROM onecolumn AS a LEFT OUTER JOIN empty AS b USING(x) ORDER BY x +# ---- +# null null 2 +# null 42 0 +# null 44 1 + +statement ok +SELECT * FROM empty AS a(aid, x) LEFT OUTER JOIN onecolumn AS b(bid, 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(aid, x) RIGHT OUTER JOIN empty AS b(bid, y) ON a.x = b.y + +statement ok +SELECT * FROM onecolumn AS a RIGHT OUTER JOIN empty AS b USING(x) + +# TODO: Full Join on nested loop join +query II +SELECT * FROM empty AS a(aid, x) FULL OUTER JOIN onecolumn AS b(bid, y) ON a.x = b.y ORDER BY b.y +---- +null null 1 null +null null 2 42 +null null 0 44 + +statement ok +SELECT * FROM empty AS a FULL OUTER JOIN onecolumn AS b USING(x) ORDER BY x + +# TODO: Full Join on nested loop join +# 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 + +# TODO: Full Join on nested loop join +# query +# SELECT * FROM onecolumn AS a FULL OUTER JOIN empty AS b USING(x) ORDER BY x + +# TODO: Full Join on nested loop join +# 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 + +# TODO: Full Join on nested loop join +# 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 (t_id INT PRIMARY KEY, x INT NULL, y INT NULL) + +statement ok +INSERT INTO twocolumn(t_id, x, y) VALUES (0,44,51), (1,NULL,52), (2,42,53), (3,45,45) + +query II +SELECT * FROM onecolumn NATURAL JOIN twocolumn +---- +0 44 0 51 +2 42 2 53 + +query IIII +SELECT * FROM twocolumn AS a JOIN twocolumn AS b ON a.x = a.y order by a.x +---- +3 45 45 0 44 51 +3 45 45 1 null 52 +3 45 45 2 42 53 +3 45 45 3 45 45 + +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 +---- +null null +42 53 +44 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 +---- +null null +42 null +44 51 + +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 +---- +null null +42 null +44 51 + +# TODO: Full Join on nested loop join +# 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 (id int primary key, i int) + +statement ok +INSERT INTO a VALUES (0, 1), (1, 2), (2, 3) + +statement ok +CREATE TABLE b (id int primary key, i int, b boolean) + +statement ok +INSERT INTO b VALUES (0, 2, true), (1, 3, true), (2, 4, false) + +query III +SELECT * FROM a INNER JOIN b ON a.i = b.i +---- +1 2 0 2 true +2 3 1 3 true + +query ITT +SELECT * FROM a LEFT OUTER JOIN b ON a.i = b.i +---- +1 2 0 2 true +2 3 1 3 true +0 1 null null null + +query III +SELECT * FROM a RIGHT OUTER JOIN b ON a.i = b.i order by b +---- +null null 2 4 false +1 2 0 2 true +2 3 1 3 true + +query III +SELECT * FROM a FULL OUTER JOIN b ON a.i = b.i order by b +---- +0 1 null null null +null null 2 4 false +1 2 0 2 true +2 3 1 3 true + +query III +SELECT * FROM a FULL OUTER JOIN b ON (a.i = b.i and a.i>2) ORDER BY a.i, b.i +---- +null null 2 4 false +0 1 null null null +1 2 0 2 true +2 3 1 3 true + +statement ok +INSERT INTO b VALUES (3, 3, false) + +query III +SELECT * FROM a RIGHT OUTER JOIN b ON a.i=b.i ORDER BY b.i, b.b +---- +1 2 0 2 true +2 3 3 3 false +2 3 1 3 true +null null 2 4 false + +query III +SELECT * FROM a FULL OUTER JOIN b ON a.i=b.i ORDER BY b.i, b.b +---- +0 1 null null null +1 2 0 2 true +2 3 3 3 false +2 3 1 3 true +null null 2 4 false + +# TODO +# query IIIIII +# SELECT * FROM (onecolumn CROSS JOIN twocolumn JOIN onecolumn AS a(aid,b) ON a.b=twocolumn.x JOIN twocolumn AS c(cid,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) +---- +0 44 + +# TODO +# 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 + +# TODO +# 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 + +# TODO +# statement error 1065 +# SELECT * FROM (onecolumn AS a JOIN onecolumn AS b USING(y)) + +# TODO +# 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 (ot_id int primary key, x VARCHAR) + +# TODO +# statement error 1065 +# SELECT * FROM (onecolumn JOIN onecolumn USING(x)) + +# TODO +# statement error 1065 +# SELECT * FROM (onecolumn JOIN twocolumn USING(x) JOIN onecolumn USING(x)) + +# TODO +# query II +# SELECT * FROM (SELECT * FROM onecolumn), (SELECT * FROM onecolumn) + +# TODO +# query I +# SELECT x FROM (onecolumn JOIN othercolumn USING (x)) JOIN (onecolumn AS a JOIN othercolumn AS b USING(x)) USING(x) + +# TODO +# statement error 1065 +# SELECT x FROM (SELECT * FROM onecolumn), (SELECT * FROM onecolumn) + +# TODO +# statement error 1065 +# SELECT * FROM (onecolumn AS a JOIN onecolumn AS b ON x > 32) + +# TODO +# 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(sid int primary key, x INT) + +statement ok +INSERT INTO s(sid, x) VALUES (0, 1),(1, 2),(2, 3),(3, 4),(4, 5),(5, 6),(6, 7),(7, 8),(8, 9),(9, 10) + +statement ok +drop table if exists pairs + +statement ok +drop table if exists square + +statement ok +CREATE TABLE square (n INT PRIMARY KEY, 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 (pid INT PRIMARY KEY, a INT, b INT) + +statement ok +INSERT INTO pairs VALUES (0,1,1), (1,1,2), (2,1,3), (3,1,4), (4,1,5), (5,1,6), (6,2,3), (7,2,4), (8,2,5), (9,2,6), (10,3,4), (11,3,5), (12,3,6), (13,4,5), (14,4,6) + +query IIII +SELECT * FROM pairs, square WHERE pairs.b = square.n order by a +---- +0 1 1 1 1 +1 1 2 2 4 +2 1 3 3 9 +3 1 4 4 16 +4 1 5 5 25 +5 1 6 6 36 +6 2 3 3 9 +7 2 4 4 16 +8 2 5 5 25 +9 2 6 6 36 +10 3 4 4 16 +11 3 5 5 25 +12 3 6 6 36 +13 4 5 5 25 +14 4 6 6 36 + +query IIII +SELECT * FROM pairs, square WHERE pairs.a + pairs.b = square.sq +---- +2 1 3 2 4 +12 3 6 3 9 +13 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 + +# TODO: Full Join on nested loop join +# 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 + +# TODO: Full Join on nested loop join +# 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 rowsort +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 1 2 null null +10 3 4 2 4 +11 3 5 null null +12 3 6 null null +13 4 5 null null +14 4 6 null null +2 1 3 null null +3 1 4 null null +4 1 5 null null +5 1 6 null null +6 2 3 null null +7 2 4 2 4 +8 2 5 null null +9 2 6 null null + +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 +---- +null null null 2 4 +10 3 4 2 4 +null null null 3 9 +null null null 4 16 +null null null 5 25 +null 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 PRIMARY KEY, x INT, col2 INT, y INT) + +statement ok +CREATE TABLE t2 (col3 INT PRIMARY KEY, 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 PRIMARY KEY, b INT, c INT, d INT) + +statement ok +CREATE TABLE pkBC (a INT PRIMARY KEY, b INT, c INT, d INT) + +statement ok +CREATE TABLE pkBAC (a INT PRIMARY KEY, b INT, c INT, d INT) + +statement ok +CREATE TABLE pkBAD (a INT PRIMARY KEY, 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 (aid INT PRIMARY KEY, a INT, s VARCHAR) + +statement ok +INSERT INTO str1 VALUES (0, 1, 'a' ), (1, 2, 'A'), (2, 3, 'c'), (3, 4, 'D') + +statement ok +CREATE TABLE str2 (bid INT PRIMARY KEY, a INT, s VARCHAR) + +statement ok +INSERT INTO str2 VALUES (0, 1, 'A'), (1, 2, 'B'), (2, 3, 'C'), (3, 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 + +# FIXME: The fields output by Using are determined by JoinType. +query TTT +SELECT s, str1.s, str2.s FROM str1 RIGHT OUTER JOIN str2 USING(s) order by str2.s +---- +A A A +null null B +null null C +null null E + +query ITIT +SELECT * FROM str1 LEFT OUTER JOIN str2 ON str1.s = str2.s order by str1.a +---- +0 1 a null null null +1 2 A 0 1 A +2 3 c null null null +3 4 D null null null + +statement ok +INSERT INTO str1 VALUES (4, 1, 'a' ), (5, 2, 'A'), (6, 3, 'c'), (7, 4, 'D') + +query ITIT +select * from str1 right join str2 on str1.s = str2.s order by str2.a +---- +1 2 A 0 1 A +5 2 A 0 1 A +null null null 1 2 B +null null null 2 3 C +null null null 3 4 E + +query ITIT +select * from str1 right join str2 on false order by str2.a +---- +null null null 0 1 A +null null null 1 2 B +null null null 2 3 C +null null null 3 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 (uid INT PRIMARY key, x INT, y INT, u INT) + +statement ok +INSERT INTO xyu VALUES (0, 0, 0, 0), (1, 1, 1, 1), (2, 3, 1, 31), (3, 3, 2, 32), (4, 4, 4, 44) + +statement ok +CREATE TABLE xyv (vid INT PRIMARY key, x INT, y INT, v INT) + +statement ok +INSERT INTO xyv VALUES (5, 1, 1, 1), (6, 2, 2, 2), (7, 3, 1, 31), (8, 3, 3, 33), (9, 5, 5, 55) + +query IIII +SELECT * FROM xyu INNER JOIN xyv USING(x, y) WHERE x > 2 +---- +2 3 1 31 7 31 + +query IIII +SELECT * FROM xyu LEFT OUTER JOIN xyv USING(x, y) WHERE x > 2 order by xyu.uid +---- +2 3 1 31 7 31 +3 3 2 32 null null +4 4 4 44 null null + +# FIXME: The fields output by Using are determined by JoinType. +# 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 5 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 5 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 order by xyu.uid +---- +0 0 0 0 null null null null +1 1 1 1 5 1 1 1 +2 3 1 31 null null null null +3 3 2 32 null null null null +4 4 4 44 null 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 5 1 1 1 +null null null null 6 2 2 2 +null null null null 7 3 1 31 +null null null null 8 3 3 33 +null null null null 9 5 5 55 + +query IIII rowsort +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 +---- +31 2 3 1 31 7 +32 3 3 2 null null +44 4 4 4 null null + +# FIXME: The fields output by Using are determined by JoinType. +# 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 rowsort +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 0 null null null null +1 1 1 1 1 5 1 1 +31 2 3 1 null null null null +32 3 3 2 null null null null +44 4 4 4 null 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 5 1 1 +null null null null 2 6 2 2 +null null null null 31 7 3 1 +null null null null 33 8 3 3 +null null null null 55 9 5 5 + +statement ok +drop table if exists l + +statement ok +drop table if exists r + +statement ok +CREATE TABLE l (a INT PRIMARY KEY, b1 INT) + +statement ok +CREATE TABLE r (a INT PRIMARY KEY, 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 +---- +3 1 1 + +# FIXME: The fields output by Using are determined by JoinType. +# 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 + +# TODO: Join Cast +# statement ok +# CREATE TABLE foo (a INT PRIMARY KEY, b INT, c FLOAT, d FLOAT) +# TODO: Join Cast +# statement ok +# INSERT INTO foo VALUES (1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3) +# TODO: Join Cast +# statement ok +# CREATE TABLE bar (a INT PRIMARY KEY, b FLOAT, c FLOAT, d INT) +# TODO: Join Cast +# statement ok +# INSERT INTO bar VALUES (1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3) +# TODO: Join Cast +# 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 +# TODO: Join Cast +# 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 +# TODO: Join Cast +# 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 +# TODO: Join Cast +# 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 +# TODO: Join Cast +# 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 +# TODO: Join Cast +# 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 +# TODO: Join Cast +# 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 +# TODO: Join Cast +# 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 +# TODO: Join Cast +# 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(aid, x) RIGHT JOIN twocolumn ON false order by y +---- +null null 3 45 45 +null null 0 44 51 +null null 1 null 52 +null null 2 42 53 + +statement ok +SELECT * FROM onecolumn AS a(aid, x) RIGHT JOIN twocolumn ON true where false order by y + +statement ok +SELECT * FROM onecolumn AS a(aid, x) LEFT JOIN twocolumn ON true where twocolumn.x > 50 order by y + +statement ok +insert into onecolumn values(42) + +# TODO: Explicitly declare Semi/AntiJoin +# 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 +# TODO: Explicitly declare Semi/AntiJoin +# 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 +# TODO: Explicitly declare Semi/AntiJoin +# 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 +# TODO: Explicitly declare Semi/AntiJoin +# 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 +# TODO: Explicitly declare Semi/AntiJoin +# 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 +# TODO: Explicitly declare Semi/AntiJoin +# 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 +# TODO: Explicitly declare Semi/AntiJoin +# 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 +# TODO: Explicitly declare Semi/AntiJoin +# statement ok +# select * from onecolumn as a right anti join twocolumn as b on true order by b.x +# TODO: Explicitly declare Semi/AntiJoin +# statement ok +# select * from onecolumn as a right semi join twocolumn as b on false order by b.x +# TODO: Explicitly declare Semi/AntiJoin +# 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 +---- +0 44 0 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 +---- +2 42 2 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 +---- +0 44 0 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)