diff --git a/src/PlanViewer.App/Dialogs/ConnectionDialog.axaml b/src/PlanViewer.App/Dialogs/ConnectionDialog.axaml
index 38bdec0..ce477f1 100644
--- a/src/PlanViewer.App/Dialogs/ConnectionDialog.axaml
+++ b/src/PlanViewer.App/Dialogs/ConnectionDialog.axaml
@@ -2,8 +2,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="PlanViewer.App.Dialogs.ConnectionDialog"
Title="Connect to Server"
- Width="480" Height="520"
- MinWidth="400" MinHeight="480"
+ Width="480" Height="588"
+ MinWidth="400" MinHeight="548"
WindowStartupLocation="CenterOwner"
CanResize="False"
Icon="avares://PlanViewer.App/EDD.ico"
@@ -85,6 +85,15 @@
Foreground="{DynamicResource ForegroundBrush}"
ToolTip.Tip="Sets ApplicationIntent=ReadOnly. Required when connecting via an AG listener or Azure failover group endpoint to route to a readable secondary (including Query Store on secondary replicas)."
FontSize="12"/>
+
+
+
+
+
+
diff --git a/src/PlanViewer.App/Dialogs/ConnectionDialog.axaml.cs b/src/PlanViewer.App/Dialogs/ConnectionDialog.axaml.cs
index 5f32da3..cf0c93f 100644
--- a/src/PlanViewer.App/Dialogs/ConnectionDialog.axaml.cs
+++ b/src/PlanViewer.App/Dialogs/ConnectionDialog.axaml.cs
@@ -78,6 +78,7 @@ private void ApplySavedConnection(ServerConnection saved)
TrustCertBox.IsChecked = saved.TrustServerCertificate;
ReadOnlyIntentCheckBox.IsChecked = saved.ApplicationIntentReadOnly;
+ DatabaseInputBox.Text = saved.DatabaseName ?? "";
// Load stored credentials
var cred = _credentialService.GetCredential(saved.Id);
@@ -125,6 +126,11 @@ private async void TestConnection_Click(object? sender, RoutedEventArgs e)
return;
}
+ // For Azure SQL DB / JIT access the login often can't open master, so connect
+ // through the database the user named (if any) instead of the hardcoded master.
+ var typedDatabase = DatabaseInputBox.Text?.Trim();
+ var connectDatabase = string.IsNullOrEmpty(typedDatabase) ? "master" : typedDatabase;
+
StatusText.Text = "Connecting...";
StatusText.Foreground = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Color.FromRgb(0xE4, 0xE6, 0xEB));
TestButton.IsEnabled = false;
@@ -135,12 +141,13 @@ private async void TestConnection_Click(object? sender, RoutedEventArgs e)
var connectionString = connection.GetConnectionString(
LoginBox.Text?.Trim(),
PasswordBox.Text,
- "master");
+ connectDatabase);
await using var conn = new SqlConnection(connectionString);
await conn.OpenAsync();
- // Fetch databases
+ // Fetch databases the login can see. On Azure SQL DB connected to a single user
+ // database this returns master + that database, which is expected.
var databases = new List();
using var cmd = new SqlCommand(
"SELECT name FROM sys.databases WHERE state_desc = 'ONLINE' ORDER BY name", conn);
@@ -148,13 +155,21 @@ private async void TestConnection_Click(object? sender, RoutedEventArgs e)
while (await reader.ReadAsync())
databases.Add(reader.GetString(0));
+ // The named database is reachable (OpenAsync succeeded), so make sure it's
+ // selectable even if enumeration didn't surface it (restricted JIT permissions).
+ if (!string.IsNullOrEmpty(typedDatabase) &&
+ !databases.Contains(typedDatabase, StringComparer.OrdinalIgnoreCase))
+ databases.Insert(0, typedDatabase);
+
DatabaseBox.ItemsSource = databases;
DatabaseBox.IsEnabled = true;
ConnectButton.IsEnabled = true;
- // Default to master if available
- var masterIdx = databases.IndexOf("master");
- if (masterIdx >= 0) DatabaseBox.SelectedIndex = masterIdx;
+ // Pre-select the named database when given, otherwise default to master.
+ var preferred = string.IsNullOrEmpty(typedDatabase) ? "master" : typedDatabase;
+ var preferredIdx = databases.FindIndex(d => d.Equals(preferred, StringComparison.OrdinalIgnoreCase));
+ if (preferredIdx >= 0) DatabaseBox.SelectedIndex = preferredIdx;
+ else if (databases.Count > 0) DatabaseBox.SelectedIndex = 0;
StatusText.Text = $"Connected ({databases.Count} databases)";
StatusText.Foreground = Avalonia.Media.Brushes.LimeGreen;
@@ -213,11 +228,13 @@ private void Cancel_Click(object? sender, RoutedEventArgs e)
private ServerConnection BuildServerConnection()
{
var serverName = ServerNameBox.Text?.Trim() ?? "";
+ var databaseName = DatabaseInputBox.Text?.Trim();
return new ServerConnection
{
Id = serverName,
ServerName = serverName,
DisplayName = serverName,
+ DatabaseName = string.IsNullOrEmpty(databaseName) ? null : databaseName,
AuthenticationType = GetSelectedAuthType(),
TrustServerCertificate = TrustCertBox.IsChecked == true,
EncryptMode = GetSelectedEncryptMode(),
diff --git a/src/PlanViewer.Core/Models/ServerConnection.cs b/src/PlanViewer.Core/Models/ServerConnection.cs
index 9bf6877..16f1487 100644
--- a/src/PlanViewer.Core/Models/ServerConnection.cs
+++ b/src/PlanViewer.Core/Models/ServerConnection.cs
@@ -18,6 +18,13 @@ public class ServerConnection
public bool TrustServerCertificate { get; set; } = false;
public bool ApplicationIntentReadOnly { get; set; } = false;
+ ///
+ /// Optional database name for the initial connection. Use for Azure SQL Database or
+ /// Just-In-Time access where the login can't open master.
+ /// Leave empty for on-premises SQL Server (defaults to master).
+ ///
+ public string? DatabaseName { get; set; }
+
[JsonIgnore]
public string AuthenticationDisplay => AuthenticationType switch
{