不要怂,就是干,撸起袖子干!

Commit 8462350b by Mick Hansen

Merge pull request #1195 from mickhansen/query-generator-dry-refactor

Refactoring queryGenerator to make it more ... DRY
2 parents 58d51729 64c29225
var AbstractDialect = function() {
}
AbstractDialect.prototype.supports = {
'RETURNING': false,
'DEFAULT': true,
'DEFAULT VALUES': false,
'VALUES ()': false
}
module.exports = AbstractDialect
\ No newline at end of file
......@@ -35,8 +35,12 @@ module.exports = (function() {
- originalTableName: Name of the table before execution.
- futureTableName: Name of the table after execution.
*/
renameTableQuery: function(originalTableName, futureTableName) {
throwMethodUndefined('renameTableQuery')
renameTableQuery: function(before, after) {
var query = "ALTER TABLE <%= before %> RENAME TO <%= after %>;"
return Utils._.template(query)({
before: this.quoteTable(before),
after: this.quoteTable(after)
})
},
/*
......@@ -100,10 +104,56 @@ module.exports = (function() {
/*
Returns an insert into command. Parameters: table name + hash of attribute-value-pairs.
*/
insertQuery: function(tableName, attrValueHash) {
throwMethodUndefined('insertQuery')
},
insertQuery: function(table, valueHash, modelAttributes) {
var query
, valueQuery = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>)"
, emptyQuery = "INSERT INTO <%= table %>"
, fields = []
, values = []
, key
, value
if (this._dialect.supports['DEFAULT VALUES']) {
emptyQuery += " DEFAULT VALUES"
} else if (this._dialect.supports['VALUES ()']) {
emptyQuery += " VALUES ()"
}
if (this._dialect.supports['RETURNING']) {
valueQuery += " RETURNING *"
emptyQuery += " RETURNING *"
}
valueHash = Utils.removeNullValuesFromHash(valueHash, this.options.omitNull)
for (key in valueHash) {
if (valueHash.hasOwnProperty(key)) {
value = valueHash[key]
fields.push(this.quoteIdentifier(key))
// SERIALS' can't be NULL in postgresql, use DEFAULT where supported
if (modelAttributes && modelAttributes[key] && modelAttributes[key].autoIncrement === true && !value) {
if (this._dialect.supports['DEFAULT']) {
values.push('DEFAULT')
} else {
values.push(this.escape(null))
}
} else {
values.push(this.escape(value, (modelAttributes && modelAttributes[key]) || undefined))
}
}
}
var replacements = {
table: this.quoteTable(table),
attributes: fields.join(","),
values: values.join(",")
}
query = (replacements.attributes.length ? valueQuery : emptyQuery) + ";"
return Utils._.template(query)(replacements)
},
/*
Returns an insert into command for multiple values.
Parameters: table name + list of hashes of attribute-value-pairs.
......@@ -122,8 +172,29 @@ module.exports = (function() {
OR a string with conditions (e.g. 'name="foo"').
If you use a string, you have to escape it on your own.
*/
updateQuery: function(tableName, values, where) {
throwMethodUndefined('updateQuery')
updateQuery: function(tableName, attrValueHash, where, options, attributes) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)
var query
, values = []
query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
if (this._dialect.supports['RETURNING']) {
query += " RETURNING *"
}
for (var key in attrValueHash) {
var value = attrValueHash[key]
values.push(this.quoteIdentifier(key) + "=" + this.escape(value, (!!attributes && !!attributes[key] ? attributes[key] : undefined)))
}
var replacements = {
table: this.quoteTable(tableName),
values: values.join(","),
where: this.getWhereConditions(where)
}
return Utils._.template(query)(replacements)
},
/*
......@@ -169,8 +240,35 @@ module.exports = (function() {
OR a string with conditions (e.g. 'name="foo"').
If you use a string, you have to escape it on your own.
*/
incrementQuery: function(tableName, values, where) {
throwMethodUndefined('incrementQuery')
incrementQuery: function(tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query
, values = []
query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
if (this._dialect.supports['RETURNING']) {
query += " RETURNING *"
}
for (var key in attrValueHash) {
var value = attrValueHash[key]
values.push(this.quoteIdentifier(key) + "=" + this.quoteIdentifier(key) + " + " + this.escape(value))
}
options = options || {}
for (var key in options) {
var value = options[key];
values.push(this.quoteIdentifier(key) + "=" + this.escape(value))
}
var replacements = {
table: this.quoteIdentifiers(tableName),
values: values.join(","),
where: this.getWhereConditions(where)
}
return Utils._.template(query)(replacements)
},
/*
......@@ -489,11 +587,11 @@ module.exports = (function() {
* @return {String} The generated sql query.
*/
setAutocommitQuery: function(value) {
throwMethodUndefined('setAutocommitQuery')
return "SET autocommit = " + (!!value ? 1 : 0) + ";"
},
setIsolationLevelQuery: function(value) {
throwMethodUndefined('setIsolationLevelQuery')
return "SET SESSION TRANSACTION ISOLATION LEVEL " + value + ";"
},
/**
......@@ -503,7 +601,7 @@ module.exports = (function() {
* @return {String} The generated sql query.
*/
startTransactionQuery: function(options) {
throwMethodUndefined('startTransactionQuery')
return "START TRANSACTION;"
},
/**
......@@ -513,7 +611,7 @@ module.exports = (function() {
* @return {String} The generated sql query.
*/
commitTransactionQuery: function(options) {
throwMethodUndefined('commitTransactionQuery')
return "COMMIT;"
},
/**
......@@ -523,7 +621,7 @@ module.exports = (function() {
* @return {String} The generated sql query.
*/
rollbackTransactionQuery: function(options) {
throwMethodUndefined('rollbackTransactionQuery')
return "ROLLBACK;"
},
addLimitAndOffset: function(options, query){
......
var _ = require('lodash')
, MySQL = require('./mysql')
var MariaDialect = function(sequelize) {
this.sequelize = sequelize
}
MariaDialect.prototype = _.extend({
}, MySQL.prototype)
module.exports = MariaDialect
\ No newline at end of file
var _ = require('lodash')
, Abstract = require('./abstract/dialect')
var MysqlDialect = function(sequelize) {
this.sequelize = sequelize
}
MysqlDialect.prototype.supports = _.extend(Abstract.prototype.supports, {
'VALUES ()': true
})
module.exports = MysqlDialect
\ No newline at end of file
......@@ -99,11 +99,6 @@ module.exports = (function() {
})
},
renameTableQuery: function(before, after) {
var query = "RENAME TABLE `<%= before %>` TO `<%= after %>`;"
return Utils._.template(query)({ before: before, after: after })
},
showTablesQuery: function() {
return 'SHOW TABLES;'
},
......@@ -162,18 +157,6 @@ module.exports = (function() {
return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') })
},
insertQuery: function(tableName, attrValueHash) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var table = this.quoteIdentifier(tableName)
var attributes = Object.keys(attrValueHash).map(function(attr){return this.quoteIdentifier(attr)}.bind(this)).join(",")
var values = Utils._.values(attrValueHash).map(function(v) { return this.escape(v) }.bind(this)).join(",")
var query = "INSERT INTO " + table + " (" + attributes + ") VALUES (" + values + ");"
return query
},
bulkInsertQuery: function(tableName, attrValueHashes) {
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %>;"
, tuples = []
......@@ -204,25 +187,6 @@ module.exports = (function() {
return Utils._.template(query)(replacements)
},
updateQuery: function(tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)
var values = []
for (var key in attrValueHash) {
var value = attrValueHash[key]
, _value = this.escape(value)
values.push(this.quoteIdentifier(key) + "=" + _value)
}
var query = "UPDATE " + this.quoteIdentifier(tableName) +
" SET " + values.join(",") +
" WHERE " + this.getWhereConditions(where)
return query
},
deleteQuery: function(tableName, where, options) {
options = options || {}
......@@ -257,33 +221,6 @@ module.exports = (function() {
return query
},
incrementQuery: function (tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var values = []
for (var key in attrValueHash) {
var value = attrValueHash[key]
, _value = this.escape(value)
values.push(this.quoteIdentifier(key) + "=" + this.quoteIdentifier(key) + " + " + _value)
}
options = options || {}
for (var key in options) {
var value = options[key];
values.push(this.quoteIdentifier(key) + "=" + this.escape(value))
}
var table = this.quoteIdentifier(tableName)
values = values.join(",")
where = this.getWhereConditions(where)
var query = "UPDATE " + table + " SET " + values + " WHERE " + where
return query
},
addIndexQuery: function(tableName, attributes, options) {
var transformedAttributes = attributes.map(function(attribute) {
if(typeof attribute === 'string') {
......@@ -346,38 +283,6 @@ module.exports = (function() {
return Utils._.template(sql)({ tableName: tableName, indexName: indexName })
},
/**
* Returns a query that starts a transaction.
*
* @param {Boolean} value A boolean that states whether autocommit shall be done or not.
* @return {String} The generated sql query.
*/
setAutocommitQuery: function(value) {
return "SET autocommit = " + (!!value ? 1 : 0) + ";"
},
setIsolationLevelQuery: function(value) {
return "SET SESSION TRANSACTION ISOLATION LEVEL " + value + ";"
},
/**
* Returns a query that starts a transaction.
*
* @param {Object} options An object with options.
* @return {String} The generated sql query.
*/
startTransactionQuery: function(options) {
return "START TRANSACTION;"
},
commitTransactionQuery: function(options) {
return "COMMIT;"
},
rollbackTransactionQuery: function(options) {
return "ROLLBACK;"
},
attributesToSQL: function(attributes) {
var result = {}
......@@ -489,6 +394,10 @@ module.exports = (function() {
return identifiers.split('.').map(function(v) { return this.quoteIdentifier(v, force) }.bind(this)).join('.')
},
quoteTable: function(table) {
return this.quoteIdentifier(table)
},
/**
* Generates an SQL query that returns all foreign keys of a table.
*
......
var _ = require('lodash')
, Abstract = require('./abstract/dialect')
var PostgresDialect = function(sequelize) {
this.sequelize = sequelize
}
PostgresDialect.prototype.supports = _.extend(Abstract.prototype.supports, {
'RETURNING': true,
'DEFAULT VALUES': true
})
module.exports = PostgresDialect
\ No newline at end of file
......@@ -97,14 +97,6 @@ module.exports = (function() {
})
},
renameTableQuery: function(before, after) {
var query = "ALTER TABLE <%= before %> RENAME TO <%= after %>;"
return Utils._.template(query)({
before: this.quoteIdentifier(before),
after: this.quoteIdentifier(after)
})
},
showTablesQuery: function() {
return "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';"
},
......@@ -255,43 +247,6 @@ module.exports = (function() {
})
},
insertQuery: function(tableName, attrValueHash, attributes) {
var query
, valueQuery = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>) RETURNING *;"
, emptyQuery = "INSERT INTO <%= table %> DEFAULT VALUES RETURNING *;"
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
// Remove serials that are null or undefined, which causes an error in PG
Utils._.forEach(attrValueHash, function(value, key, hash) {
if (tables[tableName]) {
switch (tables[tableName][key]) {
case 'bigserial':
case 'serial':
if ([null, undefined].indexOf(hash[key]) !== -1) delete hash[key]
break
}
}
});
var rowValues = []
Object.keys(attrValueHash).forEach(function(attr) {
rowValues[rowValues.length] = this.escape(attrValueHash[attr], (!!attributes && !!attributes[attr] ? attributes[attr] : undefined))
}.bind(this))
var replacements = {
table: this.quoteIdentifiers(tableName)
, attributes: Object.keys(attrValueHash).map(function(attr){
return this.quoteIdentifier(attr)
}.bind(this)).join(",")
, values: rowValues.join(",")
}
query = replacements.attributes.length ? valueQuery : emptyQuery
return Utils._.template(query)(replacements)
},
bulkInsertQuery: function(tableName, attrValueHashes) {
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %> RETURNING *;"
, tuples = []
......@@ -332,26 +287,6 @@ module.exports = (function() {
return Utils._.template(query)(replacements)
},
updateQuery: function(tableName, attrValueHash, where, options, attributes) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)
var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %> RETURNING *"
, values = []
for (var key in attrValueHash) {
var value = attrValueHash[key]
values.push(this.quoteIdentifier(key) + "=" + this.escape(value, (!!attributes && !!attributes[key] ? attributes[key] : undefined)))
}
var replacements = {
table: this.quoteIdentifiers(tableName),
values: values.join(","),
where: this.getWhereConditions(where)
}
return Utils._.template(query)(replacements)
},
deleteQuery: function(tableName, where, options, factory) {
options = options || {}
......@@ -391,33 +326,6 @@ module.exports = (function() {
return Utils._.template(query)(replacements)
},
incrementQuery: function(tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %> RETURNING *"
, values = []
for (var key in attrValueHash) {
var value = attrValueHash[key]
values.push(this.quoteIdentifier(key) + "=" + this.quoteIdentifier(key) + " + " + this.escape(value))
}
options = options || {}
for (var key in options) {
var value = options[key];
values.push(this.quoteIdentifier(key) + "=" + this.escape(value))
}
var replacements = {
table: this.quoteIdentifiers(tableName),
values: values.join(","),
where: this.getWhereConditions(where)
}
return Utils._.template(query)(replacements)
},
addIndexQuery: function(tableName, attributes, options) {
var transformedAttributes = attributes.map(function(attribute) {
if (typeof attribute === 'string') {
......@@ -882,6 +790,10 @@ module.exports = (function() {
return identifiers.split('.').map(function(t) { return this.quoteIdentifier(t, force) }.bind(this)).join('.')
},
quoteTable: function(table) {
return this.quoteIdentifiers(table)
},
/**
* Generates an SQL query that returns all foreign keys of a table.
*
......@@ -902,39 +814,7 @@ module.exports = (function() {
*/
dropForeignKeyQuery: function(tableName, foreignKey) {
return 'ALTER TABLE ' + this.quoteIdentifier(tableName) + ' DROP CONSTRAINT ' + this.quoteIdentifier(foreignKey) + ';'
},
/**
* Returns a query that starts a transaction.
*
* @param {Boolean} value A boolean that states whether autocommit shall be done or not.
* @return {String} The generated sql query.
*/
setAutocommitQuery: function(value) {
return "SET autocommit = " + (!!value ? 1 : 0) + ";"
},
setIsolationLevelQuery: function(value) {
return "SET SESSION TRANSACTION ISOLATION LEVEL " + value + ";"
},
/**
* Returns a query that starts a transaction.
*
* @param {Object} options An object with options.
* @return {String} The generated sql query.
*/
startTransactionQuery: function(options) {
return "START TRANSACTION;"
},
commitTransactionQuery: function(options) {
return "COMMIT;"
},
rollbackTransactionQuery: function(options) {
return "ROLLBACK;"
},
}
}
// Private
......
var _ = require('lodash')
, Abstract = require('./abstract/dialect')
var SqliteDialect = function(sequelize) {
this.sequelize = sequelize
}
SqliteDialect.prototype.supports = _.extend(Abstract.prototype.supports, {
'DEFAULT': false,
'DEFAULT VALUES': true
})
module.exports = SqliteDialect
\ No newline at end of file
......@@ -153,25 +153,6 @@ module.exports = (function() {
return "SELECT name FROM sqlite_master WHERE type='table' and name!='sqlite_sequence';"
},
insertQuery: function(tableName, attrValueHash) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query
, valueQuery = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>);"
, emptyQuery = "INSERT INTO <%= table %> DEFAULT VALUES;"
var replacements = {
table: this.quoteIdentifier(tableName),
attributes: Object.keys(attrValueHash).map(function(attr){return this.quoteIdentifier(attr)}.bind(this)).join(","),
values: Utils._.values(attrValueHash).map(function(value){
return this.escape(value)
}.bind(this)).join(",")
}
query = replacements.attributes.length ? valueQuery : emptyQuery
return Utils._.template(query)(replacements)
},
bulkInsertQuery: function(tableName, attrValueHashes) {
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %>;"
, tuples = []
......@@ -202,26 +183,6 @@ module.exports = (function() {
return Utils._.template(query)(replacements)
},
updateQuery: function(tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)
var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
, values = []
for (var key in attrValueHash) {
var value = attrValueHash[key]
values.push(this.quoteIdentifier(key) + "=" + this.escape(value))
}
var replacements = {
table: this.quoteIdentifier(tableName),
values: values.join(","),
where: this.getWhereConditions(where)
}
return Utils._.template(query)(replacements)
},
deleteQuery: function(tableName, where, options) {
options = options || {}
......@@ -234,32 +195,6 @@ module.exports = (function() {
return Utils._.template(query)(replacements)
},
incrementQuery: function(tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
, values = []
for (var key in attrValueHash) {
var value = attrValueHash[key]
values.push(this.quoteIdentifier(key) + "=" + this.quoteIdentifier(key) + "+ " + this.escape(value))
}
options = options || {}
for (var key in options) {
var value = options[key];
values.push(this.quoteIdentifier(key) + "=" + this.escape(value))
}
var replacements = {
table: this.quoteIdentifier(tableName),
values: values.join(","),
where: this.getWhereConditions(where)
}
return Utils._.template(query)(replacements)
},
attributesToSQL: function(attributes) {
var result = {}
......@@ -374,11 +309,6 @@ module.exports = (function() {
return Utils._.template(sql, { tableName: this.addSchema({tableName: tableName, options: options})})
},
renameTableQuery: function(before, after) {
var query = "ALTER TABLE `<%= before %>` RENAME TO `<%= after %>`;"
return Utils._.template(query, { before: before, after: after })
},
removeColumnQuery: function(tableName, attributes) {
attributes = this.attributesToSQL(attributes)
......@@ -457,6 +387,10 @@ module.exports = (function() {
return identifiers.split('.').map(function(v) { return this.quoteIdentifier(v, force) }.bind(this)).join('.')
},
quoteTable: function(table) {
return this.quoteIdentifier(table)
},
/**
* Generates an SQL query that returns all foreign keys of a table.
*
......
......@@ -8,6 +8,7 @@ module.exports = (function() {
this.sequelize = sequelize
this.QueryGenerator = require('./dialects/' + this.sequelize.options.dialect + '/query-generator')
this.QueryGenerator.options = this.sequelize.options
this.QueryGenerator._dialect = this.sequelize.dialect
}
Utils.addEventEmitter(QueryInterface)
......
......@@ -112,6 +112,11 @@ module.exports = (function() {
dialectOptions: this.options.dialectOptions,
}
try {
this.dialect = new (require("./dialects/" + this.getDialect()))(this)
} catch(err) {
throw new Error("The dialect " + this.getDialect() + " is not supported.")
}
this.daoFactoryManager = new DAOFactoryManager(this)
this.transactionManager = new TransactionManager(this)
......
......@@ -1255,7 +1255,8 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it("should be able to create and update records under any valid schematic", function(done){
var self = this
self.UserPublic.sync({ force: true }).success(function(UserPublicSync){
self.UserPublic.sync({ force: true }).done(function(err, UserPublicSync){
expect(err).not.to.be.ok
UserPublicSync.create({age: 3}).on('sql', function(UserPublic){
self.UserSpecialSync.schema('special').create({age: 3})
.on('sql', function(UserSpecial){
......@@ -1275,7 +1276,8 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).to.be.above(-1)
}
})
.success(function(UserSpecial){
.done(function(err, UserSpecial){
expect(err).not.to.be.ok
UserSpecial.updateAttributes({age: 5})
.on('sql', function(user){
expect(user).to.exist
......@@ -1285,8 +1287,12 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
expect(user.indexOf('UPDATE `special.UserSpecials`')).to.be.above(-1)
}
done()
}).error(function (err) {
expect(err).not.to.be.ok
})
})
}).error(function (err) {
expect(err).not.to.be.ok
})
})
})
......
......@@ -598,7 +598,18 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow blank creates (with timestamps: false)', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) {
Worker.create().done(function (err, worker) {
Worker.create({}, {fields: []}).done(function (err, worker) {
expect(err).not.to.be.ok
expect(worker).to.be.ok
done()
})
})
})
it('should allow truly blank creates', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) {
Worker.create({}, {fields: []}).done(function (err, worker) {
expect(err).not.to.be.ok
expect(worker).to.be.ok
done()
......
......@@ -516,6 +516,7 @@ if (Support.dialectIsMySQL()) {
test.arguments[1] = test.arguments[1](this.sequelize)
}
QueryGenerator.options = context.options
QueryGenerator._dialect = this.sequelize.dialect
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).to.deep.equal(test.expectation)
done()
......
......@@ -904,6 +904,7 @@ if (dialect.match(/^postgres/)) {
test.arguments[1] = test.arguments[1](this.sequelize)
}
QueryGenerator.options = context.options
QueryGenerator._dialect = this.sequelize.dialect
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).to.deep.equal(test.expectation)
done()
......
......@@ -444,6 +444,7 @@ if (dialect === 'sqlite') {
test.arguments[1] = test.arguments[1](this.sequelize)
}
QueryGenerator.options = context.options
QueryGenerator._dialect = this.sequelize.dialect
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).to.deep.equal(test.expectation)
done()
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!