feat(settings): configurable query result max rows with unified load limit
This commit is contained in:
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 ScheduledDatabaseBackupSettings from "@/components/backup/ScheduledDatabaseBackupSettings.vue";
|
||||||
import SqlFormatterSettingsPanel from "./SqlFormatterSettingsPanel.vue";
|
import SqlFormatterSettingsPanel from "./SqlFormatterSettingsPanel.vue";
|
||||||
import { APP_THEME_PALETTES, type AppCornerStyle, type AppThemeAppearance, type AppThemeMode, type AppThemePalette } from "@/lib/app/appTheme";
|
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 { useConnectionStore } from "@/stores/connectionStore";
|
||||||
import { useSavedSqlStore } from "@/stores/savedSqlStore";
|
import { useSavedSqlStore } from "@/stores/savedSqlStore";
|
||||||
import { usePromptTemplateStore } from "@/stores/promptTemplateStore";
|
import { usePromptTemplateStore } from "@/stores/promptTemplateStore";
|
||||||
@@ -134,6 +134,7 @@ import { buildFontFamilyOptions, displayFontFamily, isPresetFontFamily, loadSyst
|
|||||||
import { buildAppSupportInfoRows, formatAppSupportInfoForClipboard, type AppSupportInfoLabels } from "@/lib/app/supportInfo";
|
import { buildAppSupportInfoRows, formatAppSupportInfoForClipboard, type AppSupportInfoLabels } from "@/lib/app/supportInfo";
|
||||||
import { DateTimePatterns, normalizeSupportedDateTimePattern } from "@/lib/dataGrid/columnFormatter";
|
import { DateTimePatterns, normalizeSupportedDateTimePattern } from "@/lib/dataGrid/columnFormatter";
|
||||||
import { MAX_RESULT_PAGE_SIZE, MIN_RESULT_PAGE_SIZE } from "@/lib/dataGrid/paginationPageSize";
|
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 type { PromptTemplate } from "@/types/promptTemplate";
|
||||||
import { GLOBAL_INSTRUCTIONS_MAX, PROMPT_TEMPLATE_CONTENT_MAX, PROMPT_TEMPLATE_NAME_MAX, promptTemplateCharacterCount } 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 editCompactColumnHeaderActions = ref(settingsStore.editorSettings.compactColumnHeaderActions);
|
||||||
const editDataGridQuickEntry = ref(settingsStore.editorSettings.dataGridQuickEntry);
|
const editDataGridQuickEntry = ref(settingsStore.editorSettings.dataGridQuickEntry);
|
||||||
const editDataGridAutoTransposeSingleRow = ref(settingsStore.editorSettings.dataGridAutoTransposeSingleRow);
|
const editDataGridAutoTransposeSingleRow = ref(settingsStore.editorSettings.dataGridAutoTransposeSingleRow);
|
||||||
|
const editPageSize = ref(settingsStore.editorSettings.pageSize);
|
||||||
const editTableOpenPageSize = ref(settingsStore.editorSettings.tableOpenPageSize);
|
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 editInfiniteScroll = ref(settingsStore.editorSettings.infiniteScroll);
|
||||||
const editInfiniteScrollMaxRows = ref(settingsStore.editorSettings.infiniteScrollMaxRows);
|
|
||||||
const editRegexMaxMatchCount = ref(settingsStore.editorSettings.regexMaxMatchCount);
|
const editRegexMaxMatchCount = ref(settingsStore.editorSettings.regexMaxMatchCount);
|
||||||
const editAutoCalculateTotalRows = ref(settingsStore.editorSettings.autoCalculateTotalRows);
|
const editAutoCalculateTotalRows = ref(settingsStore.editorSettings.autoCalculateTotalRows);
|
||||||
const editTableColumnTemplateRows = ref<TableColumnTemplateGridRow[]>(tableColumnTemplateRowsFromSettings(settingsStore.editorSettings.tableColumnTemplateFields));
|
const editTableColumnTemplateRows = ref<TableColumnTemplateGridRow[]>(tableColumnTemplateRowsFromSettings(settingsStore.editorSettings.tableColumnTemplateFields));
|
||||||
@@ -328,6 +331,22 @@ function updateTableOpenPageSizeDraft(value: string | number) {
|
|||||||
editTableOpenPageSize.value = normalizeTableOpenPageSizeDraft(value);
|
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 {
|
function sqlVariableSyntaxToggle(key: keyof SqlVariableSyntaxToggles): boolean {
|
||||||
return editSqlVariableSyntaxOverrides.value[editSqlVariableSyntaxDatabaseType.value]?.[key] ?? true;
|
return editSqlVariableSyntaxOverrides.value[editSqlVariableSyntaxDatabaseType.value]?.[key] ?? true;
|
||||||
}
|
}
|
||||||
@@ -471,9 +490,11 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
|
|||||||
compactColumnHeaderActions: editCompactColumnHeaderActions.value,
|
compactColumnHeaderActions: editCompactColumnHeaderActions.value,
|
||||||
dataGridQuickEntry: editDataGridQuickEntry.value,
|
dataGridQuickEntry: editDataGridQuickEntry.value,
|
||||||
dataGridAutoTransposeSingleRow: editDataGridAutoTransposeSingleRow.value,
|
dataGridAutoTransposeSingleRow: editDataGridAutoTransposeSingleRow.value,
|
||||||
|
pageSize: editPageSize.value,
|
||||||
tableOpenPageSize: editTableOpenPageSize.value,
|
tableOpenPageSize: editTableOpenPageSize.value,
|
||||||
|
queryResultMaxRowsEnabled: editQueryResultMaxRowsEnabled.value,
|
||||||
|
queryResultMaxRows: editQueryResultMaxRows.value,
|
||||||
infiniteScroll: editInfiniteScroll.value,
|
infiniteScroll: editInfiniteScroll.value,
|
||||||
infiniteScrollMaxRows: editInfiniteScrollMaxRows.value,
|
|
||||||
regexMaxMatchCount: editRegexMaxMatchCount.value,
|
regexMaxMatchCount: editRegexMaxMatchCount.value,
|
||||||
autoCalculateTotalRows: editAutoCalculateTotalRows.value,
|
autoCalculateTotalRows: editAutoCalculateTotalRows.value,
|
||||||
tableColumnTemplateFields: normalizedEditTableColumnTemplateFields.value,
|
tableColumnTemplateFields: normalizedEditTableColumnTemplateFields.value,
|
||||||
@@ -743,9 +764,11 @@ function syncEditorSettingsDraftFromStore() {
|
|||||||
editCompactColumnHeaderActions.value = settingsStore.editorSettings.compactColumnHeaderActions;
|
editCompactColumnHeaderActions.value = settingsStore.editorSettings.compactColumnHeaderActions;
|
||||||
editDataGridQuickEntry.value = settingsStore.editorSettings.dataGridQuickEntry;
|
editDataGridQuickEntry.value = settingsStore.editorSettings.dataGridQuickEntry;
|
||||||
editDataGridAutoTransposeSingleRow.value = settingsStore.editorSettings.dataGridAutoTransposeSingleRow;
|
editDataGridAutoTransposeSingleRow.value = settingsStore.editorSettings.dataGridAutoTransposeSingleRow;
|
||||||
|
editPageSize.value = settingsStore.editorSettings.pageSize;
|
||||||
editTableOpenPageSize.value = settingsStore.editorSettings.tableOpenPageSize;
|
editTableOpenPageSize.value = settingsStore.editorSettings.tableOpenPageSize;
|
||||||
|
editQueryResultMaxRowsEnabled.value = settingsStore.editorSettings.queryResultMaxRowsEnabled;
|
||||||
|
editQueryResultMaxRows.value = settingsStore.editorSettings.queryResultMaxRows;
|
||||||
editInfiniteScroll.value = settingsStore.editorSettings.infiniteScroll;
|
editInfiniteScroll.value = settingsStore.editorSettings.infiniteScroll;
|
||||||
editInfiniteScrollMaxRows.value = settingsStore.editorSettings.infiniteScrollMaxRows;
|
|
||||||
editRegexMaxMatchCount.value = settingsStore.editorSettings.regexMaxMatchCount;
|
editRegexMaxMatchCount.value = settingsStore.editorSettings.regexMaxMatchCount;
|
||||||
editAutoCalculateTotalRows.value = settingsStore.editorSettings.autoCalculateTotalRows;
|
editAutoCalculateTotalRows.value = settingsStore.editorSettings.autoCalculateTotalRows;
|
||||||
editTableColumnTemplateRows.value = tableColumnTemplateRowsFromSettings(settingsStore.editorSettings.tableColumnTemplateFields);
|
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 duckDbWorkerSettingsRequireRestart = computed(() => editDuckDbWorkerProcessIsolation.value !== startupDuckDbWorkerProcessIsolation.value || normalizeDuckDbWorkerMaxProcesses(editDuckDbWorkerMaxProcesses.value) !== startupDuckDbWorkerMaxProcesses.value);
|
||||||
const hasBlockingShortcutConflicts = computed(() => shortcutsChanged.value && hasShortcutConflicts.value);
|
const hasBlockingShortcutConflicts = computed(() => shortcutsChanged.value && hasShortcutConflicts.value);
|
||||||
const hasBlockingFormatterConfig = computed(() => activeSettingsTab.value === "formatter" && !sqlFormatterConfigValid.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 {
|
function hasChanges(): boolean {
|
||||||
return (
|
return (
|
||||||
@@ -994,9 +1018,11 @@ function resetDefaultsForTab(tab: SettingsCategory) {
|
|||||||
editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions;
|
editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions;
|
||||||
editDataGridQuickEntry.value = DEFAULT_EDITOR_SETTINGS.dataGridQuickEntry;
|
editDataGridQuickEntry.value = DEFAULT_EDITOR_SETTINGS.dataGridQuickEntry;
|
||||||
editDataGridAutoTransposeSingleRow.value = DEFAULT_EDITOR_SETTINGS.dataGridAutoTransposeSingleRow;
|
editDataGridAutoTransposeSingleRow.value = DEFAULT_EDITOR_SETTINGS.dataGridAutoTransposeSingleRow;
|
||||||
|
editPageSize.value = DEFAULT_EDITOR_SETTINGS.pageSize;
|
||||||
editTableOpenPageSize.value = DEFAULT_EDITOR_SETTINGS.tableOpenPageSize;
|
editTableOpenPageSize.value = DEFAULT_EDITOR_SETTINGS.tableOpenPageSize;
|
||||||
|
editQueryResultMaxRowsEnabled.value = DEFAULT_EDITOR_SETTINGS.queryResultMaxRowsEnabled;
|
||||||
|
editQueryResultMaxRows.value = DEFAULT_EDITOR_SETTINGS.queryResultMaxRows;
|
||||||
editInfiniteScroll.value = DEFAULT_EDITOR_SETTINGS.infiniteScroll;
|
editInfiniteScroll.value = DEFAULT_EDITOR_SETTINGS.infiniteScroll;
|
||||||
editInfiniteScrollMaxRows.value = DEFAULT_EDITOR_SETTINGS.infiniteScrollMaxRows;
|
|
||||||
editRegexMaxMatchCount.value = DEFAULT_EDITOR_SETTINGS.regexMaxMatchCount;
|
editRegexMaxMatchCount.value = DEFAULT_EDITOR_SETTINGS.regexMaxMatchCount;
|
||||||
editAutoCalculateTotalRows.value = DEFAULT_EDITOR_SETTINGS.autoCalculateTotalRows;
|
editAutoCalculateTotalRows.value = DEFAULT_EDITOR_SETTINGS.autoCalculateTotalRows;
|
||||||
editDuckDbWorkerProcessIsolation.value = DEFAULT_DESKTOP_SETTINGS.duckdb_worker_process_isolation;
|
editDuckDbWorkerProcessIsolation.value = DEFAULT_DESKTOP_SETTINGS.duckdb_worker_process_isolation;
|
||||||
@@ -1061,9 +1087,11 @@ function resetAllDefaults() {
|
|||||||
editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions;
|
editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions;
|
||||||
editDataGridQuickEntry.value = DEFAULT_EDITOR_SETTINGS.dataGridQuickEntry;
|
editDataGridQuickEntry.value = DEFAULT_EDITOR_SETTINGS.dataGridQuickEntry;
|
||||||
editDataGridAutoTransposeSingleRow.value = DEFAULT_EDITOR_SETTINGS.dataGridAutoTransposeSingleRow;
|
editDataGridAutoTransposeSingleRow.value = DEFAULT_EDITOR_SETTINGS.dataGridAutoTransposeSingleRow;
|
||||||
|
editPageSize.value = DEFAULT_EDITOR_SETTINGS.pageSize;
|
||||||
editTableOpenPageSize.value = DEFAULT_EDITOR_SETTINGS.tableOpenPageSize;
|
editTableOpenPageSize.value = DEFAULT_EDITOR_SETTINGS.tableOpenPageSize;
|
||||||
|
editQueryResultMaxRowsEnabled.value = DEFAULT_EDITOR_SETTINGS.queryResultMaxRowsEnabled;
|
||||||
|
editQueryResultMaxRows.value = DEFAULT_EDITOR_SETTINGS.queryResultMaxRows;
|
||||||
editInfiniteScroll.value = DEFAULT_EDITOR_SETTINGS.infiniteScroll;
|
editInfiniteScroll.value = DEFAULT_EDITOR_SETTINGS.infiniteScroll;
|
||||||
editInfiniteScrollMaxRows.value = DEFAULT_EDITOR_SETTINGS.infiniteScrollMaxRows;
|
|
||||||
editRegexMaxMatchCount.value = DEFAULT_EDITOR_SETTINGS.regexMaxMatchCount;
|
editRegexMaxMatchCount.value = DEFAULT_EDITOR_SETTINGS.regexMaxMatchCount;
|
||||||
editAutoCalculateTotalRows.value = DEFAULT_EDITOR_SETTINGS.autoCalculateTotalRows;
|
editAutoCalculateTotalRows.value = DEFAULT_EDITOR_SETTINGS.autoCalculateTotalRows;
|
||||||
editTableColumnTemplateRows.value = tableColumnTemplateRowsFromSettings(DEFAULT_EDITOR_SETTINGS.tableColumnTemplateFields);
|
editTableColumnTemplateRows.value = tableColumnTemplateRowsFromSettings(DEFAULT_EDITOR_SETTINGS.tableColumnTemplateFields);
|
||||||
@@ -4390,119 +4418,6 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<Separator />
|
<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="space-y-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Label>{{ t("settings.toolbarTitle") }}</Label>
|
<Label>{{ t("settings.toolbarTitle") }}</Label>
|
||||||
@@ -4831,7 +4746,141 @@ onUnmounted(() => {
|
|||||||
{{ t("settings.tableOpenPageSizeDescription") }}
|
{{ t("settings.tableOpenPageSizeDescription") }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ import {
|
|||||||
replaceColumnValueFilterCondition,
|
replaceColumnValueFilterCondition,
|
||||||
} from "@/lib/dataGrid/dataGridColumnFilter";
|
} from "@/lib/dataGrid/dataGridColumnFilter";
|
||||||
import { normalizeResultPageSize, resultPageSizeMenuOptions } from "@/lib/dataGrid/paginationPageSize";
|
import { normalizeResultPageSize, resultPageSizeMenuOptions } from "@/lib/dataGrid/paginationPageSize";
|
||||||
|
import { continuousQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
|
||||||
import { allNullColumnIndexes } from "@/lib/dataGrid/dataGridColumnVisibility";
|
import { allNullColumnIndexes } from "@/lib/dataGrid/dataGridColumnVisibility";
|
||||||
import { buildDataGridColumnLookupItems, dataGridColumnCommentFor, filterDataGridColumnLookupItems } from "@/lib/dataGrid/dataGridColumnLookup";
|
import { buildDataGridColumnLookupItems, dataGridColumnCommentFor, filterDataGridColumnLookupItems } from "@/lib/dataGrid/dataGridColumnLookup";
|
||||||
import { uniqueDataGridColumnOrderKeys } from "@/lib/dataGrid/dataGridColumnOrder";
|
import { uniqueDataGridColumnOrderKeys } from "@/lib/dataGrid/dataGridColumnOrder";
|
||||||
@@ -528,7 +529,7 @@ const dataGridRenderMode = computed(() => settingsStore.editorSettings.dataGridR
|
|||||||
const dataGridSearchMode = computed(() => settingsStore.editorSettings.dataGridSearchMode);
|
const dataGridSearchMode = computed(() => settingsStore.editorSettings.dataGridSearchMode);
|
||||||
const compactDataGridToolbar = computed(() => isDataGridToolbarCompact(dataGridTopbarWidth.value, dataGridViewportWidth.value, DATA_GRID_CONDITION_TOOLBAR_MIN_WIDTH));
|
const compactDataGridToolbar = computed(() => isDataGridToolbarCompact(dataGridTopbarWidth.value, dataGridViewportWidth.value, DATA_GRID_CONDITION_TOOLBAR_MIN_WIDTH));
|
||||||
const infiniteScrollEnabled = computed(() => props.paginationEnabled && settingsStore.editorSettings.infiniteScroll);
|
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);
|
const expandedCellEditor = ref<{ rowId: number; col: number } | null>(null);
|
||||||
|
|
||||||
function headerColumnComment(column: string): string {
|
function headerColumnComment(column: string): string {
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ const mocks = vi.hoisted(() => ({
|
|||||||
updateRedisDbKeyStats: vi.fn(),
|
updateRedisDbKeyStats: vi.fn(),
|
||||||
redisScanPageSize: 100,
|
redisScanPageSize: 100,
|
||||||
infiniteScroll: false,
|
infiniteScroll: false,
|
||||||
infiniteScrollMaxRows: 5000,
|
queryResultMaxRowsEnabled: true,
|
||||||
|
queryResultMaxRows: 5000,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("@/lib/backend/api", () => ({
|
vi.mock("@/lib/backend/api", () => ({
|
||||||
@@ -66,7 +67,8 @@ vi.mock("@/stores/settingsStore", () => ({
|
|||||||
useSettingsStore: () => ({
|
useSettingsStore: () => ({
|
||||||
editorSettings: {
|
editorSettings: {
|
||||||
infiniteScroll: mocks.infiniteScroll,
|
infiniteScroll: mocks.infiniteScroll,
|
||||||
infiniteScrollMaxRows: mocks.infiniteScrollMaxRows,
|
queryResultMaxRowsEnabled: mocks.queryResultMaxRowsEnabled,
|
||||||
|
queryResultMaxRows: mocks.queryResultMaxRows,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
@@ -358,7 +360,8 @@ function resetApiMocks() {
|
|||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
mocks.redisScanPageSize = 100;
|
mocks.redisScanPageSize = 100;
|
||||||
mocks.infiniteScroll = false;
|
mocks.infiniteScroll = false;
|
||||||
mocks.infiniteScrollMaxRows = 5000;
|
mocks.queryResultMaxRowsEnabled = true;
|
||||||
|
mocks.queryResultMaxRows = 5000;
|
||||||
mocks.redisScanKeysBatch.mockResolvedValue({ cursor: 0, keys: [], total_keys: 0 });
|
mocks.redisScanKeysBatch.mockResolvedValue({ cursor: 0, keys: [], total_keys: 0 });
|
||||||
mocks.redisGetValue.mockImplementation((_connectionId: string, _db: number, keyRaw: string) => Promise.resolve(redisValue(keyRaw)));
|
mocks.redisGetValue.mockImplementation((_connectionId: string, _db: number, keyRaw: string) => Promise.resolve(redisValue(keyRaw)));
|
||||||
mocks.redisSetString.mockResolvedValue(undefined);
|
mocks.redisSetString.mockResolvedValue(undefined);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import type { RedisKeyInfo, RedisScanResult, RedisValue, HistoryEntry } from "@/
|
|||||||
import { uuid } from "@/lib/common/utils";
|
import { uuid } from "@/lib/common/utils";
|
||||||
import { useConnectionStore } from "@/stores/connectionStore";
|
import { useConnectionStore } from "@/stores/connectionStore";
|
||||||
import { useSettingsStore } from "@/stores/settingsStore";
|
import { useSettingsStore } from "@/stores/settingsStore";
|
||||||
|
import { continuousQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
|
||||||
import {
|
import {
|
||||||
appendRedisKeysToTreeIndex,
|
appendRedisKeysToTreeIndex,
|
||||||
canBuildRedisFuzzyTree,
|
canBuildRedisFuzzyTree,
|
||||||
@@ -171,7 +172,7 @@ const loadingEmptyText = computed(() => (isValueSearchMode.value && valueQuery.v
|
|||||||
const redisKeySeparator = computed(() => connectionStore.getConfig(props.connectionId)?.redis_key_separator ?? ":");
|
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 redisScanPageSize = computed(() => connectionStore.getConfig(props.connectionId)?.redis_scan_page_size ?? REDIS_SCAN_PAGE_SIZE_DEFAULT);
|
||||||
const redisInfiniteScrollEnabled = computed(() => settingsStore.editorSettings.infiniteScroll);
|
const redisInfiniteScrollEnabled = computed(() => settingsStore.editorSettings.infiniteScroll);
|
||||||
const redisInfiniteScrollMaxKeys = computed(() => settingsStore.editorSettings.infiniteScrollMaxRows);
|
const redisInfiniteScrollMaxKeys = computed(() => continuousQueryResultMaxRows(settingsStore.editorSettings.queryResultMaxRowsEnabled, settingsStore.editorSettings.queryResultMaxRows));
|
||||||
watch(redisKeySeparator, () => {
|
watch(redisKeySeparator, () => {
|
||||||
if (flatKeys.value.length === 0) return;
|
if (flatKeys.value.length === 0) return;
|
||||||
if (useFlatKeySearchRows.value) {
|
if (useFlatKeySearchRows.value) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { applyMongoFindSort } from "@/lib/mongo/mongoShellCommand";
|
|||||||
import { uuid } from "@/lib/common/utils";
|
import { uuid } from "@/lib/common/utils";
|
||||||
import { simpleDataGridOrderByReferencesMissingColumn, type DataGridSortMode } from "@/lib/dataGrid/dataGridSort";
|
import { simpleDataGridOrderByReferencesMissingColumn, type DataGridSortMode } from "@/lib/dataGrid/dataGridSort";
|
||||||
import type { DataGridReloadIntent } from "@/lib/dataGrid/dataGridToolbar";
|
import type { DataGridReloadIntent } from "@/lib/dataGrid/dataGridToolbar";
|
||||||
|
import { continuousQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
|
||||||
import { queryResultBaseSql, queryResultExecutionSql } from "@/lib/tabs/tabPresentation";
|
import { queryResultBaseSql, queryResultExecutionSql } from "@/lib/tabs/tabPresentation";
|
||||||
import { sqlExecutionTargetCapabilities } from "@/lib/database/sqlExecutionTargetCapabilities";
|
import { sqlExecutionTargetCapabilities } from "@/lib/database/sqlExecutionTargetCapabilities";
|
||||||
|
|
||||||
@@ -265,7 +266,13 @@ export function useDataGridActions(activeTab: ComputedRef<QueryTab | undefined>)
|
|||||||
const tab = activeTab.value;
|
const tab = activeTab.value;
|
||||||
if (!tab) return;
|
if (!tab) return;
|
||||||
const appendResult = settingsStore.editorSettings.infiniteScroll && offset > 0 && offset === tab.result?.rows.length;
|
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") {
|
if (tab.mode !== "data") {
|
||||||
const sortColumns = visibleQuerySortColumns(tab.result?.columns ?? [], tab.result?.hidden_column_indexes, tab.resultSortColumnIndex ?? -1);
|
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;
|
const hasDatabaseSort = !!tab.result?.hidden_column_indexes?.length && tab.resultSortMode === "database" && !!tab.resultSortDirection && !!tab.resultSortColumn && !!sortColumns;
|
||||||
|
|||||||
@@ -5397,6 +5397,13 @@ export default {
|
|||||||
dataGridDisplay: "Data grid display",
|
dataGridDisplay: "Data grid display",
|
||||||
tableOpenPageSize: "Default rows per page when opening tables",
|
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.",
|
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",
|
showColumnCommentsInHeader: "Show column comments under names",
|
||||||
showColumnCommentsInHeaderDescription: "Display table column comments directly below grid column names.",
|
showColumnCommentsInHeaderDescription: "Display table column comments directly below grid column names.",
|
||||||
showColumnTypesInHeader: "Show column types under names",
|
showColumnTypesInHeader: "Show column types under names",
|
||||||
@@ -5413,8 +5420,6 @@ export default {
|
|||||||
autoCalculateTotalRows: "Auto-calculate total row count",
|
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.",
|
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.",
|
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",
|
regexMaxMatchCount: "Maximum select-all matches",
|
||||||
regexMaxMatchCountDescription: "Limits the number of selections created by Select All Matches; search counting remains complete (100–10000).",
|
regexMaxMatchCountDescription: "Limits the number of selections created by Select All Matches; search counting remains complete (100–10000).",
|
||||||
tableColumnTemplateFields: "New Table Preset Fields",
|
tableColumnTemplateFields: "New Table Preset Fields",
|
||||||
|
|||||||
@@ -5157,6 +5157,13 @@ export default withEnglishFallback({
|
|||||||
dataGridDisplay: "Visualización de la tabla",
|
dataGridDisplay: "Visualización de la tabla",
|
||||||
tableOpenPageSize: "Filas predeterminadas por página al abrir tablas",
|
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.",
|
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",
|
showColumnCommentsInHeader: "Mostrar comentarios bajo los nombres",
|
||||||
showColumnCommentsInHeaderDescription: "Muestra los comentarios de columnas directamente debajo del nombre de la columna.",
|
showColumnCommentsInHeaderDescription: "Muestra los comentarios de columnas directamente debajo del nombre de la columna.",
|
||||||
showColumnTypesInHeader: "Mostrar tipos de columna bajo los nombres",
|
showColumnTypesInHeader: "Mostrar tipos de columna bajo los nombres",
|
||||||
@@ -5173,8 +5180,6 @@ export default withEnglishFallback({
|
|||||||
autoCalculateTotalRows: "Calcular automáticamente el total de filas",
|
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.",
|
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.",
|
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",
|
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).",
|
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",
|
tableColumnTemplateFields: "Campos predefinidos para tablas nuevas",
|
||||||
|
|||||||
@@ -5157,6 +5157,13 @@ export default withEnglishFallback({
|
|||||||
dataGridDisplay: "Visualizzazione griglia dati",
|
dataGridDisplay: "Visualizzazione griglia dati",
|
||||||
tableOpenPageSize: "Righe predefinite per pagina all'apertura delle tabelle",
|
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.",
|
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",
|
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.",
|
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",
|
showColumnTypesInHeader: "Mostra i tipi di colonna sotto i nomi",
|
||||||
@@ -5173,8 +5180,6 @@ export default withEnglishFallback({
|
|||||||
autoCalculateTotalRows: "Calcola automaticamente il totale delle righe",
|
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.",
|
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.",
|
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",
|
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).",
|
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",
|
tableColumnTemplateFields: "Campi predefiniti per nuove tabelle",
|
||||||
|
|||||||
@@ -5194,6 +5194,13 @@ export default withEnglishFallback({
|
|||||||
dataGridDisplay: "データグリッド表示",
|
dataGridDisplay: "データグリッド表示",
|
||||||
tableOpenPageSize: "テーブルを開くときのデフォルト行数",
|
tableOpenPageSize: "テーブルを開くときのデフォルト行数",
|
||||||
tableOpenPageSizeDescription: "新しいテーブルを開くときに 1 ページあたり読み込む行数です。テーブルで行数を変更すると、今後のテーブルタブのデフォルトも更新されます。",
|
tableOpenPageSizeDescription: "新しいテーブルを開くときに 1 ページあたり読み込む行数です。テーブルで行数を変更すると、今後のテーブルタブのデフォルトも更新されます。",
|
||||||
|
queryPageSize: "クエリ結果のデフォルト行数",
|
||||||
|
queryPageSizeDescription: "クエリ結果の 1 ページあたりの行数です。最大 {max} 行まで指定できます。",
|
||||||
|
queryResultMaxRows: "クエリ結果の最大行数",
|
||||||
|
queryResultMaxRowsEnabled: "クエリ結果の行数を制限",
|
||||||
|
queryResultMaxRowsDescription: "クエリ、テーブルデータ、ストアドプロシージャ、および連続読み込みで保持する合計行数を制限します。",
|
||||||
|
queryResultMaxRowsTooSmall: "クエリのページ行数({pageSize})以上にしてください。",
|
||||||
|
queryResultMaxRowsUnlimitedDescription: "合計行数または連続読み込み数を制限しません。大きな結果は大量のメモリを使用する可能性があります。",
|
||||||
showColumnCommentsInHeader: "列名の下にコメントを表示",
|
showColumnCommentsInHeader: "列名の下にコメントを表示",
|
||||||
showColumnCommentsInHeaderDescription: "グリッド列名の直下にテーブル列コメントを表示します。",
|
showColumnCommentsInHeaderDescription: "グリッド列名の直下にテーブル列コメントを表示します。",
|
||||||
showColumnTypesInHeader: "列名の下にデータ型を表示",
|
showColumnTypesInHeader: "列名の下にデータ型を表示",
|
||||||
@@ -5600,8 +5607,6 @@ export default withEnglishFallback({
|
|||||||
autoCalculateTotalRows: "総行数を自動計算",
|
autoCalculateTotalRows: "総行数を自動計算",
|
||||||
autoCalculateTotalRowsDescription: "クエリごとに COUNT(*) を自動実行し、一致する総行数を表示します。大きなクエリを高速に保つため既定はオフです。結果フッターから必要に応じて計算できます。",
|
autoCalculateTotalRowsDescription: "クエリごとに COUNT(*) を自動実行し、一致する総行数を表示します。大きなクエリを高速に保つため既定はオフです。結果フッターから必要に応じて計算できます。",
|
||||||
infiniteScrollDescription: "末尾までスクロールしたときに、次のデータを自動的に読み込みます。",
|
infiniteScrollDescription: "末尾までスクロールしたときに、次のデータを自動的に読み込みます。",
|
||||||
infiniteScrollMaxRows: "無限スクロールの最大項目数",
|
|
||||||
infiniteScrollMaxRowsDescription: "無限スクロールモードで自動読み込みする項目の最大数(1000〜50000)。",
|
|
||||||
regexMaxMatchCount: "すべての一致を選択する最大数",
|
regexMaxMatchCount: "すべての一致を選択する最大数",
|
||||||
regexMaxMatchCountDescription: "「すべての一致を選択」で一度に作成する選択範囲の数を制限します。検索の一致数は完全に集計されます(100〜10000)。",
|
regexMaxMatchCountDescription: "「すべての一致を選択」で一度に作成する選択範囲の数を制限します。検索の一致数は完全に集計されます(100〜10000)。",
|
||||||
exportRowLimitEnabled: "エクスポート行数を制限",
|
exportRowLimitEnabled: "エクスポート行数を制限",
|
||||||
|
|||||||
@@ -4896,6 +4896,13 @@ export default withEnglishFallback({
|
|||||||
dataGridDisplay: "데이터 그리드 표시",
|
dataGridDisplay: "데이터 그리드 표시",
|
||||||
tableOpenPageSize: "테이블을 열 때 기본 페이지당 행 수",
|
tableOpenPageSize: "테이블을 열 때 기본 페이지당 행 수",
|
||||||
tableOpenPageSizeDescription: "새 테이블을 열 때 페이지당 로드되는 행 수입니다. 테이블에서 페이지당 행을 변경하면 향후 테이블 탭의 기본값이 업데이트됩니다.",
|
tableOpenPageSizeDescription: "새 테이블을 열 때 페이지당 로드되는 행 수입니다. 테이블에서 페이지당 행을 변경하면 향후 테이블 탭의 기본값이 업데이트됩니다.",
|
||||||
|
queryPageSize: "쿼리 결과 기본 페이지당 행 수",
|
||||||
|
queryPageSizeDescription: "쿼리 결과 페이지당 로드할 행 수입니다. 최대 {max}행을 지원합니다.",
|
||||||
|
queryResultMaxRows: "쿼리 결과 최대 행 수",
|
||||||
|
queryResultMaxRowsEnabled: "쿼리 결과 행 수 제한",
|
||||||
|
queryResultMaxRowsDescription: "쿼리, 테이블 데이터, 저장 프로시저 및 연속 로딩에서 유지할 총 행 수를 제한합니다.",
|
||||||
|
queryResultMaxRowsTooSmall: "쿼리 페이지 크기({pageSize}) 이상이어야 합니다.",
|
||||||
|
queryResultMaxRowsUnlimitedDescription: "총 행 수 또는 연속 로딩 수를 제한하지 않습니다. 큰 결과는 많은 메모리를 사용할 수 있습니다.",
|
||||||
showColumnCommentsInHeader: "이름 아래에 컬럼 주석 표시",
|
showColumnCommentsInHeader: "이름 아래에 컬럼 주석 표시",
|
||||||
showColumnCommentsInHeaderDescription: "그리드 컬럼 이름 바로 아래에 테이블 컬럼 주석을 표시합니다.",
|
showColumnCommentsInHeaderDescription: "그리드 컬럼 이름 바로 아래에 테이블 컬럼 주석을 표시합니다.",
|
||||||
showColumnTypesInHeader: "이름 아래에 컬럼 타입 표시",
|
showColumnTypesInHeader: "이름 아래에 컬럼 타입 표시",
|
||||||
@@ -4912,8 +4919,6 @@ export default withEnglishFallback({
|
|||||||
autoCalculateTotalRows: "전체 행 수 자동 집계",
|
autoCalculateTotalRows: "전체 행 수 자동 집계",
|
||||||
autoCalculateTotalRowsDescription: "각 쿼리 후 자동으로 COUNT(*)를 실행하여 일치하는 전체 행 수를 표시합니다. 대규모 쿼리를 빠르게 유지하기 위해 기본적으로 꺼져 있으며, 결과 푸터에서 요청 시 집계할 수 있습니다.",
|
autoCalculateTotalRowsDescription: "각 쿼리 후 자동으로 COUNT(*)를 실행하여 일치하는 전체 행 수를 표시합니다. 대규모 쿼리를 빠르게 유지하기 위해 기본적으로 꺼져 있으며, 결과 푸터에서 요청 시 집계할 수 있습니다.",
|
||||||
infiniteScrollDescription: "목록 하단으로 스크롤할 때 다음 데이터 묶음을 자동으로 불러옵니다.",
|
infiniteScrollDescription: "목록 하단으로 스크롤할 때 다음 데이터 묶음을 자동으로 불러옵니다.",
|
||||||
infiniteScrollMaxRows: "무한 스크롤 최대 항목 수",
|
|
||||||
infiniteScrollMaxRowsDescription: "무한 스크롤 모드에서 자동으로 불러올 항목의 최대 수 (1000–50000).",
|
|
||||||
tableColumnTemplateFields: "새 테이블 미리 정의된 필드",
|
tableColumnTemplateFields: "새 테이블 미리 정의된 필드",
|
||||||
tableColumnTemplateFieldsDescription: "데이터베이스 유형을 선택한 다음 새 테이블을 만들 때 사용할 미리 정의된 필드 타입을 구성하세요.",
|
tableColumnTemplateFieldsDescription: "데이터베이스 유형을 선택한 다음 새 테이블을 만들 때 사용할 미리 정의된 필드 타입을 구성하세요.",
|
||||||
tableColumnTemplateAdd: "필드 추가",
|
tableColumnTemplateAdd: "필드 추가",
|
||||||
|
|||||||
@@ -5159,6 +5159,13 @@ export default withEnglishFallback({
|
|||||||
dataGridDisplay: "Exibição da grade de dados",
|
dataGridDisplay: "Exibição da grade de dados",
|
||||||
tableOpenPageSize: "Linhas padrão por página ao abrir tabelas",
|
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.",
|
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",
|
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.",
|
showColumnCommentsInHeaderDescription: "Exibir comentários de colunas da tabela diretamente abaixo dos nomes das colunas da grade.",
|
||||||
showColumnTypesInHeader: "Mostrar tipos de coluna sob os nomes",
|
showColumnTypesInHeader: "Mostrar tipos de coluna sob os nomes",
|
||||||
@@ -5175,8 +5182,6 @@ export default withEnglishFallback({
|
|||||||
autoCalculateTotalRows: "Calcular automaticamente o total de linhas",
|
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.",
|
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.",
|
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",
|
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).",
|
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",
|
tableColumnTemplateFields: "Campos predefinidos para novas tabelas",
|
||||||
|
|||||||
@@ -5319,6 +5319,13 @@ export default withEnglishFallback({
|
|||||||
dataGridDisplay: "数据表格显示",
|
dataGridDisplay: "数据表格显示",
|
||||||
tableOpenPageSize: "打开表默认每页行数",
|
tableOpenPageSize: "打开表默认每页行数",
|
||||||
tableOpenPageSizeDescription: "新打开数据表时每页加载的行数。在表数据页修改每页行数后,也会更新后续新建表标签页的默认值。",
|
tableOpenPageSizeDescription: "新打开数据表时每页加载的行数。在表数据页修改每页行数后,也会更新后续新建表标签页的默认值。",
|
||||||
|
queryPageSize: "查询结果默认每页行数",
|
||||||
|
queryPageSizeDescription: "每个查询结果分页加载的行数,最大支持 {max} 行。",
|
||||||
|
queryResultMaxRows: "查询结果最大行数",
|
||||||
|
queryResultMaxRowsEnabled: "限制查询结果行数",
|
||||||
|
queryResultMaxRowsDescription: "限制查询、表数据、存储过程及连续滚动累计保留的总行数。",
|
||||||
|
queryResultMaxRowsTooSmall: "不能小于查询结果默认每页行数({pageSize})。",
|
||||||
|
queryResultMaxRowsUnlimitedDescription: "不限制返回总行数或连续滚动累计数量,大结果集可能占用较多内存。",
|
||||||
showColumnCommentsInHeader: "在字段名下方显示注释",
|
showColumnCommentsInHeader: "在字段名下方显示注释",
|
||||||
showColumnCommentsInHeaderDescription: "把表字段注释直接显示在结果表头字段名下方。",
|
showColumnCommentsInHeaderDescription: "把表字段注释直接显示在结果表头字段名下方。",
|
||||||
showColumnTypesInHeader: "在字段名下方显示数据类型",
|
showColumnTypesInHeader: "在字段名下方显示数据类型",
|
||||||
@@ -5335,8 +5342,6 @@ export default withEnglishFallback({
|
|||||||
autoCalculateTotalRows: "自动统计总行数",
|
autoCalculateTotalRows: "自动统计总行数",
|
||||||
autoCalculateTotalRowsDescription: "每次查询后自动执行 COUNT(*) 显示匹配的总行数。默认关闭以保证大查询速度 —— 可在结果栏按需手动统计。",
|
autoCalculateTotalRowsDescription: "每次查询后自动执行 COUNT(*) 显示匹配的总行数。默认关闭以保证大查询速度 —— 可在结果栏按需手动统计。",
|
||||||
infiniteScrollDescription: "滚动到底部时自动加载下一批数据,无需手动翻页或加载。",
|
infiniteScrollDescription: "滚动到底部时自动加载下一批数据,无需手动翻页或加载。",
|
||||||
infiniteScrollMaxRows: "无限滚动最大条目数",
|
|
||||||
infiniteScrollMaxRowsDescription: "无限滚动模式下自动加载的最大条目数(1000–50000)。",
|
|
||||||
regexMaxMatchCount: "全部选中最大数量",
|
regexMaxMatchCount: "全部选中最大数量",
|
||||||
regexMaxMatchCountDescription: "限制“全部选中”一次创建的选区数量;搜索匹配仍完整统计(100–10000)。",
|
regexMaxMatchCountDescription: "限制“全部选中”一次创建的选区数量;搜索匹配仍完整统计(100–10000)。",
|
||||||
tableColumnTemplateFields: "新建表预设字段",
|
tableColumnTemplateFields: "新建表预设字段",
|
||||||
|
|||||||
@@ -4603,6 +4603,13 @@ export default withEnglishFallback({
|
|||||||
dataGridDisplay: "資料表格顯示",
|
dataGridDisplay: "資料表格顯示",
|
||||||
tableOpenPageSize: "開啟資料表預設每頁列數",
|
tableOpenPageSize: "開啟資料表預設每頁列數",
|
||||||
tableOpenPageSizeDescription: "新開啟資料表時每頁載入的列數。在資料表頁面調整每頁列數後,也會更新後續新建資料表分頁的預設值。",
|
tableOpenPageSizeDescription: "新開啟資料表時每頁載入的列數。在資料表頁面調整每頁列數後,也會更新後續新建資料表分頁的預設值。",
|
||||||
|
queryPageSize: "查詢結果預設每頁列數",
|
||||||
|
queryPageSizeDescription: "每個查詢結果分頁載入的列數,最多支援 {max} 列。",
|
||||||
|
queryResultMaxRows: "查詢結果最大列數",
|
||||||
|
queryResultMaxRowsEnabled: "限制查詢結果列數",
|
||||||
|
queryResultMaxRowsDescription: "限制查詢、資料表資料、預存程序及連續捲動累計保留的總列數。",
|
||||||
|
queryResultMaxRowsTooSmall: "不能小於查詢結果預設每頁列數({pageSize})。",
|
||||||
|
queryResultMaxRowsUnlimitedDescription: "不限制傳回總列數或連續捲動累計數量,大型結果集可能占用大量記憶體。",
|
||||||
showColumnCommentsInHeader: "在欄位名稱下方顯示註解",
|
showColumnCommentsInHeader: "在欄位名稱下方顯示註解",
|
||||||
showColumnCommentsInHeaderDescription: "直接在資料表格欄位名稱下方顯示資料表欄位註解。",
|
showColumnCommentsInHeaderDescription: "直接在資料表格欄位名稱下方顯示資料表欄位註解。",
|
||||||
showColumnTypesInHeader: "在欄位名稱下方顯示資料類型",
|
showColumnTypesInHeader: "在欄位名稱下方顯示資料類型",
|
||||||
@@ -4619,8 +4626,6 @@ export default withEnglishFallback({
|
|||||||
autoCalculateTotalRows: "自動統計總筆數",
|
autoCalculateTotalRows: "自動統計總筆數",
|
||||||
autoCalculateTotalRowsDescription: "每次查詢後自動執行 COUNT(*) 顯示符合的總筆數。預設關閉以確保大型查詢速度 —— 可在結果列按需手動統計。",
|
autoCalculateTotalRowsDescription: "每次查詢後自動執行 COUNT(*) 顯示符合的總筆數。預設關閉以確保大型查詢速度 —— 可在結果列按需手動統計。",
|
||||||
infiniteScrollDescription: "滾動到底部時自動載入下一批資料,無需手動翻頁或載入。",
|
infiniteScrollDescription: "滾動到底部時自動載入下一批資料,無需手動翻頁或載入。",
|
||||||
infiniteScrollMaxRows: "無限滾動最大項目數",
|
|
||||||
infiniteScrollMaxRowsDescription: "無限滾動模式下自動載入的最大項目數(1000–50000)。",
|
|
||||||
regexMaxMatchCount: "全部選取最大數量",
|
regexMaxMatchCount: "全部選取最大數量",
|
||||||
regexMaxMatchCountDescription: "限制「全部選取」一次建立的選取範圍數量;搜尋符合結果仍會完整統計(100–10000)。",
|
regexMaxMatchCountDescription: "限制「全部選取」一次建立的選取範圍數量;搜尋符合結果仍會完整統計(100–10000)。",
|
||||||
tableColumnTemplateFields: "新建表預設欄位",
|
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 RESULT_PAGE_SIZE_OPTIONS = [50, 100, 500, 1000];
|
||||||
export const DEFAULT_RESULT_PAGE_SIZE = 100;
|
export const DEFAULT_RESULT_PAGE_SIZE = 100;
|
||||||
export const MIN_RESULT_PAGE_SIZE = 1;
|
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 {
|
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;
|
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 { 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";
|
import type { EditorSettings } from "@/stores/settingsStore";
|
||||||
|
|
||||||
function makeSettings(overrides: Partial<EditorSettings> = {}): EditorSettings {
|
function makeSettings(overrides: Partial<EditorSettings> = {}): EditorSettings {
|
||||||
@@ -7,6 +7,8 @@ function makeSettings(overrides: Partial<EditorSettings> = {}): EditorSettings {
|
|||||||
autoCalculateTotalRows: false,
|
autoCalculateTotalRows: false,
|
||||||
pageSize: 100,
|
pageSize: 100,
|
||||||
tableOpenPageSize: 100,
|
tableOpenPageSize: 100,
|
||||||
|
queryResultMaxRowsEnabled: true,
|
||||||
|
queryResultMaxRows: 100000,
|
||||||
sqlEngine: "desktop",
|
sqlEngine: "desktop",
|
||||||
tabSize: 2,
|
tabSize: 2,
|
||||||
keywordCase: "upper",
|
keywordCase: "upper",
|
||||||
@@ -35,7 +37,10 @@ describe("EDITOR_SETTINGS_DRAFT_KEYS", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("includes the table-open page size", () => {
|
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("tableOpenPageSize");
|
||||||
|
expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("queryResultMaxRowsEnabled");
|
||||||
|
expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("queryResultMaxRows");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("includes the saved SQL open target mode", () => {
|
it("includes the saved SQL open target mode", () => {
|
||||||
@@ -89,7 +94,8 @@ describe("editorSettingsDraftFromSettings", () => {
|
|||||||
|
|
||||||
describe("normalizeTableOpenPageSizeDraft", () => {
|
describe("normalizeTableOpenPageSizeDraft", () => {
|
||||||
it.each([
|
it.each([
|
||||||
[200000, 100000],
|
[200000, 200000],
|
||||||
|
[2000000, 1000000],
|
||||||
[0, 100],
|
[0, 100],
|
||||||
[-1, 100],
|
[-1, 100],
|
||||||
["123.9", 123],
|
["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", () => {
|
describe("editorSettingsDraftChanged", () => {
|
||||||
it("detects change in continueOnErrorOnBatch", () => {
|
it("detects change in continueOnErrorOnBatch", () => {
|
||||||
const settings = makeSettings({ continueOnErrorOnBatch: false });
|
const settings = makeSettings({ continueOnErrorOnBatch: false });
|
||||||
@@ -181,7 +198,7 @@ describe("editorSettingsPatchFromDraft", () => {
|
|||||||
const draft = editorSettingsDraftFromSettings(settings);
|
const draft = editorSettingsDraftFromSettings(settings);
|
||||||
const base = editorSettingsDraftFromSettings(settings);
|
const base = editorSettingsDraftFromSettings(settings);
|
||||||
draft.tableOpenPageSize = 200000.9;
|
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", () => {
|
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.savedSqlOpenTarget", category: "editor", targetId: "editor" },
|
||||||
{ titleKey: "settings.confirmDangerousSqlExecution", category: "editor", targetId: "editor" },
|
{ titleKey: "settings.confirmDangerousSqlExecution", category: "editor", targetId: "editor" },
|
||||||
{ titleKey: "settings.continueOnErrorOnBatch", 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: "transfer.dataTransfer", category: "appearance", targetId: "appearance" },
|
||||||
{ titleKey: "toolbar.driverManager", category: "appearance", targetId: "appearance" },
|
{ titleKey: "toolbar.driverManager", category: "appearance", targetId: "appearance" },
|
||||||
{ titleKey: "toolbar.theme", 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.sqlFormatterParamTypes", category: "formatter", targetId: "formatter" },
|
||||||
{ titleKey: "settings.routineSourceOpenMode", category: "navigation", targetId: "navigation" },
|
{ titleKey: "settings.routineSourceOpenMode", category: "navigation", targetId: "navigation" },
|
||||||
{ titleKey: "settings.disconnectTabHandlingMode", category: "navigation", targetId: "navigation" },
|
{ titleKey: "settings.disconnectTabHandlingMode", category: "navigation", targetId: "navigation" },
|
||||||
{ titleKey: "settings.compactColumnHeaderActions", category: "appearance", targetId: "appearance" },
|
{ titleKey: "settings.compactColumnHeaderActions", category: "data", targetId: "data" },
|
||||||
{ titleKey: "settings.infiniteScrollMaxRows", category: "appearance", targetId: "appearance" },
|
{ titleKey: "settings.infiniteScroll", category: "data", targetId: "data" },
|
||||||
{ titleKey: "settings.globalDateTimeDisplayFormat", category: "data", targetId: "data" },
|
{ titleKey: "settings.globalDateTimeDisplayFormat", category: "data", targetId: "data" },
|
||||||
{ titleKey: "settings.globalDateTimeExportFormat", category: "data", targetId: "data" },
|
{ titleKey: "settings.globalDateTimeExportFormat", category: "data", targetId: "data" },
|
||||||
{ titleKey: "settings.globalDateTimeImportFormat", category: "data", targetId: "data" },
|
{ titleKey: "settings.globalDateTimeImportFormat", category: "data", targetId: "data" },
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { EditorSettings } from "@/stores/settingsStore";
|
import type { EditorSettings } from "@/stores/settingsStore";
|
||||||
import { normalizeResultPageSize } from "@/lib/dataGrid/paginationPageSize";
|
import { normalizeResultPageSize } from "@/lib/dataGrid/paginationPageSize";
|
||||||
|
import { normalizeQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
|
||||||
import { normalizeCompletionTriggerMode } from "@/lib/sql/sqlCompletionTriggerPolicy";
|
import { normalizeCompletionTriggerMode } from "@/lib/sql/sqlCompletionTriggerPolicy";
|
||||||
|
|
||||||
export const EDITOR_SETTINGS_DRAFT_KEYS = [
|
export const EDITOR_SETTINGS_DRAFT_KEYS = [
|
||||||
@@ -36,9 +37,11 @@ export const EDITOR_SETTINGS_DRAFT_KEYS = [
|
|||||||
"compactColumnHeaderActions",
|
"compactColumnHeaderActions",
|
||||||
"dataGridQuickEntry",
|
"dataGridQuickEntry",
|
||||||
"dataGridAutoTransposeSingleRow",
|
"dataGridAutoTransposeSingleRow",
|
||||||
|
"pageSize",
|
||||||
"tableOpenPageSize",
|
"tableOpenPageSize",
|
||||||
|
"queryResultMaxRowsEnabled",
|
||||||
|
"queryResultMaxRows",
|
||||||
"infiniteScroll",
|
"infiniteScroll",
|
||||||
"infiniteScrollMaxRows",
|
|
||||||
"regexMaxMatchCount",
|
"regexMaxMatchCount",
|
||||||
"autoCalculateTotalRows",
|
"autoCalculateTotalRows",
|
||||||
"tableColumnTemplateFields",
|
"tableColumnTemplateFields",
|
||||||
@@ -86,8 +89,13 @@ export function normalizeTableOpenPageSizeDraft(value: unknown): number {
|
|||||||
return normalizeResultPageSize(value);
|
return normalizeResultPageSize(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeQueryResultMaxRowsDraft(value: unknown): number {
|
||||||
|
return normalizeQueryResultMaxRows(value);
|
||||||
|
}
|
||||||
|
|
||||||
function normalizedDraftValue(key: EditorSettingsDraftKey, value: unknown): unknown {
|
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);
|
if (key === "completionTriggerMode") return normalizeCompletionTriggerMode(value);
|
||||||
return 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-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-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: "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: "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: "query-result-max-rows", category: "data", titleKey: "settings.queryResultMaxRows", descriptionKey: "settings.queryResultMaxRowsDescription", targetId: "data" },
|
||||||
{ id: "appearance-header-types", category: "appearance", titleKey: "settings.showColumnTypesInHeader", descriptionKey: "settings.showColumnTypesInHeaderDescription", targetId: "appearance" },
|
{ id: "data-grid-header-comments", category: "data", titleKey: "settings.showColumnCommentsInHeader", descriptionKey: "settings.showColumnCommentsInHeaderDescription", targetId: "data" },
|
||||||
{ id: "appearance-index-indicators", category: "appearance", titleKey: "settings.showIndexIndicatorsInHeader", descriptionKey: "settings.showIndexIndicatorsInHeaderDescription", targetId: "appearance" },
|
{ id: "data-grid-header-types", category: "data", titleKey: "settings.showColumnTypesInHeader", descriptionKey: "settings.showColumnTypesInHeaderDescription", targetId: "data" },
|
||||||
{ id: "appearance-compact-header-actions", category: "appearance", titleKey: "settings.compactColumnHeaderActions", descriptionKey: "settings.compactColumnHeaderActionsDescription", targetId: "appearance" },
|
{ id: "data-grid-index-indicators", category: "data", titleKey: "settings.showIndexIndicatorsInHeader", descriptionKey: "settings.showIndexIndicatorsInHeaderDescription", targetId: "data" },
|
||||||
{ id: "appearance-auto-total", category: "appearance", titleKey: "settings.autoCalculateTotalRows", descriptionKey: "settings.autoCalculateTotalRowsDescription", targetId: "appearance" },
|
{ id: "data-grid-compact-header-actions", category: "data", titleKey: "settings.compactColumnHeaderActions", descriptionKey: "settings.compactColumnHeaderActionsDescription", targetId: "data" },
|
||||||
{ id: "appearance-infinite-scroll", category: "appearance", titleKey: "settings.infiniteScroll", descriptionKey: "settings.infiniteScrollDescription", targetId: "appearance" },
|
{ id: "data-grid-auto-total", category: "data", titleKey: "settings.autoCalculateTotalRows", descriptionKey: "settings.autoCalculateTotalRowsDescription", targetId: "data" },
|
||||||
{ id: "appearance-infinite-scroll-limit", category: "appearance", titleKey: "settings.infiniteScrollMaxRows", descriptionKey: "settings.infiniteScrollMaxRowsDescription", targetId: "appearance" },
|
{ id: "data-grid-infinite-scroll", category: "data", titleKey: "settings.infiniteScroll", descriptionKey: "settings.infiniteScrollDescription", targetId: "data" },
|
||||||
{ id: "appearance-auto-transpose", category: "appearance", titleKey: "settings.dataGridAutoTransposeSingleRow", descriptionKey: "settings.dataGridAutoTransposeSingleRowDescription", targetId: "appearance" },
|
{ id: "data-grid-auto-transpose", category: "data", titleKey: "settings.dataGridAutoTransposeSingleRow", descriptionKey: "settings.dataGridAutoTransposeSingleRowDescription", targetId: "data" },
|
||||||
{ id: "appearance-quick-entry", category: "appearance", titleKey: "settings.dataGridQuickEntry", descriptionKey: "settings.dataGridQuickEntryDescription", targetId: "appearance" },
|
{ 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-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" },
|
{ id: "appearance-exclusive-sidebar-panels", category: "appearance", titleKey: "settings.exclusiveRightSidebarPanels", descriptionKey: "settings.exclusiveRightSidebarPanelsDescription", targetId: "appearance" },
|
||||||
...createToolbarVisibilitySettingsSearchDefinitions(),
|
...createToolbarVisibilitySettingsSearchDefinitions(),
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ import { frontendQueryTimeoutDelayMs, frontendQueryTimeoutSecsForSql, queryTimeo
|
|||||||
import { queryResultNameFromPreamble, queryResultSourceLabel } from "@/lib/sql/queryResultSource";
|
import { queryResultNameFromPreamble, queryResultSourceLabel } from "@/lib/sql/queryResultSource";
|
||||||
import { beginDataGridNativeSelectionBlock, finishDataGridNativeSelectionBlock } from "@/lib/dataGrid/dataGridNativeSelection";
|
import { beginDataGridNativeSelectionBlock, finishDataGridNativeSelectionBlock } from "@/lib/dataGrid/dataGridNativeSelection";
|
||||||
import { simpleDataGridOrderByReferencesMissingColumn, sortDataGridRowIndexes, type DataGridSortDirection } from "@/lib/dataGrid/dataGridSort";
|
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 { elasticsearchRestRequestRanges, executableStatementRanges, splitSqlStatementRanges } from "@/lib/sql/sqlStatementRanges";
|
||||||
import { externalSqlFileDisplayTitles, normalizeExternalSqlPath } from "@/lib/sql/sqlFileOpen";
|
import { externalSqlFileDisplayTitles, normalizeExternalSqlPath } from "@/lib/sql/sqlFileOpen";
|
||||||
import { clearDataGridPendingSnapshotsForTab } from "@/composables/useDataGridEditor";
|
import { clearDataGridPendingSnapshotsForTab } from "@/composables/useDataGridEditor";
|
||||||
@@ -3610,6 +3611,7 @@ export const useQueryStore = defineStore("query", () => {
|
|||||||
traceId: string;
|
traceId: string;
|
||||||
elapsed: () => string;
|
elapsed: () => string;
|
||||||
timeoutSecs: number;
|
timeoutSecs: number;
|
||||||
|
maxRows?: number;
|
||||||
}) {
|
}) {
|
||||||
const resultRowCount = options.result.rows.length;
|
const resultRowCount = options.result.rows.length;
|
||||||
if (resultRowCount <= 0) {
|
if (resultRowCount <= 0) {
|
||||||
@@ -3652,10 +3654,11 @@ export const useQueryStore = defineStore("query", () => {
|
|||||||
setQueryTotalRowCountIfCurrent(options.tabId, options.executionId, options.result, undefined);
|
setQueryTotalRowCountIfCurrent(options.tabId, options.executionId, options.result, undefined);
|
||||||
return;
|
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", {
|
queryExecutionLog("info", "count:done", {
|
||||||
traceId: options.traceId,
|
traceId: options.traceId,
|
||||||
total,
|
total: cappedTotal,
|
||||||
elapsed: options.elapsed(),
|
elapsed: options.elapsed(),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -4284,6 +4287,8 @@ export const useQueryStore = defineStore("query", () => {
|
|||||||
return producedResult;
|
return producedResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const queryResultMaxRows = effectiveQueryResultMaxRows(settingsStore.editorSettings.queryResultMaxRowsEnabled, settingsStore.editorSettings.queryResultMaxRows);
|
||||||
|
|
||||||
if (tab.mode === "query") {
|
if (tab.mode === "query") {
|
||||||
const prepared = await prepareEditableQueryExecution(tab, sqlToExecute, conn, effectiveDbType, executionDatabase, traceId, elapsed);
|
const prepared = await prepareEditableQueryExecution(tab, sqlToExecute, conn, effectiveDbType, executionDatabase, traceId, elapsed);
|
||||||
sqlToExecute = prepared.sql;
|
sqlToExecute = prepared.sql;
|
||||||
@@ -4305,7 +4310,7 @@ export const useQueryStore = defineStore("query", () => {
|
|||||||
sqlToExecute = sorted.sql;
|
sqlToExecute = sorted.sql;
|
||||||
resultSortedSql = 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({
|
const plan = await api.prepareQueryPaginationExecutionPlan({
|
||||||
sql: sqlToExecute,
|
sql: sqlToExecute,
|
||||||
queryBaseSql,
|
queryBaseSql,
|
||||||
@@ -4336,8 +4341,15 @@ export const useQueryStore = defineStore("query", () => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (tab.mode === "data") {
|
} else if (tab.mode === "data") {
|
||||||
pageLimit = options?.pagination?.limit ?? tableOpenPageLimit(settingsStore.editorSettings.tableOpenPageSize);
|
const pagination = limitQueryPagination(
|
||||||
pageOffset = options?.pagination?.offset ?? 0;
|
{
|
||||||
|
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");
|
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() });
|
queryExecutionLog("info", "execute-in-txn:invoke", { traceId, txnSessionId: tab.txnSessionId, elapsed: elapsed() });
|
||||||
executionDispatched = true;
|
executionDispatched = true;
|
||||||
executionPromise = api.executeInManualTransaction(tab.txnSessionId, sqlToExecute, executionDatabase, executionSchema, pageLimit);
|
executionPromise = api.executeInManualTransaction(tab.txnSessionId, sqlToExecute, executionDatabase, executionSchema, pageLimit ?? agentProtocolQueryResultMaxRows(queryResultMaxRows));
|
||||||
} else {
|
} else {
|
||||||
queryExecutionLog("info", "execute-multi:start", { traceId, elapsed: elapsed() });
|
queryExecutionLog("info", "execute-multi:start", { traceId, elapsed: elapsed() });
|
||||||
// Query and data tabs use a tab-scoped pool so repeated executions keep
|
// Query and data tabs use a tab-scoped pool so repeated executions keep
|
||||||
@@ -4364,14 +4376,13 @@ export const useQueryStore = defineStore("query", () => {
|
|||||||
? useAgentResultSession
|
? useAgentResultSession
|
||||||
? {
|
? {
|
||||||
// Agent cursors apply maxRows cumulatively across fetched pages.
|
// Agent cursors apply maxRows cumulatively across fetched pages.
|
||||||
// Keep multi-page navigation available without allowing unbounded reads.
|
maxRows: agentProtocolQueryResultMaxRows(queryResultMaxRows),
|
||||||
maxRows: MAX_RESULT_PAGE_SIZE,
|
|
||||||
fetchSize: pageLimit,
|
fetchSize: pageLimit,
|
||||||
pageSize: pageLimit,
|
pageSize: pageLimit,
|
||||||
resultSessionId: options?.pagination?.sessionId,
|
resultSessionId: options?.pagination?.sessionId,
|
||||||
}
|
}
|
||||||
: { maxRows: pageLimit, fetchSize: pageLimit }
|
: { maxRows: pageLimit, fetchSize: pageLimit }
|
||||||
: {}),
|
: { maxRows: agentProtocolQueryResultMaxRows(queryResultMaxRows) }),
|
||||||
...(executionClientSessionId ? { clientSessionId: executionClientSessionId } : {}),
|
...(executionClientSessionId ? { clientSessionId: executionClientSessionId } : {}),
|
||||||
timeoutSecs: queryTimeoutSecs,
|
timeoutSecs: queryTimeoutSecs,
|
||||||
catalog: executionCatalog,
|
catalog: executionCatalog,
|
||||||
@@ -4497,6 +4508,12 @@ export const useQueryStore = defineStore("query", () => {
|
|||||||
current.resultTotalRowCount = undefined;
|
current.resultTotalRowCount = undefined;
|
||||||
}
|
}
|
||||||
const resultRowCount = current.result?.rows.length ?? 0;
|
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 totalKnownFromIncompletePage = !!current.result && typeof exactTotalFromIncompletePage(current.result, pageLimit, pageOffset, useAgentResultSession) === "number";
|
||||||
const dataCountTarget =
|
const dataCountTarget =
|
||||||
current.mode === "data"
|
current.mode === "data"
|
||||||
@@ -4514,7 +4531,8 @@ export const useQueryStore = defineStore("query", () => {
|
|||||||
};
|
};
|
||||||
})()
|
})()
|
||||||
: undefined;
|
: 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;
|
current.resultTotalRowCountLoading = canAutoCalculateTotalRows;
|
||||||
// Server-side pagination without a countSql: the backend (currently
|
// Server-side pagination without a countSql: the backend (currently
|
||||||
// the Elasticsearch driver) already reports the true match total via
|
// the Elasticsearch driver) already reports the true match total via
|
||||||
@@ -4528,7 +4546,7 @@ export const useQueryStore = defineStore("query", () => {
|
|||||||
}
|
}
|
||||||
touchResult(current);
|
touchResult(current);
|
||||||
syncDisplayedResultRun(current, queryBaseSql, captureResultRun);
|
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({
|
countQueryTotalRowsInBackground({
|
||||||
tabId: id,
|
tabId: id,
|
||||||
connectionId: executionConnectionId,
|
connectionId: executionConnectionId,
|
||||||
@@ -4550,6 +4568,7 @@ export const useQueryStore = defineStore("query", () => {
|
|||||||
traceId,
|
traceId,
|
||||||
elapsed,
|
elapsed,
|
||||||
timeoutSecs: queryTimeoutSecs,
|
timeoutSecs: queryTimeoutSecs,
|
||||||
|
maxRows: queryResultMaxRows,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
queryExecutionLog("info", "result:assigned", {
|
queryExecutionLog("info", "result:assigned", {
|
||||||
|
|||||||
@@ -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 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 { type DataGridCopyPreference, type DataGridExtractorOptions, DEFAULT_DATA_GRID_EXTRACTOR_OPTIONS, normalizeDataGridCopyPreference, normalizeDataGridExtractorOptions } from "@/lib/dataGrid/dataGridCopyExtractor";
|
||||||
import { normalizeResultPageSize } from "@/lib/dataGrid/paginationPageSize";
|
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 { normalizeShortcutSettings, type ShortcutSettings } from "@/lib/editor/shortcutRegistry";
|
||||||
import type { SavedSqlOpenTargetMode } from "@/lib/savedSql/savedSqlExecutionTarget";
|
import type { SavedSqlOpenTargetMode } from "@/lib/savedSql/savedSqlExecutionTarget";
|
||||||
import type { ConnectionListSortMode } from "@/lib/sidebar/connectionListSort";
|
import type { ConnectionListSortMode } from "@/lib/sidebar/connectionListSort";
|
||||||
@@ -515,7 +516,10 @@ export interface EditorSettings {
|
|||||||
appLayout: "separated" | "classic";
|
appLayout: "separated" | "classic";
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
tableOpenPageSize: number;
|
tableOpenPageSize: number;
|
||||||
|
queryResultMaxRowsEnabled: boolean;
|
||||||
|
queryResultMaxRows: number;
|
||||||
infiniteScroll: boolean;
|
infiniteScroll: boolean;
|
||||||
|
/** Preserved for downgrade compatibility; current clients use queryResultMaxRows. */
|
||||||
infiniteScrollMaxRows: number;
|
infiniteScrollMaxRows: number;
|
||||||
regexMaxMatchCount: number;
|
regexMaxMatchCount: number;
|
||||||
autoCalculateTotalRows: boolean;
|
autoCalculateTotalRows: boolean;
|
||||||
@@ -702,6 +706,8 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
|||||||
appLayout: "classic",
|
appLayout: "classic",
|
||||||
pageSize: 100,
|
pageSize: 100,
|
||||||
tableOpenPageSize: 100,
|
tableOpenPageSize: 100,
|
||||||
|
queryResultMaxRowsEnabled: true,
|
||||||
|
queryResultMaxRows: DEFAULT_QUERY_RESULT_MAX_ROWS,
|
||||||
infiniteScroll: false,
|
infiniteScroll: false,
|
||||||
infiniteScrollMaxRows: 5000,
|
infiniteScrollMaxRows: 5000,
|
||||||
regexMaxMatchCount: 1000,
|
regexMaxMatchCount: 1000,
|
||||||
@@ -1036,6 +1042,8 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
|
|||||||
appLayout: settings.appLayout ?? DEFAULT_EDITOR_SETTINGS.appLayout,
|
appLayout: settings.appLayout ?? DEFAULT_EDITOR_SETTINGS.appLayout,
|
||||||
pageSize: normalizeResultPageSize(settings.pageSize),
|
pageSize: normalizeResultPageSize(settings.pageSize),
|
||||||
tableOpenPageSize: normalizeResultPageSize(settings.tableOpenPageSize, DEFAULT_EDITOR_SETTINGS.tableOpenPageSize),
|
tableOpenPageSize: normalizeResultPageSize(settings.tableOpenPageSize, DEFAULT_EDITOR_SETTINGS.tableOpenPageSize),
|
||||||
|
queryResultMaxRowsEnabled: settings.queryResultMaxRowsEnabled !== false,
|
||||||
|
queryResultMaxRows: normalizeQueryResultMaxRows(settings.queryResultMaxRows),
|
||||||
infiniteScroll: settings.infiniteScroll ?? DEFAULT_EDITOR_SETTINGS.infiniteScroll,
|
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,
|
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,
|
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.appLayout !== undefined) editorSettings.value.appLayout = partial.appLayout;
|
||||||
if (partial.pageSize !== undefined) editorSettings.value.pageSize = normalizeResultPageSize(partial.pageSize);
|
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.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.infiniteScroll !== undefined) editorSettings.value.infiniteScroll = partial.infiniteScroll;
|
||||||
if (partial.infiniteScrollMaxRows !== undefined)
|
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;
|
editorSettings.value.infiniteScrollMaxRows = typeof partial.infiniteScrollMaxRows === "number" && partial.infiniteScrollMaxRows >= 1000 && partial.infiniteScrollMaxRows <= 50000 ? Math.round(partial.infiniteScrollMaxRows) : DEFAULT_EDITOR_SETTINGS.infiniteScrollMaxRows;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ use crate::sql_risk::{classify_sql_risk_for_database, SqlRisk};
|
|||||||
|
|
||||||
pub const QUERY_TIMEOUT: Duration = Duration::from_secs(30);
|
pub const QUERY_TIMEOUT: Duration = Duration::from_secs(30);
|
||||||
pub const MAX_ROWS: usize = 10000;
|
pub const MAX_ROWS: usize = 10000;
|
||||||
|
pub const AGENT_PROTOCOL_MAX_ROWS: usize = i32::MAX as usize;
|
||||||
pub const QUERY_CANCELED: &str = "Query canceled";
|
pub const QUERY_CANCELED: &str = "Query canceled";
|
||||||
/// Fallback when a Mongo connection hits the generic SQL executor instead of the shell path.
|
/// Fallback when a Mongo connection hits the generic SQL executor instead of the shell path.
|
||||||
/// Wording must match packages/mongo-shell `MONGO_SHELL_COMMAND_HINT`
|
/// Wording must match packages/mongo-shell `MONGO_SHELL_COMMAND_HINT`
|
||||||
@@ -659,7 +660,7 @@ pub fn agent_execute_query_params(
|
|||||||
) -> serde_json::Value {
|
) -> serde_json::Value {
|
||||||
let mut params = serde_json::json!({
|
let mut params = serde_json::json!({
|
||||||
"sql": sql,
|
"sql": sql,
|
||||||
"maxRows": options.max_rows.unwrap_or(MAX_ROWS),
|
"maxRows": agent_protocol_row_count(options.max_rows.unwrap_or(MAX_ROWS)),
|
||||||
});
|
});
|
||||||
if let Some(database) = database.map(str::trim).filter(|database| !database.is_empty()) {
|
if let Some(database) = database.map(str::trim).filter(|database| !database.is_empty()) {
|
||||||
params["database"] = serde_json::json!(database);
|
params["database"] = serde_json::json!(database);
|
||||||
@@ -668,7 +669,7 @@ pub fn agent_execute_query_params(
|
|||||||
params["schema"] = serde_json::json!(schema);
|
params["schema"] = serde_json::json!(schema);
|
||||||
}
|
}
|
||||||
if let Some(fetch_size) = options.fetch_size {
|
if let Some(fetch_size) = options.fetch_size {
|
||||||
params["fetchSize"] = serde_json::json!(fetch_size);
|
params["fetchSize"] = serde_json::json!(agent_protocol_row_count(fetch_size));
|
||||||
}
|
}
|
||||||
if let Some(timeout_secs) = options.timeout_secs {
|
if let Some(timeout_secs) = options.timeout_secs {
|
||||||
params["timeoutSecs"] = serde_json::json!(timeout_secs);
|
params["timeoutSecs"] = serde_json::json!(timeout_secs);
|
||||||
@@ -684,8 +685,8 @@ pub fn agent_execute_query_page_params(
|
|||||||
) -> serde_json::Value {
|
) -> serde_json::Value {
|
||||||
let mut params = serde_json::json!({
|
let mut params = serde_json::json!({
|
||||||
"sql": sql,
|
"sql": sql,
|
||||||
"pageSize": options.page_size.unwrap_or(MAX_ROWS),
|
"pageSize": agent_protocol_row_count(options.page_size.unwrap_or(MAX_ROWS)),
|
||||||
"maxRows": options.max_rows.unwrap_or(MAX_ROWS),
|
"maxRows": agent_protocol_row_count(options.max_rows.unwrap_or(MAX_ROWS)),
|
||||||
});
|
});
|
||||||
if let Some(database) = database.map(str::trim).filter(|database| !database.is_empty()) {
|
if let Some(database) = database.map(str::trim).filter(|database| !database.is_empty()) {
|
||||||
params["database"] = serde_json::json!(database);
|
params["database"] = serde_json::json!(database);
|
||||||
@@ -694,7 +695,7 @@ pub fn agent_execute_query_page_params(
|
|||||||
params["schema"] = serde_json::json!(schema);
|
params["schema"] = serde_json::json!(schema);
|
||||||
}
|
}
|
||||||
if let Some(fetch_size) = options.fetch_size {
|
if let Some(fetch_size) = options.fetch_size {
|
||||||
params["fetchSize"] = serde_json::json!(fetch_size);
|
params["fetchSize"] = serde_json::json!(agent_protocol_row_count(fetch_size));
|
||||||
}
|
}
|
||||||
if let Some(timeout_secs) = options.timeout_secs {
|
if let Some(timeout_secs) = options.timeout_secs {
|
||||||
params["timeoutSecs"] = serde_json::json!(timeout_secs);
|
params["timeoutSecs"] = serde_json::json!(timeout_secs);
|
||||||
@@ -705,10 +706,14 @@ pub fn agent_execute_query_page_params(
|
|||||||
pub fn agent_fetch_query_page_params(session_id: &str, page_size: usize) -> serde_json::Value {
|
pub fn agent_fetch_query_page_params(session_id: &str, page_size: usize) -> serde_json::Value {
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"sessionId": session_id,
|
"sessionId": session_id,
|
||||||
"pageSize": page_size,
|
"pageSize": agent_protocol_row_count(page_size),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn agent_protocol_row_count(value: usize) -> usize {
|
||||||
|
value.clamp(1, AGENT_PROTOCOL_MAX_ROWS)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn agent_close_query_session_params(session_id: &str) -> serde_json::Value {
|
pub fn agent_close_query_session_params(session_id: &str) -> serde_json::Value {
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"sessionId": session_id,
|
"sessionId": session_id,
|
||||||
@@ -6484,6 +6489,27 @@ for line in sys.stdin:
|
|||||||
assert_eq!(params["maxRows"], MAX_ROWS);
|
assert_eq!(params["maxRows"], MAX_ROWS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn agent_query_row_counts_are_clamped_to_java_signed_int_range() {
|
||||||
|
let oversized = AGENT_PROTOCOL_MAX_ROWS.saturating_add(1);
|
||||||
|
let params = agent_execute_query_page_params(
|
||||||
|
"SELECT * FROM events",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
QueryExecutionOptions {
|
||||||
|
page_size: Some(oversized),
|
||||||
|
fetch_size: Some(oversized),
|
||||||
|
max_rows: Some(oversized),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(params["pageSize"], AGENT_PROTOCOL_MAX_ROWS);
|
||||||
|
assert_eq!(params["fetchSize"], AGENT_PROTOCOL_MAX_ROWS);
|
||||||
|
assert_eq!(params["maxRows"], AGENT_PROTOCOL_MAX_ROWS);
|
||||||
|
assert_eq!(agent_fetch_query_page_params("session-1", oversized)["pageSize"], AGENT_PROTOCOL_MAX_ROWS);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn agent_fetch_query_page_params_include_session_and_page_size() {
|
fn agent_fetch_query_page_params_include_session_and_page_size() {
|
||||||
let params = agent_fetch_query_page_params("session-1", 500);
|
let params = agent_fetch_query_page_params("session-1", 500);
|
||||||
|
|||||||
+8
-5
@@ -3950,15 +3950,18 @@ test("MongoDB query result export rejects pagination-plan failures without repla
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("jdbc query pagination uses result sessions without capping max rows to one page", async () => {
|
test("jdbc query pagination sends the Agent-safe unlimited row boundary when the global limit is disabled", async () => {
|
||||||
const restoreStorage = installMemoryStorage();
|
const restoreStorage = installMemoryStorage();
|
||||||
setActivePinia(createPinia());
|
setActivePinia(createPinia());
|
||||||
const connectionStore = useConnectionStore();
|
const connectionStore = useConnectionStore();
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
const store = useQueryStore();
|
const store = useQueryStore();
|
||||||
const originalFetch = globalThis.fetch;
|
const originalFetch = globalThis.fetch;
|
||||||
let prepareBody: any;
|
let prepareBody: any;
|
||||||
let executeBody: any;
|
let executeBody: any;
|
||||||
|
|
||||||
|
settingsStore.updateEditorSettings({ queryResultMaxRowsEnabled: false });
|
||||||
|
|
||||||
connectionStore.addEphemeralConnection({
|
connectionStore.addEphemeralConnection({
|
||||||
...conn("jdbc-1"),
|
...conn("jdbc-1"),
|
||||||
db_type: "jdbc",
|
db_type: "jdbc",
|
||||||
@@ -4014,7 +4017,7 @@ test("jdbc query pagination uses result sessions without capping max rows to one
|
|||||||
assert.equal(prepareBody.options.useAgentCursor, true);
|
assert.equal(prepareBody.options.useAgentCursor, true);
|
||||||
assert.equal(executeBody.pageSize, 100);
|
assert.equal(executeBody.pageSize, 100);
|
||||||
assert.equal(executeBody.fetchSize, 100);
|
assert.equal(executeBody.fetchSize, 100);
|
||||||
assert.equal(executeBody.maxRows, 100000);
|
assert.equal(executeBody.maxRows, 2147483647);
|
||||||
assert.equal(executeBody.clientSessionId, tabId);
|
assert.equal(executeBody.clientSessionId, tabId);
|
||||||
assert.equal(tab.resultSessionId, "session-1");
|
assert.equal(tab.resultSessionId, "session-1");
|
||||||
assert.equal(tab.result?.has_more, true);
|
assert.equal(tab.result?.has_more, true);
|
||||||
@@ -6438,7 +6441,7 @@ for (const resultState of [
|
|||||||
{ label: "truncated", result: { truncated: true, has_more: false } },
|
{ label: "truncated", result: { truncated: true, has_more: false } },
|
||||||
{ label: "ambiguous exhaustion", result: {} },
|
{ label: "ambiguous exhaustion", result: {} },
|
||||||
]) {
|
]) {
|
||||||
test(`oracle agent ${resultState.label} short page uses COUNT instead of inferring 10000`, async () => {
|
test(`oracle agent ${resultState.label} short page uses COUNT and caps the configured result total`, async () => {
|
||||||
const restoreStorage = installMemoryStorage();
|
const restoreStorage = installMemoryStorage();
|
||||||
setActivePinia(createPinia());
|
setActivePinia(createPinia());
|
||||||
const connectionStore = useConnectionStore();
|
const connectionStore = useConnectionStore();
|
||||||
@@ -6496,11 +6499,11 @@ for (const resultState of [
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await store.executeTabSql(tabId, "SELECT ID FROM LARGE_TABLE");
|
await store.executeTabSql(tabId, "SELECT ID FROM LARGE_TABLE");
|
||||||
await waitFor(() => tab.resultTotalRowCount === 3_357_833);
|
await waitFor(() => tab.resultTotalRowCount === 100_000);
|
||||||
|
|
||||||
assert.equal(countRequests, 1);
|
assert.equal(countRequests, 1);
|
||||||
assert.equal(tab.result?.rows.length, 10_000);
|
assert.equal(tab.result?.rows.length, 10_000);
|
||||||
assert.notEqual(tab.resultTotalRowCount, 10_000);
|
assert.equal(tab.resultTotalRowCount, 100_000);
|
||||||
} finally {
|
} finally {
|
||||||
globalThis.fetch = originalFetch;
|
globalThis.fetch = originalFetch;
|
||||||
restoreStorage();
|
restoreStorage();
|
||||||
|
|||||||
+14
-2
@@ -63,20 +63,32 @@ async function withMockLocalStorage(initial: Record<string, string>, run: () =>
|
|||||||
test("normalizes saved query result page size", () => {
|
test("normalizes saved query result page size", () => {
|
||||||
assert.equal(DEFAULT_EDITOR_SETTINGS.pageSize, 100);
|
assert.equal(DEFAULT_EDITOR_SETTINGS.pageSize, 100);
|
||||||
assert.equal(normalizeEditorSettings({ pageSize: 5000 }).pageSize, 5000);
|
assert.equal(normalizeEditorSettings({ pageSize: 5000 }).pageSize, 5000);
|
||||||
assert.equal(normalizeEditorSettings({ pageSize: 200000 }).pageSize, 100000);
|
assert.equal(normalizeEditorSettings({ pageSize: 200000 }).pageSize, 200000);
|
||||||
|
assert.equal(normalizeEditorSettings({ pageSize: 2000000 }).pageSize, 1000000);
|
||||||
assert.equal(normalizeEditorSettings({ pageSize: 0 }).pageSize, 100);
|
assert.equal(normalizeEditorSettings({ pageSize: 0 }).pageSize, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("normalizes the dedicated default row limit for table opens", () => {
|
test("normalizes the dedicated default row limit for table opens", () => {
|
||||||
assert.equal(DEFAULT_EDITOR_SETTINGS.tableOpenPageSize, 100);
|
assert.equal(DEFAULT_EDITOR_SETTINGS.tableOpenPageSize, 100);
|
||||||
assert.equal(normalizeEditorSettings({ tableOpenPageSize: 1000 }).tableOpenPageSize, 1000);
|
assert.equal(normalizeEditorSettings({ tableOpenPageSize: 1000 }).tableOpenPageSize, 1000);
|
||||||
assert.equal(normalizeEditorSettings({ tableOpenPageSize: 200000 }).tableOpenPageSize, 100000);
|
assert.equal(normalizeEditorSettings({ tableOpenPageSize: 200000 }).tableOpenPageSize, 200000);
|
||||||
|
assert.equal(normalizeEditorSettings({ tableOpenPageSize: 2000000 }).tableOpenPageSize, 1000000);
|
||||||
assert.equal(normalizeEditorSettings({ tableOpenPageSize: 0 }).tableOpenPageSize, 100);
|
assert.equal(normalizeEditorSettings({ tableOpenPageSize: 0 }).tableOpenPageSize, 100);
|
||||||
assert.equal(tableOpenPageLimit(), 100);
|
assert.equal(tableOpenPageLimit(), 100);
|
||||||
assert.equal(tableOpenPageLimit(1000), 1000);
|
assert.equal(tableOpenPageLimit(1000), 1000);
|
||||||
assert.equal(tableOpenPageLimit(0), 100);
|
assert.equal(tableOpenPageLimit(0), 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("normalizes the global query result row limit", () => {
|
||||||
|
assert.equal(DEFAULT_EDITOR_SETTINGS.queryResultMaxRowsEnabled, true);
|
||||||
|
assert.equal(DEFAULT_EDITOR_SETTINGS.queryResultMaxRows, 100000);
|
||||||
|
assert.equal(normalizeEditorSettings({}).queryResultMaxRowsEnabled, true);
|
||||||
|
assert.equal(normalizeEditorSettings({}).queryResultMaxRows, 100000);
|
||||||
|
assert.equal(normalizeEditorSettings({ queryResultMaxRowsEnabled: false, queryResultMaxRows: 250000 }).queryResultMaxRowsEnabled, false);
|
||||||
|
assert.equal(normalizeEditorSettings({ queryResultMaxRows: 250000 }).queryResultMaxRows, 250000);
|
||||||
|
assert.equal(normalizeEditorSettings({ queryResultMaxRows: 2147483648 }).queryResultMaxRows, 2147483647);
|
||||||
|
});
|
||||||
|
|
||||||
test("numericColumnRightAlign defaults to true and round-trips through normalizeEditorSettings", () => {
|
test("numericColumnRightAlign defaults to true and round-trips through normalizeEditorSettings", () => {
|
||||||
assert.equal(DEFAULT_EDITOR_SETTINGS.numericColumnRightAlign, true);
|
assert.equal(DEFAULT_EDITOR_SETTINGS.numericColumnRightAlign, true);
|
||||||
assert.equal(normalizeEditorSettings({}).numericColumnRightAlign, true);
|
assert.equal(normalizeEditorSettings({}).numericColumnRightAlign, true);
|
||||||
|
|||||||
+1
-1
@@ -198,7 +198,7 @@ test("infinite pagination fetches and appends only the next table segment", asyn
|
|||||||
const connectionStore = useConnectionStore();
|
const connectionStore = useConnectionStore();
|
||||||
const queryStore = useQueryStore();
|
const queryStore = useQueryStore();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
settingsStore.updateEditorSettings({ infiniteScroll: true, infiniteScrollMaxRows: 5000 });
|
settingsStore.updateEditorSettings({ infiniteScroll: true, queryResultMaxRowsEnabled: true, queryResultMaxRows: 5000 });
|
||||||
connectionStore.addEphemeralConnection(conn("mysql-1"));
|
connectionStore.addEphemeralConnection(conn("mysql-1"));
|
||||||
const tabId = queryStore.createTab("mysql-1", "app", "orders", "data");
|
const tabId = queryStore.createTab("mysql-1", "app", "orders", "data");
|
||||||
queryStore.setTableMeta(tabId, { tableName: "orders", tableType: "TABLE", columns: [], primaryKeys: [] });
|
queryStore.setTableMeta(tabId, { tableName: "orders", tableType: "TABLE", columns: [], primaryKeys: [] });
|
||||||
|
|||||||
Reference in new issue
Block a user