1#[derive(Debug, Clone, thiserror::Error)]
10pub enum SQLError {
11 #[error("{0}")]
12 Parse(String),
13 #[error("{0}")]
14 Unsupported(String),
15 #[error("LISTEN and UNLISTEN require a notification subscription")]
17 NotificationRequiresSubscription,
18 #[error("relation \"{0}\" does not exist")]
19 UnknownTable(String),
20 #[error("column \"{0}\" does not exist")]
21 UnknownColumn(String),
22 #[error("column reference \"{0}\" is ambiguous")]
23 AmbiguousColumn(String),
24 #[error("unknown function: {0}")]
25 UnknownFunction(String),
26 #[error("type mismatch: {0}")]
27 TypeMismatch(String),
28 #[error("invalid argument count for `{name}`: expected {expected}, got {actual}")]
29 BadArity {
30 name: String,
31 expected: String,
32 actual: usize,
33 },
34 #[error("No value supplied for parameter ${0}")]
35 MissingParam(usize),
36 #[error("vector dimension mismatch: expected {expected}, got {actual}")]
37 VectorDimMismatch { expected: usize, actual: usize },
38 #[error("{0}")]
39 Cancelled(#[from] uqa_core::QueryCancelled),
40 #[error("{message}")]
45 Routine { sqlstate: String, message: String },
46 #[error("{message}")]
48 Diagnostic {
49 sqlstate: String,
50 message: String,
51 detail: Option<String>,
52 hint: Option<String>,
53 },
54 #[error("internal error: {0}")]
55 Internal(String),
56}
57
58impl SQLError {
59 pub const fn code(&self) -> Option<&'static str> {
61 match self {
62 Self::NotificationRequiresSubscription => Some("NOTIFICATION_REQUIRES_SUBSCRIPTION"),
63 _ => None,
64 }
65 }
66
67 pub fn undefined_function_call(signature: &str) -> Self {
69 Self::Diagnostic {
70 sqlstate: "42883".into(),
71 message: format!("function {signature} does not exist"),
72 detail: None,
73 hint: Some(
74 "No function matches the given name and argument types. You might need to add explicit type casts."
75 .into(),
76 ),
77 }
78 }
79
80 pub fn ambiguous_function_call(signature: &str) -> Self {
82 Self::Diagnostic {
83 sqlstate: "42725".into(),
84 message: format!("function {signature} is not unique"),
85 detail: None,
86 hint: Some(
87 "Could not choose a best candidate function. You might need to add explicit type casts."
88 .into(),
89 ),
90 }
91 }
92
93 pub fn function_call_resolution(sqlstate: &str, signature: &str, suffix: &str) -> Self {
95 match (sqlstate, suffix) {
96 ("42883", "does not exist") => Self::undefined_function_call(signature),
97 ("42725", "is not unique") => Self::ambiguous_function_call(signature),
98 _ => Self::Routine {
99 sqlstate: sqlstate.into(),
100 message: format!("function {signature} {suffix}"),
101 },
102 }
103 }
104
105 pub fn unknown_qualified_column(qualifier: &str, column: &str) -> Self {
106 Self::Routine {
107 sqlstate: "42703".into(),
108 message: format!("column {qualifier}.{column} does not exist"),
109 }
110 }
111
112 pub fn sqlstate(&self) -> Option<&str> {
116 match self {
117 SQLError::Cancelled(cancelled) => Some(cancelled.sqlstate()),
118 SQLError::Parse(_) => Some("42601"), SQLError::Unsupported(_) | SQLError::NotificationRequiresSubscription => Some("0A000"), SQLError::UnknownTable(_) => Some("42P01"), SQLError::UnknownColumn(_) => Some("42703"), SQLError::AmbiguousColumn(_) => Some("42702"), SQLError::UnknownFunction(_) => Some("42883"), SQLError::TypeMismatch(_) => Some("42804"), SQLError::BadArity { .. } => Some("42883"), SQLError::MissingParam(_) => Some("S1002"), SQLError::VectorDimMismatch { .. } => Some("22023"), SQLError::Routine { sqlstate, .. } | SQLError::Diagnostic { sqlstate, .. } => {
129 Some(sqlstate)
130 }
131 SQLError::Internal(_) => Some("XX000"), }
133 }
134
135 pub fn detail(&self) -> Option<&str> {
137 match self {
138 SQLError::Diagnostic { detail, .. } => detail.as_deref(),
139 _ => None,
140 }
141 }
142
143 pub fn hint(&self) -> Option<&str> {
145 match self {
146 SQLError::Diagnostic { hint, .. } => hint.as_deref(),
147 _ => None,
148 }
149 }
150}
151
152pub type Result<T> = std::result::Result<T, SQLError>;
153
154impl From<pg_query::Error> for SQLError {
155 fn from(value: pg_query::Error) -> Self {
156 match value {
157 pg_query::Error::ParseDiagnostic(diagnostic) => Self::Diagnostic {
158 sqlstate: diagnostic.sqlstate,
159 message: diagnostic.message,
160 detail: diagnostic.detail,
161 hint: diagnostic.hint,
162 },
163 pg_query::Error::Parse(message) => Self::Parse(message),
164 other => Self::Parse(other.to_string()),
165 }
166 }
167}
168
169impl From<uqa_core::memory::MemoryError> for SQLError {
170 fn from(error: uqa_core::memory::MemoryError) -> Self {
171 Self::Routine {
172 sqlstate: "53200".into(),
173 message: error.to_string(),
174 }
175 }
176}
177
178impl From<uqa_core::ValueRetentionError> for SQLError {
179 fn from(error: uqa_core::ValueRetentionError) -> Self {
180 match error {
181 uqa_core::ValueRetentionError::Memory(error) => error.into(),
182 uqa_core::ValueRetentionError::Cancelled(error) => error.into(),
183 error @ uqa_core::ValueRetentionError::Malformed { .. } => Self::Routine {
184 sqlstate: "XX001".into(),
185 message: error.to_string(),
186 },
187 }
188 }
189}