1use crate::metadata::RirMeta;
4use crate::rir::RirNode;
5use xlog_core::RelId;
6
7#[derive(Debug, Clone)]
9pub struct Scc {
10 pub id: u32,
12 pub predicates: Vec<String>,
14 pub is_recursive: bool,
16}
17
18#[derive(Debug, Clone)]
20pub struct Stratum {
21 pub id: u32,
23 pub sccs: Vec<u32>,
25}
26
27#[derive(Debug, Clone)]
29pub struct CompiledRule {
30 pub head: String,
32 pub body: RirNode,
34 pub meta: RirMeta,
36}
37
38#[derive(Debug, Clone)]
40pub struct ExecutionPlan {
41 pub sccs: Vec<Scc>,
43 pub strata: Vec<Stratum>,
45 pub rules_by_scc: Vec<Vec<CompiledRule>>,
47 pub est_memory_peak: u64,
49 pub rel_arities: std::collections::HashMap<RelId, usize>,
54}
55
56impl ExecutionPlan {
57 pub fn new(sccs: Vec<Scc>) -> Self {
59 Self {
60 sccs,
61 strata: vec![],
62 rules_by_scc: vec![],
63 est_memory_peak: 0,
64 rel_arities: std::collections::HashMap::new(),
65 }
66 }
67
68 pub fn with_strata(mut self, strata: Vec<Stratum>) -> Self {
70 self.strata = strata;
71 self
72 }
73
74 pub fn recursive_scc_count(&self) -> usize {
76 self.sccs.iter().filter(|s| s.is_recursive).count()
77 }
78
79 pub fn has_recursion(&self) -> bool {
81 self.sccs.iter().any(|s| s.is_recursive)
82 }
83}
84
85#[derive(Debug, Default)]
87pub struct PlanBuilder {
88 sccs: Vec<Scc>,
89 strata: Vec<Stratum>,
90 rules: Vec<Vec<CompiledRule>>,
91}
92
93impl PlanBuilder {
94 pub fn new() -> Self {
96 Self::default()
97 }
98
99 pub fn add_scc(&mut self, scc: Scc) -> &mut Self {
101 self.sccs.push(scc);
102 self.rules.push(vec![]);
103 self
104 }
105
106 pub fn add_rule(&mut self, scc_id: u32, rule: CompiledRule) -> &mut Self {
108 if let Some(rules) = self.rules.get_mut(scc_id as usize) {
109 rules.push(rule);
110 }
111 self
112 }
113
114 pub fn add_stratum(&mut self, stratum: Stratum) -> &mut Self {
116 self.strata.push(stratum);
117 self
118 }
119
120 pub fn build(self) -> ExecutionPlan {
122 ExecutionPlan {
123 sccs: self.sccs,
124 strata: self.strata,
125 rules_by_scc: self.rules,
126 est_memory_peak: 0,
127 rel_arities: std::collections::HashMap::new(),
128 }
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn test_scc_ordering() {
138 let sccs = vec![
139 Scc {
140 id: 0,
141 predicates: vec!["edge".into()],
142 is_recursive: false,
143 },
144 Scc {
145 id: 1,
146 predicates: vec!["reach".into()],
147 is_recursive: true,
148 },
149 ];
150 let plan = ExecutionPlan::new(sccs);
151 assert_eq!(plan.sccs.len(), 2);
152 assert!(!plan.sccs[0].is_recursive);
153 assert!(plan.sccs[1].is_recursive);
154 }
155
156 #[test]
157 fn test_stratum_assignment() {
158 let strata = [
159 Stratum {
160 id: 0,
161 sccs: vec![0, 1],
162 },
163 Stratum {
164 id: 1,
165 sccs: vec![2],
166 },
167 ];
168 assert_eq!(strata[0].sccs.len(), 2);
169 }
170
171 #[test]
172 fn test_plan_builder() {
173 let mut builder = PlanBuilder::new();
174 builder.add_scc(Scc {
175 id: 0,
176 predicates: vec!["p".into()],
177 is_recursive: false,
178 });
179 builder.add_stratum(Stratum {
180 id: 0,
181 sccs: vec![0],
182 });
183 let plan = builder.build();
184 assert_eq!(plan.sccs.len(), 1);
185 assert_eq!(plan.strata.len(), 1);
186 }
187
188 #[test]
189 fn test_has_recursion() {
190 let non_recursive = ExecutionPlan::new(vec![Scc {
191 id: 0,
192 predicates: vec!["p".into()],
193 is_recursive: false,
194 }]);
195 assert!(!non_recursive.has_recursion());
196
197 let recursive = ExecutionPlan::new(vec![Scc {
198 id: 0,
199 predicates: vec!["reach".into()],
200 is_recursive: true,
201 }]);
202 assert!(recursive.has_recursion());
203 }
204}