Skip to content

Commit fc4133c

Browse files
authored
fix: prevent transactions in the Cordova driver (typeorm#7771)
the cordova driver silently ignores all attempts to begin or commit transactions. this leads to data loss and invalid operations occurring when developers expect transactions to work and then they are silently ignored
1 parent 5cf368a commit fc4133c

File tree

2 files changed

+58
-7
lines changed

2 files changed

+58
-7
lines changed

src/driver/cordova/CordovaQueryRunner.ts

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import {Broadcaster} from "../../subscriber/Broadcaster";
99
* Runs queries on a single sqlite database connection.
1010
*/
1111
export class CordovaQueryRunner extends AbstractSqliteQueryRunner {
12-
12+
1313
/**
1414
* Database driver used by connection.
1515
*/
1616
driver: CordovaDriver;
17-
17+
1818
// -------------------------------------------------------------------------
1919
// Constructor
2020
// -------------------------------------------------------------------------
@@ -54,7 +54,7 @@ export class CordovaQueryRunner extends AbstractSqliteQueryRunner {
5454
for (let i = 0; i < result.rows.length; i++) {
5555
resultSet.push(result.rows.item(i));
5656
}
57-
57+
5858
ok(resultSet);
5959
}
6060
}, (err: any) => {
@@ -98,6 +98,48 @@ export class CordovaQueryRunner extends AbstractSqliteQueryRunner {
9898
});
9999
}*/
100100

101+
/**
102+
* Would start a transaction but this driver does not support transactions.
103+
*/
104+
async startTransaction(): Promise<void> {
105+
throw new Error('Transactions are not supported by the Cordova driver')
106+
}
107+
108+
/**
109+
* Would start a transaction but this driver does not support transactions.
110+
*/
111+
async commitTransaction(): Promise<void> {
112+
throw new Error('Transactions are not supported by the Cordova driver')
113+
}
114+
115+
/**
116+
* Would start a transaction but this driver does not support transactions.
117+
*/
118+
async rollbackTransaction(): Promise<void> {
119+
throw new Error('Transactions are not supported by the Cordova driver')
120+
}
121+
122+
/**
123+
* Removes all tables from the currently connected database.
124+
* Be careful with using this method and avoid using it in production or migrations
125+
* (because it can clear all your database).
126+
*/
127+
async clearDatabase(): Promise<void> {
128+
await this.query(`PRAGMA foreign_keys = OFF;`);
129+
try {
130+
const selectViewDropsQuery = `SELECT 'DROP VIEW "' || name || '";' as query FROM "sqlite_master" WHERE "type" = 'view'`;
131+
const dropViewQueries: ObjectLiteral[] = await this.query(selectViewDropsQuery);
132+
133+
const selectTableDropsQuery = `SELECT 'DROP TABLE "' || name || '";' as query FROM "sqlite_master" WHERE "type" = 'table' AND "name" != 'sqlite_sequence'`;
134+
const dropTableQueries: ObjectLiteral[] = await this.query(selectTableDropsQuery);
135+
136+
await Promise.all(dropViewQueries.map(q => this.query(q["query"])));
137+
await Promise.all(dropTableQueries.map(q => this.query(q["query"])));
138+
} finally {
139+
await this.query(`PRAGMA foreign_keys = ON;`);
140+
}
141+
}
142+
101143
// -------------------------------------------------------------------------
102144
// Protected Methods
103145
// -------------------------------------------------------------------------
@@ -108,4 +150,4 @@ export class CordovaQueryRunner extends AbstractSqliteQueryRunner {
108150
protected parametrize(objectLiteral: ObjectLiteral, startIndex: number = 0): string[] {
109151
return Object.keys(objectLiteral).map((key, index) => `"${key}"` + "=?");
110152
}
111-
}
153+
}

src/schema-builder/RdbmsSchemaBuilder.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,15 @@ export class RdbmsSchemaBuilder implements SchemaBuilder {
6767
this.queryRunner = this.connection.createQueryRunner();
6868
// CockroachDB implements asynchronous schema sync operations which can not been executed in transaction.
6969
// E.g. if you try to DROP column and ADD it again in the same transaction, crdb throws error.
70-
if (!(this.connection.driver instanceof CockroachDriver))
70+
const isUsingTransactions = (
71+
!(this.connection.driver instanceof CockroachDriver) &&
72+
this.connection.options.migrationsTransactionMode !== "none"
73+
);
74+
75+
if (isUsingTransactions) {
7176
await this.queryRunner.startTransaction();
77+
}
78+
7279
try {
7380
const tablePaths = this.entityToSyncMetadatas.map(metadata => metadata.tablePath);
7481
// TODO: typeorm_metadata table needs only for Views for now.
@@ -83,14 +90,16 @@ export class RdbmsSchemaBuilder implements SchemaBuilder {
8390
if (this.connection.queryResultCache)
8491
await this.connection.queryResultCache.synchronize(this.queryRunner);
8592

86-
if (!(this.connection.driver instanceof CockroachDriver))
93+
if (isUsingTransactions) {
8794
await this.queryRunner.commitTransaction();
95+
}
8896

8997
} catch (error) {
9098

9199
try { // we throw original error even if rollback thrown an error
92-
if (!(this.connection.driver instanceof CockroachDriver))
100+
if (isUsingTransactions) {
93101
await this.queryRunner.rollbackTransaction();
102+
}
94103
} catch (rollbackError) { }
95104
throw error;
96105

0 commit comments

Comments
 (0)