in src/Microsoft.SqlTools.ServiceLayer/Admin/Database/DatabasePrototype.cs [1830:2056]
public virtual Database ApplyChanges()
{
Database db = null;
if (this.ChangesExist())
{
bool scripting = (SqlExecutionModes.CaptureSql == this.context.Server.ConnectionContext.SqlExecutionModes);
bool mustRollback = false;
db = this.GetDatabase();
// Other connections will need to be closed if the following is true
// 1) The database already exists, AND
// 2) We are not scripting, AND
// a) read-only state is changing, OR
// b) user-access is changing, OR
// c) date correlation optimization is changing
// There are also additional properties we don't currently expose that also need
// to be changed when no one else is connected:
// d) emergency, OR
// e) offline, (moving to offline - obviously not necessary to check when moving from offline)
// f) read committed snapshot
if (this.Exists && !scripting &&
((this.currentState.isReadOnly != this.originalState.isReadOnly) ||
(this.currentState.filestreamDirectoryName != this.originalState.filestreamDirectoryName) ||
(this.currentState.filestreamNonTransactedAccess != this.originalState.filestreamNonTransactedAccess) ||
(this.currentState.restrictAccess != this.originalState.restrictAccess) ||
(this.currentState.dateCorrelationOptimization != this.originalState.dateCorrelationOptimization) ||
(this.currentState.isReadCommittedSnapshotOn != this.originalState.isReadCommittedSnapshotOn)))
{
// If the user lacks permissions to enumerate other connections (e.g. the user is not SA)
// assume there is a connection to close. This occasionally results in unnecessary
// prompts, but the database alter does succeed this way. If we assume no other connections,
// then we get errors when other connections do exist.
int numberOfOpenConnections = 1;
try
{
numberOfOpenConnections = db.ActiveConnections;
}
catch (Exception)
{
// do nothing - the user doesn't have permission to check whether there are active connections
//STrace.LogExCatch(ex);
}
if (0 < numberOfOpenConnections)
{
// DialogResult result = (DialogResult)marshallingControl.Invoke(new SimplePrompt(this.PromptToCloseConnections));
// if (result == DialogResult.No)
// {
// throw new OperationCanceledException();
// }
mustRollback = true;
throw new OperationCanceledException();
}
}
// create/alter filegroups
foreach (FilegroupPrototype filegroup in Filegroups)
{
filegroup.ApplyChanges(db);
}
// create/alter files
foreach (DatabaseFilePrototype file in Files)
{
file.ApplyChanges(db);
}
// set the database properties
this.SaveProperties(db);
// alter the database to match the properties
if (!this.Exists)
{
// this is to prevent silent creation of db behind users back
// eg. the alter statements to set properties fail when filestream directory name is invalid bug #635273
// but create database statement already succeeded
// if filestream directory name has been set by user validate it
if (!string.IsNullOrEmpty(this.FilestreamDirectoryName))
{
// check is filestream directory name is valid
if (!FileNameHelper.IsValidFilename(this.FilestreamDirectoryName))
{
string message = String.Format(System.Globalization.CultureInfo.InvariantCulture,
SR.Error_InvalidDirectoryName,
this.FilestreamDirectoryName);
throw new ArgumentException(message);
}
int rowCount = 0;
try
{
//if filestream directory name already exists in this instance
string sqlFilestreamQuery = string.Format(System.Globalization.CultureInfo.InvariantCulture,
"SELECT * from sys.database_filestream_options WHERE directory_name = {0}",
SqlSmoObject.MakeSqlString(this.FilestreamDirectoryName));
DataSet filestreamResults = this.context.ServerConnection.ExecuteWithResults(sqlFilestreamQuery);
rowCount = filestreamResults.Tables[0].Rows.Count;
}
catch
{
// lets not do anything if there is an exception while validating
// this is will prevent bugs in validation logic from preventing creation of valid databases
// if database settings are invalid create database tsql statement will fail anyways
}
if (rowCount != 0)
{
string message = String.Format(System.Globalization.CultureInfo.InvariantCulture,
SR.Error_ExistingDirectoryName,
this.FilestreamDirectoryName, this.Name);
throw new ArgumentException(message);
}
}
db.Create();
}
else
{
TerminationClause termination =
mustRollback ?
TerminationClause.RollbackTransactionsImmediately :
TerminationClause.FailOnOpenTransactions;
db.Alter(termination);
}
// have to explicitly set the default filegroup after the database has been created
foreach (FilegroupPrototype filegroup in Filegroups)
{
if (filegroup.IsDefault && !(filegroup.Exists && db.FileGroups[filegroup.Name].IsDefault))
{
if ((filegroup.IsFileStream || filegroup.IsMemoryOptimized))
{
db.SetDefaultFileStreamFileGroup(filegroup.Name);
}
else
{
db.SetDefaultFileGroup(filegroup.Name);
}
}
}
FilegroupPrototype fg = null;
// drop should happen after alter so that if we delete default filegroup it makes another default before deleting.
// drop removed files and filegroups for existing databases
if (this.Exists)
{
foreach (FilegroupPrototype filegroup in this.removedFilegroups)
{
// In case all filegroups are removed from filestream . memory optimized one default will remain and that has to be the last.
if ((filegroup.IsFileStream || filegroup.IsMemoryOptimized) &&
db.FileGroups[filegroup.Name].IsDefault)
{
fg = filegroup;
}
else
{
filegroup.ApplyChanges(db);
}
}
if (fg != null)
{
fg.ApplyChanges(db);
}
foreach (DatabaseFilePrototype file in this.removedFiles)
{
file.ApplyChanges(db);
}
}
// SnapshotIsolation and Owner cannot be set during scripting time for a newly creating database
// and even in capture mode. Hence this check has been made
if (db.State == SqlSmoState.Existing)
{
if (this.originalState.allowSnapshotIsolation != this.currentState.allowSnapshotIsolation)
{
db.SetSnapshotIsolation(this.currentState.allowSnapshotIsolation);
}
// Set the database owner. Note that setting owner is an "immediate" operation that
// has to happen after the database is created. There is a SMO limitation where SMO
// throws an exception if immediate operations such as SetOwner() are attempted on
// an object that doesn't exist on the server.
if ((this.Owner.Length != 0) &&
(this.currentState.owner != this.originalState.owner))
{
//
// bug 20000092 says the error message is confusing if this fails, so
// wrap this and throw a nicer error on failure.
//
try
{
db.SetOwner(this.Owner, false);
}
catch (Exception ex)
{
SqlException sqlException = CUtils.GetSqlException(ex);
if ((null != sqlException) && CUtils.IsPermissionDeniedException(sqlException))
{
throw new Exception(SR.SetOwnerFailed(this.Owner) + ex.ToString());
}
else
{
throw;
}
}
}
}
}
return db;
}