From 02a62a430f1d452c231d00e5a2a86c34d5e43740 Mon Sep 17 00:00:00 2001 From: Alan Ren Date: Mon, 5 Jun 2023 19:07:30 -0700 Subject: [PATCH] handle Excel limits (#23321) --- .../contrib/query/browser/actions.ts | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/sql/workbench/contrib/query/browser/actions.ts b/src/sql/workbench/contrib/query/browser/actions.ts index 7768667f64..808f9ca235 100644 --- a/src/sql/workbench/contrib/query/browser/actions.ts +++ b/src/sql/workbench/contrib/query/browser/actions.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Action } from 'vs/base/common/actions'; +import { Action, toAction } from 'vs/base/common/actions'; import { localize } from 'vs/nls'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Table } from 'sql/base/browser/ui/table/table'; @@ -23,6 +23,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IStorageService } from 'vs/platform/storage/common/storage'; import { getChartMaxRowCount, notifyMaxRowCountExceeded } from 'sql/workbench/contrib/charts/browser/utils'; import { IEncodingSupport } from 'vs/workbench/services/textfile/common/textfiles'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; export interface IGridActionContext { gridDataProvider: IGridDataProvider; @@ -43,6 +44,10 @@ function mapForNumberColumn(ranges: Slick.Range[]): Slick.Range[] { } } +const ExcelSpecUrl = 'https://support.microsoft.com/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3'; +const ExcelRowLimit: number = 1048576; +const ExcelColumnLimit: number = 16384; + export class SaveResultAction extends Action { public static SAVECSV_ID = 'grid.saveAsCsv'; public static SAVECSV_LABEL = localize('saveAsCsv', "Save As CSV"); @@ -71,11 +76,35 @@ export class SaveResultAction extends Action { private format: SaveFormat, @INotificationService private notificationService: INotificationService, @IEditorService private editorService: IEditorService, + @IOpenerService private openerService: IOpenerService ) { super(id, label, icon); } public override async run(context: IGridActionContext): Promise { + if (!context.gridDataProvider.canSerialize) { + this.notificationService.warn(localize('saveToFileNotSupported', "Save to file is not supported by the backing data source")); + return; + } + + if (this.format === SaveFormat.EXCEL && (context.table.getData().getLength() > ExcelRowLimit || context.table.columns.length > ExcelColumnLimit)) { + this.notificationService.notify({ + severity: Severity.Error, + message: localize('excelLimitExceededError', "The number of rows or columns in the table has exceeded the Excel limits. Please try a different format instead."), + actions: { + primary: [ + toAction({ + id: 'openExcelSpecs', + label: localize('openExcelSpecs', "View Excel specifications"), + run: () => { + this.openerService.open(ExcelSpecUrl); + } + }) + ] + } + }); + return; + } const activeEditor = this.editorService.activeEditorPane as unknown as IEncodingSupport; if (typeof activeEditor.getEncoding === 'function' && activeEditor.getEncoding() !== 'utf8') { @@ -86,10 +115,6 @@ export class SaveResultAction extends Action { }); } - if (!context.gridDataProvider.canSerialize) { - this.notificationService.warn(localize('saveToFileNotSupported', "Save to file is not supported by the backing data source")); - return; - } try { await context.gridDataProvider.serializeResults(this.format, mapForNumberColumn(context.selection)); } catch (error) {