diff --git a/lib/dialects/abstract/query-generator.js b/lib/dialects/abstract/query-generator.js index 2d345ee8bf58..6e8683dbef73 100644 --- a/lib/dialects/abstract/query-generator.js +++ b/lib/dialects/abstract/query-generator.js @@ -97,23 +97,6 @@ module.exports = (function() { throwMethodUndefined('renameColumnQuery') }, - /* - Returns a query for selecting elements in the table . - Options: - - attributes -> An array of attributes (e.g. ['name', 'birthday']). Default: * - - where -> A hash with conditions (e.g. {name: 'foo'}) - OR an ID as integer - OR a string with conditions (e.g. 'name="foo"'). - If you use a string, you have to escape it on your own. - - order -> e.g. 'id DESC' - - group - - limit -> The maximum count you want to get. - - offset -> An offset value to start from. Only useable with limit! - */ - selectQuery: function(tableName, options) { - throwMethodUndefined('selectQuery') - }, - /* Returns an insert into command. Parameters: table name + hash of attribute-value-pairs. */ @@ -230,21 +213,6 @@ module.exports = (function() { throwMethodUndefined('removeIndexQuery') }, - /* - Takes something and transforms it into values of a where condition. - */ - getWhereConditions: function(smth) { - throwMethodUndefined('getWhereConditions') - }, - - /* - Takes a hash and transforms it into a mysql where condition: {key: value, key2: value2} ==> key=value AND key2=value2 - The values are transformed by the relevant datatype. - */ - hashToWhereConditions: function(hash) { - throwMethodUndefined('hashToWhereConditions') - }, - /* This method transforms an array of attribute hashes into equivalent sql attribute definition. @@ -376,6 +344,333 @@ module.exports = (function() { */ dropForeignKeyQuery: function(tableName, foreignKey) { throwMethodUndefined('dropForeignKeyQuery') + }, + + + /* + Returns a query for selecting elements in the table . + Options: + - attributes -> An array of attributes (e.g. ['name', 'birthday']). Default: * + - where -> A hash with conditions (e.g. {name: 'foo'}) + OR an ID as integer + OR a string with conditions (e.g. 'name="foo"'). + If you use a string, you have to escape it on your own. + - order -> e.g. 'id DESC' + - group + - limit -> The maximum count you want to get. + - offset -> An offset value to start from. Only useable with limit! + */ + selectQuery: function(tableName, options, factory) { + var table = null, + joinQuery = "" + + options = options || {} + options.table = table = Array.isArray(tableName) ? tableName.map(function(t) { return this.quoteIdentifier(t)}.bind(this)).join(", ") : this.quoteIdentifier(tableName) + options.attributes = options.attributes && options.attributes.map(function(attr){ + if(Array.isArray(attr) && attr.length == 2) { + return [attr[0], this.quoteIdentifier(attr[1])].join(' as ') + } else { + return attr.indexOf(Utils.TICK_CHAR) < 0 ? this.quoteIdentifiers(attr) : attr + } + }.bind(this)).join(", ") + options.attributes = options.attributes || '*' + + if (options.include) { + var optAttributes = options.attributes === '*' ? [options.table + '.*'] : [options.attributes] + + options.include.forEach(function(include) { + var attributes = include.attributes.map(function(attr) { + return this.quoteIdentifier(include.as) + "." + this.quoteIdentifier(attr) + " AS " + this.quoteIdentifier(include.as + "." + attr) + }.bind(this)) + + optAttributes = optAttributes.concat(attributes) + + var table = include.daoFactory.tableName + , as = include.as + + if (!include.association.connectorDAO) { + var primaryKeysLeft = ((include.association.associationType === 'BelongsTo') ? Object.keys(include.association.target.primaryKeys) : Object.keys(include.association.source.primaryKeys)) + , tableLeft = ((include.association.associationType === 'BelongsTo') ? include.as : tableName) + , attrLeft = ((primaryKeysLeft.length !== 1) ? 'id' : primaryKeysLeft[0]) + , tableRight = ((include.association.associationType === 'BelongsTo') ? tableName : include.as) + , attrRight = include.association.identifier + + joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableLeft) + "." + this.quoteIdentifier(attrLeft) + " = " + this.quoteIdentifier(tableRight) + "." + this.quoteIdentifier(attrRight) + } else { + var primaryKeysSource = Object.keys(include.association.source.primaryKeys) + , tableSource = tableName + , identSource = include.association.identifier + , attrSource = ((!include.association.source.hasPrimaryKeys || primaryKeysSource.length !== 1) ? 'id' : primaryKeysSource[0]) + + var primaryKeysTarget = Object.keys(include.association.target.primaryKeys) + , tableTarget = include.as + , identTarget = include.association.foreignIdentifier + , attrTarget = ((!include.association.target.hasPrimaryKeys || primaryKeysTarget.length !== 1) ? 'id' : primaryKeysTarget[0]) + + var tableJunction = include.association.connectorDAO.tableName + joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(tableJunction) + " ON " + this.quoteIdentifier(tableSource) + "." + this.quoteIdentifier(attrSource) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identSource) + joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableTarget) + "." + this.quoteIdentifier(attrTarget) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identTarget) + } + }.bind(this)) + + options.attributes = optAttributes.join(', ') + } + + var conditionalJoins = this.getConditionalJoins(options, factory), + query; + + if (conditionalJoins) { + query = "SELECT " + options.attributes + " FROM ( " + + "SELECT " + options.table + ".* FROM " + options.table + this.getConditionalJoins(options, factory) + } else { + query = "SELECT " + options.attributes + " FROM " + options.table + query += joinQuery + } + + if (options.hasOwnProperty('where')) { + options.where = this.getWhereConditions(options.where, tableName, factory) + query += " WHERE " + options.where + } + + if (options.group) { + options.group = Array.isArray(options.group) ? options.group.map(function (t) { return this.quote(t) }.bind(this)).join(', ') : options.group + query += " GROUP BY " + options.group + } + + if (options.order) { + options.order = Array.isArray(options.order) ? options.order.map(function (t) { return this.quote(t) }.bind(this)).join(', ') : options.order + query += " ORDER BY " + options.order + } + + query = this.addLimitAndOffset(options, query) + + if (conditionalJoins) { + query += ") AS " + options.table + query += joinQuery + } + + query += ";" + + return query + }, + + addLimitAndOffset: function(options, query){ + if (options.offset && !options.limit) { + query += " LIMIT " + options.offset + ", " + 10000000000000; + } else if (options.limit && !(options.include && (options.limit === 1))) { + if (options.offset) { + query += " LIMIT " + options.offset + ", " + options.limit + } else { + query += " LIMIT " + options.limit + } + } + return query; + }, + + /* + Takes something and transforms it into values of a where condition. + */ + getWhereConditions: function(smth, tableName, factory) { + var result = null + , where = {} + + if (Utils.isHash(smth)) { + smth = Utils.prependTableNameToHash(tableName, smth) + result = this.hashToWhereConditions(smth, factory) + } else if (typeof smth === 'number') { + var primaryKeys = !!factory ? Object.keys(factory.primaryKeys) : [] + if (primaryKeys.length > 0) { + // Since we're just a number, assume only the first key + primaryKeys = primaryKeys[0] + } else { + primaryKeys = 'id' + } + + where[primaryKeys] = smth + smth = Utils.prependTableNameToHash(tableName, where) + result = this.hashToWhereConditions(smth) + } else if (typeof smth === "string") { + result = smth + } else if (Array.isArray(smth)) { + result = Utils.format(smth, this.dialect) + } + + return result ? result : '1=1' + }, + + findAssociation: function(attribute, dao){ + var associationToReturn; + + Object.keys(dao.associations).forEach(function(key){ + if(!dao.associations[key]) return; + + + var association = dao.associations[key] + , associationName + + if (association.associationType === 'BelongsTo') { + associationName = Utils.singularize(association.associationAccessor[0].toLowerCase() + association.associationAccessor.slice(1)); + } else { + associationName = association.accessors.get.replace('get', '') + associationName = associationName[0].toLowerCase() + associationName.slice(1); + } + + if(associationName === attribute){ + associationToReturn = association; + } + }); + + return associationToReturn; + }, + + getAssociationFilterDAO: function(filterStr, dao){ + var associationParts = filterStr.split('.') + , self = this + + associationParts.pop() + + associationParts.forEach(function (attribute) { + dao = self.findAssociation(attribute, dao).target; + }); + + return dao; + }, + + isAssociationFilter: function(filterStr, dao){ + if(!dao){ + return false; + } + + var pattern = /^[a-z][a-zA-Z0-9]+(\.[a-z][a-zA-Z0-9]+)+$/; + if (!pattern.test(filterStr)) return false; + + var associationParts = filterStr.split('.') + , attributePart = associationParts.pop() + , self = this + + + return associationParts.every(function (attribute) { + var association = self.findAssociation(attribute, dao); + if (!association) return false; + dao = association.target; + return !!dao; + }) && dao.rawAttributes.hasOwnProperty(attributePart); + }, + + getAssociationFilterColumn: function(filterStr, dao){ + var associationParts = filterStr.split('.') + , attributePart = associationParts.pop() + , self = this + + associationParts.forEach(function (attribute) { + dao = self.findAssociation(attribute, dao).target; + }) + + return dao.tableName + '.' + attributePart; + }, + + getConditionalJoins: function(options, dao){ + var joins = '' + , self = this + + if (Utils.isHash(options.where)) { + Object.keys(options.where).forEach(function(filterStr){ + var associationParts = filterStr.split('.') + , attributePart = associationParts.pop() + + if (self.isAssociationFilter(filterStr, dao)) { + associationParts.forEach(function (attribute) { + var association = self.findAssociation(attribute, dao); + + if(association.associationType === 'BelongsTo'){ + joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.target.tableName) + joins += ' ON ' + self.quoteIdentifiers(association.source.tableName + '.' + association.identifier) + joins += ' = ' + self.quoteIdentifiers(association.target.tableName + '.' + association.target.autoIncrementField) + } else if (association.connectorDAO){ + joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.connectorDAO.tableName) + joins += ' ON ' + self.quoteIdentifiers(association.source.tableName + '.' + association.source.autoIncrementField) + joins += ' = ' + self.quoteIdentifiers(association.connectorDAO.tableName + '.' + association.identifier) + + joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.target.tableName) + joins += ' ON ' + self.quoteIdentifiers(association.connectorDAO.tableName + '.' + association.foreignIdentifier) + joins += ' = ' + self.quoteIdentifiers(association.target.tableName + '.' + association.target.autoIncrementField) + } else { + joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.target.tableName) + joins += ' ON ' + self.quoteIdentifiers(association.source.tableName + '.' + association.source.autoIncrementField) + joins += ' = ' + self.quoteIdentifiers(association.target.tableName + '.' + association.identifier) + } + dao = association.target; + }); + } + }); + } + + return joins; + }, + + /* + Takes a hash and transforms it into a mysql where condition: {key: value, key2: value2} ==> key=value AND key2=value2 + The values are transformed by the relevant datatype. + */ + hashToWhereConditions: function(hash, dao) { + var result = [] + + for (var key in hash) { + var value = hash[key] + + if(this.isAssociationFilter(key, dao)){ + key = this.getAssociationFilterColumn(key, dao); + } + + //handle qualified key names + var _key = this.quoteIdentifiers(key) + , _value = null + + if (Array.isArray(value)) { + // is value an array? + if (value.length === 0) { value = [null] } + _value = "(" + value.map(function(v) { return this.escape(v) }.bind(this)).join(',') + ")" + + result.push([_key, _value].join(" IN ")) + } else if ((value) && (typeof value == 'object') && !(value instanceof Date)) { + if (!!value.join) { + //using as sentinel for join column => value + _value = this.quoteIdentifiers(value.join) + result.push([_key, _value].join("=")) + } else { + for (var logic in value) { + var logicResult = Utils.getWhereLogic(logic) + if (logic === "IN" || logic === "NOT IN") { + var values = Array.isArray(where[i][ii]) ? where[i][ii] : [where[i][ii]] + _where[_where.length] = i + ' ' + logic + ' (' + values.map(function(){ return '?' }).join(',') + ')' + _whereArgs = _whereArgs.concat(values) + } + else if (logicResult === "BETWEEN" || logicResult === "NOT BETWEEN") { + _value = this.escape(value[logic][0]) + var _value2 = this.escape(value[logic][1]) + + result.push(' (' + _key + ' ' + logicResult + ' ' + _value + ' AND ' + _value2 + ') ') + } else { + _value = this.escape(value[logic]) + result.push([_key, _value].join(' ' + logicResult + ' ')) + } + } + } + } else { + if (typeof value === 'boolean') { + _value = this.booleanValue(value); + } else { + _value = this.escape(value) + } + result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("=")) + } + } + + return result.join(" AND ") + }, + + booleanValue: function(value){ + return value; } } diff --git a/lib/dialects/mysql/query-generator.js b/lib/dialects/mysql/query-generator.js index 2c77e6e4d866..067832727962 100644 --- a/lib/dialects/mysql/query-generator.js +++ b/lib/dialects/mysql/query-generator.js @@ -162,100 +162,6 @@ module.exports = (function() { return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') }) }, - selectQuery: function(tableName, options, factory) { - var table = null, - joinQuery = "" - - options = options || {} - options.table = table = Array.isArray(tableName) ? tableName.map(function(t) { return this.quoteIdentifier(t)}.bind(this)).join(", ") : this.quoteIdentifier(tableName) - options.attributes = options.attributes && options.attributes.map(function(attr){ - if(Array.isArray(attr) && attr.length == 2) { - return [attr[0], this.quoteIdentifier(attr[1])].join(' as ') - } else { - return attr.indexOf(Utils.TICK_CHAR) < 0 ? this.quoteIdentifiers(attr) : attr - } - }.bind(this)).join(", ") - options.attributes = options.attributes || '*' - - if (options.include) { - var optAttributes = options.attributes === '*' ? [options.table + '.*'] : [options.attributes] - - options.include.forEach(function(include) { - var attributes = include.attributes.map(function(attr) { - return this.quoteIdentifier(include.as) + "." + this.quoteIdentifier(attr) + " AS " + this.quoteIdentifier(include.as + "." + attr) - }.bind(this)) - - optAttributes = optAttributes.concat(attributes) - - var table = include.daoFactory.tableName - , as = include.as - - if (!include.association.connectorDAO) { - var primaryKeysLeft = ((include.association.associationType === 'BelongsTo') ? Object.keys(include.association.target.primaryKeys) : Object.keys(include.association.source.primaryKeys)) - , tableLeft = ((include.association.associationType === 'BelongsTo') ? include.as : tableName) - , attrLeft = ((primaryKeysLeft.length !== 1) ? 'id' : primaryKeysLeft[0]) - , tableRight = ((include.association.associationType === 'BelongsTo') ? tableName : include.as) - , attrRight = include.association.identifier - - joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableLeft) + "." + this.quoteIdentifier(attrLeft) + " = " + this.quoteIdentifier(tableRight) + "." + this.quoteIdentifier(attrRight) - } else { - var primaryKeysSource = Object.keys(include.association.source.primaryKeys) - , tableSource = tableName - , identSource = include.association.identifier - , attrSource = ((!include.association.source.hasPrimaryKeys || primaryKeysSource.length !== 1) ? 'id' : primaryKeysSource[0]) - - var primaryKeysTarget = Object.keys(include.association.target.primaryKeys) - , tableTarget = include.as - , identTarget = include.association.foreignIdentifier - , attrTarget = ((!include.association.target.hasPrimaryKeys || primaryKeysTarget.length !== 1) ? 'id' : primaryKeysTarget[0]) - - var tableJunction = include.association.connectorDAO.tableName - joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(tableJunction) + " ON " + this.quoteIdentifier(tableSource) + "." + this.quoteIdentifier(attrSource) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identSource) - joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableTarget) + "." + this.quoteIdentifier(attrTarget) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identTarget) - } - }.bind(this)) - - options.attributes = optAttributes.join(', ') - } - - var query = "SELECT " + options.attributes + " FROM " + options.table - query += joinQuery - - if (options.hasOwnProperty('where')) { - options.where = this.getWhereConditions(options.where, tableName, factory) - query += " WHERE " + options.where - } - - if (options.group) { - options.group = Array.isArray(options.group) ? options.group.map(function (t) { return this.quote(t) }.bind(this)).join(', ') : options.group - query += " GROUP BY " + options.group - } - - if (options.order) { - options.order = Array.isArray(options.order) ? options.order.map(function (t) { return this.quote(t) }.bind(this)).join(', ') : options.order - query += " ORDER BY " + options.order - } - - if (options.offset && !options.limit) { - /* - * If no limit is defined, our best bet is to use the max number of rows in a table. From the MySQL docs: - * There is a limit of 2^32 (~4.295E+09) rows in a MyISAM table. If you build MySQL with the --with-big-tables option, - * the row limitation is increased to (2^32)^2 (1.844E+19) rows. - */ - query += " LIMIT " + options.offset + ", " + 18440000000000000000; - } else if (options.limit && !(options.include && (options.limit === 1))) { - if (options.offset) { - query += " LIMIT " + options.offset + ", " + options.limit - } else { - query += " LIMIT " + options.limit - } - } - - query += ";" - - return query - }, - insertQuery: function(tableName, attrValueHash) { attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull) @@ -421,83 +327,6 @@ module.exports = (function() { return Utils._.template(sql)({ tableName: tableName, indexName: indexName }) }, - getWhereConditions: function(smth, tableName, factory) { - var result = null - , where = {} - - if (Utils.isHash(smth)) { - smth = Utils.prependTableNameToHash(tableName, smth) - result = this.hashToWhereConditions(smth) - } else if (typeof smth === 'number') { - var primaryKeys = !!factory ? Object.keys(factory.primaryKeys) : [] - if (primaryKeys.length > 0) { - // Since we're just a number, assume only the first key - primaryKeys = primaryKeys[0] - } else { - primaryKeys = 'id' - } - - where[primaryKeys] = smth - smth = Utils.prependTableNameToHash(tableName, where) - result = this.hashToWhereConditions(smth) - } else if (typeof smth === "string") { - result = smth - } else if (Array.isArray(smth)) { - result = Utils.format(smth, this.dialect) - } - - return result ? result : '1=1' - }, - - hashToWhereConditions: function(hash) { - var result = [] - - for (var key in hash) { - var value = hash[key] - - //handle qualified key names - var _key = this.quoteIdentifiers(key) - , _value = null - - if (Array.isArray(value)) { - // is value an array? - if (value.length === 0) { value = [null] } - _value = "(" + value.map(function(v) { return this.escape(v) }.bind(this)).join(',') + ")" - - result.push([_key, _value].join(" IN ")) - } else if ((value) && (typeof value == 'object') && !(value instanceof Date)) { - if (!!value.join) { - //using as sentinel for join column => value - _value = this.quoteIdentifiers(value.join) - result.push([_key, _value].join("=")) - } else { - for (var logic in value) { - var logicResult = Utils.getWhereLogic(logic) - if (logic === "IN" || logic === "NOT IN") { - var values = Array.isArray(where[i][ii]) ? where[i][ii] : [where[i][ii]] - _where[_where.length] = i + ' ' + logic + ' (' + values.map(function(){ return '?' }).join(',') + ')' - _whereArgs = _whereArgs.concat(values) - } - else if (logicResult === "BETWEEN" || logicResult === "NOT BETWEEN") { - _value = this.escape(value[logic][0]) - var _value2 = this.escape(value[logic][1]) - - result.push(' (' + _key + ' ' + logicResult + ' ' + _value + ' AND ' + _value2 + ') ') - } else { - _value = this.escape(value[logic]) - result.push([_key, _value].join(' ' + logicResult + ' ')) - } - } - } - } else { - _value = this.escape(value) - result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("=")) - } - } - - return result.join(" AND ") - }, - attributesToSQL: function(attributes) { var result = {} @@ -588,6 +417,19 @@ module.exports = (function() { return fields }, + addLimitAndOffset: function(options, query){ + if (options.offset && !options.limit) { + query += " LIMIT " + options.offset + ", " + 18440000000000000000; + } else if (options.limit && !(options.include && (options.limit === 1))) { + if (options.offset) { + query += " LIMIT " + options.offset + ", " + options.limit + } else { + query += " LIMIT " + options.limit + } + } + return query; + }, + quoteIdentifier: function(identifier, force) { return Utils.addTicks(identifier, "`") }, diff --git a/lib/dialects/sqlite/query-generator.js b/lib/dialects/sqlite/query-generator.js index 9b0349b0c225..819debb45af7 100644 --- a/lib/dialects/sqlite/query-generator.js +++ b/lib/dialects/sqlite/query-generator.js @@ -109,6 +109,10 @@ module.exports = (function() { return this.replaceBooleanDefaults(sql) }, + booleanValue: function(value){ + return !!value ? 1 : 0; + }, + dropTableQuery: function(tableName, options) { options = options || {} @@ -119,6 +123,19 @@ module.exports = (function() { }) }, + addLimitAndOffset: function(options, query){ + if (options.offset && !options.limit) { + query += " LIMIT " + options.offset + ", " + 10000000000000; + } else if (options.limit && !(options.include && (options.limit === 1))) { + if (options.offset) { + query += " LIMIT " + options.offset + ", " + options.limit + } else { + query += " LIMIT " + options.limit + } + } + return query; + }, + addColumnQuery: function() { var sql = MySqlQueryGenerator.addColumnQuery.apply(this, arguments) return this.replaceBooleanDefaults(sql) @@ -165,303 +182,6 @@ module.exports = (function() { return Utils._.template(query)(replacements) }, - selectQuery: function(tableName, options, factory) { - var table = null, - joinQuery = "" - - options = options || {} - options.table = table = Array.isArray(tableName) ? tableName.map(function(t) { return this.quoteIdentifier(t)}.bind(this)).join(", ") : this.quoteIdentifier(tableName) - options.attributes = options.attributes && options.attributes.map(function(attr){ - if(Array.isArray(attr) && attr.length == 2) { - return [attr[0], this.quoteIdentifier(attr[1])].join(' as ') - } else { - return attr.indexOf(Utils.TICK_CHAR) < 0 ? this.quoteIdentifiers(attr) : attr - } - }.bind(this)).join(", ") - options.attributes = options.attributes || '*' - - if (options.include) { - var optAttributes = options.attributes === '*' ? [options.table + '.*'] : [options.attributes] - - options.include.forEach(function(include) { - var attributes = include.attributes.map(function(attr) { - return this.quoteIdentifier(include.as) + "." + this.quoteIdentifier(attr) + " AS " + this.quoteIdentifier(include.as + "." + attr) - }.bind(this)) - - optAttributes = optAttributes.concat(attributes) - - var table = include.daoFactory.tableName - , as = include.as - - if (!include.association.connectorDAO) { - var primaryKeysLeft = ((include.association.associationType === 'BelongsTo') ? Object.keys(include.association.target.primaryKeys) : Object.keys(include.association.source.primaryKeys)) - , tableLeft = ((include.association.associationType === 'BelongsTo') ? include.as : tableName) - , attrLeft = ((primaryKeysLeft.length !== 1) ? 'id' : primaryKeysLeft[0]) - , tableRight = ((include.association.associationType === 'BelongsTo') ? tableName : include.as) - , attrRight = include.association.identifier - - joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableLeft) + "." + this.quoteIdentifier(attrLeft) + " = " + this.quoteIdentifier(tableRight) + "." + this.quoteIdentifier(attrRight) - } else { - var primaryKeysSource = Object.keys(include.association.source.primaryKeys) - , tableSource = tableName - , identSource = include.association.identifier - , attrSource = ((!include.association.source.hasPrimaryKeys || primaryKeysSource.length !== 1) ? 'id' : primaryKeysSource[0]) - - var primaryKeysTarget = Object.keys(include.association.target.primaryKeys) - , tableTarget = include.as - , identTarget = include.association.foreignIdentifier - , attrTarget = ((!include.association.target.hasPrimaryKeys || primaryKeysTarget.length !== 1) ? 'id' : primaryKeysTarget[0]) - - var tableJunction = include.association.connectorDAO.tableName - joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(tableJunction) + " ON " + this.quoteIdentifier(tableSource) + "." + this.quoteIdentifier(attrSource) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identSource) - joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableTarget) + "." + this.quoteIdentifier(attrTarget) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identTarget) - } - }.bind(this)) - - options.attributes = optAttributes.join(', ') - } - - var conditionalJoins = this.getConditionalJoins(options, factory), - query; - - if (conditionalJoins) { - query = "SELECT " + options.attributes + " FROM ( " - + "SELECT * FROM " + options.table + this.getConditionalJoins(options, factory) - } else { - query = "SELECT " + options.attributes + " FROM " + options.table - query += joinQuery - } - - if (options.hasOwnProperty('where')) { - options.where = this.getWhereConditions(options.where, tableName, factory) - query += " WHERE " + options.where - } - - if (options.group) { - options.group = Array.isArray(options.group) ? options.group.map(function (t) { return this.quote(t) }.bind(this)).join(', ') : options.group - query += " GROUP BY " + options.group - } - - if (options.order) { - options.order = Array.isArray(options.order) ? options.order.map(function (t) { return this.quote(t) }.bind(this)).join(', ') : options.order - query += " ORDER BY " + options.order - } - - if (options.offset && !options.limit) { - query += " LIMIT " + options.offset + ", " + 10000000000000; - } else if (options.limit && !(options.include && (options.limit === 1))) { - if (options.offset) { - query += " LIMIT " + options.offset + ", " + options.limit - } else { - query += " LIMIT " + options.limit - } - } - - if (conditionalJoins) { - query += ") AS " + options.table - query += joinQuery - } - - query += ";" - - return query - }, - - getWhereConditions: function(smth, tableName, factory) { - var result = null - , where = {} - - if (Utils.isHash(smth)) { - smth = Utils.prependTableNameToHash(tableName, smth) - result = this.hashToWhereConditions(smth, factory) - } else if (typeof smth === 'number') { - var primaryKeys = !!factory ? Object.keys(factory.primaryKeys) : [] - if (primaryKeys.length > 0) { - // Since we're just a number, assume only the first key - primaryKeys = primaryKeys[0] - } else { - primaryKeys = 'id' - } - - where[primaryKeys] = smth - smth = Utils.prependTableNameToHash(tableName, where) - result = this.hashToWhereConditions(smth) - } else if (typeof smth === "string") { - result = smth - } else if (Array.isArray(smth)) { - result = Utils.format(smth, this.dialect) - } - - return result ? result : '1=1' - }, - - findAssociation: function(attribute, dao){ - var associationToReturn; - - Object.keys(dao.associations).forEach(function(key){ - if(!dao.associations[key]) return; - - - var association = dao.associations[key] - , associationName - - if (association.associationType === 'BelongsTo') { - associationName = Utils.singularize(association.associationAccessor[0].toLowerCase() + association.associationAccessor.slice(1)); - } else { - associationName = association.accessors.get.replace('get', '') - associationName = associationName[0].toLowerCase() + associationName.slice(1); - } - - if(associationName === attribute){ - associationToReturn = association; - } - }); - - return associationToReturn; - }, - - getAssociationFilterDAO: function(filterStr, dao){ - var associationParts = filterStr.split('.') - , self = this - - associationParts.pop() - - associationParts.forEach(function (attribute) { - dao = self.findAssociation(attribute, dao).target; - }); - - return dao; - }, - - isAssociationFilter: function(filterStr, dao){ - if(!dao){ - return false; - } - - var pattern = /^[a-z][a-zA-Z0-9]+(\.[a-z][a-zA-Z0-9]+)+$/; - if (!pattern.test(filterStr)) return false; - - var associationParts = filterStr.split('.') - , attributePart = associationParts.pop() - , self = this - - - return associationParts.every(function (attribute) { - var association = self.findAssociation(attribute, dao); - if (!association) return false; - dao = association.target; - return !!dao; - }) && dao.rawAttributes.hasOwnProperty(attributePart); - }, - - getAssociationFilterColumn: function(filterStr, dao){ - var associationParts = filterStr.split('.') - , attributePart = associationParts.pop() - , self = this - - associationParts.forEach(function (attribute) { - dao = self.findAssociation(attribute, dao).target; - }) - - return dao.tableName + '.' + attributePart; - }, - - getConditionalJoins: function(options, dao){ - var joins = '' - , self = this - - if (Utils.isHash(options.where)) { - Object.keys(options.where).forEach(function(filterStr){ - var associationParts = filterStr.split('.') - , attributePart = associationParts.pop() - - if (self.isAssociationFilter(filterStr, dao)) { - associationParts.forEach(function (attribute) { - var association = self.findAssociation(attribute, dao); - - if(association.associationType === 'BelongsTo'){ - joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.target.tableName) - joins += ' ON ' + self.quoteIdentifiers(association.source.tableName + '.' + association.identifier) - joins += ' = ' + self.quoteIdentifiers(association.target.tableName + '.' + association.target.autoIncrementField) - } else if (association.connectorDAO){ - joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.connectorDAO.tableName) - joins += ' ON ' + self.quoteIdentifiers(association.source.tableName + '.' + association.source.autoIncrementField) - joins += ' = ' + self.quoteIdentifiers(association.connectorDAO.tableName + '.' + association.identifier) - - joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.target.tableName) - joins += ' ON ' + self.quoteIdentifiers(association.connectorDAO.tableName + '.' + association.foreignIdentifier) - joins += ' = ' + self.quoteIdentifiers(association.target.tableName + '.' + association.target.autoIncrementField) - } else { - joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.target.tableName) - joins += ' ON ' + self.quoteIdentifiers(association.source.tableName + '.' + association.source.autoIncrementField) - joins += ' = ' + self.quoteIdentifiers(association.target.tableName + '.' + association.identifier) - } - dao = association.target; - }); - } - }); - } - - return joins; - }, - - hashToWhereConditions: function(hash, dao) { - var result = [] - - for (var key in hash) { - var value = hash[key] - - if(this.isAssociationFilter(key, dao)){ - key = this.getAssociationFilterColumn(key, dao); - } - - //handle qualified key names - var _key = this.quoteIdentifiers(key) - , _value = null - - if (Array.isArray(value)) { - // is value an array? - if (value.length === 0) { value = [null] } - _value = "(" + value.map(function(v) { return this.escape(v) }.bind(this)).join(',') + ")" - - result.push([_key, _value].join(" IN ")) - } else if ((value) && (typeof value == 'object') && !(value instanceof Date)) { - if (!!value.join) { - //using as sentinel for join column => value - _value = this.quoteIdentifiers(value.join) - result.push([_key, _value].join("=")) - } else { - for (var logic in value) { - var logicResult = Utils.getWhereLogic(logic) - if (logic === "IN" || logic === "NOT IN") { - var values = Array.isArray(where[i][ii]) ? where[i][ii] : [where[i][ii]] - _where[_where.length] = i + ' ' + logic + ' (' + values.map(function(){ return '?' }).join(',') + ')' - _whereArgs = _whereArgs.concat(values) - } - else if (logicResult === "BETWEEN" || logicResult === "NOT BETWEEN") { - _value = this.escape(value[logic][0]) - var _value2 = this.escape(value[logic][1]) - - result.push(' (' + _key + ' ' + logicResult + ' ' + _value + ' AND ' + _value2 + ') ') - } else { - _value = this.escape(value[logic]) - result.push([_key, _value].join(' ' + logicResult + ' ')) - } - } - } - } else { - if (typeof value === 'boolean') { - _value = !!value ? 1 : 0 - } else { - _value = this.escape(value) - result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("=")) - } - } - } - - return result.join(" AND ") - }, - updateQuery: function(tableName, attrValueHash, where, options) { attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)