Skip to main content

rustledger_query/
ast.rs

1//! BQL Abstract Syntax Tree types.
2//!
3//! This module defines the AST for Beancount Query Language (BQL),
4//! a SQL-like query language for financial data analysis.
5
6use rust_decimal::Decimal;
7use rustledger_core::NaiveDate;
8use std::fmt;
9
10/// A complete BQL query.
11#[derive(Debug, Clone, PartialEq)]
12pub enum Query {
13    /// SELECT query (boxed to reduce enum size).
14    Select(Box<SelectQuery>),
15    /// JOURNAL shorthand query.
16    Journal(JournalQuery),
17    /// BALANCES shorthand query.
18    Balances(BalancesQuery),
19    /// PRINT shorthand query.
20    Print(PrintQuery),
21    /// CREATE TABLE statement.
22    CreateTable(CreateTableStmt),
23    /// INSERT statement.
24    Insert(InsertStmt),
25}
26
27/// Column definition for CREATE TABLE.
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct ColumnDef {
30    /// Column name.
31    pub name: String,
32    /// Optional type hint (BQL is dynamically typed, but hints are allowed).
33    pub type_hint: Option<String>,
34}
35
36/// CREATE TABLE statement.
37#[derive(Debug, Clone, PartialEq)]
38pub struct CreateTableStmt {
39    /// Table name.
40    pub table_name: String,
41    /// Column definitions.
42    pub columns: Vec<ColumnDef>,
43    /// Optional AS SELECT (create from query).
44    pub as_select: Option<Box<SelectQuery>>,
45}
46
47/// INSERT statement.
48#[derive(Debug, Clone, PartialEq)]
49pub struct InsertStmt {
50    /// Target table name.
51    pub table_name: String,
52    /// Optional column list (if omitted, uses all columns in order).
53    pub columns: Option<Vec<String>>,
54    /// Source data: either VALUES or SELECT.
55    pub source: InsertSource,
56}
57
58/// Source data for INSERT.
59#[derive(Debug, Clone, PartialEq)]
60pub enum InsertSource {
61    /// VALUES clause with literal rows.
62    Values(Vec<Vec<Expr>>),
63    /// SELECT query as source.
64    Select(Box<SelectQuery>),
65}
66
67/// A SELECT query.
68#[derive(Debug, Clone, PartialEq)]
69pub struct SelectQuery {
70    /// Whether DISTINCT was specified.
71    pub distinct: bool,
72    /// Target columns/expressions.
73    pub targets: Vec<Target>,
74    /// FROM clause (transaction-level filtering).
75    pub from: Option<FromClause>,
76    /// WHERE clause (posting-level filtering).
77    pub where_clause: Option<Expr>,
78    /// GROUP BY clause.
79    pub group_by: Option<Vec<Expr>>,
80    /// HAVING clause (filter on aggregated results).
81    pub having: Option<Expr>,
82    /// PIVOT BY clause (pivot table transformation).
83    pub pivot_by: Option<Vec<Expr>>,
84    /// ORDER BY clause.
85    pub order_by: Option<Vec<OrderSpec>>,
86    /// LIMIT clause.
87    pub limit: Option<u64>,
88}
89
90/// A target in the SELECT clause.
91#[derive(Debug, Clone, PartialEq)]
92pub struct Target {
93    /// The expression to select.
94    pub expr: Expr,
95    /// Optional alias (AS name).
96    pub alias: Option<String>,
97}
98
99/// FROM clause with transaction-level modifiers.
100#[derive(Debug, Clone, PartialEq)]
101pub struct FromClause {
102    /// OPEN ON date - summarize entries before this date.
103    pub open_on: Option<NaiveDate>,
104    /// CLOSE ON date - truncate entries after this date.
105    pub close_on: Option<NaiveDate>,
106    /// CLEAR - transfer income/expense to equity.
107    pub clear: bool,
108    /// Filter expression.
109    pub filter: Option<Expr>,
110    /// Subquery (derived table).
111    pub subquery: Option<Box<SelectQuery>>,
112    /// Table name (for querying user-created tables).
113    pub table_name: Option<String>,
114}
115
116/// ORDER BY specification.
117#[derive(Debug, Clone, PartialEq)]
118pub struct OrderSpec {
119    /// Expression to order by.
120    pub expr: Expr,
121    /// Sort direction.
122    pub direction: SortDirection,
123}
124
125/// Sort direction.
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
127pub enum SortDirection {
128    /// Ascending (default).
129    #[default]
130    Asc,
131    /// Descending.
132    Desc,
133}
134
135/// JOURNAL shorthand query.
136#[derive(Debug, Clone, PartialEq)]
137pub struct JournalQuery {
138    /// Account pattern to filter by.
139    pub account_pattern: String,
140    /// Optional aggregation function (AT cost, AT units, etc.).
141    pub at_function: Option<String>,
142    /// Optional FROM clause.
143    pub from: Option<FromClause>,
144}
145
146/// BALANCES shorthand query.
147#[derive(Debug, Clone, PartialEq)]
148pub struct BalancesQuery {
149    /// Optional aggregation function.
150    pub at_function: Option<String>,
151    /// Optional FROM clause.
152    pub from: Option<FromClause>,
153}
154
155/// PRINT shorthand query.
156#[derive(Debug, Clone, PartialEq)]
157pub struct PrintQuery {
158    /// Optional FROM clause.
159    pub from: Option<FromClause>,
160}
161
162/// An expression in BQL.
163#[derive(Debug, Clone, PartialEq)]
164pub enum Expr {
165    /// Wildcard (*).
166    Wildcard,
167    /// Column reference.
168    Column(String),
169    /// Literal value.
170    Literal(Literal),
171    /// Function call.
172    Function(FunctionCall),
173    /// Window function call (with OVER clause).
174    Window(WindowFunction),
175    /// Binary operation.
176    BinaryOp(Box<BinaryOp>),
177    /// Unary operation.
178    UnaryOp(Box<UnaryOp>),
179    /// Parenthesized expression.
180    Paren(Box<Self>),
181    /// BETWEEN ... AND expression.
182    Between {
183        /// Value to test.
184        value: Box<Self>,
185        /// Lower bound.
186        low: Box<Self>,
187        /// Upper bound.
188        high: Box<Self>,
189    },
190}
191
192/// A literal value.
193#[derive(Debug, Clone, PartialEq, Eq)]
194pub enum Literal {
195    /// String literal.
196    String(String),
197    /// Numeric literal.
198    Number(Decimal),
199    /// Integer literal.
200    Integer(i64),
201    /// Date literal.
202    Date(NaiveDate),
203    /// Boolean literal.
204    Boolean(bool),
205    /// NULL literal.
206    Null,
207}
208
209/// A function call.
210#[derive(Debug, Clone, PartialEq)]
211pub struct FunctionCall {
212    /// Function name.
213    pub name: String,
214    /// Arguments.
215    pub args: Vec<Expr>,
216}
217
218/// A window function call (function with OVER clause).
219#[derive(Debug, Clone, PartialEq)]
220pub struct WindowFunction {
221    /// Function name (`ROW_NUMBER`, RANK, SUM, etc.).
222    pub name: String,
223    /// Function arguments.
224    pub args: Vec<Expr>,
225    /// Window specification.
226    pub over: WindowSpec,
227}
228
229/// Window specification for OVER clause.
230#[derive(Debug, Clone, PartialEq, Default)]
231pub struct WindowSpec {
232    /// PARTITION BY expressions.
233    pub partition_by: Option<Vec<Expr>>,
234    /// ORDER BY specifications.
235    pub order_by: Option<Vec<OrderSpec>>,
236}
237
238/// A binary operation.
239#[derive(Debug, Clone, PartialEq)]
240pub struct BinaryOp {
241    /// Left operand.
242    pub left: Expr,
243    /// Operator.
244    pub op: BinaryOperator,
245    /// Right operand.
246    pub right: Expr,
247}
248
249/// Binary operators.
250#[derive(Debug, Clone, Copy, PartialEq, Eq)]
251pub enum BinaryOperator {
252    // Comparison
253    /// Equal (=).
254    Eq,
255    /// Not equal (!=).
256    Ne,
257    /// Less than (<).
258    Lt,
259    /// Less than or equal (<=).
260    Le,
261    /// Greater than (>).
262    Gt,
263    /// Greater than or equal (>=).
264    Ge,
265    /// Regular expression match (~).
266    Regex,
267    /// Regular expression not match (!~).
268    NotRegex,
269    /// IN operator.
270    In,
271    /// NOT IN operator.
272    NotIn,
273
274    // Logical
275    /// Logical AND.
276    And,
277    /// Logical OR.
278    Or,
279
280    // Arithmetic
281    /// Addition (+).
282    Add,
283    /// Subtraction (-).
284    Sub,
285    /// Multiplication (*).
286    Mul,
287    /// Division (/).
288    Div,
289    /// Modulo (%).
290    Mod,
291}
292
293/// A unary operation.
294#[derive(Debug, Clone, PartialEq)]
295pub struct UnaryOp {
296    /// Operator.
297    pub op: UnaryOperator,
298    /// Operand.
299    pub operand: Expr,
300}
301
302/// Unary operators.
303#[derive(Debug, Clone, Copy, PartialEq, Eq)]
304pub enum UnaryOperator {
305    /// Logical NOT.
306    Not,
307    /// Negation (-).
308    Neg,
309    /// IS NULL.
310    IsNull,
311    /// IS NOT NULL.
312    IsNotNull,
313}
314
315impl SelectQuery {
316    /// Create a new SELECT query with the given targets.
317    pub const fn new(targets: Vec<Target>) -> Self {
318        Self {
319            distinct: false,
320            targets,
321            from: None,
322            where_clause: None,
323            group_by: None,
324            having: None,
325            pivot_by: None,
326            order_by: None,
327            limit: None,
328        }
329    }
330
331    /// Set the DISTINCT flag.
332    pub const fn distinct(mut self) -> Self {
333        self.distinct = true;
334        self
335    }
336
337    /// Set the FROM clause.
338    pub fn from(mut self, from: FromClause) -> Self {
339        self.from = Some(from);
340        self
341    }
342
343    /// Set the WHERE clause.
344    pub fn where_clause(mut self, expr: Expr) -> Self {
345        self.where_clause = Some(expr);
346        self
347    }
348
349    /// Set the GROUP BY clause.
350    pub fn group_by(mut self, exprs: Vec<Expr>) -> Self {
351        self.group_by = Some(exprs);
352        self
353    }
354
355    /// Set the HAVING clause.
356    pub fn having(mut self, expr: Expr) -> Self {
357        self.having = Some(expr);
358        self
359    }
360
361    /// Set the PIVOT BY clause.
362    pub fn pivot_by(mut self, exprs: Vec<Expr>) -> Self {
363        self.pivot_by = Some(exprs);
364        self
365    }
366
367    /// Set the ORDER BY clause.
368    pub fn order_by(mut self, specs: Vec<OrderSpec>) -> Self {
369        self.order_by = Some(specs);
370        self
371    }
372
373    /// Set the LIMIT.
374    pub const fn limit(mut self, n: u64) -> Self {
375        self.limit = Some(n);
376        self
377    }
378}
379
380impl Target {
381    /// Create a new target from an expression.
382    pub const fn new(expr: Expr) -> Self {
383        Self { expr, alias: None }
384    }
385
386    /// Create a target with an alias.
387    pub fn with_alias(expr: Expr, alias: impl Into<String>) -> Self {
388        Self {
389            expr,
390            alias: Some(alias.into()),
391        }
392    }
393}
394
395impl FromClause {
396    /// Create a new empty FROM clause.
397    pub const fn new() -> Self {
398        Self {
399            open_on: None,
400            close_on: None,
401            clear: false,
402            filter: None,
403            subquery: None,
404            table_name: None,
405        }
406    }
407
408    /// Create a FROM clause from a subquery.
409    pub fn from_subquery(query: SelectQuery) -> Self {
410        Self {
411            open_on: None,
412            close_on: None,
413            clear: false,
414            filter: None,
415            subquery: Some(Box::new(query)),
416            table_name: None,
417        }
418    }
419
420    /// Create a FROM clause from a table name.
421    pub fn from_table(name: impl Into<String>) -> Self {
422        Self {
423            open_on: None,
424            close_on: None,
425            clear: false,
426            filter: None,
427            subquery: None,
428            table_name: Some(name.into()),
429        }
430    }
431
432    /// Set the OPEN ON date.
433    pub const fn open_on(mut self, date: NaiveDate) -> Self {
434        self.open_on = Some(date);
435        self
436    }
437
438    /// Set the CLOSE ON date.
439    pub const fn close_on(mut self, date: NaiveDate) -> Self {
440        self.close_on = Some(date);
441        self
442    }
443
444    /// Set the CLEAR flag.
445    pub const fn clear(mut self) -> Self {
446        self.clear = true;
447        self
448    }
449
450    /// Set the filter expression.
451    pub fn filter(mut self, expr: Expr) -> Self {
452        self.filter = Some(expr);
453        self
454    }
455
456    /// Set the subquery.
457    pub fn subquery(mut self, query: SelectQuery) -> Self {
458        self.subquery = Some(Box::new(query));
459        self
460    }
461}
462
463impl Default for FromClause {
464    fn default() -> Self {
465        Self::new()
466    }
467}
468
469impl Expr {
470    /// Create a column reference.
471    pub fn column(name: impl Into<String>) -> Self {
472        Self::Column(name.into())
473    }
474
475    /// Create a string literal.
476    pub fn string(s: impl Into<String>) -> Self {
477        Self::Literal(Literal::String(s.into()))
478    }
479
480    /// Create a number literal.
481    pub const fn number(n: Decimal) -> Self {
482        Self::Literal(Literal::Number(n))
483    }
484
485    /// Create an integer literal.
486    pub const fn integer(n: i64) -> Self {
487        Self::Literal(Literal::Integer(n))
488    }
489
490    /// Create a date literal.
491    pub const fn date(d: NaiveDate) -> Self {
492        Self::Literal(Literal::Date(d))
493    }
494
495    /// Create a boolean literal.
496    pub const fn boolean(b: bool) -> Self {
497        Self::Literal(Literal::Boolean(b))
498    }
499
500    /// Create a NULL literal.
501    pub const fn null() -> Self {
502        Self::Literal(Literal::Null)
503    }
504
505    /// Create a function call.
506    pub fn function(name: impl Into<String>, args: Vec<Self>) -> Self {
507        Self::Function(FunctionCall {
508            name: name.into(),
509            args,
510        })
511    }
512
513    /// Create a binary operation.
514    pub fn binary(left: Self, op: BinaryOperator, right: Self) -> Self {
515        Self::BinaryOp(Box::new(BinaryOp { left, op, right }))
516    }
517
518    /// Create a unary operation.
519    pub fn unary(op: UnaryOperator, operand: Self) -> Self {
520        Self::UnaryOp(Box::new(UnaryOp { op, operand }))
521    }
522
523    /// Create a BETWEEN ... AND expression.
524    pub fn between(value: Self, low: Self, high: Self) -> Self {
525        Self::Between {
526            value: Box::new(value),
527            low: Box::new(low),
528            high: Box::new(high),
529        }
530    }
531}
532
533impl OrderSpec {
534    /// Create an ascending order spec.
535    pub const fn asc(expr: Expr) -> Self {
536        Self {
537            expr,
538            direction: SortDirection::Asc,
539        }
540    }
541
542    /// Create a descending order spec.
543    pub const fn desc(expr: Expr) -> Self {
544        Self {
545            expr,
546            direction: SortDirection::Desc,
547        }
548    }
549}
550
551impl fmt::Display for Expr {
552    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553        match self {
554            Self::Wildcard => write!(f, "*"),
555            Self::Column(name) => write!(f, "{name}"),
556            Self::Literal(lit) => write!(f, "{lit}"),
557            Self::Function(func) => {
558                write!(f, "{}(", func.name)?;
559                for (i, arg) in func.args.iter().enumerate() {
560                    if i > 0 {
561                        write!(f, ", ")?;
562                    }
563                    write!(f, "{arg}")?;
564                }
565                write!(f, ")")
566            }
567            Self::Window(wf) => {
568                write!(f, "{}(", wf.name)?;
569                for (i, arg) in wf.args.iter().enumerate() {
570                    if i > 0 {
571                        write!(f, ", ")?;
572                    }
573                    write!(f, "{arg}")?;
574                }
575                write!(f, ") OVER ()")
576            }
577            Self::BinaryOp(op) => write!(f, "({} {} {})", op.left, op.op, op.right),
578            Self::UnaryOp(op) => {
579                // IS NULL and IS NOT NULL are postfix operators
580                match op.op {
581                    UnaryOperator::IsNull => write!(f, "{} IS NULL", op.operand),
582                    UnaryOperator::IsNotNull => write!(f, "{} IS NOT NULL", op.operand),
583                    _ => write!(f, "{}{}", op.op, op.operand),
584                }
585            }
586            Self::Paren(inner) => write!(f, "({inner})"),
587            Self::Between { value, low, high } => {
588                write!(f, "{value} BETWEEN {low} AND {high}")
589            }
590        }
591    }
592}
593
594impl fmt::Display for Literal {
595    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
596        match self {
597            Self::String(s) => write!(f, "\"{s}\""),
598            Self::Number(n) => write!(f, "{n}"),
599            Self::Integer(n) => write!(f, "{n}"),
600            Self::Date(d) => write!(f, "{d}"),
601            Self::Boolean(b) => write!(f, "{b}"),
602            Self::Null => write!(f, "NULL"),
603        }
604    }
605}
606
607impl fmt::Display for BinaryOperator {
608    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
609        let s = match self {
610            Self::Eq => "=",
611            Self::Ne => "!=",
612            Self::Lt => "<",
613            Self::Le => "<=",
614            Self::Gt => ">",
615            Self::Ge => ">=",
616            Self::Regex => "~",
617            Self::NotRegex => "!~",
618            Self::In => "IN",
619            Self::NotIn => "NOT IN",
620            Self::And => "AND",
621            Self::Or => "OR",
622            Self::Add => "+",
623            Self::Sub => "-",
624            Self::Mul => "*",
625            Self::Div => "/",
626            Self::Mod => "%",
627        };
628        write!(f, "{s}")
629    }
630}
631
632impl fmt::Display for UnaryOperator {
633    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
634        let s = match self {
635            Self::Not => "NOT ",
636            Self::Neg => "-",
637            Self::IsNull => " IS NULL",
638            Self::IsNotNull => " IS NOT NULL",
639        };
640        write!(f, "{s}")
641    }
642}
643
644#[cfg(test)]
645mod tests {
646    use super::*;
647    use rust_decimal_macros::dec;
648
649    #[test]
650    fn test_expr_display_wildcard() {
651        assert_eq!(Expr::Wildcard.to_string(), "*");
652    }
653
654    #[test]
655    fn test_expr_display_column() {
656        assert_eq!(Expr::Column("account".to_string()).to_string(), "account");
657    }
658
659    #[test]
660    fn test_expr_display_literals() {
661        assert_eq!(Expr::string("hello").to_string(), "\"hello\"");
662        assert_eq!(Expr::integer(42).to_string(), "42");
663        assert_eq!(Expr::number(dec!(3.14)).to_string(), "3.14");
664        assert_eq!(Expr::boolean(true).to_string(), "true");
665        assert_eq!(Expr::null().to_string(), "NULL");
666    }
667
668    #[test]
669    fn test_expr_display_date() {
670        let date = NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
671        assert_eq!(Expr::date(date).to_string(), "2024-01-15");
672    }
673
674    #[test]
675    fn test_expr_display_function_no_args() {
676        let func = Expr::function("now", vec![]);
677        assert_eq!(func.to_string(), "now()");
678    }
679
680    #[test]
681    fn test_expr_display_function_one_arg() {
682        let func = Expr::function("account_sortkey", vec![Expr::column("account")]);
683        assert_eq!(func.to_string(), "account_sortkey(account)");
684    }
685
686    #[test]
687    fn test_expr_display_function_multiple_args() {
688        let func = Expr::function(
689            "coalesce",
690            vec![Expr::column("a"), Expr::column("b"), Expr::integer(0)],
691        );
692        assert_eq!(func.to_string(), "coalesce(a, b, 0)");
693    }
694
695    #[test]
696    fn test_expr_display_window() {
697        let wf = Expr::Window(WindowFunction {
698            name: "row_number".to_string(),
699            args: vec![],
700            over: WindowSpec::default(),
701        });
702        assert_eq!(wf.to_string(), "row_number() OVER ()");
703    }
704
705    #[test]
706    fn test_expr_display_window_with_args() {
707        let wf = Expr::Window(WindowFunction {
708            name: "sum".to_string(),
709            args: vec![Expr::column("amount")],
710            over: WindowSpec::default(),
711        });
712        assert_eq!(wf.to_string(), "sum(amount) OVER ()");
713    }
714
715    #[test]
716    fn test_expr_display_binary_op() {
717        let expr = Expr::binary(Expr::column("a"), BinaryOperator::Add, Expr::integer(1));
718        assert_eq!(expr.to_string(), "(a + 1)");
719    }
720
721    #[test]
722    fn test_expr_display_unary_not() {
723        let expr = Expr::unary(UnaryOperator::Not, Expr::column("flag"));
724        assert_eq!(expr.to_string(), "NOT flag");
725    }
726
727    #[test]
728    fn test_expr_display_unary_neg() {
729        let expr = Expr::unary(UnaryOperator::Neg, Expr::column("x"));
730        assert_eq!(expr.to_string(), "-x");
731    }
732
733    #[test]
734    fn test_expr_display_is_null() {
735        let expr = Expr::unary(UnaryOperator::IsNull, Expr::column("x"));
736        assert_eq!(expr.to_string(), "x IS NULL");
737    }
738
739    #[test]
740    fn test_expr_display_is_not_null() {
741        let expr = Expr::unary(UnaryOperator::IsNotNull, Expr::column("x"));
742        assert_eq!(expr.to_string(), "x IS NOT NULL");
743    }
744
745    #[test]
746    fn test_expr_display_paren() {
747        let inner = Expr::binary(Expr::column("a"), BinaryOperator::Add, Expr::column("b"));
748        let expr = Expr::Paren(Box::new(inner));
749        assert_eq!(expr.to_string(), "((a + b))");
750    }
751
752    #[test]
753    fn test_expr_display_between() {
754        let expr = Expr::between(Expr::column("x"), Expr::integer(1), Expr::integer(10));
755        assert_eq!(expr.to_string(), "x BETWEEN 1 AND 10");
756    }
757
758    #[test]
759    fn test_binary_operator_display() {
760        assert_eq!(BinaryOperator::Eq.to_string(), "=");
761        assert_eq!(BinaryOperator::Ne.to_string(), "!=");
762        assert_eq!(BinaryOperator::Lt.to_string(), "<");
763        assert_eq!(BinaryOperator::Le.to_string(), "<=");
764        assert_eq!(BinaryOperator::Gt.to_string(), ">");
765        assert_eq!(BinaryOperator::Ge.to_string(), ">=");
766        assert_eq!(BinaryOperator::Regex.to_string(), "~");
767        assert_eq!(BinaryOperator::NotRegex.to_string(), "!~");
768        assert_eq!(BinaryOperator::In.to_string(), "IN");
769        assert_eq!(BinaryOperator::NotIn.to_string(), "NOT IN");
770        assert_eq!(BinaryOperator::And.to_string(), "AND");
771        assert_eq!(BinaryOperator::Or.to_string(), "OR");
772        assert_eq!(BinaryOperator::Add.to_string(), "+");
773        assert_eq!(BinaryOperator::Sub.to_string(), "-");
774        assert_eq!(BinaryOperator::Mul.to_string(), "*");
775        assert_eq!(BinaryOperator::Div.to_string(), "/");
776        assert_eq!(BinaryOperator::Mod.to_string(), "%");
777    }
778
779    #[test]
780    fn test_unary_operator_display() {
781        assert_eq!(UnaryOperator::Not.to_string(), "NOT ");
782        assert_eq!(UnaryOperator::Neg.to_string(), "-");
783        assert_eq!(UnaryOperator::IsNull.to_string(), " IS NULL");
784        assert_eq!(UnaryOperator::IsNotNull.to_string(), " IS NOT NULL");
785    }
786
787    #[test]
788    fn test_literal_display() {
789        assert_eq!(Literal::String("test".to_string()).to_string(), "\"test\"");
790        assert_eq!(Literal::Number(dec!(1.5)).to_string(), "1.5");
791        assert_eq!(Literal::Integer(42).to_string(), "42");
792        assert_eq!(Literal::Boolean(false).to_string(), "false");
793        assert_eq!(Literal::Null.to_string(), "NULL");
794    }
795}