Skip to main content

aurora_db/parser/
executor_utils.rs

1use crate::error::{AqlError, ErrorCode, Result};
2use crate::parser::ast;
3
4/// Filter with compiled regexes for performance
5#[derive(Debug, Clone)]
6pub enum CompiledFilter {
7    Eq(String, ast::Value),
8    Ne(String, ast::Value),
9    Gt(String, ast::Value),
10    Gte(String, ast::Value),
11    Lt(String, ast::Value),
12    Lte(String, ast::Value),
13    In(String, ast::Value),
14    NotIn(String, ast::Value),
15    Contains(String, ast::Value),
16    ContainsAny(String, ast::Value),
17    ContainsAll(String, ast::Value),
18    StartsWith(String, ast::Value),
19    EndsWith(String, ast::Value),
20    Matches(String, regex::Regex),
21    IsNull(String),
22    IsNotNull(String),
23    And(Vec<CompiledFilter>),
24    Or(Vec<CompiledFilter>),
25    Not(Box<CompiledFilter>),
26}
27
28/// Compile an AQL filter into a CompiledFilter
29pub fn compile_filter(filter: &ast::Filter) -> Result<CompiledFilter> {
30    match filter {
31        ast::Filter::Eq(f, v) => Ok(CompiledFilter::Eq(f.clone(), v.clone())),
32        ast::Filter::Ne(f, v) => Ok(CompiledFilter::Ne(f.clone(), v.clone())),
33        ast::Filter::Gt(f, v) => Ok(CompiledFilter::Gt(f.clone(), v.clone())),
34        ast::Filter::Gte(f, v) => Ok(CompiledFilter::Gte(f.clone(), v.clone())),
35        ast::Filter::Lt(f, v) => Ok(CompiledFilter::Lt(f.clone(), v.clone())),
36        ast::Filter::Lte(f, v) => Ok(CompiledFilter::Lte(f.clone(), v.clone())),
37        ast::Filter::In(f, v) => Ok(CompiledFilter::In(f.clone(), v.clone())),
38        ast::Filter::NotIn(f, v) => Ok(CompiledFilter::NotIn(f.clone(), v.clone())),
39        ast::Filter::Contains(f, v) => Ok(CompiledFilter::Contains(f.clone(), v.clone())),
40        ast::Filter::ContainsAny(f, v) => Ok(CompiledFilter::ContainsAny(f.clone(), v.clone())),
41        ast::Filter::ContainsAll(f, v) => Ok(CompiledFilter::ContainsAll(f.clone(), v.clone())),
42        ast::Filter::StartsWith(f, v) => Ok(CompiledFilter::StartsWith(f.clone(), v.clone())),
43        ast::Filter::EndsWith(f, v) => Ok(CompiledFilter::EndsWith(f.clone(), v.clone())),
44        ast::Filter::Matches(f, v) => {
45            if let ast::Value::String(pattern) = v {
46                // Use RegexBuilder with size limits to prevent ReDoS attacks
47                let re = regex::RegexBuilder::new(pattern)
48                    .size_limit(10_000_000) // 10MB compiled regex size limit
49                    .dfa_size_limit(2_000_000) // 2MB DFA size limit
50                    .build()
51                    .map_err(|e| {
52                        AqlError::new(
53                            ErrorCode::SecurityError,
54                            format!(
55                                "Regex pattern '{}' is too complex or invalid: {}",
56                                pattern, e
57                            ),
58                        )
59                    })?;
60                Ok(CompiledFilter::Matches(f.clone(), re))
61            } else {
62                Err(AqlError::new(
63                    ErrorCode::InvalidInput,
64                    "Matches filter requires a string pattern",
65                ))
66            }
67        }
68        ast::Filter::IsNull(f) => Ok(CompiledFilter::IsNull(f.clone())),
69        ast::Filter::IsNotNull(f) => Ok(CompiledFilter::IsNotNull(f.clone())),
70        ast::Filter::And(filters) => {
71            let compiled = filters
72                .iter()
73                .map(compile_filter)
74                .collect::<Result<Vec<_>>>()?;
75            Ok(CompiledFilter::And(compiled))
76        }
77        ast::Filter::Or(filters) => {
78            let compiled = filters
79                .iter()
80                .map(compile_filter)
81                .collect::<Result<Vec<_>>>()?;
82            Ok(CompiledFilter::Or(compiled))
83        }
84        ast::Filter::Not(filter) => {
85            let compiled = compile_filter(filter)?;
86            Ok(CompiledFilter::Not(Box::new(compiled)))
87        }
88    }
89}