Skip to main content

rorm_sql/
create_column.rs

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 = "postgres")]
10use crate::db_specific::postgres;
11#[cfg(feature = "sqlite")]
12use crate::db_specific::sqlite;
13use crate::error::Error;
14use crate::{Annotation, DbType, Value};
15
16/**
17Trait representing the create table builder.
18*/
19pub trait CreateColumn<'post_build>: Sized {
20    /**
21    Builds the column based on the data.
22
23    **Parameter**:
24    - `s`: mutable reference to a String to write the operation to
25    */
26    fn build(self, s: &mut String) -> Result<(), Error>;
27}
28
29/**
30Representation of an annotation
31 */
32#[derive(Debug)]
33pub struct SQLAnnotation<'post_build> {
34    pub(crate) annotation: &'post_build Annotation,
35}
36
37/**
38Representation of the data of the creation of a column for the sqlite dialect
39 */
40#[derive(Debug)]
41#[cfg(feature = "sqlite")]
42pub struct CreateColumnSQLiteData<'until_build, 'post_build> {
43    pub(crate) name: &'until_build str,
44    pub(crate) table_name: &'until_build str,
45    pub(crate) data_type: DbType,
46    pub(crate) annotations: Vec<SQLAnnotation<'post_build>>,
47    pub(crate) statements: Option<&'until_build mut Vec<(String, Vec<Value<'post_build>>)>>,
48    pub(crate) lookup: Option<&'until_build mut Vec<Value<'post_build>>>,
49}
50
51/**
52Representation of the data of the creation of a column for the mysql dialect
53 */
54#[derive(Debug)]
55#[cfg(feature = "postgres")]
56pub struct CreateColumnPostgresData<'until_build, 'post_build> {
57    pub(crate) name: &'until_build str,
58    pub(crate) table_name: &'until_build str,
59    pub(crate) data_type: DbType,
60    pub(crate) annotations: Vec<SQLAnnotation<'post_build>>,
61    pub(crate) pre_statements: Option<&'until_build mut Vec<(String, Vec<Value<'post_build>>)>>,
62    pub(crate) statements: Option<&'until_build mut Vec<(String, Vec<Value<'post_build>>)>>,
63}
64
65/**
66Representation of the different implementations of the [CreateColumn] trait.
67
68Should only be constructed via [crate::DBImpl::create_column].
69*/
70#[derive(Debug)]
71pub enum CreateColumnImpl<'until_build, 'post_build> {
72    /**
73    SQLite representation of the create column operation.
74     */
75    #[cfg(feature = "sqlite")]
76    SQLite(CreateColumnSQLiteData<'until_build, 'post_build>),
77    /**
78    Postgres representation of the create column operation.
79     */
80    #[cfg(feature = "postgres")]
81    Postgres(CreateColumnPostgresData<'until_build, 'post_build>),
82}
83
84impl<'post_build> CreateColumn<'post_build> for CreateColumnImpl<'_, 'post_build> {
85    fn build(self, s: &mut String) -> Result<(), Error> {
86        match self {
87            #[cfg(feature = "sqlite")]
88            CreateColumnImpl::SQLite(mut d) => {
89                write!(
90                    s,
91                    "\"{}\" {} ",
92                    d.name,
93                    match d.data_type {
94                        DbType::Binary | DbType::Uuid => "BLOB",
95                        DbType::VarChar
96                        | DbType::Date
97                        | DbType::DateTime
98                        | DbType::Timestamp
99                        | DbType::Time
100                        | DbType::Choices => "TEXT",
101                        DbType::Int8
102                        | DbType::Int16
103                        | DbType::Int32
104                        | DbType::Int64
105                        | DbType::Boolean => "INTEGER",
106                        DbType::Float | DbType::Double => "REAL",
107                        DbType::BitVec | DbType::MacAddress | DbType::IpNetwork => unreachable!(
108                            "BitVec, MacAddress and IpNetwork are not available for sqlite"
109                        ),
110                    }
111                )
112                .unwrap();
113
114                for (idx, x) in d.annotations.iter().enumerate() {
115                    if let Some(ref mut s) = d.statements {
116                        trigger_annotation_to_trigger_sqlite(
117                            x.annotation,
118                            &d.data_type,
119                            d.table_name,
120                            d.name,
121                            s,
122                        );
123                    }
124
125                    match &x.annotation {
126                        Annotation::AutoIncrement => write!(s, "AUTOINCREMENT").unwrap(),
127                        Annotation::AutoCreateTime => {
128                            write!(
129                                s,
130                                "DEFAULT {}",
131                                match d.data_type {
132                                    DbType::Date => "CURRENT_DATE",
133                                    DbType::DateTime => "CURRENT_TIMESTAMP",
134                                    DbType::Timestamp => "CURRENT_TIMESTAMP",
135                                    DbType::Time => "CURRENT_TIME",
136                                    _ => "",
137                                }
138                            )
139                            .unwrap();
140                        }
141                        Annotation::DefaultValue(d) => match d {
142                            DefaultValue::String(dv) => {
143                                write!(s, "DEFAULT {}", sqlite::fmt(dv)).unwrap()
144                            }
145                            DefaultValue::Integer(i) => write!(s, "DEFAULT {i}").unwrap(),
146                            DefaultValue::Float(f) => write!(s, "DEFAULT {f}").unwrap(),
147                            DefaultValue::Boolean(b) => {
148                                if *b {
149                                    write!(s, "DEFAULT 1").unwrap();
150                                } else {
151                                    write!(s, "DEFAULT 0").unwrap();
152                                }
153                            }
154                        },
155                        Annotation::NotNull => write!(s, "NOT NULL").unwrap(),
156                        Annotation::PrimaryKey => write!(s, "PRIMARY KEY").unwrap(),
157                        Annotation::Unique => write!(s, "UNIQUE").unwrap(),
158                        Annotation::ForeignKey(fk) => write!(
159                            s,
160                            "REFERENCES \"{}\" (\"{}\") ON DELETE {} ON UPDATE {}",
161                            fk.table_name, fk.column_name, fk.on_delete, fk.on_update
162                        )
163                        .unwrap(),
164                        _ => {}
165                    }
166
167                    if idx != d.annotations.len() - 1 {
168                        write!(s, " ").unwrap();
169                    }
170                }
171
172                Ok(())
173            }
174            #[cfg(feature = "postgres")]
175            CreateColumnImpl::Postgres(mut d) => {
176                write!(s, "\"{}\" ", d.name).unwrap();
177
178                match d.data_type {
179                    DbType::VarChar => {
180                        let max_length = d
181                            .annotations
182                            .iter()
183                            .find_map(|x| match x.annotation {
184                                Annotation::MaxLength(x) => Some(*x),
185                                _ => None,
186                            })
187                            .ok_or_else(|| {
188                                Error::SQLBuildError(
189                                    "character varying must have a max_length annotation"
190                                        .to_string(),
191                                )
192                            })?;
193
194                        write!(s, "character varying ({max_length}) ").unwrap();
195                    }
196                    DbType::Choices => {
197                        let a_opt = d
198                            .annotations
199                            .iter()
200                            .find(|x| matches!(x.annotation, Annotation::Choices(_)));
201
202                        if let Some(a) = a_opt {
203                            if let Annotation::Choices(values) = a.annotation {
204                                if let Some(stmts) = d.pre_statements {
205                                    stmts.push((
206                                        format!(
207                                            "CREATE TYPE _{}_{} AS ENUM({});",
208                                            d.table_name,
209                                            d.name,
210                                            values
211                                                .iter()
212                                                .map(|x| { postgres::fmt(x) })
213                                                .collect::<Vec<String>>()
214                                                .join(", ")
215                                        ),
216                                        vec![],
217                                    ));
218                                };
219                                write!(s, "_{}_{} ", d.table_name, d.name,).unwrap();
220                            } else {
221                                return Err(Error::SQLBuildError(
222                                    "VARCHAR must have a MaxLength annotation".to_string(),
223                                ));
224                            }
225                        } else {
226                            return Err(Error::SQLBuildError(
227                                "VARCHAR must have a MaxLength annotation".to_string(),
228                            ));
229                        }
230                    }
231                    DbType::Uuid => write!(s, "uuid ").unwrap(),
232                    DbType::MacAddress => write!(s, "macaddr ").unwrap(),
233                    DbType::IpNetwork => write!(s, "inet ").unwrap(),
234                    DbType::BitVec => write!(s, "varbit ").unwrap(),
235                    DbType::Binary => write!(s, "bytea ").unwrap(),
236                    DbType::Int8 => write!(s, "smallint ").unwrap(),
237                    DbType::Int16 => {
238                        if d.annotations
239                            .iter()
240                            .any(|x| matches!(x.annotation, Annotation::AutoIncrement))
241                        {
242                            write!(s, "smallserial ").unwrap();
243                        } else {
244                            write!(s, "smallint ").unwrap();
245                        }
246                    }
247                    DbType::Int32 => {
248                        if d.annotations
249                            .iter()
250                            .any(|x| matches!(x.annotation, Annotation::AutoIncrement))
251                        {
252                            write!(s, "serial ").unwrap();
253                        } else {
254                            write!(s, "integer ").unwrap();
255                        }
256                    }
257                    DbType::Int64 => {
258                        if d.annotations
259                            .iter()
260                            .any(|x| matches!(x.annotation, Annotation::AutoIncrement))
261                        {
262                            write!(s, "bigserial ").unwrap();
263                        } else {
264                            write!(s, "bigint ").unwrap();
265                        }
266                    }
267                    DbType::Float => write!(s, "real ").unwrap(),
268                    DbType::Double => write!(s, "double precision ").unwrap(),
269                    DbType::Boolean => write!(s, "boolean ").unwrap(),
270                    DbType::Date => write!(s, "date ").unwrap(),
271                    DbType::DateTime => write!(s, "timestamptz ").unwrap(),
272                    DbType::Timestamp => write!(s, "timestamp ").unwrap(),
273                    DbType::Time => write!(s, "time ").unwrap(),
274                };
275
276                for (idx, x) in d.annotations.iter().enumerate() {
277                    if let Some(ref mut s) = d.statements {
278                        trigger_annotation_to_trigger_postgres(
279                            x.annotation,
280                            d.table_name,
281                            d.name,
282                            s,
283                        );
284                    }
285
286                    match &x.annotation {
287                        Annotation::AutoCreateTime => {
288                            write!(
289                                s,
290                                "DEFAULT {}",
291                                match d.data_type {
292                                    DbType::Date => "CURRENT_DATE",
293                                    DbType::DateTime => "now()",
294                                    DbType::Timestamp => "CURRENT_TIMESTAMP",
295                                    DbType::Time => "CURRENT_TIME",
296                                    _ => "",
297                                }
298                            )
299                            .unwrap();
300                        }
301                        Annotation::DefaultValue(d) => match d {
302                            DefaultValue::String(dv) => {
303                                write!(s, "DEFAULT {}", postgres::fmt(dv)).unwrap()
304                            }
305                            DefaultValue::Integer(i) => write!(s, "DEFAULT {i}").unwrap(),
306                            DefaultValue::Float(f) => write!(s, "DEFAULT {f}").unwrap(),
307                            DefaultValue::Boolean(b) => {
308                                if *b {
309                                    write!(s, "DEFAULT true").unwrap();
310                                } else {
311                                    write!(s, "DEFAULT false").unwrap();
312                                }
313                            }
314                        },
315                        Annotation::NotNull => write!(s, "NOT NULL").unwrap(),
316                        Annotation::PrimaryKey => write!(s, "PRIMARY KEY").unwrap(),
317                        Annotation::Unique => write!(s, "UNIQUE").unwrap(),
318                        Annotation::ForeignKey(fk) => write!(
319                            s,
320                            "REFERENCES \"{}\"(\"{}\") ON DELETE {} ON UPDATE {}",
321                            fk.table_name, fk.column_name, fk.on_delete, fk.on_update
322                        )
323                        .unwrap(),
324                        _ => {}
325                    };
326
327                    if idx != d.annotations.len() - 1 {
328                        write!(s, " ").unwrap();
329                    }
330                }
331
332                Ok(())
333            }
334        }
335    }
336}