This commit is contained in:
seb
2026-10-13 12:19:35 +02:00
parent 2ef2c0edbf
commit a5886b7c92
4 changed files with 85 additions and 42 deletions

View File

@@ -5,6 +5,7 @@
*/ */
import sql from 'mssql'; import sql from 'mssql';
import { logSlowQuery } from '../provider.js';
function normalizeRows(result) { function normalizeRows(result) {
const recordset = result?.recordset ?? []; const recordset = result?.recordset ?? [];
@@ -24,11 +25,16 @@ export class MssqlExecutor {
/** @returns {Promise<import('../provider.js').QueryResult>} */ /** @returns {Promise<import('../provider.js').QueryResult>} */
async query(sql, params = {}) { async query(sql, params = {}) {
const start = performance.now();
try {
const req = this.#requestFactory(); const req = this.#requestFactory();
for (const [key, value] of Object.entries(params)) { for (const [key, value] of Object.entries(params)) {
req.input(key, value); req.input(key, value);
} }
return normalizeRows(await req.query(sql)); return normalizeRows(await req.query(sql));
} finally {
logSlowQuery(sql, performance.now() - start, params);
}
} }
/** /**
@@ -37,6 +43,8 @@ export class MssqlExecutor {
* (`getnextnumberwithprefixandpadding`). * (`getnextnumberwithprefixandpadding`).
*/ */
async exec(procName, { inputs = {}, outputs = [] } = {}) { async exec(procName, { inputs = {}, outputs = [] } = {}) {
const start = performance.now();
try {
const req = this.#requestFactory(); const req = this.#requestFactory();
for (const [key, value] of Object.entries(inputs)) { for (const [key, value] of Object.entries(inputs)) {
req.input(key, value); req.input(key, value);
@@ -53,6 +61,9 @@ export class MssqlExecutor {
insertId: null, insertId: null,
outputs: result.output ?? {}, outputs: result.output ?? {},
}; };
} finally {
logSlowQuery(`EXEC ${procName}`, performance.now() - start, inputs);
}
} }
} }

View File

@@ -46,3 +46,22 @@ export function isForeignKeyConstraintError(err) {
err?.code === 'SQLITE_CONSTRAINT_FOREIGNKEY' err?.code === 'SQLITE_CONSTRAINT_FOREIGNKEY'
); );
} }
export const SLOW_QUERY_THRESHOLD_MS = 20;
/**
* Logs a SQL statement to the console if execution time exceeded SLOW_QUERY_THRESHOLD_MS (20ms).
*
* @param {string} sql
* @param {number} durationMs
* @param {object} [params]
*/
export function logSlowQuery(sql, durationMs, params = null) {
if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
const timeStr = `${durationMs.toFixed(1)}ms`;
console.log(`[SQL >20ms] (${timeStr})\n${typeof sql === 'string' ? sql.trim() : sql}`);
if (params && Object.keys(params).length > 0) {
console.log('Params:', params);
}
}
}

View File

@@ -6,6 +6,7 @@
*/ */
import Database from 'better-sqlite3'; import Database from 'better-sqlite3';
import { logSlowQuery } from '../provider.js';
import { SCHEMA_SCRIPTS } from './schema.js'; import { SCHEMA_SCRIPTS } from './schema.js';
function normalizeParamValue(value) { function normalizeParamValue(value) {
@@ -236,6 +237,8 @@ export class BetterSqlite3Executor {
/** @returns {Promise<import('../provider.js').QueryResult>} */ /** @returns {Promise<import('../provider.js').QueryResult>} */
async query(sql, params = {}) { async query(sql, params = {}) {
const start = performance.now();
try {
const statement = this.db.prepare(sql); const statement = this.db.prepare(sql);
const bindings = bindParams(params, sql); const bindings = bindParams(params, sql);
@@ -254,6 +257,9 @@ export class BetterSqlite3Executor {
rowsAffected: result.changes, rowsAffected: result.changes,
insertId: normalizeInsertId(result.lastInsertRowid), insertId: normalizeInsertId(result.lastInsertRowid),
}; };
} finally {
logSlowQuery(sql, performance.now() - start, params);
}
} }
} }

View File

@@ -12,11 +12,15 @@
* @returns {((sql: string, params?: object) => Promise<import('../providers/provider.js').QueryResult>) & { query: Function, dialect?: string }} * @returns {((sql: string, params?: object) => Promise<import('../providers/provider.js').QueryResult>) & { query: Function, dialect?: string }}
*/ */
import { logSlowQuery } from '../providers/provider.js';
/** Wraps a raw mssql pool/transaction in the provider query contract. */ /** Wraps a raw mssql pool/transaction in the provider query contract. */
function wrapMssql(target) { function wrapMssql(target) {
return { return {
dialect: 'mssql', dialect: 'mssql',
async query(sql, params = {}) { async query(sql, params = {}) {
const start = performance.now();
try {
const req = target.request(); const req = target.request();
for (const [key, value] of Object.entries(params)) { for (const [key, value] of Object.entries(params)) {
req.input(key, value); req.input(key, value);
@@ -27,6 +31,9 @@ function wrapMssql(target) {
? result.rowsAffected.reduce((a, b) => a + b, 0) ? result.rowsAffected.reduce((a, b) => a + b, 0)
: 0; : 0;
return { rows, rowsAffected, insertId: null }; return { rows, rowsAffected, insertId: null };
} finally {
logSlowQuery(sql, performance.now() - start, params);
}
}, },
}; };
} }