Skip to main content

toasty_sql/stmt/
alter_column.rs

1use crate::stmt::ColumnDef;
2
3use super::Statement;
4
5use toasty_core::{
6    driver::Capability,
7    schema::db::{Column, ColumnId, Type},
8};
9
10/// A statement to alter a column in a table.
11#[derive(Debug, Clone)]
12pub struct AlterColumn {
13    /// ID of the column being altered.
14    pub id: ColumnId,
15
16    /// Current column definition.
17    pub column_def: ColumnDef,
18
19    /// Changes to be made to the column.
20    pub changes: AlterColumnChanges,
21}
22
23/// A statement to alter a column in a table.
24#[derive(Debug, Clone)]
25pub struct AlterColumnChanges {
26    /// New name for the column (if renaming).
27    pub new_name: Option<String>,
28
29    /// New type information.
30    pub new_ty: Option<Type>,
31
32    /// New nullability constraint.
33    pub new_not_null: Option<bool>,
34
35    /// New auto increment behavior.
36    pub new_auto_increment: Option<bool>,
37}
38
39impl AlterColumnChanges {
40    /// Computes the set of changes between two column definitions.
41    ///
42    /// Each field is `Some` only when the corresponding property differs
43    /// between `previous` and `next`.
44    pub fn from_diff(previous: &Column, next: &Column) -> Self {
45        Self {
46            new_name: (previous.name != next.name).then(|| next.name.clone()),
47            new_ty: (previous.storage_ty != next.storage_ty).then(|| next.storage_ty.clone()),
48            new_not_null: (previous.nullable != next.nullable).then_some(!next.nullable),
49            new_auto_increment: (previous.auto_increment != next.auto_increment)
50                .then_some(next.auto_increment),
51        }
52    }
53
54    /// Splits up this set of changes into a [`Vec`] of individual changes.
55    ///
56    /// The rename change is emitted last so that any preceding statements
57    /// (type, nullability, auto-increment) still find the column under its
58    /// pre-rename name.
59    pub fn split(self) -> Vec<Self> {
60        let Self {
61            new_name,
62            new_ty,
63            new_not_null,
64            new_auto_increment,
65        } = self;
66        let default = AlterColumnChanges {
67            new_name: None,
68            new_ty: None,
69            new_not_null: None,
70            new_auto_increment: None,
71        };
72        let mut result = vec![];
73        if new_ty.is_some() {
74            result.push(Self {
75                new_ty,
76                ..default.clone()
77            });
78        }
79        if new_not_null.is_some() {
80            result.push(Self {
81                new_not_null,
82                ..default.clone()
83            });
84        }
85        if new_auto_increment.is_some() {
86            result.push(Self {
87                new_auto_increment,
88                ..default.clone()
89            });
90        }
91        if new_name.is_some() {
92            result.push(Self {
93                new_name,
94                ..default.clone()
95            });
96        }
97        result
98    }
99
100    /// Returns `true` if any type-level property changed (type, nullability, or auto-increment).
101    pub fn has_type_change(&self) -> bool {
102        self.new_ty.is_some() || self.new_not_null.is_some() || self.new_auto_increment.is_some()
103    }
104}
105
106impl Statement {
107    /// Alters a column.
108    pub fn alter_column(
109        column: &Column,
110        changes: AlterColumnChanges,
111        capability: &Capability,
112    ) -> Self {
113        AlterColumn {
114            id: column.id,
115            column_def: ColumnDef::from_schema(column, &capability.storage_types, capability),
116            changes,
117        }
118        .into()
119    }
120}
121
122impl From<AlterColumn> for Statement {
123    fn from(value: AlterColumn) -> Self {
124        Self::AlterColumn(value)
125    }
126}