1pub mod canonical;
6mod check_constraint;
7mod diff;
8pub mod from_db;
9pub mod from_macro;
10pub mod read;
11#[cfg(test)]
12mod test;
13pub mod tokenizer;
14
15use crate::{
16 lower::{
17 self, emit,
18 list_writer::{Alias, ListWriter},
19 },
20 schema::{
21 canonical::ColumnType,
22 from_macro::{Schema, Table},
23 },
24};
25
26impl ColumnType {
27 pub fn rusqlite_type(&self) -> &'static str {
28 match self {
29 ColumnType::Integer => "INTEGER",
30 ColumnType::Real => "REAL",
31 ColumnType::Text => "TEXT",
32 ColumnType::Blob => "BLOB",
33 ColumnType::Unknown(_) => unreachable!(),
34 }
35 }
36}
37
38mod normalize {
39 use crate::schema::{canonical, from_db};
40
41 impl from_db::Index {
42 pub fn normalize(self) -> Option<canonical::Unique> {
43 self.unique.then_some(canonical::Unique {
44 columns: self.columns.into_iter().collect(),
45 })
46 }
47 }
48
49 #[cfg(feature = "dev")]
50 impl crate::schema::from_macro::Table {
51 fn normalize(self) -> canonical::Table {
52 canonical::Table {
53 columns: self.columns.into_iter().map(|(k, v)| (k, v.def)).collect(),
54 indices: self
55 .indices
56 .into_iter()
57 .filter_map(|idx| idx.def.normalize())
58 .collect(),
59 }
60 }
61 }
62
63 #[cfg(feature = "dev")]
64 impl crate::schema::from_macro::Schema {
65 pub(crate) fn normalize(self) -> canonical::Schema {
66 canonical::Schema {
67 tables: self
68 .tables
69 .into_iter()
70 .map(|(k, v)| (k.to_owned(), v.normalize()))
71 .collect(),
72 }
73 }
74 }
75}
76
77impl Table {
78 pub(crate) fn new<T: crate::Table>() -> Self {
79 let mut f = crate::schema::from_macro::TypBuilder::default();
80 T::typs(&mut f);
81 f.ast.span = T::SPAN;
82 f.ast
83 }
84}
85
86impl Schema {
87 pub(crate) fn new<S: crate::migrate::Schema>() -> Self {
88 let mut b = crate::migrate::TableTypBuilder::default();
89 S::typs(&mut b);
90 b.ast.span = S::SPAN;
91 b.ast
92 }
93}
94
95impl Table {
96 pub fn to_db(self) -> from_db::Table {
97 from_db::Table {
98 columns: self
99 .columns
100 .into_iter()
101 .map(|(name, col)| (name, col.def))
102 .collect(),
103 indices: self.indices.into_iter().map(|idx| idx.def).collect(),
104 }
105 }
106}
107
108impl from_db::Table {
109 pub fn create(&self, table: lower::JoinableTable, primary: &'static str) -> String {
110 let mut stmt = emit::Stmt::default();
111
112 stmt.write("CREATE TABLE ");
113 table.emit(&mut stmt);
114
115 stmt.write(" (");
116 let mut list = ListWriter::new(&mut stmt, ", ");
117 list.item()
118 .write(Alias(primary))
119 .write(" INTEGER PRIMARY KEY");
120 for (name, col) in &self.columns {
121 let item = list.item().write(Alias(&name));
122 item.write(" ").write(col.typ.rusqlite_type());
123 if !col.nullable {
124 item.write(" NOT NULL");
125 }
126 if let Some(check) = &col.check {
127 item.write(format!(" CHECK ({check})"));
128 }
129 if let Some((table, fk)) = &col.fk {
130 item.write(format!(" REFERENCES {} ({})", Alias(table), Alias(fk)));
131 }
132 }
133 for index in &self.indices {
134 if index.unique {
138 let item = list.item().write("UNIQUE (");
139 let mut unique_list = ListWriter::new(item, ", ");
141 for col in &index.columns {
142 unique_list.item().write(Alias(col));
143 }
144 item.write(")");
145 }
147 }
148 stmt.write(") STRICT");
149 assert!(stmt.params.is_empty());
150 stmt.sql
151 }
152
153 pub fn delayed_indices(&self, table_name: &str) -> impl Iterator<Item = String> {
158 self.indices
159 .iter()
160 .filter(|x| !x.unique)
161 .enumerate()
162 .map(move |(index_num, index)| {
163 let stmt =
164 index.create_not_unique(&format!("{table_name}_index_{index_num}"), table_name);
165 assert!(stmt.params.is_empty());
166 stmt.sql
167 })
168 }
169}
170
171impl from_db::Index {
172 pub fn create_not_unique(&self, index_name: &str, table_name: &str) -> emit::Stmt {
173 assert!(!self.unique);
174
175 let mut stmt = emit::Stmt::default();
176 stmt.write("CREATE INDEX ")
177 .write(Alias(index_name))
178 .write(" ON ")
179 .write(Alias(table_name))
180 .write(" (");
181 let mut list = ListWriter::new(&mut stmt, ", ");
184 for col in &self.columns {
185 list.item().write(Alias(col));
186 }
187 stmt.write(")");
188 stmt
190 }
191}
192
193#[cfg(feature = "dev")]
194pub mod dev {
195 use std::hash::{Hash, Hasher};
196
197 use k12::{ExtendableOutput, Kt128, Update, XofReader};
198
199 #[derive(Default)]
200 pub struct KangarooHasher {
201 inner: Kt128,
202 }
203
204 impl Hasher for KangarooHasher {
205 fn finish(&self) -> u64 {
206 let mut xof = self.inner.clone().finalize_xof();
207 let mut buf = [0; 8];
208 xof.read(&mut buf);
209 u64::from_le_bytes(buf)
210 }
211
212 fn write(&mut self, bytes: &[u8]) {
213 self.inner.update(bytes);
214 }
215 }
216
217 pub fn hash_schema<S: crate::migrate::Schema>() -> String {
221 let mut hasher = KangarooHasher::default();
222 super::Schema::new::<S>().normalize().hash(&mut hasher);
223 format!("{:x}", hasher.finish())
224 }
225}