Files
MyGoNavi/frontend/src/utils/sql.test.ts
Syngnat eeaf3c658b 🐛 fix(duckdb): 修复唯一索引识别与多库对象解析
- 合并 DuckDB 约束与索引元数据,恢复唯一索引表的可编辑判定
- 修复 attach 多库场景下 catalog/schema/table 定位混乱问题
- 统一前后端 qualified name 解析,支持带点和带引号对象名
- 补充 DuckDB 元数据与编辑链路回归测试
2026-06-02 21:12:59 +08:00

67 lines
2.8 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { buildOrderBySQL, buildPaginatedSelectSQL, quoteQualifiedIdent, reverseOrderBySQL } from './sql';
describe('buildOrderBySQL', () => {
it('does not add fallback ORDER BY for DuckDB without explicit sort', () => {
expect(buildOrderBySQL('duckdb', [], ['ID'])).toBe('');
});
it('keeps explicit DuckDB sort', () => {
expect(buildOrderBySQL('duckdb', { columnKey: 'ID', order: 'descend' }, ['NAME'])).toBe(' ORDER BY "ID" DESC');
});
});
describe('buildPaginatedSelectSQL', () => {
it('uses SQL Server TOP for the first page to support old compatibility levels', () => {
const sql = buildPaginatedSelectSQL('sqlserver', 'SELECT * FROM [Users]', ' ORDER BY [ID] ASC', 101, 0);
expect(sql).toBe('SELECT TOP 101 * FROM [Users] ORDER BY [ID] ASC');
expect(sql.toLowerCase()).not.toContain('fetch next');
expect(sql.toLowerCase()).not.toContain('offset');
});
it('adds SQL Server TOP after DISTINCT', () => {
expect(buildPaginatedSelectSQL('mssql', 'SELECT DISTINCT [Name] FROM [Users]', '', 50, 0))
.toBe('SELECT DISTINCT TOP 50 [Name] FROM [Users]');
});
it('does not add another SQL Server TOP when base SQL already has one', () => {
expect(buildPaginatedSelectSQL('sqlserver', 'SELECT TOP 10 * FROM [Users]', '', 50, 0))
.toBe('SELECT TOP 10 * FROM [Users]');
});
it('uses SQL Server TOP window pagination instead of OFFSET FETCH for sorted pages', () => {
const sql = buildPaginatedSelectSQL('sqlserver', 'SELECT * FROM [Users]', ' ORDER BY [ID] ASC', 25, 50);
expect(sql).toContain('SELECT TOP 25 * FROM (SELECT TOP 75 * FROM (SELECT * FROM [Users])');
expect(sql).toContain('ORDER BY [ID] DESC');
expect(sql.endsWith('ORDER BY [ID] ASC')).toBe(true);
expect(sql.toLowerCase()).not.toContain('fetch next');
});
it('keeps generic pagination for other databases', () => {
expect(buildPaginatedSelectSQL('postgres', 'SELECT * FROM users', ' ORDER BY id ASC', 20, 40))
.toBe('SELECT * FROM users ORDER BY id ASC LIMIT 20 OFFSET 40');
});
});
describe('reverseOrderBySQL', () => {
it('reverses comma separated order parts without splitting function arguments', () => {
expect(reverseOrderBySQL(' ORDER BY COALESCE([a], [b]) ASC, [id] DESC'))
.toBe(' ORDER BY COALESCE([a], [b]) DESC, [id] ASC');
});
});
describe('quoteQualifiedIdent', () => {
it('does not split dots inside quoted DuckDB identifiers', () => {
expect(quoteQualifiedIdent('duckdb', '"daily.events"."2026.06"'))
.toBe('"daily.events"."2026.06"');
});
it('preserves three-part DuckDB names with quoted dots', () => {
expect(quoteQualifiedIdent('duckdb', '"analytics.catalog"."main.schema"."daily.events"'))
.toBe('"analytics.catalog"."main.schema"."daily.events"');
});
});