sqlinsight-pro/tests/SQLInsight.Collectors.Tests/SqlScripts/SqlScriptSources.cs
Søren Eggert Lundsteen Olsen cb99778a3f Review 1.5: SQL Scripts for Windows servers and synthetic checks follow main's structure and registry
After the rebase onto 1.4 the 16 SQLI-WIN/SQLI-SYN scripts had their own /* */ format,
so SqlScriptTests (rule ids, review header, -- Kilde: blocks) failed.

- The scripts have main's header (Regel-id, Titel, Alvor, Versioner, Vurdering,
  Grænseværdier, Rettigheder, Data kommer fra, Regelkode, Read-only); the Windows
  WQL/XPath stays word for word in comments.
- The synthetic SQL check's SQL is registered (SyntheticCheckRunner.UseDatabaseSql,
  BeginTransactionSql, RollbackSql, SyntheticCheckSettings.DefaultQuery) and shown
  as -- Kilde: blocks in SQLI-SYN-001/002 and _collectors/SyntheticCheckRunner.sql.
- SqlScriptTests know ServerRuleCatalog; only those rules may be without SQL blocks.
- ServerScriptTests: every CimQueries query/filter is in a SQLI-WIN script word for word.
- The SQL check cancels the rest of a large answer instead of draining it on close.
- Rule counts: README (108 built-in: 92 SQL Server + 13 Windows + 3 synthetic),
  SQL Scripts/README (rule table, 124 rules, Windows/synthetic section), RuleCatalogTests,
  CONTRIBUTING (how to add a server rule).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:18:24 +02:00

