diff --git a/SQL Scripts/README.md b/SQL Scripts/README.md index 170ccc0..1765e6f 100644 --- a/SQL Scripts/README.md +++ b/SQL Scripts/README.md @@ -14,6 +14,7 @@ identisk med koden (se [Hold mappen i sync](#hold-mappen-i-sync)). | `.sql` | Én fil pr. regel, navngivet med regel-id'et præcis som i systemet: `SQLI-…` for de indbyggede regler (C#), `COM-…` for community-regler (JSON i `rules/community/`). | | `_collectors/.sql` | Alle forespørgsler fra én collector. Flere regler deler ofte samme collector; regelfilerne gentager SQL'en, så hver fil kan køres for sig. | | `_collectors/InstanceProbe.sql` | Registreringen af instansen (version, udgave, Always On) og de sessionsindstillinger, agenten sætter på hver forbindelse. | +| `_collectors/DatabaseDiscovery.sql`, `_collectors/PermissionCheck.sql` | Søgningen efter databaser (med Dynamics AX-genkendelse) og forbindelsestesten med rettighedstjek under *Servere og databaser* (fra 1.3). De køres kun, når der bedes om dem i webbrugerfladen, og søgningen desuden hver time. | ## Opbygning af en regelfil diff --git a/SQL Scripts/_collectors/CustomQueryCollector.sql b/SQL Scripts/_collectors/CustomQueryCollector.sql index dd8961f..083aa66 100644 --- a/SQL Scripts/_collectors/CustomQueryCollector.sql +++ b/SQL Scripts/_collectors/CustomQueryCollector.sql @@ -4,6 +4,8 @@ -- Hvad: Community-reglernes (rules/community/*.json) og lokale reglers forespørgsler: én gang i master -- (server-regler) eller i hver læsbar brugerdatabase (database-regler). -- Fil: src/SQLInsight.Collectors/Custom/CustomQueryCollector.cs +-- Databaser: Fra 1.3 kun de databaser, der er valgt under Servere og databaser (systemdatabaserne altid; se +-- _collectors/DatabaseDiscovery.sql). Fravalgte databaser sorteres fra i koden efter forespørgslen; SQL'en er uændret. -- Interval: 15 min som standard (SQLInsight:CollectorIntervals:custom-rules) -- Versioner: SQL Server 2005 og nyere (pr. regel: minimumVersion) -- diff --git a/SQL Scripts/_collectors/DatabaseCollector.sql b/SQL Scripts/_collectors/DatabaseCollector.sql index 19c3d86..f6981da 100644 --- a/SQL Scripts/_collectors/DatabaseCollector.sql +++ b/SQL Scripts/_collectors/DatabaseCollector.sql @@ -4,6 +4,8 @@ -- Hvad: Databaseindstillinger og -tilstand, backup-historik (msdb), seneste fejlfri CHECKDB, VLF'er, Query -- Store-tilstand og filer. -- Fil: src/SQLInsight.Collectors/Databases/DatabaseCollector.cs +-- Databaser: Fra 1.3 kun de databaser, der er valgt under Servere og databaser (systemdatabaserne altid; se +-- _collectors/DatabaseDiscovery.sql). Fravalgte databaser sorteres fra i koden efter forespørgslen; SQL'en er uændret. -- Interval: 15 min som standard (SQLInsight:CollectorIntervals:databases) -- Versioner: Alle (SQL Server 2000 og nyere); CHECKDB og VLF'er fra 2016 SP2; Query Store fra 2016 -- diff --git a/SQL Scripts/_collectors/DatabaseDiscovery.sql b/SQL Scripts/_collectors/DatabaseDiscovery.sql new file mode 100644 index 0000000..36d7178 --- /dev/null +++ b/SQL Scripts/_collectors/DatabaseDiscovery.sql @@ -0,0 +1,71 @@ +-- =================================================================================================================== +-- Collector: DatabaseDiscovery (Servere og databaser, fra 1.3) +-- =================================================================================================================== +-- Hvad: Finder alle databaser på en instans med tilstand, størrelse, recovery model og compatibility level, om +-- agenten kan åbne dem, og om de er Dynamics AX-databaser (AX' systemtabeller, som AX-reglerne genkender). +-- Resultatet vises under Overvågning → Servere og databaser, hvor man vælger, hvilke databaser der overvåges. +-- Valget bruges af de databasespecifikke collectors (DatabaseCollector, IndexCollector, QueryStoreCollector, +-- CustomQueryCollector): de sorterer fravalgte databaser fra i koden; deres SQL er uændret. +-- Fil: src/SQLInsight.Collectors/Discovery/DatabaseDiscovery.cs +-- Interval: Når en server tilføjes, når der trykkes "Søg efter databaser nu", og ellers hver time pr. instans +-- (AgentRequestService.DiscoveryInterval) +-- Versioner: SQL Server 2000 (legacy ODBC-sti) og nyere +-- +-- Rettigheder til agentens login (se db/permissions/grant-least-privilege.sql; [agent] = agentens login): +-- GRANT VIEW ANY DEFINITION TO [agent]; -- metadata; giver IKKE adgang til data +-- GRANT CONNECT ANY DATABASE TO [agent]; -- SQL Server 2014+; giver IKKE adgang til data +-- Før 2014 i stedet, i hver brugerdatabase: CREATE USER [agent] FOR LOGIN [agent]; +-- Databaser, agenten ikke kan åbne (HAS_DBACCESS = 0), springes over i AX-genkendelsen. +-- +-- Bruges af: Ingen regel direkte. Styrer, hvilke databaser de databasespecifikke regler vurderes i, og hvilke +-- AX-databaser der må sende AX-fund (COM-AX-*) til SQLInsight Hub. +-- Read-only: Ja: kun SELECT på metadata, via tre-delte navne. Sessionen skifter aldrig database, og intet ændrer +-- data, indstillinger eller objekter. +-- Kørsel: Kør i SSMS som agentens login. Hver variant er sin egen batch (GO); en variant for en anden SQL Server-version +-- end din kan fejle - det er forventet. Agenten kører altid med sessionsindstillingerne i _collectors/InstanceProbe.sql. +-- =================================================================================================================== + +-- Variant: SQL Server 2005 og nyere: alle databaser +-- Kilde: DatabaseDiscovery.Databases2005Sql +-- Kode: src/SQLInsight.Collectors/Discovery/DatabaseDiscovery.cs : DatabaseDiscovery.Databases2005Sql +SELECT d.name, d.database_id, d.state_desc, d.recovery_model_desc, d.compatibility_level, d.is_read_only, + CASE WHEN d.source_database_id IS NULL THEN 0 ELSE 1 END, + HAS_DBACCESS(d.name), + (SELECT SUM(CAST(f.size AS bigint)) / 128 FROM sys.master_files f WHERE f.database_id = d.database_id) +FROM sys.databases d +ORDER BY d.name +GO + +-- Variant: SQL Server 2000 (legacy ODBC-sti): alle databaser +-- Kilde: DatabaseDiscovery.Databases2000Sql +-- Kode: src/SQLInsight.Collectors/Discovery/DatabaseDiscovery.cs : DatabaseDiscovery.Databases2000Sql +SELECT d.name, d.dbid, + CAST(DATABASEPROPERTYEX(d.name, 'Status') AS nvarchar(60)), + CAST(DATABASEPROPERTYEX(d.name, 'Recovery') AS nvarchar(60)), + d.cmptlevel, + CASE WHEN DATABASEPROPERTYEX(d.name, 'Updateability') = 'READ_ONLY' THEN 1 ELSE 0 END, + 0, + HAS_DBACCESS(d.name), + (SELECT SUM(CAST(f.size AS bigint)) / 128 FROM master.dbo.sysaltfiles f WHERE f.dbid = d.dbid) +FROM master.dbo.sysdatabases d +ORDER BY d.name +GO + +-- Variant: SQL Server 2005 og nyere: Dynamics AX-genkendelse, én gang pr. online brugerdatabase, agenten kan åbne (vist for msdb) +-- Kilde: DatabaseDiscovery.DynamicsAxSql(2005) +-- Kode: src/SQLInsight.Collectors/Discovery/DatabaseDiscovery.cs : DatabaseDiscovery.DynamicsAxSql(database, version) +SELECT COUNT(DISTINCT t.name), MAX(CASE WHEN t.name = N'SQLDICTIONARY' THEN c.precision END) +FROM [msdb].sys.tables t +LEFT JOIN [msdb].sys.columns c ON c.object_id = t.object_id AND c.name = N'RECID' +WHERE t.name IN (N'SQLDICTIONARY', N'DATAAREA', N'SYSCONFIG') +GO + +-- Variant: SQL Server 2000 (legacy ODBC-sti): Dynamics AX-genkendelse, én gang pr. online brugerdatabase (vist for msdb) +-- Kilde: DatabaseDiscovery.DynamicsAxSql(2000) +-- Kode: src/SQLInsight.Collectors/Discovery/DatabaseDiscovery.cs : DatabaseDiscovery.DynamicsAxSql(database, version) +SELECT COUNT(DISTINCT o.name), MAX(CASE WHEN o.name = N'SQLDICTIONARY' THEN c.xprec END) +FROM [msdb].dbo.sysobjects o +LEFT JOIN [msdb].dbo.syscolumns c ON c.id = o.id AND c.name = N'RECID' +WHERE o.xtype = 'U' AND o.name IN (N'SQLDICTIONARY', N'DATAAREA', N'SYSCONFIG') +GO + diff --git a/SQL Scripts/_collectors/IndexCollector.sql b/SQL Scripts/_collectors/IndexCollector.sql index ff60674..84bce60 100644 --- a/SQL Scripts/_collectors/IndexCollector.sql +++ b/SQL Scripts/_collectors/IndexCollector.sql @@ -4,6 +4,8 @@ -- Hvad: Index-inventar med brug, manglende index-forslag og statistikker for hver læsbar brugerdatabase. Kun -- metadata og DMV'er; ingen tabeldata, ingen scanning. -- Fil: src/SQLInsight.Collectors/Databases/IndexCollector.cs +-- Databaser: Fra 1.3 kun de databaser, der er valgt under Servere og databaser (systemdatabaserne altid; se +-- _collectors/DatabaseDiscovery.sql). Fravalgte databaser sorteres fra i koden efter forespørgslen; SQL'en er uændret. -- Interval: 6 timer som standard (SQLInsight:CollectorIntervals:indexes) -- Versioner: SQL Server 2005 og nyere -- diff --git a/SQL Scripts/_collectors/PermissionCheck.sql b/SQL Scripts/_collectors/PermissionCheck.sql new file mode 100644 index 0000000..b0706f3 --- /dev/null +++ b/SQL Scripts/_collectors/PermissionCheck.sql @@ -0,0 +1,103 @@ +-- =================================================================================================================== +-- Collector: PermissionCheck (forbindelsestest under Servere og databaser, fra 1.3) +-- =================================================================================================================== +-- Hvad: Forbindelsestesten: hvilket login agenten ankommer som, om det er sysadmin, og hvilke af rettighederne fra +-- db/permissions/grant-least-privilege.sql det har, som login'et selv ser dem (HAS_PERMS_BY_NAME). Før SQL +-- Server 2014 også de online brugerdatabaser, login'et ikke kan åbne. Resultatet vises med "Har", "Mangler" +-- eller "Valgfri, mangler", og GRANT-scriptet udfyldes med login'et. +-- Fil: src/SQLInsight.Collectors/Discovery/PermissionCheck.cs +-- Interval: Kun når der bedes om en forbindelsestest (en server tilføjes eller rettes, eller "Test forbindelsen") +-- Versioner: Login og sysadmin: SQL Server 2000 og nyere. Rettighederne: SQL Server 2005 og nyere (SQL Server 2000 har +-- ikke HAS_PERMS_BY_NAME; dér vises en note i stedet). +-- +-- Rettigheder til agentens login: ingen ud over at kunne logge ind. HAS_PERMS_BY_NAME og SUSER_SNAME svarer på +-- login'ets egne rettigheder. Det er netop de rettigheder, der tjekkes for: +-- GRANT VIEW SERVER STATE TO [agent]; -- før 2022 (fra 2022: VIEW SERVER PERFORMANCE STATE og VIEW SERVER SECURITY STATE) +-- GRANT VIEW ANY DEFINITION TO [agent]; +-- GRANT CONNECT ANY DATABASE TO [agent]; -- SQL Server 2014+ +-- GRANT SELECT ON msdb.dbo.backupset TO [agent]; -- osv. for backup-, restore- og jobtabellerne i msdb +-- GRANT ALTER TRACE TO [agent]; -- valgfri +-- +-- Bruges af: Ingen regel. Kun visningen under Servere og databaser og API'et (GET …/connection-test). +-- Read-only: Ja: kun SELECT af funktioner. Intet gives, ændres eller oprettes; GRANT-scriptet køres aldrig af SQLInsight. +-- Kørsel: Kør i SSMS som agentens login. Hver variant er sin egen batch (GO); en variant for en anden SQL Server-version +-- end din kan fejle - det er forventet. Agenten kører altid med sessionsindstillingerne i _collectors/InstanceProbe.sql. +-- =================================================================================================================== + +-- Variant: Alle versioner: login'et og om det er sysadmin +-- Kilde: PermissionCheck.WhoSql +-- Kode: src/SQLInsight.Collectors/Discovery/PermissionCheck.cs : PermissionCheck.WhoSql +SELECT SUSER_SNAME(), IS_SRVROLEMEMBER('sysadmin') +GO + +-- Variant: SQL Server 2022 og nyere: rettighederne +-- Kilde: PermissionCheck.BuildSql(2022+) +-- Kode: src/SQLInsight.Collectors/Discovery/PermissionCheck.cs : PermissionCheck.BuildSql(checks) +SELECT 0, HAS_PERMS_BY_NAME(NULL, NULL, N'VIEW SERVER PERFORMANCE STATE') +UNION ALL SELECT 1, HAS_PERMS_BY_NAME(NULL, NULL, N'VIEW SERVER SECURITY STATE') +UNION ALL SELECT 2, HAS_PERMS_BY_NAME(NULL, NULL, N'VIEW ANY DEFINITION') +UNION ALL SELECT 3, HAS_PERMS_BY_NAME(NULL, NULL, N'CONNECT ANY DATABASE') +UNION ALL SELECT 4, HAS_PERMS_BY_NAME(N'msdb.dbo.backupset', N'OBJECT', N'SELECT') +UNION ALL SELECT 5, HAS_PERMS_BY_NAME(N'msdb.dbo.backupmediafamily', N'OBJECT', N'SELECT') +UNION ALL SELECT 6, HAS_PERMS_BY_NAME(N'msdb.dbo.backupmediaset', N'OBJECT', N'SELECT') +UNION ALL SELECT 7, HAS_PERMS_BY_NAME(N'msdb.dbo.restorehistory', N'OBJECT', N'SELECT') +UNION ALL SELECT 8, HAS_PERMS_BY_NAME(N'msdb.dbo.suspect_pages', N'OBJECT', N'SELECT') +UNION ALL SELECT 9, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobs', N'OBJECT', N'SELECT') +UNION ALL SELECT 10, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobsteps', N'OBJECT', N'SELECT') +UNION ALL SELECT 11, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobhistory', N'OBJECT', N'SELECT') +UNION ALL SELECT 12, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobactivity', N'OBJECT', N'SELECT') +UNION ALL SELECT 13, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobschedules', N'OBJECT', N'SELECT') +UNION ALL SELECT 14, HAS_PERMS_BY_NAME(N'msdb.dbo.sysschedules', N'OBJECT', N'SELECT') +UNION ALL SELECT 15, HAS_PERMS_BY_NAME(N'msdb.dbo.syscategories', N'OBJECT', N'SELECT') +UNION ALL SELECT 16, HAS_PERMS_BY_NAME(NULL, NULL, N'ALTER TRACE') +GO + +-- Variant: SQL Server 2014 – 2019 og Azure SQL Managed Instance: rettighederne +-- Kilde: PermissionCheck.BuildSql(2014) +-- Kode: src/SQLInsight.Collectors/Discovery/PermissionCheck.cs : PermissionCheck.BuildSql(checks) +SELECT 0, HAS_PERMS_BY_NAME(NULL, NULL, N'VIEW SERVER STATE') +UNION ALL SELECT 1, HAS_PERMS_BY_NAME(NULL, NULL, N'VIEW ANY DEFINITION') +UNION ALL SELECT 2, HAS_PERMS_BY_NAME(NULL, NULL, N'CONNECT ANY DATABASE') +UNION ALL SELECT 3, HAS_PERMS_BY_NAME(N'msdb.dbo.backupset', N'OBJECT', N'SELECT') +UNION ALL SELECT 4, HAS_PERMS_BY_NAME(N'msdb.dbo.backupmediafamily', N'OBJECT', N'SELECT') +UNION ALL SELECT 5, HAS_PERMS_BY_NAME(N'msdb.dbo.backupmediaset', N'OBJECT', N'SELECT') +UNION ALL SELECT 6, HAS_PERMS_BY_NAME(N'msdb.dbo.restorehistory', N'OBJECT', N'SELECT') +UNION ALL SELECT 7, HAS_PERMS_BY_NAME(N'msdb.dbo.suspect_pages', N'OBJECT', N'SELECT') +UNION ALL SELECT 8, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobs', N'OBJECT', N'SELECT') +UNION ALL SELECT 9, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobsteps', N'OBJECT', N'SELECT') +UNION ALL SELECT 10, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobhistory', N'OBJECT', N'SELECT') +UNION ALL SELECT 11, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobactivity', N'OBJECT', N'SELECT') +UNION ALL SELECT 12, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobschedules', N'OBJECT', N'SELECT') +UNION ALL SELECT 13, HAS_PERMS_BY_NAME(N'msdb.dbo.sysschedules', N'OBJECT', N'SELECT') +UNION ALL SELECT 14, HAS_PERMS_BY_NAME(N'msdb.dbo.syscategories', N'OBJECT', N'SELECT') +UNION ALL SELECT 15, HAS_PERMS_BY_NAME(NULL, NULL, N'ALTER TRACE') +GO + +-- Variant: SQL Server 2005 – 2012: rettighederne +-- Kilde: PermissionCheck.BuildSql(2005) +-- Kode: src/SQLInsight.Collectors/Discovery/PermissionCheck.cs : PermissionCheck.BuildSql(checks) +SELECT 0, HAS_PERMS_BY_NAME(NULL, NULL, N'VIEW SERVER STATE') +UNION ALL SELECT 1, HAS_PERMS_BY_NAME(NULL, NULL, N'VIEW ANY DEFINITION') +UNION ALL SELECT 2, HAS_PERMS_BY_NAME(N'msdb.dbo.backupset', N'OBJECT', N'SELECT') +UNION ALL SELECT 3, HAS_PERMS_BY_NAME(N'msdb.dbo.backupmediafamily', N'OBJECT', N'SELECT') +UNION ALL SELECT 4, HAS_PERMS_BY_NAME(N'msdb.dbo.backupmediaset', N'OBJECT', N'SELECT') +UNION ALL SELECT 5, HAS_PERMS_BY_NAME(N'msdb.dbo.restorehistory', N'OBJECT', N'SELECT') +UNION ALL SELECT 6, HAS_PERMS_BY_NAME(N'msdb.dbo.suspect_pages', N'OBJECT', N'SELECT') +UNION ALL SELECT 7, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobs', N'OBJECT', N'SELECT') +UNION ALL SELECT 8, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobsteps', N'OBJECT', N'SELECT') +UNION ALL SELECT 9, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobhistory', N'OBJECT', N'SELECT') +UNION ALL SELECT 10, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobactivity', N'OBJECT', N'SELECT') +UNION ALL SELECT 11, HAS_PERMS_BY_NAME(N'msdb.dbo.sysjobschedules', N'OBJECT', N'SELECT') +UNION ALL SELECT 12, HAS_PERMS_BY_NAME(N'msdb.dbo.sysschedules', N'OBJECT', N'SELECT') +UNION ALL SELECT 13, HAS_PERMS_BY_NAME(N'msdb.dbo.syscategories', N'OBJECT', N'SELECT') +UNION ALL SELECT 14, HAS_PERMS_BY_NAME(NULL, NULL, N'ALTER TRACE') +GO + +-- Variant: SQL Server 2005 – 2012: online brugerdatabaser, login'et ikke kan åbne +-- Kilde: PermissionCheck.DatabasesWithoutAccessSql +-- Kode: src/SQLInsight.Collectors/Discovery/PermissionCheck.cs : PermissionCheck.DatabasesWithoutAccessSql +SELECT name FROM sys.databases +WHERE state = 0 AND database_id > 4 AND is_read_only = 0 AND source_database_id IS NULL AND HAS_DBACCESS(name) = 0 +ORDER BY name +GO + diff --git a/SQL Scripts/_collectors/QueryStoreCollector.sql b/SQL Scripts/_collectors/QueryStoreCollector.sql index 9622475..be18d53 100644 --- a/SQL Scripts/_collectors/QueryStoreCollector.sql +++ b/SQL Scripts/_collectors/QueryStoreCollector.sql @@ -4,6 +4,8 @@ -- Hvad: Query Store i hver brugerdatabase, hvor det er slået til: de 100 tungeste queries over 8 dage og -- forcerede planer, der fejler. -- Fil: src/SQLInsight.Collectors/Performance/QueryStoreCollector.cs +-- Databaser: Fra 1.3 kun de databaser, der er valgt under Servere og databaser (systemdatabaserne altid; se +-- _collectors/DatabaseDiscovery.sql). Fravalgte databaser sorteres fra i koden efter forespørgslen; SQL'en er uændret. -- Interval: 30 min som standard (SQLInsight:CollectorIntervals:query-store) -- Versioner: SQL Server 2016 og nyere -- diff --git a/src/SQLInsight.Collectors/Discovery/DatabaseDiscovery.cs b/src/SQLInsight.Collectors/Discovery/DatabaseDiscovery.cs index bf29dbf..9588b41 100644 --- a/src/SQLInsight.Collectors/Discovery/DatabaseDiscovery.cs +++ b/src/SQLInsight.Collectors/Discovery/DatabaseDiscovery.cs @@ -13,7 +13,7 @@ namespace SQLInsight.Collectors.Discovery; public static class DatabaseDiscovery { /// SQL Server 2005 and later. - internal const string Databases2005 = """ + internal const string Databases2005Sql = """ SELECT d.name, d.database_id, d.state_desc, d.recovery_model_desc, d.compatibility_level, d.is_read_only, CASE WHEN d.source_database_id IS NULL THEN 0 ELSE 1 END, HAS_DBACCESS(d.name), @@ -23,7 +23,7 @@ public static class DatabaseDiscovery """; /// SQL Server 2000: no sys.databases; status and recovery model from DATABASEPROPERTYEX. - internal const string Databases2000 = """ + internal const string Databases2000Sql = """ SELECT d.name, d.dbid, CAST(DATABASEPROPERTYEX(d.name, 'Status') AS nvarchar(60)), CAST(DATABASEPROPERTYEX(d.name, 'Recovery') AS nvarchar(60)), @@ -68,7 +68,7 @@ public static class DatabaseDiscovery { ArgumentNullException.ThrowIfNull(connection); ArgumentNullException.ThrowIfNull(instance); - var databases = await Db.QueryAsync(connection, instance.Version.Major >= 9 ? Databases2005 : Databases2000, r => new DiscoveredDatabase + var databases = await Db.QueryAsync(connection, instance.Version.Major >= 9 ? Databases2005Sql : Databases2000Sql, r => new DiscoveredDatabase { Name = r.String(0), DatabaseId = r.Int(1), diff --git a/src/SQLInsight.Collectors/Discovery/PermissionCheck.cs b/src/SQLInsight.Collectors/Discovery/PermissionCheck.cs index 8cbf8a6..c5a4893 100644 --- a/src/SQLInsight.Collectors/Discovery/PermissionCheck.cs +++ b/src/SQLInsight.Collectors/Discovery/PermissionCheck.cs @@ -71,6 +71,9 @@ public static class PermissionCheck return sql.ToString(); } + /// The login the agent arrived as, and whether it is sysadmin (SQL Server 2000 and later). + internal const string WhoSql = "SELECT SUSER_SNAME(), IS_SRVROLEMEMBER('sysadmin')"; + /// Online user databases the login cannot enter: before 2014 it needs a user in each (the GRANT script creates them). internal const string DatabasesWithoutAccessSql = """ SELECT name FROM sys.databases @@ -84,7 +87,7 @@ public static class PermissionCheck ArgumentNullException.ThrowIfNull(connection); ArgumentNullException.ThrowIfNull(instance); ArgumentNullException.ThrowIfNull(result); - var who = (await Db.QueryAsync(connection, "SELECT SUSER_SNAME(), IS_SRVROLEMEMBER('sysadmin')", r => (Login: r.NullableString(0), Sysadmin: r.NullableInt(1) == 1), cancellationToken) + var who = (await Db.QueryAsync(connection, WhoSql, r => (Login: r.NullableString(0), Sysadmin: r.NullableInt(1) == 1), cancellationToken) .ConfigureAwait(false))[0]; result = result with { Login = who.Login, IsSysadmin = who.Sysadmin }; var notes = new List(result.Notes); diff --git a/tests/SQLInsight.Collectors.Tests/DiscoveryTests.cs b/tests/SQLInsight.Collectors.Tests/DiscoveryTests.cs index f302954..47cd08d 100644 --- a/tests/SQLInsight.Collectors.Tests/DiscoveryTests.cs +++ b/tests/SQLInsight.Collectors.Tests/DiscoveryTests.cs @@ -76,13 +76,13 @@ public sealed class DiscoveryTests var sql2005 = Instance("9.0.5000.0"); var batches = new[] { - DatabaseDiscovery.Databases2005, DatabaseDiscovery.Databases2000, DatabaseDiscovery.DynamicsAxSql("AX30", sql2005.Version), + DatabaseDiscovery.Databases2005Sql, DatabaseDiscovery.Databases2000Sql, DatabaseDiscovery.DynamicsAxSql("AX30", sql2005.Version), PermissionCheck.BuildSql(PermissionCheck.ChecksFor(sql2005)), PermissionCheck.DatabasesWithoutAccessSql, }; Assert.All(batches, sql => Assert.DoesNotContain(Sql2008Only, p => p.IsMatch(sql))); Assert.All(batches, sql => Assert.DoesNotContain("PXI_BATCHMONITORTABLE", sql, StringComparison.OrdinalIgnoreCase)); - Assert.DoesNotContain("sys.", DatabaseDiscovery.Databases2000, StringComparison.Ordinal); + Assert.DoesNotContain("sys.", DatabaseDiscovery.Databases2000Sql, StringComparison.Ordinal); } [Fact] diff --git a/tests/SQLInsight.Collectors.Tests/SqlScripts/SqlScriptSources.cs b/tests/SQLInsight.Collectors.Tests/SqlScripts/SqlScriptSources.cs index 986b892..69362d6 100644 --- a/tests/SQLInsight.Collectors.Tests/SqlScripts/SqlScriptSources.cs +++ b/tests/SQLInsight.Collectors.Tests/SqlScripts/SqlScriptSources.cs @@ -3,6 +3,7 @@ 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; @@ -36,7 +37,9 @@ internal static class SqlScriptSources 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/"; @@ -317,6 +320,27 @@ internal static class SqlScriptSources 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), }; return sources;