1use std::fmt::Write;
2
3use rorm_declaration::imr::DefaultValue;
4
5#[cfg(feature = "postgres")]
6use crate::create_trigger::trigger_annotation_to_trigger_postgres;
7#[cfg(feature = "sqlite")]
8use crate::create_trigger::trigger_annotation_to_trigger_sqlite;
9#[cfg(feature = "mysql")]
10use crate::db_specific::mysql;
11#[cfg(feature = "postgres")]
12use crate::db_specific::postgres;
13#[cfg(feature = "sqlite")]
14use crate::db_specific::sqlite;
15use crate::error::Error;
16use crate::{Annotation, DbType, Value};
17
18pub trait CreateColumn<'post_build>: Sized {
22 fn build(self, s: &mut String) -> Result<(), Error>;
29}
30
31#[derive(Debug)]
35pub struct SQLAnnotation<'post_build> {
36 pub(crate) annotation: &'post_build Annotation,
37}
38
39#[derive(Debug)]
43#[cfg(feature = "sqlite")]
44pub struct CreateColumnSQLiteData<'until_build, 'post_build> {
45 pub(crate) name: &'until_build str,
46 pub(crate) table_name: &'until_build str,
47 pub(crate) data_type: DbType,
48 pub(crate) annotations: Vec<SQLAnnotation<'post_build>>,
49 pub(crate) statements: Option<&'until_build mut Vec<(String, Vec<Value<'post_build>>)>>,
50 pub(crate) lookup: Option<&'until_build mut Vec<Value<'post_build>>>,
51}
52
53#[derive(Debug)]
57#[cfg(feature = "mysql")]
58pub struct CreateColumnMySQLData<'until_build, 'post_build> {
59 pub(crate) name: &'until_build str,
60 pub(crate) data_type: DbType,
61 pub(crate) annotations: Vec<SQLAnnotation<'post_build>>,
62 pub(crate) statements: Option<&'until_build mut Vec<(String, Vec<Value<'post_build>>)>>,
63 pub(crate) lookup: Option<&'until_build mut Vec<Value<'post_build>>>,
64}
65
66#[derive(Debug)]
70#[cfg(feature = "postgres")]
71pub struct CreateColumnPostgresData<'until_build, 'post_build> {
72 pub(crate) name: &'until_build str,
73 pub(crate) table_name: &'until_build str,
74 pub(crate) data_type: DbType,
75 pub(crate) annotations: Vec<SQLAnnotation<'post_build>>,
76 pub(crate) pre_statements: Option<&'until_build mut Vec<(String, Vec<Value<'post_build>>)>>,
77 pub(crate) statements: Option<&'until_build mut Vec<(String, Vec<Value<'post_build>>)>>,
78}
79
80#[derive(Debug)]
86pub enum CreateColumnImpl<'until_build, 'post_build> {
87 #[cfg(feature = "sqlite")]
91 SQLite(CreateColumnSQLiteData<'until_build, 'post_build>),
92 #[cfg(feature = "mysql")]
96 MySQL(CreateColumnMySQLData<'until_build, 'post_build>),
97 #[cfg(feature = "postgres")]
101 Postgres(CreateColumnPostgresData<'until_build, 'post_build>),
102}
103
104impl<'post_build> CreateColumn<'post_build> for CreateColumnImpl<'_, 'post_build> {
105 fn build(self, s: &mut String) -> Result<(), Error> {
106 match self {
107 #[cfg(feature = "sqlite")]
108 CreateColumnImpl::SQLite(mut d) => {
109 write!(
110 s,
111 "\"{}\" {} ",
112 d.name,
113 match d.data_type {
114 DbType::Binary | DbType::Uuid => "BLOB",
115 DbType::VarChar
116 | DbType::Date
117 | DbType::DateTime
118 | DbType::Timestamp
119 | DbType::Time
120 | DbType::Choices => "TEXT",
121 DbType::Int8
122 | DbType::Int16
123 | DbType::Int32
124 | DbType::Int64
125 | DbType::Boolean => "INTEGER",
126 DbType::Float | DbType::Double => "REAL",
127 DbType::BitVec | DbType::MacAddress | DbType::IpNetwork => unreachable!(
128 "BitVec, MacAddress and IpNetwork are not available for sqlite"
129 ),
130 }
131 )
132 .unwrap();
133
134 for (idx, x) in d.annotations.iter().enumerate() {
135 if let Some(ref mut s) = d.statements {
136 trigger_annotation_to_trigger_sqlite(
137 x.annotation,
138 &d.data_type,
139 d.table_name,
140 d.name,
141 s,
142 );
143 }
144
145 match &x.annotation {
146 Annotation::AutoIncrement => write!(s, "AUTOINCREMENT").unwrap(),
147 Annotation::AutoCreateTime => {
148 write!(
149 s,
150 "DEFAULT {}",
151 match d.data_type {
152 DbType::Date => "CURRENT_DATE",
153 DbType::DateTime => "CURRENT_TIMESTAMP",
154 DbType::Timestamp => "CURRENT_TIMESTAMP",
155 DbType::Time => "CURRENT_TIME",
156 _ => "",
157 }
158 )
159 .unwrap();
160 }
161 Annotation::DefaultValue(d) => match d {
162 DefaultValue::String(dv) => {
163 write!(s, "DEFAULT {}", sqlite::fmt(dv)).unwrap()
164 }
165 DefaultValue::Integer(i) => write!(s, "DEFAULT {i}").unwrap(),
166 DefaultValue::Float(f) => write!(s, "DEFAULT {f}").unwrap(),
167 DefaultValue::Boolean(b) => {
168 if *b {
169 write!(s, "DEFAULT 1").unwrap();
170 } else {
171 write!(s, "DEFAULT 0").unwrap();
172 }
173 }
174 },
175 Annotation::NotNull => write!(s, "NOT NULL").unwrap(),
176 Annotation::PrimaryKey => write!(s, "PRIMARY KEY").unwrap(),
177 Annotation::Unique => write!(s, "UNIQUE").unwrap(),
178 Annotation::ForeignKey(fk) => write!(
179 s,
180 "REFERENCES \"{}\" (\"{}\") ON DELETE {} ON UPDATE {}",
181 fk.table_name, fk.column_name, fk.on_delete, fk.on_update
182 )
183 .unwrap(),
184 _ => {}
185 }
186
187 if idx != d.annotations.len() - 1 {
188 write!(s, " ").unwrap();
189 }
190 }
191
192 Ok(())
193 }
194 #[cfg(feature = "mysql")]
195 CreateColumnImpl::MySQL(mut d) => {
196 write!(s, "`{}` ", d.name).unwrap();
197
198 match d.data_type {
199 DbType::VarChar => {
200 let a_opt = d
201 .annotations
202 .iter()
203 .find(|x| x.annotation.eq_shallow(&Annotation::MaxLength(0)));
204
205 if let Some(a) = a_opt {
206 if let Annotation::MaxLength(max_length) = a.annotation {
207 if *max_length < 2i32.pow(14) - 1 {
209 write!(s, "VARCHAR({max_length}) ").unwrap();
210 } else {
211 write!(s, "LONGTEXT ").unwrap();
212 }
213 } else {
214 return Err(Error::SQLBuildError(String::from(
215 "VARCHAR must have a max_length annotation",
216 )));
217 }
218 } else {
219 return Err(Error::SQLBuildError(String::from(
220 "VARCHAR must have a max_length annotation",
221 )));
222 }
223 }
224 DbType::Binary | DbType::Uuid => write!(s, "LONGBLOB ").unwrap(),
225 DbType::Int8 => write!(s, "TINYINT(255) ").unwrap(),
226 DbType::Int16 => write!(s, "SMALLINT(255) ").unwrap(),
227 DbType::Int32 => write!(s, "INT(255) ").unwrap(),
228 DbType::Int64 => write!(s, "BIGINT(255) ").unwrap(),
229 DbType::Float => write!(s, "FLOAT(24) ").unwrap(),
230 DbType::Double => write!(s, "DOUBLE(53) ").unwrap(),
231 DbType::Boolean => write!(s, "BOOL ").unwrap(),
232 DbType::Date => write!(s, "DATE ").unwrap(),
233 DbType::DateTime => write!(s, "DATETIME ").unwrap(),
234 DbType::Timestamp => write!(s, "TIMESTAMP ").unwrap(),
235 DbType::Time => write!(s, "TIME ").unwrap(),
236 DbType::Choices => {
237 let a_opt = d.annotations.iter().find(|x| {
238 x.annotation
239 .eq_shallow(&Annotation::Choices(Default::default()))
240 });
241
242 if let Some(a) = a_opt {
243 if let Annotation::Choices(values) = a.annotation {
244 write!(
245 s,
246 "ENUM({}) ",
247 values
248 .iter()
249 .map(|x| mysql::fmt(x))
250 .collect::<Vec<String>>()
251 .join(", ")
252 )
253 .unwrap();
254 } else {
255 return Err(Error::SQLBuildError(
256 "VARCHAR must have a MaxLength annotation".to_string(),
257 ));
258 }
259 } else {
260 return Err(Error::SQLBuildError(
261 "VARCHAR must have a MaxLength annotation".to_string(),
262 ));
263 }
264 }
265 DbType::BitVec | DbType::IpNetwork | DbType::MacAddress => {
266 unreachable!("BitVec, MacAddress and IpNetwork are not available for mysql")
267 }
268 };
269
270 for (idx, x) in d.annotations.iter().enumerate() {
271 match &x.annotation {
272 Annotation::AutoIncrement => write!(s, "AUTO_INCREMENT").unwrap(),
273 Annotation::AutoCreateTime => {
274 write!(
275 s,
276 "DEFAULT {}",
277 match d.data_type {
278 DbType::Date => "CURRENT_DATE",
279 DbType::DateTime => "CURRENT_TIMESTAMP",
280 DbType::Timestamp => "CURRENT_TIMESTAMP",
281 DbType::Time => "CURRENT_TIME",
282 _ => "",
283 }
284 )
285 .unwrap();
286 }
287 Annotation::AutoUpdateTime => write!(
288 s,
289 "ON UPDATE {}",
290 match d.data_type {
291 DbType::Date => "CURRENT_DATE",
292 DbType::DateTime => "CURRENT_TIMESTAMP",
293 DbType::Timestamp => "CURRENT_TIMESTAMP",
294 DbType::Time => "CURRENT_TIME",
295 _ => "",
296 }
297 )
298 .unwrap(),
299 Annotation::DefaultValue(v) => match v {
300 DefaultValue::String(dv) => {
301 if let Some(l) = &mut d.lookup {
302 l.push(Value::String(dv))
303 }
304 write!(s, "DEFAULT ?").unwrap();
305 }
306 DefaultValue::Integer(i) => write!(s, "DEFAULT {i}").unwrap(),
307 DefaultValue::Float(f) => write!(s, "DEFAULT {f}").unwrap(),
308 DefaultValue::Boolean(b) => {
309 if *b {
310 write!(s, "DEFAULT 1").unwrap();
311 } else {
312 write!(s, "DEFAULT 0").unwrap();
313 }
314 }
315 },
316 Annotation::NotNull => write!(s, "NOT NULL").unwrap(),
317 Annotation::PrimaryKey => write!(s, "PRIMARY KEY").unwrap(),
318 Annotation::Unique => write!(s, "UNIQUE").unwrap(),
319 Annotation::ForeignKey(fk) => write!(
320 s,
321 "REFERENCES `{}`(`{}`) ON DELETE {} ON UPDATE {}",
322 fk.table_name, fk.column_name, fk.on_delete, fk.on_update
323 )
324 .unwrap(),
325 _ => {}
326 }
327
328 if idx != d.annotations.len() - 1 {
329 write!(s, " ").unwrap();
330 }
331 }
332
333 Ok(())
334 }
335 #[cfg(feature = "postgres")]
336 CreateColumnImpl::Postgres(mut d) => {
337 write!(s, "\"{}\" ", d.name).unwrap();
338
339 match d.data_type {
340 DbType::VarChar => {
341 let a_opt = d
342 .annotations
343 .iter()
344 .find(|x| x.annotation.eq_shallow(&Annotation::MaxLength(0)));
345
346 if let Some(a) = a_opt {
347 if let Annotation::MaxLength(max_length) = a.annotation {
348 write!(s, "character varying ({max_length}) ").unwrap();
349 } else {
350 return Err(Error::SQLBuildError(
351 "character varying must have a max_length annotation"
352 .to_string(),
353 ));
354 }
355 } else {
356 return Err(Error::SQLBuildError(
357 "character varying must have a max_length annotation".to_string(),
358 ));
359 }
360 }
361 DbType::Choices => {
362 let a_opt = d.annotations.iter().find(|x| {
363 x.annotation
364 .eq_shallow(&Annotation::Choices(Default::default()))
365 });
366
367 if let Some(a) = a_opt {
368 if let Annotation::Choices(values) = a.annotation {
369 if let Some(stmts) = d.pre_statements {
370 stmts.push((
371 format!(
372 "CREATE TYPE _{}_{} AS ENUM({});",
373 d.table_name,
374 d.name,
375 values
376 .iter()
377 .map(|x| { postgres::fmt(x) })
378 .collect::<Vec<String>>()
379 .join(", ")
380 ),
381 vec![],
382 ));
383 };
384 write!(s, "_{}_{} ", d.table_name, d.name,).unwrap();
385 } else {
386 return Err(Error::SQLBuildError(
387 "VARCHAR must have a MaxLength annotation".to_string(),
388 ));
389 }
390 } else {
391 return Err(Error::SQLBuildError(
392 "VARCHAR must have a MaxLength annotation".to_string(),
393 ));
394 }
395 }
396 DbType::Uuid => write!(s, "uuid ").unwrap(),
397 DbType::MacAddress => write!(s, "macaddr ").unwrap(),
398 DbType::IpNetwork => write!(s, "inet ").unwrap(),
399 DbType::BitVec => write!(s, "varbit ").unwrap(),
400 DbType::Binary => write!(s, "bytea ").unwrap(),
401 DbType::Int8 => write!(s, "smallint ").unwrap(),
402 DbType::Int16 => {
403 if d.annotations
404 .iter()
405 .any(|x| x.annotation.eq_shallow(&Annotation::AutoIncrement))
406 {
407 write!(s, "smallserial ").unwrap();
408 } else {
409 write!(s, "smallint ").unwrap();
410 }
411 }
412 DbType::Int32 => {
413 if d.annotations
414 .iter()
415 .any(|x| x.annotation.eq_shallow(&Annotation::AutoIncrement))
416 {
417 write!(s, "serial ").unwrap();
418 } else {
419 write!(s, "integer ").unwrap();
420 }
421 }
422 DbType::Int64 => {
423 if d.annotations
424 .iter()
425 .any(|x| x.annotation.eq_shallow(&Annotation::AutoIncrement))
426 {
427 write!(s, "bigserial ").unwrap();
428 } else {
429 write!(s, "bigint ").unwrap();
430 }
431 }
432 DbType::Float => write!(s, "real ").unwrap(),
433 DbType::Double => write!(s, "double precision ").unwrap(),
434 DbType::Boolean => write!(s, "boolean ").unwrap(),
435 DbType::Date => write!(s, "date ").unwrap(),
436 DbType::DateTime => write!(s, "timestamptz ").unwrap(),
437 DbType::Timestamp => write!(s, "timestamp ").unwrap(),
438 DbType::Time => write!(s, "time ").unwrap(),
439 };
440
441 for (idx, x) in d.annotations.iter().enumerate() {
442 if let Some(ref mut s) = d.statements {
443 trigger_annotation_to_trigger_postgres(
444 x.annotation,
445 d.table_name,
446 d.name,
447 s,
448 );
449 }
450
451 match &x.annotation {
452 Annotation::AutoCreateTime => {
453 write!(
454 s,
455 "DEFAULT {}",
456 match d.data_type {
457 DbType::Date => "CURRENT_DATE",
458 DbType::DateTime => "now()",
459 DbType::Timestamp => "CURRENT_TIMESTAMP",
460 DbType::Time => "CURRENT_TIME",
461 _ => "",
462 }
463 )
464 .unwrap();
465 }
466 Annotation::DefaultValue(d) => match d {
467 DefaultValue::String(dv) => {
468 write!(s, "DEFAULT {}", postgres::fmt(dv)).unwrap()
469 }
470 DefaultValue::Integer(i) => write!(s, "DEFAULT {i}").unwrap(),
471 DefaultValue::Float(f) => write!(s, "DEFAULT {f}").unwrap(),
472 DefaultValue::Boolean(b) => {
473 if *b {
474 write!(s, "DEFAULT true").unwrap();
475 } else {
476 write!(s, "DEFAULT false").unwrap();
477 }
478 }
479 },
480 Annotation::NotNull => write!(s, "NOT NULL").unwrap(),
481 Annotation::PrimaryKey => write!(s, "PRIMARY KEY").unwrap(),
482 Annotation::Unique => write!(s, "UNIQUE").unwrap(),
483 Annotation::ForeignKey(fk) => write!(
484 s,
485 "REFERENCES \"{}\"(\"{}\") ON DELETE {} ON UPDATE {}",
486 fk.table_name, fk.column_name, fk.on_delete, fk.on_update
487 )
488 .unwrap(),
489 _ => {}
490 };
491
492 if idx != d.annotations.len() - 1 {
493 write!(s, " ").unwrap();
494 }
495 }
496
497 Ok(())
498 }
499 }
500 }
501}