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    /// UNNEST - Row expansion function that splits delimited strings
188    /// Used like: SELECT UNNEST(accounts, '|') AS account FROM fix_trades
189    /// Causes row multiplication - one input row becomes N output rows
190    Unnest {
191        column: Box<SqlExpression>,
192        delimiter: String,
193    },
194    /// NOT IN subquery
195    /// Used in expressions like: WHERE col NOT IN (SELECT id FROM table WHERE ...)
196    NotInSubquery {
197        expr: Box<SqlExpression>,
198        subquery: Box<SelectStatement>,
199    },
200}
201
202#[derive(Debug, Clone)]
203pub struct WhenBranch {
204    pub condition: Box<SqlExpression>,
205    pub result: Box<SqlExpression>,
206}
207
208#[derive(Debug, Clone)]
209pub struct SimpleWhenBranch {
210    pub value: Box<SqlExpression>,
211    pub result: Box<SqlExpression>,
212}
213
214// ===== WHERE Clause Types =====
215
216#[derive(Debug, Clone)]
217pub struct WhereClause {
218    pub conditions: Vec<Condition>,
219}
220
221#[derive(Debug, Clone)]
222pub struct Condition {
223    pub expr: SqlExpression,
224    pub connector: Option<LogicalOp>, // AND/OR connecting to next condition
225}
226
227#[derive(Debug, Clone)]
228pub enum LogicalOp {
229    And,
230    Or,
231}
232
233// ===== ORDER BY Types =====
234
235#[derive(Debug, Clone, PartialEq)]
236pub enum SortDirection {
237    Asc,
238    Desc,
239}
240
241#[derive(Debug, Clone)]
242pub struct OrderByColumn {
243    pub column: String,
244    pub direction: SortDirection,
245}
246
247// ===== Window Function Types =====
248
249/// Window frame bounds
250#[derive(Debug, Clone, PartialEq)]
251pub enum FrameBound {
252    UnboundedPreceding,
253    CurrentRow,
254    Preceding(i64),
255    Following(i64),
256    UnboundedFollowing,
257}
258
259/// Window frame unit (ROWS or RANGE)
260#[derive(Debug, Clone, PartialEq)]
261pub enum FrameUnit {
262    Rows,
263    Range,
264}
265
266/// Window frame specification
267#[derive(Debug, Clone)]
268pub struct WindowFrame {
269    pub unit: FrameUnit,
270    pub start: FrameBound,
271    pub end: Option<FrameBound>, // None means CURRENT ROW
272}
273
274#[derive(Debug, Clone)]
275pub struct WindowSpec {
276    pub partition_by: Vec<String>,
277    pub order_by: Vec<OrderByColumn>,
278    pub frame: Option<WindowFrame>, // Optional window frame
279}
280
281// ===== SELECT Statement Types =====
282
283/// Represents a SELECT item - either a simple column or a computed expression with alias
284#[derive(Debug, Clone)]
285pub enum SelectItem {
286    /// Simple column reference: "`column_name`"
287    Column(ColumnRef),
288    /// Computed expression with alias: "expr AS alias"
289    Expression { expr: SqlExpression, alias: String },
290    /// Star selector: "*"
291    Star,
292}
293
294#[derive(Debug, Clone)]
295pub struct SelectStatement {
296    pub distinct: bool,                // SELECT DISTINCT flag
297    pub columns: Vec<String>,          // Keep for backward compatibility, will be deprecated
298    pub select_items: Vec<SelectItem>, // New field for computed expressions
299    pub from_table: Option<String>,
300    pub from_subquery: Option<Box<SelectStatement>>, // Subquery in FROM clause
301    pub from_function: Option<TableFunction>,        // Table function like RANGE() in FROM clause
302    pub from_alias: Option<String>,                  // Alias for subquery (AS name)
303    pub joins: Vec<JoinClause>,                      // JOIN clauses
304    pub where_clause: Option<WhereClause>,
305    pub order_by: Option<Vec<OrderByColumn>>,
306    pub group_by: Option<Vec<SqlExpression>>, // Changed from Vec<String> to support expressions
307    pub having: Option<SqlExpression>,        // HAVING clause for post-aggregation filtering
308    pub limit: Option<usize>,
309    pub offset: Option<usize>,
310    pub ctes: Vec<CTE>, // Common Table Expressions (WITH clause)
311}
312
313// ===== Table and Join Types =====
314
315/// Table function that generates virtual tables
316#[derive(Debug, Clone)]
317pub enum TableFunction {
318    Generator {
319        name: String,
320        args: Vec<SqlExpression>,
321    },
322}
323
324/// Common Table Expression (CTE) structure
325#[derive(Debug, Clone)]
326pub struct CTE {
327    pub name: String,
328    pub column_list: Option<Vec<String>>, // Optional column list: WITH t(col1, col2) AS ...
329    pub cte_type: CTEType,
330}
331
332/// Type of CTE - standard SQL or WEB fetch
333#[derive(Debug, Clone)]
334pub enum CTEType {
335    Standard(SelectStatement),
336    Web(WebCTESpec),
337}
338
339/// Specification for WEB CTEs
340#[derive(Debug, Clone)]
341pub struct WebCTESpec {
342    pub url: String,
343    pub format: Option<DataFormat>,        // CSV, JSON, or auto-detect
344    pub headers: Vec<(String, String)>,    // HTTP headers
345    pub cache_seconds: Option<u64>,        // Cache duration
346    pub method: Option<HttpMethod>,        // HTTP method (GET, POST, etc.)
347    pub body: Option<String>,              // Request body for POST/PUT
348    pub json_path: Option<String>, // JSON path to extract (e.g., "Result" for {Result: [...]})
349    pub form_files: Vec<(String, String)>, // Multipart form files: (field_name, file_path)
350    pub form_fields: Vec<(String, String)>, // Multipart form fields: (field_name, value)
351}
352
353/// HTTP methods for WEB CTEs
354#[derive(Debug, Clone)]
355pub enum HttpMethod {
356    GET,
357    POST,
358    PUT,
359    DELETE,
360    PATCH,
361}
362
363/// Data format for WEB CTEs
364#[derive(Debug, Clone)]
365pub enum DataFormat {
366    CSV,
367    JSON,
368    Auto, // Auto-detect from Content-Type or extension
369}
370
371/// Table source - either a file/table name or a derived table (subquery/CTE)
372#[derive(Debug, Clone)]
373pub enum TableSource {
374    Table(String), // Regular table from CSV/JSON
375    DerivedTable {
376        // Both CTE and subquery
377        query: Box<SelectStatement>,
378        alias: String, // Required alias for subqueries
379    },
380}
381
382/// Join type enumeration
383#[derive(Debug, Clone, PartialEq)]
384pub enum JoinType {
385    Inner,
386    Left,
387    Right,
388    Full,
389    Cross,
390}
391
392/// Join operator for join conditions
393#[derive(Debug, Clone, PartialEq)]
394pub enum JoinOperator {
395    Equal,
396    NotEqual,
397    LessThan,
398    GreaterThan,
399    LessThanOrEqual,
400    GreaterThanOrEqual,
401}
402
403/// Single join condition
404#[derive(Debug, Clone)]
405pub struct SingleJoinCondition {
406    pub left_column: String, // Column from left table (can include table prefix)
407    pub operator: JoinOperator, // Join operator
408    pub right_column: String, // Column from right table (can include table prefix)
409}
410
411/// Join condition - can be multiple conditions connected by AND
412#[derive(Debug, Clone)]
413pub struct JoinCondition {
414    pub conditions: Vec<SingleJoinCondition>, // Multiple conditions connected by AND
415}
416
417/// Join clause structure
418#[derive(Debug, Clone)]
419pub struct JoinClause {
420    pub join_type: JoinType,
421    pub table: TableSource,       // The table being joined
422    pub alias: Option<String>,    // Optional alias for the joined table
423    pub condition: JoinCondition, // ON condition(s)
424}