import {Connection, ConnectionNotReadyError} from './Connection' import {Logging} from '../../service/Logging' import {Inject} from '../../di' import {open, Database} from 'sqlite' import {FieldType, QueryResult, QueryRow} from '../types' import {Schema} from '../schema/Schema' import {Awaitable, collect, Collection, hasOwnProperty, UniversalPath} from '../../util' import {SQLDialect} from '../dialect/SQLDialect' import {SQLiteDialect} from '../dialect/SQLiteDialect' import {SQLiteSchema} from '../schema/SQLiteSchema' import * as sqlite3 from 'sqlite3' import {ModelField} from '../model/Field' export interface SQLiteConnectionConfig { filename: string, } export class SQLiteConnection extends Connection { @Inject() protected readonly logging!: Logging protected client?: Database public dialect(): SQLDialect { return this.make(SQLiteDialect) } public async init(): Promise { if ( this.config?.filename instanceof UniversalPath ) { this.config.filename = this.config.filename.toLocal } this.logging.debug(`Opening SQLite connection ${this.name} (${this.config?.filename})...`) this.client = await open({ ...this.config, driver: sqlite3.Database, }) } public async close(): Promise { this.logging.debug(`Closing SQLite connection ${this.name}...`) if ( this.client ) { await this.client.close() } } public async query(query: string): Promise { if ( !this.client ) { throw new ConnectionNotReadyError(this.name, { config: this.config, }) } this.logging.verbose(`Executing query in connection ${this.name}: \n${query.split('\n').map(x => ' ' + x) .join('\n')}`) try { const result = await this.client.all(query) // FIXME: this probably won't work for non-select statements? await this.queryExecuted(query) return { rows: collect(result), rowCount: result.length, } } catch (e) { if ( e instanceof Error ) { throw this.app().errorWrapContext(e, { query, connection: this.name, }) } throw e } } public async asTransaction(closure: () => Awaitable): Promise { if ( !this.client ) { throw new ConnectionNotReadyError(this.name, { config: this.config, }) } // fixme: sqlite doesn't support tx's properly in node atm await this.client.run('BEGIN') try { const result = await closure() await this.client.run('COMMIT') return result } catch (e) { await this.client.run('ROLLBACK') if ( e instanceof Error ) { throw this.app().errorWrapContext(e, { connection: this.name, }) } throw e } } public normalizeRow(row: QueryRow, fields: Collection): QueryRow { fields.where('type', '=', FieldType.json) .pluck('databaseKey') .filter(key => hasOwnProperty(row, key)) .filter(key => typeof row[key] === 'string') .each(key => row[key] = JSON.parse(row[key])) return row } public schema(): Schema { return new SQLiteSchema(this) } }