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

Commit 66dc7694 by Jan Aagaard Meier

Merge pull request #2057 from sequelize/indexes

Support for specifying indexes on a model
2 parents 2c5b46e9 779dc404
......@@ -3,6 +3,8 @@ Notice: All 1.7.x changes are present in 2.0.x aswell
# Next
- [FEATURE] Added to option of setting a timezone offset in the sequelize constructor (`timezone` option). This timezone is used when initializing a connection (using `SET TIME ZONE` or equivalent), and when converting a timestamp string from the DB to a JS date with mysql (postgres stores the timezone, so for postgres we rely on what's in the DB).
- [FEATURE] Allow setting plural and singular name on the model (`options.name` in `sequelize.define`) and in associations (`options.as`) to circumvent issues with weird pluralization.
- [FEATURE] Added support for passing an `indexes` array in options to `sequelize.define`. [#1485](https://github.com/sequelize/sequelize/issues/1485). See API reference for details.
- [FEATURE/INTERNALS] Standardized the output from `QueryInterface.showIndex`.
- [FEATURE] Include deleted rows in find [#2083](https://github.com/sequelize/sequelize/pull/2083)
- [BUG] Hid `dottie.transform` on raw queries behind a flag (`nest`) [#2064](https://github.com/sequelize/sequelize/pull/2064)
- [BUG] Fixed problems with transcation parameter being removed / not passed on in associations [#1789](https://github.com/sequelize/sequelize/issues/1789) and [#1968](https://github.com/sequelize/sequelize/issues/1968)
......
......@@ -10,7 +10,15 @@ AbstractDialect.prototype.supports = {
'DEFAULT VALUES': false,
'VALUES ()': false,
'LIMIT ON UPDATE': false,
schemas: false
schemas: false,
index: {
collate: true,
length: false,
parser: false,
concurrently: false,
type: false,
using: true,
},
};
module.exports = AbstractDialect;
\ No newline at end of file
module.exports = AbstractDialect;
......@@ -3,7 +3,8 @@
var Utils = require('../../utils')
, SqlString = require('../../sql-string')
, Model = require('../../model')
, _ = require('lodash');
, _ = require('lodash')
, util = require('util');
module.exports = (function() {
var QueryGenerator = {
......@@ -321,7 +322,7 @@ module.exports = (function() {
/*
Returns an add index query.
Parameters:
- tableName -> Name of an existing table.
- tableName -> Name of an existing table, possibly with schema.
- attributes:
An array of attributes as string or as hash.
If the attribute is a hash, it must have the following content:
......@@ -332,9 +333,72 @@ module.exports = (function() {
- indicesType: UNIQUE|FULLTEXT|SPATIAL
- indexName: The name of the index. Default is <tableName>_<attrName1>_<attrName2>
- parser
- rawTablename, the name of the table, without schema. Used to create the name of the index
*/
addIndexQuery: function(tableName, attributes, options) {
throwMethodUndefined('addIndexQuery');
addIndexQuery: function(tableName, attributes, options, rawTablename) {
options = options || {};
var transformedAttributes = attributes.map(function(attribute) {
if (typeof attribute === 'string') {
return this.quoteIdentifier(attribute);
} else {
var result = '';
if (!attribute.attribute) {
throw new Error('The following index attribute has no attribute: ' + util.inspect(attribute));
}
result += this.quoteIdentifier(attribute.attribute);
if (this._dialect.supports.index.collate && attribute.collate) {
result += ' COLLATE ' + this.quoteIdentifier(attribute.collate);
}
if (this._dialect.supports.index.length && attribute.length) {
result += '(' + attribute.length + ')';
}
if (attribute.order) {
result += ' ' + attribute.order;
}
return result;
}
}.bind(this));
var onlyAttributeNames = attributes.map(function(attribute) {
return (typeof attribute === 'string') ? attribute : attribute.attribute;
}.bind(this));
options = Utils._.defaults(options, {
type: '',
indicesType: options.type || '',
indexType: options.method || undefined,
indexName: options.name || Utils.inflection.underscore(rawTablename + '_' + onlyAttributeNames.join('_')),
parser: null
});
if (options.indicesType.toLowerCase() === 'unique') {
options.unique = true;
delete options.indicesType;
}
if (!this._dialect.supports.index.type) {
delete options.indicesType;
}
return Utils._.compact([
'CREATE',
options.unique ? 'UNIQUE' : '',
options.indicesType, 'INDEX',
this._dialect.supports.index.concurrently && options.concurrently ? 'CONCURRENTLY' : undefined,
this.quoteIdentifiers(options.indexName),
this._dialect.supports.index.using === 1 && options.indexType ? 'USING ' + options.indexType : '',
'ON', this.quoteIdentifiers(tableName),
this._dialect.supports.index.using === 2 && options.indexType ? 'USING ' + options.indexType : '',
'(' + transformedAttributes.join(', ') + ')',
(this._dialect.supports.index.parser && options.parser ? 'WITH PARSER ' + options.parser : undefined)
]).join(' ');
},
/*
......@@ -676,6 +740,7 @@ module.exports = (function() {
mainAttributes = [mainTableAs + '.*'];
}
if (options.include) {
var generateJoinQueries = function(include, parentTable) {
var table = include.model.getTableName()
......@@ -946,7 +1011,6 @@ module.exports = (function() {
mainQueryItems.push(' HAVING ' + options.having);
}
}
// Add ORDER to sub or main query
if (options.order) {
var mainQueryOrder = [];
......
......@@ -86,16 +86,8 @@ module.exports = (function() {
defaultValue: _result.Default
};
});
} else if (this.sql.toLowerCase().indexOf('show index from') === 0) {
result = Utils._.uniq(result.map(function(result) {
return {
name: result.Key_name,
tableName: result.Table,
unique: (result.Non_unique !== 1)
};
}), false, function(row) {
return row.name;
});
} else if (this.isShowIndexesQuery()) {
result = this.handleShowIndexesQuery(data);
}
} else if (this.isCallQuery()) {
result = data[0];
......
......@@ -11,9 +11,7 @@ var MariaDialect = function(sequelize) {
this.connectionManager.initPools();
};
MariaDialect.prototype = _.defaults({
'LIMIT ON UPDATE': true
}, MySQL.prototype);
MariaDialect.prototype = MySQL.prototype;
MariaDialect.prototype.Query = Query;
......
'use strict';
var Utils = require('../../utils')
, AbstractQuery = require('../abstract/query');
, MysqlQuery = require('../mysql/query');
module.exports = (function() {
var Query = function(client, sequelize, callee, options) {
......@@ -22,7 +22,7 @@ module.exports = (function() {
});
};
Utils.inherit(Query, AbstractQuery);
Utils.inherit(Query, MysqlQuery);
Query.prototype.run = function(sql) {
this.sql = sql;
......
......@@ -11,12 +11,19 @@ var MysqlDialect = function(sequelize) {
this.connectionManager.initPools();
};
MysqlDialect.prototype.supports = _.defaults({
MysqlDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.supports), {
'VALUES ()': true,
'LIMIT ON UPDATE': true,
lock: true,
forShare: 'LOCK IN SHARE MODE'
}, Abstract.prototype.supports);
forShare: 'LOCK IN SHARE MODE',
index: {
collate: false,
length: true,
parser: true,
type: true,
using: 1,
}
});
MysqlDialect.prototype.Query = Query;
......
......@@ -221,49 +221,6 @@ module.exports = (function() {
return 'DELETE FROM ' + table + ' WHERE ' + where + limit;
},
addIndexQuery: function(tableName, attributes, options) {
var transformedAttributes = attributes.map(function(attribute) {
if (typeof attribute === 'string') {
return this.quoteIdentifier(attribute);
} else {
var result = '';
if (!attribute.attribute) {
throw new Error('The following index attribute has no attribute: ' + util.inspect(attribute));
}
result += this.quoteIdentifier(attribute.attribute);
if (attribute.length) {
result += '(' + attribute.length + ')';
}
if (attribute.order) {
result += ' ' + attribute.order;
}
return result;
}
}.bind(this));
var onlyAttributeNames = attributes.map(function(attribute) {
return (typeof attribute === 'string') ? attribute : attribute.attribute;
}.bind(this));
options = Utils._.extend({
indicesType: null,
indexName: Utils.inflection.underscore(tableName + '_' + onlyAttributeNames.join('_')),
parser: null
}, options || {});
return Utils._.compact([
'CREATE', options.indicesType, 'INDEX', options.indexName,
(options.indexType ? ('USING ' + options.indexType) : undefined),
'ON', this.quoteIdentifiers(tableName), '(' + transformedAttributes.join(', ') + ')',
(options.parser ? 'WITH PARSER ' + options.parser : undefined)
]).join(' ');
},
showIndexQuery: function(tableName, options) {
var sql = 'SHOW INDEX FROM <%= tableName %><%= options %>';
return Utils._.template(sql)({
......
......@@ -46,5 +46,39 @@ module.exports = (function() {
return promise;
};
Query.prototype.isShowIndexesQuery = function () {
return this.sql.toLowerCase().indexOf('show index from') === 0;
};
Query.prototype.handleShowIndexesQuery = function (data) {
// Group by index name, and collect all fields
data = Utils._.foldl(data, function (acc, item) {
if (!(item.Key_name in acc)) {
acc[item.Key_name] = item;
item.fields = [];
}
acc[item.Key_name].fields[item.Seq_in_index - 1] = {
attribute: item.Column_name,
length: item.Sub_part || undefined,
order: item.Collation === 'A' ? 'ASC' : undefined
};
delete item.column_name;
return acc;
}, {});
return Utils._.map(data, function(item) {
return {
primary: item.Key_name === 'PRIMARY',
fields: item.fields,
name: item.Key_name,
tableName: item.Table,
unique: (item.Non_unique !== 1),
type: item.Index_type,
};
});
};
return Query;
})();
......@@ -11,13 +11,17 @@ var PostgresDialect = function(sequelize) {
this.connectionManager.initPools();
};
PostgresDialect.prototype.supports = _.defaults({
PostgresDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.supports), {
'RETURNING': true,
'DEFAULT VALUES': true,
schemas: true,
lock: true,
forShare: 'FOR SHARE'
}, Abstract.prototype.supports);
forShare: 'FOR SHARE',
index: {
concurrently: true,
using: 2,
}
});
PostgresDialect.prototype.Query = Query;
......
......@@ -352,51 +352,10 @@ module.exports = (function() {
return Utils._.template(query)(replacements);
},
addIndexQuery: function(tableName, attributes, options) {
var transformedAttributes = attributes.map(function(attribute) {
if (typeof attribute === 'string') {
return this.quoteIdentifier(attribute);
} else {
var result = '';
if (!attribute.attribute) {
throw new Error('The following index attribute has no attribute: ' + util.inspect(attribute));
}
result += this.quoteIdentifier(attribute.attribute);
if (attribute.length) {
result += '(' + attribute.length + ')';
}
if (attribute.order) {
result += ' ' + attribute.order;
}
return result;
}
}.bind(this));
var onlyAttributeNames = attributes.map(function(attribute) {
return (typeof attribute === 'string') ? attribute : attribute.attribute;
}.bind(this));
var indexTable = tableName.split('.');
options = Utils._.extend({
indicesType: null,
indexName: Utils.inflection.underscore(indexTable[indexTable.length - 1] + '_' + onlyAttributeNames.join('_')),
parser: null
}, options || {});
return Utils._.compact([
'CREATE', options.indicesType, 'INDEX', this.quoteIdentifiers(options.indexName),
'ON', this.quoteIdentifiers(tableName), (options.indexType ? ('USING ' + options.indexType) : undefined),
'(' + transformedAttributes.join(', ') + ')'
]).join(' ');
},
showIndexQuery: function(tableName, options) {
var query = "SELECT relname FROM pg_class WHERE oid IN ( SELECT indexrelid FROM pg_index, pg_class WHERE pg_class.relname='<%= tableName %>' AND pg_class.oid=pg_index.indrelid);";
// This is ARCANE!
var query = "SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND a.attnum = ANY(ix.indkey) AND t.relkind = 'r' and t.relname = '<%= tableName %>' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;";
return Utils._.template(query)({ tableName: tableName });
},
......
......@@ -91,7 +91,36 @@ module.exports = (function() {
return results;
}
if (self.isSelectQuery()) {
if (self.isShowIndexesQuery()) {
results.forEach(function (result) {
var attributes = /ON .*? (?:USING .*?\s)?\((.*)\)/gi.exec(result.definition)[1].split(',')
, field
, attribute
, columns;
// Map column index in table to column name
columns = Utils._.zipObject(
result.column_indexes,
self.sequelize.queryInterface.QueryGenerator.fromArray(result.column_names)
);
delete result.column_indexes;
delete result.column_names;
// Indkey is the order of attributes in the index, specified by a string of attribute indexes
result.fields = result.indkey.split(' ').map(function (indKey, index) {
field = columns[indKey];
attribute = attributes[index];
return {
attribute: field,
collate: attribute.match(/COLLATE "(.*?)"/) ? /COLLATE "(.*?)"/.exec(attribute)[1] : undefined,
order: attribute.indexOf('DESC') !== -1 ? 'DESC' : attribute.indexOf('ASC') !== -1 ? 'ASC': undefined,
length: undefined,
};
});
delete result.columns;
});
return results;
} else if (self.isSelectQuery()) {
if (self.sql.toLowerCase().indexOf('select c.column_name') === 0) {
result = {};
......@@ -192,6 +221,10 @@ module.exports = (function() {
return this;
};
Query.prototype.isShowIndexesQuery = function () {
return this.sql.indexOf('pg_get_indexdef') !== -1;
};
Query.prototype.getInsertIdField = function() {
return 'id';
};
......
......@@ -10,10 +10,13 @@ var SqliteDialect = function(sequelize) {
this.connectionManager = new ConnectionManager(this, sequelize);
};
SqliteDialect.prototype.supports = _.defaults({
SqliteDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.supports), {
'DEFAULT': false,
'DEFAULT VALUES': true
}, Abstract.prototype.supports);
'DEFAULT VALUES': true,
index: {
using: false
}
});
SqliteDialect.prototype.Query = Query;
SqliteDialect.prototype.name = 'sqlite';
......
......@@ -3,7 +3,8 @@
var Utils = require('../../utils')
, DataTypes = require('../../data-types')
, SqlString = require('../../sql-string')
, Transaction = require('../../transaction');
, Transaction = require('../../transaction')
, util = require('util');
var MySqlQueryGenerator = Utils._.extend(
Utils._.clone(require('../abstract/query-generator')),
......
......@@ -97,14 +97,9 @@ module.exports = (function() {
} else if (self.isShowOrDescribeQuery()) {
result = results;
} else if (self.sql.indexOf('PRAGMA INDEX_LIST') !== -1) {
// this is the sqlite way of getting the indexes of a table
result = results.map(function(result) {
return {
name: result.name,
tableName: result.name.split('_')[0],
unique: (result.unique === 0)
};
});
result = self.handleShowIndexesQuery(results);
} else if (self.sql.indexOf('PRAGMA INDEX_INFO') !== -1) {
result = results;
} else if (self.sql.indexOf('PRAGMA TABLE_INFO') !== -1) {
// this is the sqlite way of getting the metadata of a table
result = {};
......@@ -159,7 +154,7 @@ module.exports = (function() {
if (tableName !== 'sqlite_master') {
return new Utils.Promise(function(resolve) {
// get the column types
self.database.all('PRAGMA table_info(' + tableName + ')', function(err, results) {
self.database.all('PRAGMA table_info(`' + tableName + '`)', function(err, results) {
if (!err) {
for (var i = 0, l = results.length; i < l; i++) {
columnTypes[tableName + '.' + results[i].name] = columnTypes[results[i].name] = results[i].type;
......@@ -178,6 +173,29 @@ module.exports = (function() {
});
};
Query.prototype.handleShowIndexesQuery = function (data) {
var self = this;
// Sqlite returns indexes so the one that was defined last is returned first. Lets reverse that!
return this.sequelize.Promise.map(data.reverse(), function (item) {
item.fields = [];
item.primary = false;
item.unique = !!item.unique;
return self.run('PRAGMA INDEX_INFO(' + item.name + ')').then(function (columns) {
columns.forEach(function (column) {
item.fields[column.seqno] = {
attribute: column.name,
length: undefined,
order: undefined,
};
});
return item;
});
});
};
Query.prototype.getDatabaseMethod = function() {
if (this.isInsertQuery() || this.isUpdateQuery() || (this.sql.toLowerCase().indexOf('CREATE TEMPORARY TABLE'.toLowerCase()) !== -1) || this.options.type === QueryTypes.BULKDELETE) {
return 'run';
......
......@@ -389,6 +389,10 @@ module.exports = (function() {
}
}).then(function () {
return self.QueryInterface.createTable(self.getTableName(options), attributes, options);
}).then(function () {
return Promise.map(self.options.indexes, function (index) {
return self.QueryInterface.addIndex(self.getTableName(options), index.fields, index, self.tableName);
});
}).return(this);
};
......
......@@ -199,7 +199,6 @@ module.exports = (function() {
};
var skip = options.skip || [];
return self.showAllTables().then(function(tableNames) {
if (self.sequelize.options.dialect === 'sqlite') {
return self.sequelize.query('PRAGMA foreign_keys;').then(function(result) {
......@@ -374,8 +373,29 @@ module.exports = (function() {
}.bind(this));
};
QueryInterface.prototype.addIndex = function(tableName, attributes, options) {
var sql = this.QueryGenerator.addIndexQuery(tableName, attributes, options);
QueryInterface.prototype.addIndex = function(tableName, _attributes, _options, _rawTablename) {
var attributes, options, rawTablename;
// Support for passing tableName, attributes, options or tableName, options (with a fields param which is the attributes)
if (Array.isArray(_attributes)) {
attributes = _attributes;
options = _options;
rawTablename = _rawTablename;
} else {
// Support for passing an options object with a fields attribute instead of attributes, options
options = _attributes;
attributes = options.fields;
rawTablename = _options;
}
if (!rawTablename) {
// Map for backwards compat
rawTablename = tableName;
}
var sql = this.QueryGenerator.addIndexQuery(tableName, attributes, options, rawTablename);
return this.sequelize.query(sql, null, {logging: this.sequelize.options.logging});
};
......
......@@ -351,6 +351,13 @@ module.exports = (function() {
* @param {Object} [options.name] An object with two attributes, `singular` and `plural`, which are used when this model is associated to others.
* @param {String} [options.name.singular=inflection.singularize(modelName)]
* @param {String} [options.name.plural=inflection.pluralize(modelName)]
* @param {Array<Object>} [options.indexes]
* @param {String} [options.indexes[].name] The name of the index. Defaults to model name + _ + fields concatenated
* @param {String} [options.indexes[].type] Index type. Only used by mysql. One of `UNIQUE`, `FULLTEXT` and `SPATIAL`
* @param {String} [options.indexes[].method] The method to create the index by (`USING` statement in SQL). BTREE and HASH are supported by mysql and postgres, and postgres additionally supports GIST and GIN.
* @param {Boolean} [options.indexes[].unique=false] Should the index by unique? Can also be triggered by setting type to `UNIQUE`
* @param {Boolean} [options.indexes[].concurrently=false] PostgreSQL will build the index without taking any write locks. Postgres only
* @param {Array<String|Object} [options.indexes[].fields] An array of the fields to index. Each field can either be a string containing the name of the field, or an object with the following attributes: `attribute` (field name), `length` (create a prefix index of length chars), `order` (the direction the column should be sorted in), `collate` (the collation (sort order) for the column)
* @param {String|Boolean} [options.createdAt] Override the name of the createdAt column if a string is provided, or disable it if false. Timestamps must be true
* @param {String|Boolean} [options.updatedAt] Override the name of the updatedAt column if a string is provided, or disable it if false. Timestamps must be true
* @param {String|Boolean} [options.deletedAt] Override the name of the deletedAt column if a string is provided, or disable it if false. Timestamps must be true
......@@ -388,7 +395,8 @@ module.exports = (function() {
name: {
plural: Utils.inflection.pluralize(modelName),
singular: Utils.inflection.singularize(modelName)
}
},
indexes: [],
}, options);
options.omitNull = globalOptions.omitNull;
......
......@@ -1297,7 +1297,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
});
it('makes join table non-paranoid by default', function () {
var paranoidSequelize = new Sequelize('','','', {
var paranoidSequelize = Support.createSequelizeInstance({
define: {
paranoid: true
}
......
......@@ -328,6 +328,89 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
})
})
it('should allow the user to specify indexes in options', function () {
var Model = this.sequelize.define('model', {
fieldA: Sequelize.STRING,
fieldB: Sequelize.INTEGER,
fieldC: Sequelize.STRING
}, {
indexes: [
{
name: 'a_b_uniq',
unique: true,
method: 'BTREE',
fields: ['fieldB', {attribute:'fieldA', collate: dialect === 'sqlite' ? 'RTRIM' : 'en_US', order: 'DESC', length: 5}]
},
{
type: 'FULLTEXT',
fields: ['fieldC'],
concurrently: true
},
],
engine: 'MyISAM'
})
return this.sequelize.sync().bind(this).then(function () {
return this.sequelize.queryInterface.showIndex(Model.tableName);
}).spread(function () {
var primary, idx1, idx2;
if (dialect === 'sqlite') {
// PRAGMA index_info does not return the primary index
idx1 = arguments[0];
idx2 = arguments[1];
expect(idx1.fields).to.deep.equal([
{ attribute: 'fieldB', length: undefined, order: undefined},
{ attribute: 'fieldA', length: undefined, order: undefined},
]);
expect(idx2.fields).to.deep.equal([
{ attribute: 'fieldC', length: undefined, order: undefined}
]);
} else if (dialect === 'postgres') {
// Postgres returns indexes in alphabetical order
primary = arguments[2];
idx1 = arguments[0];
idx2 = arguments[1];
expect(idx1.fields).to.deep.equal([
{ attribute: 'fieldB', length: undefined, order: undefined, collate: undefined},
{ attribute: 'fieldA', length: undefined, order: 'DESC', collate: 'en_US'},
]);
expect(idx2.fields).to.deep.equal([
{ attribute: 'fieldC', length: undefined, order: undefined, collate: undefined}
]);
} else {
// And finally mysql returns the primary first, and then the rest in the order they were defined
primary = arguments[0];
idx1 = arguments[1];
idx2 = arguments[2];
expect(primary.primary).to.be.ok;
expect(idx1.type).to.equal('BTREE');
expect(idx2.type).to.equal('FULLTEXT');
expect(idx1.fields).to.deep.equal([
{ attribute: 'fieldB', length: undefined, order: 'ASC'},
{ attribute: 'fieldA', length: 5, order: 'ASC'},
]);
expect(idx2.fields).to.deep.equal([
{ attribute: 'fieldC', length: undefined, order: undefined}
]);
}
expect(idx1.name).to.equal('a_b_uniq');
expect(idx1.unique).to.be.ok;
expect(idx2.name).to.equal('models_field_c');
expect(idx2.unique).not.to.be.ok;
});
});
})
describe('build', function() {
......
"use strict";
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
......@@ -186,7 +188,7 @@ if (Support.dialectIsMySQL()) {
arguments: ['myTable', function (sequelize) {
return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
}
};
}],
expectation: "SELECT * FROM `myTable` ORDER BY f1(f2(`id`)) DESC;",
context: QueryGenerator,
......@@ -199,7 +201,7 @@ if (Support.dialectIsMySQL()) {
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f2', 12, 'lalala', new Date(Date.UTC(2011, 2, 27, 10, 1, 55))), 'ASC']
]
}
};
}],
expectation: "SELECT * FROM `myTable` ORDER BY f1(`myTable`.`id`) DESC, f2(12, 'lalala', '2011-03-27 10:01:55') ASC;",
context: QueryGenerator,
......@@ -218,7 +220,7 @@ if (Support.dialectIsMySQL()) {
arguments: ['myTable', function (sequelize) {
return {
group: [sequelize.fn('YEAR', sequelize.col('createdAt'))]
}
};
}],
expectation: "SELECT * FROM `myTable` GROUP BY YEAR(`createdAt`);",
context: QueryGenerator,
......@@ -228,7 +230,7 @@ if (Support.dialectIsMySQL()) {
arguments: ['myTable', function (sequelize) {
return {
group: [sequelize.fn('YEAR', sequelize.col('createdAt')), 'title']
}
};
}],
expectation: "SELECT * FROM `myTable` GROUP BY YEAR(`createdAt`), `title`;",
context: QueryGenerator,
......@@ -244,7 +246,7 @@ if (Support.dialectIsMySQL()) {
attributes: ['*', [sequelize.fn('YEAR', sequelize.col('createdAt')), 'creationYear']],
group: ['creationYear', 'title'],
having: ['creationYear > ?', 2002]
}
};
}],
expectation: "SELECT *, YEAR(`createdAt`) AS `creationYear` FROM `myTable` GROUP BY `creationYear`, `title` HAVING creationYear > 2002;",
context: QueryGenerator,
......@@ -256,7 +258,7 @@ if (Support.dialectIsMySQL()) {
attributes: ['*', [sequelize.fn('YEAR', sequelize.col('createdAt')), 'creationYear']],
group: ['creationYear', 'title'],
having: { creationYear: { gt: 2002 } }
}
};
}],
expectation: "SELECT *, YEAR(`createdAt`) AS `creationYear` FROM `myTable` GROUP BY `creationYear`, `title` HAVING `creationYear` > 2002;",
context: QueryGenerator,
......@@ -353,7 +355,7 @@ if (Support.dialectIsMySQL()) {
arguments: ['myTable', function (sequelize) {
return {
foo: sequelize.fn('NOW')
}
};
}],
expectation: "INSERT INTO `myTable` (`foo`) VALUES (NOW());",
needsSequelize: true
......@@ -431,7 +433,7 @@ if (Support.dialectIsMySQL()) {
arguments: ['myTable', function (sequelize) {
return {
bar: sequelize.fn('NOW')
}
};
}, {name: 'foo'}],
expectation: "UPDATE `myTable` SET `bar`=NOW() WHERE `name`='foo'",
needsSequelize: true
......@@ -439,7 +441,7 @@ if (Support.dialectIsMySQL()) {
arguments: ['myTable', function (sequelize) {
return {
bar: sequelize.col('foo')
}
};
}, {name: 'foo'}],
expectation: "UPDATE `myTable` SET `bar`=`foo` WHERE `name`='foo'",
needsSequelize: true
......@@ -473,27 +475,42 @@ if (Support.dialectIsMySQL()) {
addIndexQuery: [
{
arguments: ['User', ['username', 'isAdmin']],
expectation: 'CREATE INDEX user_username_is_admin ON `User` (`username`, `isAdmin`)'
arguments: ['User', ['username', 'isAdmin'], {}, 'User'],
expectation: 'CREATE INDEX `user_username_is_admin` ON `User` (`username`, `isAdmin`)'
}, {
arguments: [
'User', [
{ attribute: 'username', length: 10, order: 'ASC'},
'isAdmin'
]
],
{},
'User'
],
expectation: "CREATE INDEX user_username_is_admin ON `User` (`username`(10) ASC, `isAdmin`)"
expectation: "CREATE INDEX `user_username_is_admin` ON `User` (`username`(10) ASC, `isAdmin`)"
}, {
arguments: [
'User', ['username', 'isAdmin'], { parser: 'foo', indicesType: 'FULLTEXT', indexName: 'bar'}
'User', ['username', 'isAdmin'], { parser: 'foo', indicesType: 'FULLTEXT', indexName: 'bar'}, 'User'
],
expectation: "CREATE FULLTEXT INDEX bar ON `User` (`username`, `isAdmin`) WITH PARSER foo"
expectation: "CREATE FULLTEXT INDEX `bar` ON `User` (`username`, `isAdmin`) WITH PARSER foo"
}, {
arguments: [
'User', ['username', 'isAdmin'], { indicesType: 'UNIQUE'}
'User', ['username', 'isAdmin'], { indicesType: 'UNIQUE'}, 'User'
],
expectation: "CREATE UNIQUE INDEX user_username_is_admin ON `User` (`username`, `isAdmin`)"
}
expectation: "CREATE UNIQUE INDEX `user_username_is_admin` ON `User` (`username`, `isAdmin`)"
}, {
arguments: ['User', ['fieldB', {attribute:'fieldA', collate: 'en_US', order: 'DESC', length: 5}], {
name: 'a_b_uniq',
unique: true,
method: 'BTREE'
}, 'User'],
expectation: 'CREATE UNIQUE INDEX `a_b_uniq` USING BTREE ON `User` (`fieldB`, `fieldA`(5) DESC)'
}, {
arguments: ['User', ['fieldC'], {
type: 'FULLTEXT',
concurrently: true
}, 'User'],
expectation: 'CREATE FULLTEXT INDEX `user_field_c` ON `User` (`fieldC`)'
},
],
showIndexQuery: [
......@@ -547,26 +564,25 @@ if (Support.dialectIsMySQL()) {
expectation: "`birthday` IN ('2011-07-01 10:01:55','2013-07-02 10:01:22')"
}
]
}
};
_.each(suites, function(tests, suiteTitle) {
describe(suiteTitle, function() {
tests.forEach(function(test) {
var title = test.title || 'MySQL correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments)
it(title, function(done) {
var title = test.title || 'MySQL correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments);
it(title, function() {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
var context = test.context || {options: {}};
if (test.needsSequelize) {
test.arguments[1] = test.arguments[1](this.sequelize)
test.arguments[1] = test.arguments[1](this.sequelize);
}
QueryGenerator.options = context.options
QueryGenerator._dialect = this.sequelize.dialect
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).to.deep.equal(test.expectation)
done()
})
})
})
})
})
QueryGenerator.options = context.options;
QueryGenerator._dialect = this.sequelize.dialect;
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments);
expect(conditions).to.deep.equal(test.expectation);
});
});
});
});
});
}
"use strict";
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
......@@ -7,23 +9,21 @@ var chai = require('chai')
, DataTypes = require(__dirname + "/../../lib/data-types")
, moment = require('moment')
, util = require("util")
, _ = require('lodash')
, _ = require('lodash');
chai.config.includeStack = true
chai.config.includeStack = true;
if (dialect.match(/^postgres/)) {
describe('[POSTGRES Specific] QueryGenerator', function() {
beforeEach(function(done) {
beforeEach(function() {
this.User = this.sequelize.define('User', {
username: DataTypes.STRING,
email: {type: DataTypes.ARRAY(DataTypes.TEXT)},
numbers: {type: DataTypes.ARRAY(DataTypes.FLOAT)},
document: {type: DataTypes.HSTORE, defaultValue: '"default"=>"value"'}
})
this.User.sync({ force: true }).success(function() {
done()
})
})
});
return this.User.sync({ force: true });
});
var suites = {
attributesToSQL: [
......@@ -274,7 +274,7 @@ if (dialect.match(/^postgres/)) {
arguments: ['myTable', function (sequelize) {
return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
}
};
}],
expectation: 'SELECT * FROM "myTable" ORDER BY f1(f2("id")) DESC;',
context: QueryGenerator,
......@@ -287,7 +287,7 @@ if (dialect.match(/^postgres/)) {
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f2', 12, 'lalala', new Date(Date.UTC(2011, 2, 27, 10, 1, 55))), 'ASC']
]
}
};
}],
expectation: 'SELECT * FROM "myTable" ORDER BY f1("myTable"."id") DESC, f2(12, \'lalala\', \'2011-03-27 10:01:55.000 +00:00\') ASC;',
context: QueryGenerator,
......@@ -304,7 +304,7 @@ if (dialect.match(/^postgres/)) {
arguments: ['myTable', function (sequelize) {
return {
group: [sequelize.fn('YEAR', sequelize.col('createdAt'))]
}
};
}],
expectation: "SELECT * FROM \"myTable\" GROUP BY YEAR(\"createdAt\");",
needsSequelize: true
......@@ -313,7 +313,7 @@ if (dialect.match(/^postgres/)) {
arguments: ['myTable', function (sequelize) {
return {
group: [sequelize.fn('YEAR', sequelize.col('createdAt')), 'title']
}
};
}],
expectation: "SELECT * FROM \"myTable\" GROUP BY YEAR(\"createdAt\"), \"title\";",
context: QueryGenerator,
......@@ -328,7 +328,7 @@ if (dialect.match(/^postgres/)) {
attributes: ['*', [sequelize.fn('YEAR', sequelize.col('createdAt')), 'creationYear']],
group: ['creationYear', 'title'],
having: ['creationYear > ?', 2002]
}
};
}],
expectation: "SELECT *, YEAR(\"createdAt\") AS \"creationYear\" FROM \"myTable\" GROUP BY \"creationYear\", \"title\" HAVING creationYear > 2002;",
context: QueryGenerator,
......@@ -340,7 +340,7 @@ if (dialect.match(/^postgres/)) {
attributes: ['*', [sequelize.fn('YEAR', sequelize.col('createdAt')), 'creationYear']],
group: ['creationYear', 'title'],
having: { creationYear: { gt: 2002 } }
}
};
}],
expectation: "SELECT *, YEAR(\"createdAt\") AS \"creationYear\" FROM \"myTable\" GROUP BY \"creationYear\", \"title\" HAVING \"creationYear\" > 2002;",
context: QueryGenerator,
......@@ -505,7 +505,7 @@ if (dialect.match(/^postgres/)) {
arguments: ['myTable', function (sequelize) {
return {
foo: sequelize.fn('NOW')
}
};
}],
expectation: "INSERT INTO \"myTable\" (\"foo\") VALUES (NOW());",
needsSequelize: true
......@@ -698,7 +698,7 @@ if (dialect.match(/^postgres/)) {
arguments: ['myTable', function (sequelize) {
return {
bar: sequelize.fn('NOW')
}
};
}, {name: 'foo'}],
expectation: "UPDATE \"myTable\" SET \"bar\"=NOW() WHERE \"name\"='foo'",
needsSequelize: true
......@@ -706,7 +706,7 @@ if (dialect.match(/^postgres/)) {
arguments: ['myTable', function (sequelize) {
return {
bar: sequelize.col('foo')
}
};
}, {name: 'foo'}],
expectation: "UPDATE \"myTable\" SET \"bar\"=\"foo\" WHERE \"name\"='foo'",
needsSequelize: true
......@@ -827,29 +827,42 @@ if (dialect.match(/^postgres/)) {
addIndexQuery: [
{
arguments: ['User', ['username', 'isAdmin']],
arguments: ['User', ['username', 'isAdmin'], {}, 'User'],
expectation: 'CREATE INDEX \"user_username_is_admin\" ON \"User\" (\"username\", \"isAdmin\")'
}, {
arguments: [
'User', [
{ attribute: 'username', length: 10, order: 'ASC'},
'isAdmin'
]
],
{},
'User'
],
expectation: "CREATE INDEX \"user_username_is_admin\" ON \"User\" (\"username\"(10) ASC, \"isAdmin\")"
expectation: "CREATE INDEX \"user_username_is_admin\" ON \"User\" (\"username\" ASC, \"isAdmin\")"
}, {
arguments: [
'User', ['username', 'isAdmin'], { indicesType: 'FULLTEXT', indexName: 'bar'}
],
expectation: "CREATE FULLTEXT INDEX \"bar\" ON \"User\" (\"username\", \"isAdmin\")"
arguments: ['User', ['username', 'isAdmin'], { indexName: 'bar'}, {}, 'User'],
expectation: "CREATE INDEX \"bar\" ON \"User\" (\"username\", \"isAdmin\")"
}, {
arguments: ['mySchema.User', ['username', 'isAdmin']],
arguments: ['mySchema.User', ['username', 'isAdmin'], {}, 'User'],
expectation: 'CREATE INDEX \"user_username_is_admin\" ON \"mySchema\".\"User\" (\"username\", \"isAdmin\")'
}, {
arguments: ['User', ['fieldB', {attribute:'fieldA', collate: 'en_US', order: 'DESC', length: 5}], {
name: 'a_b_uniq',
unique: true,
method: 'BTREE'
}, 'User'],
expectation: 'CREATE UNIQUE INDEX "a_b_uniq" ON "User" USING BTREE ("fieldB", "fieldA" COLLATE "en_US" DESC)'
}, {
arguments: ['User', ['fieldC'], {
type: 'FULLTEXT',
concurrently: true
}, 'User'],
expectation: 'CREATE INDEX CONCURRENTLY "user_field_c" ON "User" ("fieldC")'
},
// Variants when quoteIdentifiers is false
{
arguments: ['User', ['username', 'isAdmin']],
arguments: ['User', ['username', 'isAdmin'], {}, 'User'],
expectation: 'CREATE INDEX user_username_is_admin ON User (username, isAdmin)',
context: {options: {quoteIdentifiers: false}}
}, {
......@@ -857,34 +870,23 @@ if (dialect.match(/^postgres/)) {
'User', [
{ attribute: 'username', length: 10, order: 'ASC'},
'isAdmin'
]
],
{},
'User'
],
expectation: "CREATE INDEX user_username_is_admin ON User (username(10) ASC, isAdmin)",
expectation: "CREATE INDEX user_username_is_admin ON User (username ASC, isAdmin)",
context: {options: {quoteIdentifiers: false}}
}, {
arguments: [
'User', ['username', 'isAdmin'], { indicesType: 'FULLTEXT', indexName: 'bar'}
],
expectation: "CREATE FULLTEXT INDEX bar ON User (username, isAdmin)",
arguments: ['User', ['username', 'isAdmin'], { indexName: 'bar'}, {}, 'User'],
expectation: "CREATE INDEX bar ON User (username, isAdmin)",
context: {options: {quoteIdentifiers: false}}
}, {
arguments: ['mySchema.User', ['username', 'isAdmin']],
arguments: ['mySchema.User', ['username', 'isAdmin'], {}, 'User'],
expectation: 'CREATE INDEX user_username_is_admin ON mySchema.User (username, isAdmin)',
context: {options: {quoteIdentifiers: false}}
}
],
// FIXME: not implemented
//showIndexQuery: [
// {
// arguments: ['User'],
// expectation: 'SHOW INDEX FROM \"User\"'
// }, {
// arguments: ['User', { database: 'sequelize' }],
// expectation: "SHOW INDEX FROM \"User\" FROM \"sequelize\""
// }
//],
removeIndexQuery: [
{
arguments: ['User', 'user_foo_bar'],
......@@ -953,32 +955,30 @@ if (dialect.match(/^postgres/)) {
context: {options: {quoteIdentifiers: false}}
}
]
}
};
_.each(suites, function(tests, suiteTitle) {
describe(suiteTitle, function() {
afterEach(function(done) {
this.sequelize.options.quoteIdentifiers = true
QueryGenerator.options.quoteIdentifiers = true
done()
})
afterEach(function() {
this.sequelize.options.quoteIdentifiers = true;
QueryGenerator.options.quoteIdentifiers = true;
});
tests.forEach(function(test) {
var title = test.title || 'Postgres correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments)
it(title, function(done) {
var title = test.title || 'Postgres correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments);
it(title, function() {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
var context = test.context || {options: {}};
if (test.needsSequelize) {
test.arguments[1] = test.arguments[1](this.sequelize)
test.arguments[1] = test.arguments[1](this.sequelize);
}
QueryGenerator.options = context.options
QueryGenerator._dialect = this.sequelize.dialect
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).to.deep.equal(test.expectation)
done()
})
})
})
})
})
QueryGenerator.options = context.options;
QueryGenerator._dialect = this.sequelize.dialect;
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments);
expect(conditions).to.deep.equal(test.expectation);
});
});
});
});
});
}
"use strict";
/* jshint multistr: true */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, _ = require('lodash')
, _ = require('lodash');
chai.config.includeStack = true
chai.config.includeStack = true;
describe(Support.getTestDialectTeaser("QueryGenerators"), function () {
describe("comments", function() {
it("should create a comment for a column", function(done) {
it("should create a comment for a column", function() {
var self = this
, User = this.sequelize.define('User', {
username: {type: DataTypes.STRING, comment: 'Some lovely info for my DBA'}
})
});
User.sync({ force: true }).success(function() {
var sql = ''
return User.sync({ force: true }).then(function() {
var sql = '';
if (Support.dialectIsMySQL()) {
sql = 'SELECT COLUMN_COMMENT as cmt FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = \'' + self.sequelize.config.database + '\' AND TABLE_NAME = \'Users\' AND COLUMN_NAME = \'username\'';
}
......@@ -30,19 +32,14 @@ describe(Support.getTestDialectTeaser("QueryGenerators"), function () {
}
else if (dialect === "sqlite") {
// sqlite doesn't support comments except for explicit comments in the file
expect(true).to.be.true
return done()
} else {
console.log('FIXME: This dialect is not supported :(');
expect(true).to.be.true
return done()
expect(true).to.be.true;
return;
}
self.sequelize.query(sql, null, {raw: true}).success(function(result) {
return self.sequelize.query(sql, null, {raw: true}).then(function(result) {
expect(result[0].cmt).to.equal('Some lovely info for my DBA');
done()
})
})
})
})
})
});
});
});
});
});
......@@ -32,7 +32,6 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () {
, connectionSpy = ConnectionManager.prototype.connect = chai.spy(ConnectionManager.prototype.connect);
var sequelize = Support.createSequelizeInstance({
dialect: dialect,
pool: {
minConnections: 2
}
......
......@@ -87,7 +87,7 @@ describe(Support.getTestDialectTeaser("Sequelize#transaction"), function () {
// how could we enforce an authentication error in sqlite?
} else {
it("gets triggered once an error occurs", function(done) {
var sequelize = Support.createSequelizeInstance({ dialect: Support.getTestDialect() });
var sequelize = Support.createSequelizeInstance();
// lets overwrite the host to get an error
sequelize.config.username = 'foobarbaz';
......
"use strict";
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
......@@ -6,20 +8,18 @@ var chai = require('chai')
, util = require("util")
, _ = require('lodash')
, moment = require('moment')
, QueryGenerator = require("../../lib/dialects/sqlite/query-generator")
, QueryGenerator = require("../../lib/dialects/sqlite/query-generator");
chai.config.includeStack = true
chai.config.includeStack = true;
if (dialect === 'sqlite') {
describe('[SQLITE Specific] QueryGenerator', function() {
beforeEach(function(done) {
beforeEach(function() {
this.User = this.sequelize.define('User', {
username: DataTypes.STRING
})
this.User.sync({ force: true }).success(function() {
done()
})
})
});
return this.User.sync({ force: true });
});
var suites = {
attributesToSQL: [
......@@ -167,7 +167,7 @@ if (dialect === 'sqlite') {
arguments: ['myTable', function (sequelize) {
return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
}
};
}],
expectation: "SELECT * FROM `myTable` ORDER BY f1(f2(`id`)) DESC;",
context: QueryGenerator,
......@@ -180,7 +180,7 @@ if (dialect === 'sqlite') {
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f2', 12, 'lalala', new Date(Date.UTC(2011, 2, 27, 10, 1, 55))), 'ASC']
]
}
};
}],
expectation: "SELECT * FROM `myTable` ORDER BY f1(`myTable`.`id`) DESC, f2(12, 'lalala', '2011-03-27 10:01:55.000 +00:00') ASC;",
context: QueryGenerator,
......@@ -199,7 +199,7 @@ if (dialect === 'sqlite') {
arguments: ['myTable', function (sequelize) {
return {
group: [sequelize.fn('YEAR', sequelize.col('createdAt'))]
}
};
}],
expectation: "SELECT * FROM `myTable` GROUP BY YEAR(`createdAt`);",
context: QueryGenerator,
......@@ -209,7 +209,7 @@ if (dialect === 'sqlite') {
arguments: ['myTable', function (sequelize) {
return {
group: [sequelize.fn('YEAR', sequelize.col('createdAt')), 'title']
}
};
}],
expectation: "SELECT * FROM `myTable` GROUP BY YEAR(`createdAt`), `title`;",
context: QueryGenerator,
......@@ -229,7 +229,7 @@ if (dialect === 'sqlite') {
attributes: ['*', [sequelize.fn('YEAR', sequelize.col('createdAt')), 'creationYear']],
group: ['creationYear', 'title'],
having: ['creationYear > ?', 2002]
}
};
}],
expectation: "SELECT *, YEAR(`createdAt`) AS `creationYear` FROM `myTable` GROUP BY `creationYear`, `title` HAVING creationYear > 2002;",
context: QueryGenerator,
......@@ -241,7 +241,7 @@ if (dialect === 'sqlite') {
attributes: ['*', [sequelize.fn('YEAR', sequelize.col('createdAt')), 'creationYear']],
group: ['creationYear', 'title'],
having: { creationYear: { gt: 2002 } }
}
};
}],
expectation: "SELECT *, YEAR(`createdAt`) AS `creationYear` FROM `myTable` GROUP BY `creationYear`, `title` HAVING `creationYear` > 2002;",
context: QueryGenerator,
......@@ -341,7 +341,7 @@ if (dialect === 'sqlite') {
arguments: ['myTable', function (sequelize) {
return {
foo: sequelize.fn('NOW')
}
};
}],
expectation: "INSERT INTO `myTable` (`foo`) VALUES (NOW());",
needsSequelize: true
......@@ -431,7 +431,7 @@ if (dialect === 'sqlite') {
arguments: ['myTable', function (sequelize) {
return {
bar: sequelize.fn('NOW')
}
};
}, {name: 'foo'}],
expectation: "UPDATE `myTable` SET `bar`=NOW() WHERE `name`='foo'",
needsSequelize: true
......@@ -439,7 +439,7 @@ if (dialect === 'sqlite') {
arguments: ['myTable', function (sequelize) {
return {
bar: sequelize.col('foo')
}
};
}, {name: 'foo'}],
expectation: "UPDATE `myTable` SET `bar`=`foo` WHERE `name`='foo'",
needsSequelize: true
......@@ -466,28 +466,59 @@ if (dialect === 'sqlite') {
arguments: ['myTable', {name: 'foo'}, {limit: null}],
expectation: "DELETE FROM `myTable` WHERE `name`='foo'"
}
]
}
],
addIndexQuery: [
{
arguments: ['User', ['username', 'isAdmin'], {}, 'User'],
expectation: 'CREATE INDEX `user_username_is_admin` ON `User` (`username`, `isAdmin`)'
}, {
arguments: [
'User', [
{ attribute: 'username', length: 10, order: 'ASC'},
'isAdmin'
],
{},
'User'
],
expectation: "CREATE INDEX `user_username_is_admin` ON `User` (`username` ASC, `isAdmin`)"
}, {
arguments: ['User', ['username', 'isAdmin'], { indexName: 'bar'}, {}, 'User'],
expectation: "CREATE INDEX `bar` ON `User` (`username`, `isAdmin`)"
}, {
arguments: ['User', ['fieldB', {attribute:'fieldA', collate: 'en_US', order: 'DESC', length: 5}], {
name: 'a_b_uniq',
unique: true,
method: 'BTREE'
}, 'User'],
expectation: 'CREATE UNIQUE INDEX `a_b_uniq` ON `User` (`fieldB`, `fieldA` COLLATE `en_US` DESC)'
}, {
arguments: ['User', ['fieldC'], {
type: 'FULLTEXT',
concurrently: true
}, 'User'],
expectation: 'CREATE INDEX `user_field_c` ON `User` (`fieldC`)'
},
],
};
_.each(suites, function(tests, suiteTitle) {
describe(suiteTitle, function() {
tests.forEach(function(test) {
var title = test.title || 'SQLite correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments)
it(title, function(done) {
var title = test.title || 'SQLite correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments);
it(title, function() {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
var context = test.context || {options: {}};
if (test.needsSequelize) {
test.arguments[1] = test.arguments[1](this.sequelize)
test.arguments[1] = test.arguments[1](this.sequelize);
}
QueryGenerator.options = context.options
QueryGenerator._dialect = this.sequelize.dialect
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).to.deep.equal(test.expectation)
done()
})
})
})
})
})
QueryGenerator.options = context.options;
QueryGenerator._dialect = this.sequelize.dialect;
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments);
expect(conditions).to.deep.equal(test.expectation);
});
});
});
});
});
}
......@@ -68,7 +68,7 @@ var Support = {
createSequelizeInstance: function(options) {
options = options || {};
options.dialect = options.dialect || 'mysql';
options.dialect = this.getTestDialect();
var config = Config[options.dialect];
......@@ -187,7 +187,7 @@ var Support = {
}
};
var sequelize = Support.createSequelizeInstance({ dialect: Support.getTestDialect() });
var sequelize = Support.createSequelizeInstance();
// For Postgres' HSTORE functionality and to properly execute it's commands we'll need this...
before(function() {
......
......@@ -16,7 +16,6 @@ if (dialect !== 'sqlite') {
beforeEach(function () {
this.sequelizeWithTimezone = Support.createSequelizeInstance({
timezone: '+07:00',
dialect: dialect
});
});
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!