servers/postgres/types/error.rs
1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use common_error::status_code::StatusCode;
16use pgwire::error::ErrorInfo;
17use snafu::Snafu;
18use strum::{AsRefStr, Display, EnumIter, EnumMessage};
19
20#[derive(Display, Debug, PartialEq)]
21#[allow(dead_code)]
22pub enum PgErrorSeverity {
23 #[strum(serialize = "INFO")]
24 Info,
25 #[strum(serialize = "DEBUG")]
26 Debug,
27 #[strum(serialize = "NOTICE")]
28 Notice,
29 #[strum(serialize = "WARNING")]
30 Warning,
31 #[strum(serialize = "ERROR")]
32 Error,
33 #[strum(serialize = "FATAL")]
34 Fatal,
35 #[strum(serialize = "PANIC")]
36 Panic,
37}
38
39// refer to: https://www.postgresql.org/docs/16/errcodes-appendix.html
40#[derive(Snafu, Debug, AsRefStr, EnumIter, EnumMessage)]
41pub enum PgErrorCode {
42 // === Begin of Class 00 — Successful Completion ===
43 /// successful_completion
44 #[snafu(display("successful_completion"))]
45 Ec00000 = 0,
46 // === End of Class 00 — Successful Completion =====
47
48 // === Begin of Class 01 — Warning ===
49 /// warning
50 #[snafu(display("warning"))]
51 Ec01000 = 100,
52 // === End of Class 01 — Warning =====
53
54 // === Begin of Class 02 — No Data (this is also a warning class per the SQL standard) ===
55 /// no_data
56 #[snafu(display("no_data"))]
57 Ec02000 = 200,
58 // === End of Class 02 — No Data (this is also a warning class per the SQL standard) =====
59
60 // === Begin of Class 03 — SQL Statement Not Yet Complete ===
61 /// sql_statement_not_yet_complete
62 #[snafu(display("sql_statement_not_yet_complete"))]
63 Ec03000 = 300,
64 // === End of Class 03 — SQL Statement Not Yet Complete =====
65
66 // === Begin of Class 08 — Connection Exception ===
67 /// connection_exception
68 #[snafu(display("connection_exception"))]
69 Ec08000 = 400,
70 /// protocol_violation
71 #[snafu(display("protocol_violation"))]
72 Ec08P01 = 401,
73 // === End of Class 08 — Connection Exception =====
74
75 // === Begin of Class 09 — Triggered Action Exception ===
76 /// triggered_action_exception
77 #[snafu(display("triggered_action_exception"))]
78 Ec09000 = 500,
79 // === End of Class 09 — Triggered Action Exception =====
80
81 // === Begin of Class 0A — Feature Not Supported ===
82 /// feature_not_supported
83 #[snafu(display("feature_not_supported"))]
84 Ec0A000 = 600,
85 // === End of Class 0A — Feature Not Supported =====
86
87 // === Begin of Class 0B — Invalid Transaction Initiation ===
88 /// invalid_transaction_initiation
89 #[snafu(display("invalid_transaction_initiation"))]
90 Ec0B000 = 700,
91 // === End of Class 0B — Invalid Transaction Initiation =====
92
93 // === Begin of Class 0F — Locator Exception ===
94 /// locator_exception
95 #[snafu(display("locator_exception"))]
96 Ec0F000 = 800,
97 // === End of Class 0F — Locator Exception =====
98
99 // === Begin of Class 0L — Invalid Grantor ===
100 /// invalid_grantor
101 #[snafu(display("invalid_grantor"))]
102 Ec0L000 = 900,
103 // === End of Class 0L — Invalid Grantor =====
104
105 // === Begin of Class 0P — Invalid Role Specification ===
106 /// invalid_role_specification
107 #[snafu(display("invalid_role_specification"))]
108 Ec0P000 = 1000,
109 // === End of Class 0P — Invalid Role Specification =====
110
111 // === Begin of Class 0Z — Diagnostics Exception ===
112 /// diagnostics_exception
113 #[snafu(display("diagnostics_exception"))]
114 Ec0Z000 = 1100,
115 // === End of Class 0Z — Diagnostics Exception =====
116
117 // === Begin of Class 20 — Case Not Found ===
118 /// case_not_found
119 #[snafu(display("case_not_found"))]
120 Ec20000 = 1200,
121 // === End of Class 20 — Case Not Found =====
122
123 // === Begin of Class 21 — Cardinality Violation ===
124 /// cardinality_violation
125 #[snafu(display("cardinality_violation"))]
126 Ec21000 = 1300,
127 // === End of Class 21 — Cardinality Violation =====
128
129 // === Begin of Class 22 — Data Exception ===
130 /// data_exception
131 #[snafu(display("data_exception"))]
132 Ec22000 = 1400,
133 /// invalid_parameter_value
134 #[snafu(display("invalid_parameter_value"))]
135 Ec22023 = 1401,
136 /// invalid_text_representation
137 #[snafu(display("invalid_text_representation"))]
138 Ec22P02 = 1402,
139 /// bad_copy_file_format
140 #[snafu(display("bad_copy_file_format"))]
141 Ec22P04 = 1403,
142 // === End of Class 22 — Data Exception =====
143
144 // === Begin of Class 23 — Integrity Constraint Violation ===
145 /// integrity_constraint_violation
146 #[snafu(display("integrity_constraint_violation"))]
147 Ec23000 = 1500,
148 // === End of Class 23 — Integrity Constraint Violation =====
149
150 // === Begin of Class 24 — Invalid Cursor State ===
151 /// invalid_cursor_state
152 #[snafu(display("invalid_cursor_state"))]
153 Ec24000 = 1600,
154 // === End of Class 24 — Invalid Cursor State =====
155
156 // === Begin of Class 25 — Invalid Transaction State ===
157 /// invalid_transaction_state
158 #[snafu(display("invalid_transaction_state"))]
159 Ec25000 = 1700,
160 /// read_only_sql_transaction
161 #[snafu(display("read_only_sql_transaction"))]
162 Ec25006 = 1701,
163 // === End of Class 25 — Invalid Transaction State =====
164
165 // === Begin of Class 26 — Invalid SQL Statement Name ===
166 /// invalid_sql_statement_name
167 #[snafu(display("invalid_sql_statement_name"))]
168 Ec26000 = 1800,
169 // === End of Class 26 — Invalid SQL Statement Name =====
170
171 // === Begin of Class 27 — Triggered Data Change Violation ===
172 /// triggered_data_change_violation
173 #[snafu(display("triggered_data_change_violation"))]
174 Ec27000 = 1900,
175 // === End of Class 27 — Triggered Data Change Violation =====
176
177 // === Begin of Class 28 — Invalid Authorization Specification ===
178 /// invalid_authorization_specification
179 #[snafu(display("invalid_authorization_specification"))]
180 Ec28000 = 2000,
181 /// invalid_password
182 #[snafu(display("invalid_password"))]
183 Ec28P01 = 1901,
184 // === End of Class 28 — Invalid Authorization Specification =====
185
186 // === Begin of Class 2B — Dependent Privilege Descriptors Still Exist ===
187 /// dependent_privilege_descriptors_still_exist
188 #[snafu(display("dependent_privilege_descriptors_still_exist"))]
189 Ec2B000 = 2100,
190 // === End of Class 2B — Dependent Privilege Descriptors Still Exist =====
191
192 // === Begin of Class 2D — Invalid Transaction Termination ===
193 /// invalid_transaction_termination
194 #[snafu(display("invalid_transaction_termination"))]
195 Ec2D000 = 2200,
196 // === End of Class 2D — Invalid Transaction Termination =====
197
198 // === Begin of Class 2F — SQL Routine Exception ===
199 /// sql_routine_exception
200 #[snafu(display("sql_routine_exception"))]
201 Ec2F000 = 2300,
202 // === End of Class 2F — SQL Routine Exception =====
203
204 // === Begin of Class 34 — Invalid Cursor Name ===
205 /// invalid_cursor_name
206 #[snafu(display("invalid_cursor_name"))]
207 Ec34000 = 2400,
208 // === End of Class 34 — Invalid Cursor Name =====
209
210 // === Begin of Class 38 — External Routine Exception ===
211 /// external_routine_exception
212 #[snafu(display("external_routine_exception"))]
213 Ec38000 = 2500,
214 // === End of Class 38 — External Routine Exception =====
215
216 // === Begin of Class 39 — External Routine Invocation Exception ===
217 /// external_routine_invocation_exception
218 #[snafu(display("external_routine_invocation_exception"))]
219 Ec39000 = 2600,
220 // === End of Class 39 — External Routine Invocation Exception =====
221
222 // === Begin of Class 3B — Savepoint Exception ===
223 /// savepoint_exception
224 #[snafu(display("savepoint_exception"))]
225 Ec3B000 = 2700,
226 // === End of Class 3B — Savepoint Exception =====
227
228 // === Begin of Class 3D — Invalid Catalog Name ===
229 /// invalid_catalog_name
230 #[snafu(display("invalid_catalog_name"))]
231 Ec3D000 = 2800,
232 // === End of Class 3D — Invalid Catalog Name =====
233
234 // === Begin of Class 3F — Invalid Schema Name ===
235 /// invalid_schema_name
236 #[snafu(display("invalid_schema_name"))]
237 Ec3F000 = 2900,
238 // === End of Class 3F — Invalid Schema Name =====
239
240 // === Begin of Class 40 — Transaction Rollback ===
241 /// transaction_rollback
242 #[snafu(display("transaction_rollback"))]
243 Ec40000 = 3000,
244 // === End of Class 40 — Transaction Rollback =====
245
246 // === Begin of Class 42 — Syntax Error or Access Rule Violation ===
247 /// syntax_error_or_access_rule_violation
248 #[snafu(display("syntax_error_or_access_rule_violation"))]
249 Ec42000 = 3100,
250 /// syntax_error
251 #[snafu(display("syntax_error"))]
252 Ec42601 = 3101,
253 /// insufficient_privilege
254 #[snafu(display("insufficient_privilege"))]
255 Ec42501 = 3102,
256 /// undefined_column
257 #[snafu(display("undefined_column"))]
258 Ec42703 = 3103,
259 /// undefined_table
260 #[snafu(display("undefined_table"))]
261 Ec42P01 = 3104,
262 /// undefined_object
263 #[snafu(display("undefined_object"))]
264 Ec42704 = 3105,
265 /// duplicate_column
266 #[snafu(display("duplicate_column"))]
267 Ec42701 = 3106,
268 /// duplicate_database
269 #[snafu(display("duplicate_database"))]
270 Ec42P04 = 3107,
271 /// duplicate_table
272 #[snafu(display("duplicate_table"))]
273 Ec42P07 = 3108,
274 /// invalid_prepared_statement_definition
275 #[snafu(display("invalid_prepared_statement_definition"))]
276 Ec42P14 = 3109,
277 // === End of Class 42 — Syntax Error or Access Rule Violation =====
278
279 // === Begin of Class 44 — WITH CHECK OPTION Violation ===
280 /// with_check_option_violation
281 #[snafu(display("with_check_option_violation"))]
282 Ec44000 = 3200,
283 // === End of Class 44 — WITH CHECK OPTION Violation =====
284
285 // === Begin of Class 53 — Insufficient Resources ===
286 /// insufficient_resources
287 #[snafu(display("insufficient_resources"))]
288 Ec53000 = 3300,
289 // === End of Class 53 — Insufficient Resources =====
290
291 // === Begin of Class 54 — Program Limit Exceeded ===
292 /// program_limit_exceeded
293 #[snafu(display("program_limit_exceeded"))]
294 Ec54000 = 3400,
295 // === End of Class 54 — Program Limit Exceeded =====
296
297 // === Begin of Class 55 — Object Not In Prerequisite State ===
298 /// object_not_in_prerequisite_state
299 #[snafu(display("object_not_in_prerequisite_state"))]
300 Ec55000 = 3500,
301 // === End of Class 55 — Object Not In Prerequisite State =====
302
303 // === Begin of Class 57 — Operator Intervention ===
304 /// operator_intervention
305 #[snafu(display("operator_intervention"))]
306 Ec57000 = 3600,
307 /// query_canceled
308 #[snafu(display("query_canceled"))]
309 Ec57014 = 3602,
310
311 /// cannot_connect_now
312 #[snafu(display("cannot_connect_now"))]
313 Ec57P03 = 3601,
314 // === End of Class 57 — Operator Intervention =====
315
316 // === Begin of Class 58 — System Error (errors external to PostgreSQL itself) ===
317 /// system_error
318 #[snafu(display("system_error"))]
319 Ec58000 = 3700,
320 // === End of Class 58 — System Error (errors external to PostgreSQL itself) =====
321
322 // === Begin of Class 72 — Snapshot Failure ===
323 /// snapshot_too_old
324 #[snafu(display("snapshot_too_old"))]
325 Ec72000 = 3800,
326 // === End of Class 72 — Snapshot Failure =====
327
328 // === Begin of Class F0 — Configuration File Error ===
329 /// config_file_error
330 #[snafu(display("config_file_error"))]
331 EcF0000 = 3900,
332 // === End of Class F0 — Configuration File Error =====
333
334 // === Begin of Class HV — Foreign Data Wrapper Error (SQL/MED) ===
335 /// fdw_error
336 #[snafu(display("fdw_error"))]
337 EcHV000 = 4000,
338 // === End of Class HV — Foreign Data Wrapper Error (SQL/MED) =====
339
340 // === Begin of Class P0 — PL/pgSQL Error ===
341 /// plpgsql_error
342 #[snafu(display("plpgsql_error"))]
343 EcP0000 = 4100,
344 // === End of Class P0 — PL/pgSQL Error =====
345
346 // === Begin of Class XX — Internal Error ===
347 /// internal_error
348 #[snafu(display("internal_error"))]
349 EcXX000 = 4200,
350 // === End of Class XX — Internal Error =====
351}
352
353impl PgErrorCode {
354 fn severity(&self) -> PgErrorSeverity {
355 match self {
356 PgErrorCode::Ec00000 => PgErrorSeverity::Info,
357 PgErrorCode::Ec01000 => PgErrorSeverity::Warning,
358
359 PgErrorCode::EcXX000 | PgErrorCode::Ec42P14 | PgErrorCode::Ec22023 => {
360 PgErrorSeverity::Error
361 }
362 PgErrorCode::Ec28000 | PgErrorCode::Ec28P01 | PgErrorCode::Ec3D000 => {
363 PgErrorSeverity::Fatal
364 }
365
366 _ => PgErrorSeverity::Error,
367 }
368 }
369
370 pub(crate) fn code(&self) -> String {
371 self.as_ref()[2..].to_string()
372 }
373
374 pub fn to_err_info(&self, msg: String) -> ErrorInfo {
375 ErrorInfo::new(self.severity().to_string(), self.code(), msg)
376 }
377}
378
379impl From<PgErrorCode> for ErrorInfo {
380 fn from(code: PgErrorCode) -> ErrorInfo {
381 code.to_err_info(code.to_string())
382 }
383}
384
385impl From<StatusCode> for PgErrorCode {
386 fn from(code: StatusCode) -> PgErrorCode {
387 match code {
388 // ====== Begin of common status code ==============
389 StatusCode::Success => PgErrorCode::Ec00000,
390 StatusCode::Unsupported => PgErrorCode::Ec0A000,
391 StatusCode::InvalidArguments => PgErrorCode::Ec22023,
392 StatusCode::Cancelled => PgErrorCode::Ec57000,
393 StatusCode::Suspended => PgErrorCode::Ec57P03,
394 StatusCode::DeadlineExceeded => PgErrorCode::Ec57000,
395 StatusCode::External => PgErrorCode::Ec58000,
396
397 StatusCode::PartialFailure
398 | StatusCode::Unknown
399 | StatusCode::Unexpected
400 | StatusCode::IllegalState
401 | StatusCode::Internal => PgErrorCode::EcXX000,
402 // ====== End of common status code ================
403
404 // ====== Begin of SQL & query related status code =========
405 StatusCode::InvalidSyntax => PgErrorCode::Ec42601,
406 StatusCode::PlanQuery | StatusCode::EngineExecuteQuery => PgErrorCode::EcP0000,
407 // ====== End of SQL & query related status code ===========
408
409 // ====== Begin of catalog & flow related status code =====
410 StatusCode::TableNotFound
411 | StatusCode::FlowNotFound
412 | StatusCode::RegionNotFound
413 | StatusCode::TriggerNotFound => PgErrorCode::Ec42P01,
414 StatusCode::TableAlreadyExists
415 | StatusCode::FlowAlreadyExists
416 | StatusCode::TriggerAlreadyExists
417 | StatusCode::RegionAlreadyExists => PgErrorCode::Ec42P07,
418
419 StatusCode::TableColumnNotFound => PgErrorCode::Ec42703,
420 StatusCode::TableColumnExists => PgErrorCode::Ec42701,
421 StatusCode::DatabaseNotFound => PgErrorCode::Ec42704,
422 StatusCode::DatabaseAlreadyExists => PgErrorCode::Ec42P04,
423 StatusCode::RegionReadonly => PgErrorCode::Ec25006,
424
425 StatusCode::RegionNotReady | StatusCode::RegionBusy | StatusCode::TableUnavailable => {
426 PgErrorCode::Ec55000
427 }
428 // ====== End of catalog & flow related status code =======
429
430 // ====== Begin of storage & server related status code =====
431 StatusCode::StorageUnavailable | StatusCode::RequestOutdated => PgErrorCode::EcXX000,
432 StatusCode::RuntimeResourcesExhausted => PgErrorCode::Ec53000,
433 StatusCode::RateLimited => PgErrorCode::Ec54000,
434 // ====== End of storage & server related status code =======
435
436 // ====== Begin of auth related status code =====
437 StatusCode::UserNotFound
438 | StatusCode::UnsupportedPasswordType
439 | StatusCode::UserPasswordMismatch => PgErrorCode::Ec28P01,
440
441 StatusCode::AuthHeaderNotFound | StatusCode::InvalidAuthHeader => PgErrorCode::Ec28000,
442 StatusCode::AccessDenied | StatusCode::PermissionDenied => PgErrorCode::Ec42501,
443 // ====== End of auth related status code =====
444 }
445 }
446}
447
448#[cfg(test)]
449mod tests {
450 use common_error::status_code::StatusCode;
451 use strum::{EnumMessage, IntoEnumIterator};
452
453 use super::{ErrorInfo, PgErrorCode, PgErrorSeverity};
454
455 #[test]
456 fn test_error_severity() {
457 // test for ErrorSeverity enum
458 assert_eq!("INFO", PgErrorSeverity::Info.to_string());
459 assert_eq!("DEBUG", PgErrorSeverity::Debug.to_string());
460 assert_eq!("NOTICE", PgErrorSeverity::Notice.to_string());
461 assert_eq!("WARNING", PgErrorSeverity::Warning.to_string());
462
463 assert_eq!("ERROR", PgErrorSeverity::Error.to_string());
464 assert_eq!("FATAL", PgErrorSeverity::Fatal.to_string());
465 assert_eq!("PANIC", PgErrorSeverity::Panic.to_string());
466
467 // test for severity method
468 for code in PgErrorCode::iter() {
469 let name = code.as_ref();
470 assert_eq!("Ec", &name[0..2]);
471
472 if name.starts_with("Ec00") {
473 assert_eq!(PgErrorSeverity::Info, code.severity());
474 } else if name.starts_with("Ec01") {
475 assert_eq!(PgErrorSeverity::Warning, code.severity());
476 } else if name.starts_with("Ec28") || name.starts_with("Ec3D") {
477 assert_eq!(PgErrorSeverity::Fatal, code.severity());
478 } else {
479 assert_eq!(PgErrorSeverity::Error, code.severity());
480 }
481 }
482 }
483
484 #[test]
485 fn test_pg_error_code() {
486 let code = PgErrorCode::Ec00000;
487 assert_eq!("00000", code.code());
488 assert_eq!("successful_completion", code.to_string());
489
490 let code = PgErrorCode::Ec01000;
491 assert_eq!("01000", code.code());
492 assert_eq!("warning", code.to_string());
493
494 // test display is correct
495 for code in PgErrorCode::iter() {
496 assert_eq!(&code.to_string(), code.get_documentation().unwrap())
497 }
498 }
499
500 #[test]
501 fn test_pg_err_info() {
502 let err_info = ErrorInfo::from(PgErrorCode::Ec00000);
503 assert_eq!("INFO", err_info.severity);
504 assert_eq!("00000", err_info.code);
505 assert_eq!("successful_completion", err_info.message);
506
507 let err_info = ErrorInfo::from(PgErrorCode::Ec01000);
508 assert_eq!("WARNING", err_info.severity);
509 assert_eq!("01000", err_info.code);
510 assert_eq!("warning", err_info.message);
511
512 let err_info =
513 PgErrorCode::Ec28P01.to_err_info("password authentication failed".to_string());
514 assert_eq!("FATAL", err_info.severity);
515 assert_eq!("28P01", err_info.code);
516 assert_eq!("password authentication failed", err_info.message);
517
518 let err_info =
519 PgErrorCode::Ec42P14.to_err_info("invalid_prepared_statement_definition".to_string());
520 assert_eq!("ERROR", err_info.severity);
521 assert_eq!("42P14", err_info.code);
522 assert_eq!("invalid_prepared_statement_definition", err_info.message);
523 }
524
525 fn assert_status_code(msg: &str, code: StatusCode) {
526 let code = PgErrorCode::from(code);
527 assert_eq!(msg, &code.to_string());
528 }
529
530 #[test]
531 fn test_from_status_code() {
532 assert_status_code("successful_completion", StatusCode::Success);
533 assert_status_code("invalid_parameter_value", StatusCode::InvalidArguments);
534 assert_status_code("undefined_table", StatusCode::TableNotFound);
535 assert_status_code("internal_error", StatusCode::Unknown);
536 assert_status_code("invalid_password", StatusCode::UserPasswordMismatch);
537 }
538}