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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! Rewriting the definition of a trait
//!
//! This module provides the definition of a trait that encompasses heuristic rules for rewriting code.
//! Heuristic rules do not rely on cost calculations and always produce either better or equivalent plans.
//!
//! The current implementation uses types that are independent of the planner layer and no longer relies on the optimizer module.
use crate::optimizer::heuristic::context::RewriteContext;
use crate::optimizer::heuristic::pattern::Pattern;
use crate::optimizer::heuristic::result::{RewriteResult, TransformResult};
use crate::optimizer::heuristic::rule::RewriteRule;
use crate::planning::plan::PlanNodeEnum;
/// Heuristic rewriting rule trait (compatibility layer)
///
/// A simplified interface provided for simple rewriting rules that do not require the full context of the optimizer.
/// These rules always generate better or equivalent plans, without the need for cost calculations.
///
/// For rules that require access to the optimizer’s status or cost information, please use the `RewriteRule` trait.
pub trait HeuristicRule: std::fmt::Debug + Send + Sync {
/// Rule Name
fn name(&self) -> &'static str;
/// Check whether it matches the current planned node.
fn matches(&self, node: &PlanNodeEnum) -> bool;
/// Apply the rule for rewriting the text.
///
/// # Parameters
/// `ctx`: Rewrite the context.
/// `node`: The current planned node
///
/// # Return
/// - `Ok(Some(node))`: rewrite succeeded, returns the new node
/// - `Ok(None)`: no match, keeps the original node
/// - `Err(e)`: rewrite failed
fn apply(
&self,
ctx: &mut RewriteContext,
node: &PlanNodeEnum,
) -> RewriteResult<Option<PlanNodeEnum>>;
}
/// Create a wrapper that adapts `HeuristicRule` to `RewriteRule`.
#[derive(Debug)]
pub struct HeuristicRuleAdapter<T: HeuristicRule> {
inner: T,
}
impl<T: HeuristicRule> HeuristicRuleAdapter<T> {
pub fn new(rule: T) -> Self {
Self { inner: rule }
}
pub fn into_inner(self) -> T {
self.inner
}
}
impl<T: HeuristicRule> RewriteRule for HeuristicRuleAdapter<T> {
fn name(&self) -> &'static str {
self.inner.name()
}
fn pattern(&self) -> Pattern {
// Heuristic rules use wildcard patterns, and precise matching is performed by the matches method.
Pattern::new()
}
fn apply(
&self,
ctx: &mut RewriteContext,
node: &PlanNodeEnum,
) -> RewriteResult<Option<TransformResult>> {
match self.inner.apply(ctx, node)? {
Some(new_node) => {
let mut result = TransformResult::new();
result.add_new_node(new_node);
Ok(Some(result))
}
None => Ok(None),
}
}
fn matches(&self, node: &PlanNodeEnum) -> bool {
self.inner.matches(node)
}
}
/// Provide a trait for the adapter constructor of HeuristicRule
pub trait IntoOptRule: HeuristicRule + Sized {
fn into_opt_rule(self) -> HeuristicRuleAdapter<Self> {
HeuristicRuleAdapter::new(self)
}
}
impl<T: HeuristicRule + Sized> IntoOptRule for T {}
#[cfg(test)]
mod tests {
use super::*;
use crate::planning::plan::core::nodes::access::graph_scan_node::ScanVerticesNode;
#[derive(Debug)]
struct TestHeuristicRule;
impl HeuristicRule for TestHeuristicRule {
fn name(&self) -> &'static str {
"TestHeuristicRule"
}
fn matches(&self, node: &PlanNodeEnum) -> bool {
node.is_scan_vertices()
}
fn apply(
&self,
_ctx: &mut RewriteContext,
node: &PlanNodeEnum,
) -> RewriteResult<Option<PlanNodeEnum>> {
if self.matches(node) {
// Return to the original node (the actual rule would involve more complex logic.)
Ok(Some(node.clone()))
} else {
Ok(None)
}
}
}
#[test]
fn test_heuristic_rule_adapter() {
let rule = TestHeuristicRule;
let adapter = rule.into_opt_rule();
assert_eq!(adapter.name(), "TestHeuristicRule");
let node = PlanNodeEnum::ScanVertices(ScanVerticesNode::new(1, "default"));
assert!(adapter.matches(&node));
}
#[test]
fn test_heuristic_rule_apply() {
let rule = TestHeuristicRule;
let mut ctx = RewriteContext::new();
let node = PlanNodeEnum::ScanVertices(ScanVerticesNode::new(1, "default"));
let result = rule
.apply(&mut ctx, &node)
.expect("Failed to apply rewrite rule");
assert!(result.is_some());
}
}