Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
- .NET 10 solution with Core, Connectivity, Rules, Contracts, Agent, Web and Hub - Capability model gating collectors and rules by SQL Server version (2000 and later); rules that cannot run are reported as not measurable, never as passed - Connectivity via Microsoft.Data.SqlClient with ODBC fallback for legacy instances, hardened monitoring session settings valid back to SQL Server 2000 - Support lifecycle rule (SQLI-VER-001) with lifecycle data shipped as a rule pack - Version-aware least-privilege permissions script, verified by integration tests against SQL Server in Docker (reads monitoring sources, cannot read customer data) - Forgejo Actions CI, contributing guide, code of conduct, issue templates, legacy version notes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
62 lines
2.6 KiB
C#
62 lines
2.6 KiB
C#
using System.Data.Common;
|
|
using SQLInsight.Core.Instances;
|
|
|
|
namespace SQLInsight.Connectivity;
|
|
|
|
/// <summary>
|
|
/// Reads version, edition and key settings from an instance. Works on every version from SQL Server 2000:
|
|
/// only <c>SERVERPROPERTY</c> is used, and properties an older version does not know return NULL.
|
|
/// </summary>
|
|
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<InstanceInfo> 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;
|
|
}
|