This is an automated email from the ASF dual-hosted git repository.
agrove pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-datafusion.git
The following commit(s) were added to refs/heads/master by this push:
new fd64e6f2e Improve formatting of logical plans containing subqueries
(#2899)
fd64e6f2e is described below
commit fd64e6f2e5ca5ba8c33c431b75a3ba9441091970
Author: Andy Grove <[email protected]>
AuthorDate: Wed Jul 13 14:13:45 2022 -0600
Improve formatting of logical plans containing subqueries (#2899)
---
datafusion/expr/src/expr.rs | 17 ++
datafusion/expr/src/logical_plan/builder.rs | 30 ++--
datafusion/expr/src/logical_plan/plan.rs | 102 +++++++++---
datafusion/optimizer/src/filter_push_down.rs | 10 +-
datafusion/optimizer/src/limit_push_down.rs | 16 +-
.../optimizer/src/subquery_filter_to_join.rs | 23 +--
datafusion/sql/src/planner.rs | 176 +++++++++------------
7 files changed, 222 insertions(+), 152 deletions(-)
diff --git a/datafusion/expr/src/expr.rs b/datafusion/expr/src/expr.rs
index 202605b4d..ad0b58fac 100644
--- a/datafusion/expr/src/expr.rs
+++ b/datafusion/expr/src/expr.rs
@@ -484,6 +484,23 @@ impl std::fmt::Display for Expr {
/// List of expressions to feed to the functions as arguments
ref args,
} => fmt_function(f, &fun.to_string(), false, args, true),
+ Expr::Exists { negated, .. } => {
+ if *negated {
+ write!(f, "NOT EXISTS (<subquery>)")
+ } else {
+ write!(f, "EXISTS (<subquery>)")
+ }
+ }
+ Expr::InSubquery { negated, .. } => {
+ if *negated {
+ write!(f, "NOT IN (<subquery>)")
+ } else {
+ write!(f, "IN (<subquery>)")
+ }
+ }
+ Expr::ScalarSubquery(_) => {
+ write!(f, "(<subquery>)")
+ }
_ => write!(f, "{:?}", self),
}
}
diff --git a/datafusion/expr/src/logical_plan/builder.rs
b/datafusion/expr/src/logical_plan/builder.rs
index a3abc694d..05cc842f1 100644
--- a/datafusion/expr/src/logical_plan/builder.rs
+++ b/datafusion/expr/src/logical_plan/builder.rs
@@ -1217,11 +1217,13 @@ mod tests {
.filter(exists(Arc::new(subquery)))?
.build()?;
- let expected = "Filter: EXISTS (\
- Subquery: Filter: #foo.a = #bar.a\
- \n Projection: #foo.a\
- \n TableScan: foo)\
- \n Projection: #bar.a\n TableScan: bar";
+ let expected = "Filter: EXISTS (<subquery>)\
+ \n Subquery:\
+ \n Filter: #foo.a = #bar.a\
+ \n Projection: #foo.a\
+ \n TableScan: foo\
+ \n Projection: #bar.a\
+ \n TableScan: bar";
assert_eq!(expected, format!("{:?}", outer_query));
Ok(())
@@ -1243,9 +1245,11 @@ mod tests {
.filter(in_subquery(col("a"), Arc::new(subquery)))?
.build()?;
- let expected = "Filter: #bar.a IN (Subquery: Filter: #foo.a = #bar.a\
- \n Projection: #foo.a\
- \n TableScan: foo)\
+ let expected = "Filter: #bar.a IN (<subquery>)\
+ \n Subquery:\
+ \n Filter: #foo.a = #bar.a\
+ \n Projection: #foo.a\
+ \n TableScan: foo\
\n Projection: #bar.a\
\n TableScan: bar";
assert_eq!(expected, format!("{:?}", outer_query));
@@ -1268,10 +1272,12 @@ mod tests {
.project(vec![scalar_subquery(Arc::new(subquery))])?
.build()?;
- let expected = "Projection: (Subquery: Filter: #foo.a = #bar.a\
- \n Projection: #foo.b\
- \n TableScan: foo)\
- \n TableScan: bar";
+ let expected = "Projection: (<subquery>)\
+ \n Subquery:\
+ \n Filter: #foo.a = #bar.a\
+ \n Projection: #foo.b\
+ \n TableScan: foo\
+ \n TableScan: bar";
assert_eq!(expected, format!("{:?}", outer_query));
Ok(())
diff --git a/datafusion/expr/src/logical_plan/plan.rs
b/datafusion/expr/src/logical_plan/plan.rs
index 0e52ff253..b0c011449 100644
--- a/datafusion/expr/src/logical_plan/plan.rs
+++ b/datafusion/expr/src/logical_plan/plan.rs
@@ -262,7 +262,7 @@ impl LogicalPlan {
}
/// returns all inputs of this `LogicalPlan` node. Does not
- /// include inputs to inputs.
+ /// include inputs to inputs, or subqueries.
pub fn inputs(self: &LogicalPlan) -> Vec<&LogicalPlan> {
match self {
LogicalPlan::Projection(Projection { input, .. }) => vec![input],
@@ -396,8 +396,10 @@ impl LogicalPlan {
}
let recurse = match self {
- LogicalPlan::Projection(Projection { input, .. }) =>
input.accept(visitor)?,
- LogicalPlan::Filter(Filter { input, .. }) =>
input.accept(visitor)?,
+ LogicalPlan::Projection(Projection { .. }) => {
+ self.visit_all_inputs(visitor)?
+ }
+ LogicalPlan::Filter(Filter { .. }) =>
self.visit_all_inputs(visitor)?,
LogicalPlan::Repartition(Repartition { input, .. }) => {
input.accept(visitor)?
}
@@ -457,6 +459,51 @@ impl LogicalPlan {
Ok(true)
}
+
+ /// Visit all inputs, including subqueries
+ pub fn visit_all_inputs<V>(&self, visitor: &mut V) -> Result<bool,
V::Error>
+ where
+ V: PlanVisitor,
+ {
+ for input in self.all_inputs() {
+ if !input.accept(visitor)? {
+ return Ok(false);
+ }
+ }
+
+ Ok(true)
+ }
+
+ /// Get all plan inputs, including subqueries from expressions
+ fn all_inputs(&self) -> Vec<Arc<LogicalPlan>> {
+ let mut inputs = vec![];
+ for expr in self.expressions() {
+ self.collect_subqueries(&expr, &mut inputs);
+ }
+ for input in self.inputs() {
+ inputs.push(Arc::new(input.clone()));
+ }
+ inputs
+ }
+
+ fn collect_subqueries(&self, expr: &Expr, sub: &mut Vec<Arc<LogicalPlan>>)
{
+ match expr {
+ Expr::BinaryExpr { left, right, .. } => {
+ self.collect_subqueries(left, sub);
+ self.collect_subqueries(right, sub);
+ }
+ Expr::Exists { subquery, .. } => {
+ sub.push(Arc::new(LogicalPlan::Subquery(subquery.clone())));
+ }
+ Expr::InSubquery { subquery, .. } => {
+ sub.push(Arc::new(LogicalPlan::Subquery(subquery.clone())));
+ }
+ Expr::ScalarSubquery(subquery) => {
+ sub.push(Arc::new(LogicalPlan::Subquery(subquery.clone())));
+ }
+ _ => {}
+ }
+ }
}
// Various implementations for printing out LogicalPlans
@@ -826,8 +873,8 @@ impl LogicalPlan {
fetch.map_or_else(|| "None".to_string(), |x|
x.to_string())
)
}
- LogicalPlan::Subquery(Subquery { subquery, .. }) => {
- write!(f, "Subquery: {:?}", subquery)
+ LogicalPlan::Subquery(Subquery { .. }) => {
+ write!(f, "Subquery:")
}
LogicalPlan::SubqueryAlias(SubqueryAlias { ref alias, ..
}) => {
write!(f, "SubqueryAlias: {}", alias)
@@ -1245,7 +1292,7 @@ pub struct Subquery {
impl Debug for Subquery {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- write!(f, "Subquery: {:?}", self.subquery)
+ write!(f, "<subquery>")
}
}
@@ -1360,8 +1407,9 @@ pub trait ToStringifiedPlan {
mod tests {
use super::*;
use crate::logical_plan::table_scan;
- use crate::{col, lit};
+ use crate::{col, in_subquery, lit};
use arrow::datatypes::{DataType, Field, Schema};
+ use datafusion_common::Result;
fn employee_schema() -> Schema {
Schema::new(vec![
@@ -1373,42 +1421,47 @@ mod tests {
])
}
- fn display_plan() -> LogicalPlan {
- table_scan(Some("employee_csv"), &employee_schema(), Some(vec![0, 3]))
- .unwrap()
- .filter(col("state").eq(lit("CO")))
- .unwrap()
- .project(vec![col("id")])
- .unwrap()
+ fn display_plan() -> Result<LogicalPlan> {
+ let plan1 = table_scan(Some("employee_csv"), &employee_schema(),
Some(vec![3]))?
+ .build()?;
+
+ table_scan(Some("employee_csv"), &employee_schema(), Some(vec![0, 3]))?
+ .filter(in_subquery(col("state"), Arc::new(plan1)))?
+ .project(vec![col("id")])?
.build()
- .unwrap()
}
#[test]
- fn test_display_indent() {
- let plan = display_plan();
+ fn test_display_indent() -> Result<()> {
+ let plan = display_plan()?;
let expected = "Projection: #employee_csv.id\
- \n Filter: #employee_csv.state = Utf8(\"CO\")\
+ \n Filter: #employee_csv.state IN (<subquery>)\
+ \n Subquery:\
+ \n TableScan: employee_csv projection=[state]\
\n TableScan: employee_csv projection=[id, state]";
assert_eq!(expected, format!("{}", plan.display_indent()));
+ Ok(())
}
#[test]
- fn test_display_indent_schema() {
- let plan = display_plan();
+ fn test_display_indent_schema() -> Result<()> {
+ let plan = display_plan()?;
let expected = "Projection: #employee_csv.id [id:Int32]\
- \n Filter: #employee_csv.state = Utf8(\"CO\")
[id:Int32, state:Utf8]\
- \n TableScan: employee_csv projection=[id, state]
[id:Int32, state:Utf8]";
+ \n Filter: #employee_csv.state IN (<subquery>) [id:Int32, state:Utf8]\
+ \n Subquery: [state:Utf8]\
+ \n TableScan: employee_csv projection=[state] [state:Utf8]\
+ \n TableScan: employee_csv projection=[id, state] [id:Int32,
state:Utf8]";
assert_eq!(expected, format!("{}", plan.display_indent_schema()));
+ Ok(())
}
#[test]
- fn test_display_graphviz() {
- let plan = display_plan();
+ fn test_display_graphviz() -> Result<()> {
+ let plan = display_plan()?;
// just test for a few key lines in the output rather than the
// whole thing to make test mainteance easier.
@@ -1435,6 +1488,7 @@ mod tests {
"\n{}",
plan.display_graphviz()
);
+ Ok(())
}
/// Tests for the Visitor trait and walking logical plan nodes
diff --git a/datafusion/optimizer/src/filter_push_down.rs
b/datafusion/optimizer/src/filter_push_down.rs
index c358ed534..2ac5b6e3b 100644
--- a/datafusion/optimizer/src/filter_push_down.rs
+++ b/datafusion/optimizer/src/filter_push_down.rs
@@ -2072,7 +2072,10 @@ mod tests {
// filter on col b in subquery
let expected_before = "\
- Filter: #b IN (Subquery: Projection: #sq.c\n TableScan: sq)\
+ Filter: #b IN (<subquery>)\
+ \n Subquery:\
+ \n Projection: #sq.c\
+ \n TableScan: sq\
\n Projection: #test.a AS b, #test.c\
\n TableScan: test";
assert_eq!(format!("{:?}", plan), expected_before);
@@ -2080,7 +2083,10 @@ mod tests {
// rewrite filter col b to test.a
let expected_after = "\
Projection: #test.a AS b, #test.c\
- \n Filter: #test.a IN (Subquery: Projection: #sq.c\n TableScan: sq)\
+ \n Filter: #test.a IN (<subquery>)\
+ \n Subquery:\
+ \n Projection: #sq.c\
+ \n TableScan: sq\
\n TableScan: test";
assert_optimized_plan_eq(&plan, expected_after);
diff --git a/datafusion/optimizer/src/limit_push_down.rs
b/datafusion/optimizer/src/limit_push_down.rs
index 9726be087..f92061b41 100644
--- a/datafusion/optimizer/src/limit_push_down.rs
+++ b/datafusion/optimizer/src/limit_push_down.rs
@@ -676,9 +676,11 @@ mod test {
// Limit pushdown Not supported in sub_query
let expected = "Limit: skip=10, fetch=100\
- \n Filter: EXISTS (Subquery: Filter: #test1.a = #test1.a\
- \n Projection: #test1.a\
- \n TableScan: test1)\
+ \n Filter: EXISTS (<subquery>)\
+ \n Subquery:\
+ \n Filter: #test1.a = #test1.a\
+ \n Projection: #test1.a\
+ \n TableScan: test1\
\n Projection: #test2.a\
\n TableScan: test2";
@@ -705,9 +707,11 @@ mod test {
// Limit pushdown Not supported in sub_query
let expected = "Limit: skip=10, fetch=100\
- \n Filter: EXISTS (Subquery: Filter: #test1.a = #test1.a\
- \n Projection: #test1.a\
- \n TableScan: test1)\
+ \n Filter: EXISTS (<subquery>)\
+ \n Subquery:\
+ \n Filter: #test1.a = #test1.a\
+ \n Projection: #test1.a\
+ \n TableScan: test1\
\n Projection: #test2.a\
\n TableScan: test2";
diff --git a/datafusion/optimizer/src/subquery_filter_to_join.rs
b/datafusion/optimizer/src/subquery_filter_to_join.rs
index b173e425f..092f315bc 100644
--- a/datafusion/optimizer/src/subquery_filter_to_join.rs
+++ b/datafusion/optimizer/src/subquery_filter_to_join.rs
@@ -328,9 +328,10 @@ mod tests {
.build()?;
let expected = "Projection: #test.b [b:UInt32]\
- \n Filter: #test.a = UInt32(1) AND #test.b < UInt32(30) OR #test.c IN
(\
- Subquery: Projection: #sq.c\
- \n TableScan: sq) [a:UInt32, b:UInt32, c:UInt32]\
+ \n Filter: #test.a = UInt32(1) AND #test.b < UInt32(30) OR #test.c IN
(<subquery>) [a:UInt32, b:UInt32, c:UInt32]\
+ \n Subquery: [c:UInt32]\
+ \n Projection: #sq.c [c:UInt32]\
+ \n TableScan: sq [a:UInt32, b:UInt32, c:UInt32]\
\n TableScan: test [a:UInt32, b:UInt32, c:UInt32]";
assert_optimized_plan_eq(&plan, expected);
@@ -352,9 +353,13 @@ mod tests {
.build()?;
let expected = "Projection: #test.b [b:UInt32]\
- \n Filter: #test.a = UInt32(1) OR #test.b IN (Subquery: Projection:
#sq1.c\
- \n TableScan: sq1) AND #test.c IN (Subquery: Projection: #sq2.c\
- \n TableScan: sq2) [a:UInt32, b:UInt32, c:UInt32]\
+ \n Filter: #test.a = UInt32(1) OR #test.b IN (<subquery>) AND #test.c
IN (<subquery>) [a:UInt32, b:UInt32, c:UInt32]\
+ \n Subquery: [c:UInt32]\
+ \n Projection: #sq1.c [c:UInt32]\
+ \n TableScan: sq1 [a:UInt32, b:UInt32, c:UInt32]\
+ \n Subquery: [c:UInt32]\
+ \n Projection: #sq2.c [c:UInt32]\
+ \n TableScan: sq2 [a:UInt32, b:UInt32, c:UInt32]\
\n TableScan: test [a:UInt32, b:UInt32, c:UInt32]";
assert_optimized_plan_eq(&plan, expected);
@@ -405,9 +410,9 @@ mod tests {
.build()?;
let expected = "Projection: #wrapped.b [b:UInt32]\
- \n Filter: #wrapped.b < UInt32(30) OR #wrapped.c IN (\
- Subquery: Projection: #sq_outer.c\
- \n TableScan: sq_outer) [b:UInt32, c:UInt32]\
+ \n Filter: #wrapped.b < UInt32(30) OR #wrapped.c IN (<subquery>)
[b:UInt32, c:UInt32]\
+ \n Subquery: [c:UInt32]\n Projection: #sq_outer.c [c:UInt32]\
+ \n TableScan: sq_outer [a:UInt32, b:UInt32, c:UInt32]\
\n Projection: #test.b, #test.c, alias=wrapped [b:UInt32, c:UInt32]\
\n Semi Join: #test.c = #sq_inner.c [a:UInt32, b:UInt32,
c:UInt32]\
\n TableScan: test [a:UInt32, b:UInt32, c:UInt32]\
diff --git a/datafusion/sql/src/planner.rs b/datafusion/sql/src/planner.rs
index 250b4e9c8..edabbf8f2 100644
--- a/datafusion/sql/src/planner.rs
+++ b/datafusion/sql/src/planner.rs
@@ -4657,18 +4657,15 @@ mod tests {
WHERE last_name = p.last_name \
AND state = p.state)";
- let subquery_expected = "Subquery: Projection: #person.first_name\
- \n Filter: #person.last_name = #p.last_name AND #person.state =
#p.state\
- \n TableScan: person";
-
- let expected = format!(
- "Projection: #p.id\
- \n Filter: EXISTS ({})\
+ let expected = "Projection: #p.id\
+ \n Filter: EXISTS (<subquery>)\
+ \n Subquery:\
+ \n Projection: #person.first_name\
+ \n Filter: #person.last_name = #p.last_name AND #person.state =
#p.state\
+ \n TableScan: person\
\n SubqueryAlias: p\
- \n TableScan: person",
- subquery_expected
- );
- quick_test(sql, &expected);
+ \n TableScan: person";
+ quick_test(sql, expected);
}
#[test]
@@ -4681,23 +4678,20 @@ mod tests {
AND person.last_name = p.last_name \
AND person.state = p.state)";
- let subquery_expected = "Subquery: Projection: #person.first_name\
- \n Filter: #person.last_name = #p.last_name AND #person.state =
#p.state\
- \n Inner Join: #person.id = #p2.id\
+ let expected = "Projection: #person.id\
+ \n Filter: EXISTS (<subquery>)\
+ \n Subquery:\
+ \n Projection: #person.first_name\
+ \n Filter: #person.last_name = #p.last_name AND #person.state =
#p.state\
+ \n Inner Join: #person.id = #p2.id\
+ \n TableScan: person\
+ \n SubqueryAlias: p2\
+ \n TableScan: person\
+ \n Inner Join: #person.id = #p.id\
\n TableScan: person\
- \n SubqueryAlias: p2\
+ \n SubqueryAlias: p\
\n TableScan: person";
-
- let expected = format!(
- "Projection: #person.id\
- \n Filter: EXISTS ({})\
- \n Inner Join: #person.id = #p.id\
- \n TableScan: person\
- \n SubqueryAlias: p\
- \n TableScan: person",
- subquery_expected
- );
- quick_test(sql, &expected);
+ quick_test(sql, expected);
}
#[test]
@@ -4707,19 +4701,15 @@ mod tests {
WHERE last_name = p.last_name \
AND state = p.state)";
- let subquery_expected = "Subquery: Projection: #person.id,
#person.first_name, \
- #person.last_name, #person.age, #person.state, #person.salary,
#person.birth_date, #person.😀\
- \n Filter: #person.last_name = #p.last_name AND #person.state =
#p.state\
- \n TableScan: person";
-
- let expected = format!(
- "Projection: #p.id\
- \n Filter: EXISTS ({})\
- \n SubqueryAlias: p\
- \n TableScan: person",
- subquery_expected
- );
- quick_test(sql, &expected);
+ let expected = "Projection: #p.id\
+ \n Filter: EXISTS (<subquery>)\
+ \n Subquery:\
+ \n Projection: #person.id, #person.first_name, #person.last_name,
#person.age, #person.state, #person.salary, #person.birth_date, #person.😀\
+ \n Filter: #person.last_name = #p.last_name AND #person.state =
#p.state\
+ \n TableScan: person\
+ \n SubqueryAlias: p\
+ \n TableScan: person";
+ quick_test(sql, expected);
}
#[test]
@@ -4727,17 +4717,14 @@ mod tests {
let sql = "SELECT id FROM person p WHERE id IN \
(SELECT id FROM person)";
- let subquery_expected = "Subquery: Projection: #person.id\
- \n TableScan: person";
-
- let expected = format!(
- "Projection: #p.id\
- \n Filter: #p.id IN ({})\
- \n SubqueryAlias: p\
- \n TableScan: person",
- subquery_expected
- );
- quick_test(sql, &expected);
+ let expected = "Projection: #p.id\
+ \n Filter: #p.id IN (<subquery>)\
+ \n Subquery:\
+ \n Projection: #person.id\
+ \n TableScan: person\
+ \n SubqueryAlias: p\
+ \n TableScan: person";
+ quick_test(sql, expected);
}
#[test]
@@ -4745,36 +4732,30 @@ mod tests {
let sql = "SELECT id FROM person p WHERE id NOT IN \
(SELECT id FROM person WHERE last_name = p.last_name AND state =
'CO')";
- let subquery_expected = "Subquery: Projection: #person.id\
- \n Filter: #person.last_name = #p.last_name AND #person.state =
Utf8(\"CO\")\
- \n TableScan: person";
-
- let expected = format!(
- "Projection: #p.id\
- \n Filter: #p.id NOT IN ({})\
- \n SubqueryAlias: p\
- \n TableScan: person",
- subquery_expected
- );
- quick_test(sql, &expected);
+ let expected = "Projection: #p.id\
+ \n Filter: #p.id NOT IN (<subquery>)\
+ \n Subquery:\
+ \n Projection: #person.id\
+ \n Filter: #person.last_name = #p.last_name AND #person.state =
Utf8(\"CO\")\
+ \n TableScan: person\
+ \n SubqueryAlias: p\
+ \n TableScan: person";
+ quick_test(sql, expected);
}
#[test]
fn scalar_subquery() {
let sql = "SELECT p.id, (SELECT MAX(id) FROM person WHERE last_name =
p.last_name) FROM person p";
- let subquery_expected = "Subquery: Projection: #MAX(person.id)\
- \n Aggregate: groupBy=[[]], aggr=[[MAX(#person.id)]]\
- \n Filter: #person.last_name = #p.last_name\
- \n TableScan: person";
-
- let expected = format!(
- "Projection: #p.id, ({})\
- \n SubqueryAlias: p\
- \n TableScan: person",
- subquery_expected
- );
- quick_test(sql, &expected);
+ let expected = "Projection: #p.id, (<subquery>)\
+ \n Subquery:\
+ \n Projection: #MAX(person.id)\
+ \n Aggregate: groupBy=[[]], aggr=[[MAX(#person.id)]]\
+ \n Filter: #person.last_name = #p.last_name\
+ \n TableScan: person\
+ \n SubqueryAlias: p\
+ \n TableScan: person";
+ quick_test(sql, expected);
}
#[test]
@@ -4787,23 +4768,20 @@ mod tests {
WHERE j2_id = j1_id \
AND j1_id = j3_id)";
- let subquery = "Subquery: Projection: #COUNT(UInt8(1))\
- \n Aggregate: groupBy=[[]], aggr=[[COUNT(UInt8(1))]]\
- \n Filter: #j2.j2_id = #j1.j1_id\
- \n Inner Join: #j1.j1_id = #j3.j3_id\
- \n TableScan: j1\
- \n TableScan: j3";
-
- let expected = format!(
- "Projection: #j1.j1_string, #j2.j2_string\
- \n Filter: #j1.j1_id = #j2.j2_id - Int64(1) AND #j2.j2_id < ({})\
- \n CrossJoin:\
- \n TableScan: j1\
- \n TableScan: j2",
- subquery
- );
+ let expected = "Projection: #j1.j1_string, #j2.j2_string\
+ \n Filter: #j1.j1_id = #j2.j2_id - Int64(1) AND #j2.j2_id <
(<subquery>)\
+ \n Subquery:\
+ \n Projection: #COUNT(UInt8(1))\
+ \n Aggregate: groupBy=[[]], aggr=[[COUNT(UInt8(1))]]\
+ \n Filter: #j2.j2_id = #j1.j1_id\
+ \n Inner Join: #j1.j1_id = #j3.j3_id\
+ \n TableScan: j1\
+ \n TableScan: j3\
+ \n CrossJoin:\
+ \n TableScan: j1\
+ \n TableScan: j2";
- quick_test(sql, &expected);
+ quick_test(sql, expected);
}
#[tokio::test]
@@ -4812,16 +4790,16 @@ mod tests {
cte AS (SELECT * FROM person) \
SELECT * FROM person WHERE EXISTS (SELECT * FROM cte WHERE id =
person.id)";
- let subquery = "Subquery: Projection: #cte.id, #cte.first_name,
#cte.last_name, #cte.age, #cte.state, #cte.salary, #cte.birth_date, #cte.😀\
- \n Filter: #cte.id = #person.id\
- \n Projection: #person.id, #person.first_name, #person.last_name,
#person.age, #person.state, #person.salary, #person.birth_date, #person.😀,
alias=cte\
- \n TableScan: person";
-
- let expected = format!("Projection: #person.id, #person.first_name,
#person.last_name, #person.age, #person.state, #person.salary,
#person.birth_date, #person.😀\
- \n Filter: EXISTS ({})\
- \n TableScan: person", subquery);
+ let expected = "Projection: #person.id, #person.first_name,
#person.last_name, #person.age, #person.state, #person.salary,
#person.birth_date, #person.😀\
+ \n Filter: EXISTS (<subquery>)\
+ \n Subquery:\
+ \n Projection: #cte.id, #cte.first_name, #cte.last_name,
#cte.age, #cte.state, #cte.salary, #cte.birth_date, #cte.😀\
+ \n Filter: #cte.id = #person.id\
+ \n Projection: #person.id, #person.first_name,
#person.last_name, #person.age, #person.state, #person.salary,
#person.birth_date, #person.😀, alias=cte\
+ \n TableScan: person\
+ \n TableScan: person";
- quick_test(sql, &expected)
+ quick_test(sql, expected)
}
#[tokio::test]