fixed mssql driver, updated to 4 version of mssql

This commit is contained in:
Umed Khudoiberdiev 2017-06-15 19:51:23 +05:00
parent d59458b513
commit d02f74ae8b
11 changed files with 91 additions and 120 deletions

View File

@ -48,6 +48,7 @@ More env variable names you can find in `ConnectionOptionsEnvReader` class.
* `setLimit` and `setOffset` in `QueryBuilder` were renamed into `limit` and `offset`
* `nativeInterface` has been removed from a driver interface and implementations.
Now
* now typeorm works with the latest version of mssql (version 4)
### DEPRECATIONS
@ -69,6 +70,7 @@ Now
* moved `query`, `transaction` and `createQueryBuilder` to the `Connection`.
`EntityManager` now simply use them from the connection.
* refactored how query runner works, removed query runner provider
* fixed some issues with sqlite, sqlite now strongly works on a single connection
### BUG FIXES

View File

@ -60,7 +60,7 @@
"gulpclass": "^0.1.2",
"mocha": "^2.5.3",
"mongodb": "^2.2.26",
"mssql": "^3.3.0",
"mssql": "^4.0.4",
"mysql": "^2.12.0",
"mysql2": "^1.2.0",
"pg": "^6.1.5",

View File

@ -208,6 +208,7 @@ export class Connection {
async dropDatabase(): Promise<void> {
const queryRunner = await this.driver.createQueryRunner();
await queryRunner.clearDatabase();
await queryRunner.release();
}
/**

View File

@ -113,9 +113,6 @@ export class MysqlQueryRunner implements QueryRunner {
} catch (error) {
await this.rollbackTransaction();
throw error;
} finally {
await this.release();
}
}

View File

@ -121,9 +121,6 @@ export class OracleQueryRunner implements QueryRunner {
} catch (error) {
await this.rollbackTransaction();
throw error;
} finally {
await this.release();
}
}

View File

@ -127,9 +127,6 @@ export class PostgresQueryRunner implements QueryRunner {
} catch (error) {
await this.rollbackTransaction();
throw error;
} finally {
await this.release();
}
}

View File

@ -11,6 +11,7 @@ import {RdbmsSchemaBuilder} from "../../schema-builder/RdbmsSchemaBuilder";
import {SqliteConnectionOptions} from "./SqliteConnectionOptions";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {ColumnType} from "../types/ColumnTypes";
import {QueryRunner} from "../../query-runner/QueryRunner";
/**
* Organizes communication with sqlite DBMS.
@ -36,6 +37,16 @@ export class SqliteDriver implements Driver {
*/
sqlite: any;
/**
* Sqlite has a single QueryRunner because it works on a single database connection.
*/
queryRunner?: QueryRunner;
/**
* Real database connection with sqlite database.
*/
databaseConnection: any;
// -------------------------------------------------------------------------
// Public Implemented Properties
// -------------------------------------------------------------------------
@ -114,22 +125,18 @@ export class SqliteDriver implements Driver {
/**
* Performs connection to the database.
*/
connect(): Promise<void> {
return Promise.resolve();
async connect(): Promise<void> {
this.databaseConnection = await this.createDatabaseConnection();
}
/**
* Closes connection with database.
*/
disconnect(): Promise<void> {
return Promise.resolve();
// todo: what to do with this function?
// return new Promise<void>((ok, fail) => {
// const handler = (err: any) => err ? fail(err) : ok();
// if (!this.databaseConnection)
// return fail(new ConnectionIsNotSetError("sqlite"));
// this.databaseConnection.connection.close(handler);
// });
async disconnect(): Promise<void> {
return new Promise<void>((ok, fail) => {
this.queryRunner = undefined;
this.databaseConnection.close((err: any) => err ? fail(err) : ok());
});
}
/**
@ -143,7 +150,10 @@ export class SqliteDriver implements Driver {
* Creates a query runner used to execute database queries.
*/
createQueryRunner() {
return new SqliteQueryRunner(this);
if (!this.queryRunner)
this.queryRunner = new SqliteQueryRunner(this);
return this.queryRunner;
}
/**
@ -300,6 +310,24 @@ export class SqliteDriver implements Driver {
// Protected Methods
// -------------------------------------------------------------------------
/**
* Creates connection with the database.
*/
protected createDatabaseConnection() {
return new Promise<void>((ok, fail) => {
const databaseConnection = new this.sqlite.Database(this.options.database, (err: any) => {
if (err) return fail(err);
// we need to enable foreign keys in sqlite to make sure all foreign key related features
// working properly. this also makes onDelete to work with sqlite.
databaseConnection.run(`PRAGMA foreign_keys = ON;`, (err: any, result: any) => {
if (err) return fail(err);
ok(databaseConnection);
});
});
});
}
/**
* If driver dependency is not given explicitly, then try to load it via "require".
*/
@ -307,7 +335,7 @@ export class SqliteDriver implements Driver {
try {
this.sqlite = PlatformTools.load("sqlite3").verbose();
} catch (e) { // todo: better error for browser env
} catch (e) {
throw new DriverPackageNotInstalledError("SQLite", "sqlite3");
}
}

View File

@ -65,44 +65,15 @@ export class SqliteQueryRunner implements QueryRunner {
* Returns obtained database connection.
*/
connect(): Promise<any> {
if (this.databaseConnection)
return Promise.resolve(this.databaseConnection);
if (this.databaseConnectionPromise)
return this.databaseConnectionPromise;
this.databaseConnectionPromise = new Promise<void>((ok, fail) => {
this.databaseConnection = new this.driver.sqlite.Database(this.driver.options.database, (err: any) => {
if (err) {
this.databaseConnection = null;
return fail(err);
}
// we need to enable foreign keys in sqlite to make sure all foreign key related features
// working properly. this also makes onDelete to work with sqlite.
this.databaseConnection.run(`PRAGMA foreign_keys = ON;`, (err: any, result: any) => {
if (err)
return fail(err);
ok(this.databaseConnection);
});
});
});
return this.databaseConnectionPromise;
return Promise.resolve(this.driver.databaseConnection);
}
/**
* Releases used database connection.
* You cannot use query runner methods once its released.
* We don't do anything here because sqlite do not support multiple connections thus query runners.
*/
release(): Promise<void> {
return new Promise<void>((ok, fail) => {
const handler = (err: any) => err ? fail(err) : ok();
if (this.databaseConnection)
this.databaseConnection.close(handler);
this.isReleased = true;
});
return Promise.resolve();
}
/**
@ -126,7 +97,6 @@ export class SqliteQueryRunner implements QueryRunner {
} finally {
await this.query(`PRAGMA foreign_keys = ON;`);
await this.release();
}
}

View File

@ -149,7 +149,7 @@ export class SqlServerDriver implements Driver {
// pooling is enabled either when its set explicitly to true,
// either when its not defined at all (e.g. enabled by default)
return new Promise<void>((ok, fail) => {
const connection = new this.mssql.Connection(options).connect((err: any) => {
const connection = new this.mssql.ConnectionPool(options).connect((err: any) => {
if (err) return fail(err);
this.connectionPool = connection;
ok();

View File

@ -41,14 +41,13 @@ export class SqlServerQueryRunner implements QueryRunner {
protected databaseConnection: any;
/**
* Transaction instance opened for this query executor.
* Last executed query in a transaction.
* This is needed because in transaction mode mssql cannot execute parallel queries,
* that's why we store last executed query promise to wait it when we execute next query.
*
* @see https://github.com/patriksimek/node-mssql/issues/491
*/
protected transaction: any;
/**
* Special callback provided by a driver used to release a created connection.
*/
protected databaseConnectionPromise: Promise<any>;
protected queryResponsibilityChain: Promise<any>[] = [];
// -------------------------------------------------------------------------
// Constructor
@ -66,19 +65,7 @@ export class SqlServerQueryRunner implements QueryRunner {
* Returns obtained database connection.
*/
connect(): Promise<any> {
if (this.databaseConnection)
return Promise.resolve(this.databaseConnection);
if (this.databaseConnectionPromise)
return this.databaseConnectionPromise;
this.databaseConnectionPromise = new Promise((ok, fail) => {
const driver = this.driver as SqlServerDriver;
this.databaseConnection = driver.connectionPool; // todo: fix it
ok(this.databaseConnection);
});
return this.databaseConnectionPromise;
return Promise.resolve();
}
/**
@ -87,7 +74,6 @@ export class SqlServerQueryRunner implements QueryRunner {
*/
release(): Promise<void> {
this.isReleased = true;
// todo
return Promise.resolve();
}
@ -120,36 +106,7 @@ export class SqlServerQueryRunner implements QueryRunner {
} catch (error) {
await this.rollbackTransaction();
throw error;
} finally {
await this.release();
}
// const selectDropsQuery = `SELECT 'DROP TABLE "' + TABLE_NAME + '"' as query FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE';`;
// const dropQueries: ObjectLiteral[] = await this.query(selectDropsQuery);
// const allQueries = [`EXEC sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"`]
// .concat(dropQueries.map(q => this.query(q["query"])).join("; "));
//
// return new Promise<void>((ok, fail) => {
//
// const request = new this.driver.mssql.Request(this.isTransactionActive() ? this.databaseConnection.transaction : this.databaseConnection.connection);
// request.multiple = true;
// request.query(allQueries, (err: any, result: any) => {
// if (err) {
// this.connection.logger.logFailedQuery(allQueries);
// this.connection.logger.logQueryError(err);
// return fail(err);
// }
//
// ok();
// });
// });
// const selectDropsQuery = `SELECT 'DROP TABLE "' + TABLE_NAME + '";' as query FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE';`;
// const dropQueries: ObjectLiteral[] = await this.query(selectDropsQuery);
// await this.query(`EXEC sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"`);
// await Promise.all(dropQueries.map(q => this.query(q["query"])));
// await this.query(`EXEC sp_msforeachtable 'drop table [?]'`);
}
/**
@ -164,9 +121,8 @@ export class SqlServerQueryRunner implements QueryRunner {
return new Promise<void>(async (ok, fail) => {
this.isTransactionActive = true;
const dbConnection = await this.connect();
this.transaction = dbConnection.transaction();
this.transaction.begin((err: any) => {
this.databaseConnection = this.driver.connectionPool.transaction();
this.databaseConnection.begin((err: any) => {
if (err) {
this.isTransactionActive = false;
return fail(err);
@ -188,9 +144,10 @@ export class SqlServerQueryRunner implements QueryRunner {
throw new TransactionNotStartedError();
return new Promise<void>((ok, fail) => {
this.transaction.commit((err: any) => {
this.databaseConnection.commit((err: any) => {
if (err) return fail(err);
this.isTransactionActive = false;
this.databaseConnection = null;
ok();
});
});
@ -208,9 +165,10 @@ export class SqlServerQueryRunner implements QueryRunner {
throw new TransactionNotStartedError();
return new Promise<void>((ok, fail) => {
this.transaction.rollback((err: any) => {
this.databaseConnection.rollback((err: any) => {
if (err) return fail(err);
this.isTransactionActive = false;
this.databaseConnection = null;
ok();
});
});
@ -219,31 +177,54 @@ export class SqlServerQueryRunner implements QueryRunner {
/**
* Executes a given SQL query.
*/
query(query: string, parameters?: any[]): Promise<any> {
async query(query: string, parameters?: any[]): Promise<any> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
return new Promise(async (ok, fail) => {
let waitingOkay: Function;
const waitingPromise = new Promise((ok) => waitingOkay = ok);
if (this.queryResponsibilityChain.length) {
const otherWaitingPromises = [...this.queryResponsibilityChain];
this.queryResponsibilityChain.push(waitingPromise);
await Promise.all(otherWaitingPromises);
}
const promise = new Promise(async (ok, fail) => {
this.driver.connection.logger.logQuery(query, parameters);
const mssql = this.driver.mssql;
const dbConnection = await this.connect();
const request = new mssql.Request(this.isTransactionActive ? this.transaction : dbConnection);
const request = new this.driver.mssql.Request(this.isTransactionActive ? this.databaseConnection : this.driver.connectionPool);
if (parameters && parameters.length) {
parameters.forEach((parameter, index) => {
request.input(index, parameters![index]);
});
}
request.query(query, (err: any, result: any) => {
const resolveChain = () => {
if (promiseIndex !== -1)
this.queryResponsibilityChain.splice(promiseIndex, 1);
if (waitingPromiseIndex !== -1)
this.queryResponsibilityChain.splice(waitingPromiseIndex, 1);
waitingOkay();
};
let promiseIndex = this.queryResponsibilityChain.indexOf(promise);
let waitingPromiseIndex = this.queryResponsibilityChain.indexOf(waitingPromise);
if (err) {
this.driver.connection.logger.logFailedQuery(query, parameters);
this.driver.connection.logger.logQueryError(err);
resolveChain();
return fail(err);
}
ok(result);
ok(result.recordset);
resolveChain();
});
});
if (this.isTransactionActive)
this.queryResponsibilityChain.push(promise);
return promise;
}
/**

View File

@ -117,8 +117,6 @@ export class WebsqlQueryRunner implements QueryRunner {
await this.rollbackTransaction();
throw error;
} finally {
await this.release();
// await this.query(`PRAGMA foreign_keys = ON;`);
}
}