From d3ec979a3177c848cde8bb358d3d2ca6c5fa444e Mon Sep 17 00:00:00 2001 From: Alexey Gordeyev Date: Sun, 8 Jan 2017 10:31:17 +0200 Subject: [PATCH] fix define primary keys in mysql and sqlite adapters --- lib/adapters/mysql.js | 760 ++++++++++++++++++++-------------------- lib/adapters/sqlite3.js | 8 +- package.json | 2 +- 3 files changed, 384 insertions(+), 386 deletions(-) diff --git a/lib/adapters/mysql.js b/lib/adapters/mysql.js index e61555b..e9fbaa3 100644 --- a/lib/adapters/mysql.js +++ b/lib/adapters/mysql.js @@ -1,15 +1,15 @@ /** * Module dependencies */ -var utils = require( '../utils' ); -var util = require( 'util' ); +var utils = require('../utils'); +var util = require('util'); var safeRequire = utils.safeRequire; -var mysql = safeRequire( 'mysql' ); -var BaseSQL = require( '../sql' ); +var mysql = safeRequire('mysql'); +var BaseSQL = require('../sql'); exports.initialize = function initializeSchema(schema, callback) { 'use strict'; - if ( !mysql ) { + if (!mysql) { return; } var s = schema.settings; @@ -24,89 +24,89 @@ exports.initialize = function initializeSchema(schema, callback) { conSettings.database = dbName; conSettings.acquireTimeout = s.acquireTimeout || 10000; - if ( s.pool ) { + if (s.pool) { conSettings.connectionLimit = s.connectionLimit || 10; conSettings.queueLimit = s.queueLimit || 0; conSettings.waitForConnections = s.waitForConnections || true; - schema.client = mysql.createPool( conSettings ); - schema.client.getConnection( function (err, connection) { - if ( err ) { - throw new Error( err ); + schema.client = mysql.createPool(conSettings); + schema.client.getConnection(function (err, connection) { + if (err) { + throw new Error(err); } - } ); - schema.adapter = new MySQL( schema.client, conSettings ); + }); + schema.adapter = new MySQL(schema.client, conSettings); schema.adapter.schema = schema; - schema.client.once( 'connection', function (connection) { - if ( s.autoReconnect ) { - setInterval( function () { - schema.client.query( 'SELECT 1+1', function (err, data) { + schema.client.once('connection', function (connection) { + if (s.autoReconnect) { + setInterval(function () { + schema.client.query('SELECT 1+1', function (err, data) { // - } ); - }, conSettings.acquireTimeout ); + }); + }, conSettings.acquireTimeout); } - startAdapter( schema, dbName, callback ); - } ); + startAdapter(schema, dbName, callback); + }); } else { - schema.client = mysql.createConnection( conSettings ); - schema.adapter = new MySQL( schema.client, conSettings ); + schema.client = mysql.createConnection(conSettings); + schema.adapter = new MySQL(schema.client, conSettings); schema.adapter.schema = schema; - if ( s.autoReconnect ) { - setInterval( function () { - schema.client.query( 'SELECT 1+1', function (err, data) { + if (s.autoReconnect) { + setInterval(function () { + schema.client.query('SELECT 1+1', function (err, data) { // - } ); - }, conSettings.acquireTimeout ); + }); + }, conSettings.acquireTimeout); } - startAdapter( schema, dbName, callback ); + startAdapter(schema, dbName, callback); } function handleDisconnect(client) { - schema.client.on( 'error', function (error) { - if ( error.code !== 'PROTOCOL_CONNECTION_LOST' ) throw error; - console.log( "> Re-connecting lost MySQL connection: " + error.stack ); - if ( s.pool ) { - schema.client = mysql.createPool( conSettings ); - schema.client.getConnection( function (err, connection) { - if ( err ) { - throw new Error( err ); + schema.client.on('error', function (error) { + if (error.code !== 'PROTOCOL_CONNECTION_LOST') throw error; + console.log("> Re-connecting lost MySQL connection: " + error.stack); + if (s.pool) { + schema.client = mysql.createPool(conSettings); + schema.client.getConnection(function (err, connection) { + if (err) { + throw new Error(err); } - } ); - schema.adapter = new MySQL( schema.client, conSettings ); + }); + schema.adapter = new MySQL(schema.client, conSettings); schema.adapter.schema = schema; - schema.client.once( 'connection', function (connection) { - startAdapter( schema, dbName, callback ); - } ); - handleDisconnect( schema.client ); + schema.client.once('connection', function (connection) { + startAdapter(schema, dbName, callback); + }); + handleDisconnect(schema.client); } else { - schema.client = mysql.createConnection( conSettings ); - schema.adapter = new MySQL( schema.client, conSettings ); + schema.client = mysql.createConnection(conSettings); + schema.adapter = new MySQL(schema.client, conSettings); schema.adapter.schema = schema; - startAdapter( schema, dbName, callback ); - handleDisconnect( schema.client ); + startAdapter(schema, dbName, callback); + handleDisconnect(schema.client); } - } ); + }); } - handleDisconnect( schema.client ); + handleDisconnect(schema.client); }; function startAdapter(schema, dbName, callback) { 'use strict'; - schema.client.query( 'USE `' + dbName + '`', function (err) { - if ( err && err.message.match( /unknown database/i ) ) { - schema.client.query( 'CREATE DATABASE ' + dbName, function (error) { - if ( !error ) { - schema.client.query( 'USE ' + dbName, callback ); + schema.client.query('USE `' + dbName + '`', function (err) { + if (err && err.message.match(/unknown database/i)) { + schema.client.query('CREATE DATABASE ' + dbName, function (error) { + if (!error) { + schema.client.query('USE ' + dbName, callback); } else { throw error; } - } ); + }); } else { callback(); } - } ); + }); } /** @@ -123,41 +123,41 @@ function MySQL(client, conSettings) { this.settings = conSettings; } -util.inherits( MySQL, BaseSQL ); +util.inherits(MySQL, BaseSQL); MySQL.prototype.query = function (sql, callback) { 'use strict'; var self = this; var client = self.client; var log = self.log || console.log; - if ( typeof callback !== 'function' ) { - throw new Error( 'callback should be a function' ); + if (typeof callback !== 'function') { + throw new Error('callback should be a function'); } - client.query( sql, function (err, data) { - if ( log ) { + 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) { - if ( !error ) { - client.query( sql, callback ); + if (err && err.message.match(/unknown database/i)) { + var dbName = err.message.match(/unknown database '(.*?)'/i)[1]; + client.query('CREATE DATABASE ' + dbName, function (error) { + if (!error) { + client.query(sql, callback); } else { - callback( err ); + callback(err); } - } ); - } else if ( err && (err.message.match( /No\s+database\s+selected/gi ) || parseInt( err.errno ) === 1046) ) { - client.query( 'USE `' + self.schema.settings.database + '`', function (error) { - if ( !error ) { - client.query( sql, callback ); + }); + } else if (err && (err.message.match(/No\s+database\s+selected/gi) || parseInt(err.errno) === 1046)) { + client.query('USE `' + self.schema.settings.database + '`', function (error) { + if (!error) { + client.query(sql, callback); } else { - callback( error ); + callback(error); } - } ); + }); } else { - return callback( err, data ); + return callback(err, data); } - } ); + }); }; /** @@ -167,11 +167,11 @@ MySQL.prototype.query = function (sql, callback) { */ MySQL.prototype.begin = function (params, callback) { 'use strict'; - if ( 'function' === typeof params ) { + if ('function' === typeof params) { callback = params; params = null; } - this.query( 'START TRANSACTION', callback ); + this.query('START TRANSACTION', callback); }; /** @@ -181,11 +181,11 @@ MySQL.prototype.begin = function (params, callback) { */ MySQL.prototype.commit = function (params, callback) { 'use strict'; - if ( 'function' === typeof params ) { + if ('function' === typeof params) { callback = params; params = null; } - this.query( 'COMMIT', callback ); + this.query('COMMIT', callback); }; /** @@ -195,11 +195,11 @@ MySQL.prototype.commit = function (params, callback) { */ MySQL.prototype.rollback = function (params, callback) { 'use strict'; - if ( 'function' === typeof params ) { + if ('function' === typeof params) { callback = params; params = null; } - this.query( 'ROLLBACK', callback ); + this.query('ROLLBACK', callback); }; /** @@ -212,18 +212,18 @@ MySQL.prototype.rollback = function (params, callback) { MySQL.prototype.ensureIndex = function (model, fields, params, callback) { 'use strict'; var self = this, sql = "", keyName = params.name || null, afld = [], kind = ""; - Object.keys( fields ).forEach( function (field) { - if ( !keyName ) { + Object.keys(fields).forEach(function (field) { + if (!keyName) { keyName = "idx_" + field; } - afld.push( '`' + field + '`' ); - } ); - if ( params.unique ) { + afld.push('`' + field + '`'); + }); + if (params.unique) { kind = "UNIQUE"; } // CREATE INDEX IF NOT EXISTS xi ON xx5 (x); - sql += 'CREATE INDEX IF NOT EXISTS ' + kind + ' INDEX `' + keyName + '` ON `' + model + '` (' + afld.join( ', ' ) + ');'; - self.query( sql, callback ); + sql += 'CREATE INDEX IF NOT EXISTS ' + kind + ' INDEX `' + keyName + '` ON `' + model + '` (' + afld.join(', ') + ');'; + self.query(sql, callback); }; /** @@ -236,16 +236,16 @@ MySQL.prototype.ensureIndex = function (model, fields, params, callback) { MySQL.prototype.createIndexes = function (model, props, done) { 'use strict'; var self = this, sql = [], m = props; - for ( var prop in m.properties ) { - if ( 'undefined' !== typeof m.properties[ prop ][ 'index' ] - || 'undefined' !== typeof m.properties[ prop ][ 'unique' ] ) { - var UNIQ = 'undefined' !== typeof m.properties[ prop ][ 'unique' ] ? ' UNIQUE ' : ''; - sql.push( ' ADD ' + UNIQ + ' INDEX `' + prop + '` (`' + prop + '`)' ); + for (var prop in m.properties) { + if ('undefined' !== typeof m.properties[prop]['index'] + || 'undefined' !== typeof m.properties[prop]['unique']) { + var UNIQ = 'undefined' !== typeof m.properties[prop]['unique'] ? ' UNIQUE ' : ''; + sql.push(' ADD ' + UNIQ + ' INDEX `' + prop + '` (`' + prop + '`)'); } } - if ( sql.length ) { - self.command( 'ALTER TABLE ' + self.tableEscaped( model ) + sql.join( ',\n' ) + ';', done ); + if (sql.length) { + self.command('ALTER TABLE ' + self.tableEscaped(model) + sql.join(',\n') + ';', done); } else { done(); } @@ -258,18 +258,18 @@ MySQL.prototype.createIndexes = function (model, props, done) { */ MySQL.prototype.create = function (model, data, callback) { 'use strict'; - var fields = this.toFields( model, data ); - var sql = 'INSERT INTO ' + this.tableEscaped( model ); + var fields = this.toFields(model, data); + var sql = 'INSERT INTO ' + this.tableEscaped(model); - if ( fields ) { + if (fields) { sql += ' SET ' + fields; } else { sql += ' VALUES ()'; } - this.query( sql, function (err, info) { - callback( err, info && info.insertId ); - } ); + this.query(sql, function (err, info) { + callback(err, info && info.insertId); + }); }; /** @@ -281,57 +281,57 @@ MySQL.prototype.create = function (model, data, callback) { */ MySQL.prototype.update = function (model, filter, data, callback) { 'use strict'; - if ( 'function' === typeof filter ) { - return filter( new Error( "Get parametrs undefined" ), null ); + if ('function' === typeof filter) { + return filter(new Error("Get parametrs undefined"), null); } - if ( 'function' === typeof data ) { - return data( new Error( "Set parametrs undefined" ), null ); + if ('function' === typeof data) { + return data(new Error("Set parametrs undefined"), null); } filter = filter.where ? filter.where : filter; var self = this; var combined = []; - var props = self._models[ model ].properties; - Object.keys( data ).forEach( function (key) { - if ( props[ key ] || key === 'id' ) { + var props = self._models[model].properties; + Object.keys(data).forEach(function (key) { + if (props[key] || key === 'id') { var k = '`' + key + '`'; var v; - if ( key !== 'id' ) { - v = self.toDatabase( props[ key ], data[ key ] ); + if (key !== 'id') { + v = self.toDatabase(props[key], data[key]); } else { - v = data[ key ]; + v = data[key]; } - combined.push( k + ' = ' + v ); + combined.push(k + ' = ' + v); } - } ); - var sql = 'UPDATE ' + this.tableEscaped( model ); - sql += ' SET ' + combined.join( ', ' ); - sql += ' ' + self.buildWhere( filter, self, model ); + }); + var sql = 'UPDATE ' + this.tableEscaped(model); + sql += ' SET ' + combined.join(', '); + sql += ' ' + self.buildWhere(filter, self, model); - this.query( sql, function (err, affected) { - callback( err, ((affected || {}).affectedRows || affected) ); - } ); + this.query(sql, function (err, affected) { + callback(err, ((affected || {}).affectedRows || affected)); + }); }; MySQL.prototype.toFields = function (model, data) { 'use strict'; var fields = []; - var props = this._models[ model ].properties; - Object.keys( data ).forEach( function (key) { - if ( props[ key ] ) { - fields.push( '`' + key.replace( /\./g, '`.`' ) + '` = ' + this.toDatabase( props[ key ], data[ key ] ) ); + var props = this._models[model].properties; + Object.keys(data).forEach(function (key) { + if (props[key]) { + fields.push('`' + key.replace(/\./g, '`.`') + '` = ' + this.toDatabase(props[key], data[key])); } - }.bind( this ) ); - return fields.join( ',' ); + }.bind(this)); + return fields.join(','); }; function dateToMysql(val) { 'use strict'; return val.getUTCFullYear() + '-' + - fillZeros( val.getUTCMonth() + 1 ) + '-' + - fillZeros( val.getUTCDate() ) + ' ' + - fillZeros( val.getUTCHours() ) + ':' + - fillZeros( val.getUTCMinutes() ) + ':' + - fillZeros( val.getUTCSeconds() ); + fillZeros(val.getUTCMonth() + 1) + '-' + + fillZeros(val.getUTCDate()) + ' ' + + fillZeros(val.getUTCHours()) + ':' + + fillZeros(val.getUTCMinutes()) + ':' + + fillZeros(val.getUTCSeconds()); function fillZeros(v) { 'use strict'; @@ -341,147 +341,147 @@ function dateToMysql(val) { MySQL.prototype.toDatabase = function (prop, val) { 'use strict'; - if ( val === null ) { + if (val === null) { return 'NULL'; } - if ( val.constructor.name === 'Object' ) { - var operator = Object.keys( val )[ 0 ]; - val = val[ operator ]; - if ( operator === 'between' ) { - if ( prop.type.name === 'Date' ) { - return 'STR_TO_DATE(' + this.toDatabase( prop, val[ 0 ] ) + ', "%Y-%m-%d %H:%i:%s")' + + if (val.constructor.name === 'Object') { + var operator = Object.keys(val)[0]; + val = val[operator]; + if (operator === 'between') { + if (prop.type.name === 'Date') { + return 'STR_TO_DATE(' + this.toDatabase(prop, val[0]) + ', "%Y-%m-%d %H:%i:%s")' + ' AND STR_TO_DATE(' + - this.toDatabase( prop, val[ 1 ] ) + ', "%Y-%m-%d %H:%i:%s")'; + this.toDatabase(prop, val[1]) + ', "%Y-%m-%d %H:%i:%s")'; } else { - return this.toDatabase( prop, val[ 0 ] ) + + return this.toDatabase(prop, val[0]) + ' AND ' + - this.toDatabase( prop, val[ 1 ] ); + this.toDatabase(prop, val[1]); } - } else if ( operator === 'in' || operator === 'inq' || operator === 'nin' ) { - if ( !(val.propertyIsEnumerable( 'length' )) && typeof val === 'object' && typeof val.length === 'number' ) { //if value is array - for ( var i = 0; i < val.length; i++ ) { - if ( /^"(?:\\"|.)*?"$/gi.test( val[ i ] ) || /^'(?:\\'|.)*?'$/gi.test( val[ i ] ) ) { - val[ i ] = val[ i ]; + } else if (operator === 'in' || operator === 'inq' || operator === 'nin') { + if (!(val.propertyIsEnumerable('length')) && typeof val === 'object' && typeof val.length === 'number') { //if value is array + for (var i = 0; i < val.length; i++) { + if (/^"(?:\\"|.)*?"$/gi.test(val[i]) || /^'(?:\\'|.)*?'$/gi.test(val[i])) { + val[i] = val[i]; } else { - val[ i ] = this.client.escape( val[ i ] ); + val[i] = this.client.escape(val[i]); } } - return val.join( ',' ); + return val.join(','); } else { return val; } } } - if ( !prop ) { + if (!prop) { return val; } - if ( prop.type.name === 'Number' ) { + if (prop.type.name === 'Number') { return val; } - if ( prop.type.name === 'Date' ) { - if ( !val ) { + if (prop.type.name === 'Date') { + if (!val) { return 'NULL'; } - if ( typeof val === 'string' ) { - val = val.split( '.' )[ 0 ].replace( 'T', ' ' ); - val = Date.parse( val ); + if (typeof val === 'string') { + val = val.split('.')[0].replace('T', ' '); + val = Date.parse(val); } - if ( typeof val === 'number' ) { - val = new Date( val ); + if (typeof val === 'number') { + val = new Date(val); } - if ( val instanceof Date ) { - val = '"' + dateToMysql( val ) + '"'; + if (val instanceof Date) { + val = '"' + dateToMysql(val) + '"'; } return val; } - if ( prop.type.name === "Boolean" ) { + if (prop.type.name === "Boolean") { return val ? 1 : 0; } - return /^"(?:\\"|.)*?"$/gi.test( val ) || /^'(?:\\'|.)*?'$/gi.test( val ) ? val : this.client.escape( val ); + return /^"(?:\\"|.)*?"$/gi.test(val) || /^'(?:\\'|.)*?'$/gi.test(val) ? val : this.client.escape(val); }; MySQL.prototype.fromDatabase = function (model, data) { 'use strict'; - if ( !data ) { + if (!data) { return null; } - var props = this._models[ model ].properties; - Object.keys( data ).forEach( function (key) { - var val = data[ key ]; - if ( props[ key ] ) { - if ( props[ key ].type.name === 'Date' && val !== null ) { - val = new Date( val.toString().replace( /GMT.*$/, 'GMT' ) ); + var props = this._models[model].properties; + Object.keys(data).forEach(function (key) { + var val = data[key]; + if (props[key]) { + if (props[key].type.name === 'Date' && val !== null) { + val = new Date(val.toString().replace(/GMT.*$/, 'GMT')); } - if ( (props[ key ].type.name || '').toString().toLowerCase() === 'json' && typeof val == "string" ) { + if ((props[key].type.name || '').toString().toLowerCase() === 'json' && typeof val == "string") { try { - val = JSON.parse( val ); - } catch ( err ) { + val = JSON.parse(val); + } catch (err) { } } } - data[ key ] = val; - } ); + data[key] = val; + }); return data; }; MySQL.prototype.escapeName = function (name) { 'use strict'; - return '`' + name.replace( /\./g, '`.`' ) + '`'; + return '`' + name.replace(/\./g, '`.`') + '`'; }; MySQL.prototype.all = function all(model, filter, callback) { 'use strict'; var self = this, sFields = '*'; - if ( 'function' === typeof filter ) { + if ('function' === typeof filter) { callback = filter; filter = {}; } - if ( !filter ) { + if (!filter) { filter = {}; } - var sql = 'SELECT ' + sFields + ' FROM ' + self.tableEscaped( model ); + var sql = 'SELECT ' + sFields + ' FROM ' + self.tableEscaped(model); - if ( filter ) { + if (filter) { - if ( filter.fields ) { - if ( typeof filter.fields === 'string' ) { - sFields = self.tableEscaped( filter.fields ); - } else if ( Object.prototype.toString.call( filter.fields ) === '[object Array]' ) { - sFields = filter.fields.map( function (field) { + if (filter.fields) { + if (typeof filter.fields === 'string') { + sFields = self.tableEscaped(filter.fields); + } else if (Object.prototype.toString.call(filter.fields) === '[object Array]') { + sFields = filter.fields.map(function (field) { return '`' + field + '`'; - } ).join( ', ' ); + }).join(', '); } - sql = sql.replace( '*', sFields ); + sql = sql.replace('*', sFields); } - if ( filter.where ) { - sql += ' ' + self.buildWhere( filter.where, self, model ); + if (filter.where) { + sql += ' ' + self.buildWhere(filter.where, self, model); } - if ( filter.order ) { - sql += ' ' + self.buildOrderBy( filter.order ); + if (filter.order) { + sql += ' ' + self.buildOrderBy(filter.order); } - if ( filter.group ) { - sql += ' ' + self.buildGroupBy( filter.group ); + if (filter.group) { + sql += ' ' + self.buildGroupBy(filter.group); } - if ( filter.limit ) { - sql += ' ' + self.buildLimit( filter.limit, filter.offset || filter.skip || 0 ); + if (filter.limit) { + sql += ' ' + self.buildLimit(filter.limit, filter.offset || filter.skip || 0); } } - this.query( sql, function (err, data) { - if ( err ) { - return callback( err, [] ); + this.query(sql, function (err, data) { + if (err) { + return callback(err, []); } - callback( null, data.map( function (obj) { - return self.fromDatabase( model, obj ); - } ) ); - }.bind( this ) ); + callback(null, data.map(function (obj) { + return self.fromDatabase(model, obj); + })); + }.bind(this)); return sql; }; @@ -494,24 +494,24 @@ MySQL.prototype.autoupdate = function (cb) { 'use strict'; var self = this; var wait = 0; - Object.keys( this._models ).forEach( function (model) { + Object.keys(this._models).forEach(function (model) { wait += 1; - self.query( 'SHOW FIELDS FROM ' + self.tableEscaped( model ), function (err, fields) { - self.query( 'SHOW INDEXES FROM ' + self.tableEscaped( model ), function (err, indexes) { - if ( !err && fields.length ) { - self.alterTable( model, fields, indexes, done ); + self.query('SHOW FIELDS FROM ' + self.tableEscaped(model), function (err, fields) { + self.query('SHOW INDEXES FROM ' + self.tableEscaped(model), function (err, indexes) { + if (!err && fields.length) { + self.alterTable(model, fields, indexes, done); } else { - self.createTable( model, indexes, done ); + self.createTable(model, indexes, done); } - } ); - } ); - } ); + }); + }); + }); function done(err) { - if ( err ) { - console.log( err ); + if (err) { + console.log(err); } - if ( --wait === 0 && cb ) { + if (--wait === 0 && cb) { cb(); } } @@ -527,22 +527,22 @@ MySQL.prototype.isActual = function (cb) { var ok = false; var self = this; var wait = 0; - Object.keys( this._models ).forEach( function (model) { + Object.keys(this._models).forEach(function (model) { wait += 1; - self.query( 'SHOW FIELDS FROM ' + model, function (err, fields) { - self.query( 'SHOW INDEXES FROM ' + model, function (err, indexes) { - self.alterTable( model, fields, indexes, done, true ); - } ); - } ); - } ); + self.query('SHOW FIELDS FROM ' + model, function (err, fields) { + self.query('SHOW INDEXES FROM ' + model, function (err, indexes) { + self.alterTable(model, fields, indexes, done, true); + }); + }); + }); function done(err, needAlter) { - if ( err ) { - console.log( err ); + if (err) { + console.log(err); } ok = ok || needAlter; - if ( --wait === 0 && cb ) { - cb( null, !ok ); + if (--wait === 0 && cb) { + cb(null, !ok); } } }; @@ -550,149 +550,149 @@ MySQL.prototype.isActual = function (cb) { MySQL.prototype.alterTable = function (model, actualFields, actualIndexes, done, checkOnly) { 'use strict'; var self = this; - var m = this._models[ model ]; - var propNames = Object.keys( m.properties ).filter( function (name) { - return !!m.properties[ name ]; - } ); - var indexNames = m.settings.indexes ? Object.keys( m.settings.indexes ).filter( function (name) { - return !!m.settings.indexes[ name ]; - } ) : []; + var m = this._models[model]; + var propNames = Object.keys(m.properties).filter(function (name) { + return !!m.properties[name]; + }); + var indexNames = m.settings.indexes ? Object.keys(m.settings.indexes).filter(function (name) { + return !!m.settings.indexes[name]; + }) : []; var sql = []; var ai = {}; - if ( actualIndexes ) { - actualIndexes.forEach( function (i) { + if (actualIndexes) { + actualIndexes.forEach(function (i) { var name = i.Key_name; - if ( !ai[ name ] ) { - ai[ name ] = { + if (!ai[name]) { + ai[name] = { info: i, columns: [] }; } - ai[ name ].columns[ i.Seq_in_index - 1 ] = i.Column_name; - } ); + ai[name].columns[i.Seq_in_index - 1] = i.Column_name; + }); } - var aiNames = Object.keys( ai ); + var aiNames = Object.keys(ai); // change/add new fields - propNames.forEach( function (propName) { - if ( propName === 'id' ) { + propNames.forEach(function (propName) { + if (propName === 'id') { return; } var found; - actualFields.forEach( function (f) { - if ( f.Field === propName ) { + actualFields.forEach(function (f) { + if (f.Field === propName) { found = f; } - } ); + }); - if ( found ) { - actualize( propName, found ); + if (found) { + actualize(propName, found); } else { - sql.push( 'ADD COLUMN `' + propName + '` ' + self.propertySettingsSQL( model, propName ) ); + sql.push('ADD COLUMN `' + propName + '` ' + self.propertySettingsSQL(model, propName)); } - } ); + }); // drop columns - actualFields.forEach( function (f) { - var notFound = !~propNames.indexOf( f.Field ); - if ( f.Field === 'id' ) { + actualFields.forEach(function (f) { + var notFound = !~propNames.indexOf(f.Field); + if (f.Field === 'id') { return; } - if ( notFound || !m.properties[ f.Field ] ) { - sql.push( 'DROP COLUMN `' + f.Field + '` ' ); + if (notFound || !m.properties[f.Field]) { + sql.push('DROP COLUMN `' + f.Field + '` '); } - } ); + }); // remove indexes - aiNames.forEach( function (indexName) { - if ( indexName === 'id' || indexName === 'PRIMARY' ) { + aiNames.forEach(function (indexName) { + if (indexName === 'id' || indexName === 'PRIMARY') { return; } - if ( (indexNames.indexOf( indexName ) === -1 && !m.properties[ indexName ]) - || ((m.properties[ indexName ] && !m.properties[ indexName ].index) - && (m.properties[ indexName ] && !m.properties[ indexName ].unique)) ) { - sql.push( 'DROP INDEX `' + indexName + '`' ); + if ((indexNames.indexOf(indexName) === -1 && !m.properties[indexName]) + || ((m.properties[indexName] && !m.properties[indexName].index) + && (m.properties[indexName] && !m.properties[indexName].unique))) { + sql.push('DROP INDEX `' + indexName + '`'); } else { // first: check single (only type and kind) - if ( m.properties[ indexName ] && (!m.properties[ indexName ].index || !m.properties[ indexName ].unique) ) { + if (m.properties[indexName] && (!m.properties[indexName].index || !m.properties[indexName].unique)) { // TODO return; } // second: check multiple indexes var orderMatched = true; - if ( indexNames.indexOf( indexName ) !== -1 ) { - m.settings.indexes[ indexName ].columns.split( /,\s*/ ).forEach( function (columnName, i) { - if ( ai[ indexName ].columns[ i ] !== columnName ) + if (indexNames.indexOf(indexName) !== -1) { + m.settings.indexes[indexName].columns.split(/,\s*/).forEach(function (columnName, i) { + if (ai[indexName].columns[i] !== columnName) orderMatched = false; - } ); + }); } - if ( !orderMatched ) { - sql.push( 'DROP INDEX `' + indexName + '`' ); - delete ai[ indexName ]; + if (!orderMatched) { + sql.push('DROP INDEX `' + indexName + '`'); + delete ai[indexName]; } } - } ); + }); // add single-column indexes - propNames.forEach( function (propName) { - var i = m.properties[ propName ].index || m.properties[ propName ].unique; - if ( !i ) { + propNames.forEach(function (propName) { + var i = m.properties[propName].index || m.properties[propName].unique; + if (!i) { return; } - var found = ai[ propName ] && ai[ propName ].info; - if ( !found ) { - var prop = m.properties[ propName ]; + var found = ai[propName] && ai[propName].info; + if (!found) { + var prop = m.properties[propName]; var type = ''; var kind = ''; - if ( i.type ) { + if (i.type) { type = 'USING ' + i.type; } - if ( i.kind || prop.unique ) { + if (i.kind || prop.unique) { kind = i.kind ? i.kind : 'UNIQUE'; } - if ( kind && type ) { - sql.push( 'ADD ' + kind + ' INDEX `' + propName + '` (`' + propName + '`) ' + type ); + if (kind && type) { + sql.push('ADD ' + kind + ' INDEX `' + propName + '` (`' + propName + '`) ' + type); } else { - sql.push( 'ADD ' + kind + ' INDEX `' + propName + '` ' + type + ' (`' + propName + '`) ' ); + sql.push('ADD ' + kind + ' INDEX `' + propName + '` ' + type + ' (`' + propName + '`) '); } - ai[ propName ] = { + ai[propName] = { info: true }; } - } ); + }); // add multi-column indexes - indexNames.forEach( function (indexName) { - var i = m.settings.indexes[ indexName ]; - var found = ai[ indexName ] && ai[ indexName ].info; - if ( !found ) { + indexNames.forEach(function (indexName) { + var i = m.settings.indexes[indexName]; + var found = ai[indexName] && ai[indexName].info; + if (!found) { var type = ''; var kind = ''; - if ( i.type ) { + if (i.type) { type = 'USING ' + i.kind; } - if ( i.kind ) { + if (i.kind) { kind = i.kind; } - var columns = i.columns.split( ',' ).map( function (c) { - return '`' + c.replace( /^\s+|\s+$/, '' ) + "`"; - } ).join( ',' ); - if ( kind && type ) { - sql.push( 'ADD ' + kind + ' INDEX `' + indexName + '` (' + columns + ') ' + type ); + var columns = i.columns.split(',').map(function (c) { + return '`' + c.replace(/^\s+|\s+$/, '') + "`"; + }).join(','); + if (kind && type) { + sql.push('ADD ' + kind + ' INDEX `' + indexName + '` (' + columns + ') ' + type); } else { - sql.push( 'ADD ' + kind + ' INDEX ' + type + ' `' + indexName + '` (' + columns + ')' ); + sql.push('ADD ' + kind + ' INDEX ' + type + ' `' + indexName + '` (' + columns + ')'); } } - } ); + }); - if ( sql.length ) { - var query = 'ALTER TABLE ' + self.tableEscaped( model ) + ' \n' + sql.join( ',\n' ); - if ( checkOnly ) { - done( null, true, { + if (sql.length) { + var query = 'ALTER TABLE ' + self.tableEscaped(model) + ' \n' + sql.join(',\n'); + if (checkOnly) { + done(null, true, { statements: sql, query: query - } ); + }); } else { - this.query( query, done ); + this.query(query, done); } } else { done(); @@ -700,21 +700,21 @@ MySQL.prototype.alterTable = function (model, actualFields, actualIndexes, done, function actualize(propName, oldSettings) { 'use strict'; - var newSettings = m.properties[ propName ]; - if ( newSettings && changed( newSettings, oldSettings ) ) { - sql.push( 'CHANGE COLUMN `' + propName + '` `' + propName + '` ' + self.propertySettingsSQL( model, propName ) ); + var newSettings = m.properties[propName]; + if (newSettings && changed(newSettings, oldSettings)) { + sql.push('CHANGE COLUMN `' + propName + '` `' + propName + '` ' + self.propertySettingsSQL(model, propName)); } } function changed(newSettings, oldSettings) { 'use strict'; - if ( oldSettings.Null === 'YES' && (newSettings.allowNull === false || newSettings.null === false) ) { + if (oldSettings.Null === 'YES' && (newSettings.allowNull === false || newSettings.null === false)) { return true; } - if ( oldSettings.Null === 'NO' && (getDefaultValue( newSettings ) !== getDefaultValue( oldSettings )) ) { + if (oldSettings.Null === 'NO' && (getDefaultValue(newSettings) !== getDefaultValue(oldSettings))) { return true; } - if ( oldSettings.Type.toUpperCase() !== datatype( newSettings ) ) { + if (oldSettings.Type.toUpperCase() !== datatype(newSettings)) { return true; } return false; @@ -725,67 +725,67 @@ MySQL.prototype.propertiesSQL = function (model) { 'use strict'; var self = this; var sql = []; - var primaryKeys = this._models[ model ].settings.primaryKeys || []; - var foreignKeys = this._models[ model ].settings.foreignKeys || []; + var primaryKeys = this._models[model].settings.primaryKeys || []; + var foreignKeys = this._models[model].settings.foreignKeys || []; - primaryKeys = primaryKeys.slice( 0 ); - foreignKeys = foreignKeys.slice( 0 ); + primaryKeys = primaryKeys.slice(0); + foreignKeys = foreignKeys.slice(0); - Object.keys( this._models[ model ].properties ).forEach( function (prop) { - if ( prop === 'id' ) { + Object.keys(this._models[model].properties).forEach(function (prop) { + if (prop === 'id') { return; } - return sql.push( '`' + prop + '` ' + self.propertySettingsSQL( model, prop ) ); - } ); + return sql.push('`' + prop + '` ' + self.propertySettingsSQL(model, prop)); + }); - if ( primaryKeys.length ) { - for ( var i = 0, length = primaryKeys.length; i < length; i++ ) { - primaryKeys[ i ] = "`" + primaryKeys[ i ] + "`"; + if (primaryKeys.length) { + for (var i = 0, length = primaryKeys.length; i < length; i++) { + primaryKeys[i] = "`" + primaryKeys[i].toString().replace(/,\s+|,/, '`,`') + "`"; } - sql.push( "PRIMARY KEY (" + primaryKeys.join( ', ' ) + ")" ); + sql.push("PRIMARY KEY (" + primaryKeys.join(', ') + ")"); } else { - sql.push( '`id` INT(11) NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY' ); + sql.push('`id` INT(11) NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY'); } - if ( foreignKeys.length ) { - for ( var i = 0, length = foreignKeys.length; i < length; i++ ) { - var foreignKey = foreignKeys[ i ]; - var str = "FOREIGN KEY (`" + foreignKeys[ i ].localCol + "`) REFERENCES " + foreignKey.foreignTable + "(`" + foreignKey.foreignCol + "`)"; + if (foreignKeys.length) { + for (var i = 0, length = foreignKeys.length; i < length; i++) { + var foreignKey = foreignKeys[i]; + var str = "FOREIGN KEY (`" + foreignKeys[i].localCol + "`) REFERENCES " + foreignKey.foreignTable + "(`" + foreignKey.foreignCol + "`)"; - if ( foreignKey.onDelete ) { + if (foreignKey.onDelete) { str += " ON DELETE CASCADE"; } - if ( foreignKey.onUpdate ) { + if (foreignKey.onUpdate) { str += " ON UPDATE CASCADE"; } - sql.push( str ); + sql.push(str); } } - return sql.join( ',\n ' ); + return sql.join(',\n '); }; MySQL.prototype.propertySettingsSQL = function (model, prop) { 'use strict'; - var p = this._models[ model ].properties[ prop ], field = []; + var p = this._models[model].properties[prop], field = []; - field.push( datatype( p ) ); - field.push( p.allowNull === false || (typeof p[ 'default' ] !== 'undefined' && acceptedDefaults( p )) ? 'NOT NULL' : 'NULL' ); - if ( typeof p[ 'default' ] !== 'undefined' && acceptedDefaults( p ) && typeof p[ 'default' ] !== 'function' ) { - field.push( 'DEFAULT ' + getDefaultValue( p ) ); + field.push(datatype(p)); + field.push(p.allowNull === false || (typeof p['default'] !== 'undefined' && acceptedDefaults(p)) ? 'NOT NULL' : 'NULL'); + if (typeof p['default'] !== 'undefined' && acceptedDefaults(p) && typeof p['default'] !== 'function') { + field.push('DEFAULT ' + getDefaultValue(p)); } - if ( p.unique === true ) { - field.push( 'UNIQUE' ); + if (p.unique === true) { + field.push('UNIQUE'); } - return field.join( " " ); + return field.join(" "); }; function datatype(p) { 'use strict'; var dt = ''; - switch ( (p.type.name || 'string').toLowerCase() ) { + switch ((p.type.name || 'string').toLowerCase()) { case 'json': case 'text': dt = 'TEXT'; @@ -793,7 +793,7 @@ function datatype(p) { case 'int': case 'integer': case 'number': - var ftype = (parseFloat( p.limit ) > 11) ? "BIGINT" : "INT"; + var ftype = (parseFloat(p.limit) > 11) ? "BIGINT" : "INT"; dt = ftype + '(' + (p.limit || 11) + ')'; break; case 'float': @@ -820,46 +820,46 @@ MySQL.prototype.buildWhere = function buildWhere(conds, adapter, model) { 'use strict'; var cs = [], or = [], self = adapter, - props = self._models[ model ].properties; + props = self._models[model].properties; - Object.keys( conds ).forEach( function (key) { - if ( key !== 'or' ) { - cs = parseCond( cs, key, props, conds, self ); + Object.keys(conds).forEach(function (key) { + if (key !== 'or') { + cs = parseCond(cs, key, props, conds, self); } else { - conds[ key ].forEach( function (oconds) { - Object.keys( oconds ).forEach( function (okey) { - or = parseCond( or, okey, props, oconds, self ); - } ); - } ); + conds[key].forEach(function (oconds) { + Object.keys(oconds).forEach(function (okey) { + or = parseCond(or, okey, props, oconds, self); + }); + }); } - } ); + }); - if ( cs.length === 0 && or.length === 0 ) { + if (cs.length === 0 && or.length === 0) { return ''; } var orop = ""; - if ( or.length ) { - orop = ' (' + or.join( ' OR ' ) + ') '; + if (or.length) { + orop = ' (' + or.join(' OR ') + ') '; } orop += (orop !== "" && cs.length > 0) ? ' AND ' : ''; - return 'WHERE ' + orop + cs.join( ' AND ' ); + return 'WHERE ' + orop + cs.join(' AND '); }; function parseCond(cs, key, props, conds, self) { 'use strict'; - var keyEscaped = '`' + key.replace( /\./g, '`.`' ) + '`'; - var val = self.toDatabase( props[ key ], conds[ key ] ); - if ( conds[ key ] === null ) { - cs.push( keyEscaped + ' IS NULL' ); - } else if ( conds[ key ].constructor.name === 'Object' ) { - Object.keys( conds[ key ] ).forEach( function (condType) { - val = self.toDatabase( props[ key ], conds[ key ][ condType ] ); + var keyEscaped = '`' + key.replace(/\./g, '`.`') + '`'; + var val = self.toDatabase(props[key], conds[key]); + if (conds[key] === null) { + cs.push(keyEscaped + ' IS NULL'); + } else if (conds[key].constructor.name === 'Object') { + Object.keys(conds[key]).forEach(function (condType) { + val = self.toDatabase(props[key], conds[key][condType]); var sqlCond = keyEscaped; - if ( (condType === 'inq' || condType === 'nin') && val.length === 0 ) { - cs.push( condType === 'inq' ? 0 : 1 ); + if ((condType === 'inq' || condType === 'nin') && val.length === 0) { + cs.push(condType === 'inq' ? 0 : 1); return true; } - switch ( condType ) { + switch (condType) { case 'gt': sqlCond += ' > '; break; @@ -899,31 +899,31 @@ function parseCond(cs, key, props, conds, self) { sqlCond += ' ' + condType + ' '; break; } - if ( condType === 'between' ) { - sqlCond += val[ 0 ] + ' AND ' + val[ 1 ]; - } else if ( condType === 'in' || condType === 'inq' || condType === 'nin' ) { + if (condType === 'between') { + sqlCond += val[0] + ' AND ' + val[1]; + } else if (condType === 'in' || condType === 'inq' || condType === 'nin') { sqlCond += "(" + val + ")"; } else { sqlCond += val; } - cs.push( sqlCond ); - } ); + cs.push(sqlCond); + }); - } else if ( /^\//gi.test( conds[ key ] ) ) { - var reg = val.toString().split( '/' ); - cs.push( keyEscaped + ' REGEXP "' + reg[ 1 ] + '"' ); + } else if (/^\//gi.test(conds[key])) { + var reg = val.toString().split('/'); + cs.push(keyEscaped + ' REGEXP "' + reg[1] + '"'); } else { - cs.push( keyEscaped + ' = ' + val ); + cs.push(keyEscaped + ' = ' + val); } return cs; } MySQL.prototype.buildOrderBy = function buildOrderBy(order) { 'use strict'; - if ( typeof order === 'string' ) { - order = [ order ]; + if (typeof order === 'string') { + order = [order]; } - return 'ORDER BY ' + order.join( ', ' ); + return 'ORDER BY ' + order.join(', '); }; MySQL.prototype.buildLimit = function buildLimit(limit, offset) { @@ -933,15 +933,15 @@ MySQL.prototype.buildLimit = function buildLimit(limit, offset) { MySQL.prototype.buildGroupBy = function buildGroupBy(group) { 'use strict'; - if ( typeof group === 'string' ) { - group = [ group ]; + if (typeof group === 'string') { + group = [group]; } - return 'GROUP BY ' + group.join( ', ' ); + return 'GROUP BY ' + group.join(', '); }; function acceptedDefaults(prop) { 'use strict'; - if ( /^INT|^BIGINT|^VAR|^TINY/i.test( datatype( prop ) ) ) { + if (/^INT|^BIGINT|^VAR|^TINY/i.test(datatype(prop))) { return true; } else { return false; @@ -950,11 +950,11 @@ function acceptedDefaults(prop) { function getDefaultValue(prop) { 'use strict'; - if ( /^INT|^BIGINT/i.test( prop.Type || datatype( prop ) ) ) { - return parseInt( prop[ 'default' ] || prop[ 'Default' ] || 0 ); - } else if ( /^TINY/i.test( prop.Type || datatype( prop ) ) ) { - return prop[ 'default' ] || prop[ 'Default' ] ? 1 : 0; + if (/^INT|^BIGINT/i.test(prop.Type || datatype(prop))) { + return parseInt(prop['default'] || prop['Default'] || 0); + } else if (/^TINY/i.test(prop.Type || datatype(prop))) { + return prop['default'] || prop['Default'] ? 1 : 0; } else { - return "'" + (prop[ 'default' ] || prop[ 'Default' ] || '') + "'"; + return "'" + (prop['default'] || prop['Default'] || '') + "'"; } } diff --git a/lib/adapters/sqlite3.js b/lib/adapters/sqlite3.js index 593d6e2..d0de10c 100644 --- a/lib/adapters/sqlite3.js +++ b/lib/adapters/sqlite3.js @@ -659,9 +659,7 @@ SQLite3.prototype.createIndexes = function (model, props, done) { SQLite3.prototype.propertiesSQL = function (model) { var self = this, id = false, sql = [], props = Object.keys(self._models[model].properties); var primaryKeys = this._models[model].settings.primaryKeys || []; - primaryKeys = primaryKeys.slice(0); - props.forEach(function (prop) { if (prop === 'id') { return; @@ -669,7 +667,7 @@ SQLite3.prototype.propertiesSQL = function (model) { if (self._models[model].properties[prop] !== false) { return sql.push('`' + prop + '` ' + self.propertySettingsSQL(model, prop)); } - }.bind(this)); + }); if (primaryKeys.length) { for (var i = 0, length = primaryKeys.length; i < length; i++) { @@ -678,12 +676,12 @@ SQLite3.prototype.propertiesSQL = function (model) { id = true; sql.push('`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'); } else { - sql.push('`' + primaryKeys[i] + '` ' + self.propertySettingsSQL(model, primaryKeys[i])); + sql.push('`' + primaryKeys[i].toString().replace(/,\s|,/,'`,`') + '` ' + self.propertySettingsSQL(model, primaryKeys[i])); } } } if (!id) { - sql.push('PRIMARY KEY (`' + primaryKeys.join('`, `') + '`)'); + sql.push('PRIMARY KEY (`' + primaryKeys.join('`, `').toString().replace(/,\s|,/,'`,`') + '`)'); } } else { if (!id) { diff --git a/package.json b/package.json index 849e37c..ca4995a 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.2.18", + "version": "0.2.19", "author": { "name": "Aleksej Gordejev", "email": "aleksej@gordejev.lv",