diff --git a/extensions/mssql/src/contracts.ts b/extensions/mssql/src/contracts.ts index 62c93a80bf..5ada2f59da 100644 --- a/extensions/mssql/src/contracts.ts +++ b/extensions/mssql/src/contracts.ts @@ -1123,7 +1123,16 @@ export interface GetExecutionPlanParams { } export namespace GetExecutionPlanRequest { - export const type = new RequestType('queryexecutionplan/getexecutionplan'); + export const type = new RequestType('queryExecutionPlan/getExecutionPlan'); +} + +export interface ExecutionPlanComparisonParams { + firstExecutionPlanGraphInfo: azdata.executionPlan.ExecutionPlanGraphInfo; + secondExecutionPlanGraphInfo: azdata.executionPlan.ExecutionPlanGraphInfo; +} + +export namespace ExecutionPlanComparisonRequest { + export const type = new RequestType('queryExecutionPlan/compareExecutionPlanGraph'); } // ------------------------------- < Execution Plan > ------------------------------------ diff --git a/extensions/mssql/src/features.ts b/extensions/mssql/src/features.ts index e1be7c8f6d..fa4feb46ed 100644 --- a/extensions/mssql/src/features.ts +++ b/extensions/mssql/src/features.ts @@ -1221,10 +1221,25 @@ export class ExecutionPlanServiceFeature extends SqlOpsFeature { ); }; + const compareExecutionPlanGraph = (firstPlanFile: azdata.executionPlan.ExecutionPlanGraphInfo, secondPlanFile: azdata.executionPlan.ExecutionPlanGraphInfo): Thenable => { + const params: contracts.ExecutionPlanComparisonParams = { + firstExecutionPlanGraphInfo: firstPlanFile, + secondExecutionPlanGraphInfo: secondPlanFile + }; + + return client.sendRequest(contracts.ExecutionPlanComparisonRequest.type, params).then( + r => r, + e => { + client.logFailedRequest(contracts.ExecutionPlanComparisonRequest.type, e); + return Promise.reject(e); + } + ); + }; + return azdata.dataprotocol.registerExecutionPlanProvider({ providerId: client.providerId, - getExecutionPlan + getExecutionPlan, + compareExecutionPlanGraph }); } } - diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index b3c2f1abbd..c2d9ace749 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -1268,6 +1268,38 @@ declare module 'azdata' { graphs: ExecutionPlanGraph[] } + export interface ExecutionGraphComparisonResult { + /** + * The base ExecutionPlanNode for the ExecutionGraphComparisonResult. + */ + baseNode: ExecutionPlanNode; + /** + * The children of the ExecutionGraphComparisonResult. + */ + children: ExecutionGraphComparisonResult[]; + /** + * The group index of the ExecutionGraphComparisonResult. + */ + groupIndex: number; + /** + * Flag to indicate if the ExecutionGraphComparisonResult has a matching node in the compared execution plan. + */ + hasMatch: boolean; + /** + * List of matching nodes for the ExecutionGraphComparisonResult. + */ + matchingNodes: ExecutionGraphComparisonResult[]; + /** + * The parent of the ExecutionGraphComparisonResult. + */ + parentNode: ExecutionGraphComparisonResult; + } + + export interface ExecutionPlanComparisonResult extends ResultStatus { + firstComparisonResult: ExecutionGraphComparisonResult; + secondComparisonResult: ExecutionGraphComparisonResult; + } + export interface ExecutionPlanProvider extends DataProvider { // execution plan service methods @@ -1276,6 +1308,12 @@ declare module 'azdata' { * @param planFile file that contains the execution plan */ getExecutionPlan(planFile: ExecutionPlanGraphInfo): Thenable; + /** + * Compares two execution plans and identifies matching regions in both execution plans. + * @param firstPlanFile file that contains the first execution plan. + * @param secondPlanFile file that contains the second execution plan. + */ + compareExecutionPlanGraph(firstPlanFile: ExecutionPlanGraphInfo, secondPlanFile: ExecutionPlanGraphInfo): Thenable; } } diff --git a/src/sql/workbench/api/common/extHostDataProtocol.ts b/src/sql/workbench/api/common/extHostDataProtocol.ts index 61115b053e..2e6bbfb1f3 100644 --- a/src/sql/workbench/api/common/extHostDataProtocol.ts +++ b/src/sql/workbench/api/common/extHostDataProtocol.ts @@ -933,4 +933,8 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { public override $getExecutionPlan(handle: number, planFile: azdata.executionPlan.ExecutionPlanGraphInfo): Thenable { return this._resolveProvider(handle).getExecutionPlan(planFile); } + + public override $compareExecutionPlanGraph(handle: number, firstPlanFile: azdata.executionPlan.ExecutionPlanGraphInfo, secondPlanFile: azdata.executionPlan.ExecutionPlanGraphInfo): Thenable { + return this._resolveProvider(handle).compareExecutionPlanGraph(firstPlanFile, secondPlanFile); + } } diff --git a/src/sql/workbench/api/common/sqlExtHost.protocol.ts b/src/sql/workbench/api/common/sqlExtHost.protocol.ts index 16031ad236..57272b5ef9 100644 --- a/src/sql/workbench/api/common/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/common/sqlExtHost.protocol.ts @@ -572,6 +572,10 @@ export abstract class ExtHostDataProtocolShape { * Gets the generic execution plan graph for a plan file. */ $getExecutionPlan(handle: number, planFile: azdata.executionPlan.ExecutionPlanGraphInfo): Thenable { throw ni(); } + /** + * Compares two execution plans and identifies matching sections in both. + */ + $compareExecutionPlanGraph(handle: number, firstPlanFile: azdata.executionPlan.ExecutionPlanGraphInfo, secondPlanFile: azdata.executionPlan.ExecutionPlanGraphInfo): Thenable { throw ni(); } } /** diff --git a/src/sql/workbench/services/executionPlan/common/executionPlanService.ts b/src/sql/workbench/services/executionPlan/common/executionPlanService.ts index 6f09f6b69d..6b29d4eb7b 100644 --- a/src/sql/workbench/services/executionPlan/common/executionPlanService.ts +++ b/src/sql/workbench/services/executionPlan/common/executionPlanService.ts @@ -123,6 +123,12 @@ export class ExecutionPlanService implements IExecutionPlanService { }); } + compareExecutionPlanGraph(firstPlanFile: azdata.executionPlan.ExecutionPlanGraphInfo, secondPlanFile: azdata.executionPlan.ExecutionPlanGraphInfo): Promise { + return this._runAction(firstPlanFile.graphFileType, (runner) => { + return runner.compareExecutionPlanGraph(firstPlanFile, secondPlanFile); + }); + } + getSupportedExecutionPlanExtensionsForProvider(providerId: string): string[] | undefined { return this._capabilitiesService.getCapabilities(providerId).connection.supportedExecutionPlanFileExtensions; } diff --git a/src/sql/workbench/services/executionPlan/common/interfaces.ts b/src/sql/workbench/services/executionPlan/common/interfaces.ts index 332187f036..1517353017 100644 --- a/src/sql/workbench/services/executionPlan/common/interfaces.ts +++ b/src/sql/workbench/services/executionPlan/common/interfaces.ts @@ -22,6 +22,12 @@ export interface IExecutionPlanService { * Gets an execution plan for the given planFile. */ getExecutionPlan(planFile: azdata.executionPlan.ExecutionPlanGraphInfo): Promise; + /** + * Compares two execution plans and identifies matching regions in both execution plans. + * @param firstPlanFile file that contains the first execution plan. + * @param secondPlanFile file that contains the second execution plan. + */ + compareExecutionPlanGraph(firstPlanFile: azdata.executionPlan.ExecutionPlanGraphInfo, secondPlanFile: azdata.executionPlan.ExecutionPlanGraphInfo): Promise; /** * Get execution plan file extensions supported by the provider.