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

typeorm / typeorm / 14843948000

05 May 2025 06:54PM CUT coverage: 17.102% (-59.2%) from 76.346%
14843948000

Pull #11441

github

web-flow
Merge f25b47395 into 3ffeea590
Pull Request #11441: fix: change collation update detection in PostgresDriver

1598 of 12765 branches covered (12.52%)

Branch coverage included in aggregate %.

0 of 4 new or added lines in 1 file covered. (0.0%)

14137 existing lines in 165 files now uncovered.

4682 of 23955 relevant lines covered (19.54%)

180.61 hits per line

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

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

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

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

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

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

36
    constructor(connection: DataSource) {
UNCOV
37
        super(connection)
×
UNCOV
38
        this.connection = connection
×
UNCOV
39
        this.options = connection.options as SqliteConnectionOptions
×
UNCOV
40
        this.database = this.options.database
×
41

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

46
    // -------------------------------------------------------------------------
47
    // Public Methods
48
    // -------------------------------------------------------------------------
49

50
    /**
51
     * Closes connection with database.
52
     */
53
    async disconnect(): Promise<void> {
UNCOV
54
        return new Promise<void>((ok, fail) => {
×
UNCOV
55
            this.queryRunner = undefined
×
UNCOV
56
            this.databaseConnection.close((err: any) =>
×
UNCOV
57
                err ? fail(err) : ok(),
×
58
            )
59
        })
60
    }
61

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

UNCOV
68
        return this.queryRunner
×
69
    }
70

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

UNCOV
81
        return super.normalizeType(column)
×
82
    }
83

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

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

UNCOV
102
        if (database === this.options.database) return tableName
×
103

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

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

UNCOV
117
        return `${identifierHash}.${tableName}`
×
118
    }
119

120
    // -------------------------------------------------------------------------
121
    // Protected Methods
122
    // -------------------------------------------------------------------------
123

124
    /**
125
     * Creates connection with the database.
126
     */
127
    protected async createDatabaseConnection() {
UNCOV
128
        if (
×
129
            this.options.flags === undefined ||
×
130
            !(this.options.flags & this.sqlite.OPEN_URI)
131
        ) {
UNCOV
132
            await this.createDatabaseDirectory(this.options.database)
×
133
        }
134

UNCOV
135
        const databaseConnection: any = await new Promise((ok, fail) => {
×
UNCOV
136
            if (this.options.flags === undefined) {
×
UNCOV
137
                const connection = new this.sqlite.Database(
×
138
                    this.options.database,
139
                    (err: any) => {
UNCOV
140
                        if (err) return fail(err)
×
UNCOV
141
                        ok(connection)
×
142
                    },
143
                )
144
            } else {
UNCOV
145
                const connection = new this.sqlite.Database(
×
146
                    this.options.database,
147
                    this.options.flags,
148
                    (err: any) => {
UNCOV
149
                        if (err) return fail(err)
×
UNCOV
150
                        ok(connection)
×
151
                    },
152
                )
153
            }
154
        })
155

156
        // Internal function to run a command on the connection and fail if an error occured.
157
        function run(line: string): Promise<void> {
UNCOV
158
            return new Promise((ok, fail) => {
×
UNCOV
159
                databaseConnection.run(line, (err: any) => {
×
UNCOV
160
                    if (err) return fail(err)
×
UNCOV
161
                    ok()
×
162
                })
163
            })
164
        }
165
        // in the options, if encryption key for SQLCipher is setted.
166
        // Must invoke key pragma before trying to do any other interaction with the database.
UNCOV
167
        if (this.options.key) {
×
168
            await run(`PRAGMA key = ${JSON.stringify(this.options.key)}`)
×
169
        }
170

UNCOV
171
        if (this.options.enableWAL) {
×
UNCOV
172
            await run(`PRAGMA journal_mode = WAL`)
×
173
        }
174

UNCOV
175
        if (
×
176
            this.options.busyTimeout &&
×
177
            typeof this.options.busyTimeout === "number" &&
178
            this.options.busyTimeout > 0
179
        ) {
UNCOV
180
            await run(`PRAGMA busy_timeout = ${this.options.busyTimeout}`)
×
181
        }
182

183
        // we need to enable foreign keys in sqlite to make sure all foreign key related features
184
        // working properly. this also makes onDelete to work with sqlite.
UNCOV
185
        await run(`PRAGMA foreign_keys = ON`)
×
186

UNCOV
187
        return databaseConnection
×
188
    }
189

190
    /**
191
     * If driver dependency is not given explicitly, then try to load it via "require".
192
     */
193
    protected loadDependencies(): void {
UNCOV
194
        try {
×
UNCOV
195
            const sqlite = this.options.driver || PlatformTools.load("sqlite3")
×
UNCOV
196
            this.sqlite = sqlite.verbose()
×
197
        } catch (e) {
198
            throw new DriverPackageNotInstalledError("SQLite", "sqlite3")
×
199
        }
200
    }
201

202
    /**
203
     * Auto creates database directory if it does not exist.
204
     */
205
    protected async createDatabaseDirectory(fullPath: string): Promise<void> {
UNCOV
206
        await fs.mkdir(path.dirname(fullPath), { recursive: true })
×
207
    }
208

209
    /**
210
     * Performs the attaching of the database files. The attachedDatabase should have been populated during calls to #buildTableName
211
     * during EntityMetadata production (see EntityMetadata#buildTablePath)
212
     *
213
     * https://sqlite.org/lang_attach.html
214
     */
215
    protected async attachDatabases() {
216
        // @todo - possibly check number of databases (but unqueriable at runtime sadly) - https://www.sqlite.org/limits.html#max_attached
UNCOV
217
        for await (const {
×
218
            attachHandle,
219
            attachFilepathAbsolute,
220
        } of Object.values(this.attachedDatabases)) {
UNCOV
221
            await this.createDatabaseDirectory(attachFilepathAbsolute)
×
UNCOV
222
            await this.connection.query(
×
223
                `ATTACH "${attachFilepathAbsolute}" AS "${attachHandle}"`,
224
            )
225
        }
226
    }
227

228
    protected getMainDatabasePath(): string {
UNCOV
229
        const optionsDb = this.options.database
×
UNCOV
230
        return path.dirname(
×
231
            isAbsolute(optionsDb)
×
232
                ? optionsDb
233
                : path.join(process.cwd(), optionsDb),
234
        )
235
    }
236
}
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