Skip to content

Commit 8d47f9a

Browse files
committed
Cleanup
1 parent 98a6bce commit 8d47f9a

File tree

6 files changed

+35
-356
lines changed

6 files changed

+35
-356
lines changed

deno.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"@std/async": "jsr:@std/async@^1",
1616
"@std/cache": "jsr:@std/cache@^0",
1717
"@std/collections": "jsr:@std/collections@^1",
18-
"@std/fmt": "jsr:@std/fmt@^1"
18+
"@std/fmt": "jsr:@std/fmt@^1",
19+
"hub": "../hub/mod.ts"
1920
},
2021
"lock": false,
2122
"tasks": {

src/db.ts

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { blue, bold, white } from "@std/fmt/colors";
2+
import { hub } from "hub";
13
import { DDL } from "./ddl.ts";
24
import type { Class, Identifiable, Parameter, Row, Schema } from "./types.ts";
35
import { Repository } from "./repository.ts";
@@ -147,19 +149,9 @@ export class DB {
147149
static readonly ALL = Number.MAX_SAFE_INTEGER;
148150
static client: Client;
149151
static #schemas = new Map<string, Schema>();
150-
static logger = DB.#createLogger();
152+
static logger: ReturnType<typeof hub> = hub("dbx");
151153
static type: string;
152154

153-
static #createLogger(level: typeof DB.LEVELS[number] = "info"): Console & { level: string } {
154-
const logger = Object.setPrototypeOf({ n: DB.LEVELS.indexOf(level) }, console) as Console & { level: string, n: number };
155-
DB.LEVELS.forEach((l, i) => (logger as any)[l] = (...args: unknown[]) => logger.n <= i ? (console as any)[l](...args) : () => {});
156-
Object.defineProperty(logger, "level", {
157-
get: function () { return DB.LEVELS[this.n]; },
158-
set: function (l: typeof DB.LEVELS[number]) { return this.n = DB.LEVELS.indexOf(l) },
159-
});
160-
return logger;
161-
}
162-
163155
// Mainly for debugging/tests (useful for SQLite)
164156
static _sqlFilter = function (sql: string): string {
165157
return sql.replaceAll(" ORDER BY NULL", "");
@@ -228,6 +220,16 @@ export class DB {
228220
return sql;
229221
}
230222

223+
static #sql = hub("sql", undefined, { icon: "🛢️ " });
224+
static _logSql(sql: string, parameters: Parameter[], rows: number, start: number) {
225+
if (this.#sql.level !== "debug") return;
226+
const time = "[" + rows + "row" + (rows === 1 ? "" : "s") + " in " + (Date.now() - start) + "ms]";
227+
let i = 0;
228+
sql = sql.replace(/\?/g, () => blue(String(i < parameters.length ? parameters[i++] : "⚠️")));
229+
sql = sql.replace(RESERVED, (w) => bold(w));
230+
this.#sql.debug(sql.trim() + " " + bold(white(time)));
231+
}
232+
231233
static async query(sql: string, parameters?: Parameter[] | { [key: string]: Parameter }, debug?: boolean): Promise<Row[]> {
232234
// If values are not an array, they need to be transformed (as well as the SQL)
233235
const arrayParameters: Parameter[] = [];
@@ -242,16 +244,18 @@ export class DB {
242244

243245
// At this point SQL contains only `?` and the parameters is an array
244246
try {
245-
// Need to await to be able to catch potential errors
246-
return await DB.client.query(DB._sqlFilter(sql), parameters, debug);
247+
const start = Date.now();
248+
const result = await DB.client.query(DB._sqlFilter(sql), parameters, debug);
249+
this._logSql(sql, parameters ?? [], result.length ?? 0, start);
250+
return result;
247251
} catch (ex) {
248252
if (Deno.stderr.isTerminal()) DB.error(ex as Error, sql, parameters);
249253
this.logger.error({ method: "query", sql: clean(sql), parameters, error: (ex as Error).message, stack: (ex as Error).stack });
250254
throw ex;
251255
}
252256
}
253257

254-
static execute(sql: string, parameters?: Parameter[] | { [key: string]: Parameter }, debug?: boolean): Promise<{ affectedRows?: number; lastInsertId?: number }> {
258+
static async execute(sql: string, parameters?: Parameter[] | { [key: string]: Parameter }, debug?: boolean): Promise<{ affectedRows?: number; lastInsertId?: number }> {
255259
// If values are not an array, they need to be transformed (as well as the SQL)
256260
const arrayParameters: Parameter[] = [];
257261
if (parameters && !Array.isArray(parameters)) {
@@ -265,8 +269,10 @@ export class DB {
265269

266270
// At this point SQL contains only `?` and the parameters is an array
267271
try {
268-
// Need to await to be able to catch potential errors
269-
return DB.client.execute(DB._sqlFilter(sql), parameters, debug);
272+
const start = Date.now();
273+
const result = await DB.client.execute(DB._sqlFilter(sql), parameters, debug);
274+
this._logSql(sql, parameters ?? [], result.affectedRows ?? 0, start);
275+
return result;
270276
} catch (ex) {
271277
if (Deno.stderr.isTerminal()) DB.error(ex as Error, sql, parameters);
272278
this.logger.error({ method: "execute", sql: clean(sql), parameters, error: (ex as Error).message, stack: (ex as Error).stack });
@@ -314,6 +320,10 @@ export class DB {
314320
}
315321
}
316322

323+
// Debug Client
324+
// deno-fmt-ignore
325+
const RESERVED = new RegExp("\\b(ACCESSIBLE|ADD|ALL|ALTER|ANALYZE|AND|AS|ASC|ASENSITIVE|BEFORE|BETWEEN|BIGINT|BINARY|BLOB|BOTH|BY|CALL|CASCADE|CASE|CHANGE|CHAR|CHARACTER|CHECK|COLLATE|COLUMN|CONDITION|CONSTRAINT|CONTINUE|CONVERT|CREATE|CROSS|CUBE|CUME_DIST|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DATABASES|DAY_HOUR|DAY_MICROSECOND|DAY_MINUTE|DAY_SECOND|DEC|DECIMAL|DECLARE|DEFAULT|DELAYED|DELETE|DENSE_RANK|DESC|DESCRIBE|DETERMINISTIC|DISTINCT|DISTINCTROW|DIV|DOUBLE|DROP|DUAL|EACH|ELSE|ELSEIF|EMPTY|ENCLOSED|ESCAPED|EXCEPT|EXISTS|EXIT|EXPLAIN|FALSE|FETCH|FIRST_VALUE|FLOAT|FLOAT4|FLOAT8|FOR|FORCE|FOREIGN|FROM|FULLTEXT|FUNCTION|GENERATED|GET|GRANT|GROUP|GROUPING|GROUPS|HAVING|HIGH_PRIORITY|HOUR_MICROSECOND|HOUR_MINUTE|HOUR_SECOND|IF|IGNORE|IN|INDEX|INFILE|INNER|INOUT|INSENSITIVE|INSERT|INT|INT1|INT2|INT3|INT4|INT8|INTEGER|INTERSECT|INTERVAL|INTO|IO_AFTER_GTIDS|IO_BEFORE_GTIDS|IS|ITERATE|JOIN|JSON_TABLE|KEY|KEYS|KILL|LAG|LAST_VALUE|LATERAL|LEAD|LEADING|LEAVE|LEFT|LIKE|LIMIT|LINEAR|LINES|LOAD|LOCALTIME|LOCALTIMESTAMP|LOCK|LONG|LONGBLOB|LONGTEXT|LOOP|LOW_PRIORITY|MASTER_BIND|MASTER_SSL_VERIFY_SERVER_CERT|MATCH|MAXVALUE|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MIDDLEINT|MINUTE_MICROSECOND|MINUTE_SECOND|MOD|MODIFIES|NATURAL|NOT|NO_WRITE_TO_BINLOG|NTH_VALUE|NTILE|NULL|NUMERIC|OF|ON|OPTIMIZE|OPTIMIZER_COSTS|OPTION|OPTIONALLY|OR|ORDER|OUT|OUTER|OUTFILE|OVER|PARTITION|PERCENT_RANK|PRECISION|PRIMARY|PROCEDURE|PURGE|RANGE|RANK|READ|READS|READ_WRITE|REAL|RECURSIVE|REFERENCES|REGEXP|RELEASE|RENAME|REPEAT|REPLACE|REQUIRE|RESIGNAL|RESTRICT|RETURN|REVOKE|RIGHT|RLIKE|ROW|ROWS|ROW_NUMBER|SCHEMA|SCHEMAS|SECOND_MICROSECOND|SELECT|SENSITIVE|SEPARATOR|SET|SHOW|SIGNAL|SMALLINT|SPATIAL|SPECIFIC|SQL|SQLEXCEPTION|SQLSTATE|SQLWARNING|SQL_BIG_RESULT|SQL_CALC_FOUND_ROWS|SQL_SMALL_RESULT|SSL|STARTING|STORED|STRAIGHT_JOIN|SYSTEM|TABLE|TERMINATED|THEN|TINYBLOB|TINYINT|TINYTEXT|TO|TRAILING|TRIGGER|TRUE|UNDO|UNION|UNIQUE|UNLOCK|UNSIGNED|UPDATE|USAGE|USE|USING|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|VALUES|VARBINARY|VARCHAR|VARCHARACTER|VARYING|VIRTUAL|WHEN|WHERE|WHILE|WINDOW|WITH|WRITE|XOR|YEAR_MONTH|ZEROFILL)\\b", "g");
326+
317327
type LocalClientConfig = ClientConfig;
318328
type LocalProvider = Provider;
319329
type LocalSchema = Schema;

src/hub.ts

Lines changed: 0 additions & 135 deletions
This file was deleted.

src/repository.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { assert } from "@std/assert";
2+
import { hub } from "hub";
23
import { DB } from "./db.ts";
34
import type { Class, Condition, Filter, Identifiable, Order, Primitive, Schema, Where } from "./types.ts";
45

@@ -38,6 +39,8 @@ const Hook = {
3839

3940
// Loopback like model
4041
export class Repository<T extends Identifiable> extends EventTarget {
42+
static #logger: ReturnType<typeof hub> = hub("dbx");
43+
4144
table: string;
4245
type: Class<T>;
4346
schema?: Schema;
@@ -183,7 +186,7 @@ export class Repository<T extends Identifiable> extends EventTarget {
183186
if (debug) console.debug({ method: "insert", sql: clean(sql), parameters });
184187
this.dispatchEvent(new CustomEvent(Hook.BEFORE_INSERT, { detail: object }));
185188
const result = await DB.execute(sql, parameters as Primitive[]);
186-
if (!result.lastInsertId) DB.logger.warn({ method: "insert", sql: clean(sql), warning: "Insert did produce a last inserted ID" });
189+
if (!result.lastInsertId) Repository.#logger.warn({ method: "insert", sql: clean(sql), warning: "Insert did produce a last inserted ID" });
187190
if (result.lastInsertId) object.id = result.lastInsertId;
188191
this.dispatchEvent(new CustomEvent(Hook.AFTER_INSERT, { detail: object }));
189192
return object;
@@ -205,8 +208,8 @@ export class Repository<T extends Identifiable> extends EventTarget {
205208
const result = await DB.execute(sql, parameters as Primitive[]);
206209
if (result.lastInsertId) object.id = result.lastInsertId;
207210
this.dispatchEvent(new CustomEvent(Hook.AFTER_UPDATE, { detail: object }));
208-
if (result.affectedRows === 0) DB.logger.warn({ method: "update", sql: clean(sql), warning: "Update had no affected rows" });
209-
if (result.affectedRows! > 1) DB.logger.warn({ method: "update", sql: clean(sql), warning: "Update had more than one affected rows" });
211+
if (result.affectedRows === 0) Repository.#logger.warn({ method: "update", sql: clean(sql), warning: "Update had no affected rows" });
212+
if (result.affectedRows! > 1) Repository.#logger.warn({ method: "update", sql: clean(sql), warning: "Update had more than one affected rows" });
210213
return result.affectedRows === 1 ? object as T : undefined;
211214
}
212215

test/basic.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ Deno.test("Find by ID and update", options, async function () {
127127
Deno.test("Constraint(s)", options, async function () {
128128
// Turn off logging temporarily and restore it after the test
129129
const level = DB.logger.level;
130-
DB.logger.level = "none";
130+
DB.logger.level = "off";
131131
assert(await repo.insert(new Account({ name: Math.random().toString() })));
132132
await assertRejects(() => repo.insert(new Account({ name: Math.random().toString(), email: "me" })));
133133
await assertRejects(() => repo.insert(new Account({ name: Math.random().toString(), country: "United States" })));

0 commit comments

Comments
 (0)