diff --git a/crates/harness-store/src/lib.rs b/crates/harness-store/src/lib.rs index d418dfa..1d78956 100644 --- a/crates/harness-store/src/lib.rs +++ b/crates/harness-store/src/lib.rs @@ -140,6 +140,21 @@ pub struct StoreBundle { pub channel_instances: Arc, } +/// In-memory `TenantStore` fallback for the SQL backends. +/// +/// There's no SQL `TenantStore` impl yet (only JSON-file and SQLite), +/// so the Postgres / MySQL arms fall back to in-memory — tenant rows +/// won't survive restarts under those deployments until a SQL impl +/// lands. Mirrors the channel-store fallback used in the same arms. +#[cfg(any(feature = "postgres", feature = "mysql"))] +fn ephemeral_tenants(backend: &str) -> Arc { + tracing::warn!( + backend, + "no SQL TenantStore impl yet; tenants fall back to in-memory and won't survive restarts" + ); + Arc::new(MemoryTenantStore::new()) as Arc +} + /// Open both stores for a given database URL. The scheme selects the /// backend (see [module docs](crate)). /// diff --git a/crates/harness-store/src/mysql.rs b/crates/harness-store/src/mysql.rs index 3962759..25dbf2d 100644 --- a/crates/harness-store/src/mysql.rs +++ b/crates/harness-store/src/mysql.rs @@ -118,15 +118,22 @@ async fn migrate(pool: &MySqlPool) -> Result<(), StoreError> { .await? > 0; if !has_workspaces { - sqlx::query("ALTER TABLE projects ADD COLUMN workspaces TEXT NOT NULL") + // Add the column nullable first: under strict SQL mode (MySQL 8 + // default STRICT_TRANS_TABLES) an `ADD COLUMN ... TEXT NOT NULL` + // with no default aborts on a populated table because TEXT has no + // implicit default. So add it nullable, backfill, then tighten to + // NOT NULL — this works across MySQL 5.7/8.x regardless of sql_mode. + sqlx::query("ALTER TABLE projects ADD COLUMN workspaces TEXT") .execute(pool) .await?; - // Backfill so the NOT NULL constraint is satisfied for legacy rows. sqlx::query( "UPDATE projects SET workspaces = '[]' WHERE workspaces IS NULL OR workspaces = ''", ) .execute(pool) .await?; + sqlx::query("ALTER TABLE projects MODIFY COLUMN workspaces TEXT NOT NULL") + .execute(pool) + .await?; } let has_columns: bool = sqlx::query_scalar::<_, i64>( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS