import { Pool , PoolOptions , PoolConnection , RowDataPacket , OkPacket , ResultSetHeader , } from 'mysql2' ; import { DatabaseConnection , QueryResult , } from '../../driver/database-connection.js' ; import { Driver , TransactionSettings } from '../../driver/driver.js' ; import { CompiledQuery } from '../../query-compiler/compiled-query.js' ; import { isFunction , isObject , freeze } from '../../util/object-utils.js' ; import { MysqlDialectConfig } from './mysql-dialect-config.js' ; const PRIVATE_RELEASE_METHOD = Symbol ( ) ; export class MysqlDriver implements Driver { readonly #config : MysqlDialectConfig ; readonly #connections = new WeakMap < PoolConnection , DatabaseConnection > ( ) ; #pool ? : Pool ; constructor ( config ) { this . #config = freeze ( { ... config } ) ; } async init ( ) { const poolFactory = await importMysqlPoolFactory ( ) ; this . #pool = poolFactory ( this . #config ) ; } async acquireConnection ( ) { const rawConnection = await this . #acquireConnection ( ) ; let connection = this . #connections . get ( rawConnection ) ; if ( ! connection ) { connection = new MysqlConnection ( rawConnection ) ; this . #connections . set ( rawConnection , connection ) ; if ( this . #config . onCreateConnection ) { await this . #config . onCreateConnection ( connection ) ; } } return connection ; } async #acquireConnection ( ) { return new Promise ( ( resolve , reject ) => { this . #pool ! . getConnection ( async ( err , rawConnection ) => { if ( err ) { reject ( err ) ; } else { resolve ( rawConnection ) ; } } ) ; } ) ; } async beginTransaction ( connection , settings ) { if ( settings . isolationLevel ) { await connection . executeQuery ( CompiledQuery . raw ( `set transaction isolation level ${ settings . isolationLevel }` ) ) ; } await connection . executeQuery ( CompiledQuery . raw ( 'begin' ) ) ; } async commitTransaction ( connection ) { await connection . executeQuery ( CompiledQuery . raw ( 'commit' ) ) ; } async rollbackTransaction ( connection ) { await connection . executeQuery ( CompiledQuery . raw ( 'rollback' ) ) ; } async releaseConnection ( connection ) { const mysqlConnection = connection as MysqlConnection ; mysqlConnection [ PRIVATE_RELEASE_METHOD ] ( ) ; } async destroy ( ) { return new Promise ( ( resolve , reject ) => { this . #pool ! . end ( ( err ) => { if ( err ) { reject ( err ) ; } else { resolve ( ) ; } } ) ; } ) ; } } type CreatePool = ( config : PoolOptions ) => Pool ; async function importMysqlPoolFactory ( ) { try { const mysqlModule = await import ( 'mysql2' ) ; if ( isFunction ( mysqlModule . createPool ) ) { return mysqlModule . createPool ; } else { return mysqlModule . default . createPool ; } } catch ( error ) { throw new Error ( 'MySQL client not installed. Please run `npm install mysql2`' ) ; } } class MysqlConnection implements DatabaseConnection { readonly #rawConnection : PoolConnection ; constructor ( rawConnection ) { this . #rawConnection = rawConnection ; } async executeQuery < O > ( compiledQuery ) { const result = await this . #executeQuery ( compiledQuery ) ; if ( isOkPacket ( result ) ) { const { insertId , affectedRows } = result ; return { insertId : insertId !== undefined && insertId !== null && insertId . toString ( ) !== '0' ? BigInt ( insertId ) : undefined , numUpdatedOrDeletedRows : affectedRows !== undefined && insertId !== null ? BigInt ( affectedRows ) : undefined , rows : [ ] , } ; } else if ( Array . isArray ( result ) ) { return { rows : result as O [ ] , } ; } return { rows : [ ] , } ; } #executeQuery ( compiledQuery ) { return new Promise ( ( resolve , reject ) => { this . #rawConnection . query ( compiledQuery . sql , compiledQuery . parameters , ( err , result ) => { if ( err ) { reject ( err ) ; } else { resolve ( result ) ; } } ) ; } ) ; } [ PRIVATE_RELEASE_METHOD ] ( ) { this . #rawConnection . release ( ) ; } } function isOkPacket ( obj ) { return isObject ( obj ) && 'insertId' in obj && 'affectedRows' in obj ; }