1+ import { blue , bold , white } from "@std/fmt/colors" ;
2+ import { hub } from "hub" ;
13import { DDL } from "./ddl.ts" ;
24import type { Class , Identifiable , Parameter , Row , Schema } from "./types.ts" ;
35import { 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+
317327type LocalClientConfig = ClientConfig ;
318328type LocalProvider = Provider ;
319329type LocalSchema = Schema ;
0 commit comments