Skip to content

Transaction support for connections #586

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Oct 4, 2013
40 changes: 39 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,45 @@ connection.query(options, function(err, results) {
});
```

## Transactions

Simple transaction support is available at the connection level:

```js
connection.beginTransaction(function(err) {
if (err) { throw err; }
connection.query('INSERT INTO posts SET title=?', title, function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
}

var log = 'Post ' + result.insertId + ' added';

connection.query('INSERT INTO log SET data=?', log, function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
}
connection.commit(function(err) {
if (err) {
connection.rollback(function() {
throw err;
});
}
console.log('success!');
});
});
});
});
```
Please note that beginTransaction(), commit() and rollback() are simply convenience
functions that execute the START TRANSACTION, COMMIT, and ROLLBACK commands respectively.
It is important to understand that many commands in MySQL can cause an implicit commit,
as described [in the MySQL documentation](http://dev.mysql.com/doc/refman/5.5/en/implicit-commit.html)

## Error handling

This module comes with a consistent approach to error handling that you should
Expand Down Expand Up @@ -973,4 +1012,3 @@ For example, if you have an installation of mysql running on localhost:3306 and
* Prepared statements
* setTimeout() for Connection / Query
* Support for encodings other than UTF-8 / ASCII
* API support for transactions, similar to [php](http://www.php.net/manual/en/mysqli.quickstart.transactions.php)
31 changes: 31 additions & 0 deletions lib/Connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ Connection.prototype.connect = function(cb) {
};

Connection.prototype.changeUser = function(options, cb){
cb = cb || function() {};

this._implyConnect();

if (typeof options === 'function') {
Expand All @@ -84,6 +86,8 @@ Connection.prototype.changeUser = function(options, cb){
? Config.getCharsetNumber(options.charset)
: this.config.charsetNumber;

var self = this;

return this._protocol.changeUser({
user : options.user || this.config.user,
password : options.password || this.config.password,
Expand All @@ -93,6 +97,33 @@ Connection.prototype.changeUser = function(options, cb){
}, cb);
};

Connection.prototype.beginTransaction = function(cb) {
this._implyConnect();

var query = Connection.createQuery('START TRANSACTION', cb);
query._connection = this;

return this._protocol._enqueue(query);
};

Connection.prototype.commit = function(cb) {
this._implyConnect();

var query = Connection.createQuery('COMMIT', cb);
query._connection = this;

return this._protocol._enqueue(query);
};

Connection.prototype.rollback = function(cb) {
this._implyConnect();

var query = Connection.createQuery('ROLLBACK', cb);
query._connection = this;

return this._protocol._enqueue(query);
};

Connection.prototype.query = function(sql, values, cb) {
this._implyConnect();

Expand Down
52 changes: 24 additions & 28 deletions test/integration/connection/test-transaction-commit.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,32 @@ common.useTestDb(connection);

var table = 'transaction_test';
connection.query([
'CREATE TEMPORARY TABLE `' + table + '` (',
'`id` int(11) unsigned NOT NULL AUTO_INCREMENT,',
'`title` varchar(255),',
'PRIMARY KEY (`id`)',
') ENGINE=InnoDB DEFAULT CHARSET=utf8'
].join('\n'));

connection.query('START TRANSACTION');

var rowCount = 10;
for (var i = 1; i <= rowCount; i++) {
'CREATE TEMPORARY TABLE `' + table + '` (',
'`id` int(11) unsigned NOT NULL AUTO_INCREMENT,',
'`title` varchar(255),',
'PRIMARY KEY (`id`)',
') ENGINE=InnoDB DEFAULT CHARSET=utf8'
].join('\n'));

connection.beginTransaction(function (err) {
assert.ifError(err);

var row = {
id: i,
title: 'Row #' + i,
id: 1,
title: 'Test row'
};

connection.query('INSERT INTO ' + table + ' SET ?', row);
}

connection.query('COMMIT');

var rows = [];
var query = connection.query('SELECT * FROM ' + table, function(err, _rows) {
if (err) throw err;

rows = _rows;
});
connection.query('INSERT INTO ' + table + ' SET ?', row, function(err) {
assert.ifError(err);

connection.end();
connection.commit(function(err) {
assert.ifError(err);

process.on('exit', function() {
assert.equal(rows.length, rowCount);
});
connection.query('SELECT * FROM ' + table, function(err, rows) {
assert.ifError(err);
connection.end();
assert.equal(rows.length, 1);
});
});
});
});
52 changes: 24 additions & 28 deletions test/integration/connection/test-transaction-rollback.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,32 @@ common.useTestDb(connection);

var table = 'transaction_test';
connection.query([
'CREATE TEMPORARY TABLE `' + table + '` (',
'`id` int(11) unsigned NOT NULL AUTO_INCREMENT,',
'`title` varchar(255),',
'PRIMARY KEY (`id`)',
') ENGINE=InnoDB DEFAULT CHARSET=utf8'
].join('\n'));

connection.query('START TRANSACTION');

var rowCount = 10;
for (var i = 1; i <= rowCount; i++) {
'CREATE TEMPORARY TABLE `' + table + '` (',
'`id` int(11) unsigned NOT NULL AUTO_INCREMENT,',
'`title` varchar(255),',
'PRIMARY KEY (`id`)',
') ENGINE=InnoDB DEFAULT CHARSET=utf8'
].join('\n'));

connection.beginTransaction(function (err) {
assert.ifError(err);

var row = {
id: i,
title: 'Row #' + i,
id: 1,
title: 'Test row'
};

connection.query('INSERT INTO ' + table + ' SET ?', row);
}

connection.query('ROLLBACK');

var rows;
var query = connection.query('SELECT * FROM ' + table, function(err, _rows) {
if (err) throw err;

rows = _rows;
});
connection.query('INSERT INTO ' + table + ' SET ?', row, function(err) {
assert.ifError(err);

connection.end();
connection.rollback(function(err) {
assert.ifError(err);

process.on('exit', function() {
assert.equal(rows.length, 0);
});
connection.query('SELECT * FROM ' + table, function(err, rows) {
assert.ifError(err);
connection.end();
assert.equal(rows.length, 0);
});
});
});
});