From 29a4fc0d6ef92e48e1d693833ee3da62ed3b405d Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 3 Mar 2014 10:10:04 +0200 Subject: [PATCH] Add to MySQL adapter transaction support --- lib/abstract-class.js | 5 ++- lib/adapters/mysql.js | 77 +++++++++++++++++++++++++++++++++---------- lib/schema.js | 60 +++++++++++++++++++++++++++++++++ package.json | 2 +- 4 files changed, 123 insertions(+), 21 deletions(-) diff --git a/lib/abstract-class.js b/lib/abstract-class.js index 0a44115..9cfaa19 100644 --- a/lib/abstract-class.js +++ b/lib/abstract-class.js @@ -106,7 +106,7 @@ AbstractClass.prototype._initProperties = function(data, applySetters) { }); ctor.forEachProperty(function(attr) { - var type = properties[attr].type; + var type = properties[attr].type; if (BASE_TYPES.indexOf(type.name) === -1) { if (typeof self.__data[attr] !== 'object' && self.__data[attr]) { try { @@ -1073,7 +1073,7 @@ AbstractClass.hasMany = function hasMany(anotherClass, params) { // which is actually just anotherClass.all({where: {thisModelNameId: this.id}}, cb); defineScope(this.prototype, anotherClass, methodName, function() { var x = {}, id; - if(typeof this.id === 'object') { + if (typeof this.id === 'object') { id = this.id.toString(); } else { id = this.id; @@ -1386,7 +1386,6 @@ AbstractClass.ensureIndex = function ensureIndex(fields, params, callback) { } }; - function buildQuery(opts, model) { for (var okey in model.q.conditions) { diff --git a/lib/adapters/mysql.js b/lib/adapters/mysql.js index 9a728fc..ebf0f5d 100644 --- a/lib/adapters/mysql.js +++ b/lib/adapters/mysql.js @@ -9,7 +9,7 @@ exports.initialize = function initializeSchema(schema, callback) { if (!mysql) { return; } - + var s = schema.settings; var conSettings = { host: s.host || 'localhost', @@ -18,7 +18,7 @@ exports.initialize = function initializeSchema(schema, callback) { password: s.password, debug: s.debug }; - + if (s.pool) { var pool = mysql.createPool(conSettings); schema.client = pool; @@ -32,7 +32,7 @@ exports.initialize = function initializeSchema(schema, callback) { schema.client = mysql.createConnection(conSettings); } - schema.adapter = new MySQL(schema.client); + schema.adapter = new MySQL(schema.client, conSettings); schema.adapter.schema = schema; schema.client.query('USE `' + s.database + '`', function(err) { if (err && err.message.match(/^unknown database/i)) { @@ -53,22 +53,27 @@ exports.initialize = function initializeSchema(schema, callback) { /** * MySQL adapter * @param {Object} client + * @param {Object} conSettings */ -function MySQL(client) { +function MySQL(client, conSettings) { this._models = {}; + this.log = console.log; this.client = client; + this.settings = conSettings; } require('util').inherits(MySQL, BaseSQL); MySQL.prototype.query = function(sql, callback) { var client = this.client; - var time = Date.now(); var log = this.log || console.log; if (typeof callback !== 'function') { throw new Error('callback should be a function'); } - this.client.query(sql, function(err, data) { + client.query(sql, function(err, data) { + if (log) { + // log(new Date().toISOString(), '###', sql, err); + } if (err && err.message.match(/^unknown database/i)) { var dbName = err.message.match(/^unknown database '(.*?)'/i)[1]; client.query('CREATE DATABASE ' + dbName, function(error) { @@ -78,15 +83,54 @@ MySQL.prototype.query = function(sql, callback) { callback(err); } }); - return; - } - if (log) { - log(sql, time); + } else { + return callback(err, data); } - callback(err, data); }); }; +/** + * Start transaction callback(err, id) + * @param {Object} params + * @param {Function} callback + */ +MySQL.prototype.begin = function(params, callback) { + var self = this; + if ('function' === typeof params) { + callback = params; + params = null; + } + self.query('USE `' + self.schema.settings.database + '`', function() { + self.query('START TRANSACTION', callback); + }); +}; + +/** + * Commit transaction callback(err, id) + * @param {Object} params + * @param {Function} callback + */ +MySQL.prototype.commit = function(params, callback) { + if ('function' === typeof params) { + callback = params; + params = null; + } + this.query('COMMIT', callback); +}; + +/** + * Rollback transaction callback(err, id) + * @param {Object} params + * @param {Function} callback + */ +MySQL.prototype.rollback = function(params, callback) { + if ('function' === typeof params) { + callback = params; + params = null; + } + this.query('ROLLBACK', callback); +}; + /** * Create multi column index callback(err, id) * @param {Object} model @@ -107,8 +151,7 @@ MySQL.prototype.ensureIndex = function(model, fields, params, callback) { } // sql = 'USE `' + self.schema.settings.database + '`; '; sql += 'ALTER TABLE `' + model + '` ADD ' + kind + ' INDEX `' + keyName + '` (' + afld.join(', ') + ');'; - - self.command(sql, callback); + self.query(sql, callback); }; /** @@ -120,7 +163,7 @@ MySQL.prototype.ensureIndex = function(model, fields, params, callback) { MySQL.prototype.create = function(model, data, callback) { var fields = this.toFields(model, data); var sql = 'INSERT INTO ' + this.tableEscaped(model); - + if (fields) { sql += ' SET ' + fields; } else { @@ -173,7 +216,7 @@ MySQL.prototype.toFields = function(model, data) { if (props[key]) { fields.push('`' + key.replace(/\./g, '`.`') + '` = ' + this.toDatabase(props[key], data[key])); } - }.bind(this)); + }.bind(this)); return fields.join(','); }; @@ -379,7 +422,7 @@ MySQL.prototype.alterTable = function(model, actualFields, actualIndexes, done, return; } var found; - actualFields.forEach(function(f) { + actualFields.forEach(function(f) { if (f.Field === propName) { found = f; } @@ -391,7 +434,7 @@ MySQL.prototype.alterTable = function(model, actualFields, actualIndexes, done, sql.push('ADD COLUMN `' + propName + '` ' + self.propertySettingsSQL(model, propName)); } }); - + // drop columns actualFields.forEach(function(f) { var notFound = !~propNames.indexOf(f.Field); diff --git a/lib/schema.js b/lib/schema.js index d5be51a..5c3a28b 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -428,6 +428,66 @@ Schema.prototype.defineForeignKey = function defineForeignKey(className, key) { this.models[className].registerProperty(key); }; +/** + * Start transaction + * + * @param {Object} params + * @param {Function} callback + */ +Schema.prototype.begin = function begin(params, callback) { + if (typeof callback === 'undefined') { + callback = function(err) { + return err; + }; + } + + if (typeof this.adapter.begin === 'undefined') { + callback(new Error('TRANSACTION::begin method not defined for this adapter')); + } else { + this.adapter.begin(params, callback); + } +}; + +/** + * Commit transaction + * + * @param {Object} params + * @param {Function} callback + */ +Schema.prototype.commit = function commit(params, callback) { + if (typeof callback === 'undefined') { + callback = function(err) { + return err; + }; + } + + if (typeof this.adapter.commit === 'undefined') { + callback(new Error('TRANSACTION::commit method not defined for this adapter')); + } else { + this.adapter.commit(params, callback); + } +}; + +/** + * Rollback transaction + * + * @param {Object} params + * @param {Function} callback + */ +Schema.prototype.rollback = function rollback(params, callback) { + if (typeof callback === 'undefined') { + callback = function(err) { + return err; + }; + } + + if (typeof this.adapter.rollback === 'undefined') { + callback(new Error('TRANSACTION::rollback method not defined for this adapter')); + } else { + this.adapter.rollback(params, callback); + } +}; + /** * Close database connection */ diff --git a/package.json b/package.json index 52acb9d..8386e47 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "caminte", "description": "ORM for every database: redis, mysql, neo4j, mongodb, rethinkdb, postgres, sqlite, tingodb", - "version": "0.0.13", + "version": "0.0.14", "author": "Aleksej Gordejev (http://www.gordejev.lv)", "homepage": "http://camintejs.com/", "contributors": [