Skip to main content

coven_database/
schema_error.rs

1//! The schema rules checked when a store opens and after it migrates.
2
3/// A schema rule checked on open and after migrating (§9, §15.1).
4#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
5pub enum SchemaError {
6    /// Two namespace declarations share a name (E1).
7    #[error("file namespace {namespace} is declared twice")]
8    DuplicateNamespace {
9        /// The namespace.
10        namespace: String,
11    },
12    /// A synced foreign key does not reference the complete parent primary key (§9.1).
13    #[error("foreign key {table}: {key} must reference the parent's primary key")]
14    ForeignKeyNotPrimaryKey {
15        /// The referencing synced table.
16        table: String,
17        /// The foreign key's columns and referenced table and columns, as SQL.
18        key: String,
19    },
20    /// SET DEFAULT is on a synced table or references one (§9.1).
21    #[error("foreign key {table}: {key} cannot use SET DEFAULT")]
22    SetDefault {
23        /// The referencing table.
24        table: String,
25        /// The foreign key's columns and referenced table and columns, as SQL.
26        key: String,
27    },
28    /// A local foreign key could prevent deletion of a synced row (§9.1).
29    #[error("local reference {table}.{column} must use CASCADE or nullable SET NULL")]
30    LocalChildAction {
31        /// The local child table.
32        table: String,
33        /// The referencing column.
34        column: String,
35    },
36    /// A local foreign key could stop coven updating a synced value in place (§9.1).
37    #[error("local reference {table}.{column} must use ON UPDATE CASCADE or SET NULL")]
38    LocalChildUpdate {
39        /// The local child table.
40        table: String,
41        /// The referencing column.
42        column: String,
43    },
44    /// A declared synced table isn't in the database (E2).
45    #[error("declared synced table {table} is missing")]
46    MissingTable {
47        /// The declared table.
48        table: String,
49    },
50    /// Two declarations name one table (E2).
51    #[error("synced table {table} is declared twice")]
52    DuplicateTable {
53        /// The repeated table.
54        table: String,
55    },
56    /// A synced table's `owner_only` declaration changed without a breaking
57    /// migration to raise the schema (§18.3).
58    #[error("synced table {table} changed its owner_only declaration without a migration")]
59    OwnerOnlyChanged {
60        /// The table whose declaration changed.
61        table: String,
62    },
63    /// A table declares both `audience_column` and `audience_from` (E2).
64    #[error("synced table {table} declares two audience sources")]
65    TwoAudiences {
66        /// The declared table.
67        table: String,
68    },
69    /// A synced table's file reference columns changed without a breaking
70    /// migration to raise the schema (§18.1).
71    #[error("synced table {table} changed its file columns without a migration")]
72    FileColumnsChanged {
73        /// The table whose declaration changed.
74        table: String,
75    },
76    /// A column is declared a file reference twice (E2).
77    #[error("file column {table}.{column} is declared twice")]
78    FileColumnRepeated {
79        /// The declared table.
80        table: String,
81        /// The repeated column.
82        column: String,
83    },
84    /// A file reference column is generated, so coven can't write it (E2).
85    #[error("file column {table}.{column} is generated")]
86    FileColumnGenerated {
87        /// The declared table.
88        table: String,
89        /// The generated column.
90        column: String,
91    },
92    /// An SQL foreign key references coven's files table, which only
93    /// declared file columns reference (§17.2, E2). `key` names the
94    /// referencing columns and referenced table and columns as SQL.
95    #[error("{table}'s foreign key {key} references coven_files; declare a file column instead")]
96    FilesForeignKey {
97        /// The table holding the key.
98        table: String,
99        /// The key, as SQL.
100        key: String,
101    },
102    /// A file reference column is also an SQL foreign key (E2).
103    #[error("file column {table}.{column} is also a foreign key")]
104    FileColumnForeignKey {
105        /// The declared table.
106        table: String,
107        /// The column.
108        column: String,
109    },
110    /// A declared file column isn't in the table (E2).
111    #[error("file column {table}.{column} is missing")]
112    FileColumn {
113        /// The declared table.
114        table: String,
115        /// The missing column.
116        column: String,
117    },
118    /// A trigger declared shared isn't on the table (§9.4).
119    #[error("shared trigger {trigger} is missing from {table}")]
120    MissingTrigger {
121        /// The declared table.
122        table: String,
123        /// The missing trigger.
124        trigger: String,
125    },
126    /// A synced table has no primary key (§9.2).
127    #[error("synced table {table} has no primary key")]
128    NoPrimaryKey {
129        /// The declared table.
130        table: String,
131    },
132    /// A primary-key column of a synced or device-local table permits NULL
133    /// (§9.2).
134    #[error("primary-key column {table}.{column} allows NULL")]
135    NullableKey {
136        /// The table.
137        table: String,
138        /// The nullable key column.
139        column: String,
140    },
141    /// A synced key column compares text by a collation other than BINARY (§9.2, D3).
142    #[error(
143        "key column {table}.{column} uses collation {collation}; synced keys compare text by bytes"
144    )]
145    KeyCollation {
146        /// The table holding the key, unique constraint or foreign key.
147        table: String,
148        /// The column, or for an expression term the unique index naming it.
149        column: String,
150        /// The column's or index term's collation.
151        collation: String,
152    },
153    /// SQLite chooses the primary key itself (§9.2).
154    #[error("SQLite generates the primary key of {table}")]
155    GeneratedPrimaryKey {
156        /// The declared table.
157        table: String,
158    },
159    /// An independent key has no text-affinity column to hold its UUID (§9.2).
160    #[error("independent key of {table} has no text-affinity column to hold its UUID")]
161    IndependentKeyNotUuid {
162        /// The declared table.
163        table: String,
164    },
165    /// Declared key columns do not match the table's primary key (E2).
166    #[error("declared key columns differ from the primary key of {table}")]
167    KeyColumns {
168        /// The declared table.
169        table: String,
170    },
171    /// A foreign key includes a generated column (§9.1).
172    #[error("foreign key includes generated column {table}.{column}")]
173    GeneratedForeignKey {
174        /// The synced or local table.
175        table: String,
176        /// The generated referencing column.
177        column: String,
178    },
179    /// SET NULL would put NULL in a NOT NULL column (§9.1).
180    #[error("foreign key action would put NULL in non-null column {table}.{column}")]
181    ImpossibleAction {
182        /// The synced or local table.
183        table: String,
184        /// The non-null referencing column.
185        column: String,
186    },
187    /// SET NULL acts on a primary-key column (§9.1).
188    #[error("foreign key action changes key column {table}.{column}")]
189    PrimaryKeyAction {
190        /// The declared table.
191        table: String,
192        /// The primary-key column.
193        column: String,
194    },
195    /// The audience root column is nullable, is not text, or is generated
196    /// (§15, E2).
197    #[error("audience column {table}.{column} must be non-null text, not generated")]
198    AudienceColumn {
199        /// The declared table.
200        table: String,
201        /// The audience column.
202        column: String,
203    },
204    /// The audience foreign key spans more than one column (§15.1).
205    #[error("audience foreign key of {table} must have one column")]
206    AudienceForeignKeyColumns {
207        /// The declared table.
208        table: String,
209    },
210    /// The audience foreign key does not point into a synced table (§15.1).
211    #[error("audience foreign key {table}.{column} must point into a synced table")]
212    AudienceForeignKeyTarget {
213        /// The declared table.
214        table: String,
215        /// The audience foreign-key column.
216        column: String,
217    },
218    /// The audience foreign key uses SET NULL (§15.1).
219    #[error("audience foreign key {table}.{column} cannot use SET NULL")]
220    AudienceForeignKeyAction {
221        /// The declared table.
222        table: String,
223        /// The audience foreign-key column.
224        column: String,
225    },
226    /// Following audience foreign keys forms a loop (§15.1).
227    #[error("audience inheritance from {table} forms a cycle")]
228    AudienceCycle {
229        /// The table from which the cycle was reached.
230        table: String,
231    },
232    /// A unique constraint or shared key spans audiences (§15.2).
233    #[error("constraint {constraint} of {table} spans audiences")]
234    AudienceConstraint {
235        /// The declared table.
236        table: String,
237        /// The primary key or unique index.
238        constraint: String,
239    },
240    /// A shared trigger lacks WHEN NOT coven_applying() (§9.4).
241    #[error("shared trigger {trigger} on {table} requires WHEN NOT coven_applying()")]
242    SharedTriggerGuard {
243        /// The declared table.
244        table: String,
245        /// The shared trigger.
246        trigger: String,
247    },
248    /// A trigger on `coven_files`, `coven_pins`, `coven_pin_requests`,
249    /// `coven_moves`, `coven_upload_queue`, `coven_sync_status` or
250    /// `coven_retry_effect` runs before the change, where it could skip or
251    /// refuse coven's own (§9.4).
252    #[error("trigger {trigger} on {table} must run AFTER the change")]
253    TriggerTiming {
254        /// The trigger.
255        trigger: String,
256        /// The table of coven's it follows.
257        table: String,
258    },
259}