sql_cli/sql/parser/
ast.rs

1//! Abstract Syntax Tree (AST) definitions for SQL queries
2//!
3//! This module contains all the data structures that represent
4//! the parsed SQL query structure.
5
6// ===== Expression Types =====
7
8/// Quote style for identifiers (column names, table names, etc.)
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10pub enum QuoteStyle {
11    /// No quotes needed (valid unquoted identifier)
12    None,
13    /// Double quotes: "Customer Id"
14    DoubleQuotes,
15    /// SQL Server style brackets: [Customer Id]
16    Brackets,
17}
18
19/// Column reference with optional quoting information and table prefix
20#[derive(Debug, Clone, PartialEq, Eq, Hash)]
21pub struct ColumnRef {
22    pub name: String,
23    pub quote_style: QuoteStyle,
24    /// Optional table/alias prefix (e.g., "messages" in "messages.field_name")
25    pub table_prefix: Option<String>,
26}
27
28impl ColumnRef {
29    /// Create an unquoted column reference
30    pub fn unquoted(name: String) -> Self {
31        Self {
32            name,
33            quote_style: QuoteStyle::None,
34            table_prefix: None,
35        }
36    }
37
38    /// Create a double-quoted column reference
39    pub fn quoted(name: String) -> Self {
40        Self {
41            name,
42            quote_style: QuoteStyle::DoubleQuotes,
43            table_prefix: None,
44        }
45    }
46
47    /// Create a qualified column reference (table.column)
48    pub fn qualified(table: String, name: String) -> Self {
49        Self {
50            name,
51            quote_style: QuoteStyle::None,
52            table_prefix: Some(table),
53        }
54    }
55
56    /// Get the full qualified string representation
57    pub fn to_qualified_string(&self) -> String {
58        match &self.table_prefix {
59            Some(table) => format!("{}.{}", table, self.name),
60            None => self.name.clone(),
61        }
62    }
63
64    /// Create a bracket-quoted column reference
65    pub fn bracketed(name: String) -> Self {
66        Self {
67            name,
68            quote_style: QuoteStyle::Brackets,
69            table_prefix: None,
70        }
71    }
72
73    /// Format the column reference with appropriate quoting
74    pub fn to_sql(&self) -> String {
75        let column_part = match self.quote_style {
76            QuoteStyle::None => self.name.clone(),
77            QuoteStyle::DoubleQuotes => format!("\"{}\"", self.name),
78            QuoteStyle::Brackets => format!("[{}]", self.name),
79        };
80
81        match &self.table_prefix {
82            Some(table) => format!("{}.{}", table, column_part),
83            None => column_part,
84        }
85    }
86}
87
88impl PartialEq<str> for ColumnRef {
89    fn eq(&self, other: &str) -> bool {
90        self.name == other
91    }
92}
93
94impl PartialEq<&str> for ColumnRef {
95    fn eq(&self, other: &&str) -> bool {
96        self.name == *other
97    }
98}
99
100impl std::fmt::Display for ColumnRef {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        write!(f, "{}", self.to_sql())
103    }
104}
105
106#[derive(Debug, Clone)]
107pub enum SqlExpression {
108    Column(ColumnRef),
109    StringLiteral(String),
110    NumberLiteral(String),
111    BooleanLiteral(bool),
112    Null, // NULL literal
113    DateTimeConstructor {
114        year: i32,
115        month: u32,
116        day: u32,
117        hour: Option<u32>,
118        minute: Option<u32>,
119        second: Option<u32>,
120    },
121    DateTimeToday {
122        hour: Option<u32>,
123        minute: Option<u32>,
124        second: Option<u32>,
125    },
126    MethodCall {
127        object: String,
128        method: String,
129        args: Vec<SqlExpression>,
130    },
131    ChainedMethodCall {
132        base: Box<SqlExpression>,
133        method: String,
134        args: Vec<SqlExpression>,
135    },
136    FunctionCall {
137        name: String,
138        args: Vec<SqlExpression>,
139        distinct: bool, // For COUNT(DISTINCT col), SUM(DISTINCT col), etc.
140    },
141    WindowFunction {
142        name: String,
143        args: Vec<SqlExpression>,
144        window_spec: WindowSpec,
145    },
146    BinaryOp {
147        left: Box<SqlExpression>,
148        op: String,
149        right: Box<SqlExpression>,
150    },
151    InList {
152        expr: Box<SqlExpression>,
153        values: Vec<SqlExpression>,
154    },
155    NotInList {
156        expr: Box<SqlExpression>,
157        values: Vec<SqlExpression>,
158    },
159    Between {
160        expr: Box<SqlExpression>,
161        lower: Box<SqlExpression>,
162        upper: Box<SqlExpression>,
163    },
164    Not {
165        expr: Box<SqlExpression>,
166    },
167    CaseExpression {
168        when_branches: Vec<WhenBranch>,
169        else_branch: Option<Box<SqlExpression>>,
170    },
171    SimpleCaseExpression {
172        expr: Box<SqlExpression>,
173        when_branches: Vec<SimpleWhenBranch>,
174        else_branch: Option<Box<SqlExpression>>,
175    },
176    /// Scalar subquery that returns a single value
177    /// Used in expressions like: WHERE col = (SELECT MAX(id) FROM table)
178    ScalarSubquery {
179        query: Box<SelectStatement>,
180    },
181    /// IN subquery that returns multiple values  
182    /// Used in expressions like: WHERE col IN (SELECT id FROM table WHERE ...)
183    InSubquery {
184        expr: Box<SqlExpression>,
185        subquery: Box<SelectStatement>,
186    },
187    /// NOT IN subquery
188    /// Used in expressions like: WHERE col NOT IN (SELECT id FROM table WHERE ...)
189    NotInSubquery {
190        expr: Box<SqlExpression>,
191        subquery: Box<SelectStatement>,
192    },
193}
194
195#[derive(Debug, Clone)]
196pub struct WhenBranch {
197    pub condition: Box<SqlExpression>,
198    pub result: Box<SqlExpression>,
199}
200
201#[derive(Debug, Clone)]
202pub struct SimpleWhenBranch {
203    pub value: Box<SqlExpression>,
204    pub result: Box<SqlExpression>,
205}
206
207// ===== WHERE Clause Types =====
208
209#[derive(Debug, Clone)]
210pub struct WhereClause {
211    pub conditions: Vec<Condition>,
212}
213
214#[derive(Debug, Clone)]
215pub struct Condition {
216    pub expr: SqlExpression,
217    pub connector: Option<LogicalOp>, // AND/OR connecting to next condition
218}
219
220#[derive(Debug, Clone)]
221pub enum LogicalOp {
222    And,
223    Or,
224}
225
226// ===== ORDER BY Types =====
227
228#[derive(Debug, Clone, PartialEq)]
229pub enum SortDirection {
230    Asc,
231    Desc,
232}
233
234#[derive(Debug, Clone)]
235pub struct OrderByColumn {
236    pub column: String,
237    pub direction: SortDirection,
238}
239
240// ===== Window Function Types =====
241
242/// Window frame bounds
243#[derive(Debug, Clone, PartialEq)]
244pub enum FrameBound {
245    UnboundedPreceding,
246    CurrentRow,
247    Preceding(i64),
248    Following(i64),
249    UnboundedFollowing,
250}
251
252/// Window frame unit (ROWS or RANGE)
253#[derive(Debug, Clone, PartialEq)]
254pub enum FrameUnit {
255    Rows,
256    Range,
257}
258
259/// Window frame specification
260#[derive(Debug, Clone)]
261pub struct WindowFrame {
262    pub unit: FrameUnit,
263    pub start: FrameBound,
264    pub end: Option<FrameBound>, // None means CURRENT ROW
265}
266
267#[derive(Debug, Clone)]
268pub struct WindowSpec {
269    pub partition_by: Vec<String>,
270    pub order_by: Vec<OrderByColumn>,
271    pub frame: Option<WindowFrame>, // Optional window frame
272}
273
274// ===== SELECT Statement Types =====
275
276/// Represents a SELECT item - either a simple column or a computed expression with alias
277#[derive(Debug, Clone)]
278pub enum SelectItem {
279    /// Simple column reference: "`column_name`"
280    Column(ColumnRef),
281    /// Computed expression with alias: "expr AS alias"
282    Expression { expr: SqlExpression, alias: String },
283    /// Star selector: "*"
284    Star,
285}
286
287#[derive(Debug, Clone)]
288pub struct SelectStatement {
289    pub distinct: bool,                // SELECT DISTINCT flag
290    pub columns: Vec<String>,          // Keep for backward compatibility, will be deprecated
291    pub select_items: Vec<SelectItem>, // New field for computed expressions
292    pub from_table: Option<String>,
293    pub from_subquery: Option<Box<SelectStatement>>, // Subquery in FROM clause
294    pub from_function: Option<TableFunction>,        // Table function like RANGE() in FROM clause
295    pub from_alias: Option<String>,                  // Alias for subquery (AS name)
296    pub joins: Vec<JoinClause>,                      // JOIN clauses
297    pub where_clause: Option<WhereClause>,
298    pub order_by: Option<Vec<OrderByColumn>>,
299    pub group_by: Option<Vec<SqlExpression>>, // Changed from Vec<String> to support expressions
300    pub having: Option<SqlExpression>,        // HAVING clause for post-aggregation filtering
301    pub limit: Option<usize>,
302    pub offset: Option<usize>,
303    pub ctes: Vec<CTE>, // Common Table Expressions (WITH clause)
304}
305
306// ===== Table and Join Types =====
307
308/// Table function that generates virtual tables
309#[derive(Debug, Clone)]
310pub enum TableFunction {
311    Generator {
312        name: String,
313        args: Vec<SqlExpression>,
314    },
315}
316
317/// Common Table Expression (CTE) structure
318#[derive(Debug, Clone)]
319pub struct CTE {
320    pub name: String,
321    pub column_list: Option<Vec<String>>, // Optional column list: WITH t(col1, col2) AS ...
322    pub cte_type: CTEType,
323}
324
325/// Type of CTE - standard SQL or WEB fetch
326#[derive(Debug, Clone)]
327pub enum CTEType {
328    Standard(SelectStatement),
329    Web(WebCTESpec),
330}
331
332/// Specification for WEB CTEs
333#[derive(Debug, Clone)]
334pub struct WebCTESpec {
335    pub url: String,
336    pub format: Option<DataFormat>,     // CSV, JSON, or auto-detect
337    pub headers: Vec<(String, String)>, // HTTP headers
338    pub cache_seconds: Option<u64>,     // Cache duration
339}
340
341/// Data format for WEB CTEs
342#[derive(Debug, Clone)]
343pub enum DataFormat {
344    CSV,
345    JSON,
346    Auto, // Auto-detect from Content-Type or extension
347}
348
349/// Table source - either a file/table name or a derived table (subquery/CTE)
350#[derive(Debug, Clone)]
351pub enum TableSource {
352    Table(String), // Regular table from CSV/JSON
353    DerivedTable {
354        // Both CTE and subquery
355        query: Box<SelectStatement>,
356        alias: String, // Required alias for subqueries
357    },
358}
359
360/// Join type enumeration
361#[derive(Debug, Clone, PartialEq)]
362pub enum JoinType {
363    Inner,
364    Left,
365    Right,
366    Full,
367    Cross,
368}
369
370/// Join operator for join conditions
371#[derive(Debug, Clone, PartialEq)]
372pub enum JoinOperator {
373    Equal,
374    NotEqual,
375    LessThan,
376    GreaterThan,
377    LessThanOrEqual,
378    GreaterThanOrEqual,
379}
380
381/// Join condition - initially just column equality
382#[derive(Debug, Clone)]
383pub struct JoinCondition {
384    pub left_column: String, // Column from left table (can include table prefix)
385    pub operator: JoinOperator, // Join operator (initially just Equal)
386    pub right_column: String, // Column from right table (can include table prefix)
387}
388
389/// Join clause structure
390#[derive(Debug, Clone)]
391pub struct JoinClause {
392    pub join_type: JoinType,
393    pub table: TableSource,       // The table being joined
394    pub alias: Option<String>,    // Optional alias for the joined table
395    pub condition: JoinCondition, // ON condition
396}