Skip to main content

nodedb_sql/ddl_ast/
statement.rs

1//! The [`NodedbStatement`] enum — one variant per DDL command.
2
3/// Typed representation of every NodeDB DDL statement.
4///
5/// Handlers receive a fully-parsed variant instead of raw `&[&str]`
6/// parts, eliminating array-index panics and enabling exhaustive
7/// match coverage for new DDL commands.
8#[derive(Debug, Clone, PartialEq)]
9pub enum NodedbStatement {
10    // ── Collection lifecycle ─────────────────────────────────────
11    CreateCollection {
12        name: String,
13        if_not_exists: bool,
14        raw_sql: String,
15    },
16    DropCollection {
17        name: String,
18        if_exists: bool,
19        /// `DROP COLLECTION <n> PURGE` — skip the soft-delete step and
20        /// go straight to `CatalogEntry::PurgeCollection`. Requires
21        /// superuser or tenant_admin. Fails if dependents exist unless
22        /// `cascade` is also set.
23        purge: bool,
24        /// `DROP COLLECTION <n> CASCADE` — recursively drop dependents
25        /// (triggers, RLS, MVs sourcing the collection, change streams,
26        /// schedules). Rejects on cascade cycles (32-deep limit).
27        cascade: bool,
28        /// `DROP COLLECTION <n> CASCADE FORCE` — only difference from
29        /// `cascade`: also drops schedules whose SQL carries
30        /// `references_unknown = true`. Requires superuser.
31        cascade_force: bool,
32    },
33    /// `UNDROP COLLECTION <n>` — restore a soft-deleted collection
34    /// whose retention window has not yet elapsed. Flips
35    /// `StoredCollection.is_active` back to true via a new
36    /// `PutCollection` catalog entry. Fails if the retention window
37    /// has already expired (storage is already purged) or if an
38    /// active collection of the same name exists.
39    UndropCollection {
40        name: String,
41    },
42    AlterCollection {
43        name: String,
44        raw_sql: String,
45    },
46    DescribeCollection {
47        name: String,
48    },
49    ShowCollections,
50
51    // ── Index ────────────────────────────────────────────────────
52    CreateIndex {
53        unique: bool,
54        raw_sql: String,
55    },
56    DropIndex {
57        name: String,
58        collection: Option<String>,
59        if_exists: bool,
60    },
61    ShowIndexes {
62        collection: Option<String>,
63    },
64    Reindex {
65        collection: String,
66    },
67
68    // ── Trigger ──────────────────────────────────────────────────
69    CreateTrigger {
70        or_replace: bool,
71        deferred: bool,
72        sync: bool,
73        raw_sql: String,
74    },
75    DropTrigger {
76        name: String,
77        collection: String,
78        if_exists: bool,
79    },
80    AlterTrigger {
81        raw_sql: String,
82    },
83    ShowTriggers {
84        collection: Option<String>,
85    },
86
87    // ── Schedule ─────────────────────────────────────────────────
88    CreateSchedule {
89        raw_sql: String,
90    },
91    DropSchedule {
92        name: String,
93        if_exists: bool,
94    },
95    AlterSchedule {
96        raw_sql: String,
97    },
98    ShowSchedules,
99    ShowScheduleHistory {
100        name: String,
101    },
102
103    // ── Sequence ─────────────────────────────────────────────────
104    CreateSequence {
105        name: String,
106        if_not_exists: bool,
107        raw_sql: String,
108    },
109    DropSequence {
110        name: String,
111        if_exists: bool,
112    },
113    AlterSequence {
114        raw_sql: String,
115    },
116    DescribeSequence {
117        name: String,
118    },
119    ShowSequences,
120
121    // ── Alert ────────────────────────────────────────────────────
122    CreateAlert {
123        raw_sql: String,
124    },
125    DropAlert {
126        name: String,
127        if_exists: bool,
128    },
129    AlterAlert {
130        raw_sql: String,
131    },
132    ShowAlerts,
133    ShowAlertStatus {
134        name: String,
135    },
136
137    // ── Retention policy ─────────────────────────────────────────
138    CreateRetentionPolicy {
139        raw_sql: String,
140    },
141    DropRetentionPolicy {
142        name: String,
143        if_exists: bool,
144    },
145    AlterRetentionPolicy {
146        raw_sql: String,
147    },
148    ShowRetentionPolicies,
149
150    // ── Change stream ────────────────────────────────────────────
151    CreateChangeStream {
152        raw_sql: String,
153    },
154    DropChangeStream {
155        name: String,
156        if_exists: bool,
157    },
158    AlterChangeStream {
159        raw_sql: String,
160    },
161    ShowChangeStreams,
162
163    // ── Consumer group ───────────────────────────────────────────
164    CreateConsumerGroup {
165        raw_sql: String,
166    },
167    DropConsumerGroup {
168        name: String,
169        stream: String,
170        if_exists: bool,
171    },
172    ShowConsumerGroups {
173        stream: Option<String>,
174    },
175
176    // ── RLS policy ───────────────────────────────────────────────
177    CreateRlsPolicy {
178        raw_sql: String,
179    },
180    DropRlsPolicy {
181        name: String,
182        collection: String,
183        if_exists: bool,
184    },
185    ShowRlsPolicies {
186        collection: Option<String>,
187    },
188
189    // ── Materialized view ────────────────────────────────────────
190    CreateMaterializedView {
191        raw_sql: String,
192    },
193    DropMaterializedView {
194        name: String,
195        if_exists: bool,
196    },
197    ShowMaterializedViews,
198
199    // ── Continuous aggregate ─────────────────────────────────────
200    CreateContinuousAggregate {
201        raw_sql: String,
202    },
203    DropContinuousAggregate {
204        name: String,
205        if_exists: bool,
206    },
207    ShowContinuousAggregates,
208
209    // ── Backup / restore ─────────────────────────────────────────
210    BackupTenant {
211        raw_sql: String,
212    },
213    RestoreTenant {
214        dry_run: bool,
215        raw_sql: String,
216    },
217
218    // ── Cluster admin ────────────────────────────────────────────
219    ShowNodes,
220    ShowNode {
221        node_id: String,
222    },
223    RemoveNode {
224        node_id: String,
225    },
226    ShowCluster,
227    ShowMigrations,
228    ShowRanges,
229    ShowRouting,
230    ShowSchemaVersion,
231    ShowPeerHealth,
232    Rebalance,
233    ShowRaftGroups,
234    ShowRaftGroup {
235        group_id: String,
236    },
237    AlterRaftGroup {
238        raw_sql: String,
239    },
240
241    // ── Maintenance ──────────────────────────────────────────────
242    Analyze {
243        collection: Option<String>,
244    },
245    Compact {
246        collection: String,
247    },
248    ShowStorage {
249        collection: Option<String>,
250    },
251    ShowCompactionStatus,
252
253    // ── User / auth / grant ──────────────────────────────────────
254    CreateUser {
255        raw_sql: String,
256    },
257    DropUser {
258        username: String,
259    },
260    AlterUser {
261        raw_sql: String,
262    },
263    ShowUsers,
264    GrantRole {
265        raw_sql: String,
266    },
267    RevokeRole {
268        raw_sql: String,
269    },
270    GrantPermission {
271        raw_sql: String,
272    },
273    RevokePermission {
274        raw_sql: String,
275    },
276    ShowPermissions {
277        collection: Option<String>,
278    },
279    ShowGrants {
280        username: Option<String>,
281    },
282
283    // ── Miscellaneous ────────────────────────────────────────────
284    ShowTenants,
285    ShowAuditLog,
286    ShowConstraints {
287        collection: String,
288    },
289    ShowTypeGuards {
290        collection: String,
291    },
292
293    // ── Graph DSL ─────────────────────────────────────────────────
294    //
295    // Typed variants replace substring-matched parsing in the
296    // pgwire handlers. The `ddl_ast::graph_parse` module is the
297    // single source of truth for graph-DSL syntax — quote- and
298    // brace-aware, so node ids / labels / property values that
299    // shadow DSL keywords cannot short-circuit extraction.
300    GraphInsertEdge {
301        collection: String,
302        src: String,
303        dst: String,
304        label: String,
305        properties: GraphProperties,
306    },
307    GraphDeleteEdge {
308        collection: String,
309        src: String,
310        dst: String,
311        label: String,
312    },
313    /// `GRAPH LABEL` / `GRAPH UNLABEL`.
314    GraphSetLabels {
315        node_id: String,
316        labels: Vec<String>,
317        /// `true` for `UNLABEL`, `false` for `LABEL`.
318        remove: bool,
319    },
320    GraphTraverse {
321        start: String,
322        depth: usize,
323        edge_label: Option<String>,
324        direction: GraphDirection,
325    },
326    GraphNeighbors {
327        node: String,
328        edge_label: Option<String>,
329        direction: GraphDirection,
330    },
331    GraphPath {
332        src: String,
333        dst: String,
334        max_depth: usize,
335        edge_label: Option<String>,
336    },
337    GraphAlgo {
338        algorithm: String,
339        collection: String,
340        damping: Option<f64>,
341        tolerance: Option<f64>,
342        resolution: Option<f64>,
343        max_iterations: Option<usize>,
344        sample_size: Option<usize>,
345        source_node: Option<String>,
346        direction: Option<String>,
347        mode: Option<String>,
348    },
349    /// `MATCH (x)-[:l]->(y) RETURN x, y` — the query body is
350    /// compiled deep inside the Data Plane via
351    /// `engine::graph::pattern::compiler::parse`, so the AST
352    /// variant just captures the raw SQL for that consumer.
353    MatchQuery {
354        raw_sql: String,
355    },
356
357    /// `GRAPH RAG FUSION ON <collection> QUERY ARRAY[…] [options…]`
358    ///
359    /// All numeric and label options are optional at parse time; the pgwire
360    /// handler applies defaults and caps so validation errors surface with
361    /// proper SQLSTATE codes rather than parser panics.
362    ///
363    /// `query_vector` is `None` when no `ARRAY[…]` clause was found — the
364    /// handler rejects such requests with SQLSTATE 42601.
365    GraphRagFusion {
366        collection: String,
367        params: crate::ddl_ast::graph_parse::FusionParams,
368    },
369
370    /// Catch-all for DDL-like commands not yet promoted to their
371    /// own variant. Preserves the raw SQL for the legacy dispatch
372    /// path so new variants can be added incrementally without
373    /// breaking existing handlers.
374    Other {
375        raw_sql: String,
376    },
377}
378
379/// Traversal direction for graph DSL variants. Mirrors the engine's
380/// own `Direction` enum so `nodedb-sql` has no dependency cycle with
381/// `nodedb`.
382#[derive(Debug, Clone, Copy, PartialEq, Eq)]
383pub enum GraphDirection {
384    In,
385    Out,
386    Both,
387}
388
389/// The `PROPERTIES` clause of `GRAPH INSERT EDGE`. Captured in its
390/// source form so the pgwire handler — which already depends on a
391/// JSON serializer (sonic_rs) — can do the conversion to storage
392/// bytes without dragging JSON deps into `nodedb-sql`.
393#[derive(Debug, Clone, PartialEq, Eq)]
394pub enum GraphProperties {
395    None,
396    /// Raw `{ ... }` object-literal span, including the outer braces
397    /// and brace-balanced inner content. Parsed by
398    /// `crate::parser::object_literal` at the handler boundary.
399    Object(String),
400    /// Content of `'...'` (outer quotes stripped, `''` un-escaped);
401    /// expected to already be a JSON document.
402    Quoted(String),
403}