typeorm
Version:
Data-Mapper ORM for TypeScript and ES2021+. Supports MySQL/MariaDB, PostgreSQL, MS SQL Server, Oracle, SAP HANA, SQLite, MongoDB databases.
154 lines (152 loc) • 6.71 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BetterSqlite3Driver = void 0;
const tslib_1 = require("tslib");
const promises_1 = tslib_1.__importDefault(require("fs/promises"));
const path_1 = tslib_1.__importDefault(require("path"));
const error_1 = require("../../error");
const PlatformTools_1 = require("../../platform/PlatformTools");
const AbstractSqliteDriver_1 = require("../sqlite-abstract/AbstractSqliteDriver");
const BetterSqlite3QueryRunner_1 = require("./BetterSqlite3QueryRunner");
const PathUtils_1 = require("../../util/PathUtils");
/**
* Organizes communication with sqlite DBMS.
*/
class BetterSqlite3Driver extends AbstractSqliteDriver_1.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_1.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 = (0, PathUtils_1.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 = (0, PathUtils_1.isAbsolute)(database)
? database
: path_1.default.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_1.default.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_1.PlatformTools.load("better-sqlite3");
this.sqlite = sqlite;
}
catch (e) {
throw new error_1.DriverPackageNotInstalledError("SQLite", "better-sqlite3");
}
}
/**
* Auto creates database directory if it does not exist.
*/
async createDatabaseDirectory(dbPath) {
await promises_1.default.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_1.default.dirname(attachFilepathAbsolute));
await this.connection.query(`ATTACH "${attachFilepathAbsolute}" AS "${attachHandle}"`);
}
}
getMainDatabasePath() {
const optionsDb = this.options.database;
return path_1.default.dirname((0, PathUtils_1.isAbsolute)(optionsDb)
? optionsDb
: path_1.default.join(this.options.baseDirectory, optionsDb));
}
}
exports.BetterSqlite3Driver = BetterSqlite3Driver;
//# sourceMappingURL=BetterSqlite3Driver.js.map