This is an automated email from the ASF dual-hosted git repository.
alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git
The following commit(s) were added to refs/heads/main by this push:
new 344f0897d1 Revert "Improve push down filter of join (#13184)" (#13229)
344f0897d1 is described below
commit 344f0897d105cdd588d7a7ee8cef0646f844937a
Author: Emil Ejbyfeldt <[email protected]>
AuthorDate: Sat Nov 2 11:22:21 2024 +0100
Revert "Improve push down filter of join (#13184)" (#13229)
This reverts commit 7ae1ccb4971474d376e5bed1d7116fcaf23f906f.
---
datafusion/optimizer/src/push_down_filter.rs | 337 +++---------
datafusion/optimizer/src/utils.rs | 8 -
.../test_files/join_disable_repartition_joins.slt | 23 +-
.../test_files/push_down_filter_join.slt | 612 ---------------------
4 files changed, 71 insertions(+), 909 deletions(-)
diff --git a/datafusion/optimizer/src/push_down_filter.rs
b/datafusion/optimizer/src/push_down_filter.rs
index 269ce29100..acb7ba0fa7 100644
--- a/datafusion/optimizer/src/push_down_filter.rs
+++ b/datafusion/optimizer/src/push_down_filter.rs
@@ -36,9 +36,7 @@ use datafusion_expr::{
};
use crate::optimizer::ApplyOrder;
-use crate::utils::{
- contain_all_columns, has_all_column_refs, is_restrict_null_predicate,
-};
+use crate::utils::{has_all_column_refs, is_restrict_null_predicate};
use crate::{OptimizerConfig, OptimizerRule};
/// Optimizer rule for pushing (moving) filter expressions down in a plan so
@@ -217,19 +215,19 @@ impl<'a> ColumnChecker<'a> {
}
/// Return true if the expression references only columns from the left
side of the join
- fn left_only(&mut self, column_refs: &HashSet<&Column>) -> bool {
+ fn is_left_only(&mut self, predicate: &Expr) -> bool {
if self.left_columns.is_none() {
self.left_columns = Some(schema_columns(self.left_schema));
}
- contain_all_columns(column_refs, self.left_columns.as_ref().unwrap())
+ has_all_column_refs(predicate, self.left_columns.as_ref().unwrap())
}
/// Return true if the expression references only columns from the right
side of the join
- fn right_only(&mut self, column_refs: &HashSet<&Column>) -> bool {
+ fn is_right_only(&mut self, predicate: &Expr) -> bool {
if self.right_columns.is_none() {
self.right_columns = Some(schema_columns(self.right_schema));
}
- contain_all_columns(column_refs, self.right_columns.as_ref().unwrap())
+ has_all_column_refs(predicate, self.right_columns.as_ref().unwrap())
}
}
@@ -413,13 +411,10 @@ fn extract_or_clause(expr: &Expr, schema_columns:
&HashSet<Column>) -> Option<Ex
/// push down join/cross-join
fn push_down_all_join(
predicates: Vec<Expr>,
- inferred_from_predicates: Vec<Expr>,
+ inferred_join_predicates: Vec<Expr>,
mut join: Join,
on_filter: Vec<Expr>,
- inferred_from_on_filter: Vec<Expr>,
) -> Result<Transformed<LogicalPlan>> {
- assert_ne!(join.join_type, JoinType::Full);
-
let is_inner_join = join.join_type == JoinType::Inner;
// Get pushable predicates from current optimizer state
let (left_preserved, right_preserved) = lr_is_preserved(join.join_type);
@@ -435,24 +430,14 @@ fn push_down_all_join(
let mut keep_predicates = vec![];
let mut join_conditions = vec![];
let mut checker = ColumnChecker::new(left_schema, right_schema);
-
for predicate in predicates {
- let columns = predicate.column_refs();
- macro_rules! restrict_null {
- () => {{
- let predicate_cloned = predicate.clone();
- let cols = columns.iter().cloned();
- is_restrict_null_predicate(predicate_cloned,
cols).unwrap_or(false)
- }};
- }
-
- if checker.left_only(&columns) && (left_preserved || restrict_null!())
{
+ if left_preserved && checker.is_left_only(&predicate) {
left_push.push(predicate);
- } else if checker.right_only(&columns) && (right_preserved ||
restrict_null!()) {
+ } else if right_preserved && checker.is_right_only(&predicate) {
right_push.push(predicate);
} else if is_inner_join && can_evaluate_as_join_condition(&predicate)?
{
- // Here we do not differ it is eq or non-eq predicate,
ExtractEquijoinPredicate will
- // extract the eq predicate and convert to the join on condition
+ // Here we do not differ it is eq or non-eq predicate,
ExtractEquijoinPredicate will extract the eq predicate
+ // and convert to the join on condition
join_conditions.push(predicate);
} else {
keep_predicates.push(predicate);
@@ -460,13 +445,10 @@ fn push_down_all_join(
}
// For infer predicates, if they can not push through join, just drop them
- // Because we check whether it is_restrict_null in the process of Infer,
there is no need to
- // check again
- for predicate in inferred_from_predicates {
- let columns = predicate.column_refs();
- if checker.left_only(&columns) {
+ for predicate in inferred_join_predicates {
+ if left_preserved && checker.is_left_only(&predicate) {
left_push.push(predicate);
- } else if checker.right_only(&columns) {
+ } else if right_preserved && checker.is_right_only(&predicate) {
right_push.push(predicate);
}
}
@@ -474,24 +456,15 @@ fn push_down_all_join(
let mut on_filter_join_conditions = vec![];
let (on_left_preserved, on_right_preserved) =
on_lr_is_preserved(join.join_type);
- for on in on_filter {
- let columns = on.column_refs();
- if on_left_preserved && checker.left_only(&columns) {
- left_push.push(on)
- } else if on_right_preserved && checker.right_only(&columns) {
- right_push.push(on)
- } else {
- on_filter_join_conditions.push(on)
- }
- }
-
- // For infer predicates, if they can not push through join, just drop them
- for on in inferred_from_on_filter {
- let columns = on.column_refs();
- if on_left_preserved && checker.left_only(&columns) {
- left_push.push(on)
- } else if on_right_preserved && checker.right_only(&columns) {
- right_push.push(on)
+ if !on_filter.is_empty() {
+ for on in on_filter {
+ if on_left_preserved && checker.is_left_only(&on) {
+ left_push.push(on)
+ } else if on_right_preserved && checker.is_right_only(&on) {
+ right_push.push(on)
+ } else {
+ on_filter_join_conditions.push(on)
+ }
}
}
@@ -547,17 +520,6 @@ fn push_down_join(
join: Join,
parent_predicate: Option<&Expr>,
) -> Result<Transformed<LogicalPlan>> {
- if matches!(join.join_type, JoinType::Full) {
- let plan = LogicalPlan::Join(join);
- return Ok(match parent_predicate {
- Some(predicate) =>
Transformed::yes(LogicalPlan::Filter(Filter::try_new(
- predicate.clone(),
- Arc::new(plan),
- )?)),
- None => Transformed::no(plan),
- });
- }
-
// Split the parent predicate into individual conjunctive parts.
let predicates = parent_predicate
.map_or_else(Vec::new, |pred| split_conjunction_owned(pred.clone()));
@@ -569,24 +531,17 @@ fn push_down_join(
.map_or_else(Vec::new, |filter|
split_conjunction_owned(filter.clone()));
// Are there any new join predicates that can be inferred from the filter
expressions?
- let (inferred_from_predicates, inferred_from_on_filter) =
+ let inferred_join_predicates =
infer_join_predicates(&join, &predicates, &on_filters)?;
if on_filters.is_empty()
- && inferred_from_on_filter.is_empty()
&& predicates.is_empty()
- && inferred_from_predicates.is_empty()
+ && inferred_join_predicates.is_empty()
{
return Ok(Transformed::no(LogicalPlan::Join(join)));
}
- push_down_all_join(
- predicates,
- inferred_from_predicates,
- join,
- on_filters,
- inferred_from_on_filter,
- )
+ push_down_all_join(predicates, inferred_join_predicates, join, on_filters)
}
/// Extracts any equi-join join predicates from the given filter expressions.
@@ -599,13 +554,11 @@ fn push_down_join(
/// * `on_filters` filters from the join ON clause that have not already been
/// identified as join predicates
///
-/// # Return Value
-/// A tuple of Expr Vec - (inferred_from_predicates, inferred_from_on_filters).
fn infer_join_predicates(
join: &Join,
predicates: &[Expr],
on_filters: &[Expr],
-) -> Result<(Vec<Expr>, Vec<Expr>)> {
+) -> Result<Vec<Expr>> {
// Only allow both side key is column.
let join_col_keys = join
.on
@@ -626,7 +579,6 @@ fn infer_join_predicates(
predicates,
&mut inferred_predicates,
)?;
- let inferred_from_predicates = inferred_predicates.take_all();
infer_join_predicates_from_on_filters(
&join_col_keys,
@@ -634,9 +586,8 @@ fn infer_join_predicates(
on_filters,
&mut inferred_predicates,
)?;
- let inferred_from_on_filters = inferred_predicates.predicates;
- Ok((inferred_from_predicates, inferred_from_on_filters))
+ Ok(inferred_predicates.predicates)
}
/// Inferred predicates collector.
@@ -660,12 +611,6 @@ impl InferredPredicates {
}
}
- fn take_all(&mut self) -> Vec<Expr> {
- let mut temp = vec![];
- std::mem::swap(&mut self.predicates, &mut temp);
- temp
- }
-
fn try_build_predicate(
&mut self,
predicate: Expr,
@@ -2165,10 +2110,11 @@ mod tests {
// filter not duplicated nor pushed down - i.e. noop
let expected = "\
- Left Join: Using test.a = test2.a\
- \n TableScan: test, full_filters=[test.a <= Int64(1)]\
- \n Projection: test2.a\
- \n TableScan: test2, full_filters=[test2.a <= Int64(1)]";
+ Filter: test2.a <= Int64(1)\
+ \n Left Join: Using test.a = test2.a\
+ \n TableScan: test, full_filters=[test.a <= Int64(1)]\
+ \n Projection: test2.a\
+ \n TableScan: test2";
assert_optimized_plan_eq(plan, expected)
}
@@ -2202,10 +2148,11 @@ mod tests {
// filter not duplicated nor pushed down - i.e. noop
let expected = "\
- Right Join: Using test.a = test2.a\
- \n TableScan: test, full_filters=[test.a <= Int64(1)]\
- \n Projection: test2.a\
- \n TableScan: test2, full_filters=[test2.a <= Int64(1)]";
+ Filter: test.a <= Int64(1)\
+ \n Right Join: Using test.a = test2.a\
+ \n TableScan: test\
+ \n Projection: test2.a\
+ \n TableScan: test2, full_filters=[test2.a <= Int64(1)]";
assert_optimized_plan_eq(plan, expected)
}
@@ -2243,7 +2190,7 @@ mod tests {
Left Join: Using test.a = test2.a\
\n TableScan: test, full_filters=[test.a <= Int64(1)]\
\n Projection: test2.a\
- \n TableScan: test2, full_filters=[test2.a <= Int64(1)]";
+ \n TableScan: test2";
assert_optimized_plan_eq(plan, expected)
}
@@ -2279,7 +2226,7 @@ mod tests {
// filter sent to right side of join, not duplicated to the left
let expected = "\
Right Join: Using test.a = test2.a\
- \n TableScan: test, full_filters=[test.a <= Int64(1)]\
+ \n TableScan: test\
\n Projection: test2.a\
\n TableScan: test2, full_filters=[test2.a <= Int64(1)]";
assert_optimized_plan_eq(plan, expected)
@@ -2328,47 +2275,6 @@ mod tests {
assert_optimized_plan_eq(plan, expected)
}
- #[test]
- fn join_with_non_restrict_null_predicate() -> Result<()> {
- let table_scan = test_table_scan()?;
- let left = LogicalPlanBuilder::from(table_scan)
- .project(vec![col("a"), col("b"), col("c")])?
- .build()?;
- let right_table_scan = test_table_scan_with_name("test2")?;
- let right = LogicalPlanBuilder::from(right_table_scan)
- .project(vec![col("a"), col("b"), col("c")])?
- .build()?;
- let filter = col("test.b").is_null().and(col("test2.b").is_null());
- let plan = LogicalPlanBuilder::from(left)
- .join(
- right,
- JoinType::Inner,
- (vec![Column::from_name("a")], vec![Column::from_name("a")]),
- None,
- )?
- .filter(filter)?
- .build()?;
-
- // not part of the test, just good to know:
- assert_eq!(
- format!("{plan}"),
- "Filter: test.b IS NULL AND test2.b IS NULL\
- \n Inner Join: test.a = test2.a\
- \n Projection: test.a, test.b, test.c\
- \n TableScan: test\
- \n Projection: test2.a, test2.b, test2.c\
- \n TableScan: test2"
- );
-
- let expected = "\
- Inner Join: test.a = test2.a\
- \n Projection: test.a, test.b, test.c\
- \n TableScan: test, full_filters=[test.b IS NULL]\
- \n Projection: test2.a, test2.b, test2.c\
- \n TableScan: test2, full_filters=[test2.b IS NULL]";
- assert_optimized_plan_eq(plan, expected)
- }
-
/// join filter should be completely removed after pushdown
#[test]
fn join_filter_removed() -> Result<()> {
@@ -2490,49 +2396,7 @@ mod tests {
\n Projection: test.a, test.b, test.c\
\n TableScan: test\
\n Projection: test2.a, test2.b, test2.c\
- \n TableScan: test2, full_filters=[test2.c > UInt32(4), test2.a >
UInt32(1)]";
- assert_optimized_plan_eq(plan, expected)
- }
-
- #[test]
- fn left_join_with_non_restrict_null_predicate() -> Result<()> {
- let table_scan = test_table_scan()?;
- let left = LogicalPlanBuilder::from(table_scan)
- .project(vec![col("a"), col("b"), col("c")])?
- .build()?;
- let right_table_scan = test_table_scan_with_name("test2")?;
- let right = LogicalPlanBuilder::from(right_table_scan)
- .project(vec![col("a"), col("b"), col("c")])?
- .build()?;
- let filter = col("test.b").is_null().and(col("test2.b").is_null());
- let plan = LogicalPlanBuilder::from(left)
- .join(
- right,
- JoinType::Left,
- (vec![Column::from_name("a")], vec![Column::from_name("a")]),
- None,
- )?
- .filter(filter)?
- .build()?;
-
- // not part of the test, just good to know:
- assert_eq!(
- format!("{plan}"),
- "Filter: test.b IS NULL AND test2.b IS NULL\
- \n Left Join: test.a = test2.a\
- \n Projection: test.a, test.b, test.c\
- \n TableScan: test\
- \n Projection: test2.a, test2.b, test2.c\
- \n TableScan: test2"
- );
-
- let expected = "\
- Filter: test2.b IS NULL\
- \n Left Join: test.a = test2.a\
- \n Projection: test.a, test.b, test.c\
- \n TableScan: test, full_filters=[test.b IS NULL]\
- \n Projection: test2.a, test2.b, test2.c\
- \n TableScan: test2";
+ \n TableScan: test2, full_filters=[test2.c > UInt32(4)]";
assert_optimized_plan_eq(plan, expected)
}
@@ -2579,87 +2443,6 @@ mod tests {
assert_optimized_plan_eq(plan, expected)
}
- #[test]
- fn right_join_with_non_restrict_null_predicate() -> Result<()> {
- let table_scan = test_table_scan()?;
- let left = LogicalPlanBuilder::from(table_scan)
- .project(vec![col("a"), col("b"), col("c")])?
- .build()?;
- let right_table_scan = test_table_scan_with_name("test2")?;
- let right = LogicalPlanBuilder::from(right_table_scan)
- .project(vec![col("a"), col("b"), col("c")])?
- .build()?;
- let filter = col("test.b").is_null().and(col("test2.b").is_null());
- let plan = LogicalPlanBuilder::from(left)
- .join(
- right,
- JoinType::Right,
- (vec![Column::from_name("a")], vec![Column::from_name("a")]),
- None,
- )?
- .filter(filter)?
- .build()?;
-
- // not part of the test, just good to know:
- assert_eq!(
- format!("{plan}"),
- "Filter: test.b IS NULL AND test2.b IS NULL\
- \n Right Join: test.a = test2.a\
- \n Projection: test.a, test.b, test.c\
- \n TableScan: test\
- \n Projection: test2.a, test2.b, test2.c\
- \n TableScan: test2"
- );
-
- let expected = "\
- Filter: test.b IS NULL\
- \n Right Join: test.a = test2.a\
- \n Projection: test.a, test.b, test.c\
- \n TableScan: test\
- \n Projection: test2.a, test2.b, test2.c\
- \n TableScan: test2, full_filters=[test2.b IS NULL]";
- assert_optimized_plan_eq(plan, expected)
- }
-
- #[test]
- fn full_join() -> Result<()> {
- let table_scan = test_table_scan()?;
- let left = LogicalPlanBuilder::from(table_scan)
- .project(vec![col("a"), col("b"), col("c")])?
- .build()?;
- let right_table_scan = test_table_scan_with_name("test2")?;
- let right = LogicalPlanBuilder::from(right_table_scan)
- .project(vec![col("a"), col("b"), col("c")])?
- .build()?;
- let filter = col("test.a")
- .gt(lit(1u32))
- .and(col("test.b").lt(col("test2.b")))
- .and(col("test2.c").gt(lit(4u32)));
- let plan = LogicalPlanBuilder::from(left)
- .join(
- right,
- JoinType::Full,
- (vec![Column::from_name("a")], vec![Column::from_name("a")]),
- None,
- )?
- .filter(filter)?
- .build()?;
-
- // not part of the test, just good to know:
- assert_eq!(
- format!("{plan}"),
- "Filter: test.a > UInt32(1) AND test.b < test2.b AND test2.c >
UInt32(4)\
- \n Full Join: test.a = test2.a\
- \n Projection: test.a, test.b, test.c\
- \n TableScan: test\
- \n Projection: test2.a, test2.b, test2.c\
- \n TableScan: test2"
- );
-
- let expected = &format!("{plan}");
- assert_optimized_plan_eq(plan, expected)
- }
-
/// single table predicate parts of ON condition should not be pushed
#[test]
fn full_join_on_with_filter() -> Result<()> {
@@ -3183,10 +2966,11 @@ Projection: a, b
// Inferred the predicate `test1.a <= Int64(1)` and push it down to
the left side.
let expected = "\
- LeftSemi Join: test1.a = test2.a\
- \n TableScan: test1, full_filters=[test1.a <= Int64(1)]\
- \n Projection: test2.a, test2.b\
- \n TableScan: test2, full_filters=[test2.a <= Int64(1)]";
+ Filter: test2.a <= Int64(1)\
+ \n LeftSemi Join: test1.a = test2.a\
+ \n TableScan: test1, full_filters=[test1.a <= Int64(1)]\
+ \n Projection: test2.a, test2.b\
+ \n TableScan: test2";
assert_optimized_plan_eq(plan, expected)
}
@@ -3263,10 +3047,11 @@ Projection: a, b
// Inferred the predicate `test2.a <= Int64(1)` and push it down to
the right side.
let expected = "\
- RightSemi Join: test1.a = test2.a\
- \n TableScan: test1, full_filters=[test1.a <= Int64(1)]\
- \n Projection: test2.a, test2.b\
- \n TableScan: test2, full_filters=[test2.a <= Int64(1)]";
+ Filter: test1.a <= Int64(1)\
+ \n RightSemi Join: test1.a = test2.a\
+ \n TableScan: test1\
+ \n Projection: test2.a, test2.b\
+ \n TableScan: test2, full_filters=[test2.a <= Int64(1)]";
assert_optimized_plan_eq(plan, expected)
}
@@ -3347,11 +3132,12 @@ Projection: a, b
// For left anti, filter of the right side filter can be pushed down.
let expected = "\
- LeftAnti Join: test1.a = test2.a\
- \n Projection: test1.a, test1.b\
- \n TableScan: test1, full_filters=[test1.a > UInt32(2)]\
- \n Projection: test2.a, test2.b\
- \n TableScan: test2, full_filters=[test2.a > UInt32(2)]";
+ Filter: test2.a > UInt32(2)\
+ \n LeftAnti Join: test1.a = test2.a\
+ \n Projection: test1.a, test1.b\
+ \n TableScan: test1, full_filters=[test1.a > UInt32(2)]\
+ \n Projection: test2.a, test2.b\
+ \n TableScan: test2";
assert_optimized_plan_eq(plan, expected)
}
@@ -3437,11 +3223,12 @@ Projection: a, b
// For right anti, filter of the left side can be pushed down.
let expected = "\
- RightAnti Join: test1.a = test2.a\
- \n Projection: test1.a, test1.b\
- \n TableScan: test1, full_filters=[test1.a > UInt32(2)]\
- \n Projection: test2.a, test2.b\
- \n TableScan: test2, full_filters=[test2.a > UInt32(2)]";
+ Filter: test1.a > UInt32(2)\
+ \n RightAnti Join: test1.a = test2.a\
+ \n Projection: test1.a, test1.b\
+ \n TableScan: test1\
+ \n Projection: test2.a, test2.b\
+ \n TableScan: test2, full_filters=[test2.a > UInt32(2)]";
assert_optimized_plan_eq(plan, expected)
}
diff --git a/datafusion/optimizer/src/utils.rs
b/datafusion/optimizer/src/utils.rs
index 9f8d7b7f97..9f325bc01b 100644
--- a/datafusion/optimizer/src/utils.rs
+++ b/datafusion/optimizer/src/utils.rs
@@ -79,14 +79,6 @@ pub fn optimize_children(
/// Returns true if `expr` contains all columns in `schema_cols`
pub(crate) fn has_all_column_refs(expr: &Expr, schema_cols: &HashSet<Column>)
-> bool {
let column_refs = expr.column_refs();
- contain_all_columns(&column_refs, schema_cols)
-}
-
-/// Returns true if `column_refs` contains all columns in `schema_cols`
-pub(crate) fn contain_all_columns(
- column_refs: &HashSet<&Column>,
- schema_cols: &HashSet<Column>,
-) -> bool {
// note can't use HashSet::intersect because of different types (owned vs
References)
schema_cols
.iter()
diff --git
a/datafusion/sqllogictest/test_files/join_disable_repartition_joins.slt
b/datafusion/sqllogictest/test_files/join_disable_repartition_joins.slt
index 44c5ed0d2d..cf897d628d 100644
--- a/datafusion/sqllogictest/test_files/join_disable_repartition_joins.slt
+++ b/datafusion/sqllogictest/test_files/join_disable_repartition_joins.slt
@@ -89,25 +89,20 @@ logical_plan
02)--Projection: t2.a AS a2, t2.b
03)----RightSemi Join: t1.d = t2.d, t1.c = t2.c
04)------SubqueryAlias: t1
-05)--------Filter: annotated_data.d = Int32(3)
-06)----------TableScan: annotated_data projection=[c, d],
partial_filters=[annotated_data.d = Int32(3)]
-07)------SubqueryAlias: t2
-08)--------Filter: annotated_data.d = Int32(3)
-09)----------TableScan: annotated_data projection=[a, b, c, d],
partial_filters=[annotated_data.d = Int32(3)]
+05)--------TableScan: annotated_data projection=[c, d]
+06)------SubqueryAlias: t2
+07)--------Filter: annotated_data.d = Int32(3)
+08)----------TableScan: annotated_data projection=[a, b, c, d],
partial_filters=[annotated_data.d = Int32(3)]
physical_plan
01)SortPreservingMergeExec: [a2@0 ASC NULLS LAST, b@1 ASC NULLS LAST], fetch=10
02)--ProjectionExec: expr=[a@0 as a2, b@1 as b]
03)----CoalesceBatchesExec: target_batch_size=8192, fetch=10
04)------HashJoinExec: mode=CollectLeft, join_type=RightSemi, on=[(d@1, d@3),
(c@0, c@2)], projection=[a@0, b@1]
-05)--------CoalescePartitionsExec
-06)----------CoalesceBatchesExec: target_batch_size=8192
-07)------------FilterExec: d@1 = 3
-08)--------------RepartitionExec: partitioning=RoundRobinBatch(4),
input_partitions=1
-09)----------------CsvExec: file_groups={1 group:
[[WORKSPACE_ROOT/datafusion/core/tests/data/window_2.csv]]}, projection=[c, d],
has_header=true
-10)--------CoalesceBatchesExec: target_batch_size=8192
-11)----------FilterExec: d@3 = 3
-12)------------RepartitionExec: partitioning=RoundRobinBatch(4),
input_partitions=1
-13)--------------CsvExec: file_groups={1 group:
[[WORKSPACE_ROOT/datafusion/core/tests/data/window_2.csv]]}, projection=[a, b,
c, d], output_ordering=[a@0 ASC NULLS LAST, b@1 ASC NULLS LAST, c@2 ASC NULLS
LAST], has_header=true
+05)--------CsvExec: file_groups={1 group:
[[WORKSPACE_ROOT/datafusion/core/tests/data/window_2.csv]]}, projection=[c, d],
has_header=true
+06)--------CoalesceBatchesExec: target_batch_size=8192
+07)----------FilterExec: d@3 = 3
+08)------------RepartitionExec: partitioning=RoundRobinBatch(4),
input_partitions=1
+09)--------------CsvExec: file_groups={1 group:
[[WORKSPACE_ROOT/datafusion/core/tests/data/window_2.csv]]}, projection=[a, b,
c, d], output_ordering=[a@0 ASC NULLS LAST, b@1 ASC NULLS LAST, c@2 ASC NULLS
LAST], has_header=true
# preserve_right_semi_join
query II nosort
diff --git a/datafusion/sqllogictest/test_files/push_down_filter_join.slt
b/datafusion/sqllogictest/test_files/push_down_filter_join.slt
deleted file mode 100644
index f687c542a6..0000000000
--- a/datafusion/sqllogictest/test_files/push_down_filter_join.slt
+++ /dev/null
@@ -1,612 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-
-# http://www.apache.org/licenses/LICENSE-2.0
-
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-# Contents
-# Inner Join
-# `WHERE` clause
-# `ON` clause
-# Left Outer Join
-# `WHERE` clause
-# `ON` clause
-# Right Outer Join
-# `WHERE` clause
-# `ON` clause
-# Full Outer Join
-# `WHERE` clause
-# `ON` clause
-
-# Create table t1
-statement ok
-CREATE TABLE t1(t1_id INT, t1_name VARCHAR) AS VALUES
-(11, 'a'),
-(22, 'b'),
-(33, 'c'),
-(44, 'd'),
-(77, 'e'),
-(88, NULL),
-(99, NULL)
-
-# Create table t2
-statement ok
-CREATE TABLE t2(t2_id INT, t2_name VARCHAR) AS VALUES
-(11, 'z'),
-(22, NULL),
-(44, 'x'),
-(55, 'w'),
-(99, 'u')
-
-# Inner Join
-
-## `WHERE` clause
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id ORDER BY t1_id, t1_name,
t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-44 d 44 x
-99 NULL 99 u
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_name = 'a'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_name = 'z'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-99 NULL 99 u
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-22 b 22 NULL
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_id <= 22
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_id <= 22
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_id IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_id IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-
-## `ON` clause
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_name = 'a'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_name = 'z'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-99 NULL 99 u
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-22 b 22 NULL
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-99 NULL 99 u
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-22 b 22 NULL
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_id <= 22 ORDER
BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-
-query ITIT
-SELECT * FROM t1 INNER JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_id <= 22 ORDER
BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-
-# Left Outer Join
-
-## `WHERE` clause
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id ORDER BY t1_id, t1_name,
t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-33 c NULL NULL
-44 d 44 x
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL 99 u
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_name = 'a'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_name = 'z'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-88 NULL NULL NULL
-99 NULL 99 u
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-22 b 22 NULL
-33 c NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_id <= 22
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_id <= 22
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_id IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_id IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-33 c NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-
-## `ON` clause
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_name = 'a'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b NULL NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL NULL NULL
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_name = 'z'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b NULL NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL NULL NULL
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a NULL NULL
-22 b NULL NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL 99 u
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a NULL NULL
-22 b 22 NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL NULL NULL
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a NULL NULL
-22 b NULL NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL 99 u
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a NULL NULL
-22 b 22 NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL NULL NULL
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_id <= 22 ORDER
BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL NULL NULL
-
-query ITIT
-SELECT * FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_id <= 22 ORDER
BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL NULL NULL
-
-# Right Outer Join
-
-## `WHERE` clause
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id ORDER BY t1_id, t1_name,
t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-44 d 44 x
-99 NULL 99 u
-NULL NULL 55 w
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_name = 'a'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_name = 'z'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-99 NULL 99 u
-NULL NULL 55 w
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-22 b 22 NULL
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_id <= 22
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_id <= 22
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_id IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-NULL NULL 55 w
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_id IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-
-## `ON` clause
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_name = 'a'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-NULL NULL 22 NULL
-NULL NULL 44 x
-NULL NULL 55 w
-NULL NULL 99 u
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_name = 'z'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-NULL NULL 22 NULL
-NULL NULL 44 x
-NULL NULL 55 w
-NULL NULL 99 u
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-99 NULL 99 u
-NULL NULL 11 z
-NULL NULL 22 NULL
-NULL NULL 44 x
-NULL NULL 55 w
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-22 b 22 NULL
-NULL NULL 11 z
-NULL NULL 44 x
-NULL NULL 55 w
-NULL NULL 99 u
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-99 NULL 99 u
-NULL NULL 11 z
-NULL NULL 22 NULL
-NULL NULL 44 x
-NULL NULL 55 w
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-22 b 22 NULL
-NULL NULL 11 z
-NULL NULL 44 x
-NULL NULL 55 w
-NULL NULL 99 u
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_id <= 22 ORDER
BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-NULL NULL 44 x
-NULL NULL 55 w
-NULL NULL 99 u
-
-query ITIT
-SELECT * FROM t1 RIGHT JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_id <= 22 ORDER
BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-NULL NULL 44 x
-NULL NULL 55 w
-NULL NULL 99 u
-
-# Full Outer Join
-
-## `WHERE` clause
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id ORDER BY t1_id, t1_name,
t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-33 c NULL NULL
-44 d 44 x
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL 99 u
-NULL NULL 55 w
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_name = 'a'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_name = 'z'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-88 NULL NULL NULL
-99 NULL 99 u
-NULL NULL 55 w
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-22 b 22 NULL
-33 c NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_id <= 22
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_id <= 22
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id WHERE t1.t1_id IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-NULL NULL 55 w
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id WHERE t2.t2_id IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-33 c NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-
-## `ON` clause
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_name = 'a'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b NULL NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL NULL NULL
-NULL NULL 22 NULL
-NULL NULL 44 x
-NULL NULL 55 w
-NULL NULL 99 u
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_name = 'z'
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b NULL NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL NULL NULL
-NULL NULL 22 NULL
-NULL NULL 44 x
-NULL NULL 55 w
-NULL NULL 99 u
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a NULL NULL
-22 b NULL NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL 99 u
-NULL NULL 11 z
-NULL NULL 22 NULL
-NULL NULL 44 x
-NULL NULL 55 w
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a NULL NULL
-22 b 22 NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL NULL NULL
-NULL NULL 11 z
-NULL NULL 44 x
-NULL NULL 55 w
-NULL NULL 99 u
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a NULL NULL
-22 b NULL NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL 99 u
-NULL NULL 11 z
-NULL NULL 22 NULL
-NULL NULL 44 x
-NULL NULL 55 w
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_name IS NULL
ORDER BY t1_id, t1_name, t2_id, t2_name
-----
-11 a NULL NULL
-22 b 22 NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL NULL NULL
-NULL NULL 11 z
-NULL NULL 44 x
-NULL NULL 55 w
-NULL NULL 99 u
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id AND t1.t1_id <= 22 ORDER
BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL NULL NULL
-NULL NULL 44 x
-NULL NULL 55 w
-NULL NULL 99 u
-
-query ITIT
-SELECT * FROM t1 FULL JOIN t2 ON t1.t1_id = t2.t2_id AND t2.t2_id <= 22 ORDER
BY t1_id, t1_name, t2_id, t2_name
-----
-11 a 11 z
-22 b 22 NULL
-33 c NULL NULL
-44 d NULL NULL
-77 e NULL NULL
-88 NULL NULL NULL
-99 NULL NULL NULL
-NULL NULL 44 x
-NULL NULL 55 w
-NULL NULL 99 u
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]