mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-06 10:31:30 +08:00
Compare commits
6 Commits
release/0.
...
feature/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85f89018ab | ||
|
|
b85c7529ec | ||
|
|
e521d2125f | ||
|
|
450fdfa59e | ||
|
|
819d201483 | ||
|
|
c87b15b22a |
@@ -10,7 +10,7 @@ import { useStore } from '../store';
|
|||||||
import type { ColumnDefinition } from '../types';
|
import type { ColumnDefinition } from '../types';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import 'react-resizable/css/styles.css';
|
import 'react-resizable/css/styles.css';
|
||||||
import { buildOrderBySQL, buildWhereSQL, escapeLiteral, quoteIdentPart, quoteQualifiedIdent, withSortBufferTuningSQL, type FilterCondition } from '../utils/sql';
|
import { buildOrderBySQL, buildPaginatedSelectSQL, buildWhereSQL, escapeLiteral, quoteIdentPart, quoteQualifiedIdent, withSortBufferTuningSQL, type FilterCondition } from '../utils/sql';
|
||||||
import { isMacLikePlatform, normalizeOpacityForPlatform, resolveAppearanceValues } from '../utils/appearance';
|
import { isMacLikePlatform, normalizeOpacityForPlatform, resolveAppearanceValues } from '../utils/appearance';
|
||||||
import { getDataSourceCapabilities } from '../utils/dataSourceCapabilities';
|
import { getDataSourceCapabilities } from '../utils/dataSourceCapabilities';
|
||||||
|
|
||||||
@@ -2447,18 +2447,18 @@ const DataGrid: React.FC<DataGridProps> = ({
|
|||||||
return clauses.join(' OR ');
|
return clauses.join(' OR ');
|
||||||
}, [pkColumns, tableName]);
|
}, [pkColumns, tableName]);
|
||||||
|
|
||||||
const buildCurrentPageSql = useCallback((dbType: string) => {
|
const buildCurrentPageSql = useCallback((dbType: string) => {
|
||||||
if (!tableName || !pagination) return '';
|
if (!tableName || !pagination) return '';
|
||||||
const whereSQL = buildWhereSQL(dbType, filterConditions);
|
const whereSQL = buildWhereSQL(dbType, filterConditions);
|
||||||
let sql = `SELECT * FROM ${quoteQualifiedIdent(dbType, tableName)} ${whereSQL}`;
|
const baseSql = `SELECT * FROM ${quoteQualifiedIdent(dbType, tableName)} ${whereSQL}`;
|
||||||
sql += buildOrderBySQL(dbType, sortInfo, pkColumns);
|
const orderBySQL = buildOrderBySQL(dbType, sortInfo, pkColumns);
|
||||||
const normalizedType = String(dbType || '').trim().toLowerCase();
|
const normalizedType = String(dbType || '').trim().toLowerCase();
|
||||||
const hasExplicitSort = !!sortInfo?.columnKey && (sortInfo?.order === 'ascend' || sortInfo?.order === 'descend');
|
const hasExplicitSort = !!sortInfo?.columnKey && (sortInfo?.order === 'ascend' || sortInfo?.order === 'descend');
|
||||||
|
const offset = (pagination.current - 1) * pagination.pageSize;
|
||||||
|
let sql = buildPaginatedSelectSQL(dbType, baseSql, orderBySQL, pagination.pageSize, offset);
|
||||||
if (hasExplicitSort && (normalizedType === 'mysql' || normalizedType === 'mariadb')) {
|
if (hasExplicitSort && (normalizedType === 'mysql' || normalizedType === 'mariadb')) {
|
||||||
sql = withSortBufferTuningSQL(normalizedType, sql, 32 * 1024 * 1024);
|
sql = withSortBufferTuningSQL(normalizedType, sql, 32 * 1024 * 1024);
|
||||||
}
|
}
|
||||||
const offset = (pagination.current - 1) * pagination.pageSize;
|
|
||||||
sql += ` LIMIT ${pagination.pageSize} OFFSET ${offset}`;
|
|
||||||
return sql;
|
return sql;
|
||||||
}, [tableName, pagination, filterConditions, sortInfo, pkColumns]);
|
}, [tableName, pagination, filterConditions, sortInfo, pkColumns]);
|
||||||
|
|
||||||
@@ -3384,22 +3384,17 @@ const DataGrid: React.FC<DataGridProps> = ({
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
checked={cond.enabled !== false}
|
checked={cond.enabled !== false}
|
||||||
onChange={e => updateFilter(cond.id, 'enabled', e.target.checked)}
|
onChange={e => updateFilter(cond.id, 'enabled', e.target.checked)}
|
||||||
style={{ marginTop: 6 }}
|
style={{ marginTop: 6, flex: '0 0 auto', whiteSpace: 'nowrap' }}
|
||||||
>
|
>
|
||||||
启用
|
启用
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
{condIndex === 0 ? (
|
<Select
|
||||||
<div style={{ width: 96, marginTop: 7, textAlign: 'center', fontSize: 12, color: '#8c8c8c' }}>
|
style={{ width: 96, minWidth: 96, maxWidth: 96, flex: '0 0 96px' }}
|
||||||
首条
|
value={condIndex === 0 ? '__FIRST__' : (cond.logic === 'OR' ? 'OR' : 'AND')}
|
||||||
</div>
|
onChange={v => updateFilter(cond.id, 'logic', v)}
|
||||||
) : (
|
options={condIndex === 0 ? [{ value: '__FIRST__', label: '首条' }] : (filterLogicOptions as any)}
|
||||||
<Select
|
disabled={condIndex === 0}
|
||||||
style={{ width: 96 }}
|
/>
|
||||||
value={cond.logic === 'OR' ? 'OR' : 'AND'}
|
|
||||||
onChange={v => updateFilter(cond.id, 'logic', v)}
|
|
||||||
options={filterLogicOptions as any}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Select
|
<Select
|
||||||
style={{ width: 180 }}
|
style={{ width: 180 }}
|
||||||
value={cond.column}
|
value={cond.column}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { TabData, ColumnDefinition } from '../types';
|
|||||||
import { useStore } from '../store';
|
import { useStore } from '../store';
|
||||||
import { DBQuery, DBGetColumns } from '../../wailsjs/go/app/App';
|
import { DBQuery, DBGetColumns } from '../../wailsjs/go/app/App';
|
||||||
import DataGrid, { GONAVI_ROW_KEY } from './DataGrid';
|
import DataGrid, { GONAVI_ROW_KEY } from './DataGrid';
|
||||||
import { buildOrderBySQL, buildWhereSQL, quoteIdentPart, quoteQualifiedIdent, withSortBufferTuningSQL, type FilterCondition } from '../utils/sql';
|
import { buildOrderBySQL, buildPaginatedSelectSQL, buildWhereSQL, quoteIdentPart, quoteQualifiedIdent, withSortBufferTuningSQL, type FilterCondition } from '../utils/sql';
|
||||||
import { buildMongoCountCommand, buildMongoFilter, buildMongoFindCommand, buildMongoSort } from '../utils/mongodb';
|
import { buildMongoCountCommand, buildMongoFilter, buildMongoFindCommand, buildMongoSort } from '../utils/mongodb';
|
||||||
import { getDataSourceCapabilities } from '../utils/dataSourceCapabilities';
|
import { getDataSourceCapabilities } from '../utils/dataSourceCapabilities';
|
||||||
|
|
||||||
@@ -455,7 +455,7 @@ const DataViewer: React.FC<{ tab: TabData }> = ({ tab }) => {
|
|||||||
if (pageRowCount > 0) {
|
if (pageRowCount > 0) {
|
||||||
const tailOffset = Math.max(0, totalRows - (offset + pageRowCount));
|
const tailOffset = Math.max(0, totalRows - (offset + pageRowCount));
|
||||||
if (tailOffset < offset) {
|
if (tailOffset < offset) {
|
||||||
sql = `${baseSql}${reverseOrderSQL} LIMIT ${pageRowCount} OFFSET ${tailOffset}`;
|
sql = buildPaginatedSelectSQL(dbType, baseSql, reverseOrderSQL, pageRowCount, tailOffset);
|
||||||
useClickHouseReversePagination = true;
|
useClickHouseReversePagination = true;
|
||||||
clickHouseReverseLimit = pageRowCount;
|
clickHouseReverseLimit = pageRowCount;
|
||||||
clickHouseReverseHasMore = currentPage < totalPages;
|
clickHouseReverseHasMore = currentPage < totalPages;
|
||||||
@@ -464,7 +464,7 @@ const DataViewer: React.FC<{ tab: TabData }> = ({ tab }) => {
|
|||||||
}
|
}
|
||||||
if (!useClickHouseReversePagination) {
|
if (!useClickHouseReversePagination) {
|
||||||
// 大表性能:打开表不阻塞在 COUNT(*),先通过多取 1 条判断是否还有下一页;总数在后台统计并异步回填。
|
// 大表性能:打开表不阻塞在 COUNT(*),先通过多取 1 条判断是否还有下一页;总数在后台统计并异步回填。
|
||||||
sql += ` LIMIT ${size + 1} OFFSET ${offset}`;
|
sql = buildPaginatedSelectSQL(dbType, baseSql, orderBySQL, size + 1, offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,8 +534,7 @@ const DataViewer: React.FC<{ tab: TabData }> = ({ tab }) => {
|
|||||||
|
|
||||||
if (safeSelect) {
|
if (safeSelect) {
|
||||||
let fallbackSql = `SELECT ${safeSelect} FROM ${quoteQualifiedIdent(dbType, tableName)} ${whereSQL}`;
|
let fallbackSql = `SELECT ${safeSelect} FROM ${quoteQualifiedIdent(dbType, tableName)} ${whereSQL}`;
|
||||||
fallbackSql += buildOrderBySQL(dbType, sortInfo, pkColumns);
|
fallbackSql = buildPaginatedSelectSQL(dbType, fallbackSql, buildOrderBySQL(dbType, sortInfo, pkColumns), size + 1, offset);
|
||||||
fallbackSql += ` LIMIT ${size + 1} OFFSET ${offset}`;
|
|
||||||
executedSql = fallbackSql;
|
executedSql = fallbackSql;
|
||||||
resData = await executeDataQuery(fallbackSql, '复杂类型降级重试');
|
resData = await executeDataQuery(fallbackSql, '复杂类型降级重试');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,6 +134,41 @@ export const buildOrderBySQL = (
|
|||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const buildPaginatedSelectSQL = (
|
||||||
|
dbType: string,
|
||||||
|
baseSql: string,
|
||||||
|
orderBySQL: string,
|
||||||
|
limit: number,
|
||||||
|
offset: number,
|
||||||
|
) => {
|
||||||
|
const normalizedType = String(dbType || '').trim().toLowerCase();
|
||||||
|
const safeLimit = Math.max(0, Math.floor(Number(limit) || 0));
|
||||||
|
const safeOffset = Math.max(0, Math.floor(Number(offset) || 0));
|
||||||
|
const base = String(baseSql || '').trim();
|
||||||
|
const orderBy = String(orderBySQL || '');
|
||||||
|
|
||||||
|
if (!base || safeLimit <= 0) {
|
||||||
|
return `${base}${orderBy}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (normalizedType) {
|
||||||
|
case 'oracle': {
|
||||||
|
const orderedSql = `${base}${orderBy}`;
|
||||||
|
const upperBound = safeOffset + safeLimit;
|
||||||
|
if (safeOffset <= 0) {
|
||||||
|
return `SELECT * FROM (${orderedSql}) WHERE ROWNUM <= ${upperBound}`;
|
||||||
|
}
|
||||||
|
return `SELECT * FROM (SELECT "__gonavi_page__".*, ROWNUM "__gonavi_rn__" FROM (${orderedSql}) "__gonavi_page__" WHERE ROWNUM <= ${upperBound}) WHERE "__gonavi_rn__" > ${safeOffset}`;
|
||||||
|
}
|
||||||
|
case 'sqlserver': {
|
||||||
|
const effectiveOrderBy = orderBy.trim() ? orderBy : ' ORDER BY (SELECT NULL)';
|
||||||
|
return `${base}${effectiveOrderBy} OFFSET ${safeOffset} ROWS FETCH NEXT ${safeLimit} ROWS ONLY`;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return `${base}${orderBy} LIMIT ${safeLimit} OFFSET ${safeOffset}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const parseListValues = (val: string) => {
|
export const parseListValues = (val: string) => {
|
||||||
const raw = (val || '').trim();
|
const raw = (val || '').trim();
|
||||||
if (!raw) return [];
|
if (!raw) return [];
|
||||||
|
|||||||
Reference in New Issue
Block a user