Review 1.3: SQL Scripts for database discovery and the permission check
Some checks failed
deploy community / deploy (push) Successful in 15s
deploy hub / deploy (push) Successful in 19s
CI / Build og unit tests (push) Successful in 7s
CI / MSI-installer (push) Successful in 17s
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Has been cancelled
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Has been cancelled
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Has been cancelled
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Has been cancelled

Registers 1.3's SQL in SqlScriptSources (DatabaseDiscovery: databases 2005/2000
and the Dynamics AX signature per database; PermissionCheck: login/sysadmin, the
HAS_PERMS_BY_NAME checks for 2005-2012, 2014-2019/MI and 2022+, databases without
access) and adds _collectors/DatabaseDiscovery.sql and PermissionCheck.sql with
Danish review headers. The constants are renamed *Sql and the login query moved to
PermissionCheck.WhoSql so the registry test finds them. The scripts of the
collectors that now skip unchosen databases say so (their SQL is unchanged).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Søren Eggert Lundsteen Olsen 2026-10-02 13:26:16 +02:00
parent 473dccafff
commit 631edc2adc
11 changed files with 216 additions and 6 deletions

View file

@ -14,6 +14,7 @@ identisk med koden (se [Hold mappen i sync](#hold-mappen-i-sync)).
| `<regel-id>.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/`). | | `<regel-id>.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/<Collector>.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/<Collector>.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/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 ## Opbygning af en regelfil

View file

@ -4,6 +4,8 @@
-- Hvad: Community-reglernes (rules/community/*.json) og lokale reglers forespørgsler: én gang i master -- 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). -- (server-regler) eller i hver læsbar brugerdatabase (database-regler).
-- Fil: src/SQLInsight.Collectors/Custom/CustomQueryCollector.cs -- 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) -- Interval: 15 min som standard (SQLInsight:CollectorIntervals:custom-rules)
-- Versioner: SQL Server 2005 og nyere (pr. regel: minimumVersion) -- Versioner: SQL Server 2005 og nyere (pr. regel: minimumVersion)
-- --

View file

@ -4,6 +4,8 @@
-- Hvad: Databaseindstillinger og -tilstand, backup-historik (msdb), seneste fejlfri CHECKDB, VLF'er, Query -- Hvad: Databaseindstillinger og -tilstand, backup-historik (msdb), seneste fejlfri CHECKDB, VLF'er, Query
-- Store-tilstand og filer. -- Store-tilstand og filer.
-- Fil: src/SQLInsight.Collectors/Databases/DatabaseCollector.cs -- 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) -- 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 -- Versioner: Alle (SQL Server 2000 og nyere); CHECKDB og VLF'er fra 2016 SP2; Query Store fra 2016
-- --

View file

@ -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

View file

@ -4,6 +4,8 @@
-- Hvad: Index-inventar med brug, manglende index-forslag og statistikker for hver læsbar brugerdatabase. Kun -- Hvad: Index-inventar med brug, manglende index-forslag og statistikker for hver læsbar brugerdatabase. Kun
-- metadata og DMV'er; ingen tabeldata, ingen scanning. -- metadata og DMV'er; ingen tabeldata, ingen scanning.
-- Fil: src/SQLInsight.Collectors/Databases/IndexCollector.cs -- 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) -- Interval: 6 timer som standard (SQLInsight:CollectorIntervals:indexes)
-- Versioner: SQL Server 2005 og nyere -- Versioner: SQL Server 2005 og nyere
-- --

View file

@ -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

View file

@ -4,6 +4,8 @@
-- Hvad: Query Store i hver brugerdatabase, hvor det er slået til: de 100 tungeste queries over 8 dage og -- Hvad: Query Store i hver brugerdatabase, hvor det er slået til: de 100 tungeste queries over 8 dage og
-- forcerede planer, der fejler. -- forcerede planer, der fejler.
-- Fil: src/SQLInsight.Collectors/Performance/QueryStoreCollector.cs -- 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) -- Interval: 30 min som standard (SQLInsight:CollectorIntervals:query-store)
-- Versioner: SQL Server 2016 og nyere -- Versioner: SQL Server 2016 og nyere
-- --

View file

@ -13,7 +13,7 @@ namespace SQLInsight.Collectors.Discovery;
public static class DatabaseDiscovery public static class DatabaseDiscovery
{ {
/// <summary>SQL Server 2005 and later.</summary> /// <summary>SQL Server 2005 and later.</summary>
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, 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, CASE WHEN d.source_database_id IS NULL THEN 0 ELSE 1 END,
HAS_DBACCESS(d.name), HAS_DBACCESS(d.name),
@ -23,7 +23,7 @@ public static class DatabaseDiscovery
"""; """;
/// <summary>SQL Server 2000: no sys.databases; status and recovery model from DATABASEPROPERTYEX.</summary> /// <summary>SQL Server 2000: no sys.databases; status and recovery model from DATABASEPROPERTYEX.</summary>
internal const string Databases2000 = """ internal const string Databases2000Sql = """
SELECT d.name, d.dbid, SELECT d.name, d.dbid,
CAST(DATABASEPROPERTYEX(d.name, 'Status') AS nvarchar(60)), CAST(DATABASEPROPERTYEX(d.name, 'Status') AS nvarchar(60)),
CAST(DATABASEPROPERTYEX(d.name, 'Recovery') AS nvarchar(60)), CAST(DATABASEPROPERTYEX(d.name, 'Recovery') AS nvarchar(60)),
@ -68,7 +68,7 @@ public static class DatabaseDiscovery
{ {
ArgumentNullException.ThrowIfNull(connection); ArgumentNullException.ThrowIfNull(connection);
ArgumentNullException.ThrowIfNull(instance); 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), Name = r.String(0),
DatabaseId = r.Int(1), DatabaseId = r.Int(1),

View file

@ -71,6 +71,9 @@ public static class PermissionCheck
return sql.ToString(); return sql.ToString();
} }
/// <summary>The login the agent arrived as, and whether it is sysadmin (SQL Server 2000 and later).</summary>
internal const string WhoSql = "SELECT SUSER_SNAME(), IS_SRVROLEMEMBER('sysadmin')";
/// <summary>Online user databases the login cannot enter: before 2014 it needs a user in each (the GRANT script creates them).</summary> /// <summary>Online user databases the login cannot enter: before 2014 it needs a user in each (the GRANT script creates them).</summary>
internal const string DatabasesWithoutAccessSql = """ internal const string DatabasesWithoutAccessSql = """
SELECT name FROM sys.databases SELECT name FROM sys.databases
@ -84,7 +87,7 @@ public static class PermissionCheck
ArgumentNullException.ThrowIfNull(connection); ArgumentNullException.ThrowIfNull(connection);
ArgumentNullException.ThrowIfNull(instance); ArgumentNullException.ThrowIfNull(instance);
ArgumentNullException.ThrowIfNull(result); 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]; .ConfigureAwait(false))[0];
result = result with { Login = who.Login, IsSysadmin = who.Sysadmin }; result = result with { Login = who.Login, IsSysadmin = who.Sysadmin };
var notes = new List<string>(result.Notes); var notes = new List<string>(result.Notes);

View file

@ -76,13 +76,13 @@ public sealed class DiscoveryTests
var sql2005 = Instance("9.0.5000.0"); var sql2005 = Instance("9.0.5000.0");
var batches = new[] 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, PermissionCheck.BuildSql(PermissionCheck.ChecksFor(sql2005)), PermissionCheck.DatabasesWithoutAccessSql,
}; };
Assert.All(batches, sql => Assert.DoesNotContain(Sql2008Only, p => p.IsMatch(sql))); Assert.All(batches, sql => Assert.DoesNotContain(Sql2008Only, p => p.IsMatch(sql)));
Assert.All(batches, sql => Assert.DoesNotContain("PXI_BATCHMONITORTABLE", sql, StringComparison.OrdinalIgnoreCase)); 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] [Fact]

View file

@ -3,6 +3,7 @@ using System.Text.RegularExpressions;
using SQLInsight.Collectors.Concurrency; using SQLInsight.Collectors.Concurrency;
using SQLInsight.Collectors.Custom; using SQLInsight.Collectors.Custom;
using SQLInsight.Collectors.Databases; using SQLInsight.Collectors.Databases;
using SQLInsight.Collectors.Discovery;
using SQLInsight.Collectors.Instance; using SQLInsight.Collectors.Instance;
using SQLInsight.Collectors.Performance; using SQLInsight.Collectors.Performance;
using SQLInsight.Core.Instances; 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 V2008 = SqlServerVersion.Parse("10.0.6000.29");
private static readonly SqlServerVersion V2008R2Sp1 = SqlServerVersion.Parse("10.50.2500.0"); 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 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 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/"; 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", 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)", C + "Custom/CustomQueryCollector.cs", "CustomQueryCollector.BuildRuleSql(database, query)",
() => Tokens(CustomQueryCollector.BuildRuleSql("§database§", "§forespoergsel§"), "database", "forespoergsel")), () => 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; return sources;