rustledger_query/error.rs
1//! BQL error types.
2
3use thiserror::Error;
4
5/// Error returned when parsing a BQL query fails.
6#[derive(Debug, Error)]
7#[error("syntax error at position {position}: {kind}")]
8pub struct ParseError {
9 /// The kind of error.
10 pub kind: ParseErrorKind,
11 /// Position in the input where the error occurred.
12 pub position: usize,
13}
14
15/// The kind of parse error.
16#[derive(Debug, Error)]
17pub enum ParseErrorKind {
18 /// Unexpected end of input.
19 #[error("unexpected end of input")]
20 UnexpectedEof,
21 /// Syntax error with details.
22 #[error("{0}")]
23 SyntaxError(String),
24}
25
26impl ParseError {
27 /// Create a new parse error.
28 pub const fn new(kind: ParseErrorKind, position: usize) -> Self {
29 Self { kind, position }
30 }
31}
32
33/// Error returned when executing a query fails.
34///
35/// Marked `#[non_exhaustive]` so adding new variants doesn't break
36/// downstream consumers that match exhaustively.
37#[derive(Debug, Error)]
38#[non_exhaustive]
39pub enum QueryError {
40 /// Parse error.
41 #[error("parse error: {0}")]
42 Parse(#[from] ParseError),
43 /// Type error (incompatible types in operation).
44 #[error("type error: {0}")]
45 Type(String),
46 /// Unknown column name.
47 #[error("column '{0}' not found")]
48 UnknownColumn(String),
49 /// Unknown function name.
50 #[error("no function matches \"{0}\"")]
51 UnknownFunction(String),
52 /// Invalid function arguments.
53 #[error("invalid arguments for function {0}: {1}")]
54 InvalidArguments(String, String),
55 /// Aggregation error.
56 #[error("aggregation error: {0}")]
57 Aggregation(String),
58 /// Evaluation error.
59 #[error("evaluation error: {0}")]
60 Evaluation(String),
61 /// PIVOT BY clause does not have exactly two columns.
62 ///
63 /// Matches bean-query's compiler check (`_compile_pivot_by` in
64 /// `beanquery/compiler.py`). The first column is the pivot value
65 /// (whose values become new column headers); the second is the
66 /// GROUP BY column to keep as the row key.
67 #[error("PIVOT BY requires exactly two columns, got {0}")]
68 PivotWrongArity(usize),
69 /// PIVOT BY's two columns refer to the same target.
70 ///
71 /// Bean-query message: `the two PIVOT BY columns cannot be the
72 /// same column`. Same wording reused for upstream parity.
73 #[error("the two PIVOT BY columns cannot be the same column")]
74 PivotSameColumn,
75 /// PIVOT BY's second column isn't in the GROUP BY clause.
76 ///
77 /// The second pivot column has to be a GROUP BY key — otherwise
78 /// the pivot output rows wouldn't have a stable identity. Bean-
79 /// query message: `the second PIVOT BY column must be a GROUP BY
80 /// column`.
81 #[error("the second PIVOT BY column must be a GROUP BY column")]
82 PivotSecondNotInGroupBy,
83 /// PIVOT BY used on a query with no `GROUP BY` clause.
84 ///
85 /// Implicit grouping (a SELECT with aggregates but no GROUP BY)
86 /// produces a single row whose key is undefined; PIVOT BY's second
87 /// column has nothing meaningful to refer to. Distinct from
88 /// `PivotSecondNotInGroupBy` (which is for "GROUP BY exists but the
89 /// key column isn't in it").
90 #[error("PIVOT BY requires an explicit GROUP BY clause")]
91 PivotWithoutGroupBy,
92}