diff --git a/Microsoft.Research/Clousot.Cache/Clousot.Cache.csproj b/Microsoft.Research/Clousot.Cache/Clousot.Cache.csproj index 06ff85a4..0f24a8bb 100644 --- a/Microsoft.Research/Clousot.Cache/Clousot.Cache.csproj +++ b/Microsoft.Research/Clousot.Cache/Clousot.Cache.csproj @@ -116,6 +116,7 @@ + diff --git a/Microsoft.Research/Clousot.Cache/SQLCacheModel.cs b/Microsoft.Research/Clousot.Cache/SQLCacheModel.cs index 0e37ac77..af5ffe9e 100644 --- a/Microsoft.Research/Clousot.Cache/SQLCacheModel.cs +++ b/Microsoft.Research/Clousot.Cache/SQLCacheModel.cs @@ -16,6 +16,9 @@ using System; using System.Collections.Generic; +using System.Data.Common; +using System.Data.Linq; +using System.Globalization; using System.Linq; using System.Text; using Microsoft.Research.CodeAnalysis.Caching.Models; @@ -558,6 +561,45 @@ public string CacheName { get { return this.cachename; } } + + protected void DetachDeletedDb(string connection) + { + // If anyone has manually deleted the cache file, it might be still registered with LocalDB. + // If this is the case, we can't create a new database and the user is stuck unless he knows the tricky internals of LocalDB and how to get rid of the registration. + // => If we use LocalDB and the file does not exist, we try to detach it. + try + { + var connectionString = new DbConnectionStringBuilder { ConnectionString = connection }; + var fileName = (string)connectionString["AttachDbFileName"]; + if (File.Exists(fileName)) + return; + + var catalog = (string)connectionString["Initial Catalog"]; + var dataSource = (string)connectionString["Data Source"]; + + using (var master = new DataContext(string.Format(CultureInfo.InvariantCulture, @"Data Source={0};Initial Catalog=master;Integrated Security=True", dataSource))) + { + master.ExecuteCommand(@"exec sp_detach_db {0}", catalog); + } + } + catch (ArgumentException) + { + // Not using LocalDB at all (any of the connection string parameters did not exist). + } + catch (SqlException) + { + // The file was never registered (first time used) or is already detached. + } + catch (Exception ex) + { + // Ignore other errors, could be any DbProvider specific exception. + // Usually we won't get here, but the active provider might not be LocalDB, in this case we know nothing about the provider and the exceptions it might raise. + if (this.trace) + { + Console.WriteLine("[cache] DetachDeletedDb failed: {0}", ex.Message); + } + } + } } public class SqlCacheModelNoCreate : SQLCacheModel @@ -614,6 +656,8 @@ public SqlCacheModelClearExisting(string connection, string cachename, bool trac : base(connection, cachename, trace) { Contract.Requires(cachename != null); + + DetachDeletedDb(connection); } } @@ -637,6 +681,8 @@ public SqlCacheModelDropOnModelChange(string connection, string cachename, bool : base(connection, cachename, trace) { Contract.Requires(cachename != null); + + DetachDeletedDb(connection); } }