Skip to content

Commit cf68f11

Browse files
allancascanteAllan CascanteCopilot
committed
Query Profiler database filter (#21417)
* refactor to filtering parse types in both filters and columns values * refactor on method signature used by sorting functionality * Update extensions/mssql/src/profiler/profilerConfigService.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update extensions/mssql/src/profiler/profilerWebviewController.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Adding launch from OE for databases to include a filter at database level * reverting changes from merge * reverting merged changes adding duplicate tests * removing duplicate test * fix for azure database launch * reverting unwanted changes * PR comment * test refactor * adding constant * test changes * tests updates * making note to unit test instructions for agents --------- Co-authored-by: Allan Cascante <acascante@microsoft.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 0b696eb commit cf68f11

9 files changed

Lines changed: 591 additions & 307 deletions

File tree

extensions/mssql/AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ yarn lint src/ test/
6565

6666
#### Unit Tests
6767

68+
See [test/unit/AGENTS.md](test/unit/AGENTS.md) for unit testing conventions and patterns.
69+
6870
```bash
6971
# Unit tests require VS Code download and cannot run in sandboxed environments
7072
# This is expected behavior - tests work in CI with proper VS Code setup

extensions/mssql/package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,11 @@
574574
"when": "view == objectExplorer && viewItem =~ /\\btype=(disconnectedServer|Server)\\b/",
575575
"group": "4_MSSQL_discoverAndProfile@2"
576576
},
577+
{
578+
"command": "mssql.profiler.launchFromDatabase",
579+
"when": "view == objectExplorer && viewItem =~ /\\btype=(Database)\\b/",
580+
"group": "1a_MSSQL_profiler@1"
581+
},
577582
{
578583
"command": "mssql.schemaDesigner",
579584
"when": "view === objectExplorer && viewItem =~ /\\btype=(Database|Server)\\b.*\\bsubType=(Database|DockerContainerDatabase)\\b/",
@@ -1447,6 +1452,11 @@
14471452
"title": "%mssql.profiler.launchFromObjectExplorer%",
14481453
"category": "MS SQL"
14491454
},
1455+
{
1456+
"command": "mssql.profiler.launchFromDatabase",
1457+
"title": "%mssql.profiler.launchFromObjectExplorer%",
1458+
"category": "MS SQL"
1459+
},
14501460
{
14511461
"command": "mssql.openAzureDataStudioMigration",
14521462
"title": "%mssql.openAzureDataStudioMigration%",

extensions/mssql/src/profiler/profilerConfigService.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ import {
2020
import { defaultProfilerConfig } from "./profilerDefaultConfig";
2121
import { ProfilerSelectedEventDetails, ProfilerEventProperty } from "../sharedInterfaces/profiler";
2222

23+
/**
24+
* The canonical column/field name used for database-name filtering in profiler views.
25+
*/
26+
export const FIELD_DATABASE_NAME = "DatabaseName";
27+
2328
/**
2429
* Service for managing profiler templates and view configurations.
2530
* Maps between views and sessions are computed at runtime from the templates'

extensions/mssql/src/profiler/profilerController.ts

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { Logger } from "../models/logger";
2020
import { Profiler as LocProfiler } from "../constants/locConstants";
2121
import * as Constants from "../constants/constants";
2222
import { TreeNodeInfo } from "../objectExplorer/nodes/treeNodeInfo";
23+
import { ObjectExplorerUtils } from "../objectExplorer/objectExplorerUtils";
2324
import { IConnectionProfile } from "../models/interfaces";
2425
import { getServerTypes, isAzureSqlDbCompatible } from "../models/connectionInfo";
2526
import { getErrorMessage, uuid } from "../utils/utils";
@@ -59,9 +60,11 @@ export class ProfilerController {
5960
* Launches the profiler UI with a provided connection profile (from Object Explorer).
6061
* This is the main entry point - profiler can only be launched via right-click context menu.
6162
* @param connectionProfile - The connection profile to use for profiling
63+
* @param databaseScopeFilter - If provided, pre-populates a DatabaseName filter in the profiler UI
6264
*/
6365
public async launchProfilerWithConnection(
6466
connectionProfile: IConnectionProfile,
67+
databaseScopeFilter?: string,
6568
): Promise<void> {
6669
this._logger.verbose(
6770
`Launching profiler with connection to ${connectionProfile.server}...`,
@@ -90,13 +93,23 @@ export class ProfilerController {
9093
// For Azure SQL and Fabric, we need to ensure a user database is selected
9194
let profileToUse = connectionProfile;
9295
if (isAzureOrFabric) {
93-
const updatedProfile = await this.ensureAzureDatabaseSelected(connectionProfile);
96+
// When launched from a Database node, pre-fill the database so
97+
// the user is not prompted to select one again.
98+
if (databaseScopeFilter) {
99+
profileToUse = { ...connectionProfile, database: databaseScopeFilter };
100+
}
101+
102+
const updatedProfile = await this.ensureAzureDatabaseSelected(profileToUse);
94103
if (!updatedProfile) {
95104
// User cancelled database selection
96105
this._logger.verbose("User cancelled database selection");
97106
return;
98107
}
99108
profileToUse = updatedProfile;
109+
110+
// Azure sessions are already database-scoped via ON DATABASE,
111+
// so skip the client-side DatabaseName filter entirely.
112+
databaseScopeFilter = undefined;
100113
}
101114

102115
// Generate a unique URI for this profiler connection
@@ -118,7 +131,7 @@ export class ProfilerController {
118131
this._profilerEngineTypes.set(profilerUri, this._currentEngineType);
119132

120133
// Use the common setup method - pass engine type to avoid race condition
121-
await this.setupProfilerUI(profilerUri, this._currentEngineType);
134+
await this.setupProfilerUI(profilerUri, this._currentEngineType, databaseScopeFilter);
122135
} catch (e) {
123136
this._logger.error(`Error launching profiler: ${e}`);
124137
vscode.window.showErrorMessage(LocProfiler.failedToLaunchProfiler(getErrorMessage(e)));
@@ -245,6 +258,31 @@ export class ProfilerController {
245258
),
246259
);
247260

261+
// Launch Profiler from a Database node in Object Explorer (pre-filters by database)
262+
this._context.subscriptions.push(
263+
vscode.commands.registerCommand(
264+
"mssql.profiler.launchFromDatabase",
265+
async (treeNodeInfo: TreeNodeInfo) => {
266+
try {
267+
const connectionProfile = treeNodeInfo.connectionProfile;
268+
// Use ObjectExplorerUtils.getDatabaseName to reliably get the database name.
269+
// connectionProfile.database is often empty for Database nodes because they
270+
// inherit the parent Server node's connection profile unchanged.
271+
const databaseName = ObjectExplorerUtils.getDatabaseName(treeNodeInfo);
272+
this._logger.verbose(
273+
`Launching profiler from database node: ${databaseName}`,
274+
);
275+
await this.launchProfilerWithConnection(connectionProfile, databaseName);
276+
} catch (e) {
277+
this._logger.error(`Command error: ${e}`);
278+
vscode.window.showErrorMessage(
279+
LocProfiler.failedToLaunchProfiler(getErrorMessage(e)),
280+
);
281+
}
282+
},
283+
),
284+
);
285+
248286
// Open XEL File command
249287
this._context.subscriptions.push(
250288
vscode.commands.registerCommand("mssql.profiler.openXelFile", async () => {
@@ -524,8 +562,13 @@ export class ProfilerController {
524562
* and auto-starts profiling.
525563
* @param profilerUri - The URI of the established profiler connection
526564
* @param engineType - The engine type for filtering templates
565+
* @param databaseScopeFilter - If provided, pre-populates a DatabaseName filter in the profiler UI
527566
*/
528-
private async setupProfilerUI(profilerUri: string, engineType: EngineType): Promise<void> {
567+
private async setupProfilerUI(
568+
profilerUri: string,
569+
engineType: EngineType,
570+
databaseScopeFilter?: string,
571+
): Promise<void> {
529572
this._profilerUri = profilerUri;
530573

531574
// Step 1: Show template selection quick pick (filtered by engine type)
@@ -616,6 +659,11 @@ export class ProfilerController {
616659
selectedTemplate.template.id,
617660
);
618661

662+
// If launched from a database node, set the initial database filter
663+
if (databaseScopeFilter) {
664+
webviewController.setInitialDatabaseFilter(databaseScopeFilter);
665+
}
666+
619667
// Track this webview controller along with its profiler URI for cleanup
620668
const webviewId = uuid();
621669
const webviewProfilerUri = profilerUri; // Capture for cleanup

0 commit comments

Comments
 (0)