Skip to main content

sea_schema/sqlite/def/
column.rs

1use super::DefaultType;
2use crate::{sqlite::def::ColumnVisibility, sqlx_types::SqlxRow};
3use sea_query::{
4    Alias, ColumnType, Index, IndexCreateStatement,
5    foreign_key::ForeignKeyAction as SeaQueryForeignKeyAction,
6};
7use std::num::ParseIntError;
8
9/// An SQLite column definition
10#[derive(Debug, PartialEq, Clone)]
11pub struct ColumnInfo {
12    /// Column id
13    pub cid: i64,
14    /// Column name.
15    pub name: String,
16    /// Declared type
17    pub r#type: ColumnType,
18    /// Whether a NOT NULL constraint is present.
19    pub not_null: bool,
20    pub default_value: DefaultType,
21    /// Column is part of the PRIMARY KEY.
22    pub primary_key: bool,
23    /// Hidden status
24    pub hidden: ColumnVisibility,
25}
26
27#[cfg(feature = "sqlx-sqlite")]
28impl ColumnInfo {
29    /// Map an [SqliteRow] into a column definition type [ColumnInfo]
30    pub fn to_column_def(row: SqlxRow) -> Result<ColumnInfo, ParseIntError> {
31        use crate::sqlx_types::Row;
32        let row = row.sqlite();
33        let col_not_null: i8 = row.get(3);
34        let is_pk: i8 = row.get(5);
35        let default_value: Option<String> = row.get(4);
36        let default_value = default_value.unwrap_or_default();
37        let hidden: i8 = row.get(6);
38        Ok(ColumnInfo {
39            cid: row.get(0),
40            name: row.get(1),
41            r#type: super::parse_type(row.get(2))?,
42            not_null: col_not_null != 0 || is_pk == 1,
43            default_value: if default_value == "NULL" {
44                DefaultType::Null
45            } else if default_value.is_empty() {
46                DefaultType::Unspecified
47            } else {
48                let value = default_value.to_owned().replace('\'', "");
49
50                if let Ok(is_int) = value.parse::<i64>() {
51                    DefaultType::Integer(is_int)
52                } else if let Ok(is_float) = value.parse::<f32>() {
53                    DefaultType::Float(is_float)
54                } else if value == "CURRENT_TIMESTAMP" {
55                    DefaultType::CurrentTimestamp
56                } else {
57                    DefaultType::String(value)
58                }
59            },
60            primary_key: is_pk != 0,
61            hidden: ColumnVisibility::from_hidden(hidden),
62        })
63    }
64
65    #[inline]
66    pub fn is_hidden(&self) -> bool {
67        self.hidden == ColumnVisibility::HiddenVirtual
68    }
69}
70
71#[cfg(not(feature = "sqlx-sqlite"))]
72impl ColumnInfo {
73    pub fn to_column_def(_: SqlxRow) -> Result<ColumnInfo, ParseIntError> {
74        unimplemented!()
75    }
76
77    #[inline]
78    pub fn is_hidden(&self) -> bool {
79        unimplemented!()
80    }
81}
82
83/// Maps the index and all columns in the index which is the result of queries
84/// `PRAGMA index_list(table_name)` and
85/// `SELECT * FROM sqlite_master where name = 'index_name'`
86#[derive(Debug, Default, Clone, PartialEq)]
87pub struct IndexInfo {
88    /// Is it a SQLindex
89    pub r#type: String,
90    pub index_name: String,
91    pub table_name: String,
92    pub unique: bool,
93    pub origin: String,
94    pub partial: i64,
95    pub columns: Vec<String>,
96}
97
98impl IndexInfo {
99    /// Write all the discovered index into a [IndexCreateStatement]
100    pub fn write(&self) -> IndexCreateStatement {
101        let mut new_index = Index::create();
102        // The name is only correct if the index was created by a CREATE INDEX statement. Otherwise it's autogenerated, so we drop it.
103        if self.origin.as_str() == "c" {
104            new_index.name(&self.index_name);
105        }
106        new_index.table(Alias::new(&self.table_name));
107
108        if self.unique {
109            new_index.unique();
110        }
111
112        self.columns.iter().for_each(|column| {
113            new_index.col(Alias::new(column));
114        });
115
116        new_index
117    }
118}
119
120/// Maps the index all columns as a result of using query
121/// `PRAGMA index_list(table_name)`
122#[allow(dead_code)]
123#[derive(Debug, Default, Clone)]
124pub(crate) struct PartialIndexInfo {
125    pub(crate) seq: i64,
126    pub(crate) name: String,
127    pub(crate) unique: bool,
128    pub(crate) origin: String,
129    pub(crate) partial: i64,
130}
131
132#[cfg(feature = "sqlx-sqlite")]
133impl From<SqlxRow> for PartialIndexInfo {
134    fn from(row: SqlxRow) -> Self {
135        use crate::sqlx_types::Row;
136        let row = row.sqlite();
137        let is_unique: i8 = row.get(2);
138        Self {
139            seq: row.get(0),
140            name: row.get(1),
141            unique: is_unique != 0,
142            origin: row.get(3),
143            partial: row.get(4),
144        }
145    }
146}
147
148#[cfg(not(feature = "sqlx-sqlite"))]
149impl From<SqlxRow> for PartialIndexInfo {
150    fn from(_: SqlxRow) -> Self {
151        Self::default()
152    }
153}
154
155/// Maps all the columns in an index as a result of using query
156/// `SELECT * FROM sqlite_master where name = 'index_name'`
157#[allow(dead_code)]
158#[derive(Debug, Default, Clone)]
159pub(crate) struct IndexedColumns {
160    pub(crate) r#type: String,
161    pub(crate) name: String,
162    pub(crate) table: String,
163    pub(crate) root_page: i64,
164    pub(crate) indexed_columns: Vec<String>,
165}
166
167#[cfg(feature = "sqlx-sqlite")]
168impl From<(SqlxRow, Vec<SqlxRow>)> for IndexedColumns {
169    fn from((row, rows): (SqlxRow, Vec<SqlxRow>)) -> Self {
170        use crate::sqlx_types::Row;
171
172        let row = row.sqlite();
173        let columns_to_index = rows
174            .into_iter()
175            .map(|r| r.sqlite().get(2))
176            .collect::<Vec<String>>();
177
178        Self {
179            r#type: row.get(0),
180            name: row.get(1),
181            table: row.get(2),
182            root_page: row.get(3),
183            indexed_columns: columns_to_index,
184        }
185    }
186}
187
188#[cfg(not(feature = "sqlx-sqlite"))]
189impl From<(SqlxRow, Vec<SqlxRow>)> for IndexedColumns {
190    fn from(_: (SqlxRow, Vec<SqlxRow>)) -> Self {
191        Self::default()
192    }
193}
194
195/// Confirms if a table's primary key is set to autoincrement as a result of using query
196/// `SELECT COUNT(*) from sqlite_sequence where name = 'table_name';
197#[allow(dead_code)]
198#[derive(Debug, Default, Clone)]
199pub(crate) struct PrimaryKeyAutoincrement(pub(crate) u8);
200
201#[cfg(feature = "sqlx-sqlite")]
202impl From<SqlxRow> for PrimaryKeyAutoincrement {
203    fn from(row: SqlxRow) -> Self {
204        use crate::sqlx_types::Row;
205        Self(row.sqlite().get(0))
206    }
207}
208
209#[cfg(not(feature = "sqlx-sqlite"))]
210impl From<SqlxRow> for PrimaryKeyAutoincrement {
211    fn from(_: SqlxRow) -> Self {
212        Self::default()
213    }
214}
215
216/// Indexes the foreign keys
217#[derive(Debug, Default, Clone, PartialEq)]
218#[non_exhaustive]
219pub struct ForeignKeysInfo {
220    pub id: i64,
221    pub seq: i64,
222    pub table: String,
223    pub from: Vec<String>,
224    pub to: Vec<String>,
225    pub on_update: ForeignKeyAction,
226    pub on_delete: ForeignKeyAction,
227    pub r#match: MatchAction,
228}
229
230#[cfg(feature = "sqlx-sqlite")]
231impl From<SqlxRow> for ForeignKeysInfo {
232    fn from(row: SqlxRow) -> Self {
233        use crate::sqlx_types::Row;
234        let row = row.sqlite();
235        Self {
236            id: row.get(0),
237            seq: row.get(1),
238            table: row.get(2),
239            from: vec![row.get(3)],
240            to: vec![row.get(4)],
241            on_update: {
242                let op: String = row.get(5);
243                op.as_str().into()
244            },
245            on_delete: {
246                let op: String = row.get(6);
247                op.as_str().into()
248            },
249            r#match: {
250                let op: String = row.get(7);
251                op.as_str().into()
252            },
253        }
254    }
255}
256
257#[cfg(not(feature = "sqlx-sqlite"))]
258impl From<SqlxRow> for ForeignKeysInfo {
259    fn from(_: SqlxRow) -> Self {
260        Self::default()
261    }
262}
263
264/// Indexes the actions performed on the foreign keys of a table
265#[derive(Debug, Default, PartialEq, Eq, Clone)]
266pub enum ForeignKeyAction {
267    #[default]
268    NoAction,
269    Restrict,
270    SetNull,
271    SetDefault,
272    Cascade,
273}
274
275impl From<&str> for ForeignKeyAction {
276    fn from(action: &str) -> Self {
277        match action {
278            "NO ACTION" => Self::NoAction,
279            "RESTRICT" => Self::Restrict,
280            "SET NULL" => Self::SetNull,
281            "SET DEFAULT" => Self::SetDefault,
282            "CASCADE" => Self::Cascade,
283            _ => Self::NoAction,
284        }
285    }
286}
287
288impl ForeignKeyAction {
289    pub(crate) fn to_seaquery_foreign_key_action(&self) -> SeaQueryForeignKeyAction {
290        match self {
291            Self::NoAction => SeaQueryForeignKeyAction::NoAction,
292            Self::Restrict => SeaQueryForeignKeyAction::Restrict,
293            Self::SetNull => SeaQueryForeignKeyAction::SetNull,
294            Self::SetDefault => SeaQueryForeignKeyAction::SetDefault,
295            Self::Cascade => SeaQueryForeignKeyAction::Cascade,
296        }
297    }
298}
299
300/// Maps to the SQLite `MATCH` actions
301#[derive(Debug, Default, PartialEq, Eq, Clone)]
302pub enum MatchAction {
303    Simple,
304    Partial,
305    Full,
306    #[default]
307    None,
308}
309
310impl From<&str> for MatchAction {
311    fn from(action: &str) -> Self {
312        match action {
313            "MATCH SIMPLE" => Self::Simple,
314            "MATCH PARTIAL" => Self::Partial,
315            "MATCH FULL" => Self::Full,
316            "MATCH NONE" => Self::None,
317            _ => Self::None,
318        }
319    }
320}