1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//! Logical rewrite rule trait.
//!
//! Heuristic rules operating on the pure logical tree
//! (`LogicalNodeEnum`). This mirrors the physical `RewriteRule`
//! (`rule.rs`) but targets logical operators only, keeping logical and
//! physical optimization boundaries explicit.
use std::sync::Arc;
use crate::optimizer::error::OptimizeResult;
use crate::optimizer::stats::StatisticsManager;
use crate::planning::plan::logical::LogicalNodeEnum;
/// Context shared by logical rewrite rules.
pub struct LogicalRuleContext {
/// Statistics manager for selectivity / cardinality lookups.
pub stats: Arc<StatisticsManager>,
/// Whether any rule has changed the tree so far.
pub changed: bool,
}
impl LogicalRuleContext {
/// Create a context backed by a fresh statistics manager.
pub fn new() -> Self {
Self {
stats: Arc::new(StatisticsManager::new()),
changed: false,
}
}
/// Create a context sharing the given statistics manager.
pub fn with_stats(stats: Arc<StatisticsManager>) -> Self {
Self {
stats,
changed: false,
}
}
/// Record that the tree was changed.
pub fn mark_changed(&mut self) {
self.changed = true;
}
}
impl Default for LogicalRuleContext {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for LogicalRuleContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LogicalRuleContext")
.field("changed", &self.changed)
.finish_non_exhaustive()
}
}
/// Rewrite rule operating on `LogicalNodeEnum`.
///
/// Rules identify a local logical pattern and rewrite it in place,
/// returning `true` when the node was changed.
pub trait LogicalRule: std::fmt::Debug + Send + Sync {
/// Rule name for diagnostics.
fn name(&self) -> &str;
/// Apply the rule to `node`, returning `true` on change.
fn apply(
&self,
node: &mut LogicalNodeEnum,
ctx: &mut LogicalRuleContext,
) -> OptimizeResult<bool>;
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
struct NoopRule;
impl LogicalRule for NoopRule {
fn name(&self) -> &str {
"NoopRule"
}
fn apply(
&self,
_node: &mut LogicalNodeEnum,
_ctx: &mut LogicalRuleContext,
) -> OptimizeResult<bool> {
Ok(false)
}
}
#[test]
fn logical_rule_context_defaults() {
let ctx = LogicalRuleContext::new();
assert!(!ctx.changed);
let rule = NoopRule;
assert_eq!(rule.name(), "NoopRule");
}
}