mirror of
https://github.com/knex/knex.git
synced 2025-12-28 23:48:58 +00:00
Implement support for returning started transaction without using transaction() methods callback function (#3099)
This commit is contained in:
parent
c9d4a22413
commit
5307dacc66
@ -23,5 +23,6 @@ module.exports = {
|
||||
env: {
|
||||
node: true,
|
||||
mocha: true,
|
||||
es6: true,
|
||||
},
|
||||
};
|
||||
|
||||
@ -164,7 +164,7 @@ assign(Client_SQLite3.prototype, {
|
||||
|
||||
formatter() {
|
||||
return new SQLite3_Formatter(this, ...arguments);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default Client_SQLite3;
|
||||
|
||||
@ -18,6 +18,16 @@ export default class Transaction extends EventEmitter {
|
||||
|
||||
const txid = (this.txid = uniqueId('trx'));
|
||||
|
||||
// If there is no container provided, assume user wants to get instance of transaction and use it directly
|
||||
if (!container) {
|
||||
this.initPromise = new Promise((resolve, reject) => {
|
||||
this.initRejectFn = reject;
|
||||
container = (transactor) => {
|
||||
resolve(transactor);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
this.client = client;
|
||||
this.logger = client.logger;
|
||||
this.outerTx = outerTx;
|
||||
@ -67,14 +77,24 @@ export default class Transaction extends EventEmitter {
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.catch((e) => this._rejecter(e));
|
||||
.catch((e) => {
|
||||
if (this.initRejectFn) {
|
||||
this.initRejectFn();
|
||||
}
|
||||
return this._rejecter(e);
|
||||
});
|
||||
|
||||
return new Promise((resolver, rejecter) => {
|
||||
this._resolver = resolver;
|
||||
this._rejecter = rejecter;
|
||||
});
|
||||
}
|
||||
);
|
||||
).catch((err) => {
|
||||
if (this.initRejectFn) {
|
||||
this.initRejectFn(err);
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
|
||||
this._completed = false;
|
||||
|
||||
|
||||
@ -34,12 +34,31 @@ function initContext(knexFn) {
|
||||
return batchInsert(this, table, batch, chunkSize);
|
||||
},
|
||||
|
||||
// Runs a new transaction, taking a container and returning a promise
|
||||
// for when the transaction is resolved.
|
||||
// Creates a new transaction.
|
||||
// If container is provided, returns a promise for when the transaction is resolved.
|
||||
// If container is not provided, returns a promise with a transaction that is resolved
|
||||
// when transaction is ready to be used.
|
||||
transaction(container, config) {
|
||||
const trx = this.client.transaction(container, config);
|
||||
trx.userParams = this.userParams;
|
||||
return trx;
|
||||
|
||||
if (container) {
|
||||
return trx;
|
||||
}
|
||||
// If no container was passed, assume user wants to get a transaction and use it directly
|
||||
else {
|
||||
return trx.initPromise;
|
||||
}
|
||||
},
|
||||
|
||||
transactionProvider(config) {
|
||||
let trx;
|
||||
return () => {
|
||||
if (!trx) {
|
||||
trx = this.transaction(config);
|
||||
}
|
||||
return trx;
|
||||
};
|
||||
},
|
||||
|
||||
// Typically never needed, initializes the pool for a knex client.
|
||||
@ -131,6 +150,7 @@ function redefineProperties(knex, client) {
|
||||
knex.raw = context.raw;
|
||||
knex.batchInsert = context.batchInsert;
|
||||
knex.transaction = context.transaction;
|
||||
knex.transactionProvider = context.transactionProvider;
|
||||
knex.initialize = context.initialize;
|
||||
knex.destroy = context.destroy;
|
||||
knex.ref = context.ref;
|
||||
|
||||
@ -35,6 +35,26 @@ module.exports = function(knex) {
|
||||
});
|
||||
});
|
||||
|
||||
it('supports direct retrieval of a transaction without a callback', () => {
|
||||
const trxPromise = knex.transaction();
|
||||
const query =
|
||||
knex.client.driverName === 'oracledb'
|
||||
? '1 as "result" from DUAL'
|
||||
: '1 as result';
|
||||
|
||||
let transaction;
|
||||
return trxPromise
|
||||
.then((trx) => {
|
||||
transaction = trx;
|
||||
expect(trx.client.transacting).to.equal(true);
|
||||
return knex.transacting(trx).select(knex.raw(query));
|
||||
})
|
||||
.then((rows) => {
|
||||
expect(rows[0].result).to.equal(1);
|
||||
return transaction.commit();
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw when null transaction is sent to transacting', function() {
|
||||
return knex
|
||||
.transaction(function(t) {
|
||||
|
||||
@ -19,6 +19,16 @@ var pool = {
|
||||
},
|
||||
};
|
||||
|
||||
var poolSqlite = {
|
||||
min: 0,
|
||||
max: 1,
|
||||
acquireTimeoutMillis: 5000,
|
||||
afterCreate: function(connection, callback) {
|
||||
assert.ok(typeof connection.__knexUid !== 'undefined');
|
||||
callback(null, connection);
|
||||
},
|
||||
};
|
||||
|
||||
var mysqlPool = _.extend({}, pool, {
|
||||
afterCreate: function(connection, callback) {
|
||||
Promise.promisify(connection.query, { context: connection })(
|
||||
@ -50,8 +60,8 @@ var testConfigs = {
|
||||
charset: 'utf8',
|
||||
},
|
||||
pool: mysqlPool,
|
||||
migrations: migrations,
|
||||
seeds: seeds,
|
||||
migrations,
|
||||
seeds,
|
||||
},
|
||||
|
||||
mysql2: {
|
||||
@ -65,8 +75,8 @@ var testConfigs = {
|
||||
charset: 'utf8',
|
||||
},
|
||||
pool: mysqlPool,
|
||||
migrations: migrations,
|
||||
seeds: seeds,
|
||||
migrations,
|
||||
seeds,
|
||||
},
|
||||
|
||||
oracledb: {
|
||||
@ -78,8 +88,8 @@ var testConfigs = {
|
||||
// https://github.com/oracle/node-oracledb/issues/525
|
||||
stmtCacheSize: 0,
|
||||
},
|
||||
pool: pool,
|
||||
migrations: migrations,
|
||||
pool,
|
||||
migrations,
|
||||
},
|
||||
|
||||
postgres: {
|
||||
@ -92,9 +102,9 @@ var testConfigs = {
|
||||
user: 'testuser',
|
||||
password: 'knextest',
|
||||
},
|
||||
pool: pool,
|
||||
migrations: migrations,
|
||||
seeds: seeds,
|
||||
pool,
|
||||
migrations,
|
||||
seeds,
|
||||
},
|
||||
|
||||
redshift: {
|
||||
@ -107,9 +117,9 @@ var testConfigs = {
|
||||
port: '5439',
|
||||
host: process.env.REDSHIFT_HOST || '127.0.0.1',
|
||||
},
|
||||
pool: pool,
|
||||
migrations: migrations,
|
||||
seeds: seeds,
|
||||
pool,
|
||||
migrations,
|
||||
seeds,
|
||||
},
|
||||
|
||||
sqlite3: {
|
||||
@ -117,9 +127,9 @@ var testConfigs = {
|
||||
connection: testConfig.sqlite3 || {
|
||||
filename: __dirname + '/test.sqlite3',
|
||||
},
|
||||
pool,
|
||||
migrations: migrations,
|
||||
seeds: seeds,
|
||||
pool: poolSqlite,
|
||||
migrations,
|
||||
seeds,
|
||||
},
|
||||
|
||||
mssql: {
|
||||
@ -132,8 +142,8 @@ var testConfigs = {
|
||||
database: 'knex_test',
|
||||
},
|
||||
pool: pool,
|
||||
migrations: migrations,
|
||||
seeds: seeds,
|
||||
migrations,
|
||||
seeds,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -310,6 +310,40 @@ describe('knex', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('propagates error correctly when all connections are in use', function() {
|
||||
this.timeout(30000);
|
||||
const knex = Knex(sqliteConfig);
|
||||
return knex
|
||||
.transaction()
|
||||
.then(() => {
|
||||
return knex.transaction();
|
||||
})
|
||||
.then(() => {
|
||||
throw new Error('Should not reach here');
|
||||
})
|
||||
.catch((err) => {
|
||||
expect(err.message).to.include('Timeout acquiring a connection');
|
||||
});
|
||||
});
|
||||
|
||||
it('supports direct retrieval of a transaction from provider', () => {
|
||||
const knex = Knex(sqliteConfig);
|
||||
const trxProvider = knex.transactionProvider();
|
||||
const trxPromise = trxProvider();
|
||||
|
||||
let transaction;
|
||||
return trxPromise
|
||||
.then((trx) => {
|
||||
transaction = trx;
|
||||
expect(trx.client.transacting).to.equal(true);
|
||||
return knex.transacting(trx).select(knex.raw('1 as result'));
|
||||
})
|
||||
.then((rows) => {
|
||||
expect(rows[0].result).to.equal(1);
|
||||
return transaction.commit();
|
||||
});
|
||||
});
|
||||
|
||||
it('creating transaction copy with user params should throw an error', () => {
|
||||
if (!sqliteConfig) {
|
||||
return;
|
||||
|
||||
@ -1427,7 +1427,7 @@ describe('QueryBuilder', function() {
|
||||
sql:
|
||||
'select * from `users` where (`a`, `b`) in ( values (?, ?), (?, ?), (?, ?))',
|
||||
bindings: [1, 2, 3, 4, 5, 6],
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@ -672,7 +672,7 @@ describe('OracleDb SchemaBuilder', function() {
|
||||
tableSql = client
|
||||
.schemaBuilder()
|
||||
.table('users', function() {
|
||||
this.dateTime('foo', {useTz: true});
|
||||
this.dateTime('foo', { useTz: true });
|
||||
})
|
||||
.toSQL();
|
||||
equal(1, tableSql.length);
|
||||
@ -694,7 +694,7 @@ describe('OracleDb SchemaBuilder', function() {
|
||||
tableSql = client
|
||||
.schemaBuilder()
|
||||
.table('users', function() {
|
||||
this.dateTime('foo', {useTz: false});
|
||||
this.dateTime('foo', { useTz: false });
|
||||
})
|
||||
.toSQL();
|
||||
equal(1, tableSql.length);
|
||||
|
||||
11
types/index.d.ts
vendored
11
types/index.d.ts
vendored
@ -266,8 +266,17 @@ interface Knex<TRecord extends {} = any, TResult = unknown[]>
|
||||
__knex__: string;
|
||||
|
||||
raw: Knex.RawBuilder<TRecord, TResult>;
|
||||
|
||||
transactionProvider(
|
||||
config?: any
|
||||
): () => Promise<Knex.Transaction>;
|
||||
transaction(
|
||||
transactionScope?: undefined | null,
|
||||
config?: any
|
||||
): Promise<Knex.Transaction>;
|
||||
transaction<T>(
|
||||
transactionScope: (trx: Knex.Transaction) => Promise<T> | Bluebird<T> | void
|
||||
transactionScope: (trx: Knex.Transaction) => Promise<T> | Bluebird<T> | void,
|
||||
config?: any
|
||||
): Bluebird<T>;
|
||||
initialize(config?: Knex.Config): void;
|
||||
destroy(callback: Function): void;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user