Skip to main content

limbo_sqlite3_parser/to_sql_string/stmt/
create_table.rs

1use std::fmt::Display;
2
3use crate::{ast, to_sql_string::ToSqlString};
4
5impl ToSqlString for ast::CreateTableBody {
6    fn to_sql_string<C: crate::to_sql_string::ToSqlContext>(&self, context: &C) -> String {
7        match self {
8            Self::AsSelect(select) => format!("AS {}", select.to_sql_string(context)),
9            Self::ColumnsAndConstraints {
10                columns,
11                constraints,
12                options,
13            } => {
14                format!(
15                    "({}{}){}",
16                    columns
17                        .iter()
18                        .map(|(_, col)| col.to_sql_string(context))
19                        .collect::<Vec<_>>()
20                        .join(", "),
21                    constraints
22                        .as_ref()
23                        .map_or("".to_string(), |constraints| format!(
24                            ", {}",
25                            constraints
26                                .iter()
27                                .map(|constraint| constraint.to_sql_string(context))
28                                .collect::<Vec<_>>()
29                                .join(", ")
30                        )),
31                    options
32                )
33            }
34        }
35    }
36}
37
38impl ToSqlString for ast::NamedTableConstraint {
39    fn to_sql_string<C: crate::to_sql_string::ToSqlContext>(&self, context: &C) -> String {
40        if let Some(name) = &self.name {
41            format!(
42                "CONSTRAINT {} {}",
43                name.0,
44                self.constraint.to_sql_string(context)
45            )
46        } else {
47            format!("{}", self.constraint.to_sql_string(context))
48        }
49    }
50}
51
52impl ToSqlString for ast::TableConstraint {
53    fn to_sql_string<C: crate::to_sql_string::ToSqlContext>(&self, context: &C) -> String {
54        match self {
55            Self::Check(expr) => format!("CHECK ({})", expr.to_sql_string(context)),
56            Self::ForeignKey {
57                columns,
58                clause,
59                deref_clause,
60            } => format!(
61                "FOREIGN KEY ({}) {}{}",
62                columns
63                    .iter()
64                    .map(|col| col.to_string())
65                    .collect::<Vec<_>>()
66                    .join(", "),
67                clause,
68                if let Some(deref) = deref_clause {
69                    deref.to_string()
70                } else {
71                    "".to_string()
72                }
73            ),
74            Self::PrimaryKey {
75                columns,
76                auto_increment,
77                conflict_clause,
78            } => format!(
79                "PRIMARY KEY ({}){}{}",
80                columns
81                    .iter()
82                    .map(|col| col.to_sql_string(context))
83                    .collect::<Vec<_>>()
84                    .join(", "),
85                conflict_clause.map_or("".to_string(), |conflict| format!(" {}", conflict)),
86                auto_increment.then_some(" AUTOINCREMENT").unwrap_or("")
87            ),
88            Self::Unique {
89                columns,
90                conflict_clause,
91            } => format!(
92                "UNIQUE ({}){}",
93                columns
94                    .iter()
95                    .map(|col| col.to_sql_string(context))
96                    .collect::<Vec<_>>()
97                    .join(", "),
98                conflict_clause.map_or("".to_string(), |conflict| format!(" {}", conflict))
99            ),
100        }
101    }
102}
103
104impl Display for ast::TableOptions {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        write!(
107            f,
108            "{}",
109            if *self == Self::NONE {
110                ""
111            } else if *self == Self::STRICT {
112                " STRICT"
113            } else {
114                " WITHOUT ROWID"
115            }
116        )
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use crate::to_sql_string_test;
123
124    to_sql_string_test!(
125        test_create_table_simple,
126        "CREATE TABLE t (a INTEGER, b TEXT);"
127    );
128
129    to_sql_string_test!(
130        test_create_table_primary_key,
131        "CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);"
132    );
133
134    to_sql_string_test!(
135        test_create_table_multi_primary_key,
136        "CREATE TABLE t (a INTEGER, b TEXT, PRIMARY KEY (a, b));"
137    );
138
139    to_sql_string_test!(
140        test_create_table_data_types,
141        "CREATE TABLE t (a INTEGER, b TEXT, c REAL, d BLOB, e NUMERIC);"
142    );
143
144    to_sql_string_test!(
145        test_create_table_foreign_key,
146        "CREATE TABLE t2 (id INTEGER PRIMARY KEY, t_id INTEGER, FOREIGN KEY (t_id) REFERENCES t(id));"
147    );
148
149    to_sql_string_test!(
150        test_create_table_foreign_key_cascade,
151        "CREATE TABLE t2 (id INTEGER PRIMARY KEY, t_id INTEGER, FOREIGN KEY (t_id) REFERENCES t(id) ON DELETE CASCADE);"
152    );
153
154    to_sql_string_test!(
155        test_create_table_unique,
156        "CREATE TABLE t (a INTEGER UNIQUE, b TEXT);"
157    );
158
159    to_sql_string_test!(
160        test_create_table_not_null,
161        "CREATE TABLE t (a INTEGER NOT NULL, b TEXT);"
162    );
163
164    to_sql_string_test!(
165        test_create_table_check,
166        "CREATE TABLE t (a INTEGER CHECK (a > 0), b TEXT);"
167    );
168
169    to_sql_string_test!(
170        test_create_table_default,
171        "CREATE TABLE t (a INTEGER DEFAULT 0, b TEXT);"
172    );
173
174    to_sql_string_test!(
175        test_create_table_multiple_constraints,
176        "CREATE TABLE t (a INTEGER NOT NULL UNIQUE, b TEXT DEFAULT 'default');"
177    );
178
179    to_sql_string_test!(
180        test_create_table_generated_column,
181        "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER AS (a + b));"
182    );
183
184    to_sql_string_test!(
185        test_create_table_generated_stored,
186        "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER AS (a + b) STORED);"
187    );
188
189    to_sql_string_test!(
190        test_create_table_generated_virtual,
191        "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER AS (a + b) VIRTUAL);"
192    );
193
194    to_sql_string_test!(
195        test_create_table_quoted_columns,
196        "CREATE TABLE t (\"select\" INTEGER, \"from\" TEXT);"
197    );
198
199    to_sql_string_test!(
200        test_create_table_quoted_table,
201        "CREATE TABLE \"my table\" (a INTEGER);"
202    );
203
204    to_sql_string_test!(
205        test_create_table_if_not_exists,
206        "CREATE TABLE IF NOT EXISTS t (a INTEGER);"
207    );
208
209    to_sql_string_test!(test_create_temp_table, "CREATE TEMP TABLE t (a INTEGER);");
210
211    to_sql_string_test!(
212        test_create_table_without_rowid,
213        "CREATE TABLE t (a INTEGER PRIMARY KEY, b TEXT) WITHOUT ROWID;"
214    );
215
216    to_sql_string_test!(
217        test_create_table_named_primary_key,
218        "CREATE TABLE t (a INTEGER CONSTRAINT pk_a PRIMARY KEY);"
219    );
220
221    to_sql_string_test!(
222        test_create_table_named_unique,
223        "CREATE TABLE t (a INTEGER, CONSTRAINT unique_a UNIQUE (a));"
224    );
225
226    to_sql_string_test!(
227        test_create_table_named_foreign_key,
228        "CREATE TABLE t2 (id INTEGER, t_id INTEGER, CONSTRAINT fk_t FOREIGN KEY (t_id) REFERENCES t(id));"
229    );
230
231    to_sql_string_test!(
232        test_create_table_complex,
233        "CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER NOT NULL, b TEXT DEFAULT 'default', c INTEGER AS (a * 2), CONSTRAINT unique_a UNIQUE (a));"
234    );
235
236    to_sql_string_test!(
237        test_create_table_multiple_foreign_keys,
238        "CREATE TABLE t3 (id INTEGER PRIMARY KEY, t1_id INTEGER, t2_id INTEGER, FOREIGN KEY (t1_id) REFERENCES t1(id), FOREIGN KEY (t2_id) REFERENCES t2(id));"
239    );
240}