1pub mod expr;
11mod ops;
12pub mod plan;
13
14use crate::encoding::{encode_literal, named_node_id, term_id, EncodedRows, DEFAULT_GRAPH_ID};
15use crate::error::{Error, Result};
16use crate::reason::{Entailment, GraphFilter};
17use crate::sql::Capabilities;
18use crate::stats::Stats;
19use expr::V;
20use oxrdf::{Literal, Term, Variable};
21use plan::Pos;
22use spargebra::algebra::{Expression, GraphPattern, OrderExpression, QueryDataset};
23use spargebra::term::{GroundTerm, NamedNodePattern, TermPattern, TriplePattern};
24use std::collections::{BTreeMap, HashMap, HashSet};
25use std::fmt::Write;
26
27#[derive(Debug, Clone, Default, PartialEq, Eq)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30#[cfg_attr(feature = "serde", serde(default, rename_all = "camelCase"))]
31pub struct QueryOptions {
32 pub union_default_graph: bool,
34 pub sqlite_planner: bool,
36 pub default_graph: Option<Vec<i64>>,
39 pub named_graphs: Option<Vec<i64>>,
41 pub reasoning: crate::reason::Reasoning,
43 pub include_inferred: bool,
45 pub var_types: BTreeMap<String, ValueType>,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
55pub enum ValueType {
56 Numeric,
58 String,
60 Boolean,
61}
62
63#[derive(Debug, Clone)]
65pub(crate) enum Col {
66 Id(String),
68 Val(Box<V>),
70}
71
72impl Col {
73 pub(crate) fn key(&self) -> Option<&str> {
75 match self {
76 Self::Id(x) => Some(x),
77 Self::Val(v) => v.id.as_deref(),
78 }
79 }
80
81 pub(crate) fn value(&self) -> V {
82 match self {
83 Self::Id(x) => V::from_id(x),
84 Self::Val(v) => (**v).clone(),
85 }
86 }
87}
88
89#[derive(Debug, Clone)]
90pub(crate) struct Binding {
91 pub col: Col,
92 pub nullable: bool,
93 #[allow(dead_code)]
96 pub computed: bool,
97 pub correlated: bool,
99}
100
101impl Binding {
102 fn id(sql: impl Into<String>) -> Self {
103 Self {
104 col: Col::Id(sql.into()),
105 nullable: false,
106 computed: false,
107 correlated: false,
108 }
109 }
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
113pub(crate) enum Join {
114 First,
115 Cross,
116 Inner,
117 #[allow(dead_code)]
119 Left(String),
120}
121
122#[derive(Debug, Clone)]
123pub(crate) struct FromItem {
124 pub join: Join,
125 pub item: String,
126}
127
128#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
129pub(crate) enum Stage {
130 #[default]
131 Plain,
132 Grouped,
133 Ordered,
134 Distinct,
135 Sliced,
136}
137
138#[derive(Debug, Clone, Default)]
139pub(crate) struct Block {
140 pub from: Vec<FromItem>,
141 pub wheres: Vec<String>,
142 pub cols: BTreeMap<usize, Binding>,
143 pub group_by: Option<Vec<String>>,
144 pub order_by: Vec<String>,
145 pub distinct: bool,
146 pub limit: Option<usize>,
147 pub offset: usize,
148 pub stage: Stage,
149 pub extra_select: Vec<String>,
151 pub no_flatten: bool,
155}
156
157pub(crate) const VAL_FIELDS: [&str; 10] = ["i", "k", "l", "d", "g", "n", "t", "s", "b", "x"];
158
159pub(crate) fn val_fields(v: &V) -> [String; 10] {
160 [
161 v.id.clone().unwrap_or_else(|| "NULL".into()),
162 v.kind.clone(),
163 if v.computed_num {
164 "NULL".into()
165 } else {
166 v.lex.clone()
167 },
168 v.dt.clone(),
169 v.lang.clone(),
170 v.num.clone(),
171 v.nt.clone(),
172 v.ts.clone(),
173 v.boolv.clone(),
174 v.aux.clone(),
175 ]
176}
177
178impl Block {
179 pub(crate) fn is_plain(&self) -> bool {
180 self.stage == Stage::Plain
181 }
182
183 pub(crate) fn is_unit(&self) -> bool {
184 self.from.is_empty() && self.wheres.is_empty() && self.cols.is_empty()
185 }
186
187 pub(crate) fn render_from(items: &[FromItem]) -> String {
188 let mut out = String::new();
189 for (i, f) in items.iter().enumerate() {
190 if i == 0 {
191 out.push_str(&f.item);
192 continue;
193 }
194 match &f.join {
195 Join::First | Join::Inner => {
196 let _ = write!(out, " JOIN {}", f.item);
197 }
198 Join::Cross => {
199 let _ = write!(out, " CROSS JOIN {}", f.item);
200 }
201 Join::Left(on) => {
202 let _ = write!(out, " LEFT JOIN {} ON ({on})", f.item);
203 }
204 }
205 }
206 out
207 }
208
209 fn select_list(&self, vars: &[usize], text_ids: bool) -> Vec<String> {
211 let mut sel = Vec::new();
212 for idx in vars {
213 match self.cols.get(idx).map(|b| &b.col) {
214 Some(Col::Id(x)) => sel.push(if text_ids {
215 format!("CAST({x} AS TEXT) AS v{idx}")
216 } else {
217 format!("{x} AS v{idx}")
218 }),
219 Some(Col::Val(v)) => {
220 for (suffix, field) in VAL_FIELDS.iter().zip(val_fields(v)) {
221 if text_ids && *suffix == "i" {
222 sel.push(format!("CAST({field} AS TEXT) AS v{idx}_{suffix}"));
223 } else {
224 sel.push(format!("{field} AS v{idx}_{suffix}"));
225 }
226 }
227 }
228 None => sel.push(format!("NULL AS v{idx}")),
229 }
230 }
231 sel
232 }
233
234 pub(crate) fn tail(&self) -> String {
236 let mut sql = String::new();
237 if !self.from.is_empty() {
238 sql.push_str(" FROM ");
239 sql.push_str(&Self::render_from(&self.from));
240 }
241 if !self.wheres.is_empty() {
242 sql.push_str(" WHERE ");
243 sql.push_str(&self.wheres.join(" AND "));
244 }
245 if let Some(g) = &self.group_by {
246 if !g.is_empty() {
247 sql.push_str(" GROUP BY ");
248 sql.push_str(&g.join(", "));
249 }
250 }
251 if !self.order_by.is_empty() {
252 sql.push_str(" ORDER BY ");
253 sql.push_str(&self.order_by.join(", "));
254 }
255 if self.limit.is_some() || self.offset > 0 || self.no_flatten {
256 let _ = write!(
257 sql,
258 " LIMIT {}",
259 self.limit.map_or_else(|| "-1".into(), |l| l.to_string())
260 );
261 if self.offset > 0 {
262 let _ = write!(sql, " OFFSET {}", self.offset);
263 }
264 }
265 sql
266 }
267
268 pub(crate) fn to_select(&self, vars: Option<&[usize]>, text_ids: bool) -> String {
270 let all: Vec<usize> = self.cols.keys().copied().collect();
271 let mut sel = self.select_list(vars.unwrap_or(&all), text_ids);
272 sel.extend(self.extra_select.iter().cloned());
273 if sel.is_empty() {
274 sel.push("1 AS _u".into());
275 }
276 format!(
277 "SELECT {}{}{}",
278 if self.distinct { "DISTINCT " } else { "" },
279 sel.join(", "),
280 self.tail()
281 )
282 }
283}
284
285#[derive(Debug, Clone)]
286enum DefaultGraph {
287 Zero,
288 Union,
289 List(Vec<i64>),
290}
291
292#[derive(Debug, Clone)]
293struct Dataset {
294 default: DefaultGraph,
295 named: Option<Vec<i64>>,
296}
297
298#[derive(Debug, Clone, Copy)]
299enum GraphScope {
300 Default,
301 Fixed(i64),
302 Var(usize),
303}
304
305pub(crate) struct Compiler<'a> {
307 pub stats: &'a Stats,
308 pub caps: &'a Capabilities,
309 pub options: &'a QueryOptions,
310 vars: HashMap<Variable, usize>,
311 pub var_names: Vec<Variable>,
312 aliases: usize,
313 pub constants: HashMap<i64, Term>,
315 pub outer: Vec<BTreeMap<usize, Binding>>,
317 pub now: Literal,
318 pub base_iri: Option<String>,
319 dataset: Dataset,
320 scope: GraphScope,
321 pub rows: EncodedRows,
323 pub notes: Vec<String>,
325 pending_triples: Vec<(usize, TriplePattern)>,
327 pub(crate) plan_hint: Vec<HashSet<usize>>,
330}
331
332impl<'a> Compiler<'a> {
333 pub(crate) fn new(
334 stats: &'a Stats,
335 caps: &'a Capabilities,
336 options: &'a QueryOptions,
337 dataset: Option<&QueryDataset>,
338 base_iri: Option<String>,
339 ) -> Self {
340 let mut dataset = match dataset {
341 Some(ds) => Dataset {
342 default: DefaultGraph::List(
343 ds.default
344 .iter()
345 .map(|g| named_node_id(g.as_str()))
346 .collect(),
347 ),
348 named: ds
350 .named
351 .as_ref()
352 .map(|n| n.iter().map(|g| named_node_id(g.as_str())).collect()),
353 },
354 None => Dataset {
355 default: if options.union_default_graph {
356 DefaultGraph::Union
357 } else {
358 DefaultGraph::Zero
359 },
360 named: None,
361 },
362 };
363 if let Some(d) = &options.default_graph {
364 dataset.default = DefaultGraph::List(d.clone());
365 }
366 if let Some(n) = &options.named_graphs {
367 dataset.named = Some(n.clone());
368 }
369 Self {
370 stats,
371 caps,
372 options,
373 vars: HashMap::new(),
374 var_names: Vec::new(),
375 aliases: 0,
376 constants: HashMap::new(),
377 outer: Vec::new(),
378 now: Literal::from(oxsdatatypes::DateTime::now()),
379 base_iri,
380 dataset,
381 scope: GraphScope::Default,
382 rows: EncodedRows::default(),
383 notes: Vec::new(),
384 pending_triples: Vec::new(),
385 plan_hint: Vec::new(),
386 }
387 }
388
389 pub(crate) fn join_terms(
394 &mut self,
395 b: &mut Block,
396 e: &Expression,
397 ) -> Vec<(usize, Binding, String)> {
398 if matches!(e, Expression::Variable(_) | Expression::Bound(_)) {
399 return Vec::new();
400 }
401 let mut vars = Vec::new();
402 expression_variables(e, &mut vars);
403 self.join_term_vars(b, &vars)
404 }
405
406 pub(crate) fn join_term_vars(
408 &mut self,
409 b: &mut Block,
410 vars: &[Variable],
411 ) -> Vec<(usize, Binding, String)> {
412 let mut restore = Vec::new();
413 if b.stage >= Stage::Grouped || b.from.is_empty() || !b.is_plain() {
414 return restore;
415 }
416 for v in vars {
417 let Some(&idx) = self.vars.get(v) else {
418 continue;
419 };
420 let Some(bind) = b.cols.get(&idx) else {
421 continue;
422 };
423 let Col::Id(x) = &bind.col else {
424 continue;
425 };
426 if bind.correlated || bind.computed {
427 continue;
428 }
429 let x = x.clone();
430 let t = self.alias("t");
431 b.from.push(FromItem {
432 join: Join::Left(format!("{t}.id = {x}")),
433 item: format!("terms {t}"),
434 });
435 let mut joined = bind.clone();
436 let v = V::from_joined(&x, &t);
437 let marker = v.lex.clone();
438 joined.col = Col::Val(Box::new(v));
439 restore.push((idx, bind.clone(), marker));
440 b.cols.insert(idx, joined);
441 }
442 restore
443 }
444
445 pub(crate) fn restore_terms(b: &mut Block, restore: Vec<(usize, Binding, String)>) {
448 for (i, bind, marker) in restore {
449 if let Some(cur) = b.cols.get_mut(&i) {
450 if matches!(&cur.col, Col::Val(v) if v.lex == marker) {
451 *cur = bind;
452 }
453 }
454 }
455 }
456
457 pub(crate) fn shallow(&mut self, b: Block, e: &Expression) -> Result<(Block, Expression)> {
462 use Expression as E;
463 let two = |me: &mut Self, b: Block, x: &E, y: &E| -> Result<(Block, E, E)> {
464 let (b, x) = me.shallow_arg(b, x)?;
465 let (b, y) = me.shallow_arg(b, y)?;
466 Ok((b, x, y))
467 };
468 Ok(match e {
469 E::FunctionCall(f, args) => {
470 let mut b = b;
471 let mut out = Vec::with_capacity(args.len());
472 for a in args {
473 let (nb, a) = self.shallow_arg(b, a)?;
474 b = nb;
475 out.push(a);
476 }
477 (b, E::FunctionCall(f.clone(), out))
478 }
479 E::Equal(x, y) => {
480 let (b, x, y) = two(self, b, x, y)?;
481 (b, E::Equal(Box::new(x), Box::new(y)))
482 }
483 E::Less(x, y) => {
484 let (b, x, y) = two(self, b, x, y)?;
485 (b, E::Less(Box::new(x), Box::new(y)))
486 }
487 E::LessOrEqual(x, y) => {
488 let (b, x, y) = two(self, b, x, y)?;
489 (b, E::LessOrEqual(Box::new(x), Box::new(y)))
490 }
491 E::Greater(x, y) => {
492 let (b, x, y) = two(self, b, x, y)?;
493 (b, E::Greater(Box::new(x), Box::new(y)))
494 }
495 E::GreaterOrEqual(x, y) => {
496 let (b, x, y) = two(self, b, x, y)?;
497 (b, E::GreaterOrEqual(Box::new(x), Box::new(y)))
498 }
499 E::Add(x, y) => {
500 let (b, x, y) = two(self, b, x, y)?;
501 (b, E::Add(Box::new(x), Box::new(y)))
502 }
503 E::Subtract(x, y) => {
504 let (b, x, y) = two(self, b, x, y)?;
505 (b, E::Subtract(Box::new(x), Box::new(y)))
506 }
507 E::Multiply(x, y) => {
508 let (b, x, y) = two(self, b, x, y)?;
509 (b, E::Multiply(Box::new(x), Box::new(y)))
510 }
511 E::Divide(x, y) => {
512 let (b, x, y) = two(self, b, x, y)?;
513 (b, E::Divide(Box::new(x), Box::new(y)))
514 }
515 E::And(x, y) => {
516 let (b, x) = self.shallow(b, x)?;
517 let (b, y) = self.shallow(b, y)?;
518 (b, E::And(Box::new(x), Box::new(y)))
519 }
520 E::Or(x, y) => {
521 let (b, x) = self.shallow(b, x)?;
522 let (b, y) = self.shallow(b, y)?;
523 (b, E::Or(Box::new(x), Box::new(y)))
524 }
525 E::Not(x) => {
526 let (b, x) = self.shallow(b, x)?;
527 (b, E::Not(Box::new(x)))
528 }
529 _ => (b, e.clone()),
530 })
531 }
532
533 fn shallow_arg(&mut self, b: Block, a: &Expression) -> Result<(Block, Expression)> {
536 const LIMIT: usize = 1_500;
537 let (mut b, a) = self.shallow(b, a)?;
538 if !matches!(a, Expression::FunctionCall(..)) {
539 return Ok((b, a));
540 }
541 let v = self.expr_term(&a, &b.cols)?;
542 if v.size() <= LIMIT {
543 return Ok((b, a));
544 }
545 let hidden = self.fresh_var("arg");
546 b.cols.insert(
547 hidden,
548 Binding {
549 col: Col::Val(Box::new(v)),
550 nullable: true,
551 computed: true,
552 correlated: false,
553 },
554 );
555 b = self.seal(b);
556 Ok((b, Expression::Variable(self.var_names[hidden].clone())))
557 }
558
559 pub(crate) fn var(&mut self, v: &Variable) -> usize {
560 if let Some(i) = self.vars.get(v) {
561 return *i;
562 }
563 let i = self.var_names.len();
564 self.vars.insert(v.clone(), i);
565 self.var_names.push(v.clone());
566 i
567 }
568
569 pub(crate) fn fresh_var(&mut self, hint: &str) -> usize {
570 let v = Variable::new_unchecked(format!("\u{1}{hint}{}", self.var_names.len()));
571 self.var(&v)
572 }
573
574 pub(crate) fn alias(&mut self, prefix: &str) -> String {
575 self.aliases += 1;
576 format!("{prefix}{}", self.aliases)
577 }
578
579 pub(crate) fn constant_id(&mut self, t: &Term) -> Result<i64> {
581 let id = match t {
582 Term::Triple(tr) => {
583 let id = self.rows.triple(tr.as_ref().as_ref());
584 self.constants.insert(id, t.clone());
585 self.constants.insert(
587 term_id(tr.subject.as_ref().into()),
588 tr.subject.clone().into(),
589 );
590 self.constants.insert(
591 named_node_id(tr.predicate.as_str()),
592 tr.predicate.clone().into(),
593 );
594 self.constants
595 .insert(term_id(tr.object.as_ref()), tr.object.clone());
596 return Ok(id);
597 }
598 Term::Literal(l) => encode_literal(l.as_ref()).0,
599 _ => term_id(t.as_ref()),
600 };
601 self.constants.insert(id, t.clone());
602 Ok(id)
603 }
604
605 fn outer_binding(&self, idx: usize) -> Option<&Binding> {
606 self.outer.iter().rev().find_map(|s| s.get(&idx))
607 }
608
609 pub(crate) fn seal(&mut self, b: Block) -> Block {
611 let alias = self.alias("s");
612 let sql = b.to_select(None, false);
613 let mut cols = BTreeMap::new();
614 for (idx, bind) in &b.cols {
615 let col = match &bind.col {
616 Col::Id(_) => Col::Id(format!("{alias}.v{idx}")),
617 Col::Val(v) => Col::Val(Box::new(V {
618 id: v.id.as_ref().map(|_| format!("{alias}.v{idx}_i")),
619 kind: format!("{alias}.v{idx}_k"),
620 lex: format!("{alias}.v{idx}_l"),
621 dt: format!("{alias}.v{idx}_d"),
622 lang: format!("{alias}.v{idx}_g"),
623 num: format!("{alias}.v{idx}_n"),
624 nt: format!("{alias}.v{idx}_t"),
625 ts: format!("{alias}.v{idx}_s"),
626 boolv: format!("{alias}.v{idx}_b"),
627 stat: v.stat,
628 computed_num: false,
629 decodable: v.decodable,
630 aux: format!("{alias}.v{idx}_x"),
631 tz: "NULL".into(),
632 })),
633 };
634 cols.insert(
635 *idx,
636 Binding {
637 col,
638 nullable: bind.nullable,
639 computed: false,
640 correlated: false,
641 },
642 );
643 }
644 for (idx, bind) in &b.cols {
646 if let Col::Val(v) = &bind.col {
647 if v.computed_num {
648 if let Some(Binding {
649 col: Col::Val(sv), ..
650 }) = cols.get_mut(idx)
651 {
652 let n = V::numeric(sv.num.clone(), sv.nt.clone());
653 sv.lex = n.lex;
654 sv.computed_num = true;
655 }
656 }
657 }
658 }
659 Block {
660 from: vec![FromItem {
661 join: Join::First,
662 item: format!("({sql}) AS {alias}"),
663 }],
664 cols,
665 ..Block::default()
666 }
667 }
668
669 fn plain(&mut self, b: Block) -> Block {
670 if b.is_plain() {
671 b
672 } else {
673 self.seal(b)
674 }
675 }
676
677 pub(crate) fn pattern(&mut self, p: &GraphPattern) -> Result<Block> {
679 match p {
680 GraphPattern::Bgp { patterns } => self.bgp(patterns),
681 GraphPattern::Join { left, right } => {
682 let a = self.pattern(left)?;
683 if let GraphPattern::Path {
684 subject,
685 path,
686 object,
687 } = right.as_ref()
688 {
689 if let Some(b) = self.seeded_path(&a, subject, path, object)? {
690 return self.join(a, b);
691 }
692 }
693 let b = self.pattern(right)?;
694 self.join(a, b)
695 }
696 GraphPattern::Filter { expr, inner } => {
697 let b = self.pattern(inner)?;
698 let b = self.plain(b);
699 let (b, expr) = self.shallow(b, expr)?;
700 let mut b = b;
701 let conds = self.filter_conditions(&expr, &b.cols)?;
702 b.wheres.extend(conds);
703 Ok(b)
704 }
705 GraphPattern::Graph { name, inner } => {
706 let saved = self.scope;
707 self.scope = match name {
708 NamedNodePattern::NamedNode(n) => {
709 let id = self.constant_id(&n.clone().into())?;
710 GraphScope::Fixed(id)
711 }
712 NamedNodePattern::Variable(v) => GraphScope::Var(self.var(v)),
713 };
714 let accesses = self.aliases;
715 let r = self.pattern(inner);
716 let scope = self.scope;
717 self.scope = saved;
718 let mut b = r?;
719 match scope {
720 GraphScope::Var(v) => {
721 if !b.cols.contains_key(&v) {
722 let g = self.graph_list_block(v);
724 b = self.join(b, g)?;
725 }
726 }
727 GraphScope::Fixed(id) => {
728 if self.aliases == accesses || !has_quad_access(p) {
729 b = self.plain(b);
731 b.wheres
732 .push(format!("EXISTS (SELECT 1 FROM graphs WHERE id = {id})"));
733 }
734 }
735 GraphScope::Default => {}
736 }
737 Ok(b)
738 }
739 GraphPattern::Extend {
740 inner,
741 variable,
742 expression,
743 } => {
744 let b = self.pattern(inner)?;
745 let mut b = self.plain(b);
746 let restore = self.join_terms(&mut b, expression);
747 let (b, expression) = self.shallow(b, expression)?;
748 let mut b = b;
749 let expression = &expression;
750 let v = self.expr_term(expression, &b.cols)?;
751 Self::restore_terms(&mut b, restore);
752 let idx = self.var(variable);
753 let (nullable, computed) = match expression {
754 Expression::Variable(x) => {
755 let xi = self.var(x);
756 (b.cols.get(&xi).is_none_or(|b| b.nullable), false)
757 }
758 Expression::NamedNode(_) | Expression::Literal(_) => (false, true),
759 _ => (true, true),
760 };
761 let col = match &v.id {
762 Some(id) if v.decodable => Col::Id(id.clone()),
763 _ => Col::Val(Box::new(v)),
764 };
765 b.cols.insert(
766 idx,
767 Binding {
768 col,
769 nullable,
770 computed,
771 correlated: false,
772 },
773 );
774 Ok(b)
775 }
776 GraphPattern::Values {
777 variables,
778 bindings,
779 } => self.values(variables, bindings),
780 GraphPattern::OrderBy { inner, expression } => {
781 let mut b = self.pattern(inner)?;
782 if b.stage > Stage::Grouped {
783 b = self.seal(b);
784 }
785 for oe in expression {
786 let (e, desc) = match oe {
787 OrderExpression::Asc(e) => (e, false),
788 OrderExpression::Desc(e) => (e, true),
789 };
790 let v = self.expr_term(e, &b.cols)?;
791 for k in order_keys(&v) {
792 b.order_by.push(if desc { format!("{k} DESC") } else { k });
793 }
794 }
795 b.stage = Stage::Ordered;
796 Ok(b)
797 }
798 GraphPattern::Project { inner, variables } => {
799 let graph_var = match self.scope {
803 GraphScope::Var(g) => Some(g),
804 _ => None,
805 };
806 let hidden = graph_var.map(|_| self.fresh_var("g"));
807 let saved = self.scope;
808 if let Some(h) = hidden {
809 self.scope = GraphScope::Var(h);
810 }
811 let r = self.pattern(inner);
812 self.scope = saved;
813 let mut b = r?;
814 if b.stage >= Stage::Distinct {
815 b = self.seal(b);
816 }
817 let hidden_binding = hidden.and_then(|h| b.cols.get(&h).cloned());
818 let mut cols = BTreeMap::new();
819 for v in variables {
820 let idx = self.var(v);
821 let bind = b.cols.remove(&idx).unwrap_or(Binding {
822 col: Col::Id("NULL".into()),
823 nullable: true,
824 computed: true,
825 correlated: false,
826 });
827 cols.insert(idx, bind);
828 }
829 b.cols = cols;
830 if let (Some(g), Some(h)) = (graph_var, hidden_binding) {
831 match b.cols.get(&g).and_then(|x| x.col.key().map(str::to_string)) {
832 Some(inner_g) => {
833 let hk = h.col.key().unwrap_or("NULL").to_string();
834 b.wheres.push(format!("{inner_g} = {hk}"));
835 }
836 None => {
837 b.cols.insert(g, h);
838 }
839 }
840 }
841 Ok(b)
842 }
843 GraphPattern::Distinct { inner } => {
844 let mut b = self.pattern(inner)?;
845 if b.stage >= Stage::Distinct {
846 b = self.seal(b);
847 }
848 b.distinct = true;
849 b.stage = Stage::Distinct;
850 Ok(b)
851 }
852 GraphPattern::Reduced { inner } => self.pattern(&GraphPattern::Distinct {
854 inner: inner.clone(),
855 }),
856 GraphPattern::Slice {
857 inner,
858 start,
859 length,
860 } => {
861 let mut b = self.pattern(inner)?;
862 if b.stage == Stage::Sliced {
863 b = self.seal(b);
864 }
865 b.offset = *start;
866 b.limit = *length;
867 b.stage = Stage::Sliced;
868 Ok(b)
869 }
870 other => self.pattern_ext(other),
871 }
872 }
873
874 fn pattern_ext(&mut self, p: &GraphPattern) -> Result<Block> {
876 self.pattern_m2(p)
877 }
878
879 pub(crate) fn unify(a: &Binding, b: &Binding) -> (String, Binding) {
882 let eq = match (a.col.key(), b.col.key()) {
883 (Some(x), Some(y)) => format!("{x} = {y}"),
884 _ => expr::same_term(&a.col.value(), &b.col.value()),
885 };
886 if !a.nullable && !b.nullable {
887 return (eq, a.clone());
888 }
889 let is_null = |x: &Binding| match &x.col {
890 Col::Id(i) => format!("{i} IS NULL"),
891 Col::Val(v) => format!("({}) IS NULL", v.kind),
892 };
893 let cond = format!("({eq} OR {} OR {})", is_null(a), is_null(b));
894 let col = match (&a.col, &b.col) {
895 (Col::Id(x), Col::Id(y)) => Col::Id(format!("COALESCE({x}, {y})")),
896 _ => {
897 let (av, bv) = (a.col.value(), b.col.value());
898 Col::Val(Box::new(Self::choose(
899 &format!("(({}) IS NOT NULL)", av.kind),
900 &av,
901 &bv,
902 )))
903 }
904 };
905 (
906 cond,
907 Binding {
908 col,
909 nullable: a.nullable && b.nullable,
910 computed: true,
911 correlated: a.correlated || b.correlated,
912 },
913 )
914 }
915
916 fn graph_list_block(&mut self, v: usize) -> Block {
917 let a = self.alias("gr");
918 let mut b = Block::default();
919 b.from.push(FromItem {
920 join: Join::First,
921 item: format!("graphs {a}"),
922 });
923 if let Some(named) = &self.dataset.named {
924 b.wheres.push(in_list(&format!("{a}.id"), named));
925 }
926 b.cols.insert(v, Binding::id(format!("{a}.id")));
927 b
928 }
929
930 fn pos(&mut self, t: &TermPattern, bnodes: &mut HashMap<String, usize>) -> Result<Pos> {
931 Ok(match t {
932 TermPattern::NamedNode(n) => Pos::Const(self.constant_id(&n.clone().into())?),
933 TermPattern::Literal(l) => Pos::Const(self.constant_id(&l.clone().into())?),
934 TermPattern::Variable(v) => Pos::Var(self.var(v)),
935 TermPattern::BlankNode(b) => {
936 let _ = bnodes;
939 Pos::Var(self.var(&Variable::new_unchecked(format!("\u{1}b{}", b.as_str()))))
940 }
941 TermPattern::Triple(tp) => {
942 if let Some(t) = ground_triple(tp) {
943 Pos::Const(self.constant_id(&t.into())?)
944 } else {
945 let v = self.fresh_var("t");
946 self.pending_triples.push((v, (**tp).clone()));
947 Pos::Var(v)
948 }
949 }
950 })
951 }
952
953 fn pos_nn(&mut self, p: &NamedNodePattern) -> Result<Pos> {
954 Ok(match p {
955 NamedNodePattern::NamedNode(n) => Pos::Const(self.constant_id(&n.clone().into())?),
956 NamedNodePattern::Variable(v) => Pos::Var(self.var(v)),
957 })
958 }
959
960 pub(crate) fn bind_pos(&mut self, b: &mut Block, colsql: &str, pos: Pos) -> Result<()> {
962 match pos {
963 Pos::Const(id) => b.wheres.push(format!("{colsql} = {id}")),
964 Pos::Var(v) => {
965 if let Some(existing) = b.cols.get(&v) {
966 let cond = match existing.col.key() {
967 Some(x) => format!("{colsql} = {x}"),
968 None => expr::same_term(&V::from_id(colsql), &existing.col.value()),
969 };
970 b.wheres.push(if existing.nullable {
971 let null = match &existing.col {
972 Col::Id(x) => format!("{x} IS NULL"),
973 Col::Val(v) => format!("({}) IS NULL", v.kind),
974 };
975 format!("({null} OR {cond})")
976 } else {
977 cond
978 });
979 return Ok(());
980 }
981 let mut correlated = false;
982 if let Some(outer) = self.outer_binding(v).cloned() {
983 let (cond, _) = Self::unify(&Binding::id(colsql), &outer);
984 b.wheres.push(cond);
985 correlated = true;
986 }
987 b.cols.insert(
988 v,
989 Binding {
990 correlated,
991 ..Binding::id(colsql)
992 },
993 );
994 }
995 }
996 Ok(())
997 }
998
999 pub(crate) fn graph_pos(&mut self, b: &mut Block, q: &str, selective: bool) -> Result<()> {
1005 let plus = if selective { "+" } else { "" };
1006 match self.scope {
1007 GraphScope::Default => match self.dataset.default.clone() {
1008 DefaultGraph::Zero => b.wheres.push(format!("{plus}{q}.g = {DEFAULT_GRAPH_ID}")),
1009 DefaultGraph::List(l) if l.is_empty() => b.wheres.push("0".into()),
1010 DefaultGraph::List(l) if l.len() == 1 => {
1011 b.wheres.push(format!("{plus}{q}.g = {}", l[0]))
1012 }
1013 DefaultGraph::List(l) => {
1014 b.wheres.push(in_list(&format!("{q}.g"), &l));
1015 let d = self.alias("d");
1016 b.wheres.push(format!(
1017 "NOT EXISTS (SELECT 1 FROM {} {d} WHERE {d}.s = {q}.s AND {d}.p = {q}.p AND {d}.o = {q}.o AND {d}.g < {q}.g AND {})",
1018 self.entailment().base(),
1019 in_list(&format!("{d}.g"), &l)
1020 ));
1021 }
1022 DefaultGraph::Union => {
1023 let d = self.alias("d");
1024 let base = self.entailment().base();
1025 b.wheres.push(format!(
1026 "NOT EXISTS (SELECT 1 FROM {base} {d} WHERE {d}.s = {q}.s AND {d}.p = {q}.p AND {d}.o = {q}.o AND {d}.g < {q}.g)"
1027 ));
1028 }
1029 },
1030 GraphScope::Fixed(id) => {
1031 if self
1032 .dataset
1033 .named
1034 .as_ref()
1035 .is_some_and(|n| !n.contains(&id))
1036 {
1037 b.wheres.push("0".into());
1038 }
1039 b.wheres.push(format!("{plus}{q}.g = {id}"));
1040 }
1041 GraphScope::Var(v) => {
1042 b.wheres.push(format!("{q}.g <> {DEFAULT_GRAPH_ID}"));
1043 if let Some(named) = self.dataset.named.clone() {
1044 b.wheres.push(in_list(&format!("{q}.g"), &named));
1045 }
1046 self.bind_pos(b, &format!("{q}.g"), Pos::Var(v))?;
1047 }
1048 }
1049 Ok(())
1050 }
1051
1052 pub(crate) fn entailment(&self) -> Entailment<'a> {
1054 Entailment {
1055 reasoning: self.options.reasoning,
1056 inferred: self.options.include_inferred,
1057 transitive: &self.stats.transitive,
1058 max_compound: self.caps.max_compound_select,
1059 }
1060 }
1061
1062 pub(crate) fn quad_access(
1064 &mut self,
1065 b: &mut Block,
1066 join: Join,
1067 s: Pos,
1068 p: Pos,
1069 o: Pos,
1070 ) -> Result<String> {
1071 let q = self.alias("q");
1072 let bound = |pos: Pos, b: &Block, me: &Self| match pos {
1073 Pos::Const(_) => true,
1074 Pos::Var(v) => b.cols.contains_key(&v) || me.outer_binding(v).is_some(),
1075 };
1076 let selective = bound(s, b, self) || bound(p, b, self) || bound(o, b, self);
1077 let ent = self.entailment();
1078 let merge = match (&self.scope, &self.dataset.default) {
1081 (GraphScope::Default, DefaultGraph::Union) if ent.active() => {
1082 Some(GraphFilter::Merge(None))
1083 }
1084 (GraphScope::Default, DefaultGraph::List(l)) if ent.active() && l.len() > 1 => {
1085 Some(GraphFilter::Merge(Some(l.clone())))
1086 }
1087 _ => None,
1088 };
1089 let source = if ent.active() {
1090 let c = |p: Pos| match p {
1091 Pos::Const(id) => Some(id),
1092 Pos::Var(_) => None,
1093 };
1094 ent.source(
1095 c(s),
1096 c(p),
1097 c(o),
1098 merge.as_ref().unwrap_or(&GraphFilter::Keep),
1099 )
1100 } else {
1101 "quads".into()
1102 };
1103 b.from.push(FromItem {
1104 join: if b.from.is_empty() { Join::First } else { join },
1105 item: format!("{source} {q}"),
1106 });
1107 self.bind_pos(b, &format!("{q}.s"), s)?;
1108 self.bind_pos(b, &format!("{q}.p"), p)?;
1109 self.bind_pos(b, &format!("{q}.o"), o)?;
1110 if merge.is_none() {
1111 self.graph_pos(b, &q, selective)?;
1112 }
1113 Ok(q)
1114 }
1115
1116 fn bgp(&mut self, patterns: &[TriplePattern]) -> Result<Block> {
1117 if patterns.is_empty() {
1118 return Ok(Block::default());
1119 }
1120 let mut bnodes = HashMap::new();
1121 let mut enc = Vec::with_capacity(patterns.len());
1122 for tp in patterns {
1123 enc.push([
1124 self.pos(&tp.subject, &mut bnodes)?,
1125 self.pos_nn(&tp.predicate)?,
1126 self.pos(&tp.object, &mut bnodes)?,
1127 ]);
1128 }
1129 let pre: HashSet<usize> = self
1130 .outer
1131 .iter()
1132 .flat_map(|m| m.keys().copied())
1133 .chain(self.plan_hint.iter().flatten().copied())
1134 .collect();
1135 let ord: Vec<usize> = if self.options.sqlite_planner {
1136 (0..enc.len()).collect()
1137 } else {
1138 plan::order(&enc, &pre, self.stats)
1139 };
1140 let join = if self.options.sqlite_planner {
1141 Join::Inner
1142 } else {
1143 Join::Cross
1144 };
1145 {
1147 let mut bound = pre.clone();
1148 let mut steps = Vec::new();
1149 for (n, i) in ord.iter().enumerate() {
1150 let est = plan::estimate(&enc[*i], &bound, self.stats);
1151 let vars: Vec<usize> = enc[*i]
1152 .iter()
1153 .filter_map(|p| match p {
1154 Pos::Var(v) => Some(*v),
1155 Pos::Const(_) => None,
1156 })
1157 .collect();
1158 if n > 0 && !vars.iter().any(|v| bound.contains(v)) {
1159 self.notes.push(format!(
1160 "warning: Cartesian product: triple pattern {} shares no variable with the patterns before it",
1161 patterns[*i]
1162 ));
1163 }
1164 steps.push(format!("{} (~{est:.0} rows)", patterns[*i]));
1165 bound.extend(vars);
1166 }
1167 self.notes.push(format!(
1168 "join order ({}): {}",
1169 if self.options.sqlite_planner {
1170 "SQLite planner"
1171 } else if self.stats.available {
1172 "statistics"
1173 } else {
1174 "heuristics, run optimize() for statistics"
1175 },
1176 steps.join(" → ")
1177 ));
1178 }
1179 let mut b = Block::default();
1180 for i in ord {
1181 let [s, p, o] = enc[i];
1182 self.quad_access(&mut b, join.clone(), s, p, o)?;
1183 }
1184 while let Some((v, tp)) = self.pending_triples.pop() {
1187 let Some(key) = b.cols.get(&v).and_then(|x| x.col.key().map(str::to_string)) else {
1188 return Err(Error::unsupported("unbound triple term pattern"));
1189 };
1190 let t = self.alias("tt");
1191 b.from.push(FromItem {
1192 join: Join::Inner,
1193 item: format!("triple_terms {t}"),
1194 });
1195 b.wheres.push(format!("{t}.id = {key}"));
1196 let sp = self.pos(&tp.subject, &mut bnodes)?;
1197 let pp = self.pos_nn(&tp.predicate)?;
1198 let op = self.pos(&tp.object, &mut bnodes)?;
1199 self.bind_pos(&mut b, &format!("{t}.s"), sp)?;
1200 self.bind_pos(&mut b, &format!("{t}.p"), pp)?;
1201 self.bind_pos(&mut b, &format!("{t}.o"), op)?;
1202 }
1203 Ok(b)
1204 }
1205
1206 pub(crate) fn join(&mut self, a: Block, b: Block) -> Result<Block> {
1208 let mut a = self.plain(a);
1209 let b = self.plain(b);
1210 if a.is_unit() {
1211 return Ok(b);
1212 }
1213 if b.is_unit() {
1214 return Ok(a);
1215 }
1216 for (idx, bb) in b.cols {
1217 match a.cols.get(&idx).cloned() {
1218 None => {
1219 a.cols.insert(idx, bb);
1220 }
1221 Some(ab) => {
1222 let (cond, merged) = Self::unify(&ab, &bb);
1223 a.wheres.push(cond);
1224 a.cols.insert(idx, merged);
1225 }
1226 }
1227 }
1228 for mut item in b.from {
1229 if a.from.is_empty() {
1230 item.join = Join::First;
1231 } else if item.join == Join::First {
1232 item.join = Join::Inner;
1233 }
1234 a.from.push(item);
1235 }
1236 a.wheres.extend(b.wheres);
1237 Ok(a)
1238 }
1239
1240 fn values(
1241 &mut self,
1242 variables: &[Variable],
1243 bindings: &[Vec<Option<GroundTerm>>],
1244 ) -> Result<Block> {
1245 let mut b = Block::default();
1246 if variables.is_empty() {
1247 match bindings.len() {
1248 0 => b.wheres.push("0".into()),
1249 1 => {}
1250 n => b.from.push(FromItem {
1251 join: Join::First,
1252 item: format!(
1253 "(VALUES {}) AS {}",
1254 vec!["(1)"; n].join(","),
1255 self.alias("u")
1256 ),
1257 }),
1258 }
1259 return Ok(b);
1260 }
1261 let idxs: Vec<usize> = variables.iter().map(|v| self.var(v)).collect();
1262 if bindings.is_empty() {
1263 b.wheres.push("0".into());
1264 for i in idxs {
1265 b.cols.insert(
1266 i,
1267 Binding {
1268 nullable: true,
1269 computed: true,
1270 ..Binding::id("NULL")
1271 },
1272 );
1273 }
1274 return Ok(b);
1275 }
1276 let mut rows = Vec::new();
1279 let mut nullable = vec![false; idxs.len()];
1280 for row in bindings {
1281 let mut vals = Vec::new();
1282 for (j, t) in row.iter().enumerate() {
1283 let fields = match t {
1284 None => {
1285 nullable[j] = true;
1286 val_fields(&V::null())
1287 }
1288 Some(t) => {
1289 let term = ground_to_term(t);
1290 let id = self.constant_id(&term)?;
1291 val_fields(&V::from_term(&term, id)?)
1292 }
1293 };
1294 vals.extend(fields);
1295 }
1296 rows.push(format!("({})", vals.join(",")));
1297 }
1298 let a = self.alias("vals");
1299 b.from.push(FromItem {
1300 join: Join::First,
1301 item: format!("(VALUES {}) AS {a}", rows.join(",")),
1302 });
1303 let n = VAL_FIELDS.len();
1304 for (j, i) in idxs.into_iter().enumerate() {
1305 let c = |k: usize| format!("{a}.column{}", j * n + k + 1);
1306 let v = V {
1307 id: Some(c(0)),
1308 kind: c(1),
1309 lex: c(2),
1310 dt: c(3),
1311 lang: c(4),
1312 num: c(5),
1313 nt: c(6),
1314 ts: c(7),
1315 boolv: c(8),
1316 stat: expr::Stat::Any,
1317 computed_num: false,
1318 decodable: false,
1319 aux: c(9),
1320 tz: "NULL".into(),
1321 };
1322 b.cols.insert(
1323 i,
1324 Binding {
1325 col: Col::Val(Box::new(v)),
1326 nullable: nullable[j],
1327 computed: false,
1328 correlated: false,
1329 },
1330 );
1331 }
1332 Ok(b)
1333 }
1334
1335 pub(crate) fn filter_conditions(
1337 &mut self,
1338 e: &Expression,
1339 cols: &BTreeMap<usize, Binding>,
1340 ) -> Result<Vec<String>> {
1341 if let Expression::And(a, b) = e {
1342 let mut out = self.filter_conditions(a, cols)?;
1343 out.extend(self.filter_conditions(b, cols)?);
1344 return Ok(out);
1345 }
1346 if let Expression::Equal(a, b) | Expression::SameTerm(a, b) = e {
1347 let pair = match (a.as_ref(), b.as_ref()) {
1348 (Expression::Variable(v), c) | (c, Expression::Variable(v)) => Some((v, c)),
1349 _ => None,
1350 };
1351 if let Some((v, c)) = pair {
1352 let t: Option<Term> = match c {
1353 Expression::NamedNode(n) => Some(n.clone().into()),
1354 Expression::Literal(l)
1355 if matches!(e, Expression::SameTerm(..))
1356 || l.language().is_some()
1357 || l.datatype() == oxrdf::vocab::xsd::STRING =>
1358 {
1359 Some(l.clone().into())
1360 }
1361 _ => None,
1362 };
1363 if let Some(t) = t {
1364 let idx = self.var(v);
1365 let binding = cols.get(&idx).or_else(|| self.outer_binding(idx)).cloned();
1366 if let Some(Binding {
1367 col: Col::Id(x), ..
1368 }) = binding
1369 {
1370 let id = self.constant_id(&t)?;
1372 return Ok(vec![format!("{x} = {id}")]);
1373 }
1374 }
1375 }
1376 }
1377 Ok(vec![self.expr_bool(e, cols)?])
1378 }
1379}
1380
1381fn ground_triple(tp: &TriplePattern) -> Option<oxrdf::Triple> {
1383 fn term(t: &TermPattern) -> Option<Term> {
1384 Some(match t {
1385 TermPattern::NamedNode(n) => n.clone().into(),
1386 TermPattern::Literal(l) => l.clone().into(),
1387 TermPattern::Triple(tp) => ground_triple(tp)?.into(),
1388 TermPattern::BlankNode(_) | TermPattern::Variable(_) => return None,
1389 })
1390 }
1391 let s = match term(&tp.subject)? {
1392 Term::NamedNode(n) => oxrdf::NamedOrBlankNode::from(n),
1393 _ => return None,
1394 };
1395 let NamedNodePattern::NamedNode(p) = &tp.predicate else {
1396 return None;
1397 };
1398 Some(oxrdf::Triple::new(s, p.clone(), term(&tp.object)?))
1399}
1400
1401fn has_quad_access(p: &GraphPattern) -> bool {
1402 match p {
1403 GraphPattern::Bgp { patterns } => !patterns.is_empty(),
1404 GraphPattern::Path { .. } => true,
1405 GraphPattern::Graph { .. } | GraphPattern::Values { .. } => false,
1406 GraphPattern::Join { left, right }
1407 | GraphPattern::LeftJoin { left, right, .. }
1408 | GraphPattern::Union { left, right }
1409 | GraphPattern::Minus { left, right } => has_quad_access(left) || has_quad_access(right),
1410 GraphPattern::Filter { inner, .. }
1411 | GraphPattern::Extend { inner, .. }
1412 | GraphPattern::OrderBy { inner, .. }
1413 | GraphPattern::Project { inner, .. }
1414 | GraphPattern::Distinct { inner }
1415 | GraphPattern::Reduced { inner }
1416 | GraphPattern::Slice { inner, .. }
1417 | GraphPattern::Group { inner, .. } => has_quad_access(inner),
1418 _ => true,
1419 }
1420}
1421
1422fn expression_variables(e: &Expression, out: &mut Vec<Variable>) {
1424 match e {
1425 Expression::Variable(v) | Expression::Bound(v) if !out.contains(v) => out.push(v.clone()),
1426 Expression::Or(a, b)
1427 | Expression::And(a, b)
1428 | Expression::Equal(a, b)
1429 | Expression::SameTerm(a, b)
1430 | Expression::Greater(a, b)
1431 | Expression::GreaterOrEqual(a, b)
1432 | Expression::Less(a, b)
1433 | Expression::LessOrEqual(a, b)
1434 | Expression::Add(a, b)
1435 | Expression::Subtract(a, b)
1436 | Expression::Multiply(a, b)
1437 | Expression::Divide(a, b) => {
1438 expression_variables(a, out);
1439 expression_variables(b, out);
1440 }
1441 Expression::UnaryPlus(a) | Expression::UnaryMinus(a) | Expression::Not(a) => {
1442 expression_variables(a, out)
1443 }
1444 Expression::In(a, l) => {
1445 expression_variables(a, out);
1446 for x in l {
1447 expression_variables(x, out);
1448 }
1449 }
1450 Expression::If(a, b, c) => {
1451 for x in [a, b, c] {
1452 expression_variables(x, out);
1453 }
1454 }
1455 Expression::Coalesce(l) | Expression::FunctionCall(_, l) => {
1456 for x in l {
1457 expression_variables(x, out);
1458 }
1459 }
1460 _ => {}
1461 }
1462}
1463
1464pub(crate) fn in_list(col: &str, ids: &[i64]) -> String {
1465 if ids.is_empty() {
1466 return "0".into();
1467 }
1468 format!(
1469 "{col} IN ({})",
1470 ids.iter()
1471 .map(ToString::to_string)
1472 .collect::<Vec<_>>()
1473 .join(",")
1474 )
1475}
1476
1477pub(crate) fn ground_to_term(t: &GroundTerm) -> Term {
1478 match t {
1479 GroundTerm::NamedNode(n) => n.clone().into(),
1480 GroundTerm::Literal(l) => l.clone().into(),
1481 GroundTerm::Triple(t) => oxrdf::Triple::new(
1482 t.subject.clone(),
1483 t.predicate.clone(),
1484 ground_to_term(&t.object),
1485 )
1486 .into(),
1487 }
1488}
1489
1490pub(crate) fn order_keys(v: &V) -> Vec<String> {
1494 use expr::{Stat, K_BNODE, K_IRI, K_TRIPLE};
1495 match v.stat {
1496 Stat::Numeric => vec![format!("({}) IS NOT NULL", v.kind), v.num.clone()],
1497 Stat::String => vec![format!("({}) IS NOT NULL", v.kind), v.lex.clone()],
1498 _ => vec![
1499 format!(
1500 "CASE WHEN ({k}) IS NULL THEN 0 WHEN ({k}) = {K_BNODE} THEN 1 WHEN ({k}) = {K_IRI} THEN 2 WHEN ({k}) = {K_TRIPLE} THEN 4 ELSE 3 END",
1501 k = v.kind
1502 ),
1503 format!("({}) IS NULL", v.num),
1504 v.num.clone(),
1505 v.lex.clone(),
1506 v.dt.clone(),
1507 v.lang.clone(),
1508 ]
1509 .into_iter()
1510 .chain(triple_order_keys(v))
1511 .collect(),
1512 }
1513}
1514
1515fn triple_order_keys(v: &V) -> Vec<String> {
1517 use expr::K_TRIPLE;
1518 let (Some(id), true) = (&v.id, v.decodable) else {
1519 return Vec::new();
1520 };
1521 vec![format!(
1522 "(CASE WHEN ({}) = {K_TRIPLE} THEN (SELECT sk FROM triple_terms WHERE id = {id}) END)",
1523 v.kind
1524 )]
1525}