Skip to content
Closed
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,14 @@ public DatabaseEngineEdition DatabaseEngineEdition
}
}

/// <summary>
/// The original edition of the Database Engine before any conversion/mapping
/// </summary>
internal DatabaseEngineEdition OriginalDatabaseEngineEdition
{
get { return GetServerInformation().OriginalDatabaseEngineEdition; }
}

/// <summary>
/// The host platform of the server (Linux/Windows/etc)
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ internal class ServerInformation
private readonly Version productVersion;
private readonly DatabaseEngineType databaseEngineType;
private readonly DatabaseEngineEdition databaseEngineEdition;
private readonly DatabaseEngineEdition originalDatabaseEngineEdition;
private readonly string hostPlatform;
private readonly NetworkProtocol connectionProtocol;

Expand All @@ -37,7 +38,7 @@ internal class ServerInformation
/// <param name="dt"></param>
/// <param name="databaseEngineEdition"></param>
public ServerInformation(ServerVersion sv, Version productVersion, DatabaseEngineType dt, DatabaseEngineEdition databaseEngineEdition)
: this(sv, productVersion, dt, databaseEngineEdition, HostPlatformNames.Windows, NetworkProtocol.NotSpecified)
: this(sv, productVersion, dt, databaseEngineEdition, databaseEngineEdition, HostPlatformNames.Windows, NetworkProtocol.NotSpecified)
{

}
Expand All @@ -49,15 +50,17 @@ public ServerInformation(ServerVersion sv, Version productVersion, DatabaseEngin
/// <param name="productVersion"></param>
/// <param name="dt"></param>
/// <param name="databaseEngineEdition"></param>
/// <param name="originalDatabaseEngineEdition"></param>
/// <param name="hostPlatform"></param>
/// <param name="connectionProtocol">net_transport value from dm_exec_connections for the current spid</param>
public ServerInformation(ServerVersion sv, Version productVersion, DatabaseEngineType dt, DatabaseEngineEdition databaseEngineEdition,
string hostPlatform, NetworkProtocol connectionProtocol)
DatabaseEngineEdition originalDatabaseEngineEdition, string hostPlatform, NetworkProtocol connectionProtocol)
{
this.serverVersion = sv;
this.productVersion = productVersion;
this.databaseEngineType = dt;
this.databaseEngineEdition = databaseEngineEdition;
this.originalDatabaseEngineEdition = originalDatabaseEngineEdition;
this.hostPlatform = hostPlatform;
this.connectionProtocol = connectionProtocol;
}
Expand Down Expand Up @@ -103,6 +106,14 @@ public DatabaseEngineEdition DatabaseEngineEdition
get { return databaseEngineEdition; }
}

/// <summary>
/// The original DatabaseEngineEdition value before any conversion/mapping
/// </summary>
public DatabaseEngineEdition OriginalDatabaseEngineEdition
{
get { return originalDatabaseEngineEdition; }
}

