1use oxirs_core::model::{
7 BlankNode as CoreBlankNode, Literal as CoreLiteral, NamedNode, Object, Predicate, Subject,
8};
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11use std::fmt;
12
13pub use oxirs_core::model::Variable;
15
16pub type Iri = NamedNode;
18
19#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
21pub struct Literal {
22 pub value: String,
23 pub language: Option<String>,
24 pub datatype: Option<NamedNode>,
25}
26
27impl fmt::Display for Literal {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 write!(f, "\"{}\"", self.value)?;
30 if let Some(lang) = &self.language {
31 write!(f, "@{lang}")?;
32 } else if let Some(dt) = &self.datatype {
33 write!(f, "^^{dt}")?;
34 }
35 Ok(())
36 }
37}
38
39impl Literal {
40 pub fn with_language(value: String, language: String) -> Self {
42 Self {
43 value,
44 language: Some(language),
45 datatype: None,
46 }
47 }
48}
49
50impl From<CoreLiteral> for Literal {
51 fn from(core_literal: CoreLiteral) -> Self {
52 let (value, datatype, language) = core_literal.destruct();
53 Self {
54 value,
55 language,
56 datatype,
57 }
58 }
59}
60
61impl From<Literal> for CoreLiteral {
62 fn from(literal: Literal) -> Self {
63 if let Some(lang) = literal.language {
64 CoreLiteral::new_language_tagged_literal(&literal.value, lang)
65 .unwrap_or_else(|_| CoreLiteral::new_simple_literal(literal.value))
66 } else if let Some(datatype) = literal.datatype {
67 CoreLiteral::new_typed_literal(literal.value, datatype)
68 } else {
69 CoreLiteral::new_simple_literal(literal.value)
70 }
71 }
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
76pub enum Term {
77 Variable(Variable),
78 Iri(NamedNode),
79 Literal(Literal),
80 BlankNode(String),
81 QuotedTriple(Box<TriplePattern>),
82 PropertyPath(PropertyPath),
83}
84
85impl fmt::Display for Term {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 match self {
88 Term::Variable(v) => write!(f, "?{v}"),
89 Term::Iri(iri) => write!(f, "{iri}"),
90 Term::Literal(lit) => write!(f, "{lit}"),
91 Term::BlankNode(id) => write!(f, "_:{id}"),
92 Term::QuotedTriple(triple) => write!(
93 f,
94 "<<{} {} {}>>",
95 triple.subject, triple.predicate, triple.object
96 ),
97 Term::PropertyPath(path) => write!(f, "{path}"),
98 }
99 }
100}
101
102impl From<Subject> for Term {
103 fn from(subject: Subject) -> Self {
104 match subject {
105 Subject::NamedNode(n) => Term::Iri(n),
106 Subject::BlankNode(b) => Term::BlankNode(b.id().to_string()),
107 Subject::Variable(v) => Term::Variable(v),
108 Subject::QuotedTriple(quoted_triple) => {
109 Term::QuotedTriple(Box::new(TriplePattern {
111 subject: Term::from(quoted_triple.subject().clone()),
112 predicate: Term::from(quoted_triple.predicate().clone()),
113 object: Term::from(quoted_triple.object().clone()),
114 }))
115 }
116 }
117 }
118}
119
120impl From<Predicate> for Term {
121 fn from(predicate: Predicate) -> Self {
122 match predicate {
123 Predicate::NamedNode(n) => Term::Iri(n),
124 Predicate::Variable(v) => Term::Variable(v),
125 }
126 }
127}
128
129impl From<Object> for Term {
130 fn from(object: Object) -> Self {
131 match object {
132 Object::NamedNode(n) => Term::Iri(n),
133 Object::BlankNode(b) => Term::BlankNode(b.id().to_string()),
134 Object::Literal(l) => Term::Literal(l.into()),
135 Object::Variable(v) => Term::Variable(v),
136 Object::QuotedTriple(quoted_triple) => {
137 Term::QuotedTriple(Box::new(TriplePattern {
139 subject: Term::from(quoted_triple.subject().clone()),
140 predicate: Term::from(quoted_triple.predicate().clone()),
141 object: Term::from(quoted_triple.object().clone()),
142 }))
143 }
144 }
145 }
146}
147
148impl From<NamedNode> for Term {
149 fn from(node: NamedNode) -> Self {
150 Term::Iri(node)
151 }
152}
153
154impl From<CoreBlankNode> for Term {
155 fn from(node: CoreBlankNode) -> Self {
156 Term::BlankNode(node.id().to_string())
157 }
158}
159
160impl From<CoreLiteral> for Term {
161 fn from(literal: CoreLiteral) -> Self {
162 Term::Literal(literal.into())
163 }
164}
165
166impl From<Variable> for Term {
167 fn from(variable: Variable) -> Self {
168 Term::Variable(variable)
169 }
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
174pub struct TriplePattern {
175 pub subject: Term,
176 pub predicate: Term,
177 pub object: Term,
178}
179
180pub type Triple = TriplePattern;
182
183impl fmt::Display for TriplePattern {
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 write!(f, "{} {} {}", self.subject, self.predicate, self.object)
186 }
187}
188
189#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
191pub enum Expression {
192 Variable(Variable),
194 Literal(Literal),
196 Iri(Iri),
198 Function { name: String, args: Vec<Expression> },
200 Binary {
202 op: BinaryOperator,
203 left: Box<Expression>,
204 right: Box<Expression>,
205 },
206 Unary {
208 op: UnaryOperator,
209 operand: Box<Expression>,
210 },
211 Conditional {
213 condition: Box<Expression>,
214 then_expr: Box<Expression>,
215 else_expr: Box<Expression>,
216 },
217 Bound(Variable),
219 Exists(Box<Algebra>),
221 NotExists(Box<Algebra>),
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
227pub enum BinaryOperator {
228 Add,
229 Subtract,
230 Multiply,
231 Divide,
232 Equal,
233 NotEqual,
234 Less,
235 LessEqual,
236 Greater,
237 GreaterEqual,
238 And,
239 Or,
240 SameTerm,
241 In,
242 NotIn,
243}
244
245impl std::fmt::Display for BinaryOperator {
246 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
247 match self {
248 BinaryOperator::Add => write!(f, "+"),
249 BinaryOperator::Subtract => write!(f, "-"),
250 BinaryOperator::Multiply => write!(f, "*"),
251 BinaryOperator::Divide => write!(f, "/"),
252 BinaryOperator::Equal => write!(f, "="),
253 BinaryOperator::NotEqual => write!(f, "!="),
254 BinaryOperator::Less => write!(f, "<"),
255 BinaryOperator::LessEqual => write!(f, "<="),
256 BinaryOperator::Greater => write!(f, ">"),
257 BinaryOperator::GreaterEqual => write!(f, ">="),
258 BinaryOperator::And => write!(f, "&&"),
259 BinaryOperator::Or => write!(f, "||"),
260 BinaryOperator::SameTerm => write!(f, "sameTerm"),
261 BinaryOperator::In => write!(f, "IN"),
262 BinaryOperator::NotIn => write!(f, "NOT IN"),
263 }
264 }
265}
266
267#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
269pub enum UnaryOperator {
270 Not,
271 Plus,
272 Minus,
273 IsIri,
274 IsBlank,
275 IsLiteral,
276 IsNumeric,
277}
278
279#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
281pub enum Aggregate {
282 Count {
283 distinct: bool,
284 expr: Option<Expression>,
285 },
286 Sum {
287 distinct: bool,
288 expr: Expression,
289 },
290 Min {
291 distinct: bool,
292 expr: Expression,
293 },
294 Max {
295 distinct: bool,
296 expr: Expression,
297 },
298 Avg {
299 distinct: bool,
300 expr: Expression,
301 },
302 Sample {
303 distinct: bool,
304 expr: Expression,
305 },
306 GroupConcat {
307 distinct: bool,
308 expr: Expression,
309 separator: Option<String>,
310 },
311}
312
313pub fn aggregate_function_name(name: &str) -> Option<&'static str> {
325 match name.trim_start_matches(':').to_ascii_uppercase().as_str() {
326 "COUNT" => Some("COUNT"),
327 "SUM" => Some("SUM"),
328 "MIN" => Some("MIN"),
329 "MAX" => Some("MAX"),
330 "AVG" => Some("AVG"),
331 "SAMPLE" => Some("SAMPLE"),
332 "GROUP_CONCAT" => Some("GROUP_CONCAT"),
333 _ => None,
334 }
335}
336
337pub fn check_aggregate_arity(name: &str, arg_count: usize) -> Result<(), String> {
346 let Some(canonical) = aggregate_function_name(name) else {
347 return Ok(());
348 };
349 match canonical {
350 "COUNT" => {
351 if arg_count > 1 {
352 return Err("COUNT in HAVING expects at most one argument".to_string());
353 }
354 }
355 other => {
356 if arg_count != 1 {
357 return Err(format!("{other} in HAVING expects exactly one argument"));
358 }
359 }
360 }
361 Ok(())
362}
363
364pub type Binding = HashMap<Variable, Term>;
366
367pub type Solution = Vec<Binding>;
369
370#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
372pub struct OrderCondition {
373 pub expr: Expression,
374 pub ascending: bool,
375}
376
377#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
379pub struct GroupCondition {
380 pub expr: Expression,
381 pub alias: Option<Variable>,
382}
383
384#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
386pub enum PropertyPath {
387 Iri(Iri),
389 Variable(Variable),
391 Inverse(Box<PropertyPath>),
393 Sequence(Box<PropertyPath>, Box<PropertyPath>),
395 Alternative(Box<PropertyPath>, Box<PropertyPath>),
397 ZeroOrMore(Box<PropertyPath>),
399 OneOrMore(Box<PropertyPath>),
401 ZeroOrOne(Box<PropertyPath>),
403 NegatedPropertySet(Vec<PropertyPath>),
405}
406
407#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
409pub struct PropertyPathPattern {
410 pub subject: Term,
411 pub path: PropertyPath,
412 pub object: Term,
413}
414
415#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
417pub enum Algebra {
418 Bgp(Vec<TriplePattern>),
420
421 PropertyPath {
423 subject: Term,
424 path: PropertyPath,
425 object: Term,
426 },
427
428 Join {
430 left: Box<Algebra>,
431 right: Box<Algebra>,
432 },
433
434 LeftJoin {
436 left: Box<Algebra>,
437 right: Box<Algebra>,
438 filter: Option<Expression>,
439 },
440
441 Union {
443 left: Box<Algebra>,
444 right: Box<Algebra>,
445 },
446
447 Filter {
449 pattern: Box<Algebra>,
450 condition: Expression,
451 },
452
453 Extend {
455 pattern: Box<Algebra>,
456 variable: Variable,
457 expr: Expression,
458 },
459
460 Minus {
462 left: Box<Algebra>,
463 right: Box<Algebra>,
464 },
465
466 Service {
468 endpoint: Term,
469 pattern: Box<Algebra>,
470 silent: bool,
471 },
472
473 Graph { graph: Term, pattern: Box<Algebra> },
475
476 Project {
478 pattern: Box<Algebra>,
479 variables: Vec<Variable>,
480 },
481
482 Distinct { pattern: Box<Algebra> },
484
485 Reduced { pattern: Box<Algebra> },
487
488 Slice {
490 pattern: Box<Algebra>,
491 offset: Option<usize>,
492 limit: Option<usize>,
493 },
494
495 OrderBy {
497 pattern: Box<Algebra>,
498 conditions: Vec<OrderCondition>,
499 },
500
501 Group {
503 pattern: Box<Algebra>,
504 variables: Vec<GroupCondition>,
505 aggregates: Vec<(Variable, Aggregate)>,
506 },
507
508 Having {
510 pattern: Box<Algebra>,
511 condition: Expression,
512 },
513
514 Values {
516 variables: Vec<Variable>,
517 bindings: Vec<Binding>,
518 },
519
520 Table,
522
523 Zero,
525
526 Empty,
528}
529
530#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
532pub enum JoinAlgorithm {
533 #[default]
534 HashJoin,
535 SortMergeJoin,
536 NestedLoopJoin,
537 IndexNestedLoopJoin,
538 BindJoin,
539}
540
541#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
543pub enum FilterPlacement {
544 Early, Late, #[default]
547 Optimal, }
549
550#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
552pub struct ServiceCapabilities {
553 pub supports_projection: bool,
554 pub supports_filtering: bool,
555 pub supports_ordering: bool,
556 pub supports_aggregation: bool,
557 pub max_query_size: Option<usize>,
558}
559
560#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
562pub enum ProjectionType {
563 #[default]
564 Standard,
565 Streaming,
566 Cached,
567}
568
569#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
571pub enum SortAlgorithm {
572 #[default]
573 QuickSort,
574 MergeSort,
575 HeapSort,
576 ExternalSort,
577}
578
579#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
581pub enum GroupingAlgorithm {
582 #[default]
583 HashGrouping,
584 SortGrouping,
585 StreamingGrouping,
586}
587
588#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
590pub enum MaterializationStrategy {
591 InMemory,
592 Disk,
593 #[default]
594 Adaptive,
595}
596
597#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
599pub enum ParallelismType {
600 #[default]
601 DataParallel,
602 PipelineParallel,
603 Hybrid,
604}
605
606pub use crate::optimizer::index_types::IndexType;
608
609#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
611pub struct Statistics {
612 pub cardinality: u64,
614 pub selectivity: f64,
616 pub available_indexes: Vec<IndexType>,
618 pub cost: f64,
620 pub memory_estimate: u64,
622 pub io_estimate: u64,
624}
625
626#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
628pub struct OptimizationHints {
629 pub join_algorithm: Option<JoinAlgorithm>,
631 pub filter_placement: FilterPlacement,
633 pub materialization: MaterializationStrategy,
635 pub parallelism: Option<ParallelismType>,
637 pub preferred_indexes: Vec<IndexType>,
639 pub statistics: Option<Statistics>,
641}
642
643#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
645pub struct AnnotatedAlgebra {
646 pub algebra: Algebra,
648 pub hints: OptimizationHints,
650 pub context: Option<String>,
652}
653
654impl PropertyPath {
655 pub fn iri(iri: Iri) -> Self {
657 PropertyPath::Iri(iri)
658 }
659
660 pub fn inverse(path: PropertyPath) -> Self {
662 PropertyPath::Inverse(Box::new(path))
663 }
664
665 pub fn sequence(left: PropertyPath, right: PropertyPath) -> Self {
667 PropertyPath::Sequence(Box::new(left), Box::new(right))
668 }
669
670 pub fn alternative(left: PropertyPath, right: PropertyPath) -> Self {
672 PropertyPath::Alternative(Box::new(left), Box::new(right))
673 }
674
675 pub fn zero_or_more(path: PropertyPath) -> Self {
677 PropertyPath::ZeroOrMore(Box::new(path))
678 }
679
680 pub fn one_or_more(path: PropertyPath) -> Self {
682 PropertyPath::OneOrMore(Box::new(path))
683 }
684
685 pub fn zero_or_one(path: PropertyPath) -> Self {
687 PropertyPath::ZeroOrOne(Box::new(path))
688 }
689
690 pub fn is_simple(&self) -> bool {
692 matches!(self, PropertyPath::Iri(_) | PropertyPath::Variable(_))
693 }
694
695 pub fn variables(&self) -> Vec<Variable> {
697 let mut vars = Vec::new();
698 self.collect_variables(&mut vars);
699 vars.sort();
700 vars.dedup();
701 vars
702 }
703
704 fn collect_variables(&self, vars: &mut Vec<Variable>) {
705 match self {
706 PropertyPath::Variable(var) => vars.push(var.clone()),
707 PropertyPath::Inverse(path) => path.collect_variables(vars),
708 PropertyPath::Sequence(left, right) | PropertyPath::Alternative(left, right) => {
709 left.collect_variables(vars);
710 right.collect_variables(vars);
711 }
712 PropertyPath::ZeroOrMore(path)
713 | PropertyPath::OneOrMore(path)
714 | PropertyPath::ZeroOrOne(path) => path.collect_variables(vars),
715 PropertyPath::NegatedPropertySet(paths) => {
716 for path in paths {
717 path.collect_variables(vars);
718 }
719 }
720 PropertyPath::Iri(_) => {}
721 }
722 }
723
724 pub fn complexity(&self) -> usize {
726 match self {
727 PropertyPath::Iri(_) | PropertyPath::Variable(_) => 1,
728 PropertyPath::Inverse(path) => path.complexity() + 10,
729 PropertyPath::Sequence(left, right) => left.complexity() + right.complexity() + 20,
730 PropertyPath::Alternative(left, right) => {
731 std::cmp::max(left.complexity(), right.complexity()) + 15
732 }
733 PropertyPath::ZeroOrMore(_) | PropertyPath::OneOrMore(_) => 1000, PropertyPath::ZeroOrOne(path) => path.complexity() + 5,
735 PropertyPath::NegatedPropertySet(paths) => {
736 paths.iter().map(|p| p.complexity()).sum::<usize>() + 50
737 }
738 }
739 }
740}
741
742impl PropertyPathPattern {
743 pub fn new(subject: Term, path: PropertyPath, object: Term) -> Self {
745 PropertyPathPattern {
746 subject,
747 path,
748 object,
749 }
750 }
751
752 pub fn variables(&self) -> Vec<Variable> {
754 let mut vars = Vec::new();
755 self.collect_variables(&mut vars);
756 vars.sort();
757 vars.dedup();
758 vars
759 }
760
761 fn collect_variables(&self, vars: &mut Vec<Variable>) {
762 self.subject.collect_variables(vars);
763 self.path.collect_variables(vars);
764 self.object.collect_variables(vars);
765 }
766}
767
768impl fmt::Display for PropertyPath {
769 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
770 match self {
771 PropertyPath::Iri(iri) => write!(f, "{iri}"),
772 PropertyPath::Variable(var) => write!(f, "?{var}"),
773 PropertyPath::Inverse(path) => write!(f, "^{path}"),
774 PropertyPath::Sequence(left, right) => write!(f, "{left}/{right}"),
775 PropertyPath::Alternative(left, right) => write!(f, "{left}|{right}"),
776 PropertyPath::ZeroOrMore(path) => write!(f, "{path}*"),
777 PropertyPath::OneOrMore(path) => write!(f, "{path} +"),
778 PropertyPath::ZeroOrOne(path) => write!(f, "{path}?"),
779 PropertyPath::NegatedPropertySet(paths) => {
780 write!(f, "!(")?;
781 for (i, path) in paths.iter().enumerate() {
782 if i > 0 {
783 write!(f, "|")?
784 }
785 write!(f, "{path}")?;
786 }
787 write!(f, ")")
788 }
789 }
790 }
791}
792
793impl fmt::Display for PropertyPathPattern {
794 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
795 write!(f, "{} {} {}", self.subject, self.path, self.object)
796 }
797}
798
799impl Algebra {
800 pub fn bgp(patterns: Vec<TriplePattern>) -> Self {
802 Algebra::Bgp(patterns)
803 }
804
805 pub fn property_path(subject: Term, path: PropertyPath, object: Term) -> Self {
807 Algebra::PropertyPath {
808 subject,
809 path,
810 object,
811 }
812 }
813
814 pub fn join(left: Algebra, right: Algebra) -> Self {
816 Algebra::Join {
817 left: Box::new(left),
818 right: Box::new(right),
819 }
820 }
821
822 pub fn left_join(left: Algebra, right: Algebra, filter: Option<Expression>) -> Self {
824 Algebra::LeftJoin {
825 left: Box::new(left),
826 right: Box::new(right),
827 filter,
828 }
829 }
830
831 pub fn union(left: Algebra, right: Algebra) -> Self {
833 Algebra::Union {
834 left: Box::new(left),
835 right: Box::new(right),
836 }
837 }
838
839 pub fn filter(pattern: Algebra, condition: Expression) -> Self {
841 Algebra::Filter {
842 pattern: Box::new(pattern),
843 condition,
844 }
845 }
846
847 pub fn extend(pattern: Algebra, variable: Variable, expr: Expression) -> Self {
849 Algebra::Extend {
850 pattern: Box::new(pattern),
851 variable,
852 expr,
853 }
854 }
855
856 pub fn project(pattern: Algebra, variables: Vec<Variable>) -> Self {
858 Algebra::Project {
859 pattern: Box::new(pattern),
860 variables,
861 }
862 }
863
864 pub fn slice(pattern: Algebra, offset: Option<usize>, limit: Option<usize>) -> Self {
866 Algebra::Slice {
867 pattern: Box::new(pattern),
868 offset,
869 limit,
870 }
871 }
872
873 pub fn variables(&self) -> Vec<Variable> {
875 let mut vars = Vec::new();
876 self.collect_variables(&mut vars);
877 vars.sort();
878 vars.dedup();
879 vars
880 }
881
882 fn collect_variables(&self, vars: &mut Vec<Variable>) {
883 match self {
884 Algebra::Bgp(patterns) => {
885 for pattern in patterns {
886 pattern.collect_variables(vars);
887 }
888 }
889 Algebra::PropertyPath {
890 subject,
891 path,
892 object,
893 } => {
894 subject.collect_variables(vars);
895 path.collect_variables(vars);
896 object.collect_variables(vars);
897 }
898 Algebra::Join { left, right }
899 | Algebra::Union { left, right }
900 | Algebra::Minus { left, right } => {
901 left.collect_variables(vars);
902 right.collect_variables(vars);
903 }
904 Algebra::LeftJoin {
905 left,
906 right,
907 filter,
908 } => {
909 left.collect_variables(vars);
910 right.collect_variables(vars);
911 if let Some(filter) = filter {
912 filter.collect_variables(vars);
913 }
914 }
915 Algebra::Filter { pattern, condition } => {
916 pattern.collect_variables(vars);
917 condition.collect_variables(vars);
918 }
919 Algebra::Extend {
920 pattern,
921 variable,
922 expr,
923 } => {
924 pattern.collect_variables(vars);
925 vars.push(variable.clone());
926 expr.collect_variables(vars);
927 }
928 Algebra::Service { pattern, .. } => {
929 pattern.collect_variables(vars);
930 }
931 Algebra::Graph { pattern, .. } => {
932 pattern.collect_variables(vars);
933 }
934 Algebra::Project { pattern, variables } => {
935 pattern.collect_variables(vars);
936 vars.extend(variables.clone());
937 }
938 Algebra::Distinct { pattern }
939 | Algebra::Reduced { pattern }
940 | Algebra::Slice { pattern, .. } => {
941 pattern.collect_variables(vars);
942 }
943 Algebra::OrderBy {
944 pattern,
945 conditions,
946 } => {
947 pattern.collect_variables(vars);
948 for condition in conditions {
949 condition.expr.collect_variables(vars);
950 }
951 }
952 Algebra::Group {
953 pattern,
954 variables: group_vars,
955 aggregates,
956 } => {
957 pattern.collect_variables(vars);
958 for group_var in group_vars {
959 group_var.expr.collect_variables(vars);
960 if let Some(alias) = &group_var.alias {
961 vars.push(alias.clone());
962 }
963 }
964 for (var, aggregate) in aggregates {
965 vars.push(var.clone());
966 aggregate.collect_variables(vars);
967 }
968 }
969 Algebra::Having { pattern, condition } => {
970 pattern.collect_variables(vars);
971 condition.collect_variables(vars);
972 }
973 Algebra::Values { variables, .. } => {
974 vars.extend(variables.clone());
975 }
976 Algebra::Table | Algebra::Zero | Algebra::Empty => {}
977 }
978 }
979}
980
981impl TriplePattern {
982 pub fn new(subject: Term, predicate: Term, object: Term) -> Self {
984 TriplePattern {
985 subject,
986 predicate,
987 object,
988 }
989 }
990
991 fn collect_variables(&self, vars: &mut Vec<Variable>) {
992 self.subject.collect_variables(vars);
993 self.predicate.collect_variables(vars);
994 self.object.collect_variables(vars);
995 }
996
997 pub fn variables(&self) -> Vec<Variable> {
999 let mut vars = Vec::new();
1000 self.collect_variables(&mut vars);
1001 vars
1002 }
1003}
1004
1005impl Term {
1006 fn collect_variables(&self, vars: &mut Vec<Variable>) {
1007 if let Term::Variable(var) = self {
1008 vars.push(var.clone());
1009 }
1010 }
1011}
1012
1013impl Expression {
1014 fn collect_variables(&self, vars: &mut Vec<Variable>) {
1015 match self {
1016 Expression::Variable(var) => vars.push(var.clone()),
1017 Expression::Function { args, .. } => {
1018 for arg in args {
1019 arg.collect_variables(vars);
1020 }
1021 }
1022 Expression::Binary { left, right, .. } => {
1023 left.collect_variables(vars);
1024 right.collect_variables(vars);
1025 }
1026 Expression::Unary { operand, .. } => {
1027 operand.collect_variables(vars);
1028 }
1029 Expression::Conditional {
1030 condition,
1031 then_expr,
1032 else_expr,
1033 } => {
1034 condition.collect_variables(vars);
1035 then_expr.collect_variables(vars);
1036 else_expr.collect_variables(vars);
1037 }
1038 Expression::Bound(var) => vars.push(var.clone()),
1039 Expression::Exists(algebra) | Expression::NotExists(algebra) => {
1040 algebra.collect_variables(vars);
1041 }
1042 Expression::Literal(_) | Expression::Iri(_) => {}
1043 }
1044 }
1045}
1046
1047impl Aggregate {
1048 fn collect_variables(&self, vars: &mut Vec<Variable>) {
1049 match self {
1050 Aggregate::Count {
1051 expr: Some(expr), ..
1052 }
1053 | Aggregate::Sum { expr, .. }
1054 | Aggregate::Min { expr, .. }
1055 | Aggregate::Max { expr, .. }
1056 | Aggregate::Avg { expr, .. }
1057 | Aggregate::Sample { expr, .. }
1058 | Aggregate::GroupConcat { expr, .. } => {
1059 expr.collect_variables(vars);
1060 }
1061 Aggregate::Count { expr: None, .. } => {}
1062 }
1063 }
1064}
1065
1066#[macro_export]
1068macro_rules! triple {
1069 ($s:expr_2021, $p:expr_2021, $o:expr_2021) => {
1070 TriplePattern::new($s, $p, $o)
1071 };
1072}
1073
1074#[macro_export]
1075macro_rules! var {
1076 ($name:expr_2021) => {
1077 Term::Variable($name.to_string())
1078 };
1079}
1080
1081#[macro_export]
1082macro_rules! iri {
1083 ($iri:expr_2021) => {
1084 Term::Iri(NamedNode::new($iri).expect("macro argument should be valid IRI"))
1085 };
1086}
1087
1088#[macro_export]
1089macro_rules! literal {
1090 ($value:expr_2021) => {
1091 Term::Literal(Literal::new($value.to_string(), None, None))
1092 };
1093 ($value:expr_2021, lang: $lang:expr_2021) => {
1094 Term::Literal(Literal::new(
1095 $value.to_string(),
1096 Some($lang.to_string()),
1097 None,
1098 ))
1099 };
1100 ($value:expr_2021, datatype: $dt:expr_2021) => {
1101 Term::Literal(Literal::new(
1102 $value.to_string(),
1103 None,
1104 Some(NamedNode::new($dt).expect("macro datatype argument should be valid IRI")),
1105 ))
1106 };
1107}
1108
1109impl Default for ServiceCapabilities {
1110 fn default() -> Self {
1111 Self {
1112 supports_projection: true,
1113 supports_filtering: true,
1114 supports_ordering: false,
1115 supports_aggregation: false,
1116 max_query_size: None,
1117 }
1118 }
1119}
1120
1121impl Literal {
1122 pub fn new(value: String, language: Option<String>, datatype: Option<Iri>) -> Self {
1124 Literal {
1125 value,
1126 language,
1127 datatype,
1128 }
1129 }
1130
1131 pub fn string(value: impl Into<String>) -> Self {
1133 Literal {
1134 value: value.into(),
1135 language: None,
1136 datatype: None,
1137 }
1138 }
1139
1140 pub fn lang_string(value: impl Into<String>, language: impl Into<String>) -> Self {
1142 Literal {
1143 value: value.into(),
1144 language: Some(language.into()),
1145 datatype: None,
1146 }
1147 }
1148
1149 pub fn typed(value: impl Into<String>, datatype: Iri) -> Self {
1151 Literal {
1152 value: value.into(),
1153 language: None,
1154 datatype: Some(datatype),
1155 }
1156 }
1157
1158 pub fn integer(value: i64) -> Self {
1160 Literal::typed(
1161 value.to_string(),
1162 NamedNode::new("http://www.w3.org/2001/XMLSchema#integer")
1163 .expect("XSD URI is well-formed and valid"),
1164 )
1165 }
1166
1167 pub fn decimal(value: f64) -> Self {
1169 Literal::typed(
1170 value.to_string(),
1171 NamedNode::new("http://www.w3.org/2001/XMLSchema#decimal")
1172 .expect("XSD URI is well-formed and valid"),
1173 )
1174 }
1175
1176 pub fn boolean(value: bool) -> Self {
1178 Literal::typed(
1179 value.to_string(),
1180 NamedNode::new("http://www.w3.org/2001/XMLSchema#boolean")
1181 .expect("XSD URI is well-formed and valid"),
1182 )
1183 }
1184
1185 pub fn date(value: impl Into<String>) -> Self {
1187 Literal::typed(
1188 value.into(),
1189 NamedNode::new("http://www.w3.org/2001/XMLSchema#date")
1190 .expect("XSD URI is well-formed and valid"),
1191 )
1192 }
1193
1194 pub fn datetime(value: impl Into<String>) -> Self {
1196 Literal::typed(
1197 value.into(),
1198 NamedNode::new("http://www.w3.org/2001/XMLSchema#dateTime")
1199 .expect("XSD URI is well-formed and valid"),
1200 )
1201 }
1202
1203 pub fn effective_datatype(&self) -> Iri {
1205 if let Some(ref dt) = self.datatype {
1206 dt.clone()
1207 } else if self.language.is_some() {
1208 NamedNode::new("http://www.w3.org/1999/02/22-rdf-syntax-ns#langString")
1209 .expect("RDF URI is well-formed and valid")
1210 } else {
1211 NamedNode::new("http://www.w3.org/2001/XMLSchema#string")
1212 .expect("XSD URI is well-formed and valid")
1213 }
1214 }
1215
1216 pub fn is_numeric(&self) -> bool {
1218 if let Some(ref dt) = self.datatype {
1219 matches!(
1220 dt.as_str(),
1221 "http://www.w3.org/2001/XMLSchema#integer"
1222 | "http://www.w3.org/2001/XMLSchema#decimal"
1223 | "http://www.w3.org/2001/XMLSchema#float"
1224 | "http://www.w3.org/2001/XMLSchema#double"
1225 | "http://www.w3.org/2001/XMLSchema#long"
1226 | "http://www.w3.org/2001/XMLSchema#int"
1227 | "http://www.w3.org/2001/XMLSchema#short"
1228 | "http://www.w3.org/2001/XMLSchema#byte"
1229 | "http://www.w3.org/2001/XMLSchema#unsignedLong"
1230 | "http://www.w3.org/2001/XMLSchema#unsignedInt"
1231 | "http://www.w3.org/2001/XMLSchema#unsignedShort"
1232 | "http://www.w3.org/2001/XMLSchema#unsignedByte"
1233 | "http://www.w3.org/2001/XMLSchema#positiveInteger"
1234 | "http://www.w3.org/2001/XMLSchema#nonNegativeInteger"
1235 | "http://www.w3.org/2001/XMLSchema#negativeInteger"
1236 | "http://www.w3.org/2001/XMLSchema#nonPositiveInteger"
1237 )
1238 } else {
1239 false
1240 }
1241 }
1242
1243 pub fn is_string(&self) -> bool {
1245 self.datatype.is_none() && self.language.is_none()
1246 }
1247
1248 pub fn is_lang_string(&self) -> bool {
1250 self.language.is_some()
1251 }
1252
1253 pub fn is_boolean(&self) -> bool {
1255 if let Some(ref dt) = self.datatype {
1256 dt.as_str() == "http://www.w3.org/2001/XMLSchema#boolean"
1257 } else {
1258 false
1259 }
1260 }
1261
1262 pub fn is_datetime(&self) -> bool {
1264 if let Some(ref dt) = self.datatype {
1265 matches!(
1266 dt.as_str(),
1267 "http://www.w3.org/2001/XMLSchema#date"
1268 | "http://www.w3.org/2001/XMLSchema#dateTime"
1269 | "http://www.w3.org/2001/XMLSchema#time"
1270 | "http://www.w3.org/2001/XMLSchema#gYear"
1271 | "http://www.w3.org/2001/XMLSchema#gYearMonth"
1272 | "http://www.w3.org/2001/XMLSchema#gMonth"
1273 | "http://www.w3.org/2001/XMLSchema#gMonthDay"
1274 | "http://www.w3.org/2001/XMLSchema#gDay"
1275 | "http://www.w3.org/2001/XMLSchema#duration"
1276 | "http://www.w3.org/2001/XMLSchema#dayTimeDuration"
1277 | "http://www.w3.org/2001/XMLSchema#yearMonthDuration"
1278 )
1279 } else {
1280 false
1281 }
1282 }
1283}
1284
1285impl Default for Statistics {
1286 fn default() -> Self {
1287 Self {
1288 cardinality: 0,
1289 selectivity: 1.0,
1290 available_indexes: Vec::new(),
1291 cost: 0.0,
1292 memory_estimate: 0,
1293 io_estimate: 0,
1294 }
1295 }
1296}
1297
1298impl Statistics {
1299 pub fn with_cardinality(cardinality: u64) -> Self {
1301 Self {
1302 cardinality,
1303 selectivity: 1.0,
1304 available_indexes: Vec::new(),
1305 cost: cardinality as f64,
1306 memory_estimate: cardinality * 64, io_estimate: cardinality / 1000, }
1309 }
1310
1311 pub fn with_selectivity(mut self, selectivity: f64) -> Self {
1313 self.selectivity = selectivity.clamp(0.0, 1.0);
1314 self.cardinality = (self.cardinality as f64 * self.selectivity) as u64;
1315 self.cost *= self.selectivity;
1316 self
1317 }
1318
1319 pub fn with_index(mut self, index: IndexType) -> Self {
1321 self.available_indexes.push(index);
1322 self.cost *= 0.8;
1324 self
1325 }
1326
1327 pub fn combine(&self, other: &Statistics) -> Self {
1329 Self {
1330 cardinality: self.cardinality * other.cardinality,
1331 selectivity: self.selectivity * other.selectivity,
1332 available_indexes: self
1333 .available_indexes
1334 .iter()
1335 .chain(other.available_indexes.iter())
1336 .cloned()
1337 .collect(),
1338 cost: self.cost + other.cost,
1339 memory_estimate: self.memory_estimate + other.memory_estimate,
1340 io_estimate: self.io_estimate + other.io_estimate,
1341 }
1342 }
1343}
1344
1345impl OptimizationHints {
1346 pub fn for_bgp(patterns: &[TriplePattern]) -> Self {
1348 let mut hints = OptimizationHints::default();
1349
1350 let cardinality = match patterns.len() {
1352 0 => 0,
1353 1 => 1000, n => 1000 / (n as u64 * n as u64), };
1356
1357 hints.statistics = Some(Statistics::with_cardinality(cardinality));
1358
1359 for pattern in patterns {
1361 if let Term::Variable(_) = pattern.subject {
1362 hints.preferred_indexes.push(IndexType::PredicateIndex);
1363 }
1364 if let Term::Variable(_) = pattern.predicate {
1365 hints.preferred_indexes.push(IndexType::SubjectIndex);
1366 }
1367 if let Term::Variable(_) = pattern.object {
1368 hints
1369 .preferred_indexes
1370 .push(IndexType::SubjectPredicateIndex);
1371 }
1372 }
1373
1374 hints
1375 }
1376
1377 pub fn for_join(left_hints: &OptimizationHints, right_hints: &OptimizationHints) -> Self {
1379 let mut hints = OptimizationHints::default();
1380
1381 if let (Some(left_stats), Some(right_stats)) =
1383 (&left_hints.statistics, &right_hints.statistics)
1384 {
1385 hints.statistics = Some(left_stats.combine(right_stats));
1386
1387 hints.join_algorithm = Some(match (left_stats.cardinality, right_stats.cardinality) {
1389 (l, r) if l < 1000 && r < 1000 => JoinAlgorithm::NestedLoopJoin,
1390 (l, r) if l > 100000 || r > 100000 => JoinAlgorithm::SortMergeJoin,
1391 _ => JoinAlgorithm::HashJoin,
1392 });
1393 }
1394
1395 hints.preferred_indexes = left_hints
1397 .preferred_indexes
1398 .iter()
1399 .chain(right_hints.preferred_indexes.iter())
1400 .cloned()
1401 .collect();
1402
1403 hints
1404 }
1405
1406 pub fn for_filter(pattern_hints: &OptimizationHints, condition: &Expression) -> Self {
1408 let mut hints = pattern_hints.clone();
1409
1410 if let Some(ref mut stats) = hints.statistics {
1412 let filter_selectivity = estimate_filter_selectivity(condition);
1413 *stats = stats.clone().with_selectivity(filter_selectivity);
1414 }
1415
1416 hints.filter_placement = if estimate_filter_selectivity(condition) < 0.1 {
1418 FilterPlacement::Early
1419 } else {
1420 FilterPlacement::Optimal
1421 };
1422
1423 hints
1424 }
1425}
1426
1427impl AnnotatedAlgebra {
1428 pub fn new(algebra: Algebra) -> Self {
1430 let hints = match &algebra {
1431 Algebra::Bgp(patterns) => OptimizationHints::for_bgp(patterns),
1432 Algebra::Join { left: _, right: _ } => {
1433 OptimizationHints::default()
1435 }
1436 Algebra::Filter { .. } => OptimizationHints::default(),
1437 _ => OptimizationHints::default(),
1438 };
1439
1440 Self {
1441 algebra,
1442 hints,
1443 context: None,
1444 }
1445 }
1446
1447 pub fn with_hints(algebra: Algebra, hints: OptimizationHints) -> Self {
1449 Self {
1450 algebra,
1451 hints,
1452 context: None,
1453 }
1454 }
1455
1456 pub fn with_context(mut self, context: String) -> Self {
1458 self.context = Some(context);
1459 self
1460 }
1461
1462 pub fn estimated_cost(&self) -> f64 {
1464 self.hints
1465 .statistics
1466 .as_ref()
1467 .map(|s| s.cost)
1468 .unwrap_or(0.0)
1469 }
1470
1471 pub fn estimated_cardinality(&self) -> u64 {
1473 self.hints
1474 .statistics
1475 .as_ref()
1476 .map(|s| s.cardinality)
1477 .unwrap_or(0)
1478 }
1479}
1480
1481fn estimate_filter_selectivity(condition: &Expression) -> f64 {
1483 match condition {
1484 Expression::Binary { op, .. } => match op {
1485 BinaryOperator::Equal => 0.01, BinaryOperator::NotEqual => 0.99, BinaryOperator::Less
1488 | BinaryOperator::LessEqual
1489 | BinaryOperator::Greater
1490 | BinaryOperator::GreaterEqual => 0.33, BinaryOperator::And => 0.25, BinaryOperator::Or => 0.75, _ => 0.5, },
1495 Expression::Function { name, .. } => match name.as_str() {
1496 "regex" | "contains" => 0.2, "bound" => 0.8, "isIRI" | "isLiteral" | "isBlank" => 0.3, _ => 0.5, },
1501 Expression::Unary {
1502 op: UnaryOperator::Not,
1503 ..
1504 } => 0.5, Expression::Unary { .. } => 0.5,
1506 _ => 0.5, }
1508}
1509
1510#[derive(Debug, Clone, Default)]
1512pub struct EvaluationContext {
1513 pub bindings: HashMap<Variable, Term>,
1515 pub dataset: Option<String>,
1517 pub options: HashMap<String, String>,
1519}
1520
1521#[cfg(test)]
1522mod algebra_tests;