Backend changes: - SQL Server dialect: GETDATE() → SYSUTCDATETIME() - MySQL dialect: NOW() → UTC_TIMESTAMP() - Ensures OperationTime and EndTime use consistent UTC timezone Frontend changes: - formatDateTime: display UTC timestamps in user's local timezone - Uses getFullYear/getMonth/getDate/getHours (local) instead of UTC methods Data migration: - Executed migration script to fix historical OperationTime records - All existing records now have correct UTC timestamps - Execution duration now accurate (minutes, not hours) Impact: - New executions store UTC timestamps correctly - UI displays times in user's local timezone (UTC+8 for CN users) - Historical data corrected via migration - Time difference between OperationTime and EndTime now accurate
72 lines
1.9 KiB
TypeScript
72 lines
1.9 KiB
TypeScript
/**
|
|
* MySQL SQL Dialect Implementation
|
|
*
|
|
* Encapsulates MySQL-specific SQL syntax for:
|
|
* - Table name quoting (underscore-separated)
|
|
* - Positional parameter placeholders (?)
|
|
* - INSERT ... ON DUPLICATE KEY UPDATE upsert
|
|
* - LIMIT/OFFSET pagination
|
|
*/
|
|
|
|
import type { SqlDialect } from '../../../types/sql-dialect.types'
|
|
|
|
export class MySqlDialect implements SqlDialect {
|
|
readonly dbType = 'mysql' as const
|
|
|
|
quoteTableName(schema: string, table: string): string {
|
|
return `${schema}_${table}`
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
param(_index: number): string {
|
|
return '?'
|
|
}
|
|
|
|
params(count: number): string {
|
|
return Array.from({ length: count }, () => '?').join(',')
|
|
}
|
|
|
|
currentTimestamp(): string {
|
|
return 'UTC_TIMESTAMP()'
|
|
}
|
|
|
|
upsert(params: {
|
|
table: string
|
|
keyColumns: string[]
|
|
allColumns: string[]
|
|
startParamIndex: number
|
|
}): { sql: string; nextParamIndex: number } {
|
|
const { table, keyColumns, allColumns, startParamIndex } = params
|
|
|
|
const columns = allColumns.join(', ')
|
|
const placeholders = allColumns.map(() => '?').join(', ')
|
|
|
|
const nonKeyColumns = allColumns.filter((col) => !keyColumns.includes(col))
|
|
const updateClause = nonKeyColumns.map((col) => `${col} = VALUES(${col})`).join(', ')
|
|
|
|
const sql = `INSERT INTO ${table} (${columns}) VALUES (${placeholders}) ON DUPLICATE KEY UPDATE ${updateClause}`
|
|
|
|
return {
|
|
sql,
|
|
nextParamIndex: startParamIndex + allColumns.length
|
|
}
|
|
}
|
|
|
|
paginate(params: { sql: string; limit: number; offset?: number; paramIndex: number }): {
|
|
sql: string
|
|
nextParamIndex: number
|
|
} {
|
|
const { sql, limit, offset, paramIndex } = params
|
|
|
|
return {
|
|
sql: `${sql} LIMIT ${limit} OFFSET ${offset ?? 0}`,
|
|
nextParamIndex: paramIndex
|
|
}
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
maxBatchRows(_columnsPerRow: number): number {
|
|
return 1000
|
|
}
|
|
}
|