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;
///
/// 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 -- Kilde: <key> 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.
///
internal static class SqlScriptSources
{
/// The database per-database queries are shown for: it exists on every instance, so the scripts run as they are.
public const string ExampleDatabase = "msdb";
/// What the script's -- Kilde: line says.
/// The collector (or InstanceProbe/connection) that sends it.
/// The SQL Server versions or situation the text is sent for.
/// The source file, relative to the repository root.
/// The constant or method holding the text.
public sealed record Source(string Key, string Collector, string Variant, string File, string Member, Func 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 All { get; } = Create();
public static IReadOnlyDictionary ByKey { get; } = All.ToDictionary(s => s.Key, StringComparer.Ordinal);
public static IEnumerable For(string collector) => All.Where(s => s.Collector == collector);
/// LF line endings: the sources and the scripts may be checked out with CRLF.
public static string Normalize(string sql) => sql.Replace("\r\n", "\n", StringComparison.Ordinal).Replace('\r', '\n');
/// A regex that finds in a script, with any text where it has tokens.
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 Create()
{
var probe = typeof(Connectivity.InstanceProbe);
var factory = typeof(Connectivity.SqlConnectionFactory);
var sources = new List
{
// 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;
}
///
/// 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.
///
private static IEnumerable 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);
}
/// The AX database the Dynamics AX scripts are shown for: there is no AX database on every instance, so the reader changes it.
public const string AxExampleDatabase = "AXDB";
/// A RecId the history watermark is rendered with, replaced by the token ⟪fra⟫ (the agent uses the highest RecId it has read).
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");
///
/// 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.
///
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 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));
}