Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Execute SQL statement at cursor location #934

Merged
merged 7 commits into from
Jul 14, 2017
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
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"onLanguage:sql",
"onCommand:extension.connect",
"onCommand:extension.runQuery",
"onCommand:extension.runCurrentStatement",
"onCommand:extension.disconnect",
"onCommand:extension.manageProfiles",
"onCommand:extension.chooseDatabase",
Expand Down Expand Up @@ -158,6 +159,11 @@
"title": "Execute Query",
"category": "MS SQL"
},
{
"command": "extension.runCurrentStatement",
"title": "Execute Current Statement",
"category": "MS SQL"
},
{
"command": "extension.cancelQuery",
"title": "Cancel Query",
Expand Down
2 changes: 1 addition & 1 deletion src/configurations/dev.config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"service": {
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "1.1.0-alpha.3",
"version": "1.1.0-alpha.5",
"downloadFileNames": {
"Windows_7_86": "win-x86-netcoreapp2.0.zip",
"Windows_7_64": "win-x64-netcoreapp2.0.zip",
Expand Down
2 changes: 1 addition & 1 deletion src/configurations/production.config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"service": {
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "1.1.0-alpha.3",
"version": "1.1.0-alpha.5",
"downloadFileNames": {
"Windows_7_86": "win-x86-netcoreapp2.0.zip",
"Windows_7_64": "win-x64-netcoreapp2.0.zip",
Expand Down
1 change: 1 addition & 0 deletions src/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const outputChannelName = 'MSSQL';
export const connectionConfigFilename = 'settings.json';
export const connectionsArrayName = 'mssql.connections';
export const cmdRunQuery = 'extension.runQuery';
export const cmdRunCurrentStatement = 'extension.runCurrentStatement';
export const cmdCancelQuery = 'extension.cancelQuery';
export const cmdConnect = 'extension.connect';
export const cmdDisconnect = 'extension.disconnect';
Expand Down
146 changes: 101 additions & 45 deletions src/controllers/mainController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import vscode = require('vscode');
import Constants = require('../constants/constants');
import LocalizedConstants = require('../constants/localizedConstants');
import Utils = require('../models/utils');
import { SqlOutputContentProvider } from '../models/SqlOutputContentProvider';
import { SqlOutputContentProvider } from '../models/sqlOutputContentProvider';
import { RebuildIntelliSenseNotification } from '../models/contracts/languageService';
import StatusView from '../views/statusView';
import ConnectionManager from './connectionManager';
Expand Down Expand Up @@ -101,6 +101,8 @@ export default class MainController implements vscode.Disposable {
this.registerCommand(Constants.cmdRunQuery);
this._event.on(Constants.cmdRunQuery, () => { self.onRunQuery(); });
this.registerCommand(Constants.cmdManageConnectionProfiles);
this._event.on(Constants.cmdRunCurrentStatement, () => { self.onRunCurrentStatement(); });
this.registerCommand(Constants.cmdRunCurrentStatement);
this._event.on(Constants.cmdManageConnectionProfiles, () => { self.runAndLogErrors(self.onManageProfiles(), 'onManageProfiles'); });
this.registerCommand(Constants.cmdChooseDatabase);
this._event.on(Constants.cmdChooseDatabase, () => { self.runAndLogErrors(self.onChooseDatabase(), 'onChooseDatabase') ; } );
Expand Down Expand Up @@ -254,6 +256,42 @@ export default class MainController implements vscode.Disposable {
}
}

/**
* execute the SQL statement for the current cursor position
*/
public onRunCurrentStatement(): void {
try {
if (!this.CanRunCommand()) {
return;
}

// check if we're connected and editing a SQL file
if (this.isRetryRequiredBeforeQuery(this.onRunCurrentStatement)) {
return;
}

let editor = this._vscodeWrapper.activeTextEditor;
let uri = this._vscodeWrapper.activeTextEditorUri;
let title = path.basename(editor.document.fileName);
let querySelection: ISelectionData;

Telemetry.sendTelemetryEvent('RunCurrentStatement');

// only the start line and column are used to determine the current statement
querySelection = {
startLine: editor.selection.start.line,
startColumn: editor.selection.start.character,
endLine: 0,
endColumn: 0
};

this._outputContentProvider.runCurrentStatement(this._statusview, uri, querySelection, title);
} catch (err) {
Telemetry.sendTelemetryEventForException(err, 'onRunCurrentStatement');
}

}

/**
* get the T-SQL query from the editor, run it and show output
*/
Expand All @@ -262,57 +300,75 @@ export default class MainController implements vscode.Disposable {
if (!this.CanRunCommand()) {
return;
}
const self = this;
if (!this._vscodeWrapper.isEditingSqlFile) {
// Prompt the user to change the language mode to SQL before running a query
this._connectionMgr.connectionUI.promptToChangeLanguageMode().then( result => {
if (result) {
self.onRunQuery();
}
}).catch(err => {
self._vscodeWrapper.showErrorMessage(LocalizedConstants.msgError + err);
});
} else if (!this._connectionMgr.isConnected(this._vscodeWrapper.activeTextEditorUri)) {
// If we are disconnected, prompt the user to choose a connection before executing
this.onNewConnection().then(result => {
if (result) {
self.onRunQuery();
}
}).catch(err => {
self._vscodeWrapper.showErrorMessage(LocalizedConstants.msgError + err);
});
} else {
let editor = this._vscodeWrapper.activeTextEditor;
let uri = this._vscodeWrapper.activeTextEditorUri;
let title = path.basename(editor.document.fileName);
let querySelection: ISelectionData;

// Calculate the selection if we have a selection, otherwise we'll use null to indicate
// the entire document is the selection
if (!editor.selection.isEmpty) {
let selection = editor.selection;
querySelection = {
startLine: selection.start.line,
startColumn: selection.start.character,
endLine: selection.end.line,
endColumn: selection.end.character
};
}

// Trim down the selection. If it is empty after selecting, then we don't execute
let selectionToTrim = editor.selection.isEmpty ? undefined : editor.selection;
if (editor.document.getText(selectionToTrim).trim().length === 0) {
return;
}
// check if we're connected and editing a SQL file
if (this.isRetryRequiredBeforeQuery(this.onRunQuery)) {
return;
}

Telemetry.sendTelemetryEvent('RunQuery');
let editor = this._vscodeWrapper.activeTextEditor;
let uri = this._vscodeWrapper.activeTextEditorUri;
let title = path.basename(editor.document.fileName);
let querySelection: ISelectionData;

// Calculate the selection if we have a selection, otherwise we'll use null to indicate
// the entire document is the selection
if (!editor.selection.isEmpty) {
let selection = editor.selection;
querySelection = {
startLine: selection.start.line,
startColumn: selection.start.character,
endLine: selection.end.line,
endColumn: selection.end.character
};
}

this._outputContentProvider.runQuery(this._statusview, uri, querySelection, title);
// Trim down the selection. If it is empty after selecting, then we don't execute
let selectionToTrim = editor.selection.isEmpty ? undefined : editor.selection;
if (editor.document.getText(selectionToTrim).trim().length === 0) {
return;
}

Telemetry.sendTelemetryEvent('RunQuery');

this._outputContentProvider.runQuery(this._statusview, uri, querySelection, title);
} catch (err) {
Telemetry.sendTelemetryEventForException(err, 'OnRunquery');
Telemetry.sendTelemetryEventForException(err, 'onRunQuery');
}
}

/**
* Check if the state is ready to execute a query and retry
* the query execution method if needed
*/
public isRetryRequiredBeforeQuery(retryMethod: any): boolean {
const self = this;
if (!this._vscodeWrapper.isEditingSqlFile) {
// Prompt the user to change the language mode to SQL before running a query
this._connectionMgr.connectionUI.promptToChangeLanguageMode().then( result => {
if (result) {
retryMethod();
}
}).catch(err => {
self._vscodeWrapper.showErrorMessage(LocalizedConstants.msgError + err);
});
return true;

} else if (!this._connectionMgr.isConnected(this._vscodeWrapper.activeTextEditorUri)) {
// If we are disconnected, prompt the user to choose a connection before executing
this.onNewConnection().then(result => {
if (result) {
retryMethod();
}
}).catch(err => {
self._vscodeWrapper.showErrorMessage(LocalizedConstants.msgError + err);
});
return true;
} else {

// we don't need to do anything to configure environment before running query
return false;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Class for handler and distributing notification coming from the
* service layer
*/
import QueryRunner from './QueryRunner';
import QueryRunner from './queryRunner';
import SqlToolsServiceClient from '../languageservice/serviceclient';
import {
QueryExecuteCompleteNotification,
Expand Down
59 changes: 44 additions & 15 deletions src/controllers/QueryRunner.ts → src/controllers/queryRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ import { EventEmitter } from 'events';

import StatusView from '../views/statusView';
import SqlToolsServerClient from '../languageservice/serviceclient';
import {QueryNotificationHandler} from './QueryNotificationHandler';
import {QueryNotificationHandler} from './queryNotificationHandler';
import VscodeWrapper from './vscodeWrapper';
import { BatchSummary, QueryExecuteParams, QueryExecuteRequest,
QueryExecuteStatementParams, QueryExecuteStatementRequest,
QueryExecuteCompleteNotificationResult, QueryExecuteSubsetResult,
QueryExecuteResultSetCompleteNotificationParams,
QueryExecuteSubsetParams, QueryExecuteSubsetRequest,
QueryExecuteMessageParams,
QueryExecuteBatchNotificationParams } from '../models/contracts/queryExecute';
import { QueryDisposeParams, QueryDisposeRequest } from '../models/contracts/QueryDispose';
import { QueryCancelParams, QueryCancelResult, QueryCancelRequest } from '../models/contracts/QueryCancel';
import { QueryDisposeParams, QueryDisposeRequest } from '../models/contracts/queryDispose';
import { QueryCancelParams, QueryCancelResult, QueryCancelRequest } from '../models/contracts/queryCancel';
import { ISlickRange, ISelectionData } from '../models/interfaces';
import Constants = require('../constants/constants');
import LocalizedConstants = require('../constants/localizedConstants');
Expand Down Expand Up @@ -110,35 +111,63 @@ export default class QueryRunner {
return this._client.sendRequest(QueryCancelRequest.type, cancelParams);
}

// Pulls the query text from the current document/selection and initiates the query
public runStatement(line: number, column: number): Thenable<void> {
return this.doRunQuery(
<ISelectionData>{ startLine: line, startColumn: column, endLine: 0, endColumn: 0 },
(onSuccess, onError) => {
// Put together the request
let queryDetails: QueryExecuteStatementParams = {
ownerUri: this._uri,
line: line,
column: column
};

// Send the request to execute the query
return this._client.sendRequest(QueryExecuteStatementRequest.type, queryDetails).then(onSuccess, onError);
});
}

// Pulls the query text from the current document/selection and initiates the query
public runQuery(selection: ISelectionData): Thenable<void> {
return this.doRunQuery(
selection,
(onSuccess, onError) => {
// Put together the request
let queryDetails: QueryExecuteParams = {
ownerUri: this._uri,
querySelection: selection
};

// Send the request to execute the query
return this._client.sendRequest(QueryExecuteRequest.type, queryDetails).then(onSuccess, onError);
});
}

// Pulls the query text from the current document/selection and initiates the query
private doRunQuery(selection: ISelectionData, queryCallback: any): Thenable<void> {
const self = this;
this._vscodeWrapper.logToOutputChannel(Utils.formatString(LocalizedConstants.msgStartedExecute, this._uri));

// Put together the request
let queryDetails: QueryExecuteParams = {
ownerUri: this._uri,
querySelection: selection
};

// Update internal state to show that we're executing the query
this._resultLineOffset = selection ? selection.startLine : 0;
this._isExecuting = true;
this._totalElapsedMilliseconds = 0;
this._statusView.executingQuery(this.uri);

// Send the request to execute the query
return this._client.sendRequest(QueryExecuteRequest.type, queryDetails).then(result => {
let onSuccess = (result) => {
// The query has started, so lets fire up the result pane
self.eventEmitter.emit('start');
self._notificationHandler.registerRunner(self, queryDetails.ownerUri);
}, error => {
// Attempting to launch the query failed, show the error message
self._notificationHandler.registerRunner(self, self._uri);
};
let onError = (error) => {
self._statusView.executedQuery(self.uri);
self._isExecuting = false;
// TODO: localize
self._vscodeWrapper.showErrorMessage('Execution failed: ' + error.message);
});
};

return queryCallback(onSuccess, onError);
}

// handle the result of the notification
Expand Down
14 changes: 14 additions & 0 deletions src/models/contracts/queryExecute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,25 @@ export namespace QueryExecuteRequest {
};
}

export namespace QueryExecuteStatementRequest {
export const type: RequestType<QueryExecuteStatementParams, QueryExecuteResult, void> = {
get method(): string {
return 'query/executedocumentstatement';
}
};
}

export class QueryExecuteParams {
ownerUri: string;
querySelection: ISelectionData;
}

export class QueryExecuteStatementParams {
ownerUri: string;
line: number;
column: number;
}

export class QueryExecuteResult {}

// --------------------------------- < Query Results Request > ------------------------------------------
Expand Down
Loading