Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions extensions/mssql/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ yarn lint src/ test/

#### Unit Tests

See [test/unit/AGENTS.md](test/unit/AGENTS.md) for unit testing conventions and patterns.

```bash
# Unit tests require VS Code download and cannot run in sandboxed environments
# This is expected behavior - tests work in CI with proper VS Code setup
Expand Down
10 changes: 10 additions & 0 deletions extensions/mssql/package.json
Comment thread
allancascante marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,11 @@
"when": "view == objectExplorer && viewItem =~ /\\btype=(disconnectedServer|Server)\\b/",
"group": "4_MSSQL_discoverAndProfile@2"
},
{
"command": "mssql.profiler.launchFromDatabase",
"when": "view == objectExplorer && viewItem =~ /\\btype=(Database)\\b/",
Comment thread
allancascante marked this conversation as resolved.
"group": "1a_MSSQL_profiler@1"
},
{
Comment thread
allancascante marked this conversation as resolved.
"command": "mssql.schemaDesigner",
"when": "view === objectExplorer && viewItem =~ /\\btype=(Database|Server)\\b.*\\bsubType=(Database|DockerContainerDatabase)\\b/",
Expand Down Expand Up @@ -1447,6 +1452,11 @@
"title": "%mssql.profiler.launchFromObjectExplorer%",
"category": "MS SQL"
},
{
"command": "mssql.profiler.launchFromDatabase",
"title": "%mssql.profiler.launchFromObjectExplorer%",
Comment thread
allancascante marked this conversation as resolved.
"category": "MS SQL"
},
Comment thread
allancascante marked this conversation as resolved.
{
"command": "mssql.openAzureDataStudioMigration",
"title": "%mssql.openAzureDataStudioMigration%",
Expand Down
5 changes: 5 additions & 0 deletions extensions/mssql/src/profiler/profilerConfigService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ import {
import { defaultProfilerConfig } from "./profilerDefaultConfig";
import { ProfilerSelectedEventDetails, ProfilerEventProperty } from "../sharedInterfaces/profiler";

/**
* The canonical column/field name used for database-name filtering in profiler views.
*/
export const FIELD_DATABASE_NAME = "DatabaseName";

/**
* Service for managing profiler templates and view configurations.
* Maps between views and sessions are computed at runtime from the templates'
Expand Down
54 changes: 51 additions & 3 deletions extensions/mssql/src/profiler/profilerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Logger } from "../models/logger";
import { Profiler as LocProfiler } from "../constants/locConstants";
import * as Constants from "../constants/constants";
import { TreeNodeInfo } from "../objectExplorer/nodes/treeNodeInfo";
import { ObjectExplorerUtils } from "../objectExplorer/objectExplorerUtils";
import { IConnectionProfile } from "../models/interfaces";
import { getServerTypes, isAzureSqlDbCompatible } from "../models/connectionInfo";
import { getErrorMessage, uuid } from "../utils/utils";
Expand Down Expand Up @@ -59,9 +60,11 @@ export class ProfilerController {
* Launches the profiler UI with a provided connection profile (from Object Explorer).
* This is the main entry point - profiler can only be launched via right-click context menu.
* @param connectionProfile - The connection profile to use for profiling
* @param databaseScopeFilter - If provided, pre-populates a DatabaseName filter in the profiler UI
*/
public async launchProfilerWithConnection(
connectionProfile: IConnectionProfile,
databaseScopeFilter?: string,
): Promise<void> {
this._logger.verbose(
`Launching profiler with connection to ${connectionProfile.server}...`,
Expand Down Expand Up @@ -90,13 +93,23 @@ export class ProfilerController {
// For Azure SQL and Fabric, we need to ensure a user database is selected
let profileToUse = connectionProfile;
if (isAzureOrFabric) {
const updatedProfile = await this.ensureAzureDatabaseSelected(connectionProfile);
// When launched from a Database node, pre-fill the database so
// the user is not prompted to select one again.
if (databaseScopeFilter) {
profileToUse = { ...connectionProfile, database: databaseScopeFilter };
}

const updatedProfile = await this.ensureAzureDatabaseSelected(profileToUse);
if (!updatedProfile) {
// User cancelled database selection
this._logger.verbose("User cancelled database selection");
return;
}
profileToUse = updatedProfile;

// Azure sessions are already database-scoped via ON DATABASE,
// so skip the client-side DatabaseName filter entirely.
databaseScopeFilter = undefined;
}

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

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

// Launch Profiler from a Database node in Object Explorer (pre-filters by database)
this._context.subscriptions.push(
vscode.commands.registerCommand(
"mssql.profiler.launchFromDatabase",
async (treeNodeInfo: TreeNodeInfo) => {
try {
const connectionProfile = treeNodeInfo.connectionProfile;
// Use ObjectExplorerUtils.getDatabaseName to reliably get the database name.
// connectionProfile.database is often empty for Database nodes because they
// inherit the parent Server node's connection profile unchanged.
const databaseName = ObjectExplorerUtils.getDatabaseName(treeNodeInfo);
Comment thread
allancascante marked this conversation as resolved.
this._logger.verbose(
`Launching profiler from database node: ${databaseName}`,
);
await this.launchProfilerWithConnection(connectionProfile, databaseName);
} catch (e) {
Comment thread
allancascante marked this conversation as resolved.
this._logger.error(`Command error: ${e}`);
vscode.window.showErrorMessage(
LocProfiler.failedToLaunchProfiler(getErrorMessage(e)),
);
}
},
),
);

// Open XEL File command
this._context.subscriptions.push(
vscode.commands.registerCommand("mssql.profiler.openXelFile", async () => {
Expand Down Expand Up @@ -524,8 +562,13 @@ export class ProfilerController {
* and auto-starts profiling.
* @param profilerUri - The URI of the established profiler connection
* @param engineType - The engine type for filtering templates
* @param databaseScopeFilter - If provided, pre-populates a DatabaseName filter in the profiler UI
*/
private async setupProfilerUI(profilerUri: string, engineType: EngineType): Promise<void> {
private async setupProfilerUI(
profilerUri: string,
engineType: EngineType,
databaseScopeFilter?: string,
): Promise<void> {
this._profilerUri = profilerUri;

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

// If launched from a database node, set the initial database filter
if (databaseScopeFilter) {
webviewController.setInitialDatabaseFilter(databaseScopeFilter);
}

// Track this webview controller along with its profiler URI for cleanup
const webviewId = uuid();
const webviewProfilerUri = profilerUri; // Capture for cleanup
Expand Down
Loading
Loading