Compare commits

...

17 Commits
1.4.2 ... 1.3.9

Author SHA1 Message Date
kisantia
cacf0777c7 Moving onValidityChanged listener to showPage() so that it gets added to pages that are added to the wizard after the initial start up (#3691) 2019-01-14 15:01:54 -08:00
Karl Burtram
cb433fbeac Bump Azure Data Studio to 1.3.9 2019-01-14 14:24:28 -08:00
Kevin Cunnane
8f6736df5e Fix #3736 Notebook: cannot connect to SQL big data cluster due to empty config.json file (#3738)
- Writing the config file in the core for now, will look to move to the extension in Feb release
2019-01-14 14:19:45 -08:00
Anthony Dresser
ef76d730e3 fix html formatting in grid (#3722) 2019-01-14 14:15:25 -08:00
Karl Burtram
aabbeeffa9 Add connection dialog icon dark theme and HC styles (#3721) 2019-01-14 14:13:43 -08:00
Anthony Dresser
7a513de543 Duplicate Result sets (fix merge conflicts) 2019-01-14 14:12:29 -08:00
Karl Burtram
abbd5ec037 Add Idera extension to recommendation list (#3709) 2019-01-14 14:09:08 -08:00
Karl Burtram
84009f65ec Save grid selection/vertical scroll when switching tabs 2019-01-08 15:37:20 -08:00
kisantia
191648df0a Fix database not getting set correctly in DacFx wizard deploy scenario (#3641)
* fix db not getting set correctly for deploy scenario if coming from import page

* removed space so that comments go directly after //
2019-01-07 12:06:20 -08:00
Matt Irvine
1265a56ff4 Update edit data for result set streaming changes (#3634) 2019-01-07 12:06:11 -08:00
Aditya Bist
2171d44922 removed potentially PII (#3619) 2018-12-12 14:13:50 -08:00
Anthony Dresser
812f315e69 Account for different situations for stream setting (#3615)
* add cases for different situation

* default streaming setting false
2018-12-12 12:12:18 -08:00
Karl Burtram
d48258a0fb Turn off "something went wrong" message (#3606) 2018-12-11 16:19:23 -08:00
Karl Burtram
8ec78f67af Merge branch 'master' into release/1.3 2018-12-11 15:10:32 -08:00
Karl Burtram
348b03327e Merge branch 'master' into release/1.3 2018-12-11 11:29:01 -08:00
Karl Burtram
6497bbcc38 Merge branch 'master' into release/1.3 2018-12-10 17:39:23 -08:00
Karl Burtram
a1abca26df Remove a style that is breaking the "Clear MRU" list (#3587) 2018-12-10 17:36:55 -08:00
16 changed files with 215 additions and 160 deletions

View File

@@ -32,7 +32,7 @@ export class DeployConfigPage extends DacFxConfigPage {
} }
async start(): Promise<boolean> { async start(): Promise<boolean> {
let serverComponent = await this.createServerDropdown(true); let serverComponent = await this.createServerDropdown(true);
let fileBrowserComponent = await this.createFileBrowser(); let fileBrowserComponent = await this.createFileBrowser();
this.databaseComponent = await this.createDatabaseTextBox(); this.databaseComponent = await this.createDatabaseTextBox();
this.databaseComponent.title = localize('dacFx.databaseNameTextBox', 'Database Name'); this.databaseComponent.title = localize('dacFx.databaseNameTextBox', 'Database Name');
@@ -131,7 +131,7 @@ export class DeployConfigPage extends DacFxConfigPage {
this.model.database = this.databaseTextBox.value; this.model.database = this.databaseTextBox.value;
}); });
// Initialize with upgrade existing true //Initialize with upgrade existing true
upgradeRadioButton.checked = true; upgradeRadioButton.checked = true;
this.model.upgradeExisting = true; this.model.upgradeExisting = true;
@@ -149,10 +149,10 @@ export class DeployConfigPage extends DacFxConfigPage {
} }
protected async createDeployDatabaseDropdown(): Promise<sqlops.FormComponent> { protected async createDeployDatabaseDropdown(): Promise<sqlops.FormComponent> {
this.databaseDropdown = this.view.modelBuilder.dropDown().withProperties({ this.databaseDropdown = this.view.modelBuilder.dropDown().withProperties({
required: true required: true
}).component(); }).component();
// Handle database changes //Handle database changes
this.databaseDropdown.onValueChanged(async () => { this.databaseDropdown.onValueChanged(async () => {
this.model.database = (<sqlops.CategoryValue>this.databaseDropdown.value).name; this.model.database = (<sqlops.CategoryValue>this.databaseDropdown.value).name;
}); });
@@ -172,7 +172,8 @@ export class DeployConfigPage extends DacFxConfigPage {
} }
let values = await this.getDatabaseValues(); let values = await this.getDatabaseValues();
if (this.model.database === undefined) { //set the database to the first dropdown value if upgrading, otherwise it should get set to the textbox value
if (this.model.upgradeExisting) {
this.model.database = values[0].name; this.model.database = values[0].name;
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "azuredatastudio", "name": "azuredatastudio",
"version": "1.3.8", "version": "1.3.9",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee", "distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": { "author": {
"name": "Microsoft Corporation" "name": "Microsoft Corporation"

View File

@@ -42,7 +42,8 @@
"Microsoft.server-report", "Microsoft.server-report",
"Microsoft.sql-vnext", "Microsoft.sql-vnext",
"Microsoft.whoisactive", "Microsoft.whoisactive",
"Redgate.sql-search" "Redgate.sql-search",
"IDERA.sqldm-performance-insights"
], ],
"extensionsGallery": { "extensionsGallery": {
"serviceUrl": "https://sqlopsextensions.blob.core.windows.net/marketplace/v1/extensionsGallery.json" "serviceUrl": "https://sqlopsextensions.blob.core.windows.net/marketplace/v1/extensionsGallery.json"

View File

@@ -90,6 +90,8 @@
margin: 0px 13px; margin: 0px 13px;
} }
.vs-dark .connection-dialog .connection-history-actions .action-label.icon,
.hc-black .connection-dialog .connection-history-actions .action-label.icon,
.connection-dialog .connection-history-actions .action-label.icon { .connection-dialog .connection-history-actions .action-label.icon {
display: block; display: block;
height: 20px; height: 20px;
@@ -105,10 +107,12 @@
background: url('clear-search-results.svg'); background: url('clear-search-results.svg');
} }
/*
.vs-dark .search-action.clear-search-results, .vs-dark .search-action.clear-search-results,
.hc-black .search-action.clear-search-results { .hc-black .search-action.clear-search-results {
background: url('clear-search-results-dark.svg'); background: url('clear-search-results-dark.svg');
} }
*/
.connection-details-title { .connection-details-title {
font-size: 14px; font-size: 14px;

View File

@@ -168,8 +168,7 @@ export const DashboardModule = (params, selector: string, instantiationService:
if (e instanceof NavigationEnd) { if (e instanceof NavigationEnd) {
this.navigations++; this.navigations++;
TelemetryUtils.addTelemetry(this.telemetryService, TelemetryKeys.DashboardNavigated, { TelemetryUtils.addTelemetry(this.telemetryService, TelemetryKeys.DashboardNavigated, {
numberOfNavigations: this.navigations, numberOfNavigations: this.navigations
routeUrl: e.url
}); });
} }
}); });

View File

@@ -48,13 +48,13 @@ export function textFormatter(row: number, cell: any, value: any, columnDef: any
if (!value.isNull) { if (!value.isNull) {
valueToDisplay = value.displayValue.replace(/(\r\n|\n|\r)/g, ' '); valueToDisplay = value.displayValue.replace(/(\r\n|\n|\r)/g, ' ');
valueToDisplay = escape(valueToDisplay.length > 250 ? valueToDisplay.slice(0, 250) + '...' : valueToDisplay); valueToDisplay = escape(valueToDisplay.length > 250 ? valueToDisplay.slice(0, 250) + '...' : valueToDisplay);
titleValue = value.displayValue; titleValue = valueToDisplay;
} else { } else {
cellClasses += ' missing-value'; cellClasses += ' missing-value';
} }
} else if (typeof value === 'string') { } else if (typeof value === 'string') {
valueToDisplay = escape(value.length > 250 ? value.slice(0, 250) + '...' : value); valueToDisplay = escape(value.length > 250 ? value.slice(0, 250) + '...' : value);
titleValue = value; titleValue = valueToDisplay;
} }
return `<span title="${titleValue}" class="${cellClasses}">${valueToDisplay}</span>`; return `<span title="${titleValue}" class="${cellClasses}">${valueToDisplay}</span>`;

View File

@@ -343,6 +343,9 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
handleResultSet(self: EditDataComponent, event: any): void { handleResultSet(self: EditDataComponent, event: any): void {
// Clone the data before altering it to avoid impacting other subscribers // Clone the data before altering it to avoid impacting other subscribers
let resultSet = Object.assign({}, event.data); let resultSet = Object.assign({}, event.data);
if (!resultSet.complete) {
return;
}
// Add an extra 'new row' // Add an extra 'new row'
resultSet.rowCount++; resultSet.rowCount++;

View File

@@ -350,7 +350,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
if (!newConnection && this._activeContexts.defaultConnection.options['host'] === host) { if (!newConnection && this._activeContexts.defaultConnection.options['host'] === host) {
newConnection = this._activeContexts.defaultConnection; newConnection = this._activeContexts.defaultConnection;
} }
SparkMagicContexts.configureContext(this.notebookOptions); SparkMagicContexts.configureContext();
this._hadoopConnection = new NotebookConnection(newConnection); this._hadoopConnection = new NotebookConnection(newConnection);
this.refreshConnections(newConnection); this.refreshConnections(newConnection);
this._clientSession.updateConnection(this._hadoopConnection); this._clientSession.updateConnection(this._hadoopConnection);

View File

@@ -8,7 +8,6 @@
import * as path from 'path'; import * as path from 'path';
import { nb } from 'sqlops'; import { nb } from 'sqlops';
import * as json from 'vs/base/common/json';
import * as pfs from 'vs/base/node/pfs'; import * as pfs from 'vs/base/node/pfs';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
@@ -17,11 +16,48 @@ import * as notebookUtils from '../notebookUtils';
import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotificationService } from 'vs/platform/notification/common/notification';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
const configBase = {
'kernel_python_credentials': {
'url': ''
},
'kernel_scala_credentials': {
'url': ''
},
'kernel_r_credentials': {
'url': ''
},
'ignore_ssl_errors': true,
'logging_config': {
'version': 1,
'formatters': {
'magicsFormatter': {
'format': '%(asctime)s\t%(levelname)s\t%(message)s',
'datefmt': ''
}
},
'handlers': {
'magicsHandler': {
'class': 'hdijupyterutils.filehandler.MagicsFileHandler',
'formatter': 'magicsFormatter',
'home_path': ''
}
},
'loggers': {
'magicsLogger': {
'handlers': ['magicsHandler'],
'level': 'DEBUG',
'propagate': 0
}
}
}
};
export class SparkMagicContexts { export class SparkMagicContexts {
public static get DefaultContext(): IDefaultConnection { public static get DefaultContext(): IDefaultConnection {
// TODO NOTEBOOK REFACTOR fix default connection handling // TODO NOTEBOOK REFACTOR fix default connection handling
let defaultConnection: IConnectionProfile = <any> { let defaultConnection: IConnectionProfile = <any>{
providerName: notebookConstants.hadoopKnoxProviderName, providerName: notebookConstants.hadoopKnoxProviderName,
id: '-1', id: '-1',
options: options:
@@ -47,7 +83,7 @@ export class SparkMagicContexts {
let connections: IDefaultConnection = this.DefaultContext; let connections: IDefaultConnection = this.DefaultContext;
if (!profile) { if (!profile) {
if (!kernelChangedArgs || !kernelChangedArgs.newValue || if (!kernelChangedArgs || !kernelChangedArgs.newValue ||
(kernelChangedArgs.oldValue && kernelChangedArgs.newValue.id === kernelChangedArgs.oldValue.id)) { (kernelChangedArgs.oldValue && kernelChangedArgs.newValue.id === kernelChangedArgs.oldValue.id)) {
// nothing to do, kernels are the same or new kernel is undefined // nothing to do, kernels are the same or new kernel is undefined
return connections; return connections;
} }
@@ -55,7 +91,7 @@ export class SparkMagicContexts {
if (kernelChangedArgs && kernelChangedArgs.newValue && kernelChangedArgs.newValue.name) { if (kernelChangedArgs && kernelChangedArgs.newValue && kernelChangedArgs.newValue.name) {
switch (kernelChangedArgs.newValue.name) { switch (kernelChangedArgs.newValue.name) {
case (notebookConstants.python3): case (notebookConstants.python3):
// python3 case, use this.DefaultContext for the only connection // python3 case, use this.DefaultContext for the only connection
break; break;
//TO DO: Handle server connections based on kernel type. Right now, we call the same method for all kernel types. //TO DO: Handle server connections based on kernel type. Right now, we call the same method for all kernel types.
default: default:
@@ -76,13 +112,13 @@ export class SparkMagicContexts {
let defaultConnection: IConnectionProfile = SparkMagicContexts.DefaultContext.defaultConnection; let defaultConnection: IConnectionProfile = SparkMagicContexts.DefaultContext.defaultConnection;
let activeConnections: IConnectionProfile[] = await connectionService.getActiveConnections(); let activeConnections: IConnectionProfile[] = await connectionService.getActiveConnections();
// If no connections exist, only show 'n/a' // If no connections exist, only show 'n/a'
if (activeConnections && activeConnections.length > 0) { if (activeConnections && activeConnections.length > 0) {
// Remove all non-Spark connections // Remove all non-Spark connections
activeConnections = activeConnections.filter(conn => conn.providerName === notebookConstants.hadoopKnoxProviderName); activeConnections = activeConnections.filter(conn => conn.providerName === notebookConstants.hadoopKnoxProviderName);
} }
if (activeConnections.length === 0) { if (activeConnections.length === 0) {
return SparkMagicContexts.DefaultContext; return SparkMagicContexts.DefaultContext;
} }
// If launched from the right click or server dashboard, connection profile data exists, so use that as default // If launched from the right click or server dashboard, connection profile data exists, so use that as default
if (profile && profile.options) { if (profile && profile.options) {
@@ -95,7 +131,7 @@ export class SparkMagicContexts {
defaultConnection = activeConnections[0]; defaultConnection = activeConnections[0];
} else { } else {
// TODO NOTEBOOK REFACTOR change this so it's no longer incompatible with IConnectionProfile // TODO NOTEBOOK REFACTOR change this so it's no longer incompatible with IConnectionProfile
defaultConnection = <IConnectionProfile> <any>{ defaultConnection = <IConnectionProfile><any>{
providerName: notebookConstants.hadoopKnoxProviderName, providerName: notebookConstants.hadoopKnoxProviderName,
id: '-1', id: '-1',
options: options:
@@ -107,31 +143,28 @@ export class SparkMagicContexts {
} }
} }
return { return {
otherConnections: activeConnections, otherConnections: activeConnections,
defaultConnection: defaultConnection defaultConnection: defaultConnection
}; };
} }
public static async configureContext(options: INotebookModelOptions): Promise<object> { public static async configureContext(): Promise<object> {
let sparkmagicConfDir = path.join(notebookUtils.getUserHome(), '.sparkmagic'); let sparkmagicConfDir = path.join(notebookUtils.getUserHome(), '.sparkmagic');
// TODO NOTEBOOK REFACTOR re-enable this or move to extension. Requires config files to be available in order to work // TODO NOTEBOOK REFACTOR re-enable this or move to extension. Requires config files to be available in order to work
// await notebookUtils.mkDir(sparkmagicConfDir); await notebookUtils.mkDir(sparkmagicConfDir);
// // Default to localhost in config file. // Default to localhost in config file.
// let creds: ICredentials = { let creds: ICredentials = {
// 'url': 'http://localhost:8088' 'url': 'http://localhost:8088'
// }; };
// let configPath = notebookUtils.getTemplatePath(options.extensionContext.extensionPath, path.join('jupyter_config', 'sparkmagic_config.json')); let config: ISparkMagicConfig = Object.assign({}, configBase);
// let fileBuffer: Buffer = await pfs.readFile(configPath); SparkMagicContexts.updateConfig(config, creds, sparkmagicConfDir);
// let fileContents: string = fileBuffer.toString();
// let config: ISparkMagicConfig = json.parse(fileContents);
// SparkMagicContexts.updateConfig(config, creds, sparkmagicConfDir);
// let configFilePath = path.join(sparkmagicConfDir, 'config.json'); let configFilePath = path.join(sparkmagicConfDir, 'config.json');
// await pfs.writeFile(configFilePath, JSON.stringify(config)); await pfs.writeFile(configFilePath, JSON.stringify(config));
return {'SPARKMAGIC_CONF_DIR': sparkmagicConfDir}; return { 'SPARKMAGIC_CONF_DIR': sparkmagicConfDir };
} }
/** /**
* *
@@ -186,10 +219,13 @@ interface ISparkMagicConfig {
kernel_python_credentials: ICredentials; kernel_python_credentials: ICredentials;
kernel_scala_credentials: ICredentials; kernel_scala_credentials: ICredentials;
kernel_r_credentials: ICredentials; kernel_r_credentials: ICredentials;
ignore_ssl_errors?: boolean;
logging_config: { logging_config: {
handlers: { handlers: {
magicsHandler: { magicsHandler: {
home_path: string; home_path: string;
class?: string;
formatter?: string
} }
} }
}; };

View File

@@ -308,7 +308,7 @@ let registryProperties = {
'sql.results.streaming': { 'sql.results.streaming': {
'type': 'boolean', 'type': 'boolean',
'description': localize('sql.results.streaming', 'Enable results streaming; contains few minor visual issues'), 'description': localize('sql.results.streaming', 'Enable results streaming; contains few minor visual issues'),
'default': true 'default': false
}, },
'sql.copyIncludeHeaders': { 'sql.copyIncludeHeaders': {
'type': 'boolean', 'type': 'boolean',

View File

@@ -88,7 +88,8 @@ export class GridTableState extends Disposable {
private _canBeMaximized: boolean; private _canBeMaximized: boolean;
/* The top row of the current scroll */ /* The top row of the current scroll */
public scrollPosition = 0; public scrollPositionY = 0;
public scrollPositionX = 0;
public selection: Slick.Range[]; public selection: Slick.Range[];
public activeCell: Slick.Cell; public activeCell: Slick.Cell;
@@ -145,7 +146,7 @@ export class GridPanel extends ViewletPanel {
super(options, keybindingService, contextMenuService, configurationService); super(options, keybindingService, contextMenuService, configurationService);
this.splitView = new ScrollableSplitView(this.container, { enableResizing: false, verticalScrollbarVisibility: ScrollbarVisibility.Visible }); this.splitView = new ScrollableSplitView(this.container, { enableResizing: false, verticalScrollbarVisibility: ScrollbarVisibility.Visible });
this.splitView.onScroll(e => { this.splitView.onScroll(e => {
if (this.state) { if (this.state && this.splitView.length !== 0) {
this.state.scrollPosition = e; this.state.scrollPosition = e;
} }
}); });
@@ -185,12 +186,31 @@ export class GridPanel extends ViewletPanel {
} }
this.reset(); this.reset();
})); }));
this.addResultSet(this.runner.batchSets.reduce<sqlops.ResultSetSummary[]>((p, e) => {
if (this.configurationService.getValue<boolean>('sql.results.streaming')) {
p = p.concat(e.resultSetSummaries);
} else {
p = p.concat(e.resultSetSummaries.filter(c => c.complete));
}
return p;
}, []));
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(this.state.scrollPosition);
}
} }
private onResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) { private onResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) {
if (this.configurationService.getValue<boolean>('sql.results.streaming')) { let resultsToAdd: sqlops.ResultSetSummary[];
this.addResultSet(resultSet); if (!Array.isArray(resultSet)) {
resultsToAdd = [resultSet];
} else {
resultsToAdd = resultSet.splice(0);
}
const sizeChanges = () => {
this.tables.map(t => { this.tables.map(t => {
t.state.canBeMaximized = this.tables.length > 1; t.state.canBeMaximized = this.tables.length > 1;
}); });
@@ -202,6 +222,17 @@ export class GridPanel extends ViewletPanel {
if (this.state && this.state.scrollPosition) { if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(this.state.scrollPosition); this.splitView.setScrollPosition(this.state.scrollPosition);
} }
};
if (this.configurationService.getValue<boolean>('sql.results.streaming')) {
this.addResultSet(resultsToAdd);
sizeChanges();
} else {
resultsToAdd = resultsToAdd.filter(e => e.complete);
if (resultsToAdd.length > 0) {
this.addResultSet(resultsToAdd);
}
sizeChanges();
} }
} }
@@ -210,9 +241,19 @@ export class GridPanel extends ViewletPanel {
if (!Array.isArray(resultSet)) { if (!Array.isArray(resultSet)) {
resultsToUpdate = [resultSet]; resultsToUpdate = [resultSet];
} else { } else {
resultsToUpdate = resultSet; resultsToUpdate = resultSet.splice(0);
} }
const sizeChanges = () => {
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(this.state.scrollPosition);
}
};
if (this.configurationService.getValue<boolean>('sql.results.streaming')) { if (this.configurationService.getValue<boolean>('sql.results.streaming')) {
for (let set of resultsToUpdate) { for (let set of resultsToUpdate) {
let table = this.tables.find(t => t.resultSet.batchId === set.batchId && t.resultSet.id === set.id); let table = this.tables.find(t => t.resultSet.batchId === set.batchId && t.resultSet.id === set.id);
@@ -222,47 +263,20 @@ export class GridPanel extends ViewletPanel {
warn('Got result set update request for non-existant table'); warn('Got result set update request for non-existant table');
} }
} }
sizeChanges();
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(this.state.scrollPosition);
}
} else { } else {
let change = false; resultsToUpdate = resultsToUpdate.filter(e => e.complete);
if (resultsToUpdate.length > 0) {
for (let set of resultsToUpdate) { this.addResultSet(resultsToUpdate);
if (set.complete) {
this.addResultSet(set);
change = true;
}
}
if (change) {
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(this.state.scrollPosition);
}
} }
sizeChanges();
} }
} }
private addResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) { private addResultSet(resultSet: sqlops.ResultSetSummary[]) {
let resultsToAdd: sqlops.ResultSetSummary[];
if (!Array.isArray(resultSet)) {
resultsToAdd = [resultSet];
} else {
resultsToAdd = resultSet;
}
let tables: GridTable<any>[] = []; let tables: GridTable<any>[] = [];
for (let set of resultsToAdd) { for (let set of resultSet) {
let tableState: GridTableState; let tableState: GridTableState;
if (this._state) { if (this._state) {
tableState = this.state.tableStates.find(e => e.batchId === set.batchId && e.resultId === set.id); tableState = this.state.tableStates.find(e => e.batchId === set.batchId && e.resultId === set.id);
@@ -442,6 +456,7 @@ class GridTable<T> extends Disposable implements IView {
}); });
this.dataProvider.dataRows = collection; this.dataProvider.dataRows = collection;
this.table.updateRowCount(); this.table.updateRowCount();
this.setupState();
} }
public onRemove() { public onRemove() {
@@ -535,12 +550,18 @@ class GridTable<T> extends Disposable implements IView {
}); });
this.table.grid.onScroll.subscribe((e, data) => { this.table.grid.onScroll.subscribe((e, data) => {
if (!this.scrolled && this.state.scrollPosition && isInDOM(this.container)) { if (!this.visible) {
// If the grid is not set up yet it can get scroll events resetting the top to 0px,
// so ignore those events
return;
}
if (!this.scrolled && (this.state.scrollPositionY || this.state.scrollPositionX) && isInDOM(this.container)) {
this.scrolled = true; this.scrolled = true;
this.table.grid.scrollTo(this.state.scrollPosition); this.restoreScrollState();
} }
if (this.state && isInDOM(this.container)) { if (this.state && isInDOM(this.container)) {
this.state.scrollPosition = data.scrollTop; this.state.scrollPositionY = data.scrollTop;
this.state.scrollPositionX = data.scrollLeft;
} }
}); });
@@ -549,8 +570,13 @@ class GridTable<T> extends Disposable implements IView {
this.state.activeCell = this.table.grid.getActiveCell(); this.state.activeCell = this.table.grid.getActiveCell();
} }
}); });
}
this.setupState(); private restoreScrollState() {
if (this.state.scrollPositionX || this.state.scrollPositionY) {
this.table.grid.scrollTo(this.state.scrollPositionY);
this.table.grid.getContainerNode().children[3].scrollLeft = this.state.scrollPositionX;
}
} }
private setupState() { private setupState() {
@@ -559,20 +585,18 @@ class GridTable<T> extends Disposable implements IView {
this._register(this.state.onCanBeMaximizedChange(this.rebuildActionBar, this)); this._register(this.state.onCanBeMaximizedChange(this.rebuildActionBar, this));
if (this.state.scrollPosition) { this.restoreScrollState();
// most of the time this won't do anything
this.table.grid.scrollTo(this.state.scrollPosition);
// the problem here is that the scrolling state slickgrid uses
// doesn't work with it offDOM.
}
if (this.state.selection) { // Setting the active cell resets the selection so save it here
this.selectionModel.setSelectedRanges(this.state.selection); let savedSelection = this.state.selection;
}
if (this.state.activeCell) { if (this.state.activeCell) {
this.table.setActiveCell(this.state.activeCell.row, this.state.activeCell.cell); this.table.setActiveCell(this.state.activeCell.row, this.state.activeCell.cell);
} }
if (savedSelection) {
this.selectionModel.setSelectedRanges(savedSelection);
}
} }
public get state(): GridTableState { public get state(): GridTableState {

View File

@@ -144,6 +144,7 @@ export class MessagePanel extends ViewletPanel {
this.reset(); this.reset();
this.queryRunnerDisposables.push(runner.onQueryStart(() => this.reset())); this.queryRunnerDisposables.push(runner.onQueryStart(() => this.reset()));
this.queryRunnerDisposables.push(runner.onMessage(e => this.onMessage(e))); this.queryRunnerDisposables.push(runner.onMessage(e => this.onMessage(e)));
this.onMessage(runner.messages);
} }
private onMessage(message: IResultMessage | IResultMessage[]) { private onMessage(message: IResultMessage | IResultMessage[]) {

View File

@@ -387,8 +387,12 @@ 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); queryRunner = this._instantiationService.createInstance(QueryRunner, ownerUri);
const resultSetEventType = 'resultSet';
queryRunner.addListener(QREvents.RESULT_SET, resultSet => { queryRunner.addListener(QREvents.RESULT_SET, resultSet => {
this._fireQueryEvent(ownerUri, 'resultSet', resultSet); this._fireQueryEvent(ownerUri, resultSetEventType, resultSet);
});
queryRunner.onResultSetUpdate(resultSetSummary => {
this._fireQueryEvent(ownerUri, resultSetEventType, resultSetSummary);
}); });
queryRunner.addListener(QREvents.BATCH_START, batch => { queryRunner.addListener(QREvents.BATCH_START, batch => {
let link = undefined; let link = undefined;

View File

@@ -13,6 +13,7 @@ import { IQueryManagementService } from 'sql/parts/query/common/queryManagement'
import * as Utils from 'sql/parts/connection/common/utils'; import * as Utils from 'sql/parts/connection/common/utils';
import { SaveFormat } from 'sql/parts/grid/common/interfaces'; import { SaveFormat } from 'sql/parts/grid/common/interfaces';
import { echo, debounceEvent } from 'sql/base/common/event'; import { echo, debounceEvent } from 'sql/base/common/event';
import { Deferred } from 'sql/base/common/promise';
import Severity from 'vs/base/common/severity'; import Severity from 'vs/base/common/severity';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
@@ -26,7 +27,6 @@ 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,6 +69,7 @@ export default class QueryRunner extends Disposable {
private _isExecuting: boolean = false; private _isExecuting: boolean = false;
private _hasCompleted: boolean = false; private _hasCompleted: boolean = false;
private _batchSets: sqlops.BatchSummary[] = []; private _batchSets: sqlops.BatchSummary[] = [];
private _messages: sqlops.IResultMessage[] = [];
private _eventEmitter = new EventEmitter(); private _eventEmitter = new EventEmitter();
private _isQueryPlan: boolean; private _isQueryPlan: boolean;
@@ -77,40 +78,13 @@ export default class QueryRunner extends Disposable {
public get planXml(): Thenable<string> { return this._planXml.promise; } public get planXml(): Thenable<string> { return this._planXml.promise; }
private _onMessage = this._register(new Emitter<sqlops.IResultMessage>()); private _onMessage = this._register(new Emitter<sqlops.IResultMessage>());
private _debouncedMessage = debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(this._onMessage.event, (l, e) => { public readonly onMessage = this._onMessage.event;
// on first run
if (types.isUndefinedOrNull(l)) {
return [e];
} else {
return l.concat(e);
}
});
private _echoedMessages = echo(this._debouncedMessage.event);
public readonly onMessage = this._echoedMessages.event;
private _onResultSet = this._register(new Emitter<sqlops.ResultSetSummary>()); private _onResultSet = this._register(new Emitter<sqlops.ResultSetSummary>());
private _debouncedResultSet = debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(this._onResultSet.event, (l, e) => { public readonly onResultSet = this._onResultSet.event;
// on first run
if (types.isUndefinedOrNull(l)) {
return [e];
} else {
return l.concat(e);
}
});
private _echoedResultSet = echo(this._debouncedResultSet.event);
public readonly onResultSet = this._echoedResultSet.event;
private _onResultSetUpdate = this._register(new Emitter<sqlops.ResultSetSummary>()); private _onResultSetUpdate = this._register(new Emitter<sqlops.ResultSetSummary>());
private _debouncedResultSetUpdate = debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(this._onResultSetUpdate.event, (l, e) => { public readonly onResultSetUpdate = this._onResultSetUpdate.event;
// on first run
if (types.isUndefinedOrNull(l)) {
return [e];
} else {
return l.concat(e);
}
});
private _echoedResultSetUpdate = echo(this._debouncedResultSetUpdate.event);
public readonly onResultSetUpdate = this._echoedResultSetUpdate.event;
private _onQueryStart = this._register(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;
@@ -153,8 +127,18 @@ export default class QueryRunner extends Disposable {
return this._hasCompleted; return this._hasCompleted;
} }
get batchSets(): sqlops.BatchSummary[] { /**
return this._batchSets; * For public use only, for private use, directly access the member
*/
public get batchSets(): sqlops.BatchSummary[] {
return this._batchSets.slice(0);
}
/**
* For public use only, for private use, directly access the member
*/
public get messages(): sqlops.IResultMessage[] {
return this._messages.slice(0);
} }
// PUBLIC METHODS ====================================================== // PUBLIC METHODS ======================================================
@@ -202,10 +186,6 @@ export default class QueryRunner extends Disposable {
if (this.isExecuting) { if (this.isExecuting) {
return TPromise.as(undefined); return TPromise.as(undefined);
} }
this._echoedMessages.clear();
this._echoedResultSet.clear();
this._debouncedMessage.clear();
this._debouncedResultSet.clear();
this._planXml = new Deferred<string>(); this._planXml = new Deferred<string>();
this._batchSets = []; this._batchSets = [];
this._hasCompleted = false; this._hasCompleted = false;
@@ -274,7 +254,7 @@ export default class QueryRunner extends Disposable {
this._hasCompleted = true; this._hasCompleted = true;
this._batchSets = result.batchSummaries ? result.batchSummaries : []; this._batchSets = result.batchSummaries ? result.batchSummaries : [];
this.batchSets.map(batch => { this._batchSets.map(batch => {
if (batch.selection) { if (batch.selection) {
batch.selection.startLine = batch.selection.startLine + this._resultLineOffset; batch.selection.startLine = batch.selection.startLine + this._resultLineOffset;
batch.selection.endLine = batch.selection.endLine + this._resultLineOffset; batch.selection.endLine = batch.selection.endLine + this._resultLineOffset;
@@ -291,6 +271,7 @@ export default class QueryRunner extends Disposable {
isError: false, isError: false,
time: undefined time: undefined
}; };
this._messages.push(message);
this._onQueryEnd.fire(timeStamp); this._onQueryEnd.fire(timeStamp);
this._onMessage.fire(message); this._onMessage.fire(message);
@@ -312,7 +293,7 @@ export default class QueryRunner extends Disposable {
batch.resultSetSummaries = []; batch.resultSetSummaries = [];
// Store the batch // Store the batch
this.batchSets[batch.id] = batch; this._batchSets[batch.id] = batch;
let message = { let message = {
// account for index by 1 // account for index by 1
@@ -321,6 +302,7 @@ export default class QueryRunner extends Disposable {
selection: batch.selection, selection: batch.selection,
isError: false isError: false
}; };
this._messages.push(message);
this._eventEmitter.emit(EventType.BATCH_START, batch); this._eventEmitter.emit(EventType.BATCH_START, batch);
this._onMessage.fire(message); this._onMessage.fire(message);
this._onBatchStart.fire(batch); this._onBatchStart.fire(batch);
@@ -333,7 +315,7 @@ export default class QueryRunner extends Disposable {
let batch: sqlops.BatchSummary = result.batchSummary; let batch: sqlops.BatchSummary = result.batchSummary;
// Store the batch again to get the rest of the data // Store the batch again to get the rest of the data
this.batchSets[batch.id] = batch; this._batchSets[batch.id] = batch;
let executionTime = <number>(Utils.parseTimeString(batch.executionElapsed) || 0); let executionTime = <number>(Utils.parseTimeString(batch.executionElapsed) || 0);
this._totalElapsedMilliseconds += executionTime; this._totalElapsedMilliseconds += executionTime;
if (executionTime > 0) { if (executionTime > 0) {
@@ -355,8 +337,8 @@ export default class QueryRunner extends Disposable {
if (!resultSet.batchId) { if (!resultSet.batchId) {
// Missing the batchId. In this case, default to always using the first batch in the list // Missing the batchId. In this case, default to always using the first batch in the list
// or create one in the case the DMP extension didn't obey the contract perfectly // or create one in the case the DMP extension didn't obey the contract perfectly
if (this.batchSets.length > 0) { if (this._batchSets.length > 0) {
batchSet = this.batchSets[0]; batchSet = this._batchSets[0];
} else { } else {
batchSet = <sqlops.BatchSummary>{ batchSet = <sqlops.BatchSummary>{
id: 0, id: 0,
@@ -364,10 +346,10 @@ export default class QueryRunner extends Disposable {
hasError: false, hasError: false,
resultSetSummaries: [] resultSetSummaries: []
}; };
this.batchSets[0] = batchSet; this._batchSets[0] = batchSet;
} }
} else { } else {
batchSet = this.batchSets[resultSet.batchId]; batchSet = this._batchSets[resultSet.batchId];
} }
// handle getting queryPlanxml if we need too // handle getting queryPlanxml if we need too
if (this.isQueryPlan) { if (this.isQueryPlan) {
@@ -392,7 +374,7 @@ export default class QueryRunner extends Disposable {
if (result && result.resultSetSummary) { if (result && result.resultSetSummary) {
let resultSet = result.resultSetSummary; let resultSet = result.resultSetSummary;
let batchSet: sqlops.BatchSummary; let batchSet: sqlops.BatchSummary;
batchSet = this.batchSets[resultSet.batchId]; batchSet = this._batchSets[resultSet.batchId];
// handle getting queryPlanxml if we need too // handle getting queryPlanxml if we need too
if (this.isQueryPlan) { if (this.isQueryPlan) {
// check if this result has show plan, this needs work, it won't work for any other provider // check if this result has show plan, this needs work, it won't work for any other provider
@@ -415,6 +397,7 @@ export default class QueryRunner extends Disposable {
public handleMessage(obj: sqlops.QueryExecuteMessageParams): void { public handleMessage(obj: sqlops.QueryExecuteMessageParams): void {
let message = obj.message; let message = obj.message;
message.time = new Date(message.time).toLocaleTimeString(); message.time = new Date(message.time).toLocaleTimeString();
this._messages.push(message);
// Send the message to the results pane // Send the message to the results pane
this._eventEmitter.emit(EventType.MESSAGE, message); this._eventEmitter.emit(EventType.MESSAGE, message);
@@ -434,10 +417,10 @@ export default class QueryRunner extends Disposable {
}; };
return this._queryManagementService.getQueryRows(rowData).then(r => r, error => { return this._queryManagementService.getQueryRows(rowData).then(r => r, error => {
this._notificationService.notify({ // this._notificationService.notify({
severity: Severity.Error, // severity: Severity.Error,
message: nls.localize('query.gettingRowsFailedError', 'Something went wrong getting more rows: {0}', error) // message: nls.localize('query.gettingRowsFailedError', 'Something went wrong getting more rows: {0}', error)
}); // });
return error; return error;
}); });
} }
@@ -492,11 +475,11 @@ export default class QueryRunner extends Disposable {
} }
resolve(result); resolve(result);
}, error => { }, error => {
let errorMessage = nls.localize('query.moreRowsFailedError', 'Something went wrong getting more rows:'); // let errorMessage = nls.localize('query.moreRowsFailedError', 'Something went wrong getting more rows:');
self._notificationService.notify({ // self._notificationService.notify({
severity: Severity.Error, // severity: Severity.Error,
message: `${errorMessage} ${error}` // message: `${errorMessage} ${error}`
}); // });
reject(error); reject(error);
}); });
}); });
@@ -636,7 +619,7 @@ export default class QueryRunner extends Disposable {
private getColumnHeaders(batchId: number, resultId: number, range: Slick.Range): string[] { private getColumnHeaders(batchId: number, resultId: number, range: Slick.Range): string[] {
let headers: string[] = undefined; let headers: string[] = undefined;
let batchSummary: sqlops.BatchSummary = this.batchSets[batchId]; let batchSummary: sqlops.BatchSummary = this._batchSets[batchId];
if (batchSummary !== undefined) { if (batchSummary !== undefined) {
let resultSetSummary = batchSummary.resultSetSummaries[resultId]; let resultSetSummary = batchSummary.resultSetSummaries[resultId];
headers = resultSetSummary.columnInfo.slice(range.fromCell, range.toCell + 1).map((info, i) => { headers = resultSetSummary.columnInfo.slice(range.fromCell, range.toCell + 1).map((info, i) => {
@@ -669,6 +652,7 @@ export default class QueryRunner extends Disposable {
time: undefined, time: undefined,
isError: false isError: false
}; };
this._messages.push(message);
// Send the message to the results pane // Send the message to the results pane
this._onMessage.fire(message); this._onMessage.fire(message);
} }

View File

@@ -97,15 +97,6 @@ export class WizardModal extends Modal {
messageChangeHandler(this._wizard.message); messageChangeHandler(this._wizard.message);
this._wizard.onMessageChange(message => messageChangeHandler(message)); this._wizard.onMessageChange(message => messageChangeHandler(message));
this._wizard.pages.forEach((page, index) => {
page.onValidityChanged(valid => {
if (index === this._wizard.currentPage) {
this._nextButton.enabled = this._wizard.nextButton.enabled && page.valid;
this._doneButton.enabled = this._wizard.doneButton.enabled && page.valid;
}
});
});
} }
private addDialogButton(button: DialogButton, onSelect: () => void = () => undefined, registerClickEvent: boolean = true, requirePageValid: boolean = false): Button { private addDialogButton(button: DialogButton, onSelect: () => void = () => undefined, registerClickEvent: boolean = true, requirePageValid: boolean = false): Button {
@@ -198,6 +189,13 @@ export class WizardModal extends Modal {
let currentPageValid = this._wizard.pages[this._wizard.currentPage].valid; let currentPageValid = this._wizard.pages[this._wizard.currentPage].valid;
this._nextButton.enabled = this._wizard.nextButton.enabled && currentPageValid; this._nextButton.enabled = this._wizard.nextButton.enabled && currentPageValid;
this._doneButton.enabled = this._wizard.doneButton.enabled && currentPageValid; this._doneButton.enabled = this._wizard.doneButton.enabled && currentPageValid;
pageToShow.onValidityChanged(valid => {
if (index === this._wizard.currentPage) {
this._nextButton.enabled = this._wizard.nextButton.enabled && pageToShow.valid;
this._doneButton.enabled = this._wizard.doneButton.enabled && pageToShow.valid;
}
});
} }
private setButtonsForPage(index: number) { private setButtonsForPage(index: number) {

View File

@@ -24,7 +24,7 @@ export function resolveCommonProperties(commit: string, version: string, machine
result['sessionID'] = ''; result['sessionID'] = '';
// __GDPR__COMMON__ "commitHash" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } // __GDPR__COMMON__ "commitHash" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
result['commitHash'] = commit; result['commitHash'] = '';
// __GDPR__COMMON__ "version" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } // __GDPR__COMMON__ "version" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
result['version'] = version; result['version'] = version;
// __GDPR__COMMON__ "common.platformVersion" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } // __GDPR__COMMON__ "common.platformVersion" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }