Skip to main content

uqa_sql/catalog/
errors.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Preserve SQL diagnostics carried through catalog storage errors.
8use crate::SQLError;
9
10pub fn storage_error(action: &str, err: &(dyn std::error::Error + 'static)) -> SQLError {
11    let mut source: Option<&(dyn std::error::Error + 'static)> = Some(err);
12    while let Some(error) = source {
13        if let Some(error) = error.downcast_ref::<SQLError>() {
14            return SQLError::Routine {
15                sqlstate: error.sqlstate().unwrap_or("XX000").into(),
16                message: error.to_string(),
17            };
18        }
19        source = error.source();
20    }
21    SQLError::Internal(format!("{action} failed in storage backend: {err}"))
22}
23
24pub fn dml_storage_error(action: &str, err: impl std::fmt::Display) -> SQLError {
25    SQLError::Internal(format!("{action} failed in storage backend: {err}"))
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31    #[derive(Debug)]
32    struct CatalogFailure(SQLError);
33    impl std::fmt::Display for CatalogFailure {
34        fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35            formatter.write_str("catalog lookup failed")
36        }
37    }
38    impl std::error::Error for CatalogFailure {
39        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
40            Some(&self.0)
41        }
42    }
43    #[test]
44    fn catalog_error_chain_preserves_the_embedded_sql_diagnostic() {
45        let original = SQLError::Routine {
46            sqlstate: "42501".into(),
47            message: "permission denied for schema secret".into(),
48        };
49        let expected = original.to_string();
50        let error = storage_error("column type coercion", &CatalogFailure(original));
51        assert_eq!(error.sqlstate(), Some("42501"));
52        assert_eq!(error.to_string(), expected);
53    }
54    #[test]
55    fn plain_catalog_errors_retain_the_operation_context() {
56        let error = std::io::Error::other("unavailable");
57        assert!(
58            matches!(storage_error("column type coercion", &error), SQLError::Internal(message) if message == "column type coercion failed in storage backend: unavailable")
59        );
60    }
61}