sql/query/
alter_table.rs

1use crate::schema::Constraint;
2use crate::util::SqlExtension;
3use crate::{Column, Dialect, ToSql, Type};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub enum AlterColumnAction {
7    SetType { typ: Type, using: Option<String> },
8    SetNullable(bool),
9}
10
11/// Alter table action
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum AlterAction {
14    AddColumn {
15        column: Column,
16    },
17    AlterColumn {
18        name: String,
19        action: AlterColumnAction,
20    },
21    AddConstraint {
22        name: String,
23        column: String,
24        constraint: Constraint,
25    },
26}
27
28impl AlterAction {
29    pub fn set_nullable(name: String, nullable: bool) -> Self {
30        Self::AlterColumn {
31            name,
32            action: AlterColumnAction::SetNullable(nullable),
33        }
34    }
35
36    pub fn set_type(name: String, typ: Type) -> Self {
37        Self::AlterColumn {
38            name,
39            action: AlterColumnAction::SetType { typ, using: None },
40        }
41    }
42
43    pub fn add_constraint(table: &str, column: String, constraint: Constraint) -> Self {
44        let name = format!("fk_{table}_{column}");
45        Self::AddConstraint {
46            name,
47            column,
48            constraint,
49        }
50    }
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct AlterTable {
55    pub schema: Option<String>,
56    pub name: String,
57    pub actions: Vec<AlterAction>,
58}
59
60impl ToSql for AlterTable {
61    fn write_sql(&self, buf: &mut String, dialect: Dialect) {
62        #[cfg(feature = "tracing")]
63        tracing::error_span!(
64            "alter-table",
65            table = format!(
66                "{}{}{}",
67                self.schema.as_deref().unwrap_or(""),
68                if self.schema.is_some() { "." } else { "" },
69                self.name
70            )
71        );
72        buf.push_str("ALTER TABLE ");
73        buf.push_table_name(&self.schema, &self.name);
74        buf.push_sql_sequence(&self.actions, ",", dialect);
75    }
76}
77
78impl ToSql for AlterAction {
79    fn write_sql(&self, buf: &mut String, dialect: Dialect) {
80        use AlterAction::*;
81        match self {
82            AddColumn { column } => {
83                buf.push_str(" ADD COLUMN ");
84                buf.push_str(&column.to_sql(dialect));
85            }
86            AlterColumn { name, action } => {
87                use AlterColumnAction::*;
88                buf.push_str(" ALTER COLUMN ");
89                buf.push_quoted(name);
90                match action {
91                    SetType { typ, using } => {
92                        buf.push_str(" TYPE ");
93                        buf.push_sql(typ, dialect);
94                        buf.push_str(" USING ");
95                        if let Some(using) = using {
96                            buf.push_str(&using)
97                        } else {
98                            buf.push_quoted(name);
99                            buf.push_str("::");
100                            buf.push_sql(typ, dialect);
101                        }
102                    }
103                    SetNullable(nullable) => {
104                        if *nullable {
105                            buf.push_str(" DROP NOT NULL");
106                        } else {
107                            buf.push_str(" SET NOT NULL");
108                        }
109                    }
110                }
111            }
112            AddConstraint {
113                name,
114                column,
115                constraint,
116            } => {
117                buf.push_str(" ADD CONSTRAINT ");
118                buf.push_quoted(name);
119                buf.push_str(" FOREIGN KEY (");
120                buf.push_quoted(column);
121                buf.push_str(") ");
122                buf.push_sql(constraint, dialect);
123            }
124        }
125    }
126}
127
128#[cfg(test)]
129mod test {
130    use super::*;
131
132    #[test]
133    fn test_alter_action() {
134        let alter = AlterAction::AlterColumn {
135            name: "foo".to_string(),
136            action: AlterColumnAction::SetType {
137                typ: Type::Text,
138                using: None,
139            },
140        };
141        assert_eq!(
142            alter.to_sql(Dialect::Postgres),
143            r#" ALTER COLUMN "foo" TYPE character varying USING "foo"::character varying"#
144        );
145
146        let alter = AlterAction::AlterColumn {
147            name: "foo".to_string(),
148            action: AlterColumnAction::SetType {
149                typ: Type::Text,
150                using: Some("SUBSTRING(foo, 1, 3)".to_string()),
151            },
152        };
153        assert_eq!(
154            alter.to_sql(Dialect::Postgres),
155            r#" ALTER COLUMN "foo" TYPE character varying USING SUBSTRING(foo, 1, 3)"#
156        );
157    }
158}