UNPKG

typeorm

Version:

Data-Mapper ORM for TypeScript and ES2021+. Supports MySQL/MariaDB, PostgreSQL, MS SQL Server, Oracle, SAP HANA, SQLite, MongoDB databases.

149 lines (147 loc) 6.29 kB
import fs from "fs/promises"; import path from "path"; import { DriverPackageNotInstalledError } from "../../error"; import { PlatformTools } from "../../platform/PlatformTools"; import { AbstractSqliteDriver } from "../sqlite-abstract/AbstractSqliteDriver"; import { BetterSqlite3QueryRunner } from "./BetterSqlite3QueryRunner"; import { filepathToName, isAbsolute } from "../../util/PathUtils"; /** * Organizes communication with sqlite DBMS. */ export class BetterSqlite3Driver extends AbstractSqliteDriver { // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- constructor(connection) { super(connection); this.connection = connection; this.options = connection.options; this.database = this.options.database; // load sqlite package this.loadDependencies(); } // ------------------------------------------------------------------------- // Public Methods // ------------------------------------------------------------------------- /** * Closes connection with database. */ async disconnect() { this.queryRunner = undefined; this.databaseConnection.close(); } /** * Creates a query runner used to execute database queries. */ createQueryRunner(mode) { if (!this.queryRunner) this.queryRunner = new BetterSqlite3QueryRunner(this); return this.queryRunner; } normalizeType(column) { if (column.type === Buffer) { return "blob"; } return super.normalizeType(column); } async afterConnect() { return this.attachDatabases(); } /** * For SQLite, the database may be added in the decorator metadata. It will be a filepath to a database file. */ buildTableName(tableName, _schema, database) { if (!database) return tableName; if (this.getAttachedDatabaseHandleByRelativePath(database)) return `${this.getAttachedDatabaseHandleByRelativePath(database)}.${tableName}`; if (database === this.options.database) return tableName; // we use the decorated name as supplied when deriving attach handle (ideally without non-portable absolute path) const identifierHash = filepathToName(database); // decorated name will be assumed relative to main database file when non absolute. Paths supplied as absolute won't be portable const absFilepath = isAbsolute(database) ? database : path.join(this.getMainDatabasePath(), database); this.attachedDatabases[database] = { attachFilepathAbsolute: absFilepath, attachFilepathRelative: database, attachHandle: identifierHash, }; return `${identifierHash}.${tableName}`; } // ------------------------------------------------------------------------- // Protected Methods // ------------------------------------------------------------------------- /** * Creates connection with the database. */ async createDatabaseConnection() { // not to create database directory if is in memory if (this.options.database !== ":memory:") await this.createDatabaseDirectory(path.dirname(this.options.database)); const { database, readonly = false, fileMustExist = false, timeout = 5000, verbose = null, nativeBinding = null, prepareDatabase, } = this.options; const databaseConnection = this.sqlite(database, { readonly, fileMustExist, timeout, verbose, nativeBinding, }); // in the options, if encryption key for SQLCipher is setted. // Must invoke key pragma before trying to do any other interaction with the database. if (this.options.key) { databaseConnection.exec(`PRAGMA key = ${JSON.stringify(this.options.key)}`); } // function to run before a database is used in typeorm. if (typeof prepareDatabase === "function") { prepareDatabase(databaseConnection); } // we need to enable foreign keys in sqlite to make sure all foreign key related features // working properly. this also makes onDelete to work with sqlite. databaseConnection.exec(`PRAGMA foreign_keys = ON`); // turn on WAL mode to enhance performance if (this.options.enableWAL) { databaseConnection.exec(`PRAGMA journal_mode = WAL`); } return databaseConnection; } /** * If driver dependency is not given explicitly, then try to load it via "require". */ loadDependencies() { try { const sqlite = this.options.driver || PlatformTools.load("better-sqlite3"); this.sqlite = sqlite; } catch (e) { throw new DriverPackageNotInstalledError("SQLite", "better-sqlite3"); } } /** * Auto creates database directory if it does not exist. */ async createDatabaseDirectory(dbPath) { await fs.mkdir(dbPath, { recursive: true }); } /** * Performs the attaching of the database files. The attachedDatabase should have been populated during calls to #buildTableName * during EntityMetadata production (see EntityMetadata#buildTablePath) * * https://sqlite.org/lang_attach.html */ async attachDatabases() { // @todo - possibly check number of databases (but unqueriable at runtime sadly) - https://www.sqlite.org/limits.html#max_attached for await (const { attachHandle, attachFilepathAbsolute, } of Object.values(this.attachedDatabases)) { await this.createDatabaseDirectory(path.dirname(attachFilepathAbsolute)); await this.connection.query(`ATTACH "${attachFilepathAbsolute}" AS "${attachHandle}"`); } } getMainDatabasePath() { const optionsDb = this.options.database; return path.dirname(isAbsolute(optionsDb) ? optionsDb : path.join(this.options.baseDirectory, optionsDb)); } } //# sourceMappingURL=BetterSqlite3Driver.js.map