Skip to content

Commit 1d3095e

Browse files
authored
fix(query): reject duplicate named windows (#19978)
1 parent 66a717a commit 1d3095e

5 files changed

Lines changed: 40 additions & 4 deletions

File tree

src/query/sql/src/planner/binder/window.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
use std::collections::HashMap;
1616
use std::collections::HashSet;
17+
use std::collections::hash_map::Entry;
1718
use std::sync::Arc;
1819

1920
use databend_common_ast::Span;
@@ -97,9 +98,25 @@ impl Binder {
9798
let mut window_specs = HashMap::new();
9899
let mut resolved_window_specs = HashMap::new();
99100
for window in window_list {
100-
window_specs.insert(window.name.name.clone(), window.spec.clone());
101-
if window.spec.existing_window_name.is_none() {
102-
resolved_window_specs.insert(window.name.name.clone(), window.spec.clone());
101+
let window_name = self.normalize_identifier(&window.name).name;
102+
let mut window_spec = window.spec.clone();
103+
if let Some(existing_window_name) = &mut window_spec.existing_window_name {
104+
*existing_window_name = self.normalize_identifier(existing_window_name);
105+
}
106+
107+
match window_specs.entry(window_name.clone()) {
108+
Entry::Vacant(entry) => {
109+
entry.insert(window_spec.clone());
110+
}
111+
Entry::Occupied(_) => {
112+
return Err(ErrorCode::SemanticError(format!(
113+
"Duplicate window name: {window_name}"
114+
)));
115+
}
116+
}
117+
118+
if window_spec.existing_window_name.is_none() {
119+
resolved_window_specs.insert(window_name, window_spec);
103120
}
104121
}
105122
Ok((window_specs, resolved_window_specs))

src/query/sql/src/planner/semantic/type_check/window.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use super::CoreOrderByExprs;
4545
use super::TypeCheckAdapter;
4646
use super::TypeChecker;
4747
use crate::binder::ExprContext;
48+
use crate::planner::semantic::normalize_identifier;
4849
use crate::plans::CastExpr;
4950
use crate::plans::LagLeadFunction;
5051
use crate::plans::NthValueFunction;
@@ -377,10 +378,12 @@ where A: TypeCheckAdapter
377378
let spec = match window {
378379
CoreWindow::WindowSpec(spec) => spec,
379380
CoreWindow::WindowReference(window_name) => {
381+
let normalized_window_name =
382+
normalize_identifier(window_name, self.name_resolution_ctx).name;
380383
let spec = self
381384
.bind_context
382385
.window_definitions
383-
.get(&window_name.name)
386+
.get(&normalized_window_name)
384387
.ok_or_else(|| {
385388
ErrorCode::SyntaxException(format!(
386389
"Window definition {} not found",

src/query/sql/tests/it/semantic/binder.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,12 @@ async fn test_binder_named_window_paths() -> Result<()> {
444444
setup_sqls: &["CREATE TABLE empsalary(depname String, salary UInt64)"],
445445
sql: "SELECT depname, sum(sum(salary)) OVER w FROM empsalary GROUP BY depname",
446446
},
447+
SqlTestCase {
448+
name: "named_window_rejects_duplicate_name",
449+
description: "A WINDOW clause must reject duplicate names instead of silently keeping the later definition.",
450+
setup_sqls: &["CREATE TABLE empsalary(depname String, salary UInt64)"],
451+
sql: "SELECT rank() OVER w FROM empsalary WINDOW w AS (PARTITION BY depname), W AS (ORDER BY salary)",
452+
},
447453
SqlTestCase {
448454
name: "inherited_named_window_rejects_partition_override",
449455
description: "Referencing a named window must not add a new PARTITION BY clause.",

src/query/sql/tests/it/semantic/binder_window_named.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,13 @@ status: error
139139
code: 1005
140140
message: Window definition w not found
141141

142+
=== named_window_rejects_duplicate_name ===
143+
description: A WINDOW clause must reject duplicate names instead of silently keeping the later definition.
144+
sql: SELECT rank() OVER w FROM empsalary WINDOW w AS (PARTITION BY depname), W AS (ORDER BY salary)
145+
status: error
146+
code: 1065
147+
message: Duplicate window name: w
148+
142149
=== inherited_named_window_rejects_partition_override ===
143150
description: Referencing a named window must not add a new PARTITION BY clause.
144151
sql: SELECT rank() OVER w2 FROM empsalary WINDOW w1 AS (ORDER BY salary), w2 AS (w1 PARTITION BY depname)

tests/sqllogictests/suites/query/window_function/named_window_basic.test

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,5 +313,8 @@ SELECT sum(a) OVER w FROM t1 WINDOW w AS (ORDER BY unnest([1,2,3]))
313313
statement error 1065
314314
SELECT sum(a) OVER w FROM t1 WINDOW w AS (PARTITION BY (sum(a) OVER()))
315315

316+
statement error (?s)1065.*Duplicate window name: w
317+
SELECT rank() OVER w FROM empsalary WINDOW w AS (PARTITION BY depname), W AS (ORDER BY salary)
318+
316319
statement ok
317320
DROP DATABASE test_named_window_basic

0 commit comments

Comments
 (0)