surrealdb_core/idx/planner/
tree.rs

1use std::collections::HashMap;
2use std::hash::Hash;
3use std::ops::Deref;
4use std::sync::Arc;
5
6use anyhow::Result;
7use reblessive::tree::Stk;
8
9use crate::catalog::{DatabaseId, NamespaceId};
10use crate::expr::index::Index;
11use crate::expr::operator::NearestNeighbor;
12use crate::expr::order::{OrderList, Ordering};
13use crate::expr::statements::{DefineFieldStatement, DefineIndexStatement};
14use crate::expr::{
15	BinaryOperator, Cond, Expr, FlowResultExt as _, Ident, Idiom, Kind, Literal, Order, Part, With,
16};
17use crate::idx::planner::StatementContext;
18use crate::idx::planner::executor::{
19	KnnBruteForceExpression, KnnBruteForceExpressions, KnnExpressions,
20};
21use crate::idx::planner::plan::{IndexOperator, IndexOption};
22use crate::idx::planner::rewriter::KnnConditionRewriter;
23use crate::kvs::Transaction;
24use crate::val::{Array, Number, Value};
25
26pub(super) struct Tree {
27	pub(super) root: Option<Node>,
28	pub(super) index_map: IndexesMap,
29	pub(super) with_indexes: Option<Vec<IndexReference>>,
30	pub(super) knn_expressions: KnnExpressions,
31	pub(super) knn_brute_force_expressions: KnnBruteForceExpressions,
32	pub(super) knn_condition: Option<Cond>,
33	/// Is every expression backed by an index?
34	pub(super) all_expressions_with_index: bool,
35	/// Does the whole query contain only AND relations?
36	pub(super) all_and: bool,
37	/// Does a group contain only AND relations?
38	pub(super) all_and_groups: HashMap<GroupRef, bool>,
39}
40
41impl Tree {
42	/// Traverse all the conditions and extract every expression
43	/// that can be resolved by an index.
44	pub(super) async fn build<'a>(
45		stk: &mut Stk,
46		stm_ctx: &'a StatementContext<'a>,
47		table: &'a Ident,
48	) -> Result<Self> {
49		let mut b = TreeBuilder::new(stm_ctx, table);
50		if let Some(cond) = stm_ctx.cond {
51			b.eval_cond(stk, cond).await?;
52		}
53		b.eval_order().await?;
54		Ok(Self {
55			root: b.root,
56			index_map: b.index_map,
57			with_indexes: b.with_indexes,
58			knn_expressions: b.knn_expressions,
59			knn_brute_force_expressions: b.knn_brute_force_expressions,
60			knn_condition: b.knn_condition,
61			all_expressions_with_index: b.leaf_nodes_count > 0
62				&& b.leaf_nodes_with_index_count == b.leaf_nodes_count,
63			all_and: b.all_and.unwrap_or(true),
64			all_and_groups: b.all_and_groups,
65		})
66	}
67}
68
69struct TreeBuilder<'a> {
70	ctx: &'a StatementContext<'a>,
71	table: &'a Ident,
72	first_order: Option<&'a Order>,
73	schemas: HashMap<Ident, SchemaCache>,
74	idioms_indexes: HashMap<Ident, HashMap<Arc<Idiom>, LocalIndexRefs>>,
75	resolved_expressions: HashMap<Arc<Expr>, ResolvedExpression>,
76	resolved_idioms: HashMap<Arc<Idiom>, Node>,
77	index_map: IndexesMap,
78	with_indexes: Option<Vec<IndexReference>>,
79	knn_brute_force_expressions: HashMap<Arc<Expr>, KnnBruteForceExpression>,
80	knn_expressions: KnnExpressions,
81	idioms_record_options: HashMap<Arc<Idiom>, RecordOptions>,
82	//group_sequence: GroupRef,
83	root: Option<Node>,
84	knn_condition: Option<Cond>,
85	leaf_nodes_count: usize,
86	leaf_nodes_with_index_count: usize,
87	all_and: Option<bool>,
88	all_and_groups: HashMap<GroupRef, bool>,
89}
90
91#[derive(Debug, Clone, Eq, PartialEq, Hash)]
92pub(super) struct RecordOptions {
93	locals: LocalIndexRefs,
94	remotes: RemoteIndexRefs,
95}
96
97pub(super) type IdiomCol = usize;
98pub(super) type LocalIndexRefs = Vec<(IndexReference, IdiomCol)>;
99pub(super) type RemoteIndexRefs = Arc<Vec<(Arc<Idiom>, LocalIndexRefs)>>;
100
101impl<'a> TreeBuilder<'a> {
102	fn new(ctx: &'a StatementContext<'a>, table: &'a Ident) -> Self {
103		let with_indexes = match ctx.with {
104			Some(With::Index(ixs)) => Some(Vec::with_capacity(ixs.len())),
105			_ => None,
106		};
107		let first_order = if let Some(Ordering::Order(OrderList(o))) = ctx.order {
108			o.first()
109		} else {
110			None
111		};
112		Self {
113			ctx,
114			table,
115			first_order,
116			schemas: Default::default(),
117			idioms_indexes: Default::default(),
118			resolved_expressions: Default::default(),
119			resolved_idioms: Default::default(),
120			index_map: Default::default(),
121			with_indexes,
122			knn_brute_force_expressions: Default::default(),
123			knn_expressions: Default::default(),
124			idioms_record_options: Default::default(),
125			//group_sequence: 0,
126			root: None,
127			knn_condition: None,
128			all_and: None,
129			all_and_groups: Default::default(),
130			leaf_nodes_count: 0,
131			leaf_nodes_with_index_count: 0,
132		}
133	}
134
135	async fn lazy_load_schema_resolver(&mut self, tx: &Transaction, table: &Ident) -> Result<()> {
136		if self.schemas.contains_key(table) {
137			return Ok(());
138		}
139		let (ns, db) = self.ctx.ctx.expect_ns_db_ids(self.ctx.opt).await?;
140		let l = SchemaCache::new(ns, db, table, tx).await?;
141		self.schemas.insert(table.clone(), l);
142		Ok(())
143	}
144
145	async fn eval_order(&mut self) -> Result<()> {
146		if let Some(o) = self.first_order {
147			if let Node::IndexedField(id, irf) = self.resolve_idiom(&o.value).await? {
148				for (ixr, id_col) in &irf {
149					if *id_col == 0 {
150						self.index_map.order_limit = Some(IndexOption::new(
151							ixr.clone(),
152							Some(id),
153							IdiomPosition::None,
154							IndexOperator::Order(!o.direction),
155						));
156						break;
157					}
158				}
159			}
160		}
161		Ok(())
162	}
163
164	async fn eval_cond(&mut self, stk: &mut Stk, cond: &Cond) -> Result<()> {
165		self.root = Some(self.eval_value(stk, 0, &cond.0).await?);
166		self.knn_condition = if self.knn_expressions.is_empty() {
167			None
168		} else {
169			KnnConditionRewriter::build(&self.knn_expressions, cond)
170		};
171		Ok(())
172	}
173
174	async fn eval_value(&mut self, stk: &mut Stk, group: GroupRef, v: &Expr) -> Result<Node> {
175		match v {
176			Expr::Binary {
177				left,
178				op,
179				right,
180			} => {
181				// Did we already compute the same expression?
182				if let Some(re) = self.resolved_expressions.get(v).cloned() {
183					return Ok(re.into());
184				}
185				self.check_boolean_operator(group, op);
186				let left_node = stk.run(|stk| self.eval_value(stk, group, left)).await?;
187				let right_node = stk.run(|stk| self.eval_value(stk, group, right)).await?;
188				// If both values are computable, then we can delegate the computation to the
189				// parent
190				if left_node == Node::Computable && right_node == Node::Computable {
191					return Ok(Node::Computable);
192				}
193				let exp = Arc::new(v.clone());
194				let left = Arc::new(self.compute(stk, left, left_node).await?);
195				let right = Arc::new(self.compute(stk, right, right_node).await?);
196				let io = if let Some((id, local_irs, remote_irs)) = left.is_indexed_field() {
197					self.lookup_index_options(
198						op,
199						id,
200						&right,
201						&exp,
202						IdiomPosition::Left,
203						local_irs,
204						remote_irs,
205					)?
206				} else if let Some((id, local_irs, remote_irs)) = right.is_indexed_field() {
207					self.lookup_index_options(
208						op,
209						id,
210						&left,
211						&exp,
212						IdiomPosition::Right,
213						local_irs,
214						remote_irs,
215					)?
216				} else {
217					None
218				};
219				if let Some(id) = left.is_field() {
220					self.eval_bruteforce_knn(id, &right, &exp)?;
221				} else if let Some(id) = right.is_field() {
222					self.eval_bruteforce_knn(id, &left, &exp)?;
223				}
224				self.check_leaf_node_with_index(io.as_ref());
225				let re = ResolvedExpression {
226					group,
227					exp: exp.clone(),
228					io,
229					left: left.clone(),
230					right: right.clone(),
231				};
232				self.resolved_expressions.insert(exp, re.clone());
233				Ok(re.into())
234			}
235			Expr::Idiom(i) => self.eval_idiom(stk, group, i).await,
236			Expr::Literal(
237				Literal::Integer(_)
238				| Literal::Bool(_)
239				| Literal::Strand(_)
240				| Literal::RecordId(_)
241				| Literal::Duration(_)
242				| Literal::Uuid(_)
243				| Literal::Datetime(_)
244				| Literal::None
245				| Literal::Null
246				| Literal::Decimal(_)
247				| Literal::Float(_),
248			)
249			| Expr::Param(_)
250			| Expr::FunctionCall(_) => {
251				self.leaf_nodes_count += 1;
252				Ok(Node::Computable)
253			}
254			Expr::Literal(Literal::Array(a)) => self.eval_array(stk, a).await,
255			_ => Ok(Node::Unsupported(format!("Unsupported expression: {}", v))),
256		}
257	}
258
259	async fn compute(&self, stk: &mut Stk, v: &Expr, n: Node) -> Result<Node> {
260		Ok(if n == Node::Computable {
261			match stk.run(|stk| v.compute(stk, self.ctx.ctx, self.ctx.opt, None)).await {
262				Ok(v) => Node::Computed(v.into()),
263				Err(_) => Node::Unsupported(format!("Unsupported expression: {}", v)),
264			}
265		} else {
266			n
267		})
268	}
269
270	async fn eval_array(&mut self, stk: &mut Stk, a: &[Expr]) -> Result<Node> {
271		self.leaf_nodes_count += 1;
272		let mut values = Vec::with_capacity(a.len());
273		for v in a {
274			values.push(
275				stk.run(|stk| v.compute(stk, self.ctx.ctx, self.ctx.opt, None))
276					.await
277					.catch_return()?,
278			);
279		}
280		Ok(Node::Computed(Arc::new(Value::Array(Array(values)))))
281	}
282
283	async fn eval_idiom(&mut self, stk: &mut Stk, gr: GroupRef, i: &Idiom) -> Result<Node> {
284		self.leaf_nodes_count += 1;
285		// Check if the idiom has already been resolved
286		if let Some(node) = self.resolved_idioms.get(i).cloned() {
287			return Ok(node);
288		};
289
290		// Compute the idiom value if it is a param
291		if let Some(Part::Start(x)) = i.0.first() {
292			if matches!(x, Expr::Param(_)) {
293				let v = stk
294					.run(|stk| i.compute(stk, self.ctx.ctx, self.ctx.opt, None))
295					.await
296					.catch_return()?;
297				let v = v.into_literal();
298				return stk.run(|stk| self.eval_value(stk, gr, &v)).await;
299			}
300		}
301
302		let n = self.resolve_idiom(i).await?;
303		Ok(n)
304	}
305
306	async fn resolve_idiom(&mut self, i: &Idiom) -> Result<Node> {
307		let tx = self.ctx.ctx.tx();
308		self.lazy_load_schema_resolver(&tx, self.table).await?;
309		let i = Arc::new(i.clone());
310		// Try to detect if it matches an index
311		let n = if let Some(schema) = self.schemas.get(self.table).cloned() {
312			let irs = self.resolve_indexes(self.table, &i, &schema);
313			if !irs.is_empty() {
314				Node::IndexedField(i.clone(), irs)
315			} else if let Some(ro) =
316				self.resolve_record_field(&tx, schema.fields.as_ref(), &i).await?
317			{
318				// Try to detect an indexed record field
319				Node::RecordField(i.clone(), ro)
320			} else {
321				Node::NonIndexedField(i.clone())
322			}
323		} else {
324			Node::NonIndexedField(i.clone())
325		};
326		self.resolved_idioms.insert(i.clone(), n.clone());
327		Ok(n)
328	}
329
330	fn resolve_indexes(&mut self, t: &Ident, i: &Idiom, schema: &SchemaCache) -> LocalIndexRefs {
331		// Did we already resolve this idiom?
332		if let Some(m) = self.idioms_indexes.get(t) {
333			if let Some(irs) = m.get(i).cloned() {
334				return irs;
335			}
336		}
337		let mut irs = Vec::new();
338		for (idx, ix) in schema.indexes.iter().enumerate() {
339			if let Some(idiom_index) = ix.cols.iter().position(|p| p.eq(i)) {
340				let ixr = schema.new_reference(idx);
341				// Check if the WITH clause allow the index to be used
342				if let Some(With::Index(ixs)) = &self.ctx.with {
343					if ixs.iter().any(|x| x == ix.name.as_str()) {
344						if let Some(wi) = &mut self.with_indexes {
345							wi.push(ixr.clone());
346						} else {
347							self.with_indexes = Some(vec![ixr.clone()]);
348						}
349					}
350				}
351				irs.push((ixr, idiom_index));
352			}
353		}
354		let i = Arc::new(i.clone());
355		if let Some(e) = self.idioms_indexes.get_mut(t) {
356			e.insert(i, irs.clone());
357		} else {
358			self.idioms_indexes.insert(t.clone(), HashMap::from([(i, irs.clone())]));
359		}
360		irs
361	}
362
363	async fn resolve_record_field(
364		&mut self,
365		tx: &Transaction,
366		fields: &[DefineFieldStatement],
367		idiom: &Arc<Idiom>,
368	) -> Result<Option<RecordOptions>> {
369		for field in fields.iter() {
370			if let Some(Kind::Record(tables)) = &field.field_kind {
371				if idiom.starts_with(&field.name.0) {
372					let (local_field, remote_field) = idiom.0.split_at(field.name.0.len());
373					if remote_field.is_empty() {
374						return Ok(None);
375					}
376					let local_field = Idiom(local_field.to_vec());
377					self.lazy_load_schema_resolver(tx, self.table).await?;
378					let locals;
379					if let Some(shema) = self.schemas.get(self.table).cloned() {
380						locals = self.resolve_indexes(self.table, &local_field, &shema);
381					} else {
382						return Ok(None);
383					}
384
385					let remote_field = Arc::new(Idiom(remote_field.to_vec()));
386					let mut remotes = vec![];
387					for table in tables {
388						let table = Ident::try_new(table.clone())?;
389						self.lazy_load_schema_resolver(tx, &table).await?;
390						if let Some(schema) = self.schemas.get(&table).cloned() {
391							let remote_irs = self.resolve_indexes(&table, &remote_field, &schema);
392							remotes.push((remote_field.clone(), remote_irs));
393						} else {
394							return Ok(None);
395						}
396					}
397					let ro = RecordOptions {
398						locals,
399						remotes: Arc::new(remotes),
400					};
401					self.idioms_record_options.insert(idiom.clone(), ro.clone());
402					return Ok(Some(ro));
403				}
404			}
405		}
406		Ok(None)
407	}
408
409	fn check_boolean_operator(&mut self, gr: GroupRef, op: &BinaryOperator) {
410		match op {
411			BinaryOperator::Or => {
412				if self.all_and != Some(false) {
413					self.all_and = Some(false);
414				}
415				self.all_and_groups.entry(gr).and_modify(|b| *b = false).or_insert(false);
416			}
417			BinaryOperator::And => {
418				if self.all_and.is_none() {
419					self.all_and = Some(true);
420				}
421				self.all_and_groups.entry(gr).or_insert(true);
422			}
423			_ => {
424				self.all_and_groups.entry(gr).or_insert(true);
425			}
426		}
427	}
428
429	fn check_leaf_node_with_index(&mut self, io: Option<&IndexOption>) {
430		if let Some(io) = io {
431			if let Some(wi) = &self.with_indexes {
432				if !wi.contains(io.ix_ref()) {
433					return;
434				}
435			}
436			self.leaf_nodes_with_index_count += 2;
437		}
438	}
439
440	#[expect(clippy::too_many_arguments)]
441	fn lookup_index_options(
442		&mut self,
443		o: &BinaryOperator,
444		id: &Arc<Idiom>,
445		node: &Node,
446		exp: &Arc<Expr>,
447		p: IdiomPosition,
448		local_irs: &LocalIndexRefs,
449		remote_irs: Option<&RemoteIndexRefs>,
450	) -> Result<Option<IndexOption>> {
451		if let Some(remote_irs) = remote_irs {
452			let mut remote_ios = Vec::with_capacity(remote_irs.len());
453			for (id, irs) in remote_irs.iter() {
454				if let Some(io) = self.lookup_index_option(irs, o, id, node, exp, p)? {
455					remote_ios.push(io);
456				} else {
457					return Ok(None);
458				}
459			}
460			if let Some((irf, _)) = self.lookup_join_index_ref(local_irs) {
461				let io =
462					IndexOption::new(irf, Some(id.clone()), p, IndexOperator::Join(remote_ios));
463				return Ok(Some(io));
464			}
465			return Ok(None);
466		}
467		let io = self.lookup_index_option(local_irs, o, id, node, exp, p)?;
468		Ok(io)
469	}
470
471	fn lookup_index_option(
472		&mut self,
473		irs: &LocalIndexRefs,
474		op: &BinaryOperator,
475		id: &Arc<Idiom>,
476		n: &Node,
477		e: &Arc<Expr>,
478		p: IdiomPosition,
479	) -> Result<Option<IndexOption>> {
480		let mut res = None;
481		for (ixr, col) in irs.iter() {
482			let op = match &ixr.index {
483				Index::Idx => self.eval_index_operator(ixr, op, n, p, *col),
484				Index::Uniq => self.eval_index_operator(ixr, op, n, p, *col),
485				Index::Search {
486					..
487				}
488				| Index::FullText {
489					..
490				} if *col == 0 => Self::eval_matches_operator(op, n),
491				Index::MTree(_) if *col == 0 => self.eval_mtree_knn(e, op, n)?,
492				Index::Hnsw(_) if *col == 0 => self.eval_hnsw_knn(e, op, n)?,
493				_ => None,
494			};
495			if res.is_none() {
496				if let Some(op) = op {
497					let io = IndexOption::new(ixr.clone(), Some(id.clone()), p, op);
498					self.index_map.options.push((e.clone(), io.clone()));
499					res = Some(io);
500				}
501			}
502		}
503		Ok(res)
504	}
505
506	fn lookup_join_index_ref(&self, irs: &LocalIndexRefs) -> Option<(IndexReference, IdiomCol)> {
507		for (ixr, id_col) in irs.iter().filter(|(_, id_col)| 0.eq(id_col)) {
508			match &ixr.index {
509				Index::Idx | Index::Uniq => return Some((ixr.clone(), *id_col)),
510				_ => {}
511			};
512		}
513		None
514	}
515
516	fn eval_matches_operator(op: &BinaryOperator, n: &Node) -> Option<IndexOperator> {
517		if let Some(v) = n.is_computed() {
518			if let BinaryOperator::Matches(mr) = op {
519				return Some(IndexOperator::Matches(v.to_raw_string(), mr.clone()));
520			}
521		}
522		None
523	}
524
525	fn eval_mtree_knn(
526		&mut self,
527		exp: &Arc<Expr>,
528		op: &BinaryOperator,
529		n: &Node,
530	) -> Result<Option<IndexOperator>> {
531		let BinaryOperator::NearestNeighbor(nn) = op else {
532			return Ok(None);
533		};
534		let NearestNeighbor::KTree(k) = &**nn else {
535			return Ok(None);
536		};
537
538		if let Node::Computed(v) = n {
539			let vec: Arc<Vec<Number>> = Arc::new(v.as_ref().clone().coerce_to()?);
540			self.knn_expressions.insert(exp.clone());
541			return Ok(Some(IndexOperator::Knn(vec, *k)));
542		}
543		Ok(None)
544	}
545
546	fn eval_hnsw_knn(
547		&mut self,
548		exp: &Arc<Expr>,
549		op: &BinaryOperator,
550		n: &Node,
551	) -> Result<Option<IndexOperator>> {
552		let BinaryOperator::NearestNeighbor(nn) = op else {
553			return Ok(None);
554		};
555		let NearestNeighbor::Approximate(k, ef) = &**nn else {
556			return Ok(None);
557		};
558
559		if let Node::Computed(v) = n {
560			let vec: Arc<Vec<Number>> = Arc::new(v.as_ref().clone().coerce_to()?);
561			self.knn_expressions.insert(exp.clone());
562			return Ok(Some(IndexOperator::Ann(vec, *k, *ef)));
563		}
564
565		Ok(None)
566	}
567
568	fn eval_bruteforce_knn(&mut self, id: &Idiom, val: &Node, exp: &Arc<Expr>) -> Result<()> {
569		let Expr::Binary {
570			op,
571			..
572		} = &**exp
573		else {
574			return Ok(());
575		};
576
577		let BinaryOperator::NearestNeighbor(nn) = op else {
578			return Ok(());
579		};
580		let NearestNeighbor::K(k, d) = &**nn else {
581			return Ok(());
582		};
583
584		if let Node::Computed(v) = val {
585			let vec: Arc<Vec<Number>> = Arc::new(v.as_ref().clone().coerce_to()?);
586			self.knn_expressions.insert(exp.clone());
587			self.knn_brute_force_expressions
588				.insert(exp.clone(), KnnBruteForceExpression::new(*k, id.clone(), vec, d.clone()));
589		}
590		Ok(())
591	}
592
593	fn eval_index_operator(
594		&mut self,
595		ixr: &IndexReference,
596		op: &BinaryOperator,
597		n: &Node,
598		p: IdiomPosition,
599		col: IdiomCol,
600	) -> Option<IndexOperator> {
601		if let Some(v) = n.is_computed() {
602			match (op, v, p) {
603				(BinaryOperator::Equal | BinaryOperator::ExactEqual, v, _) => {
604					let iop = IndexOperator::Equality(v);
605					self.index_map.check_compound(ixr, col, &iop);
606					if col == 0 {
607						return Some(iop);
608					}
609				}
610				(BinaryOperator::Contain, v, IdiomPosition::Left) => {
611					if col == 0 {
612						return Some(IndexOperator::Equality(v));
613					}
614				}
615				(BinaryOperator::Inside, v, IdiomPosition::Right) => {
616					if col == 0 {
617						return Some(IndexOperator::Equality(v));
618					}
619				}
620				(BinaryOperator::Inside, v, IdiomPosition::Left) => {
621					if let Value::Array(a) = v.as_ref() {
622						self.index_map.check_compound_array(ixr, col, a);
623						if col == 0 {
624							return Some(IndexOperator::Union(v));
625						}
626					}
627				}
628				(
629					BinaryOperator::ContainAny | BinaryOperator::ContainAll,
630					v,
631					IdiomPosition::Left,
632				) => {
633					if v.is_array() && col == 0 {
634						return Some(IndexOperator::Union(v));
635					}
636				}
637				(
638					BinaryOperator::LessThan
639					| BinaryOperator::LessThanEqual
640					| BinaryOperator::MoreThan
641					| BinaryOperator::MoreThanEqual,
642					v,
643					p,
644				) => {
645					let iop = IndexOperator::RangePart(p.transform(op), v);
646					self.index_map.check_compound(ixr, col, &iop);
647					if col == 0 {
648						return Some(iop);
649					}
650				}
651				_ => {}
652			}
653		}
654		None
655	}
656}
657
658pub(super) type CompoundIndexes = HashMap<IndexReference, Vec<Vec<IndexOperator>>>;
659
660/// For each expression a possible index option
661#[derive(Default)]
662pub(super) struct IndexesMap {
663	pub(super) options: Vec<(Arc<Expr>, IndexOption)>,
664	/// For each index, tells if the columns are requested
665	pub(super) compound_indexes: CompoundIndexes,
666	pub(super) order_limit: Option<IndexOption>,
667}
668
669impl IndexesMap {
670	pub(crate) fn check_compound(&mut self, ixr: &IndexReference, col: usize, iop: &IndexOperator) {
671		let cols = ixr.cols.len();
672		let values = self.compound_indexes.entry(ixr.clone()).or_insert(vec![vec![]; cols]);
673		if let Some(a) = values.get_mut(col) {
674			a.push(iop.clone());
675		}
676	}
677
678	pub(crate) fn check_compound_array(&mut self, ixr: &IndexReference, col: usize, a: &Array) {
679		for v in a.iter() {
680			let iop = IndexOperator::Equality(Arc::new(v.clone()));
681			self.check_compound(ixr, col, &iop)
682		}
683	}
684}
685
686#[derive(Debug, Clone)]
687pub(super) struct IndexReference {
688	indexes: Arc<[DefineIndexStatement]>,
689	idx: usize,
690}
691
692impl IndexReference {
693	pub(super) fn new(indexes: Arc<[DefineIndexStatement]>, idx: usize) -> Self {
694		Self {
695			indexes,
696			idx,
697		}
698	}
699}
700
701impl Hash for IndexReference {
702	fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
703		state.write_usize(self.idx);
704	}
705}
706
707impl PartialEq for IndexReference {
708	fn eq(&self, other: &Self) -> bool {
709		self.idx == other.idx
710	}
711}
712
713impl Eq for IndexReference {}
714
715impl Deref for IndexReference {
716	type Target = DefineIndexStatement;
717
718	fn deref(&self) -> &Self::Target {
719		&self.indexes[self.idx]
720	}
721}
722
723#[derive(Clone)]
724struct SchemaCache {
725	indexes: Arc<[DefineIndexStatement]>,
726	fields: Arc<[DefineFieldStatement]>,
727}
728
729impl SchemaCache {
730	async fn new(ns: NamespaceId, db: DatabaseId, table: &Ident, tx: &Transaction) -> Result<Self> {
731		let indexes = tx.all_tb_indexes(ns, db, table).await?;
732		let fields = tx.all_tb_fields(ns, db, table, None).await?;
733		Ok(Self {
734			indexes,
735			fields,
736		})
737	}
738
739	fn new_reference(&self, idx: usize) -> IndexReference {
740		IndexReference::new(self.indexes.clone(), idx)
741	}
742}
743
744pub(super) type GroupRef = u16;
745
746#[derive(Debug, Clone, PartialEq)]
747pub(super) enum Node {
748	Expression {
749		group: GroupRef,
750		io: Option<IndexOption>,
751		left: Arc<Node>,
752		right: Arc<Node>,
753		exp: Arc<Expr>,
754	},
755	IndexedField(Arc<Idiom>, LocalIndexRefs),
756	RecordField(Arc<Idiom>, RecordOptions),
757	NonIndexedField(Arc<Idiom>),
758	Computable,
759	Computed(Arc<Value>),
760	Unsupported(String),
761}
762
763impl Node {
764	pub(super) fn is_computed(&self) -> Option<Arc<Value>> {
765		if let Self::Computed(v) = self {
766			Some(v.clone())
767		} else {
768			None
769		}
770	}
771
772	pub(super) fn is_indexed_field(
773		&self,
774	) -> Option<(&Arc<Idiom>, &LocalIndexRefs, Option<&RemoteIndexRefs>)> {
775		match self {
776			Self::IndexedField(id, irs) => Some((id, irs, None)),
777			Self::RecordField(id, ro) => Some((id, &ro.locals, Some(&ro.remotes))),
778			_ => None,
779		}
780	}
781
782	pub(super) fn is_field(&self) -> Option<&Idiom> {
783		match self {
784			Self::IndexedField(id, _) => Some(id),
785			Self::RecordField(id, _) => Some(id),
786			Self::NonIndexedField(id) => Some(id),
787			_ => None,
788		}
789	}
790}
791
792#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
793pub(super) enum IdiomPosition {
794	/// The idiom is on the left of the condition clause
795	Left,
796	/// The idiom is on the right tf the condition clause
797	Right,
798	/// Eg. ORDER LIMIT
799	None,
800}
801
802impl IdiomPosition {
803	// Reverses the operator for non-commutative operators
804	fn transform(&self, op: &BinaryOperator) -> BinaryOperator {
805		match self {
806			IdiomPosition::Left => op.clone(),
807			IdiomPosition::Right => match op {
808				BinaryOperator::LessThan => BinaryOperator::MoreThan,
809				BinaryOperator::LessThanEqual => BinaryOperator::MoreThanEqual,
810				BinaryOperator::MoreThan => BinaryOperator::LessThan,
811				BinaryOperator::MoreThanEqual => BinaryOperator::LessThanEqual,
812				_ => op.clone(),
813			},
814			IdiomPosition::None => op.clone(),
815		}
816	}
817}
818
819#[derive(Clone)]
820struct ResolvedExpression {
821	group: GroupRef,
822	exp: Arc<Expr>,
823	io: Option<IndexOption>,
824	left: Arc<Node>,
825	right: Arc<Node>,
826}
827impl From<ResolvedExpression> for Node {
828	fn from(re: ResolvedExpression) -> Self {
829		Node::Expression {
830			group: re.group,
831			io: re.io,
832			left: re.left,
833			right: re.right,
834			exp: re.exp,
835		}
836	}
837}