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

Commit d8b1565d by Jonathan M. Altman

Merge remote-tracking branch 'upstream/master' into basic_function_and_trigger_migrations

2 parents af723ee4 df2ae8b5
...@@ -4,4 +4,6 @@ test*.js ...@@ -4,4 +4,6 @@ test*.js
.DS_STORE .DS_STORE
node_modules node_modules
npm-debug.log npm-debug.log
*~ *~
\ No newline at end of file test/binary/tmp/*
test/tmp/*
...@@ -4,6 +4,7 @@ before_script: ...@@ -4,6 +4,7 @@ before_script:
script: script:
- "make test" - "make test"
- "make binary"
notifications: notifications:
email: email:
......
...@@ -26,6 +26,8 @@ postgres: ...@@ -26,6 +26,8 @@ postgres:
@DIALECT=postgres make test @DIALECT=postgres make test
postgres-native: postgres-native:
@DIALECT=postgres-native make test @DIALECT=postgres-native make test
binary:
@./test/binary/sequelize.test.bats
# test aliases # test aliases
...@@ -36,4 +38,4 @@ postgresn: postgres-native ...@@ -36,4 +38,4 @@ postgresn: postgres-native
all: sqlite mysql postgres postgres-native all: sqlite mysql postgres postgres-native
.PHONY: sqlite mysql postgres pgsql postgres-native postgresn all test .PHONY: sqlite mysql postgres pgsql postgres-native postgresn all test
\ No newline at end of file
#!/usr/bin/env node #!/usr/bin/env node
const path = require("path") var path = require("path")
, fs = require("fs") , fs = require("fs")
, program = require("commander") , program = require("commander")
, Sequelize = require(__dirname + '/../index') , Sequelize = require(__dirname + '/../index')
, moment = require("moment") , moment = require("moment")
, _ = Sequelize.Utils._ , _ = Sequelize.Utils._
var configPath = process.cwd() + '/config' var configuration = {
, environment = process.env.NODE_ENV || 'development' configFile: process.cwd() + '/config/config.json',
, migrationsPath = process.cwd() + '/migrations' environment: process.env.NODE_ENV || 'development',
, packageJsonPath = __dirname + '/../package.json' version: require(__dirname + '/../package.json').version,
, packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString()) migrationsPath: process.cwd() + '/migrations'
, configFile = configPath + '/config.json' }
, configPathExists = fs.existsSync(configPath)
, configFileExists = fs.existsSync(configFile) var configFileExists = function() {
return fs.existsSync(configuration.configFile)
var writeConfig = function(config) { }
!configPathExists && fs.mkdirSync(configPath)
var relativeConfigFile = function() {
config = JSON.stringify(config) return path.relative(process.cwd(), configuration.configFile)
config = config.replace('{', '{\n ') }
config = config.replace(/,/g, ",\n ")
config = config.replace('}', "\n}") var writeDefaultConfig = function(config) {
var configPath = path.dirname(configuration.configFile)
fs.writeFileSync(configFile, config)
if (!fs.existsSync(configPath)) {
fs.mkdirSync(configPath)
}
config = JSON.stringify({
development: {
username: "root",
password: null,
database: 'database_development',
host: '127.0.0.1'
},
test: {
username: "root",
password: null,
database: 'database_test',
host: '127.0.0.1'
},
production: {
username: "root",
password: null,
database: 'database_production',
host: '127.0.0.1'
}
}, undefined, 2) + "\n"
fs.writeFileSync(configuration.configFile, config)
} }
var createMigrationsFolder = function(force) { var createMigrationsFolder = function(force) {
if(force) { if(force) {
console.log('Deleting the migrations folder.') console.log('Deleting the migrations folder. (--force)')
try { try {
fs.readdirSync(migrationsPath).forEach(function(filename) { fs.readdirSync(configuration.migrationsPath).forEach(function(filename) {
fs.unlinkSync(migrationsPath + '/' + filename) fs.unlinkSync(configuration.migrationsPath + '/' + filename)
}) })
} catch(e) {} } catch(e) {}
try { try {
fs.rmdirSync(migrationsPath) fs.rmdirSync(configuration.migrationsPath)
console.log('Successfully deleted the migrations folder.') console.log('Successfully deleted the migrations folder.')
} catch(e) {} } catch(e) {}
} }
console.log('Creating migrations folder.')
try { try {
fs.mkdirSync(migrationsPath) fs.mkdirSync(configuration.migrationsPath)
console.log('Successfully create migrations folder.') console.log('Successfully created migrations folder at "' + configuration.migrationsPath + '".')
} catch(e) { } catch(e) {
console.log('Migrations folder already exist.')
} }
} }
var readConfig = function() { var readConfig = function() {
var config var config
try { try {
config = fs.readFileSync(configFile) config = fs.readFileSync(configuration.configFile)
} catch(e) { } catch(e) {
throw new Error('Error reading "config/config.json".') throw new Error('Error reading "' + relativeConfigFile() + '".')
} }
try { try {
config = JSON.parse(config) config = JSON.parse(config)
} catch (e) { } catch (e) {
throw new Error('Error parsing "config/config.json" as JSON.') throw new Error('Error parsing "' + relativeConfigFile() + '" as JSON.')
} }
console.log('Loaded configuration file "' + relativeConfigFile() + '".')
if (config[environment]) { if (config[configuration.environment]) {
config = config[environment] console.log('Using environment "' + configuration.environment + '".')
config = config[configuration.environment]
} }
return config return config
} }
program program
.version(packageJson.version) .version(configuration.version)
.option('-i, --init', 'Initializes the project.') .option('-i, --init', 'Initializes the project.')
.option('-e, --env <environment>', 'Specify the environment.') .option('-e, --env <environment>', 'Specify the environment.')
.option('-m, --migrate', 'Run pending migrations.') .option('-m, --migrate', 'Run pending migrations.')
.option('-u, --undo', 'Undo the last migration.') .option('-u, --undo', 'Undo the last migration.')
.option('-f, --force', 'Forces the action to be done.') .option('-f, --force', 'Forces the action to be done.')
.option('-c, --create-migration [migration-name]', 'Creates a new migration.') .option('-c, --create-migration [migration-name]', 'Creates a new migration.')
.option('--config <config_file>', 'Specifies alternate config file.')
.parse(process.argv) .parse(process.argv)
if(typeof program.config === 'string') {
configuration.configFile = program.config
}
if(typeof program.env === 'string') { if(typeof program.env === 'string') {
environment = program.env configuration.environment = program.env
} }
console.log("Using environment '" + environment + "'.")
if(program.migrate) { if (program.migrate) {
if(configFileExists) { if (configFileExists()) {
var config var config
, options = {} , options = {}
...@@ -101,57 +130,58 @@ if(program.migrate) { ...@@ -101,57 +130,58 @@ if(program.migrate) {
if(['database', 'username', 'password'].indexOf(key) == -1) { if(['database', 'username', 'password'].indexOf(key) == -1) {
options[key] = value options[key] = value
} }
if (key === "use_env_variable") {
if (process.env[value]) {
var db_info = process.env[value].match(
/([^:]+):\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/(.+)/);
config.database = db_info[6];
config.username = db_info[2];
config.password = db_info[3];
options = _.extend(options, {
host: db_info[4],
port: db_info[5],
dialect: db_info[1],
protocol: db_info[1]
});
}
}
}) })
options = _.extend(options, { logging: false }) options = _.extend(options, { logging: false })
var sequelize = new Sequelize(config.database, config.username, config.password, options) var sequelize = new Sequelize(config.database, config.username, config.password, options)
, migratorOptions = { path: migrationsPath } , migratorOptions = { path: configuration.migrationsPath }
, migrator = sequelize.getMigrator(migratorOptions) , migrator = sequelize.getMigrator(migratorOptions)
if(program.undo) { if (program.undo) {
sequelize.migrator.findOrCreateSequelizeMetaDAO().success(function(Meta) { sequelize.migrator.findOrCreateSequelizeMetaDAO().success(function(Meta) {
Meta.find({ order: 'id DESC' }).success(function(meta) { Meta.find({ order: 'id DESC' }).success(function(meta) {
if(meta) { if (meta) {
migrator = sequelize.getMigrator(_.extend(migratorOptions, meta), true) migrator = sequelize.getMigrator(_.extend(migratorOptions, meta), true)
} }
migrator.migrate({ method: 'down' }) migrator.migrate({ method: 'down' }).success(function() {
process.exit(0)
})
}) })
}) })
} else { } else {
sequelize.migrate() sequelize.migrate().success(function() {
process.exit(0)
})
} }
} else { } else {
console.log('Cannot find "config/config.json". Have you run "sequelize --init"?') console.log('Cannot find "' + relativeConfigFile() + '". Have you run "sequelize --init"?')
process.exit(1) process.exit(1)
} }
} else if(program.init) { } else if(program.init) {
if(!configFileExists || !!program.force) { if(!configFileExists() || !!program.force) {
writeConfig({ writeDefaultConfig()
development: {
username: "root",
password: null,
database: 'database_development',
host: '127.0.0.1'
},
test: {
username: "root",
password: null,
database: 'database_test',
host: '127.0.0.1'
},
production: {
username: "root",
password: null,
database: 'database_production',
host: '127.0.0.1'
}
})
console.log('Created "config/config.json"') console.log('Created "' + relativeConfigFile() + '"')
} else { } else {
console.log('"config/config.json" already exists. Run "sequelize --init --force" to overwrite.') console.log('The file "' + relativeConfigFile() + '" already exists. Run "sequelize --init --force" to overwrite it.')
process.exit(1) process.exit(1)
} }
...@@ -175,9 +205,11 @@ if(program.migrate) { ...@@ -175,9 +205,11 @@ if(program.migrate) {
" done()", " done()",
" }", " }",
"}" "}"
].join('\n') ].join('\n') + "\n"
fs.writeFileSync(migrationsPath + '/' + migrationName, migrationContent) fs.writeFileSync(configuration.migrationsPath + '/' + migrationName, migrationContent)
console.log('New migration "' + migrationName + '" was added to "' +
path.relative(process.cwd(), configuration.migrationsPath) + '/".')
} else { } else {
console.log('Try "sequelize --help" for usage information.') console.log('No action specified. Try "sequelize --help" for usage information.')
} }
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
- [BUG] For MySQL users, if their collation allows case insensitivity then allow enums to be case insensitive as well [#794](https://github.com/sequelize/sequelize/pull/794). thanks to durango - [BUG] For MySQL users, if their collation allows case insensitivity then allow enums to be case insensitive as well [#794](https://github.com/sequelize/sequelize/pull/794). thanks to durango
- [BUG] Custom primary key (not keys, just singular) should no longer be a problem for models when using any of the data retrievals with just a number or through associations [#771](https://github.com/sequelize/sequelize/pull/771). thanks to sdephold & durango - [BUG] Custom primary key (not keys, just singular) should no longer be a problem for models when using any of the data retrievals with just a number or through associations [#771](https://github.com/sequelize/sequelize/pull/771). thanks to sdephold & durango
- [BUG] Default schemas should now be utilized when describing tables [#812](https://github.com/sequelize/sequelize/pull/812). thanks to durango - [BUG] Default schemas should now be utilized when describing tables [#812](https://github.com/sequelize/sequelize/pull/812). thanks to durango
- [BUG] Fixed eager loading for many-to-many associations. [#834](https://github.com/sequelize/sequelize/pull/834). thanks to lemon-tree
- [FEATURE] Validate a model before it gets saved. [#601](https://github.com/sequelize/sequelize/pull/601). thanks to durango - [FEATURE] Validate a model before it gets saved. [#601](https://github.com/sequelize/sequelize/pull/601). thanks to durango
- [FEATURE] Schematics. [#564](https://github.com/sequelize/sequelize/pull/564). thanks to durango - [FEATURE] Schematics. [#564](https://github.com/sequelize/sequelize/pull/564). thanks to durango
- [FEATURE] Foreign key constraints. [#595](https://github.com/sequelize/sequelize/pull/595). thanks to optilude - [FEATURE] Foreign key constraints. [#595](https://github.com/sequelize/sequelize/pull/595). thanks to optilude
...@@ -54,6 +55,7 @@ ...@@ -54,6 +55,7 @@
- [FEATURE] Drop index if exists has been added to sqlite [#766](https://github.com/sequelize/sequelize/pull/776) thanks to coderbuzz - [FEATURE] Drop index if exists has been added to sqlite [#766](https://github.com/sequelize/sequelize/pull/776) thanks to coderbuzz
- [FEATURE] bulkCreate() now has a third argument which gives you the ability to validate each row before attempting to bulkInsert [#797](https://github.com/sequelize/sequelize/pull/797). thanks to durango - [FEATURE] bulkCreate() now has a third argument which gives you the ability to validate each row before attempting to bulkInsert [#797](https://github.com/sequelize/sequelize/pull/797). thanks to durango
- [FEATURE] Added `isDirty` to model instances. [#798](https://github.com/sequelize/sequelize/pull/798). Thanks to mstorgaard - [FEATURE] Added `isDirty` to model instances. [#798](https://github.com/sequelize/sequelize/pull/798). Thanks to mstorgaard
- [FEATURE] Added possibility to use env variable for the database connection. [#784](https://github.com/sequelize/sequelize/pull/784). Thanks to sykopomp.
- [REFACTORING] hasMany now uses a single SQL statement when creating and destroying associations, instead of removing each association seperately [690](https://github.com/sequelize/sequelize/pull/690). Inspired by [#104](https://github.com/sequelize/sequelize/issues/104). janmeier - [REFACTORING] hasMany now uses a single SQL statement when creating and destroying associations, instead of removing each association seperately [690](https://github.com/sequelize/sequelize/pull/690). Inspired by [#104](https://github.com/sequelize/sequelize/issues/104). janmeier
- [REFACTORING] Consistent handling of offset across dialects. Offset is now always applied, and limit is set to max table size of not limit is given [#725](https://github.com/sequelize/sequelize/pull/725). janmeier - [REFACTORING] Consistent handling of offset across dialects. Offset is now always applied, and limit is set to max table size of not limit is given [#725](https://github.com/sequelize/sequelize/pull/725). janmeier
- [REFACTORING] Moved Jasmine to Buster and then Buster to Mocha + Chai. sdepold and durango - [REFACTORING] Moved Jasmine to Buster and then Buster to Mocha + Chai. sdepold and durango
......
...@@ -47,7 +47,9 @@ module.exports = (function() { ...@@ -47,7 +47,9 @@ module.exports = (function() {
this.foreignIdentifier = Utils._.underscoredIf((this.options.as || this.target.tableName) + 'Id', this.options.underscored) this.foreignIdentifier = Utils._.underscoredIf((this.options.as || this.target.tableName) + 'Id', this.options.underscored)
} else { } else {
this.foreignIdentifier = this.target.associations[this.associationAccessor].identifier this.foreignIdentifier = this.target.associations[this.associationAccessor].identifier
this.target.associations[this.associationAccessor].foreignIdentifier = this.identifier
delete this.source.rawAttributes[this.foreignIdentifier] delete this.source.rawAttributes[this.foreignIdentifier]
delete this.target.associations[this.associationAccessor].source.rawAttributes[this.identifier]
} }
// define a new model, which connects the models // define a new model, which connects the models
......
...@@ -354,14 +354,8 @@ module.exports = (function() { ...@@ -354,14 +354,8 @@ module.exports = (function() {
DAOFactory.prototype.findAndCountAll = function(options) { DAOFactory.prototype.findAndCountAll = function(options) {
var self = this var self = this
, opts = Utils._.cloneDeep(options) // no limit, offset, order, attributes or include for the options given to count()
, copts = Utils._.extend({}, Utils._.cloneDeep(options) || {}, { , copts = Utils._.omit(options || {}, ['offset', 'limit', 'order', 'include', 'attributes'])
// no limit, offset, order or include for the options given to count()
offset : 0,
limit : 0,
order : null,
include : null
})
return new Utils.CustomEventEmitter(function (emitter) { return new Utils.CustomEventEmitter(function (emitter) {
var emit = { var emit = {
...@@ -377,20 +371,21 @@ module.exports = (function() { ...@@ -377,20 +371,21 @@ module.exports = (function() {
, sql : function(s) { // emit SQL , sql : function(s) { // emit SQL
emitter.emit('sql', s); emitter.emit('sql', s);
} }
} }
self.count(copts) self.count(copts)
.on('sql', emit.sql) .on('sql', emit.sql)
.error(emit.err) .error(emit.err)
.success(function(cnt) { .success(function(cnt) {
if (cnt === 0) if (cnt === 0) {
return emit.okay(cnt) // no records, no need for another query return emit.okay(cnt) // no records, no need for another query
}
self.findAll(options) self.findAll(options)
.on('sql', emit.sql) .on('sql', emit.sql)
.error(emit.err) .error(emit.err)
.success(function(rows) { .success(function(rows) {
emit.okay(cnt, rows) emit.okay(cnt, rows)
}) })
}) })
......
...@@ -183,7 +183,7 @@ module.exports = { ...@@ -183,7 +183,7 @@ module.exports = {
} }
result.type = 'HSTORE' result.type = 'HSTORE'
result.toString = result.valueOf = function() { return 'TEXT' } result.toString = result.valueOf = function() { return 'HSTORE' }
return result return result
} }
......
...@@ -5,6 +5,7 @@ var Utils = require("../../utils") ...@@ -5,6 +5,7 @@ var Utils = require("../../utils")
module.exports = (function() { module.exports = (function() {
var QueryGenerator = { var QueryGenerator = {
dialect: 'mysql',
addSchema: function(opts) { addSchema: function(opts) {
var tableName var tableName
var schema = (!!opts && !!opts.options && !!opts.options.schema ? opts.options.schema : undefined) var schema = (!!opts && !!opts.options && !!opts.options.schema ? opts.options.schema : undefined)
...@@ -187,14 +188,27 @@ module.exports = (function() { ...@@ -187,14 +188,27 @@ module.exports = (function() {
optAttributes = optAttributes.concat(attributes) optAttributes = optAttributes.concat(attributes)
var table = include.daoFactory.tableName if (!include.association.connectorDAO) {
var as = include.as var table = include.daoFactory.tableName
var tableLeft = ((include.association.associationType === 'BelongsTo') ? include.as : tableName) var as = include.as
var attrLeft = 'id' var tableLeft = ((include.association.associationType === 'BelongsTo') ? include.as : tableName)
var tableRight = ((include.association.associationType === 'BelongsTo') ? tableName : include.as) var attrLeft = 'id'
var attrRight = include.association.identifier var tableRight = ((include.association.associationType === 'BelongsTo') ? tableName : include.as)
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableLeft) + "." + this.quoteIdentifier(attrLeft) + " = " + this.quoteIdentifier(tableRight) + "." + this.quoteIdentifier(attrRight) var 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 table = include.daoFactory.tableName
var as = include.as
var tableLeft = tableName
var identLeft = include.association.identifier
var attrLeft = 'id'
var tableRight = include.as
var identRight = include.association.foreignIdentifier
var attrRight = 'id'
var tableJunction = include.association.connectorDAO.tableName
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(tableJunction) + " ON " + this.quoteIdentifier(tableLeft) + "." + this.quoteIdentifier(attrLeft) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identLeft)
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableRight) + "." + this.quoteIdentifier(attrRight) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identRight)
}
}.bind(this)) }.bind(this))
options.attributes = optAttributes.join(', ') options.attributes = optAttributes.join(', ')
...@@ -424,7 +438,7 @@ module.exports = (function() { ...@@ -424,7 +438,7 @@ module.exports = (function() {
} else if (typeof smth === "string") { } else if (typeof smth === "string") {
result = smth result = smth
} else if (Array.isArray(smth)) { } else if (Array.isArray(smth)) {
result = Utils.format(smth) result = Utils.format(smth, this.dialect)
} }
return result ? result : '1=1' return result ? result : '1=1'
......
...@@ -72,8 +72,9 @@ module.exports = (function() { ...@@ -72,8 +72,9 @@ module.exports = (function() {
var emitter = new (require('events').EventEmitter)() var emitter = new (require('events').EventEmitter)()
// in case database is slow to connect, prevent orphaning the client // in case database is slow to connect, prevent orphaning the client
if (this.isConnecting && !this.pooling) { // TODO: We really need some sort of queue/flush/drain mechanism
emitter.emit('success') if (this.isConnecting && !this.pooling && this.client === null) {
emitter.emit('success', null)
return emitter return emitter
} }
...@@ -118,7 +119,7 @@ module.exports = (function() { ...@@ -118,7 +119,7 @@ module.exports = (function() {
if (this.pooling) { if (this.pooling) {
// acquire client from pool // acquire client from pool
this.poolIdentifier = this.pg.pools.getOrCreate(this.sequelize.config) this.poolIdentifier = this.pg.pools.getOrCreate(uri)
this.poolIdentifier.connect(connectCallback) this.poolIdentifier.connect(connectCallback)
} else { } else {
if (!!this.client) { if (!!this.client) {
......
...@@ -8,6 +8,7 @@ var Utils = require("../../utils") ...@@ -8,6 +8,7 @@ var Utils = require("../../utils")
module.exports = (function() { module.exports = (function() {
var QueryGenerator = { var QueryGenerator = {
options: {}, options: {},
dialect: 'postgres',
addSchema: function(opts) { addSchema: function(opts) {
var tableName = undefined var tableName = undefined
...@@ -117,7 +118,7 @@ module.exports = (function() { ...@@ -117,7 +118,7 @@ module.exports = (function() {
schema = 'public'; schema = 'public';
} }
var query = 'SELECT c.column_name as "Field", c.column_default as "Default", c.is_nullable as "Null", c.data_type as "Type", (SELECT array_agg(e.enumlabel) FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS "special" FROM information_schema.columns c WHERE table_name = <%= table %> AND table_schema = <%= schema %>' var query = 'SELECT c.column_name as "Field", c.column_default as "Default", c.is_nullable as "Null", CASE WHEN c.udt_name = \'hstore\' THEN c.udt_name ELSE c.data_type END as "Type", (SELECT array_agg(e.enumlabel) FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS "special" FROM information_schema.columns c WHERE table_name = <%= table %> AND table_schema = <%= schema %>'
return Utils._.template(query)({ return Utils._.template(query)({
table: this.escape(tableName), table: this.escape(tableName),
...@@ -260,14 +261,34 @@ module.exports = (function() { ...@@ -260,14 +261,34 @@ module.exports = (function() {
var joinQuery = ' LEFT OUTER JOIN <%= table %> AS <%= as %> ON <%= tableLeft %>.<%= attrLeft %> = <%= tableRight %>.<%= attrRight %>' var joinQuery = ' LEFT OUTER JOIN <%= table %> AS <%= as %> ON <%= tableLeft %>.<%= attrLeft %> = <%= tableRight %>.<%= attrRight %>'
query += Utils._.template(joinQuery)({ if (!include.association.connectorDAO) {
table: this.quoteIdentifiers(include.daoFactory.tableName), query += Utils._.template(joinQuery)({
as: this.quoteIdentifier(include.as), table: this.quoteIdentifiers(include.daoFactory.tableName),
tableLeft: this.quoteIdentifiers((include.association.associationType === 'BelongsTo') ? include.as : tableName), as: this.quoteIdentifier(include.as),
attrLeft: this.quoteIdentifier('id'), tableLeft: this.quoteIdentifiers((include.association.associationType === 'BelongsTo') ? include.as : tableName),
tableRight: this.quoteIdentifiers((include.association.associationType === 'BelongsTo') ? tableName : include.as), attrLeft: this.quoteIdentifier('id'),
attrRight: this.quoteIdentifier(include.association.identifier) tableRight: this.quoteIdentifiers((include.association.associationType === 'BelongsTo') ? tableName : include.as),
}) attrRight: this.quoteIdentifier(include.association.identifier)
})
} else {
query += Utils._.template(joinQuery)({
table: this.quoteIdentifiers(include.association.connectorDAO.tableName),
as: this.quoteIdentifier(include.association.connectorDAO.tableName),
tableLeft: this.quoteIdentifiers(tableName),
attrLeft: this.quoteIdentifier('id'),
tableRight: this.quoteIdentifiers(include.association.connectorDAO.tableName),
attrRight: this.quoteIdentifier(include.association.identifier)
})
query += Utils._.template(joinQuery)({
table: this.quoteIdentifiers(include.daoFactory.tableName),
as: this.quoteIdentifier(include.as),
tableLeft: this.quoteIdentifiers(include.as),
attrLeft: this.quoteIdentifier('id'),
tableRight: this.quoteIdentifiers(include.association.connectorDAO.tableName),
attrRight: this.quoteIdentifier(include.association.foreignIdentifier)
})
}
}.bind(this)) }.bind(this))
options.attributes = optAttributes.join(', ') options.attributes = optAttributes.join(', ')
...@@ -787,8 +808,8 @@ module.exports = (function() { ...@@ -787,8 +808,8 @@ module.exports = (function() {
var template = '<%= protocol %>://<%= user %>:<%= password %>@<%= host %><% if(port) { %>:<%= port %><% } %>/<%= database %>' var template = '<%= protocol %>://<%= user %>:<%= password %>@<%= host %><% if(port) { %>:<%= port %><% } %>/<%= database %>'
return Utils._.template(template)({ return Utils._.template(template)({
user: encodeURIComponent(config.username), user: config.username,
password: encodeURIComponent(config.password), password: config.password,
database: config.database, database: config.database,
host: config.host, host: config.host,
port: config.port, port: config.port,
......
...@@ -12,6 +12,7 @@ var hashToWhereConditions = MySqlQueryGenerator.hashToWhereConditions ...@@ -12,6 +12,7 @@ var hashToWhereConditions = MySqlQueryGenerator.hashToWhereConditions
module.exports = (function() { module.exports = (function() {
var QueryGenerator = { var QueryGenerator = {
options: {}, options: {},
dialect: 'sqlite',
addSchema: function(opts) { addSchema: function(opts) {
var tableName = undefined var tableName = undefined
...@@ -169,13 +170,27 @@ module.exports = (function() { ...@@ -169,13 +170,27 @@ module.exports = (function() {
optAttributes = optAttributes.concat(attributes) optAttributes = optAttributes.concat(attributes)
var table = include.daoFactory.tableName if (!include.association.connectorDAO) {
var as = include.as var table = include.daoFactory.tableName
var tableLeft = ((include.association.associationType === 'BelongsTo') ? include.as : tableName) var as = include.as
var attrLeft = 'id' var tableLeft = ((include.association.associationType === 'BelongsTo') ? include.as : tableName)
var tableRight = ((include.association.associationType === 'BelongsTo') ? tableName : include.as) var attrLeft = 'id'
var attrRight = include.association.identifier var tableRight = ((include.association.associationType === 'BelongsTo') ? tableName : include.as)
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableLeft) + "." + this.quoteIdentifier(attrLeft) + " = " + this.quoteIdentifier(tableRight) + "." + this.quoteIdentifier(attrRight) + "" var 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 table = include.daoFactory.tableName
var as = include.as
var tableLeft = tableName
var identLeft = include.association.identifier
var attrLeft = 'id'
var tableRight = include.as
var identRight = include.association.foreignIdentifier
var attrRight = 'id'
var tableJunction = include.association.connectorDAO.tableName
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(tableJunction) + " ON " + this.quoteIdentifier(tableLeft) + "." + this.quoteIdentifier(attrLeft) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identLeft)
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableRight) + "." + this.quoteIdentifier(attrRight) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identRight)
}
}.bind(this)) }.bind(this))
......
...@@ -58,8 +58,10 @@ module.exports = (function() { ...@@ -58,8 +58,10 @@ module.exports = (function() {
} else { } else {
self.options.logging("Running migrations...") self.options.logging("Running migrations...")
} }
migrations.forEach(function(migration) { migrations.forEach(function(migration) {
var migrationTime var migrationTime
chainer.add(migration, 'execute', [options], { chainer.add(migration, 'execute', [options], {
before: function(migration) { before: function(migration) {
if (self.options.logging !== false) { if (self.options.logging !== false) {
...@@ -67,6 +69,7 @@ module.exports = (function() { ...@@ -67,6 +69,7 @@ module.exports = (function() {
} }
migrationTime = process.hrtime() migrationTime = process.hrtime()
}, },
after: function(migration) { after: function(migration) {
migrationTime = process.hrtime(migrationTime) migrationTime = process.hrtime(migrationTime)
migrationTime = Math.round( (migrationTime[0] * 1000) + (migrationTime[1] / 1000000)); migrationTime = Math.round( (migrationTime[0] * 1000) + (migrationTime[1] / 1000000));
...@@ -75,6 +78,7 @@ module.exports = (function() { ...@@ -75,6 +78,7 @@ module.exports = (function() {
self.options.logging('Completed in ' + migrationTime + 'ms') self.options.logging('Completed in ' + migrationTime + 'ms')
} }
}, },
success: function(migration, callback) { success: function(migration, callback) {
if (options.method === 'down') { if (options.method === 'down') {
deleteUndoneMigration.call(self, from, migration, callback) deleteUndoneMigration.call(self, from, migration, callback)
...@@ -233,11 +237,21 @@ module.exports = (function() { ...@@ -233,11 +237,21 @@ module.exports = (function() {
var self = this var self = this
return new Utils.CustomEventEmitter(function(emitter) { return new Utils.CustomEventEmitter(function(emitter) {
self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) { self
SequelizeMeta.find({ order: 'id DESC' }).success(function(meta) { .findOrCreateSequelizeMetaDAO()
emitter.emit('success', meta ? meta : null) .success(function(SequelizeMeta) {
}).error(function(err) { emitter.emit('error', err) }) SequelizeMeta
}).error(function(err) { emitter.emit(err) }) .find({ order: 'id DESC' })
.success(function(meta) {
emitter.emit('success', meta ? meta : null)
})
.error(function(err) {
emitter.emit('error', err)
})
})
.error(function(err) {
emitter.emit('error', err)
})
}).run() }).run()
} }
...@@ -245,7 +259,8 @@ module.exports = (function() { ...@@ -245,7 +259,8 @@ module.exports = (function() {
var self = this var self = this
return new Utils.CustomEventEmitter(function(emitter) { return new Utils.CustomEventEmitter(function(emitter) {
getLastMigrationFromDatabase.call(self) getLastMigrationFromDatabase
.call(self)
.success(function(meta) { .success(function(meta) {
emitter.emit('success', meta ? meta.to : null) emitter.emit('success', meta ? meta.to : null)
}) })
......
...@@ -210,12 +210,17 @@ module.exports = (function() { ...@@ -210,12 +210,17 @@ module.exports = (function() {
} }
Sequelize.prototype.migrate = function(options) { Sequelize.prototype.migrate = function(options) {
this.getMigrator().migrate(options) return this.getMigrator().migrate(options)
} }
Sequelize.prototype.query = function(sql, callee, options, replacements) { Sequelize.prototype.query = function(sql, callee, options, replacements) {
if (arguments.length === 4) { if (arguments.length === 4) {
sql = Utils.format([sql].concat(replacements), this.options.dialect) if (Array.isArray(replacements)) {
sql = Utils.format([sql].concat(replacements), this.options.dialect)
}
else {
sql = Utils.formatNamedParameters(sql, replacements, this.options.dialect)
}
} else if (arguments.length === 3) { } else if (arguments.length === 3) {
options = options options = options
} else if (arguments.length === 2) { } else if (arguments.length === 2) {
......
...@@ -106,6 +106,17 @@ SqlString.format = function(sql, values, timeZone, dialect) { ...@@ -106,6 +106,17 @@ SqlString.format = function(sql, values, timeZone, dialect) {
}); });
}; };
SqlString.formatNamedParameters = function(sql, values, timeZone, dialect) {
return sql.replace(/\:(\w+)/g, function (value, key) {
if (values.hasOwnProperty(key)) {
return SqlString.escape(values[key], false, timeZone, dialect);
}
else {
throw new Error('Named parameter "' + value + '" has no value in the given object.');
}
});
};
SqlString.dateToString = function(date, timeZone, dialect) { SqlString.dateToString = function(date, timeZone, dialect) {
var dt = new Date(date); var dt = new Date(date);
......
...@@ -29,6 +29,22 @@ var Utils = module.exports = { ...@@ -29,6 +29,22 @@ var Utils = module.exports = {
} }
return result return result
},
/*
* Returns an array with some falsy values removed. The values null, "", undefined and NaN are considered falsey.
*/
compactLite: function(array) {
var index = -1,
length = array ? array.length : 0,
result = [];
while (++index < length) {
var value = array[index];
if (typeof value === "boolean" || value === 0 || value) {
result.push(value);
}
}
return result;
} }
}) })
...@@ -41,6 +57,10 @@ var Utils = module.exports = { ...@@ -41,6 +57,10 @@ var Utils = module.exports = {
var timeZone = null; var timeZone = null;
return SqlString.format(arr.shift(), arr, timeZone, dialect) return SqlString.format(arr.shift(), arr, timeZone, dialect)
}, },
formatNamedParameters: function(sql, parameters, dialect) {
var timeZone = null;
return SqlString.formatNamedParameters(sql, parameters, timeZone, dialect)
},
// smartWhere can accept an array of {where} objects, or a single {where} object. // smartWhere can accept an array of {where} objects, or a single {where} object.
// The smartWhere function breaks down the collection of where objects into a more // The smartWhere function breaks down the collection of where objects into a more
// centralized object for each column so we can avoid duplicates // centralized object for each column so we can avoid duplicates
...@@ -197,7 +217,7 @@ var Utils = module.exports = { ...@@ -197,7 +217,7 @@ var Utils = module.exports = {
} }
} }
return lodash.compact([text.join(' AND ')].concat(whereArgs)) return Utils._.compactLite([text.join(' AND ')].concat(whereArgs))
}, },
getWhereLogic: function(logic) { getWhereLogic: function(logic) {
switch (logic) { switch (logic) {
......
{ {
"name": "sequelize", "name": "sequelize",
"description": "Multi dialect ORM for Node.JS", "description": "Multi dialect ORM for Node.JS",
"version": "1.7.0-alpha2", "version": "1.7.0-alpha3",
"author": "Sascha Depold <sascha@depold.com>", "author": "Sascha Depold <sascha@depold.com>",
"contributors": [ "contributors": [
{ {
......
...@@ -381,6 +381,13 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -381,6 +381,13 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
}) })
it("get associated objects with an eager load", function(done) {
this.User.find({where: {username: 'John'}, include: [ this.Task ]}).success(function (john) {
expect(john.tasks).to.have.length(2);
done();
})
})
}) })
it("removes the reference id, which was added in the first place", function(done) { it("removes the reference id, which was added in the first place", function(done) {
......
...@@ -19,7 +19,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() { ...@@ -19,7 +19,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
Task.create({ title: 'task', status: 'inactive' }).success(function(task) { Task.create({ title: 'task', status: 'inactive' }).success(function(task) {
user.setTaskXYZ(task).success(function() { user.setTaskXYZ(task).success(function() {
user.getTaskXYZ({where: ['status = ?', 'active']}).success(function(task) { user.getTaskXYZ({where: ['status = ?', 'active']}).success(function(task) {
expect(task).to.equal(null) expect(task).to.be.null
done() done()
}) })
}) })
......
...@@ -21,7 +21,8 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -21,7 +21,8 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
secretValue: DataTypes.STRING, secretValue: DataTypes.STRING,
data: DataTypes.STRING, data: DataTypes.STRING,
intVal: DataTypes.INTEGER, intVal: DataTypes.INTEGER,
theDate: DataTypes.DATE theDate: DataTypes.DATE,
aBool: DataTypes.BOOLEAN
}) })
this.User.sync({ force: true }).success(function() { this.User.sync({ force: true }).success(function() {
done() done()
...@@ -999,6 +1000,72 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -999,6 +1000,72 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
}) })
it('should be able to handle false/true values just fine...', function(done) {
var User = this.User
, escapeChar = (dialect === "postgres" || dialect === "postgres-native") ? '"' : '`'
User.bulkCreate([
{username: 'boo5', aBool: false},
{username: 'boo6', aBool: true}
]).success(function() {
User.all({where: [escapeChar + 'aBool' + escapeChar + ' = ?', false]}).success(function(users) {
expect(users).to.have.length(1)
expect(users[0].username).to.equal('boo5')
User.all({where: [escapeChar + 'aBool' + escapeChar + ' = ?', true]}).success(function(_users) {
expect(_users).to.have.length(1)
expect(_users[0].username).to.equal('boo6')
done()
})
})
})
})
it('should be able to handle false/true values through associations as well...', function(done) {
var User = this.User
, escapeChar = (dialect === "postgres" || dialect === "postgres-native") ? '"' : '`'
var Passports = this.sequelize.define('Passports', {
isActive: Sequelize.BOOLEAN
})
User.hasMany(Passports)
Passports.belongsTo(User)
User.sync({ force: true }).success(function() {
Passports.sync({ force: true }).success(function() {
User.bulkCreate([
{username: 'boo5', aBool: false},
{username: 'boo6', aBool: true}
]).success(function() {
Passports.bulkCreate([
{isActive: true},
{isActive: false}
]).success(function() {
User.find(1).success(function(user) {
Passports.find(1).success(function(passport) {
user.setPassports([passport]).success(function() {
User.find(2).success(function(_user) {
Passports.find(2).success(function(_passport) {
_user.setPassports([_passport]).success(function() {
_user.getPassports({where: [escapeChar + 'isActive' + escapeChar + ' = ?', false]}).success(function(theFalsePassport) {
user.getPassports({where: [escapeChar + 'isActive' + escapeChar + ' = ?', true]}).success(function(theTruePassport) {
expect(theFalsePassport).to.have.length(1)
expect(theFalsePassport[0].isActive).to.be.false
expect(theTruePassport).to.have.length(1)
expect(theTruePassport[0].isActive).to.be.true
done()
})
})
})
})
})
})
})
})
})
})
})
})
})
it('should be able to retun a record with primaryKey being null for new inserts', function(done) { it('should be able to retun a record with primaryKey being null for new inserts', function(done) {
var Session = this.sequelize.define('Session', { var Session = this.sequelize.define('Session', {
token: { type: DataTypes.TEXT, allowNull: false }, token: { type: DataTypes.TEXT, allowNull: false },
...@@ -2231,6 +2298,16 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -2231,6 +2298,16 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
done() done()
}) })
}) })
it("handles attributes", function(done) {
this.User.findAndCountAll({where: "id != " + this.users[0].id, attributes: ['data']}).success(function(info) {
expect(info.count).to.equal(2)
expect(Array.isArray(info.rows)).to.be.ok
expect(info.rows.length).to.equal(2)
expect(info.rows[0].selectedValues).to.not.have.property('username')
expect(info.rows[1].selectedValues).to.not.have.property('username')
done()
})
})
}) })
describe('all', function() { describe('all', function() {
......
...@@ -2,7 +2,6 @@ var chai = require('chai') ...@@ -2,7 +2,6 @@ var chai = require('chai')
, expect = chai.expect , expect = chai.expect
, Support = require(__dirname + '/../support') , Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect() , dialect = Support.getTestDialect()
, config = require(__dirname + '/../config/config')
, DataTypes = require(__dirname + "/../../lib/data-types") , DataTypes = require(__dirname + "/../../lib/data-types")
chai.Assertion.includeStack = true chai.Assertion.includeStack = true
...@@ -26,6 +25,13 @@ if (dialect.match(/^postgres/)) { ...@@ -26,6 +25,13 @@ if (dialect.match(/^postgres/)) {
done() done()
}) })
it('describeTable should tell me that a column is hstore and not USER-DEFINED', function(done) {
this.sequelize.queryInterface.describeTable('Users').success(function(table) {
expect(table.document.type).to.equal('HSTORE')
done()
})
})
describe('integers', function() { describe('integers', function() {
describe('integer', function() { describe('integer', function() {
beforeEach(function(done) { beforeEach(function(done) {
......
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, _ = require('lodash')
, exec = require('child_process').exec
, version = (require(__dirname + '/../package.json')).version
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("Executable"), function() {
describe('call without arguments', function() {
it("prints usage instructions", function(done) {
exec('bin/sequelize', function(err, stdout, stderr) {
expect(stdout).to.include("No action specified. Try \"sequelize --help\" for usage information.")
done()
})
})
})
;(function(flags) {
flags.forEach(function(flag) {
describe(flag, function() {
it("prints the help", function(done) {
exec("bin/sequelize " + flag, function(err, stdout, stderr) {
expect(stdout).to.include("Usage: sequelize [options]")
done()
})
})
})
})
})(["--help", "-h"])
;(function(flags) {
flags.forEach(function(flag) {
describe(flag, function() {
it("prints the help", function(done) {
exec("bin/sequelize " + flag, function(err, stdout, stderr) {
expect(version).to.not.be.empty
expect(stdout).to.include(version)
done()
})
})
})
})
})(['--version', '-V'])
;(function(flags) {
flags.forEach(function(flag) {
describe(flag, function() {
;(function(folders) {
folders.forEach(function(folder) {
it("creates a '" + folder + "' folder", function(done) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() {
exec("ls -ila", { cwd: __dirname + '/tmp' }, function(err, stdout) {
expect(stdout).to.include(folder)
done()
})
})
})
})
})
})(['config', 'migrations'])
it("creates a config.json file", function(done) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() {
exec("ls -ila config", { cwd: __dirname + '/tmp' }, function(err, stdout) {
expect(stdout).to.include('config.json')
done()
})
})
})
})
it("does not overwrite an existing config.json file", function(done) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() {
exec("echo 'foo' > config/config.json", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function(err) {
expect(err.code).to.equal(1)
exec("cat config/config.json", { cwd: __dirname + '/tmp' }, function(err, stdout) {
expect(stdout).to.equal("foo\n")
done()
})
})
})
})
})
})
})
})
})(['--init', '-i'])
;(function(flags) {
flags.forEach(function(flag) {
var prepare = function(callback) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize " + flag + " 'foo'", { cwd: __dirname + '/tmp' }, callback)
})
})
}
describe(flag, function() {
it("creates a new file with the current timestamp", function(done) {
prepare(function() {
exec("ls -1 migrations", { cwd: __dirname + '/tmp' }, function(err, stdout) {
var date = new Date()
, format = function(i) { return (parseInt(i, 10) < 10 ? '0' + i : i) }
, sDate = [date.getFullYear(), format(date.getMonth() + 1), format(date.getDate()), format(date.getHours()), format(date.getMinutes())].join('')
expect(stdout).to.match(new RegExp(sDate + "..-foo.js"))
done()
})
})
})
it("adds a skeleton with an up and a down method", function(done) {
prepare(function() {
exec("cat migrations/*-foo.js", { cwd: __dirname + '/tmp' }, function(err, stdout) {
expect(stdout).to.include('up: function(migration, DataTypes, done) {')
expect(stdout).to.include('down: function(migration, DataTypes, done) {')
done()
})
})
})
it("calls the done callback", function(done) {
prepare(function() {
exec("cat migrations/*-foo.js", { cwd: __dirname + '/tmp' }, function(err, stdout) {
expect(stdout).to.include('done()')
expect(stdout.match(/(done\(\))/)).to.have.length(2)
done()
})
})
})
})
})
})(['--create-migration', '-c'])
;(function(flags) {
flags.forEach(function(flag) {
var prepare = function(callback) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() {
exec("cp ../assets/migrations/*-createPerson.js ./migrations/", { cwd: __dirname + '/tmp' }, function() {
exec("cat ../support.js|sed s,/../,/../../, > ./support.js", { cwd: __dirname + '/tmp' }, function() {
var dialect = Support.getTestDialect()
, config = require(__dirname + '/config/config.js')
config.sqlite.storage = __dirname + "/tmp/test.sqlite"
config = _.extend(config, config[dialect], { dialect: dialect })
exec("echo '" + JSON.stringify(config) + "' > config/config.json", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize " + flag, { cwd: __dirname + "/tmp" }, callback)
})
})
})
})
})
}
describe(flag, function() {
it("creates a SequelizeMeta table", function(done) {
var sequelize = this.sequelize
if (this.sequelize.options.dialect === 'sqlite') {
var options = this.sequelize.options
options.storage = __dirname + "/tmp/test.sqlite"
sequelize = new Support.Sequelize("", "", "", options)
}
prepare(function() {
sequelize.getQueryInterface().showAllTables().success(function(tables) {
tables = tables.sort()
expect(tables).to.have.length(2)
expect(tables[1]).to.equal("SequelizeMeta")
done()
})
}.bind(this))
})
it("creates the respective table", function(done) {
var sequelize = this.sequelize
if (this.sequelize.options.dialect === 'sqlite') {
var options = this.sequelize.options
options.storage = __dirname + "/tmp/test.sqlite"
sequelize = new Support.Sequelize("", "", "", options)
}
prepare(function() {
sequelize.getQueryInterface().showAllTables().success(function(tables) {
tables = tables.sort()
expect(tables).to.have.length(2)
expect(tables[0]).to.equal("Person")
done()
})
}.bind(this))
})
})
})
})(['--migrate', '-m'])
})
...@@ -177,6 +177,67 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () { ...@@ -177,6 +177,67 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () {
}) })
}) })
it('replaces named parameters with the passed object', function(done) {
this.sequelize.query('select :one as foo, :two as bar', null, { raw: true }, { one: 1, two: 2 }).success(function(result) {
expect(result).to.deep.equal([{ foo: 1, bar: 2 }])
done()
})
})
it('replaces named parameters with the passed object using the same key twice', function(done) {
this.sequelize.query('select :one as foo, :two as bar, :one as baz', null, { raw: true }, { one: 1, two: 2 }).success(function(result) {
expect(result).to.deep.equal([{ foo: 1, bar: 2, baz: 1 }])
done()
})
})
it('replaces named parameters with the passed object having a null property', function(done) {
this.sequelize.query('select :one as foo, :two as bar', null, { raw: true }, { one: 1, two: null }).success(function(result) {
expect(result).to.deep.equal([{ foo: 1, bar: null }])
done()
})
})
it('throw an exception when key is missing in the passed object', function(done) {
var self = this
expect(function() {
self.sequelize.query('select :one as foo, :two as bar, :three as baz', null, { raw: true }, { one: 1, two: 2 })
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g)
done()
})
it('throw an exception with the passed number', function(done) {
var self = this
expect(function() {
self.sequelize.query('select :one as foo, :two as bar', null, { raw: true }, 2)
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g)
done()
})
it('throw an exception with the passed empty object', function(done) {
var self = this
expect(function() {
self.sequelize.query('select :one as foo, :two as bar', null, { raw: true }, {})
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g)
done()
})
it('throw an exception with the passed string', function(done) {
var self = this
expect(function() {
self.sequelize.query('select :one as foo, :two as bar', null, { raw: true }, 'foobar')
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g)
done()
})
it('throw an exception with the passed date', function(done) {
var self = this
expect(function() {
self.sequelize.query('select :one as foo, :two as bar', null, { raw: true }, new Date())
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g)
done()
})
it('handles AS in conjunction with functions just fine', function(done) { it('handles AS in conjunction with functions just fine', function(done) {
this.sequelize.query('SELECT ' + (dialect === "sqlite" ? 'date(\'now\')' : 'NOW()') + ' AS t').success(function(result) { this.sequelize.query('SELECT ' + (dialect === "sqlite" ? 'date(\'now\')' : 'NOW()') + ' AS t').success(function(result) {
expect(moment(result[0].t).isValid()).to.be.true expect(moment(result[0].t).isValid()).to.be.true
......
...@@ -116,6 +116,18 @@ var Support = { ...@@ -116,6 +116,18 @@ var Support = {
var sequelize = Support.createSequelizeInstance({ dialect: Support.getTestDialect() }) var sequelize = Support.createSequelizeInstance({ dialect: Support.getTestDialect() })
// For Postgres' HSTORE functionality and to properly execute it's commands we'll need this...
before(function(done) {
var dialect = Support.getTestDialect()
if (dialect !== "postgres" && dialect !== "postgres-native") {
return done()
}
sequelize.query('CREATE EXTENSION IF NOT EXISTS hstore', null, {raw: true}).success(function() {
done()
})
})
beforeEach(function(done) { beforeEach(function(done) {
this.sequelize = sequelize this.sequelize = sequelize
Support.clearDatabase(this.sequelize, function() { Support.clearDatabase(this.sequelize, function() {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!