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

Commit 78ba32c6 by Mick Hansen

Merge pull request #2697 from mbroadst/feature/mssql-dialect

Feature/mssql dialect
2 parents 3a8ef3be adfeea85
...@@ -15,10 +15,11 @@ env: ...@@ -15,10 +15,11 @@ env:
- DB=mysql DIALECT=postgres-native - DB=mysql DIALECT=postgres-native
- DB=mysql DIALECT=sqlite - DB=mysql DIALECT=sqlite
- DB=mysql DIALECT=mariadb - DB=mysql DIALECT=mariadb
- DB=mysql DIALECT=mssql
addons: addons:
postgresql: "9.3" postgresql: "9.3"
before_script: before_script:
- "mysql -e 'create database sequelize_test;'" - "mysql -e 'create database sequelize_test;'"
- "psql -c 'create database sequelize_test;' -U postgres" - "psql -c 'create database sequelize_test;' -U postgres"
...@@ -39,6 +40,8 @@ matrix: ...@@ -39,6 +40,8 @@ matrix:
allow_failures: allow_failures:
- node_js: "0.10" - node_js: "0.10"
env: COVERAGE=true env: COVERAGE=true
- node_js: "0.10"
env: DB=mysql DIALECT=mssql
notifications: notifications:
hipchat: hipchat:
......
...@@ -38,6 +38,8 @@ sqlite: ...@@ -38,6 +38,8 @@ sqlite:
@DIALECT=sqlite make test @DIALECT=sqlite make test
mysql: mysql:
@DIALECT=mysql make test @DIALECT=mysql make test
mssql:
@DIALECT=mssql make test
postgres: postgres:
@DIALECT=postgres make test @DIALECT=postgres make test
postgres-native: postgres-native:
......
"use strict";
var AbstractConnectionManager = require('../abstract/connection-manager')
, ConnectionManager
, Utils = require('../../utils')
, Promise = require('../../promise')
, sequelizeErrors = require('../../errors');
ConnectionManager = function(dialect, sequelize) {
AbstractConnectionManager.call(this, dialect, sequelize);
this.sequelize = sequelize;
this.sequelize.config.port = this.sequelize.config.port || 1433;
try {
this.lib = require(sequelize.config.dialectModulePath || 'tedious');
} catch (err) {
throw new Error('Please install tedious package manually');
}
};
Utils._.extend(ConnectionManager.prototype, AbstractConnectionManager.prototype);
ConnectionManager.prototype.connect = function(config) {
var self = this;
return new Promise(function (resolve, reject) {
var connectionConfig = {
userName: config.username,
password: config.password,
server: config.host,
/* domain: 'DOMAIN' */
options: {
port: config.port,
database: config.database,
}
};
if (config.dialectOptions) {
Object.keys(config.dialectOptions).forEach(function(key) {
connectionConfig[key] = config.dialectOptions[key];
});
}
var connection = new self.lib.Connection(connectionConfig);
connection.lib = self.lib;
connection.on('connect', function(err) {
if (!err) {
resolve(connection);
return;
}
if (!err.code) {
reject(new sequelizeErrors.ConnectionError(err));
return;
}
switch (err.code) {
case 'ESOCKET':
if (Utils._.contains(err.message, 'connect EHOSTUNREACH')) {
reject(new sequelizeErrors.HostNotReachableError(err));
} else if (Utils._.contains(err.message, 'connect ECONNREFUSED')) {
reject(new sequelizeErrors.ConnectionRefusedError(err));
} else {
reject(new sequelizeErrors.ConnectionError(err));
}
break;
case 'ECONNREFUSED':
reject(new sequelizeErrors.ConnectionRefusedError(err));
break;
case 'ER_ACCESS_DENIED_ERROR':
reject(new sequelizeErrors.AccessDeniedError(err));
break;
case 'ENOTFOUND':
reject(new sequelizeErrors.HostNotFoundError(err));
break;
case 'EHOSTUNREACH':
reject(new sequelizeErrors.HostNotReachableError(err));
break;
case 'EINVAL':
reject(new sequelizeErrors.InvalidConnectionError(err));
break;
default:
reject(new sequelizeErrors.ConnectionError(err));
break;
}
});
});
};
ConnectionManager.prototype.disconnect = function(connection) {
// Dont disconnect a connection that is already disconnected
if (!connection.connected) {
return Promise.resolve();
}
return new Promise(function (resolve, reject) {
connection.on('end', resolve);
connection.close();
});
};
ConnectionManager.prototype.validate = function(connection) {
return connection && connection.loggedIn;
};
module.exports = ConnectionManager;
'use strict';
var DataTypes = require('../../data-types');
//drop table Group
DataTypes.BOOLEAN = 'BIT';
DataTypes.NOW = 'GETDATE()';
DataTypes.UUID = 'UNIQUEIDENTIFIER';
DataTypes.BLOB = 'VARBINARY(MAX)';
DataTypes.STRING._typeName = 'NVARCHAR';
DataTypes.STRING.prototype = {
get BINARY() {
this._binary = true;
return this;
},
get type() {
return this.toString();
},
toString: function() {
if(!this._binary){
return 'NVARCHAR(' + this._length + ')';
}else{
return 'BINARY(' + this._length + ')';
}
}
};
module.exports = DataTypes;
'use strict';
var _ = require('lodash')
, Abstract = require('../abstract')
, ConnectionManager = require('./connection-manager')
, Query = require('./query');
var MssqlDialect = function(sequelize) {
this.sequelize = sequelize;
this.connectionManager = new ConnectionManager(this, sequelize);
this.connectionManager.initPools();
};
MssqlDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.supports), {
'RETURNING': false,
'OUTPUT': true,
'DEFAULT': true,
'DEFAULT VALUES': true,
'LIMIT ON UPDATE': true,
lock: false,
transactions: false,
migrations: false,
upserts: false,
autoIncrement: {
identityInsert: true,
defaultValue: false,
update: false
},
constraints: {
restrict: false
},
index: {
collate: false,
length: false,
parser: false,
type: true,
using: false,
}
});
MssqlDialect.prototype.Query = Query;
module.exports = MssqlDialect;
'use strict';
var Utils = require('../../utils')
, AbstractQuery = require('../abstract/query')
, sequelizeErrors = require('../../errors.js');
module.exports = (function() {
var Query = function(connection, sequelize, callee, options) {
this.connection = connection;
this.callee = callee;
this.sequelize = sequelize;
this.options = Utils._.extend({
logging: console.log,
plain: false,
raw: false
}, options || {});
var self = this;
this.checkLoggingOption();
};
Utils.inherit(Query, AbstractQuery);
Query.prototype.getInsertIdField = function() {
return 'id';
};
Query.prototype.run = function(sql) {
var self = this;
this.sql = sql;
if (this.options.logging !== false) {
this.sequelize.log('Executing (' + (this.connection.uuid || 'default') + '): ' + this.sql);
}
var promise = new Utils.Promise(function(resolve, reject) {
// TRANSACTION SUPPORT
if (Utils._.contains(self.sql, 'BEGIN TRANSACTION')) {
self.connection.beginTransaction(function(err) {
if (!!err) {
reject(self.formatError(err));
} else {
resolve(self.formatResults());
}
} /* name, isolation_level */);
} else if (Utils._.contains(self.sql, 'COMMIT TRANSACTION')) {
self.connection.commitTransaction(function(err) {
if (!!err) {
reject(self.formatError(err));
} else {
resolve(self.formatResults());
}
});
} else if (Utils._.contains(self.sql, 'ROLLBACK TRANSACTION')) {
self.connection.rollbackTransaction(function(err) {
if (!!err) {
reject(self.formatError(err));
} else {
resolve(self.formatResults());
}
});
} else {
// QUERY SUPPORT
var results = []
, columns = {};
var request = new self.connection.lib.Request(self.sql, function(err, rowCount) {
promise.emit('sql', self.sql, self.connection.uuid);
if (err) {
err.sql = sql;
reject(self.formatError(err));
} else {
resolve(self.formatResults(results));
}
});
request.on('row', function(columns) {
var row = {};
columns.forEach(function(column) {
row[column.metadata.colName] = column.value;
});
results.push(row);
});
self.connection.execSql(request);
}
});
return promise;
};
/**
* High level function that handles the results of a query execution.
*
*
* Example:
* query.formatResults([
* {
* id: 1, // this is from the main table
* attr2: 'snafu', // this is from the main table
* Tasks.id: 1, // this is from the associated table
* Tasks.title: 'task' // this is from the associated table
* }
* ])
*
* @param {Array} data - The result of the query execution.
*/
Query.prototype.formatResults = function(data) {
var result = this.callee;
if (this.isInsertQuery(data)) {
this.handleInsertQuery(data);
if (!this.callee) {
// NOTE: super contrived. This just passes the newly added query-interface
// test returning only the PK. There isn't a way in MSSQL to identify
// that a given return value is the PK, and we have no schema information
// because there was no calling Model.
var record = data[0];
result = record[Object.keys(record)[0]];
}
}
if (this.isShowTableQuery()) {
result = this.handleShowTableQuery(data);
} else if (this.isShowOrDescribeQuery()) {
result = data;
if (this.sql.toLowerCase().indexOf("select c.column_name as 'name', c.data_type as 'type', c.is_nullable as 'isnull'") === 0) {
result = {};
data.forEach(function(_result) {
if (_result.Default)
_result.Default = _result.Default.replace('(\'','').replace('\')','').replace(/'/g,'');
result[_result.Name] = {
type: _result.Type.toUpperCase(),
allowNull: (_result.IsNull === 'YES' ? true : false),
defaultValue: _result.Default
};
});
} else if (this.isShowIndexesQuery()) {
result = this.handleShowIndexesQuery(data);
}
} else if (this.isSelectQuery()) {
result = this.handleSelectQuery(data);
} else if (this.isCallQuery()) {
result = data[0];
} else if (this.isBulkUpdateQuery()) {
result = data.length;
} else if (this.isBulkDeleteQuery()){
result = data[0].AFFECTEDROWS;
} else if (this.isVersionQuery()) {
result = data[0].version;
}
return result;
};
Query.prototype.isShowTableQuery = function() {
return (this.sql.toLowerCase().indexOf('select name from sys.tables') === 0);
};
Query.prototype.formatError = function (err) {
var match;
match = err.message.match(/Violation of UNIQUE KEY constraint '(.*)'. Cannot insert duplicate key in object '?(.*?)$/);
match = match || err.message.match(/Cannot insert duplicate key row in object .* with unique index '(.*)'/);
if (match && match.length > 1) {
return new sequelizeErrors.UniqueConstraintError({
name: 'SequelizeUniqueConstraintError',
fields: null,
index: 0,
value: match[2],
parent: err
});
}
match = err.message.match(/Failed on step '(.*)'.Could not create constraint. See previous errors./);
match = err.message.match(/The DELETE statement conflicted with the REFERENCE constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./);
if (match && match.length > 0) {
return new sequelizeErrors.ForeignKeyConstraintError({
fields: null,
index: match[1],
parent: err
});
}
return new sequelizeErrors.DatabaseError(err);
};
Query.prototype.isShowOrDescribeQuery = function() {
var result = false;
result = result || (this.sql.toLowerCase().indexOf("select c.column_name as 'name', c.data_type as 'type', c.is_nullable as 'isnull'") === 0);
result = result || (this.sql.toLowerCase().indexOf('select tablename = t.name, name = ind.name,') === 0);
result = result || (this.sql.toLowerCase().indexOf('exec sys.sp_helpindex @objname') === 0);
return result;
};
Query.prototype.isShowIndexesQuery = function () {
return this.sql.toLowerCase().indexOf('exec sys.sp_helpindex @objname') === 0;
};
Query.prototype.handleShowIndexesQuery = function (data) {
// Group by index name, and collect all fields
data = Utils._.foldl(data, function (acc, item) {
if (!(item.index_name in acc)) {
acc[item.index_name] = item;
item.fields = [];
}
Utils._.forEach(item.index_keys.split(','), function(column) {
var columnName = column.trim();
if (columnName.indexOf('(-)') !== -1) {
columnName = columnName.replace('(-)','');
}
acc[item.index_name].fields.push({
attribute: columnName,
length: undefined,
order: (column.indexOf('(-)') !== -1 ? 'DESC' : 'ASC'),
collate: undefined
});
});
delete item.index_keys;
return acc;
}, {});
return Utils._.map(data, function(item) {
return {
primary: (item.index_name.toLowerCase().indexOf('pk') === 0),
fields: item.fields,
name: item.index_name,
tableName: undefined,
unique: (item.index_description.toLowerCase().indexOf('unique') !== -1),
type: undefined,
};
});
};
Query.prototype.handleInsertQuery = function(results, metaData) {
if (this.callee) {
// add the inserted row id to the instance
var autoIncrementField = this.callee.Model.autoIncrementField
, autoIncrementFieldAlias = null
, id = null;
if (this.callee.Model.rawAttributes.hasOwnProperty(autoIncrementField) &&
this.callee.Model.rawAttributes[autoIncrementField].field !== undefined)
autoIncrementFieldAlias = this.callee.Model.rawAttributes[autoIncrementField].field ;
id = id || (results && results[0][this.getInsertIdField()]);
id = id || (metaData && metaData[this.getInsertIdField()]);
id = id || (results && results[0][autoIncrementField]);
id = id || (autoIncrementFieldAlias && results && results[0][autoIncrementFieldAlias]);
this.callee[autoIncrementField] = id;
}
};
return Query;
})();
...@@ -1259,8 +1259,8 @@ module.exports = (function() { ...@@ -1259,8 +1259,8 @@ module.exports = (function() {
} }
var dialect = this.sequelize.options.dialect; var dialect = this.sequelize.options.dialect;
if (options.ignoreDuplicates && dialect === 'postgres') { if (options.ignoreDuplicates && ['postgres', 'mssql'].indexOf(dialect) !== -1) {
return Promise.reject(new Error('Postgres does not support the \'ignoreDuplicates\' option.')); return Promise.reject(new Error(dialect + ' does not support the \'ignoreDuplicates\' option.'));
} }
if (options.updateOnDuplicate && ['mysql', 'mariadb'].indexOf(dialect) === -1) { if (options.updateOnDuplicate && ['mysql', 'mariadb'].indexOf(dialect) === -1) {
return Promise.reject(new Error(dialect + ' does not support the \'updateOnDuplicate\' option.')); return Promise.reject(new Error(dialect + ' does not support the \'updateOnDuplicate\' option.'));
...@@ -1398,7 +1398,7 @@ module.exports = (function() { ...@@ -1398,7 +1398,7 @@ module.exports = (function() {
* @param {Boolean} [options.force=false] Delete instead of setting deletedAt to current timestamp (only applicable if `paranoid` is enabled) * @param {Boolean} [options.force=false] Delete instead of setting deletedAt to current timestamp (only applicable if `paranoid` is enabled)
* @param {Boolean} [options.truncate=false] If set to true, dialects that support it will use TRUNCATE instead of DELETE FROM. If a table is truncated the where and limit options are ignored * @param {Boolean} [options.truncate=false] If set to true, dialects that support it will use TRUNCATE instead of DELETE FROM. If a table is truncated the where and limit options are ignored
* @param {Boolean} [options.cascade=false] Only used in conjuction with TRUNCATE. Truncates all tables that have foreign-key references to the named table, or to any tables added to the group due to CASCADE. * @param {Boolean} [options.cascade=false] Only used in conjuction with TRUNCATE. Truncates all tables that have foreign-key references to the named table, or to any tables added to the group due to CASCADE.
* @param {Transaction} [options.transaction] * @param {Transaction} [options.transaction]
* @return {Promise<undefined>} * @return {Promise<undefined>}
*/ */
Model.prototype.destroy = function(options) { Model.prototype.destroy = function(options) {
...@@ -1466,7 +1466,7 @@ module.exports = (function() { ...@@ -1466,7 +1466,7 @@ module.exports = (function() {
* @param {Boolean} [options.hooks=true] Run before / after bulk restore hooks? * @param {Boolean} [options.hooks=true] Run before / after bulk restore hooks?
* @param {Boolean} [options.individualHooks=false] If set to true, restore will find all records within the where parameter and will execute before / after bulkRestore hooks on each row * @param {Boolean} [options.individualHooks=false] If set to true, restore will find all records within the where parameter and will execute before / after bulkRestore hooks on each row
* @param {Number} [options.limit] How many rows to undelete * @param {Number} [options.limit] How many rows to undelete
* @param {Transaction} [options.transaction] * @param {Transaction} [options.transaction]
* *
* @return {Promise<undefined>} * @return {Promise<undefined>}
*/ */
...@@ -1536,7 +1536,7 @@ module.exports = (function() { ...@@ -1536,7 +1536,7 @@ module.exports = (function() {
* @param {Boolean} [options.individualHooks=false] Run before / after update hooks? * @param {Boolean} [options.individualHooks=false] Run before / after update hooks?
* @param {Boolean} [options.returning=false] Return the affected rows (only for postgres) * @param {Boolean} [options.returning=false] Return the affected rows (only for postgres)
* @param {Number} [options.limit] How many rows to update (only for mysql and mariadb) * @param {Number} [options.limit] How many rows to update (only for mysql and mariadb)
* @param {Transaction} [options.transaction] * @param {Transaction} [options.transaction]
* *
* @return {Promise<Array<affectedCount,affectedRows>>} * @return {Promise<Array<affectedCount,affectedRows>>}
*/ */
......
...@@ -42,16 +42,17 @@ SqlString.escape = function(val, stringifyObjects, timeZone, dialect, field) { ...@@ -42,16 +42,17 @@ SqlString.escape = function(val, stringifyObjects, timeZone, dialect, field) {
dialect = stringifyObjects.dialect || null; dialect = stringifyObjects.dialect || null;
field = stringifyObjects.field || null; field = stringifyObjects.field || null;
} }
if (val === undefined || val === null) { if (val === undefined || val === null) {
return 'NULL'; return 'NULL';
} }
switch (typeof val) { switch (typeof val) {
case 'boolean': case 'boolean':
// SQLite doesn't have true/false support. MySQL aliases true/false to 1/0 // SQLite doesn't have true/false support. MySQL aliases true/false to 1/0
// for us. Postgres actually has a boolean type with true/false literals, // for us. Postgres actually has a boolean type with true/false literals,
// but sequelize doesn't use it yet. // but sequelize doesn't use it yet.
if (dialect === 'mssql') {
return "'" + val + "'";
}
return dialect === 'sqlite' ? +!!val : ('' + !!val); return dialect === 'sqlite' ? +!!val : ('' + !!val);
case 'number': case 'number':
return val + ''; return val + '';
...@@ -76,7 +77,7 @@ SqlString.escape = function(val, stringifyObjects, timeZone, dialect, field) { ...@@ -76,7 +77,7 @@ SqlString.escape = function(val, stringifyObjects, timeZone, dialect, field) {
} }
} }
if (dialect === 'postgres' || dialect === 'sqlite') { if (dialect === 'postgres' || dialect === 'sqlite' || dialect === 'mssql') {
// http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS // http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS
// http://stackoverflow.com/q/603572/130598 // http://stackoverflow.com/q/603572/130598
val = val.replace(/'/g, "''"); val = val.replace(/'/g, "''");
...@@ -182,7 +183,10 @@ SqlString.bufferToString = function(buffer, dialect) { ...@@ -182,7 +183,10 @@ SqlString.bufferToString = function(buffer, dialect) {
if (dialect === 'postgres') { if (dialect === 'postgres') {
// bytea hex format http://www.postgresql.org/docs/current/static/datatype-binary.html // bytea hex format http://www.postgresql.org/docs/current/static/datatype-binary.html
return "E'\\\\x" + hex + "'"; return "E'\\\\x" + hex + "'";
} else if (dialect === 'mssql') {
return "0x" + hex;
} }
return "X'" + hex + "'"; return "X'" + hex + "'";
}; };
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
"sqlite3": "~3.0.0", "sqlite3": "~3.0.0",
"mysql": "~2.5.0", "mysql": "~2.5.0",
"pg": "~3.6.0", "pg": "~3.6.0",
"tedious": "^1.7.0",
"watchr": "~2.4.11", "watchr": "~2.4.11",
"chai": "~1.9.2", "chai": "~1.9.2",
"mocha": "~2.0.0", "mocha": "~2.0.0",
...@@ -68,6 +69,7 @@ ...@@ -68,6 +69,7 @@
"sqlite", "sqlite",
"postgresql", "postgresql",
"postgres", "postgres",
"mssql",
"orm", "orm",
"nodejs", "nodejs",
"object relational mapper" "object relational mapper"
......
...@@ -496,35 +496,38 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() { ...@@ -496,35 +496,38 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() {
} }
it("can cascade updates", function(done) { // NOTE: mssql does not support changing an autoincrement primary key
var Task = this.sequelize.define('Task', { title: DataTypes.STRING }) if (Support.getTestDialect() !== 'mssql') {
, User = this.sequelize.define('User', { username: DataTypes.STRING }) it("can cascade updates", function(done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING })
Task.belongsTo(User, {onUpdate: 'cascade'}) Task.belongsTo(User, {onUpdate: 'cascade'})
this.sequelize.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) { User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) { Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() { task.setUser(user).success(function() {
// Changing the id of a DAO requires a little dance since // Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the // the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause // `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.Model) var tableName = user.QueryInterface.QueryGenerator.addSchema(user.Model)
user.QueryInterface.update(user, tableName, {id: 999}, user.id) user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.success(function() { .success(function() {
Task.findAll().success(function(tasks) { Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1) expect(tasks).to.have.length(1)
expect(tasks[0].UserId).to.equal(999) expect(tasks[0].UserId).to.equal(999)
done() done()
})
}) })
}) })
}) })
}) })
}) })
}) })
}) }
}) })
......
...@@ -10,7 +10,8 @@ var chai = require('chai') ...@@ -10,7 +10,8 @@ var chai = require('chai')
, moment = require('moment') , moment = require('moment')
, sinon = require('sinon') , sinon = require('sinon')
, Promise = Sequelize.Promise , Promise = Sequelize.Promise
, current = Support.sequelize; , current = Support.sequelize
, dialect = Support.getTestDialect();
chai.config.includeStack = true; chai.config.includeStack = true;
...@@ -1431,7 +1432,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -1431,7 +1432,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
this.Task.create({ id: 15, title: 'task2' }), this.Task.create({ id: 15, title: 'task2' }),
]).spread(function(user, task1, task2) { ]).spread(function(user, task1, task2) {
return user.setTasks([task1, task2]).on('sql', spy).on('sql', _.after(2, function (sql) { return user.setTasks([task1, task2]).on('sql', spy).on('sql', _.after(2, function (sql) {
var tickChar = (Support.getTestDialect() === 'postgres') ? '"' : '`'; var tickChar = (Support.getTestDialect() === 'postgres' || dialect === 'mssql') ? '"' : '`';
expect(sql).to.have.string("INSERT INTO %TasksUsers% (%TaskId%,%UserId%) VALUES (12,1),(15,1)".replace(/%/g, tickChar)); expect(sql).to.have.string("INSERT INTO %TasksUsers% (%TaskId%,%UserId%) VALUES (12,1),(15,1)".replace(/%/g, tickChar));
})); }));
}).then(function () { }).then(function () {
...@@ -2328,33 +2329,36 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -2328,33 +2329,36 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}); });
}); });
it("can cascade updates", function() { // NOTE: mssql does not support changing an autoincrement primary key
var Task = this.sequelize.define('Task', { title: DataTypes.STRING }) if (dialect !== 'mssql') {
, User = this.sequelize.define('User', { username: DataTypes.STRING }); it("can cascade updates", function() {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING });
User.hasMany(Task, {onUpdate: 'cascade'}); User.hasMany(Task, {onUpdate: 'cascade'});
return this.sequelize.sync({ force: true }).then(function () { return this.sequelize.sync({ force: true }).then(function () {
return Promise.all([ return Promise.all([
User.create({ username: 'foo' }), User.create({ username: 'foo' }),
Task.create({ title: 'task' }), Task.create({ title: 'task' }),
]); ]);
}).spread(function (user, task) { }).spread(function (user, task) {
return user.setTasks([task]).return(user); return user.setTasks([task]).return(user);
}).then(function(user) { }).then(function(user) {
// Changing the id of a DAO requires a little dance since // Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the // the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause // `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.Model); var tableName = user.QueryInterface.QueryGenerator.addSchema(user.Model);
return user.QueryInterface.update(user, tableName, {id: 999}, user.id); return user.QueryInterface.update(user, tableName, {id: 999}, user.id);
}).then(function() { }).then(function() {
return Task.findAll(); return Task.findAll();
}).then(function(tasks) { }).then(function(tasks) {
expect(tasks).to.have.length(1); expect(tasks).to.have.length(1);
expect(tasks[0].UserId).to.equal(999); expect(tasks[0].UserId).to.equal(999);
});
}); });
}); }
if (current.dialect.supports.constraints.restrict) { if (current.dialect.supports.constraints.restrict) {
it("can restrict deletes", function() { it("can restrict deletes", function() {
......
...@@ -394,29 +394,32 @@ describe(Support.getTestDialectTeaser("HasOne"), function() { ...@@ -394,29 +394,32 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
}) })
}) })
it("can cascade updates", function(done) { // NOTE: mssql does not support changing an autoincrement primary key
var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) if (Support.getTestDialect() !== 'mssql') {
, User = this.sequelize.define('User', { username: Sequelize.STRING }) it("can cascade updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, {onUpdate: 'cascade'}) User.hasOne(Task, {onUpdate: 'cascade'})
User.sync({ force: true }).success(function() { User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() { Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) { User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) { Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() { user.setTask(task).success(function() {
// Changing the id of a DAO requires a little dance since // Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the // the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause // `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.Model) var tableName = user.QueryInterface.QueryGenerator.addSchema(user.Model)
user.QueryInterface.update(user, tableName, {id: 999}, user.id) user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.success(function() { .success(function() {
Task.findAll().success(function(tasks) { Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1) expect(tasks).to.have.length(1)
expect(tasks[0].UserId).to.equal(999) expect(tasks[0].UserId).to.equal(999)
done() done()
})
}) })
}) })
}) })
...@@ -424,7 +427,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() { ...@@ -424,7 +427,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
}) })
}) })
}) })
}) }
if (current.dialect.supports.constraints.restrict) { if (current.dialect.supports.constraints.restrict) {
......
...@@ -9,7 +9,19 @@ module.exports = { ...@@ -9,7 +9,19 @@ module.exports = {
}, },
rand: function() { rand: function() {
return parseInt(Math.random() * 999, 10) return parseInt(Math.random() * 999, 10);
},
mssql: {
database: process.env.SEQ_MSSQL_DB || process.env.SEQ_DB || ('sequelize_test_' + ~~(Math.random() * 100)),
username: process.env.SEQ_MSSQL_USER || process.env.SEQ_USER || "sequelize",
password: process.env.SEQ_MSSQL_PW || process.env.SEQ_PW || "test",
host: process.env.SEQ_MSSQL_HOST || process.env.SEQ_HOST || "ec2-54-76-93-58.eu-west-1.compute.amazonaws.com",
port: process.env.SEQ_MSSQL_PORT || process.env.SEQ_PORT || 1433,
pool: {
maxConnections: process.env.SEQ_MSSQL_POOL_MAX || process.env.SEQ_POOL_MAX || 5,
maxIdleTime: process.env.SEQ_MSSQL_POOL_IDLE || process.env.SEQ_POOL_IDLE || 3000
}
}, },
//make maxIdleTime small so that tests exit promptly //make maxIdleTime small so that tests exit promptly
...@@ -51,4 +63,4 @@ module.exports = { ...@@ -51,4 +63,4 @@ module.exports = {
maxIdleTime: process.env.SEQ_MYSQL_POOL_IDLE || process.env.SEQ_POOL_IDLE || 3000 maxIdleTime: process.env.SEQ_MYSQL_POOL_IDLE || process.env.SEQ_POOL_IDLE || 3000
} }
} }
} };
...@@ -20,6 +20,8 @@ describe(Support.getTestDialectTeaser("Configuration"), function() { ...@@ -20,6 +20,8 @@ describe(Support.getTestDialectTeaser("Configuration"), function() {
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
// SQLite doesn't have a breakdown of error codes, so we are unable to discern between the different types of errors. // SQLite doesn't have a breakdown of error codes, so we are unable to discern between the different types of errors.
return expect(seq.query('select 1 as hello')).to.eventually.be.rejectedWith(seq.ConnectionError, 'SQLITE_CANTOPEN: unable to open database file') return expect(seq.query('select 1 as hello')).to.eventually.be.rejectedWith(seq.ConnectionError, 'SQLITE_CANTOPEN: unable to open database file')
} else if (dialect === 'mssql') {
return expect(seq.query('select 1 as hello')).to.eventually.be.rejectedWith([seq.HostNotReachableError, seq.InvalidConnectionError]);
} else { } else {
return expect(seq.query('select 1 as hello')).to.eventually.be.rejectedWith(seq.InvalidConnectionError, 'connect EINVAL') return expect(seq.query('select 1 as hello')).to.eventually.be.rejectedWith(seq.InvalidConnectionError, 'connect EINVAL')
} }
...@@ -32,6 +34,13 @@ describe(Support.getTestDialectTeaser("Configuration"), function() { ...@@ -32,6 +34,13 @@ describe(Support.getTestDialectTeaser("Configuration"), function() {
return; return;
} }
if (dialect === 'mssql') {
// NOTE: Travis seems to be having trouble with this test against the
// AWS instance. Works perfectly fine on a local setup.
expect(true).to.be.true;
return;
}
var seq = new Sequelize(config[dialect].database, config[dialect].username, 'fakepass123', {logging: false, host: config[dialect].host, port: 1, dialect: dialect}) var seq = new Sequelize(config[dialect].database, config[dialect].username, 'fakepass123', {logging: false, host: config[dialect].host, port: 1, dialect: dialect})
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
// SQLite doesn't require authentication and `select 1 as hello` is a valid query, so this should be fulfilled not rejected for it. // SQLite doesn't require authentication and `select 1 as hello` is a valid query, so this should be fulfilled not rejected for it.
......
...@@ -104,6 +104,10 @@ describe(Support.getTestDialectTeaser('DataTypes'), function() { ...@@ -104,6 +104,10 @@ describe(Support.getTestDialectTeaser('DataTypes'), function() {
tests.forEach(function(test) { tests.forEach(function(test) {
it('transforms "' + test[1] + '" to "' + test[2] + '"', function(done) { it('transforms "' + test[1] + '" to "' + test[2] + '"', function(done) {
if (Support.getTestDialect() === 'mssql' && test[1] ==='STRING') {
test[2] = 'NVARCHAR(255)';
}
expect(test[0].toString()).to.equal(test[2]) expect(test[0].toString()).to.equal(test[2])
done() done()
}) })
......
...@@ -9,6 +9,7 @@ var chai = require('chai') ...@@ -9,6 +9,7 @@ var chai = require('chai')
, datetime = require('chai-datetime') , datetime = require('chai-datetime')
, async = require('async') , async = require('async')
, _ = require('lodash') , _ = require('lodash')
, dialect = Support.getTestDialect();
chai.use(datetime) chai.use(datetime)
chai.config.includeStack = true chai.config.includeStack = true
...@@ -619,15 +620,25 @@ describe(Support.getTestDialectTeaser("Include"), function () { ...@@ -619,15 +620,25 @@ describe(Support.getTestDialectTeaser("Include"), function () {
comment_title: 'WAT' comment_title: 'WAT'
}); });
}).then(function () { }).then(function () {
var findAttributes;
if (dialect === 'mssql') {
findAttributes = [
Sequelize.literal('CAST(CASE WHEN EXISTS(SELECT 1) THEN 1 ELSE 0 END AS BIT) AS "PostComments.someProperty"'),
[Sequelize.literal('CAST(CASE WHEN EXISTS(SELECT 1) THEN 1 ELSE 0 END AS BIT)'), 'someProperty2']
];
} else {
findAttributes = [
Sequelize.literal('EXISTS(SELECT 1) AS "PostComments.someProperty"'),
[Sequelize.literal('EXISTS(SELECT 1)'), 'someProperty2']
];
}
findAttributes.push(['comment_title', 'commentTitle']);
return Post.findAll({ return Post.findAll({
include: [ include: [
{ {
model: PostComment, model: PostComment,
attributes: [ attributes: findAttributes
Sequelize.literal('EXISTS(SELECT 1) AS "PostComments.someProperty"'),
[Sequelize.literal('EXISTS(SELECT 1)'), 'someProperty2'],
['comment_title', 'commentTitle']
]
} }
] ]
}) })
......
...@@ -22,7 +22,6 @@ describe(Support.getTestDialectTeaser("Include"), function () { ...@@ -22,7 +22,6 @@ describe(Support.getTestDialectTeaser("Include"), function () {
// Associations // Associations
A.hasMany(B); A.hasMany(B);
B.belongsTo(B);
B.belongsTo(D); B.belongsTo(D);
B.hasMany(C, { B.hasMany(C, {
through: 'BC', through: 'BC',
......
...@@ -252,14 +252,14 @@ describe(Support.getTestDialectTeaser("Include"), function () { ...@@ -252,14 +252,14 @@ describe(Support.getTestDialectTeaser("Include"), function () {
SubscriptionForm.belongsTo(Category, {foreignKey:'boundCategory'}); SubscriptionForm.belongsTo(Category, {foreignKey:'boundCategory'});
Category.hasMany(SubscriptionForm, {foreignKey:'boundCategory'}); Category.hasMany(SubscriptionForm, {foreignKey:'boundCategory'});
Capital.hasMany(Category, { foreignKey: 'boundCapital'}); Capital.hasMany(Category, { foreignKey: 'boundCapital'});
Category.belongsTo(Capital, {foreignKey:'boundCapital'}); Category.belongsTo(Capital, {foreignKey:'boundCapital'});
Category.hasMany(SubCategory, {foreignKey:'boundCategory'}); Category.hasMany(SubCategory, {foreignKey:'boundCategory'});
SubCategory.belongsTo(Category, {foreignKey: 'boundCategory'}); SubCategory.belongsTo(Category, {foreignKey: 'boundCategory'});
return this.sequelize.sync({force: true}).then(function() { return this.sequelize.sync({force: true}).then(function() {
return User.find({ return User.find({
include: [ include: [
...@@ -1158,7 +1158,7 @@ describe(Support.getTestDialectTeaser("Include"), function () { ...@@ -1158,7 +1158,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
chainer.run().done(callback) chainer.run().done(callback)
}] }]
}, function (err) { }, function (err) {
expect(err).not.to.be.ok expect(err).not.to.be.ok
User.findAll({ User.findAll({
......
...@@ -17,6 +17,7 @@ var sortById = function(a, b) { ...@@ -17,6 +17,7 @@ var sortById = function(a, b) {
describe(Support.getTestDialectTeaser("Includes with schemas"), function () { describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
describe('findAll', function () { describe('findAll', function () {
this.timeout(30000);
beforeEach(function () { beforeEach(function () {
var self = this var self = this
this.fixtureA = function(done) { this.fixtureA = function(done) {
......
...@@ -1480,7 +1480,9 @@ describe(Support.getTestDialectTeaser("Instance"), function () { ...@@ -1480,7 +1480,9 @@ describe(Support.getTestDialectTeaser("Instance"), function () {
} }
else if (Support.dialectIsMySQL()) { else if (Support.dialectIsMySQL()) {
expect(sql).to.equal("DELETE FROM `UserDestroys` WHERE `newId`='123ABC' LIMIT 1") expect(sql).to.equal("DELETE FROM `UserDestroys` WHERE `newId`='123ABC' LIMIT 1")
} else { } else if (dialect === 'mssql') {
expect(sql).to.equal('DELETE TOP(1) FROM "UserDestroys" WHERE "newId"=\'123ABC\'; SELECT @@ROWCOUNT AS AFFECTEDROWS;')
}else {
expect(sql).to.equal("DELETE FROM `UserDestroys` WHERE `newId`='123ABC'") expect(sql).to.equal("DELETE FROM `UserDestroys` WHERE `newId`='123ABC'")
} }
done() done()
...@@ -1588,6 +1590,11 @@ describe(Support.getTestDialectTeaser("Instance"), function () { ...@@ -1588,6 +1590,11 @@ describe(Support.getTestDialectTeaser("Instance"), function () {
var query = { where: { username: 'fnord' }} var query = { where: { username: 'fnord' }}
self.User.find(query).success(function(user2) { self.User.find(query).success(function(user2) {
if (dialect === 'mssql') {
user1.dataValues.uuidv1 = user1.dataValues.uuidv1.toUpperCase();
user1.dataValues.uuidv4 = user1.dataValues.uuidv4.toUpperCase();
}
expect(user1.equals(user2)).to.be.true expect(user1.equals(user2)).to.be.true
done() done()
}) })
...@@ -1730,7 +1737,7 @@ describe(Support.getTestDialectTeaser("Instance"), function () { ...@@ -1730,7 +1737,7 @@ describe(Support.getTestDialectTeaser("Instance"), function () {
expect(download.finishedAt).to.not.be.ok expect(download.finishedAt).to.not.be.ok
Download.all({ Download.all({
where: (dialect === 'postgres' ? '"finishedAt" IS NULL' : "`finishedAt` IS NULL") where: (dialect === 'postgres' || dialect === 'mssql' ? '"finishedAt" IS NULL' : "`finishedAt` IS NULL")
}).success(function(downloads) { }).success(function(downloads) {
downloads.forEach(function(download) { downloads.forEach(function(download) {
expect(download.startedAt instanceof Date).to.be.true expect(download.startedAt instanceof Date).to.be.true
......
...@@ -120,7 +120,9 @@ describe(Support.getTestDialectTeaser("DAO"), function () { ...@@ -120,7 +120,9 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
// Create the user first to set the proper default values. PG does not support column references in insert, // Create the user first to set the proper default values. PG does not support column references in insert,
// so we must create a record with the right value for always_false, then reference it in an update // so we must create a record with the right value for always_false, then reference it in an update
var now = dialect === 'sqlite' ? self.sequelize.fn('', self.sequelize.fn('datetime', 'now')) : self.sequelize.fn('NOW') var now = dialect === 'sqlite' ? self.sequelize.fn('', self.sequelize.fn('datetime', 'now')) : self.sequelize.fn('NOW')
if(dialect === 'mssql'){
now = self.sequelize.fn('', self.sequelize.fn('getdate'))
}
user.set({ user.set({
d: now, d: now,
b: self.sequelize.col('always_false') b: self.sequelize.col('always_false')
......
...@@ -35,7 +35,11 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -35,7 +35,11 @@ describe(Support.getTestDialectTeaser("Model"), function () {
this.User.find({ this.User.find({
where: Sequelize[method]( "1=1", "2=2" ) where: Sequelize[method]( "1=1", "2=2" )
}).on('sql', function(sql) { }).on('sql', function(sql) {
expect(sql).to.contain("WHERE (1=1 " + word + " 2=2) LIMIT 1") if(dialect === 'mssql'){
expect(sql).to.contain("WHERE (1=1 " + word + " 2=2)")
}else{
expect(sql).to.contain("WHERE (1=1 " + word + " 2=2) LIMIT 1")
}
done() done()
}) })
}) })
...@@ -44,7 +48,11 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -44,7 +48,11 @@ describe(Support.getTestDialectTeaser("Model"), function () {
this.User.find({ this.User.find({
where: Sequelize[method]( ["1=?", 1], ["2=?", 2] ) where: Sequelize[method]( ["1=?", 1], ["2=?", 2] )
}).on('sql', function(sql) { }).on('sql', function(sql) {
expect(sql).to.contain("WHERE (1=1 " + word + " 2=2) LIMIT 1") if(dialect === 'mssql'){
expect(sql).to.contain("WHERE (1=1 " + word + " 2=2)")
}else{
expect(sql).to.contain("WHERE (1=1 " + word + " 2=2) LIMIT 1")
}
done() done()
}) })
}) })
...@@ -55,6 +63,7 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -55,6 +63,7 @@ describe(Support.getTestDialectTeaser("Model"), function () {
}).on('sql', function(sql) { }).on('sql', function(sql) {
var expectation = ({ var expectation = ({
mysql: "WHERE (`User`.`username`='foo' AND `User`.`intVal`=2 " + word + " `User`.`secretValue`='bar')", mysql: "WHERE (`User`.`username`='foo' AND `User`.`intVal`=2 " + word + " `User`.`secretValue`='bar')",
mssql: 'WHERE ("User"."username"=\'foo\' AND "User"."intVal"=2 ' + word + ' "User"."secretValue"=\'bar\')',
sqlite: "WHERE (`User`.`username`='foo' AND `User`.`intVal`=2 " + word + " `User`.`secretValue`='bar')", sqlite: "WHERE (`User`.`username`='foo' AND `User`.`intVal`=2 " + word + " `User`.`secretValue`='bar')",
postgres: 'WHERE ("User"."username"=\'foo\' AND "User"."intVal"=2 ' + word + ' "User"."secretValue"=\'bar\')', postgres: 'WHERE ("User"."username"=\'foo\' AND "User"."intVal"=2 ' + word + ' "User"."secretValue"=\'bar\')',
mariadb: "WHERE (`User`.`username`='foo' AND `User`.`intVal`=2 " + word + " `User`.`secretValue`='bar')" mariadb: "WHERE (`User`.`username`='foo' AND `User`.`intVal`=2 " + word + " `User`.`secretValue`='bar')"
...@@ -79,6 +88,7 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -79,6 +88,7 @@ describe(Support.getTestDialectTeaser("Model"), function () {
mysql: "WHERE (`User`.`id`=1 " + word + " `User`.`id`=2)", mysql: "WHERE (`User`.`id`=1 " + word + " `User`.`id`=2)",
sqlite: "WHERE (`User`.`id`=1 " + word + " `User`.`id`=2)", sqlite: "WHERE (`User`.`id`=1 " + word + " `User`.`id`=2)",
postgres: 'WHERE ("User"."id"=1 ' + word + ' "User"."id"=2)', postgres: 'WHERE ("User"."id"=1 ' + word + ' "User"."id"=2)',
mssql: 'WHERE ("User"."id"=1 ' + word + ' "User"."id"=2)',
mariadb: "WHERE (`User`.`id`=1 " + word + " `User`.`id`=2)" mariadb: "WHERE (`User`.`id`=1 " + word + " `User`.`id`=2)"
})[Support.getTestDialect()] })[Support.getTestDialect()]
...@@ -100,20 +110,25 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -100,20 +110,25 @@ describe(Support.getTestDialectTeaser("Model"), function () {
this.User.find({ this.User.find({
where: Sequelize.and( Sequelize.or("1=1", "2=2"), Sequelize.or("3=3", "4=4") ) where: Sequelize.and( Sequelize.or("1=1", "2=2"), Sequelize.or("3=3", "4=4") )
}).on('sql', function(sql) { }).on('sql', function(sql) {
expect(sql).to.contain("WHERE ((1=1 OR 2=2) AND (3=3 OR 4=4)) LIMIT 1") if(dialect === 'mssql'){
expect(sql).to.contain("WHERE ((1=1 OR 2=2) AND (3=3 OR 4=4))")
}else{
expect(sql).to.contain("WHERE ((1=1 OR 2=2) AND (3=3 OR 4=4)) LIMIT 1")
}
done() done()
}) })
}) })
it('allows nesting of Sequelize.or using object notation', function(done) { it('allows nesting of Sequelize.or using object notation', function(done) {
this.User.find({ this.User.find({
where: Sequelize.and( Sequelize.or({username: {eq: "foo"}}, {username: {eq:"bar"}}), where: Sequelize.and( Sequelize.or({username: {eq: "foo"}}, {username: {eq:"bar"}}),
Sequelize.or({id: {eq: 1}}, {id: {eq:4}}) ) Sequelize.or({id: {eq: 1}}, {id: {eq:4}}) )
}).on('sql', function(sql) { }).on('sql', function(sql) {
var expectation = ({ var expectation = ({
mysql: "WHERE ((`User`.`username` = 'foo' OR `User`.`username` = 'bar') AND (`User`.`id` = 1 OR `User`.`id` = 4)) LIMIT 1", mysql: "WHERE ((`User`.`username` = 'foo' OR `User`.`username` = 'bar') AND (`User`.`id` = 1 OR `User`.`id` = 4)) LIMIT 1",
sqlite: "WHERE ((`User`.`username` = 'foo' OR `User`.`username` = 'bar') AND (`User`.`id` = 1 OR `User`.`id` = 4)) LIMIT 1", sqlite: "WHERE ((`User`.`username` = 'foo' OR `User`.`username` = 'bar') AND (`User`.`id` = 1 OR `User`.`id` = 4)) LIMIT 1",
postgres: 'WHERE (("User"."username" = \'foo\' OR "User"."username" = \'bar\') AND ("User"."id" = 1 OR "User"."id" = 4)) LIMIT 1', postgres: 'WHERE (("User"."username" = \'foo\' OR "User"."username" = \'bar\') AND ("User"."id" = 1 OR "User"."id" = 4)) LIMIT 1',
mssql: 'WHERE (("User"."username" = \'foo\' OR "User"."username" = \'bar\') AND ("User"."id" = 1 OR "User"."id" = 4))',
mariadb: "WHERE ((`User`.`username` = 'foo' OR `User`.`username` = 'bar') AND (`User`.`id` = 1 OR `User`.`id` = 4)) LIMIT 1" mariadb: "WHERE ((`User`.`username` = 'foo' OR `User`.`username` = 'bar') AND (`User`.`id` = 1 OR `User`.`id` = 4)) LIMIT 1"
})[Support.getTestDialect()] })[Support.getTestDialect()]
...@@ -131,20 +146,25 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -131,20 +146,25 @@ describe(Support.getTestDialectTeaser("Model"), function () {
this.User.find({ this.User.find({
where: Sequelize.or( Sequelize.and("1=1", "2=2"), Sequelize.and("3=3", "4=4") ) where: Sequelize.or( Sequelize.and("1=1", "2=2"), Sequelize.and("3=3", "4=4") )
}).on('sql', function(sql) { }).on('sql', function(sql) {
expect(sql).to.contain("WHERE ((1=1 AND 2=2) OR (3=3 AND 4=4)) LIMIT 1") if(dialect === 'mssql'){
expect(sql).to.contain("WHERE ((1=1 AND 2=2) OR (3=3 AND 4=4))")
}else{
expect(sql).to.contain("WHERE ((1=1 AND 2=2) OR (3=3 AND 4=4)) LIMIT 1")
}
done() done()
}) })
}) })
it('allows nesting of Sequelize.and using object notation', function(done) { it('allows nesting of Sequelize.and using object notation', function(done) {
this.User.find({ this.User.find({
where: Sequelize.or( Sequelize.and({username: {eq: "foo"}}, {username: {eq:"bar"}}), where: Sequelize.or( Sequelize.and({username: {eq: "foo"}}, {username: {eq:"bar"}}),
Sequelize.and({id: {eq: 1}}, {id: {eq:4}}) ) Sequelize.and({id: {eq: 1}}, {id: {eq:4}}) )
}).on('sql', function(sql) { }).on('sql', function(sql) {
var expectation = ({ var expectation = ({
mysql: "WHERE ((`User`.`username` = 'foo' AND `User`.`username` = 'bar') OR (`User`.`id` = 1 AND `User`.`id` = 4)) LIMIT 1", mysql: "WHERE ((`User`.`username` = 'foo' AND `User`.`username` = 'bar') OR (`User`.`id` = 1 AND `User`.`id` = 4)) LIMIT 1",
sqlite: "WHERE ((`User`.`username` = 'foo' AND `User`.`username` = 'bar') OR (`User`.`id` = 1 AND `User`.`id` = 4)) LIMIT 1", sqlite: "WHERE ((`User`.`username` = 'foo' AND `User`.`username` = 'bar') OR (`User`.`id` = 1 AND `User`.`id` = 4)) LIMIT 1",
postgres: 'WHERE (("User"."username" = \'foo\' AND "User"."username" = \'bar\') OR ("User"."id" = 1 AND "User"."id" = 4)) LIMIT 1', postgres: 'WHERE (("User"."username" = \'foo\' AND "User"."username" = \'bar\') OR ("User"."id" = 1 AND "User"."id" = 4)) LIMIT 1',
mssql: 'WHERE (("User"."username" = \'foo\' AND "User"."username" = \'bar\') OR ("User"."id" = 1 AND "User"."id" = 4))',
mariadb: "WHERE ((`User`.`username` = 'foo' AND `User`.`username` = 'bar') OR (`User`.`id` = 1 AND `User`.`id` = 4)) LIMIT 1" mariadb: "WHERE ((`User`.`username` = 'foo' AND `User`.`username` = 'bar') OR (`User`.`id` = 1 AND `User`.`id` = 4)) LIMIT 1"
})[Support.getTestDialect()] })[Support.getTestDialect()]
...@@ -186,7 +206,7 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -186,7 +206,7 @@ describe(Support.getTestDialectTeaser("Model"), function () {
) )
] ]
}).on('sql', function(sql) { }).on('sql', function(sql) {
if (Support.getTestDialect() === 'postgres') { if (Support.getTestDialect() === 'postgres' || dialect === 'mssql') {
expect(sql).to.contain( expect(sql).to.contain(
'WHERE (' + [ 'WHERE (' + [
'"User"."id"=42 AND 2=2 AND 1=1 AND "User"."username"=\'foo\' AND ', '"User"."id"=42 AND 2=2 AND 1=1 AND "User"."username"=\'foo\' AND ',
......
...@@ -402,12 +402,23 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -402,12 +402,23 @@ describe(Support.getTestDialectTeaser("Model"), function () {
return this.sequelize.sync({ force: true }).then(function () { return this.sequelize.sync({ force: true }).then(function () {
return Test.create({}); return Test.create({});
}).then(function () { }).then(function () {
return Test.findAll({ var findAttributes;
attributes: [ if (dialect === 'mssql') {
findAttributes = [
Sequelize.literal('CAST(CASE WHEN EXISTS(SELECT 1) THEN 1 ELSE 0 END AS BIT) AS "someProperty"'),
[Sequelize.literal('CAST(CASE WHEN EXISTS(SELECT 1) THEN 1 ELSE 0 END AS BIT)'), 'someProperty2']
];
} else {
findAttributes = [
Sequelize.literal('EXISTS(SELECT 1) AS "someProperty"'), Sequelize.literal('EXISTS(SELECT 1) AS "someProperty"'),
[Sequelize.literal('EXISTS(SELECT 1)'), 'someProperty2'] [Sequelize.literal('EXISTS(SELECT 1)'), 'someProperty2']
] ];
}
return Test.findAll({
attributes: findAttributes
}); });
}).then(function (tests) { }).then(function (tests) {
expect(tests[0].get('someProperty')).to.be.ok; expect(tests[0].get('someProperty')).to.be.ok;
expect(tests[0].get('someProperty2')).to.be.ok; expect(tests[0].get('someProperty2')).to.be.ok;
...@@ -476,4 +487,4 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -476,4 +487,4 @@ describe(Support.getTestDialectTeaser("Model"), function () {
}); });
}); });
}); });
}); });
\ No newline at end of file
...@@ -101,7 +101,12 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -101,7 +101,12 @@ describe(Support.getTestDialectTeaser("Model"), function () {
return this.sequelize.sync({ force: true}).then(function () { return this.sequelize.sync({ force: true}).then(function () {
return Post.bulkCreate([{ text: 'text1' },{ text: 'text2' }]); return Post.bulkCreate([{ text: 'text1' },{ text: 'text2' }]);
}).then(function () { }).then(function () {
return Post.find({ attributes: ['id','text',Sequelize.literal('EXISTS(SELECT 1) AS "someBoolean"')] }); var boolQuery = 'EXISTS(SELECT 1) AS "someBoolean"';
if (dialect === 'mssql') {
boolQuery = 'CAST(CASE WHEN EXISTS(SELECT 1) THEN 1 ELSE 0 END AS BIT) AS "someBoolean"';
}
return Post.find({ attributes: ['id','text', Sequelize.literal(boolQuery)] });
}).then(function (post) { }).then(function (post) {
expect(post.get('someBoolean')).to.be.ok; expect(post.get('someBoolean')).to.be.ok;
expect(post.get().someBoolean).to.be.ok; expect(post.get().someBoolean).to.be.ok;
...@@ -138,4 +143,4 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -138,4 +143,4 @@ describe(Support.getTestDialectTeaser("Model"), function () {
}); });
}); });
}); });
}); });
\ No newline at end of file
...@@ -991,9 +991,9 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -991,9 +991,9 @@ describe(Support.getTestDialectTeaser("Model"), function () {
it('properly handles disparate field lists', function(done) { it('properly handles disparate field lists', function(done) {
var self = this var self = this
, data = [{username: 'Peter', secretValue: '42' }, , data = [{username: 'Peter', secretValue: '42', uniqueName:'1' },
{username: 'Paul'}, {username: 'Paul', uniqueName:'2'},
{username: 'Steve'}] {username: 'Steve', uniqueName:'3'}]
this.User.bulkCreate(data).success(function() { this.User.bulkCreate(data).success(function() {
self.User.findAll({where: {username: 'Paul'}}).success(function(users) { self.User.findAll({where: {username: 'Paul'}}).success(function(users) {
...@@ -1007,10 +1007,10 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -1007,10 +1007,10 @@ describe(Support.getTestDialectTeaser("Model"), function () {
it('inserts multiple values respecting the white list', function(done) { it('inserts multiple values respecting the white list', function(done) {
var self = this var self = this
, data = [{ username: 'Peter', secretValue: '42' }, , data = [{ username: 'Peter', secretValue: '42', uniqueName:'1' },
{ username: 'Paul', secretValue: '23'}] { username: 'Paul', secretValue: '23', uniqueName:'2'}]
this.User.bulkCreate(data, { fields: ['username'] }).success(function() { this.User.bulkCreate(data, { fields: ['username','uniqueName'] }).success(function() {
self.User.findAll({order: 'id'}).success(function(users) { self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(2) expect(users.length).to.equal(2)
expect(users[0].username).to.equal("Peter") expect(users[0].username).to.equal("Peter")
...@@ -1024,8 +1024,8 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -1024,8 +1024,8 @@ describe(Support.getTestDialectTeaser("Model"), function () {
it('should store all values if no whitelist is specified', function(done) { it('should store all values if no whitelist is specified', function(done) {
var self = this var self = this
, data = [{ username: 'Peter', secretValue: '42' }, , data = [{ username: 'Peter', secretValue: '42', uniqueName:'1' },
{ username: 'Paul', secretValue: '23'}] { username: 'Paul', secretValue: '23', uniqueName:'2'}]
this.User.bulkCreate(data).success(function() { this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) { self.User.findAll({order: 'id'}).success(function(users) {
...@@ -1042,8 +1042,8 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -1042,8 +1042,8 @@ describe(Support.getTestDialectTeaser("Model"), function () {
it('saves data with single quote', function(done) { it('saves data with single quote', function(done) {
var self = this var self = this
, quote = "Single'Quote" , quote = "Single'Quote"
, data = [{ username: 'Peter', data: quote}, , data = [{ username: 'Peter', data: quote, uniqueName: '1'},
{ username: 'Paul', data: quote}] { username: 'Paul', data: quote, uniqueName: '2'}]
this.User.bulkCreate(data).success(function() { this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) { self.User.findAll({order: 'id'}).success(function(users) {
...@@ -1060,8 +1060,8 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -1060,8 +1060,8 @@ describe(Support.getTestDialectTeaser("Model"), function () {
it('saves data with double quote', function(done) { it('saves data with double quote', function(done) {
var self = this var self = this
, quote = 'Double"Quote' , quote = 'Double"Quote'
, data = [{ username: 'Peter', data: quote}, , data = [{ username: 'Peter', data: quote, uniqueName: '1'},
{ username: 'Paul', data: quote}] { username: 'Paul', data: quote, uniqueName: '2'}]
this.User.bulkCreate(data).success(function() { this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) { self.User.findAll({order: 'id'}).success(function(users) {
...@@ -1078,8 +1078,8 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -1078,8 +1078,8 @@ describe(Support.getTestDialectTeaser("Model"), function () {
it('saves stringified JSON data', function(done) { it('saves stringified JSON data', function(done) {
var self = this var self = this
, json = JSON.stringify({ key: 'value' }) , json = JSON.stringify({ key: 'value' })
, data = [{ username: 'Peter', data: json}, , data = [{ username: 'Peter', data: json, uniqueName: '1'},
{ username: 'Paul', data: json}] { username: 'Paul', data: json, uniqueName: '2'}]
this.User.bulkCreate(data).success(function() { this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) { self.User.findAll({order: 'id'}).success(function(users) {
...@@ -1107,8 +1107,8 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -1107,8 +1107,8 @@ describe(Support.getTestDialectTeaser("Model"), function () {
it('stores the current date in createdAt', function(done) { it('stores the current date in createdAt', function(done) {
var self = this var self = this
, data = [{ username: 'Peter'}, , data = [{ username: 'Peter', uniqueName: '1'},
{ username: 'Paul'}] { username: 'Paul', uniqueName: '2'}]
this.User.bulkCreate(data).success(function() { this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) { self.User.findAll({order: 'id'}).success(function(users) {
...@@ -1244,7 +1244,7 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -1244,7 +1244,7 @@ describe(Support.getTestDialectTeaser("Model"), function () {
}); });
}); });
if (Support.getTestDialect() !== 'postgres') { if (dialect !== 'postgres' && dialect !== 'mssql') {
it("should support the ignoreDuplicates option", function(done) { it("should support the ignoreDuplicates option", function(done) {
var self = this var self = this
, data = [{ uniqueName: 'Peter', secretValue: '42' }, , data = [{ uniqueName: 'Peter', secretValue: '42' },
...@@ -1277,7 +1277,12 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -1277,7 +1277,12 @@ describe(Support.getTestDialectTeaser("Model"), function () {
self.User.bulkCreate(data, { fields: ['uniqueName', 'secretValue'], ignoreDuplicates: true }).error(function(err) { self.User.bulkCreate(data, { fields: ['uniqueName', 'secretValue'], ignoreDuplicates: true }).error(function(err) {
expect(err).to.exist expect(err).to.exist
expect(err.message).to.match(/Postgres does not support the \'ignoreDuplicates\' option./) if (dialect === 'mssql') {
console.log(err.message);
expect(err.message).to.match(/mssql does not support the \'ignoreDuplicates\' option./)
} else {
expect(err.message).to.match(/postgres does not support the \'ignoreDuplicates\' option./)
}
done(); done();
}) })
......
...@@ -683,7 +683,7 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -683,7 +683,7 @@ describe(Support.getTestDialectTeaser("Model"), function () {
it('including two has many relations should not result in duplicate values', function(done) { it('including two has many relations should not result in duplicate values', function(done) {
var self = this var self = this
self.Contact = self.sequelize.define('Contact', { name: DataTypes.TEXT }) self.Contact = self.sequelize.define('Contact', { name: DataTypes.STRING })
self.Photo = self.sequelize.define('Photo', { img: DataTypes.TEXT }) self.Photo = self.sequelize.define('Photo', { img: DataTypes.TEXT })
self.PhoneNumber = self.sequelize.define('PhoneNumber', { phone: DataTypes.TEXT }) self.PhoneNumber = self.sequelize.define('PhoneNumber', { phone: DataTypes.TEXT })
......
...@@ -68,7 +68,6 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -68,7 +68,6 @@ describe(Support.getTestDialectTeaser("Model"), function () {
this.buf = new Buffer(16); this.buf = new Buffer(16);
this.buf.fill('\x01'); this.buf.fill('\x01');
this.User.bulkCreate([ this.User.bulkCreate([
{username: 'boo', intVal: 5, theDate: '2013-01-01 12:00'}, {username: 'boo', intVal: 5, theDate: '2013-01-01 12:00'},
{username: 'boo2', intVal: 10, theDate: '2013-01-10 12:00', binary: this.buf } {username: 'boo2', intVal: 10, theDate: '2013-01-10 12:00', binary: this.buf }
...@@ -207,7 +206,7 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -207,7 +206,7 @@ describe(Support.getTestDialectTeaser("Model"), function () {
it('should be able to handle false/true values just fine...', function(done) { it('should be able to handle false/true values just fine...', function(done) {
var User = this.User var User = this.User
, escapeChar = (dialect === "postgres") ? '"' : '`' , escapeChar = (dialect === "postgres" || dialect === 'mssql') ? '"' : '`'
User.bulkCreate([ User.bulkCreate([
{username: 'boo5', aBool: false}, {username: 'boo5', aBool: false},
...@@ -228,7 +227,7 @@ describe(Support.getTestDialectTeaser("Model"), function () { ...@@ -228,7 +227,7 @@ describe(Support.getTestDialectTeaser("Model"), function () {
it('should be able to handle false/true values through associations as well...', function(done) { it('should be able to handle false/true values through associations as well...', function(done) {
var User = this.User var User = this.User
, escapeChar = (dialect === "postgres") ? '"' : '`' , escapeChar = (dialect === "postgres" || dialect === 'mssql') ? '"' : '`'
var Passports = this.sequelize.define('Passports', { var Passports = this.sequelize.define('Passports', {
isActive: Sequelize.BOOLEAN isActive: Sequelize.BOOLEAN
}) })
......
...@@ -88,7 +88,8 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () { ...@@ -88,7 +88,8 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () {
self.queryInterface.showIndex('Group').complete(function(err, indexes) { self.queryInterface.showIndex('Group').complete(function(err, indexes) {
expect(err).to.be.null expect(err).to.be.null
var indexColumns = _.uniq(indexes.map(function(index) { return index.name })) var indexColumns = _.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).to.include('group_username_is_admin') expect(indexColumns).to.include('group_username_is_admin')
self.queryInterface.removeIndex('Group', ['username', 'isAdmin']).complete(function(err) { self.queryInterface.removeIndex('Group', ['username', 'isAdmin']).complete(function(err) {
...@@ -96,7 +97,6 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () { ...@@ -96,7 +97,6 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () {
self.queryInterface.showIndex('Group').complete(function(err, indexes) { self.queryInterface.showIndex('Group').complete(function(err, indexes) {
expect(err).to.be.null expect(err).to.be.null
indexColumns = _.uniq(indexes.map(function(index) { return index.name })) indexColumns = _.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).to.be.empty expect(indexColumns).to.be.empty
...@@ -124,16 +124,33 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () { ...@@ -124,16 +124,33 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () {
Users.sync({ force: true }).success(function() { Users.sync({ force: true }).success(function() {
self.queryInterface.describeTable('_Users').complete(function(err, metadata) { self.queryInterface.describeTable('_Users').complete(function(err, metadata) {
expect(err).to.be.null expect(err).to.be.null
var username = metadata.username var username = metadata.username
var isAdmin = metadata.isAdmin var isAdmin = metadata.isAdmin
var enumVals = metadata.enumVals var enumVals = metadata.enumVals
expect(username.type).to.equal(dialect === 'postgres' ? 'CHARACTER VARYING' : 'VARCHAR(255)') var assertVal = 'VARCHAR(255)';
switch(dialect){
case 'postgres':
assertVal = 'CHARACTER VARYING';
break;
case 'mssql':
assertVal = 'NVARCHAR';
break;
}
expect(username.type).to.equal(assertVal)
expect(username.allowNull).to.be.true expect(username.allowNull).to.be.true
expect(username.defaultValue).to.be.null expect(username.defaultValue).to.be.null
expect(isAdmin.type).to.equal(dialect === 'postgres' ? 'BOOLEAN' : 'TINYINT(1)') assertVal = 'TINYINT(1)';
switch(dialect){
case 'postgres':
assertVal = 'BOOLEAN';
break;
case 'mssql':
assertVal = 'BIT';
break;
}
expect(isAdmin.type).to.equal(assertVal)
expect(isAdmin.allowNull).to.be.true expect(isAdmin.allowNull).to.be.true
expect(isAdmin.defaultValue).to.be.null expect(isAdmin.defaultValue).to.be.null
...@@ -141,7 +158,6 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () { ...@@ -141,7 +158,6 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () {
expect(enumVals.special).to.be.instanceof(Array) expect(enumVals.special).to.be.instanceof(Array)
expect(enumVals.special).to.have.length(2); expect(enumVals.special).to.have.length(2);
} }
done() done()
}) })
}) })
...@@ -214,7 +230,7 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () { ...@@ -214,7 +230,7 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () {
var self = this var self = this
var Users = self.sequelize.define('User', { var Users = self.sequelize.define('User', {
username: DataTypes.STRING username: DataTypes.STRING
}, { }, {
tableName: 'Users', tableName: 'Users',
schema: 'archive' schema: 'archive'
}) })
...@@ -407,13 +423,13 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () { ...@@ -407,13 +423,13 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () {
var keys = Object.keys(fks[0]), var keys = Object.keys(fks[0]),
keys2 = Object.keys(fks[1]), keys2 = Object.keys(fks[1]),
keys3 = Object.keys(fks[2]) keys3 = Object.keys(fks[2])
if (dialect === "postgres" || dialect === "postgres-native") { if (dialect === "postgres" || dialect === "postgres-native" ) {
expect(keys).to.have.length(6) expect(keys).to.have.length(6)
expect(keys2).to.have.length(7) expect(keys2).to.have.length(7)
expect(keys3).to.have.length(7) expect(keys3).to.have.length(7)
} else if (dialect === "sqlite") { } else if (dialect === "sqlite") {
expect(keys).to.have.length(8) expect(keys).to.have.length(8)
} else if (dialect === "mysql") { } else if (dialect === "mysql" || dialect == 'mssql') {
expect(keys).to.have.length(1) expect(keys).to.have.length(1)
} else { } else {
console.log("This test doesn't support " + dialect) console.log("This test doesn't support " + dialect)
......
...@@ -17,7 +17,7 @@ var chai = require('chai') ...@@ -17,7 +17,7 @@ var chai = require('chai')
chai.config.includeStack = true chai.config.includeStack = true
var qq = function(str) { var qq = function(str) {
if (dialect == 'postgres' || dialect == 'sqlite') { if (dialect == 'postgres' || dialect == 'sqlite' || dialect === 'mssql') {
return '"' + str + '"' return '"' + str + '"'
} else if (Support.dialectIsMySQL()) { } else if (Support.dialectIsMySQL()) {
return '`' + str + '`' return '`' + str + '`'
...@@ -118,7 +118,12 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () { ...@@ -118,7 +118,12 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () {
expect( expect(
err.message.match(/connect ECONNREFUSED/) || err.message.match(/connect ECONNREFUSED/) ||
err.message.match(/invalid port number/) err.message.match(/invalid port number/)
).to.be.ok ).to.be.ok;
} else if (dialect === 'mssql'){
expect(
err.message.match(/ConnectionError: Login failed for user/) ||
err.message.match(/RangeError: Port should be > 0 and < 65536/)
).to.be.ok;
} else { } else {
expect(err.message).to.match(/connect ECONNREFUSED/) expect(err.message).to.match(/connect ECONNREFUSED/)
} }
...@@ -328,7 +333,7 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () { ...@@ -328,7 +333,7 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () {
}) })
it('dot separated attributes when doing a raw query without nest', function(done) { it('dot separated attributes when doing a raw query without nest', function(done) {
var tickChar = (dialect === 'postgres') ? '"' : '`' var tickChar = (dialect === 'postgres' || dialect === 'mssql') ? '"' : '`'
, sql = "select 1 as " + Sequelize.Utils.addTicks('foo.bar.baz', tickChar) , sql = "select 1 as " + Sequelize.Utils.addTicks('foo.bar.baz', tickChar)
this.sequelize.query(sql, null, { raw: true, nest: false }).success(function(result) { this.sequelize.query(sql, null, { raw: true, nest: false }).success(function(result) {
...@@ -338,7 +343,7 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () { ...@@ -338,7 +343,7 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () {
}) })
it('destructs dot separated attributes when doing a raw query using nest', function(done) { it('destructs dot separated attributes when doing a raw query using nest', function(done) {
var tickChar = (dialect === 'postgres') ? '"' : '`' var tickChar = (dialect === 'postgres' || dialect === 'mssql') ? '"' : '`'
, sql = "select 1 as " + Sequelize.Utils.addTicks('foo.bar.baz', tickChar) , sql = "select 1 as " + Sequelize.Utils.addTicks('foo.bar.baz', tickChar)
this.sequelize.query(sql, null, { raw: true, nest: true }).success(function(result) { this.sequelize.query(sql, null, { raw: true, nest: true }).success(function(result) {
...@@ -423,11 +428,16 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () { ...@@ -423,11 +428,16 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () {
}) })
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) { var datetime = (dialect === "sqlite" ? 'date(\'now\')' : 'NOW()');
if (dialect==="mssql") {
datetime = "GETDATE()"
}
this.sequelize.query('SELECT ' + datetime + ' AS t').success(function(result) {
expect(moment(result[0].t).isValid()).to.be.true expect(moment(result[0].t).isValid()).to.be.true
done() done()
}) });
}) });
if (Support.getTestDialect() === 'postgres') { if (Support.getTestDialect() === 'postgres') {
it('replaces named parameters with the passed object and ignores casts', function(done) { it('replaces named parameters with the passed object and ignores casts', function(done) {
...@@ -624,8 +634,10 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () { ...@@ -624,8 +634,10 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () {
'FATAL: role "bar" does not exist', 'FATAL: role "bar" does not exist',
'password authentication failed for user "bar"' 'password authentication failed for user "bar"'
].indexOf(err.message.trim()) !== -1) ].indexOf(err.message.trim()) !== -1)
} else if (dialect === 'mssql') {
expect(err.message).to.match(/.*ECONNREFUSED.*/);
} else { } else {
expect(err.message.toString()).to.match(/.*Access\ denied.*/) expect(err.message.toString()).to.match(/.*Access\ denied.*/);
} }
done() done()
}) })
...@@ -1127,4 +1139,4 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () { ...@@ -1127,4 +1139,4 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () {
}); });
}); });
}); });
}); });
\ No newline at end of file
...@@ -19,10 +19,14 @@ if (dialect !== 'sqlite') { ...@@ -19,10 +19,14 @@ if (dialect !== 'sqlite') {
}); });
it('returns the same value for current timestamp', function () { it('returns the same value for current timestamp', function () {
var now = 'now()'; var now = 'now()'
var query = "SELECT " + now + " as now"; , startQueryTime = Date.now();
var startQueryTime = Date.now();
if (dialect === 'mssql') {
now = 'GETDATE()';
}
var query = "SELECT " + now + " as now";
return Promise.all([ return Promise.all([
this.sequelize.query(query), this.sequelize.query(query),
this.sequelizeWithTimezone.query(query) this.sequelizeWithTimezone.query(query)
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!