mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Query Editor Memory improvements (#3041)
* working through memory issues * work in progress * add missing value * work in progress * more work in progress * various more memory fixes * additional improvements * fix imports * change test that we dispose queries on close not dispose * update tests
This commit is contained in:
@@ -36,7 +36,6 @@ export interface IPanelTab {
|
|||||||
interface IInternalPanelTab extends IPanelTab {
|
interface IInternalPanelTab extends IPanelTab {
|
||||||
header: HTMLElement;
|
header: HTMLElement;
|
||||||
label: HTMLElement;
|
label: HTMLElement;
|
||||||
dispose(): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultOptions: IPanelOptions = {
|
const defaultOptions: IPanelOptions = {
|
||||||
@@ -143,8 +142,6 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
this.tabList.appendChild(tabHeaderElement);
|
this.tabList.appendChild(tabHeaderElement);
|
||||||
tab.header = tabHeaderElement;
|
tab.header = tabHeaderElement;
|
||||||
tab.label = tabLabel;
|
tab.label = tabLabel;
|
||||||
tab.dispose = () => { };
|
|
||||||
this._register(tab);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public showTab(id: PanelTabIdentifier): void {
|
public showTab(id: PanelTabIdentifier): void {
|
||||||
|
|||||||
@@ -3,11 +3,14 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { IDisposableDataProvider } from 'sql/base/browser/ui/table/interfaces';
|
||||||
|
|
||||||
export interface IObservableCollection<T> {
|
export interface IObservableCollection<T> {
|
||||||
getLength(): number;
|
getLength(): number;
|
||||||
at(index: number): T;
|
at(index: number): T;
|
||||||
getRange(start: number, end: number): T[];
|
getRange(start: number, end: number): T[];
|
||||||
setCollectionChangedCallback(callback: (change: CollectionChange, startIndex: number, count: number) => void): void;
|
setCollectionChangedCallback(callback: (change: CollectionChange, startIndex: number, count: number) => void): void;
|
||||||
|
dispose(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IGridDataRow {
|
export interface IGridDataRow {
|
||||||
@@ -24,24 +27,27 @@ class LoadCancellationToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DataWindow<TData> {
|
class DataWindow<TData> {
|
||||||
private _dataSourceLength: number;
|
|
||||||
private _data: TData[];
|
private _data: TData[];
|
||||||
private _length: number = 0;
|
private _length: number = 0;
|
||||||
private _offsetFromDataSource: number = -1;
|
private _offsetFromDataSource: number = -1;
|
||||||
|
|
||||||
private loadFunction: (offset: number, count: number) => Thenable<TData[]>;
|
|
||||||
private lastLoadCancellationToken: LoadCancellationToken;
|
private lastLoadCancellationToken: LoadCancellationToken;
|
||||||
private loadCompleteCallback: (start: number, end: number) => void;
|
|
||||||
private placeholderItemGenerator: (index: number) => TData;
|
|
||||||
|
|
||||||
constructor(dataSourceLength: number,
|
constructor(
|
||||||
loadFunction: (offset: number, count: number) => Thenable<TData[]>,
|
private loadFunction: (offset: number, count: number) => Thenable<TData[]>,
|
||||||
placeholderItemGenerator: (index: number) => TData,
|
private placeholderItemGenerator: (index: number) => TData,
|
||||||
loadCompleteCallback: (start: number, end: number) => void) {
|
private loadCompleteCallback: (start: number, end: number) => void
|
||||||
this._dataSourceLength = dataSourceLength;
|
) {
|
||||||
this.loadFunction = loadFunction;
|
}
|
||||||
this.placeholderItemGenerator = placeholderItemGenerator;
|
|
||||||
this.loadCompleteCallback = loadCompleteCallback;
|
dispose() {
|
||||||
|
this._data = undefined;
|
||||||
|
this.loadFunction = undefined;
|
||||||
|
this.placeholderItemGenerator = undefined;
|
||||||
|
this.loadCompleteCallback = undefined;
|
||||||
|
if (this.lastLoadCancellationToken) {
|
||||||
|
this.lastLoadCancellationToken.isCancelled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getStartIndex(): number {
|
getStartIndex(): number {
|
||||||
@@ -76,10 +82,9 @@ class DataWindow<TData> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cancellationToken = new LoadCancellationToken();
|
this.lastLoadCancellationToken = new LoadCancellationToken();
|
||||||
this.lastLoadCancellationToken = cancellationToken;
|
|
||||||
this.loadFunction(offset, length).then(data => {
|
this.loadFunction(offset, length).then(data => {
|
||||||
if (!cancellationToken.isCancelled) {
|
if (!this.lastLoadCancellationToken.isCancelled) {
|
||||||
this._data = data;
|
this._data = data;
|
||||||
this.loadCompleteCallback(this._offsetFromDataSource, this._offsetFromDataSource + this._length);
|
this.loadCompleteCallback(this._offsetFromDataSource, this._offsetFromDataSource + this._length);
|
||||||
}
|
}
|
||||||
@@ -97,10 +102,12 @@ export class VirtualizedCollection<TData> implements IObservableCollection<TData
|
|||||||
|
|
||||||
private collectionChangedCallback: (change: CollectionChange, startIndex: number, count: number) => void;
|
private collectionChangedCallback: (change: CollectionChange, startIndex: number, count: number) => void;
|
||||||
|
|
||||||
constructor(windowSize: number,
|
constructor(
|
||||||
|
windowSize: number,
|
||||||
length: number,
|
length: number,
|
||||||
loadFn: (offset: number, count: number) => Thenable<TData[]>,
|
loadFn: (offset: number, count: number) => Thenable<TData[]>,
|
||||||
private _placeHolderGenerator: (index: number) => TData) {
|
private _placeHolderGenerator: (index: number) => TData
|
||||||
|
) {
|
||||||
this._windowSize = windowSize;
|
this._windowSize = windowSize;
|
||||||
this._length = length;
|
this._length = length;
|
||||||
|
|
||||||
@@ -110,9 +117,15 @@ export class VirtualizedCollection<TData> implements IObservableCollection<TData
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this._bufferWindowBefore = new DataWindow(length, loadFn, _placeHolderGenerator, loadCompleteCallback);
|
this._bufferWindowBefore = new DataWindow(loadFn, _placeHolderGenerator, loadCompleteCallback);
|
||||||
this._window = new DataWindow(length, loadFn, _placeHolderGenerator, loadCompleteCallback);
|
this._window = new DataWindow(loadFn, _placeHolderGenerator, loadCompleteCallback);
|
||||||
this._bufferWindowAfter = new DataWindow(length, loadFn, _placeHolderGenerator, loadCompleteCallback);
|
this._bufferWindowAfter = new DataWindow(loadFn, _placeHolderGenerator, loadCompleteCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this._bufferWindowAfter.dispose();
|
||||||
|
this._bufferWindowBefore.dispose();
|
||||||
|
this._window.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
setCollectionChangedCallback(callback: (change: CollectionChange, startIndex: number, count: number) => void): void {
|
setCollectionChangedCallback(callback: (change: CollectionChange, startIndex: number, count: number) => void): void {
|
||||||
@@ -197,7 +210,7 @@ export class VirtualizedCollection<TData> implements IObservableCollection<TData
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AsyncDataProvider<TData extends IGridDataRow> implements Slick.DataProvider<TData> {
|
export class AsyncDataProvider<TData extends IGridDataRow> implements IDisposableDataProvider<TData> {
|
||||||
|
|
||||||
constructor(private dataRows: IObservableCollection<TData>) { }
|
constructor(private dataRows: IObservableCollection<TData>) { }
|
||||||
|
|
||||||
@@ -212,4 +225,8 @@ export class AsyncDataProvider<TData extends IGridDataRow> implements Slick.Data
|
|||||||
public getRange(start: number, end: number): TData[] {
|
public getRange(start: number, end: number): TData[] {
|
||||||
return !this.dataRows ? undefined : this.dataRows.getRange(start, end);
|
return !this.dataRows ? undefined : this.dataRows.getRange(start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.dataRows.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
31
src/sql/base/browser/ui/table/interfaces.ts
Normal file
31
src/sql/base/browser/ui/table/interfaces.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
|
||||||
|
import { Color } from 'vs/base/common/color';
|
||||||
|
|
||||||
|
export interface IDisposableDataProvider<T> extends Slick.DataProvider<T> {
|
||||||
|
dispose(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITableMouseEvent {
|
||||||
|
anchor: HTMLElement | { x: number, y: number };
|
||||||
|
cell?: { row: number, cell: number };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITableStyles extends IListStyles {
|
||||||
|
tableHeaderBackground?: Color;
|
||||||
|
tableHeaderForeground?: Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITableSorter<T> {
|
||||||
|
sort(args: Slick.OnSortEventArgs<T>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITableConfiguration<T> {
|
||||||
|
dataProvider?: IDisposableDataProvider<T> | Array<T>;
|
||||||
|
columns?: Slick.Column<T>[];
|
||||||
|
sorter?: ITableSorter<T>;
|
||||||
|
}
|
||||||
@@ -5,28 +5,18 @@
|
|||||||
|
|
||||||
import 'vs/css!./media/table';
|
import 'vs/css!./media/table';
|
||||||
import { TableDataView } from './tableDataView';
|
import { TableDataView } from './tableDataView';
|
||||||
|
import { IDisposableDataProvider, ITableSorter, ITableMouseEvent, ITableConfiguration, ITableStyles } from 'sql/base/browser/ui/table/interfaces';
|
||||||
|
|
||||||
import { IThemable } from 'vs/platform/theme/common/styler';
|
import { IThemable } from 'vs/platform/theme/common/styler';
|
||||||
import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
|
|
||||||
import * as DOM from 'vs/base/browser/dom';
|
import * as DOM from 'vs/base/browser/dom';
|
||||||
import { Color } from 'vs/base/common/color';
|
|
||||||
import { mixin } from 'vs/base/common/objects';
|
import { mixin } from 'vs/base/common/objects';
|
||||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
|
import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
|
||||||
import { Widget } from 'vs/base/browser/ui/widget';
|
import { Widget } from 'vs/base/browser/ui/widget';
|
||||||
import { isArray, isBoolean } from 'vs/base/common/types';
|
import { isArray, isBoolean } from 'vs/base/common/types';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import { range } from 'vs/base/common/arrays';
|
import { range } from 'vs/base/common/arrays';
|
||||||
|
import { $ } from 'vs/base/browser/builder';
|
||||||
export interface ITableMouseEvent {
|
|
||||||
anchor: HTMLElement | { x: number, y: number };
|
|
||||||
cell?: { row: number, cell: number };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ITableStyles extends IListStyles {
|
|
||||||
tableHeaderBackground?: Color;
|
|
||||||
tableHeaderForeground?: Color;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDefaultOptions<T>(): Slick.GridOptions<T> {
|
function getDefaultOptions<T>(): Slick.GridOptions<T> {
|
||||||
return <Slick.GridOptions<T>>{
|
return <Slick.GridOptions<T>>{
|
||||||
@@ -35,23 +25,13 @@ function getDefaultOptions<T>(): Slick.GridOptions<T> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITableSorter<T> {
|
|
||||||
sort(args: Slick.OnSortEventArgs<T>);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ITableConfiguration<T> {
|
|
||||||
dataProvider?: Slick.DataProvider<T> | Array<T>;
|
|
||||||
columns?: Slick.Column<T>[];
|
|
||||||
sorter?: ITableSorter<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Table<T extends Slick.SlickData> extends Widget implements IThemable, IDisposable {
|
export class Table<T extends Slick.SlickData> extends Widget implements IThemable, IDisposable {
|
||||||
private styleElement: HTMLStyleElement;
|
private styleElement: HTMLStyleElement;
|
||||||
private idPrefix: string;
|
private idPrefix: string;
|
||||||
|
|
||||||
private _grid: Slick.Grid<T>;
|
private _grid: Slick.Grid<T>;
|
||||||
private _columns: Slick.Column<T>[];
|
private _columns: Slick.Column<T>[];
|
||||||
private _data: Slick.DataProvider<T>;
|
private _data: IDisposableDataProvider<T>;
|
||||||
private _sorter: ITableSorter<T>;
|
private _sorter: ITableSorter<T>;
|
||||||
|
|
||||||
private _autoscroll: boolean;
|
private _autoscroll: boolean;
|
||||||
@@ -60,8 +40,6 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
|
|||||||
|
|
||||||
private _classChangeTimeout: number;
|
private _classChangeTimeout: number;
|
||||||
|
|
||||||
private _disposables: IDisposable[] = [];
|
|
||||||
|
|
||||||
private _onContextMenu = new Emitter<ITableMouseEvent>();
|
private _onContextMenu = new Emitter<ITableMouseEvent>();
|
||||||
public readonly onContextMenu: Event<ITableMouseEvent> = this._onContextMenu.event;
|
public readonly onContextMenu: Event<ITableMouseEvent> = this._onContextMenu.event;
|
||||||
|
|
||||||
@@ -76,6 +54,8 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
|
|||||||
this._data = configuration.dataProvider;
|
this._data = configuration.dataProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._register(this._data);
|
||||||
|
|
||||||
if (configuration && configuration.columns) {
|
if (configuration && configuration.columns) {
|
||||||
this._columns = configuration.columns;
|
this._columns = configuration.columns;
|
||||||
} else {
|
} else {
|
||||||
@@ -117,6 +97,12 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._register({
|
||||||
|
dispose: () => {
|
||||||
|
this._grid.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.mapMouseEvent(this._grid.onContextMenu, this._onContextMenu);
|
this.mapMouseEvent(this._grid.onContextMenu, this._onContextMenu);
|
||||||
this.mapMouseEvent(this._grid.onClick, this._onClick);
|
this.mapMouseEvent(this._grid.onClick, this._onClick);
|
||||||
}
|
}
|
||||||
@@ -131,7 +117,8 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
|
|||||||
}
|
}
|
||||||
|
|
||||||
public dispose() {
|
public dispose() {
|
||||||
dispose(this._disposables);
|
$(this._container).dispose();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public invalidateRows(rows: number[], keepEditor: boolean) {
|
public invalidateRows(rows: number[], keepEditor: boolean) {
|
||||||
@@ -166,7 +153,7 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
|
|||||||
this._grid.setData(this._data, true);
|
this._grid.setData(this._data, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
getData(): Slick.DataProvider<T> {
|
getData(): IDisposableDataProvider<T> {
|
||||||
return this._data;
|
return this._data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import { Event, Emitter } from 'vs/base/common/event';
|
|||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
import * as types from 'vs/base/common/types';
|
import * as types from 'vs/base/common/types';
|
||||||
|
|
||||||
|
import { IDisposableDataProvider } from 'sql/base/browser/ui/table/interfaces';
|
||||||
|
|
||||||
export interface IFindPosition {
|
export interface IFindPosition {
|
||||||
col: number;
|
col: number;
|
||||||
row: number;
|
row: number;
|
||||||
@@ -20,7 +22,7 @@ function defaultSort<T>(args: Slick.OnSortEventArgs<T>, data: Array<T>): Array<T
|
|||||||
return data.sort((a, b) => (a[field] === b[field] ? 0 : (a[field] > b[field] ? 1 : -1)) * sign);
|
return data.sort((a, b) => (a[field] === b[field] ? 0 : (a[field] > b[field] ? 1 : -1)) * sign);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TableDataView<T extends Slick.SlickData> implements Slick.DataProvider<T> {
|
export class TableDataView<T extends Slick.SlickData> implements IDisposableDataProvider<T> {
|
||||||
private _data: Array<T>;
|
private _data: Array<T>;
|
||||||
private _findArray: Array<IFindPosition>;
|
private _findArray: Array<IFindPosition>;
|
||||||
private _findObs: Observable<IFindPosition>;
|
private _findObs: Observable<IFindPosition>;
|
||||||
@@ -154,4 +156,10 @@ export class TableDataView<T extends Slick.SlickData> implements Slick.DataProvi
|
|||||||
get findCount(): number {
|
get findCount(): number {
|
||||||
return types.isUndefinedOrNull(this._findArray) ? 0 : this._findArray.length;
|
return types.isUndefinedOrNull(this._findArray) ? 0 : this._findArray.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this._data = undefined;
|
||||||
|
this._findArray = undefined;
|
||||||
|
this._findObs = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,6 @@ export interface IQueryEditorService {
|
|||||||
// Creates new edit data session
|
// Creates new edit data session
|
||||||
newEditDataEditor(schemaName: string, tableName: string, queryString: string): Promise<IConnectableInput>;
|
newEditDataEditor(schemaName: string, tableName: string, queryString: string): Promise<IConnectableInput>;
|
||||||
|
|
||||||
// Clears any QueryEditor data for the given URI held by this service
|
|
||||||
onQueryInputClosed(uri: string): void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles updating of SQL files on a save as event. These need special consideration
|
* Handles updating of SQL files on a save as event. These need special consideration
|
||||||
* due to query results and other information being tied to the URI of the file
|
* due to query results and other information being tied to the URI of the file
|
||||||
|
|||||||
@@ -116,11 +116,11 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this._configurationService) {
|
if (this._configurationService) {
|
||||||
this._configurationService.onDidChangeConfiguration(e => {
|
this._toDispose.push(this._configurationService.onDidChangeConfiguration(e => {
|
||||||
if (e.affectedKeys.includes('sql.showConnectionInfoInTitle')) {
|
if (e.affectedKeys.includes('sql.showConnectionInfoInTitle')) {
|
||||||
this._onDidChangeLabel.fire();
|
this._onDidChangeLabel.fire();
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onDisconnect();
|
this.onDisconnect();
|
||||||
@@ -196,17 +196,17 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
|
|||||||
|
|
||||||
// State update funtions
|
// State update funtions
|
||||||
public runQuery(selection: ISelectionData, executePlanOptions?: ExecutionPlanOptions): void {
|
public runQuery(selection: ISelectionData, executePlanOptions?: ExecutionPlanOptions): void {
|
||||||
this._queryModelService.runQuery(this.uri, selection, this.uri, this, executePlanOptions);
|
this._queryModelService.runQuery(this.uri, selection, this, executePlanOptions);
|
||||||
this.showQueryResultsEditor();
|
this.showQueryResultsEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public runQueryStatement(selection: ISelectionData): void {
|
public runQueryStatement(selection: ISelectionData): void {
|
||||||
this._queryModelService.runQueryStatement(this.uri, selection, this.uri, this);
|
this._queryModelService.runQueryStatement(this.uri, selection, this);
|
||||||
this.showQueryResultsEditor();
|
this.showQueryResultsEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public runQueryString(text: string): void {
|
public runQueryString(text: string): void {
|
||||||
this._queryModelService.runQueryString(this.uri, text, this.uri, this);
|
this._queryModelService.runQueryString(this.uri, text, this);
|
||||||
this.showQueryResultsEditor();
|
this.showQueryResultsEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +276,6 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
|
|||||||
|
|
||||||
// Clean up functions
|
// Clean up functions
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
this._queryModelService.disposeQuery(this.uri);
|
|
||||||
this._sql.dispose();
|
this._sql.dispose();
|
||||||
this._results.dispose();
|
this._results.dispose();
|
||||||
this._toDispose = dispose(this._toDispose);
|
this._toDispose = dispose(this._toDispose);
|
||||||
@@ -285,7 +284,7 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
|
|||||||
}
|
}
|
||||||
|
|
||||||
public close(): void {
|
public close(): void {
|
||||||
this._queryEditorService.onQueryInputClosed(this.uri);
|
this._queryModelService.disposeQuery(this.uri);
|
||||||
this._connectionManagementService.disconnectEditor(this, true);
|
this._connectionManagementService.disconnectEditor(this, true);
|
||||||
|
|
||||||
this._sql.close();
|
this._sql.close();
|
||||||
|
|||||||
@@ -215,6 +215,7 @@ export class QueryManagementService implements IQueryManagementService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
public disposeQuery(ownerUri: string): Thenable<void> {
|
public disposeQuery(ownerUri: string): Thenable<void> {
|
||||||
|
this._queryRunners.delete(ownerUri);
|
||||||
return this._runAction(ownerUri, (runner) => {
|
return this._runAction(ownerUri, (runner) => {
|
||||||
return runner.disposeQuery(ownerUri);
|
return runner.disposeQuery(ownerUri);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,6 +29,13 @@ export class ResultsViewState {
|
|||||||
constructor(@IConfigurationService private configurationService: IConfigurationService) {
|
constructor(@IConfigurationService private configurationService: IConfigurationService) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.gridPanelState.dispose();
|
||||||
|
this.messagePanelState.dispose();
|
||||||
|
this.chartState.dispose();
|
||||||
|
this.queryPlanState.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,7 +57,11 @@ export class QueryResultsInput extends EditorInput {
|
|||||||
public readonly onRestoreViewStateEmitter = new Emitter<void>();
|
public readonly onRestoreViewStateEmitter = new Emitter<void>();
|
||||||
public readonly onSaveViewStateEmitter = new Emitter<void>();
|
public readonly onSaveViewStateEmitter = new Emitter<void>();
|
||||||
|
|
||||||
public readonly state = new ResultsViewState(this.configurationService);
|
private _state = new ResultsViewState(this.configurationService);
|
||||||
|
|
||||||
|
public get state(): ResultsViewState {
|
||||||
|
return this._state;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(private _uri: string,
|
constructor(private _uri: string,
|
||||||
@IConfigurationService private configurationService: IConfigurationService
|
@IConfigurationService private configurationService: IConfigurationService
|
||||||
@@ -60,6 +71,12 @@ export class QueryResultsInput extends EditorInput {
|
|||||||
this._hasBootstrapped = false;
|
this._hasBootstrapped = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.state.dispose();
|
||||||
|
this._state = undefined;
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
getTypeId(): string {
|
getTypeId(): string {
|
||||||
return QueryResultsInput.ID;
|
return QueryResultsInput.ID;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,4 +33,8 @@ export class ChartTab implements IPanelTab {
|
|||||||
public dispose() {
|
public dispose() {
|
||||||
this.view.dispose();
|
this.view.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
this.view.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ export class ChartState {
|
|||||||
options: IInsightOptions = {
|
options: IInsightOptions = {
|
||||||
type: ChartType.Bar
|
type: ChartType.Bar
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare class Proxy {
|
declare class Proxy {
|
||||||
@@ -134,6 +138,15 @@ export class ChartView extends Disposable implements IPanelView {
|
|||||||
this.buildOptions();
|
this.buildOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
dispose(this.optionDisposables);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
render(container: HTMLElement): void {
|
render(container: HTMLElement): void {
|
||||||
if (!this.container) {
|
if (!this.container) {
|
||||||
this.container = $('div.chart-parent-container');
|
this.container = $('div.chart-parent-container');
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import { attachTableStyler } from 'sql/common/theme/styler';
|
import { attachTableStyler } from 'sql/common/theme/styler';
|
||||||
import QueryRunner from 'sql/parts/query/execution/queryRunner';
|
import QueryRunner from 'sql/parts/query/execution/queryRunner';
|
||||||
import { VirtualizedCollection, AsyncDataProvider } from 'sql/base/browser/ui/table/asyncDataView';
|
import { VirtualizedCollection, AsyncDataProvider } from 'sql/base/browser/ui/table/asyncDataView';
|
||||||
import { Table, ITableStyles, ITableMouseEvent } from 'sql/base/browser/ui/table/table';
|
import { Table } from 'sql/base/browser/ui/table/table';
|
||||||
import { ScrollableSplitView } from 'sql/base/browser/ui/scrollableSplitview/scrollableSplitview';
|
import { ScrollableSplitView } from 'sql/base/browser/ui/scrollableSplitview/scrollableSplitview';
|
||||||
import { MouseWheelSupport } from 'sql/base/browser/ui/table/plugins/mousewheelTableScroll.plugin';
|
import { MouseWheelSupport } from 'sql/base/browser/ui/table/plugins/mousewheelTableScroll.plugin';
|
||||||
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
|
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
|
||||||
@@ -19,6 +19,7 @@ import { escape } from 'sql/base/common/strings';
|
|||||||
import { hyperLinkFormatter, textFormatter } from 'sql/parts/grid/services/sharedServices';
|
import { hyperLinkFormatter, textFormatter } from 'sql/parts/grid/services/sharedServices';
|
||||||
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin';
|
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin';
|
||||||
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
|
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
|
||||||
|
import { ITableStyles, ITableMouseEvent } from 'sql/base/browser/ui/table/interfaces';
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import * as pretty from 'pretty-data';
|
import * as pretty from 'pretty-data';
|
||||||
@@ -61,6 +62,10 @@ export class GridPanelState {
|
|||||||
public tableStates: GridTableState[] = [];
|
public tableStates: GridTableState[] = [];
|
||||||
public scrollPosition: number;
|
public scrollPosition: number;
|
||||||
public collapsed = false;
|
public collapsed = false;
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
dispose(this.tableStates);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IGridTableState {
|
export interface IGridTableState {
|
||||||
@@ -68,14 +73,14 @@ export interface IGridTableState {
|
|||||||
maximized: boolean;
|
maximized: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GridTableState {
|
export class GridTableState extends Disposable {
|
||||||
|
|
||||||
private _maximized: boolean;
|
private _maximized: boolean;
|
||||||
|
|
||||||
private _onMaximizedChange = new Emitter<boolean>();
|
private _onMaximizedChange = this._register(new Emitter<boolean>());
|
||||||
public onMaximizedChange: Event<boolean> = this._onMaximizedChange.event;
|
public onMaximizedChange: Event<boolean> = this._onMaximizedChange.event;
|
||||||
|
|
||||||
private _onCanBeMaximizedChange = new Emitter<boolean>();
|
private _onCanBeMaximizedChange = this._register(new Emitter<boolean>());
|
||||||
public onCanBeMaximizedChange: Event<boolean> = this._onCanBeMaximizedChange.event;
|
public onCanBeMaximizedChange: Event<boolean> = this._onCanBeMaximizedChange.event;
|
||||||
|
|
||||||
private _canBeMaximized: boolean;
|
private _canBeMaximized: boolean;
|
||||||
@@ -86,6 +91,7 @@ export class GridTableState {
|
|||||||
public activeCell: Slick.Cell;
|
public activeCell: Slick.Cell;
|
||||||
|
|
||||||
constructor(public readonly resultId: number, public readonly batchId: number) {
|
constructor(public readonly resultId: number, public readonly batchId: number) {
|
||||||
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get canBeMaximized(): boolean {
|
public get canBeMaximized(): boolean {
|
||||||
@@ -217,13 +223,13 @@ export class GridPanel extends ViewletPanel {
|
|||||||
}
|
}
|
||||||
let table = this.instantiationService.createInstance(GridTable, this.runner, set);
|
let table = this.instantiationService.createInstance(GridTable, this.runner, set);
|
||||||
table.state = tableState;
|
table.state = tableState;
|
||||||
tableState.onMaximizedChange(e => {
|
this.tableDisposable.push(tableState.onMaximizedChange(e => {
|
||||||
if (e) {
|
if (e) {
|
||||||
this.maximizeTable(table.id);
|
this.maximizeTable(table.id);
|
||||||
} else {
|
} else {
|
||||||
this.minimizeTables();
|
this.minimizeTables();
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
this.tableDisposable.push(attachTableStyler(table, this.themeService));
|
this.tableDisposable.push(attachTableStyler(table, this.themeService));
|
||||||
|
|
||||||
tables.push(table);
|
tables.push(table);
|
||||||
@@ -238,11 +244,17 @@ export class GridPanel extends ViewletPanel {
|
|||||||
this.tables = this.tables.concat(tables);
|
this.tables = this.tables.concat(tables);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
private reset() {
|
private reset() {
|
||||||
for (let i = this.splitView.length - 1; i >= 0; i--) {
|
for (let i = this.splitView.length - 1; i >= 0; i--) {
|
||||||
this.splitView.removeView(i);
|
this.splitView.removeView(i);
|
||||||
}
|
}
|
||||||
dispose(this.tables);
|
dispose(this.tables);
|
||||||
|
dispose(this.tableDisposable);
|
||||||
|
this.tableDisposable = [];
|
||||||
this.tables = [];
|
this.tables = [];
|
||||||
this.maximizedGrid = undefined;
|
this.maximizedGrid = undefined;
|
||||||
|
|
||||||
@@ -293,6 +305,15 @@ export class GridPanel extends ViewletPanel {
|
|||||||
public get state(): GridPanelState {
|
public get state(): GridPanelState {
|
||||||
return this._state;
|
return this._state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
dispose(this.queryRunnerDisposables);
|
||||||
|
dispose(this.tableDisposable);
|
||||||
|
dispose(this.tables);
|
||||||
|
this.tableDisposable = undefined;
|
||||||
|
this.tables = undefined;
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GridTable<T> extends Disposable implements IView {
|
class GridTable<T> extends Disposable implements IView {
|
||||||
@@ -444,9 +465,9 @@ class GridTable<T> extends Disposable implements IView {
|
|||||||
|
|
||||||
private setupState() {
|
private setupState() {
|
||||||
// change actionbar on maximize change
|
// change actionbar on maximize change
|
||||||
this.state.onMaximizedChange(this.rebuildActionBar, this);
|
this._register(this.state.onMaximizedChange(this.rebuildActionBar, this));
|
||||||
|
|
||||||
this.state.onCanBeMaximizedChange(this.rebuildActionBar, this);
|
this._register(this.state.onCanBeMaximizedChange(this.rebuildActionBar, this));
|
||||||
|
|
||||||
if (this.state.scrollPosition) {
|
if (this.state.scrollPosition) {
|
||||||
// most of the time this won't do anything
|
// most of the time this won't do anything
|
||||||
@@ -656,6 +677,8 @@ class GridTable<T> extends Disposable implements IView {
|
|||||||
|
|
||||||
public dispose() {
|
public dispose() {
|
||||||
$(this.container).destroy();
|
$(this.container).destroy();
|
||||||
|
this.table.dispose();
|
||||||
|
this.actionBar.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import 'vs/css!./media/messagePanel';
|
import 'vs/css!./media/messagePanel';
|
||||||
import { IMessagesActionContext, SelectAllMessagesAction, CopyMessagesAction } from './actions';
|
import { IMessagesActionContext, SelectAllMessagesAction, CopyMessagesAction } from './actions';
|
||||||
import QueryRunner from 'sql/parts/query/execution/queryRunner';
|
import QueryRunner from 'sql/parts/query/execution/queryRunner';
|
||||||
|
import { QueryInput } from 'sql/parts/query/common/queryInput';
|
||||||
|
|
||||||
import { IResultMessage, ISelectionData } from 'sqlops';
|
import { IResultMessage, ISelectionData } from 'sqlops';
|
||||||
|
|
||||||
@@ -28,8 +29,6 @@ import { $ } from 'vs/base/browser/builder';
|
|||||||
import { isArray, isUndefinedOrNull } from 'vs/base/common/types';
|
import { isArray, isUndefinedOrNull } from 'vs/base/common/types';
|
||||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||||
import { IEditor } from 'vs/editor/common/editorCommon';
|
|
||||||
import { QueryInput } from 'sql/parts/query/common/queryInput';
|
|
||||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||||
|
|
||||||
export interface IResultMessageIntern extends IResultMessage {
|
export interface IResultMessageIntern extends IResultMessage {
|
||||||
@@ -71,6 +70,10 @@ export class MessagePanelState {
|
|||||||
this.collapsed = !messagesOpenedSettings;
|
this.collapsed = !messagesOpenedSettings;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MessagePanel extends ViewletPanel {
|
export class MessagePanel extends ViewletPanel {
|
||||||
@@ -102,6 +105,7 @@ export class MessagePanel extends ViewletPanel {
|
|||||||
renderer: this.renderer,
|
renderer: this.renderer,
|
||||||
controller: this.controller
|
controller: this.controller
|
||||||
}, { keyboardSupport: false, horizontalScrollMode: ScrollbarVisibility.Auto });
|
}, { keyboardSupport: false, horizontalScrollMode: ScrollbarVisibility.Auto });
|
||||||
|
this.disposables.push(this.tree);
|
||||||
this.tree.onDidScroll(e => {
|
this.tree.onDidScroll(e => {
|
||||||
if (this.state) {
|
if (this.state) {
|
||||||
this.state.scrollPosition = this.tree.getScrollPosition();
|
this.state.scrollPosition = this.tree.getScrollPosition();
|
||||||
@@ -117,7 +121,7 @@ export class MessagePanel extends ViewletPanel {
|
|||||||
protected renderBody(container: HTMLElement): void {
|
protected renderBody(container: HTMLElement): void {
|
||||||
this.container.style.width = '100%';
|
this.container.style.width = '100%';
|
||||||
this.container.style.height = '100%';
|
this.container.style.height = '100%';
|
||||||
attachListStyler(this.tree, this.themeService);
|
this.disposables.push(attachListStyler(this.tree, this.themeService));
|
||||||
container.appendChild(this.container);
|
container.appendChild(this.container);
|
||||||
this.tree.setInput(this.model);
|
this.tree.setInput(this.model);
|
||||||
}
|
}
|
||||||
@@ -193,9 +197,19 @@ export class MessagePanel extends ViewletPanel {
|
|||||||
}
|
}
|
||||||
this.setExpanded(!this.state.collapsed);
|
this.setExpanded(!this.state.collapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get state(): MessagePanelState {
|
public get state(): MessagePanelState {
|
||||||
return this._state;
|
return this._state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
dispose(this.queryRunnerDisposables);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessageDataSource implements IDataSource {
|
class MessageDataSource implements IDataSource {
|
||||||
|
|||||||
@@ -478,11 +478,11 @@ export class QueryEditor extends BaseEditor {
|
|||||||
|
|
||||||
this.setTaskbarContent();
|
this.setTaskbarContent();
|
||||||
|
|
||||||
this._configurationService.onDidChangeConfiguration(e => {
|
this._toDispose.push(this._configurationService.onDidChangeConfiguration(e => {
|
||||||
if (e.affectedKeys.includes('workbench.enablePreviewFeatures')) {
|
if (e.affectedKeys.includes('workbench.enablePreviewFeatures')) {
|
||||||
this.setTaskbarContent();
|
this.setTaskbarContent();
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private setTaskbarContent(): void {
|
private setTaskbarContent(): void {
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ export class QueryResultsEditor extends BaseEditor {
|
|||||||
public static ID: string = 'workbench.editor.queryResultsEditor';
|
public static ID: string = 'workbench.editor.queryResultsEditor';
|
||||||
public static AngularSelectorString: string = 'slickgrid-container.slickgridContainer';
|
public static AngularSelectorString: string = 'slickgrid-container.slickgridContainer';
|
||||||
protected _rawOptions: BareResultsGridInfo;
|
protected _rawOptions: BareResultsGridInfo;
|
||||||
protected _input: QueryResultsInput;
|
|
||||||
|
|
||||||
private resultsView: QueryResultsView;
|
private resultsView: QueryResultsView;
|
||||||
private styleSheet = DOM.createStyleSheet();
|
private styleSheet = DOM.createStyleSheet();
|
||||||
@@ -104,17 +103,17 @@ export class QueryResultsEditor extends BaseEditor {
|
|||||||
) {
|
) {
|
||||||
super(QueryResultsEditor.ID, telemetryService, themeService);
|
super(QueryResultsEditor.ID, telemetryService, themeService);
|
||||||
this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
|
this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
|
||||||
this._configurationService.onDidChangeConfiguration(e => {
|
this._register(this._configurationService.onDidChangeConfiguration(e => {
|
||||||
if (e.affectsConfiguration('resultsGrid')) {
|
if (e.affectsConfiguration('resultsGrid')) {
|
||||||
this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
|
this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
|
||||||
this.applySettings();
|
this.applySettings();
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
this.applySettings();
|
this.applySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get input(): QueryResultsInput {
|
public get input(): QueryResultsInput {
|
||||||
return this._input;
|
return this._input as QueryResultsInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
private applySettings() {
|
private applySettings() {
|
||||||
@@ -133,10 +132,16 @@ export class QueryResultsEditor extends BaseEditor {
|
|||||||
this.styleSheet.remove();
|
this.styleSheet.remove();
|
||||||
parent.appendChild(this.styleSheet);
|
parent.appendChild(this.styleSheet);
|
||||||
if (!this.resultsView) {
|
if (!this.resultsView) {
|
||||||
this.resultsView = new QueryResultsView(parent, this._instantiationService, this._queryModelService);
|
this.resultsView = this._register(new QueryResultsView(parent, this._instantiationService, this._queryModelService));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.styleSheet.remove();
|
||||||
|
this.styleSheet = undefined;
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
layout(dimension: DOM.Dimension): void {
|
layout(dimension: DOM.Dimension): void {
|
||||||
this.resultsView.layout(dimension);
|
this.resultsView.layout(dimension);
|
||||||
}
|
}
|
||||||
@@ -147,6 +152,11 @@ export class QueryResultsEditor extends BaseEditor {
|
|||||||
return TPromise.wrap<void>(null);
|
return TPromise.wrap<void>(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearInput() {
|
||||||
|
this.resultsView.clearInput();
|
||||||
|
super.clearInput();
|
||||||
|
}
|
||||||
|
|
||||||
public chart(dataId: { batchId: number, resultId: number }) {
|
public chart(dataId: { batchId: number, resultId: number }) {
|
||||||
this.resultsView.chartData(dataId);
|
this.resultsView.chartData(dataId);
|
||||||
}
|
}
|
||||||
@@ -154,11 +164,4 @@ export class QueryResultsEditor extends BaseEditor {
|
|||||||
public showQueryPlan(xml: string) {
|
public showQueryPlan(xml: string) {
|
||||||
this.resultsView.showPlan(xml);
|
this.resultsView.showPlan(xml);
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose(): void {
|
|
||||||
super.dispose();
|
|
||||||
if (this.resultsView) {
|
|
||||||
this.resultsView.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,15 @@ class ResultsView extends Disposable implements IPanelView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
this.gridPanel.clear();
|
||||||
|
this.messagePanel.clear();
|
||||||
|
}
|
||||||
|
|
||||||
remove(): void {
|
remove(): void {
|
||||||
this.container.remove();
|
this.container.remove();
|
||||||
}
|
}
|
||||||
@@ -151,6 +160,10 @@ class ResultsTab implements IPanelTab {
|
|||||||
public dispose() {
|
public dispose() {
|
||||||
dispose(this.view);
|
dispose(this.view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
this.view.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QueryResultsView extends Disposable {
|
export class QueryResultsView extends Disposable {
|
||||||
@@ -221,8 +234,11 @@ export class QueryResultsView extends Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose() {
|
clearInput() {
|
||||||
this._panelView.dispose();
|
this._input = undefined;
|
||||||
|
this.resultsTab.clear();
|
||||||
|
this.qpTab.clear();
|
||||||
|
this.chartTab.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get input(): QueryResultsInput {
|
public get input(): QueryResultsInput {
|
||||||
@@ -264,4 +280,8 @@ export class QueryResultsView extends Disposable {
|
|||||||
this._panelView.removeTab(this.qpTab.identifier);
|
this._panelView.removeTab(this.qpTab.identifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ export interface IQueryModelService {
|
|||||||
getConfig(): Promise<{ [key: string]: any }>;
|
getConfig(): Promise<{ [key: string]: any }>;
|
||||||
getShortcuts(): Promise<any>;
|
getShortcuts(): Promise<any>;
|
||||||
getQueryRows(uri: string, rowStart: number, numberOfRows: number, batchId: number, resultId: number): Thenable<ResultSetSubset>;
|
getQueryRows(uri: string, rowStart: number, numberOfRows: number, batchId: number, resultId: number): Thenable<ResultSetSubset>;
|
||||||
runQuery(uri: string, selection: ISelectionData, title: string, queryInput: QueryInput, runOptions?: ExecutionPlanOptions): void;
|
runQuery(uri: string, selection: ISelectionData, queryInput: QueryInput, runOptions?: ExecutionPlanOptions): void;
|
||||||
runQueryStatement(uri: string, selection: ISelectionData, title: string, queryInput: QueryInput): void;
|
runQueryStatement(uri: string, selection: ISelectionData, queryInput: QueryInput): void;
|
||||||
runQueryString(uri: string, selection: string, title: string, queryInput: QueryInput);
|
runQueryString(uri: string, selection: string, queryInput: QueryInput);
|
||||||
cancelQuery(input: QueryRunner | string): void;
|
cancelQuery(input: QueryRunner | string): void;
|
||||||
disposeQuery(uri: string): void;
|
disposeQuery(uri: string): void;
|
||||||
isRunningQuery(uri: string): boolean;
|
isRunningQuery(uri: string): boolean;
|
||||||
|
|||||||
@@ -209,32 +209,28 @@ export class QueryModelService implements IQueryModelService {
|
|||||||
/**
|
/**
|
||||||
* Run a query for the given URI with the given text selection
|
* Run a query for the given URI with the given text selection
|
||||||
*/
|
*/
|
||||||
public runQuery(uri: string, selection: sqlops.ISelectionData,
|
public runQuery(uri: string, selection: sqlops.ISelectionData, queryInput: QueryInput, runOptions?: sqlops.ExecutionPlanOptions): void {
|
||||||
title: string, queryInput: QueryInput, runOptions?: sqlops.ExecutionPlanOptions): void {
|
this.doRunQuery(uri, selection, queryInput, false, runOptions);
|
||||||
this.doRunQuery(uri, selection, title, queryInput, false, runOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the current SQL statement for the given URI
|
* Run the current SQL statement for the given URI
|
||||||
*/
|
*/
|
||||||
public runQueryStatement(uri: string, selection: sqlops.ISelectionData,
|
public runQueryStatement(uri: string, selection: sqlops.ISelectionData, queryInput: QueryInput): void {
|
||||||
title: string, queryInput: QueryInput): void {
|
this.doRunQuery(uri, selection, queryInput, true);
|
||||||
this.doRunQuery(uri, selection, title, queryInput, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the current SQL statement for the given URI
|
* Run the current SQL statement for the given URI
|
||||||
*/
|
*/
|
||||||
public runQueryString(uri: string, selection: string,
|
public runQueryString(uri: string, selection: string, queryInput: QueryInput): void {
|
||||||
title: string, queryInput: QueryInput): void {
|
this.doRunQuery(uri, selection, queryInput, true);
|
||||||
this.doRunQuery(uri, selection, title, queryInput, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run Query implementation
|
* Run Query implementation
|
||||||
*/
|
*/
|
||||||
private doRunQuery(uri: string, selection: sqlops.ISelectionData | string,
|
private doRunQuery(uri: string, selection: sqlops.ISelectionData | string, queryInput: QueryInput,
|
||||||
title: string, queryInput: QueryInput,
|
|
||||||
runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): void {
|
runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): void {
|
||||||
// Reuse existing query runner if it exists
|
// Reuse existing query runner if it exists
|
||||||
let queryRunner: QueryRunner;
|
let queryRunner: QueryRunner;
|
||||||
@@ -256,7 +252,7 @@ export class QueryModelService implements IQueryModelService {
|
|||||||
} else {
|
} else {
|
||||||
// We do not have a query runner for this editor, so create a new one
|
// We do not have a query runner for this editor, so create a new one
|
||||||
// and map it to the results uri
|
// and map it to the results uri
|
||||||
info = this.initQueryRunner(uri, title);
|
info = this.initQueryRunner(uri);
|
||||||
queryRunner = info.queryRunner;
|
queryRunner = info.queryRunner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,8 +273,8 @@ export class QueryModelService implements IQueryModelService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private initQueryRunner(uri: string, title: string): QueryInfo {
|
private initQueryRunner(uri: string): QueryInfo {
|
||||||
let queryRunner = this._instantiationService.createInstance(QueryRunner, uri, title);
|
let queryRunner = this._instantiationService.createInstance(QueryRunner, uri);
|
||||||
let info = new QueryInfo();
|
let info = new QueryInfo();
|
||||||
queryRunner.addListener(QREvents.RESULT_SET, e => {
|
queryRunner.addListener(QREvents.RESULT_SET, e => {
|
||||||
this._fireQueryEvent(uri, 'resultSet', e);
|
this._fireQueryEvent(uri, 'resultSet', e);
|
||||||
@@ -363,6 +359,10 @@ export class QueryModelService implements IQueryModelService {
|
|||||||
if (queryRunner) {
|
if (queryRunner) {
|
||||||
queryRunner.disposeQuery();
|
queryRunner.disposeQuery();
|
||||||
}
|
}
|
||||||
|
// remove our info map
|
||||||
|
if (this._queryInfoMap.has(ownerUri)) {
|
||||||
|
this._queryInfoMap.delete(ownerUri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EDIT DATA METHODS /////////////////////////////////////////////////////
|
// EDIT DATA METHODS /////////////////////////////////////////////////////
|
||||||
@@ -386,7 +386,7 @@ export class QueryModelService implements IQueryModelService {
|
|||||||
|
|
||||||
// We do not have a query runner for this editor, so create a new one
|
// We do not have a query runner for this editor, so create a new one
|
||||||
// and map it to the results uri
|
// and map it to the results uri
|
||||||
queryRunner = this._instantiationService.createInstance(QueryRunner, ownerUri, ownerUri);
|
queryRunner = this._instantiationService.createInstance(QueryRunner, ownerUri);
|
||||||
queryRunner.addListener(QREvents.RESULT_SET, resultSet => {
|
queryRunner.addListener(QREvents.RESULT_SET, resultSet => {
|
||||||
this._fireQueryEvent(ownerUri, 'resultSet', resultSet);
|
this._fireQueryEvent(ownerUri, 'resultSet', resultSet);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import * as nls from 'vs/nls';
|
|||||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||||
import * as types from 'vs/base/common/types';
|
import * as types from 'vs/base/common/types';
|
||||||
import { EventEmitter } from 'sql/base/common/eventEmitter';
|
import { EventEmitter } from 'sql/base/common/eventEmitter';
|
||||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
import { Emitter, Event } from 'vs/base/common/event';
|
import { Emitter, Event } from 'vs/base/common/event';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
@@ -62,7 +62,7 @@ export interface IGridMessage extends sqlops.IResultMessage {
|
|||||||
* Query Runner class which handles running a query, reports the results to the content manager,
|
* Query Runner class which handles running a query, reports the results to the content manager,
|
||||||
* and handles getting more rows from the service layer and disposing when the content is closed.
|
* and handles getting more rows from the service layer and disposing when the content is closed.
|
||||||
*/
|
*/
|
||||||
export default class QueryRunner {
|
export default class QueryRunner extends Disposable {
|
||||||
// MEMBER VARIABLES ////////////////////////////////////////////////////
|
// MEMBER VARIABLES ////////////////////////////////////////////////////
|
||||||
private _resultLineOffset: number;
|
private _resultLineOffset: number;
|
||||||
private _totalElapsedMilliseconds: number = 0;
|
private _totalElapsedMilliseconds: number = 0;
|
||||||
@@ -76,7 +76,7 @@ export default class QueryRunner {
|
|||||||
private _planXml = new Deferred<string>();
|
private _planXml = new Deferred<string>();
|
||||||
public get planXml(): Thenable<string> { return this._planXml.promise; }
|
public get planXml(): Thenable<string> { return this._planXml.promise; }
|
||||||
|
|
||||||
private _onMessage = new Emitter<sqlops.IResultMessage>();
|
private _onMessage = this._register(new Emitter<sqlops.IResultMessage>());
|
||||||
private _debouncedMessage = debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(this._onMessage.event, (l, e) => {
|
private _debouncedMessage = debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(this._onMessage.event, (l, e) => {
|
||||||
// on first run
|
// on first run
|
||||||
if (types.isUndefinedOrNull(l)) {
|
if (types.isUndefinedOrNull(l)) {
|
||||||
@@ -88,7 +88,7 @@ export default class QueryRunner {
|
|||||||
private _echoedMessages = echo(this._debouncedMessage.event);
|
private _echoedMessages = echo(this._debouncedMessage.event);
|
||||||
public readonly onMessage = this._echoedMessages.event;
|
public readonly onMessage = this._echoedMessages.event;
|
||||||
|
|
||||||
private _onResultSet = new Emitter<sqlops.ResultSetSummary>();
|
private _onResultSet = this._register(new Emitter<sqlops.ResultSetSummary>());
|
||||||
private _debouncedResultSet = debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(this._onResultSet.event, (l, e) => {
|
private _debouncedResultSet = debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(this._onResultSet.event, (l, e) => {
|
||||||
// on first run
|
// on first run
|
||||||
if (types.isUndefinedOrNull(l)) {
|
if (types.isUndefinedOrNull(l)) {
|
||||||
@@ -100,16 +100,16 @@ export default class QueryRunner {
|
|||||||
private _echoedResultSet = echo(this._debouncedResultSet.event);
|
private _echoedResultSet = echo(this._debouncedResultSet.event);
|
||||||
public readonly onResultSet = this._echoedResultSet.event;
|
public readonly onResultSet = this._echoedResultSet.event;
|
||||||
|
|
||||||
private _onQueryStart = new Emitter<void>();
|
private _onQueryStart = this._register(new Emitter<void>());
|
||||||
public readonly onQueryStart: Event<void> = this._onQueryStart.event;
|
public readonly onQueryStart: Event<void> = this._onQueryStart.event;
|
||||||
|
|
||||||
private _onQueryEnd = new Emitter<string>();
|
private _onQueryEnd = this._register(new Emitter<string>());
|
||||||
public readonly onQueryEnd: Event<string> = this._onQueryEnd.event;
|
public readonly onQueryEnd: Event<string> = this._onQueryEnd.event;
|
||||||
|
|
||||||
private _onBatchStart = new Emitter<sqlops.BatchSummary>();
|
private _onBatchStart = this._register(new Emitter<sqlops.BatchSummary>());
|
||||||
public readonly onBatchStart: Event<sqlops.BatchSummary> = this._onBatchStart.event;
|
public readonly onBatchStart: Event<sqlops.BatchSummary> = this._onBatchStart.event;
|
||||||
|
|
||||||
private _onBatchEnd = new Emitter<sqlops.BatchSummary>();
|
private _onBatchEnd = this._register(new Emitter<sqlops.BatchSummary>());
|
||||||
public readonly onBatchEnd: Event<sqlops.BatchSummary> = this._onBatchEnd.event;
|
public readonly onBatchEnd: Event<sqlops.BatchSummary> = this._onBatchEnd.event;
|
||||||
|
|
||||||
private _queryStartTime: Date;
|
private _queryStartTime: Date;
|
||||||
@@ -124,13 +124,14 @@ export default class QueryRunner {
|
|||||||
// CONSTRUCTOR /////////////////////////////////////////////////////////
|
// CONSTRUCTOR /////////////////////////////////////////////////////////
|
||||||
constructor(
|
constructor(
|
||||||
public uri: string,
|
public uri: string,
|
||||||
public title: string,
|
|
||||||
@IQueryManagementService private _queryManagementService: IQueryManagementService,
|
@IQueryManagementService private _queryManagementService: IQueryManagementService,
|
||||||
@INotificationService private _notificationService: INotificationService,
|
@INotificationService private _notificationService: INotificationService,
|
||||||
@IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService,
|
@IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService,
|
||||||
@IClipboardService private _clipboardService: IClipboardService,
|
@IClipboardService private _clipboardService: IClipboardService,
|
||||||
@IInstantiationService private instantiationService: IInstantiationService
|
@IInstantiationService private instantiationService: IInstantiationService
|
||||||
) { }
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
get isExecuting(): boolean {
|
get isExecuting(): boolean {
|
||||||
return this._isExecuting;
|
return this._isExecuting;
|
||||||
@@ -504,10 +505,16 @@ export default class QueryRunner {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes the Query from the service client
|
* Disposes the Query from the service client
|
||||||
* @returns A promise that will be rejected if a problem occured
|
|
||||||
*/
|
*/
|
||||||
public disposeQuery(): void {
|
public disposeQuery(): void {
|
||||||
this._queryManagementService.disposeQuery(this.uri);
|
this._queryManagementService.disposeQuery(this.uri).then(() => {
|
||||||
|
this.dispose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
this._batchSets = undefined;
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
get totalElapsedMilliseconds(): number {
|
get totalElapsedMilliseconds(): number {
|
||||||
|
|||||||
@@ -152,12 +152,6 @@ export class QueryEditorService implements IQueryEditorService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears any QueryEditor data for the given URI held by this service
|
|
||||||
*/
|
|
||||||
public onQueryInputClosed(uri: string): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
onSaveAsCompleted(oldResource: URI, newResource: URI): void {
|
onSaveAsCompleted(oldResource: URI, newResource: URI): void {
|
||||||
let oldResourceString: string = oldResource.toString();
|
let oldResourceString: string = oldResource.toString();
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ import { dispose, Disposable } from 'vs/base/common/lifecycle';
|
|||||||
|
|
||||||
export class QueryPlanState {
|
export class QueryPlanState {
|
||||||
xml: string;
|
xml: string;
|
||||||
|
dispose() {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QueryPlanTab implements IPanelTab {
|
export class QueryPlanTab implements IPanelTab {
|
||||||
@@ -29,6 +32,10 @@ export class QueryPlanTab implements IPanelTab {
|
|||||||
public dispose() {
|
public dispose() {
|
||||||
dispose(this.view);
|
dispose(this.view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
this.view.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QueryPlanView implements IPanelView {
|
export class QueryPlanView implements IPanelView {
|
||||||
@@ -59,6 +66,12 @@ export class QueryPlanView implements IPanelView {
|
|||||||
this.container.style.height = dimension.height + 'px';
|
this.container.style.height = dimension.height + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
if (this.qp) {
|
||||||
|
this.qp.xml = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public showPlan(xml: string) {
|
public showPlan(xml: string) {
|
||||||
if (this.qp) {
|
if (this.qp) {
|
||||||
this.qp.xml = xml;
|
this.qp.xml = xml;
|
||||||
|
|||||||
@@ -141,6 +141,8 @@ suite('SQL QueryEditor Tests', () => {
|
|||||||
connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, memento.object, undefined);
|
connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, memento.object, undefined);
|
||||||
connectionManagementService.callBase = true;
|
connectionManagementService.callBase = true;
|
||||||
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAny())).returns(() => false);
|
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAny())).returns(() => false);
|
||||||
|
connectionManagementService.setup(x => x.disconnectEditor(TypeMoq.It.isAny())).returns(() => void 0);
|
||||||
|
connectionManagementService.setup(x => x.ensureDefaultLanguageFlavor(TypeMoq.It.isAnyString())).returns(() => void 0);
|
||||||
|
|
||||||
// Create a QueryModelService
|
// Create a QueryModelService
|
||||||
queryModelService = new QueryModelService(instantiationService.object, notificationService.object);
|
queryModelService = new QueryModelService(instantiationService.object, notificationService.object);
|
||||||
@@ -328,6 +330,9 @@ suite('SQL QueryEditor Tests', () => {
|
|||||||
queryConnectionService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, memento.object, undefined);
|
queryConnectionService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, memento.object, undefined);
|
||||||
queryConnectionService.callBase = true;
|
queryConnectionService.callBase = true;
|
||||||
|
|
||||||
|
queryConnectionService.setup(x => x.disconnectEditor(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => void 0);
|
||||||
|
queryConnectionService.setup(x => x.ensureDefaultLanguageFlavor(TypeMoq.It.isAnyString())).returns(() => void 0);
|
||||||
|
|
||||||
// Mock InstantiationService to give us the actions
|
// Mock InstantiationService to give us the actions
|
||||||
queryActionInstantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose);
|
queryActionInstantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose);
|
||||||
|
|
||||||
@@ -354,12 +359,13 @@ suite('SQL QueryEditor Tests', () => {
|
|||||||
let fileInput = new UntitledEditorInput(URI.parse('testUri'), false, '', '', '', instantiationService.object, undefined, undefined, undefined);
|
let fileInput = new UntitledEditorInput(URI.parse('testUri'), false, '', '', '', instantiationService.object, undefined, undefined, undefined);
|
||||||
queryModelService = TypeMoq.Mock.ofType(QueryModelService, TypeMoq.MockBehavior.Loose, undefined, undefined);
|
queryModelService = TypeMoq.Mock.ofType(QueryModelService, TypeMoq.MockBehavior.Loose, undefined, undefined);
|
||||||
queryModelService.callBase = true;
|
queryModelService.callBase = true;
|
||||||
|
queryModelService.setup(x => x.disposeQuery(TypeMoq.It.isAny())).returns(() => void 0);
|
||||||
queryInput = new QueryInput(
|
queryInput = new QueryInput(
|
||||||
'',
|
'',
|
||||||
fileInput,
|
fileInput,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
connectionManagementService.object,
|
||||||
queryModelService.object,
|
queryModelService.object,
|
||||||
undefined,
|
undefined,
|
||||||
undefined
|
undefined
|
||||||
@@ -395,7 +401,7 @@ suite('SQL QueryEditor Tests', () => {
|
|||||||
test('Test that we attempt to dispose query when the queryInput is disposed', (done) => {
|
test('Test that we attempt to dispose query when the queryInput is disposed', (done) => {
|
||||||
let queryResultsInput = new QueryResultsInput('testUri', configurationService.object);
|
let queryResultsInput = new QueryResultsInput('testUri', configurationService.object);
|
||||||
queryInput['_results'] = queryResultsInput;
|
queryInput['_results'] = queryResultsInput;
|
||||||
queryInput.dispose();
|
queryInput.close();
|
||||||
queryModelService.verify(x => x.disposeQuery(TypeMoq.It.isAnyString()), TypeMoq.Times.once());
|
queryModelService.verify(x => x.disposeQuery(TypeMoq.It.isAnyString()), TypeMoq.Times.once());
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user