/// <summary>
/// Protocol used for the connection.
/// </summary>
Expand Down Expand Up @@ -156,6 +167,7 @@ static public ServerInformation GetServerInformation(IDbConnection sqlConnection

var engineType = (DatabaseEngineType)dataSet.Tables[0].Rows[0]["DatabaseEngineType"];
var edition = (DatabaseEngineEdition)dataSet.Tables[0].Rows[0]["DatabaseEngineEdition"];
var originalEdition = edition; // Preserve the original edition value
// Treat unknown editions from Azure the same as Azure SQL database
if (engineType == DatabaseEngineType.SqlAzureDatabase && !validEditions.Contains(edition))
{
Expand All @@ -180,6 +192,7 @@ static public ServerInformation GetServerInformation(IDbConnection sqlConnection
new Version((string)dataSet.Tables[0].Rows[0]["ProductVersion"]),
engineType,
edition,
originalEdition,
(string)dataSet.Tables[1].Rows[0]["host_platform"],
connectionProtocol == DBNull.Value ? NetworkProtocol.NotSpecified : ProtocolFromNetTransport((string)connectionProtocol)
);
Expand Down
5 changes: 5 additions & 0 deletions src/Microsoft/SqlServer/Management/Smo/ExecutionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,11 @@ internal DatabaseEngineEdition GetDatabaseEngineEdition()
return this.ConnectionContext.DatabaseEngineEdition;
}

internal DatabaseEngineEdition GetOriginalDatabaseEngineEdition()
{
return this.ConnectionContext.OriginalDatabaseEngineEdition;
}

internal NetworkProtocol GetConnectionProtocol()
{
return this.ConnectionContext.ConnectionProtocol;
Expand Down
4 changes: 3 additions & 1 deletion src/Microsoft/SqlServer/Management/Smo/serverbase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3047,7 +3047,9 @@ private IEnumerable<string> CreateInitFieldsColl(Type typeObject)
// the Database can only be the same edition as the ServerConnection.
// If we are connected to logical master then Database can have a different edition, so
// add RealEngineEdition to the query so we get the edition from sys.database_service_objectives
if (DatabaseEngineEdition == DatabaseEngineEdition.SqlDatabase) {
// Only add RealEngineEdition for Azure SQL Database where the original edition was SqlDatabase
// (not for unknown editions like Dynamics CRM edition 1000 that get converted to SqlDatabase)
if (this.ExecutionManager.GetOriginalDatabaseEngineEdition() == DatabaseEngineEdition.SqlDatabase && DatabaseEngineType == DatabaseEngineType.SqlAzureDatabase) {
yield return "RealEngineEdition";
}
}
Expand Down
85 changes: 85 additions & 0 deletions src/UnitTest/ConnectionInfo/ServerInformationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,91 @@ select N'Windows' as host_platform
connectMock.VerifyAll();
}

[TestMethod]
[TestCategory("Unit")]
public void When_Edition_is_1000_from_Azure_server_it_is_converted_to_SqlDatabase()
{
// Test for Dynamics CRM scenario (edition 1000)
const string versionString = "16.00.1000.6";

var connectMock = new Mock<IDbConnection>();
var commandMock = new Mock<IDbCommand>();
var dataAdapterMock = new Mock<IDbDataAdapter>();

connectMock.Setup(c => c.CreateCommand()).Returns(commandMock.Object);
dataAdapterMock.SetupSet(d => d.SelectCommand = commandMock.Object);
dataAdapterMock.Setup(d => d.Fill(It.IsAny<DataSet>())).Callback(
(DataSet ds) =>
{
// Simulate Dynamics CRM server returning edition 1000 with Azure engine type
FillTestDataSet(ds, versionString, DatabaseEngineType.SqlAzureDatabase, (DatabaseEngineEdition)1000, 0x10000FA0, HostPlatformNames.Windows, "TCP");
});

var si = ServerInformation.GetServerInformation(connectMock.Object, dataAdapterMock.Object, versionString);

// Verify that edition 1000 from Azure is converted to SqlDatabase
Assert.That(si.DatabaseEngineType, Is.EqualTo(DatabaseEngineType.SqlAzureDatabase), "Engine type should remain SqlAzureDatabase");
Assert.That(si.DatabaseEngineEdition, Is.EqualTo(DatabaseEngineEdition.SqlDatabase), "Edition 1000 from Azure should be converted to SqlDatabase");
Assert.That(si.OriginalDatabaseEngineEdition, Is.EqualTo((DatabaseEngineEdition)1000), "Original edition should be preserved as 1000");
Assert.That(si.ProductVersion, Is.EqualTo(new Version(versionString)), "Unexpected ProductVersion");
}

[TestMethod]
[TestCategory("Unit")]
public void When_Edition_is_1000_from_Standalone_server_it_remains_unchanged()
{
// Test for edition 1000 from a standalone server (not Azure)
const string versionString = "16.00.1000.6";

var connectMock = new Mock<IDbConnection>();
var commandMock = new Mock<IDbCommand>();
var dataAdapterMock = new Mock<IDbDataAdapter>();

connectMock.Setup(c => c.CreateCommand()).Returns(commandMock.Object);
dataAdapterMock.SetupSet(d => d.SelectCommand = commandMock.Object);
dataAdapterMock.Setup(d => d.Fill(It.IsAny<DataSet>())).Callback(
(DataSet ds) =>
{
// Simulate standalone server returning edition 1000
FillTestDataSet(ds, versionString, DatabaseEngineType.Standalone, (DatabaseEngineEdition)1000, 0x10000FA0, HostPlatformNames.Windows, "TCP");
});

var si = ServerInformation.GetServerInformation(connectMock.Object, dataAdapterMock.Object, versionString);

// Verify that edition 1000 from standalone server is NOT converted
Assert.That(si.DatabaseEngineType, Is.EqualTo(DatabaseEngineType.Standalone), "Engine type should be Standalone");
Assert.That(si.DatabaseEngineEdition, Is.EqualTo((DatabaseEngineEdition)1000), "Edition 1000 from standalone should remain as 1000");
Assert.That(si.OriginalDatabaseEngineEdition, Is.EqualTo((DatabaseEngineEdition)1000), "Original edition should be 1000");
}

[TestMethod]
[TestCategory("Unit")]
public void When_Edition_is_SqlDatabase_original_edition_matches_converted_edition()
{
// Test that genuine Azure SQL Database preserves original edition correctly
const string versionString = "12.00.6024.0";

var connectMock = new Mock<IDbConnection>();
var commandMock = new Mock<IDbCommand>();
var dataAdapterMock = new Mock<IDbDataAdapter>();

connectMock.Setup(c => c.CreateCommand()).Returns(commandMock.Object);
dataAdapterMock.SetupSet(d => d.SelectCommand = commandMock.Object);
dataAdapterMock.Setup(d => d.Fill(It.IsAny<DataSet>())).Callback(
(DataSet ds) =>
{
// Simulate genuine Azure SQL Database (edition 5)
FillTestDataSet(ds, versionString, DatabaseEngineType.SqlAzureDatabase, DatabaseEngineEdition.SqlDatabase, 0x0C001788, HostPlatformNames.Windows, "TCP");
});

var si = ServerInformation.GetServerInformation(connectMock.Object, dataAdapterMock.Object, versionString);

// Verify that SqlDatabase edition stays the same and original matches
Assert.That(si.DatabaseEngineType, Is.EqualTo(DatabaseEngineType.SqlAzureDatabase), "Engine type should be SqlAzureDatabase");
Assert.That(si.DatabaseEngineEdition, Is.EqualTo(DatabaseEngineEdition.SqlDatabase), "Edition should be SqlDatabase");
Assert.That(si.OriginalDatabaseEngineEdition, Is.EqualTo(DatabaseEngineEdition.SqlDatabase), "Original edition should also be SqlDatabase (no conversion)");
}

private void FillTestDataSet(DataSet ds, string productVersion, DatabaseEngineType databaseEngineType, DatabaseEngineEdition databaseEngineEdition,
int microsoftVersion, string hostPlatform, string protocol)
{
Expand Down