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