Skip to content

Commit ab80f49

Browse files
authored
Merge branch 'main' into fix/binary-map-initial-map-size
2 parents d3d9b37 + 1e68674 commit ab80f49

7 files changed

Lines changed: 65 additions & 16 deletions

File tree

datafusion/expr/src/logical_plan/builder.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,10 +1931,12 @@ fn project_with_validation(
19311931
expr: impl IntoIterator<Item = (impl Into<SelectExpr>, bool)>,
19321932
) -> Result<LogicalPlan> {
19331933
let mut projected_expr = vec![];
1934+
let mut has_wildcard = false;
19341935
for (e, validate) in expr {
19351936
let e = e.into();
19361937
match e {
19371938
SelectExpr::Wildcard(opt) => {
1939+
has_wildcard = true;
19381940
let expanded = expand_wildcard(plan.schema(), &plan, Some(&opt))?;
19391941

19401942
// If there is a REPLACE statement, replace that column with the given
@@ -1955,6 +1957,7 @@ fn project_with_validation(
19551957
}
19561958
}
19571959
SelectExpr::QualifiedWildcard(table_ref, opt) => {
1960+
has_wildcard = true;
19581961
let expanded =
19591962
expand_qualified_wildcard(&table_ref, plan.schema(), Some(&opt))?;
19601963

@@ -1984,6 +1987,12 @@ fn project_with_validation(
19841987
}
19851988
}
19861989
}
1990+
if has_wildcard && projected_expr.is_empty() && !plan.schema().fields().is_empty() {
1991+
return plan_err!(
1992+
"SELECT list is empty after resolving * expressions, \
1993+
the wildcard expanded to zero columns"
1994+
);
1995+
}
19871996
validate_unique_names("Projections", projected_expr.iter())?;
19881997

19891998
Projection::try_new(projected_expr, Arc::new(plan)).map(LogicalPlan::Projection)

datafusion/functions/src/core/named_struct.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ use std::sync::Arc;
2727

2828
#[user_doc(
2929
doc_section(label = "Struct Functions"),
30-
description = "Returns an Arrow struct using the specified name and input expressions pairs.",
30+
description = "Returns an Arrow struct using the specified name and input expressions pairs.
31+
For information on comparing and ordering struct values (including `NULL` handling),
32+
see [Comparison and Ordering](struct_coercion.md#comparison-and-ordering).",
3133
syntax_example = "named_struct(expression1_name, expression1_input[, ..., expression_n_name, expression_n_input])",
3234
sql_example = r#"
3335
For example, this query converts two columns `a` and `b` to a single column with

datafusion/functions/src/core/struct.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ use std::sync::Arc;
2727
doc_section(label = "Struct Functions"),
2828
description = "Returns an Arrow struct using the specified input expressions optionally named.
2929
Fields in the returned struct use the optional name or the `cN` naming convention.
30-
For example: `c0`, `c1`, `c2`, etc.",
30+
For example: `c0`, `c1`, `c2`, etc.
31+
For information on comparing and ordering struct values (including `NULL` handling),
32+
see [Comparison and Ordering](struct_coercion.md#comparison-and-ordering).",
3133
syntax_example = "struct(expression1[, ..., expression_n])",
3234
sql_example = r#"For example, this query converts two columns `a` and `b` to a single column with
3335
a struct type of fields `field_a` and `c1`:

datafusion/sqllogictest/test_files/select.slt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,25 +1318,25 @@ statement error
13181318
SELECT * EXCLUDE(a, a)
13191319
FROM table1
13201320

1321-
# if EXCEPT all the columns, query should still succeed but return empty
1322-
statement ok
1321+
# if EXCEPT all the columns, query should return an error
1322+
statement error DataFusion error: Error during planning: SELECT list is empty after resolving \* expressions, the wildcard expanded to zero columns
13231323
SELECT * EXCEPT(a, b, c, d)
13241324
FROM table1
13251325

1326-
# try zero column with LIMIT, 1 row but empty
1327-
statement ok
1326+
# try zero column with LIMIT, should error
1327+
statement error DataFusion error: Error during planning: SELECT list is empty after resolving \* expressions, the wildcard expanded to zero columns
13281328
SELECT * EXCEPT (a, b, c, d)
13291329
FROM table1
13301330
LIMIT 1
13311331

1332-
# try zero column with GROUP BY, 2 row but empty
1333-
statement ok
1332+
# try zero column with GROUP BY, should error
1333+
statement error DataFusion error: Error during planning: SELECT list is empty after resolving \* expressions, the wildcard expanded to zero columns
13341334
SELECT * EXCEPT (a, b, c, d)
13351335
FROM table1
13361336
GROUP BY a
13371337

1338-
# try zero column with WHERE, 1 row but empty
1339-
statement ok
1338+
# try zero column with WHERE, should error
1339+
statement error DataFusion error: Error during planning: SELECT list is empty after resolving \* expressions, the wildcard expanded to zero columns
13401340
SELECT * EXCEPT (a, b, c, d)
13411341
FROM table1
13421342
WHERE a = 1
@@ -1352,15 +1352,15 @@ CREATE TABLE table2 (
13521352
(1, 10, 100, 1000),
13531353
(2, 20, 200, 2000);
13541354

1355-
# try zero column with inner JOIN, 2 row but empty
1356-
statement ok
1355+
# try zero column with inner JOIN, should error
1356+
statement error DataFusion error: Error during planning: SELECT list is empty after resolving \* expressions, the wildcard expanded to zero columns
13571357
WITH t1 AS (SELECT a AS t1_a FROM table1), t2 AS (SELECT a AS t2_a FROM table2)
13581358
SELECT * EXCEPT (t1_a, t2_a)
13591359
FROM t1
13601360
JOIN t2 ON (t1_a = t2_a)
13611361

1362-
# try zero column with more JOIN, 2 row but empty
1363-
statement ok
1362+
# try zero column with more JOIN, should error
1363+
statement error DataFusion error: Error during planning: SELECT list is empty after resolving \* expressions, the wildcard expanded to zero columns
13641364
SELECT * EXCEPT (b1, b2)
13651365
FROM (
13661366
SELECT b AS b1 FROM table1
@@ -1369,8 +1369,8 @@ JOIN (
13691369
SELECT b AS b2 FROM table2
13701370
) ON b1 = b2
13711371

1372-
# try zero column with Window, 2 row but empty
1373-
statement ok
1372+
# try zero column with Window, should error
1373+
statement error DataFusion error: Error during planning: SELECT list is empty after resolving \* expressions, the wildcard expanded to zero columns
13741374
SELECT * EXCEPT (a, b, row_num)
13751375
FROM (
13761376
SELECT

docs/source/user-guide/sql/scalar_functions.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4709,6 +4709,8 @@ _Alias of [string_to_array](#string_to_array)._
47094709
### `named_struct`
47104710

47114711
Returns an Arrow struct using the specified name and input expressions pairs.
4712+
For information on comparing and ordering struct values (including `NULL` handling),
4713+
see [Comparison and Ordering](struct_coercion.md#comparison-and-ordering).
47124714

47134715
```sql
47144716
named_struct(expression1_name, expression1_input[, ..., expression_n_name, expression_n_input])
@@ -4750,6 +4752,8 @@ _Alias of [struct](#struct)._
47504752
Returns an Arrow struct using the specified input expressions optionally named.
47514753
Fields in the returned struct use the optional name or the `cN` naming convention.
47524754
For example: `c0`, `c1`, `c2`, etc.
4755+
For information on comparing and ordering struct values (including `NULL` handling),
4756+
see [Comparison and Ordering](struct_coercion.md#comparison-and-ordering).
47534757

47544758
```sql
47554759
struct(expression1[, ..., expression_n])

docs/source/user-guide/sql/struct_coercion.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,26 @@ SELECT [
208208
] FROM t_left JOIN t_right;
209209
```
210210

211+
## Comparison and Ordering
212+
213+
DataFusion supports comparing `STRUCT` values with standard comparison operators
214+
(`=`, `!=`, `<`, `<=`, `>`, `>=`). Ordering comparisons are lexicographical and
215+
follow DataFusion's default ascending comparison behavior, where `NULL` sorts
216+
before non-`NULL` values.
217+
218+
### Examples
219+
220+
```sql
221+
SELECT {x: 1, y: 2} < {x: 1, y: 3};
222+
-- true
223+
224+
SELECT {x: 1, y: NULL} < {x: 1, y: 2};
225+
-- true
226+
227+
SELECT {x: 1, y: NULL} = {x: 1, y: NULL};
228+
--true
229+
```
230+
211231
## Migration Guide: From Positional to Name-Based Matching
212232

213233
If you have existing code that relied on **positional** struct field matching, you may need to update it.

docs/source/user-guide/sql/subqueries.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,18 @@ SELECT * FROM x WHERE column_1 NOT IN (1,3);
102102
+----------+----------+
103103
```
104104

105+
#### `IN` with tuple-like values and `NULL`
106+
107+
For tuple-like values, `IN` uses DataFusion's struct equality semantics:
108+
109+
```sql
110+
SELECT (1, 1) IN ((1, NULL));
111+
-- false
112+
113+
SELECT (1, NULL) IN ((1, NULL));
114+
-- true
115+
```
116+
105117
## SELECT clause subqueries
106118

107119
`SELECT` clause subqueries use values returned from the inner query as part

0 commit comments

Comments
 (0)