Skip to content

Conversation

Copy link

Copilot AI commented Oct 9, 2025

Fix for ClearAndInitialize() failure on Dynamics CRM servers (edition 1000)

Problem

When connecting to a Dynamics CRM server and calling ClearAndInitialize() on the Databases property, the operation fails with:

An exception occurred while executing a Transact-SQL statement or batch.
Inner exception: CREATE TABLE' statement is not supported. Line:2, Position:9

This was working in version 171.30.0 but has been broken since 172.52.0.

Root Cause

The issue was introduced when RealEngineEdition was added to the default set of properties on the Database object. This property requires querying sys.database_service_objectives, which creates a temp table (#dso). Dynamics CRM servers (which report SERVERPROPERTY('EngineEdition') = 1000) do not support CREATE TABLE statements.

The core problem was that:

  1. ServerInformation converts unknown Azure editions (like 1000) to SqlDatabase
  2. The Server class then checks if DatabaseEngineEdition == SqlDatabase to decide whether to add RealEngineEdition
  3. Since edition 1000 was already converted to SqlDatabase, the check would pass
  4. RealEngineEdition would be added, triggering temp table creation
  5. Dynamics CRM would fail with "CREATE TABLE not supported"

Solution

Modified the code to preserve the ORIGINAL edition value before any conversion/mapping:

  1. ServerInformation.cs: Added OriginalDatabaseEngineEdition property to preserve the edition value before conversion
  2. ConnectionManager.cs: Exposed the original edition through OriginalDatabaseEngineEdition internal property
  3. ExecutionManager.cs: Added GetOriginalDatabaseEngineEdition() method
  4. serverbase.cs: Changed the condition to directly call ExecutionManager.GetOriginalDatabaseEngineEdition() instead of the potentially converted DatabaseEngineEdition

Key change:

// Before (checking converted edition - ineffective):
if (DatabaseEngineEdition == DatabaseEngineEdition.SqlDatabase && DatabaseEngineType == DatabaseEngineType.SqlAzureDatabase) {
    yield return "RealEngineEdition";
}

// After (checking original edition before conversion):
if (this.ExecutionManager.GetOriginalDatabaseEngineEdition() == DatabaseEngineEdition.SqlDatabase && DatabaseEngineType == DatabaseEngineType.SqlAzureDatabase) {
    yield return "RealEngineEdition";
}

This ensures RealEngineEdition is only queried when:

  • The ORIGINAL edition from the server was SqlDatabase (5), not a value that was converted to SqlDatabase
  • The engine type is SqlAzureDatabase

For Dynamics CRM:

  • Original edition: 1000 (unknown)
  • Converted edition: SqlDatabase (5)
  • GetOriginalDatabaseEngineEdition() == SqlDatabase → FALSE
  • RealEngineEdition is NOT added → No temp table creation → No error ✓

For genuine Azure SQL Database:

  • Original edition: SqlDatabase (5)
  • Stays as: SqlDatabase (5)
  • GetOriginalDatabaseEngineEdition() == SqlDatabase → TRUE
  • RealEngineEdition IS added correctly ✓

Testing

Added comprehensive unit tests in ServerInformationTests.cs:

  1. When_Edition_is_1000_from_Azure_server_it_is_converted_to_SqlDatabase: Verifies that edition 1000 from Azure engine type is converted to SqlDatabase while preserving the original edition
  2. When_Edition_is_1000_from_Standalone_server_it_remains_unchanged: Verifies that edition 1000 from standalone servers is NOT converted
  3. When_Edition_is_SqlDatabase_original_edition_matches_converted_edition: Verifies that genuine Azure SQL Database preserves the original edition correctly (no conversion)

Impact

  • Targeted: Only affects the decision to add RealEngineEdition to default fields
  • Safe: Genuine Azure SQL Database servers still get RealEngineEdition correctly
  • Minimal: Server directly accesses ExecutionManager without unnecessary property bubbling
  • Backward Compatible: Restores functionality that worked in 171.30.0
  • Well-tested: Comprehensive unit tests cover all scenarios

Fixes #207

Original prompt

This section details on the original issue you should resolve

<issue_title>ClearAndInitialize() fails when called on the Databases property of a Dynamics CRM server</issue_title>
<issue_description>This was working in 171.30.0 but has been broken since 172.52.0 (including latest).

Minimal repro:

using Microsoft.Data.SqlClient;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;

SqlConnectionStringBuilder connectionString = new SqlConnectionStringBuilder("Data Source=org12345678.crm.dynamics.com,1433;User [email protected];Authentication=ActiveDirectoryInteractive");

Server server = new Server(new ServerConnection(new SqlConnection(connectionString.ConnectionString)));
var databases = server.Databases;

try
{
    databases.ClearAndInitialize("[(@IsSystemObject = 0)]", ["Status"]);
    Console.WriteLine("Databases initialized successfully.");
} catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

Error message:

An exception occurred while executing a Transact-SQL statement or batch.
Inner exception: CREATE TABLE' statement​ is not supported. Line:2, Position:9

Stack trace:

   at Microsoft.SqlServer.Management.Common.ServerConnection.GetExecuteReader(SqlCommand command)
   at Microsoft.SqlServer.Management.Common.ServerConnection.ExecuteReader(String sqlCommand, SqlCommand& command)
   at Microsoft.SqlServer.Management.Smo.ExecuteSql.GetDataReader(String query, SqlCommand& command)
   at Microsoft.SqlServer.Management.Smo.DataProvider.SetConnectionAndQuery(ExecuteSql execSql, String query)
   at Microsoft.SqlServer.Management.Smo.ExecuteSql.GetDataProvider(StringCollection query, Object con, StatementBuilder sb, RetriveMode rm)
   at Microsoft.SqlServer.Management.Smo.ExecuteSql.GetDataProvider(StringCollection query, Object con, StatementBuilder sb)
   at Microsoft.SqlServer.Management.Smo.SqlObjectBase.FillData(ResultType resultType, StringCollection sql, Object connectionInfo, StatementBuilder sb)
   at Microsoft.SqlServer.Management.Smo.SqlObjectBase.FillDataWithUseFailure(SqlEnumResult sqlresult, ResultType resultType)
   at Microsoft.SqlServer.Management.Smo.SqlObjectBase.BuildResult(EnumResult result)
   at Microsoft.SqlServer.Management.Smo.DatabaseLevel.GetData(EnumResult res)
   at Microsoft.SqlServer.Management.Sdk.Sfc.Environment.GetData()
   at Microsoft.SqlServer.Management.Sdk.Sfc.Environment.GetData(Request req, Object ci)
   at Microsoft.SqlServer.Management.Sdk.Sfc.Enumerator.GetData(Object connectionInfo, Request request)
   at Microsoft.SqlServer.Management.Smo.ExecutionManager.GetEnumeratorDataReader(Request req)
   at Microsoft.SqlServer.Management.Smo.SqlSmoObject.InitChildLevel(Urn levelFilter, ScriptingPreferences sp, Boolean forScripting, IEnumerable`1 extraFields)
   at Microsoft.SqlServer.Management.Smo.SmoCollectionBase.InitializeChildCollection(Boolean refresh, ScriptingPreferences sp, String filterQuery, IEnumerable`1 extraFields)
   at Microsoft.SqlServer.Management.Smo.SmoCollectionBase.ClearAndInitialize(String filterQuery, IEnumerable`1 extraFields)
   at Program.<Main>$(String[] args)
```</issue_description>

<agent_instructions>In ServerInformation.cs, if the edition is 1000, treat the server as Standalone type with Express edition</agent_instructions>

## Comments on the Issue (you are @copilot in this section)

<comments>
<comment_new><author>@shueybubbles</author><body>
SMO has used temp tables in this query for years. </body></comment_new>
</comments>

Fixes #207


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@shueybubbles shueybubbles requested a review from Benjin October 9, 2025 20:34
Copilot AI changed the title [WIP] Fix ClearAndInitialize() failure on Databases property Fix ClearAndInitialize() failure on Dynamics CRM servers (edition 1000) Oct 9, 2025
Copilot AI requested a review from shueybubbles October 9, 2025 20:36
@shueybubbles shueybubbles marked this pull request as ready for review October 9, 2025 20:38
edition = DatabaseEngineEdition.SqlDatabase;
}
// If edition is 1000 (Dynamics CRM), treat it as Standalone with Express edition
if ((int)edition == 1000)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about having a safe default fallback for any unknown editions?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot-swe-agent move this check inside the if block on line 160

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the edition 1000 check inside a more general unknown editions block (commit 56a7427 → new commit). This now provides a safe default fallback: unknown editions are checked first, then edition 1000 gets Standalone+Express, and other Azure unknown editions fall back to SqlDatabase.

edition = DatabaseEngineEdition.SqlDatabase;
}
// If edition is 1000 (Dynamics CRM), treat it as Standalone with Express edition
if ((int)edition == 1000)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So do we know why this broke in the first place? The description implies it was working fine before 172.52.0

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue was introduced when RealEngineEdition was added to the default set of properties on the Database object (as mentioned by @shueybubbles). This caused SMO to generate queries with temp tables to fetch that property, which Dynamics CRM doesn't support. Before that change, those queries weren't being executed.

