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

typeorm / typeorm / 14764529297

30 Apr 2025 09:16PM UTC coverage: 76.309% (+0.002%) from 76.307%
14764529297

push

github

web-flow
fix: beforeQuery promises not awaited before query execution (#11086)

* fix: beforeQuery promises not awaited before query execution

Closes: #11085

* fix: run format

Closes: #11085

* fix: apply same beforeQuery & afterQuery logic to all drivers

* fix: use a different broadcaster for BeforeQuery / AfterQuery

* fix: BeforeQuery / AfterQuery event types

* fix: move broadCasterResult.wait in finally block

* fix: remove duplicated broadcasterResult.wait in ReactNativeQueryRunner

* fix: fix prettier issue

* fix: implemented requested changes

* fix: broken sqlite tests

* Revert "fix: broken sqlite tests"

This reverts commit 4bacd5f4b.

* Revert "fix: implemented requested changes"

This reverts commit 1d2f59bf2.

* review: undefined type at the end

* fix: move database connection logic outside of the promise bloc

---------

Co-authored-by: Lucian Mocanu <alumni@users.noreply.github.com>

9201 of 12761 branches covered (72.1%)

Branch coverage included in aggregate %.

100 of 142 new or added lines in 14 files covered. (70.42%)

4 existing lines in 3 files now uncovered.

18802 of 23936 relevant lines covered (78.55%)

197469.14 hits per line

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

11.27
/src/driver/cordova/CordovaQueryRunner.ts
1
import { ObjectLiteral } from "../../common/ObjectLiteral"
2
import { TypeORMError } from "../../error"
40✔
3
import { QueryFailedError } from "../../error/QueryFailedError"
40✔
4
import { QueryRunnerAlreadyReleasedError } from "../../error/QueryRunnerAlreadyReleasedError"
40✔
5
import { QueryResult } from "../../query-runner/QueryResult"
40✔
6
import { Broadcaster } from "../../subscriber/Broadcaster"
40✔
7
import { BroadcasterResult } from "../../subscriber/BroadcasterResult"
40✔
8
import { AbstractSqliteQueryRunner } from "../sqlite-abstract/AbstractSqliteQueryRunner"
40✔
9
import { CordovaDriver } from "./CordovaDriver"
10

11
/**
12
 * Runs queries on a single sqlite database connection.
13
 */
14
export class CordovaQueryRunner extends AbstractSqliteQueryRunner {
40✔
15
    /**
16
     * Database driver used by connection.
17
     */
18
    driver: CordovaDriver
19

20
    // -------------------------------------------------------------------------
21
    // Constructor
22
    // -------------------------------------------------------------------------
23

24
    constructor(driver: CordovaDriver) {
25
        super()
×
26
        this.driver = driver
×
27
        this.connection = driver.connection
×
28
        this.broadcaster = new Broadcaster(this)
×
29
    }
30

31
    /**
32
     * Called before migrations are run.
33
     */
34
    async beforeMigration(): Promise<void> {
35
        await this.query(`PRAGMA foreign_keys = OFF`)
×
36
    }
37

38
    /**
39
     * Called after migrations are run.
40
     */
41
    async afterMigration(): Promise<void> {
42
        await this.query(`PRAGMA foreign_keys = ON`)
×
43
    }
44

45
    /**
46
     * Executes a given SQL query.
47
     */
48
    async query(
49
        query: string,
50
        parameters?: any[],
51
        useStructuredResult = false,
×
52
    ): Promise<any> {
53
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
×
54

55
        const databaseConnection = await this.connect()
×
56

UNCOV
57
        this.driver.connection.logger.logQuery(query, parameters, this)
×
NEW
58
        await this.broadcaster.broadcast("BeforeQuery", query, parameters)
×
59

NEW
60
        const broadcasterResult = new BroadcasterResult()
×
UNCOV
61
        const queryStartTime = Date.now()
×
62

63
        try {
×
64
            const raw = await new Promise<any>(async (ok, fail) => {
×
65
                databaseConnection.executeSql(
×
66
                    query,
67
                    parameters,
68
                    (raw: any) => ok(raw),
×
69
                    (err: any) => fail(err),
×
70
                )
71
            })
72

73
            // log slow queries if maxQueryExecution time is set
74
            const maxQueryExecutionTime =
75
                this.driver.options.maxQueryExecutionTime
×
76
            const queryEndTime = Date.now()
×
77
            const queryExecutionTime = queryEndTime - queryStartTime
×
78

79
            this.broadcaster.broadcastAfterQueryEvent(
×
80
                broadcasterResult,
81
                query,
82
                parameters,
83
                true,
84
                queryExecutionTime,
85
                raw,
86
                undefined,
87
            )
88

89
            if (
×
90
                maxQueryExecutionTime &&
×
91
                queryExecutionTime > maxQueryExecutionTime
92
            ) {
93
                this.driver.connection.logger.logQuerySlow(
×
94
                    queryExecutionTime,
95
                    query,
96
                    parameters,
97
                    this,
98
                )
99
            }
100

101
            const result = new QueryResult()
×
102

103
            if (query.substr(0, 11) === "INSERT INTO") {
×
104
                result.raw = raw.insertId
×
105
            } else {
106
                const resultSet = []
×
107
                for (let i = 0; i < raw.rows.length; i++) {
×
108
                    resultSet.push(raw.rows.item(i))
×
109
                }
110

111
                result.records = resultSet
×
112
                result.raw = resultSet
×
113
            }
114

115
            if (useStructuredResult) {
×
116
                return result
×
117
            } else {
118
                return result.raw
×
119
            }
120
        } catch (err) {
121
            this.driver.connection.logger.logQueryError(
×
122
                err,
123
                query,
124
                parameters,
125
                this,
126
            )
127
            this.broadcaster.broadcastAfterQueryEvent(
×
128
                broadcasterResult,
129
                query,
130
                parameters,
131
                false,
132
                undefined,
133
                undefined,
134
                err,
135
            )
136

137
            throw new QueryFailedError(query, parameters, err)
×
138
        } finally {
139
            await broadcasterResult.wait()
×
140
        }
141
    }
142

143
    /**
144
     * Insert a new row with given values into the given table.
145
     * Returns value of the generated column if given and generate column exist in the table.
146
     // todo: implement new syntax
147
    async insert(tableName: string, keyValues: ObjectLiteral): Promise<InsertResult> {
148
        const keys = Object.keys(keyValues);
149
        const columns = keys.map(key => `"${key}"`).join(", ");
150
        const values = keys.map(key => "?").join(",");
151
        const generatedColumns = this.connection.hasMetadata(tableName) ? this.connection.getMetadata(tableName).generatedColumns : [];
152
        const sql = columns.length > 0 ? (`INSERT INTO "${tableName}"(${columns}) VALUES (${values})`) : `INSERT INTO "${tableName}" DEFAULT VALUES`;
153
        const parameters = keys.map(key => keyValues[key]);
154

155
        return new Promise<InsertResult>(async (ok, fail) => {
156
            this.driver.connection.logger.logQuery(sql, parameters, this);
157
            const __this = this;
158
            const databaseConnection = await this.connect();
159
            databaseConnection.executeSql(sql, parameters, (resultSet: any) => {
160
                const generatedMap = generatedColumns.reduce((map, generatedColumn) => {
161
                    const value = generatedColumn.isPrimary && generatedColumn.generationStrategy === "increment" && resultSet.insertId ? resultSet.insertId : keyValues[generatedColumn.databaseName];
162
                    if (!value) return map;
163
                    return OrmUtils.mergeDeep(map, generatedColumn.createValueMap(value));
164
                }, {} as ObjectLiteral);
165

166
                ok({
167
                    result: undefined,
168
                    generatedMap: Object.keys(generatedMap).length > 0 ? generatedMap : undefined
169
                });
170
            }, (err: any) => {
171
                __this.driver.connection.logger.logQueryError(err, sql, parameters, this);
172
                fail(err);
173
            });
174
        });
175
    }*/
176

177
    /**
178
     * Would start a transaction but this driver does not support transactions.
179
     */
180
    async startTransaction(): Promise<void> {
181
        throw new TypeORMError(
×
182
            "Transactions are not supported by the Cordova driver",
183
        )
184
    }
185

186
    /**
187
     * Would start a transaction but this driver does not support transactions.
188
     */
189
    async commitTransaction(): Promise<void> {
190
        throw new TypeORMError(
×
191
            "Transactions are not supported by the Cordova driver",
192
        )
193
    }
194

195
    /**
196
     * Would start a transaction but this driver does not support transactions.
197
     */
198
    async rollbackTransaction(): Promise<void> {
199
        throw new TypeORMError(
×
200
            "Transactions are not supported by the Cordova driver",
201
        )
202
    }
203

204
    /**
205
     * Removes all tables from the currently connected database.
206
     * Be careful with using this method and avoid using it in production or migrations
207
     * (because it can clear all your database).
208
     */
209
    async clearDatabase(): Promise<void> {
210
        await this.query(`PRAGMA foreign_keys = OFF`)
×
211
        try {
×
212
            const selectViewDropsQuery = `SELECT 'DROP VIEW "' || name || '";' as query FROM "sqlite_master" WHERE "type" = 'view'`
×
213
            const dropViewQueries: ObjectLiteral[] = await this.query(
×
214
                selectViewDropsQuery,
215
            )
216

217
            const selectTableDropsQuery = `SELECT 'DROP TABLE "' || name || '";' as query FROM "sqlite_master" WHERE "type" = 'table' AND "name" != 'sqlite_sequence'`
×
218
            const dropTableQueries: ObjectLiteral[] = await this.query(
×
219
                selectTableDropsQuery,
220
            )
221

222
            await Promise.all(
×
223
                dropViewQueries.map((q) => this.query(q["query"])),
×
224
            )
225
            await Promise.all(
×
226
                dropTableQueries.map((q) => this.query(q["query"])),
×
227
            )
228
        } finally {
229
            await this.query(`PRAGMA foreign_keys = ON`)
×
230
        }
231
    }
232

233
    // -------------------------------------------------------------------------
234
    // Protected Methods
235
    // -------------------------------------------------------------------------
236

237
    /**
238
     * Parametrizes given object of values. Used to create column=value queries.
239
     */
240
    protected parametrize(
241
        objectLiteral: ObjectLiteral,
242
        startIndex: number = 0,
×
243
    ): string[] {
244
        return Object.keys(objectLiteral).map((key, index) => `"${key}"` + "=?")
×
245
    }
246
}
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