1use super::binding::Var;
13use super::op::*;
14
15pub trait OpVisitor {
17 fn visit_bgp(&mut self, _op: &OpBGP) {}
19
20 fn visit_triple(&mut self, _op: &OpTriple) {}
22
23 fn visit_join(&mut self, _op: &OpJoin) {}
25
26 fn visit_left_join(&mut self, _op: &OpLeftJoin) {}
28
29 fn visit_filter(&mut self, _op: &OpFilter) {}
31
32 fn visit_union(&mut self, _op: &OpUnion) {}
34
35 fn visit_project(&mut self, _op: &OpProject) {}
37
38 fn visit_distinct(&mut self, _op: &OpDistinct) {}
40
41 fn visit_reduced(&mut self, _op: &OpReduced) {}
43
44 fn visit_slice(&mut self, _op: &OpSlice) {}
46
47 fn visit_order(&mut self, _op: &OpOrder) {}
49
50 fn visit_group(&mut self, _op: &OpGroup) {}
52
53 fn visit_extend(&mut self, _op: &OpExtend) {}
55
56 fn visit_minus(&mut self, _op: &OpMinus) {}
58
59 fn visit_right_join(&mut self, _op: &OpRightJoin) {}
61
62 fn visit_cross_join(&mut self, _op: &OpCrossJoin) {}
64
65 fn visit_intersect(&mut self, _op: &OpIntersect) {}
67
68 fn visit_table(&mut self, _op: &OpTable) {}
70
71 fn visit_sequence(&mut self, _op: &OpSequence) {}
73
74 fn visit_disjunction(&mut self, _op: &OpDisjunction) {}
76
77 fn visit_null(&mut self, _op: &OpNull) {}
79}
80
81pub fn walk_op<V: OpVisitor>(visitor: &mut V, op: &Op) {
83 match op {
84 Op::BGP(o) => {
85 visitor.visit_bgp(o);
86 }
87 Op::Triple(o) => {
88 visitor.visit_triple(o);
89 }
90 Op::Join(o) => {
91 walk_op(visitor, &o.left);
92 walk_op(visitor, &o.right);
93 visitor.visit_join(o);
94 }
95 Op::LeftJoin(o) => {
96 walk_op(visitor, &o.left);
97 walk_op(visitor, &o.right);
98 visitor.visit_left_join(o);
99 }
100 Op::Filter(o) => {
101 walk_op(visitor, &o.sub_op);
102 visitor.visit_filter(o);
103 }
104 Op::Union(o) => {
105 walk_op(visitor, &o.left);
106 walk_op(visitor, &o.right);
107 visitor.visit_union(o);
108 }
109 Op::Project(o) => {
110 walk_op(visitor, &o.sub_op);
111 visitor.visit_project(o);
112 }
113 Op::Distinct(o) => {
114 walk_op(visitor, &o.sub_op);
115 visitor.visit_distinct(o);
116 }
117 Op::Reduced(o) => {
118 walk_op(visitor, &o.sub_op);
119 visitor.visit_reduced(o);
120 }
121 Op::Slice(o) => {
122 walk_op(visitor, &o.sub_op);
123 visitor.visit_slice(o);
124 }
125 Op::Order(o) => {
126 walk_op(visitor, &o.sub_op);
127 visitor.visit_order(o);
128 }
129 Op::Group(o) => {
130 walk_op(visitor, &o.sub_op);
131 visitor.visit_group(o);
132 }
133 Op::Extend(o) => {
134 walk_op(visitor, &o.sub_op);
135 visitor.visit_extend(o);
136 }
137 Op::Minus(o) => {
138 walk_op(visitor, &o.left);
139 walk_op(visitor, &o.right);
140 visitor.visit_minus(o);
141 }
142 Op::RightJoin(o) => {
143 walk_op(visitor, &o.left);
144 walk_op(visitor, &o.right);
145 visitor.visit_right_join(o);
146 }
147 Op::CrossJoin(o) => {
148 walk_op(visitor, &o.left);
149 walk_op(visitor, &o.right);
150 visitor.visit_cross_join(o);
151 }
152 Op::Intersect(o) => {
153 walk_op(visitor, &o.left);
154 walk_op(visitor, &o.right);
155 visitor.visit_intersect(o);
156 }
157 Op::Table(o) => {
158 visitor.visit_table(o);
159 }
160 Op::Sequence(o) => {
161 for sub in &o.ops {
162 walk_op(visitor, sub);
163 }
164 visitor.visit_sequence(o);
165 }
166 Op::Disjunction(o) => {
167 for sub in &o.ops {
168 walk_op(visitor, sub);
169 }
170 visitor.visit_disjunction(o);
171 }
172 Op::Null(o) => {
173 visitor.visit_null(o);
174 }
175 }
176}
177
178pub trait OpTransform {
180 fn transform_bgp(&mut self, op: OpBGP) -> Op {
182 Op::BGP(op)
183 }
184
185 fn transform_triple(&mut self, op: OpTriple) -> Op {
187 Op::Triple(op)
188 }
189
190 fn transform_join(&mut self, left: Op, right: Op, _original: &OpJoin) -> Op {
192 Op::Join(OpJoin::new(left, right))
193 }
194
195 fn transform_left_join(&mut self, left: Op, right: Op, original: &OpLeftJoin) -> Op {
197 Op::LeftJoin(OpLeftJoin {
198 left: Box::new(left),
199 right: Box::new(right),
200 filter: original.filter.clone(),
201 })
202 }
203
204 fn transform_right_join(&mut self, left: Op, right: Op, original: &OpRightJoin) -> Op {
206 Op::RightJoin(OpRightJoin {
207 left: Box::new(left),
208 right: Box::new(right),
209 filter: original.filter.clone(),
210 })
211 }
212
213 fn transform_cross_join(&mut self, left: Op, right: Op, _original: &OpCrossJoin) -> Op {
215 Op::CrossJoin(OpCrossJoin {
216 left: Box::new(left),
217 right: Box::new(right),
218 })
219 }
220
221 fn transform_intersect(&mut self, left: Op, right: Op, _original: &OpIntersect) -> Op {
223 Op::Intersect(OpIntersect {
224 left: Box::new(left),
225 right: Box::new(right),
226 })
227 }
228
229 fn transform_filter(&mut self, sub_op: Op, original: &OpFilter) -> Op {
231 Op::Filter(OpFilter {
232 filter: original.filter.clone(),
233 sub_op: Box::new(sub_op),
234 })
235 }
236
237 fn transform_union(&mut self, left: Op, right: Op, _original: &OpUnion) -> Op {
239 Op::Union(OpUnion::new(left, right))
240 }
241
242 fn transform_project(&mut self, sub_op: Op, original: &OpProject) -> Op {
244 Op::Project(OpProject {
245 vars: original.vars.clone(),
246 sub_op: Box::new(sub_op),
247 })
248 }
249
250 fn transform_distinct(&mut self, sub_op: Op, _original: &OpDistinct) -> Op {
252 Op::Distinct(OpDistinct::new(sub_op))
253 }
254
255 fn transform_reduced(&mut self, sub_op: Op, _original: &OpReduced) -> Op {
257 Op::Reduced(OpReduced::new(sub_op))
258 }
259
260 fn transform_slice(&mut self, sub_op: Op, original: &OpSlice) -> Op {
262 Op::Slice(OpSlice::new(sub_op, original.offset, original.limit))
263 }
264
265 fn transform_order(&mut self, sub_op: Op, original: &OpOrder) -> Op {
267 Op::Order(OpOrder {
268 sub_op: Box::new(sub_op),
269 keys: original.keys.clone(),
270 })
271 }
272
273 fn transform_group(&mut self, sub_op: Op, original: &OpGroup) -> Op {
275 Op::Group(OpGroup {
276 sub_op: Box::new(sub_op),
277 group_vars: original.group_vars.clone(),
278 aggregates: original.aggregates.clone(),
279 })
280 }
281
282 fn transform_extend(&mut self, sub_op: Op, original: &OpExtend) -> Op {
284 Op::Extend(OpExtend {
285 sub_op: Box::new(sub_op),
286 var: original.var.clone(),
287 expr: original.expr.clone(),
288 })
289 }
290
291 fn transform_minus(&mut self, left: Op, right: Op, _original: &OpMinus) -> Op {
293 Op::Minus(OpMinus::new(left, right))
294 }
295
296 fn transform_table(&mut self, op: OpTable) -> Op {
298 Op::Table(op)
299 }
300
301 fn transform_sequence(&mut self, ops: Vec<Op>, _original: &OpSequence) -> Op {
303 Op::Sequence(OpSequence::new(ops))
304 }
305
306 fn transform_disjunction(&mut self, ops: Vec<Op>, _original: &OpDisjunction) -> Op {
308 Op::Disjunction(OpDisjunction::new(ops))
309 }
310
311 fn transform_null(&mut self, op: OpNull) -> Op {
313 Op::Null(op)
314 }
315}
316
317pub fn transform_op<T: OpTransform>(transformer: &mut T, op: Op) -> Op {
319 match op {
320 Op::BGP(o) => transformer.transform_bgp(o),
321 Op::Triple(o) => transformer.transform_triple(o),
322 Op::Join(o) => {
323 let orig = o.clone();
324 let left = transform_op(transformer, *o.left);
325 let right = transform_op(transformer, *o.right);
326 transformer.transform_join(left, right, &orig)
327 }
328 Op::LeftJoin(o) => {
329 let orig = o.clone();
330 let left = transform_op(transformer, *o.left);
331 let right = transform_op(transformer, *o.right);
332 transformer.transform_left_join(left, right, &orig)
333 }
334 Op::Filter(o) => {
335 let orig = o.clone();
336 let sub_op = transform_op(transformer, *o.sub_op);
337 transformer.transform_filter(sub_op, &orig)
338 }
339 Op::Union(o) => {
340 let orig = o.clone();
341 let left = transform_op(transformer, *o.left);
342 let right = transform_op(transformer, *o.right);
343 transformer.transform_union(left, right, &orig)
344 }
345 Op::Project(o) => {
346 let orig = o.clone();
347 let sub_op = transform_op(transformer, *o.sub_op);
348 transformer.transform_project(sub_op, &orig)
349 }
350 Op::Distinct(o) => {
351 let orig = o.clone();
352 let sub_op = transform_op(transformer, *o.sub_op);
353 transformer.transform_distinct(sub_op, &orig)
354 }
355 Op::Reduced(o) => {
356 let orig = o.clone();
357 let sub_op = transform_op(transformer, *o.sub_op);
358 transformer.transform_reduced(sub_op, &orig)
359 }
360 Op::Slice(o) => {
361 let orig = o.clone();
362 let sub_op = transform_op(transformer, *o.sub_op);
363 transformer.transform_slice(sub_op, &orig)
364 }
365 Op::Order(o) => {
366 let orig = o.clone();
367 let sub_op = transform_op(transformer, *o.sub_op);
368 transformer.transform_order(sub_op, &orig)
369 }
370 Op::Group(o) => {
371 let orig = o.clone();
372 let sub_op = transform_op(transformer, *o.sub_op);
373 transformer.transform_group(sub_op, &orig)
374 }
375 Op::Extend(o) => {
376 let orig = o.clone();
377 let sub_op = transform_op(transformer, *o.sub_op);
378 transformer.transform_extend(sub_op, &orig)
379 }
380 Op::Minus(o) => {
381 let orig = o.clone();
382 let left = transform_op(transformer, *o.left);
383 let right = transform_op(transformer, *o.right);
384 transformer.transform_minus(left, right, &orig)
385 }
386 Op::RightJoin(o) => {
387 let orig = o.clone();
388 let left = transform_op(transformer, *o.left);
389 let right = transform_op(transformer, *o.right);
390 transformer.transform_right_join(left, right, &orig)
391 }
392 Op::CrossJoin(o) => {
393 let orig = o.clone();
394 let left = transform_op(transformer, *o.left);
395 let right = transform_op(transformer, *o.right);
396 transformer.transform_cross_join(left, right, &orig)
397 }
398 Op::Intersect(o) => {
399 let orig = o.clone();
400 let left = transform_op(transformer, *o.left);
401 let right = transform_op(transformer, *o.right);
402 transformer.transform_intersect(left, right, &orig)
403 }
404 Op::Table(o) => transformer.transform_table(o),
405 Op::Sequence(o) => {
406 let ops: Vec<Op> = o
407 .ops
408 .into_iter()
409 .map(|sub| transform_op(transformer, sub))
410 .collect();
411 transformer.transform_sequence(ops, &OpSequence::new(Vec::new()))
412 }
413 Op::Disjunction(o) => {
414 let ops: Vec<Op> = o
415 .ops
416 .into_iter()
417 .map(|sub| transform_op(transformer, sub))
418 .collect();
419 transformer.transform_disjunction(ops, &OpDisjunction::new(Vec::new()))
420 }
421 Op::Null(o) => transformer.transform_null(o),
422 }
423}
424
425#[derive(Debug, Default)]
427pub struct TransformCopy;
428
429impl TransformCopy {
430 pub fn new() -> Self {
432 Self
433 }
434}
435
436impl OpTransform for TransformCopy {}
437
438#[derive(Debug, Default)]
443pub struct TransformPushFilter {
444 pending_filters: Vec<FilterExpr>,
446}
447
448impl TransformPushFilter {
449 pub fn new() -> Self {
451 Self {
452 pending_filters: Vec::new(),
453 }
454 }
455
456 fn filter_uses_only(&self, filter: &FilterExpr, vars: &[Var]) -> bool {
458 let filter_vars = self.extract_filter_vars(filter);
459 filter_vars.iter().all(|v| vars.contains(v))
460 }
461
462 fn extract_filter_vars(&self, filter: &FilterExpr) -> Vec<Var> {
464 let mut vars = Vec::new();
465 Self::collect_filter_vars(filter, &mut vars);
466 vars
467 }
468
469 fn collect_filter_vars(filter: &FilterExpr, vars: &mut Vec<Var>) {
470 match filter {
471 FilterExpr::Eq(l, r)
472 | FilterExpr::NotEq(l, r)
473 | FilterExpr::Lt(l, r)
474 | FilterExpr::LtEq(l, r)
475 | FilterExpr::Gt(l, r)
476 | FilterExpr::GtEq(l, r) => {
477 Self::collect_term_vars(l, vars);
478 Self::collect_term_vars(r, vars);
479 }
480 FilterExpr::And(l, r) | FilterExpr::Or(l, r) => {
481 Self::collect_filter_vars(l, vars);
482 Self::collect_filter_vars(r, vars);
483 }
484 FilterExpr::Not(e) => {
485 Self::collect_filter_vars(e, vars);
486 }
487 FilterExpr::Bound(v) => {
488 if !vars.contains(v) {
489 vars.push(v.clone());
490 }
491 }
492 FilterExpr::Regex(t, _, _)
493 | FilterExpr::StartsWith(t, _)
494 | FilterExpr::EndsWith(t, _)
495 | FilterExpr::Contains(t, _)
496 | FilterExpr::IsUri(t)
497 | FilterExpr::IsLiteral(t)
498 | FilterExpr::IsBlank(t) => {
499 Self::collect_term_vars(t, vars);
500 }
501 FilterExpr::In(t, list) | FilterExpr::NotIn(t, list) => {
502 Self::collect_term_vars(t, vars);
503 for item in list {
504 Self::collect_term_vars(item, vars);
505 }
506 }
507 FilterExpr::True | FilterExpr::False => {}
508 }
509 }
510
511 fn collect_term_vars(term: &ExprTerm, vars: &mut Vec<Var>) {
512 match term {
513 ExprTerm::Var(v) => {
514 if !vars.contains(v) {
515 vars.push(v.clone());
516 }
517 }
518 ExprTerm::Str(inner)
519 | ExprTerm::LCase(inner)
520 | ExprTerm::UCase(inner)
521 | ExprTerm::StrLen(inner) => {
522 Self::collect_term_vars(inner, vars);
523 }
524 ExprTerm::Concat(terms) => {
525 for t in terms {
526 Self::collect_term_vars(t, vars);
527 }
528 }
529 ExprTerm::Const(_) => {}
530 }
531 }
532
533 fn apply_pending_filters(&mut self, op: Op) -> Op {
535 if self.pending_filters.is_empty() {
536 return op;
537 }
538
539 let op_vars = op.vars();
540 let mut applicable = Vec::new();
541 let mut remaining = Vec::new();
542
543 let filters: Vec<_> = self.pending_filters.drain(..).collect();
545 for filter in filters {
546 if self.filter_uses_only(&filter, &op_vars) {
547 applicable.push(filter);
548 } else {
549 remaining.push(filter);
550 }
551 }
552
553 self.pending_filters = remaining;
554
555 let mut result = op;
556 for filter in applicable {
557 result = Op::Filter(OpFilter::new(filter, result));
558 }
559 result
560 }
561}
562
563impl OpTransform for TransformPushFilter {
564 fn transform_filter(&mut self, sub_op: Op, original: &OpFilter) -> Op {
565 self.pending_filters.push(original.filter.clone());
567 self.apply_pending_filters(sub_op)
568 }
569
570 fn transform_join(&mut self, left: Op, right: Op, _original: &OpJoin) -> Op {
571 let left = self.apply_pending_filters(left);
573 let right = self.apply_pending_filters(right);
574
575 let result = Op::Join(OpJoin::new(left, right));
576 self.apply_pending_filters(result)
577 }
578
579 fn transform_bgp(&mut self, op: OpBGP) -> Op {
580 self.apply_pending_filters(Op::BGP(op))
581 }
582
583 fn transform_triple(&mut self, op: OpTriple) -> Op {
584 self.apply_pending_filters(Op::Triple(op))
585 }
586
587 fn transform_table(&mut self, op: OpTable) -> Op {
588 self.apply_pending_filters(Op::Table(op))
589 }
590}
591
592#[derive(Debug, Default)]
594pub struct VarCollector {
595 pub vars: Vec<Var>,
596}
597
598impl VarCollector {
599 pub fn new() -> Self {
601 Self { vars: Vec::new() }
602 }
603
604 pub fn collect(op: &Op) -> Vec<Var> {
606 let mut collector = Self::new();
607 walk_op(&mut collector, op);
608 collector.vars
609 }
610
611 fn add_var(&mut self, var: &Var) {
612 if !self.vars.contains(var) {
613 self.vars.push(var.clone());
614 }
615 }
616}
617
618impl OpVisitor for VarCollector {
619 fn visit_bgp(&mut self, op: &OpBGP) {
620 for triple in &op.triples {
621 for v in triple.vars() {
622 self.add_var(&v);
623 }
624 }
625 }
626
627 fn visit_triple(&mut self, op: &OpTriple) {
628 for v in op.triple.vars() {
629 self.add_var(&v);
630 }
631 }
632
633 fn visit_project(&mut self, op: &OpProject) {
634 for v in &op.vars {
635 self.add_var(v);
636 }
637 }
638
639 fn visit_group(&mut self, op: &OpGroup) {
640 for v in &op.group_vars {
641 self.add_var(v);
642 }
643 for (v, _) in &op.aggregates {
644 self.add_var(v);
645 }
646 }
647
648 fn visit_extend(&mut self, op: &OpExtend) {
649 self.add_var(&op.var);
650 }
651
652 fn visit_table(&mut self, op: &OpTable) {
653 for v in &op.vars {
654 self.add_var(v);
655 }
656 }
657}
658
659#[derive(Debug)]
661pub struct OpPrinter {
662 indent: usize,
663 output: String,
664}
665
666impl OpPrinter {
667 pub fn new() -> Self {
669 Self {
670 indent: 0,
671 output: String::new(),
672 }
673 }
674
675 pub fn print(op: &Op) -> String {
677 let mut printer = Self::new();
678 printer.print_op(op);
679 printer.output
680 }
681
682 fn print_op(&mut self, op: &Op) {
683 let indent_str = " ".repeat(self.indent);
684
685 match op {
686 Op::BGP(o) => {
687 self.output.push_str(&format!("{}BGP\n", indent_str));
688 self.indent += 1;
689 for triple in &o.triples {
690 self.output
691 .push_str(&format!("{} {}\n", indent_str, triple));
692 }
693 self.indent -= 1;
694 }
695 Op::Triple(o) => {
696 self.output
697 .push_str(&format!("{}Triple({})\n", indent_str, o.triple));
698 }
699 Op::Join(o) => {
700 self.output.push_str(&format!("{}Join\n", indent_str));
701 self.indent += 1;
702 self.print_op(&o.left);
703 self.print_op(&o.right);
704 self.indent -= 1;
705 }
706 Op::LeftJoin(o) => {
707 self.output.push_str(&format!("{}LeftJoin\n", indent_str));
708 self.indent += 1;
709 self.print_op(&o.left);
710 self.print_op(&o.right);
711 self.indent -= 1;
712 }
713 Op::Filter(o) => {
714 self.output.push_str(&format!("{}Filter\n", indent_str));
715 self.indent += 1;
716 self.print_op(&o.sub_op);
717 self.indent -= 1;
718 }
719 Op::Union(o) => {
720 self.output.push_str(&format!("{}Union\n", indent_str));
721 self.indent += 1;
722 self.print_op(&o.left);
723 self.print_op(&o.right);
724 self.indent -= 1;
725 }
726 Op::Project(o) => {
727 let vars: Vec<String> = o.vars.iter().map(|v| format!("{}", v)).collect();
728 self.output
729 .push_str(&format!("{}Project({})\n", indent_str, vars.join(", ")));
730 self.indent += 1;
731 self.print_op(&o.sub_op);
732 self.indent -= 1;
733 }
734 Op::Distinct(o) => {
735 self.output.push_str(&format!("{}Distinct\n", indent_str));
736 self.indent += 1;
737 self.print_op(&o.sub_op);
738 self.indent -= 1;
739 }
740 Op::Reduced(o) => {
741 self.output.push_str(&format!("{}Reduced\n", indent_str));
742 self.indent += 1;
743 self.print_op(&o.sub_op);
744 self.indent -= 1;
745 }
746 Op::Slice(o) => {
747 self.output.push_str(&format!(
748 "{}Slice(offset={}, limit={:?})\n",
749 indent_str, o.offset, o.limit
750 ));
751 self.indent += 1;
752 self.print_op(&o.sub_op);
753 self.indent -= 1;
754 }
755 Op::Order(o) => {
756 self.output.push_str(&format!("{}Order\n", indent_str));
757 self.indent += 1;
758 self.print_op(&o.sub_op);
759 self.indent -= 1;
760 }
761 Op::Group(o) => {
762 let vars: Vec<String> = o.group_vars.iter().map(|v| format!("{}", v)).collect();
763 self.output
764 .push_str(&format!("{}Group({})\n", indent_str, vars.join(", ")));
765 self.indent += 1;
766 self.print_op(&o.sub_op);
767 self.indent -= 1;
768 }
769 Op::Extend(o) => {
770 self.output
771 .push_str(&format!("{}Extend({})\n", indent_str, o.var));
772 self.indent += 1;
773 self.print_op(&o.sub_op);
774 self.indent -= 1;
775 }
776 Op::Minus(o) => {
777 self.output.push_str(&format!("{}Minus\n", indent_str));
778 self.indent += 1;
779 self.print_op(&o.left);
780 self.print_op(&o.right);
781 self.indent -= 1;
782 }
783 Op::RightJoin(o) => {
784 self.output.push_str(&format!("{}RightJoin\n", indent_str));
785 self.indent += 1;
786 self.print_op(&o.left);
787 self.print_op(&o.right);
788 self.indent -= 1;
789 }
790 Op::CrossJoin(o) => {
791 self.output.push_str(&format!("{}CrossJoin\n", indent_str));
792 self.indent += 1;
793 self.print_op(&o.left);
794 self.print_op(&o.right);
795 self.indent -= 1;
796 }
797 Op::Intersect(o) => {
798 self.output.push_str(&format!("{}Intersect\n", indent_str));
799 self.indent += 1;
800 self.print_op(&o.left);
801 self.print_op(&o.right);
802 self.indent -= 1;
803 }
804 Op::Table(o) => {
805 self.output.push_str(&format!(
806 "{}Table({} vars, {} rows)\n",
807 indent_str,
808 o.vars.len(),
809 o.rows.len()
810 ));
811 }
812 Op::Sequence(o) => {
813 self.output.push_str(&format!("{}Sequence\n", indent_str));
814 self.indent += 1;
815 for sub in &o.ops {
816 self.print_op(sub);
817 }
818 self.indent -= 1;
819 }
820 Op::Disjunction(o) => {
821 self.output
822 .push_str(&format!("{}Disjunction\n", indent_str));
823 self.indent += 1;
824 for sub in &o.ops {
825 self.print_op(sub);
826 }
827 self.indent -= 1;
828 }
829 Op::Null(_) => {
830 self.output.push_str(&format!("{}Null\n", indent_str));
831 }
832 }
833 }
834}
835
836impl Default for OpPrinter {
837 fn default() -> Self {
838 Self::new()
839 }
840}
841
842#[derive(Debug, Default)]
844pub struct OpStats {
845 pub bgp_count: usize,
846 pub triple_count: usize,
847 pub join_count: usize,
848 pub filter_count: usize,
849 pub union_count: usize,
850 pub total_ops: usize,
851}
852
853impl OpStats {
854 pub fn new() -> Self {
856 Self::default()
857 }
858
859 pub fn collect(op: &Op) -> Self {
861 let mut stats = Self::new();
862 walk_op(&mut stats, op);
863 stats
864 }
865}
866
867impl OpVisitor for OpStats {
868 fn visit_bgp(&mut self, op: &OpBGP) {
869 self.bgp_count += 1;
870 self.triple_count += op.triples.len();
871 self.total_ops += 1;
872 }
873
874 fn visit_triple(&mut self, _op: &OpTriple) {
875 self.triple_count += 1;
876 self.total_ops += 1;
877 }
878
879 fn visit_join(&mut self, _op: &OpJoin) {
880 self.join_count += 1;
881 self.total_ops += 1;
882 }
883
884 fn visit_filter(&mut self, _op: &OpFilter) {
885 self.filter_count += 1;
886 self.total_ops += 1;
887 }
888
889 fn visit_union(&mut self, _op: &OpUnion) {
890 self.union_count += 1;
891 self.total_ops += 1;
892 }
893
894 fn visit_left_join(&mut self, _op: &OpLeftJoin) {
895 self.total_ops += 1;
896 }
897
898 fn visit_project(&mut self, _op: &OpProject) {
899 self.total_ops += 1;
900 }
901
902 fn visit_distinct(&mut self, _op: &OpDistinct) {
903 self.total_ops += 1;
904 }
905
906 fn visit_reduced(&mut self, _op: &OpReduced) {
907 self.total_ops += 1;
908 }
909
910 fn visit_slice(&mut self, _op: &OpSlice) {
911 self.total_ops += 1;
912 }
913
914 fn visit_order(&mut self, _op: &OpOrder) {
915 self.total_ops += 1;
916 }
917
918 fn visit_group(&mut self, _op: &OpGroup) {
919 self.total_ops += 1;
920 }
921
922 fn visit_extend(&mut self, _op: &OpExtend) {
923 self.total_ops += 1;
924 }
925
926 fn visit_minus(&mut self, _op: &OpMinus) {
927 self.total_ops += 1;
928 }
929
930 fn visit_table(&mut self, _op: &OpTable) {
931 self.total_ops += 1;
932 }
933
934 fn visit_sequence(&mut self, _op: &OpSequence) {
935 self.total_ops += 1;
936 }
937
938 fn visit_disjunction(&mut self, _op: &OpDisjunction) {
939 self.total_ops += 1;
940 }
941
942 fn visit_null(&mut self, _op: &OpNull) {
943 self.total_ops += 1;
944 }
945}
946
947#[cfg(test)]
948mod tests {
949 use super::super::binding::Value;
950 use super::*;
951
952 fn make_bgp() -> OpBGP {
953 let mut bgp = OpBGP::new();
954 bgp.add(Triple::new(
955 Pattern::Var(Var::new("s")),
956 Pattern::Uri("knows".to_string()),
957 Pattern::Var(Var::new("o")),
958 ));
959 bgp
960 }
961
962 #[test]
963 fn test_walk_op() {
964 let bgp = make_bgp();
965 let filter = Op::Filter(OpFilter::new(FilterExpr::True, Op::BGP(bgp)));
966
967 let mut stats = OpStats::new();
968 walk_op(&mut stats, &filter);
969
970 assert_eq!(stats.bgp_count, 1);
971 assert_eq!(stats.filter_count, 1);
972 assert_eq!(stats.total_ops, 2);
973 }
974
975 #[test]
976 fn test_transform_copy() {
977 let bgp = make_bgp();
978 let op = Op::BGP(bgp);
979
980 let mut copy = TransformCopy::new();
981 let result = transform_op(&mut copy, op.clone());
982
983 assert!(matches!(result, Op::BGP(_)));
985 }
986
987 #[test]
988 fn test_var_collector() {
989 let bgp = make_bgp();
990 let op = Op::BGP(bgp);
991
992 let vars = VarCollector::collect(&op);
993 assert_eq!(vars.len(), 2);
994 assert!(vars.contains(&Var::new("s")));
995 assert!(vars.contains(&Var::new("o")));
996 }
997
998 #[test]
999 fn test_op_printer() {
1000 let bgp = make_bgp();
1001 let filter = Op::Filter(OpFilter::new(
1002 FilterExpr::True,
1003 Op::Project(OpProject::new(vec![Var::new("s")], Op::BGP(bgp))),
1004 ));
1005
1006 let output = OpPrinter::print(&filter);
1007 assert!(output.contains("Filter"));
1008 assert!(output.contains("Project"));
1009 assert!(output.contains("BGP"));
1010 }
1011
1012 #[test]
1013 fn test_op_stats() {
1014 let bgp1 = make_bgp();
1015 let bgp2 = make_bgp();
1016
1017 let join = Op::Join(OpJoin::new(Op::BGP(bgp1), Op::BGP(bgp2)));
1018
1019 let stats = OpStats::collect(&join);
1020 assert_eq!(stats.bgp_count, 2);
1021 assert_eq!(stats.join_count, 1);
1022 assert_eq!(stats.triple_count, 2);
1023 }
1024
1025 #[test]
1026 fn test_push_filter_transform() {
1027 let mut bgp1 = OpBGP::new();
1030 bgp1.add(Triple::new(
1031 Pattern::Var(Var::new("s")),
1032 Pattern::Uri("name".to_string()),
1033 Pattern::Var(Var::new("name")),
1034 ));
1035
1036 let mut bgp2 = OpBGP::new();
1037 bgp2.add(Triple::new(
1038 Pattern::Var(Var::new("s")),
1039 Pattern::Uri("age".to_string()),
1040 Pattern::Var(Var::new("age")),
1041 ));
1042
1043 let filter = FilterExpr::Gt(
1045 ExprTerm::Var(Var::new("age")),
1046 ExprTerm::Const(Value::Integer(18)),
1047 );
1048
1049 let op = Op::Filter(OpFilter::new(
1050 filter,
1051 Op::Join(OpJoin::new(Op::BGP(bgp1), Op::BGP(bgp2))),
1052 ));
1053
1054 let mut transform = TransformPushFilter::new();
1055 let result = transform_op(&mut transform, op);
1056
1057 let stats = OpStats::collect(&result);
1059 assert_eq!(stats.filter_count, 1);
1060 }
1061}