1use super::{IntoSchemaManagerConnection, SchemaManagerConnection};
2use sea_orm::sea_query::{
3 extension::postgres::{TypeAlterStatement, TypeCreateStatement, TypeDropStatement},
4 ForeignKeyCreateStatement, ForeignKeyDropStatement, IndexCreateStatement, IndexDropStatement,
5 SelectStatement, TableAlterStatement, TableCreateStatement, TableDropStatement,
6 TableRenameStatement, TableTruncateStatement,
7};
8use sea_orm::{ConnectionTrait, DbBackend, DbErr, StatementBuilder};
9#[allow(unused_imports)]
10use sea_schema::probe::SchemaProbe;
11
12pub struct SchemaManager<'c> {
14 conn: SchemaManagerConnection<'c>,
15}
16
17impl<'c> SchemaManager<'c> {
18 pub fn new<T>(conn: T) -> Self
19 where
20 T: IntoSchemaManagerConnection<'c>,
21 {
22 Self {
23 conn: conn.into_schema_manager_connection(),
24 }
25 }
26
27 pub async fn exec_stmt<S>(&self, stmt: S) -> Result<(), DbErr>
28 where
29 S: StatementBuilder,
30 {
31 let builder = self.conn.get_database_backend();
32 self.conn.execute(builder.build(&stmt)).await.map(|_| ())
33 }
34
35 pub fn get_database_backend(&self) -> DbBackend {
36 self.conn.get_database_backend()
37 }
38
39 pub fn get_connection(&self) -> &SchemaManagerConnection<'c> {
40 &self.conn
41 }
42}
43
44impl SchemaManager<'_> {
46 pub async fn create_table(&self, stmt: TableCreateStatement) -> Result<(), DbErr> {
47 self.exec_stmt(stmt).await
48 }
49
50 pub async fn create_index(&self, stmt: IndexCreateStatement) -> Result<(), DbErr> {
51 self.exec_stmt(stmt).await
52 }
53
54 pub async fn create_foreign_key(&self, stmt: ForeignKeyCreateStatement) -> Result<(), DbErr> {
55 self.exec_stmt(stmt).await
56 }
57
58 pub async fn create_type(&self, stmt: TypeCreateStatement) -> Result<(), DbErr> {
59 self.exec_stmt(stmt).await
60 }
61}
62
63impl SchemaManager<'_> {
65 pub async fn alter_table(&self, stmt: TableAlterStatement) -> Result<(), DbErr> {
66 self.exec_stmt(stmt).await
67 }
68
69 pub async fn drop_table(&self, stmt: TableDropStatement) -> Result<(), DbErr> {
70 self.exec_stmt(stmt).await
71 }
72
73 pub async fn rename_table(&self, stmt: TableRenameStatement) -> Result<(), DbErr> {
74 self.exec_stmt(stmt).await
75 }
76
77 pub async fn truncate_table(&self, stmt: TableTruncateStatement) -> Result<(), DbErr> {
78 self.exec_stmt(stmt).await
79 }
80
81 pub async fn drop_index(&self, stmt: IndexDropStatement) -> Result<(), DbErr> {
82 self.exec_stmt(stmt).await
83 }
84
85 pub async fn drop_foreign_key(&self, stmt: ForeignKeyDropStatement) -> Result<(), DbErr> {
86 self.exec_stmt(stmt).await
87 }
88
89 pub async fn alter_type(&self, stmt: TypeAlterStatement) -> Result<(), DbErr> {
90 self.exec_stmt(stmt).await
91 }
92
93 pub async fn drop_type(&self, stmt: TypeDropStatement) -> Result<(), DbErr> {
94 self.exec_stmt(stmt).await
95 }
96}
97
98impl SchemaManager<'_> {
100 pub async fn has_table<T>(&self, table: T) -> Result<bool, DbErr>
101 where
102 T: AsRef<str>,
103 {
104 has_table(&self.conn, table).await
105 }
106
107 pub async fn has_column<T, C>(&self, _table: T, _column: C) -> Result<bool, DbErr>
108 where
109 T: AsRef<str>,
110 C: AsRef<str>,
111 {
112 let _stmt: SelectStatement = match self.conn.get_database_backend() {
113 #[cfg(feature = "sqlx-mysql")]
114 DbBackend::MySql => sea_schema::mysql::MySql.has_column(_table, _column),
115 #[cfg(feature = "sqlx-postgres")]
116 DbBackend::Postgres => sea_schema::postgres::Postgres.has_column(_table, _column),
117 #[cfg(feature = "sqlx-sqlite")]
118 DbBackend::Sqlite => sea_schema::sqlite::Sqlite.has_column(_table, _column),
119 #[allow(unreachable_patterns)]
120 other => panic!("{other:?} feature is off"),
121 };
122
123 #[allow(unreachable_code)]
124 let builder = self.conn.get_database_backend();
125 let res = self
126 .conn
127 .query_one(builder.build(&_stmt))
128 .await?
129 .ok_or_else(|| DbErr::Custom("Failed to check column exists".to_owned()))?;
130
131 res.try_get("", "has_column")
132 }
133
134 pub async fn has_index<T, I>(&self, _table: T, _index: I) -> Result<bool, DbErr>
135 where
136 T: AsRef<str>,
137 I: AsRef<str>,
138 {
139 let _stmt: SelectStatement = match self.conn.get_database_backend() {
140 #[cfg(feature = "sqlx-mysql")]
141 DbBackend::MySql => sea_schema::mysql::MySql.has_index(_table, _index),
142 #[cfg(feature = "sqlx-postgres")]
143 DbBackend::Postgres => sea_schema::postgres::Postgres.has_index(_table, _index),
144 #[cfg(feature = "sqlx-sqlite")]
145 DbBackend::Sqlite => sea_schema::sqlite::Sqlite.has_index(_table, _index),
146 #[allow(unreachable_patterns)]
147 other => panic!("{other:?} feature is off"),
148 };
149
150 #[allow(unreachable_code)]
151 let builder = self.conn.get_database_backend();
152 let res = self
153 .conn
154 .query_one(builder.build(&_stmt))
155 .await?
156 .ok_or_else(|| DbErr::Custom("Failed to check index exists".to_owned()))?;
157
158 res.try_get("", "has_index")
159 }
160}
161
162pub(crate) async fn has_table<C, T>(conn: &C, _table: T) -> Result<bool, DbErr>
163where
164 C: ConnectionTrait,
165 T: AsRef<str>,
166{
167 let _stmt: SelectStatement = match conn.get_database_backend() {
168 #[cfg(feature = "sqlx-mysql")]
169 DbBackend::MySql => sea_schema::mysql::MySql.has_table(_table),
170 #[cfg(feature = "sqlx-postgres")]
171 DbBackend::Postgres => sea_schema::postgres::Postgres.has_table(_table),
172 #[cfg(feature = "sqlx-sqlite")]
173 DbBackend::Sqlite => sea_schema::sqlite::Sqlite.has_table(_table),
174 #[allow(unreachable_patterns)]
175 other => panic!("{other:?} feature is off"),
176 };
177
178 #[allow(unreachable_code)]
179 let builder = conn.get_database_backend();
180 let res = conn
181 .query_one(builder.build(&_stmt))
182 .await?
183 .ok_or_else(|| DbErr::Custom("Failed to check table exists".to_owned()))?;
184
185 res.try_get("", "has_table")
186}