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    pub into_table: Option<IntoTable>, // INTO clause for temporary tables
312}
313
314/// INTO clause for creating temporary tables
315#[derive(Debug, Clone, PartialEq)]
316pub struct IntoTable {
317    /// Name of the temporary table (must start with #)
318    pub name: String,
319}
320
321// ===== Table and Join Types =====
322
323/// Table function that generates virtual tables
324#[derive(Debug, Clone)]
325pub enum TableFunction {
326    Generator {
327        name: String,
328        args: Vec<SqlExpression>,
329    },
330}
331
332/// Common Table Expression (CTE) structure
333#[derive(Debug, Clone)]
334pub struct CTE {
335    pub name: String,
336    pub column_list: Option<Vec<String>>, // Optional column list: WITH t(col1, col2) AS ...
337    pub cte_type: CTEType,
338}
339
340/// Type of CTE - standard SQL or WEB fetch
341#[derive(Debug, Clone)]
342pub enum CTEType {
343    Standard(SelectStatement),
344    Web(WebCTESpec),
345}
346
347/// Specification for WEB CTEs
348#[derive(Debug, Clone)]
349pub struct WebCTESpec {
350    pub url: String,
351    pub format: Option<DataFormat>,        // CSV, JSON, or auto-detect
352    pub headers: Vec<(String, String)>,    // HTTP headers
353    pub cache_seconds: Option<u64>,        // Cache duration
354    pub method: Option<HttpMethod>,        // HTTP method (GET, POST, etc.)
355    pub body: Option<String>,              // Request body for POST/PUT
356    pub json_path: Option<String>, // JSON path to extract (e.g., "Result" for {Result: [...]})
357    pub form_files: Vec<(String, String)>, // Multipart form files: (field_name, file_path)
358    pub form_fields: Vec<(String, String)>, // Multipart form fields: (field_name, value)
359    pub template_vars: Vec<TemplateVar>, // Template variables for injection from temp tables
360}
361
362/// Template variable for injecting temp table data into WEB CTEs
363#[derive(Debug, Clone)]
364pub struct TemplateVar {
365    pub placeholder: String,    // e.g., "${#instruments}"
366    pub table_name: String,     // e.g., "#instruments"
367    pub column: Option<String>, // e.g., Some("symbol") for ${#instruments.symbol}
368    pub index: Option<usize>,   // e.g., Some(0) for ${#instruments[0]}
369}
370
371/// HTTP methods for WEB CTEs
372#[derive(Debug, Clone)]
373pub enum HttpMethod {
374    GET,
375    POST,
376    PUT,
377    DELETE,
378    PATCH,
379}
380
381/// Data format for WEB CTEs
382#[derive(Debug, Clone)]
383pub enum DataFormat {
384    CSV,
385    JSON,
386    Auto, // Auto-detect from Content-Type or extension
387}
388
389/// Table source - either a file/table name or a derived table (subquery/CTE)
390#[derive(Debug, Clone)]
391pub enum TableSource {
392    Table(String), // Regular table from CSV/JSON
393    DerivedTable {
394        // Both CTE and subquery
395        query: Box<SelectStatement>,
396        alias: String, // Required alias for subqueries
397    },
398}
399
400/// Join type enumeration
401#[derive(Debug, Clone, PartialEq)]
402pub enum JoinType {
403    Inner,
404    Left,
405    Right,
406    Full,
407    Cross,
408}
409
410/// Join operator for join conditions
411#[derive(Debug, Clone, PartialEq)]
412pub enum JoinOperator {
413    Equal,
414    NotEqual,
415    LessThan,
416    GreaterThan,
417    LessThanOrEqual,
418    GreaterThanOrEqual,
419}
420
421/// Single join condition
422#[derive(Debug, Clone)]
423pub struct SingleJoinCondition {
424    pub left_column: String, // Column from left table (can include table prefix)
425    pub operator: JoinOperator, // Join operator
426    pub right_column: String, // Column from right table (can include table prefix)
427}
428
429/// Join condition - can be multiple conditions connected by AND
430#[derive(Debug, Clone)]
431pub struct JoinCondition {
432    pub conditions: Vec<SingleJoinCondition>, // Multiple conditions connected by AND
433}
434
435/// Join clause structure
436#[derive(Debug, Clone)]
437pub struct JoinClause {
438    pub join_type: JoinType,
439    pub table: TableSource,       // The table being joined
440    pub alias: Option<String>,    // Optional alias for the joined table
441    pub condition: JoinCondition, // ON condition(s)
442}