Skip to main content

easy_sql/database_structs/
easy_sql_tables.rs

1use std::fmt::Debug;
2
3use crate::{
4    Driver, Output,
5    markers::{HasTable, NotJoinedTable},
6    traits::{DriverConnection, InternalDriver},
7};
8use easy_macros::always_context;
9use easy_sql_macros::{Insert, Update};
10use sqlx::TypeInfo;
11
12use crate::{DatabaseSetup, EasyExecutor, Table, driver::TableField};
13
14/// Internal metadata table used by Easy SQL to track per-table schema versions.
15///
16/// This table is created automatically via [`DatabaseSetup`](macro@crate::DatabaseSetup) and is used by the
17/// [`Table`](macro@crate::Table) derive macro to store the last known version of each table.
18/// It lives in the database as `easy_sql_tables`, keyed by `table_id`.
19#[derive(Insert, Debug)]
20#[sql(table = EasySqlTables)]
21pub struct EasySqlTables {
22    /// The logical identifier of the table being tracked.
23    pub table_id: String,
24    /// The stored schema version for the table.
25    pub version: i64,
26}
27
28impl HasTable<EasySqlTables> for EasySqlTables {}
29
30impl NotJoinedTable for EasySqlTables {}
31
32#[macro_export]
33#[doc(hidden)]
34/// Used by Table derive macro
35macro_rules! EasySqlTables_create {
36    ($driver:path, $conn:expr, $table_id:expr, $version:expr) => {
37        use $crate::macro_support::Context;
38        let inserted = $crate::EasySqlTables {
39            table_id: $table_id,
40            version: $version,
41        };
42        $crate::query!($conn, INSERT INTO $crate::EasySqlTables VALUES { &inserted })
43        .await
44        .with_context($crate::macro_support::context!(
45            "Failed to create EasySqlTables | inserted: {:?}",
46            inserted
47        ))?;
48    };
49}
50
51#[macro_export]
52#[doc(hidden)]
53/// Used by Table derive macro
54macro_rules! EasySqlTables_update_version {
55    ($driver:path, $conn:expr, $table_id:expr, $new_version:expr) => {{
56        use $crate::macro_support::Context;
57
58        $crate::query!($conn, UPDATE $crate::EasySqlTables SET version = { $new_version } WHERE table_id = { $table_id })
59            .await
60            .with_context($crate::macro_support::context!(
61                "Failed to update EasySqlTables version | table_id: {:?} | new_version: {:?}",
62                $table_id,
63                $new_version
64            ))?;
65    }};
66}
67
68#[macro_export]
69#[doc(hidden)]
70/// Used by Table derive macro
71macro_rules! EasySqlTables_get_version {
72    ($driver:path, $conn:expr, $table_id:expr) => {{
73        #[derive($crate::Update, $crate::Output, Debug)]
74        #[sql(table = $crate::EasySqlTables)]
75        struct EasySqlTableVersion {
76            pub version: i64,
77        }
78
79        let version: Option<EasySqlTableVersion> = $crate::query!($conn, SELECT Option<EasySqlTableVersion> FROM $crate::EasySqlTables WHERE $crate::EasySqlTables.table_id = { $table_id })
80            .await?;
81        version.map(|v| v.version)
82    }};
83}
84
85#[derive(Update, Output, Debug)]
86#[sql(table = EasySqlTables)]
87struct EasySqlTableVersion {
88    pub version: i64,
89}
90
91#[always_context]
92impl<D: Driver + 'static> Table<D> for EasySqlTables
93where
94    DriverConnection<D>: Send + Sync,
95{
96    fn table_name() -> &'static str {
97        "easy_sql_tables"
98    }
99
100    fn primary_keys() -> std::vec::Vec<&'static str> {
101        vec!["table_id"]
102    }
103
104    fn table_joins(_current_query: &mut String) {}
105}
106
107#[always_context]
108impl<D: Driver + 'static> DatabaseSetup<D> for EasySqlTables
109where
110    DriverConnection<D>: Send + Sync,
111    String: sqlx::Type<InternalDriver<D>>,
112    i64: sqlx::Type<InternalDriver<D>>,
113{
114    async fn setup(conn: &mut (impl EasyExecutor<D> + Send + Sync)) -> anyhow::Result<()> {
115        use anyhow::Context;
116
117        let table_name = <EasySqlTables as Table<D>>::table_name();
118
119        let table_exists = D::table_exists(
120            #[context(no)]
121            conn,
122            table_name,
123        )
124        .await?;
125
126        if !table_exists {
127            D::create_table(
128                #[context(no)]
129                conn,
130                table_name,
131                vec![
132                    TableField {
133                        name: "table_id",
134                        data_type: <String as sqlx::Type<InternalDriver<D>>>::type_info()
135                            .name()
136                            .to_owned(),
137                        is_unique: false,
138                        is_not_null: true,
139                        default: None,
140                        is_auto_increment: false,
141                    },
142                    TableField {
143                        name: "version",
144                        data_type: <i64 as sqlx::Type<InternalDriver<D>>>::type_info()
145                            .name()
146                            .to_owned(),
147                        is_unique: false,
148                        is_not_null: true,
149                        default: None,
150                        is_auto_increment: false,
151                    },
152                ],
153                vec!["table_id"],
154                #[context(no)]
155                Default::default(),
156            )
157            .await?;
158        }
159
160        Ok(())
161    }
162}