diff --git a/lib/postgresql.js b/lib/postgresql.js index 26f09e9c..0a57ef90 100644 --- a/lib/postgresql.js +++ b/lib/postgresql.js @@ -194,8 +194,17 @@ PostgreSQL.prototype.executeSQL = function(sql, params, options, callback) { } var transaction = options.transaction; - if (transaction && transaction.connection && - transaction.connector === this) { + if (transaction && transaction.connector === this) { + if (!transaction.connection) { + return process.nextTick(function() { + callback(new Error(g.f('Connection does not exist'))); + }); + } + if (transaction.txId !== transaction.connection.txId) { + return process.nextTick(function() { + callback(new Error(g.f('Transaction is not active'))); + }); + } debug('Execute SQL within a transaction'); // Do not release the connection executeWithConnection(transaction.connection, null); diff --git a/lib/transaction.js b/lib/transaction.js index 9db790fb..03c79e2b 100644 --- a/lib/transaction.js +++ b/lib/transaction.js @@ -5,6 +5,8 @@ 'use strict'; var debug = require('debug')('loopback:connector:postgresql:transaction'); +var uuid = require('uuid'); +var Transaction = require('loopback-connector').Transaction; module.exports = mixinTransaction; @@ -18,6 +20,7 @@ function mixinTransaction(PostgreSQL) { * @param cb */ PostgreSQL.prototype.beginTransaction = function(isolationLevel, cb) { + var connector = this; debug('Begin a transaction with isolation level: %s', isolationLevel); this.pg.connect(function(err, connection, done) { if (err) return cb(err); @@ -25,7 +28,10 @@ function mixinTransaction(PostgreSQL) { connection.query('BEGIN TRANSACTION ISOLATION LEVEL ' + isolationLevel, function(err) { if (err) return cb(err); - cb(null, connection); + var tx = new Transaction(connector, connection); + tx.txId = uuid.v1(); + connection.txId = tx.txId; + cb(null, tx); }); }); }; @@ -65,6 +71,7 @@ function mixinTransaction(PostgreSQL) { PostgreSQL.prototype.releaseConnection = function(connection, err) { if (typeof connection.autorelease === 'function') { + connection.txId = null; connection.autorelease(err); connection.autorelease = null; } else { diff --git a/package.json b/package.json index 45d64eca..9a4b51a2 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "debug": "^2.1.1", "loopback-connector": "^4.0.0", "pg": "^6.0.0", - "strong-globalize": "^2.6.2" + "strong-globalize": "^2.6.2", + "uuid": "^3.0.1" }, "devDependencies": { "eslint": "^2.13.1", diff --git a/test/postgresql.transaction.test.js b/test/postgresql.transaction.test.js index a9926498..eb0d3f63 100644 --- a/test/postgresql.transaction.test.js +++ b/test/postgresql.transaction.test.js @@ -71,11 +71,11 @@ describe('transactions', function() { Post.create(post, {transaction: tx}, function(err, p) { if (err) { - done(err); + return done(err); } else { tx.commit(function(err) { if (err) { - done(err); + return done(err); } completed++; checkResults(); @@ -124,4 +124,29 @@ describe('transactions', function() { it('should not see the rolledback insert', expectToFindPosts(post, 0)); }); + + describe('finished', function() { + var post = {title: 't2', content: 'c2'}; + beforeEach(createPostInTx(post)); + + it('should throw an error when creating in a committed transaction', function(done) { + currentTx.commit(function(err) { + if (err) return done(err); + Post.create({title: 't4', content: 'c4'}, {transaction: currentTx}, function(err, post) { + if (!err) return done(new Error('should throw error')); + done(); + }); + }); + }); + + it('should throw an error when creating in a rolled back transaction', function(done) { + currentTx.rollback(function(err) { + if (err) return done(err); + Post.create({title: 't4', content: 'c4'}, {transaction: currentTx}, function(err, post) { + if (!err) return done(new Error('should throw error')); + done(); + }); + }); + }); + }); });