@@ -24,6 +24,16 @@ pub struct DbAdminOutcome {
2424 pub seeded_demo : bool ,
2525}
2626
27+ #[ derive( Debug , Clone ) ]
28+ pub struct DbMigrationStatus {
29+ pub database_kind : & ' static str ,
30+ pub applied_count : i64 ,
31+ pub expected_count : usize ,
32+ pub latest_applied_version : Option < String > ,
33+ pub latest_applied_at : Option < String > ,
34+ pub expected_latest_version : Option < & ' static str > ,
35+ }
36+
2737#[ derive( Clone , Copy ) ]
2838struct Migration {
2939 version : & ' static str ,
@@ -1287,6 +1297,110 @@ pub async fn run_db_admin_action(
12871297 bail ! ( "Nicht unterstuetztes DATABASE_URL-Schema fuer Rust-DB-Admin" ) ;
12881298}
12891299
1300+ pub async fn migration_status ( database_url : & str ) -> anyhow:: Result < DbMigrationStatus > {
1301+ let normalized_url = normalize_database_url ( database_url) ;
1302+ if normalized_url. starts_with ( "postgres://" ) || normalized_url. starts_with ( "postgresql://" ) {
1303+ let pool = PgPoolOptions :: new ( )
1304+ . max_connections ( 1 )
1305+ . connect ( & normalized_url)
1306+ . await
1307+ . context ( "PostgreSQL-Verbindung fuer Rust-Migrationsstatus fehlgeschlagen" ) ?;
1308+ let table_exists: bool =
1309+ sqlx:: query_scalar ( "SELECT to_regclass('public.iscy_schema_migrations') IS NOT NULL" )
1310+ . fetch_one ( & pool)
1311+ . await
1312+ . context ( "PostgreSQL-Migrationstabelle konnte nicht geprueft werden" ) ?;
1313+ if !table_exists {
1314+ return Ok ( empty_migration_status ( "postgres" ) ) ;
1315+ }
1316+ let applied_count: i64 =
1317+ sqlx:: query_scalar ( "SELECT COUNT(*)::bigint FROM iscy_schema_migrations" )
1318+ . fetch_one ( & pool)
1319+ . await
1320+ . context ( "PostgreSQL-Migrationsanzahl konnte nicht gelesen werden" ) ?;
1321+ let latest = sqlx:: query (
1322+ "SELECT version, applied_at FROM iscy_schema_migrations ORDER BY version DESC LIMIT 1" ,
1323+ )
1324+ . fetch_optional ( & pool)
1325+ . await
1326+ . context ( "PostgreSQL-letzte Migration konnte nicht gelesen werden" ) ?;
1327+ return Ok ( migration_status_from_row (
1328+ "postgres" ,
1329+ applied_count,
1330+ latest. map ( |row| {
1331+ (
1332+ row. try_get :: < String , _ > ( "version" ) ,
1333+ row. try_get :: < String , _ > ( "applied_at" ) ,
1334+ )
1335+ } ) ,
1336+ ) ?) ;
1337+ }
1338+ if normalized_url. starts_with ( "sqlite:" ) {
1339+ let options = SqliteConnectOptions :: from_str ( & normalized_url)
1340+ . context ( "SQLite-DATABASE_URL konnte nicht gelesen werden" ) ?
1341+ . create_if_missing ( false ) ;
1342+ let pool = SqlitePoolOptions :: new ( )
1343+ . max_connections ( 1 )
1344+ . connect_with ( options)
1345+ . await
1346+ . context ( "SQLite-Verbindung fuer Rust-Migrationsstatus fehlgeschlagen" ) ?;
1347+ if !sqlite_table_exists ( & pool, "iscy_schema_migrations" ) . await ? {
1348+ return Ok ( empty_migration_status ( "sqlite" ) ) ;
1349+ }
1350+ let applied_count: i64 = sqlx:: query_scalar ( "SELECT COUNT(*) FROM iscy_schema_migrations" )
1351+ . fetch_one ( & pool)
1352+ . await
1353+ . context ( "SQLite-Migrationsanzahl konnte nicht gelesen werden" ) ?;
1354+ let latest = sqlx:: query (
1355+ "SELECT version, applied_at FROM iscy_schema_migrations ORDER BY version DESC LIMIT 1" ,
1356+ )
1357+ . fetch_optional ( & pool)
1358+ . await
1359+ . context ( "SQLite-letzte Migration konnte nicht gelesen werden" ) ?;
1360+ return Ok ( migration_status_from_row (
1361+ "sqlite" ,
1362+ applied_count,
1363+ latest. map ( |row| {
1364+ (
1365+ row. try_get :: < String , _ > ( "version" ) ,
1366+ row. try_get :: < String , _ > ( "applied_at" ) ,
1367+ )
1368+ } ) ,
1369+ ) ?) ;
1370+ }
1371+ bail ! ( "Nicht unterstuetztes DATABASE_URL-Schema fuer Rust-Migrationsstatus" ) ;
1372+ }
1373+
1374+ fn empty_migration_status ( database_kind : & ' static str ) -> DbMigrationStatus {
1375+ DbMigrationStatus {
1376+ database_kind,
1377+ applied_count : 0 ,
1378+ expected_count : MIGRATIONS . len ( ) ,
1379+ latest_applied_version : None ,
1380+ latest_applied_at : None ,
1381+ expected_latest_version : MIGRATIONS . last ( ) . map ( |migration| migration. version ) ,
1382+ }
1383+ }
1384+
1385+ fn migration_status_from_row (
1386+ database_kind : & ' static str ,
1387+ applied_count : i64 ,
1388+ latest : Option < ( Result < String , sqlx:: Error > , Result < String , sqlx:: Error > ) > ,
1389+ ) -> Result < DbMigrationStatus , sqlx:: Error > {
1390+ let ( latest_applied_version, latest_applied_at) = match latest {
1391+ Some ( ( version, applied_at) ) => ( Some ( version?) , Some ( applied_at?) ) ,
1392+ None => ( None , None ) ,
1393+ } ;
1394+ Ok ( DbMigrationStatus {
1395+ database_kind,
1396+ applied_count,
1397+ expected_count : MIGRATIONS . len ( ) ,
1398+ latest_applied_version,
1399+ latest_applied_at,
1400+ expected_latest_version : MIGRATIONS . last ( ) . map ( |migration| migration. version ) ,
1401+ } )
1402+ }
1403+
12901404pub async fn run_sqlite_migrations ( pool : & SqlitePool ) -> anyhow:: Result < Vec < & ' static str > > {
12911405 sqlx:: query (
12921406 r#"
0 commit comments