using System.Data.Common;
using SQLInsight.Core.Instances;
namespace SQLInsight.Connectivity;
///
/// Reads version, edition and key settings from an instance. Works on every version from SQL Server 2000:
/// only SERVERPROPERTY is used, and properties an older version does not know return NULL.
///
public static class InstanceProbe
{
internal const string Query = """
SELECT
CAST(SERVERPROPERTY('ServerName') AS nvarchar(256)),
CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(128)),
CAST(SERVERPROPERTY('ProductLevel') AS nvarchar(128)),
CAST(SERVERPROPERTY('ProductUpdateLevel') AS nvarchar(128)),
CAST(SERVERPROPERTY('Edition') AS nvarchar(128)),
CAST(SERVERPROPERTY('EngineEdition') AS int),
CAST(SERVERPROPERTY('IsClustered') AS int),
CAST(SERVERPROPERTY('IsHadrEnabled') AS int)
""";
public static async Task DetectAsync(DbConnection connection, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(connection);
await using var command = connection.CreateCommand();
command.CommandText = Query;
command.CommandTimeout = 30;
await using var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
if (!await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
throw new InvalidOperationException("SERVERPROPERTY returned no rows.");
}
var productVersion = GetString(reader, 1);
if (!SqlServerVersion.TryParse(productVersion, out var version))
{
throw new InvalidOperationException($"Could not parse ProductVersion '{productVersion}'.");
}
return new InstanceInfo
{
ServerName = GetString(reader, 0) ?? string.Empty,
Version = version,
ProductLevel = GetString(reader, 2),
ProductUpdateLevel = GetString(reader, 3),
Edition = GetString(reader, 4) ?? string.Empty,
EngineEdition = reader.IsDBNull(5) ? EngineEdition.Unknown : (EngineEdition)reader.GetInt32(5),
IsClustered = GetBool(reader, 6),
IsHadrEnabled = GetBool(reader, 7),
};
}
private static string? GetString(DbDataReader reader, int ordinal) =>
reader.IsDBNull(ordinal) ? null : reader.GetString(ordinal);
private static bool? GetBool(DbDataReader reader, int ordinal) =>
reader.IsDBNull(ordinal) ? null : reader.GetInt32(ordinal) == 1;
}