Skip to content

Commit 8b1c84d

Browse files
authored
fix: normalize window ident (#15639)
* fix: normalize window name * format
1 parent a11aa42 commit 8b1c84d

File tree

2 files changed

+86
-48
lines changed

2 files changed

+86
-48
lines changed

datafusion/sql/src/select.rs

Lines changed: 54 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::utils::{
2929

3030
use datafusion_common::error::DataFusionErrorBuilder;
3131
use datafusion_common::tree_node::{TreeNode, TreeNodeRecursion};
32-
use datafusion_common::{not_impl_err, plan_err, DataFusionError, Result};
32+
use datafusion_common::{not_impl_err, plan_err, Result};
3333
use datafusion_common::{RecursionUnnestOption, UnnestOptions};
3434
use datafusion_expr::expr::{Alias, PlannedReplaceSelectItem, WildcardOptions};
3535
use datafusion_expr::expr_rewriter::{
@@ -85,7 +85,7 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
8585

8686
// Handle named windows before processing the projection expression
8787
check_conflicting_windows(&select.named_window)?;
88-
match_window_definitions(&mut select.projection, &select.named_window)?;
88+
self.match_window_definitions(&mut select.projection, &select.named_window)?;
8989
// Process the SELECT expressions
9090
let select_exprs = self.prepare_select_exprs(
9191
&base_plan,
@@ -868,42 +868,34 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
868868

869869
Ok((plan, select_exprs_post_aggr, having_expr_post_aggr))
870870
}
871-
}
872-
873-
// If there are any multiple-defined windows, we raise an error.
874-
fn check_conflicting_windows(window_defs: &[NamedWindowDefinition]) -> Result<()> {
875-
for (i, window_def_i) in window_defs.iter().enumerate() {
876-
for window_def_j in window_defs.iter().skip(i + 1) {
877-
if window_def_i.0 == window_def_j.0 {
878-
return plan_err!(
879-
"The window {} is defined multiple times!",
880-
window_def_i.0
881-
);
882-
}
883-
}
884-
}
885-
Ok(())
886-
}
887871

888-
// If the projection is done over a named window, that window
889-
// name must be defined. Otherwise, it gives an error.
890-
fn match_window_definitions(
891-
projection: &mut [SelectItem],
892-
named_windows: &[NamedWindowDefinition],
893-
) -> Result<()> {
894-
for proj in projection.iter_mut() {
895-
if let SelectItem::ExprWithAlias { expr, alias: _ }
896-
| SelectItem::UnnamedExpr(expr) = proj
897-
{
898-
let mut err = None;
899-
visit_expressions_mut(expr, |expr| {
900-
if let SQLExpr::Function(f) = expr {
901-
if let Some(WindowType::NamedWindow(_)) = &f.over {
902-
for NamedWindowDefinition(window_ident, window_expr) in
903-
named_windows
904-
{
905-
if let Some(WindowType::NamedWindow(ident)) = &f.over {
906-
if ident.eq(window_ident) {
872+
// If the projection is done over a named window, that window
873+
// name must be defined. Otherwise, it gives an error.
874+
fn match_window_definitions(
875+
&self,
876+
projection: &mut [SelectItem],
877+
named_windows: &[NamedWindowDefinition],
878+
) -> Result<()> {
879+
let named_windows: Vec<(&NamedWindowDefinition, String)> = named_windows
880+
.iter()
881+
.map(|w| (w, self.ident_normalizer.normalize(w.0.clone())))
882+
.collect();
883+
for proj in projection.iter_mut() {
884+
if let SelectItem::ExprWithAlias { expr, alias: _ }
885+
| SelectItem::UnnamedExpr(expr) = proj
886+
{
887+
let mut err = None;
888+
visit_expressions_mut(expr, |expr| {
889+
if let SQLExpr::Function(f) = expr {
890+
if let Some(WindowType::NamedWindow(ident)) = &f.over {
891+
let normalized_ident =
892+
self.ident_normalizer.normalize(ident.clone());
893+
for (
894+
NamedWindowDefinition(_, window_expr),
895+
normalized_window_ident,
896+
) in named_windows.iter()
897+
{
898+
if normalized_ident.eq(normalized_window_ident) {
907899
f.over = Some(match window_expr {
908900
NamedWindowExpr::NamedWindow(ident) => {
909901
WindowType::NamedWindow(ident.clone())
@@ -914,20 +906,34 @@ fn match_window_definitions(
914906
})
915907
}
916908
}
917-
}
918-
// All named windows must be defined with a WindowSpec.
919-
if let Some(WindowType::NamedWindow(ident)) = &f.over {
920-
err = Some(DataFusionError::Plan(format!(
921-
"The window {ident} is not defined!"
922-
)));
923-
return ControlFlow::Break(());
909+
// All named windows must be defined with a WindowSpec.
910+
if let Some(WindowType::NamedWindow(ident)) = &f.over {
911+
err =
912+
Some(plan_err!("The window {ident} is not defined!"));
913+
return ControlFlow::Break(());
914+
}
924915
}
925916
}
917+
ControlFlow::Continue(())
918+
});
919+
if let Some(err) = err {
920+
return err;
926921
}
927-
ControlFlow::Continue(())
928-
});
929-
if let Some(err) = err {
930-
return Err(err);
922+
}
923+
}
924+
Ok(())
925+
}
926+
}
927+
928+
// If there are any multiple-defined windows, we raise an error.
929+
fn check_conflicting_windows(window_defs: &[NamedWindowDefinition]) -> Result<()> {
930+
for (i, window_def_i) in window_defs.iter().enumerate() {
931+
for window_def_j in window_defs.iter().skip(i + 1) {
932+
if window_def_i.0 == window_def_j.0 {
933+
return plan_err!(
934+
"The window {} is defined multiple times!",
935+
window_def_i.0
936+
);
931937
}
932938
}
933939
}

datafusion/sqllogictest/test_files/window.slt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5610,3 +5610,35 @@ DROP TABLE aggregate_test_100_utf8view;
56105610

56115611
statement ok
56125612
DROP TABLE aggregate_test_100
5613+
5614+
# window definitions with aliases
5615+
query II rowsort
5616+
SELECT
5617+
t1.v1,
5618+
SUM(t1.v1) OVER W + 1
5619+
FROM
5620+
generate_series(1, 5) AS t1(v1)
5621+
WINDOW
5622+
w AS (ORDER BY t1.v1);
5623+
----
5624+
1 2
5625+
2 4
5626+
3 7
5627+
4 11
5628+
5 16
5629+
5630+
# window definitions with aliases
5631+
query II rowsort
5632+
SELECT
5633+
t1.v1,
5634+
SUM(t1.v1) OVER w + 1
5635+
FROM
5636+
generate_series(1, 5) AS t1(v1)
5637+
WINDOW
5638+
W AS (ORDER BY t1.v1);
5639+
----
5640+
1 2
5641+
2 4
5642+
3 7
5643+
4 11
5644+
5 16

0 commit comments

Comments
 (0)