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

typeorm / typeorm / 22256862981

21 Feb 2026 12:31PM UTC coverage: 81.291% (+0.1%) from 81.176%
22256862981

push

github

web-flow
feat!: remove deprecated `Connection` and `ConnectionOptions` (#12022)

27682 of 33521 branches covered (82.58%)

Branch coverage included in aggregate %.

1205 of 1301 new or added lines in 76 files covered. (92.62%)

1 existing line in 1 file now uncovered.

93920 of 116068 relevant lines covered (80.92%)

71235.59 hits per line

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

83.61
/src/cache/DbQueryResultCache.ts
1
import { ObjectLiteral } from "../common/ObjectLiteral"
26✔
2
import { DataSource } from "../data-source/DataSource"
26✔
3
import { MssqlParameter } from "../driver/sqlserver/MssqlParameter"
26✔
4
import { QueryRunner } from "../query-runner/QueryRunner"
26✔
5
import { Table } from "../schema-builder/table/Table"
26✔
6
import { RandomGenerator } from "../util/RandomGenerator"
26✔
7
import { QueryResultCache } from "./QueryResultCache"
26✔
8
import { QueryResultCacheOptions } from "./QueryResultCacheOptions"
26✔
9

26✔
10
/**
26✔
11
 * Caches query result into current database, into separate table called "query-result-cache".
26✔
12
 */
26✔
13
export class DbQueryResultCache implements QueryResultCache {
26✔
14
    // -------------------------------------------------------------------------
26✔
15
    // Private properties
26✔
16
    // -------------------------------------------------------------------------
26✔
17

26✔
18
    private queryResultCacheTable: string
26✔
19

26✔
20
    private queryResultCacheDatabase?: string
26✔
21

26✔
22
    private queryResultCacheSchema?: string
26✔
23

26✔
24
    // -------------------------------------------------------------------------
26✔
25
    // Constructor
26✔
26
    // -------------------------------------------------------------------------
26✔
27

26✔
28
    constructor(protected dataSource: DataSource) {
26✔
29
        const { schema } = this.dataSource.driver.options as any
120✔
30
        const database = this.dataSource.driver.database
120✔
31
        const cacheOptions =
120✔
32
            typeof this.dataSource.options.cache === "object"
120✔
33
                ? this.dataSource.options.cache
120✔
34
                : {}
120✔
35
        const cacheTableName = cacheOptions.tableName || "query-result-cache"
120✔
36

120✔
37
        this.queryResultCacheDatabase = database
120✔
38
        this.queryResultCacheSchema = schema
120✔
39
        this.queryResultCacheTable = this.dataSource.driver.buildTableName(
120✔
40
            cacheTableName,
120✔
41
            schema,
120✔
42
            database,
120✔
43
        )
120✔
44
    }
120✔
45

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

26✔
50
    /**
26✔
51
     * Creates a connection with given cache provider.
26✔
52
     */
26✔
53
    async connect(): Promise<void> {}
26✔
54

26✔
55
    /**
26✔
56
     * Disconnects with given cache provider.
26✔
57
     */
26✔
58
    async disconnect(): Promise<void> {}
26✔
59

26✔
60
    /**
26✔
61
     * Creates table for storing cache if it does not exist yet.
26✔
62
     * @param queryRunner
26✔
63
     */
26✔
64
    async synchronize(queryRunner?: QueryRunner): Promise<void> {
26✔
65
        queryRunner = this.getQueryRunner(queryRunner)
296✔
66
        const driver = this.dataSource.driver
296✔
67
        const tableExist = await queryRunner.hasTable(
296✔
68
            this.queryResultCacheTable,
296✔
69
        ) // todo: table name should be configurable
296✔
70
        if (tableExist) return
296!
71

296✔
72
        await queryRunner.createTable(
296✔
73
            new Table({
296✔
74
                database: this.queryResultCacheDatabase,
296✔
75
                schema: this.queryResultCacheSchema,
296✔
76
                name: this.queryResultCacheTable,
296✔
77
                columns: [
296✔
78
                    {
296✔
79
                        name: "id",
296✔
80
                        isPrimary: true,
296✔
81
                        isNullable: false,
296✔
82
                        type: driver.normalizeType({
296✔
83
                            type: driver.mappedDataTypes.cacheId,
296✔
84
                        }),
296✔
85
                        generationStrategy:
296✔
86
                            driver.options.type === "spanner"
296✔
87
                                ? "uuid"
296!
88
                                : "increment",
296✔
89
                        isGenerated: true,
296✔
90
                    },
296✔
91
                    {
296✔
92
                        name: "identifier",
296✔
93
                        type: driver.normalizeType({
296✔
94
                            type: driver.mappedDataTypes.cacheIdentifier,
296✔
95
                        }),
296✔
96
                        isNullable: true,
296✔
97
                    },
296✔
98
                    {
296✔
99
                        name: "time",
296✔
100
                        type: driver.normalizeType({
296✔
101
                            type: driver.mappedDataTypes.cacheTime,
296✔
102
                        }),
296✔
103
                        isPrimary: false,
296✔
104
                        isNullable: false,
296✔
105
                    },
296✔
106
                    {
296✔
107
                        name: "duration",
296✔
108
                        type: driver.normalizeType({
296✔
109
                            type: driver.mappedDataTypes.cacheDuration,
296✔
110
                        }),
296✔
111
                        isPrimary: false,
296✔
112
                        isNullable: false,
296✔
113
                    },
296✔
114
                    {
296✔
115
                        name: "query",
296✔
116
                        type: driver.normalizeType({
296✔
117
                            type: driver.mappedDataTypes.cacheQuery,
296✔
118
                        }),
296✔
119
                        isPrimary: false,
296✔
120
                        isNullable: false,
296✔
121
                    },
296✔
122
                    {
296✔
123
                        name: "result",
296✔
124
                        type: driver.normalizeType({
296✔
125
                            type: driver.mappedDataTypes.cacheResult,
296✔
126
                        }),
296✔
127
                        isNullable: false,
296✔
128
                    },
296✔
129
                ],
296✔
130
            }),
296✔
131
        )
296✔
132
    }
296✔
133

26✔
134
    /**
26✔
135
     * Get data from cache.
26✔
136
     * Returns cache result if found.
26✔
137
     * Returns undefined if result is not cached.
26✔
138
     * @param options
26✔
139
     * @param queryRunner
26✔
140
     */
26✔
141
    getFromCache(
26✔
142
        options: QueryResultCacheOptions,
716✔
143
        queryRunner?: QueryRunner,
716✔
144
    ): Promise<QueryResultCacheOptions | undefined> {
716✔
145
        queryRunner = this.getQueryRunner(queryRunner)
716✔
146
        const qb = this.dataSource
716✔
147
            .createQueryBuilder(queryRunner)
716✔
148
            .select()
716✔
149
            .from(this.queryResultCacheTable, "cache")
716✔
150

716✔
151
        if (options.identifier) {
716✔
152
            return qb
196✔
153
                .where(
196✔
154
                    `${qb.escape("cache")}.${qb.escape(
196✔
155
                        "identifier",
196✔
156
                    )} = :identifier`,
196✔
157
                )
196✔
158
                .setParameters({
196✔
159
                    identifier:
196✔
160
                        this.dataSource.driver.options.type === "mssql"
196✔
161
                            ? new MssqlParameter(options.identifier, "nvarchar")
196!
162
                            : options.identifier,
196!
163
                })
196✔
164
                .cache(false) // disable cache to avoid infinite loops when cache is alwaysEnable
196✔
165
                .getRawOne()
196✔
166
        } else if (options.query) {
716✔
167
            if (this.dataSource.driver.options.type === "oracle") {
520!
168
                return qb
36✔
169
                    .where(
36✔
170
                        `dbms_lob.compare(${qb.escape("cache")}.${qb.escape(
36✔
171
                            "query",
36✔
172
                        )}, :query) = 0`,
36✔
173
                        { query: options.query },
36✔
174
                    )
36✔
175
                    .cache(false) // disable cache to avoid infinite loops when cache is alwaysEnable
36✔
176
                    .getRawOne()
36✔
177
            }
36✔
178

484!
179
            return qb
484✔
180
                .where(`${qb.escape("cache")}.${qb.escape("query")} = :query`)
484✔
181
                .setParameters({
484✔
182
                    query:
484✔
183
                        this.dataSource.driver.options.type === "mssql"
484✔
184
                            ? new MssqlParameter(options.query, "nvarchar")
484!
185
                            : options.query,
520!
186
                })
520✔
187
                .cache(false) // disable cache to avoid infinite loops when cache is alwaysEnable
520✔
188
                .getRawOne()
520✔
189
        }
520✔
190

×
191
        return Promise.resolve(undefined)
×
192
    }
×
193

26✔
194
    /**
26✔
195
     * Checks if cache is expired or not.
26✔
196
     * @param savedCache
26✔
197
     */
26✔
198
    isExpired(savedCache: QueryResultCacheOptions): boolean {
26✔
199
        const duration =
456✔
200
            typeof savedCache.duration === "string"
456✔
201
                ? parseInt(savedCache.duration)
456!
202
                : savedCache.duration
456!
203
        return (
456✔
204
            (typeof savedCache.time === "string"
456✔
205
                ? parseInt(savedCache.time as any)
456✔
206
                : savedCache.time)! +
456!
207
                duration <
456✔
208
            Date.now()
456✔
209
        )
456✔
210
    }
456✔
211

26✔
212
    /**
26✔
213
     * Stores given query result in the cache.
26✔
214
     * @param options
26✔
215
     * @param savedCache
26✔
216
     * @param queryRunner
26✔
217
     */
26✔
218
    async storeInCache(
26✔
219
        options: QueryResultCacheOptions,
400✔
220
        savedCache: QueryResultCacheOptions | undefined,
400✔
221
        queryRunner?: QueryRunner,
400✔
222
    ): Promise<void> {
400✔
223
        const shouldCreateQueryRunner =
400✔
224
            queryRunner === undefined ||
400✔
225
            queryRunner?.getReplicationMode() === "slave"
400✔
226

400✔
227
        if (queryRunner === undefined || shouldCreateQueryRunner) {
400!
228
            queryRunner = this.dataSource.createQueryRunner("master")
270✔
229
        }
270✔
230

400✔
231
        let insertedValues: ObjectLiteral = options
400✔
232
        if (this.dataSource.driver.options.type === "mssql") {
400!
233
            // todo: bad abstraction, re-implement this part, probably better if we create an entity metadata for cache table
28✔
234
            insertedValues = {
28✔
235
                identifier: new MssqlParameter(options.identifier, "nvarchar"),
28✔
236
                time: new MssqlParameter(options.time, "bigint"),
28✔
237
                duration: new MssqlParameter(options.duration, "int"),
28✔
238
                query: new MssqlParameter(options.query, "nvarchar"),
28✔
239
                result: new MssqlParameter(options.result, "nvarchar"),
28✔
240
            }
28✔
241
        }
28✔
242

400✔
243
        if (savedCache && savedCache.identifier) {
400✔
244
            // if exist then update
28✔
245
            const qb = queryRunner.manager
28✔
246
                .createQueryBuilder()
28✔
247
                .update(this.queryResultCacheTable)
28✔
248
                .set(insertedValues)
28✔
249

28✔
250
            qb.where(`${qb.escape("identifier")} = :condition`, {
28✔
251
                condition: insertedValues.identifier,
28✔
252
            })
28✔
253
            await qb.execute()
28✔
254
        } else if (savedCache && savedCache.query) {
400✔
255
            // if exist then update
112✔
256
            const qb = queryRunner.manager
112✔
257
                .createQueryBuilder()
112✔
258
                .update(this.queryResultCacheTable)
112✔
259
                .set(insertedValues)
112✔
260

112✔
261
            if (this.dataSource.driver.options.type === "oracle") {
112!
262
                qb.where(`dbms_lob.compare("query", :condition) = 0`, {
8✔
263
                    condition: insertedValues.query,
8✔
264
                })
8✔
265
            } else {
112!
266
                qb.where(`${qb.escape("query")} = :condition`, {
104✔
267
                    condition: insertedValues.query,
104✔
268
                })
104✔
269
            }
104✔
270

112✔
271
            await qb.execute()
112✔
272
        } else {
372✔
273
            // Spanner does not support auto-generated columns
260✔
274
            if (
260✔
275
                this.dataSource.driver.options.type === "spanner" &&
260!
276
                !insertedValues.id
×
277
            ) {
260!
278
                insertedValues.id = RandomGenerator.uuidv4()
×
279
            }
×
280

260✔
281
            // otherwise insert
260✔
282
            await queryRunner.manager
260✔
283
                .createQueryBuilder()
260✔
284
                .insert()
260✔
285
                .into(this.queryResultCacheTable)
260✔
286
                .values(insertedValues)
260✔
287
                .execute()
260✔
288
        }
260✔
289

400✔
290
        if (shouldCreateQueryRunner) {
400!
291
            await queryRunner.release()
270✔
292
        }
270✔
293
    }
400✔
294

26✔
295
    /**
26✔
296
     * Clears everything stored in the cache.
26✔
297
     * @param queryRunner
26✔
298
     */
26✔
299
    async clear(queryRunner: QueryRunner): Promise<void> {
26✔
300
        return this.getQueryRunner(queryRunner).clearTable(
×
301
            this.queryResultCacheTable,
×
302
        )
×
303
    }
×
304

26✔
305
    /**
26✔
306
     * Removes all cached results by given identifiers from cache.
26✔
307
     * @param identifiers
26✔
308
     * @param queryRunner
26✔
309
     */
26✔
310
    async remove(
26✔
311
        identifiers: string[],
×
312
        queryRunner?: QueryRunner,
×
313
    ): Promise<void> {
×
314
        const _queryRunner: QueryRunner = queryRunner || this.getQueryRunner()
×
315
        await Promise.all(
×
316
            identifiers.map((identifier) => {
×
317
                const qb = _queryRunner.manager.createQueryBuilder()
×
318
                return qb
×
319
                    .delete()
×
320
                    .from(this.queryResultCacheTable)
×
321
                    .where(`${qb.escape("identifier")} = :identifier`, {
×
322
                        identifier,
×
323
                    })
×
324
                    .execute()
×
325
            }),
×
326
        )
×
327

×
328
        if (!queryRunner) {
×
329
            await _queryRunner.release()
×
330
        }
×
331
    }
×
332

26✔
333
    // -------------------------------------------------------------------------
26✔
334
    // Protected Methods
26✔
335
    // -------------------------------------------------------------------------
26✔
336

26✔
337
    /**
26✔
338
     * Gets a query runner to work with.
26✔
339
     * @param queryRunner
26✔
340
     */
26✔
341
    protected getQueryRunner(queryRunner?: QueryRunner): QueryRunner {
26✔
342
        if (queryRunner) return queryRunner
1,012✔
343

×
NEW
344
        return this.dataSource.createQueryRunner()
×
345
    }
×
346
}
26✔
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

© 2026 Coveralls, Inc