nodedb_sql/ddl_ast/statement.rs
1// SPDX-License-Identifier: Apache-2.0
2
3//! The [`NodedbStatement`] enum — one variant per DDL command.
4
5pub use super::alter_ops::{AlterCollectionOp, AlterRoleOp, AlterUserOp};
6pub use super::graph_types::{GraphDirection, GraphProperties};
7pub use nodedb_types::{AuditDmlMode, QuotaSpec};
8
9/// Temporal anchor for a `CLONE DATABASE` statement.
10///
11/// Every `match` on this enum must be exhaustive — no `_ =>` arms.
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum CloneAsOf {
14 /// Use the source database's current commit LSN at clone time.
15 /// Corresponds to the bare `CLONE DATABASE … FROM …` form or the
16 /// explicit `… AS OF SYSTEM TIME LATEST` form.
17 Latest,
18 /// Use the LSN corresponding to the given milliseconds-since-epoch
19 /// timestamp, resolved via the `LsnMsAnchor` mechanism.
20 ///
21 /// Corresponds to `… AS OF SYSTEM TIME <ms>`.
22 SystemTimeMs(i64),
23}
24
25/// Operations available on `ALTER DATABASE <name> <operation>`.
26///
27/// Every variant must be matched exhaustively — no `_ =>` arms anywhere.
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub enum AlterDatabaseOperation {
30 /// `ALTER DATABASE <name> RENAME TO <new_name>`
31 Rename { new_name: String },
32 /// `ALTER DATABASE <name> SET QUOTA (max_memory_bytes = ..., ...)`
33 ///
34 /// All fields in the spec are optional; absent fields leave the existing
35 /// quota value unchanged (merged at apply time with the stored record or
36 /// `QuotaRecord::DEFAULT`).
37 SetQuota(QuotaSpec),
38 /// `ALTER DATABASE <name> SET DEFAULT` — marks this database as the
39 /// per-user default for future sessions. Returns
40 /// `FEATURE_NOT_YET_IMPLEMENTED` until the per-user default-database
41 /// binding lands; the canonical path is
42 /// `ALTER USER <name> SET DEFAULT DATABASE <db>`.
43 SetDefault,
44 /// `ALTER DATABASE <name> MATERIALIZE` — triggers background materialization
45 /// of a cloned database. Returns `FEATURE_NOT_YET_IMPLEMENTED` until the
46 /// clone/mirror subsystem lands.
47 Materialize,
48 /// `ALTER DATABASE <name> PROMOTE` — promotes a mirror to writable primary.
49 /// Returns `FEATURE_NOT_YET_IMPLEMENTED` until the mirror subsystem lands.
50 Promote,
51 /// `ALTER DATABASE <name> SET AUDIT_DML = <mode>` — sets the DML audit level.
52 SetAuditDml(AuditDmlMode),
53 /// `ALTER DATABASE <name> SET IDLE_TIMEOUT = <secs>` — sets the idle session
54 /// timeout in seconds for sessions in this database. `0` disables the per-database
55 /// timeout (falls back to the global `idle_timeout_secs` setting).
56 SetIdleTimeout(u64),
57}
58
59/// Operations available on `ALTER TENANT <name> IN DATABASE <db> <operation>`.
60///
61/// Every variant must be matched exhaustively — no `_ =>` arms anywhere.
62#[derive(Debug, Clone, PartialEq, Eq)]
63pub enum AlterTenantOperation {
64 /// `ALTER TENANT <name> IN DATABASE <db> SET QUOTA (...)`
65 SetQuota(QuotaSpec),
66}
67
68/// Typed representation of every NodeDB DDL statement.
69///
70/// Handlers receive a fully-parsed variant instead of raw `&[&str]`
71/// parts, eliminating array-index panics and enabling exhaustive
72/// match coverage for new DDL commands.
73#[derive(Debug, Clone, PartialEq)]
74pub enum NodedbStatement {
75 // ── Collection lifecycle ─────────────────────────────────────
76 CreateCollection {
77 name: String,
78 if_not_exists: bool,
79 /// Canonical engine name (e.g. `"kv"`, `"vector"`, `"document_strict"`).
80 /// `None` means no `engine=` key was present.
81 engine: Option<String>,
82 /// `(col_name, col_type)` pairs from the parenthesised column list.
83 columns: Vec<(String, String)>,
84 /// Key-value pairs from the `WITH (...)` clause, excluding `engine=`.
85 options: Vec<(String, String)>,
86 /// Free-standing modifier keywords: `APPEND_ONLY`, `HASH_CHAIN`, `BITEMPORAL`.
87 flags: Vec<String>,
88 /// Raw interior of a `BALANCED ON (group_key = col, ...)` clause, or `None`.
89 balanced_raw: Option<String>,
90 },
91 /// `CREATE TABLE <name> (<col_list>)` — Postgres-style strict-default DDL.
92 /// Infers strict relational mode unless overridden via `WITH (engine='...')`.
93 /// No column list → rejected with SQLSTATE `42601`.
94 CreateTable {
95 name: String,
96 if_not_exists: bool,
97 engine: Option<String>,
98 columns: Vec<(String, String)>,
99 options: Vec<(String, String)>,
100 flags: Vec<String>,
101 balanced_raw: Option<String>,
102 },
103 DropCollection {
104 name: String,
105 if_exists: bool,
106 /// Skip the soft-delete step (requires superuser/tenant_admin).
107 purge: bool,
108 /// Recursively drop dependents (triggers, RLS, MVs, streams, schedules).
109 cascade: bool,
110 /// Like `cascade` but also drops schedules with `references_unknown = true`.
111 cascade_force: bool,
112 },
113 /// `UNDROP COLLECTION <n>` — restore a soft-deleted collection within retention window.
114 UndropCollection {
115 name: String,
116 },
117 AlterCollection {
118 name: String,
119 operation: AlterCollectionOp,
120 },
121 DescribeCollection {
122 name: String,
123 },
124 ShowCollections,
125
126 // ── Index ────────────────────────────────────────────────────
127 CreateIndex {
128 unique: bool,
129 index_name: Option<String>,
130 collection: String,
131 field: String,
132 case_insensitive: bool,
133 where_condition: Option<String>,
134 },
135 DropIndex {
136 name: String,
137 collection: Option<String>,
138 if_exists: bool,
139 },
140 ShowIndexes {
141 collection: Option<String>,
142 },
143 Reindex {
144 collection: String,
145 index_name: Option<String>,
146 concurrent: bool,
147 },
148
149 // ── Trigger ──────────────────────────────────────────────────
150 CreateTrigger {
151 or_replace: bool,
152 /// "ASYNC", "SYNC", or "DEFERRED".
153 execution_mode: String,
154 name: String,
155 /// "BEFORE", "AFTER", or "INSTEAD OF".
156 timing: String,
157 events_insert: bool,
158 events_update: bool,
159 events_delete: bool,
160 collection: String,
161 /// "ROW" or "STATEMENT".
162 granularity: String,
163 when_condition: Option<String>,
164 priority: i32,
165 /// "INVOKER" or "DEFINER".
166 security: String,
167 body_sql: String,
168 },
169 DropTrigger {
170 name: String,
171 collection: String,
172 if_exists: bool,
173 },
174 AlterTrigger {
175 name: String,
176 action: String,
177 new_owner: Option<String>,
178 },
179 ShowTriggers {
180 collection: Option<String>,
181 },
182
183 // ── Schedule ─────────────────────────────────────────────────
184 CreateSchedule {
185 name: String,
186 cron_expr: String,
187 body_sql: String,
188 scope: String,
189 missed_policy: String,
190 allow_overlap: bool,
191 },
192 DropSchedule {
193 name: String,
194 if_exists: bool,
195 },
196 AlterSchedule {
197 name: String,
198 action: String,
199 cron_expr: Option<String>,
200 },
201 ShowSchedules,
202 ShowScheduleHistory {
203 name: String,
204 },
205
206 // ── Sequence ─────────────────────────────────────────────────
207 CreateSequence {
208 name: String,
209 if_not_exists: bool,
210 start: Option<i64>,
211 increment: Option<i64>,
212 min_value: Option<i64>,
213 max_value: Option<i64>,
214 cycle: bool,
215 cache: Option<i64>,
216 /// Raw `FORMAT 'template'` string (quotes stripped), or `None`.
217 format_template_raw: Option<String>,
218 /// Raw `RESET YEARLY|MONTHLY|QUARTERLY|DAILY` token, or `None`.
219 reset_period_raw: Option<String>,
220 gap_free: bool,
221 scope: Option<String>,
222 },
223 DropSequence {
224 name: String,
225 if_exists: bool,
226 },
227 AlterSequence {
228 name: String,
229 action: String,
230 with_value: Option<String>,
231 },
232 DescribeSequence {
233 name: String,
234 },
235 ShowSequences,
236
237 // ── Alert ────────────────────────────────────────────────────
238 CreateAlert {
239 name: String,
240 collection: String,
241 where_filter: Option<String>,
242 condition_raw: String,
243 group_by: Vec<String>,
244 window_raw: String,
245 fire_after: u32,
246 recover_after: u32,
247 severity: String,
248 notify_targets_raw: String,
249 },
250 DropAlert {
251 name: String,
252 if_exists: bool,
253 },
254 AlterAlert {
255 name: String,
256 action: String,
257 },
258 ShowAlerts,
259 ShowAlertStatus {
260 name: String,
261 },
262
263 // ── Retention policy ─────────────────────────────────────────
264 CreateRetentionPolicy {
265 name: String,
266 collection: String,
267 body_raw: String,
268 eval_interval_raw: Option<String>,
269 },
270 DropRetentionPolicy {
271 name: String,
272 if_exists: bool,
273 },
274 AlterRetentionPolicy {
275 name: String,
276 action: String,
277 set_key: Option<String>,
278 set_value: Option<String>,
279 },
280 ShowRetentionPolicies,
281
282 // ── Change stream ────────────────────────────────────────────
283 CreateChangeStream {
284 name: String,
285 collection: String,
286 with_clause_raw: String,
287 },
288 DropChangeStream {
289 name: String,
290 if_exists: bool,
291 },
292 AlterChangeStream {
293 name: String,
294 action: String,
295 },
296 ShowChangeStreams,
297
298 // ── Consumer group ───────────────────────────────────────────
299 CreateConsumerGroup {
300 group_name: String,
301 stream_name: String,
302 },
303 DropConsumerGroup {
304 name: String,
305 stream: String,
306 if_exists: bool,
307 },
308 ShowConsumerGroups {
309 stream: Option<String>,
310 },
311
312 // ── RLS policy ───────────────────────────────────────────────
313 CreateRlsPolicy {
314 name: String,
315 collection: String,
316 policy_type: String,
317 predicate_raw: String,
318 is_restrictive: bool,
319 on_deny_raw: Option<String>,
320 tenant_id_override: Option<u64>,
321 },
322 DropRlsPolicy {
323 name: String,
324 collection: String,
325 if_exists: bool,
326 },
327 ShowRlsPolicies {
328 collection: Option<String>,
329 },
330
331 // ── Materialized view ────────────────────────────────────────
332 CreateMaterializedView {
333 name: String,
334 source: String,
335 query_sql: String,
336 refresh_mode: String,
337 },
338 DropMaterializedView {
339 name: String,
340 if_exists: bool,
341 },
342 ShowMaterializedViews,
343
344 // ── Continuous aggregate ─────────────────────────────────────
345 CreateContinuousAggregate {
346 name: String,
347 source: String,
348 bucket_raw: String,
349 aggregate_exprs_raw: String,
350 group_by: Vec<String>,
351 with_clause_raw: String,
352 },
353 DropContinuousAggregate {
354 name: String,
355 if_exists: bool,
356 },
357 ShowContinuousAggregates,
358
359 // ── Database lifecycle ───────────────────────────────────────
360 /// `CREATE DATABASE [IF NOT EXISTS] <name> [WITH (...)]`
361 CreateDatabase {
362 name: String,
363 if_not_exists: bool,
364 /// Key-value pairs from `WITH (...)`, if present.
365 options: Vec<(String, String)>,
366 },
367 /// `DROP DATABASE [IF EXISTS] <name> [CASCADE | FORCE]`
368 ///
369 /// `FORCE` and `CASCADE` are accepted as synonyms by the parser and both
370 /// set `cascade = true`. PostgreSQL's `WITH (FORCE)` extension also
371 /// terminates active sessions; that is a separate concern handled by the
372 /// session registry at drop time and does not require a distinct AST flag.
373 DropDatabase {
374 name: String,
375 if_exists: bool,
376 cascade: bool,
377 },
378 /// `ALTER DATABASE <name> <operation>`
379 AlterDatabase {
380 name: String,
381 operation: AlterDatabaseOperation,
382 },
383 /// `SHOW DATABASES`
384 ShowDatabases,
385 /// `SHOW DATABASE QUOTA FOR <name>` — quota limits for a named database.
386 ShowDatabaseQuota {
387 name: String,
388 },
389 /// `SHOW DATABASE USAGE FOR <name>` — runtime usage counters for a database.
390 ShowDatabaseUsage {
391 name: String,
392 },
393 /// `SHOW DATABASE LINEAGE FOR <name>` — walks the parent clone chain from
394 /// `<name>` up to the root, returning one row per ancestor with
395 /// `(database_id, name, as_of_lsn, clone_created_at_lsn)`.
396 ShowDatabaseLineage {
397 name: String,
398 },
399 /// `ALTER TENANT <name> IN DATABASE <db> <operation>`
400 ///
401 /// New SQL surface. Sets per-tenant resource budgets within a specific database.
402 AlterTenant {
403 name: String,
404 database: String,
405 operation: AlterTenantOperation,
406 },
407 /// `SHOW TENANT QUOTA FOR <name> IN DATABASE <db>`
408 ShowTenantQuotaInDatabase {
409 name: String,
410 database: String,
411 },
412 /// `SHOW TENANT USAGE FOR <name> IN DATABASE <db>`
413 ShowTenantUsageInDatabase {
414 name: String,
415 database: String,
416 },
417 /// `USE DATABASE <name>` — session reset to a different database.
418 UseDatabase {
419 name: String,
420 },
421 /// `CLONE DATABASE <new> FROM <source> [AS OF SYSTEM TIME <ms> | LATEST]`
422 CloneDatabase {
423 new_name: String,
424 source_name: String,
425 /// The temporal anchor for this clone. `Latest` means "use the
426 /// source's current commit LSN at clone time".
427 as_of: CloneAsOf,
428 },
429 /// `MIRROR DATABASE <local_name> FROM <source_cluster>.<source_database> [MODE = sync | async]`
430 ///
431 /// Creates a continuously-updated read-only replica of `source_database` in
432 /// `source_cluster`. The local database is initialized with
433 /// `MirrorStatus::Bootstrapping` and transitions to `Following` once the
434 /// initial snapshot transfer completes.
435 ///
436 /// Every match on this variant must be exhaustive — no `_ =>` arms.
437 MirrorDatabase {
438 /// Name of the new local mirror database.
439 local_name: String,
440 /// Cluster identifier of the source cluster.
441 source_cluster: String,
442 /// Name of the database in the source cluster to mirror.
443 source_database: String,
444 /// Replication mode: `Sync` means the source waits for mirror ack;
445 /// `Async` (default) means the mirror trails the source.
446 mode: nodedb_types::MirrorMode,
447 },
448 /// `SHOW DATABASE MIRROR STATUS [FOR <name>]`
449 ///
450 /// Returns one row per mirror database (or one row if `FOR <name>` is given):
451 /// `name`, `source_cluster`, `source_database`, `mode`, `status`,
452 /// `last_applied_lsn`, `last_apply_ms`.
453 ///
454 /// Every match on this variant must be exhaustive — no `_ =>` arms.
455 ShowDatabaseMirrorStatus {
456 /// Filter to a specific mirror by name, or `None` to show all mirrors.
457 name: Option<String>,
458 },
459 /// `MOVE TENANT <tenant> FROM <db_a> TO <db_b>`
460 ///
461 /// Returns `FEATURE_NOT_YET_IMPLEMENTED` until the tenant-move subsystem lands.
462 MoveTenant {
463 tenant_name: String,
464 from_db: String,
465 to_db: String,
466 },
467 /// `BACKUP DATABASE <name> TO <uri>`
468 ///
469 /// Returns `FEATURE_NOT_YET_IMPLEMENTED` until the backup subsystem lands.
470 BackupDatabase {
471 name: String,
472 uri: String,
473 },
474 /// `RESTORE DATABASE <name> FROM <uri>`
475 ///
476 /// Returns `FEATURE_NOT_YET_IMPLEMENTED` until the restore subsystem lands.
477 RestoreDatabase {
478 name: String,
479 uri: String,
480 },
481
482 // ── Backup / restore ─────────────────────────────────────────
483 BackupTenant {
484 tenant_id: String,
485 },
486 RestoreTenant {
487 dry_run: bool,
488 tenant_id: String,
489 },
490
491 // ── Cluster admin ────────────────────────────────────────────
492 ShowNodes,
493 ShowNode {
494 node_id: String,
495 },
496 RemoveNode {
497 node_id: String,
498 },
499 ShowCluster,
500 ShowMigrations,
501 ShowRanges,
502 ShowRouting,
503 ShowSchemaVersion,
504 ShowPeerHealth,
505 Rebalance,
506 ShowRaftGroups,
507 ShowRaftGroup {
508 group_id: String,
509 },
510 AlterRaftGroup {
511 group_id: String,
512 action: String,
513 node_id: String,
514 },
515
516 // ── Maintenance ──────────────────────────────────────────────
517 Analyze {
518 collection: Option<String>,
519 },
520 Compact {
521 collection: String,
522 },
523 ShowStorage {
524 collection: Option<String>,
525 },
526 ShowCompactionStatus,
527
528 // ── User / auth / grant ──────────────────────────────────────
529 CreateUser {
530 username: String,
531 password: String,
532 role: Option<String>,
533 tenant_id: Option<u64>,
534 },
535 DropUser {
536 username: String,
537 },
538 AlterUser {
539 username: String,
540 op: AlterUserOp,
541 },
542 ShowUsers,
543 /// `ALTER ROLE <name> GRANT/REVOKE/SET`.
544 AlterRole {
545 name: String,
546 sub_op: AlterRoleOp,
547 },
548 GrantRole {
549 role: String,
550 username: String,
551 },
552 RevokeRole {
553 role: String,
554 username: String,
555 },
556 GrantPermission {
557 permission: String,
558 target_type: String,
559 target_name: String,
560 grantee: String,
561 },
562 /// `GRANT <privilege> ON DATABASE <name> TO <user>`
563 GrantDatabasePermission {
564 permission: String,
565 db_name: String,
566 grantee: String,
567 },
568 RevokePermission {
569 permission: String,
570 target_type: String,
571 target_name: String,
572 grantee: String,
573 },
574 /// `REVOKE <privilege> ON DATABASE <name> FROM <user>`
575 RevokeDatabasePermission {
576 permission: String,
577 db_name: String,
578 grantee: String,
579 },
580 ShowPermissions {
581 on_collection: Option<String>,
582 for_grantee: Option<String>,
583 },
584 ShowGrants {
585 username: Option<String>,
586 },
587
588 // ── OIDC providers ───────────────────────────────────────────
589 /// `CREATE OIDC PROVIDER <name> ISSUER '<iss>' JWKS_URI '<uri>'
590 /// [AUDIENCE '<aud>'] [CLAIM MAPPING WHEN <claim_name> = '<value>'
591 /// SET DEFAULT_DATABASE = <id>, ADD DATABASES [<ids>], ADD ROLES ['<role>', ...]]`
592 CreateOidcProvider {
593 name: String,
594 issuer: String,
595 jwks_uri: String,
596 audience: Option<String>,
597 /// `(claim_name, claim_value, default_database, add_databases, add_roles)` tuples.
598 claim_mappings: Vec<OidcClaimMappingClause>,
599 },
600 /// `ALTER OIDC PROVIDER <name> SET CLAIM MAPPING WHEN <claim_name> = '<value>'
601 /// SET DEFAULT_DATABASE = <id>, ADD DATABASES [<ids>], ADD ROLES ['<role>', ...]`
602 ///
603 /// Replaces the entire claim-mapping list for the named provider.
604 AlterOidcProviderClaimMapping {
605 name: String,
606 claim_mappings: Vec<OidcClaimMappingClause>,
607 },
608 /// `DROP OIDC PROVIDER [IF EXISTS] <name>`
609 DropOidcProvider {
610 name: String,
611 if_exists: bool,
612 },
613 /// `SHOW OIDC PROVIDERS`
614 ShowOidcProviders,
615
616 // ── CRDT conflict policy ─────────────────────────────────────
617 /// `SHOW CONFLICT POLICY ON <collection>`
618 ShowConflictPolicy {
619 collection: String,
620 },
621
622 // ── Miscellaneous ────────────────────────────────────────────
623 ShowTenants,
624 ShowAuditLog,
625 ShowConstraints {
626 collection: String,
627 },
628 ShowTypeGuards {
629 collection: String,
630 },
631
632 // ── Custom types ─────────────────────────────────────────────
633 /// `CREATE TYPE <name> AS ENUM ('label1', 'label2', ...)`
634 CreateEnumType {
635 name: String,
636 labels: Vec<String>,
637 },
638 /// `CREATE TYPE <name> AS (<field1> <type1>, <field2> <type2>, ...)`
639 CreateCompositeType {
640 name: String,
641 /// `(field_name, type_name)` pairs.
642 fields: Vec<(String, String)>,
643 },
644 /// `DROP TYPE [IF EXISTS] <name>`
645 DropType {
646 name: String,
647 if_exists: bool,
648 },
649 /// `ALTER TYPE <name> ADD VALUE 'label'`
650 AlterTypeAddValue {
651 type_name: String,
652 label: String,
653 },
654 /// `SHOW TYPES`
655 ShowTypes,
656
657 // ── Synonym groups ───────────────────────────────────────────
658 /// `CREATE SYNONYM GROUP <name> AS ('term1', 'term2', ...)`
659 CreateSynonymGroup {
660 name: String,
661 terms: Vec<String>,
662 },
663 /// `DROP SYNONYM GROUP [IF EXISTS] <name>`
664 DropSynonymGroup {
665 name: String,
666 if_exists: bool,
667 },
668 /// `SHOW SYNONYM GROUPS`
669 ShowSynonymGroups,
670
671 // ── Graph DSL ────────────────────────────────────────────────
672 GraphInsertEdge {
673 collection: String,
674 src: String,
675 dst: String,
676 label: String,
677 properties: GraphProperties,
678 },
679 GraphDeleteEdge {
680 collection: String,
681 src: String,
682 dst: String,
683 label: String,
684 },
685 GraphSetLabels {
686 node_id: String,
687 labels: Vec<String>,
688 remove: bool,
689 },
690 GraphTraverse {
691 start: String,
692 depth: usize,
693 edge_label: Option<String>,
694 direction: GraphDirection,
695 },
696 GraphNeighbors {
697 node: String,
698 edge_label: Option<String>,
699 direction: GraphDirection,
700 },
701 GraphPath {
702 src: String,
703 dst: String,
704 max_depth: usize,
705 edge_label: Option<String>,
706 },
707 GraphAlgo {
708 algorithm: String,
709 collection: String,
710 edge_label: Option<String>,
711 damping: Option<f64>,
712 tolerance: Option<f64>,
713 resolution: Option<f64>,
714 max_iterations: Option<usize>,
715 sample_size: Option<usize>,
716 source_node: Option<String>,
717 direction: Option<String>,
718 mode: Option<String>,
719 },
720 /// `MATCH (x)-[:l]->(y) RETURN x, y` — body forwarded verbatim to the graph pattern compiler.
721 MatchQuery {
722 body: String,
723 },
724 /// `GRAPH RAG FUSION ON <collection> QUERY ARRAY[…] [options…]`
725 GraphRagFusion {
726 collection: String,
727 params: crate::ddl_ast::graph_parse::FusionParams,
728 },
729
730 // ── Bulk import ──────────────────────────────────────────────
731 /// `COPY <collection> FROM '<path>' [WITH (FORMAT ..., DELIMITER ..., HEADER ...)]`
732 ///
733 /// Server-side file-path bulk import. Does not handle STDIN streaming
734 /// (that is a different protocol path) or COPY ... TO.
735 CopyFromFile {
736 collection: String,
737 path: String,
738 format: Option<CopyFormat>,
739 delimiter: Option<char>,
740 header: bool,
741 },
742
743 // ── Bulk export ──────────────────────────────────────────────
744 /// `COPY <collection> TO '<path>' [WITH (FORMAT ..., DELIMITER ..., HEADER ...)]`
745 /// `COPY (SELECT ...) TO '<path>' [WITH (...)]`
746 ///
747 /// Server-side file-path bulk export. Streams scan results to a file.
748 CopyToFile {
749 /// The source: either a bare collection name or a SELECT query.
750 source: CopyToSource,
751 path: String,
752 format: Option<CopyFormat>,
753 delimiter: Option<char>,
754 header: bool,
755 },
756}
757
758/// One `WHEN <claim_name> = '<value>' SET ...` clause inside a
759/// `CREATE OIDC PROVIDER` or `ALTER OIDC PROVIDER` statement.
760#[derive(Debug, Clone, PartialEq, Eq)]
761pub struct OidcClaimMappingClause {
762 pub claim_name: String,
763 pub claim_value: String,
764 /// Optional default database ID to grant for matching tokens.
765 pub default_database: Option<u64>,
766 /// Additional database IDs accessible to matching tokens.
767 pub add_databases: Vec<u64>,
768 /// Role names to grant to matching tokens.
769 pub add_roles: Vec<String>,
770}
771
772/// Source for `COPY ... TO`.
773#[derive(Debug, Clone, PartialEq, Eq)]
774pub enum CopyToSource {
775 /// `COPY <collection> TO '<path>'` — export from a named collection.
776 Collection(String),
777 /// `COPY (SELECT ...) TO '<path>'` — export from an arbitrary query.
778 Query(String),
779}
780
781/// Format for `COPY ... FROM` bulk import.
782#[derive(Debug, Clone, PartialEq, Eq)]
783pub enum CopyFormat {
784 /// One JSON object per line (`.ndjson` / `.jsonl`).
785 Ndjson,
786 /// A JSON array of objects (`.json`).
787 JsonArray,
788 /// CSV with an optional header row (`.csv`).
789 Csv,
790}