lumosai_vector_postgres/
error.rs1use thiserror::Error;
4use lumosai_vector_core::VectorError;
5
6#[derive(Error, Debug)]
8pub enum PostgresError {
9 #[error("Database connection error: {0}")]
11 Connection(String),
12
13 #[error("SQL execution error: {0}")]
15 Sql(String),
16
17 #[error("Migration error: {0}")]
19 Migration(String),
20
21 #[error("Serialization error: {0}")]
23 Serialization(String),
24
25 #[error("Configuration error: {0}")]
27 Config(String),
28
29 #[error("Connection pool error: {0}")]
31 Pool(String),
32
33 #[error("Transaction error: {0}")]
35 Transaction(String),
36
37 #[error("Index error: {0}")]
39 Index(String),
40
41 #[error("pgvector extension error: {0}")]
43 PgVector(String),
44}
45
46pub 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
115pub trait PostgresResultExt<T> {
117 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
127pub 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
141pub 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
151pub fn is_connection_error(err: &PostgresError) -> bool {
153 matches!(err,
154 PostgresError::Connection(_) |
155 PostgresError::Pool(_)
156 )
157}
158
159pub 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
166pub fn table_not_found_error(table_name: &str) -> PostgresError {
168 PostgresError::Sql(format!("Table '{}' does not exist", table_name))
169}
170
171pub 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}