Skip to content

Commit cd04fce

Browse files
committed
Differentiate 0-row and 1-row EmptyRelation in EXPLAIN
The `LogicalPlan::EmptyRelation` can produce no rows or exactly 1 null (placeholder) row of the requested schema. When viewing EXPLAIN output of a LogicalPlan it's good to know which one is the case.
1 parent 7d52145 commit cd04fce

28 files changed

+163
-162
lines changed

datafusion-cli/tests/snapshots/cli_quick_test@can_see_indent_format.snap

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ info:
55
args:
66
- "--command"
77
- EXPLAIN FORMAT indent SELECT 123
8-
snapshot_kind: text
98
---
109
success: true
1110
exit_code: 0
@@ -15,7 +14,7 @@ exit_code: 0
1514
| plan_type | plan |
1615
+---------------+------------------------------------------+
1716
| logical_plan | Projection: Int64(123) |
18-
| | EmptyRelation |
17+
| | EmptyRelation: rows=1 |
1918
| physical_plan | ProjectionExec: expr=[123 as Int64(123)] |
2019
| | PlaceholderRowExec |
2120
| | |

datafusion/core/tests/dataframe/mod.rs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,7 +1213,7 @@ async fn join_on_filter_datatype() -> Result<()> {
12131213
JoinType::Inner,
12141214
Some(Expr::Literal(ScalarValue::Null, None)),
12151215
)?;
1216-
assert_snapshot!(join.into_optimized_plan().unwrap(), @"EmptyRelation");
1216+
assert_snapshot!(join.into_optimized_plan().unwrap(), @"EmptyRelation: rows=0");
12171217

