diff --git a/toolbox/DBAToolBox.ps1 b/toolbox/DBAToolBox.ps1
index cdb3d77..370bff7 100644
--- a/toolbox/DBAToolBox.ps1
+++ b/toolbox/DBAToolBox.ps1
@@ -44,6 +44,7 @@ $btnConnect = $Window.FindName("btnConnect")
$txtServerName = $Window.FindName("txtServerName")
$txtLogs = $Window.FindName("txtLogs")
$dgServerInfo = $Window.FindName("dgServerInfo")
+$dgDatabases = $Window.FindName("dgDatabases")
$dgServerErrors = $Window.FindName("dgServerErrors")
$tvServers = $Window.FindName("tvServers")
$btnAddServer = $Window.FindName("btnAddServer")
@@ -107,6 +108,21 @@ function Refresh-ServerTree {
try {
$dbs = Get-SQLDatabases -ServerInstance $srvName
if ($dbs -and $dbs.Count -gt 0) {
+ # Cache the loaded databases
+ if ($null -eq $script:ServerConnectionStates[$srvName]) {
+ $script:ServerConnectionStates[$srvName] = @{
+ Success = $true
+ Instance = $srvName
+ Version = "Unknown (Expanded)"
+ Edition = "Unknown"
+ Status = "Online"
+ Databases = $dbs
+ Errors = $null
+ }
+ } else {
+ $script:ServerConnectionStates[$srvName].Databases = $dbs
+ }
+
foreach ($db in $dbs) {
$dbNode = New-Object System.Windows.Controls.TreeViewItem
$dbNode.Header = "$($db.Name) ($($db.Status))"
@@ -156,6 +172,13 @@ $tvServers.Add_SelectedItemChanged({
# Server is connected: show its version/details in the DataGrid
$dgServerInfo.ItemsSource = @([PSCustomObject]$connState | Select-Object Instance, Version, Edition, Status)
+ # Show cached databases if available
+ if ($null -ne $connState.Databases) {
+ $dgDatabases.ItemsSource = @($connState.Databases)
+ } else {
+ $dgDatabases.ItemsSource = $null
+ }
+
# Show cached error logs if available
if ($null -ne $connState.Errors) {
$dgServerErrors.ItemsSource = @($connState.Errors)
@@ -165,6 +188,7 @@ $tvServers.Add_SelectedItemChanged({
} else {
# Server is not connected: clear the DataGrids so no old server details/errors are visible
$dgServerInfo.ItemsSource = $null
+ $dgDatabases.ItemsSource = $null
$dgServerErrors.ItemsSource = $null
}
} elseif ($selectedItem.Tag -is [System.Management.Automation.PSCustomObject]) {
@@ -173,7 +197,8 @@ $tvServers.Add_SelectedItemChanged({
Write-Log "Selected database: $($db.Name) (Size: $($db.Size), Status: $($db.Status))"
# Show database details in the main information grid
- $dgServerInfo.ItemsSource = @($db | Select-Object Name, Status, Size, RecoveryModel)
+ $dgServerInfo.ItemsSource = @($db | Select-Object Name, Status, Size, RecoveryModel, LastFullBackup, LastLogBackup)
+ $dgDatabases.ItemsSource = $null
$dgServerErrors.ItemsSource = $null
# Also, update txtServerName with the parent server's tag so they see which server it belongs to
@@ -186,6 +211,7 @@ $tvServers.Add_SelectedItemChanged({
# Hide detail panel if selection is null or ROOT
$gridDetails.Visibility = "Collapsed"
$dgServerInfo.ItemsSource = $null
+ $dgDatabases.ItemsSource = $null
$dgServerErrors.ItemsSource = $null
}
})
@@ -267,6 +293,25 @@ $btnConnect.Add_Click({
Write-Log "Connected successfully to $($result.Instance)!"
$dgServerInfo.ItemsSource = @([PSCustomObject]$result | Select-Object Instance, Version, Edition, Status)
+ # Fetch databases list using Get-SQLDatabases
+ Write-Log "Fetching databases list for $server..."
+ try {
+ $dbs = Get-SQLDatabases -ServerInstance $server
+ if ($dbs -and $dbs.Count -gt 0) {
+ $dgDatabases.ItemsSource = @($dbs)
+ $result.Databases = $dbs
+ Write-Log "Loaded $($dbs.Count) databases."
+ } else {
+ $dgDatabases.ItemsSource = $null
+ $result.Databases = $null
+ Write-Log "No databases found on $server."
+ }
+ } catch {
+ Write-Log "Warning: Could not load databases: $($_.Exception.Message)"
+ $dgDatabases.ItemsSource = $null
+ $result.Databases = $null
+ }
+
# Fetch errors from the last hour using Get-SQLErrors
Write-Log "Fetching error logs (last hour) for $server..."
try {
@@ -277,25 +322,29 @@ $btnConnect.Add_Click({
Write-Log "Loaded $($errors.Count) error log entries."
} else {
$dgServerErrors.ItemsSource = $null
+ $result.Errors = $null
Write-Log "No errors found in the last hour."
}
} catch {
Write-Log "Warning: Could not load error log: $($_.Exception.Message)"
$dgServerErrors.ItemsSource = $null
+ $result.Errors = $null
}
- # Cache successful connection state with its fetched errors
+ # Cache successful connection state with its fetched errors and databases
$script:ServerConnectionStates[$server] = $result
} else {
Write-Log "Connection failed: $($result.Error)"
$script:ServerConnectionStates[$server] = $null
$dgServerInfo.ItemsSource = $null
+ $dgDatabases.ItemsSource = $null
$dgServerErrors.ItemsSource = $null
}
} catch {
Write-Log "Critical Error: $($_.Exception.Message)"
$script:ServerConnectionStates[$server] = $null
$dgServerInfo.ItemsSource = $null
+ $dgDatabases.ItemsSource = $null
$dgServerErrors.ItemsSource = $null
} finally {
$btnConnect.IsEnabled = $true
diff --git a/toolbox/Modules/SQLManager.psm1 b/toolbox/Modules/SQLManager.psm1
index 48dd231..98ab1d7 100644
--- a/toolbox/Modules/SQLManager.psm1
+++ b/toolbox/Modules/SQLManager.psm1
@@ -86,10 +86,37 @@ function Get-SQLDatabases {
try {
$connection.Open()
- # Query databases from sys.databases joined with sys.master_files to correctly sum file sizes.
- # sys.databases does not have a "size" column. We must group and sum size from sys.master_files.
$cmd = $connection.CreateCommand()
+ # Primary query including backups
$cmd.CommandText = @"
+SELECT
+ d.name,
+ d.state_desc,
+ COALESCE(SUM(CAST(f.size AS FLOAT) * 8.0 / 1024.0), 0.0) as size_mb,
+ d.recovery_model_desc,
+ (
+ SELECT MAX(b.backup_finish_date)
+ FROM msdb.dbo.backupset b
+ WHERE b.database_name = d.name AND b.type = 'D'
+ ) AS last_full_backup,
+ (
+ SELECT MAX(b.backup_finish_date)
+ FROM msdb.dbo.backupset b
+ WHERE b.database_name = d.name AND b.type = 'L'
+ ) AS last_log_backup
+FROM sys.databases d
+LEFT JOIN sys.master_files f ON d.database_id = f.database_id
+GROUP BY d.name, d.state_desc, d.recovery_model_desc
+"@
+
+ $adapter = New-Object System.Data.SqlClient.SqlDataAdapter($cmd)
+ $table = New-Object System.Data.DataTable
+ try {
+ $null = $adapter.Fill($table)
+ $hasBackups = $true
+ } catch {
+ # Fallback to query without backups if we don't have access to msdb or backupset table
+ $cmd.CommandText = @"
SELECT
d.name,
d.state_desc,
@@ -99,10 +126,10 @@ FROM sys.databases d
LEFT JOIN sys.master_files f ON d.database_id = f.database_id
GROUP BY d.name, d.state_desc, d.recovery_model_desc
"@
-
- $adapter = New-Object System.Data.SqlClient.SqlDataAdapter($cmd)
- $table = New-Object System.Data.DataTable
- $null = $adapter.Fill($table)
+ $table.Clear()
+ $null = $adapter.Fill($table)
+ $hasBackups = $false
+ }
$dbList = @()
foreach ($row in $table.Rows) {
@@ -115,11 +142,37 @@ GROUP BY d.name, d.state_desc, d.recovery_model_desc
$dbStatus = if ($row.state_desc -ne [System.DBNull]::Value -and $row.state_desc -ne $null) { $row.state_desc.ToString() } else { "Unknown" }
$dbRecovery = if ($row.recovery_model_desc -ne [System.DBNull]::Value -and $row.recovery_model_desc -ne $null) { $row.recovery_model_desc.ToString() } else { "Simple" }
+ $fullBackup = "Unknown"
+ $logBackup = "Unknown"
+
+ if ($hasBackups) {
+ $fullBackup = "Never"
+ if ($row.Table.Columns.Contains("last_full_backup") -and $row.last_full_backup -ne [System.DBNull]::Value -and $row.last_full_backup -ne $null) {
+ $fullBackup = ([datetime]$row.last_full_backup).ToString("yyyy-MM-dd HH:mm:ss")
+ }
+
+ $logBackup = "Never"
+ if ($dbRecovery -eq "SIMPLE") {
+ $logBackup = "N/A (Simple)"
+ } elseif ($row.Table.Columns.Contains("last_log_backup") -and $row.last_log_backup -ne [System.DBNull]::Value -and $row.last_log_backup -ne $null) {
+ $logBackup = ([datetime]$row.last_log_backup).ToString("yyyy-MM-dd HH:mm:ss")
+ }
+ } else {
+ $fullBackup = "N/A (No Access)"
+ if ($dbRecovery -eq "SIMPLE") {
+ $logBackup = "N/A (Simple)"
+ } else {
+ $logBackup = "N/A (No Access)"
+ }
+ }
+
$dbList += [pscustomobject]@{
Name = $dbName
Status = $dbStatus
Size = "$([Math]::Round($sizeVal, 2)) MB"
RecoveryModel = $dbRecovery
+ LastFullBackup = $fullBackup
+ LastLogBackup = $logBackup
}
}
return $dbList
diff --git a/toolbox/UI/MainWindow.xaml b/toolbox/UI/MainWindow.xaml
index dde30d4..0300e42 100644
--- a/toolbox/UI/MainWindow.xaml
+++ b/toolbox/UI/MainWindow.xaml
@@ -72,6 +72,9 @@
+
+
+