Skip to content

Commit c497f39

Browse files
TimelordUKclaude
andcommitted
Add comprehensive tests for complex WHERE clauses with parentheses
Added 10 new tests covering various trade query patterns including: - Basic parenthesized OR conditions (status = "active" OR status = "pending") - Mixed AND/OR with parentheses ((symbol = "AAPL" OR symbol = "GOOGL") AND price > 100) - Nested parentheses for complex logic - Multiple OR groups with AND - String methods inside parentheses (symbol.StartsWith patterns) - Date range queries with DateTime comparisons - Price/volume filtering with nested ranges - Triple nested conditions - Parentheses around AND chains These tests ensure the parser correctly handles the complex WHERE clauses commonly used in trade data analysis and filtering. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 3989564 commit c497f39

1 file changed

Lines changed: 172 additions & 0 deletions

File tree

sql-cli/src/recursive_parser.rs

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,4 +1871,176 @@ mod tests {
18711871
let partial2 = extract_partial_at_end(query2);
18721872
assert_eq!(partial2, None); // FROM is a keyword, so no partial
18731873
}
1874+
1875+
// Complex WHERE clause tests with parentheses for trade queries
1876+
#[test]
1877+
fn test_complex_where_parentheses_basic() {
1878+
// Basic parenthesized OR condition
1879+
let mut parser = Parser::new(r#"SELECT * FROM trades WHERE (status = "active" OR status = "pending")"#);
1880+
let stmt = parser.parse().unwrap();
1881+
1882+
assert!(stmt.where_clause.is_some());
1883+
let where_clause = stmt.where_clause.unwrap();
1884+
assert_eq!(where_clause.conditions.len(), 1);
1885+
1886+
// Verify the structure is a BinaryOp with OR
1887+
if let SqlExpression::BinaryOp { op, .. } = &where_clause.conditions[0].expr {
1888+
assert_eq!(op, "OR");
1889+
} else {
1890+
panic!("Expected BinaryOp with OR");
1891+
}
1892+
}
1893+
1894+
#[test]
1895+
fn test_complex_where_mixed_and_or_with_parens() {
1896+
// (condition1 OR condition2) AND condition3
1897+
let mut parser = Parser::new(
1898+
r#"SELECT * FROM trades WHERE (symbol = "AAPL" OR symbol = "GOOGL") AND price > 100"#
1899+
);
1900+
let stmt = parser.parse().unwrap();
1901+
1902+
assert!(stmt.where_clause.is_some());
1903+
let where_clause = stmt.where_clause.unwrap();
1904+
assert_eq!(where_clause.conditions.len(), 2);
1905+
1906+
// First condition should be the parenthesized OR expression
1907+
if let SqlExpression::BinaryOp { op, .. } = &where_clause.conditions[0].expr {
1908+
assert_eq!(op, "OR");
1909+
} else {
1910+
panic!("Expected first condition to be OR expression");
1911+
}
1912+
1913+
// Should have AND connector to next condition
1914+
assert!(matches!(where_clause.conditions[0].connector, Some(LogicalOp::And)));
1915+
1916+
// Second condition should be price > 100
1917+
if let SqlExpression::BinaryOp { op, .. } = &where_clause.conditions[1].expr {
1918+
assert_eq!(op, ">");
1919+
} else {
1920+
panic!("Expected second condition to be price > 100");
1921+
}
1922+
}
1923+
1924+
#[test]
1925+
fn test_complex_where_nested_parentheses() {
1926+
// ((condition1 OR condition2) AND condition3) OR condition4
1927+
let mut parser = Parser::new(
1928+
r#"SELECT * FROM trades WHERE ((symbol = "AAPL" OR symbol = "GOOGL") AND price > 100) OR status = "cancelled""#
1929+
);
1930+
let stmt = parser.parse().unwrap();
1931+
1932+
assert!(stmt.where_clause.is_some());
1933+
let where_clause = stmt.where_clause.unwrap();
1934+
1935+
// Should parse successfully with nested structure
1936+
assert!(where_clause.conditions.len() > 0);
1937+
}
1938+
1939+
#[test]
1940+
fn test_complex_where_multiple_or_groups() {
1941+
// (group1) AND (group2) - common pattern for filtering trades
1942+
let mut parser = Parser::new(
1943+
r#"SELECT * FROM trades WHERE (symbol = "AAPL" OR symbol = "GOOGL" OR symbol = "MSFT") AND (price > 100 AND price < 500)"#
1944+
);
1945+
let stmt = parser.parse().unwrap();
1946+
1947+
assert!(stmt.where_clause.is_some());
1948+
let where_clause = stmt.where_clause.unwrap();
1949+
assert_eq!(where_clause.conditions.len(), 2);
1950+
1951+
// First condition group should have OR
1952+
assert!(matches!(where_clause.conditions[0].connector, Some(LogicalOp::And)));
1953+
}
1954+
1955+
#[test]
1956+
fn test_complex_where_with_methods_in_parens() {
1957+
// Using string methods inside parentheses
1958+
let mut parser = Parser::new(
1959+
r#"SELECT * FROM trades WHERE (symbol.StartsWith("A") OR symbol.StartsWith("G")) AND volume > 1000000"#
1960+
);
1961+
let stmt = parser.parse().unwrap();
1962+
1963+
assert!(stmt.where_clause.is_some());
1964+
let where_clause = stmt.where_clause.unwrap();
1965+
assert_eq!(where_clause.conditions.len(), 2);
1966+
1967+
// First condition should be the OR of two method calls
1968+
if let SqlExpression::BinaryOp { op, left, right } = &where_clause.conditions[0].expr {
1969+
assert_eq!(op, "OR");
1970+
assert!(matches!(left.as_ref(), SqlExpression::MethodCall { .. }));
1971+
assert!(matches!(right.as_ref(), SqlExpression::MethodCall { .. }));
1972+
} else {
1973+
panic!("Expected OR of method calls");
1974+
}
1975+
}
1976+
1977+
#[test]
1978+
fn test_complex_where_date_comparisons_with_parens() {
1979+
// Date range queries common in trade analysis
1980+
let mut parser = Parser::new(
1981+
r#"SELECT * FROM trades WHERE (executionDate > DateTime(2024, 1, 1) AND executionDate < DateTime(2024, 12, 31)) AND (status = "filled" OR status = "partial")"#
1982+
);
1983+
let stmt = parser.parse().unwrap();
1984+
1985+
assert!(stmt.where_clause.is_some());
1986+
let where_clause = stmt.where_clause.unwrap();
1987+
assert_eq!(where_clause.conditions.len(), 2);
1988+
1989+
// Both condition groups should parse correctly
1990+
assert!(matches!(where_clause.conditions[0].connector, Some(LogicalOp::And)));
1991+
}
1992+
1993+
#[test]
1994+
fn test_complex_where_price_volume_filters() {
1995+
// Complex trade filtering by price and volume
1996+
let mut parser = Parser::new(
1997+
r#"SELECT * FROM trades WHERE ((price > 100 AND price < 200) OR (price > 500 AND price < 1000)) AND volume > 10000"#
1998+
);
1999+
let stmt = parser.parse().unwrap();
2000+
2001+
assert!(stmt.where_clause.is_some());
2002+
let where_clause = stmt.where_clause.unwrap();
2003+
2004+
// Should handle nested price ranges with OR
2005+
assert!(where_clause.conditions.len() > 0);
2006+
}
2007+
2008+
#[test]
2009+
fn test_complex_where_mixed_string_numeric() {
2010+
// Mix of string comparisons and numeric comparisons in groups
2011+
let mut parser = Parser::new(
2012+
r#"SELECT * FROM trades WHERE (exchange = "NYSE" OR exchange = "NASDAQ") AND (volume > 1000000 OR notes.Contains("urgent"))"#
2013+
);
2014+
let stmt = parser.parse().unwrap();
2015+
2016+
assert!(stmt.where_clause.is_some());
2017+
// Should parse without errors
2018+
}
2019+
2020+
#[test]
2021+
fn test_complex_where_triple_nested() {
2022+
// Very complex nesting - ((a OR b) AND (c OR d)) OR (e AND f)
2023+
let mut parser = Parser::new(
2024+
r#"SELECT * FROM trades WHERE ((symbol = "AAPL" OR symbol = "GOOGL") AND (price > 100 OR volume > 1000000)) OR (status = "cancelled" AND reason.Contains("timeout"))"#
2025+
);
2026+
let stmt = parser.parse().unwrap();
2027+
2028+
assert!(stmt.where_clause.is_some());
2029+
// Should handle triple nesting correctly
2030+
}
2031+
2032+
#[test]
2033+
fn test_complex_where_single_parens_around_and() {
2034+
// Parentheses around AND conditions
2035+
let mut parser = Parser::new(
2036+
r#"SELECT * FROM trades WHERE (symbol = "AAPL" AND price > 150 AND volume > 100000)"#
2037+
);
2038+
let stmt = parser.parse().unwrap();
2039+
2040+
assert!(stmt.where_clause.is_some());
2041+
let where_clause = stmt.where_clause.unwrap();
2042+
2043+
// Should correctly parse the AND chain inside parentheses
2044+
assert!(where_clause.conditions.len() > 0);
2045+
}
18742046
}

0 commit comments

Comments
 (0)