use vibesql_ast::Statement;
use vibesql_executor::ExplainExecutor;
use vibesql_parser::Parser;
use vibesql_storage::Database;
fn setup_db() -> Database {
let mut db = Database::new();
let create =
Parser::parse_sql("CREATE TABLE t5 (a INTEGER, b INTEGER, c INTEGER, d INTEGER)").unwrap();
if let Statement::CreateTable(stmt) = create {
vibesql_executor::CreateTableExecutor::execute(&stmt, &mut db).unwrap();
} else {
panic!("Expected CREATE TABLE statement");
}
let create_index = Parser::parse_sql("CREATE INDEX t5ab ON t5(a, b)").unwrap();
if let Statement::CreateIndex(stmt) = create_index {
vibesql_executor::CreateIndexExecutor::execute(&stmt, &mut db).unwrap();
} else {
panic!("Expected CREATE INDEX statement");
}
db
}
fn order_count(db: &Database, sql: &str) -> usize {
let explain_sql = format!("EXPLAIN QUERY PLAN {}", sql);
let stmt = Parser::parse_sql(&explain_sql).expect("Failed to parse SQL");
if let Statement::Explain(explain_stmt) = stmt {
let result = ExplainExecutor::execute(&explain_stmt, db).expect("EXPLAIN failed");
result.to_sqlite_eqp().matches("ORDER").count()
} else {
panic!("Expected EXPLAIN statement");
}
}
#[test]
fn test_index_satisfied_window_sort_emits_no_entry() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY a, b),
sum(c) OVER (PARTITION BY a ORDER BY b)
FROM t5",
);
assert_eq!(count, 0);
}
#[test]
fn test_duplicate_window_keys_dedup_to_one_entry() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY b, a),
sum(c) OVER (PARTITION BY b ORDER BY a)
FROM t5",
);
assert_eq!(count, 1);
}
#[test]
fn test_distinct_window_keys_emit_separate_entries() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY b, a),
sum(c) OVER (ORDER BY c, b)
FROM t5",
);
assert_eq!(count, 2);
}
#[test]
fn test_frame_clauses_ignored_for_dedup() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY b ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),
sum(c) OVER (ORDER BY b RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),
sum(c) OVER (ORDER BY b GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
FROM t5",
);
assert_eq!(count, 1);
}
#[test]
fn test_same_expression_key_dedups() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY b+1 ROWS UNBOUNDED PRECEDING),
sum(c) OVER (ORDER BY b+1 RANGE UNBOUNDED PRECEDING),
sum(c) OVER (ORDER BY b+1 GROUPS UNBOUNDED PRECEDING)
FROM t5",
);
assert_eq!(count, 1);
}
#[test]
fn test_distinct_expression_keys_emit_separate_entries() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY b+1 ROWS UNBOUNDED PRECEDING),
sum(c) OVER (ORDER BY b+2 RANGE UNBOUNDED PRECEDING),
sum(c) OVER (ORDER BY b+3 GROUPS UNBOUNDED PRECEDING)
FROM t5",
);
assert_eq!(count, 3);
}
#[test]
fn test_empty_over_clause_emits_no_entry() {
let db = setup_db();
let count = order_count(&db, "SELECT sum(c) OVER () FROM t5");
assert_eq!(count, 0);
}
#[test]
fn test_window_sort_and_statement_order_by_both_counted() {
let db = setup_db();
let count = order_count(&db, "SELECT sum(c) OVER (ORDER BY c) FROM t5 ORDER BY b");
assert_eq!(count, 2);
}
#[test]
fn test_named_window_resolved_for_key() {
let db = setup_db();
let count = order_count(
&db,
"SELECT sum(c) OVER w, sum(b) OVER (ORDER BY c) FROM t5 WINDOW w AS (ORDER BY c)",
);
assert_eq!(count, 1);
let count =
order_count(&db, "SELECT sum(c) OVER w FROM t5 WINDOW w AS (PARTITION BY a ORDER BY b)");
assert_eq!(count, 0);
}
#[test]
fn test_window_function_nested_in_arithmetic() {
let db = setup_db();
let count = order_count(&db, "SELECT 1 + sum(c) OVER (ORDER BY c) FROM t5");
assert_eq!(count, 1);
}
#[test]
fn test_direction_distinguishes_keys() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY c ASC),
sum(c) OVER (ORDER BY c DESC)
FROM t5",
);
assert_eq!(count, 2);
}
#[test]
fn test_statement_order_by_identical_to_window_key_suppressed() {
let db = setup_db();
let count = order_count(&db, "SELECT sum(c) OVER (ORDER BY c) FROM t5 ORDER BY c");
assert_eq!(count, 1);
}
#[test]
fn test_statement_order_by_extending_window_key_not_suppressed() {
let db = setup_db();
let count = order_count(&db, "SELECT sum(c) OVER (ORDER BY c) FROM t5 ORDER BY c, b");
assert_eq!(count, 2);
}
#[test]
fn test_statement_order_by_prefix_of_window_key_suppressed() {
let db = setup_db();
let count = order_count(&db, "SELECT sum(c) OVER (ORDER BY c, b) FROM t5 ORDER BY c");
assert_eq!(count, 1);
}
#[test]
fn test_statement_order_by_direction_mismatch_not_suppressed() {
let db = setup_db();
let count = order_count(&db, "SELECT sum(c) OVER (ORDER BY c) FROM t5 ORDER BY c DESC");
assert_eq!(count, 2);
}
#[test]
fn test_statement_order_by_desc_exact_match_suppressed() {
let db = setup_db();
let count = order_count(&db, "SELECT sum(c) OVER (ORDER BY c DESC) FROM t5 ORDER BY c DESC");
assert_eq!(count, 1);
}
#[test]
fn test_statement_order_by_desc_prefix_suppressed() {
let db = setup_db();
let count = order_count(&db, "SELECT sum(c) OVER (ORDER BY c DESC, b) FROM t5 ORDER BY c DESC");
assert_eq!(count, 1);
}
#[test]
fn test_statement_order_by_matching_partition_key_suppressed() {
let db = setup_db();
let count = order_count(&db, "SELECT sum(c) OVER (PARTITION BY c) FROM t5 ORDER BY c");
assert_eq!(count, 1);
}
#[test]
fn test_statement_order_by_partition_prefix_suppressed() {
let db = setup_db();
let count =
order_count(&db, "SELECT sum(c) OVER (PARTITION BY c ORDER BY b) FROM t5 ORDER BY c");
assert_eq!(count, 1);
}
#[test]
fn test_statement_order_by_collate_mismatch_not_suppressed() {
let db = setup_db();
let count =
order_count(&db, "SELECT sum(c) OVER (ORDER BY c) FROM t5 ORDER BY c COLLATE NOCASE");
assert_eq!(count, 2);
}
#[test]
fn test_statement_order_by_matching_first_window_suppressed() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY c),
sum(c) OVER (ORDER BY b)
FROM t5 ORDER BY c",
);
assert_eq!(count, 2);
}
#[test]
fn test_statement_order_by_matching_second_window_not_suppressed() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY c),
sum(c) OVER (ORDER BY b)
FROM t5 ORDER BY b",
);
assert_eq!(count, 3);
}
#[test]
fn test_empty_first_window_key_never_suppresses() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (),
sum(c) OVER (ORDER BY c)
FROM t5 ORDER BY c",
);
assert_eq!(count, 2);
}
#[test]
fn test_suppression_follows_window_order_in_select_list() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY b),
sum(c) OVER (ORDER BY c)
FROM t5 ORDER BY b",
);
assert_eq!(count, 2);
}
#[test]
fn test_index_key_first_of_two_windows_not_suppressed() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY a, b),
sum(a) OVER (ORDER BY c)
FROM t5",
);
assert_eq!(count, 2);
}
#[test]
fn test_index_key_last_of_two_windows_suppressed() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(a) OVER (ORDER BY c),
sum(c) OVER (ORDER BY a, b)
FROM t5",
);
assert_eq!(count, 1);
}
#[test]
fn test_index_key_first_of_three_windows_not_suppressed() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(d) OVER (ORDER BY a, b),
sum(d) OVER (ORDER BY c),
sum(d) OVER (ORDER BY d)
FROM t5",
);
assert_eq!(count, 3);
}
#[test]
fn test_index_key_middle_of_three_windows_not_suppressed() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(d) OVER (ORDER BY c),
sum(d) OVER (ORDER BY a, b),
sum(d) OVER (ORDER BY d)
FROM t5",
);
assert_eq!(count, 3);
}
#[test]
fn test_index_key_last_of_three_windows_suppressed() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(d) OVER (ORDER BY c),
sum(d) OVER (ORDER BY d),
sum(d) OVER (ORDER BY a, b)
FROM t5",
);
assert_eq!(count, 2);
}
#[test]
fn test_repeated_index_key_keeps_first_position_not_suppressed() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY a, b),
sum(a) OVER (ORDER BY c),
sum(b) OVER (ORDER BY a, b)
FROM t5",
);
assert_eq!(count, 2);
}
#[test]
fn test_repeated_outer_key_leaves_index_key_innermost_suppressed() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(a) OVER (ORDER BY c),
sum(c) OVER (ORDER BY a, b),
sum(b) OVER (ORDER BY c)
FROM t5",
);
assert_eq!(count, 1);
}
fn setup_window9_db() -> Database {
let mut db = Database::new();
let create = Parser::parse_sql(
"CREATE TABLE t1 (a INTEGER, b INTEGER, c INTEGER, d INTEGER, e INTEGER)",
)
.unwrap();
if let Statement::CreateTable(stmt) = create {
vibesql_executor::CreateTableExecutor::execute(&stmt, &mut db).unwrap();
} else {
panic!("Expected CREATE TABLE statement");
}
let create_index = Parser::parse_sql("CREATE INDEX i1 ON t1(a, b, c, d, e)").unwrap();
if let Statement::CreateIndex(stmt) = create_index {
vibesql_executor::CreateIndexExecutor::execute(&stmt, &mut db).unwrap();
} else {
panic!("Expected CREATE INDEX statement");
}
db
}
#[test]
fn test_window9_5_1_1_covering_index_satisfies_all_passes() {
let db = setup_window9_db();
let count = order_count(
&db,
"SELECT
sum(e) OVER (),
sum(e) OVER (ORDER BY a),
sum(e) OVER (PARTITION BY a ORDER BY b),
sum(e) OVER (PARTITION BY a, b ORDER BY c),
sum(e) OVER (PARTITION BY a, b, c ORDER BY d)
FROM t1",
);
assert_eq!(count, 0);
}
#[test]
fn test_nested_prefix_chain_fully_suppressed() {
let db = setup_window9_db();
let count = order_count(
&db,
"SELECT
sum(e) OVER (ORDER BY a),
sum(e) OVER (ORDER BY a, b),
sum(e) OVER (ORDER BY a, b, c)
FROM t1",
);
assert_eq!(count, 0);
}
#[test]
fn test_prefix_chain_break_counts_remaining_outer_keys() {
let db = setup_window9_db();
let count = order_count(
&db,
"SELECT
sum(e) OVER (ORDER BY a),
sum(e) OVER (ORDER BY c),
sum(e) OVER (ORDER BY a, b, c)
FROM t1",
);
assert_eq!(count, 2);
}
#[test]
fn test_outer_prefix_of_index_satisfied_innermost_suppressed() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY a),
sum(c) OVER (ORDER BY a, b)
FROM t5",
);
assert_eq!(count, 0);
}
#[test]
fn test_outer_non_prefix_of_index_satisfied_innermost_counts() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY b),
sum(c) OVER (ORDER BY a, b)
FROM t5",
);
assert_eq!(count, 1);
}
#[test]
fn test_outer_prefix_direction_mismatch_counts() {
let db = setup_db();
let count = order_count(
&db,
"SELECT
sum(c) OVER (ORDER BY a DESC),
sum(c) OVER (ORDER BY a, b)
FROM t5",
);
assert_eq!(count, 1);
}