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}