1use std::collections::BTreeMap;
18use std::str::FromStr;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
26pub struct PhysicalOperatorId(usize);
27
28impl PhysicalOperatorId {
29 pub const fn from_ordinal(ordinal: usize) -> Self {
31 Self(ordinal)
32 }
33
34 pub const fn ordinal(self) -> usize {
36 self.0
37 }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub enum PhysicalPlanKind {
42 CreateNodeLabel,
43 CreateRelationshipType,
44 CreateNodeTable,
45 CreateRelationshipTable,
46 CreateProperty,
47 AlterTableState,
48 AlterPropertyState,
49 CreateIndex,
50 CreateCompositeIndex,
51 CreateRangeIndex,
52 CreateFullTextIndex,
53 CreateUniqueConstraint,
54 CreateNodePropertyExistsConstraint,
55 CreateRelationshipUniqueConstraint,
56 CreateRelationshipPropertyExistsConstraint,
57 ProjectGraph,
58 GraphAlgorithm,
59 VectorSeedScan,
60 CreateNode,
61 UnwindMutationExec,
62 MergeNode,
63 MergeRelationship,
64 MergeMatchedRelationship,
65 MergeRelationshipFromMatchedRelationship,
66 MergeRelationshipToMatchedTarget,
67 MergeRelationshipFromMatchedTarget,
68 CreateMatchedRelationship,
69 SetNodeProperty,
70 SetNodeProperties,
71 SetNodePropertiesReturn,
72 SetRelationshipProperty,
73 SetRelationshipProperties,
74 DeleteNode,
75 DeleteRelationship,
76 DeleteRelationshipTargetNodes,
77 CreateRelationship,
78 GraphMatchExec,
79 EmptyExec,
80 SeqNodeScan,
81 NodeProjectionScanExec,
82 SourceSegmentScan,
83 NodeCartesianProductExec,
84 HashJoinExec,
85 NodeColumnLookupExec,
86 IndexNodeSeek,
87 IndexNodeMultiSeek,
88 IndexNodeUnionSeek,
89 IndexNodeCompositeSeek,
90 IndexNodeCompositeRangeSeek,
91 IndexNodeRangeSeek,
92 IndexNodeTextSeek,
93 AdjacencyExpandExec,
94 AdjacencyExistsExec,
95 OptionalDegreeExec,
96 OptionalRelationshipCountSumExec,
97 NodeCountExec,
98 RelationshipCountExec,
99 ThreadRepairStatsExec,
100 ShortestPathExec,
101 FilterExec,
102 ProjectExec,
103 AggregateExec,
104 DistinctExec,
105 SortExec,
106 TopNExec,
107 LimitExec,
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
111pub enum PhysicalPlanClass {
112 Schema,
113 Mutation,
114 Access,
115 Traversal,
116 Relational,
117 Procedure,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq)]
121pub enum PlanChildren<'a, P> {
122 None,
123 Unary(&'a P),
124 Binary(&'a P, &'a P),
125}
126
127pub trait PhysicalPlanNode {
128 fn kind(&self) -> PhysicalPlanKind;
129
130 fn children(&self) -> PlanChildren<'_, Self>
131 where
132 Self: Sized;
133
134 fn class(&self) -> PhysicalPlanClass {
135 self.kind().class()
136 }
137}
138
139impl PhysicalPlanKind {
140 pub fn all() -> &'static [Self] {
141 const ALL: &[PhysicalPlanKind] = &[
142 PhysicalPlanKind::CreateNodeLabel,
143 PhysicalPlanKind::CreateRelationshipType,
144 PhysicalPlanKind::CreateNodeTable,
145 PhysicalPlanKind::CreateRelationshipTable,
146 PhysicalPlanKind::CreateProperty,
147 PhysicalPlanKind::AlterTableState,
148 PhysicalPlanKind::AlterPropertyState,
149 PhysicalPlanKind::CreateIndex,
150 PhysicalPlanKind::CreateCompositeIndex,
151 PhysicalPlanKind::CreateRangeIndex,
152 PhysicalPlanKind::CreateFullTextIndex,
153 PhysicalPlanKind::CreateUniqueConstraint,
154 PhysicalPlanKind::CreateNodePropertyExistsConstraint,
155 PhysicalPlanKind::CreateRelationshipUniqueConstraint,
156 PhysicalPlanKind::CreateRelationshipPropertyExistsConstraint,
157 PhysicalPlanKind::ProjectGraph,
158 PhysicalPlanKind::GraphAlgorithm,
159 PhysicalPlanKind::VectorSeedScan,
160 PhysicalPlanKind::CreateNode,
161 PhysicalPlanKind::UnwindMutationExec,
162 PhysicalPlanKind::MergeNode,
163 PhysicalPlanKind::MergeRelationship,
164 PhysicalPlanKind::MergeMatchedRelationship,
165 PhysicalPlanKind::MergeRelationshipFromMatchedRelationship,
166 PhysicalPlanKind::MergeRelationshipToMatchedTarget,
167 PhysicalPlanKind::MergeRelationshipFromMatchedTarget,
168 PhysicalPlanKind::CreateMatchedRelationship,
169 PhysicalPlanKind::SetNodeProperty,
170 PhysicalPlanKind::SetNodeProperties,
171 PhysicalPlanKind::SetNodePropertiesReturn,
172 PhysicalPlanKind::SetRelationshipProperty,
173 PhysicalPlanKind::SetRelationshipProperties,
174 PhysicalPlanKind::DeleteNode,
175 PhysicalPlanKind::DeleteRelationship,
176 PhysicalPlanKind::DeleteRelationshipTargetNodes,
177 PhysicalPlanKind::CreateRelationship,
178 PhysicalPlanKind::GraphMatchExec,
179 PhysicalPlanKind::EmptyExec,
180 PhysicalPlanKind::SeqNodeScan,
181 PhysicalPlanKind::NodeProjectionScanExec,
182 PhysicalPlanKind::SourceSegmentScan,
183 PhysicalPlanKind::NodeCartesianProductExec,
184 PhysicalPlanKind::HashJoinExec,
185 PhysicalPlanKind::NodeColumnLookupExec,
186 PhysicalPlanKind::IndexNodeSeek,
187 PhysicalPlanKind::IndexNodeMultiSeek,
188 PhysicalPlanKind::IndexNodeUnionSeek,
189 PhysicalPlanKind::IndexNodeCompositeSeek,
190 PhysicalPlanKind::IndexNodeCompositeRangeSeek,
191 PhysicalPlanKind::IndexNodeRangeSeek,
192 PhysicalPlanKind::IndexNodeTextSeek,
193 PhysicalPlanKind::AdjacencyExpandExec,
194 PhysicalPlanKind::AdjacencyExistsExec,
195 PhysicalPlanKind::OptionalDegreeExec,
196 PhysicalPlanKind::OptionalRelationshipCountSumExec,
197 PhysicalPlanKind::NodeCountExec,
198 PhysicalPlanKind::RelationshipCountExec,
199 PhysicalPlanKind::ThreadRepairStatsExec,
200 PhysicalPlanKind::ShortestPathExec,
201 PhysicalPlanKind::FilterExec,
202 PhysicalPlanKind::ProjectExec,
203 PhysicalPlanKind::AggregateExec,
204 PhysicalPlanKind::DistinctExec,
205 PhysicalPlanKind::SortExec,
206 PhysicalPlanKind::TopNExec,
207 PhysicalPlanKind::LimitExec,
208 ];
209 ALL
210 }
211
212 pub fn as_str(self) -> &'static str {
213 match self {
214 PhysicalPlanKind::CreateNodeLabel => "CreateNodeLabel",
215 PhysicalPlanKind::CreateRelationshipType => "CreateRelationshipType",
216 PhysicalPlanKind::CreateNodeTable => "CreateNodeTable",
217 PhysicalPlanKind::CreateRelationshipTable => "CreateRelationshipTable",
218 PhysicalPlanKind::CreateProperty => "CreateProperty",
219 PhysicalPlanKind::AlterTableState => "AlterTableState",
220 PhysicalPlanKind::AlterPropertyState => "AlterPropertyState",
221 PhysicalPlanKind::CreateIndex => "CreateIndex",
222 PhysicalPlanKind::CreateCompositeIndex => "CreateCompositeIndex",
223 PhysicalPlanKind::CreateRangeIndex => "CreateRangeIndex",
224 PhysicalPlanKind::CreateFullTextIndex => "CreateFullTextIndex",
225 PhysicalPlanKind::CreateUniqueConstraint => "CreateUniqueConstraint",
226 PhysicalPlanKind::CreateNodePropertyExistsConstraint => {
227 "CreateNodePropertyExistsConstraint"
228 }
229 PhysicalPlanKind::CreateRelationshipUniqueConstraint => {
230 "CreateRelationshipUniqueConstraint"
231 }
232 PhysicalPlanKind::CreateRelationshipPropertyExistsConstraint => {
233 "CreateRelationshipPropertyExistsConstraint"
234 }
235 PhysicalPlanKind::ProjectGraph => "ProjectGraph",
236 PhysicalPlanKind::GraphAlgorithm => "GraphAlgorithm",
237 PhysicalPlanKind::VectorSeedScan => "VectorSeedScan",
238 PhysicalPlanKind::CreateNode => "CreateNode",
239 PhysicalPlanKind::UnwindMutationExec => "UnwindMutationExec",
240 PhysicalPlanKind::MergeNode => "MergeNode",
241 PhysicalPlanKind::MergeRelationship => "MergeRelationship",
242 PhysicalPlanKind::MergeMatchedRelationship => "MergeMatchedRelationship",
243 PhysicalPlanKind::MergeRelationshipFromMatchedRelationship => {
244 "MergeRelationshipFromMatchedRelationship"
245 }
246 PhysicalPlanKind::MergeRelationshipToMatchedTarget => {
247 "MergeRelationshipToMatchedTarget"
248 }
249 PhysicalPlanKind::MergeRelationshipFromMatchedTarget => {
250 "MergeRelationshipFromMatchedTarget"
251 }
252 PhysicalPlanKind::CreateMatchedRelationship => "CreateMatchedRelationship",
253 PhysicalPlanKind::SetNodeProperty => "SetNodeProperty",
254 PhysicalPlanKind::SetNodeProperties => "SetNodeProperties",
255 PhysicalPlanKind::SetNodePropertiesReturn => "SetNodePropertiesReturn",
256 PhysicalPlanKind::SetRelationshipProperty => "SetRelationshipProperty",
257 PhysicalPlanKind::SetRelationshipProperties => "SetRelationshipProperties",
258 PhysicalPlanKind::DeleteNode => "DeleteNode",
259 PhysicalPlanKind::DeleteRelationship => "DeleteRelationship",
260 PhysicalPlanKind::DeleteRelationshipTargetNodes => "DeleteRelationshipTargetNodes",
261 PhysicalPlanKind::CreateRelationship => "CreateRelationship",
262 PhysicalPlanKind::GraphMatchExec => "GraphMatchExec",
263 PhysicalPlanKind::EmptyExec => "EmptyExec",
264 PhysicalPlanKind::SeqNodeScan => "SeqNodeScan",
265 PhysicalPlanKind::NodeProjectionScanExec => "NodeProjectionScanExec",
266 PhysicalPlanKind::SourceSegmentScan => "SourceSegmentScan",
267 PhysicalPlanKind::HashJoinExec => "HashJoinExec",
268 PhysicalPlanKind::NodeCartesianProductExec => "NodeCartesianProductExec",
269 PhysicalPlanKind::NodeColumnLookupExec => "NodeColumnLookupExec",
270 PhysicalPlanKind::IndexNodeSeek => "IndexNodeSeek",
271 PhysicalPlanKind::IndexNodeMultiSeek => "IndexNodeMultiSeek",
272 PhysicalPlanKind::IndexNodeUnionSeek => "IndexNodeUnionSeek",
273 PhysicalPlanKind::IndexNodeCompositeSeek => "IndexNodeCompositeSeek",
274 PhysicalPlanKind::IndexNodeCompositeRangeSeek => "IndexNodeCompositeRangeSeek",
275 PhysicalPlanKind::IndexNodeRangeSeek => "IndexNodeRangeSeek",
276 PhysicalPlanKind::IndexNodeTextSeek => "IndexNodeTextSeek",
277 PhysicalPlanKind::AdjacencyExpandExec => "AdjacencyExpandExec",
278 PhysicalPlanKind::AdjacencyExistsExec => "AdjacencyExistsExec",
279 PhysicalPlanKind::OptionalDegreeExec => "OptionalDegreeExec",
280 PhysicalPlanKind::OptionalRelationshipCountSumExec => {
281 "OptionalRelationshipCountSumExec"
282 }
283 PhysicalPlanKind::NodeCountExec => "NodeCountExec",
284 PhysicalPlanKind::RelationshipCountExec => "RelationshipCountExec",
285 PhysicalPlanKind::ThreadRepairStatsExec => "ThreadRepairStatsExec",
286 PhysicalPlanKind::ShortestPathExec => "ShortestPathExec",
287 PhysicalPlanKind::FilterExec => "FilterExec",
288 PhysicalPlanKind::ProjectExec => "ProjectExec",
289 PhysicalPlanKind::AggregateExec => "AggregateExec",
290 PhysicalPlanKind::DistinctExec => "DistinctExec",
291 PhysicalPlanKind::SortExec => "SortExec",
292 PhysicalPlanKind::TopNExec => "TopNExec",
293 PhysicalPlanKind::LimitExec => "LimitExec",
294 }
295 }
296
297 pub fn class(self) -> PhysicalPlanClass {
298 match self {
299 PhysicalPlanKind::CreateNodeLabel
300 | PhysicalPlanKind::CreateRelationshipType
301 | PhysicalPlanKind::CreateNodeTable
302 | PhysicalPlanKind::CreateRelationshipTable
303 | PhysicalPlanKind::CreateProperty
304 | PhysicalPlanKind::AlterTableState
305 | PhysicalPlanKind::AlterPropertyState
306 | PhysicalPlanKind::CreateIndex
307 | PhysicalPlanKind::CreateCompositeIndex
308 | PhysicalPlanKind::CreateRangeIndex
309 | PhysicalPlanKind::CreateFullTextIndex
310 | PhysicalPlanKind::CreateUniqueConstraint
311 | PhysicalPlanKind::CreateNodePropertyExistsConstraint
312 | PhysicalPlanKind::CreateRelationshipUniqueConstraint
313 | PhysicalPlanKind::CreateRelationshipPropertyExistsConstraint => {
314 PhysicalPlanClass::Schema
315 }
316 PhysicalPlanKind::CreateNode
317 | PhysicalPlanKind::UnwindMutationExec
318 | PhysicalPlanKind::MergeNode
319 | PhysicalPlanKind::MergeRelationship
320 | PhysicalPlanKind::MergeMatchedRelationship
321 | PhysicalPlanKind::MergeRelationshipFromMatchedRelationship
322 | PhysicalPlanKind::MergeRelationshipToMatchedTarget
323 | PhysicalPlanKind::MergeRelationshipFromMatchedTarget
324 | PhysicalPlanKind::CreateMatchedRelationship
325 | PhysicalPlanKind::SetNodeProperty
326 | PhysicalPlanKind::SetNodeProperties
327 | PhysicalPlanKind::SetNodePropertiesReturn
328 | PhysicalPlanKind::SetRelationshipProperty
329 | PhysicalPlanKind::SetRelationshipProperties
330 | PhysicalPlanKind::DeleteNode
331 | PhysicalPlanKind::DeleteRelationship
332 | PhysicalPlanKind::DeleteRelationshipTargetNodes
333 | PhysicalPlanKind::CreateRelationship => PhysicalPlanClass::Mutation,
334 PhysicalPlanKind::SeqNodeScan
335 | PhysicalPlanKind::NodeProjectionScanExec
336 | PhysicalPlanKind::SourceSegmentScan
337 | PhysicalPlanKind::NodeColumnLookupExec
338 | PhysicalPlanKind::IndexNodeSeek
339 | PhysicalPlanKind::IndexNodeMultiSeek
340 | PhysicalPlanKind::IndexNodeUnionSeek
341 | PhysicalPlanKind::IndexNodeCompositeSeek
342 | PhysicalPlanKind::IndexNodeCompositeRangeSeek
343 | PhysicalPlanKind::IndexNodeRangeSeek
344 | PhysicalPlanKind::IndexNodeTextSeek => PhysicalPlanClass::Access,
345 PhysicalPlanKind::GraphMatchExec
346 | PhysicalPlanKind::AdjacencyExpandExec
347 | PhysicalPlanKind::AdjacencyExistsExec
348 | PhysicalPlanKind::OptionalDegreeExec
349 | PhysicalPlanKind::OptionalRelationshipCountSumExec
350 | PhysicalPlanKind::ShortestPathExec => PhysicalPlanClass::Traversal,
351 PhysicalPlanKind::NodeCountExec
352 | PhysicalPlanKind::RelationshipCountExec
353 | PhysicalPlanKind::EmptyExec
354 | PhysicalPlanKind::NodeCartesianProductExec
355 | PhysicalPlanKind::HashJoinExec
356 | PhysicalPlanKind::FilterExec
357 | PhysicalPlanKind::ProjectExec
358 | PhysicalPlanKind::AggregateExec
359 | PhysicalPlanKind::DistinctExec
360 | PhysicalPlanKind::SortExec
361 | PhysicalPlanKind::TopNExec
362 | PhysicalPlanKind::LimitExec => PhysicalPlanClass::Relational,
363 PhysicalPlanKind::ProjectGraph
364 | PhysicalPlanKind::GraphAlgorithm
365 | PhysicalPlanKind::VectorSeedScan
366 | PhysicalPlanKind::ThreadRepairStatsExec => PhysicalPlanClass::Procedure,
367 }
368 }
369}
370
371impl PhysicalPlanClass {
372 pub fn all() -> &'static [Self] {
373 const ALL: &[PhysicalPlanClass] = &[
374 PhysicalPlanClass::Schema,
375 PhysicalPlanClass::Mutation,
376 PhysicalPlanClass::Access,
377 PhysicalPlanClass::Traversal,
378 PhysicalPlanClass::Relational,
379 PhysicalPlanClass::Procedure,
380 ];
381 ALL
382 }
383
384 pub fn as_str(self) -> &'static str {
385 match self {
386 PhysicalPlanClass::Schema => "schema",
387 PhysicalPlanClass::Mutation => "mutation",
388 PhysicalPlanClass::Access => "access",
389 PhysicalPlanClass::Traversal => "traversal",
390 PhysicalPlanClass::Relational => "relational",
391 PhysicalPlanClass::Procedure => "procedure",
392 }
393 }
394}
395
396impl FromStr for PhysicalPlanKind {
397 type Err = &'static str;
398
399 fn from_str(value: &str) -> Result<Self, Self::Err> {
400 Self::all()
401 .iter()
402 .copied()
403 .find(|kind| kind.as_str() == value)
404 .ok_or("unknown physical plan kind")
405 }
406}
407
408impl FromStr for PhysicalPlanClass {
409 type Err = &'static str;
410
411 fn from_str(value: &str) -> Result<Self, Self::Err> {
412 Self::all()
413 .iter()
414 .copied()
415 .find(|class| class.as_str() == value)
416 .ok_or("unknown physical plan class")
417 }
418}
419
420impl<'a, P> PlanChildren<'a, P> {
421 pub fn len(self) -> usize {
422 match self {
423 PlanChildren::None => 0,
424 PlanChildren::Unary(_) => 1,
425 PlanChildren::Binary(_, _) => 2,
426 }
427 }
428
429 pub fn is_empty(self) -> bool {
430 self.len() == 0
431 }
432}
433
434pub fn plan_operator_counts<P>(plan: &P) -> BTreeMap<String, usize>
435where
436 P: PhysicalPlanNode,
437{
438 let mut counts = BTreeMap::new();
439 visit_plan(plan, &mut |node| {
440 *counts.entry(node.kind().as_str().to_string()).or_default() += 1;
441 });
442 counts
443}
444
445pub fn plan_class_counts<P>(plan: &P) -> BTreeMap<String, usize>
446where
447 P: PhysicalPlanNode,
448{
449 let mut counts = BTreeMap::new();
450 visit_plan(plan, &mut |node| {
451 *counts.entry(node.class().as_str().to_string()).or_default() += 1;
452 });
453 counts
454}
455
456pub fn visit_plan<P>(plan: &P, visitor: &mut impl FnMut(&P))
457where
458 P: PhysicalPlanNode,
459{
460 visitor(plan);
461 match plan.children() {
462 PlanChildren::None => {}
463 PlanChildren::Unary(input) => visit_plan(input, visitor),
464 PlanChildren::Binary(left, right) => {
465 visit_plan(left, visitor);
466 visit_plan(right, visitor);
467 }
468 }
469}
470
471pub fn visit_plan_with_ids<P>(plan: &P, visitor: &mut impl FnMut(PhysicalOperatorId, &P))
473where
474 P: PhysicalPlanNode,
475{
476 fn visit<P>(
477 plan: &P,
478 next_ordinal: &mut usize,
479 visitor: &mut impl FnMut(PhysicalOperatorId, &P),
480 ) where
481 P: PhysicalPlanNode,
482 {
483 let operator_id = PhysicalOperatorId::from_ordinal(*next_ordinal);
484 *next_ordinal = next_ordinal
485 .checked_add(1)
486 .expect("physical plan operator count exceeds usize");
487 visitor(operator_id, plan);
488 match plan.children() {
489 PlanChildren::None => {}
490 PlanChildren::Unary(input) => visit(input, next_ordinal, visitor),
491 PlanChildren::Binary(left, right) => {
492 visit(left, next_ordinal, visitor);
493 visit(right, next_ordinal, visitor);
494 }
495 }
496 }
497
498 let mut next_ordinal = 0;
499 visit(plan, &mut next_ordinal, visitor);
500}
501
502#[cfg(test)]
503mod tests {
504 use super::{
505 plan_class_counts, plan_operator_counts, visit_plan, visit_plan_with_ids,
506 PhysicalPlanClass, PhysicalPlanKind, PhysicalPlanNode, PlanChildren,
507 };
508
509 #[test]
510 fn physical_plan_kind_exposes_stable_strings_and_classes() {
511 assert_eq!(PhysicalPlanKind::IndexNodeSeek.as_str(), "IndexNodeSeek");
512 assert_eq!(
513 PhysicalPlanKind::IndexNodeSeek.class(),
514 PhysicalPlanClass::Access
515 );
516 assert_eq!(
517 PhysicalPlanKind::AdjacencyExpandExec.class(),
518 PhysicalPlanClass::Traversal
519 );
520 assert_eq!(
521 PhysicalPlanKind::ProjectExec.class(),
522 PhysicalPlanClass::Relational
523 );
524 }
525
526 #[test]
527 fn physical_plan_kind_strings_round_trip_for_diagnostics() {
528 for kind in PhysicalPlanKind::all() {
529 assert_eq!(kind.as_str().parse::<PhysicalPlanKind>(), Ok(*kind));
530 }
531 assert!("UnknownExec".parse::<PhysicalPlanKind>().is_err());
532 }
533
534 #[test]
535 fn physical_plan_class_exposes_stable_strings() {
536 assert_eq!(PhysicalPlanClass::Schema.as_str(), "schema");
537 assert_eq!(PhysicalPlanClass::Mutation.as_str(), "mutation");
538 assert_eq!(PhysicalPlanClass::Access.as_str(), "access");
539 assert_eq!(PhysicalPlanClass::Traversal.as_str(), "traversal");
540 assert_eq!(PhysicalPlanClass::Relational.as_str(), "relational");
541 assert_eq!(PhysicalPlanClass::Procedure.as_str(), "procedure");
542 }
543
544 #[test]
545 fn physical_plan_class_strings_round_trip_for_diagnostics() {
546 for class in PhysicalPlanClass::all() {
547 assert_eq!(class.as_str().parse::<PhysicalPlanClass>(), Ok(*class));
548 }
549 assert!("unknown".parse::<PhysicalPlanClass>().is_err());
550 }
551
552 #[test]
553 fn plan_children_reports_arity_without_knowing_plan_type() {
554 assert_eq!(PlanChildren::<&str>::None.len(), 0);
555 assert!(PlanChildren::<&str>::None.is_empty());
556
557 let child = "scan";
558 assert_eq!(PlanChildren::Unary(&child).len(), 1);
559
560 let left = "left";
561 let right = "right";
562 assert_eq!(PlanChildren::Binary(&left, &right).len(), 2);
563 }
564
565 #[derive(Debug)]
566 enum TestPlan {
567 Seek,
568 Project(Box<TestPlan>),
569 Join(Box<TestPlan>, Box<TestPlan>),
570 }
571
572 impl PhysicalPlanNode for TestPlan {
573 fn kind(&self) -> PhysicalPlanKind {
574 match self {
575 TestPlan::Seek => PhysicalPlanKind::IndexNodeSeek,
576 TestPlan::Project(_) => PhysicalPlanKind::ProjectExec,
577 TestPlan::Join(_, _) => PhysicalPlanKind::NodeCartesianProductExec,
578 }
579 }
580
581 fn children(&self) -> PlanChildren<'_, Self> {
582 match self {
583 TestPlan::Seek => PlanChildren::None,
584 TestPlan::Project(input) => PlanChildren::Unary(input),
585 TestPlan::Join(left, right) => PlanChildren::Binary(left, right),
586 }
587 }
588 }
589
590 #[test]
591 fn plan_node_helpers_traverse_without_knowing_plan_payloads() {
592 let plan = TestPlan::Project(Box::new(TestPlan::Join(
593 Box::new(TestPlan::Seek),
594 Box::new(TestPlan::Seek),
595 )));
596
597 let mut visit_order = Vec::new();
598 visit_plan(&plan, &mut |node| {
599 visit_order.push(node.kind().as_str());
600 });
601
602 assert_eq!(
603 visit_order,
604 vec![
605 "ProjectExec",
606 "NodeCartesianProductExec",
607 "IndexNodeSeek",
608 "IndexNodeSeek"
609 ]
610 );
611 assert_eq!(plan_operator_counts(&plan)["IndexNodeSeek"], 2);
612 assert_eq!(plan_operator_counts(&plan)["ProjectExec"], 1);
613 assert_eq!(plan_class_counts(&plan)["access"], 2);
614 assert_eq!(plan_class_counts(&plan)["relational"], 2);
615
616 let mut identified = Vec::new();
617 visit_plan_with_ids(&plan, &mut |operator_id, node| {
618 identified.push((operator_id.ordinal(), node.kind().as_str()));
619 });
620 assert_eq!(
621 identified,
622 vec![
623 (0, "ProjectExec"),
624 (1, "NodeCartesianProductExec"),
625 (2, "IndexNodeSeek"),
626 (3, "IndexNodeSeek"),
627 ]
628 );
629 }
630}