Skip to main content

uni_query_functions/rewrite/
mod.rs

1/// Query rewriting framework
2///
3/// This module provides a general-purpose framework for transforming function calls
4/// into equivalent predicate expressions at compile time. The framework enables:
5///
6/// - Full predicate pushdown to storage
7/// - Index utilization
8/// - Extensible plugin architecture for adding new rewrite rules
9///
10/// # Architecture
11///
12/// The framework consists of:
13///
14/// - **RewriteRule trait**: Interface for implementing rewrite transformations
15/// - **RewriteRegistry**: Global registry of all rewrite rules
16/// - **ExpressionWalker**: Traverses expression trees and applies rules
17/// - **RewriteContext**: Contextual information during rewriting
18///
19/// # Example Usage
20///
21/// ```ignore
22/// use uni_query::rewrite::rewrite_query;
23///
24/// // Rewrite a complete query
25/// let rewritten = rewrite_query(query)?;
26/// ```
27///
28/// # Adding New Rules
29///
30/// See `rules/README.md` for a guide on implementing custom rewrite rules.
31pub mod context;
32pub mod error;
33pub mod function_rename;
34pub mod registry;
35pub mod rule;
36pub mod rules;
37pub mod walker;
38
39use context::{RewriteContext, RewriteStats};
40use error::RewriteError;
41use registry::RewriteRegistry;
42use walker::ExpressionWalker;
43
44use std::sync::OnceLock;
45
46/// Global registry of rewrite rules, initialized once on first use
47static GLOBAL_REGISTRY: OnceLock<RewriteRegistry> = OnceLock::new();
48
49/// Get the global rewrite registry, initializing it if needed
50fn get_or_init_registry() -> &'static RewriteRegistry {
51    GLOBAL_REGISTRY.get_or_init(|| {
52        tracing::info!("Initializing query rewrite framework");
53        RewriteRegistry::with_builtin_rules()
54    })
55}
56
57/// Log rewrite statistics if any functions were visited
58fn log_rewrite_stats(stats: &RewriteStats) {
59    if stats.functions_visited > 0 {
60        tracing::info!(
61            "Rewrite pass complete: {} functions visited, {} rewritten, {} skipped",
62            stats.functions_visited,
63            stats.functions_rewritten,
64            stats.functions_skipped
65        );
66
67        if !stats.errors.is_empty() {
68            tracing::debug!("Rewrite errors: {:?}", stats.errors);
69        }
70    }
71}
72
73/// Rewrite a complete query
74///
75/// This is the main entry point for applying query rewrites. It walks the
76/// entire query tree and applies registered rewrite rules to all function calls.
77///
78/// # Arguments
79///
80/// * `query` - The query to rewrite
81///
82/// # Returns
83///
84/// The rewritten query with function calls transformed into predicates.
85///
86/// # Example
87///
88/// ```ignore
89/// let query = parse_cypher("MATCH (p)-[e:EMPLOYED_BY]->(c) WHERE uni.temporal.validAt(e, 'start', 'end', datetime('2021-06-15')) RETURN c")?;
90/// let rewritten = rewrite_query(query)?;
91/// // The validAt function will be transformed into: e.start <= ... AND (e.end IS NULL OR e.end >= ...)
92/// ```
93pub fn rewrite_query(
94    query: uni_cypher::ast::Query,
95) -> Result<uni_cypher::ast::Query, RewriteError> {
96    let registry = get_or_init_registry();
97    let context = RewriteContext::default();
98
99    let mut walker = ExpressionWalker::new(registry, context);
100    let rewritten_query = walker.rewrite_query(query);
101
102    log_rewrite_stats(&walker.context().stats);
103
104    Ok(rewritten_query)
105}