eventql_parser/ast.rs
1//! Abstract syntax tree (AST) types for EventQL.
2//!
3//! This module defines the structure of parsed EventQL queries as an abstract
4//! syntax tree. The AST represents the semantic structure of a query, making it
5//! easy to analyze, transform, or execute queries.
6//!
7//! # Core Types
8//!
9//! - [`Query`] - The root of the AST, representing a complete query
10//! - [`Expr`] - Expressions with position and type information
11//! - [`Value`] - The various kinds of expression values (literals, operators, etc.)
12//! - [`Source`] - Data sources in FROM clauses
13//!
14use std::{
15 collections::BTreeMap,
16 fmt::{self, Display},
17 mem,
18};
19
20use crate::{
21 analysis::{AnalysisOptions, Typed, static_analysis},
22 error::{AnalysisError, Error},
23 token::{Operator, Token},
24};
25use serde::Serialize;
26
27/// Position information for source code locations.
28///
29/// This struct tracks the line and column number of tokens and AST nodes,
30/// which is useful for error reporting and debugging.
31///
32/// # Examples
33///
34/// ```
35/// use eventql_parser::Pos;
36///
37/// let pos = Pos { line: 1, col: 10 };
38/// assert_eq!(pos.line, 1);
39/// assert_eq!(pos.col, 10);
40/// ```
41#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
42pub struct Pos {
43 /// Line number (1-indexed)
44 pub line: u32,
45 /// Column number (1-indexed)
46 pub col: u32,
47}
48
49impl From<Token<'_>> for Pos {
50 fn from(value: Token<'_>) -> Self {
51 Self {
52 line: value.line,
53 col: value.col,
54 }
55 }
56}
57
58/// Represents function argument types with optional parameter support.
59///
60/// This type allows defining functions that have both required and optional parameters.
61/// The `needed` field specifies how many arguments are required, while `values` contains
62/// all possible argument types (both required and optional).
63///
64/// # Examples
65///
66/// ```
67/// use eventql_parser::prelude::{FunArgs, Type};
68///
69/// // Function with all required parameters: (number, string)
70/// let required = FunArgs::required(vec![Type::Number, Type::String]);
71/// assert_eq!(required.needed, 2);
72/// assert_eq!(required.values.len(), 2);
73///
74/// // Function with optional parameters: (boolean, number?)
75/// let optional = FunArgs {
76/// values: vec![Type::Bool, Type::Number],
77/// needed: 1, // Only first parameter is required
78/// };
79/// assert!(optional.match_arg_count(1)); // Can call with just boolean
80/// assert!(optional.match_arg_count(2)); // Can call with both
81/// assert!(!optional.match_arg_count(3)); // Cannot call with 3 args
82/// ```
83#[derive(Debug, Serialize, Clone)]
84pub struct FunArgs {
85 /// All argument types, including both required and optional parameters
86 pub values: Vec<Type>,
87 /// Number of required arguments (must be <= values.len())
88 pub needed: usize,
89}
90
91impl FunArgs {
92 /// Creates a new `FunArgs` where all parameters are required.
93 ///
94 /// # Examples
95 ///
96 /// ```
97 /// use eventql_parser::prelude::{FunArgs, Type};
98 ///
99 /// let args = FunArgs::required(vec![Type::Number, Type::String]);
100 /// assert_eq!(args.needed, 2);
101 /// assert_eq!(args.values.len(), 2);
102 /// ```
103 pub fn required(args: Vec<Type>) -> Self {
104 Self {
105 needed: args.len(),
106 values: args,
107 }
108 }
109
110 /// Returns `true` if there are no argument types defined.
111 ///
112 /// # Examples
113 ///
114 /// ```
115 /// use eventql_parser::prelude::{FunArgs, Type};
116 ///
117 /// let empty = FunArgs::required(vec![]);
118 /// assert!(empty.is_empty());
119 ///
120 /// let not_empty = FunArgs::required(vec![Type::Number]);
121 /// assert!(!not_empty.is_empty());
122 /// ```
123 pub fn is_empty(&self) -> bool {
124 self.values.is_empty()
125 }
126
127 /// Checks if a given argument count is valid for this function signature.
128 ///
129 /// Returns `true` if the count is between `needed` (inclusive) and
130 /// `values.len()` (inclusive), meaning all required arguments are
131 /// provided and no extra arguments beyond the optional ones are given.
132 ///
133 /// # Examples
134 ///
135 /// ```
136 /// use eventql_parser::prelude::{FunArgs, Type};
137 ///
138 /// let args = FunArgs {
139 /// values: vec![Type::Bool, Type::Number, Type::String],
140 /// needed: 1, // Only first parameter is required
141 /// };
142 ///
143 /// assert!(!args.match_arg_count(0)); // Missing required argument
144 /// assert!(args.match_arg_count(1)); // Required argument provided
145 /// assert!(args.match_arg_count(2)); // Required + one optional
146 /// assert!(args.match_arg_count(3)); // All arguments provided
147 /// assert!(!args.match_arg_count(4)); // Too many arguments
148 /// ```
149 pub fn match_arg_count(&self, cnt: usize) -> bool {
150 cnt >= self.needed && cnt <= self.values.len()
151 }
152}
153
154impl From<Vec<Type>> for FunArgs {
155 fn from(value: Vec<Type>) -> Self {
156 Self::required(value)
157 }
158}
159
160/// Type information for expressions.
161///
162/// This enum represents the type of an expression in the E
163#[derive(Clone, Debug, Default, Serialize)]
164pub enum Type {
165 /// Type has not been determined yet
166 #[default]
167 Unspecified,
168 /// Numeric type (f64)
169 Number,
170 /// String type
171 String,
172 /// Boolean type
173 Bool,
174 /// Array type
175 Array(Box<Type>),
176 /// Record (object) type
177 Record(BTreeMap<String, Type>),
178 /// Subject pattern type
179 Subject,
180 /// Function type with support for optional parameters.
181 ///
182 /// The `args` field uses [`FunArgs`] to support both required and optional parameters.
183 /// Optional parameters are indicated when `args.needed < args.values.len()`.
184 ///
185 /// # Examples
186 ///
187 /// ```
188 /// use eventql_parser::prelude::{Type, FunArgs};
189 ///
190 /// // Function with all required parameters: (number, string) -> boolean
191 /// let all_required = Type::App {
192 /// args: vec![Type::Number, Type::String].into(),
193 /// result: Box::new(Type::Bool),
194 /// aggregate: false,
195 /// };
196 ///
197 /// // Aggregate function with optional parameter: (boolean?) => number
198 /// let with_optional = Type::App {
199 /// args: FunArgs {
200 /// values: vec![Type::Bool],
201 /// needed: 0, // All parameters are optional
202 /// },
203 /// result: Box::new(Type::Number),
204 /// aggregate: true,
205 /// };
206 /// ```
207 App {
208 /// Function argument types, supporting optional parameters
209 args: FunArgs,
210 /// Return type of the function
211 result: Box<Type>,
212 /// Whether this is an aggregate function (operates on grouped data)
213 aggregate: bool,
214 },
215 /// Date type (e.g., `2026-01-03`)
216 ///
217 /// Used when a field is explicitly converted to a date using the `AS DATE` syntax.
218 Date,
219 /// Time type (e.g., `13:45:39`)
220 ///
221 /// Used when a field is explicitly converted to a time using the `AS TIME` syntax.
222 Time,
223 /// DateTime type (e.g., `2026-01-01T13:45:39Z`)
224 ///
225 /// Used when a field is explicitly converted to a datetime using the `AS DATETIME` syntax.
226 DateTime,
227 /// Custom type not defined in the EventQL reference
228 ///
229 /// Used when a field is converted to a custom type registered in [`AnalysisOptions::custom_types`].
230 /// The string contains the custom type name as it appears in the query.
231 ///
232 /// # Examples
233 ///
234 /// ```
235 /// use eventql_parser::{parse_query, prelude::AnalysisOptions};
236 ///
237 /// let query = parse_query("FROM e IN events PROJECT INTO { ts: e.data.timestamp as CustomTimestamp }").unwrap();
238 /// let options = AnalysisOptions::default().add_custom_type("CustomTimestamp");
239 /// let typed_query = query.run_static_analysis(&options).unwrap();
240 /// ```
241 Custom(String),
242}
243
244/// Provides human-readable string formatting for types.
245///
246/// Function types display optional parameters with a `?` suffix. For example,
247/// a function with signature `(boolean, number?) -> string` accepts 1 or 2 arguments.
248/// Aggregate functions use `=>` instead of `->` in their signature.
249///
250/// # Examples
251///
252/// ```
253/// use eventql_parser::prelude::{Type, FunArgs};
254///
255/// // Basic types
256/// assert_eq!(Type::Number.to_string(), "number");
257/// assert_eq!(Type::String.to_string(), "string");
258/// assert_eq!(Type::Bool.to_string(), "boolean");
259///
260/// // Array type
261/// let arr = Type::Array(Box::new(Type::Number));
262/// assert_eq!(arr.to_string(), "[]number");
263///
264/// // Function with all required parameters
265/// let func = Type::App {
266/// args: vec![Type::Number, Type::String].into(),
267/// result: Box::new(Type::Bool),
268/// aggregate: false,
269/// };
270/// assert_eq!(func.to_string(), "(number, string) -> boolean");
271///
272/// // Function with optional parameters
273/// let func_optional = Type::App {
274/// args: FunArgs {
275/// values: vec![Type::Bool, Type::Number],
276/// needed: 1,
277/// },
278/// result: Box::new(Type::String),
279/// aggregate: false,
280/// };
281/// assert_eq!(func_optional.to_string(), "(boolean, number?) -> string");
282///
283/// // Aggregate function
284/// let agg = Type::App {
285/// args: vec![Type::Number].into(),
286/// result: Box::new(Type::Number),
287/// aggregate: true,
288/// };
289/// assert_eq!(agg.to_string(), "(number) => number");
290/// ```
291impl Display for Type {
292 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293 match self {
294 Type::Unspecified => write!(f, "any"),
295 Type::Number => write!(f, "number"),
296 Type::String => write!(f, "string"),
297 Type::Bool => write!(f, "boolean"),
298 Type::Array(tpe) => write!(f, "[]{tpe}"),
299 Type::Record(map) => {
300 write!(f, "{{ ")?;
301
302 for (idx, (name, value)) in map.iter().enumerate() {
303 if idx != 0 {
304 write!(f, ", ")?;
305 }
306
307 write!(f, "{name}: {value}")?;
308 }
309
310 write!(f, " }}")
311 }
312 Type::Subject => write!(f, "subject"),
313 Type::App {
314 args,
315 result,
316 aggregate,
317 } => {
318 write!(f, "(")?;
319
320 for (idx, arg) in args.values.iter().enumerate() {
321 if idx != 0 {
322 write!(f, ", ")?;
323 }
324
325 write!(f, "{arg}")?;
326
327 if idx + 1 > args.needed {
328 write!(f, "?")?;
329 }
330 }
331
332 write!(f, ")")?;
333
334 if *aggregate {
335 write!(f, " => ")?;
336 } else {
337 write!(f, " -> ")?;
338 }
339
340 write!(f, "{result}")
341 }
342 Type::Date => write!(f, "date"),
343 Type::Time => write!(f, "time"),
344 Type::DateTime => write!(f, "datetime"),
345 Type::Custom(n) => write!(f, "{}", n.to_lowercase()),
346 }
347 }
348}
349
350impl Type {
351 pub fn as_record_or_panic_mut(&mut self) -> &mut BTreeMap<String, Type> {
352 if let Self::Record(r) = self {
353 return r;
354 }
355
356 panic!("expected record type, got {:?}", self);
357 }
358
359 /// Checks if two types are the same.
360 ///
361 /// * If `self` is `Type::Unspecified` then `self` is updated to the more specific `Type`.
362 /// * If `self` is `Type::Subject` and is checked against a `Type::String` then `self` is updated to `Type::String`
363 pub fn check(self, attrs: &Attrs, other: Type) -> Result<Type, AnalysisError> {
364 match (self, other) {
365 (Self::Unspecified, other) => Ok(other),
366 (this, Self::Unspecified) => Ok(this),
367 (Self::Subject, Self::Subject) => Ok(Self::Subject),
368
369 // Subjects are strings so there is no reason to reject a type
370 // when compared to a string. However, when it happens, we demote
371 // a subject to a string.
372 (Self::Subject, Self::String) => Ok(Self::String),
373 (Self::String, Self::Subject) => Ok(Self::String),
374
375 (Self::Number, Self::Number) => Ok(Self::Number),
376 (Self::String, Self::String) => Ok(Self::String),
377 (Self::Bool, Self::Bool) => Ok(Self::Bool),
378 (Self::Date, Self::Date) => Ok(Self::Date),
379 (Self::Time, Self::Time) => Ok(Self::Time),
380 (Self::DateTime, Self::DateTime) => Ok(Self::DateTime),
381
382 // `DateTime` can be implicitly cast to `Date` or `Time`
383 (Self::DateTime, Self::Date) => Ok(Self::Date),
384 (Self::Date, Self::DateTime) => Ok(Self::Date),
385 (Self::DateTime, Self::Time) => Ok(Self::Time),
386 (Self::Time, Self::DateTime) => Ok(Self::Time),
387 (Self::Custom(a), Self::Custom(b)) if a.eq_ignore_ascii_case(b.as_str()) => {
388 Ok(Self::Custom(a))
389 }
390 (Self::Array(mut a), Self::Array(b)) => {
391 *a = a.as_ref().clone().check(attrs, *b)?;
392 Ok(Self::Array(a))
393 }
394
395 (Self::Record(mut a), Self::Record(b)) if a.len() == b.len() => {
396 if a.is_empty() {
397 return Ok(Self::Record(a));
398 }
399
400 for (ak, bk) in a.keys().zip(b.keys()) {
401 if ak != bk {
402 return Err(AnalysisError::TypeMismatch(
403 attrs.pos.line,
404 attrs.pos.col,
405 Self::Record(a),
406 Self::Record(b),
407 ));
408 }
409 }
410
411 for (av, bv) in a.values_mut().zip(b.into_values()) {
412 let a = mem::take(av);
413 *av = a.check(attrs, bv)?;
414 }
415
416 Ok(Self::Record(a))
417 }
418
419 (
420 Self::App {
421 args: mut a_args,
422 result: mut a_res,
423 aggregate: a_agg,
424 },
425 Self::App {
426 args: b_args,
427 result: b_res,
428 aggregate: b_agg,
429 },
430 ) if a_args.values.len() == b_args.values.len() && a_agg == b_agg => {
431 if a_args.is_empty() {
432 let tmp = mem::take(a_res.as_mut());
433 *a_res = tmp.check(attrs, *b_res)?;
434 return Ok(Self::App {
435 args: a_args,
436 result: a_res,
437 aggregate: a_agg,
438 });
439 }
440
441 for (a, b) in a_args.values.iter_mut().zip(b_args.values.into_iter()) {
442 let tmp = mem::take(a);
443 *a = tmp.check(attrs, b)?;
444 }
445
446 let tmp = mem::take(a_res.as_mut());
447 *a_res = tmp.check(attrs, *b_res)?;
448
449 Ok(Self::App {
450 args: a_args,
451 result: a_res,
452 aggregate: a_agg,
453 })
454 }
455
456 (this, other) => Err(AnalysisError::TypeMismatch(
457 attrs.pos.line,
458 attrs.pos.col,
459 this,
460 other,
461 )),
462 }
463 }
464}
465
466/// Attributes attached to each expression node.
467///
468/// These attributes provide metadata about an expression, including its
469/// position in the source code, scope information, and type information.
470#[derive(Debug, Clone, Copy, Serialize)]
471pub struct Attrs {
472 /// Source position of this expression
473 pub pos: Pos,
474}
475
476impl Attrs {
477 /// Create new attributes with unspecified type.
478 pub fn new(pos: Pos) -> Self {
479 Self { pos }
480 }
481}
482
483/// An expression with metadata.
484///
485/// This is the fundamental building block of the AST. Every expression
486/// carries attributes (position, scope, type) and a value that determines
487/// what kind of expression it is.
488#[derive(Debug, Clone, Serialize)]
489pub struct Expr {
490 /// Metadata about this expression
491 pub attrs: Attrs,
492 /// The value/kind of this expression
493 pub value: Value,
494}
495
496/// Field access expression (e.g., `e.data.price`).
497///
498/// Represents accessing a field of a record or object using dot notation.
499/// Can be chained for nested field access.
500///
501/// # Examples
502///
503/// In the query `WHERE e.data.user.id == 1`, the expression `e.data.user.id`
504/// is parsed as nested `Access` nodes.
505#[derive(Debug, Clone, Serialize)]
506pub struct Access {
507 /// The target expression being accessed
508 pub target: Box<Expr>,
509 /// The name of the field being accessed
510 pub field: String,
511}
512
513/// Function application (e.g., `sum(e.price)`, `count()`).
514///
515/// Represents a function call with zero or more arguments.
516///
517/// # Examples
518///
519/// In the query `WHERE count(e.items) > 5`, the `count(e.items)` is an `App` node.
520#[derive(Debug, Clone, Serialize)]
521pub struct App {
522 /// Name of the function being called
523 pub func: String,
524 /// Arguments passed to the function
525 pub args: Vec<Expr>,
526}
527
528/// A field in a record literal (e.g., `{name: "Alice", age: 30}`).
529///
530/// Represents a key-value pair in a record construction.
531#[derive(Debug, Clone, Serialize)]
532pub struct Field {
533 /// Field name
534 pub name: String,
535 /// Field value expression
536 pub value: Expr,
537}
538
539/// Binary operation (e.g., `a + b`, `x == y`, `p AND q`).
540///
541/// Represents operations that take two operands, including arithmetic,
542/// comparison, and logical operators.
543///
544/// # Examples
545///
546/// In `WHERE e.price > 100 AND e.active == true`, there are multiple
547/// binary operations: `>`, `==`, and `AND`.
548#[derive(Debug, Clone, Serialize)]
549pub struct Binary {
550 /// Left-hand side operand
551 pub lhs: Box<Expr>,
552 /// The operator
553 pub operator: Operator,
554 /// Right-hand side operand
555 pub rhs: Box<Expr>,
556}
557
558/// Unary operation (e.g., `-x`, `NOT active`).
559///
560/// Represents operations that take a single operand.
561///
562/// # Examples
563///
564/// In `WHERE NOT e.deleted`, the `NOT e.deleted` is a unary operation.
565#[derive(Debug, Clone, Serialize)]
566pub struct Unary {
567 /// The operator (Add for +, Sub for -, Not for NOT)
568 pub operator: Operator,
569 /// The operand expression
570 pub expr: Box<Expr>,
571}
572
573/// The kind of value an expression represents.
574///
575/// This enum contains all the different types of expressions that can appear
576/// in an EventQL query, from simple literals to complex operations.
577#[derive(Debug, Clone, Serialize)]
578pub enum Value {
579 /// Numeric literal (e.g., `42`, `3.14`)
580 Number(f64),
581 /// String literal (e.g., `"hello"`)
582 String(String),
583 /// Boolean literal (`true` or `false`)
584 Bool(bool),
585 /// Identifier (e.g., variable name `e`, `x`)
586 Id(String),
587 /// Array literal (e.g., `[1, 2, 3]`)
588 Array(Vec<Expr>),
589 /// Record literal (e.g., `{name: "Alice", age: 30}`)
590 Record(Vec<Field>),
591 /// Field access (e.g., `e.data.price`)
592 Access(Access),
593 /// Function application (e.g., `sum(e.price)`)
594 App(App),
595 /// Binary operation (e.g., `a + b`, `x == y`)
596 Binary(Binary),
597 /// Unary operation (e.g., `-x`, `NOT active`)
598 Unary(Unary),
599 /// Grouped/parenthesized expression (e.g., `(a + b)`)
600 Group(Box<Expr>),
601}
602
603/// A source binding. A name attached to a source of events.
604///
605/// # Examples
606/// in `FROM e IN events`, `e` is the binding.
607#[derive(Debug, Clone, Serialize)]
608pub struct Binding {
609 /// Name attached to a source of events
610 pub name: String,
611 /// Position in the source code where that binding was introduced
612 pub pos: Pos,
613}
614
615/// A data source in a FROM clause.
616///
617/// Sources specify where data comes from in a query. Each source has a binding
618/// (the variable name) and a kind (what it binds to).
619///
620/// # Examples
621///
622/// In `FROM e IN events`, the source has:
623/// - `binding`: `"e"`
624/// - `kind`: `SourceKind::Name("events")`
625#[derive(Debug, Clone, Serialize)]
626pub struct Source<A> {
627 /// Variable name bound to this source
628 pub binding: Binding,
629 /// What this source represents
630 pub kind: SourceKind<A>,
631}
632
633/// The kind of data source.
634///
635/// EventQL supports three types of sources:
636/// - Named sources (e.g., `FROM e IN events`)
637/// - Subject patterns (e.g., `FROM e IN "users/john"`)
638/// - Subqueries (e.g., `FROM e IN (SELECT ...)`)
639#[derive(Debug, Clone, Serialize)]
640pub enum SourceKind<A> {
641 /// Named source (identifier)
642 Name(String),
643 /// Subject pattern (string literal used as event subject pattern)
644 Subject(String),
645 /// Nested subquery
646 Subquery(Box<Query<A>>),
647}
648
649/// ORDER BY clause specification.
650///
651/// Defines how query results should be sorted.
652///
653/// # Examples
654///
655/// In `ORDER BY e.timestamp DESC`, this would be represented as:
656/// - `expr`: expression for `e.timestamp`
657/// - `order`: `Order::Desc`
658#[derive(Debug, Clone, Serialize)]
659pub struct OrderBy {
660 /// Expression to sort by
661 pub expr: Expr,
662 /// Sort direction (ascending or descending)
663 pub order: Order,
664}
665
666/// Sort order direction.
667///
668/// Specifies whether sorting is ascending or descending.
669#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
670pub enum Order {
671 /// Ascending order (smallest to largest)
672 Asc,
673 /// Descending order (largest to smallest)
674 Desc,
675}
676
677/// GROUP BY clause specification
678///
679/// Defines how query results should be order by.
680/// # Examples
681///
682/// In `GROUP BY e.age HAVING age > 123`, this would be represented as:
683/// - `expr`: expression for `e.age`
684/// - `predicate`: `age > 123`
685#[derive(Debug, Clone, Serialize)]
686pub struct GroupBy {
687 /// Expression to group by
688 pub expr: Expr,
689
690 /// Predicate to filter groups after aggregation
691 pub predicate: Option<Expr>,
692}
693
694/// Result set limit specification.
695///
696/// EventQL supports two types of limits:
697/// - `TOP n` - Take the first n results
698/// - `SKIP n` - Skip the first n results
699///
700/// # Examples
701///
702/// - `TOP 10` limits to first 10 results
703/// - `SKIP 20` skips first 20 results
704#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
705pub enum Limit {
706 /// Skip the first n results
707 Skip(u64),
708 /// Take only the first n results
709 Top(u64),
710}
711
712/// Represents the state of a query that only has a valid syntax. There are no guarantee that all
713/// the variables exists or that the query is sound. For example, if the user is asking for an event
714/// that has field that should be a string or a number at the same time.
715#[derive(Debug, Clone, Copy, Serialize)]
716pub struct Raw;
717
718/// A complete EventQL query.
719///
720/// This is the root node of the AST, representing a full query with all its clauses.
721/// A query must have at least one source and a projection; other clauses are optional.
722///
723/// # Structure
724///
725/// ```text
726/// FROM <alias> <source>
727/// [FROM <alias> <source>] ...
728/// [WHERE <condition>]
729/// [GROUP BY <field> [HAVING <condition>]]
730/// [ORDER BY <field> ASC|DESC]
731/// [TOP|SKIP <n>]
732/// PROJECT INTO [DISTINCT] <projection>
733/// ```
734///
735/// # Examples
736///
737/// ```
738/// use eventql_parser::parse_query;
739///
740/// let query = parse_query(
741/// "FROM e IN events \
742/// WHERE e.price > 100 \
743/// ORDER BY e.timestamp DESC \
744/// TOP 10 \
745/// PROJECT INTO {id: e.id, price: e.price}"
746/// ).unwrap();
747///
748/// assert_eq!(query.sources.len(), 1);
749/// assert!(query.predicate.is_some());
750/// assert!(query.order_by.is_some());
751/// assert!(query.limit.is_some());
752/// ```
753#[derive(Debug, Clone, Serialize)]
754pub struct Query<A> {
755 /// Metadata about this query
756 pub attrs: Attrs,
757 /// FROM clause sources (must have at least one)
758 pub sources: Vec<Source<A>>,
759 /// Optional WHERE clause filter predicate
760 pub predicate: Option<Expr>,
761 /// Optional GROUP BY clause expression
762 pub group_by: Option<GroupBy>,
763 /// Optional ORDER BY clause
764 pub order_by: Option<OrderBy>,
765 /// Optional LIMIT clause (TOP or SKIP)
766 pub limit: Option<Limit>,
767 /// PROJECT INTO clause expression (required)
768 pub projection: Expr,
769 /// Remove duplicate rows from the query's results
770 pub distinct: bool,
771 /// Type-level metadata about the query's analysis state.
772 ///
773 /// This field uses a generic type parameter to track whether the query
774 /// is in a raw (unparsed/untyped) state or has been statically analyzed:
775 /// - `Query<Raw>`: Query parsed but not yet type-checked
776 /// - `Query<Typed>`: Query that has passed static analysis with validated
777 /// types and variable scopes
778 ///
779 /// This provides compile-time guarantees about the query's type safety.
780 pub meta: A,
781}
782
783impl Query<Raw> {
784 /// Performs static analysis on this raw query.
785 ///
786 /// This is a convenience method that runs type checking and variable scoping
787 /// analysis on the query, converting it from a raw (untyped) query to a
788 /// typed query.
789 ///
790 /// The analysis validates:
791 /// - Variable declarations and scoping
792 /// - Type compatibility in expressions and operations
793 /// - Valid field accesses on record types
794 /// - Correct function argument types and counts
795 /// - Aggregate function usage restrictions (only in PROJECT INTO)
796 /// - No mixing of aggregate functions with source-bound fields
797 /// - Aggregate function arguments are source-bound fields
798 /// - Non-empty record literals in projections
799 ///
800 /// # Arguments
801 ///
802 /// * `options` - Configuration containing type information and default scope
803 ///
804 /// # Returns
805 ///
806 /// Returns a typed query on success, or an error if type checking fails.
807 pub fn run_static_analysis(self, options: &AnalysisOptions) -> crate::Result<Query<Typed>> {
808 static_analysis(options, self).map_err(Error::Analysis)
809 }
810}