diff --git a/extensions/mssql/package.json b/extensions/mssql/package.json index 2b79b7154e..f8d32e55dc 100644 --- a/extensions/mssql/package.json +++ b/extensions/mssql/package.json @@ -1258,7 +1258,7 @@ "ads-kerberos": "^1.1.3", "buffer-stream-reader": "^0.1.1", "bytes": "^3.1.0", - "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.1.0", + "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.2.0", "error-ex": "^1.3.2", "figures": "^2.0.0", "find-remove": "1.2.1", diff --git a/extensions/mssql/src/contracts.ts b/extensions/mssql/src/contracts.ts index 43460dcaf9..2dd681ebf9 100644 --- a/extensions/mssql/src/contracts.ts +++ b/extensions/mssql/src/contracts.ts @@ -777,3 +777,227 @@ export interface ConvertSqlToNotebookResult extends azdata.ResultStatus { } // ------------------------------- ----------------------------- + +// ------------------------------- < SQL Profiler > ------------------------------------ + +/** + * Parameters to start a profiler session + */ +export interface CreateXEventSessionParams { + /** + * Session Owner URI + */ + ownerUri: string; + + /** + * Session name + */ + sessionName: string; + + /** + * Profiler Session template + */ + template: ProfilerSessionTemplate; +} + +export interface CreateXEventSessionResponse { } + +/** + * Parameters to start a profiler session + */ +export interface StartProfilingParams { + /** + * Session Owner URI + */ + ownerUri: string; + + /** + * Session name + */ + sessionName: string; +} + +export interface StartProfilingResponse { } + +/** + * Parameters to stop a profiler session + */ +export interface StopProfilingParams { + /** + * Session Owner URI + */ + ownerUri: string; +} + +export interface StopProfilingResponse { } + +/** + * Parameters to pause a profiler session + */ +export interface PauseProfilingParams { + /** + * Session Owner URI + */ + ownerUri: string; +} + +export interface PauseProfilingResponse { } + +/** + * Parameters to get a list of XEvent sessions + */ +export interface GetXEventSessionsParams { + /** + * Session Owner URI + */ + ownerUri: string; +} + +export interface GetXEventSessionsResponse { + /** + * List of all running XEvent Sessions on target server + */ + sessions: string[]; +} + +export interface DisconnectSessionParams { + /** + * Session Owner URI + */ + ownerUri: string; +} + +export interface DisconnectSessionResponse { } + +/** + * Profiler Event + */ +export interface ProfilerEvent { + /** + * Event class name + */ + name: string; + + /** + * Event timestamp + */ + timestamp: string; + + /** + * Event values + */ + values: {}; +} + +/** + * Profiler Session Template + */ +export interface ProfilerSessionTemplate { + /** + * Template name + */ + name: string; + + /** + * Default view for template + */ + defaultView: string; + + /** + * TSQL for creating a session + */ + createStatement: string; +} + +/** + * Profiler events available notification parameters + */ +export interface ProfilerEventsAvailableParams { + /** + * Session owner URI + */ + ownerUri: string; + + /** + * New profiler events available + */ + events: ProfilerEvent[]; + + /** + * If events may have been dropped + */ + eventsLost: boolean; +} + +/** + * Profiler events available notification parameters + */ +export interface ProfilerSessionStoppedParams { + /** + * Session owner URI + */ + ownerUri: string; + + /** + * Stopped session Id + */ + sessionId: number; +} + +/** + * Profiler session created notification parameters + */ +export interface ProfilerSessionCreatedParams { + /** + * Session owner URI + */ + ownerUri: string; + + /** + * Created session name + */ + sessionName: string; + + /** + * Template used to create session + */ + templateName: string; +} + +export namespace CreateXEventSessionRequest { + export const type = new RequestType('profiler/createsession'); +} + +export namespace StartProfilingRequest { + export const type = new RequestType('profiler/start'); +} + +export namespace StopProfilingRequest { + export const type = new RequestType('profiler/stop'); +} + +export namespace PauseProfilingRequest { + export const type = new RequestType('profiler/pause'); +} + +export namespace GetXEventSessionsRequest { + export const type = new RequestType('profiler/getsessions'); +} + +export namespace DisconnectSessionRequest { + export const type = new RequestType('profiler/disconnect'); +} + +export namespace ProfilerEventsAvailableNotification { + export const type = new NotificationType('profiler/eventsavailable'); +} + +export namespace ProfilerSessionStoppedNotification { + export const type = new NotificationType('profiler/sessionstopped'); +} + +export namespace ProfilerSessionCreatedNotification { + export const type = new NotificationType('profiler/sessioncreated'); +} + +// ------------------------------- < SQL Profiler > ------------------------------------ diff --git a/extensions/mssql/src/features.ts b/extensions/mssql/src/features.ts index 23b27e8c86..77ece877cf 100644 --- a/extensions/mssql/src/features.ts +++ b/extensions/mssql/src/features.ts @@ -918,6 +918,166 @@ export class SqlAssessmentServicesFeature extends SqlOpsFeature { generateAssessmentScript }); } - - } + +export class ProfilerFeature extends SqlOpsFeature { + private static readonly messagesTypes: RPCMessageType[] = [ + contracts.StartProfilingRequest.type, + contracts.StopProfilingRequest.type, + contracts.ProfilerEventsAvailableNotification.type + ]; + + constructor(client: SqlOpsDataClient) { + super(client, ProfilerFeature.messagesTypes); + } + + public fillClientCapabilities(capabilities: ClientCapabilities): void { + } + + public initialize(capabilities: ServerCapabilities): void { + this.register(this.messages, { + id: UUID.generateUuid(), + registerOptions: undefined + }); + } + + protected registerProvider(options: undefined): Disposable { + const client = this._client; + + let createSession = (ownerUri: string, sessionName: string, template: azdata.ProfilerSessionTemplate): Thenable => { + let params: contracts.CreateXEventSessionParams = { + ownerUri, + sessionName, + template + }; + + return client.sendRequest(contracts.CreateXEventSessionRequest.type, params).then( + r => true, + e => { + client.logFailedRequest(contracts.CreateXEventSessionRequest.type, e); + return Promise.reject(e); + } + ); + }; + + let startSession = (ownerUri: string, sessionName: string): Thenable => { + let params: contracts.StartProfilingParams = { + ownerUri, + sessionName + }; + + return client.sendRequest(contracts.StartProfilingRequest.type, params).then( + r => true, + e => { + client.logFailedRequest(contracts.StartProfilingRequest.type, e); + return Promise.reject(e); + } + ); + }; + + let stopSession = (ownerUri: string): Thenable => { + let params: contracts.StopProfilingParams = { + ownerUri + }; + + return client.sendRequest(contracts.StopProfilingRequest.type, params).then( + r => true, + e => { + client.logFailedRequest(contracts.StopProfilingRequest.type, e); + return Promise.reject(e); + } + ); + }; + + let pauseSession = (ownerUri: string): Thenable => { + let params: contracts.PauseProfilingParams = { + ownerUri + }; + + return client.sendRequest(contracts.PauseProfilingRequest.type, params).then( + r => true, + e => { + client.logFailedRequest(contracts.PauseProfilingRequest.type, e); + return Promise.reject(e); + } + ); + }; + + let getXEventSessions = (ownerUri: string): Thenable => { + let params: contracts.GetXEventSessionsParams = { + ownerUri + }; + + return client.sendRequest(contracts.GetXEventSessionsRequest.type, params).then( + r => r.sessions, + e => { + client.logFailedRequest(contracts.GetXEventSessionsRequest.type, e); + return Promise.reject(e); + } + ); + }; + + let connectSession = (sessionId: string): Thenable => { + return undefined; + }; + + let disconnectSession = (ownerUri: string): Thenable => { + let params: contracts.DisconnectSessionParams = { + ownerUri: ownerUri + }; + return client.sendRequest(contracts.DisconnectSessionRequest.type, params).then( + r => true, + e => { + client.logFailedRequest(contracts.DisconnectSessionRequest.type, e); + return Promise.reject(e); + } + ); + }; + + let registerOnSessionEventsAvailable = (handler: (response: azdata.ProfilerSessionEvents) => any): void => { + client.onNotification(contracts.ProfilerEventsAvailableNotification.type, (params: contracts.ProfilerEventsAvailableParams) => { + handler({ + sessionId: params.ownerUri, + events: params.events, + eventsLost: params.eventsLost + }); + }); + }; + + + let registerOnSessionStopped = (handler: (response: azdata.ProfilerSessionStoppedParams) => any): void => { + client.onNotification(contracts.ProfilerSessionStoppedNotification.type, (params: contracts.ProfilerSessionStoppedParams) => { + handler({ + ownerUri: params.ownerUri, + sessionId: params.sessionId + }); + }); + }; + + let registerOnProfilerSessionCreated = (handler: (response: azdata.ProfilerSessionCreatedParams) => any): void => { + client.onNotification(contracts.ProfilerSessionCreatedNotification.type, (params: contracts.ProfilerSessionCreatedParams) => { + handler({ + ownerUri: params.ownerUri, + sessionName: params.sessionName, + templateName: params.templateName + }); + }); + }; + + + return azdata.dataprotocol.registerProfilerProvider({ + providerId: client.providerId, + connectSession, + disconnectSession, + registerOnSessionEventsAvailable, + registerOnSessionStopped, + registerOnProfilerSessionCreated, + createSession, + startSession, + stopSession, + pauseSession, + getXEventSessions + }); + } +} + diff --git a/extensions/mssql/src/sqlToolsServer.ts b/extensions/mssql/src/sqlToolsServer.ts index 0d0979ccda..02c55a437e 100644 --- a/extensions/mssql/src/sqlToolsServer.ts +++ b/extensions/mssql/src/sqlToolsServer.ts @@ -11,7 +11,7 @@ import * as path from 'path'; import { getCommonLaunchArgsAndCleanupOldLogFiles } from './utils'; import { Telemetry, LanguageClientErrorHandler } from './telemetry'; import { SqlOpsDataClient, ClientOptions } from 'dataprotocol-client'; -import { TelemetryFeature, AgentServicesFeature, SerializationFeature, AccountFeature, SqlAssessmentServicesFeature } from './features'; +import { TelemetryFeature, AgentServicesFeature, SerializationFeature, AccountFeature, SqlAssessmentServicesFeature, ProfilerFeature } from './features'; import { CredentialStore } from './credentialstore/credentialstore'; import { AzureResourceProvider } from './resourceProvider/resourceProvider'; import { SchemaCompareService } from './schemaCompare/schemaCompareService'; @@ -162,7 +162,8 @@ function getClientOptions(context: AppContext): ClientOptions { DacFxService.asFeature(context), CmsService.asFeature(context), SqlAssessmentService.asFeature(context), - NotebookConvertService.asFeature(context) + NotebookConvertService.asFeature(context), + ProfilerFeature ], outputChannel: new CustomOutputChannel() }; diff --git a/extensions/mssql/yarn.lock b/extensions/mssql/yarn.lock index 0a4b7398ac..9046428dd6 100644 --- a/extensions/mssql/yarn.lock +++ b/extensions/mssql/yarn.lock @@ -537,9 +537,9 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.1.0": - version "1.1.0" - resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/61a6e4dd4662a225259b6ba8a7eca215fab0cfdc" +"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.2.0": + version "1.2.0" + resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/b0cdc1380ce30feda6531dc76faa67c339af1724" dependencies: vscode-languageclient "5.2.1"