Add query execution plan extensibility APIs (#4072)

* WIP 1

* WIP 2

* Fix typos

* Iterate on API a bit

* Query Tab WIP

* More dynamic query tab impl

* Fix merge breaks

* Update interfaces

* Update to single event handler for query events

* Remove query plan extension

* Add generated JS file
This commit is contained in:
Karl Burtram
2019-03-28 10:59:02 -07:00
committed by GitHub
parent ee413f3b24
commit cc2951265e
18 changed files with 688 additions and 12 deletions

View File

@@ -16,7 +16,8 @@ import {
EditSubsetResult,
EditCreateRowResult,
EditRevertCellResult,
ExecutionPlanOptions
ExecutionPlanOptions,
queryeditor
} from 'azdata';
import { QueryInfo } from 'sql/platform/query/common/queryModelService';
@@ -24,6 +25,18 @@ export const SERVICE_ID = 'queryModelService';
export const IQueryModelService = createDecorator<IQueryModelService>(SERVICE_ID);
export interface IQueryPlanInfo {
providerId: string;
fileUri: string;
planXml: string;
}
export interface IQueryEvent {
type: queryeditor.QueryEvent;
uri: string;
params?: any;
}
/**
* Interface for the logic of handling running queries and grid interactions for all URIs.
*/
@@ -56,7 +69,7 @@ export interface IQueryModelService {
onRunQueryStart: Event<string>;
onRunQueryComplete: Event<string>;
onQueryEvent: Event<IQueryEvent>;
// Edit Data Functions
initializeEdit(ownerUri: string, schemaName: string, objectName: string, objectType: string, rowLimit: number, queryString: string): void;

View File

@@ -9,7 +9,7 @@ import * as GridContentEvents from 'sql/parts/grid/common/gridContentEvents';
import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants';
import QueryRunner, { EventType as QREvents } from 'sql/platform/query/common/queryRunner';
import { DataService } from 'sql/parts/grid/services/dataService';
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
import { IQueryModelService, IQueryPlanInfo, IQueryEvent } from 'sql/platform/query/common/queryModel';
import { QueryInput } from 'sql/parts/query/common/queryInput';
import { QueryStatusbarItem } from 'sql/parts/query/execution/queryStatus';
import { SqlFlavorStatusbarItem } from 'sql/parts/query/common/flavorStatus';
@@ -68,11 +68,13 @@ export class QueryModelService implements IQueryModelService {
private _queryInfoMap: Map<string, QueryInfo>;
private _onRunQueryStart: Emitter<string>;
private _onRunQueryComplete: Emitter<string>;
private _onQueryEvent: Emitter<IQueryEvent>;
private _onEditSessionReady: Emitter<azdata.EditSessionReadyParams>;
// EVENTS /////////////////////////////////////////////////////////////
public get onRunQueryStart(): Event<string> { return this._onRunQueryStart.event; }
public get onRunQueryComplete(): Event<string> { return this._onRunQueryComplete.event; }
public get onQueryEvent(): Event<IQueryEvent> { return this._onQueryEvent.event; }
public get onEditSessionReady(): Event<azdata.EditSessionReadyParams> { return this._onEditSessionReady.event; }
// CONSTRUCTOR /////////////////////////////////////////////////////////
@@ -83,6 +85,7 @@ export class QueryModelService implements IQueryModelService {
this._queryInfoMap = new Map<string, QueryInfo>();
this._onRunQueryStart = new Emitter<string>();
this._onRunQueryComplete = new Emitter<string>();
this._onQueryEvent = new Emitter<IQueryEvent>();
this._onEditSessionReady = new Emitter<azdata.EditSessionReadyParams>();
// Register Statusbar items
@@ -308,13 +311,40 @@ export class QueryModelService implements IQueryModelService {
});
queryRunner.addListener(QREvents.COMPLETE, totalMilliseconds => {
this._onRunQueryComplete.fire(uri);
// fire extensibility API event
let event: IQueryEvent = {
type: 'queryStop',
uri: uri
};
this._onQueryEvent.fire(event);
// fire UI event
this._fireQueryEvent(uri, 'complete', totalMilliseconds);
});
queryRunner.addListener(QREvents.START, () => {
this._onRunQueryStart.fire(uri);
// fire extensibility API event
let event: IQueryEvent = {
type: 'queryStart',
uri: uri
};
this._onQueryEvent.fire(event);
this._fireQueryEvent(uri, 'start');
});
queryRunner.addListener(QREvents.QUERY_PLAN_AVAILABLE, (planInfo) => {
// fire extensibility API event
let event: IQueryEvent = {
type: 'executionPlan',
uri: planInfo.fileUri,
params: planInfo
};
this._onQueryEvent.fire(event);
});
info.queryRunner = queryRunner;
info.dataService = this._instantiationService.createInstance(DataService, uri);
this._queryInfoMap.set(uri, info);
@@ -422,10 +452,26 @@ export class QueryModelService implements IQueryModelService {
});
queryRunner.addListener(QREvents.COMPLETE, totalMilliseconds => {
this._onRunQueryComplete.fire(ownerUri);
// fire extensibility API event
let event: IQueryEvent = {
type: 'queryStop',
uri: ownerUri
};
this._onQueryEvent.fire(event);
// fire UI event
this._fireQueryEvent(ownerUri, 'complete', totalMilliseconds);
});
queryRunner.addListener(QREvents.START, () => {
this._onRunQueryStart.fire(ownerUri);
// fire extensibility API event
let event: IQueryEvent = {
type: 'queryStart',
uri: ownerUri
};
this._onQueryEvent.fire(event);
// fire UI event
this._fireQueryEvent(ownerUri, 'start');
});
queryRunner.addListener(QREvents.EDIT_SESSION_READY, e => {

View File

@@ -25,6 +25,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ResultSerializer } from 'sql/platform/node/resultSerializer';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IQueryPlanInfo } from 'sql/platform/query/common/queryModel';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration';
import { URI } from 'vs/base/common/uri';
@@ -41,7 +42,8 @@ export const enum EventType {
BATCH_START = 'batchStart',
BATCH_COMPLETE = 'batchComplete',
RESULT_SET = 'resultSet',
EDIT_SESSION_READY = 'editSessionReady'
EDIT_SESSION_READY = 'editSessionReady',
QUERY_PLAN_AVAILABLE = 'queryPlanAvailable'
}
export interface IEventType {
@@ -52,6 +54,7 @@ export interface IEventType {
batchComplete: azdata.BatchSummary;
resultSet: azdata.ResultSetSummary;
editSessionReady: IEditSessionReadyEvent;
queryPlanAvailable: IQueryPlanInfo;
}
export interface IGridMessage extends azdata.IResultMessage {
@@ -363,7 +366,11 @@ export default class QueryRunner extends Disposable {
// check if this result has show plan, this needs work, it won't work for any other provider
let hasShowPlan = !!result.resultSetSummary.columnInfo.find(e => e.columnName === 'Microsoft SQL Server 2005 XML Showplan');
if (hasShowPlan) {
this.getQueryRows(0, 1, result.resultSetSummary.batchId, result.resultSetSummary.id).then(e => this._planXml.resolve(e.resultSubset.rows[0][0].displayValue));
this.getQueryRows(0, 1, result.resultSetSummary.batchId, result.resultSetSummary.id).then(e => {
if (e.resultSubset.rows) {
this._planXml.resolve(e.resultSubset.rows[0][0].displayValue);
}
});
}
}
// we will just ignore the set if we already have it
@@ -387,7 +394,20 @@ export default class QueryRunner extends Disposable {
// check if this result has show plan, this needs work, it won't work for any other provider
let hasShowPlan = !!result.resultSetSummary.columnInfo.find(e => e.columnName === 'Microsoft SQL Server 2005 XML Showplan');
if (hasShowPlan) {
this.getQueryRows(0, 1, result.resultSetSummary.batchId, result.resultSetSummary.id).then(e => this._planXml.resolve(e.resultSubset.rows[0][0].displayValue));
this.getQueryRows(0, 1, result.resultSetSummary.batchId, result.resultSetSummary.id).then(e => {
if (e.resultSubset.rows) {
let planXmlString = e.resultSubset.rows[0][0].displayValue;
this._planXml.resolve(e.resultSubset.rows[0][0].displayValue);
// fire query plan available event if execution is completed
if (result.resultSetSummary.complete) {
this._eventEmitter.emit(EventType.QUERY_PLAN_AVAILABLE, {
providerId: 'MSSQL',
fileUri: result.ownerUri,
planXml: planXmlString
});
}
}
});
}
}
if (batchSet) {