Skip to main content

limbo_sqlite3_parser/to_sql_string/stmt/
alter_table.rs

1use std::fmt::Display;
2
3use crate::{ast, to_sql_string::ToSqlString};
4
5impl ToSqlString for ast::AlterTableBody {
6    fn to_sql_string<C: crate::to_sql_string::ToSqlContext>(&self, context: &C) -> String {
7        match self {
8            Self::AddColumn(col_def) => format!("ADD COLUMN {}", col_def.to_sql_string(context)),
9            Self::DropColumn(name) => format!("DROP COLUMN {}", name.0),
10            Self::RenameColumn { old, new } => format!("RENAME COLUMN {} TO {}", old.0, new.0),
11            Self::RenameTo(name) => format!("RENAME TO {}", name.0),
12        }
13    }
14}
15
16impl ToSqlString for ast::ColumnDefinition {
17    fn to_sql_string<C: crate::to_sql_string::ToSqlContext>(&self, context: &C) -> String {
18        format!(
19            "{}{}{}",
20            self.col_name.0,
21            if let Some(col_type) = &self.col_type {
22                format!(" {}", col_type.to_sql_string(context))
23            } else {
24                "".to_string()
25            },
26            if !self.constraints.is_empty() {
27                format!(
28                    " {}",
29                    self.constraints
30                        .iter()
31                        .map(|constraint| constraint.to_sql_string(context))
32                        .collect::<Vec<_>>()
33                        .join(" ")
34                )
35            } else {
36                "".to_string()
37            }
38        )
39    }
40}
41
42impl ToSqlString for ast::NamedColumnConstraint {
43    fn to_sql_string<C: crate::to_sql_string::ToSqlContext>(&self, context: &C) -> String {
44        let mut ret = Vec::new();
45        if let Some(name) = &self.name {
46            ret.push(format!("CONSTRAINT {}", name.0));
47        }
48        ret.push(self.constraint.to_sql_string(context));
49        ret.join(" ")
50    }
51}
52
53impl ToSqlString for ast::ColumnConstraint {
54    fn to_sql_string<C: crate::to_sql_string::ToSqlContext>(&self, context: &C) -> String {
55        match self {
56            Self::Check(expr) => format!("CHECK ({})", expr.to_sql_string(context)),
57            Self::Collate { collation_name } => format!("COLLATE {}", collation_name.0),
58            Self::Default(expr) => {
59                if matches!(expr, ast::Expr::Literal(..)) {
60                    format!("DEFAULT {}", expr.to_sql_string(context))
61                } else {
62                    format!("DEFAULT ({})", expr.to_sql_string(context))
63                }
64            }
65            Self::Defer(expr) => expr.to_string(),
66            Self::ForeignKey {
67                clause,
68                deref_clause,
69            } => format!(
70                "{}{}",
71                clause,
72                if let Some(deref) = deref_clause {
73                    deref.to_string()
74                } else {
75                    "".to_string()
76                }
77            ),
78            Self::Generated { expr, typ } => {
79                // Don't need to add the generated part
80                format!(
81                    "AS ({}){}",
82                    expr.to_sql_string(context),
83                    if let Some(typ) = typ {
84                        format!(" {}", &typ.0)
85                    } else {
86                        "".to_string()
87                    }
88                )
89            }
90            Self::NotNull {
91                nullable: _,
92                conflict_clause,
93            } => {
94                // nullable should always be true here
95                format!(
96                    "NOT NULL{}",
97                    conflict_clause.map_or("".to_string(), |conflict| format!(" {}", conflict))
98                )
99            }
100            Self::PrimaryKey {
101                order,
102                conflict_clause,
103                auto_increment,
104            } => {
105                format!(
106                    "PRIMARY KEY{}{}{}",
107                    order.map_or("".to_string(), |order| format!(" {}", order)),
108                    conflict_clause.map_or("".to_string(), |conflict| format!(" {}", conflict)),
109                    auto_increment.then_some(" AUTOINCREMENT").unwrap_or("")
110                )
111            }
112            Self::Unique(conflict_clause) => {
113                format!(
114                    "UNIQUE{}",
115                    conflict_clause.map_or("".to_string(), |conflict| format!(" {}", conflict))
116                )
117            }
118        }
119    }
120}
121
122impl Display for ast::ForeignKeyClause {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        let value = format!(
125            "REFERENCES {}{}{}",
126            self.tbl_name.0,
127            if let Some(columns) = &self.columns {
128                format!(
129                    "({})",
130                    columns
131                        .iter()
132                        .map(|cols| cols.to_string())
133                        .collect::<Vec<_>>()
134                        .join(", ")
135                )
136            } else {
137                "".to_string()
138            },
139            if !self.args.is_empty() {
140                format!(
141                    " {}",
142                    self.args
143                        .iter()
144                        .map(|arg| arg.to_string())
145                        .collect::<Vec<_>>()
146                        .join(" ")
147                )
148            } else {
149                "".to_string()
150            }
151        );
152        write!(f, "{}", value)
153    }
154}
155
156impl Display for ast::RefArg {
157    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158        let value = match self {
159            Self::Match(name) => format!("MATCH {}", name.0),
160            Self::OnDelete(act) => format!("ON DELETE {}", act),
161            Self::OnUpdate(act) => format!("ON UPDATE {}", act),
162            Self::OnInsert(..) => unimplemented!(
163                "On Insert does not exist in SQLite: https://www.sqlite.org/lang_altertable.html"
164            ),
165        };
166        write!(f, "{}", value)
167    }
168}
169
170impl Display for ast::RefAct {
171    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172        let value = match self {
173            Self::Cascade => "CASCADE",
174            Self::NoAction => "NO ACTION",
175            Self::Restrict => "RESTRICT",
176            Self::SetDefault => "SET DEFAULT",
177            Self::SetNull => "SET NULL",
178        };
179        write!(f, "{}", value)
180    }
181}
182
183impl Display for ast::DeferSubclause {
184    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185        let value = format!(
186            "{}{}",
187            if self.deferrable {
188                "NOT DEFERRABLE"
189            } else {
190                "DEFERRABLE"
191            },
192            if let Some(init_deffered) = &self.init_deferred {
193                match init_deffered {
194                    ast::InitDeferredPred::InitiallyDeferred => " INITIALLY DEFERRED",
195                    ast::InitDeferredPred::InitiallyImmediate => " INITIALLY IMMEDIATE",
196                }
197            } else {
198                ""
199            }
200        );
201        write!(f, "{}", value)
202    }
203}
204#[cfg(test)]
205mod tests {
206    use crate::to_sql_string_test;
207
208    to_sql_string_test!(
209        test_alter_table_rename,
210        "ALTER TABLE t RENAME TO new_table_name;"
211    );
212
213    to_sql_string_test!(
214        test_alter_table_add_column,
215        "ALTER TABLE t ADD COLUMN c INTEGER;"
216    );
217
218    to_sql_string_test!(
219        test_alter_table_add_column_with_default,
220        "ALTER TABLE t ADD COLUMN c TEXT DEFAULT 'value';"
221    );
222
223    to_sql_string_test!(
224        test_alter_table_add_column_not_null_default,
225        "ALTER TABLE t ADD COLUMN c REAL NOT NULL DEFAULT 0.0;"
226    );
227
228    to_sql_string_test!(
229        test_alter_table_add_column_unique,
230        "ALTER TABLE t ADD COLUMN c TEXT UNIQUE",
231        ignore = "ParserError = Cannot add a UNIQUE column;"
232    );
233
234    to_sql_string_test!(
235        test_alter_table_rename_column,
236        "ALTER TABLE t RENAME COLUMN old_name TO new_name;"
237    );
238
239    to_sql_string_test!(test_alter_table_drop_column, "ALTER TABLE t DROP COLUMN c;");
240
241    to_sql_string_test!(
242        test_alter_table_add_column_check,
243        "ALTER TABLE t ADD COLUMN c INTEGER CHECK (c > 0);"
244    );
245
246    to_sql_string_test!(
247        test_alter_table_add_column_foreign_key,
248        "ALTER TABLE t ADD COLUMN c INTEGER REFERENCES t2(id) ON DELETE CASCADE;"
249    );
250
251    to_sql_string_test!(
252        test_alter_table_add_column_collate,
253        "ALTER TABLE t ADD COLUMN c TEXT COLLATE NOCASE;"
254    );
255
256    to_sql_string_test!(
257        test_alter_table_add_column_primary_key,
258        "ALTER TABLE t ADD COLUMN c INTEGER PRIMARY KEY;",
259        ignore = "ParserError = Cannot add a PRIMARY KEY column"
260    );
261
262    to_sql_string_test!(
263        test_alter_table_add_column_primary_key_autoincrement,
264        "ALTER TABLE t ADD COLUMN c INTEGER PRIMARY KEY AUTOINCREMENT;",
265        ignore = "ParserError = Cannot add a PRIMARY KEY column"
266    );
267
268    to_sql_string_test!(
269        test_alter_table_add_generated_column,
270        "ALTER TABLE t ADD COLUMN c_generated AS (a + b) STORED;"
271    );
272
273    to_sql_string_test!(
274        test_alter_table_add_column_schema,
275        "ALTER TABLE schema_name.t ADD COLUMN c INTEGER;"
276    );
277}