mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-11 22:39:40 +08:00
🔧 fix(duckdb-pagination): 修复 DuckDB 总数异常导致分页不可用
- 修正 DataViewer 在 hasMore 与 totalKnown 冲突时的分页状态处理 - 增强 DuckDB COUNT(*) 结果解析,兼容字段名与数值类型差异 - 将分页兜底逻辑收敛为 DuckDB 专用,避免影响其他数据库 - 修复 total=0 时分页文案显示异常 - refs #136
This commit is contained in:
@@ -549,6 +549,8 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
const showColumnComment = queryOptions?.showColumnComment !== false;
|
||||
const showColumnType = queryOptions?.showColumnType !== false;
|
||||
const selectionColumnWidth = 46;
|
||||
const connTypeLower = String(connections.find(c => c.id === connectionId)?.config?.type || '').trim().toLowerCase();
|
||||
const isDuckDBConnection = connTypeLower === 'duckdb';
|
||||
|
||||
// Background Helper
|
||||
const getBg = (darkHex: string) => {
|
||||
@@ -3089,6 +3091,10 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
pageSize={pagination.pageSize}
|
||||
total={pagination.total}
|
||||
showTotal={(total, range) => {
|
||||
if (isDuckDBConnection && (!Number.isFinite(total) || total <= 0)) {
|
||||
if (pagination.totalKnown === false) return '当前 0 条 / 正在统计总数...';
|
||||
return '当前 0 条 / 共 0 条';
|
||||
}
|
||||
const currentCount = Math.max(0, range[1] - range[0] + 1);
|
||||
if (pagination.totalKnown === false) return `当前 ${currentCount} 条 / 正在统计总数...`;
|
||||
return `当前 ${currentCount} 条 / 共 ${total} 条`;
|
||||
|
||||
@@ -6,6 +6,43 @@ import { DBQuery, DBGetColumns } from '../../wailsjs/go/app/App';
|
||||
import DataGrid, { GONAVI_ROW_KEY } from './DataGrid';
|
||||
import { buildOrderBySQL, buildWhereSQL, quoteQualifiedIdent, withSortBufferTuningSQL, type FilterCondition } from '../utils/sql';
|
||||
|
||||
const toNonNegativeFiniteNumber = (value: unknown): number | null => {
|
||||
if (typeof value === 'number') {
|
||||
return Number.isFinite(value) && value >= 0 ? value : null;
|
||||
}
|
||||
if (typeof value === 'bigint') {
|
||||
return value >= 0n ? Number(value) : null;
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
const text = value.trim();
|
||||
if (!text) return null;
|
||||
const parsed = Number(text);
|
||||
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const parseTotalFromCountRow = (row: any): number | null => {
|
||||
if (!row || typeof row !== 'object') return null;
|
||||
const entries = Object.entries(row as Record<string, unknown>);
|
||||
if (entries.length === 0) return null;
|
||||
|
||||
for (const [key, raw] of entries) {
|
||||
const normalized = String(key || '').trim().toLowerCase();
|
||||
if (normalized === 'total' || normalized === 'count' || normalized.includes('count')) {
|
||||
const parsed = toNonNegativeFiniteNumber(raw);
|
||||
if (parsed !== null) return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
for (const [, raw] of entries) {
|
||||
const parsed = toNonNegativeFiniteNumber(raw);
|
||||
if (parsed !== null) return parsed;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const DataViewer: React.FC<{ tab: TabData }> = ({ tab }) => {
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [columnNames, setColumnNames] = useState<string[]>([]);
|
||||
@@ -157,6 +194,8 @@ const DataViewer: React.FC<{ tab: TabData }> = ({ tab }) => {
|
||||
const countKey = `${tab.connectionId}|${dbName}|${tableName}|${whereSQL}`;
|
||||
const derivedTotalKnown = !hasMore;
|
||||
const derivedTotal = derivedTotalKnown ? offset + resultData.length : page * size + 1;
|
||||
const isDuckDB = dbTypeLower === 'duckdb';
|
||||
const minExpectedTotal = hasMore ? offset + resultData.length + 1 : offset + resultData.length;
|
||||
if (derivedTotalKnown) countKeyRef.current = countKey;
|
||||
|
||||
setPagination(prev => {
|
||||
@@ -164,7 +203,14 @@ const DataViewer: React.FC<{ tab: TabData }> = ({ tab }) => {
|
||||
return { ...prev, current: page, pageSize: size, total: derivedTotal, totalKnown: true };
|
||||
}
|
||||
if (prev.totalKnown && countKeyRef.current === countKey) {
|
||||
return { ...prev, current: page, pageSize: size };
|
||||
if (!isDuckDB) {
|
||||
return { ...prev, current: page, pageSize: size };
|
||||
}
|
||||
// 当当前页存在“下一页”信号时,已知总数至少应大于当前页末尾。
|
||||
// 若旧总数不满足该条件(例如历史统计值为 0),降级为未知总数并回退到 derivedTotal。
|
||||
if (Number.isFinite(prev.total) && prev.total >= minExpectedTotal) {
|
||||
return { ...prev, current: page, pageSize: size };
|
||||
}
|
||||
}
|
||||
return { ...prev, current: page, pageSize: size, total: derivedTotal, totalKnown: false };
|
||||
});
|
||||
@@ -198,8 +244,16 @@ const DataViewer: React.FC<{ tab: TabData }> = ({ tab }) => {
|
||||
if (!resCount.success) return;
|
||||
if (!Array.isArray(resCount.data) || resCount.data.length === 0) return;
|
||||
|
||||
const total = Number(resCount.data[0]?.['total']);
|
||||
if (!Number.isFinite(total) || total < 0) return;
|
||||
let total: number | null = null;
|
||||
if (dbTypeLower === 'duckdb') {
|
||||
total = parseTotalFromCountRow(resCount.data[0]);
|
||||
} else {
|
||||
const parsed = Number(resCount.data[0]?.['total']);
|
||||
if (Number.isFinite(parsed) && parsed >= 0) {
|
||||
total = parsed;
|
||||
}
|
||||
}
|
||||
if (total === null) return;
|
||||
|
||||
setPagination(prev => ({ ...prev, total, totalKnown: true }));
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user