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 let reads_aggregate = b.stage == Stage::Grouped
785 && expression.iter().any(|oe| {
786 let (OrderExpression::Asc(e) | OrderExpression::Desc(e)) = oe;
787 expr_mentions(e, &mut |v| {
788 self.vars
789 .get(v)
790 .and_then(|i| b.cols.get(i))
791 .is_some_and(|c| c.computed)
792 })
793 });
794 if b.stage > Stage::Grouped || reads_aggregate {
795 b = self.seal(b);
796 }
797 for oe in expression {
798 let (e, desc) = match oe {
799 OrderExpression::Asc(e) => (e, false),
800 OrderExpression::Desc(e) => (e, true),
801 };
802 let v = self.expr_term(e, &b.cols)?;
803 for k in order_keys(&v) {
804 b.order_by.push(if desc { format!("{k} DESC") } else { k });
805 }
806 }
807 b.stage = Stage::Ordered;
808 Ok(b)
809 }
810 GraphPattern::Project { inner, variables } => {
811 let graph_var = match self.scope {
815 GraphScope::Var(g) => Some(g),
816 _ => None,
817 };
818 let hidden = graph_var.map(|_| self.fresh_var("g"));
819 let saved = self.scope;
820 if let Some(h) = hidden {
821 self.scope = GraphScope::Var(h);
822 }
823 let r = self.pattern(inner);
824 self.scope = saved;
825 let mut b = r?;
826 if b.stage >= Stage::Distinct {
827 b = self.seal(b);
828 }
829 let hidden_binding = hidden.and_then(|h| b.cols.get(&h).cloned());
830 let mut cols = BTreeMap::new();
831 for v in variables {
832 let idx = self.var(v);
833 let bind = b.cols.remove(&idx).unwrap_or(Binding {
834 col: Col::Id("NULL".into()),
835 nullable: true,
836 computed: true,
837 correlated: false,
838 });
839 cols.insert(idx, bind);
840 }
841 b.cols = cols;
842 if let (Some(g), Some(h)) = (graph_var, hidden_binding) {
843 match b.cols.get(&g).and_then(|x| x.col.key().map(str::to_string)) {
844 Some(inner_g) => {
845 let hk = h.col.key().unwrap_or("NULL").to_string();
846 b.wheres.push(format!("{inner_g} = {hk}"));
847 }
848 None => {
849 b.cols.insert(g, h);
850 }
851 }
852 }
853 Ok(b)
854 }
855 GraphPattern::Distinct { inner } => {
856 let mut b = self.pattern(inner)?;
857 if b.stage >= Stage::Distinct {
858 b = self.seal(b);
859 }
860 b.distinct = true;
861 b.stage = Stage::Distinct;
862 Ok(b)
863 }
864 GraphPattern::Reduced { inner } => self.pattern(&GraphPattern::Distinct {
866 inner: inner.clone(),
867 }),
868 GraphPattern::Slice {
869 inner,
870 start,
871 length,
872 } => {
873 let mut b = self.pattern(inner)?;
874 if b.stage == Stage::Sliced {
875 b = self.seal(b);
876 }
877 b.offset = *start;
878 b.limit = *length;
879 b.stage = Stage::Sliced;
880 Ok(b)
881 }
882 other => self.pattern_ext(other),
883 }
884 }
885
886 fn pattern_ext(&mut self, p: &GraphPattern) -> Result<Block> {
888 self.pattern_m2(p)
889 }
890
891 pub(crate) fn unify(a: &Binding, b: &Binding) -> (String, Binding) {
894 let eq = match (a.col.key(), b.col.key()) {
895 (Some(x), Some(y)) => format!("{x} = {y}"),
896 _ => expr::same_term(&a.col.value(), &b.col.value()),
897 };
898 if !a.nullable && !b.nullable {
899 return (eq, a.clone());
900 }
901 let is_null = |x: &Binding| match &x.col {
902 Col::Id(i) => format!("{i} IS NULL"),
903 Col::Val(v) => format!("({}) IS NULL", v.kind),
904 };
905 let cond = format!("({eq} OR {} OR {})", is_null(a), is_null(b));
906 let col = match (&a.col, &b.col) {
907 (Col::Id(x), Col::Id(y)) => Col::Id(format!("COALESCE({x}, {y})")),
908 _ => {
909 let (av, bv) = (a.col.value(), b.col.value());
910 Col::Val(Box::new(Self::choose(
911 &format!("(({}) IS NOT NULL)", av.kind),
912 &av,
913 &bv,
914 )))
915 }
916 };
917 (
918 cond,
919 Binding {
920 col,
921 nullable: a.nullable && b.nullable,
922 computed: true,
923 correlated: a.correlated || b.correlated,
924 },
925 )
926 }
927
928 fn graph_list_block(&mut self, v: usize) -> Block {
929 let a = self.alias("gr");
930 let mut b = Block::default();
931 b.from.push(FromItem {
932 join: Join::First,
933 item: format!("graphs {a}"),
934 });
935 if let Some(named) = &self.dataset.named {
936 b.wheres.push(in_list(&format!("{a}.id"), named));
937 }
938 b.cols.insert(v, Binding::id(format!("{a}.id")));
939 b
940 }
941
942 fn pos(&mut self, t: &TermPattern, bnodes: &mut HashMap<String, usize>) -> Result<Pos> {
943 Ok(match t {
944 TermPattern::NamedNode(n) => Pos::Const(self.constant_id(&n.clone().into())?),
945 TermPattern::Literal(l) => Pos::Const(self.constant_id(&l.clone().into())?),
946 TermPattern::Variable(v) => Pos::Var(self.var(v)),
947 TermPattern::BlankNode(b) => {
948 let _ = bnodes;
951 Pos::Var(self.var(&Variable::new_unchecked(format!("\u{1}b{}", b.as_str()))))
952 }
953 TermPattern::Triple(tp) => {
954 if let Some(t) = ground_triple(tp) {
955 Pos::Const(self.constant_id(&t.into())?)
956 } else {
957 let v = self.fresh_var("t");
958 self.pending_triples.push((v, (**tp).clone()));
959 Pos::Var(v)
960 }
961 }
962 })
963 }
964
965 fn pos_nn(&mut self, p: &NamedNodePattern) -> Result<Pos> {
966 Ok(match p {
967 NamedNodePattern::NamedNode(n) => Pos::Const(self.constant_id(&n.clone().into())?),
968 NamedNodePattern::Variable(v) => Pos::Var(self.var(v)),
969 })
970 }
971
972 pub(crate) fn bind_pos(&mut self, b: &mut Block, colsql: &str, pos: Pos) -> Result<()> {
974 match pos {
975 Pos::Const(id) => b.wheres.push(format!("{colsql} = {id}")),
976 Pos::Var(v) => {
977 if let Some(existing) = b.cols.get(&v) {
978 let cond = match existing.col.key() {
979 Some(x) => format!("{colsql} = {x}"),
980 None => expr::same_term(&V::from_id(colsql), &existing.col.value()),
981 };
982 b.wheres.push(if existing.nullable {
983 let null = match &existing.col {
984 Col::Id(x) => format!("{x} IS NULL"),
985 Col::Val(v) => format!("({}) IS NULL", v.kind),
986 };
987 format!("({null} OR {cond})")
988 } else {
989 cond
990 });
991 return Ok(());
992 }
993 let mut correlated = false;
994 if let Some(outer) = self.outer_binding(v).cloned() {
995 let (cond, _) = Self::unify(&Binding::id(colsql), &outer);
996 b.wheres.push(cond);
997 correlated = true;
998 }
999 b.cols.insert(
1000 v,
1001 Binding {
1002 correlated,
1003 ..Binding::id(colsql)
1004 },
1005 );
1006 }
1007 }
1008 Ok(())
1009 }
1010
1011 pub(crate) fn graph_pos(&mut self, b: &mut Block, q: &str, selective: bool) -> Result<()> {
1017 let plus = if selective { "+" } else { "" };
1018 match self.scope {
1019 GraphScope::Default => match self.dataset.default.clone() {
1020 DefaultGraph::Zero => b.wheres.push(format!("{plus}{q}.g = {DEFAULT_GRAPH_ID}")),
1021 DefaultGraph::List(l) if l.is_empty() => b.wheres.push("0".into()),
1022 DefaultGraph::List(l) if l.len() == 1 => {
1023 b.wheres.push(format!("{plus}{q}.g = {}", l[0]))
1024 }
1025 DefaultGraph::List(l) => {
1026 b.wheres.push(in_list(&format!("{q}.g"), &l));
1027 let d = self.alias("d");
1028 b.wheres.push(format!(
1029 "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 {})",
1030 self.entailment().base(),
1031 in_list(&format!("{d}.g"), &l)
1032 ));
1033 }
1034 DefaultGraph::Union => {
1035 let d = self.alias("d");
1036 let base = self.entailment().base();
1037 b.wheres.push(format!(
1038 "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)"
1039 ));
1040 }
1041 },
1042 GraphScope::Fixed(id) => {
1043 if self
1044 .dataset
1045 .named
1046 .as_ref()
1047 .is_some_and(|n| !n.contains(&id))
1048 {
1049 b.wheres.push("0".into());
1050 }
1051 b.wheres.push(format!("{plus}{q}.g = {id}"));
1052 }
1053 GraphScope::Var(v) => {
1054 b.wheres.push(format!("{q}.g <> {DEFAULT_GRAPH_ID}"));
1055 if let Some(named) = self.dataset.named.clone() {
1056 b.wheres.push(in_list(&format!("{q}.g"), &named));
1057 }
1058 self.bind_pos(b, &format!("{q}.g"), Pos::Var(v))?;
1059 }
1060 }
1061 Ok(())
1062 }
1063
1064 pub(crate) fn entailment(&self) -> Entailment<'a> {
1066 Entailment {
1067 reasoning: self.options.reasoning,
1068 inferred: self.options.include_inferred,
1069 transitive: &self.stats.transitive,
1070 max_compound: self.caps.max_compound_select,
1071 }
1072 }
1073
1074 pub(crate) fn quad_access(
1076 &mut self,
1077 b: &mut Block,
1078 join: Join,
1079 s: Pos,
1080 p: Pos,
1081 o: Pos,
1082 ) -> Result<String> {
1083 let q = self.alias("q");
1084 let bound = |pos: Pos, b: &Block, me: &Self| match pos {
1085 Pos::Const(_) => true,
1086 Pos::Var(v) => b.cols.contains_key(&v) || me.outer_binding(v).is_some(),
1087 };
1088 let selective = bound(s, b, self) || bound(p, b, self) || bound(o, b, self);
1089 let ent = self.entailment();
1090 let merge = match (&self.scope, &self.dataset.default) {
1093 (GraphScope::Default, DefaultGraph::Union) if ent.active() => {
1094 Some(GraphFilter::Merge(None))
1095 }
1096 (GraphScope::Default, DefaultGraph::List(l)) if ent.active() && l.len() > 1 => {
1097 Some(GraphFilter::Merge(Some(l.clone())))
1098 }
1099 _ => None,
1100 };
1101 let source = if ent.active() {
1102 let c = |p: Pos| match p {
1103 Pos::Const(id) => Some(id),
1104 Pos::Var(_) => None,
1105 };
1106 ent.source(
1107 c(s),
1108 c(p),
1109 c(o),
1110 merge.as_ref().unwrap_or(&GraphFilter::Keep),
1111 )
1112 } else {
1113 "quads".into()
1114 };
1115 b.from.push(FromItem {
1116 join: if b.from.is_empty() { Join::First } else { join },
1117 item: format!("{source} {q}"),
1118 });
1119 self.bind_pos(b, &format!("{q}.s"), s)?;
1120 self.bind_pos(b, &format!("{q}.p"), p)?;
1121 self.bind_pos(b, &format!("{q}.o"), o)?;
1122 if merge.is_none() {
1123 self.graph_pos(b, &q, selective)?;
1124 }
1125 Ok(q)
1126 }
1127
1128 fn bgp(&mut self, patterns: &[TriplePattern]) -> Result<Block> {
1129 if patterns.is_empty() {
1130 return Ok(Block::default());
1131 }
1132 let mut bnodes = HashMap::new();
1133 let mut enc = Vec::with_capacity(patterns.len());
1134 for tp in patterns {
1135 enc.push([
1136 self.pos(&tp.subject, &mut bnodes)?,
1137 self.pos_nn(&tp.predicate)?,
1138 self.pos(&tp.object, &mut bnodes)?,
1139 ]);
1140 }
1141 let pre: HashSet<usize> = self
1142 .outer
1143 .iter()
1144 .flat_map(|m| m.keys().copied())
1145 .chain(self.plan_hint.iter().flatten().copied())
1146 .collect();
1147 let ord: Vec<usize> = if self.options.sqlite_planner {
1148 (0..enc.len()).collect()
1149 } else {
1150 plan::order(&enc, &pre, self.stats)
1151 };
1152 let join = if self.options.sqlite_planner {
1153 Join::Inner
1154 } else {
1155 Join::Cross
1156 };
1157 {
1159 let mut bound = pre.clone();
1160 let mut steps = Vec::new();
1161 for (n, i) in ord.iter().enumerate() {
1162 let est = plan::estimate(&enc[*i], &bound, self.stats);
1163 let vars: Vec<usize> = enc[*i]
1164 .iter()
1165 .filter_map(|p| match p {
1166 Pos::Var(v) => Some(*v),
1167 Pos::Const(_) => None,
1168 })
1169 .collect();
1170 if n > 0 && !vars.iter().any(|v| bound.contains(v)) {
1171 self.notes.push(format!(
1172 "warning: Cartesian product: triple pattern {} shares no variable with the patterns before it",
1173 patterns[*i]
1174 ));
1175 }
1176 steps.push(format!("{} (~{est:.0} rows)", patterns[*i]));
1177 bound.extend(vars);
1178 }
1179 self.notes.push(format!(
1180 "join order ({}): {}",
1181 if self.options.sqlite_planner {
1182 "SQLite planner"
1183 } else if self.stats.available {
1184 "statistics"
1185 } else {
1186 "heuristics, run optimize() for statistics"
1187 },
1188 steps.join(" → ")
1189 ));
1190 }
1191 let mut b = Block::default();
1192 for i in ord {
1193 let [s, p, o] = enc[i];
1194 self.quad_access(&mut b, join.clone(), s, p, o)?;
1195 }
1196 while let Some((v, tp)) = self.pending_triples.pop() {
1199 let Some(key) = b.cols.get(&v).and_then(|x| x.col.key().map(str::to_string)) else {
1200 return Err(Error::unsupported("unbound triple term pattern"));
1201 };
1202 let t = self.alias("tt");
1203 b.from.push(FromItem {
1204 join: Join::Inner,
1205 item: format!("triple_terms {t}"),
1206 });
1207 b.wheres.push(format!("{t}.id = {key}"));
1208 let sp = self.pos(&tp.subject, &mut bnodes)?;
1209 let pp = self.pos_nn(&tp.predicate)?;
1210 let op = self.pos(&tp.object, &mut bnodes)?;
1211 self.bind_pos(&mut b, &format!("{t}.s"), sp)?;
1212 self.bind_pos(&mut b, &format!("{t}.p"), pp)?;
1213 self.bind_pos(&mut b, &format!("{t}.o"), op)?;
1214 }
1215 Ok(b)
1216 }
1217
1218 pub(crate) fn join(&mut self, a: Block, b: Block) -> Result<Block> {
1220 let mut a = self.plain(a);
1221 let b = self.plain(b);
1222 if a.is_unit() {
1223 return Ok(b);
1224 }
1225 if b.is_unit() {
1226 return Ok(a);
1227 }
1228 for (idx, bb) in b.cols {
1229 match a.cols.get(&idx).cloned() {
1230 None => {
1231 a.cols.insert(idx, bb);
1232 }
1233 Some(ab) => {
1234 let (cond, merged) = Self::unify(&ab, &bb);
1235 a.wheres.push(cond);
1236 a.cols.insert(idx, merged);
1237 }
1238 }
1239 }
1240 for mut item in b.from {
1241 if a.from.is_empty() {
1242 item.join = Join::First;
1243 } else if item.join == Join::First {
1244 item.join = Join::Inner;
1245 }
1246 a.from.push(item);
1247 }
1248 a.wheres.extend(b.wheres);
1249 Ok(a)
1250 }
1251
1252 fn values(
1253 &mut self,
1254 variables: &[Variable],
1255 bindings: &[Vec<Option<GroundTerm>>],
1256 ) -> Result<Block> {
1257 let mut b = Block::default();
1258 if variables.is_empty() {
1259 match bindings.len() {
1260 0 => b.wheres.push("0".into()),
1261 1 => {}
1262 n => b.from.push(FromItem {
1263 join: Join::First,
1264 item: format!(
1265 "(VALUES {}) AS {}",
1266 vec!["(1)"; n].join(","),
1267 self.alias("u")
1268 ),
1269 }),
1270 }
1271 return Ok(b);
1272 }
1273 let idxs: Vec<usize> = variables.iter().map(|v| self.var(v)).collect();
1274 if bindings.is_empty() {
1275 b.wheres.push("0".into());
1276 for i in idxs {
1277 b.cols.insert(
1278 i,
1279 Binding {
1280 nullable: true,
1281 computed: true,
1282 ..Binding::id("NULL")
1283 },
1284 );
1285 }
1286 return Ok(b);
1287 }
1288 let mut rows = Vec::new();
1291 let mut nullable = vec![false; idxs.len()];
1292 for row in bindings {
1293 let mut vals = Vec::new();
1294 for (j, t) in row.iter().enumerate() {
1295 let fields = match t {
1296 None => {
1297 nullable[j] = true;
1298 val_fields(&V::null())
1299 }
1300 Some(t) => {
1301 let term = ground_to_term(t);
1302 let id = self.constant_id(&term)?;
1303 val_fields(&V::from_term(&term, id)?)
1304 }
1305 };
1306 vals.extend(fields);
1307 }
1308 rows.push(format!("({})", vals.join(",")));
1309 }
1310 let a = self.alias("vals");
1311 b.from.push(FromItem {
1312 join: Join::First,
1313 item: format!("(VALUES {}) AS {a}", rows.join(",")),
1314 });
1315 let n = VAL_FIELDS.len();
1316 for (j, i) in idxs.into_iter().enumerate() {
1317 let c = |k: usize| format!("{a}.column{}", j * n + k + 1);
1318 let v = V {
1319 id: Some(c(0)),
1320 kind: c(1),
1321 lex: c(2),
1322 dt: c(3),
1323 lang: c(4),
1324 num: c(5),
1325 nt: c(6),
1326 ts: c(7),
1327 boolv: c(8),
1328 stat: expr::Stat::Any,
1329 computed_num: false,
1330 decodable: false,
1331 aux: c(9),
1332 tz: "NULL".into(),
1333 };
1334 b.cols.insert(
1335 i,
1336 Binding {
1337 col: Col::Val(Box::new(v)),
1338 nullable: nullable[j],
1339 computed: false,
1340 correlated: false,
1341 },
1342 );
1343 }
1344 Ok(b)
1345 }
1346
1347 pub(crate) fn filter_conditions(
1349 &mut self,
1350 e: &Expression,
1351 cols: &BTreeMap<usize, Binding>,
1352 ) -> Result<Vec<String>> {
1353 if let Expression::And(a, b) = e {
1354 let mut out = self.filter_conditions(a, cols)?;
1355 out.extend(self.filter_conditions(b, cols)?);
1356 return Ok(out);
1357 }
1358 if let Expression::Equal(a, b) | Expression::SameTerm(a, b) = e {
1359 let pair = match (a.as_ref(), b.as_ref()) {
1360 (Expression::Variable(v), c) | (c, Expression::Variable(v)) => Some((v, c)),
1361 _ => None,
1362 };
1363 if let Some((v, c)) = pair {
1364 let t: Option<Term> = match c {
1365 Expression::NamedNode(n) => Some(n.clone().into()),
1366 Expression::Literal(l)
1367 if matches!(e, Expression::SameTerm(..))
1368 || l.language().is_some()
1369 || l.datatype() == oxrdf::vocab::xsd::STRING =>
1370 {
1371 Some(l.clone().into())
1372 }
1373 _ => None,
1374 };
1375 if let Some(t) = t {
1376 let idx = self.var(v);
1377 let binding = cols.get(&idx).or_else(|| self.outer_binding(idx)).cloned();
1378 if let Some(Binding {
1379 col: Col::Id(x), ..
1380 }) = binding
1381 {
1382 let id = self.constant_id(&t)?;
1384 return Ok(vec![format!("{x} = {id}")]);
1385 }
1386 }
1387 }
1388 }
1389 Ok(vec![self.expr_bool(e, cols)?])
1390 }
1391}
1392
1393fn ground_triple(tp: &TriplePattern) -> Option<oxrdf::Triple> {
1395 fn term(t: &TermPattern) -> Option<Term> {
1396 Some(match t {
1397 TermPattern::NamedNode(n) => n.clone().into(),
1398 TermPattern::Literal(l) => l.clone().into(),
1399 TermPattern::Triple(tp) => ground_triple(tp)?.into(),
1400 TermPattern::BlankNode(_) | TermPattern::Variable(_) => return None,
1401 })
1402 }
1403 let s = match term(&tp.subject)? {
1404 Term::NamedNode(n) => oxrdf::NamedOrBlankNode::from(n),
1405 _ => return None,
1406 };
1407 let NamedNodePattern::NamedNode(p) = &tp.predicate else {
1408 return None;
1409 };
1410 Some(oxrdf::Triple::new(s, p.clone(), term(&tp.object)?))
1411}
1412
1413fn has_quad_access(p: &GraphPattern) -> bool {
1414 match p {
1415 GraphPattern::Bgp { patterns } => !patterns.is_empty(),
1416 GraphPattern::Path { .. } => true,
1417 GraphPattern::Graph { .. } | GraphPattern::Values { .. } => false,
1418 GraphPattern::Join { left, right }
1419 | GraphPattern::LeftJoin { left, right, .. }
1420 | GraphPattern::Union { left, right }
1421 | GraphPattern::Minus { left, right } => has_quad_access(left) || has_quad_access(right),
1422 GraphPattern::Filter { inner, .. }
1423 | GraphPattern::Extend { inner, .. }
1424 | GraphPattern::OrderBy { inner, .. }
1425 | GraphPattern::Project { inner, .. }
1426 | GraphPattern::Distinct { inner }
1427 | GraphPattern::Reduced { inner }
1428 | GraphPattern::Slice { inner, .. }
1429 | GraphPattern::Group { inner, .. } => has_quad_access(inner),
1430 _ => true,
1431 }
1432}
1433
1434fn expression_variables(e: &Expression, out: &mut Vec<Variable>) {
1436 match e {
1437 Expression::Variable(v) | Expression::Bound(v) if !out.contains(v) => out.push(v.clone()),
1438 Expression::Or(a, b)
1439 | Expression::And(a, b)
1440 | Expression::Equal(a, b)
1441 | Expression::SameTerm(a, b)
1442 | Expression::Greater(a, b)
1443 | Expression::GreaterOrEqual(a, b)
1444 | Expression::Less(a, b)
1445 | Expression::LessOrEqual(a, b)
1446 | Expression::Add(a, b)
1447 | Expression::Subtract(a, b)
1448 | Expression::Multiply(a, b)
1449 | Expression::Divide(a, b) => {
1450 expression_variables(a, out);
1451 expression_variables(b, out);
1452 }
1453 Expression::UnaryPlus(a) | Expression::UnaryMinus(a) | Expression::Not(a) => {
1454 expression_variables(a, out)
1455 }
1456 Expression::In(a, l) => {
1457 expression_variables(a, out);
1458 for x in l {
1459 expression_variables(x, out);
1460 }
1461 }
1462 Expression::If(a, b, c) => {
1463 for x in [a, b, c] {
1464 expression_variables(x, out);
1465 }
1466 }
1467 Expression::Coalesce(l) | Expression::FunctionCall(_, l) => {
1468 for x in l {
1469 expression_variables(x, out);
1470 }
1471 }
1472 _ => {}
1473 }
1474}
1475
1476pub(crate) fn in_list(col: &str, ids: &[i64]) -> String {
1477 if ids.is_empty() {
1478 return "0".into();
1479 }
1480 format!(
1481 "{col} IN ({})",
1482 ids.iter()
1483 .map(ToString::to_string)
1484 .collect::<Vec<_>>()
1485 .join(",")
1486 )
1487}
1488
1489pub(crate) fn ground_to_term(t: &GroundTerm) -> Term {
1490 match t {
1491 GroundTerm::NamedNode(n) => n.clone().into(),
1492 GroundTerm::Literal(l) => l.clone().into(),
1493 GroundTerm::Triple(t) => oxrdf::Triple::new(
1494 t.subject.clone(),
1495 t.predicate.clone(),
1496 ground_to_term(&t.object),
1497 )
1498 .into(),
1499 }
1500}
1501
1502pub(crate) fn order_keys(v: &V) -> Vec<String> {
1506 use expr::{Stat, K_BNODE, K_IRI, K_TRIPLE};
1507 match v.stat {
1508 Stat::Numeric => vec![format!("({}) IS NOT NULL", v.kind), v.num.clone()],
1509 Stat::String => vec![format!("({}) IS NOT NULL", v.kind), v.lex.clone()],
1510 _ => vec![
1511 format!(
1512 "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",
1513 k = v.kind
1514 ),
1515 format!("({}) IS NULL", v.num),
1516 v.num.clone(),
1517 v.lex.clone(),
1518 v.dt.clone(),
1519 v.lang.clone(),
1520 ]
1521 .into_iter()
1522 .chain(triple_order_keys(v))
1523 .collect(),
1524 }
1525}
1526
1527fn triple_order_keys(v: &V) -> Vec<String> {
1529 use expr::K_TRIPLE;
1530 let (Some(id), true) = (&v.id, v.decodable) else {
1531 return Vec::new();
1532 };
1533 vec![format!(
1534 "(CASE WHEN ({}) = {K_TRIPLE} THEN (SELECT sk FROM triple_terms WHERE id = {id}) END)",
1535 v.kind
1536 )]
1537}
1538
1539fn expr_mentions(e: &Expression, f: &mut impl FnMut(&Variable) -> bool) -> bool {
1541 match e {
1542 Expression::NamedNode(_) | Expression::Literal(_) => false,
1543 Expression::Variable(v) | Expression::Bound(v) => f(v),
1544 Expression::Or(a, b)
1545 | Expression::And(a, b)
1546 | Expression::Equal(a, b)
1547 | Expression::SameTerm(a, b)
1548 | Expression::Greater(a, b)
1549 | Expression::GreaterOrEqual(a, b)
1550 | Expression::Less(a, b)
1551 | Expression::LessOrEqual(a, b)
1552 | Expression::Add(a, b)
1553 | Expression::Subtract(a, b)
1554 | Expression::Multiply(a, b)
1555 | Expression::Divide(a, b) => expr_mentions(a, f) || expr_mentions(b, f),
1556 Expression::UnaryPlus(a) | Expression::UnaryMinus(a) | Expression::Not(a) => {
1557 expr_mentions(a, f)
1558 }
1559 Expression::In(a, l) => expr_mentions(a, f) || l.iter().any(|x| expr_mentions(x, f)),
1560 Expression::If(a, b, c) => {
1561 expr_mentions(a, f) || expr_mentions(b, f) || expr_mentions(c, f)
1562 }
1563 Expression::Coalesce(l) | Expression::FunctionCall(_, l) => {
1564 l.iter().any(|x| expr_mentions(x, f))
1565 }
1566 Expression::Exists(_) => true,
1567 }
1568}