12181218
// JOIN ON expression must be boolean type
12191219
let join = left.join_on(right, JoinType::Inner, Some(lit("TRUE")))?;
@@ -4940,11 +4940,11 @@ async fn test_dataframe_placeholder_missing_param_values() -> Result<()> {
49404940

49414941
assert_snapshot!(
49424942
actual,
4943-
@r###"
4943+
@r"
49444944
Filter: a = $0 [a:Int32]
49454945
Projection: Int32(1) AS a [a:Int32]
4946-
EmptyRelation []
4947-
"###
4946+
EmptyRelation: rows=1 []
4947+
"
49484948
);
49494949

49504950
// Executing LogicalPlans with placeholders that don't have bound values
@@ -4973,11 +4973,11 @@ async fn test_dataframe_placeholder_missing_param_values() -> Result<()> {
49734973

49744974
assert_snapshot!(
49754975
actual,
4976-
@r###"
4976+
@r"
49774977
Filter: a = Int32(3) [a:Int32]
49784978
Projection: Int32(1) AS a [a:Int32]
4979-
EmptyRelation []
4980-
"###
4979+
EmptyRelation: rows=1 []
4980+
"
49814981
);
49824982

49834983
// N.B., the test is basically `SELECT 1 as a WHERE a = 3;` which returns no results.
@@ -5004,10 +5004,10 @@ async fn test_dataframe_placeholder_column_parameter() -> Result<()> {
50045004

50055005
assert_snapshot!(
50065006
actual,
5007-
@r###"
5007+
@r"
50085008
Projection: $1 [$1:Null;N]
5009-
EmptyRelation []
5010-
"###
5009+
EmptyRelation: rows=1 []
5010+
"
50115011
);
50125012

50135013
// Executing LogicalPlans with placeholders that don't have bound values
@@ -5034,10 +5034,10 @@ async fn test_dataframe_placeholder_column_parameter() -> Result<()> {
50345034

50355035
assert_snapshot!(
50365036
actual,
5037-
@r###"
5037+
@r"
50385038
Projection: Int32(3) AS $1 [$1:Null;N]
5039-
EmptyRelation []
5040-
"###
5039+
EmptyRelation: rows=1 []
5040+
"
50415041
);
50425042

50435043
assert_snapshot!(
@@ -5073,11 +5073,11 @@ async fn test_dataframe_placeholder_like_expression() -> Result<()> {
50735073

50745074
assert_snapshot!(
50755075
actual,
5076-
@r###"
5076+
@r#"
50775077
Filter: a LIKE $1 [a:Utf8]
50785078
Projection: Utf8("foo") AS a [a:Utf8]
5079-
EmptyRelation []
5080-
"###
5079+
EmptyRelation: rows=1 []
5080+
"#
50815081
);
50825082

50835083
// Executing LogicalPlans with placeholders that don't have bound values
@@ -5106,11 +5106,11 @@ async fn test_dataframe_placeholder_like_expression() -> Result<()> {
51065106

51075107
assert_snapshot!(
51085108
actual,
5109-
@r###"
5109+
@r#"
51105110
Filter: a LIKE Utf8("f%") [a:Utf8]
51115111
Projection: Utf8("foo") AS a [a:Utf8]
5112-
EmptyRelation []
5113-
"###
5112+
EmptyRelation: rows=1 []
5113+
"#
51145114
);
51155115

51165116
assert_snapshot!(

datafusion/core/tests/execution/logical_plan.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ fn inline_scan_projection_test() -> Result<()> {
128128
@r"
129129
SubqueryAlias: ?table?
130130
Projection: a
131-
EmptyRelation
131+
EmptyRelation: rows=0
132132
"
133133
);
134134

datafusion/core/tests/optimizer/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ fn select_arrow_cast() {
6262
plan,
6363
@r#"
6464
Projection: Float64(1234) AS f64, LargeUtf8("foo") AS large
65-
EmptyRelation
65+
EmptyRelation: rows=1
6666
"#
6767
);
6868
}

datafusion/expr/src/logical_plan/plan.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1720,7 +1720,10 @@ impl LogicalPlan {
17201720
impl Display for Wrapper<'_> {
17211721
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
17221722
match self.0 {
1723-
LogicalPlan::EmptyRelation(_) => write!(f, "EmptyRelation"),
1723+
LogicalPlan::EmptyRelation(EmptyRelation { produce_one_row, schema: _ }) => {
1724+
let rows = if *produce_one_row { 1 } else { 0 };
1725+
write!(f, "EmptyRelation: rows={rows}")
1726+
},
17241727
LogicalPlan::RecursiveQuery(RecursiveQuery {
17251728
is_distinct, ..
17261729
}) => {

datafusion/optimizer/src/analyzer/type_coercion.rs

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,7 +1188,7 @@ mod test {
11881188
plan,
11891189
@r"
11901190
Projection: a < CAST(UInt32(2) AS Float64)
1191-
EmptyRelation
1191+
EmptyRelation: rows=0
11921192
"
11931193
)
11941194
}
@@ -1231,8 +1231,8 @@ mod test {
12311231
Projection: a
12321232
Union
12331233
Projection: CAST(datafusion.test.foo.a AS Int64) AS a
1234-
EmptyRelation
1235-
EmptyRelation
1234+
EmptyRelation: rows=0
1235+
EmptyRelation: rows=0
12361236
"
12371237
)
12381238
}
@@ -1254,7 +1254,7 @@ mod test {
12541254
plan.clone(),
12551255
@r"
12561256
Projection: a
1257-
EmptyRelation
1257+
EmptyRelation: rows=0
12581258
"
12591259
)?;
12601260

@@ -1385,7 +1385,7 @@ mod test {
13851385
plan.clone(),
13861386
@r"
13871387
Projection: a
1388-
EmptyRelation
1388+
EmptyRelation: rows=0
13891389
"
13901390
)?;
13911391

@@ -1506,7 +1506,7 @@ mod test {
15061506
plan,
15071507
@r"
15081508
Projection: a < CAST(UInt32(2) AS Float64) OR a < CAST(UInt32(2) AS Float64)
1509-
EmptyRelation
1509+
EmptyRelation: rows=0
15101510
"
15111511
)
15121512
}
@@ -1552,7 +1552,7 @@ mod test {
15521552
plan,
15531553
@r"
15541554
Projection: TestScalarUDF(CAST(Int32(123) AS Float32))
1555-
EmptyRelation
1555+
EmptyRelation: rows=0
15561556
"
15571557
)
15581558
}
@@ -1589,7 +1589,7 @@ mod test {
15891589
plan,
15901590
@r"
15911591
Projection: TestScalarUDF(CAST(Int64(10) AS Float32))
1592-
EmptyRelation
1592+
EmptyRelation: rows=0
15931593
"
15941594
)
15951595
}
@@ -1619,7 +1619,7 @@ mod test {
16191619
plan,
16201620
@r"
16211621
Projection: MY_AVG(CAST(Int64(10) AS Float64))
1622-
EmptyRelation
1622+
EmptyRelation: rows=0
16231623
"
16241624
)
16251625
}
@@ -1673,7 +1673,7 @@ mod test {
16731673
plan,
16741674
@r"
16751675
Projection: avg(Float64(12))
1676-
EmptyRelation
1676+
EmptyRelation: rows=0
16771677
"
16781678
)?;
16791679

@@ -1728,7 +1728,7 @@ mod test {
17281728
plan,
17291729
@r#"
17301730
Projection: CAST(Utf8("1998-03-18") AS Date32) + IntervalDayTime("IntervalDayTime { days: 123, milliseconds: 456 }")
1731-
EmptyRelation
1731+
EmptyRelation: rows=0
17321732
"#
17331733
)
17341734
}
@@ -1743,7 +1743,7 @@ mod test {
17431743
plan,
17441744
@r"
17451745
Projection: a IN ([CAST(Int32(1) AS Int64), CAST(Int8(4) AS Int64), Int64(8)])
1746-
EmptyRelation
1746+
EmptyRelation: rows=0
17471747
")?;
17481748

17491749
// a in (1,4,8), a is decimal
@@ -1779,7 +1779,7 @@ mod test {
17791779
plan,
17801780
@r#"
17811781
Filter: CAST(a AS Date32) BETWEEN CAST(Utf8("2002-05-08") AS Date32) AND CAST(Utf8("2002-05-08") AS Date32) + IntervalYearMonth("1")
1782-
EmptyRelation
1782+
EmptyRelation: rows=0
17831783
"#
17841784
)
17851785
}
@@ -1800,7 +1800,7 @@ mod test {
18001800
plan,
18011801
@r#"
18021802
Filter: CAST(a AS Date32) BETWEEN CAST(Utf8("2002-05-08") AS Date32) + IntervalYearMonth("1") AND CAST(Utf8("2002-12-08") AS Date32)
1803-
EmptyRelation
1803+
EmptyRelation: rows=0
18041804
"#
18051805
)
18061806
}
@@ -1815,7 +1815,7 @@ mod test {
18151815
plan,
18161816
@r"
18171817
Filter: CAST(NULL AS Int64) BETWEEN CAST(NULL AS Int64) AND Int64(2)
1818-
EmptyRelation
1818+
EmptyRelation: rows=0
18191819
"
18201820
)
18211821
}
@@ -1832,7 +1832,7 @@ mod test {
18321832
plan,
18331833
@r"
18341834
Projection: a IS TRUE
1835-
EmptyRelation
1835+
EmptyRelation: rows=0
18361836
"
18371837
)?;
18381838

@@ -1896,7 +1896,7 @@ mod test {
18961896
plan,
18971897
@r#"
18981898
Projection: a LIKE Utf8("abc")
1899-
EmptyRelation
1899+
EmptyRelation: rows=0
19001900
"#
19011901
)?;
19021902

@@ -1978,7 +1978,7 @@ mod test {
19781978
plan,
19791979
@r"
19801980
Projection: a IS UNKNOWN
1981-
EmptyRelation
1981+
EmptyRelation: rows=0
19821982
"
19831983
)?;
19841984

@@ -2019,7 +2019,7 @@ mod test {
20192019
plan,
20202020
@r#"
20212021
Projection: TestScalarUDF(a, Utf8("b"), CAST(Boolean(true) AS Utf8), CAST(Boolean(false) AS Utf8), CAST(Int32(13) AS Utf8))
2022-
EmptyRelation
2022+
EmptyRelation: rows=0
20232023
"#
20242024
)
20252025
}
@@ -2076,7 +2076,7 @@ mod test {
20762076
plan,
20772077
@r#"
20782078
Projection: CAST(Utf8("1998-03-18") AS Timestamp(Nanosecond, None)) = CAST(CAST(Utf8("1998-03-18") AS Date32) AS Timestamp(Nanosecond, None))
2079-
EmptyRelation
2079+
EmptyRelation: rows=0
20802080
"#
20812081
)
20822082
}
@@ -2424,7 +2424,7 @@ mod test {
24242424
plan,
24252425
@r#"
24262426
Projection: a = CAST(CAST(a AS Map(Field { name: "key_value", data_type: Struct([Field { name: "key", data_type: Utf8, nullable: false, dict_id: 0, dict_is_ordered: false, metadata: {} }, Field { name: "value", data_type: Float64, nullable: true, dict_id: 0, dict_is_ordered: false, metadata: {} }]), nullable: false, dict_id: 0, dict_is_ordered: false, metadata: {} }, false)) AS Map(Field { name: "entries", data_type: Struct([Field { name: "key", data_type: Utf8, nullable: false, dict_id: 0, dict_is_ordered: false, metadata: {} }, Field { name: "value", data_type: Float64, nullable: true, dict_id: 0, dict_is_ordered: false, metadata: {} }]), nullable: false, dict_id: 0, dict_is_ordered: false, metadata: {} }, false))
2427-
EmptyRelation
2427+
EmptyRelation: rows=0
24282428
"#
24292429
)
24302430
}
@@ -2447,7 +2447,7 @@ mod test {
24472447
plan,
24482448
@r#"
24492449
Projection: IntervalYearMonth("12") + CAST(Utf8("2000-01-01T00:00:00") AS Timestamp(Nanosecond, None))
2450-
EmptyRelation
2450+
EmptyRelation: rows=0
24512451
"#
24522452
)
24532453
}
@@ -2472,7 +2472,7 @@ mod test {
24722472
plan,
24732473
@r#"
24742474
Projection: CAST(Utf8("1998-03-18") AS Timestamp(Nanosecond, None)) - CAST(Utf8("1998-03-18") AS Timestamp(Nanosecond, None))
2475-
EmptyRelation
2475+
EmptyRelation: rows=0
24762476
"#
24772477
)
24782478
}
@@ -2500,8 +2500,8 @@ mod test {
25002500
Filter: a IN (<subquery>)
25012501
Subquery:
25022502
Projection: CAST(a AS Int64)
2503-
EmptyRelation
2504-
EmptyRelation
2503+
EmptyRelation: rows=0
2504+
EmptyRelation: rows=0
25052505
"
25062506
)
25072507
}
@@ -2528,8 +2528,8 @@ mod test {
25282528
@r"
25292529
Filter: CAST(a AS Int64) IN (<subquery>)
25302530
Subquery:
2531-
EmptyRelation
2532-
EmptyRelation
2531+
EmptyRelation: rows=0
2532+
EmptyRelation: rows=0
25332533
"
25342534
)
25352535
}
@@ -2557,8 +2557,8 @@ mod test {
25572557
Filter: CAST(a AS Decimal128(13, 8)) IN (<subquery>)
25582558
Subquery:
25592559
Projection: CAST(a AS Decimal128(13, 8))
2560-
EmptyRelation
2561-
EmptyRelation
2560+
EmptyRelation: rows=0
2561+
EmptyRelation: rows=0
25622562
"
25632563
)
25642564
}

0 commit comments

Comments
 (0)