Skip to main content

ddnet_account_sql/
version.rs

1use crate::any::{AnyConnection, AnyPool};
2
3#[cfg(feature = "mysql")]
4mod mysql {
5    use sqlx::Executor;
6    use sqlx::Row;
7    use sqlx::SqlSafeStr;
8    use sqlx::Statement;
9
10    pub(super) async fn try_setup(con: &mut sqlx::mysql::MySqlConnection) -> anyhow::Result<()> {
11        // first create all statements (syntax check)
12        let version = con
13            .prepare(include_str!("version/mysql/version.sql").into_sql_str())
14            .await?;
15
16        // afterwards actually create tables
17        version.query().execute(&mut *con).await?;
18
19        Ok(())
20    }
21
22    pub(super) async fn get_or_set_version(
23        con: &mut sqlx::mysql::MySqlConnection,
24        name: &str,
25    ) -> anyhow::Result<i64> {
26        // first create all statements (syntax check)
27        let get_version = con
28            .prepare(include_str!("version/mysql/get_version.sql").into_sql_str())
29            .await?;
30        let set_version = con
31            .prepare(include_str!("version/mysql/set_version.sql").into_sql_str())
32            .await?;
33
34        let name = name.to_string();
35
36        // afterwards actually create tables
37        if let Some(row) = get_version
38            .query()
39            .bind(&name)
40            .fetch_optional(&mut *con)
41            .await?
42        {
43            anyhow::Ok(row.try_get("version")?)
44        } else {
45            // insert new entry
46            set_version
47                .query()
48                .bind(&name)
49                .bind(0)
50                .bind(0)
51                .execute(&mut *con)
52                .await?;
53            anyhow::Ok(0)
54        }
55    }
56
57    pub(super) async fn set_version(
58        con: &mut sqlx::mysql::MySqlConnection,
59        name: &str,
60        version: i64,
61    ) -> anyhow::Result<()> {
62        // first create all statements (syntax check)
63        let set_version = con
64            .prepare(include_str!("version/mysql/set_version.sql").into_sql_str())
65            .await?;
66
67        Ok(set_version
68            .query()
69            .bind(name)
70            .bind(version)
71            .bind(version)
72            .execute(&mut *con)
73            .await
74            .map(|_| ())?)
75    }
76
77    pub(super) async fn delete(con: &mut sqlx::mysql::MySqlConnection) -> anyhow::Result<()> {
78        // first create all statements (syntax check)
79        // delete in reverse order to creating
80        let version = con
81            .prepare(include_str!("version/mysql/delete/version.sql").into_sql_str())
82            .await?;
83
84        // afterwards actually drop tables
85        let version = version.query().execute(&mut *con).await;
86
87        // handle errors at once
88        version?;
89
90        Ok(())
91    }
92}
93
94#[cfg(feature = "sqlite")]
95mod sqlite {
96    use sqlx::Executor;
97    use sqlx::Row;
98    use sqlx::SqlSafeStr;
99    use sqlx::Statement;
100
101    pub(super) async fn try_setup(con: &mut sqlx::sqlite::SqliteConnection) -> anyhow::Result<()> {
102        // first create all statements (syntax check)
103        let version = con
104            .prepare(include_str!("version/sqlite/version.sql").into_sql_str())
105            .await?;
106
107        // afterwards actually create tables
108        version.query().execute(&mut *con).await?;
109
110        Ok(())
111    }
112
113    pub(super) async fn get_or_set_version(
114        con: &mut sqlx::sqlite::SqliteConnection,
115        name: &str,
116    ) -> anyhow::Result<i64> {
117        // first create all statements (syntax check)
118        let get_version = con
119            .prepare(include_str!("version/sqlite/get_version.sql").into_sql_str())
120            .await?;
121        let set_version = con
122            .prepare(include_str!("version/sqlite/set_version.sql").into_sql_str())
123            .await?;
124
125        let name = name.to_string();
126
127        // afterwards actually create tables
128        if let Some(row) = get_version
129            .query()
130            .bind(&name)
131            .fetch_optional(&mut *con)
132            .await?
133        {
134            anyhow::Ok(row.try_get("version")?)
135        } else {
136            // insert new entry
137            set_version
138                .query()
139                .bind(&name)
140                .bind(0)
141                .bind(0)
142                .execute(&mut *con)
143                .await?;
144            anyhow::Ok(0)
145        }
146    }
147
148    pub(super) async fn set_version(
149        con: &mut sqlx::sqlite::SqliteConnection,
150        name: &str,
151        version: i64,
152    ) -> anyhow::Result<()> {
153        // first create all statements (syntax check)
154        let set_version = con
155            .prepare(include_str!("version/sqlite/set_version.sql").into_sql_str())
156            .await?;
157
158        Ok(set_version
159            .query()
160            .bind(name)
161            .bind(version)
162            .bind(version)
163            .execute(&mut *con)
164            .await
165            .map(|_| ())?)
166    }
167
168    pub(super) async fn delete(con: &mut sqlx::sqlite::SqliteConnection) -> anyhow::Result<()> {
169        // first create all statements (syntax check)
170        // delete in reverse order to creating
171        let version = con
172            .prepare(include_str!("version/sqlite/delete/version.sql").into_sql_str())
173            .await?;
174
175        // afterwards actually drop tables
176        let version = version.query().execute(&mut *con).await;
177
178        // handle errors at once
179        version?;
180
181        Ok(())
182    }
183}
184
185/// Use this function to obtain the current version number.
186///
187/// If the version table does not exist, sets up the version table.
188/// The version table can be used to easily upgrade existing tables to a new
189/// version, without manually doing it by hand.
190pub async fn get_version(con: &mut AnyConnection<'_>, name: &str) -> anyhow::Result<i64> {
191    match con {
192        #[cfg(feature = "mysql")]
193        AnyConnection::MySql(con) => {
194            // try setup
195            mysql::try_setup(con).await?;
196            mysql::get_or_set_version(con, name).await
197        }
198        #[cfg(feature = "sqlite")]
199        AnyConnection::Sqlite(con) => {
200            // try setup
201            sqlite::try_setup(con).await?;
202            sqlite::get_or_set_version(con, name).await
203        }
204    }
205}
206
207/// After your setup is done, set the version to your current setup script.
208/// This can (and should) be called inside a transaction
209pub async fn set_version(
210    con: &mut AnyConnection<'_>,
211    name: &str,
212    version: i64,
213) -> anyhow::Result<()> {
214    match con {
215        #[cfg(feature = "mysql")]
216        AnyConnection::MySql(con) => mysql::set_version(con, name, version).await,
217        #[cfg(feature = "sqlite")]
218        AnyConnection::Sqlite(con) => sqlite::set_version(con, name, version).await,
219    }
220}
221
222/// Drop the version table...
223/// Warning: This is usually not recommended.
224pub async fn delete(pool: &AnyPool) -> anyhow::Result<()> {
225    let mut con = pool.acquire().await?;
226    let con = con.acquire().await?;
227    match con {
228        #[cfg(feature = "mysql")]
229        AnyConnection::MySql(con) => mysql::delete(con).await,
230        #[cfg(feature = "sqlite")]
231        AnyConnection::Sqlite(con) => sqlite::delete(con).await,
232    }
233}