Handle query plan flow problems (#2918)

* modify the query plan work flow to account for some errors

* formatting
This commit is contained in:
Anthony Dresser
2018-10-16 16:15:47 -07:00
committed by Karl Burtram
parent 425eecf692
commit bfa9e8c495
3 changed files with 63 additions and 62 deletions

View File

@@ -5,8 +5,8 @@
import { IThemable } from 'vs/platform/theme/common/styler'; import { IThemable } from 'vs/platform/theme/common/styler';
import { Event, Emitter } from 'vs/base/common/event'; import { Event, Emitter } from 'vs/base/common/event';
import { Dimension, EventType } from 'vs/base/browser/dom'; import { Dimension, EventType, $, addDisposableListener } from 'vs/base/browser/dom';
import { $, Builder } from 'vs/base/browser/builder'; import { $ as quickBuilder } from 'vs/base/browser/builder';
import { IAction } from 'vs/base/common/actions'; import { IAction } from 'vs/base/common/actions';
import { IActionOptions, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IActionOptions, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
@@ -34,8 +34,8 @@ export interface IPanelTab {
} }
interface IInternalPanelTab extends IPanelTab { interface IInternalPanelTab extends IPanelTab {
header: Builder; header: HTMLElement;
label: Builder; label: HTMLElement;
dispose(): void; dispose(): void;
} }
@@ -49,10 +49,10 @@ export class TabbedPanel extends Disposable implements IThemable {
private _tabMap = new Map<PanelTabIdentifier, IInternalPanelTab>(); private _tabMap = new Map<PanelTabIdentifier, IInternalPanelTab>();
private _shownTab: PanelTabIdentifier; private _shownTab: PanelTabIdentifier;
public readonly headersize = 35; public readonly headersize = 35;
private $header: Builder; private header: HTMLElement;
private $tabList: Builder; private tabList: HTMLElement;
private $body: Builder; private body: HTMLElement;
private $parent: Builder; private parent: HTMLElement;
private _actionbar: ActionBar; private _actionbar: ActionBar;
private _currentDimensions: Dimension; private _currentDimensions: Dimension;
private _collapsed = false; private _collapsed = false;
@@ -65,26 +65,26 @@ export class TabbedPanel extends Disposable implements IThemable {
constructor(private container: HTMLElement, private options: IPanelOptions = defaultOptions) { constructor(private container: HTMLElement, private options: IPanelOptions = defaultOptions) {
super(); super();
this.$parent = this._register($('.tabbedPanel')); this.parent = $('.tabbedPanel');
this.$parent.appendTo(container); container.appendChild(this.parent);
this.$header = $('.composite.title'); this.header = $('.composite.title');
this.$tabList = $('.tabList'); this.tabList = $('.tabList');
this.$tabList.attr('role', 'tablist'); this.tabList.setAttribute('role', 'tablist');
this.$tabList.style('height', this.headersize + 'px'); this.tabList.style.height = this.headersize + 'px';
this.$header.append(this.$tabList); this.header.appendChild(this.tabList);
let actionbarcontainer = $('.title-actions'); let actionbarcontainer = $('.title-actions');
this._actionbar = new ActionBar(actionbarcontainer.getHTMLElement()); this._actionbar = new ActionBar(actionbarcontainer);
this.$header.append(actionbarcontainer); this.header.appendChild(actionbarcontainer);
if (options.showHeaderWhenSingleView) { if (options.showHeaderWhenSingleView) {
this._headerVisible = true; this._headerVisible = true;
this.$parent.append(this.$header); this.parent.appendChild(this.header);
} else { } else {
this._headerVisible = false; this._headerVisible = false;
} }
this.$body = $('.tabBody'); this.body = $('.tabBody');
this.$body.attr('role', 'tabpanel'); this.body.setAttribute('role', 'tabpanel');
this.$body.attr('tabindex', '0'); this.body.setAttribute('tabindex', '0');
this.$parent.append(this.$body); this.parent.appendChild(this.body);
} }
public contains(tab: IPanelTab): boolean { public contains(tab: IPanelTab): boolean {
@@ -99,7 +99,7 @@ export class TabbedPanel extends Disposable implements IThemable {
this.showTab(tab.identifier); this.showTab(tab.identifier);
} }
if (this._tabMap.size > 1 && !this._headerVisible) { if (this._tabMap.size > 1 && !this._headerVisible) {
this.$parent.append(this.$header, 0); this.parent.insertBefore(this.header, this.parent.firstChild);
this._headerVisible = true; this._headerVisible = true;
this.layout(this._currentDimensions); this.layout(this._currentDimensions);
} }
@@ -116,30 +116,27 @@ export class TabbedPanel extends Disposable implements IThemable {
private _createTab(tab: IInternalPanelTab): void { private _createTab(tab: IInternalPanelTab): void {
let tabHeaderElement = $('.tab-header'); let tabHeaderElement = $('.tab-header');
tabHeaderElement.attr('tabindex', '0'); tabHeaderElement.setAttribute('tabindex', '0');
tabHeaderElement.attr('role', 'tab'); tabHeaderElement.setAttribute('role', 'tab');
tabHeaderElement.attr('aria-selected', 'false'); tabHeaderElement.setAttribute('aria-selected', 'false');
tabHeaderElement.attr('aria-controls', tab.identifier); tabHeaderElement.setAttribute('aria-controls', tab.identifier);
let tabElement = $('.tab'); let tabElement = $('.tab');
tabHeaderElement.append(tabElement); tabHeaderElement.appendChild(tabElement);
let tabLabel = $('a.tabLabel'); let tabLabel = $('a.tabLabel');
tabLabel.safeInnerHtml(tab.title); tabLabel.innerText = tab.title;
tabElement.append(tabLabel); tabElement.appendChild(tabLabel);
tabHeaderElement.on(EventType.CLICK, e => this.showTab(tab.identifier)); addDisposableListener(tabHeaderElement, EventType.CLICK, e => this.showTab(tab.identifier));
tabHeaderElement.on(EventType.KEY_DOWN, (e: KeyboardEvent) => { addDisposableListener(tabHeaderElement, EventType.KEY_DOWN, (e: KeyboardEvent) => {
let event = new StandardKeyboardEvent(e); let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter)) { if (event.equals(KeyCode.Enter)) {
this.showTab(tab.identifier); this.showTab(tab.identifier);
e.stopImmediatePropagation(); e.stopImmediatePropagation();
} }
}); });
this.$tabList.append(tabHeaderElement); this.tabList.appendChild(tabHeaderElement);
tab.header = tabHeaderElement; tab.header = tabHeaderElement;
tab.label = tabLabel; tab.label = tabLabel;
tab.dispose = () => { tab.dispose = () => { };
tab.header.dispose();
tab.label.dispose();
};
this._register(tab); this._register(tab);
} }
@@ -149,19 +146,20 @@ export class TabbedPanel extends Disposable implements IThemable {
} }
if (this._shownTab) { if (this._shownTab) {
this._tabMap.get(this._shownTab).label.removeClass('active'); this._tabMap.get(this._shownTab).label.classList.remove('active');
this._tabMap.get(this._shownTab).header.removeClass('active').attr('aria-selected', 'false'); this._tabMap.get(this._shownTab).header.classList.remove('active');
this._tabMap.get(this._shownTab).header.setAttribute('aria-selected', 'false');
} }
this._shownTab = id; this._shownTab = id;
this.tabHistory.push(id); this.tabHistory.push(id);
this.$body.clearChildren(); quickBuilder(this.body).empty();
let tab = this._tabMap.get(this._shownTab); let tab = this._tabMap.get(this._shownTab);
this.$body.attr('aria-labelledby', tab.identifier); this.body.setAttribute('aria-labelledby', tab.identifier);
tab.label.addClass('active'); tab.label.classList.add('active');
tab.header.addClass('active'); tab.header.classList.add('active');
tab.header.attr('aria-selected', 'true'); tab.header.setAttribute('aria-selected', 'true');
tab.view.render(this.$body.getHTMLElement()); tab.view.render(this.body);
this._onTabChange.fire(id); this._onTabChange.fire(id);
if (this._currentDimensions) { if (this._currentDimensions) {
this._layoutCurrentTab(new Dimension(this._currentDimensions.width, this._currentDimensions.height - this.headersize)); this._layoutCurrentTab(new Dimension(this._currentDimensions.width, this._currentDimensions.height - this.headersize));
@@ -170,11 +168,11 @@ export class TabbedPanel extends Disposable implements IThemable {
public removeTab(tab: PanelTabIdentifier) { public removeTab(tab: PanelTabIdentifier) {
let actualTab = this._tabMap.get(tab); let actualTab = this._tabMap.get(tab);
actualTab.header.destroy(); quickBuilder(actualTab.header).destroy();
if (actualTab.view.remove) { if (actualTab.view.remove) {
actualTab.view.remove(); actualTab.view.remove();
} }
this._tabMap.get(tab).header.destroy(); quickBuilder(this._tabMap.get(tab).header).destroy();
this._tabMap.delete(tab); this._tabMap.delete(tab);
if (this._shownTab === tab) { if (this._shownTab === tab) {
this._shownTab = undefined; this._shownTab = undefined;
@@ -192,7 +190,7 @@ export class TabbedPanel extends Disposable implements IThemable {
} }
if (!this.options.showHeaderWhenSingleView && this._tabMap.size === 1 && this._headerVisible) { if (!this.options.showHeaderWhenSingleView && this._tabMap.size === 1 && this._headerVisible) {
this.$header.offDOM(); this.header.remove();
this._headerVisible = false; this._headerVisible = false;
this.layout(this._currentDimensions); this.layout(this._currentDimensions);
} }
@@ -205,12 +203,12 @@ export class TabbedPanel extends Disposable implements IThemable {
public layout(dimension: Dimension): void { public layout(dimension: Dimension): void {
if (dimension) { if (dimension) {
this._currentDimensions = dimension; this._currentDimensions = dimension;
this.$parent.style('height', dimension.height + 'px'); this.parent.style.height = dimension.height + 'px';
this.$parent.style('width', dimension.width + 'px'); this.parent.style.height = dimension.width + 'px';
this.$header.style('width', dimension.width + 'px'); this.header.style.width = dimension.width + 'px';
this.$body.style('width', dimension.width + 'px'); this.body.style.width = dimension.width + 'px';
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0); const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
this.$body.style('height', bodyHeight + 'px'); this.body.style.height = bodyHeight + 'px';
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight)); this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
} }
} }
@@ -232,9 +230,9 @@ export class TabbedPanel extends Disposable implements IThemable {
this._collapsed = val === false ? false : true; this._collapsed = val === false ? false : true;
if (this.collapsed) { if (this.collapsed) {
this.$body.offDOM(); this.body.remove();
} else { } else {
this.$parent.append(this.$body); this.parent.appendChild(this.body);
} }
} }

View File

@@ -76,7 +76,7 @@ class ResultsView implements IPanelView {
this.panelViewlet.resizePanel(this.gridPanel, this.state.messagePanelSize); this.panelViewlet.resizePanel(this.gridPanel, this.state.messagePanelSize);
} }
this.panelViewlet.resizePanel(this.gridPanel, panelSize); this.panelViewlet.resizePanel(this.gridPanel, panelSize);
}) });
// once the user changes the sash we should stop trying to resize the grid // once the user changes the sash we should stop trying to resize the grid
once(this.panelViewlet.onDidSashChange)(e => { once(this.panelViewlet.onDidSashChange)(e => {
this.needsGridResize = false; this.needsGridResize = false;
@@ -204,9 +204,11 @@ export class QueryResultsView {
this._panelView.pushTab(this.qpTab); this._panelView.pushTab(this.qpTab);
} }
} }
this.runnerDisposables.push(queryRunner.onResultSet(() => { this.runnerDisposables.push(queryRunner.onQueryEnd(() => {
if (queryRunner.isQueryPlan) { if (queryRunner.isQueryPlan) {
this.showPlan(queryRunner.planXml); queryRunner.planXml.then(e => {
this.showPlan(e);
});
} }
})); }));
if (this.input.state.activeTab) { if (this.input.state.activeTab) {

View File

@@ -26,6 +26,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ResultSerializer } from 'sql/parts/query/common/resultSerializer'; import { ResultSerializer } from 'sql/parts/query/common/resultSerializer';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { Deferred } from 'sql/base/common/promise';
export interface IEditSessionReadyEvent { export interface IEditSessionReadyEvent {
ownerUri: string; ownerUri: string;
@@ -69,11 +70,11 @@ export default class QueryRunner {
private _hasCompleted: boolean = false; private _hasCompleted: boolean = false;
private _batchSets: sqlops.BatchSummary[] = []; private _batchSets: sqlops.BatchSummary[] = [];
private _eventEmitter = new EventEmitter(); private _eventEmitter = new EventEmitter();
private _isQueryPlan: boolean;
private _isQueryPlan: boolean;
public get isQueryPlan(): boolean { return this._isQueryPlan; } public get isQueryPlan(): boolean { return this._isQueryPlan; }
private _planXml: string; private _planXml = new Deferred<string>();
public get planXml(): string { return this._planXml; } public get planXml(): Thenable<string> { return this._planXml.promise; }
private _onMessage = new Emitter<sqlops.IResultMessage>(); private _onMessage = 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) => {
@@ -342,7 +343,7 @@ export default class QueryRunner {
} }
// handle getting queryPlanxml if we need too // handle getting queryPlanxml if we need too
if (this.isQueryPlan) { if (this.isQueryPlan) {
this.getQueryRows(0, 1, 0, 0).then(e => this._planXml = e.resultSubset.rows[0][0].displayValue); this.getQueryRows(0, 1, 0, 0).then(e => this._planXml.resolve(e.resultSubset.rows[0][0].displayValue));
} }
if (batchSet) { if (batchSet) {
// Store the result set in the batch and emit that a result set has completed // Store the result set in the batch and emit that a result set has completed