@@ -1,144 +1,136 @@
11use crate::db::Conn;
2−use anyhow::anyhow;
3−use bimap::BiMap;
4−use std::collections::{hash_map::Entry, HashMap, HashSet};
5−
2+use std::collections::{HashMap, HashSet};
3+
4+// Schema tracks changes made to tables and columns during a migration.
5+// These changes are not applied until the migration is completed but
6+// need to be taken into consideration when creating views for a migration
7+// and when a user references a table or column in a migration.
8+//
9+// The changes to a table are tracked by a `TableChanges` struct. The possible
10+// changes are:
11+// - Changing the name which updates `current_name`.
12+// - Removing which sets the `removed` flag.
13+//
14+// Changes to a column are tracked by a `ColumnChanges` struct which reside in
15+// the corresponding `TableChanges`. The possible changes are:
16+// - Changing the name which updates `current_name`.
17+// - Changing the backing column which will add the new column to the end of
18+// `intermediate_columns`. This is used when temporary columns are
19+// introduced which will eventually replace the current column.
20+// - Removing which sets the `removed` flag.
21+//
22+// Schema provides some schema introspection methods, `get_tables` and `get_table`,
23+// which will retrieve the current schema from the database and apply the changes.
624#[derive(Debug)]
725pub struct Schema {
8− table_alias_to_name: BiMap<String, String>,
9− hidden_tables: HashSet<String>,
10− table_schemas_by_name: HashMap<String, TableSchema>,
26+ table_changes: Vec<TableChanges>,
1127}
1228
1329impl Schema {
1430 pub fn new() -> Schema {
1531 Schema {
16− table_alias_to_name: BiMap::new(),
17− hidden_tables: HashSet::new(),
18− table_schemas_by_name: HashMap::new(),
32+ table_changes: Vec::new(),
1933 }
2034 }
2135
22− pub fn set_table_alias(&mut self, current_name: &str, alias: &str) {
23− self.table_alias_to_name
24− .insert(alias.to_string(), current_name.to_string());
25− }
26−
27− pub fn set_table_hidden(&mut self, table: &str) {
28− let real_name = self
29− .get_table_real_name(table)
30− .unwrap_or_else(|| table.to_string());
31− self.hidden_tables.insert(real_name);
32− }
33−
34− pub fn set_column_alias(&mut self, table: &str, current_name: &str, alias: &str) {
35− let table_real_name = self
36− .get_table_real_name(table)
37− .unwrap_or_else(|| table.to_string());
38−
39− let column = match self.table_schemas_by_name.entry(table_real_name) {
40− Entry::Occupied(o) => o.into_mut(),
41− Entry::Vacant(v) => v.insert(TableSchema::new()),
42− };
43−
44− column
45− .column_alias_to_name
46− .insert(alias.to_string(), current_name.to_string());
47− }
48−
49− pub fn set_column_hidden(&mut self, table: &str, name: &str) {
50− let table_real_name = self
51− .get_table_real_name(table)
52− .unwrap_or_else(|| table.to_string());
53− let column = match self.table_schemas_by_name.entry(table_real_name) {
54− Entry::Occupied(o) => o.into_mut(),
55− Entry::Vacant(v) => v.insert(TableSchema::new()),
56− };
57−
58− column.hidden_columns.insert(name.to_string());
59− }
36+ pub fn change_table<F>(&mut self, current_name: &str, f: F)
37+ where
38+ F: FnOnce(&mut TableChanges),
39+ {
40+ let table_change_index = self
41+ .table_changes
42+ .iter()
43+ .position(|table| table.current_name == current_name)
44+ .unwrap_or_else(|| {
45+ let new_changes = TableChanges::new(current_name.to_string());
46+ self.table_changes.push(new_changes);
47+ self.table_changes.len() - 1
48+ });
6049
61− fn get_table_real_name(&self, name: &str) -> Option<String> {
62− self.table_alias_to_name
63− .get_by_left(name)
64− .map(|real_name| real_name.to_string())
50+ let table_changes = &mut self.table_changes[table_change_index];
51+ f(table_changes)
6552 }
53+}
6654
67− fn get_table_alias_from_real_name<'a>(&'a self, real_name: &'a str) -> Option<String> {
68− self.table_alias_to_name
69− .get_by_right(real_name)
70− .map(|name| name.to_string())
71− }
55+#[derive(Debug)]
56+pub struct TableChanges {
57+ current_name: String,
58+ real_name: String,
59+ column_changes: Vec<ColumnChanges>,
60+ removed: bool,
61+}
7262
73− fn is_table_hidden(&self, table: &str) -> bool {
74− let table_real_name = self
75− .get_table_real_name(table)
76− .unwrap_or_else(|| table.to_string());
77− self.hidden_tables.contains(&table_real_name)
63+impl TableChanges {
64+ fn new(name: String) -> Self {
65+ Self {
66+ current_name: name.to_string(),
67+ real_name: name.to_string(),
68+ column_changes: Vec::new(),
69+ removed: false,
70+ }
7871 }
7972
80− fn get_column_real_name<'a>(&'a self, table: &'a str, name: &'a str) -> Option<&'a str> {
81− let table_real_name = self
82− .get_table_real_name(table)
83− .unwrap_or_else(|| table.to_string());
84−
85− self.table_schemas_by_name
86− .get(&table_real_name)
87− .map(|column| column.get_column_real_name(name))
73+ pub fn set_name(&mut self, name: &str) {
74+ self.current_name = name.to_string();
8875 }
8976
90− fn get_column_alias_from_real_name(&self, table: &str, real_name: &str) -> Option<String> {
91− let table_real_name = self
92− .get_table_real_name(table)
93− .unwrap_or_else(|| table.to_string());
77+ pub fn change_column<F>(&mut self, current_name: &str, f: F)
78+ where
79+ F: FnOnce(&mut ColumnChanges),
80+ {
81+ let column_change_index = self
82+ .column_changes
83+ .iter()
84+ .position(|column| column.current_name == current_name)
85+ .unwrap_or_else(|| {
86+ let new_changes = ColumnChanges::new(current_name.to_string());
87+ self.column_changes.push(new_changes);
88+ self.column_changes.len() - 1
89+ });
9490
95− self.table_schemas_by_name
96− .get(&table_real_name)
97− .and_then(|column| column.get_column_alias_from_real_name(real_name))
91+ let column_changes = &mut self.column_changes[column_change_index];
92+ f(column_changes)
9893 }
9994
100− fn is_column_hidden(&self, table: &str, name: &str) -> bool {
101− let table_real_name = self
102− .get_table_real_name(table)
103− .unwrap_or_else(|| table.to_string());
104− self.table_schemas_by_name
105− .get(&table_real_name)
106− .map(|column| {
107− let alias = column
108− .get_column_alias_from_real_name(name)
109− .unwrap_or_else(|| name.to_string());
110− column.hidden_columns.contains(&alias)
111− })
112− .unwrap_or(false)
95+ pub fn set_removed(&mut self) {
96+ self.removed = true;
11397 }
11498}
11599
116100#[derive(Debug)]
117−struct TableSchema {
118− column_alias_to_name: BiMap<String, String>,
119− hidden_columns: HashSet<String>,
101+pub struct ColumnChanges {
102+ current_name: String,
103+ original_name: String,
104+ intermediate_names: Vec<String>,
105+ removed: bool,
120106}
121107
122−impl TableSchema {
123− fn new() -> TableSchema {
124− TableSchema {
125− column_alias_to_name: BiMap::new(),
126− hidden_columns: HashSet::new(),
108+impl ColumnChanges {
109+ fn new(name: String) -> Self {
110+ Self {
111+ current_name: name.to_string(),
112+ original_name: name.to_string(),
113+ intermediate_names: Vec::new(),
114+ removed: false,
127115 }
128116 }
129117
130− fn get_column_real_name<'a>(&'a self, name: &'a str) -> &'a str {
131− if let Some(real_name) = self.column_alias_to_name.get_by_left(name) {
132− real_name
133− } else {
134− name
135− }
118+ pub fn set_name(&mut self, name: &str) {
119+ self.current_name = name.to_string();
136120 }
137121
138− fn get_column_alias_from_real_name(&self, real_name: &str) -> Option<String> {
139− self.column_alias_to_name
140− .get_by_right(real_name)
141− .map(|name| name.to_string())
122+ pub fn set_column(&mut self, column_name: &str) {
123+ self.intermediate_names.push(column_name.to_string())
124+ }
125+
126+ pub fn set_removed(&mut self) {
127+ self.removed = true;
128+ }
129+
130+ fn real_name(&self) -> &str {
131+ self.intermediate_names
132+ .last()
133+ .unwrap_or_else(|| &self.original_name)
142134 }
143135}
144136
@@ -168,26 +160,46 @@ impl Schema {
168160 )?
169161 .iter()
170162 .map(|row| row.get::<'_, _, String>("table_name"))
171− .filter(|real_name| !self.is_table_hidden(real_name))
172− .map(|real_name| self.get_table_by_real_name(db, &real_name))
163+ .filter_map(|real_name| {
164+ let table_changes = self
165+ .table_changes
166+ .iter()
167+ .find(|changes| changes.real_name == real_name);
168+
169+ // Skip table if it has been removed
170+ if let Some(changes) = table_changes {
171+ if changes.removed {
172+ return None;
173+ }
174+ }
175+
176+ Some(self.get_table_by_real_name(db, &real_name))
177+ })
173178 .collect()
174179 }
175180
176181 pub fn get_table(&self, db: &mut dyn Conn, table_name: &str) -> anyhow::Result<Table> {
177− let real_table_name = self
178− .get_table_real_name(table_name)
182+ let table_changes = self
183+ .table_changes
184+ .iter()
185+ .find(|changes| changes.current_name == table_name);
186+
187+ let real_table_name = table_changes
188+ .map(|changes| changes.real_name.to_string())
179189 .unwrap_or_else(|| table_name.to_string());
190+
180191 self.get_table_by_real_name(db, &real_table_name)
181192 }
182193
183− pub fn get_table_by_real_name(
194+ fn get_table_by_real_name(
184195 &self,
185196 db: &mut dyn Conn,
186197 real_table_name: &str,
187198 ) -> anyhow::Result<Table> {
188− if self.is_table_hidden(real_table_name) {
189− return Err(anyhow!("no table named {}", real_table_name));
190− }
199+ let table_changes = self
200+ .table_changes
201+ .iter()
202+ .find(|changes| changes.real_name == real_table_name);
191203
192204 let real_columns: Vec<(String, String, bool)> = db
193205 .query(&format!(
@@ -209,27 +221,43 @@ impl Schema {
209221 })
210222 .collect();
211223
212− let mut columns: Vec<Column> = Vec::new();
213−
214− for (column_name, data_type, nullable) in real_columns {
215− if self.is_column_hidden(real_table_name, &column_name) {
216− continue;
224+ let mut ignore_columns: HashSet<String> = HashSet::new();
225+ let mut aliases: HashMap<String, &str> = HashMap::new();
226+
227+ if let Some(changes) = table_changes {
228+ for column_changes in &changes.column_changes {
229+ if column_changes.removed {
230+ ignore_columns.insert(column_changes.real_name().to_string());
231+ } else {
232+ aliases.insert(
233+ column_changes.real_name().to_string(),
234+ &column_changes.current_name,
235+ );
236+ }
237+
238+ if !column_changes.intermediate_names.is_empty() {
239+ ignore_columns.insert(column_changes.original_name.to_string());
240+
241+ let non_used_intermediate = &column_changes.intermediate_names
242+ [..column_changes.intermediate_names.len() - 1];
243+ for intermediate in non_used_intermediate {
244+ ignore_columns.insert(intermediate.to_string());
245+ }
246+ }
217247 }
248+ }
249+
250+ let mut columns: Vec<Column> = Vec::new();
218251
219− if is_column_temporary(&column_name) {
252+ for (real_name, data_type, nullable) in real_columns {
253+ if ignore_columns.contains(&*real_name) {
220254 continue;
221255 }
222256
223− let (name, real_name) = if let Some(alias) =
224− self.get_column_alias_from_real_name(real_table_name, &column_name)
225− {
226− (alias, column_name)
227− } else {
228− let real_name = self
229− .get_column_real_name(real_table_name, &column_name)
230− .unwrap_or(&column_name);
231− (column_name.to_string(), real_name.to_string())
232− };
257+ let name = aliases
258+ .get(&real_name)
259+ .map(|alias| alias.to_string())
260+ .unwrap_or_else(|| real_name.to_string());
233261
234262 columns.push(Column {
235263 name,
@@ -239,12 +267,12 @@ impl Schema {
239267 });
240268 }
241269
242− let table_name = self
243− .get_table_alias_from_real_name(real_table_name)
244− .unwrap_or_else(|| real_table_name.to_string());
270+ let current_table_name = table_changes
271+ .map(|changes| changes.current_name.as_ref())
272+ .unwrap_or_else(|| real_table_name);
245273
246274 let table = Table {
247− name: table_name,
275+ name: current_table_name.to_string(),
248276 real_name: real_table_name.to_string(),
249277 columns: columns,
250278 };
@@ -252,7 +280,3 @@ impl Schema {
252280 Ok(table)
253281 }
254282}
255−
256−fn is_column_temporary(name: &str) -> bool {
257− name.starts_with("__reshape_")
258−}