Skip to main content

qail_core/ast/cmd/
mod.rs

1use crate::ast::{
2    Action, Cage, Condition, Distance, Expr, GroupByMode, IndexDef, Join, LockMode, OverridingKind,
3    SampleMethod, SetOp, TableConstraint,
4};
5
6/// The core Qail AST node representing a single database operation.
7#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
8pub struct Qail {
9    /// SQL action to perform.
10    pub action: Action,
11    /// Target table name.
12    pub table: String,
13    /// Selected / inserted / modified columns.
14    pub columns: Vec<Expr>,
15    /// Join clauses.
16    pub joins: Vec<Join>,
17    /// Filter / sort / group / limit cages.
18    pub cages: Vec<Cage>,
19    /// SELECT DISTINCT.
20    pub distinct: bool,
21    /// Index definition for CREATE INDEX.
22    pub index_def: Option<IndexDef>,
23    /// Table-level constraints (composite UNIQUE / PK).
24    pub table_constraints: Vec<TableConstraint>,
25    /// UNION / INTERSECT / EXCEPT operations.
26    pub set_ops: Vec<(SetOp, Box<Qail>)>,
27    /// HAVING clause conditions.
28    pub having: Vec<Condition>,
29    /// GROUP BY mode (simple, rollup, cube, grouping sets).
30    pub group_by_mode: GroupByMode,
31    /// Common table expressions (WITH).
32    pub ctes: Vec<CTEDef>,
33    /// DISTINCT ON columns.
34    pub distinct_on: Vec<Expr>,
35    /// RETURNING clause.
36    pub returning: Option<Vec<Expr>>,
37    /// ON CONFLICT clause for upsert.
38    pub on_conflict: Option<OnConflict>,
39    /// PostgreSQL MERGE specification.
40    #[serde(default)]
41    pub merge: Option<Merge>,
42    /// INSERT … SELECT source query.
43    pub source_query: Option<Box<Qail>>,
44    /// LISTEN/NOTIFY channel.
45    pub channel: Option<String>,
46    /// NOTIFY payload.
47    pub payload: Option<String>,
48    /// SAVEPOINT name.
49    pub savepoint_name: Option<String>,
50    /// UPDATE … FROM additional tables.
51    pub from_tables: Vec<String>,
52    /// DELETE … USING additional tables.
53    pub using_tables: Vec<String>,
54    /// Row locking (FOR UPDATE / FOR SHARE).
55    pub lock_mode: Option<LockMode>,
56    /// SKIP LOCKED modifier for row locking (FOR UPDATE SKIP LOCKED).
57    pub skip_locked: bool,
58    /// FETCH FIRST n ROWS [ONLY|WITH TIES].
59    pub fetch: Option<(u64, bool)>,
60    /// INSERT with DEFAULT VALUES.
61    pub default_values: bool,
62    /// OVERRIDING clause for generated columns.
63    pub overriding: Option<OverridingKind>,
64    /// TABLESAMPLE method, percentage, and optional seed.
65    pub sample: Option<(SampleMethod, f64, Option<u64>)>,
66    /// SELECT FROM ONLY (exclude inheritance).
67    pub only_table: bool,
68    // Vector database fields (Qdrant)
69    /// Search vector for similarity queries.
70    pub vector: Option<Vec<f32>>,
71    /// Minimum score threshold.
72    pub score_threshold: Option<f32>,
73    /// Named vector in multi-vector collections.
74    pub vector_name: Option<String>,
75    /// Include vector data in results.
76    pub with_vector: bool,
77    /// Vector dimensionality.
78    pub vector_size: Option<u64>,
79    /// Distance metric.
80    pub distance: Option<Distance>,
81    /// Store vectors on disk.
82    pub on_disk: Option<bool>,
83    // PostgreSQL procedural objects
84    /// Function definition.
85    pub function_def: Option<crate::ast::FunctionDef>,
86    /// Trigger definition.
87    pub trigger_def: Option<crate::ast::TriggerDef>,
88    /// RLS policy definition.
89    pub policy_def: Option<crate::migrate::policy::RlsPolicy>,
90}
91
92/// Common Table Expression (WITH clause) definition.
93#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
94pub struct CTEDef {
95    /// Alias name used to reference this CTE elsewhere in the query.
96    pub name: String,
97    /// Whether this is a recursive CTE.
98    pub recursive: bool,
99    /// Explicit column list.
100    pub columns: Vec<String>,
101    /// The base query.
102    pub base_query: Box<Qail>,
103    /// Recursive part (UNION ALL).
104    pub recursive_query: Option<Box<Qail>>,
105    /// Source table for data-modifying CTEs.
106    pub source_table: Option<String>,
107}
108
109/// ON CONFLICT clause for upsert.
110#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
111pub struct OnConflict {
112    /// Conflict target columns.
113    pub columns: Vec<String>,
114    /// What to do on conflict.
115    pub action: ConflictAction,
116}
117
118/// Action to take on an INSERT conflict.
119#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
120pub enum ConflictAction {
121    /// DO NOTHING.
122    DoNothing,
123    /// DO UPDATE SET.
124    DoUpdate {
125        /// Column = expression assignments.
126        assignments: Vec<(String, Expr)>,
127    },
128}
129
130/// PostgreSQL `MERGE` specification.
131#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
132pub struct Merge {
133    /// Optional target table alias.
134    pub target_alias: Option<String>,
135    /// `USING` data source.
136    pub source: MergeSource,
137    /// `ON` join conditions.
138    pub on: Vec<Condition>,
139    /// Ordered `WHEN ... THEN ...` clauses.
140    pub clauses: Vec<MergeClause>,
141}
142
143/// PostgreSQL `MERGE USING` source.
144#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
145pub enum MergeSource {
146    /// Table or view source.
147    Table {
148        /// Source relation name.
149        name: String,
150        /// Optional source alias.
151        alias: Option<String>,
152    },
153    /// Subquery source.
154    Query {
155        /// Source query.
156        query: Box<Qail>,
157        /// Optional source alias.
158        alias: Option<String>,
159    },
160}
161
162/// One ordered PostgreSQL `MERGE WHEN` clause.
163#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
164pub struct MergeClause {
165    /// Match class for the candidate row.
166    pub match_kind: MergeMatchKind,
167    /// Optional `AND` conditions after the match class.
168    pub condition: Vec<Condition>,
169    /// Action to execute for this clause.
170    pub action: MergeAction,
171}
172
173/// PostgreSQL `MERGE WHEN` match class.
174#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
175pub enum MergeMatchKind {
176    /// `WHEN MATCHED`.
177    Matched,
178    /// `WHEN NOT MATCHED [BY TARGET]`.
179    NotMatchedByTarget,
180    /// `WHEN NOT MATCHED BY SOURCE`.
181    NotMatchedBySource,
182}
183
184/// PostgreSQL `MERGE THEN` action.
185#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
186pub enum MergeAction {
187    /// `UPDATE SET ...`.
188    Update {
189        /// Column = expression assignments.
190        assignments: Vec<(String, Expr)>,
191    },
192    /// `INSERT (...) VALUES (...)`.
193    Insert {
194        /// Optional target columns.
195        columns: Vec<String>,
196        /// Insert value expressions.
197        values: Vec<Expr>,
198    },
199    /// `DELETE`.
200    Delete,
201    /// `DO NOTHING`.
202    DoNothing,
203}
204
205impl Default for OnConflict {
206    fn default() -> Self {
207        Self {
208            columns: vec![],
209            action: ConflictAction::DoNothing,
210        }
211    }
212}
213
214impl ConflictAction {
215    pub(crate) fn update_assignments(&self) -> Option<&[(String, Expr)]> {
216        match self {
217            Self::DoNothing => None,
218            Self::DoUpdate { assignments } => Some(assignments),
219        }
220    }
221}
222
223impl Default for Qail {
224    fn default() -> Self {
225        Self {
226            action: Action::Get,
227            table: String::new(),
228            columns: vec![],
229            joins: vec![],
230            cages: vec![],
231            distinct: false,
232            index_def: None,
233            table_constraints: vec![],
234            set_ops: vec![],
235            having: vec![],
236            group_by_mode: GroupByMode::Simple,
237            ctes: vec![],
238            distinct_on: vec![],
239            returning: None,
240            on_conflict: None,
241            merge: None,
242            source_query: None,
243            channel: None,
244            payload: None,
245            savepoint_name: None,
246            from_tables: vec![],
247            using_tables: vec![],
248            lock_mode: None,
249            skip_locked: false,
250            fetch: None,
251            default_values: false,
252            overriding: None,
253            sample: None,
254            only_table: false,
255            // Vector database fields
256            vector: None,
257            score_threshold: None,
258            vector_name: None,
259            with_vector: false,
260            vector_size: None,
261            distance: None,
262            on_disk: None,
263            // Procedural objects
264            function_def: None,
265            trigger_def: None,
266            policy_def: None,
267        }
268    }
269}
270
271// Submodules with builder methods
272mod advanced;
273mod constructors;
274mod cte;
275mod merge;
276mod query;
277mod rls;
278mod vector;
279
280impl std::fmt::Display for Qail {
281    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282        // Use the Formatter from the fmt module for canonical output
283        use crate::fmt::Formatter;
284        match Formatter::new().format(self) {
285            Ok(s) => write!(f, "{}", s),
286            Err(_) => write!(f, "{:?}", self), // Fallback to Debug
287        }
288    }
289}