453 lines
41 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Reflection;
using System.Text.RegularExpressions;
using SQLInsight.Collectors.Concurrency;
using SQLInsight.Collectors.Custom;
using SQLInsight.Collectors.Databases;
using SQLInsight.Collectors.Discovery;
using SQLInsight.Collectors.Instance;
using SQLInsight.Collectors.Performance;
using SQLInsight.Core.Instances;
using SQLInsight.Core.SyntheticChecks;
using SQLInsight.Servers.Synthetic;
namespace SQLInsight.Collectors.Tests.SqlScripts;
/// <summary>
/// Every SQL text the agent sends to a monitored instance, as the code builds it, keyed as the scripts in the folder
/// "SQL Scripts" name it on their <c>-- Kilde: &lt;key&gt;</c> lines. Builders are called with the same arguments as the
/// scripts show (defaults from the agent's configuration, the database msdb). Values the agent computes at run time (a
/// trace file path, a point in time) are rendered as tokens ⟪name⟫: a script may put anything there, typically a variable
/// declared above it so the script runs in SSMS.
/// </summary>
internal static class SqlScriptSources
{
/// <summary>The database per-database queries are shown for: it exists on every instance, so the scripts run as they are.</summary>
public const string ExampleDatabase = "msdb";
/// <param name="Key">What the script's <c>-- Kilde:</c> line says.</param>
/// <param name="Collector">The collector (or InstanceProbe/connection) that sends it.</param>
/// <param name="Variant">The SQL Server versions or situation the text is sent for.</param>
/// <param name="File">The source file, relative to the repository root.</param>
/// <param name="Member">The constant or method holding the text.</param>
public sealed record Source(string Key, string Collector, string Variant, string File, string Member, Func<string> Render)
{
public string Sql => Normalize(Render());
}
private static readonly SqlServerVersion V2000 = SqlServerVersion.Parse("8.0.2039.0");
private static readonly SqlServerVersion V2005 = SqlServerVersion.Parse("9.0.5000.0");
private static readonly SqlServerVersion V2008 = SqlServerVersion.Parse("10.0.6000.29");
private static readonly SqlServerVersion V2008R2Sp1 = SqlServerVersion.Parse("10.50.2500.0");
private static readonly SqlServerVersion V2012 = SqlServerVersion.Parse("11.0.7001.0");
private static readonly SqlServerVersion V2014 = SqlServerVersion.Parse("12.0.6024.0");
private static readonly SqlServerVersion V2016Sp2 = SqlServerVersion.Parse("13.0.5026.0");
private static readonly SqlServerVersion V2022 = SqlServerVersion.Parse("16.0.4135.4");
private const string C = "src/SQLInsight.Collectors/";
public static IReadOnlyList<Source> All { get; } = Create();
public static IReadOnlyDictionary<string, Source> ByKey { get; } = All.ToDictionary(s => s.Key, StringComparer.Ordinal);
public static IEnumerable<Source> For(string collector) => All.Where(s => s.Collector == collector);
/// <summary>LF line endings: the sources and the scripts may be checked out with CRLF.</summary>
public static string Normalize(string sql) => sql.Replace("\r\n", "\n", StringComparison.Ordinal).Replace('\r', '\n');
/// <summary>A regex that finds <paramref name="sql"/> in a script, with any text where it has tokens.</summary>
public static Regex Pattern(string sql)
{
var parts = Regex.Split(Normalize(sql), "⟪[a-z]+⟫");
return new Regex(string.Join("(?:.+?)", parts.Select(Regex.Escape)), RegexOptions.Singleline | RegexOptions.CultureInvariant);
}
private static string Tokens(string sql, params string[] names)
{
foreach (var name in names)
{
sql = sql.Replace("N'§" + name + "§'", "⟪" + name + "⟫", StringComparison.Ordinal)
.Replace("'§" + name + "§'", "⟪" + name + "⟫", StringComparison.Ordinal)
.Replace("§" + name + "§", "⟪" + name + "⟫", StringComparison.Ordinal);
}
return sql;
}
private static string Internal(Type type, string field) =>
(string)(type.GetField(field, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)?.GetRawConstantValue()
?? throw new InvalidOperationException($"{type.Name}.{field} findes ikke."));
private static InstanceInfo Instance(SqlServerVersion version) => new()
{
ServerName = "eksempel",
Version = version,
Edition = "Enterprise Edition",
EngineEdition = EngineEdition.Enterprise,
IsHadrEnabled = version.Major >= 11,
};
private static string DatabaseSql2005(SqlServerVersion version)
{
var instance = Instance(version);
return DatabaseCollector.BuildSql2005(instance, InstanceCapabilities.From(instance));
}
private static List<Source> Create()
{
var probe = typeof(Connectivity.InstanceProbe);
var factory = typeof(Connectivity.SqlConnectionFactory);
var sources = new List<Source>
{
// Connection and registration.
new("SqlConnectionFactory.SessionSettings", "InstanceProbe", "Alle versioner: sættes på hver forbindelse, agenten åbner",
"src/SQLInsight.Connectivity/SqlConnectionFactory.cs", "SqlConnectionFactory.SessionSettings", () => Internal(factory, "SessionSettings")),
new("InstanceProbe.Query", "InstanceProbe", "Alle versioner (SQL Server 2000 og nyere)",
"src/SQLInsight.Connectivity/InstanceProbe.cs", "InstanceProbe.Query", () => Internal(probe, "Query")),
// Server.
new("ServerCollector.BuildSql(2012+)", "ServerCollector", "SQL Server 2012 og nyere",
C + "Instance/ServerCollector.cs", "ServerCollector.BuildSql(version)", () => ServerCollector.BuildSql(V2012)),
new("ServerCollector.BuildSql(2008 R2 SP1)", "ServerCollector", "SQL Server 2008 R2 SP1 og senere 2008 R2-builds",
C + "Instance/ServerCollector.cs", "ServerCollector.BuildSql(version)", () => ServerCollector.BuildSql(V2008R2Sp1)),
new("ServerCollector.BuildSql(2008)", "ServerCollector", "SQL Server 2008 og 2008 R2 før SP1",
C + "Instance/ServerCollector.cs", "ServerCollector.BuildSql(version)", () => ServerCollector.BuildSql(V2008)),
new("ServerCollector.BuildSql(2005)", "ServerCollector", "SQL Server 2005",
C + "Instance/ServerCollector.cs", "ServerCollector.BuildSql(version)", () => ServerCollector.BuildSql(V2005)),
StartTime("ServerCollector", V2000, "SQL Server 2000 (legacy ODBC-sti): starttidspunkt"),
new("ServerCollector.Sql2000", "ServerCollector", "SQL Server 2000 (legacy ODBC-sti): CPU og hukommelse",
C + "Instance/ServerCollector.cs", "ServerCollector.Sql2000", () => ServerCollector.Sql2000),
// Configuration and trace flags.
new("ConfigurationCollector.Sql2005", "ConfigurationCollector", "SQL Server 2005 og nyere",
C + "Instance/ConfigurationCollector.cs", "ConfigurationCollector.Sql2005", () => ConfigurationCollector.Sql2005),
new("ConfigurationCollector.Sql2000", "ConfigurationCollector", "SQL Server 2000 (legacy ODBC-sti)",
C + "Instance/ConfigurationCollector.cs", "ConfigurationCollector.Sql2000", () => ConfigurationCollector.Sql2000),
new("TraceFlagCollector.Sql", "TraceFlagCollector", "Alle versioner (SQL Server 2000 og nyere)",
C + "Instance/TraceFlagCollector.cs", "TraceFlagCollector.Sql", () => TraceFlagCollector.Sql),
// Instance configuration (the configuration page and its change log).
new("InstanceConfigurationCollector.PropertiesSql", "InstanceConfigurationCollector", "Alle versioner",
C + "Instance/InstanceConfigurationCollector.cs", "InstanceConfigurationCollector.PropertiesSql", () => InstanceConfigurationCollector.PropertiesSql),
new("InstanceConfigurationCollector.TempdbSql", "InstanceConfigurationCollector", "SQL Server 2005 og nyere",
C + "Instance/InstanceConfigurationCollector.cs", "InstanceConfigurationCollector.TempdbSql", () => InstanceConfigurationCollector.TempdbSql),
new("InstanceConfigurationCollector.ColumnsSql", "InstanceConfigurationCollector", "SQL Server 2008 R2 SP1 og nyere",
C + "Instance/InstanceConfigurationCollector.cs", "InstanceConfigurationCollector.ColumnsSql", () => InstanceConfigurationCollector.ColumnsSql),
new("InstanceConfigurationCollector.BuildServicesSql(med IFI)", "InstanceConfigurationCollector", "SQL Server 2008 R2 SP1 og nyere, builds med kolonnen instant_file_initialization_enabled (se ColumnsSql)",
C + "Instance/InstanceConfigurationCollector.cs", "InstanceConfigurationCollector.BuildServicesSql(true)", () => InstanceConfigurationCollector.BuildServicesSql(true)),
new("InstanceConfigurationCollector.BuildServicesSql(uden IFI)", "InstanceConfigurationCollector", "SQL Server 2008 R2 SP1 og nyere, builds uden kolonnen instant_file_initialization_enabled",
C + "Instance/InstanceConfigurationCollector.cs", "InstanceConfigurationCollector.BuildServicesSql(false)", () => InstanceConfigurationCollector.BuildServicesSql(false)),
new("InstanceConfigurationCollector.MemoryModelSql", "InstanceConfigurationCollector", "Builds med kolonnen sql_memory_model_desc (se ColumnsSql)",
C + "Instance/InstanceConfigurationCollector.cs", "InstanceConfigurationCollector.MemoryModelSql", () => InstanceConfigurationCollector.MemoryModelSql),
// Privileges.
new("PrivilegeCollector.MembersSql", "PrivilegeCollector", "SQL Server 2005 og nyere",
C + "Instance/PrivilegeCollector.cs", "PrivilegeCollector.MembersSql", () => PrivilegeCollector.MembersSql),
new("PrivilegeCollector.SqlLoginsSql", "PrivilegeCollector", "SQL Server 2005 og nyere",
C + "Instance/PrivilegeCollector.cs", "PrivilegeCollector.SqlLoginsSql", () => PrivilegeCollector.SqlLoginsSql),
new("PrivilegeCollector.DefaultTraceEnabledSql", "PrivilegeCollector", "SQL Server 2005 og nyere",
C + "Instance/PrivilegeCollector.cs", "PrivilegeCollector.DefaultTraceEnabledSql", () => PrivilegeCollector.DefaultTraceEnabledSql),
new("PrivilegeCollector.DefaultTracePathSql", "PrivilegeCollector", "SQL Server 2005 og nyere, kun når der er fundet ændringer (kræver ALTER TRACE)",
C + "Instance/PrivilegeCollector.cs", "PrivilegeCollector.DefaultTracePathSql", () => PrivilegeCollector.DefaultTracePathSql),
new("PrivilegeCollector.BuildTraceSql", "PrivilegeCollector", "SQL Server 2005 og nyere, kun når der er fundet ændringer (kræver ALTER TRACE)",
C + "Instance/PrivilegeCollector.cs", "PrivilegeCollector.BuildTraceSql(traceFileBase, sinceLocal)",
() => Tokens(PrivilegeCollector.BuildTraceSql("§fil§", "§siden§"), "fil", "siden")),
// Databases.
new("DatabaseCollector.BuildSql2005(2016 SP2+)", "DatabaseCollector", "SQL Server 2016 SP2 og nyere (med LastGoodCheckDbTime og availability groups)",
C + "Databases/DatabaseCollector.cs", "DatabaseCollector.BuildSql2005(instance, capabilities)", () => DatabaseSql2005(V2016Sp2)),
new("DatabaseCollector.BuildSql2005(2012)", "DatabaseCollector", "SQL Server 2012 – 2016 SP1",
C + "Databases/DatabaseCollector.cs", "DatabaseCollector.BuildSql2005(instance, capabilities)", () => DatabaseSql2005(V2012)),
new("DatabaseCollector.BuildSql2005(2005)", "DatabaseCollector", "SQL Server 2005 – 2008 R2",
C + "Databases/DatabaseCollector.cs", "DatabaseCollector.BuildSql2005(instance, capabilities)", () => DatabaseSql2005(V2008R2Sp1)),
new("DatabaseCollector.BuildSql2000", "DatabaseCollector", "SQL Server 2000 (legacy ODBC-sti)",
C + "Databases/DatabaseCollector.cs", "DatabaseCollector.BuildSql2000()", DatabaseCollector.BuildSql2000),
new("DatabaseCollector.Files2005", "DatabaseCollector", "SQL Server 2005 og nyere",
C + "Databases/DatabaseCollector.cs", "DatabaseCollector.Files2005", () => DatabaseCollector.Files2005),
new("DatabaseCollector.Files2000", "DatabaseCollector", "SQL Server 2000 (legacy ODBC-sti)",
C + "Databases/DatabaseCollector.cs", "DatabaseCollector.Files2000", () => DatabaseCollector.Files2000),
new("Db.UtcOffsetSql", "DatabaseCollector", "Alle versioner: serverens afstand til UTC (msdb-tider er lokal tid)",
C + "Db.cs", "Db.UtcOffsetSql", () => Db.UtcOffsetSql),
new("DatabaseCollector.VlfSql", "DatabaseCollector", "SQL Server 2016 SP2 og nyere (sys.dm_db_log_info)",
C + "Databases/DatabaseCollector.cs", "DatabaseCollector.VlfSql", () => DatabaseCollector.VlfSql),
new("DatabaseCollector.BuildQueryStoreSql", "DatabaseCollector", "SQL Server 2016 og nyere: én gang pr. online brugerdatabase (vist for " + ExampleDatabase + ")",
C + "Databases/DatabaseCollector.cs", "DatabaseCollector.BuildQueryStoreSql(database)", () => DatabaseCollector.BuildQueryStoreSql(ExampleDatabase)),
new("SuspectPageCollector.Sql", "SuspectPageCollector", "SQL Server 2005 og nyere",
C + "Instance/SuspectPageCollector.cs", "SuspectPageCollector.Sql", () => SuspectPageCollector.Sql),
// Plan cache, waits, I/O.
new("PlanCacheCollector.Sql", "PlanCacheCollector", "SQL Server 2005 og nyere",
C + "Instance/PlanCacheCollector.cs", "PlanCacheCollector.Sql", () => PlanCacheCollector.Sql),
StartTime("WaitStatsCollector", V2008, "SQL Server 2008 og nyere: starttidspunkt (nulstilling af kumulative tal)"),
StartTime("WaitStatsCollector", V2005, "SQL Server 2005: starttidspunkt"),
StartTime("WaitStatsCollector", V2000, "SQL Server 2000 (legacy ODBC-sti): starttidspunkt"),
new("WaitStatsCollector.Sql2005", "WaitStatsCollector", "SQL Server 2005 og nyere",
C + "Performance/WaitStatsCollector.cs", "WaitStatsCollector.Sql2005", () => WaitStatsCollector.Sql2005),
new("WaitStatsCollector.Sql2000", "WaitStatsCollector", "SQL Server 2000 (legacy ODBC-sti)",
C + "Performance/WaitStatsCollector.cs", "WaitStatsCollector.Sql2000", () => WaitStatsCollector.Sql2000),
StartTime("FileIoCollector", V2008, "SQL Server 2008 og nyere: starttidspunkt"),
StartTime("FileIoCollector", V2005, "SQL Server 2005: starttidspunkt"),
new("FileIoCollector.SqlWithVolumes", "FileIoCollector", "SQL Server 2008 R2 SP1 og nyere (med drev/mount point)",
C + "Performance/FileIoCollector.cs", "FileIoCollector.SqlWithVolumes", () => FileIoCollector.SqlWithVolumes),
new("FileIoCollector.Sql", "FileIoCollector", "SQL Server 2005 – 2008 R2 RTM",
C + "Performance/FileIoCollector.cs", "FileIoCollector.Sql", () => FileIoCollector.Sql),
// Top SQL and sampling.
StartTime("QueryStatsCollector", V2008, "SQL Server 2008 og nyere: starttidspunkt"),
StartTime("QueryStatsCollector", V2005, "SQL Server 2005: starttidspunkt"),
new("QueryStatsCollector.SqlFor(2008+)", "QueryStatsCollector", "SQL Server 2008 og nyere (første kørsel: de seneste 15 minutter)",
C + "Performance/QueryStatsCollector.cs", "QueryStatsCollector.Sql2008 via SqlFor(version, since: null)", () => QueryStatsCollector.SqlFor(V2008, null)),
new("QueryStatsCollector.SqlFor(2005)", "QueryStatsCollector", "SQL Server 2005 (første kørsel: de seneste 15 minutter)",
C + "Performance/QueryStatsCollector.cs", "QueryStatsCollector.Sql2005 via SqlFor(version, since: null)", () => QueryStatsCollector.SqlFor(V2005, null)),
new("QueryStatsCollector.ServerTimeSql", "QueryStatsCollector", "Alle versioner, kun når ingen sætning har kørt siden sidst",
C + "Performance/QueryStatsCollector.cs", "QueryStatsCollector.ServerTimeSql", () => QueryStatsCollector.ServerTimeSql),
new("WaitSamplingCollector.Sql2008", "WaitSamplingCollector", "SQL Server 2008 og nyere",
C + "Performance/WaitSamplingCollector.cs", "WaitSamplingCollector.Sql2008", () => WaitSamplingCollector.Sql2008),
new("WaitSamplingCollector.Sql2005", "WaitSamplingCollector", "SQL Server 2005",
C + "Performance/WaitSamplingCollector.cs", "WaitSamplingCollector.Sql2005", () => WaitSamplingCollector.Sql2005),
new("LongOperationCollector.Sql", "LongOperationCollector", "SQL Server 2005 og nyere",
C + "Instance/LongOperationCollector.cs", "LongOperationCollector.Sql", () => LongOperationCollector.Sql),
// Error log.
new("ErrorLogCollector.CanReadErrorLogSql", "ErrorLogCollector", "SQL Server 2005 og nyere",
C + "Instance/ErrorLogCollector.cs", "ErrorLogCollector.CanReadErrorLogSql", () => ErrorLogCollector.CanReadErrorLogSql),
new("ErrorLogCollector.BuildErrorLogSql", "ErrorLogCollector", "SQL Server 2005 og nyere, kilde 1: fejlloggen (når agenten er securityadmin)",
C + "Instance/ErrorLogCollector.cs", "ErrorLogCollector.BuildErrorLogSql(fromLocal, toLocal)",
() => Tokens(ErrorLogCollector.BuildErrorLogSql("§fra§", "§til§"), "fra", "til")),
new("ErrorLogCollector.DefaultTracePathSql", "ErrorLogCollector", "SQL Server 2005 og nyere, kilde 2: default trace (når agenten har ALTER TRACE)",
C + "Instance/PrivilegeCollector.cs", "PrivilegeCollector.DefaultTracePathSql", () => PrivilegeCollector.DefaultTracePathSql),
new("ErrorLogCollector.BuildDefaultTraceSql", "ErrorLogCollector", "SQL Server 2005 og nyere, kilde 2: default trace (når agenten har ALTER TRACE)",
C + "Instance/ErrorLogCollector.cs", "ErrorLogCollector.BuildDefaultTraceSql(files, sinceLocal)",
() => Tokens(ErrorLogCollector.BuildDefaultTraceSql("§filer§", "§siden§"), "filer", "siden")),
new("ErrorLogCollector.RingBufferSql", "ErrorLogCollector", "SQL Server 2005 og nyere, kilde 3: exception ring buffer (kun VIEW SERVER STATE)",
C + "Instance/ErrorLogCollector.cs", "ErrorLogCollector.RingBufferSql", () => ErrorLogCollector.RingBufferSql),
new("ErrorLogCollector.ConnectivitySql", "ErrorLogCollector", "SQL Server 2008 og nyere, kilde 3: mislykkede logins fra connectivity ring buffer",
C + "Instance/ErrorLogCollector.cs", "ErrorLogCollector.ConnectivitySql", () => ErrorLogCollector.ConnectivitySql),
// tempdb.
new("TempdbCollector.SpaceSql", "TempdbCollector", "SQL Server 2005 og nyere",
C + "Instance/TempdbCollector.cs", "TempdbCollector.SpaceSql", () => TempdbCollector.SpaceSql),
new("TempdbCollector.SessionsSql", "TempdbCollector", "SQL Server 2005 og nyere",
C + "Instance/TempdbCollector.cs", "TempdbCollector.SessionsSql", () => TempdbCollector.SessionsSql),
new("TempdbCollector.OldestSnapshotSql", "TempdbCollector", "SQL Server 2005 og nyere",
C + "Instance/TempdbCollector.cs", "TempdbCollector.OldestSnapshotSql", () => TempdbCollector.OldestSnapshotSql),
new("TempdbCollector.GeneratorsSql", "TempdbCollector", "SQL Server 2005 og nyere, kun når version store er over halvdelen af grænsen, højst én gang i timen",
C + "Instance/TempdbCollector.cs", "TempdbCollector.GeneratorsSql", () => TempdbCollector.GeneratorsSql),
// Replication.
new("ReplicationCollector.DistributionDatabaseSql", "ReplicationCollector", "SQL Server 2005 og nyere",
C + "Instance/ReplicationCollector.cs", "ReplicationCollector.DistributionDatabaseSql", () => ReplicationCollector.DistributionDatabaseSql),
new("ReplicationCollector.CurrentDatabaseSql", "ReplicationCollector", "Kun på en distributør: husk databasen, der skal skiftes tilbage til",
C + "Db.cs", "Db.CurrentDatabaseSql", () => Db.CurrentDatabaseSql),
new("ReplicationCollector.AgentsSql", "ReplicationCollector", "Kun på en distributør, i distributionsdatabasen",
C + "Instance/ReplicationCollector.cs", "ReplicationCollector.AgentsSql", () => ReplicationCollector.AgentsSql),
new("ReplicationCollector.ErrorsSql", "ReplicationCollector", "Kun på en distributør, i distributionsdatabasen",
C + "Instance/ReplicationCollector.cs", "ReplicationCollector.ErrorsSql", () => ReplicationCollector.ErrorsSql),
// Counters and CPU.
StartTime("PerformanceCounterCollector", V2008, "SQL Server 2008 og nyere: starttidspunkt"),
StartTime("PerformanceCounterCollector", V2005, "SQL Server 2005: starttidspunkt"),
StartTime("PerformanceCounterCollector", V2000, "SQL Server 2000 (legacy ODBC-sti): starttidspunkt"),
new("PerformanceCounterCollector.BuildSql(dmv)", "PerformanceCounterCollector", "SQL Server 2005 og nyere",
C + "Performance/PerformanceCounterCollector.cs", "PerformanceCounterCollector.BuildSql(dmv: true)", () => PerformanceCounterCollector.BuildSql(true)),
new("PerformanceCounterCollector.BuildSql(sysperfinfo)", "PerformanceCounterCollector", "SQL Server 2000 (legacy ODBC-sti)",
C + "Performance/PerformanceCounterCollector.cs", "PerformanceCounterCollector.BuildSql(dmv: false)", () => PerformanceCounterCollector.BuildSql(false)),
new("CpuCollector.Sql", "CpuCollector", "SQL Server 2005 og nyere",
C + "Performance/CpuCollector.cs", "CpuCollector.Sql", () => CpuCollector.Sql),
new("CpuCollector.HostPlatformSql", "CpuCollector", "SQL Server 2017 og nyere (Windows eller Linux)",
C + "Performance/CpuCollector.cs", "CpuCollector.HostPlatformSql", () => CpuCollector.HostPlatformSql),
// Concurrency.
new("BlockingCollector.BuildSql2005(2012+)", "BlockingCollector", "SQL Server 2012 og nyere",
C + "Concurrency/BlockingCollector.cs", "BlockingCollector.BuildSql2005(version)", () => BlockingCollector.BuildSql2005(V2012)),
new("BlockingCollector.BuildSql2005(2008)", "BlockingCollector", "SQL Server 2008 – 2008 R2",
C + "Concurrency/BlockingCollector.cs", "BlockingCollector.BuildSql2005(version)", () => BlockingCollector.BuildSql2005(V2008R2Sp1)),
new("BlockingCollector.BuildSql2005(2005)", "BlockingCollector", "SQL Server 2005 (CONTEXT_INFO fra sys.sysprocesses)",
C + "Concurrency/BlockingCollector.cs", "BlockingCollector.BuildSql2005(version)", () => BlockingCollector.BuildSql2005(V2005)),
new("BlockingCollector.Sql2000", "BlockingCollector", "SQL Server 2000 (legacy ODBC-sti)",
C + "Concurrency/BlockingCollector.cs", "BlockingCollector.Sql2000", () => BlockingCollector.Sql2000),
new("ActivityCollector.BuildTransactionsSql", "ActivityCollector", "SQL Server 2005 og nyere (vist med standardgrænsen 1024 MB log)",
C + "Concurrency/ActivityCollector.cs", "ActivityCollector.BuildTransactionsSql(1024) = TransactionsSql udfyldt",
() => ActivityCollector.BuildTransactionsSql(1024)),
new("ActivityCollector.BuildLongRequestsSql", "ActivityCollector", "SQL Server 2005 og nyere (vist med standardgrænsen 15 minutter)",
C + "Concurrency/ActivityCollector.cs", "ActivityCollector.BuildLongRequestsSql(15) = LongRequestsSql udfyldt",
() => ActivityCollector.BuildLongRequestsSql(15)),
new("DeadlockCollector.BuildFileSql", "DeadlockCollector", "SQL Server 2012 og nyere (system_health-filer; første læsning)",
C + "Concurrency/DeadlockCollector.cs", "DeadlockCollector.BuildFileSql(lastFile: null, lastOffset: null)", () => DeadlockCollector.BuildFileSql(null, null)),
new("DeadlockCollector.RingBufferSql", "DeadlockCollector", "SQL Server 2008 – 2008 R2, og når event-filerne ikke kan læses",
C + "Concurrency/DeadlockCollector.cs", "DeadlockCollector.RingBufferSql", () => DeadlockCollector.RingBufferSql),
// Query Store and plans.
new("QueryStoreCollector.DatabasesSql", "QueryStoreCollector", "SQL Server 2016 og nyere",
C + "Performance/QueryStoreCollector.cs", "QueryStoreCollector.DatabasesSql", () => QueryStoreCollector.DatabasesSql),
new("QueryStoreCollector.BuildStatsSql", "QueryStoreCollector", "SQL Server 2016 og nyere: én gang pr. database med Query Store (vist for " + ExampleDatabase + ")",
C + "Performance/QueryStoreCollector.cs", "QueryStoreCollector.BuildStatsSql(database)", () => QueryStoreCollector.BuildStatsSql(ExampleDatabase)),
new("QueryStoreCollector.BuildFailedForcedSql", "QueryStoreCollector", "SQL Server 2016 og nyere: én gang pr. database med Query Store (vist for " + ExampleDatabase + ")",
C + "Performance/QueryStoreCollector.cs", "QueryStoreCollector.BuildFailedForcedSql(database)", () => QueryStoreCollector.BuildFailedForcedSql(ExampleDatabase)),
new("PlanAnalysisCollector.BuildSql(2016 SP2+)", "PlanAnalysisCollector", "SQL Server 2016 SP2 / 2017 CU3 og nyere (med spill- og memory grant-kolonner)",
C + "Performance/PlanAnalysisCollector.cs", "PlanAnalysisCollector.BuildSql(memoryColumns: true, queryHash: true)", () => PlanAnalysisCollector.BuildSql(true, true)),
new("PlanAnalysisCollector.BuildSql(2008)", "PlanAnalysisCollector", "SQL Server 2008 – 2016 SP1 og 2017 før CU3",
C + "Performance/PlanAnalysisCollector.cs", "PlanAnalysisCollector.BuildSql(memoryColumns: false, queryHash: true)", () => PlanAnalysisCollector.BuildSql(false, true)),
new("PlanAnalysisCollector.BuildSql(2005)", "PlanAnalysisCollector", "SQL Server 2005 SP2 og nyere 2005-builds",
C + "Performance/PlanAnalysisCollector.cs", "PlanAnalysisCollector.BuildSql(memoryColumns: false, queryHash: false)", () => PlanAnalysisCollector.BuildSql(false, false)),
// Indexes and statistics.
StartTime("IndexCollector", V2008, "SQL Server 2008 og nyere: starttidspunkt"),
StartTime("IndexCollector", V2005, "SQL Server 2005: starttidspunkt"),
new("IndexCollector.DatabasesSql", "IndexCollector", "SQL Server 2005 og nyere",
C + "Databases/IndexCollector.cs", "IndexCollector.DatabasesSql", () => IndexCollector.DatabasesSql),
new("IndexCollector.MissingSql", "IndexCollector", "SQL Server 2005 og nyere",
C + "Databases/IndexCollector.cs", "IndexCollector.MissingSql", () => IndexCollector.MissingSql),
new("IndexCollector.BuildIndexSql(2008+)", "IndexCollector", "SQL Server 2008 og nyere: én gang pr. database (vist for " + ExampleDatabase + ")",
C + "Databases/IndexCollector.cs", "IndexCollector.BuildIndexSql(database, filteredIndexes: true)", () => IndexCollector.BuildIndexSql(ExampleDatabase, true)),
new("IndexCollector.BuildIndexSql(2005)", "IndexCollector", "SQL Server 2005: én gang pr. database (vist for " + ExampleDatabase + ")",
C + "Databases/IndexCollector.cs", "IndexCollector.BuildIndexSql(database, filteredIndexes: false)", () => IndexCollector.BuildIndexSql(ExampleDatabase, false)),
new("IndexCollector.BuildStatisticsSql", "IndexCollector", "SQL Server 2005 og nyere: én gang pr. database (vist for " + ExampleDatabase + ")",
C + "Databases/IndexCollector.cs", "IndexCollector.BuildStatisticsSql(database)", () => IndexCollector.BuildStatisticsSql(ExampleDatabase)),
// Availability and jobs.
new("AvailabilityGroupCollector.ReplicasSql", "AvailabilityGroupCollector", "SQL Server 2012 og nyere med Always On slået til",
C + "Instance/AvailabilityGroupCollector.cs", "AvailabilityGroupCollector.ReplicasSql", () => AvailabilityGroupCollector.ReplicasSql),
new("AvailabilityGroupCollector.DatabasesSql", "AvailabilityGroupCollector", "SQL Server 2012 og nyere med Always On slået til",
C + "Instance/AvailabilityGroupCollector.cs", "AvailabilityGroupCollector.DatabasesSql", () => AvailabilityGroupCollector.DatabasesSql),
new("AvailabilityGroupCollector.ClusterMembersSql", "AvailabilityGroupCollector", "SQL Server 2012 og nyere med Always On slået til (tom uden Windows-cluster)",
C + "Instance/AvailabilityGroupCollector.cs", "AvailabilityGroupCollector.ClusterMembersSql", () => AvailabilityGroupCollector.ClusterMembersSql),
new("AgentJobCollector.RunningSql", "AgentJobCollector", "SQL Server 2005 og nyere",
C + "Instance/AgentJobCollector.cs", "AgentJobCollector.RunningSql", () => AgentJobCollector.RunningSql),
new("AgentJobCollector.JobsSql", "AgentJobCollector", "SQL Server 2005 og nyere",
C + "Instance/AgentJobCollector.cs", "AgentJobCollector.JobsSql", () => AgentJobCollector.JobsSql),
// Community and local rules.
new("CustomQueryCollector.CurrentDatabaseSql", "CustomQueryCollector", "Husk databasen, der skal skiftes tilbage til",
C + "Db.cs", "Db.CurrentDatabaseSql", () => Db.CurrentDatabaseSql),
new("CustomQueryCollector.DatabasesSql", "CustomQueryCollector", "Databaserne, database-regler køres i",
C + "Custom/CustomQueryCollector.cs", "CustomQueryCollector.DatabasesSql", () => CustomQueryCollector.DatabasesSql),
new("CustomQueryCollector.BuildRuleSql", "CustomQueryCollector", "Én regel: skift database (master for server-regler, ellers hver brugerdatabase) og kør reglens forespørgsel",
C + "Custom/CustomQueryCollector.cs", "CustomQueryCollector.BuildRuleSql(database, query)",
() => Tokens(CustomQueryCollector.BuildRuleSql("§database§", "§forespoergsel§"), "database", "forespoergsel")),
// Servere og databaser (1.3): database discovery and the permission check, run by the agent when asked in
// SQLInsight Web (and the discovery hourly). The collectors above then skip databases not chosen (in C#; their SQL is unchanged).
new("DatabaseDiscovery.Databases2005Sql", "DatabaseDiscovery", "SQL Server 2005 og nyere",
C + "Discovery/DatabaseDiscovery.cs", "DatabaseDiscovery.Databases2005Sql", () => DatabaseDiscovery.Databases2005Sql),
new("DatabaseDiscovery.Databases2000Sql", "DatabaseDiscovery", "SQL Server 2000 (legacy ODBC-sti)",
C + "Discovery/DatabaseDiscovery.cs", "DatabaseDiscovery.Databases2000Sql", () => DatabaseDiscovery.Databases2000Sql),
new("DatabaseDiscovery.DynamicsAxSql(2005)", "DatabaseDiscovery", "SQL Server 2005 og nyere: én gang pr. online brugerdatabase, agenten kan åbne (vist for " + ExampleDatabase + ")",
C + "Discovery/DatabaseDiscovery.cs", "DatabaseDiscovery.DynamicsAxSql(database, version)", () => DatabaseDiscovery.DynamicsAxSql(ExampleDatabase, V2005)),
new("DatabaseDiscovery.DynamicsAxSql(2000)", "DatabaseDiscovery", "SQL Server 2000 (legacy ODBC-sti): én gang pr. online brugerdatabase (vist for " + ExampleDatabase + ")",
C + "Discovery/DatabaseDiscovery.cs", "DatabaseDiscovery.DynamicsAxSql(database, version)", () => DatabaseDiscovery.DynamicsAxSql(ExampleDatabase, V2000)),
new("PermissionCheck.WhoSql", "PermissionCheck", "Alle versioner (SQL Server 2000 og nyere)",
C + "Discovery/PermissionCheck.cs", "PermissionCheck.WhoSql", () => PermissionCheck.WhoSql),
new("PermissionCheck.BuildSql(2022+)", "PermissionCheck", "SQL Server 2022 og nyere",
C + "Discovery/PermissionCheck.cs", "PermissionCheck.BuildSql(checks)", () => PermissionCheck.BuildSql(PermissionCheck.ChecksFor(Instance(V2022)))),
new("PermissionCheck.BuildSql(2014)", "PermissionCheck", "SQL Server 2014 – 2019 og Azure SQL Managed Instance",
C + "Discovery/PermissionCheck.cs", "PermissionCheck.BuildSql(checks)", () => PermissionCheck.BuildSql(PermissionCheck.ChecksFor(Instance(V2014)))),
new("PermissionCheck.BuildSql(2005)", "PermissionCheck", "SQL Server 2005 – 2012",
C + "Discovery/PermissionCheck.cs", "PermissionCheck.BuildSql(checks)", () => PermissionCheck.BuildSql(PermissionCheck.ChecksFor(Instance(V2005)))),
new("PermissionCheck.DatabasesWithoutAccessSql", "PermissionCheck", "SQL Server 2005 – 2012 (før CONNECT ANY DATABASE)",
C + "Discovery/PermissionCheck.cs", "PermissionCheck.DatabasesWithoutAccessSql", () => PermissionCheck.DatabasesWithoutAccessSql),
};
sources.AddRange(DynamicsAx());
sources.AddRange(SyntheticCheck());
return sources;
}
/// <summary>
/// The synthetic SQL check (1.5): the agent logs on as the check says, sets the session settings above, and runs the
/// check's query in a transaction that is always rolled back. Not a collector; registered so SQLI-SYN-001/002 show it.
/// </summary>
private static IEnumerable<Source> SyntheticCheck()
{
const string File = "src/SQLInsight.Servers/Synthetic/SyntheticCheckRunner.cs";
yield return new("SyntheticCheckRunner.UseDatabaseSql", "SyntheticCheckRunner", "Kun når tjekket har en database (vist for " + ExampleDatabase + ")",
File, "SyntheticCheckRunner.UseDatabaseSql(database)", () => SyntheticCheckRunner.UseDatabaseSql(ExampleDatabase));
yield return new("SyntheticCheckRunner.BeginTransactionSql", "SyntheticCheckRunner", "Alle versioner fra SQL Server 2005: før forespørgslen",
File, "SyntheticCheckRunner.BeginTransactionSql", () => SyntheticCheckRunner.BeginTransactionSql);
yield return new("SyntheticCheckSettings.DefaultQuery", "SyntheticCheckRunner", "Standardforespørgslen, når tjekket ikke har sin egen",
"src/SQLInsight.Core/SyntheticChecks/SyntheticCheckDefinitions.cs", "SyntheticCheckSettings.DefaultQuery", () => SyntheticCheckSettings.DefaultQuery);
yield return new("SyntheticCheckRunner.RollbackSql", "SyntheticCheckRunner", "Alle versioner fra SQL Server 2005: altid efter forespørgslen, også når den fejler",
File, "SyntheticCheckRunner.RollbackSql", () => SyntheticCheckRunner.RollbackSql);
}
/// <summary>The AX database the Dynamics AX scripts are shown for: there is no AX database on every instance, so the reader changes it.</summary>
public const string AxExampleDatabase = "AXDB";
/// <summary>A RecId the history watermark is rendered with, replaced by the token ⟪fra⟫ (the agent uses the highest RecId it has read).</summary>
private const long AxWatermark = 1_000_000_000;
private const string Ax = "src/SQLInsight.Core/DynamicsAx/";
private static string AxDirectory => Path.Combine(AppContext.BaseDirectory, "DynamicsAx");
/// <summary>
/// The AX versions SQLInsight has a schema for (docs/dynamics-ax/*.tsv): Axapta 3.0 from a real customer database,
/// AX 4.0 as 3.0 with bigint RecId in dbo, AX 2009 and 2012 R2/R3 from Microsoft's documentation.
/// </summary>
private static IReadOnlyList<(string Key, string Title, Core.DynamicsAx.AxDatabase Database)> AxVersions()
{
static Core.DynamicsAx.AxDatabase Load(string file) =>
Core.DynamicsAx.AxDetection.Recognise(AxExampleDatabase, Core.DynamicsAx.AxSchemaFile.Find(AxDirectory, file)!.DetectionRows(), [AxExampleDatabase, AxExampleDatabase + "_model"])!;
var ax40 = Core.DynamicsAx.AxDetection.Recognise(AxExampleDatabase, Core.DynamicsAx.AxSchemaFile.Find(AxDirectory, "schema-ax30")!.DetectionRows()
.Select(r => r with { Schema = "dbo", Precision = r.Column == "RECID" ? 19 : r.Precision, Type = r.Column == "RECID" ? "bigint" : r.Type }).ToList(), [AxExampleDatabase])!;
return
[
("3.0", "Axapta 3.0 (skema bmssa, verificeret mod kundedata)", Load("schema-ax30")),
("4.0", "AX 4.0 (skema dbo) - ikke verificeret mod kundedata", ax40),
("2009", "AX 2009 - baseret på Microsofts dokumenterede skema, ikke verificeret mod kundedata", Load("schema-ax2009")),
("2012", "AX 2012 RTM/R2/R3 - baseret på Microsofts dokumenterede skema, ikke verificeret mod kundedata", Load("schema-ax2012")),
];
}
private static string AxRead(Core.DynamicsAx.AxDatabase database, Core.DynamicsAx.AxQuery? query) =>
Core.DynamicsAx.AxSql.InDatabase(database.Database, query?.Sql ?? throw new InvalidOperationException(query?.NotMeasurable ?? "Ingen forespørgsel."), database.Isolation);
private static IEnumerable<Source> DynamicsAx()
{
// The shared detection: which monitored databases are AX (collector dynamics-ax, and before every AX check).
yield return new("AxDatabaseDetector.DatabasesSql", "AxDatabaseDetector", "Alle versioner fra SQL Server 2005: online brugerdatabaser og deres snapshot-indstillinger",
C + "DynamicsAx/AxDatabaseDetector.cs", "AxDatabaseDetector.DatabasesSql", () => Collectors.DynamicsAx.AxDatabaseDetector.DatabasesSql);
yield return new("AxDatabaseDetector.LoginSql", "AxDatabaseDetector", "Agentens login, til GRANT-teksten, når AX-læseadgangen mangler",
C + "DynamicsAx/AxDatabaseDetector.cs", "AxDatabaseDetector.LoginSql", () => Collectors.DynamicsAx.AxDatabaseDetector.LoginSql);
yield return new("AxDatabaseDetector.BuildDetectionSql", "AxDatabaseDetector",
"SQL Server 2005 og nyere: AX-genkendelsen i hver overvåget database (kun metadata; vist for " + AxExampleDatabase + ")",
C + "DynamicsAx/AxDatabaseDetector.cs", "AxDatabaseDetector.BuildDetectionSql(database) = AxSql.InDatabase(database, AxDetection.MetadataSql)",
() => Collectors.DynamicsAx.AxDatabaseDetector.BuildDetectionSql(AxExampleDatabase));
foreach (var (key, title, database) in AxVersions())
{
yield return new($"AxSchemaAdapter.Companies({key})", "AxDatabaseDetector", title + ": firmaerne (DATAAREA), kun med AX-læseadgang",
Ax + "AxSchemaAdapter.cs", "AxSchemaAdapter.Companies(database) via AxSql.InDatabase", () => AxRead(database, Core.DynamicsAx.AxSchemaAdapter.Companies(database)));
}
// Batch jobs (collector dynamics-ax-batch).
foreach (var (key, title, database) in AxVersions())
{
var adapter = Core.DynamicsAx.AxSchemaAdapter.For(database.Version)!;
yield return new($"AxSchemaAdapter.BatchJobs({key})", "AxBatchCollector", title + ": batchjob",
Ax + "AxSchemaAdapter.cs", "AxSchemaAdapter.BatchJobs(database) via AxSql.InDatabase", () => AxRead(database, adapter.BatchJobs(database)));
if (adapter.BatchHistory(database, null) is not null)
{
yield return new($"AxSchemaAdapter.BatchHistory({key}, første)", "AxBatchCollector", title + ": kørselshistorik, første læsning (de nyeste)",
Ax + "AxSchemaAdapter.cs", "AxSchemaAdapter.BatchHistory(database, afterRecId: null) via AxSql.InDatabase", () => AxRead(database, adapter.BatchHistory(database, null)));
yield return new($"AxSchemaAdapter.BatchHistory({key}, næste)", "AxBatchCollector",
title + $": kørselshistorik, de næste læsninger (fra den højeste RECID, SQLInsight har læst, minus {Core.DynamicsAx.AxSchemaAdapter.HistoryOverlap})",
Ax + "AxSchemaAdapter.cs", "AxSchemaAdapter.BatchHistory(database, afterRecId) via AxSql.InDatabase",
() => AxRead(database, adapter.BatchHistory(database, AxWatermark + Core.DynamicsAx.AxSchemaAdapter.HistoryOverlap))
.Replace("> " + AxWatermark.ToString(System.Globalization.CultureInfo.InvariantCulture), "> ⟪fra⟫", StringComparison.Ordinal));
}
}
// Number sequences and RecId (collector dynamics-ax-sequences).
foreach (var (key, title, database) in AxVersions())
{
var adapter = Core.DynamicsAx.AxSchemaAdapter.For(database.Version)!;
yield return new($"AxSchemaAdapter.NumberSequences({key})", "AxSequenceCollector", title + ": nummerserier",
Ax + "AxSchemaAdapter.cs", "AxSchemaAdapter.NumberSequences(database) via AxSql.InDatabase", () => AxRead(database, adapter.NumberSequences(database)));
yield return new($"AxSchemaAdapter.RecIds({key})", "AxSequenceCollector", title + ": RecId pr. tabel (SYSTEMSEQUENCES)",
Ax + "AxSchemaAdapter.cs", "AxSchemaAdapter.RecIds(database) via AxSql.InDatabase", () => AxRead(database, adapter.RecIds(database)));
}
}
private static Source StartTime(string collector, SqlServerVersion version, string variant) => new(
$"{collector}.StartTimeSql({(version.Major >= 10 ? "2008+" : version.Major == 9 ? "2005" : "2000")})",
collector, variant, C + "Db.cs", "Db.StartTimeSql(version)", () => Db.StartTimeSql(version));
}