• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

typeorm / typeorm / 15219332477

23 May 2025 09:13PM UTC coverage: 17.216% (-59.1%) from 76.346%
15219332477

Pull #11332

github

naorpeled
cr comments - move if block
Pull Request #11332: feat: add new undefined and null behavior flags

1603 of 12759 branches covered (12.56%)

Branch coverage included in aggregate %.

0 of 31 new or added lines in 3 files covered. (0.0%)

14132 existing lines in 166 files now uncovered.

4731 of 24033 relevant lines covered (19.69%)

60.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

11.11
/src/driver/better-sqlite3/BetterSqlite3Driver.ts
1
import fs from "fs/promises"
1✔
2
import path from "path"
1✔
3
import { DriverPackageNotInstalledError } from "../../error"
1✔
4
import { PlatformTools } from "../../platform/PlatformTools"
1✔
5
import { DataSource } from "../../data-source"
6
import { ColumnType } from "../types/ColumnTypes"
7
import { QueryRunner } from "../../query-runner/QueryRunner"
8
import { AbstractSqliteDriver } from "../sqlite-abstract/AbstractSqliteDriver"
1✔
9
import { BetterSqlite3ConnectionOptions } from "./BetterSqlite3ConnectionOptions"
10
import { BetterSqlite3QueryRunner } from "./BetterSqlite3QueryRunner"
1✔
11
import { ReplicationMode } from "../types/ReplicationMode"
12
import { filepathToName, isAbsolute } from "../../util/PathUtils"
1✔
13

14
/**
15
 * Organizes communication with sqlite DBMS.
16
 */
