mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 10:58:30 -05:00
Schema compare include/exclude changes (#881)
* send back success of include/exclude request * update dacfx nuget package and use new api to get affected dependencies of include/exclude request * addressing comments * rename test * Addressing comments
This commit is contained in:
@@ -20,7 +20,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.SqlServer.DacFx" Version="150.4534.2-preview" />
|
<PackageReference Include="Microsoft.SqlServer.DacFx" Version="150.4576.1-preview" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.6.0-preview3-26501-04" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.6.0-preview3-26501-04" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts
|
namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts
|
||||||
{
|
{
|
||||||
@@ -40,4 +41,12 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts
|
|||||||
public static readonly RequestType<SchemaCompareNodeParams, ResultStatus> Type =
|
public static readonly RequestType<SchemaCompareNodeParams, ResultStatus> Type =
|
||||||
RequestType<SchemaCompareNodeParams, ResultStatus>.Create("schemaCompare/includeExcludeNode");
|
RequestType<SchemaCompareNodeParams, ResultStatus>.Create("schemaCompare/includeExcludeNode");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parameters returned from a schema compare include/exclude request.
|
||||||
|
/// </summary>
|
||||||
|
public class SchemaCompareIncludeExcludeResult : ResultStatus
|
||||||
|
{
|
||||||
|
public List<DiffEntry> ChangedDifferences { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts
|
|||||||
public string TargetScript { get; set; }
|
public string TargetScript { get; set; }
|
||||||
public string SourceObjectType { get; set; }
|
public string SourceObjectType { get; set; }
|
||||||
public string TargetObjectType { get; set; }
|
public string TargetObjectType { get; set; }
|
||||||
|
public bool Included { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
|
namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
|
||||||
{
|
{
|
||||||
@@ -38,6 +39,8 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
|
|||||||
|
|
||||||
public bool Success { get; set; }
|
public bool Success { get; set; }
|
||||||
|
|
||||||
|
public List<DiffEntry> ChangedDifferences;
|
||||||
|
|
||||||
public SchemaCompareIncludeExcludeNodeOperation(SchemaCompareNodeParams parameters, SchemaComparisonResult comparisonResult)
|
public SchemaCompareIncludeExcludeNodeOperation(SchemaCompareNodeParams parameters, SchemaComparisonResult comparisonResult)
|
||||||
{
|
{
|
||||||
Validate.IsNotNull("parameters", parameters);
|
Validate.IsNotNull("parameters", parameters);
|
||||||
@@ -46,6 +49,11 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
|
|||||||
this.ComparisonResult = comparisonResult;
|
this.ComparisonResult = comparisonResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exclude will return false if included dependencies are found. Include will also include dependencies that need to be included.
|
||||||
|
/// This is the same behavior as SSDT
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mode"></param>
|
||||||
public void Execute(TaskExecutionMode mode)
|
public void Execute(TaskExecutionMode mode)
|
||||||
{
|
{
|
||||||
this.CancellationToken.ThrowIfCancellationRequested();
|
this.CancellationToken.ThrowIfCancellationRequested();
|
||||||
@@ -58,7 +66,27 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
|
|||||||
throw new InvalidOperationException(SR.SchemaCompareExcludeIncludeNodeNotFound);
|
throw new InvalidOperationException(SR.SchemaCompareExcludeIncludeNodeNotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check first if the dependencies will allow this if it's an exclude request
|
||||||
|
if (!this.Parameters.IncludeRequest)
|
||||||
|
{
|
||||||
|
IEnumerable<SchemaDifference> dependencies = this.ComparisonResult.GetExcludeDependencies(node);
|
||||||
|
|
||||||
|
bool block = dependencies.Any(d => d.Included);
|
||||||
|
if (block)
|
||||||
|
{
|
||||||
|
this.Success = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.Success = this.Parameters.IncludeRequest ? this.ComparisonResult.Include(node) : this.ComparisonResult.Exclude(node);
|
this.Success = this.Parameters.IncludeRequest ? this.ComparisonResult.Include(node) : this.ComparisonResult.Exclude(node);
|
||||||
|
|
||||||
|
// create list of affected dependencies of this request
|
||||||
|
if (this.Success)
|
||||||
|
{
|
||||||
|
IEnumerable<SchemaDifference> dependencies = this.ComparisonResult.GetIncludeDependencies(node);
|
||||||
|
this.ChangedDifferences = dependencies.Select(difference => SchemaCompareUtils.CreateDiffEntry(difference, null)).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -97,10 +125,16 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
|
|||||||
System.Reflection.PropertyInfo[] properties = diffEntry.GetType().GetProperties();
|
System.Reflection.PropertyInfo[] properties = diffEntry.GetType().GetProperties();
|
||||||
foreach (var prop in properties)
|
foreach (var prop in properties)
|
||||||
{
|
{
|
||||||
result = result &&
|
// Don't need to check if included is the same when verifying if the difference is equal
|
||||||
((prop.GetValue(diffEntry) == null &&
|
if (prop.Name != "Included")
|
||||||
prop.GetValue(entryFromDifference) == null) ||
|
{
|
||||||
prop.GetValue(diffEntry).SafeToString().Equals(prop.GetValue(entryFromDifference).SafeToString()));
|
if(!((prop.GetValue(diffEntry) == null &&
|
||||||
|
prop.GetValue(entryFromDifference) == null) ||
|
||||||
|
prop.GetValue(diffEntry).SafeToString().Equals(prop.GetValue(entryFromDifference).SafeToString())))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -245,10 +245,17 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
|
|||||||
|
|
||||||
operation.Execute(parameters.TaskExecutionMode);
|
operation.Execute(parameters.TaskExecutionMode);
|
||||||
|
|
||||||
await requestContext.SendResult(new ResultStatus()
|
// update the comparison result if the include/exclude was successful
|
||||||
|
if(operation.Success)
|
||||||
{
|
{
|
||||||
Success = true,
|
schemaCompareResults.Value[parameters.OperationId] = operation.ComparisonResult;
|
||||||
ErrorMessage = operation.ErrorMessage
|
}
|
||||||
|
|
||||||
|
await requestContext.SendResult(new SchemaCompareIncludeExcludeResult()
|
||||||
|
{
|
||||||
|
Success = operation.Success,
|
||||||
|
ErrorMessage = operation.ErrorMessage,
|
||||||
|
ChangedDifferences = operation.ChangedDifferences
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
|
|||||||
diffEntry.UpdateAction = difference.UpdateAction;
|
diffEntry.UpdateAction = difference.UpdateAction;
|
||||||
diffEntry.DifferenceType = difference.DifferenceType;
|
diffEntry.DifferenceType = difference.DifferenceType;
|
||||||
diffEntry.Name = difference.Name;
|
diffEntry.Name = difference.Name;
|
||||||
|
diffEntry.Included = difference.Included;
|
||||||
|
|
||||||
if (difference.SourceObject != null)
|
if (difference.SourceObject != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||||
<PackageReference Include="xunit" Version="2.2.0" />
|
<PackageReference Include="xunit" Version="2.2.0" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.SqlServer.DacFx" Version="150.4534.2-preview" />
|
<PackageReference Include="Microsoft.SqlServer.DacFx" Version="150.4576.1-preview" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||||
<PackageReference Include="xunit" Version="2.2.0" />
|
<PackageReference Include="xunit" Version="2.2.0" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.SqlServer.DacFx" Version="150.4534.2-preview" />
|
<PackageReference Include="Microsoft.SqlServer.DacFx" Version="150.4576.1-preview" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||||
|
|||||||
@@ -45,6 +45,19 @@ CREATE TABLE [dbo].[table3]
|
|||||||
[col1] INT NULL,
|
[col1] INT NULL,
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
private const string SourceIncludeExcludeScript = @"CREATE TABLE t1(c1 INT PRIMARY KEY, c2 INT)
|
||||||
|
GO
|
||||||
|
CREATE TABLE t2(c1 INT PRIMARY KEY, c2 INT)
|
||||||
|
GO
|
||||||
|
cREATE VIEW v1 as SELECT c1 FROM t2";
|
||||||
|
|
||||||
|
private const string TargetIncludeExcludeScript = @"CREATE TABLE t1 (c1 INT PRIMARY KEY)
|
||||||
|
GO
|
||||||
|
CREATE TABLE t3 (c3 INT PRIMARY KEY, c2 INT)
|
||||||
|
GO
|
||||||
|
CREATE VIEW v2 as SELECT t1.c1, t3.c3 FROM t1, t3
|
||||||
|
GO";
|
||||||
|
|
||||||
private const string CreateKey = @"CREATE COLUMN MASTER KEY [CMK_Auto1]
|
private const string CreateKey = @"CREATE COLUMN MASTER KEY [CMK_Auto1]
|
||||||
WITH (
|
WITH (
|
||||||
KEY_STORE_PROVIDER_NAME = N'MSSQL_CERTIFICATE_STORE',
|
KEY_STORE_PROVIDER_NAME = N'MSSQL_CERTIFICATE_STORE',
|
||||||
@@ -801,7 +814,102 @@ WITH VALUES
|
|||||||
sourceDb.Cleanup();
|
sourceDb.Cleanup();
|
||||||
targetDb.Cleanup();
|
targetDb.Cleanup();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verify the schema compare request with failing exclude request because of dependencies and that include will include dependencies
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async void SchemaCompareIncludeExcludeWithDependencies()
|
||||||
|
{
|
||||||
|
var result = SchemaCompareTestUtils.GetLiveAutoCompleteTestObjects();
|
||||||
|
SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, SourceIncludeExcludeScript, "SchemaCompareSource");
|
||||||
|
SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, TargetIncludeExcludeScript, "SchemaCompareTarget");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string targetDacpacFilePath = SchemaCompareTestUtils.CreateDacpac(targetDb);
|
||||||
|
|
||||||
|
SchemaCompareEndpointInfo sourceInfo = new SchemaCompareEndpointInfo();
|
||||||
|
SchemaCompareEndpointInfo targetInfo = new SchemaCompareEndpointInfo();
|
||||||
|
|
||||||
|
sourceInfo.EndpointType = SchemaCompareEndpointType.Database;
|
||||||
|
sourceInfo.DatabaseName = sourceDb.DatabaseName;
|
||||||
|
targetInfo.EndpointType = SchemaCompareEndpointType.Dacpac;
|
||||||
|
targetInfo.PackageFilePath = targetDacpacFilePath;
|
||||||
|
|
||||||
|
var schemaCompareParams = new SchemaCompareParams
|
||||||
|
{
|
||||||
|
SourceEndpointInfo = sourceInfo,
|
||||||
|
TargetEndpointInfo = targetInfo
|
||||||
|
};
|
||||||
|
|
||||||
|
SchemaCompareOperation schemaCompareOperation = new SchemaCompareOperation(schemaCompareParams, result.ConnectionInfo, null);
|
||||||
|
schemaCompareOperation.Execute(TaskExecutionMode.Execute);
|
||||||
|
Assert.True(schemaCompareOperation.ComparisonResult.IsValid);
|
||||||
|
Assert.False(schemaCompareOperation.ComparisonResult.IsEqual);
|
||||||
|
Assert.NotNull(schemaCompareOperation.ComparisonResult.Differences);
|
||||||
|
|
||||||
|
// try to exclude
|
||||||
|
DiffEntry t2Diff = SchemaCompareUtils.CreateDiffEntry(schemaCompareOperation.ComparisonResult.Differences.Where(x => x.SourceObject != null && x.SourceObject.Name.Parts[1] == "t2").First(), null);
|
||||||
|
SchemaCompareNodeParams t2ExcludeParams = new SchemaCompareNodeParams()
|
||||||
|
{
|
||||||
|
OperationId = schemaCompareOperation.OperationId,
|
||||||
|
DiffEntry = t2Diff,
|
||||||
|
IncludeRequest = false,
|
||||||
|
TaskExecutionMode = TaskExecutionMode.Execute
|
||||||
|
};
|
||||||
|
|
||||||
|
SchemaCompareIncludeExcludeNodeOperation t2ExcludeOperation = new SchemaCompareIncludeExcludeNodeOperation(t2ExcludeParams, schemaCompareOperation.ComparisonResult);
|
||||||
|
t2ExcludeOperation.Execute(TaskExecutionMode.Execute);
|
||||||
|
Assert.False(t2ExcludeOperation.Success, "Excluding Table t2 should fail because view v1 depends on it");
|
||||||
|
Assert.True(t2ExcludeOperation.ComparisonResult.Differences.Where(x => x.SourceObject != null && x.SourceObject.Name.Parts[1] == "t2").First().Included, "Difference Table t2 should still be included because the exclude request failed");
|
||||||
|
|
||||||
|
// exclude view first, then excluding t2 should work
|
||||||
|
DiffEntry v1Diff = SchemaCompareUtils.CreateDiffEntry(schemaCompareOperation.ComparisonResult.Differences.Where(x => x.SourceObject != null && x.SourceObject.Name.Parts[1] == "v1").First(), null);
|
||||||
|
SchemaCompareNodeParams v1ExcludeParams = new SchemaCompareNodeParams()
|
||||||
|
{
|
||||||
|
OperationId = schemaCompareOperation.OperationId,
|
||||||
|
DiffEntry = v1Diff,
|
||||||
|
IncludeRequest = false,
|
||||||
|
TaskExecutionMode = TaskExecutionMode.Execute
|
||||||
|
};
|
||||||
|
|
||||||
|
SchemaCompareIncludeExcludeNodeOperation v1ExcludeOperation = new SchemaCompareIncludeExcludeNodeOperation(v1ExcludeParams, schemaCompareOperation.ComparisonResult);
|
||||||
|
v1ExcludeOperation.Execute(TaskExecutionMode.Execute);
|
||||||
|
Assert.True(v1ExcludeOperation.Success, "Excluding View v1 should succeed");
|
||||||
|
Assert.False(t2ExcludeOperation.ComparisonResult.Differences.Where(x => x.SourceObject != null && x.SourceObject.Name.Parts[1] == "v1").First().Included, "Difference View v1 should be excluded");
|
||||||
|
|
||||||
|
// try to exclude t2 again and it should succeed this time
|
||||||
|
t2ExcludeOperation.Execute(TaskExecutionMode.Execute);
|
||||||
|
Assert.True(t2ExcludeOperation.Success, "Excluding Table t2 should succeed");
|
||||||
|
Assert.False(t2ExcludeOperation.ComparisonResult.Differences.Where(x => x.SourceObject != null && x.SourceObject.Name.Parts[1] == "t2").First().Included, "Difference Table t2 should still be excluded");
|
||||||
|
|
||||||
|
// including v1 should also include t2
|
||||||
|
SchemaCompareNodeParams v1IncludeParams = new SchemaCompareNodeParams()
|
||||||
|
{
|
||||||
|
OperationId = schemaCompareOperation.OperationId,
|
||||||
|
DiffEntry = v1Diff,
|
||||||
|
IncludeRequest = true,
|
||||||
|
TaskExecutionMode = TaskExecutionMode.Execute
|
||||||
|
};
|
||||||
|
|
||||||
|
SchemaCompareIncludeExcludeNodeOperation v1IncludeOperation = new SchemaCompareIncludeExcludeNodeOperation(v1IncludeParams, t2ExcludeOperation.ComparisonResult);
|
||||||
|
v1IncludeOperation.Execute(TaskExecutionMode.Execute);
|
||||||
|
Assert.True(v1IncludeOperation.Success, "Including v1 should succeed");
|
||||||
|
Assert.True(v1IncludeOperation.ComparisonResult.Differences.Where(x => x.SourceObject != null && x.SourceObject.Name.Parts[1] == "v1").First().Included, "Difference View v1 should be included");
|
||||||
|
Assert.True(v1IncludeOperation.ComparisonResult.Differences.Where(x => x.SourceObject != null && x.SourceObject.Name.Parts[1] == "t2").First().Included, "Difference Table t2 should still be included");
|
||||||
|
Assert.True(v1IncludeOperation.ChangedDifferences != null && v1IncludeOperation.ChangedDifferences.Count == 1, "There should be one difference");
|
||||||
|
Assert.True(v1IncludeOperation.ChangedDifferences.First().SourceValue[1] == "t2", "The affected difference of including v1 should be t2");
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
SchemaCompareTestUtils.VerifyAndCleanup(targetDacpacFilePath);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
sourceDb.Cleanup();
|
||||||
|
targetDb.Cleanup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidateSchemaCompareWithExcludeIncludeResults(SchemaCompareOperation schemaCompareOperation)
|
private void ValidateSchemaCompareWithExcludeIncludeResults(SchemaCompareOperation schemaCompareOperation)
|
||||||
|
|||||||
Reference in New Issue
Block a user