immigrant_schema/
table.rs

1use std::collections::BTreeSet;
2
3use itertools::Itertools;
4
5use super::column::Column;
6use crate::{
7	attribute::AttributeList,
8	db_name_impls, def_name_impls,
9	diagnostics::Report,
10	index::{Check, PrimaryKey, UniqueConstraint},
11	mixin::Mixin,
12	names::{
13		ColumnIdent, DbColumn, DbForeignKey, DbNativeType, DefName, ForeignKeyKind, MixinIdent,
14		TableDefName, TableIdent, TableKind, TypeIdent,
15	},
16	scalar::PropagatedScalarData,
17	uid::{next_uid, OwnUid, RenameExt, RenameMap, Uid},
18	w, HasIdent, Index, SchemaTable, TableColumn, TableForeignKey, TableIndex, TableItem,
19};
20
21#[derive(Debug)]
22pub struct Table {
23	uid: OwnUid,
24	name: TableDefName,
25	pub docs: Vec<String>,
26	pub attrlist: AttributeList,
27	pub columns: Vec<Column>,
28	pub annotations: Vec<TableAnnotation>,
29	pub foreign_keys: Vec<ForeignKey>,
30	pub mixins: Vec<MixinIdent>,
31}
32def_name_impls!(Table, TableKind);
33impl Table {
34	pub fn new(
35		docs: Vec<String>,
36		attrlist: AttributeList,
37		name: TableDefName,
38		columns: Vec<Column>,
39		annotations: Vec<TableAnnotation>,
40		foreign_keys: Vec<ForeignKey>,
41		mixins: Vec<MixinIdent>,
42	) -> Self {
43		Self {
44			uid: next_uid(),
45			name,
46			docs,
47			attrlist,
48			columns,
49			annotations,
50			foreign_keys,
51			mixins,
52		}
53	}
54	pub fn primary_key(&self) -> Option<&PrimaryKey> {
55		self.annotations
56			.iter()
57			.filter_map(TableAnnotation::as_primary_key)
58			.at_most_one()
59			.expect("at most one pk")
60	}
61	pub fn is_external(&self) -> bool {
62		self.annotations
63			.iter()
64			.any(|a| matches!(a, TableAnnotation::External))
65	}
66	pub(crate) fn assimilate_mixin(&mut self, mixin: &Mixin) {
67		let Mixin {
68			docs,
69			attrlist,
70			columns,
71			annotations,
72			foreign_keys,
73			mixins,
74			..
75		} = mixin;
76		self.docs.extend_from_slice(docs);
77		self.attrlist.0.extend_from_slice(&attrlist.0);
78		self.columns
79			.extend(columns.iter().map(|c| c.clone_for_mixin()));
80		self.annotations
81			.extend(annotations.iter().map(|a| a.clone_for_mixin()));
82		self.foreign_keys
83			.extend(foreign_keys.iter().map(|fk| fk.clone_for_mixin()));
84		self.mixins.extend(mixins.clone());
85	}
86}
87#[derive(Debug)]
88pub enum TableAnnotation {
89	Check(Check),
90	Unique(UniqueConstraint),
91	PrimaryKey(PrimaryKey),
92	Index(Index),
93	External,
94}
95
96#[derive(Debug, PartialEq, Eq, Clone, Copy)]
97pub enum OnDelete {
98	SetNull,
99	SetDefault,
100	Restrict,
101	Noop,
102	Cascade,
103}
104impl OnDelete {
105	pub fn sql(&self) -> Option<&'static str> {
106		Some(match self {
107			OnDelete::SetNull => "SET NULL",
108			OnDelete::SetDefault => "SET DEFAULT",
109			OnDelete::Restrict => "RESTRICT",
110			OnDelete::Noop => return None,
111			OnDelete::Cascade => "CASCADE",
112		})
113	}
114}
115
116#[derive(Debug)]
117pub struct ForeignKey {
118	uid: OwnUid,
119	name: Option<DbForeignKey>,
120	pub source_fields: Option<Vec<ColumnIdent>>,
121	pub target: TableIdent,
122	pub target_fields: Option<Vec<ColumnIdent>>,
123	pub on_delete: OnDelete,
124}
125db_name_impls!(ForeignKey, ForeignKeyKind);
126impl ForeignKey {
127	pub fn new(
128		name: Option<DbForeignKey>,
129		source_fields: Option<Vec<ColumnIdent>>,
130		target: TableIdent,
131		target_fields: Option<Vec<ColumnIdent>>,
132		on_delete: OnDelete,
133	) -> Self {
134		Self {
135			uid: next_uid(),
136			name,
137			source_fields,
138			target,
139			target_fields,
140			on_delete,
141		}
142	}
143
144	pub fn clone_for_mixin(&self) -> Self {
145		Self::new(
146			self.name.clone(),
147			self.source_fields.clone(),
148			self.target.clone(),
149			self.target_fields.clone(),
150			self.on_delete,
151		)
152	}
153}
154
155impl TableAnnotation {
156	pub fn as_index(&self) -> Option<&Index> {
157		match self {
158			TableAnnotation::Index(i) => Some(i),
159			_ => None,
160		}
161	}
162
163	pub fn as_primary_key(&self) -> Option<&PrimaryKey> {
164		if let Self::PrimaryKey(v) = self {
165			Some(v)
166		} else {
167			None
168		}
169	}
170
171	pub fn as_check(&self) -> Option<&Check> {
172		if let Self::Check(c) = self {
173			Some(c)
174		} else {
175			None
176		}
177	}
178
179	pub fn as_unique_constraint(&self) -> Option<&UniqueConstraint> {
180		if let Self::Unique(u) = self {
181			Some(u)
182		} else {
183			None
184		}
185	}
186
187	pub fn clone_for_mixin(&self) -> Self {
188		match self {
189			TableAnnotation::Check(c) => Self::Check(c.clone_for_propagate()),
190			TableAnnotation::Unique(u) => Self::Unique(u.clone_for_propagate()),
191			TableAnnotation::PrimaryKey(p) => Self::PrimaryKey(p.clone_for_propagate()),
192			TableAnnotation::Index(i) => Self::Index(i.clone_for_propagate()),
193			TableAnnotation::External => Self::External,
194		}
195	}
196}
197
198impl Table {
199	pub fn process(&mut self) {
200		for column in self.columns.iter_mut() {
201			let propagated = column.propagate_annotations();
202			self.annotations.extend(propagated);
203			if let Some(fk) = column.propagate_foreign_key() {
204				self.foreign_keys.push(fk);
205			}
206		}
207	}
208	pub(crate) fn propagate_scalar_data(
209		&mut self,
210		scalar: TypeIdent,
211		propagated: &PropagatedScalarData,
212	) {
213		for col in self.columns.iter_mut() {
214			col.propagate_scalar_data(scalar, propagated)
215		}
216	}
217
218	pub fn db_name(&self, column: &ColumnIdent, rn: &RenameMap) -> DbColumn {
219		for ele in self.columns.iter() {
220			if &ele.id() == column {
221				return ele.db(rn);
222			}
223		}
224		let table = self.db(rn);
225		unreachable!("unknown field: {table:?}.{column:?}");
226	}
227	pub fn db_names(
228		&self,
229		columns: impl IntoIterator<Item = ColumnIdent>,
230		rn: &RenameMap,
231	) -> Vec<DbColumn> {
232		columns.into_iter().map(|c| self.db_name(&c, rn)).collect()
233	}
234	pub fn format_index_name(
235		&self,
236		columns: impl Iterator<Item = ColumnIdent>,
237		rn: &RenameMap,
238	) -> String {
239		let mut out = String::new();
240		for (i, column) in columns.enumerate() {
241			if i != 0 {
242				w!(out, "_");
243			}
244			let db_name = self.db_name(&column, rn);
245			w!(out, "{}", db_name.raw());
246		}
247		assert!(!out.is_empty(), "passed no columns");
248		out
249	}
250	pub fn print_column_list(
251		&self,
252		out: &mut String,
253		columns: impl Iterator<Item = ColumnIdent>,
254		rn: &RenameMap,
255	) {
256		for (i, column) in columns.enumerate() {
257			if i != 0 {
258				w!(out, ", ");
259			}
260			let db_name = self.db_name(&column, rn);
261			w!(out, "{}", db_name.raw());
262		}
263	}
264}
265impl<'a> SchemaTable<'a> {
266	fn item<I>(&self, value: &'a I) -> TableItem<'a, I> {
267		TableItem::unchecked_new(*self, value)
268	}
269	pub fn schema_column(&'a self, column: ColumnIdent) -> TableColumn<'a> {
270		self.columns()
271			.find(|c| c.id() == column)
272			.unwrap_or_else(|| panic!("column not found: {column:?}"))
273	}
274	pub fn columns(&self) -> impl Iterator<Item = TableColumn<'_>> {
275		self.columns.iter().map(|i| self.item(i))
276	}
277	pub fn indexes(&self) -> impl Iterator<Item = TableIndex<'_>> {
278		self.annotations
279			.iter()
280			.filter_map(TableAnnotation::as_index)
281			.map(|i| self.item(i))
282	}
283	pub fn pk(&self) -> Option<&PrimaryKey> {
284		self.annotations
285			.iter()
286			.filter_map(TableAnnotation::as_primary_key)
287			.at_most_one()
288			.expect("pk is not merged")
289	}
290	pub fn foreign_keys(&self) -> impl Iterator<Item = TableForeignKey<'_>> {
291		self.foreign_keys.iter().map(|i| self.item(i))
292	}
293	pub fn cardinality(&self, columns: impl IntoIterator<Item = ColumnIdent>) -> Cardinality {
294		fn is_unique_by_index(
295			indexed: impl IntoIterator<Item = ColumnIdent>,
296			columns: &BTreeSet<ColumnIdent>,
297		) -> bool {
298			let indexed = indexed.into_iter().collect::<BTreeSet<_>>();
299			if indexed.difference(columns).next().is_none() {
300				// Column list has full intersection with the unique index, thus
301				// the request itself is unique
302				//
303				// I.e we have UNIQUE INDEX (a, b, c)
304				// and the request is (a, b, c, d)
305				//
306				// Subset (a, b, c) is unique, thus d is irrelevant.
307				return true;
308			}
309			false
310		}
311		let columns = columns.into_iter().collect::<BTreeSet<_>>();
312		for ele in self.annotations.iter() {
313			match ele {
314				TableAnnotation::Index(i) => {
315					if i.unique && is_unique_by_index(i.field_idents(), &columns) {
316						return Cardinality::One;
317					}
318				}
319				TableAnnotation::PrimaryKey(pk) => {
320					if is_unique_by_index(pk.columns.iter().copied(), &columns) {
321						return Cardinality::One;
322					}
323				}
324				TableAnnotation::Unique(u) => {
325					if is_unique_by_index(u.columns.iter().copied(), &columns) {
326						return Cardinality::One;
327					}
328				}
329				TableAnnotation::Check { .. } => {}
330				TableAnnotation::External => {}
331			}
332		}
333		// FIXME: Wrong assumption? When target is one, it doesn't mean the source is one too
334		// for fk in self.foreign_keys() {
335		// 	if !is_unique_by_index(fk.source_columns(), &columns) {
336		// 		// This fk can not constraint our query
337		// 		continue;
338		// 	}
339		// 	let target = fk.target_table();
340		// 	let target_columns = fk.target_columns();
341		// 	if target.cardinality(target_columns) == Cardinality::One {
342		// 		return Cardinality::One;
343		// 	}
344		// }
345		Cardinality::Many
346	}
347}
348
349#[derive(PartialEq, Eq)]
350pub enum Cardinality {
351	One,
352	Many,
353}
354
355impl<'s> TableForeignKey<'s> {
356	pub fn source_columns(&self) -> Vec<ColumnIdent> {
357		if let Some(source) = &self.source_fields {
358			return source.clone();
359		}
360		if let Some(target) = &self.target_fields {
361			return target.clone();
362		};
363		panic!("no fields defined")
364	}
365	pub fn target_columns(&self) -> Vec<ColumnIdent> {
366		if let Some(target) = &self.target_fields {
367			return target.clone();
368		};
369		if let Some(source) = &self.source_fields {
370			return source.clone();
371		}
372		panic!("no fields defined")
373	}
374	pub fn target_table(&self) -> SchemaTable<'s> {
375		let table = &self.target;
376		self.table
377			.schema
378			.schema_table(table)
379			.expect("target table is not defined")
380	}
381	pub fn source_db_columns(&self, rn: &RenameMap) -> Vec<DbColumn> {
382		self.source_columns()
383			.into_iter()
384			.map(|f| self.table.db_name(&f, rn))
385			.collect()
386	}
387	pub fn target_db_columns(&self, rn: &RenameMap) -> Vec<DbColumn> {
388		let target_table = self.target_table();
389		self.target_columns()
390			.into_iter()
391			.map(|f| target_table.db_name(&f, rn))
392			.collect()
393	}
394	pub fn db_types(&self, rn: &RenameMap, report: &mut Report) -> Vec<DbNativeType> {
395		let db_types: Vec<_> = self
396			.source_columns()
397			.into_iter()
398			.map(|f| self.table.schema_column(f).db_type(rn, report))
399			.collect();
400		// Sanity
401		let target_table = self.target_table();
402		let target_db_types: Vec<_> = self
403			.target_columns()
404			.into_iter()
405			.map(|f| target_table.schema_column(f).db_type(rn, report))
406			.collect();
407		assert_eq!(db_types, target_db_types);
408		db_types
409	}
410	pub fn cardinality(&self) -> (Cardinality, Cardinality) {
411		(
412			self.table.cardinality(self.source_columns()),
413			self.target_table().cardinality(self.target_columns()),
414		)
415	}
416}