feat(settings): configurable query result max rows with unified load limit

This commit is contained in:
zipg authored and GitHub committed 2026-08-11 13:07:35 +08:00
1 parent 89d65e96ed
commit 0c407c2d03
26 files changed
+446 -187

No files matched your search

@@ -119,7 +119,7 @@ import McpConnectionScopePicker from "@/components/settings/McpConnectionScopePi
import ScheduledDatabaseBackupSettings from "@/components/backup/ScheduledDatabaseBackupSettings.vue";
import SqlFormatterSettingsPanel from "./SqlFormatterSettingsPanel.vue";
import { APP_THEME_PALETTES, type AppCornerStyle, type AppThemeAppearance, type AppThemeMode, type AppThemePalette } from "@/lib/app/appTheme";
import { editorSettingsDraftChanged, editorSettingsDraftFromSettings, editorSettingsPatchFromDraft, normalizeTableOpenPageSizeDraft, type EditorSettingsDraft } from "@/lib/settings/editorSettingsDraft";
import { editorSettingsDraftChanged, editorSettingsDraftFromSettings, editorSettingsPatchFromDraft, normalizeQueryResultMaxRowsDraft, normalizeTableOpenPageSizeDraft, type EditorSettingsDraft } from "@/lib/settings/editorSettingsDraft";
import { useConnectionStore } from "@/stores/connectionStore";
import { useSavedSqlStore } from "@/stores/savedSqlStore";
import { usePromptTemplateStore } from "@/stores/promptTemplateStore";
@@ -134,6 +134,7 @@ import { buildFontFamilyOptions, displayFontFamily, isPresetFontFamily, loadSyst
import { buildAppSupportInfoRows, formatAppSupportInfoForClipboard, type AppSupportInfoLabels } from "@/lib/app/supportInfo";
import { DateTimePatterns, normalizeSupportedDateTimePattern } from "@/lib/dataGrid/columnFormatter";
import { MAX_RESULT_PAGE_SIZE, MIN_RESULT_PAGE_SIZE } from "@/lib/dataGrid/paginationPageSize";
import { MAX_QUERY_RESULT_MAX_ROWS } from "@/lib/dataGrid/queryResultRowLimit";
import type { PromptTemplate } from "@/types/promptTemplate";
import { GLOBAL_INSTRUCTIONS_MAX, PROMPT_TEMPLATE_CONTENT_MAX, PROMPT_TEMPLATE_NAME_MAX, promptTemplateCharacterCount } from "@/types/promptTemplate";
@@ -314,9 +315,11 @@ const editShowIndexIndicatorsInHeader = ref(settingsStore.editorSettings.showInd
const editCompactColumnHeaderActions = ref(settingsStore.editorSettings.compactColumnHeaderActions);
const editDataGridQuickEntry = ref(settingsStore.editorSettings.dataGridQuickEntry);
const editDataGridAutoTransposeSingleRow = ref(settingsStore.editorSettings.dataGridAutoTransposeSingleRow);
const editPageSize = ref(settingsStore.editorSettings.pageSize);
const editTableOpenPageSize = ref(settingsStore.editorSettings.tableOpenPageSize);
const editQueryResultMaxRowsEnabled = ref(settingsStore.editorSettings.queryResultMaxRowsEnabled);
const editQueryResultMaxRows = ref(settingsStore.editorSettings.queryResultMaxRows);
const editInfiniteScroll = ref(settingsStore.editorSettings.infiniteScroll);
const editInfiniteScrollMaxRows = ref(settingsStore.editorSettings.infiniteScrollMaxRows);
const editRegexMaxMatchCount = ref(settingsStore.editorSettings.regexMaxMatchCount);
const editAutoCalculateTotalRows = ref(settingsStore.editorSettings.autoCalculateTotalRows);
const editTableColumnTemplateRows = ref<TableColumnTemplateGridRow[]>(tableColumnTemplateRowsFromSettings(settingsStore.editorSettings.tableColumnTemplateFields));
@@ -328,6 +331,22 @@ function updateTableOpenPageSizeDraft(value: string | number) {
editTableOpenPageSize.value = normalizeTableOpenPageSizeDraft(value);
}
function updatePageSizeDraft(value: string | number) {
editPageSize.value = normalizeTableOpenPageSizeDraft(value);
}
function updateQueryResultMaxRowsInput(event: Event) {
const input = event.currentTarget as HTMLInputElement;
const parsed = Number(input.value);
if (!Number.isFinite(parsed) || parsed > MAX_QUERY_RESULT_MAX_ROWS) {
input.value = String(editQueryResultMaxRows.value);
return;
}
const normalized = normalizeQueryResultMaxRowsDraft(input.value);
if (input.value !== String(normalized)) input.value = String(normalized);
editQueryResultMaxRows.value = normalized;
}
function sqlVariableSyntaxToggle(key: keyof SqlVariableSyntaxToggles): boolean {
return editSqlVariableSyntaxOverrides.value[editSqlVariableSyntaxDatabaseType.value]?.[key] ?? true;
}
@@ -471,9 +490,11 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
compactColumnHeaderActions: editCompactColumnHeaderActions.value,
dataGridQuickEntry: editDataGridQuickEntry.value,
dataGridAutoTransposeSingleRow: editDataGridAutoTransposeSingleRow.value,
pageSize: editPageSize.value,
tableOpenPageSize: editTableOpenPageSize.value,
queryResultMaxRowsEnabled: editQueryResultMaxRowsEnabled.value,
queryResultMaxRows: editQueryResultMaxRows.value,
infiniteScroll: editInfiniteScroll.value,
infiniteScrollMaxRows: editInfiniteScrollMaxRows.value,
regexMaxMatchCount: editRegexMaxMatchCount.value,
autoCalculateTotalRows: editAutoCalculateTotalRows.value,
tableColumnTemplateFields: normalizedEditTableColumnTemplateFields.value,
@@ -743,9 +764,11 @@ function syncEditorSettingsDraftFromStore() {
editCompactColumnHeaderActions.value = settingsStore.editorSettings.compactColumnHeaderActions;
editDataGridQuickEntry.value = settingsStore.editorSettings.dataGridQuickEntry;
editDataGridAutoTransposeSingleRow.value = settingsStore.editorSettings.dataGridAutoTransposeSingleRow;
editPageSize.value = settingsStore.editorSettings.pageSize;
editTableOpenPageSize.value = settingsStore.editorSettings.tableOpenPageSize;
editQueryResultMaxRowsEnabled.value = settingsStore.editorSettings.queryResultMaxRowsEnabled;
editQueryResultMaxRows.value = settingsStore.editorSettings.queryResultMaxRows;
editInfiniteScroll.value = settingsStore.editorSettings.infiniteScroll;
editInfiniteScrollMaxRows.value = settingsStore.editorSettings.infiniteScrollMaxRows;
editRegexMaxMatchCount.value = settingsStore.editorSettings.regexMaxMatchCount;
editAutoCalculateTotalRows.value = settingsStore.editorSettings.autoCalculateTotalRows;
editTableColumnTemplateRows.value = tableColumnTemplateRowsFromSettings(settingsStore.editorSettings.tableColumnTemplateFields);
@@ -853,7 +876,8 @@ const shortcutsChanged = computed(() => JSON.stringify(editShortcuts.value) !==
const duckDbWorkerSettingsRequireRestart = computed(() => editDuckDbWorkerProcessIsolation.value !== startupDuckDbWorkerProcessIsolation.value || normalizeDuckDbWorkerMaxProcesses(editDuckDbWorkerMaxProcesses.value) !== startupDuckDbWorkerMaxProcesses.value);
const hasBlockingShortcutConflicts = computed(() => shortcutsChanged.value && hasShortcutConflicts.value);
const hasBlockingFormatterConfig = computed(() => activeSettingsTab.value === "formatter" && !sqlFormatterConfigValid.value);
const hasApplyBlocker = computed(() => hasBlockingShortcutConflicts.value || hasBlockingFormatterConfig.value);
const hasBlockingQueryResultRowLimit = computed(() => editQueryResultMaxRowsEnabled.value && editQueryResultMaxRows.value < editPageSize.value);
const hasApplyBlocker = computed(() => hasBlockingShortcutConflicts.value || hasBlockingFormatterConfig.value || hasBlockingQueryResultRowLimit.value);
function hasChanges(): boolean {
return (
@@ -994,9 +1018,11 @@ function resetDefaultsForTab(tab: SettingsCategory) {
editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions;
editDataGridQuickEntry.value = DEFAULT_EDITOR_SETTINGS.dataGridQuickEntry;
editDataGridAutoTransposeSingleRow.value = DEFAULT_EDITOR_SETTINGS.dataGridAutoTransposeSingleRow;
editPageSize.value = DEFAULT_EDITOR_SETTINGS.pageSize;
editTableOpenPageSize.value = DEFAULT_EDITOR_SETTINGS.tableOpenPageSize;
editQueryResultMaxRowsEnabled.value = DEFAULT_EDITOR_SETTINGS.queryResultMaxRowsEnabled;
editQueryResultMaxRows.value = DEFAULT_EDITOR_SETTINGS.queryResultMaxRows;
editInfiniteScroll.value = DEFAULT_EDITOR_SETTINGS.infiniteScroll;
editInfiniteScrollMaxRows.value = DEFAULT_EDITOR_SETTINGS.infiniteScrollMaxRows;
editRegexMaxMatchCount.value = DEFAULT_EDITOR_SETTINGS.regexMaxMatchCount;
editAutoCalculateTotalRows.value = DEFAULT_EDITOR_SETTINGS.autoCalculateTotalRows;
editDuckDbWorkerProcessIsolation.value = DEFAULT_DESKTOP_SETTINGS.duckdb_worker_process_isolation;
@@ -1061,9 +1087,11 @@ function resetAllDefaults() {
editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions;
editDataGridQuickEntry.value = DEFAULT_EDITOR_SETTINGS.dataGridQuickEntry;
editDataGridAutoTransposeSingleRow.value = DEFAULT_EDITOR_SETTINGS.dataGridAutoTransposeSingleRow;
editPageSize.value = DEFAULT_EDITOR_SETTINGS.pageSize;
editTableOpenPageSize.value = DEFAULT_EDITOR_SETTINGS.tableOpenPageSize;
editQueryResultMaxRowsEnabled.value = DEFAULT_EDITOR_SETTINGS.queryResultMaxRowsEnabled;
editQueryResultMaxRows.value = DEFAULT_EDITOR_SETTINGS.queryResultMaxRows;
editInfiniteScroll.value = DEFAULT_EDITOR_SETTINGS.infiniteScroll;
editInfiniteScrollMaxRows.value = DEFAULT_EDITOR_SETTINGS.infiniteScrollMaxRows;
editRegexMaxMatchCount.value = DEFAULT_EDITOR_SETTINGS.regexMaxMatchCount;
editAutoCalculateTotalRows.value = DEFAULT_EDITOR_SETTINGS.autoCalculateTotalRows;
editTableColumnTemplateRows.value = tableColumnTemplateRowsFromSettings(DEFAULT_EDITOR_SETTINGS.tableColumnTemplateFields);
@@ -4390,119 +4418,6 @@ onUnmounted(() => {
<Separator />
<div class="settings-appearance-group settings-option-stack">
<Label>{{ t("settings.dataGridDisplay") }}</Label>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="show-column-comments-in-header">
{{ t("settings.showColumnCommentsInHeader") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.showColumnCommentsInHeaderDescription") }}
</p>
</div>
<Switch id="show-column-comments-in-header" v-model="editShowColumnCommentsInHeader" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="show-column-types-in-header">
{{ t("settings.showColumnTypesInHeader") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.showColumnTypesInHeaderDescription") }}
</p>
</div>
<Switch id="show-column-types-in-header" v-model="editShowColumnTypesInHeader" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="show-index-indicators-in-header">
{{ t("settings.showIndexIndicatorsInHeader") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.showIndexIndicatorsInHeaderDescription") }}
</p>
</div>
<Switch id="show-index-indicators-in-header" v-model="editShowIndexIndicatorsInHeader" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="compact-column-header-actions">
{{ t("settings.compactColumnHeaderActions") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.compactColumnHeaderActionsDescription") }}
</p>
</div>
<Switch id="compact-column-header-actions" v-model="editCompactColumnHeaderActions" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="data-grid-quick-entry">
{{ t("settings.dataGridQuickEntry") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.dataGridQuickEntryDescription") }}
</p>
</div>
<Switch id="data-grid-quick-entry" v-model="editDataGridQuickEntry" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="data-grid-auto-transpose-single-row">
{{ t("settings.dataGridAutoTransposeSingleRow") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.dataGridAutoTransposeSingleRowDescription") }}
</p>
</div>
<Switch id="data-grid-auto-transpose-single-row" v-model="editDataGridAutoTransposeSingleRow" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="infinite-scroll">
{{ t("settings.infiniteScroll") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.infiniteScrollDescription") }}
</p>
</div>
<Switch id="infinite-scroll" v-model="editInfiniteScroll" />
</div>
<div v-if="editInfiniteScroll" class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="infinite-scroll-max-rows">
{{ t("settings.infiniteScrollMaxRows") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.infiniteScrollMaxRowsDescription") }}
</p>
</div>
<Input
id="infinite-scroll-max-rows"
v-model="editInfiniteScrollMaxRows"
type="number"
inputmode="numeric"
:min="1000"
:max="50000"
class="h-7 w-24 px-2 text-xs tabular-nums [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
/>
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="auto-calculate-total-rows">
{{ t("settings.autoCalculateTotalRows") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.autoCalculateTotalRowsDescription") }}
</p>
</div>
<Switch id="auto-calculate-total-rows" v-model="editAutoCalculateTotalRows" />
</div>
</div>
<Separator />
<div class="space-y-2">
<div class="flex items-center gap-2">
<Label>{{ t("settings.toolbarTitle") }}</Label>
@@ -4831,7 +4746,141 @@ onUnmounted(() => {
{{ t("settings.tableOpenPageSizeDescription") }}
</p>
</div>
<Input id="table-open-page-size" type="number" inputmode="numeric" class="h-7 w-24 px-2 text-right text-xs tabular-nums" :min="MIN_RESULT_PAGE_SIZE" :max="MAX_RESULT_PAGE_SIZE" :model-value="editTableOpenPageSize" @update:model-value="updateTableOpenPageSizeDraft" />
<Input id="table-open-page-size" type="number" inputmode="numeric" class="h-7 w-24 px-2 text-left text-xs tabular-nums" :min="MIN_RESULT_PAGE_SIZE" :max="MAX_RESULT_PAGE_SIZE" :model-value="editTableOpenPageSize" @update:model-value="updateTableOpenPageSizeDraft" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="query-page-size">
{{ t("settings.queryPageSize") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.queryPageSizeDescription", { max: MAX_RESULT_PAGE_SIZE.toLocaleString() }) }}
</p>
</div>
<Input
id="query-page-size"
type="number"
inputmode="numeric"
class="h-7 w-24 px-2 text-left text-xs tabular-nums"
:min="MIN_RESULT_PAGE_SIZE"
:max="MAX_RESULT_PAGE_SIZE"
:model-value="editPageSize"
:aria-invalid="hasBlockingQueryResultRowLimit"
@update:model-value="updatePageSizeDraft"
/>
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="query-result-max-rows-enabled">
{{ t("settings.queryResultMaxRows") }}
</Label>
<p class="text-xs" :class="hasBlockingQueryResultRowLimit ? 'text-destructive' : 'text-muted-foreground'">
{{ hasBlockingQueryResultRowLimit ? t("settings.queryResultMaxRowsTooSmall", { pageSize: editPageSize }) : editQueryResultMaxRowsEnabled ? t("settings.queryResultMaxRowsDescription") : t("settings.queryResultMaxRowsUnlimitedDescription") }}
</p>
</div>
<div class="flex shrink-0 items-center gap-2">
<Input
id="query-result-max-rows"
type="number"
inputmode="numeric"
class="h-7 w-[130px] px-2 text-left text-xs tabular-nums"
:min="1"
:max="MAX_QUERY_RESULT_MAX_ROWS"
:model-value="editQueryResultMaxRows"
:disabled="!editQueryResultMaxRowsEnabled"
:aria-invalid="hasBlockingQueryResultRowLimit"
@input="updateQueryResultMaxRowsInput"
/>
<Switch id="query-result-max-rows-enabled" v-model="editQueryResultMaxRowsEnabled" :aria-label="t('settings.queryResultMaxRowsEnabled')" />
</div>
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="infinite-scroll">
{{ t("settings.infiniteScroll") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.infiniteScrollDescription") }}
</p>
</div>
<Switch id="infinite-scroll" v-model="editInfiniteScroll" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="auto-calculate-total-rows">
{{ t("settings.autoCalculateTotalRows") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.autoCalculateTotalRowsDescription") }}
</p>
</div>
<Switch id="auto-calculate-total-rows" v-model="editAutoCalculateTotalRows" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="show-column-comments-in-header">
{{ t("settings.showColumnCommentsInHeader") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.showColumnCommentsInHeaderDescription") }}
</p>
</div>
<Switch id="show-column-comments-in-header" v-model="editShowColumnCommentsInHeader" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="show-column-types-in-header">
{{ t("settings.showColumnTypesInHeader") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.showColumnTypesInHeaderDescription") }}
</p>
</div>
<Switch id="show-column-types-in-header" v-model="editShowColumnTypesInHeader" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="show-index-indicators-in-header">
{{ t("settings.showIndexIndicatorsInHeader") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.showIndexIndicatorsInHeaderDescription") }}
</p>
</div>
<Switch id="show-index-indicators-in-header" v-model="editShowIndexIndicatorsInHeader" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="compact-column-header-actions">
{{ t("settings.compactColumnHeaderActions") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.compactColumnHeaderActionsDescription") }}
</p>
</div>
<Switch id="compact-column-header-actions" v-model="editCompactColumnHeaderActions" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="data-grid-quick-entry">
{{ t("settings.dataGridQuickEntry") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.dataGridQuickEntryDescription") }}
</p>
</div>
<Switch id="data-grid-quick-entry" v-model="editDataGridQuickEntry" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="data-grid-auto-transpose-single-row">
{{ t("settings.dataGridAutoTransposeSingleRow") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.dataGridAutoTransposeSingleRowDescription") }}
</p>
</div>
<Switch id="data-grid-auto-transpose-single-row" v-model="editDataGridAutoTransposeSingleRow" />
</div>
</div>
@@ -173,6 +173,7 @@ import {
replaceColumnValueFilterCondition,
} from "@/lib/dataGrid/dataGridColumnFilter";
import { normalizeResultPageSize, resultPageSizeMenuOptions } from "@/lib/dataGrid/paginationPageSize";
import { continuousQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
import { allNullColumnIndexes } from "@/lib/dataGrid/dataGridColumnVisibility";
import { buildDataGridColumnLookupItems, dataGridColumnCommentFor, filterDataGridColumnLookupItems } from "@/lib/dataGrid/dataGridColumnLookup";
import { uniqueDataGridColumnOrderKeys } from "@/lib/dataGrid/dataGridColumnOrder";
@@ -528,7 +529,7 @@ const dataGridRenderMode = computed(() => settingsStore.editorSettings.dataGridR
const dataGridSearchMode = computed(() => settingsStore.editorSettings.dataGridSearchMode);
const compactDataGridToolbar = computed(() => isDataGridToolbarCompact(dataGridTopbarWidth.value, dataGridViewportWidth.value, DATA_GRID_CONDITION_TOOLBAR_MIN_WIDTH));
const infiniteScrollEnabled = computed(() => props.paginationEnabled && settingsStore.editorSettings.infiniteScroll);
const infiniteScrollMaxRows = computed(() => settingsStore.editorSettings.infiniteScrollMaxRows);
const infiniteScrollMaxRows = computed(() => continuousQueryResultMaxRows(settingsStore.editorSettings.queryResultMaxRowsEnabled, settingsStore.editorSettings.queryResultMaxRows));
const expandedCellEditor = ref<{ rowId: number; col: number } | null>(null);
function headerColumnComment(column: string): string {
@@ -27,7 +27,8 @@ const mocks = vi.hoisted(() => ({
updateRedisDbKeyStats: vi.fn(),
redisScanPageSize: 100,
infiniteScroll: false,
infiniteScrollMaxRows: 5000,
queryResultMaxRowsEnabled: true,
queryResultMaxRows: 5000,
}));
vi.mock("@/lib/backend/api", () => ({
@@ -66,7 +67,8 @@ vi.mock("@/stores/settingsStore", () => ({
useSettingsStore: () => ({
editorSettings: {
infiniteScroll: mocks.infiniteScroll,
infiniteScrollMaxRows: mocks.infiniteScrollMaxRows,
queryResultMaxRowsEnabled: mocks.queryResultMaxRowsEnabled,
queryResultMaxRows: mocks.queryResultMaxRows,
},
}),
}));
@@ -358,7 +360,8 @@ function resetApiMocks() {
vi.clearAllMocks();
mocks.redisScanPageSize = 100;
mocks.infiniteScroll = false;
mocks.infiniteScrollMaxRows = 5000;
mocks.queryResultMaxRowsEnabled = true;
mocks.queryResultMaxRows = 5000;
mocks.redisScanKeysBatch.mockResolvedValue({ cursor: 0, keys: [], total_keys: 0 });
mocks.redisGetValue.mockImplementation((_connectionId: string, _db: number, keyRaw: string) => Promise.resolve(redisValue(keyRaw)));
mocks.redisSetString.mockResolvedValue(undefined);
@@ -26,6 +26,7 @@ import type { RedisKeyInfo, RedisScanResult, RedisValue, HistoryEntry } from "@/
import { uuid } from "@/lib/common/utils";
import { useConnectionStore } from "@/stores/connectionStore";
import { useSettingsStore } from "@/stores/settingsStore";
import { continuousQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
import {
appendRedisKeysToTreeIndex,
canBuildRedisFuzzyTree,
@@ -171,7 +172,7 @@ const loadingEmptyText = computed(() => (isValueSearchMode.value && valueQuery.v
const redisKeySeparator = computed(() => connectionStore.getConfig(props.connectionId)?.redis_key_separator ?? ":");
const redisScanPageSize = computed(() => connectionStore.getConfig(props.connectionId)?.redis_scan_page_size ?? REDIS_SCAN_PAGE_SIZE_DEFAULT);
const redisInfiniteScrollEnabled = computed(() => settingsStore.editorSettings.infiniteScroll);
const redisInfiniteScrollMaxKeys = computed(() => settingsStore.editorSettings.infiniteScrollMaxRows);
const redisInfiniteScrollMaxKeys = computed(() => continuousQueryResultMaxRows(settingsStore.editorSettings.queryResultMaxRowsEnabled, settingsStore.editorSettings.queryResultMaxRows));
watch(redisKeySeparator, () => {
if (flatKeys.value.length === 0) return;
if (useFlatKeySearchRows.value) {
@@ -16,6 +16,7 @@ import { applyMongoFindSort } from "@/lib/mongo/mongoShellCommand";
import { uuid } from "@/lib/common/utils";
import { simpleDataGridOrderByReferencesMissingColumn, type DataGridSortMode } from "@/lib/dataGrid/dataGridSort";
import type { DataGridReloadIntent } from "@/lib/dataGrid/dataGridToolbar";
import { continuousQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
import { queryResultBaseSql, queryResultExecutionSql } from "@/lib/tabs/tabPresentation";
import { sqlExecutionTargetCapabilities } from "@/lib/database/sqlExecutionTargetCapabilities";
@@ -265,7 +266,13 @@ export function useDataGridActions(activeTab: ComputedRef<QueryTab | undefined>)
const tab = activeTab.value;
if (!tab) return;
const appendResult = settingsStore.editorSettings.infiniteScroll && offset > 0 && offset === tab.result?.rows.length;
const appendOptions = appendResult ? { appendResult: { maxRows: settingsStore.editorSettings.infiniteScrollMaxRows } } : {};
const appendOptions = appendResult
? {
appendResult: {
maxRows: continuousQueryResultMaxRows(settingsStore.editorSettings.queryResultMaxRowsEnabled, settingsStore.editorSettings.queryResultMaxRows),
},
}
: {};
if (tab.mode !== "data") {
const sortColumns = visibleQuerySortColumns(tab.result?.columns ?? [], tab.result?.hidden_column_indexes, tab.resultSortColumnIndex ?? -1);
const hasDatabaseSort = !!tab.result?.hidden_column_indexes?.length && tab.resultSortMode === "database" && !!tab.resultSortDirection && !!tab.resultSortColumn && !!sortColumns;
+7 -2
View File
@@ -5397,6 +5397,13 @@ export default {
dataGridDisplay: "Data grid display",
tableOpenPageSize: "Default rows per page when opening tables",
tableOpenPageSizeDescription: "Rows loaded per page when opening a new table. Changing rows per page in a table updates this default for future table tabs.",
queryPageSize: "Default query rows per page",
queryPageSizeDescription: "Rows loaded per query result page. The maximum supported page size is {max}.",
queryResultMaxRows: "Maximum query result rows",
queryResultMaxRowsEnabled: "Limit query result rows",
queryResultMaxRowsDescription: "Caps rows returned by queries, table data, stored procedures, and continuous loading.",
queryResultMaxRowsTooSmall: "Must be at least the query page size ({pageSize}).",
queryResultMaxRowsUnlimitedDescription: "No total row or continuous loading limit. Large results can use substantial memory.",
showColumnCommentsInHeader: "Show column comments under names",
showColumnCommentsInHeaderDescription: "Display table column comments directly below grid column names.",
showColumnTypesInHeader: "Show column types under names",
@@ -5413,8 +5420,6 @@ export default {
autoCalculateTotalRows: "Auto-calculate total row count",
autoCalculateTotalRowsDescription: "Run COUNT(*) automatically after each query to show the total matching rows. Off by default to keep large queries fast — calculate it on demand from the result footer.",
infiniteScrollDescription: "Automatically load the next batch when scrolling to the bottom.",
infiniteScrollMaxRows: "Infinite scroll max items",
infiniteScrollMaxRowsDescription: "Maximum number of items to load automatically in infinite scroll mode (1000–50000).",
regexMaxMatchCount: "Maximum select-all matches",
regexMaxMatchCountDescription: "Limits the number of selections created by Select All Matches; search counting remains complete (100–10000).",
tableColumnTemplateFields: "New Table Preset Fields",
+7 -2
View File
@@ -5157,6 +5157,13 @@ export default withEnglishFallback({
dataGridDisplay: "Visualización de la tabla",
tableOpenPageSize: "Filas predeterminadas por página al abrir tablas",
tableOpenPageSizeDescription: "Filas cargadas por página al abrir una tabla nueva. Cambiar las filas por página actualiza este valor para futuras pestañas de tablas.",
queryPageSize: "Filas predeterminadas por página de consulta",
queryPageSizeDescription: "Filas cargadas por página de resultados. Se admiten hasta {max} filas.",
queryResultMaxRows: "Máximo de filas del resultado",
queryResultMaxRowsEnabled: "Limitar filas del resultado",
queryResultMaxRowsDescription: "Limita las filas conservadas por consultas, datos de tablas, procedimientos almacenados y carga continua.",
queryResultMaxRowsTooSmall: "Debe ser al menos el tamaño de página de consulta ({pageSize}).",
queryResultMaxRowsUnlimitedDescription: "Sin límite total de filas ni de carga continua. Los resultados grandes pueden usar mucha memoria.",
showColumnCommentsInHeader: "Mostrar comentarios bajo los nombres",
showColumnCommentsInHeaderDescription: "Muestra los comentarios de columnas directamente debajo del nombre de la columna.",
showColumnTypesInHeader: "Mostrar tipos de columna bajo los nombres",
@@ -5173,8 +5180,6 @@ export default withEnglishFallback({
autoCalculateTotalRows: "Calcular automáticamente el total de filas",
autoCalculateTotalRowsDescription: "Ejecuta COUNT(*) automáticamente tras cada consulta para mostrar el total de filas coincidentes. Desactivado por defecto para mantener rápidas las consultas grandes; puedes calcularlo cuando quieras desde el pie de resultados.",
infiniteScrollDescription: "Carga automáticamente el siguiente lote al desplazarse hasta el final.",
infiniteScrollMaxRows: "Máximo de elementos en desplazamiento infinito",
infiniteScrollMaxRowsDescription: "Número máximo de elementos que se cargarán automáticamente en modo desplazamiento infinito (1000–50000).",
regexMaxMatchCount: "Máximo de coincidencias al seleccionar todas",
regexMaxMatchCountDescription: "Limita el número de selecciones creadas por Seleccionar todas las coincidencias; el conteo de búsqueda permanece completo (100–10000).",
tableColumnTemplateFields: "Campos predefinidos para tablas nuevas",
+7 -2
View File
@@ -5157,6 +5157,13 @@ export default withEnglishFallback({
dataGridDisplay: "Visualizzazione griglia dati",
tableOpenPageSize: "Righe predefinite per pagina all'apertura delle tabelle",
tableOpenPageSizeDescription: "Righe caricate per pagina quando si apre una nuova tabella. La modifica delle righe per pagina aggiorna questo valore per le future schede tabella.",
queryPageSize: "Righe predefinite per pagina di query",
queryPageSizeDescription: "Righe caricate per pagina dei risultati. Sono supportate fino a {max} righe.",
queryResultMaxRows: "Numero massimo di righe del risultato",
queryResultMaxRowsEnabled: "Limita le righe del risultato",
queryResultMaxRowsDescription: "Limita le righe conservate da query, dati tabella, stored procedure e caricamento continuo.",
queryResultMaxRowsTooSmall: "Deve essere almeno pari alle righe per pagina della query ({pageSize}).",
queryResultMaxRowsUnlimitedDescription: "Nessun limite totale di righe o di caricamento continuo. I risultati grandi possono usare molta memoria.",
showColumnCommentsInHeader: "Mostra i commenti delle colonne sotto i nomi",
showColumnCommentsInHeaderDescription: "Visualizza i commenti delle colonne della tabella direttamente sotto i nomi delle colonne nella griglia.",
showColumnTypesInHeader: "Mostra i tipi di colonna sotto i nomi",
@@ -5173,8 +5180,6 @@ export default withEnglishFallback({
autoCalculateTotalRows: "Calcola automaticamente il totale delle righe",
autoCalculateTotalRowsDescription: "Esegue COUNT(*) automaticamente dopo ogni query per mostrare il totale delle righe corrispondenti. Disattivato per impostazione predefinita per mantenere veloci le query grandi; puoi calcolarlo all'occorrenza dal piè di pagina dei risultati.",
infiniteScrollDescription: "Carica automaticamente il blocco successivo quando scorri fino in fondo.",
infiniteScrollMaxRows: "Elementi max a scorrimento infinito",
infiniteScrollMaxRowsDescription: "Numero massimo di elementi da caricare automaticamente in modalità scorrimento infinito (1000–50000).",
regexMaxMatchCount: "Numero massimo di corrispondenze per Seleziona tutte",
regexMaxMatchCountDescription: "Limita il numero di selezioni create da Seleziona tutte le corrispondenze; il conteggio della ricerca rimane completo (100–10000).",
tableColumnTemplateFields: "Campi predefiniti per nuove tabelle",
+7 -2
View File
@@ -5194,6 +5194,13 @@ export default withEnglishFallback({
dataGridDisplay: "データグリッド表示",
tableOpenPageSize: "テーブルを開くときのデフォルト行数",
tableOpenPageSizeDescription: "新しいテーブルを開くときに 1 ページあたり読み込む行数です。テーブルで行数を変更すると、今後のテーブルタブのデフォルトも更新されます。",
queryPageSize: "クエリ結果のデフォルト行数",
queryPageSizeDescription: "クエリ結果の 1 ページあたりの行数です。最大 {max} 行まで指定できます。",
queryResultMaxRows: "クエリ結果の最大行数",
queryResultMaxRowsEnabled: "クエリ結果の行数を制限",
queryResultMaxRowsDescription: "クエリ、テーブルデータ、ストアドプロシージャ、および連続読み込みで保持する合計行数を制限します。",
queryResultMaxRowsTooSmall: "クエリのページ行数({pageSize})以上にしてください。",
queryResultMaxRowsUnlimitedDescription: "合計行数または連続読み込み数を制限しません。大きな結果は大量のメモリを使用する可能性があります。",
showColumnCommentsInHeader: "列名の下にコメントを表示",
showColumnCommentsInHeaderDescription: "グリッド列名の直下にテーブル列コメントを表示します。",
showColumnTypesInHeader: "列名の下にデータ型を表示",
@@ -5600,8 +5607,6 @@ export default withEnglishFallback({
autoCalculateTotalRows: "総行数を自動計算",
autoCalculateTotalRowsDescription: "クエリごとに COUNT(*) を自動実行し、一致する総行数を表示します。大きなクエリを高速に保つため既定はオフです。結果フッターから必要に応じて計算できます。",
infiniteScrollDescription: "末尾までスクロールしたときに、次のデータを自動的に読み込みます。",
infiniteScrollMaxRows: "無限スクロールの最大項目数",
infiniteScrollMaxRowsDescription: "無限スクロールモードで自動読み込みする項目の最大数(1000〜50000)。",
regexMaxMatchCount: "すべての一致を選択する最大数",
regexMaxMatchCountDescription: "「すべての一致を選択」で一度に作成する選択範囲の数を制限します。検索の一致数は完全に集計されます(100〜10000)。",
exportRowLimitEnabled: "エクスポート行数を制限",
+7 -2
View File
@@ -4896,6 +4896,13 @@ export default withEnglishFallback({
dataGridDisplay: "데이터 그리드 표시",
tableOpenPageSize: "테이블을 열 때 기본 페이지당 행 수",
tableOpenPageSizeDescription: "새 테이블을 열 때 페이지당 로드되는 행 수입니다. 테이블에서 페이지당 행을 변경하면 향후 테이블 탭의 기본값이 업데이트됩니다.",
queryPageSize: "쿼리 결과 기본 페이지당 행 수",
queryPageSizeDescription: "쿼리 결과 페이지당 로드할 행 수입니다. 최대 {max}행을 지원합니다.",
queryResultMaxRows: "쿼리 결과 최대 행 수",
queryResultMaxRowsEnabled: "쿼리 결과 행 수 제한",
queryResultMaxRowsDescription: "쿼리, 테이블 데이터, 저장 프로시저 및 연속 로딩에서 유지할 총 행 수를 제한합니다.",
queryResultMaxRowsTooSmall: "쿼리 페이지 크기({pageSize}) 이상이어야 합니다.",
queryResultMaxRowsUnlimitedDescription: "총 행 수 또는 연속 로딩 수를 제한하지 않습니다. 큰 결과는 많은 메모리를 사용할 수 있습니다.",
showColumnCommentsInHeader: "이름 아래에 컬럼 주석 표시",
showColumnCommentsInHeaderDescription: "그리드 컬럼 이름 바로 아래에 테이블 컬럼 주석을 표시합니다.",
showColumnTypesInHeader: "이름 아래에 컬럼 타입 표시",
@@ -4912,8 +4919,6 @@ export default withEnglishFallback({
autoCalculateTotalRows: "전체 행 수 자동 집계",
autoCalculateTotalRowsDescription: "각 쿼리 후 자동으로 COUNT(*)를 실행하여 일치하는 전체 행 수를 표시합니다. 대규모 쿼리를 빠르게 유지하기 위해 기본적으로 꺼져 있으며, 결과 푸터에서 요청 시 집계할 수 있습니다.",
infiniteScrollDescription: "목록 하단으로 스크롤할 때 다음 데이터 묶음을 자동으로 불러옵니다.",
infiniteScrollMaxRows: "무한 스크롤 최대 항목 수",
infiniteScrollMaxRowsDescription: "무한 스크롤 모드에서 자동으로 불러올 항목의 최대 수 (1000–50000).",
tableColumnTemplateFields: "새 테이블 미리 정의된 필드",
tableColumnTemplateFieldsDescription: "데이터베이스 유형을 선택한 다음 새 테이블을 만들 때 사용할 미리 정의된 필드 타입을 구성하세요.",
tableColumnTemplateAdd: "필드 추가",
+7 -2
View File
@@ -5159,6 +5159,13 @@ export default withEnglishFallback({
dataGridDisplay: "Exibição da grade de dados",
tableOpenPageSize: "Linhas padrão por página ao abrir tabelas",
tableOpenPageSizeDescription: "Linhas carregadas por página ao abrir uma nova tabela. Alterar as linhas por página atualiza esse padrão para futuras abas de tabelas.",
queryPageSize: "Linhas padrão por página de consulta",
queryPageSizeDescription: "Linhas carregadas por página de resultados. São suportadas até {max} linhas.",
queryResultMaxRows: "Máximo de linhas do resultado",
queryResultMaxRowsEnabled: "Limitar linhas do resultado",
queryResultMaxRowsDescription: "Limita as linhas mantidas por consultas, dados de tabelas, procedimentos armazenados e carregamento contínuo.",
queryResultMaxRowsTooSmall: "Deve ser pelo menos o tamanho da página da consulta ({pageSize}).",
queryResultMaxRowsUnlimitedDescription: "Sem limite total de linhas ou de carregamento contínuo. Resultados grandes podem usar muita memória.",
showColumnCommentsInHeader: "Mostrar comentários de coluna sob os nomes",
showColumnCommentsInHeaderDescription: "Exibir comentários de colunas da tabela diretamente abaixo dos nomes das colunas da grade.",
showColumnTypesInHeader: "Mostrar tipos de coluna sob os nomes",
@@ -5175,8 +5182,6 @@ export default withEnglishFallback({
autoCalculateTotalRows: "Calcular automaticamente o total de linhas",
autoCalculateTotalRowsDescription: "Executa COUNT(*) automaticamente após cada consulta para mostrar o total de linhas correspondentes. Desativado por padrão para manter consultas grandes rápidas; você pode calculá-lo quando quiser no rodapé dos resultados.",
infiniteScrollDescription: "Carregar automaticamente o próximo lote ao rolar até o final.",
infiniteScrollMaxRows: "Máximo de itens em rolagem infinita",
infiniteScrollMaxRowsDescription: "Número máximo de itens a carregar automaticamente no modo de rolagem infinita (1000–50000).",
regexMaxMatchCount: "Máximo de correspondências para selecionar todas",
regexMaxMatchCountDescription: "Limita o número de seleções criadas por Selecionar todas as correspondências; a contagem da pesquisa permanece completa (100–10000).",
tableColumnTemplateFields: "Campos predefinidos para novas tabelas",
+7 -2
View File
@@ -5319,6 +5319,13 @@ export default withEnglishFallback({
dataGridDisplay: "数据表格显示",
tableOpenPageSize: "打开表默认每页行数",
tableOpenPageSizeDescription: "新打开数据表时每页加载的行数。在表数据页修改每页行数后,也会更新后续新建表标签页的默认值。",
queryPageSize: "查询结果默认每页行数",
queryPageSizeDescription: "每个查询结果分页加载的行数,最大支持 {max} 行。",
queryResultMaxRows: "查询结果最大行数",
queryResultMaxRowsEnabled: "限制查询结果行数",
queryResultMaxRowsDescription: "限制查询、表数据、存储过程及连续滚动累计保留的总行数。",
queryResultMaxRowsTooSmall: "不能小于查询结果默认每页行数({pageSize})。",
queryResultMaxRowsUnlimitedDescription: "不限制返回总行数或连续滚动累计数量,大结果集可能占用较多内存。",
showColumnCommentsInHeader: "在字段名下方显示注释",
showColumnCommentsInHeaderDescription: "把表字段注释直接显示在结果表头字段名下方。",
showColumnTypesInHeader: "在字段名下方显示数据类型",
@@ -5335,8 +5342,6 @@ export default withEnglishFallback({
autoCalculateTotalRows: "自动统计总行数",
autoCalculateTotalRowsDescription: "每次查询后自动执行 COUNT(*) 显示匹配的总行数。默认关闭以保证大查询速度 —— 可在结果栏按需手动统计。",
infiniteScrollDescription: "滚动到底部时自动加载下一批数据,无需手动翻页或加载。",
infiniteScrollMaxRows: "无限滚动最大条目数",
infiniteScrollMaxRowsDescription: "无限滚动模式下自动加载的最大条目数(1000–50000)。",
regexMaxMatchCount: "全部选中最大数量",
regexMaxMatchCountDescription: "限制“全部选中”一次创建的选区数量;搜索匹配仍完整统计(100–10000)。",
tableColumnTemplateFields: "新建表预设字段",
+7 -2
View File
@@ -4603,6 +4603,13 @@ export default withEnglishFallback({
dataGridDisplay: "資料表格顯示",
tableOpenPageSize: "開啟資料表預設每頁列數",
tableOpenPageSizeDescription: "新開啟資料表時每頁載入的列數。在資料表頁面調整每頁列數後,也會更新後續新建資料表分頁的預設值。",
queryPageSize: "查詢結果預設每頁列數",
queryPageSizeDescription: "每個查詢結果分頁載入的列數,最多支援 {max} 列。",
queryResultMaxRows: "查詢結果最大列數",
queryResultMaxRowsEnabled: "限制查詢結果列數",
queryResultMaxRowsDescription: "限制查詢、資料表資料、預存程序及連續捲動累計保留的總列數。",
queryResultMaxRowsTooSmall: "不能小於查詢結果預設每頁列數({pageSize})。",
queryResultMaxRowsUnlimitedDescription: "不限制傳回總列數或連續捲動累計數量,大型結果集可能占用大量記憶體。",
showColumnCommentsInHeader: "在欄位名稱下方顯示註解",
showColumnCommentsInHeaderDescription: "直接在資料表格欄位名稱下方顯示資料表欄位註解。",
showColumnTypesInHeader: "在欄位名稱下方顯示資料類型",
@@ -4619,8 +4626,6 @@ export default withEnglishFallback({
autoCalculateTotalRows: "自動統計總筆數",
autoCalculateTotalRowsDescription: "每次查詢後自動執行 COUNT(*) 顯示符合的總筆數。預設關閉以確保大型查詢速度 —— 可在結果列按需手動統計。",
infiniteScrollDescription: "滾動到底部時自動載入下一批資料,無需手動翻頁或載入。",
infiniteScrollMaxRows: "無限滾動最大項目數",
infiniteScrollMaxRowsDescription: "無限滾動模式下自動載入的最大項目數(1000–50000)。",
regexMaxMatchCount: "全部選取最大數量",
regexMaxMatchCountDescription: "限制「全部選取」一次建立的選取範圍數量;搜尋符合結果仍會完整統計(100–10000)。",
tableColumnTemplateFields: "新建表預設欄位",
@@ -0,0 +1,26 @@
import { describe, expect, it } from "vitest";
import { MAX_QUERY_RESULT_MAX_ROWS, agentProtocolQueryResultMaxRows, capQueryResultTotal, continuousQueryResultMaxRows, effectiveQueryResultMaxRows, limitQueryPagination, normalizeQueryResultMaxRows, queryResultLimitReached } from "../queryResultRowLimit";
describe("query result row limits", () => {
it("preserves the legacy 100,000-row default", () => {
expect(effectiveQueryResultMaxRows(undefined, undefined)).toBe(100_000);
expect(effectiveQueryResultMaxRows(true, 250_000)).toBe(250_000);
});
it("uses the Java signed-int boundary for unlimited Agent execution", () => {
expect(effectiveQueryResultMaxRows(false, 100_000)).toBeUndefined();
expect(agentProtocolQueryResultMaxRows(undefined)).toBe(MAX_QUERY_RESULT_MAX_ROWS);
expect(continuousQueryResultMaxRows(false, 100_000)).toBe(MAX_QUERY_RESULT_MAX_ROWS);
expect(continuousQueryResultMaxRows(true, 250_000)).toBe(250_000);
expect(normalizeQueryResultMaxRows(MAX_QUERY_RESULT_MAX_ROWS + 1)).toBe(MAX_QUERY_RESULT_MAX_ROWS);
});
it("limits the final page and caps totals", () => {
expect(limitQueryPagination({ limit: 100, offset: 950 }, 1_000)).toEqual({ limit: 50, offset: 950 });
expect(limitQueryPagination({ limit: 100, offset: 2_000 }, 1_000)).toEqual({ limit: 1, offset: 999 });
expect(queryResultLimitReached(950, 50, 1_000)).toBe(true);
expect(queryResultLimitReached(900, 50, 1_000)).toBe(false);
expect(capQueryResultTotal(1_500, 1_000)).toBe(1_000);
expect(capQueryResultTotal(1_500, undefined)).toBe(1_500);
});
});
@@ -1,7 +1,7 @@
export const RESULT_PAGE_SIZE_OPTIONS = [50, 100, 500, 1000];
export const DEFAULT_RESULT_PAGE_SIZE = 100;
export const MIN_RESULT_PAGE_SIZE = 1;
export const MAX_RESULT_PAGE_SIZE = 100000;
export const MAX_RESULT_PAGE_SIZE = 1_000_000;
export function normalizeResultPageSize(value: unknown, fallback = DEFAULT_RESULT_PAGE_SIZE): number {
const fallbackValue = Number.isFinite(fallback) && fallback >= MIN_RESULT_PAGE_SIZE ? Math.min(Math.floor(fallback), MAX_RESULT_PAGE_SIZE) : DEFAULT_RESULT_PAGE_SIZE;
@@ -0,0 +1,36 @@
export const DEFAULT_QUERY_RESULT_MAX_ROWS = 100_000;
export const MAX_QUERY_RESULT_MAX_ROWS = 2_147_483_647;
export function normalizeQueryResultMaxRows(value: unknown, fallback = DEFAULT_QUERY_RESULT_MAX_ROWS): number {
const normalizedFallback = Number.isFinite(fallback) ? Math.min(Math.max(1, Math.floor(fallback)), MAX_QUERY_RESULT_MAX_ROWS) : DEFAULT_QUERY_RESULT_MAX_ROWS;
const parsed = Number(value);
if (!Number.isFinite(parsed)) return normalizedFallback;
return Math.min(Math.max(1, Math.floor(parsed)), MAX_QUERY_RESULT_MAX_ROWS);
}
export function effectiveQueryResultMaxRows(enabled: boolean | undefined, value: unknown): number | undefined {
return enabled !== false ? normalizeQueryResultMaxRows(value) : undefined;
}
export function continuousQueryResultMaxRows(enabled: boolean | undefined, value: unknown): number {
return effectiveQueryResultMaxRows(enabled, value) ?? MAX_QUERY_RESULT_MAX_ROWS;
}
export function agentProtocolQueryResultMaxRows(maxRows: number | undefined): number {
return maxRows ?? MAX_QUERY_RESULT_MAX_ROWS;
}
export function limitQueryPagination<T extends { limit: number; offset: number }>(pagination: T, maxRows: number | undefined): T {
if (maxRows === undefined) return pagination;
const offset = Math.min(Math.max(0, pagination.offset), maxRows - 1);
const remaining = maxRows - offset;
return { ...pagination, limit: Math.min(pagination.limit, remaining), offset };
}
export function queryResultLimitReached(offset: number | undefined, rowCount: number, maxRows: number | undefined): boolean {
return maxRows !== undefined && Math.max(0, offset ?? 0) + rowCount >= maxRows;
}
export function capQueryResultTotal(total: number, maxRows: number | undefined): number {
return maxRows === undefined ? total : Math.min(total, maxRows);
}
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { EDITOR_SETTINGS_DRAFT_KEYS, editorSettingsDraftFromSettings, editorSettingsDraftChanged, editorSettingsPatchFromDraft, normalizeTableOpenPageSizeDraft } from "../editorSettingsDraft";
import { EDITOR_SETTINGS_DRAFT_KEYS, editorSettingsDraftFromSettings, editorSettingsDraftChanged, editorSettingsPatchFromDraft, normalizeQueryResultMaxRowsDraft, normalizeTableOpenPageSizeDraft } from "../editorSettingsDraft";
import type { EditorSettings } from "@/stores/settingsStore";
function makeSettings(overrides: Partial<EditorSettings> = {}): EditorSettings {
@@ -7,6 +7,8 @@ function makeSettings(overrides: Partial<EditorSettings> = {}): EditorSettings {
autoCalculateTotalRows: false,
pageSize: 100,
tableOpenPageSize: 100,
queryResultMaxRowsEnabled: true,
queryResultMaxRows: 100000,
sqlEngine: "desktop",
tabSize: 2,
keywordCase: "upper",
@@ -35,7 +37,10 @@ describe("EDITOR_SETTINGS_DRAFT_KEYS", () => {
});
it("includes the table-open page size", () => {
expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("pageSize");
expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("tableOpenPageSize");
expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("queryResultMaxRowsEnabled");
expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("queryResultMaxRows");
});
it("includes the saved SQL open target mode", () => {
@@ -89,7 +94,8 @@ describe("editorSettingsDraftFromSettings", () => {
describe("normalizeTableOpenPageSizeDraft", () => {
it.each([
[200000, 100000],
[200000, 200000],
[2000000, 1000000],
[0, 100],
[-1, 100],
["123.9", 123],
@@ -102,6 +108,17 @@ describe("normalizeTableOpenPageSizeDraft", () => {
});
});
describe("normalizeQueryResultMaxRowsDraft", () => {
it.each([
[250000, 250000],
[0, 1],
[2147483648, 2147483647],
[Number.NaN, 100000],
])("normalizes %s to %s", (value, expected) => {
expect(normalizeQueryResultMaxRowsDraft(value)).toBe(expected);
});
});
describe("editorSettingsDraftChanged", () => {
it("detects change in continueOnErrorOnBatch", () => {
const settings = makeSettings({ continueOnErrorOnBatch: false });
@@ -181,7 +198,7 @@ describe("editorSettingsPatchFromDraft", () => {
const draft = editorSettingsDraftFromSettings(settings);
const base = editorSettingsDraftFromSettings(settings);
draft.tableOpenPageSize = 200000.9;
expect(editorSettingsPatchFromDraft(draft, base).tableOpenPageSize).toBe(100000);
expect(editorSettingsPatchFromDraft(draft, base).tableOpenPageSize).toBe(200000);
});
it("includes the saved SQL open target when changed", () => {
@@ -131,7 +131,7 @@ describe("settings search", () => {
{ titleKey: "settings.savedSqlOpenTarget", category: "editor", targetId: "editor" },
{ titleKey: "settings.confirmDangerousSqlExecution", category: "editor", targetId: "editor" },
{ titleKey: "settings.continueOnErrorOnBatch", category: "editor", targetId: "editor" },
{ titleKey: "settings.dataGridQuickEntry", category: "appearance", targetId: "appearance" },
{ titleKey: "settings.dataGridQuickEntry", category: "data", targetId: "data" },
{ titleKey: "transfer.dataTransfer", category: "appearance", targetId: "appearance" },
{ titleKey: "toolbar.driverManager", category: "appearance", targetId: "appearance" },
{ titleKey: "toolbar.theme", category: "appearance", targetId: "appearance" },
@@ -155,8 +155,8 @@ describe("settings search", () => {
{ titleKey: "settings.sqlFormatterParamTypes", category: "formatter", targetId: "formatter" },
{ titleKey: "settings.routineSourceOpenMode", category: "navigation", targetId: "navigation" },
{ titleKey: "settings.disconnectTabHandlingMode", category: "navigation", targetId: "navigation" },
{ titleKey: "settings.compactColumnHeaderActions", category: "appearance", targetId: "appearance" },
{ titleKey: "settings.infiniteScrollMaxRows", category: "appearance", targetId: "appearance" },
{ titleKey: "settings.compactColumnHeaderActions", category: "data", targetId: "data" },
{ titleKey: "settings.infiniteScroll", category: "data", targetId: "data" },
{ titleKey: "settings.globalDateTimeDisplayFormat", category: "data", targetId: "data" },
{ titleKey: "settings.globalDateTimeExportFormat", category: "data", targetId: "data" },
{ titleKey: "settings.globalDateTimeImportFormat", category: "data", targetId: "data" },
@@ -1,5 +1,6 @@
import type { EditorSettings } from "@/stores/settingsStore";
import { normalizeResultPageSize } from "@/lib/dataGrid/paginationPageSize";
import { normalizeQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
import { normalizeCompletionTriggerMode } from "@/lib/sql/sqlCompletionTriggerPolicy";
export const EDITOR_SETTINGS_DRAFT_KEYS = [
@@ -36,9 +37,11 @@ export const EDITOR_SETTINGS_DRAFT_KEYS = [
"compactColumnHeaderActions",
"dataGridQuickEntry",
"dataGridAutoTransposeSingleRow",
"pageSize",
"tableOpenPageSize",
"queryResultMaxRowsEnabled",
"queryResultMaxRows",
"infiniteScroll",
"infiniteScrollMaxRows",
"regexMaxMatchCount",
"autoCalculateTotalRows",
"tableColumnTemplateFields",
@@ -86,8 +89,13 @@ export function normalizeTableOpenPageSizeDraft(value: unknown): number {
return normalizeResultPageSize(value);
}
export function normalizeQueryResultMaxRowsDraft(value: unknown): number {
return normalizeQueryResultMaxRows(value);
}
function normalizedDraftValue(key: EditorSettingsDraftKey, value: unknown): unknown {
if (key === "tableOpenPageSize") return normalizeTableOpenPageSizeDraft(value);
if (key === "pageSize" || key === "tableOpenPageSize") return normalizeTableOpenPageSizeDraft(value);
if (key === "queryResultMaxRows") return normalizeQueryResultMaxRowsDraft(value);
if (key === "completionTriggerMode") return normalizeCompletionTriggerMode(value);
return value;
}
@@ -170,16 +170,17 @@ export const SETTINGS_SEARCH_DEFINITIONS: readonly SettingsSearchDefinition[] =
{ id: "navigation-hidden-tables", category: "navigation", titleKey: "settings.sidebarHiddenTablePrefixes", descriptionKey: "settings.sidebarHiddenTablePrefixesDescription", targetId: "navigation" },
{ id: "navigation-table-page-size", category: "navigation", titleKey: "settings.sidebarTablePageSize", descriptionKey: "settings.sidebarTablePageSizeDescription", targetId: "navigation" },
{ id: "navigation-disconnect-tabs", category: "navigation", titleKey: "settings.disconnectTabHandlingMode", descriptionKey: "settings.disconnectTabHandlingModeDescription", targetId: "navigation" },
{ id: "query-page-size", category: "data", titleKey: "settings.queryPageSize", descriptionKey: "settings.queryPageSizeDescription", targetId: "data" },
{ id: "data-page-size", category: "data", titleKey: "settings.tableOpenPageSize", descriptionKey: "settings.tableOpenPageSizeDescription", targetId: "data" },
{ id: "appearance-header-comments", category: "appearance", titleKey: "settings.showColumnCommentsInHeader", descriptionKey: "settings.showColumnCommentsInHeaderDescription", targetId: "appearance" },
{ id: "appearance-header-types", category: "appearance", titleKey: "settings.showColumnTypesInHeader", descriptionKey: "settings.showColumnTypesInHeaderDescription", targetId: "appearance" },
{ id: "appearance-index-indicators", category: "appearance", titleKey: "settings.showIndexIndicatorsInHeader", descriptionKey: "settings.showIndexIndicatorsInHeaderDescription", targetId: "appearance" },
{ id: "appearance-compact-header-actions", category: "appearance", titleKey: "settings.compactColumnHeaderActions", descriptionKey: "settings.compactColumnHeaderActionsDescription", targetId: "appearance" },
{ id: "appearance-auto-total", category: "appearance", titleKey: "settings.autoCalculateTotalRows", descriptionKey: "settings.autoCalculateTotalRowsDescription", targetId: "appearance" },
{ id: "appearance-infinite-scroll", category: "appearance", titleKey: "settings.infiniteScroll", descriptionKey: "settings.infiniteScrollDescription", targetId: "appearance" },
{ id: "appearance-infinite-scroll-limit", category: "appearance", titleKey: "settings.infiniteScrollMaxRows", descriptionKey: "settings.infiniteScrollMaxRowsDescription", targetId: "appearance" },
{ id: "appearance-auto-transpose", category: "appearance", titleKey: "settings.dataGridAutoTransposeSingleRow", descriptionKey: "settings.dataGridAutoTransposeSingleRowDescription", targetId: "appearance" },
{ id: "appearance-quick-entry", category: "appearance", titleKey: "settings.dataGridQuickEntry", descriptionKey: "settings.dataGridQuickEntryDescription", targetId: "appearance" },
{ id: "query-result-max-rows", category: "data", titleKey: "settings.queryResultMaxRows", descriptionKey: "settings.queryResultMaxRowsDescription", targetId: "data" },
{ id: "data-grid-header-comments", category: "data", titleKey: "settings.showColumnCommentsInHeader", descriptionKey: "settings.showColumnCommentsInHeaderDescription", targetId: "data" },
{ id: "data-grid-header-types", category: "data", titleKey: "settings.showColumnTypesInHeader", descriptionKey: "settings.showColumnTypesInHeaderDescription", targetId: "data" },
{ id: "data-grid-index-indicators", category: "data", titleKey: "settings.showIndexIndicatorsInHeader", descriptionKey: "settings.showIndexIndicatorsInHeaderDescription", targetId: "data" },
{ id: "data-grid-compact-header-actions", category: "data", titleKey: "settings.compactColumnHeaderActions", descriptionKey: "settings.compactColumnHeaderActionsDescription", targetId: "data" },
{ id: "data-grid-auto-total", category: "data", titleKey: "settings.autoCalculateTotalRows", descriptionKey: "settings.autoCalculateTotalRowsDescription", targetId: "data" },
{ id: "data-grid-infinite-scroll", category: "data", titleKey: "settings.infiniteScroll", descriptionKey: "settings.infiniteScrollDescription", targetId: "data" },
{ id: "data-grid-auto-transpose", category: "data", titleKey: "settings.dataGridAutoTransposeSingleRow", descriptionKey: "settings.dataGridAutoTransposeSingleRowDescription", targetId: "data" },
{ id: "data-grid-quick-entry", category: "data", titleKey: "settings.dataGridQuickEntry", descriptionKey: "settings.dataGridQuickEntryDescription", targetId: "data" },
{ id: "appearance-toolbar", category: "appearance", titleKey: "settings.toolbarTitle", descriptionKey: "settings.toolbarHiddenHint", targetId: "appearance" },
{ id: "appearance-exclusive-sidebar-panels", category: "appearance", titleKey: "settings.exclusiveRightSidebarPanels", descriptionKey: "settings.exclusiveRightSidebarPanelsDescription", targetId: "appearance" },
...createToolbarVisibilitySettingsSearchDefinitions(),
+31 -12
View File
@@ -47,7 +47,8 @@ import { frontendQueryTimeoutDelayMs, frontendQueryTimeoutSecsForSql, queryTimeo
import { queryResultNameFromPreamble, queryResultSourceLabel } from "@/lib/sql/queryResultSource";
import { beginDataGridNativeSelectionBlock, finishDataGridNativeSelectionBlock } from "@/lib/dataGrid/dataGridNativeSelection";
import { simpleDataGridOrderByReferencesMissingColumn, sortDataGridRowIndexes, type DataGridSortDirection } from "@/lib/dataGrid/dataGridSort";
import { MAX_RESULT_PAGE_SIZE, normalizeResultPageSize } from "@/lib/dataGrid/paginationPageSize";
import { normalizeResultPageSize } from "@/lib/dataGrid/paginationPageSize";
import { agentProtocolQueryResultMaxRows, capQueryResultTotal, effectiveQueryResultMaxRows, limitQueryPagination, queryResultLimitReached } from "@/lib/dataGrid/queryResultRowLimit";
import { elasticsearchRestRequestRanges, executableStatementRanges, splitSqlStatementRanges } from "@/lib/sql/sqlStatementRanges";
import { externalSqlFileDisplayTitles, normalizeExternalSqlPath } from "@/lib/sql/sqlFileOpen";
import { clearDataGridPendingSnapshotsForTab } from "@/composables/useDataGridEditor";
@@ -3610,6 +3611,7 @@ export const useQueryStore = defineStore("query", () => {
traceId: string;
elapsed: () => string;
timeoutSecs: number;
maxRows?: number;
}) {
const resultRowCount = options.result.rows.length;
if (resultRowCount <= 0) {
@@ -3652,10 +3654,11 @@ export const useQueryStore = defineStore("query", () => {
setQueryTotalRowCountIfCurrent(options.tabId, options.executionId, options.result, undefined);
return;
}
setQueryTotalRowCountIfCurrent(options.tabId, options.executionId, options.result, total);
const cappedTotal = capQueryResultTotal(total, options.maxRows);
setQueryTotalRowCountIfCurrent(options.tabId, options.executionId, options.result, cappedTotal);
queryExecutionLog("info", "count:done", {
traceId: options.traceId,
total,
total: cappedTotal,
elapsed: options.elapsed(),
});
} catch (error) {
@@ -4284,6 +4287,8 @@ export const useQueryStore = defineStore("query", () => {
return producedResult;
}
const queryResultMaxRows = effectiveQueryResultMaxRows(settingsStore.editorSettings.queryResultMaxRowsEnabled, settingsStore.editorSettings.queryResultMaxRows);
if (tab.mode === "query") {
const prepared = await prepareEditableQueryExecution(tab, sqlToExecute, conn, effectiveDbType, executionDatabase, traceId, elapsed);
sqlToExecute = prepared.sql;
@@ -4305,7 +4310,7 @@ export const useQueryStore = defineStore("query", () => {
sqlToExecute = sorted.sql;
resultSortedSql = sorted.sql;
}
const pagination = options?.pagination ?? { limit: settingsStore.editorSettings.pageSize, offset: 0 };
const pagination = limitQueryPagination(options?.pagination ?? { limit: settingsStore.editorSettings.pageSize, offset: 0 }, queryResultMaxRows);
const plan = await api.prepareQueryPaginationExecutionPlan({
sql: sqlToExecute,
queryBaseSql,
@@ -4336,8 +4341,15 @@ export const useQueryStore = defineStore("query", () => {
return false;
}
} else if (tab.mode === "data") {
pageLimit = options?.pagination?.limit ?? tableOpenPageLimit(settingsStore.editorSettings.tableOpenPageSize);
pageOffset = options?.pagination?.offset ?? 0;
const pagination = limitQueryPagination(
{
limit: options?.pagination?.limit ?? tableOpenPageLimit(settingsStore.editorSettings.tableOpenPageSize),
offset: options?.pagination?.offset ?? 0,
},
queryResultMaxRows,
);
pageLimit = pagination.limit;
pageOffset = pagination.offset;
}
const executionSchema = targetContext?.scope === "connection" ? undefined : connectionQueryExecutionSchema(conn, databaseTargetContext?.database ?? executionTarget?.database ?? tab.database, databaseTargetContext?.schema ?? executionTarget?.schema ?? tab.schema, tab.mode === "data");
@@ -4354,7 +4366,7 @@ export const useQueryStore = defineStore("query", () => {
}
queryExecutionLog("info", "execute-in-txn:invoke", { traceId, txnSessionId: tab.txnSessionId, elapsed: elapsed() });
executionDispatched = true;
executionPromise = api.executeInManualTransaction(tab.txnSessionId, sqlToExecute, executionDatabase, executionSchema, pageLimit);
executionPromise = api.executeInManualTransaction(tab.txnSessionId, sqlToExecute, executionDatabase, executionSchema, pageLimit ?? agentProtocolQueryResultMaxRows(queryResultMaxRows));
} else {
queryExecutionLog("info", "execute-multi:start", { traceId, elapsed: elapsed() });
// Query and data tabs use a tab-scoped pool so repeated executions keep
@@ -4364,14 +4376,13 @@ export const useQueryStore = defineStore("query", () => {
? useAgentResultSession
? {
// Agent cursors apply maxRows cumulatively across fetched pages.
// Keep multi-page navigation available without allowing unbounded reads.
maxRows: MAX_RESULT_PAGE_SIZE,
maxRows: agentProtocolQueryResultMaxRows(queryResultMaxRows),
fetchSize: pageLimit,
pageSize: pageLimit,
resultSessionId: options?.pagination?.sessionId,
}
: { maxRows: pageLimit, fetchSize: pageLimit }
: {}),
: { maxRows: agentProtocolQueryResultMaxRows(queryResultMaxRows) }),
...(executionClientSessionId ? { clientSessionId: executionClientSessionId } : {}),
timeoutSecs: queryTimeoutSecs,
catalog: executionCatalog,
@@ -4497,6 +4508,12 @@ export const useQueryStore = defineStore("query", () => {
current.resultTotalRowCount = undefined;
}
const resultRowCount = current.result?.rows.length ?? 0;
const resultLimitReached = !!current.result && queryResultLimitReached(pageOffset, resultRowCount, queryResultMaxRows);
if (resultLimitReached && current.result) {
current.result.has_more = false;
current.result.truncated = true;
current.resultTotalRowCount = queryResultMaxRows;
}
const totalKnownFromIncompletePage = !!current.result && typeof exactTotalFromIncompletePage(current.result, pageLimit, pageOffset, useAgentResultSession) === "number";
const dataCountTarget =
current.mode === "data"
@@ -4514,7 +4531,8 @@ export const useQueryStore = defineStore("query", () => {
};
})()
: undefined;
const canAutoCalculateTotalRows = !options?.appendResult && !!current.result && resultRowCount > 0 && !totalKnownFromIncompletePage && settingsStore.editorSettings.autoCalculateTotalRows && ((current.mode === "query" && !!countSql) || (current.mode === "data" && !!dataCountTarget));
const canAutoCalculateTotalRows =
!options?.appendResult && !!current.result && resultRowCount > 0 && !resultLimitReached && !totalKnownFromIncompletePage && settingsStore.editorSettings.autoCalculateTotalRows && ((current.mode === "query" && !!countSql) || (current.mode === "data" && !!dataCountTarget));
current.resultTotalRowCountLoading = canAutoCalculateTotalRows;
// Server-side pagination without a countSql: the backend (currently
// the Elasticsearch driver) already reports the true match total via
@@ -4528,7 +4546,7 @@ export const useQueryStore = defineStore("query", () => {
}
touchResult(current);
syncDisplayedResultRun(current, queryBaseSql, captureResultRun);
if (!options?.appendResult && !totalRowCountResolved && (current.mode === "query" || current.mode === "data") && current.result) {
if (!options?.appendResult && !resultLimitReached && !totalRowCountResolved && (current.mode === "query" || current.mode === "data") && current.result) {
countQueryTotalRowsInBackground({
tabId: id,
connectionId: executionConnectionId,
@@ -4550,6 +4568,7 @@ export const useQueryStore = defineStore("query", () => {
traceId,
elapsed,
timeoutSecs: queryTimeoutSecs,
maxRows: queryResultMaxRows,
});
}
queryExecutionLog("info", "result:assigned", {
+10
View File
@@ -8,6 +8,7 @@ import { safeLocalStorageGet, safeLocalStorageRemove } from "@/lib/backend/safeS
import { type ColumnFormatterConfig, type CustomColumnFormatterConfig, normalizeColumnFormatter, normalizeCustomColumnFormatter, normalizeGlobalDateTimePattern } from "@/lib/dataGrid/columnFormatter";
import { type DataGridCopyPreference, type DataGridExtractorOptions, DEFAULT_DATA_GRID_EXTRACTOR_OPTIONS, normalizeDataGridCopyPreference, normalizeDataGridExtractorOptions } from "@/lib/dataGrid/dataGridCopyExtractor";
import { normalizeResultPageSize } from "@/lib/dataGrid/paginationPageSize";
import { DEFAULT_QUERY_RESULT_MAX_ROWS, normalizeQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
import { normalizeShortcutSettings, type ShortcutSettings } from "@/lib/editor/shortcutRegistry";
import type { SavedSqlOpenTargetMode } from "@/lib/savedSql/savedSqlExecutionTarget";
import type { ConnectionListSortMode } from "@/lib/sidebar/connectionListSort";
@@ -515,7 +516,10 @@ export interface EditorSettings {
appLayout: "separated" | "classic";
pageSize: number;
tableOpenPageSize: number;
queryResultMaxRowsEnabled: boolean;
queryResultMaxRows: number;
infiniteScroll: boolean;
/** Preserved for downgrade compatibility; current clients use queryResultMaxRows. */
infiniteScrollMaxRows: number;
regexMaxMatchCount: number;
autoCalculateTotalRows: boolean;
@@ -702,6 +706,8 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
appLayout: "classic",
pageSize: 100,
tableOpenPageSize: 100,
queryResultMaxRowsEnabled: true,
queryResultMaxRows: DEFAULT_QUERY_RESULT_MAX_ROWS,
infiniteScroll: false,
infiniteScrollMaxRows: 5000,
regexMaxMatchCount: 1000,
@@ -1036,6 +1042,8 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
appLayout: settings.appLayout ?? DEFAULT_EDITOR_SETTINGS.appLayout,
pageSize: normalizeResultPageSize(settings.pageSize),
tableOpenPageSize: normalizeResultPageSize(settings.tableOpenPageSize, DEFAULT_EDITOR_SETTINGS.tableOpenPageSize),
queryResultMaxRowsEnabled: settings.queryResultMaxRowsEnabled !== false,
queryResultMaxRows: normalizeQueryResultMaxRows(settings.queryResultMaxRows),
infiniteScroll: settings.infiniteScroll ?? DEFAULT_EDITOR_SETTINGS.infiniteScroll,
infiniteScrollMaxRows: typeof settings.infiniteScrollMaxRows === "number" && settings.infiniteScrollMaxRows >= 1000 && settings.infiniteScrollMaxRows <= 50000 ? Math.round(settings.infiniteScrollMaxRows) : DEFAULT_EDITOR_SETTINGS.infiniteScrollMaxRows,
regexMaxMatchCount: typeof settings.regexMaxMatchCount === "number" && Number.isFinite(settings.regexMaxMatchCount) && settings.regexMaxMatchCount >= 100 && settings.regexMaxMatchCount <= 10000 ? Math.round(settings.regexMaxMatchCount) : DEFAULT_EDITOR_SETTINGS.regexMaxMatchCount,
@@ -1572,6 +1580,8 @@ export const useSettingsStore = defineStore("settings", () => {
if (partial.appLayout !== undefined) editorSettings.value.appLayout = partial.appLayout;
if (partial.pageSize !== undefined) editorSettings.value.pageSize = normalizeResultPageSize(partial.pageSize);
if (partial.tableOpenPageSize !== undefined) editorSettings.value.tableOpenPageSize = normalizeResultPageSize(partial.tableOpenPageSize, DEFAULT_EDITOR_SETTINGS.tableOpenPageSize);
if (partial.queryResultMaxRowsEnabled !== undefined) editorSettings.value.queryResultMaxRowsEnabled = Boolean(partial.queryResultMaxRowsEnabled);
if (partial.queryResultMaxRows !== undefined) editorSettings.value.queryResultMaxRows = normalizeQueryResultMaxRows(partial.queryResultMaxRows, editorSettings.value.queryResultMaxRows);
if (partial.infiniteScroll !== undefined) editorSettings.value.infiniteScroll = partial.infiniteScroll;
if (partial.infiniteScrollMaxRows !== undefined)
editorSettings.value.infiniteScrollMaxRows = typeof partial.infiniteScrollMaxRows === "number" && partial.infiniteScrollMaxRows >= 1000 && partial.infiniteScrollMaxRows <= 50000 ? Math.round(partial.infiniteScrollMaxRows) : DEFAULT_EDITOR_SETTINGS.infiniteScrollMaxRows;