feat: SQL实验室支持导出

This commit is contained in:
digua
2026-01-08 01:18:03 +08:00
parent 9197ab8ac9
commit 8a401c11b4
11 changed files with 408 additions and 82 deletions

View File

@@ -5,9 +5,6 @@
import { openDatabase } from '../core'
// 最大返回行数限制
const MAX_LIMIT = 1000
// 查询超时时间(毫秒)
const QUERY_TIMEOUT_MS = 10000
@@ -81,41 +78,17 @@ export function getSchema(sessionId: string): TableSchema[] {
}
/**
* 解析并强制添加 LIMIT
* 如果 SQL 没有 LIMIT 或 LIMIT 超过最大值,强制设置为 MAX_LIMIT
* 检查 SQL 是否包含 LIMIT 子句
*/
function enforceLimit(sql: string): { sql: string; limited: boolean } {
const trimmedSQL = sql.trim()
// 检查是否是 SELECT 语句
if (!trimmedSQL.toUpperCase().startsWith('SELECT')) {
return { sql: trimmedSQL, limited: false }
}
// 使用正则匹配 LIMIT 子句
const limitMatch = trimmedSQL.match(/\bLIMIT\s+(\d+)\s*(?:,\s*\d+)?(?:\s+OFFSET\s+\d+)?/i)
if (limitMatch) {
const currentLimit = parseInt(limitMatch[1], 10)
if (currentLimit > MAX_LIMIT) {
// 替换超出的 LIMIT
const newSQL = trimmedSQL.replace(/\bLIMIT\s+\d+/i, `LIMIT ${MAX_LIMIT}`)
return { sql: newSQL, limited: true }
}
return { sql: trimmedSQL, limited: false }
} else {
// 没有 LIMIT追加
// 需要处理可能存在的分号
const sqlWithoutSemicolon = trimmedSQL.replace(/;\s*$/, '')
return { sql: `${sqlWithoutSemicolon} LIMIT ${MAX_LIMIT}`, limited: true }
}
function hasLimit(sql: string): boolean {
return /\bLIMIT\s+\d+/i.test(sql)
}
/**
* 执行用户 SQL 查询
* - 只支持 SELECT 语句
* - 强制 LIMIT 不超过 MAX_LIMIT
* - 带超时控制
* - 强制 LIMIT,由用户自行控制
* - 带超时控制(由 Worker 管理器控制)
*/
export function executeRawSQL(sessionId: string, sql: string): SQLResult {
const db = openDatabase(sessionId)
@@ -130,16 +103,12 @@ export function executeRawSQL(sessionId: string, sql: string): SQLResult {
throw new Error('只支持 SELECT 查询语句')
}
// 强制 LIMIT
const { sql: limitedSQL, limited } = enforceLimit(trimmedSQL)
// 执行查询
const startTime = Date.now()
try {
// better-sqlite3 是同步的,我们通过 Worker 实现"超时"
// 这里先执行,超时由 Worker 管理器控制
const stmt = db.prepare(limitedSQL)
// better-sqlite3 是同步的,超时由 Worker 管理器控制
const stmt = db.prepare(trimmedSQL)
const rows = stmt.all()
const duration = Date.now() - startTime
@@ -154,7 +123,7 @@ export function executeRawSQL(sessionId: string, sql: string): SQLResult {
rows: rowData,
rowCount: rows.length,
duration,
limited: limited || rows.length >= MAX_LIMIT,
limited: false, // 不再强制限制
}
} catch (error) {
if (error instanceof Error) {