1use std::path::PathBuf;
4
5use thiserror::Error;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct SourceLocation {
10 pub file: PathBuf,
12 pub line: usize,
14 pub column: usize,
16}
17
18impl SourceLocation {
19 pub const fn new(file: PathBuf, line: usize, column: usize) -> Self {
21 Self { file, line, column }
22 }
23}
24
25impl std::fmt::Display for SourceLocation {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 write!(f, "{}:{}:{}", self.file.display(), self.line, self.column)
28 }
29}
30
31#[derive(Debug, Error)]
33pub enum ParseError {
34 #[error("I/O error reading {path}: {source}")]
36 Io {
37 path: PathBuf,
39 #[source]
41 source: std::io::Error,
42 },
43
44 #[error("pg_query parse error at {location}: {message}")]
46 PgQuery {
47 location: SourceLocation,
49 message: String,
51 },
52
53 #[error(
55 "{location}: {kind} is not supported in pgevolve v0.1 — \
56 see docs §2 for the v0.1 object-kind list; expected to land in a later phase"
57 )]
58 UnsupportedObjectKind {
59 location: SourceLocation,
61 kind: &'static str,
63 },
64
65 #[error(
68 "{location}: object name must be schema-qualified, or the file must declare \
69 `-- @pgevolve schema=<name>`"
70 )]
71 UnqualifiedName {
72 location: SourceLocation,
74 },
75
76 #[error("{location}: {message}")]
78 Structural {
79 location: SourceLocation,
81 message: String,
83 },
84
85 #[error("{location}: {source}")]
87 Ir {
88 location: SourceLocation,
90 #[source]
92 source: crate::ir::IrError,
93 },
94
95 #[error("{location}: invalid pgevolve directive: {message}")]
97 InvalidDirective {
98 location: SourceLocation,
100 message: String,
102 },
103
104 #[error("duplicate object {qname} defined at {first} and {second}")]
106 DuplicateObject {
107 qname: String,
109 first: SourceLocation,
111 second: SourceLocation,
113 },
114
115 #[error("AST resolution failed:\n{}", format_resolution_errors(.0))]
117 AstResolution(Vec<crate::parse::ast_resolution::AstResolutionError>),
118
119 #[error("{0}")]
122 AstCanon(crate::parse::ast_canon::AstCanonError),
123
124 #[error("invalid identifier {0:?}: {1}")]
126 InvalidIdentifier(String, String),
127
128 #[error("unknown ViewStmt.with_check_option integer value: {0}")]
131 UnknownCheckOptionVariant(i32),
132
133 #[error("unknown check_option value: {0:?} (expected 'local' or 'cascaded')")]
135 UnknownCheckOptionValue(String),
136
137 #[error("{1}: publication {0:?} declared more than once")]
140 DuplicatePublication(crate::identifier::Identifier, SourceLocation),
141
142 #[error(
144 "{1}: publication {0:?}: FOR ALL TABLES cannot be combined with \
145 FOR TABLE or FOR TABLES IN SCHEMA"
146 )]
147 PublicationAllTablesWithObjects(crate::identifier::Identifier, SourceLocation),
148
149 #[error("{1}: publication {0:?}: malformed publication object spec node")]
151 PublicationObjectMalformed(crate::identifier::Identifier, SourceLocation),
152
153 #[error(
155 "{1}: publication {0:?}: FOR TABLES IN CURRENT SCHEMA is not supported \
156 (not declarative; use explicit schema names)"
157 )]
158 PublicationCurrentSchemaForm(crate::identifier::Identifier, SourceLocation),
159
160 #[error(
162 "{2}: publication {1:?}: unknown publication object type {0} \
163 (expected 1=TABLE, 2=TABLES IN SCHEMA, 3=CUR_SCHEMA)"
164 )]
165 UnknownPublicationObjectType(i32, crate::identifier::Identifier, SourceLocation),
166
167 #[error("{1}: publication {0:?}: table must be schema-qualified")]
169 UnqualifiedPublicationTable(crate::identifier::Identifier, SourceLocation),
170
171 #[error("{3}: publication {0:?} table {1}: row filter parse error: {2}")]
173 PublicationFilterParse(
174 crate::identifier::Identifier,
175 crate::identifier::QualifiedName,
176 String,
177 SourceLocation,
178 ),
179
180 #[error("{1}: publication {0:?}: malformed publication option node")]
182 PublicationOptionMalformed(crate::identifier::Identifier, SourceLocation),
183
184 #[error("{2}: publication {1:?}: unknown publication option {0:?}")]
186 UnknownPublicationOption(String, crate::identifier::Identifier, SourceLocation),
187
188 #[error(
190 "{2}: publication {1:?}: unknown publish kind {0:?} \
191 (valid: insert, update, delete, truncate)"
192 )]
193 UnknownPublishKind(String, crate::identifier::Identifier, SourceLocation),
194
195 #[error("{1}: publication {0:?}: empty publish list — at least one DML kind required")]
197 EmptyPublishBitset(crate::identifier::Identifier, SourceLocation),
198
199 #[error(
201 "{1}: publication {0:?}: no scope clause — add FOR ALL TABLES, \
202 FOR TABLE ..., or FOR TABLES IN SCHEMA ..."
203 )]
204 EmptyPublicationScope(crate::identifier::Identifier, SourceLocation),
205
206 #[error("{1}: publication {0:?}: RENAME is not supported in pgevolve")]
208 PublicationRenameNotSupported(crate::identifier::Identifier, SourceLocation),
209
210 #[error("{1}: publication {0:?}: ALTER PUBLICATION before CREATE PUBLICATION")]
212 AlterPublicationBeforeCreate(crate::identifier::Identifier, SourceLocation),
213
214 #[error("{1}: subscription {0:?} declared more than once")]
217 DuplicateSubscription(crate::identifier::Identifier, SourceLocation),
218
219 #[error("{1}: subscription {0:?}: CONNECTION string must not be empty")]
221 SubscriptionEmptyConnection(crate::identifier::Identifier, SourceLocation),
222
223 #[error("{1}: subscription {0:?}: PUBLICATION list must not be empty")]
225 SubscriptionEmptyPublications(crate::identifier::Identifier, SourceLocation),
226
227 #[error("{1}: subscription {0:?}: malformed subscription option node")]
229 SubscriptionOptionMalformed(crate::identifier::Identifier, SourceLocation),
230
231 #[error("{2}: subscription {1:?}: unknown subscription option {0:?}")]
233 UnknownSubscriptionOption(String, crate::identifier::Identifier, SourceLocation),
234
235 #[error(
237 "{2}: subscription {1:?}: unknown streaming mode {0:?} \
238 (valid: off, on, parallel)"
239 )]
240 UnknownStreamingMode(String, crate::identifier::Identifier, SourceLocation),
241
242 #[error(
244 "{2}: subscription {1:?}: unknown origin mode {0:?} \
245 (valid: any, none)"
246 )]
247 UnknownOriginMode(String, crate::identifier::Identifier, SourceLocation),
248
249 #[error("{1}: subscription {0:?}: REFRESH PUBLICATION is not supported in pgevolve")]
251 SubscriptionRefreshNotSupported(crate::identifier::Identifier, SourceLocation),
252
253 #[error("{1}: subscription {0:?}: SKIP is not supported in pgevolve")]
255 SubscriptionSkipNotSupported(crate::identifier::Identifier, SourceLocation),
256
257 #[error(
260 "{1}: subscription {0:?}: standalone ENABLE/DISABLE is not supported — \
261 use WITH (enabled = true/false) in source"
262 )]
263 SubscriptionStandaloneEnableDisableNotSupported(crate::identifier::Identifier, SourceLocation),
264
265 #[error("{1}: subscription {0:?}: RENAME is not supported in pgevolve")]
267 SubscriptionRenameNotSupported(crate::identifier::Identifier, SourceLocation),
268
269 #[error("{1}: subscription {0:?}: ALTER SUBSCRIPTION before CREATE SUBSCRIPTION")]
271 AlterSubscriptionBeforeCreate(crate::identifier::Identifier, SourceLocation),
272
273 #[error("{1}: statistic {0} declared more than once")]
276 DuplicateStatistic(crate::identifier::QualifiedName, SourceLocation),
277
278 #[error("{0}: anonymous CREATE STATISTICS is not supported — provide an explicit name")]
280 StatisticAnonymous(SourceLocation),
281
282 #[error(
284 "{1}: statistic {0}: empty kinds clause (must list at least one of ndistinct, dependencies, mcv)"
285 )]
286 StatisticEmptyKinds(crate::identifier::QualifiedName, SourceLocation),
287
288 #[error("{1}: statistic {0}: empty column/expression list")]
290 StatisticEmptyColumns(crate::identifier::QualifiedName, SourceLocation),
291
292 #[error(
294 "{2}: statistic {1}: unknown statistic kind {0:?} \
295 (valid: ndistinct, dependencies, mcv)"
296 )]
297 UnknownStatisticKind(String, crate::identifier::QualifiedName, SourceLocation),
298
299 #[error("{1}: statistic {0}: INCLUDE clause is not supported (deferred to v0.4.x)")]
301 StatisticIncludeNotSupported(crate::identifier::QualifiedName, SourceLocation),
302
303 #[error("{1}: statistic {0}: RENAME is not supported in pgevolve")]
305 StatisticRenameNotSupported(crate::identifier::QualifiedName, SourceLocation),
306
307 #[error("{1}: statistic {0}: ALTER STATISTICS before CREATE STATISTICS")]
309 AlterStatisticBeforeCreate(crate::identifier::QualifiedName, SourceLocation),
310
311 #[error("{1}: statistic {0}: COMMENT ON STATISTICS before CREATE STATISTICS")]
313 CommentOnStatisticBeforeCreate(crate::identifier::QualifiedName, SourceLocation),
314
315 #[error("{1}: event trigger {0:?} declared more than once")]
318 DuplicateEventTrigger(crate::identifier::Identifier, SourceLocation),
319
320 #[error(
324 "{2}: event trigger {1:?}: unknown event {0:?} \
325 (valid: ddl_command_start, ddl_command_end, sql_drop, table_rewrite)"
326 )]
327 UnknownEventTriggerEvent(String, crate::identifier::Identifier, SourceLocation),
328
329 #[error("{1}: event trigger {0:?}: malformed WHEN clause")]
331 EventTriggerWhenClauseMalformed(crate::identifier::Identifier, SourceLocation),
332
333 #[error("{2}: event trigger {1:?}: unknown enable state {0:?} (valid: O, D, R, A)")]
336 UnknownEventTriggerEnabled(String, crate::identifier::Identifier, SourceLocation),
337
338 #[error("{1}: event trigger {0:?}: ALTER EVENT TRIGGER before CREATE EVENT TRIGGER")]
341 AlterEventTriggerBeforeCreate(crate::identifier::Identifier, SourceLocation),
342
343 #[error("{1}: event trigger {0:?}: COMMENT ON EVENT TRIGGER before CREATE EVENT TRIGGER")]
346 CommentOnEventTriggerBeforeCreate(crate::identifier::Identifier, SourceLocation),
347
348 #[error("{1}: event trigger {0:?}: OWNER TO before CREATE EVENT TRIGGER")]
351 EventTriggerOwnerBeforeCreate(crate::identifier::Identifier, SourceLocation),
352
353 #[error("{1}: event trigger {0:?}: RENAME is not supported in pgevolve")]
355 EventTriggerRenameNotSupported(crate::identifier::Identifier, SourceLocation),
356}
357
358fn format_resolution_errors(errs: &[crate::parse::ast_resolution::AstResolutionError]) -> String {
359 let mut s = String::new();
360 for (i, e) in errs.iter().enumerate() {
361 if i > 0 {
362 s.push('\n');
363 }
364 s.push_str(" - ");
365 s.push_str(&e.to_string());
366 }
367 s
368}