70 lines
2.8 KiB
JavaScript
70 lines
2.8 KiB
JavaScript
// Offline SQL-test helpers. No database access or command-line entry point.
|
|
import { readFileSync } from 'node:fs'
|
|
|
|
export function readInitialization(region = 'CN') {
|
|
const name = region === 'CN' ? '头程数据库统一增量-MySQL8.sql' : '海外区域统一增量-MySQL8.sql'
|
|
return readFileSync(new URL(`../deploy/sql/${name}`, import.meta.url), 'utf8')
|
|
}
|
|
|
|
export function tableDefinitions(sql) {
|
|
const result = new Map()
|
|
let schema
|
|
for (const match of sql.matchAll(/^USE (\w+);|^CREATE TABLE (\w+) \([\s\S]*?^\) ENGINE=[^\n]+;/gm)) {
|
|
if (match[1]) schema = match[1]
|
|
else {
|
|
const key = `${schema}.${match[2]}`
|
|
if (result.has(key)) throw new Error(`Duplicate table definition: ${key}`)
|
|
result.set(key, match[0])
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
export function table(sql, schema, name) {
|
|
const value = tableDefinitions(sql).get(`${schema}.${name}`)
|
|
if (!value) throw new Error(`Missing table: ${schema}.${name}`)
|
|
return value
|
|
}
|
|
|
|
function splitValues(value) {
|
|
const items = []
|
|
let start = 0, depth = 0, quoted = false
|
|
for (let i = 0; i < value.length; i++) {
|
|
const c = value[i]
|
|
if (quoted && c === '\\') { i++; continue }
|
|
if (c === "'") {
|
|
if (quoted && value[i + 1] === "'") { i++; continue }
|
|
quoted = !quoted
|
|
}
|
|
if (quoted) continue
|
|
if (c === '(') depth++
|
|
if (c === ')') depth--
|
|
if (c === ',' && depth === 0) { items.push(value.slice(start, i).trim()); start = i + 1 }
|
|
}
|
|
if (quoted || depth) throw new Error('Unbalanced SQL seed value')
|
|
items.push(value.slice(start).trim())
|
|
return items.map(item => item === 'NULL' ? null : item.startsWith("'")
|
|
? item.slice(1, -1).replace(/''/g, "'").replace(/\\([\\'"nrt0])/g, (_, c) => ({ n: '\n', r: '\r', t: '\t', 0: '\0' }[c] ?? c))
|
|
: item)
|
|
}
|
|
|
|
export function seedRows(sql, name) {
|
|
const pattern = new RegExp(`^INSERT INTO ${name} \\(([^\\n]+?)\\) VALUES\\s*([\\s\\S]*?);$`, 'gm')
|
|
return [...sql.replaceAll('\r\n', '\n').matchAll(pattern)].flatMap(match => {
|
|
const keys = match[1].split(',').map(key => key.trim())
|
|
return splitValues(match[2]).map(row => {
|
|
if (!row.startsWith('(') || !row.endsWith(')')) throw new Error(`Invalid seed row: ${name}`)
|
|
const values = splitValues(row.slice(1, -1))
|
|
if (keys.length !== values.length) throw new Error(`Invalid seed columns: ${name}`)
|
|
return Object.fromEntries(keys.map((key, i) => [key, values[i]]))
|
|
})
|
|
})
|
|
}
|
|
|
|
export function roleAuthorities(sql, roleCode) {
|
|
const role = seedRows(sql, 'iam_role').find(row => row.role_code === roleCode)
|
|
if (!role) throw new Error(`Missing role: ${roleCode}`)
|
|
const granted = new Set(seedRows(sql, 'iam_role_permission').filter(row => row.role_id === role.id).map(row => row.permission_id))
|
|
return new Set(seedRows(sql, 'iam_permission').filter(row => granted.has(row.id)).map(row => row.permission_code))
|
|
}
|