Skip to content

Commit 1089c45

Browse files
committed
Fix regression in SELECT FROM syntax with WHERE clause
When using 'SELECT FROM table WHERE condition', the query should create an empty projection (no columns) while still filtering rows. This was broken by PR apache#17295 which added FROM-first syntax support. The issue was that both 'FROM table' and 'SELECT FROM table' resulted in empty projection lists, making them indistinguishable. The fix checks for the presence of a WHERE clause to differentiate: - 'FROM table' (no WHERE) -> add wildcard projection (all columns) - 'SELECT FROM table WHERE ...' -> keep empty projection Also updates the test expectation to correctly show the empty Projection node in the query plan. Fixes apache#17513
1 parent fc49241 commit 1089c45

File tree

2 files changed

+17
-8
lines changed

2 files changed

+17
-8
lines changed

datafusion/sql/src/select.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
7979
let empty_from = matches!(plan, LogicalPlan::EmptyRelation(_));
8080

8181
// Process `where` clause
82+
let has_where_clause = select.selection.is_some();
8283
let base_plan = self.plan_selection(select.selection, plan, planner_context)?;
8384

8485
// Handle named windows before processing the projection expression
@@ -90,6 +91,7 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
9091
&base_plan,
9192
select.projection,
9293
empty_from,
94+
has_where_clause,
9395
planner_context,
9496
)?;
9597

@@ -671,14 +673,19 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
671673
plan: &LogicalPlan,
672674
projection: Vec<SelectItem>,
673675
empty_from: bool,
676+
has_where_clause: bool,
674677
planner_context: &mut PlannerContext,
675678
) -> Result<Vec<SelectExpr>> {
676679
let mut prepared_select_exprs = vec![];
677680
let mut error_builder = DataFusionErrorBuilder::new();
678681

679682
// Handle the case where no projection is specified but we have a valid FROM clause
680-
// In this case, implicitly add a wildcard projection (SELECT *)
681-
let projection = if projection.is_empty() && !empty_from {
683+
// This happens with FROM-first syntax like "FROM table"
684+
// But NOT with "SELECT FROM table WHERE ..." which should have empty projection
685+
// When there's a WHERE clause with empty projection, it's "SELECT FROM" syntax
686+
// and we should NOT add a wildcard - let the optimizer handle column selection
687+
let projection = if projection.is_empty() && !empty_from && !has_where_clause {
688+
// FROM-first syntax without WHERE clause - select all columns
682689
vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())]
683690
} else {
684691
projection

datafusion/sqllogictest/test_files/projection.slt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -270,10 +270,12 @@ explain format indent
270270
select from t1 where t1.a > 1;
271271
----
272272
logical_plan
273-
01)Filter: t1.a > Int64(1)
274-
02)--TableScan: t1 projection=[a], partial_filters=[t1.a > Int64(1)]
273+
01)Projection:
274+
02)--Filter: t1.a > Int64(1)
275+
03)----TableScan: t1 projection=[a], partial_filters=[t1.a > Int64(1)]
275276
physical_plan
276-
01)CoalesceBatchesExec: target_batch_size=8192
277-
02)--FilterExec: a@0 > 1
278-
03)----RepartitionExec: partitioning=RoundRobinBatch(4), input_partitions=1
279-
04)------DataSourceExec: file_groups={1 group: [[WORKSPACE_ROOT/datafusion/sqllogictest/test_files/scratch/projection/17513.parquet]]}, projection=[a], file_type=parquet, predicate=a@0 > 1, pruning_predicate=a_null_count@1 != row_count@2 AND a_max@0 > 1, required_guarantees=[]
277+
01)ProjectionExec: expr=[]
278+
02)--CoalesceBatchesExec: target_batch_size=8192
279+
03)----FilterExec: a@0 > 1
280+
04)------RepartitionExec: partitioning=RoundRobinBatch(4), input_partitions=1
281+
05)--------DataSourceExec: file_groups={1 group: [[WORKSPACE_ROOT/datafusion/sqllogictest/test_files/scratch/projection/17513.parquet]]}, projection=[a], file_type=parquet, predicate=a@0 > 1, pruning_predicate=a_null_count@1 != row_count@2 AND a_max@0 > 1, required_guarantees=[]

0 commit comments

Comments
 (0)