@@ -830,6 +830,7 @@ public static string GenerateSummaryReport(
830830 public static async Task < string ? > GetInstalledVersionAsync (
831831 string connectionString ,
832832 IProgress < InstallationProgress > ? progress = null ,
833+ bool throwOnError = false ,
833834 CancellationToken cancellationToken = default )
834835 {
835836 LogDebug ( progress , "GetInstalledVersionAsync: checking for existing installation" ) ;
@@ -890,11 +891,17 @@ rather than treating this as a fresh install (which would drop the database).
890891 catch ( SqlException ex )
891892 {
892893 LogDebug ( progress , $ "GetInstalledVersionAsync: SqlException — { ex . Number } : { ex . Message } ") ;
894+ /* The installer passes throwOnError=true so a transient/permission error can't be
895+ mistaken for "database absent" and silently trigger a fresh install over an
896+ existing database (no upgrades, then logged as SUCCESS — the #538 hazard). Soft
897+ callers (Dashboard version column, adversarial tests) keep the null fallback. */
898+ if ( throwOnError ) throw ;
893899 return null ;
894900 }
895901 catch ( Exception ex )
896902 {
897903 LogDebug ( progress , $ "GetInstalledVersionAsync: { ex . GetType ( ) . Name } — { ex . Message } ") ;
904+ if ( throwOnError ) throw ;
898905 return null ;
899906 }
900907 }
@@ -1047,6 +1054,14 @@ rather than treating this as a fresh install (which would drop the database).
10471054
10481055 totalSuccessCount += success ;
10491056 totalFailureCount += failure ;
1057+
1058+ /* Stop at the first failed hop. Later hops assume this one's schema changes applied;
1059+ running them against a partially-upgraded database compounds the damage. The caller
1060+ aborts the whole install when totalFailureCount > 0. */
1061+ if ( failure > 0 )
1062+ {
1063+ break ;
1064+ }
10501065 }
10511066
10521067 return ( totalSuccessCount , totalFailureCount , upgrades . Count ) ;
0 commit comments