vibesql/ast/
mod.rs

1//! Abstract Syntax Tree (AST) definitions for SQL statements.
2//!
3//! This module defines the complete AST structure for representing parsed SQL,
4//! following standard SQL conventions.
5
6mod expr;
7mod stmt;
8mod types;
9
10// Re-export types module first (has DataTypeSpec needed by others)
11pub use types::StructField as TypeStructField;
12pub use types::{DataTypeKind, DataTypeSpec};
13
14// Re-export expression types
15pub use expr::*;
16
17// Re-export statement types
18pub use stmt::*;
19
20use crate::error::Span;
21
22/// An identifier (table name, column name, etc.).
23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24pub struct Ident {
25    /// The identifier value (unquoted or with quotes removed).
26    pub value: String,
27    /// Whether this identifier was quoted (backticks).
28    pub quoted: bool,
29    /// Source span.
30    pub span: Span,
31}
32
33impl Ident {
34    pub fn new(value: impl Into<String>, span: Span) -> Self {
35        Self {
36            value: value.into(),
37            quoted: false,
38            span,
39        }
40    }
41
42    pub fn quoted(value: impl Into<String>, span: Span) -> Self {
43        Self {
44            value: value.into(),
45            quoted: true,
46            span,
47        }
48    }
49
50    /// Check if this identifier matches another (case-insensitive for unquoted).
51    pub fn matches(&self, other: &str) -> bool {
52        if self.quoted {
53            self.value == other
54        } else {
55            self.value.eq_ignore_ascii_case(other)
56        }
57    }
58}
59
60impl std::fmt::Display for Ident {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        if self.quoted {
63            write!(f, "`{}`", self.value)
64        } else {
65            write!(f, "{}", self.value)
66        }
67    }
68}
69
70/// A qualified name (schema.table, catalog.schema.table, etc.).
71#[derive(Debug, Clone, PartialEq)]
72pub struct ObjectName {
73    pub parts: Vec<Ident>,
74    pub span: Span,
75}
76
77impl ObjectName {
78    pub fn new(parts: Vec<Ident>, span: Span) -> Self {
79        Self { parts, span }
80    }
81
82    pub fn simple(name: Ident) -> Self {
83        let span = name.span;
84        Self {
85            parts: vec![name],
86            span,
87        }
88    }
89
90    /// Get the table/object name (last part).
91    pub fn name(&self) -> Option<&Ident> {
92        self.parts.last()
93    }
94
95    /// Get the schema name (second-to-last part, if any).
96    pub fn schema(&self) -> Option<&Ident> {
97        if self.parts.len() >= 2 {
98            Some(&self.parts[self.parts.len() - 2])
99        } else {
100            None
101        }
102    }
103
104    /// Get the catalog name (third-to-last part, if any).
105    pub fn catalog(&self) -> Option<&Ident> {
106        if self.parts.len() >= 3 {
107            Some(&self.parts[self.parts.len() - 3])
108        } else {
109            None
110        }
111    }
112}
113
114impl std::fmt::Display for ObjectName {
115    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116        let parts: Vec<String> = self.parts.iter().map(|p| p.to_string()).collect();
117        write!(f, "{}", parts.join("."))
118    }
119}
120
121/// Column definition for CREATE TABLE.
122#[derive(Debug, Clone, PartialEq)]
123pub struct ColumnDef {
124    pub name: Ident,
125    pub data_type: Option<DataTypeSpec>,
126    pub constraints: Vec<ColumnConstraint>,
127    pub options: Vec<SqlOption>,
128    pub span: Span,
129}
130
131/// Column constraint.
132#[derive(Debug, Clone, PartialEq)]
133pub enum ColumnConstraint {
134    NotNull,
135    Null,
136    PrimaryKey,
137    Unique,
138    Default(Box<Expr>),
139    Check(Box<Expr>),
140    References {
141        table: ObjectName,
142        columns: Vec<Ident>,
143        on_delete: Option<ReferentialAction>,
144        on_update: Option<ReferentialAction>,
145    },
146    Generated {
147        expr: Box<Expr>,
148        always: bool,
149    },
150    Hidden,
151}
152
153/// Referential action for foreign keys.
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155pub enum ReferentialAction {
156    NoAction,
157    Restrict,
158    Cascade,
159    SetNull,
160    SetDefault,
161}
162
163/// Table constraint definition.
164#[derive(Debug, Clone, PartialEq)]
165pub enum TableConstraint {
166    PrimaryKey {
167        name: Option<Ident>,
168        columns: Vec<SortKey>,
169        options: Vec<SqlOption>,
170    },
171    Unique {
172        name: Option<Ident>,
173        columns: Vec<Ident>,
174    },
175    ForeignKey {
176        name: Option<Ident>,
177        columns: Vec<Ident>,
178        references_table: ObjectName,
179        references_columns: Vec<Ident>,
180        on_delete: Option<ReferentialAction>,
181        on_update: Option<ReferentialAction>,
182    },
183    Check {
184        name: Option<Ident>,
185        expr: Box<Expr>,
186        enforced: Option<bool>,
187    },
188}
189
190/// Sort key for ORDER BY, PRIMARY KEY, etc.
191#[derive(Debug, Clone, PartialEq)]
192pub struct SortKey {
193    pub column: Ident,
194    pub order: Option<SortOrder>,
195    pub nulls: Option<NullsOrder>,
196}
197
198/// Sort order.
199#[derive(Debug, Clone, Copy, PartialEq, Eq)]
200pub enum SortOrder {
201    Asc,
202    Desc,
203}
204
205/// NULL ordering.
206#[derive(Debug, Clone, Copy, PartialEq, Eq)]
207pub enum NullsOrder {
208    First,
209    Last,
210}
211
212/// SQL option (key-value pair).
213#[derive(Debug, Clone, PartialEq)]
214pub struct SqlOption {
215    pub name: Ident,
216    pub value: Box<Expr>,
217}
218
219/// An alias with optional column aliases.
220#[derive(Debug, Clone, PartialEq)]
221pub struct Alias {
222    pub name: Ident,
223    pub columns: Vec<Ident>,
224}
225
226impl Alias {
227    pub fn new(name: Ident) -> Self {
228        Self {
229            name,
230            columns: Vec::new(),
231        }
232    }
233
234    pub fn with_columns(name: Ident, columns: Vec<Ident>) -> Self {
235        Self { name, columns }
236    }
237}
238
239/// WITH clause (Common Table Expressions).
240#[derive(Debug, Clone, PartialEq)]
241pub struct WithClause {
242    pub recursive: bool,
243    pub ctes: Vec<Cte>,
244    pub span: Span,
245}
246
247/// Common Table Expression.
248#[derive(Debug, Clone, PartialEq)]
249pub struct Cte {
250    pub name: Ident,
251    pub columns: Vec<Ident>,
252    pub query: Box<Query>,
253    pub span: Span,
254}
255
256/// A complete query expression (may include WITH, set operations, ORDER BY, LIMIT).
257#[derive(Debug, Clone, PartialEq)]
258pub struct Query {
259    pub with: Option<WithClause>,
260    pub body: QueryBody,
261    pub order_by: Vec<OrderByExpr>,
262    pub limit: Option<LimitClause>,
263    pub span: Span,
264}
265
266/// The body of a query (SELECT, set operations, or subquery).
267#[derive(Debug, Clone, PartialEq)]
268pub enum QueryBody {
269    Select(Box<Select>),
270    SetOperation {
271        op: SetOperator,
272        all: bool,
273        left: Box<QueryBody>,
274        right: Box<QueryBody>,
275    },
276    Parenthesized(Box<Query>),
277}
278
279/// Set operation type.
280#[derive(Debug, Clone, Copy, PartialEq, Eq)]
281pub enum SetOperator {
282    Union,
283    Intersect,
284    Except,
285}
286
287/// LIMIT clause.
288#[derive(Debug, Clone, PartialEq)]
289pub struct LimitClause {
290    pub count: Option<Box<Expr>>,
291    pub offset: Option<Box<Expr>>,
292}
293
294/// ORDER BY expression.
295#[derive(Debug, Clone, PartialEq)]
296pub struct OrderByExpr {
297    pub expr: Box<Expr>,
298    pub order: Option<SortOrder>,
299    pub nulls: Option<NullsOrder>,
300}
301
302/// SELECT statement.
303#[derive(Debug, Clone, PartialEq)]
304pub struct Select {
305    pub distinct: Option<Distinct>,
306    /// SELECT AS modifier (STRUCT, VALUE, or type name)
307    pub select_as: Option<SelectAs>,
308    pub projection: Vec<SelectItem>,
309    pub from: Option<FromClause>,
310    pub where_clause: Option<Box<Expr>>,
311    pub group_by: Option<GroupByClause>,
312    pub having: Option<Box<Expr>>,
313    pub qualify: Option<Box<Expr>>,
314    pub window: Vec<WindowDef>,
315    pub span: Span,
316}
317
318/// SELECT AS modifier for value tables.
319#[derive(Debug, Clone, PartialEq)]
320pub enum SelectAs {
321    /// SELECT AS STRUCT - returns each row as a STRUCT
322    Struct,
323    /// SELECT AS VALUE - returns a single-column value table
324    Value,
325    /// SELECT AS <type_name> - returns value table with specific type
326    TypeName(ObjectName),
327}
328
329/// DISTINCT specification.
330#[derive(Debug, Clone, PartialEq)]
331pub enum Distinct {
332    All,
333    Distinct,
334}
335
336/// SELECT list item.
337#[derive(Debug, Clone, PartialEq)]
338pub enum SelectItem {
339    /// An expression, optionally with an alias: `expr [AS alias]`
340    Expr {
341        expr: Box<Expr>,
342        alias: Option<Ident>,
343    },
344    /// Wildcard: `*`
345    Wildcard,
346    /// Qualified wildcard: `table.*`
347    QualifiedWildcard { qualifier: ObjectName },
348    /// Wildcard with EXCEPT: `* EXCEPT (col1, col2)`
349    WildcardExcept {
350        qualifier: Option<ObjectName>,
351        except: Vec<Ident>,
352    },
353    /// Wildcard with REPLACE: `* REPLACE (expr AS col)`
354    WildcardReplace {
355        qualifier: Option<ObjectName>,
356        replace: Vec<(Box<Expr>, Ident)>,
357    },
358}
359
360/// FROM clause.
361#[derive(Debug, Clone, PartialEq)]
362pub struct FromClause {
363    pub tables: Vec<TableRef>,
364}
365
366/// Table reference in FROM clause.
367#[derive(Debug, Clone, PartialEq)]
368pub enum TableRef {
369    /// Simple table reference: `table [AS alias]`
370    Table {
371        name: ObjectName,
372        alias: Option<Alias>,
373        hints: Vec<SqlOption>,
374    },
375    /// Subquery: `(SELECT ...) AS alias`
376    Subquery {
377        query: Box<Query>,
378        alias: Option<Alias>,
379    },
380    /// UNNEST: `UNNEST(array) [AS alias] [WITH OFFSET [AS offset_alias]]`
381    Unnest {
382        expr: Box<Expr>,
383        alias: Option<Alias>,
384        with_offset: bool,
385        offset_alias: Option<Ident>,
386    },
387    /// Join: `table1 JOIN table2 ON condition`
388    Join {
389        left: Box<TableRef>,
390        right: Box<TableRef>,
391        join_type: JoinType,
392        condition: Option<JoinCondition>,
393    },
394    /// Parenthesized table reference
395    Parenthesized(Box<TableRef>),
396    /// Table function: `TABLE_FUNCTION(...)`
397    TableFunction {
398        name: ObjectName,
399        args: Vec<FunctionArg>,
400        alias: Option<Alias>,
401    },
402}
403
404/// Type of JOIN.
405#[derive(Debug, Clone, Copy, PartialEq, Eq)]
406pub enum JoinType {
407    Inner,
408    Left,
409    Right,
410    Full,
411    Cross,
412    Natural,
413    LeftSemi,
414    RightSemi,
415    LeftAnti,
416    RightAnti,
417}
418
419/// JOIN condition.
420#[derive(Debug, Clone, PartialEq)]
421pub enum JoinCondition {
422    On(Box<Expr>),
423    Using(Vec<Ident>),
424}
425
426/// GROUP BY clause.
427#[derive(Debug, Clone, PartialEq)]
428pub struct GroupByClause {
429    pub items: Vec<GroupByItem>,
430}
431
432/// GROUP BY item.
433#[derive(Debug, Clone, PartialEq)]
434pub enum GroupByItem {
435    Expr(Box<Expr>),
436    Rollup(Vec<Box<Expr>>),
437    Cube(Vec<Box<Expr>>),
438    GroupingSets(Vec<Vec<Box<Expr>>>),
439}
440
441/// Named window definition.
442#[derive(Debug, Clone, PartialEq)]
443pub struct WindowDef {
444    pub name: Ident,
445    pub spec: WindowSpec,
446}
447
448/// Window specification.
449#[derive(Debug, Clone, PartialEq)]
450pub struct WindowSpec {
451    pub partition_by: Vec<Box<Expr>>,
452    pub order_by: Vec<OrderByExpr>,
453    pub frame: Option<WindowFrame>,
454}
455
456/// Window frame specification.
457#[derive(Debug, Clone, PartialEq)]
458pub struct WindowFrame {
459    pub unit: WindowFrameUnit,
460    pub start: WindowFrameBound,
461    pub end: Option<WindowFrameBound>,
462}
463
464/// Window frame unit.
465#[derive(Debug, Clone, Copy, PartialEq, Eq)]
466pub enum WindowFrameUnit {
467    Rows,
468    Range,
469    Groups,
470}
471
472/// Window frame bound.
473#[derive(Debug, Clone, PartialEq)]
474pub enum WindowFrameBound {
475    CurrentRow,
476    Preceding(Option<Box<Expr>>), // None = UNBOUNDED
477    Following(Option<Box<Expr>>), // None = UNBOUNDED
478}