1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
pub mod action;
mod compare;
mod crud;
mod inner;

use super::schema::Schema;
use crate::prelude::*;
use action::{depends, Action};
pub use compare::*;
use crud::*;

use inner::*;
#[derive(Debug)]
pub(crate) struct Actions<'table> {
    name: &'table ObjectName,
    actions: Vec<Action<'table>>,
}
impl<'table> Actions<'table> {
    pub fn new(schema: &'table Schema, target: &'table Table) -> Result<Self> {
        let table = schema.get_table(&target.name);
        let mut out = Self {
            name: &target.name,
            actions: vec![],
        };
        out.init(Inner { table, target })?;
        Ok(out)
    }

    fn init(&mut self, inner: Inner<'table>) -> Result<()> {
        if inner.table.is_none() {
            let action = Action::create_table(inner.target);
            self.actions.push(action);
            return Ok(());
        }
        let columns = inner.columns();
        let constraints = inner.constraints();

        if move_required(&columns, &constraints) {
            self.perform_move(&inner, columns, constraints);
        } else {
            let table_name = &inner.target.name;
            for col in columns.delete {
                let action = Action::drop_col(table_name, col);
                self.actions.push(action);
            }
            for cons in constraints.delete {
                let action = Action::drop_cons(table_name, cons)?;
                self.actions.push(action);
            }
            for cons in &constraints.update {
                let action = Action::drop_cons(table_name, cons)?;
                self.actions.push(action);
            }

            for col in columns.create {
                let action = Action::create_column(table_name, col);
                self.actions.push(action);
            }
            for cons in &constraints.create {
                let action = Action::create_cons(table_name, cons);
                self.actions.push(action);
            }

            for cons in &constraints.update {
                let action = Action::create_cons(table_name, cons);
                self.actions.push(action);
            }
        }
        Ok(())
    }

    fn perform_move(
        &mut self,
        inner: &Inner<'table>,
        cols: ColCRUD<'table>,
        cons: ConsCRUD<'table>,
    ) {
        let move_action = Action::move_to(inner.table.unwrap(), &cols, &cons);
        self.actions.push(move_action);
        let table_name = &inner.target.name;

        // moves do not create columns as their names may conflict with constraints.
        for &col in &cols.create {
            let action = Action::create_column(table_name, col);
            self.actions.push(action);
        }
        // created constraints that could not have been created in move.
        // Not all constraints may be created in a move
        // because they depended on columns that where not yet created.
        // SQLite does not enforce constraints so these are all created
        // in the move step
        for &cons in &cons.create {
            if depends(cons, &cols.create) && !matches!(*DIALECT, SQLite) {
                let action = Action::create_cons(table_name, cons);
                self.actions.push(action);
            }
        }
    }

    pub fn as_migrations(self) -> Result<Vec<Migration>> {
        let mut migrations = vec![];
        let mut migr = Migration::new(self.name.clone());
        for action in self.actions {
            if action.is_fallible() && !migr.is_empty() {
                migrations.push(migr);
                migr = Migration::new(self.name.clone())
            }
            migr.push_up(action)?;
        }
        migrations.push(migr);

        Ok(migrations)
    }
}
pub(crate) fn move_required<'table>(cols: &ColCRUD<'table>, cons: &ConsCRUD<'table>) -> bool {
    let sqlite_conditions = DIALECT.requires_move()
        && !(cols.update.is_empty()
            && cols.delete.is_empty()
            && cons.delete.is_empty()
            && cons.create.is_empty()
            && cons.update.is_empty());
    sqlite_conditions || !cols.update.is_empty()
}