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

Commit 9b46f913 by Sushant Committed by GitHub

refactor: indexes and uniqueKey (#9589)

1 parent 9f2c9db8
...@@ -497,22 +497,6 @@ class QueryGenerator { ...@@ -497,22 +497,6 @@ class QueryGenerator {
return _.template(query, this._templateSettings)(replacements); return _.template(query, this._templateSettings)(replacements);
} }
nameIndexes(indexes, rawTablename) {
if (typeof rawTablename === 'object') {
// don't include schema in the index name
rawTablename = rawTablename.tableName;
}
return _.map(indexes, index => {
if (!index.hasOwnProperty('name')) {
const onlyAttributeNames = index.fields.map(field => typeof field === 'string' ? field : field.name || field.attribute);
index.name = Utils.underscore(rawTablename + '_' + onlyAttributeNames.join('_'));
}
return index;
});
}
/* /*
Returns an add index query. Returns an add index query.
Parameters: Parameters:
...@@ -539,17 +523,6 @@ class QueryGenerator { ...@@ -539,17 +523,6 @@ class QueryGenerator {
options.fields = attributes; options.fields = attributes;
} }
// Backwards compatability
if (options.indexName) {
options.name = options.indexName;
}
if (options.indicesType) {
options.type = options.indicesType;
}
if (options.indexType || options.method) {
options.using = options.indexType || options.method;
}
options.prefix = options.prefix || rawTablename || tableName; options.prefix = options.prefix || rawTablename || tableName;
if (options.prefix && _.isString(options.prefix)) { if (options.prefix && _.isString(options.prefix)) {
options.prefix = options.prefix.replace(/\./g, '_'); options.prefix = options.prefix.replace(/\./g, '_');
...@@ -593,7 +566,7 @@ class QueryGenerator { ...@@ -593,7 +566,7 @@ class QueryGenerator {
if (!options.name) { if (!options.name) {
// Mostly for cases where addIndex is called directly by the user without an options object (for example in migrations) // Mostly for cases where addIndex is called directly by the user without an options object (for example in migrations)
// All calls that go through sequelize should already have a name // All calls that go through sequelize should already have a name
options = this.nameIndexes([options], options.prefix)[0]; options = Utils.nameIndex(options, options.prefix);
} }
options = Model._conformIndex(options); options = Model._conformIndex(options);
......
...@@ -407,7 +407,7 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -407,7 +407,7 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
} }
//Add unique indexes defined by indexes option to uniqueAttrs //Add unique indexes defined by indexes option to uniqueAttrs
for (const index of model.options.indexes) { for (const index of model._indexes) {
if (index.unique && index.fields) { if (index.unique && index.fields) {
for (const field of index.fields) { for (const field of index.fields) {
const fieldName = typeof field === 'string' ? field : field.name || field.attribute; const fieldName = typeof field === 'string' ? field : field.name || field.attribute;
......
'use strict';
const DataTypes = require('../../data-types');
const Promise = require('../../promise');
const QueryTypes = require('../../query-types');
const _ = require('lodash');
/**
Returns an object that handles Postgres special needs to do certain queries.
@class QueryInterface
@static
@private
*/
/**
* Ensure enum and their values
*
* @param {String} tableName Name of table to create
* @param {Object} attributes Object representing a list of normalized table attributes
* @param {Object} [attributes]
* @param {Model} [model]
*
* @return {Promise}
* @private
*/
function ensureEnums(tableName, attributes, options, model) {
const keys = Object.keys(attributes);
const keyLen = keys.length;
let sql = '';
let promises = [];
let i = 0;
for (i = 0; i < keyLen; i++) {
const attribute = attributes[keys[i]];
const type = attribute.type;
if (
type instanceof DataTypes.ENUM ||
(type instanceof DataTypes.ARRAY && type.type instanceof DataTypes.ENUM) //ARRAY sub type is ENUM
) {
sql = this.QueryGenerator.pgListEnums(tableName, attribute.field || keys[i], options);
promises.push(this.sequelize.query(
sql,
_.assign({}, options, { plain: true, raw: true, type: QueryTypes.SELECT })
));
}
}
return Promise.all(promises).then(results => {
promises = [];
let enumIdx = 0;
for (i = 0; i < keyLen; i++) {
const attribute = attributes[keys[i]];
const type = attribute.type;
const enumType = type.type || type;
if (
type instanceof DataTypes.ENUM ||
(type instanceof DataTypes.ARRAY && enumType instanceof DataTypes.ENUM) //ARRAY sub type is ENUM
) {
// If the enum type doesn't exist then create it
if (!results[enumIdx]) {
sql = this.QueryGenerator.pgEnum(tableName, attribute.field || keys[i], enumType, options);
promises.push(this.sequelize.query(
sql,
_.assign({}, options, { raw: true })
));
} else if (!!results[enumIdx] && !!model) {
const enumVals = this.QueryGenerator.fromArray(results[enumIdx].enum_value);
const vals = enumType.values;
vals.forEach((value, idx) => {
// reset out after/before options since it's for every enum value
const valueOptions = _.clone(options);
valueOptions.before = null;
valueOptions.after = null;
if (enumVals.indexOf(value) === -1) {
if (vals[idx + 1]) {
valueOptions.before = vals[idx + 1];
}
else if (vals[idx - 1]) {
valueOptions.after = vals[idx - 1];
}
valueOptions.supportsSearchPath = false;
promises.push(this.sequelize.query(this.QueryGenerator.pgEnumAdd(tableName, attribute.field || keys[i], value, valueOptions), valueOptions));
}
});
enumIdx++;
}
}
}
return Promise.all(promises)
.tap(() => {
// If ENUM processed, then refresh OIDs
if (promises.length) {
return this.sequelize.dialect.connectionManager._refreshDynamicOIDs();
}
});
});
}
exports.ensureEnums = ensureEnums;
\ No newline at end of file
'use strict'; 'use strict';
const assert = require('assert');
const Dottie = require('dottie');
const _ = require('lodash');
const Utils = require('./utils'); const Utils = require('./utils');
const BelongsTo = require('./associations/belongs-to'); const BelongsTo = require('./associations/belongs-to');
const BelongsToMany = require('./associations/belongs-to-many'); const BelongsToMany = require('./associations/belongs-to-many');
const InstanceValidator = require('./instance-validator'); const InstanceValidator = require('./instance-validator');
const QueryTypes = require('./query-types'); const QueryTypes = require('./query-types');
const sequelizeErrors = require('./errors'); const sequelizeErrors = require('./errors');
const Dottie = require('dottie');
const Promise = require('./promise'); const Promise = require('./promise');
const _ = require('lodash');
const Association = require('./associations/base'); const Association = require('./associations/base');
const HasMany = require('./associations/has-many'); const HasMany = require('./associations/has-many');
const DataTypes = require('./data-types'); const DataTypes = require('./data-types');
const Hooks = require('./hooks'); const Hooks = require('./hooks');
const associationsMixin = require('./associations/mixin'); const associationsMixin = require('./associations/mixin');
const defaultsOptions = { raw: true };
const assert = require('assert');
const Op = require('./operators'); const Op = require('./operators');
/** /**
...@@ -141,7 +141,7 @@ class Model { ...@@ -141,7 +141,7 @@ class Model {
if (Object.keys(defaults).length) { if (Object.keys(defaults).length) {
for (key in defaults) { for (key in defaults) {
if (values[key] === undefined) { if (values[key] === undefined) {
this.set(key, Utils.toDefaultValue(defaults[key], this.sequelize.options.dialect), defaultsOptions); this.set(key, Utils.toDefaultValue(defaults[key], this.sequelize.options.dialect), { raw: true });
delete values[key]; delete values[key];
} }
} }
...@@ -734,6 +734,24 @@ class Model { ...@@ -734,6 +734,24 @@ class Model {
}); });
} }
static _conformIndex(index) {
if (!index.fields) {
throw new Error('Missing "fields" property for index definition');
}
index = _.defaults(index, {
type: '',
parser: null
});
if (index.type && index.type.toLowerCase() === 'unique') {
index.unique = true;
delete index.type;
}
return index;
}
/** /**
* Initialize a model, representing a table in the DB, with attributes and options. * Initialize a model, representing a table in the DB, with attributes and options.
* *
...@@ -805,7 +823,7 @@ class Model { ...@@ -805,7 +823,7 @@ class Model {
* @param {Array<Object>} [options.indexes] * @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[].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[].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 {String} [options.indexes[].using] 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[].unique=false] Should the index by unique? Can also be triggered by setting type to `UNIQUE`
* @param {Boolean} [options.indexes[].concurrently=false] PostgresSQL will build the index without taking any write locks. Postgres only * @param {Boolean} [options.indexes[].concurrently=false] PostgresSQL 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, a sequelize object (e.g `sequelize.fn`), 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 {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, a sequelize object (e.g `sequelize.fn`), 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)
...@@ -919,35 +937,40 @@ class Model { ...@@ -919,35 +937,40 @@ class Model {
return attribute; return attribute;
}); });
this._indexes = this.options.indexes
.map(index => this._conformIndex(index))
.map(index => Utils.nameIndex(index, this.getTableName()));
this.primaryKeys = {}; this.primaryKeys = {};
this._readOnlyAttributes = [];
this._timestampAttributes = {}; this._timestampAttributes = {};
// Setup names of timestamp attributes // setup names of timestamp attributes
if (this.options.timestamps) { if (this.options.timestamps) {
if (this.options.createdAt !== false) { if (this.options.createdAt !== false) {
this._timestampAttributes.createdAt = this.options.createdAt || 'createdAt'; this._timestampAttributes.createdAt = this.options.createdAt || 'createdAt';
this._readOnlyAttributes.push(this._timestampAttributes.createdAt);
} }
if (this.options.updatedAt !== false) { if (this.options.updatedAt !== false) {
this._timestampAttributes.updatedAt = this.options.updatedAt || 'updatedAt'; this._timestampAttributes.updatedAt = this.options.updatedAt || 'updatedAt';
this._readOnlyAttributes.push(this._timestampAttributes.updatedAt);
} }
if (this.options.paranoid && this.options.deletedAt !== false) { if (this.options.paranoid && this.options.deletedAt !== false) {
this._timestampAttributes.deletedAt = this.options.deletedAt || 'deletedAt'; this._timestampAttributes.deletedAt = this.options.deletedAt || 'deletedAt';
this._readOnlyAttributes.push(this._timestampAttributes.deletedAt);
} }
} }
// Setup name for version attribute // setup name for version attribute
if (this.options.version) { if (this.options.version) {
this._versionAttribute = typeof this.options.version === 'string' ? this.options.version : 'version'; this._versionAttribute = typeof this.options.version === 'string' ? this.options.version : 'version';
}
// Add head and tail default attributes (id, timestamps)
this._readOnlyAttributes = _.values(this._timestampAttributes);
if (this._versionAttribute) {
this._readOnlyAttributes.push(this._versionAttribute); this._readOnlyAttributes.push(this._versionAttribute);
} }
this._hasReadOnlyAttributes = this._readOnlyAttributes && this._readOnlyAttributes.length;
this._isReadOnlyAttribute = _.memoize(key => this._hasReadOnlyAttributes && this._readOnlyAttributes.indexOf(key) !== -1);
this._hasReadOnlyAttributes = this._readOnlyAttributes.length > 0;
this._isReadOnlyAttribute = _.memoize(key => this._readOnlyAttributes.includes(key));
// Add head and tail default attributes (id, timestamps)
this._addDefaultAttributes(); this._addDefaultAttributes();
this.refreshAttributes(); this.refreshAttributes();
this._findAutoIncrementAttribute(); this._findAutoIncrementAttribute();
...@@ -965,27 +988,12 @@ class Model { ...@@ -965,27 +988,12 @@ class Model {
} }
}); });
this.options.indexes = this.options.indexes.map(this._conformIndex);
this.sequelize.modelManager.addModel(this); this.sequelize.modelManager.addModel(this);
this.sequelize.runHooks('afterDefine', this); this.sequelize.runHooks('afterDefine', this);
return this; return this;
} }
static _conformIndex(index) {
index = _.defaults(index, {
type: '',
parser: null
});
if (index.type && index.type.toLowerCase() === 'unique') {
index.unique = true;
delete index.type;
}
return index;
}
static refreshAttributes() { static refreshAttributes() {
const attributeManipulation = {}; const attributeManipulation = {};
...@@ -1055,7 +1063,7 @@ class Model { ...@@ -1055,7 +1063,7 @@ class Model {
this.fieldRawAttributesMap = {}; this.fieldRawAttributesMap = {};
this.primaryKeys = {}; this.primaryKeys = {};
this.options.uniqueKeys = {}; this.uniqueKeys = {};
_.each(this.rawAttributes, (definition, name) => { _.each(this.rawAttributes, (definition, name) => {
definition.type = this.sequelize.normalizeDataType(definition.type); definition.type = this.sequelize.normalizeDataType(definition.type);
...@@ -1116,7 +1124,7 @@ class Model { ...@@ -1116,7 +1124,7 @@ class Model {
idxName = this.tableName + '_' + name + '_unique'; idxName = this.tableName + '_' + name + '_unique';
} }
const idx = this.options.uniqueKeys[idxName] || { fields: [] }; const idx = this.uniqueKeys[idxName] || { fields: [] };
idx.fields.push(definition.field); idx.fields.push(definition.field);
idx.msg = idx.msg || definition.unique.msg || null; idx.msg = idx.msg || definition.unique.msg || null;
...@@ -1124,7 +1132,7 @@ class Model { ...@@ -1124,7 +1132,7 @@ class Model {
idx.column = name; idx.column = name;
idx.customIndex = definition.unique !== true; idx.customIndex = definition.unique !== true;
this.options.uniqueKeys[idxName] = idx; this.uniqueKeys[idxName] = idx;
} }
if (definition.hasOwnProperty('validate')) { if (definition.hasOwnProperty('validate')) {
...@@ -1132,10 +1140,15 @@ class Model { ...@@ -1132,10 +1140,15 @@ class Model {
} }
if (definition.index === true && definition.type instanceof DataTypes.JSONB) { if (definition.index === true && definition.type instanceof DataTypes.JSONB) {
this.options.indexes.push({ this._indexes.push(
Utils.nameIndex(
this._conformIndex({
fields: [definition.field || name], fields: [definition.field || name],
using: 'gin' using: 'gin'
}); }),
this.getTableName()
)
);
delete definition.index; delete definition.index;
} }
...@@ -1149,8 +1162,6 @@ class Model { ...@@ -1149,8 +1162,6 @@ class Model {
return map; return map;
}, {}); }, {});
this.uniqueKeys = this.options.uniqueKeys;
this._hasBooleanAttributes = !!this._booleanAttributes.length; this._hasBooleanAttributes = !!this._booleanAttributes.length;
this._isBooleanAttribute = _.memoize(key => this._booleanAttributes.indexOf(key) !== -1); this._isBooleanAttribute = _.memoize(key => this._booleanAttributes.indexOf(key) !== -1);
...@@ -1289,10 +1300,7 @@ class Model { ...@@ -1289,10 +1300,7 @@ class Model {
}) })
.then(() => this.QueryInterface.showIndex(this.getTableName(options), options)) .then(() => this.QueryInterface.showIndex(this.getTableName(options), options))
.then(indexes => { .then(indexes => {
// Assign an auto-generated name to indexes which are not named by the user indexes = _.filter(this._indexes, item1 =>
this.options.indexes = this.QueryInterface.nameIndexes(this.options.indexes, this.tableName);
indexes = _.filter(this.options.indexes, item1 =>
!_.some(indexes, item2 => item1.name === item2.name) !_.some(indexes, item2 => item1.name === item2.name)
).sort((index1, index2) => { ).sort((index1, index2) => {
if (this.sequelize.options.dialect === 'postgres') { if (this.sequelize.options.dialect === 'postgres') {
......
...@@ -6,6 +6,7 @@ const DataTypes = require('./data-types'); ...@@ -6,6 +6,7 @@ const DataTypes = require('./data-types');
const SQLiteQueryInterface = require('./dialects/sqlite/query-interface'); const SQLiteQueryInterface = require('./dialects/sqlite/query-interface');
const MSSSQLQueryInterface = require('./dialects/mssql/query-interface'); const MSSSQLQueryInterface = require('./dialects/mssql/query-interface');
const MySQLQueryInterface = require('./dialects/mysql/query-interface'); const MySQLQueryInterface = require('./dialects/mysql/query-interface');
const PostgresQueryInterface = require('./dialects/postgres/query-interface');
const Transaction = require('./transaction'); const Transaction = require('./transaction');
const Promise = require('./promise'); const Promise = require('./promise');
const QueryTypes = require('./query-types'); const QueryTypes = require('./query-types');
...@@ -142,7 +143,9 @@ class QueryInterface { ...@@ -142,7 +143,9 @@ class QueryInterface {
* { * {
* engine: 'MYISAM', // default: 'InnoDB' * engine: 'MYISAM', // default: 'InnoDB'
* charset: 'latin1', // default: null * charset: 'latin1', // default: null
* schema: 'public' // default: public, PostgreSQL only. * schema: 'public', // default: public, PostgreSQL only.
* comment: 'my table', // comment for table
* collate: 'latin1_danish_ci' // collation, MYSQL only
* } * }
* ) * )
* ``` * ```
...@@ -155,129 +158,41 @@ class QueryInterface { ...@@ -155,129 +158,41 @@ class QueryInterface {
* @return {Promise} * @return {Promise}
*/ */
createTable(tableName, attributes, options, model) { createTable(tableName, attributes, options, model) {
const keys = Object.keys(attributes);
const keyLen = keys.length;
let sql = ''; let sql = '';
let i = 0; let promise;
options = _.clone(options) || {}; options = _.clone(options) || {};
attributes = _.mapValues(attributes, attribute => { if (model) {
if (!_.isPlainObject(attribute)) { options.uniqueKeys = options.uniqueKeys || model.uniqueKeys;
attribute = { type: attribute, allowNull: true };
} }
attribute = this.sequelize.normalizeAttribute(attribute); attributes = _.mapValues(
attributes,
return attribute; attribute => this.sequelize.normalizeAttribute(attribute)
}); );
// Postgres requires a special SQL command for enums // Postgres requires special SQL commands for ENUM/ENUM[]
if (this.sequelize.options.dialect === 'postgres') { if (this.sequelize.options.dialect === 'postgres') {
const promises = []; promise = PostgresQueryInterface.ensureEnums.call(this, tableName, attributes, options, model);
} else {
for (i = 0; i < keyLen; i++) { promise = Promise.resolve();
const attribute = attributes[keys[i]];
const type = attribute.type;
if (
type instanceof DataTypes.ENUM ||
(type instanceof DataTypes.ARRAY && type.type instanceof DataTypes.ENUM) //ARRAY sub type is ENUM
) {
sql = this.QueryGenerator.pgListEnums(tableName, attribute.field || keys[i], options);
promises.push(this.sequelize.query(
sql,
_.assign({}, options, { plain: true, raw: true, type: QueryTypes.SELECT })
));
}
} }
return Promise.all(promises).then(results => {
const promises = [];
let enumIdx = 0;
for (i = 0; i < keyLen; i++) {
const attribute = attributes[keys[i]];
const type = attribute.type;
const enumType = type.type || type;
if ( if (
type instanceof DataTypes.ENUM || !tableName.schema &&
(type instanceof DataTypes.ARRAY && enumType instanceof DataTypes.ENUM) //ARRAY sub type is ENUM (options.schema || !!model && model._schema)
) { ) {
// If the enum type doesn't exist then create it
if (!results[enumIdx]) {
sql = this.QueryGenerator.pgEnum(tableName, attribute.field || keys[i], enumType, options);
promises.push(this.sequelize.query(
sql,
_.assign({}, options, { raw: true })
));
} else if (!!results[enumIdx] && !!model) {
const enumVals = this.QueryGenerator.fromArray(results[enumIdx].enum_value);
const vals = enumType.values;
vals.forEach((value, idx) => {
// reset out after/before options since it's for every enum value
const valueOptions = _.clone(options);
valueOptions.before = null;
valueOptions.after = null;
if (enumVals.indexOf(value) === -1) {
if (vals[idx + 1]) {
valueOptions.before = vals[idx + 1];
}
else if (vals[idx - 1]) {
valueOptions.after = vals[idx - 1];
}
valueOptions.supportsSearchPath = false;
promises.push(this.sequelize.query(this.QueryGenerator.pgEnumAdd(tableName, attribute.field || keys[i], value, valueOptions), valueOptions));
}
});
enumIdx++;
}
}
}
if (!tableName.schema &&
(options.schema || !!model && model._schema)) {
tableName = this.QueryGenerator.addSchema({
tableName,
_schema: !!model && model._schema || options.schema
});
}
attributes = this.QueryGenerator.attributesToSQL(attributes, {
context: 'createTable'
});
sql = this.QueryGenerator.createTableQuery(tableName, attributes, options);
return Promise.all(promises)
.tap(() => {
// If ENUM processed, then refresh OIDs
if (promises.length) {
return this.sequelize.dialect.connectionManager._refreshDynamicOIDs();
}
})
.then(() => {
return this.sequelize.query(sql, options);
});
});
} else {
if (!tableName.schema &&
(options.schema || !!model && model._schema)) {
tableName = this.QueryGenerator.addSchema({ tableName = this.QueryGenerator.addSchema({
tableName, tableName,
_schema: !!model && model._schema || options.schema _schema: !!model && model._schema || options.schema
}); });
} }
attributes = this.QueryGenerator.attributesToSQL(attributes, { attributes = this.QueryGenerator.attributesToSQL(attributes, { context: 'createTable' });
context: 'createTable'
});
sql = this.QueryGenerator.createTableQuery(tableName, attributes, options); sql = this.QueryGenerator.createTableQuery(tableName, attributes, options);
return this.sequelize.query(sql, options); return promise.then(() => this.sequelize.query(sql, options));
}
} }
/** /**
...@@ -684,10 +599,6 @@ class QueryInterface { ...@@ -684,10 +599,6 @@ class QueryInterface {
return this.sequelize.query(sql, _.assign({}, options, { type: QueryTypes.SHOWINDEXES })); return this.sequelize.query(sql, _.assign({}, options, { type: QueryTypes.SHOWINDEXES }));
} }
nameIndexes(indexes, rawTablename) {
return this.QueryGenerator.nameIndexes(indexes, rawTablename);
}
getForeignKeysForTables(tableNames, options) { getForeignKeysForTables(tableNames, options) {
if (tableNames.length === 0) { if (tableNames.length === 0) {
return Promise.resolve({}); return Promise.resolve({});
...@@ -933,12 +844,12 @@ class QueryInterface { ...@@ -933,12 +844,12 @@ class QueryInterface {
wheres.push(where); wheres.push(where);
} }
// Lets combine uniquekeys and indexes into one // Lets combine unique keys and indexes into one
indexes = _.map(model.options.uniqueKeys, value => { indexes = _.map(model.uniqueKeys, value => {
return value.fields; return value.fields;
}); });
_.each(model.options.indexes, value => { _.each(model._indexes, value => {
if (value.unique) { if (value.unique) {
// fields in the index may both the strings or objects with an attribute property - lets sanitize that // fields in the index may both the strings or objects with an attribute property - lets sanitize that
indexFields = _.map(value.fields, field => { indexFields = _.map(value.fields, field => {
......
...@@ -648,3 +648,28 @@ function classToInvokable(Class) { ...@@ -648,3 +648,28 @@ function classToInvokable(Class) {
} }
exports.classToInvokable = classToInvokable; exports.classToInvokable = classToInvokable;
/**
*
* @param {Object} index
* @param {Array} index.fields
* @param {String} [index.name]
* @param {String|Object} tableName
*
* @returns {Object}
* @private
*/
function nameIndex(index, tableName) {
if (tableName.tableName) tableName = tableName.tableName;
if (!index.hasOwnProperty('name')) {
const fields = index.fields.map(
field => typeof field === 'string' ? field : field.name || field.attribute
);
index.name = underscore(tableName + '_' + fields.join('_'));
}
return index;
}
exports.nameIndex = nameIndex;
...@@ -1570,8 +1570,8 @@ describe(Support.getTestDialectTeaser('BelongsToMany'), () => { ...@@ -1570,8 +1570,8 @@ describe(Support.getTestDialectTeaser('BelongsToMany'), () => {
expect(Object.keys(this.UserTasks2.primaryKeys)).to.deep.equal(['userTasksId']); expect(Object.keys(this.UserTasks2.primaryKeys)).to.deep.equal(['userTasksId']);
_.each([this.UserTasks, this.UserTasks2], model => { _.each([this.UserTasks, this.UserTasks2], model => {
fk = Object.keys(model.options.uniqueKeys)[0]; fk = Object.keys(model.uniqueKeys)[0];
expect(model.options.uniqueKeys[fk].fields.sort()).to.deep.equal(['TaskId', 'UserId']); expect(model.uniqueKeys[fk].fields.sort()).to.deep.equal(['TaskId', 'UserId']);
}); });
}); });
......
...@@ -265,74 +265,6 @@ describe(Support.getTestDialectTeaser('QueryInterface'), () => { ...@@ -265,74 +265,6 @@ describe(Support.getTestDialectTeaser('QueryInterface'), () => {
}); });
}); });
// FIXME: These tests should make assertions against the created table using describeTable
describe('createTable', () => {
it('should create a auto increment primary key', function() {
return this.queryInterface.createTable('TableWithPK', {
table_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
}
}).bind(this).then(function() {
return this.queryInterface.insert(null, 'TableWithPK', {}, {raw: true, returning: true, plain: true}).then(results => {
const response = _.head(results);
expect(response.table_id || typeof response !== 'object' && response).to.be.ok;
});
});
});
it('should work with enums (1)', function() {
return this.queryInterface.createTable('SomeTable', {
someEnum: DataTypes.ENUM('value1', 'value2', 'value3')
});
});
it('should work with enums (2)', function() {
return this.queryInterface.createTable('SomeTable', {
someEnum: {
type: DataTypes.ENUM,
values: ['value1', 'value2', 'value3']
}
});
});
it('should work with enums (3)', function() {
return this.queryInterface.createTable('SomeTable', {
someEnum: {
type: DataTypes.ENUM,
values: ['value1', 'value2', 'value3'],
field: 'otherName'
}
});
});
it('should work with enums (4)', function() {
return this.queryInterface.createSchema('archive').bind(this).then(function() {
return this.queryInterface.createTable('SomeTable', {
someEnum: {
type: DataTypes.ENUM,
values: ['value1', 'value2', 'value3'],
field: 'otherName'
}
}, { schema: 'archive' });
});
});
it('should work with schemas', function() {
const self = this;
return self.sequelize.createSchema('hero').then(() => {
return self.queryInterface.createTable('User', {
name: {
type: DataTypes.STRING
}
}, {
schema: 'hero'
});
});
});
});
describe('renameColumn', () => { describe('renameColumn', () => {
it('rename a simple column', function() { it('rename a simple column', function() {
const self = this; const self = this;
......
'use strict';
const chai = require('chai');
const expect = chai.expect;
const Support = require(__dirname + '/../support');
const DataTypes = require(__dirname + '/../../../lib/data-types');
const _ = require('lodash');
describe(Support.getTestDialectTeaser('QueryInterface'), () => {
beforeEach(function () {
this.sequelize.options.quoteIdenifiers = true;
this.queryInterface = this.sequelize.getQueryInterface();
});
afterEach(function () {
return this.sequelize.dropAllSchemas();
});
// FIXME: These tests should make assertions against the created table using describeTable
describe('createTable', () => {
it('should create a auto increment primary key', function () {
return this.queryInterface.createTable('TableWithPK', {
table_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
}
}).then(() => {
return this.queryInterface.insert(null, 'TableWithPK', {}, { raw: true, returning: true, plain: true })
.then(results => {
const response = _.head(results);
expect(response.table_id || typeof response !== 'object' && response).to.be.ok;
});
});
});
it('should work with enums (1)', function () {
return this.queryInterface.createTable('SomeTable', {
someEnum: DataTypes.ENUM('value1', 'value2', 'value3')
});
});
it('should work with enums (2)', function () {
return this.queryInterface.createTable('SomeTable', {
someEnum: {
type: DataTypes.ENUM,
values: ['value1', 'value2', 'value3']
}
});
});
it('should work with enums (3)', function () {
return this.queryInterface.createTable('SomeTable', {
someEnum: {
type: DataTypes.ENUM,
values: ['value1', 'value2', 'value3'],
field: 'otherName'
}
});
});
it('should work with enums (4)', function () {
return this.queryInterface.createSchema('archive').bind(this).then(function () {
return this.queryInterface.createTable('SomeTable', {
someEnum: {
type: DataTypes.ENUM,
values: ['value1', 'value2', 'value3'],
field: 'otherName'
}
}, { schema: 'archive' });
});
});
it('should work with schemas', function () {
const self = this;
return self.sequelize.createSchema('hero').then(() => {
return self.queryInterface.createTable('User', {
name: {
type: DataTypes.STRING
}
}, {
schema: 'hero'
});
});
});
});
});
\ No newline at end of file
...@@ -18,25 +18,27 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -18,25 +18,27 @@ describe(Support.getTestDialectTeaser('Model'), () => {
}); });
expect(Model.rawAttributes.eventData.index).not.to.equal(true); expect(Model.rawAttributes.eventData.index).not.to.equal(true);
expect(Model.options.indexes.length).to.equal(1); expect(Model._indexes.length).to.equal(1);
expect(Model.options.indexes[0].fields).to.eql(['data']); expect(Model._indexes[0].fields).to.eql(['data']);
expect(Model.options.indexes[0].using).to.equal('gin'); expect(Model._indexes[0].using).to.equal('gin');
}); });
it('should set the unique property when type is unique', () => { it('should set the unique property when type is unique', () => {
const Model = current.define('m', {}, { const Model = current.define('m', {}, {
indexes: [ indexes: [
{ {
type: 'unique' type: 'unique',
fields: ['name']
}, },
{ {
type: 'UNIQUE' type: 'UNIQUE',
fields: ['name']
} }
] ]
}); });
expect(Model.options.indexes[0].unique).to.eql(true); expect(Model._indexes[0].unique).to.eql(true);
expect(Model.options.indexes[1].unique).to.eql(true); expect(Model._indexes[1].unique).to.eql(true);
}); });
it('should not set rawAttributes when indexes are defined via options', () => { it('should not set rawAttributes when indexes are defined via options', () => {
......
...@@ -37,7 +37,7 @@ suite(Support.getTestDialectTeaser('SQL'), () => { ...@@ -37,7 +37,7 @@ suite(Support.getTestDialectTeaser('SQL'), () => {
} }
}); });
test('type and method', () => { test('type and using', () => {
expectsql(sql.addIndexQuery('User', ['fieldC'], { expectsql(sql.addIndexQuery('User', ['fieldC'], {
type: 'FULLTEXT', type: 'FULLTEXT',
concurrently: true concurrently: true
...@@ -51,7 +51,7 @@ suite(Support.getTestDialectTeaser('SQL'), () => { ...@@ -51,7 +51,7 @@ suite(Support.getTestDialectTeaser('SQL'), () => {
expectsql(sql.addIndexQuery('User', ['fieldB', {attribute: 'fieldA', collate: 'en_US', order: 'DESC', length: 5}], { expectsql(sql.addIndexQuery('User', ['fieldB', {attribute: 'fieldA', collate: 'en_US', order: 'DESC', length: 5}], {
name: 'a_b_uniq', name: 'a_b_uniq',
unique: true, unique: true,
method: 'BTREE', using: 'BTREE',
parser: 'foo' parser: 'foo'
}), { }), {
sqlite: 'CREATE UNIQUE INDEX `a_b_uniq` ON `User` (`fieldB`, `fieldA` COLLATE `en_US` DESC)', sqlite: 'CREATE UNIQUE INDEX `a_b_uniq` ON `User` (`fieldB`, `fieldA` COLLATE `en_US` DESC)',
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!