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; 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()); return sources; } /// 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)); }