diff --git a/src/sql/workbench/contrib/charts/browser/chartView.ts b/src/sql/workbench/contrib/charts/browser/chartView.ts
index ca468aed49..1e666b313d 100644
--- a/src/sql/workbench/contrib/charts/browser/chartView.ts
+++ b/src/sql/workbench/contrib/charts/browser/chartView.ts
@@ -234,7 +234,7 @@ export class ChartView extends Disposable implements IPanelView {
if (batch) {
let summary = batch.resultSetSummaries[this._currentData.resultId];
if (summary) {
- this._queryRunner.getQueryRows(0, Math.min(getChartMaxRowCount(this._configurationService), summary.rowCount), this._currentData.batchId, this._currentData.resultId).then(d => {
+ this._queryRunner.getQueryRowsPaged(0, Math.min(getChartMaxRowCount(this._configurationService), summary.rowCount), this._currentData.batchId, this._currentData.resultId).then(d => {
let rows = d.rows;
let columns = summary.columnInfo.map(c => c.columnName);
this.setData(rows, columns);
diff --git a/src/sql/workbench/contrib/query/browser/keyboardQueryActions.ts b/src/sql/workbench/contrib/query/browser/keyboardQueryActions.ts
index ac916b6b26..47f302454f 100644
--- a/src/sql/workbench/contrib/query/browser/keyboardQueryActions.ts
+++ b/src/sql/workbench/contrib/query/browser/keyboardQueryActions.ts
@@ -169,7 +169,7 @@ export class CopyQueryWithResultsKeyboardAction extends Action2 {
if (queryRunner && queryRunner.batchSets.length > 0) {
for (let i = 0; i < queryRunner.batchSets[0].resultSetSummaries.length; i++) {
let resultSummary = queryRunner.batchSets[0].resultSetSummaries[i];
- let result = await queryRunner.getQueryRows(0, resultSummary.rowCount, resultSummary.batchId, resultSummary.id);
+ let result = await queryRunner.getQueryRowsPaged(0, resultSummary.rowCount, resultSummary.batchId, resultSummary.id);
let tableHeaders = resultSummary.columnInfo.map((col, i) => (col.columnName));
let htmlTableHeaders = `${resultSummary.columnInfo.map((col, i) => (`| ${escape(col.columnName)} | `)).join('')}
`;
let copyString = '\n';
diff --git a/src/sql/workbench/services/insights/browser/insightsDialogController.ts b/src/sql/workbench/services/insights/browser/insightsDialogController.ts
index 17085a590f..6f27514a3e 100644
--- a/src/sql/workbench/services/insights/browser/insightsDialogController.ts
+++ b/src/sql/workbench/services/insights/browser/insightsDialogController.ts
@@ -161,7 +161,7 @@ export class InsightsDialogController {
this._columns = resultset.columnInfo;
let rows: ResultSetSubset;
try {
- rows = await this._queryRunner.getQueryRows(0, resultset.rowCount, batch.id, resultset.id);
+ rows = await this._queryRunner.getQueryRowsPaged(0, resultset.rowCount, batch.id, resultset.id);
} catch (e) {
return Promise.reject(e);
}
diff --git a/src/sql/workbench/services/insights/test/browser/insightsDialogController.test.ts b/src/sql/workbench/services/insights/test/browser/insightsDialogController.test.ts
index 2e6eeb4658..4c8a714eca 100644
--- a/src/sql/workbench/services/insights/test/browser/insightsDialogController.test.ts
+++ b/src/sql/workbench/services/insights/test/browser/insightsDialogController.test.ts
@@ -130,7 +130,7 @@ function getPrimedQueryRunner(data: string[][], columns: string[]): IPrimedQuery
];
});
- querymock.setup(x => x.getQueryRows(It.isAnyNumber(), It.isAnyNumber(), It.isAnyNumber(), It.isAnyNumber()))
+ querymock.setup(x => x.getQueryRowsPaged(It.isAnyNumber(), It.isAnyNumber(), It.isAnyNumber(), It.isAnyNumber()))
.returns(x => Promise.resolve({
rowCount: data.length,
rows: data.map(r => r.map(c => { return { displayValue: c }; }))
diff --git a/src/sql/workbench/services/query/common/queryManagement.ts b/src/sql/workbench/services/query/common/queryManagement.ts
index f7b88cf64f..aecb9e8365 100644
--- a/src/sql/workbench/services/query/common/queryManagement.ts
+++ b/src/sql/workbench/services/query/common/queryManagement.ts
@@ -51,7 +51,20 @@ export interface IQueryManagementService {
runQueryString(ownerUri: string, queryString: string): Promise;
runQueryAndReturn(ownerUri: string, queryString: string): Promise;
parseSyntax(ownerUri: string, query: string): Promise;
- getQueryRows(rowData: azdata.QueryExecuteSubsetParams, cancellationToken?: CancellationToken, onProgressCallback?: (availableRows: number) => void): Promise;
+ /**
+ * Fetches the specified rows - this will fetch all the rows at once.
+ * @param rowData The rows to fetch
+ * @deprecated getQueryRowsPaged should be preferred as it is much more performant for large data sets
+ */
+ getQueryRows(rowData: azdata.QueryExecuteSubsetParams): Promise;
+ /**
+ * Fetches the specified rows with paging - getting subsets of the total rows one page at a time and then returning the entire set once
+ * completed.
+ * @param rowData The rows to fetch
+ * @param cancellationToken Optional cancellation token for canceling the operation while fetching rows
+ * @param onProgressCallback Optional callback that will be called each time a page of rows is fetched
+ */
+ getQueryRowsPaged(rowData: azdata.QueryExecuteSubsetParams, cancellationToken?: CancellationToken, onProgressCallback?: (availableRows: number) => void): Promise;
disposeQuery(ownerUri: string): Promise;
changeConnectionUri(newUri: string, oldUri: string): Promise;
saveResults(requestParams: azdata.SaveResultsRequestParams): Promise;
@@ -272,7 +285,13 @@ export class QueryManagementService implements IQueryManagementService {
});
}
- public async getQueryRows(rowData: azdata.QueryExecuteSubsetParams, cancellationToken?: CancellationToken, onProgressCallback?: (availableRows: number) => void): Promise {
+ public async getQueryRows(rowData: azdata.QueryExecuteSubsetParams): Promise {
+ return this._runAction(rowData.ownerUri, (runner) => {
+ return runner.getQueryRows(rowData).then(r => r.resultSubset);
+ });
+ }
+
+ public async getQueryRowsPaged(rowData: azdata.QueryExecuteSubsetParams, cancellationToken?: CancellationToken, onProgressCallback?: (availableRows: number) => void): Promise {
const pageSize = 500;
return this._runAction(rowData.ownerUri, async (runner): Promise => {
const result = [];
diff --git a/src/sql/workbench/services/query/common/queryModelService.ts b/src/sql/workbench/services/query/common/queryModelService.ts
index aec97d0d9b..216c2b3b70 100644
--- a/src/sql/workbench/services/query/common/queryModelService.ts
+++ b/src/sql/workbench/services/query/common/queryModelService.ts
@@ -150,7 +150,7 @@ export class QueryModelService implements IQueryModelService {
*/
public getQueryRows(uri: string, rowStart: number, numberOfRows: number, batchId: number, resultId: number): Promise {
if (this._queryInfoMap.has(uri)) {
- return this._getQueryInfo(uri)!.queryRunner!.getQueryRows(rowStart, numberOfRows, batchId, resultId).then(results => {
+ return this._getQueryInfo(uri)!.queryRunner!.getQueryRowsPaged(rowStart, numberOfRows, batchId, resultId).then(results => {
return results;
});
} else {
diff --git a/src/sql/workbench/services/query/common/queryRunner.ts b/src/sql/workbench/services/query/common/queryRunner.ts
index 7fa7fbce54..cded1f849b 100644
--- a/src/sql/workbench/services/query/common/queryRunner.ts
+++ b/src/sql/workbench/services/query/common/queryRunner.ts
@@ -348,7 +348,7 @@ export default class QueryRunner extends Disposable {
if (hasShowPlan && resultSet.rowCount > 0) {
this._isQueryPlan = true;
- this.getQueryRows(0, 1, resultSet.batchId, resultSet.id).then(e => {
+ this.getQueryRowsPaged(0, 1, resultSet.batchId, resultSet.id).then(e => {
if (e.rows) {
this._planXml.resolve(e.rows[0][0].displayValue);
}
@@ -373,7 +373,7 @@ export default class QueryRunner extends Disposable {
let hasShowPlan = !!resultSet.columnInfo.find(e => e.columnName === 'Microsoft SQL Server 2005 XML Showplan');
if (hasShowPlan) {
this._isQueryPlan = true;
- this.getQueryRows(0, 1, resultSet.batchId, resultSet.id).then(e => {
+ this.getQueryRowsPaged(0, 1, resultSet.batchId, resultSet.id).then(e => {
if (e.rows) {
let planXmlString = e.rows[0][0].displayValue;
@@ -419,6 +419,7 @@ export default class QueryRunner extends Disposable {
/**
* Get more data rows from the current resultSets from the service layer
+ * @deprecated getQueryRowsPaged should be used instead as it is much more performant
*/
public getQueryRows(rowStart: number, numberOfRows: number, batchIndex: number, resultSetIndex: number, cancellationToken?: CancellationToken, onProgressCallback?: (availableRows: number) => void): Promise {
let rowData: QueryExecuteSubsetParams = {
@@ -429,7 +430,22 @@ export default class QueryRunner extends Disposable {
batchIndex: batchIndex
};
- return this.queryManagementService.getQueryRows(rowData, cancellationToken, onProgressCallback).then(r => r, error => {
+ return this.queryManagementService.getQueryRows(rowData);
+ }
+
+ /**
+ * Get more data rows from the current resultSets from the service layer with paging, fetching row data in batches until all rows are retrieved.
+ */
+ public getQueryRowsPaged(rowStart: number, numberOfRows: number, batchIndex: number, resultSetIndex: number, cancellationToken?: CancellationToken, onProgressCallback?: (availableRows: number) => void): Promise {
+ let rowData: QueryExecuteSubsetParams = {
+ ownerUri: this.uri,
+ resultSetIndex: resultSetIndex,
+ rowsCount: numberOfRows,
+ rowsStartIndex: rowStart,
+ batchIndex: batchIndex
+ };
+
+ return this.queryManagementService.getQueryRowsPaged(rowData, cancellationToken, onProgressCallback).then(r => r, error => {
// this._notificationService.notify({
// severity: Severity.Error,
// message: nls.localize('query.gettingRowsFailedError', 'Something went wrong getting more rows: {0}', error)
@@ -566,7 +582,7 @@ export class QueryGridDataProvider implements IGridDataProvider {
}
getRowData(rowStart: number, numberOfRows: number, cancellationToken?: CancellationToken, onProgressCallback?: (availableRows: number) => void): Promise {
- return this.queryRunner.getQueryRows(rowStart, numberOfRows, this.batchId, this.resultSetId, cancellationToken, onProgressCallback);
+ return this.queryRunner.getQueryRowsPaged(rowStart, numberOfRows, this.batchId, this.resultSetId, cancellationToken, onProgressCallback);
}
copyResults(selection: Slick.Range[], includeHeaders?: boolean, tableView?: IDisposableDataProvider): Promise {
diff --git a/src/sql/workbench/services/query/test/browser/queryRunner.test.ts b/src/sql/workbench/services/query/test/browser/queryRunner.test.ts
index 2173dce9b3..45f8a228e3 100644
--- a/src/sql/workbench/services/query/test/browser/queryRunner.test.ts
+++ b/src/sql/workbench/services/query/test/browser/queryRunner.test.ts
@@ -57,8 +57,8 @@ suite('Query Runner', () => {
// get query rows
const rowResults: ResultSetSubset = { rowCount: 100, rows: range(100).map(r => range(1).map(c => ({ displayValue: `${r}${c}` }))) };
const getRowStub = sinon.stub().returns(Promise.resolve(rowResults));
- (instantiationService as TestInstantiationService).stub(IQueryManagementService, 'getQueryRows', getRowStub);
- const resultReturn = await runner.getQueryRows(0, 100, 0, 0);
+ (instantiationService as TestInstantiationService).stub(IQueryManagementService, 'getQueryRowsPaged', getRowStub);
+ const resultReturn = await runner.getQueryRowsPaged(0, 100, 0, 0);
assert(getRowStub.calledWithExactly({ ownerUri: uri, batchIndex: 0, resultSetIndex: 0, rowsStartIndex: 0, rowsCount: 100 }, undefined, undefined));
assert.deepStrictEqual(resultReturn, rowResults);
// batch complete
@@ -121,7 +121,7 @@ suite('Query Runner', () => {
assert(runQueryStub.calledWithExactly(uri, undefined, { displayEstimatedQueryPlan: true }));
const xmlPlan = 'xml plan';
const getRowsStub = sinon.stub().returns(Promise.resolve({ rowCount: 1, rows: [[{ displayValue: xmlPlan }]] } as ResultSetSubset));
- (instantiationService as TestInstantiationService).stub(IQueryManagementService, 'getQueryRows', getRowsStub);
+ (instantiationService as TestInstantiationService).stub(IQueryManagementService, 'getQueryRowsPaged', getRowsStub);
runner.handleBatchStart({ id: 0, executionStart: '' });
runner.handleResultSetAvailable({ id: 0, batchId: 0, complete: true, rowCount: 1, columnInfo: [{ columnName: 'Microsoft SQL Server 2005 XML Showplan' }] });
const plan = await runner.planXml;
@@ -143,7 +143,7 @@ suite('Query Runner', () => {
runner.handleResultSetAvailable({ id: 0, batchId: 0, complete: false, rowCount: 0, columnInfo: [{ columnName: 'Microsoft SQL Server 2005 XML Showplan' }] });
const xmlPlan = 'xml plan';
const getRowsStub = sinon.stub().returns(Promise.resolve({ rowCount: 1, rows: [[{ displayValue: xmlPlan }]] } as ResultSetSubset));
- (instantiationService as TestInstantiationService).stub(IQueryManagementService, 'getQueryRows', getRowsStub);
+ (instantiationService as TestInstantiationService).stub(IQueryManagementService, 'getQueryRowsPaged', getRowsStub);
runner.handleResultSetUpdated({ id: 0, batchId: 0, complete: true, rowCount: 1, columnInfo: [{ columnName: 'Microsoft SQL Server 2005 XML Showplan' }] });
const plan = await runner.planXml;
assert(getRowsStub.calledOnce);
diff --git a/src/sql/workbench/services/query/test/common/testQueryManagementService.ts b/src/sql/workbench/services/query/test/common/testQueryManagementService.ts
index 482ef64833..b3c0d1f4ea 100644
--- a/src/sql/workbench/services/query/test/common/testQueryManagementService.ts
+++ b/src/sql/workbench/services/query/test/common/testQueryManagementService.ts
@@ -51,7 +51,10 @@ export class TestQueryManagementService implements IQueryManagementService {
parseSyntax(ownerUri: string, query: string): Promise {
throw new Error('Method not implemented.');
}
- getQueryRows(rowData: azdata.QueryExecuteSubsetParams, cancellationToken?: CancellationToken, onProgressCallback?: (availableRows: number) => void): Promise {
+ getQueryRows(rowData: azdata.QueryExecuteSubsetParams): Promise {
+ throw new Error('Method not implemented.');
+ }
+ getQueryRowsPaged(rowData: azdata.QueryExecuteSubsetParams, cancellationToken?: CancellationToken, onProgressCallback?: (availableRows: number) => void): Promise {
throw new Error('Method not implemented.');
}
async disposeQuery(ownerUri: string): Promise {