multisql/executor/query/select/join/
execute.rs

1use {
2	super::{JoinError, JoinMethod, JoinPlan, JoinType},
3	crate::{
4		recipe::{Ingredient, MetaRecipe, Method, PlannedRecipe, Recipe},
5		types::{ColumnInfo, Row},
6		Glue, IndexFilter, Result, Value,
7	},
8};
9
10#[derive(Debug)]
11pub struct JoinExecute {
12	pub database: Option<String>,
13	pub table: String,
14	pub method: JoinMethod,
15	pub join_type: JoinType,
16	pub widths: (usize, usize),
17	pub index_filter: Option<IndexFilter>,
18}
19
20impl JoinExecute {
21	pub fn new(
22		plan: JoinPlan,
23		plane_columns: &[ColumnInfo],
24		index_filter: Option<IndexFilter>,
25	) -> Result<Self> {
26		let JoinPlan {
27			database,
28			table,
29			join_type,
30			constraint,
31			columns,
32			..
33		} = plan;
34		let widths = (plane_columns.len(), columns.len());
35		let method = decide_method(constraint, columns, plane_columns)?;
36		Ok(Self {
37			database,
38			table,
39			method,
40			join_type,
41			widths,
42			index_filter,
43		})
44	}
45	pub fn set_first_table(&mut self) {
46		self.method = JoinMethod::FirstTable;
47	}
48	pub async fn execute<'a>(self, glue: &Glue, plane_rows: Vec<Row>) -> Result<Vec<Row>> {
49		let rows = glue
50			.get_rows(&self.table, &self.database, &self.index_filter)
51			.await?;
52		self.method.run(
53			&self.join_type,
54			self.widths.0,
55			self.widths.1,
56			plane_rows,
57			rows,
58		)
59	}
60}
61
62fn decide_method(
63	constraint: MetaRecipe,
64	self_columns: Vec<ColumnInfo>,
65	plane_columns: &[ColumnInfo],
66) -> Result<JoinMethod> {
67	Ok(match &constraint.recipe {
68		Recipe::Ingredient(Ingredient::Value(Value::Bool(true))) => JoinMethod::All,
69		Recipe::Method(method) => match **method {
70			Method::BinaryOperation(
71				operator,
72				Recipe::Ingredient(Ingredient::Column(index_l)),
73				Recipe::Ingredient(Ingredient::Column(index_r)),
74			) if operator == Value::eq => {
75				// TODO: Be more strict, ensure that one column is from plane, and another from self.
76				let column_l = constraint
77					.meta
78					.objects
79					.get(index_l)
80					.ok_or(JoinError::Unreachable)?
81					.as_ref()
82					.ok_or(JoinError::Unreachable)?;
83				let column_r = constraint
84					.meta
85					.objects
86					.get(index_r)
87					.ok_or(JoinError::Unreachable)?
88					.as_ref()
89					.ok_or(JoinError::Unreachable)?;
90
91				let (self_index, plane_index) = if let Some(self_index) =
92					self_columns.iter().position(|column| column == column_l)
93				{
94					let plane_index = plane_columns
95						.iter()
96						.position(|column| column == column_r)
97						.ok_or(JoinError::Unreachable)?;
98					(self_index, plane_index)
99				} else {
100					let self_index = self_columns
101						.iter()
102						.position(|column| column == column_r)
103						.ok_or(JoinError::Unreachable)?;
104					let plane_index = plane_columns
105						.iter()
106						.position(|column| column == column_l)
107						.ok_or(JoinError::Unreachable)?;
108					(self_index, plane_index)
109				};
110
111				JoinMethod::ColumnEqColumn {
112					plane_trust_ordered: false,
113					plane_index,
114					self_trust_ordered: false,
115					self_index,
116				}
117			}
118			// TODO: Methods for:
119			// (plan)Column = (other)Column AND (plan)Column = (other or otherother)Column
120			// (plan)Column = (other)Column OR (plan)Column = (other or otherother)Column
121			_ => JoinMethod::General(PlannedRecipe::new(constraint.clone(), plane_columns)?),
122		},
123		_ => JoinMethod::Ignore,
124	})
125}