Skip to main content

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}