17
export class BetterSqlite3Driver extends AbstractSqliteDriver {
1✔
18
    // -------------------------------------------------------------------------
19
    // Public Implemented Properties
20
    // -------------------------------------------------------------------------
21

22
    /**
23
     * Connection options.
24
     */
25
    options: BetterSqlite3ConnectionOptions
26

27
    /**
28
     * SQLite underlying library.
29
     */
30
    sqlite: any
31

32
    // -------------------------------------------------------------------------
33
    // Constructor
34
    // -------------------------------------------------------------------------
35

36
    constructor(connection: DataSource) {
UNCOV
37
        super(connection)
×
38

UNCOV
39
        this.connection = connection
×
UNCOV
40
        this.options = connection.options as BetterSqlite3ConnectionOptions
×
UNCOV
41
        this.database = this.options.database
×
42

43
        // load sqlite package
UNCOV
44
        this.loadDependencies()
×
45
    }
46

47
    // -------------------------------------------------------------------------
48
    // Public Methods
49
    // -------------------------------------------------------------------------
50

51
    /**
52
     * Closes connection with database.
53
     */
54
    async disconnect(): Promise<void> {
UNCOV
55
        this.queryRunner = undefined
×
UNCOV
56
        this.databaseConnection.close()
×
57
    }
58

59
    /**
60
     * Creates a query runner used to execute database queries.
61
     */
62
    createQueryRunner(mode: ReplicationMode): QueryRunner {
UNCOV
63
        if (!this.queryRunner)
×
UNCOV
64
            this.queryRunner = new BetterSqlite3QueryRunner(this)
×
65

UNCOV
66
        return this.queryRunner
×
67
    }
68

69
    normalizeType(column: {
70
        type?: ColumnType
71
        length?: number | string
72
        precision?: number | null
73
        scale?: number
74
    }): string {
UNCOV
75
        if ((column.type as any) === Buffer) {
×
UNCOV
76
            return "blob"
×
77
        }
78

UNCOV
79
        return super.normalizeType(column)
×
80
    }
81

82
    async afterConnect(): Promise<void> {
UNCOV
83
        return this.attachDatabases()
×
84
    }
85

86
    /**
87
     * For SQLite, the database may be added in the decorator metadata. It will be a filepath to a database file.
88
     */
89
    buildTableName(
90
        tableName: string,
91
        _schema?: string,
92
        database?: string,
93
    ): string {
UNCOV
94
        if (!database) return tableName
×
UNCOV
95
        if (this.getAttachedDatabaseHandleByRelativePath(database))
×
UNCOV
96
            return `${this.getAttachedDatabaseHandleByRelativePath(
×
97
                database,
98
            )}.${tableName}`
99

UNCOV
100
        if (database === this.options.database) return tableName
×
101

102
        // we use the decorated name as supplied when deriving attach handle (ideally without non-portable absolute path)
UNCOV
103
        const identifierHash = filepathToName(database)
×
104
        // decorated name will be assumed relative to main database file when non absolute. Paths supplied as absolute won't be portable
UNCOV
105
        const absFilepath = isAbsolute(database)
×
106
            ? database
107
            : path.join(this.getMainDatabasePath(), database)
108

UNCOV
109
        this.attachedDatabases[database] = {
×
110
            attachFilepathAbsolute: absFilepath,
111
            attachFilepathRelative: database,
112
            attachHandle: identifierHash,
113
        }
114

UNCOV
115
        return `${identifierHash}.${tableName}`
×
116
    }
117

118
    // -------------------------------------------------------------------------
119
    // Protected Methods
120
    // -------------------------------------------------------------------------
121

122
    /**
123
     * Creates connection with the database.
124
     */
125
    protected async createDatabaseConnection() {
126
        // not to create database directory if is in memory
UNCOV
127
        if (this.options.database !== ":memory:")
×
UNCOV
128
            await this.createDatabaseDirectory(
×
129
                path.dirname(this.options.database),
130
            )
131

132
        const {
133
            database,
134
            readonly = false,
×
135
            fileMustExist = false,
×
136
            timeout = 5000,
×
137
            verbose = null,
×
138
            nativeBinding = null,
×
139
            prepareDatabase,
UNCOV
140
        } = this.options
×
UNCOV
141
        const databaseConnection = this.sqlite(database, {
×
142
            readonly,
143
            fileMustExist,
144
            timeout,
145
            verbose,
146
            nativeBinding,
147
        })
148
        // in the options, if encryption key for SQLCipher is setted.
149
        // Must invoke key pragma before trying to do any other interaction with the database.
UNCOV
150
        if (this.options.key) {
×
151
            databaseConnection.exec(
×
152
                `PRAGMA key = ${JSON.stringify(this.options.key)}`,
153
            )
154
        }
155

156
        // function to run before a database is used in typeorm.
UNCOV
157
        if (typeof prepareDatabase === "function") {
×
158
            prepareDatabase(databaseConnection)
×
159
        }
160

161
        // we need to enable foreign keys in sqlite to make sure all foreign key related features
162
        // working properly. this also makes onDelete to work with sqlite.
UNCOV
163
        databaseConnection.exec(`PRAGMA foreign_keys = ON`)
×
164

165
        // turn on WAL mode to enhance performance
UNCOV
166
        if (this.options.enableWAL) {
×
UNCOV
167
            databaseConnection.exec(`PRAGMA journal_mode = WAL`)
×
168
        }
169

UNCOV
170
        return databaseConnection
×
171
    }
172

173
    /**
174
     * If driver dependency is not given explicitly, then try to load it via "require".
175
     */
176
    protected loadDependencies(): void {
UNCOV
177
        try {
×
178
            const sqlite =
UNCOV
179
                this.options.driver || PlatformTools.load("better-sqlite3")
×
UNCOV
180
            this.sqlite = sqlite
×
181
        } catch (e) {
182
            throw new DriverPackageNotInstalledError("SQLite", "better-sqlite3")
×
183
        }
184
    }
185

186
    /**
187
     * Auto creates database directory if it does not exist.
188
     */
189
    protected async createDatabaseDirectory(dbPath: string): Promise<void> {
UNCOV
190
        await fs.mkdir(dbPath, { recursive: true })
×
191
    }
192

193
    /**
194
     * Performs the attaching of the database files. The attachedDatabase should have been populated during calls to #buildTableName
195
     * during EntityMetadata production (see EntityMetadata#buildTablePath)
196
     *
197
     * https://sqlite.org/lang_attach.html
198
     */
199
    protected async attachDatabases() {
200
        // @todo - possibly check number of databases (but unqueriable at runtime sadly) - https://www.sqlite.org/limits.html#max_attached
UNCOV
201
        for await (const {
×
202
            attachHandle,
203
            attachFilepathAbsolute,
204
        } of Object.values(this.attachedDatabases)) {
UNCOV
205
            await this.createDatabaseDirectory(
×
206
                path.dirname(attachFilepathAbsolute),
207
            )
UNCOV
208
            await this.connection.query(
×
209
                `ATTACH "${attachFilepathAbsolute}" AS "${attachHandle}"`,
210
            )
211
        }
212
    }
213

214
    protected getMainDatabasePath(): string {
UNCOV
215
        const optionsDb = this.options.database
×
UNCOV
216
        return path.dirname(
×
217
            isAbsolute(optionsDb)
×
218
                ? optionsDb
219
                : path.join(this.options.baseDirectory!, optionsDb),
220
        )
221
    }
222
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc