Skip to content
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

This PR adds support for .transaction() #814

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,25 @@ async function main() {
);
```

### Using managed transaction

When using a promise pool, MySQL2 offers a .transaction() function to allow you to execute code within a transaction, with pre-defined behavior to roll back the transaction on error, and which automatically releases the connection after you are finished working with it:
```js
async function main() {
// get the client
const mysql = require('mysql2/promise');
// create the pool
const pool = mysql.createPool({host:'localhost', user: 'root', database: 'test'});
// using the pool, execute a chain of queries within a transaction
pool.transaction({ autoCommit: false, readWrite: true, consistentSnapshot: false }, async function(con) {
const [rows, fields] = await con.query('SELECT * FROM `table` WHERE `name` = ? AND `age` > ?', ['Morty', 14]);
await con.execute('INSERT INTO `table` (name,age) VALUES(?,?)', ['Bob',rows[0].age]); // Bob and Morty are the same age
});
// If the promise chain passed to .transaction() resolves, .transaction() commits the changes, and releases the connection.
// If the promise chain passed to .transaction() rejects, or if a connection or SQL error occures, .transaction() rolls back the changes, and releases the connection.
}
```

## API and Configuration

MySQL2 is mostly API compatible with [Node MySQL][node-mysql]. You should check their API documentation to see all available API options.
Expand Down
56 changes: 56 additions & 0 deletions promise.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,47 @@ PromiseConnection.prototype.changeUser = function(options) {
});
});
};
PromiseConnection.prototype.transaction = function(options,userPromise) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/nitpick userPromise is not a promise, its a callback that will return a promise, name should reflect that

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick accepted

const self = this;
if (! userPromise) {
userPromise = options;
options = {};
}
options = options || {};

var promiseChain = Promise.resolve();

if (options.autoCommit !== true) {
promiseChain = promiseChain.then(function() {
return self.query(
"START TRANSACTION" +
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might also want to support isolation levels https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html

A constant type can be exported directly by require('mysql2').ISOLATION_LEVELS

( options.consistentSnapshot === true ? " WITH CONSISTENT SNAPSHOT" :
( options.readWrite !== false ? " READ WRITE" : " READ ONLY" )
)
);
});
}

promiseChain = promiseChain.then(function() {
return userPromise(self);
});

if (options.autoCommit === false) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You meant autoCommit : true?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I meant !== true, as I don't want truthy objects to enable autocommit, only the exact value autoCommit: true

promiseChain = promiseChain.then(function(res) {
return self.query("COMMIT")
.then(function() {
return res;
});
})
.catch(function(err) {
return self.query("ROLLBACK")
.catch(function() {
throw err;
});
})
}
return promiseChain;
};

function PromisePreparedStatementInfo(statement, promiseImpl) {
this.statement = statement;
Expand Down Expand Up @@ -339,6 +380,21 @@ PromisePool.prototype.getConnection = function() {
});
};

PromisePool.prototype.transaction = function(options,userPromise) {
return this.getConnection()
.then(function(con) {
return this.Promise.resolve(con.transaction(options,userPromise))
.then(function(res) {
con.release();
return res;
})
.catch(function(err) {
con.release();
throw err;
});
});
};

PromisePool.prototype.query = function(sql, args) {
const corePool = this.pool;
const localErr = new Error();
Expand Down