@shueybubbles
Copy link
Collaborator

We added RealEngineEdition to the default set of properties on the Database object to fix other bugs.

@shueybubbles
Copy link
Collaborator

@copilot
Turns out that pretending it's not Azure doesn't work because CRM doesn't have "master". We could change ServerInformation and GetServerInformation to get the edition. The Server class would then add RealEngineEdition to the query fields for the Database object only when the server edition is AzureSqlDatabase instead of Unknown.

Copy link
Author

Copilot AI commented Oct 9, 2025

@copilot
Turns out that pretending it's not Azure doesn't work because CRM doesn't have "master". We could change ServerInformation and GetServerInformation to get the edition. The Server class woul...

Changed the condition to check both DatabaseEngineEdition == SqlDatabase AND DatabaseEngineType == SqlAzureDatabase before adding RealEngineEdition. This ensures it's only added for genuine Azure SQL Database servers, not for Dynamics CRM (edition 1000) or other unexpected configurations. This avoids the temp table creation that Dynamics CRM doesn't support.

// 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, not for unknown editions like Dynamics CRM (edition 1000)
if (DatabaseEngineEdition == DatabaseEngineEdition.SqlDatabase && DatabaseEngineType == DatabaseEngineType.SqlAzureDatabase) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(DatabaseEngineEdition

@copilot-swe-agent the DatabasEngineEdition value here will never be 1000 because the ServerInformation is converting that value to SqlDatabase during GetServerInformation. ServerConnection or ServerInformation needs to remember the original edition number and check it here to decide if asking for RealEngineEdition is needed. If the original value was anything other than SqlDatabase this property isn't needed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added OriginalDatabaseEngineEdition property throughout the stack (ServerInformation → ConnectionManager → ExecutionManager → SqlSmoObject → Server) to preserve the edition value before conversion. The Server now checks OriginalDatabaseEngineEdition == SqlDatabase instead of the converted DatabaseEngineEdition, ensuring RealEngineEdition is only added for genuine Azure SQL Database (not for edition 1000 converted to SqlDatabase). Commit: 3da2a36

/// <summary>
/// Returns the original DatabaseEngineEdition value before any conversion/mapping
/// </summary>
internal virtual DatabaseEngineEdition OriginalDatabaseEngineEdition
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internal virtual

@copilot this new property is not needed, is it? The Server can get the original edition directly from its ExecutionManager when needed and no other entity needs to see it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the OriginalDatabaseEngineEdition property from SqlSmoObject. The Server now directly calls this.ExecutionManager.GetOriginalDatabaseEngineEdition() instead of accessing it through a property. This simplifies the code since only Server needs this value. Commit: 7f4e8c1

@Benjin
Copy link

Benjin commented Oct 13, 2025

@copilot this appears to be working. can you update/add the necessary unit tests?

Copy link
Author

Copilot AI commented Oct 13, 2025

@copilot this appears to be working. can you update/add the necessary unit tests?

Added three comprehensive unit tests in ServerInformationTests.cs:

  1. When_Edition_is_1000_from_Azure_server_it_is_converted_to_SqlDatabase - Tests Dynamics CRM scenario where edition 1000 from Azure is converted
  2. When_Edition_is_1000_from_Standalone_server_it_remains_unchanged - Tests edition 1000 from standalone servers (no conversion)
  3. When_Edition_is_SqlDatabase_original_edition_matches_converted_edition - Tests genuine Azure SQL Database (original matches converted)

These tests verify the original edition preservation and conversion logic. Commit: c4f5a2d

@Benjin
Copy link

Benjin commented Oct 14, 2025

Completing this internally. Thank you for your service, Copilot. 🫡

@Benjin Benjin closed this Oct 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ClearAndInitialize() fails when called on the Databases property of a Dynamics CRM server

4 participants