vibesql/analyzer/
error.rs

1//! Analyzer-specific error types.
2
3use crate::error::Span;
4use crate::types::SqlType;
5use std::fmt;
6
7/// Analyzer error kinds.
8#[derive(Debug, Clone)]
9pub enum AnalyzerErrorKind {
10    /// Table not found in catalog.
11    TableNotFound { name: String },
12    /// Column not found.
13    ColumnNotFound { name: String, table: Option<String> },
14    /// Ambiguous column reference.
15    AmbiguousColumn { name: String, tables: Vec<String> },
16    /// Function not found.
17    FunctionNotFound { name: String },
18    /// Wrong number of arguments to function.
19    WrongArgumentCount {
20        function: String,
21        expected_min: usize,
22        expected_max: Option<usize>,
23        actual: usize,
24    },
25    /// Type mismatch in expression.
26    TypeMismatch {
27        expected: SqlType,
28        actual: SqlType,
29        context: String,
30    },
31    /// Types are not comparable.
32    TypesNotComparable { left: SqlType, right: SqlType },
33    /// Invalid use of aggregate function.
34    InvalidAggregateUse { function: String, reason: String },
35    /// Invalid use of window function.
36    InvalidWindowUse { function: String, reason: String },
37    /// Duplicate alias.
38    DuplicateAlias { name: String },
39    /// Duplicate column in GROUP BY.
40    DuplicateGroupByColumn { name: String },
41    /// Non-aggregated column in SELECT with GROUP BY.
42    NonAggregatedColumn { column: String },
43    /// ORDER BY column not in SELECT (when DISTINCT is used).
44    OrderByNotInSelect { column: String },
45    /// Invalid HAVING clause (no GROUP BY).
46    HavingWithoutGroupBy,
47    /// Invalid subquery.
48    InvalidSubquery { reason: String },
49    /// Division by zero (constant folding).
50    DivisionByZero,
51    /// Invalid CAST.
52    InvalidCast { from: SqlType, to: SqlType },
53    /// Invalid date/time literal.
54    InvalidDateTimeLiteral {
55        value: String,
56        expected_type: String,
57    },
58    /// CTE name conflict.
59    DuplicateCte { name: String },
60    /// Recursive CTE without UNION ALL.
61    InvalidRecursiveCte { reason: String },
62    /// Star (*) not allowed in this context.
63    StarNotAllowed { context: String },
64    /// EXCEPT/INTERSECT column count mismatch.
65    SetOperationColumnMismatch { left: usize, right: usize },
66    /// Unknown error.
67    Other { message: String },
68}
69
70/// An analyzer error with location information.
71#[derive(Debug, Clone)]
72pub struct AnalyzerError {
73    /// The kind of error.
74    pub kind: AnalyzerErrorKind,
75    /// The source span where the error occurred.
76    pub span: Option<Span>,
77}
78
79impl AnalyzerError {
80    /// Create a new analyzer error.
81    pub fn new(kind: AnalyzerErrorKind) -> Self {
82        Self { kind, span: None }
83    }
84
85    /// Create a new analyzer error with a span.
86    pub fn with_span(kind: AnalyzerErrorKind, span: Span) -> Self {
87        Self {
88            kind,
89            span: Some(span),
90        }
91    }
92
93    /// Table not found.
94    pub fn table_not_found(name: impl Into<String>) -> Self {
95        Self::new(AnalyzerErrorKind::TableNotFound { name: name.into() })
96    }
97
98    /// Column not found.
99    pub fn column_not_found(name: impl Into<String>, table: Option<String>) -> Self {
100        Self::new(AnalyzerErrorKind::ColumnNotFound {
101            name: name.into(),
102            table,
103        })
104    }
105
106    /// Ambiguous column.
107    pub fn ambiguous_column(name: impl Into<String>, tables: Vec<String>) -> Self {
108        Self::new(AnalyzerErrorKind::AmbiguousColumn {
109            name: name.into(),
110            tables,
111        })
112    }
113
114    /// Function not found.
115    pub fn function_not_found(name: impl Into<String>) -> Self {
116        Self::new(AnalyzerErrorKind::FunctionNotFound { name: name.into() })
117    }
118
119    /// Wrong argument count.
120    pub fn wrong_argument_count(
121        function: impl Into<String>,
122        expected_min: usize,
123        expected_max: Option<usize>,
124        actual: usize,
125    ) -> Self {
126        Self::new(AnalyzerErrorKind::WrongArgumentCount {
127            function: function.into(),
128            expected_min,
129            expected_max,
130            actual,
131        })
132    }
133
134    /// Type mismatch.
135    pub fn type_mismatch(expected: SqlType, actual: SqlType, context: impl Into<String>) -> Self {
136        Self::new(AnalyzerErrorKind::TypeMismatch {
137            expected,
138            actual,
139            context: context.into(),
140        })
141    }
142
143    /// Types not comparable.
144    pub fn types_not_comparable(left: SqlType, right: SqlType) -> Self {
145        Self::new(AnalyzerErrorKind::TypesNotComparable { left, right })
146    }
147
148    /// Non-aggregated column.
149    pub fn non_aggregated_column(column: impl Into<String>) -> Self {
150        Self::new(AnalyzerErrorKind::NonAggregatedColumn {
151            column: column.into(),
152        })
153    }
154
155    /// Invalid aggregate use.
156    pub fn invalid_aggregate_use(function: impl Into<String>, reason: impl Into<String>) -> Self {
157        Self::new(AnalyzerErrorKind::InvalidAggregateUse {
158            function: function.into(),
159            reason: reason.into(),
160        })
161    }
162
163    /// Set operation column mismatch.
164    pub fn set_operation_column_mismatch(left: usize, right: usize) -> Self {
165        Self::new(AnalyzerErrorKind::SetOperationColumnMismatch { left, right })
166    }
167}
168
169impl fmt::Display for AnalyzerError {
170    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171        match &self.kind {
172            AnalyzerErrorKind::TableNotFound { name } => {
173                write!(f, "table '{}' not found", name)
174            }
175            AnalyzerErrorKind::ColumnNotFound { name, table } => {
176                if let Some(t) = table {
177                    write!(f, "column '{}' not found in table '{}'", name, t)
178                } else {
179                    write!(f, "column '{}' not found", name)
180                }
181            }
182            AnalyzerErrorKind::AmbiguousColumn { name, tables } => {
183                write!(
184                    f,
185                    "ambiguous column '{}' found in tables: {}",
186                    name,
187                    tables.join(", ")
188                )
189            }
190            AnalyzerErrorKind::FunctionNotFound { name } => {
191                write!(f, "function '{}' not found", name)
192            }
193            AnalyzerErrorKind::WrongArgumentCount {
194                function,
195                expected_min,
196                expected_max,
197                actual,
198            } => {
199                if let Some(max) = expected_max {
200                    if expected_min == max {
201                        write!(
202                            f,
203                            "function '{}' expects {} arguments, got {}",
204                            function, expected_min, actual
205                        )
206                    } else {
207                        write!(
208                            f,
209                            "function '{}' expects {}-{} arguments, got {}",
210                            function, expected_min, max, actual
211                        )
212                    }
213                } else {
214                    write!(
215                        f,
216                        "function '{}' expects at least {} arguments, got {}",
217                        function, expected_min, actual
218                    )
219                }
220            }
221            AnalyzerErrorKind::TypeMismatch {
222                expected,
223                actual,
224                context,
225            } => {
226                write!(
227                    f,
228                    "type mismatch in {}: expected {}, got {}",
229                    context, expected, actual
230                )
231            }
232            AnalyzerErrorKind::TypesNotComparable { left, right } => {
233                write!(f, "cannot compare {} with {}", left, right)
234            }
235            AnalyzerErrorKind::InvalidAggregateUse { function, reason } => {
236                write!(
237                    f,
238                    "invalid use of aggregate function '{}': {}",
239                    function, reason
240                )
241            }
242            AnalyzerErrorKind::InvalidWindowUse { function, reason } => {
243                write!(
244                    f,
245                    "invalid use of window function '{}': {}",
246                    function, reason
247                )
248            }
249            AnalyzerErrorKind::DuplicateAlias { name } => {
250                write!(f, "duplicate alias '{}'", name)
251            }
252            AnalyzerErrorKind::DuplicateGroupByColumn { name } => {
253                write!(f, "duplicate column '{}' in GROUP BY", name)
254            }
255            AnalyzerErrorKind::NonAggregatedColumn { column } => {
256                write!(
257                    f,
258                    "column '{}' must appear in GROUP BY clause or be used in an aggregate function",
259                    column
260                )
261            }
262            AnalyzerErrorKind::OrderByNotInSelect { column } => {
263                write!(
264                    f,
265                    "ORDER BY column '{}' must appear in SELECT list when DISTINCT is used",
266                    column
267                )
268            }
269            AnalyzerErrorKind::HavingWithoutGroupBy => {
270                write!(f, "HAVING clause requires GROUP BY clause")
271            }
272            AnalyzerErrorKind::InvalidSubquery { reason } => {
273                write!(f, "invalid subquery: {}", reason)
274            }
275            AnalyzerErrorKind::DivisionByZero => {
276                write!(f, "division by zero")
277            }
278            AnalyzerErrorKind::InvalidCast { from, to } => {
279                write!(f, "cannot cast {} to {}", from, to)
280            }
281            AnalyzerErrorKind::InvalidDateTimeLiteral {
282                value,
283                expected_type,
284            } => {
285                write!(f, "invalid {} literal: '{}'", expected_type, value)
286            }
287            AnalyzerErrorKind::DuplicateCte { name } => {
288                write!(f, "duplicate CTE name '{}'", name)
289            }
290            AnalyzerErrorKind::InvalidRecursiveCte { reason } => {
291                write!(f, "invalid recursive CTE: {}", reason)
292            }
293            AnalyzerErrorKind::StarNotAllowed { context } => {
294                write!(f, "* not allowed in {}", context)
295            }
296            AnalyzerErrorKind::SetOperationColumnMismatch { left, right } => {
297                write!(
298                    f,
299                    "set operations require the same number of columns ({} vs {})",
300                    left, right
301                )
302            }
303            AnalyzerErrorKind::Other { message } => {
304                write!(f, "{}", message)
305            }
306        }
307    }
308}
309
310impl std::error::Error for AnalyzerError {}