lumosai_vector_postgres/
error.rs

1//! Error types for PostgreSQL vector storage
2
3use thiserror::Error;
4use lumosai_vector_core::VectorError;
5
6/// PostgreSQL-specific error types
7#[derive(Error, Debug)]
8pub enum PostgresError {
9    /// Database connection error
10    #[error("Database connection error: {0}")]
11    Connection(String),
12    
13    /// SQL execution error
14    #[error("SQL execution error: {0}")]
15    Sql(String),
16    
17    /// Migration error
18    #[error("Migration error: {0}")]
19    Migration(String),
20    
21    /// Serialization error
22    #[error("Serialization error: {0}")]
23    Serialization(String),
24    
25    /// Configuration error
26    #[error("Configuration error: {0}")]
27    Config(String),
28    
29    /// Pool error
30    #[error("Connection pool error: {0}")]
31    Pool(String),
32    
33    /// Transaction error
34    #[error("Transaction error: {0}")]
35    Transaction(String),
36    
37    /// Index error
38    #[error("Index error: {0}")]
39    Index(String),
40    
41    /// pgvector extension error
42    #[error("pgvector extension error: {0}")]
43    PgVector(String),
44}
45
46/// Result type for PostgreSQL operations
47pub type PostgresResult<T> = Result<T, PostgresError>;
48
49impl From<PostgresError> for VectorError {
50    fn from(err: PostgresError) -> Self {
51        match err {
52            PostgresError::Connection(msg) => VectorError::connection_failed(msg),
53            PostgresError::Sql(msg) => VectorError::OperationFailed(msg),
54            PostgresError::Migration(msg) => VectorError::OperationFailed(format!("Migration: {}", msg)),
55            PostgresError::Serialization(msg) => VectorError::serialization(msg),
56            PostgresError::Config(msg) => VectorError::OperationFailed(format!("Configuration: {}", msg)),
57            PostgresError::Pool(msg) => VectorError::connection_failed(format!("Pool: {}", msg)),
58            PostgresError::Transaction(msg) => VectorError::OperationFailed(format!("Transaction: {}", msg)),
59            PostgresError::Index(msg) => VectorError::OperationFailed(format!("Index: {}", msg)),
60            PostgresError::PgVector(msg) => VectorError::OperationFailed(format!("pgvector: {}", msg)),
61        }
62    }
63}
64
65impl From<sqlx::Error> for PostgresError {
66    fn from(err: sqlx::Error) -> Self {
67        match err {
68            sqlx::Error::Configuration(msg) => PostgresError::Config(msg.to_string()),
69            sqlx::Error::Database(db_err) => PostgresError::Sql(db_err.to_string()),
70            sqlx::Error::Io(io_err) => PostgresError::Connection(io_err.to_string()),
71            sqlx::Error::Tls(tls_err) => PostgresError::Connection(tls_err.to_string()),
72            sqlx::Error::Protocol(msg) => PostgresError::Connection(msg),
73            sqlx::Error::RowNotFound => PostgresError::Sql("Row not found".to_string()),
74            sqlx::Error::TypeNotFound { type_name } => {
75                PostgresError::Sql(format!("Type not found: {}", type_name))
76            },
77            sqlx::Error::ColumnIndexOutOfBounds { index, len } => {
78                PostgresError::Sql(format!("Column index {} out of bounds (len: {})", index, len))
79            },
80            sqlx::Error::ColumnNotFound(name) => {
81                PostgresError::Sql(format!("Column not found: {}", name))
82            },
83            sqlx::Error::ColumnDecode { index, source } => {
84                PostgresError::Serialization(format!("Column decode error at index {}: {}", index, source))
85            },
86            sqlx::Error::Decode(source) => {
87                PostgresError::Serialization(format!("Decode error: {}", source))
88            },
89            sqlx::Error::PoolTimedOut => PostgresError::Pool("Pool timed out".to_string()),
90            sqlx::Error::PoolClosed => PostgresError::Pool("Pool closed".to_string()),
91            sqlx::Error::WorkerCrashed => PostgresError::Pool("Worker crashed".to_string()),
92            _ => PostgresError::Sql(err.to_string()),
93        }
94    }
95}
96
97impl From<serde_json::Error> for PostgresError {
98    fn from(err: serde_json::Error) -> Self {
99        PostgresError::Serialization(err.to_string())
100    }
101}
102
103impl From<std::num::ParseFloatError> for PostgresError {
104    fn from(err: std::num::ParseFloatError) -> Self {
105        PostgresError::Serialization(format!("Float parse error: {}", err))
106    }
107}
108
109impl From<std::num::ParseIntError> for PostgresError {
110    fn from(err: std::num::ParseIntError) -> Self {
111        PostgresError::Serialization(format!("Integer parse error: {}", err))
112    }
113}
114
115/// Helper trait for converting results
116pub trait PostgresResultExt<T> {
117    /// Convert to VectorError result
118    fn into_vector_result(self) -> lumosai_vector_core::Result<T>;
119}
120
121impl<T> PostgresResultExt<T> for PostgresResult<T> {
122    fn into_vector_result(self) -> lumosai_vector_core::Result<T> {
123        self.map_err(|e| e.into())
124    }
125}
126
127/// Helper function to check if error is related to missing pgvector extension
128pub fn is_pgvector_missing_error(err: &PostgresError) -> bool {
129    match err {
130        PostgresError::Sql(msg) | PostgresError::PgVector(msg) => {
131            msg.contains("vector") && (
132                msg.contains("does not exist") ||
133                msg.contains("unknown type") ||
134                msg.contains("extension")
135            )
136        },
137        _ => false,
138    }
139}
140
141/// Helper function to check if error is related to table not existing
142pub fn is_table_not_found_error(err: &PostgresError) -> bool {
143    match err {
144        PostgresError::Sql(msg) => {
145            msg.contains("relation") && msg.contains("does not exist")
146        },
147        _ => false,
148    }
149}
150
151/// Helper function to check if error is related to connection issues
152pub fn is_connection_error(err: &PostgresError) -> bool {
153    matches!(err, 
154        PostgresError::Connection(_) | 
155        PostgresError::Pool(_)
156    )
157}
158
159/// Helper function to create a pgvector extension error
160pub fn pgvector_extension_error() -> PostgresError {
161    PostgresError::PgVector(
162        "pgvector extension is not installed. Please install it with: CREATE EXTENSION vector;".to_string()
163    )
164}
165
166/// Helper function to create a table not found error
167pub fn table_not_found_error(table_name: &str) -> PostgresError {
168    PostgresError::Sql(format!("Table '{}' does not exist", table_name))
169}
170
171/// Helper function to create an index creation error
172pub fn index_creation_error(index_name: &str, reason: &str) -> PostgresError {
173    PostgresError::Index(format!("Failed to create index '{}': {}", index_name, reason))
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179    
180    #[test]
181    fn test_error_conversion() {
182        let postgres_err = PostgresError::Connection("test".to_string());
183        let vector_err: VectorError = postgres_err.into();
184        
185        assert!(matches!(vector_err, VectorError::ConnectionFailed { .. }));
186    }
187    
188    #[test]
189    fn test_pgvector_error_detection() {
190        let err = PostgresError::Sql("type \"vector\" does not exist".to_string());
191        assert!(is_pgvector_missing_error(&err));
192        
193        let err = PostgresError::Sql("some other error".to_string());
194        assert!(!is_pgvector_missing_error(&err));
195    }
196    
197    #[test]
198    fn test_table_not_found_detection() {
199        let err = PostgresError::Sql("relation \"test_table\" does not exist".to_string());
200        assert!(is_table_not_found_error(&err));
201        
202        let err = PostgresError::Sql("some other error".to_string());
203        assert!(!is_table_not_found_error(&err));
204    }
205}