qail_core/ast/
columns.rs

1use serde::{Deserialize, Serialize};
2use crate::ast::{AggregateFunc, Cage, Condition, ModKind, Value};
3
4/// A column reference.
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6pub enum Column {
7    /// All columns (*)
8    Star,
9    /// A named column
10    Named(String),
11    /// An aliased column (col AS alias)
12    Aliased { name: String, alias: String },
13    /// An aggregate function (COUNT(col))
14    Aggregate { col: String, func: AggregateFunc },
15    /// Column Definition (for Make keys)
16    Def {
17        name: String,
18        data_type: String,
19        constraints: Vec<Constraint>,
20    },
21    /// Column Modification (for Mod keys)
22    Mod {
23        kind: ModKind,
24        col: Box<Column>,
25    },
26    /// Window Function Definition
27    Window {
28        name: String,
29        func: String,
30        params: Vec<Value>,
31        partition: Vec<String>,
32        order: Vec<Cage>,
33        frame: Option<WindowFrame>,
34    },
35    /// CASE WHEN expression
36    Case {
37        /// WHEN condition THEN value pairs
38        when_clauses: Vec<(Condition, Value)>,
39        /// ELSE value (optional)
40        else_value: Option<Box<Value>>,
41        /// Optional alias
42        alias: Option<String>,
43    },
44    /// JSON accessor (data->>'key' or data->'key')
45    JsonAccess {
46        /// Base column name
47        column: String,
48        /// JSON path/key
49        path: String,
50        /// true for ->> (as text), false for -> (as JSON)
51        as_text: bool,
52        /// Optional alias
53        alias: Option<String>,
54    },
55    /// Function call expression (COALESCE, NULLIF, etc.)
56    FunctionCall {
57        /// Function name (coalesce, nullif, etc.)
58        name: String,
59        /// Arguments to the function
60        args: Vec<String>,
61        /// Optional alias
62        alias: Option<String>,
63    },
64}
65
66impl std::fmt::Display for Column {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        match self {
69            Column::Star => write!(f, "*"),
70            Column::Named(name) => write!(f, "{}", name),
71            Column::Aliased { name, alias } => write!(f, "{} AS {}", name, alias),
72            Column::Aggregate { col, func } => write!(f, "{}({})", func, col),
73            Column::Def {
74                name,
75                data_type,
76                constraints,
77            } => {
78                write!(f, "{}:{}", name, data_type)?;
79                for c in constraints {
80                    write!(f, "^{}", c)?;
81                }
82                Ok(())
83            }
84            Column::Mod { kind, col } => match kind {
85                ModKind::Add => write!(f, "+{}", col),
86                ModKind::Drop => write!(f, "-{}", col),
87            },
88            Column::Window { name, func, params, partition, order, frame } => {
89                write!(f, "{}:{}(", name, func)?;
90                for (i, p) in params.iter().enumerate() {
91                    if i > 0 { write!(f, ", ")?; }
92                    write!(f, "{}", p)?;
93                }
94                write!(f, ")")?;
95                
96                // Print partitions if any
97                if !partition.is_empty() {
98                    write!(f, "{{Part=")?;
99                    for (i, p) in partition.iter().enumerate() {
100                        if i > 0 { write!(f, ",")?; }
101                        write!(f, "{}", p)?;
102                    }
103                    if let Some(fr) = frame {
104                        write!(f, ", Frame={:?}", fr)?; // Debug format for now
105                    }
106                    write!(f, "}}")?;
107                } else if frame.is_some() {
108                     write!(f, "{{Frame={:?}}}", frame.as_ref().unwrap())?;
109                }
110
111                // Print order cages
112                for _cage in order {
113                    // Order cages are sort cages - display format TBD
114                }
115                Ok(())
116            }
117            Column::Case { when_clauses, else_value, alias } => {
118                write!(f, "CASE")?;
119                for (cond, val) in when_clauses {
120                    write!(f, " WHEN {} THEN {}", cond.column, val)?;
121                }
122                if let Some(e) = else_value {
123                    write!(f, " ELSE {}", e)?;
124                }
125                write!(f, " END")?;
126                if let Some(a) = alias {
127                    write!(f, " AS {}", a)?;
128                }
129                Ok(())
130            }
131            Column::JsonAccess { column, path, as_text, alias } => {
132                let op = if *as_text { "->>" } else { "->" };
133                write!(f, "{}{}{}", column, op, path)?;
134                if let Some(a) = alias {
135                    write!(f, " AS {}", a)?;
136                }
137                Ok(())
138            }
139            Column::FunctionCall { name, args, alias } => {
140                write!(f, "{}({})", name.to_uppercase(), args.join(", "))?;
141                if let Some(a) = alias {
142                    write!(f, " AS {}", a)?;
143                }
144                Ok(())
145            }
146        }
147    }
148}
149
150/// Column definition constraints
151#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
152pub enum Constraint {
153    PrimaryKey,
154    Unique,
155    Nullable,
156    /// DEFAULT value (e.g., `= uuid()`, `= 0`, `= now()`)
157    Default(String),
158    /// CHECK constraint with allowed values (e.g., `^check("a","b")`)
159    Check(Vec<String>),
160    /// Column comment (COMMENT ON COLUMN)
161    Comment(String),
162    /// Generated column expression (GENERATED ALWAYS AS)
163    Generated(ColumnGeneration),
164}
165
166/// Generated column type (STORED or VIRTUAL)
167#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
168pub enum ColumnGeneration {
169    /// GENERATED ALWAYS AS (expr) STORED - computed and stored
170    Stored(String),
171    /// GENERATED ALWAYS AS (expr) - computed at query time (default in Postgres 18+)
172    Virtual(String),
173}
174
175/// Window frame definition for window functions
176#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
177pub enum WindowFrame {
178    /// ROWS BETWEEN start AND end
179    Rows { start: FrameBound, end: FrameBound },
180    /// RANGE BETWEEN start AND end
181    Range { start: FrameBound, end: FrameBound },
182}
183
184/// Window frame boundary
185#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
186pub enum FrameBound {
187    UnboundedPreceding,
188    Preceding(i32),
189    CurrentRow,
190    Following(i32),
191    UnboundedFollowing,
192}
193
194impl std::fmt::Display for Constraint {
195    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196        match self {
197            Constraint::PrimaryKey => write!(f, "pk"),
198            Constraint::Unique => write!(f, "uniq"),
199            Constraint::Nullable => write!(f, "?"),
200            Constraint::Default(val) => write!(f, "={}", val),
201            Constraint::Check(vals) => write!(f, "check({})", vals.join(",")),
202            Constraint::Comment(text) => write!(f, "comment(\"{}\")", text),
203            Constraint::Generated(generation) => match generation {
204                ColumnGeneration::Stored(expr) => write!(f, "gen({})", expr),
205                ColumnGeneration::Virtual(expr) => write!(f, "vgen({})", expr),
206            },
207        }
208    }
209}
210
211/// Index definition for CREATE INDEX
212#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
213pub struct IndexDef {
214    /// Index name
215    pub name: String,
216    /// Target table
217    pub table: String,
218    /// Columns to index (ordered)
219    pub columns: Vec<String>,
220    /// Whether this is a UNIQUE index
221    pub unique: bool,
222}
223
224/// Table-level constraints for composite keys
225#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
226pub enum TableConstraint {
227    /// UNIQUE (col1, col2, ...)
228    Unique(Vec<String>),
229    /// PRIMARY KEY (col1, col2, ...)
230    PrimaryKey(Vec<String>),
231}