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

Commit 0abea9f9 by Sushant Committed by GitHub

fix(indexes): duplicate unique indexes with sync (#9033)

1 parent d92d0d5a
...@@ -55,8 +55,7 @@ const QueryGenerator = { ...@@ -55,8 +55,7 @@ const QueryGenerator = {
const query = "IF OBJECT_ID('<%= table %>', 'U') IS NULL CREATE TABLE <%= table %> (<%= attributes %>)", const query = "IF OBJECT_ID('<%= table %>', 'U') IS NULL CREATE TABLE <%= table %> (<%= attributes %>)",
primaryKeys = [], primaryKeys = [],
foreignKeys = {}, foreignKeys = {},
attrStr = [], attrStr = [];
self = this;
for (const attr in attributes) { for (const attr in attributes) {
if (attributes.hasOwnProperty(attr)) { if (attributes.hasOwnProperty(attr)) {
...@@ -93,15 +92,17 @@ const QueryGenerator = { ...@@ -93,15 +92,17 @@ const QueryGenerator = {
if (options.uniqueKeys) { if (options.uniqueKeys) {
_.each(options.uniqueKeys, (columns, indexName) => { _.each(options.uniqueKeys, (columns, indexName) => {
if (!_.isString(indexName)) { if (columns.customIndex) {
indexName = 'uniq_' + tableName + '_' + columns.fields.join('_'); if (!_.isString(indexName)) {
indexName = 'uniq_' + tableName + '_' + columns.fields.join('_');
}
values.attributes += `, CONSTRAINT ${this.quoteIdentifier(indexName)} UNIQUE (${columns.fields.map(field => this.quoteIdentifier(field)).join(', ')})`;
} }
values.attributes += ', CONSTRAINT ' + self.quoteIdentifier(indexName) + ' UNIQUE (' + _.map(columns.fields, self.quoteIdentifier).join(', ') + ')';
}); });
} }
if (pkString.length > 0) { if (pkString.length > 0) {
values.attributes += ', PRIMARY KEY (' + pkString + ')'; values.attributes += `, PRIMARY KEY (${pkString})`;
} }
for (const fkey in foreignKeys) { for (const fkey in foreignKeys) {
......
...@@ -80,17 +80,17 @@ const QueryGenerator = { ...@@ -80,17 +80,17 @@ const QueryGenerator = {
if (options.uniqueKeys) { if (options.uniqueKeys) {
_.each(options.uniqueKeys, (columns, indexName) => { _.each(options.uniqueKeys, (columns, indexName) => {
if (!columns.singleField) { // If it's a single field it's handled in column def, not as an index if (columns.customIndex) {
if (!_.isString(indexName)) { if (!_.isString(indexName)) {
indexName = 'uniq_' + tableName + '_' + columns.fields.join('_'); indexName = 'uniq_' + tableName + '_' + columns.fields.join('_');
} }
values.attributes += ', UNIQUE ' + this.quoteIdentifier(indexName) + ' (' + _.map(columns.fields, this.quoteIdentifier).join(', ') + ')'; values.attributes += `, UNIQUE ${this.quoteIdentifier(indexName)} (${columns.fields.map(field => this.quoteIdentifier(field)).join(', ')})`;
} }
}); });
} }
if (pkString.length > 0) { if (pkString.length > 0) {
values.attributes += ', PRIMARY KEY (' + pkString + ')'; values.attributes += `, PRIMARY KEY (${pkString})`;
} }
for (const fkey in foreignKeys) { for (const fkey in foreignKeys) {
......
...@@ -39,9 +39,7 @@ const QueryGenerator = { ...@@ -39,9 +39,7 @@ const QueryGenerator = {
}, },
createTableQuery(tableName, attributes, options) { createTableQuery(tableName, attributes, options) {
options = _.extend({}, options || {});
options = _.extend({
}, options || {});
//Postgres 9.0 does not support CREATE TABLE IF NOT EXISTS, 9.1 and above do //Postgres 9.0 does not support CREATE TABLE IF NOT EXISTS, 9.1 and above do
const databaseVersion = _.get(this, 'sequelize.options.databaseVersion', 0); const databaseVersion = _.get(this, 'sequelize.options.databaseVersion', 0);
...@@ -72,8 +70,8 @@ const QueryGenerator = { ...@@ -72,8 +70,8 @@ const QueryGenerator = {
if (options.uniqueKeys) { if (options.uniqueKeys) {
_.each(options.uniqueKeys, columns => { _.each(options.uniqueKeys, columns => {
if (!columns.singleField) { // If it's a single field it's handled in column def, not as an index if (columns.customIndex) {
values.attributes += ', UNIQUE (' + columns.fields.map(f => this.quoteIdentifiers(f)).join(', ') + ')'; values.attributes += `, UNIQUE (${columns.fields.map(field => this.quoteIdentifier(field)).join(', ')})`;
} }
}); });
} }
...@@ -86,7 +84,7 @@ const QueryGenerator = { ...@@ -86,7 +84,7 @@ const QueryGenerator = {
}, []).join(','); }, []).join(',');
if (pks.length > 0) { if (pks.length > 0) {
values.attributes += ', PRIMARY KEY (' + pks + ')'; values.attributes += `, PRIMARY KEY (${pks})`;
} }
return `CREATE TABLE ${databaseVersion === 0 || semver.gte(databaseVersion, '9.1.0') ? 'IF NOT EXISTS ' : ''}${values.table} (${values.attributes})${values.comments};`; return `CREATE TABLE ${databaseVersion === 0 || semver.gte(databaseVersion, '9.1.0') ? 'IF NOT EXISTS ' : ''}${values.table} (${values.attributes})${values.comments};`;
......
...@@ -61,14 +61,14 @@ const QueryGenerator = { ...@@ -61,14 +61,14 @@ const QueryGenerator = {
if (options.uniqueKeys) { if (options.uniqueKeys) {
_.each(options.uniqueKeys, columns => { _.each(options.uniqueKeys, columns => {
if (!columns.singleField) { // If it's a single field it's handled in column def, not as an index if (columns.customIndex) {
attrStr += ', UNIQUE (' + columns.fields.map(field => this.quoteIdentifier(field)).join(', ') + ')'; attrStr += `, UNIQUE (${columns.fields.map(field => this.quoteIdentifier(field)).join(', ')})`;
} }
}); });
} }
if (pkString.length > 0) { if (pkString.length > 0) {
attrStr += ', PRIMARY KEY (' + pkString + ')'; attrStr += `, PRIMARY KEY (${pkString})`;
} }
const sql = `CREATE TABLE IF NOT EXISTS ${table} (${attrStr});`; const sql = `CREATE TABLE IF NOT EXISTS ${table} (${attrStr});`;
......
...@@ -994,7 +994,10 @@ class Model { ...@@ -994,7 +994,10 @@ class Model {
if (definition.hasOwnProperty('unique') && definition.unique) { if (definition.hasOwnProperty('unique') && definition.unique) {
let idxName; let idxName;
if (typeof definition.unique === 'object' && definition.unique.hasOwnProperty('name')) { if (
typeof definition.unique === 'object' &&
definition.unique.hasOwnProperty('name')
) {
idxName = definition.unique.name; idxName = definition.unique.name;
} else if (typeof definition.unique === 'string') { } else if (typeof definition.unique === 'string') {
idxName = definition.unique; idxName = definition.unique;
...@@ -1002,12 +1005,13 @@ class Model { ...@@ -1002,12 +1005,13 @@ class Model {
idxName = this.tableName + '_' + name + '_unique'; idxName = this.tableName + '_' + name + '_unique';
} }
let idx = this.options.uniqueKeys[idxName] || { fields: [] }; const idx = this.options.uniqueKeys[idxName] || { fields: [] };
idx = idx || {fields: [], msg: null};
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;
idx.name = idxName || false; idx.name = idxName || false;
idx.column = name; idx.column = name;
idx.customIndex = definition.unique !== true;
this.options.uniqueKeys[idxName] = idx; this.options.uniqueKeys[idxName] = idx;
} }
...@@ -1025,6 +1029,7 @@ class Model { ...@@ -1025,6 +1029,7 @@ class Model {
delete definition.index; delete definition.index;
} }
}); });
// Create a map of field to attribute names // Create a map of field to attribute names
this.fieldAttributeMap = _.reduce(this.fieldRawAttributesMap, (map, value, key) => { this.fieldAttributeMap = _.reduce(this.fieldRawAttributesMap, (map, value, key) => {
if (key !== value.fieldName) { if (key !== value.fieldName) {
......
...@@ -79,7 +79,7 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -79,7 +79,7 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
it('should enforce a unique constraint', function() { it('should enforce a unique constraint', function() {
const Model = this.sequelize.define('model', { const Model = this.sequelize.define('model', {
uniqueName: { type: Sequelize.STRING, unique: true } uniqueName: { type: Sequelize.STRING, unique: 'uniqueName' }
}); });
const records = [ const records = [
{ uniqueName: 'unique name one' }, { uniqueName: 'unique name one' },
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
const chai = require('chai'), const chai = require('chai'),
Sequelize = require('../../../index'), Sequelize = require('../../../index'),
expect = chai.expect, expect = chai.expect,
Support = require(__dirname + '/../support'); Support = require(__dirname + '/../support'),
dialect = Support.getTestDialect();
describe(Support.getTestDialectTeaser('Model'), () => { describe(Support.getTestDialectTeaser('Model'), () => {
describe('sync', () => { describe('sync', () => {
...@@ -127,5 +128,148 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -127,5 +128,148 @@ describe(Support.getTestDialectTeaser('Model'), () => {
return this.sequelize.sync({ alter: true }) return this.sequelize.sync({ alter: true })
.then(() => this.sequelize.sync({ alter: true })); .then(() => this.sequelize.sync({ alter: true }));
}); });
describe('indexes', () => {
it('should create only one unique index for unique:true column', function() {
const User = this.sequelize.define('testSync', {
email: {
type: Sequelize.STRING,
unique: true
}
});
return User.sync({ force: true }).then(() => {
return this.sequelize.getQueryInterface().showIndex(User.getTableName());
}).then(results => {
if (dialect === 'sqlite') {
// SQLite doesn't treat primary key as index
expect(results).to.have.length(1);
} else {
expect(results).to.have.length(2);
expect(results.filter(r => r.primary)).to.have.length(1);
}
expect(results.filter(r => r.unique === true && r.primary === false)).to.have.length(1);
});
});
it('should create only one unique index for unique:true columns', function() {
const User = this.sequelize.define('testSync', {
email: {
type: Sequelize.STRING,
unique: true
},
phone: {
type: Sequelize.STRING,
unique: true
}
});
return User.sync({ force: true }).then(() => {
return this.sequelize.getQueryInterface().showIndex(User.getTableName());
}).then(results => {
if (dialect === 'sqlite') {
// SQLite doesn't treat primary key as index
expect(results).to.have.length(2);
} else {
expect(results).to.have.length(3);
expect(results.filter(r => r.primary)).to.have.length(1);
}
expect(results.filter(r => r.unique === true && r.primary === false)).to.have.length(2);
});
});
it('should create only one unique index for unique:true columns taking care of options.indexes', function() {
const User = this.sequelize.define('testSync', {
email: {
type: Sequelize.STRING,
unique: true
},
phone: {
type: Sequelize.STRING,
unique: true
}
}, {
indexes: [
{ name: 'wow_my_index', fields: ['email', 'phone'], unique: true }
]
});
return User.sync({ force: true }).then(() => {
return this.sequelize.getQueryInterface().showIndex(User.getTableName());
}).then(results => {
if (dialect === 'sqlite') {
// SQLite doesn't treat primary key as index
expect(results).to.have.length(3);
} else {
expect(results).to.have.length(4);
expect(results.filter(r => r.primary)).to.have.length(1);
}
expect(results.filter(r => r.unique === true && r.primary === false)).to.have.length(3);
expect(results.filter(r => r.name === 'wow_my_index')).to.have.length(1);
});
});
it('should create only one unique index for unique:name column', function() {
const User = this.sequelize.define('testSync', {
email: {
type: Sequelize.STRING,
unique: 'wow_my_index'
}
});
return User.sync({ force: true }).then(() => {
return this.sequelize.getQueryInterface().showIndex(User.getTableName());
}).then(results => {
if (dialect === 'sqlite') {
// SQLite doesn't treat primary key as index
expect(results).to.have.length(1);
} else {
expect(results).to.have.length(2);
expect(results.filter(r => r.primary)).to.have.length(1);
}
expect(results.filter(r => r.unique === true && r.primary === false)).to.have.length(1);
if (['postgres', 'sqlite'].indexOf(dialect) === -1) {
// Postgres/SQLite doesn't support naming indexes in create table
expect(results.filter(r => r.name === 'wow_my_index')).to.have.length(1);
}
});
});
it('should create only one unique index for unique:name columns', function() {
const User = this.sequelize.define('testSync', {
email: {
type: Sequelize.STRING,
unique: 'wow_my_index'
},
phone: {
type: Sequelize.STRING,
unique: 'wow_my_index'
}
});
return User.sync({ force: true }).then(() => {
return this.sequelize.getQueryInterface().showIndex(User.getTableName());
}).then(results => {
if (dialect === 'sqlite') {
// SQLite doesn't treat primary key as index
expect(results).to.have.length(1);
} else {
expect(results).to.have.length(2);
expect(results.filter(r => r.primary)).to.have.length(1);
}
expect(results.filter(r => r.unique === true && r.primary === false)).to.have.length(1);
if (['postgres', 'sqlite'].indexOf(dialect) === -1) {
// Postgres/SQLite doesn't support naming indexes in create table
expect(results.filter(r => r.name === 'wow_my_index')).to.have.length(1);
}
});
});
});
}); });
}); });
...@@ -8,13 +8,6 @@ const dialect = Support.getTestDialect(); ...@@ -8,13 +8,6 @@ const dialect = Support.getTestDialect();
const Sequelize = Support.Sequelize; const Sequelize = Support.Sequelize;
const current = Support.sequelize; const current = Support.sequelize;
const _ = require('lodash'); const _ = require('lodash');
let count = 0;
const log = function() {
// sqlite fires a lot more querys than the other dbs. this is just a simple hack, since i'm lazy
if (dialect !== 'sqlite' || count === 0) {
count++;
}
};
describe(Support.getTestDialectTeaser('QueryInterface'), () => { describe(Support.getTestDialectTeaser('QueryInterface'), () => {
beforeEach(function() { beforeEach(function() {
...@@ -451,148 +444,6 @@ describe(Support.getTestDialectTeaser('QueryInterface'), () => { ...@@ -451,148 +444,6 @@ describe(Support.getTestDialectTeaser('QueryInterface'), () => {
}); });
}); });
describe('changeColumn', () => {
it('should support schemas', function() {
return this.sequelize.createSchema('archive').bind(this).then(function() {
return this.queryInterface.createTable({
tableName: 'users',
schema: 'archive'
}, {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
currency: DataTypes.INTEGER
}).bind(this).then(function() {
return this.queryInterface.changeColumn({
tableName: 'users',
schema: 'archive'
}, 'currency', {
type: DataTypes.FLOAT
});
}).then(function() {
return this.queryInterface.describeTable({
tableName: 'users',
schema: 'archive'
});
}).then(table => {
if (dialect === 'postgres' || dialect === 'postgres-native') {
expect(table.currency.type).to.equal('DOUBLE PRECISION');
} else {
expect(table.currency.type).to.equal('FLOAT');
}
});
});
});
it('should change columns', function() {
return this.queryInterface.createTable({
tableName: 'users'
}, {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
currency: DataTypes.INTEGER
}).bind(this).then(function() {
return this.queryInterface.changeColumn('users', 'currency', {
type: DataTypes.FLOAT,
allowNull: true
});
}).then(function() {
return this.queryInterface.describeTable({
tableName: 'users'
});
}).then(table => {
if (dialect === 'postgres' || dialect === 'postgres-native') {
expect(table.currency.type).to.equal('DOUBLE PRECISION');
} else {
expect(table.currency.type).to.equal('FLOAT');
}
});
});
// MSSQL doesn't support using a modified column in a check constraint.
// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-table-transact-sql
if (dialect !== 'mssql') {
it('should work with enums', function() {
return this.queryInterface.createTable({
tableName: 'users'
}, {
firstName: DataTypes.STRING
}).bind(this).then(function() {
return this.queryInterface.changeColumn('users', 'firstName', {
type: DataTypes.ENUM(['value1', 'value2', 'value3'])
});
});
});
it('should work with enums with schemas', function() {
return this.sequelize.createSchema('archive').bind(this).then(function() {
return this.queryInterface.createTable({
tableName: 'users',
schema: 'archive'
}, {
firstName: DataTypes.STRING
});
}).bind(this).then(function() {
return this.queryInterface.changeColumn({
tableName: 'users',
schema: 'archive'
}, 'firstName', {
type: DataTypes.ENUM(['value1', 'value2', 'value3'])
});
});
});
}
});
//SQlite navitely doesnt support ALTER Foreign key
if (dialect !== 'sqlite') {
describe('should support foreign keys', () => {
beforeEach(function() {
return this.queryInterface.createTable('users', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
level_id: {
type: DataTypes.INTEGER,
allowNull: false
}
})
.bind(this).then(function() {
return this.queryInterface.createTable('level', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
}
});
});
});
it('able to change column to foreign key', function() {
return this.queryInterface.changeColumn('users', 'level_id', {
type: DataTypes.INTEGER,
references: {
model: 'level',
key: 'id'
},
onUpdate: 'cascade',
onDelete: 'cascade'
}, {logging: log}).then(() => {
expect(count).to.be.equal(1);
count = 0;
});
});
});
}
describe('addColumn', () => { describe('addColumn', () => {
beforeEach(function() { beforeEach(function() {
return this.sequelize.createSchema('archive').bind(this).then(function() { return this.sequelize.createSchema('archive').bind(this).then(function() {
...@@ -669,181 +520,6 @@ describe(Support.getTestDialectTeaser('QueryInterface'), () => { ...@@ -669,181 +520,6 @@ describe(Support.getTestDialectTeaser('QueryInterface'), () => {
}); });
}); });
describe('removeColumn', () => {
describe('(without a schema)', () => {
beforeEach(function() {
return this.queryInterface.createTable('users', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstName: {
type: DataTypes.STRING,
defaultValue: 'Someone'
},
lastName: {
type: DataTypes.STRING
},
manager: {
type: DataTypes.INTEGER,
references: {
model: 'users',
key: 'id'
}
},
email: {
type: DataTypes.STRING,
unique: true
}
});
});
it('should be able to remove a column with a default value', function() {
return this.queryInterface.removeColumn('users', 'firstName').bind(this).then(function() {
return this.queryInterface.describeTable('users');
}).then(table => {
expect(table).to.not.have.property('firstName');
});
});
it('should be able to remove a column without default value', function() {
return this.queryInterface.removeColumn('users', 'lastName').bind(this).then(function() {
return this.queryInterface.describeTable('users');
}).then(table => {
expect(table).to.not.have.property('lastName');
});
});
it('should be able to remove a column with a foreign key constraint', function() {
return this.queryInterface.removeColumn('users', 'manager').bind(this).then(function() {
return this.queryInterface.describeTable('users');
}).then(table => {
expect(table).to.not.have.property('manager');
});
});
it('should be able to remove a column with primaryKey', function() {
return this.queryInterface.removeColumn('users', 'manager').bind(this).then(function() {
return this.queryInterface.describeTable('users');
}).then(function(table) {
expect(table).to.not.have.property('manager');
return this.queryInterface.removeColumn('users', 'id');
}).then(function() {
return this.queryInterface.describeTable('users');
}).then(table => {
expect(table).to.not.have.property('id');
});
});
// From MSSQL documentation on ALTER COLUMN:
// The modified column cannot be any one of the following:
// - Used in a CHECK or UNIQUE constraint.
// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-table-transact-sql#arguments
if (dialect !== 'mssql') {
it('should be able to remove a column with unique contraint', function() {
return this.queryInterface.removeColumn('users', 'email').bind(this).then(function() {
return this.queryInterface.describeTable('users');
}).then(table => {
expect(table).to.not.have.property('email');
});
});
}
});
describe('(with a schema)', () => {
beforeEach(function() {
return this.sequelize.createSchema('archive').bind(this).then(function() {
return this.queryInterface.createTable({
tableName: 'users',
schema: 'archive'
}, {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstName: {
type: DataTypes.STRING,
defaultValue: 'Someone'
},
lastName: {
type: DataTypes.STRING
},
email: {
type: DataTypes.STRING,
unique: true
}
});
});
});
it('should be able to remove a column with a default value', function() {
return this.queryInterface.removeColumn({
tableName: 'users',
schema: 'archive'
}, 'firstName'
).bind(this).then(function() {
return this.queryInterface.describeTable({
tableName: 'users',
schema: 'archive'
});
}).then(table => {
expect(table).to.not.have.property('firstName');
});
});
it('should be able to remove a column without default value', function() {
return this.queryInterface.removeColumn({
tableName: 'users',
schema: 'archive'
}, 'lastName'
).bind(this).then(function() {
return this.queryInterface.describeTable({
tableName: 'users',
schema: 'archive'
});
}).then(table => {
expect(table).to.not.have.property('lastName');
});
});
it('should be able to remove a column with primaryKey', function() {
return this.queryInterface.removeColumn({
tableName: 'users',
schema: 'archive'
}, 'id').bind(this).then(function() {
return this.queryInterface.describeTable({
tableName: 'users',
schema: 'archive'
});
}).then(table => {
expect(table).to.not.have.property('id');
});
});
// From MSSQL documentation on ALTER COLUMN:
// The modified column cannot be any one of the following:
// - Used in a CHECK or UNIQUE constraint.
// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-table-transact-sql#arguments
if (dialect !== 'mssql') {
it('should be able to remove a column with unique contraint', function() {
return this.queryInterface.removeColumn({
tableName: 'users',
schema: 'archive'
}, 'email').bind(this).then(function() {
return this.queryInterface.describeTable({
tableName: 'users',
schema: 'archive'
});
}).then(table => {
expect(table).to.not.have.property('email');
});
});
}
});
});
describe('describeForeignKeys', () => { describe('describeForeignKeys', () => {
beforeEach(function() { beforeEach(function() {
return this.queryInterface.createTable('users', { return this.queryInterface.createTable('users', {
......
'use strict';
const chai = require('chai');
const expect = chai.expect;
const Support = require(__dirname + '/../support');
const DataTypes = require(__dirname + '/../../../lib/data-types');
const dialect = Support.getTestDialect();
let count = 0;
function log() {
// sqlite fires a lot more querys than the other dbs. this is just a simple hack, since i'm lazy
if (dialect !== 'sqlite' || count === 0) {
count++;
}
};
describe(Support.getTestDialectTeaser('QueryInterface'), () => {
beforeEach(function() {
this.sequelize.options.quoteIdenifiers = true;
this.queryInterface = this.sequelize.getQueryInterface();
});
afterEach(function() {
return this.sequelize.dropAllSchemas();
});
describe('changeColumn', () => {
it('should support schemas', function() {
return this.sequelize.createSchema('archive').bind(this).then(function() {
return this.queryInterface.createTable({
tableName: 'users',
schema: 'archive'
}, {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
currency: DataTypes.INTEGER
}).bind(this).then(function() {
return this.queryInterface.changeColumn({
tableName: 'users',
schema: 'archive'
}, 'currency', {
type: DataTypes.FLOAT
});
}).then(function() {
return this.queryInterface.describeTable({
tableName: 'users',
schema: 'archive'
});
}).then(table => {
if (dialect === 'postgres' || dialect === 'postgres-native') {
expect(table.currency.type).to.equal('DOUBLE PRECISION');
} else {
expect(table.currency.type).to.equal('FLOAT');
}
});
});
});
it('should change columns', function() {
return this.queryInterface.createTable({
tableName: 'users'
}, {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
currency: DataTypes.INTEGER
}).bind(this).then(function() {
return this.queryInterface.changeColumn('users', 'currency', {
type: DataTypes.FLOAT,
allowNull: true
});
}).then(function() {
return this.queryInterface.describeTable({
tableName: 'users'
});
}).then(table => {
if (dialect === 'postgres' || dialect === 'postgres-native') {
expect(table.currency.type).to.equal('DOUBLE PRECISION');
} else {
expect(table.currency.type).to.equal('FLOAT');
}
});
});
// MSSQL doesn't support using a modified column in a check constraint.
// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-table-transact-sql
if (dialect !== 'mssql') {
it('should work with enums', function() {
return this.queryInterface.createTable({
tableName: 'users'
}, {
firstName: DataTypes.STRING
}).bind(this).then(function() {
return this.queryInterface.changeColumn('users', 'firstName', {
type: DataTypes.ENUM(['value1', 'value2', 'value3'])
});
});
});
it('should work with enums with schemas', function() {
return this.sequelize.createSchema('archive').bind(this).then(function() {
return this.queryInterface.createTable({
tableName: 'users',
schema: 'archive'
}, {
firstName: DataTypes.STRING
});
}).bind(this).then(function() {
return this.queryInterface.changeColumn({
tableName: 'users',
schema: 'archive'
}, 'firstName', {
type: DataTypes.ENUM(['value1', 'value2', 'value3'])
});
});
});
}
//SQlite navitely doesnt support ALTER Foreign key
if (dialect !== 'sqlite') {
describe('should support foreign keys', () => {
beforeEach(function() {
return this.queryInterface.createTable('users', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
level_id: {
type: DataTypes.INTEGER,
allowNull: false
}
})
.bind(this).then(function() {
return this.queryInterface.createTable('level', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
}
});
});
});
it('able to change column to foreign key', function() {
return this.queryInterface.changeColumn('users', 'level_id', {
type: DataTypes.INTEGER,
references: {
model: 'level',
key: 'id'
},
onUpdate: 'cascade',
onDelete: 'cascade'
}, {logging: log}).then(() => {
expect(count).to.be.equal(1);
count = 0;
});
});
});
}
});
});
\ No newline at end of file
'use strict';
const chai = require('chai');
const expect = chai.expect;
const Support = require(__dirname + '/../support');
const DataTypes = require(__dirname + '/../../../lib/data-types');
const dialect = Support.getTestDialect();
describe(Support.getTestDialectTeaser('QueryInterface'), () => {
beforeEach(function() {
this.sequelize.options.quoteIdenifiers = true;
this.queryInterface = this.sequelize.getQueryInterface();
});
afterEach(function() {
return this.sequelize.dropAllSchemas();
});
describe('removeColumn', () => {
describe('(without a schema)', () => {
beforeEach(function() {
return this.queryInterface.createTable('users', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstName: {
type: DataTypes.STRING,
defaultValue: 'Someone'
},
lastName: {
type: DataTypes.STRING
},
manager: {
type: DataTypes.INTEGER,
references: {
model: 'users',
key: 'id'
}
},
email: {
type: DataTypes.STRING,
unique: true
}
});
});
it('should be able to remove a column with a default value', function() {
return this.queryInterface.removeColumn('users', 'firstName').bind(this).then(function() {
return this.queryInterface.describeTable('users');
}).then(table => {
expect(table).to.not.have.property('firstName');
});
});
it('should be able to remove a column without default value', function() {
return this.queryInterface.removeColumn('users', 'lastName').bind(this).then(function() {
return this.queryInterface.describeTable('users');
}).then(table => {
expect(table).to.not.have.property('lastName');
});
});
it('should be able to remove a column with a foreign key constraint', function() {
return this.queryInterface.removeColumn('users', 'manager').bind(this).then(function() {
return this.queryInterface.describeTable('users');
}).then(table => {
expect(table).to.not.have.property('manager');
});
});
it('should be able to remove a column with primaryKey', function() {
return this.queryInterface.removeColumn('users', 'manager').bind(this).then(function() {
return this.queryInterface.describeTable('users');
}).then(function(table) {
expect(table).to.not.have.property('manager');
return this.queryInterface.removeColumn('users', 'id');
}).then(function() {
return this.queryInterface.describeTable('users');
}).then(table => {
expect(table).to.not.have.property('id');
});
});
// From MSSQL documentation on ALTER COLUMN:
// The modified column cannot be any one of the following:
// - Used in a CHECK or UNIQUE constraint.
// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-table-transact-sql#arguments
if (dialect !== 'mssql') {
it('should be able to remove a column with unique contraint', function() {
return this.queryInterface.removeColumn('users', 'email').bind(this).then(function() {
return this.queryInterface.describeTable('users');
}).then(table => {
expect(table).to.not.have.property('email');
});
});
}
});
describe('(with a schema)', () => {
beforeEach(function() {
return this.sequelize.createSchema('archive').then(() => {
return this.queryInterface.createTable({
tableName: 'users',
schema: 'archive'
}, {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstName: {
type: DataTypes.STRING,
defaultValue: 'Someone'
},
lastName: {
type: DataTypes.STRING
},
email: {
type: DataTypes.STRING,
unique: true
}
});
});
});
it('should be able to remove a column with a default value', function() {
return this.queryInterface.removeColumn({
tableName: 'users',
schema: 'archive'
}, 'firstName'
).bind(this).then(function() {
return this.queryInterface.describeTable({
tableName: 'users',
schema: 'archive'
});
}).then(table => {
expect(table).to.not.have.property('firstName');
});
});
it('should be able to remove a column without default value', function() {
return this.queryInterface.removeColumn({
tableName: 'users',
schema: 'archive'
}, 'lastName'
).bind(this).then(function() {
return this.queryInterface.describeTable({
tableName: 'users',
schema: 'archive'
});
}).then(table => {
expect(table).to.not.have.property('lastName');
});
});
it('should be able to remove a column with primaryKey', function() {
return this.queryInterface.removeColumn({
tableName: 'users',
schema: 'archive'
}, 'id').bind(this).then(function() {
return this.queryInterface.describeTable({
tableName: 'users',
schema: 'archive'
});
}).then(table => {
expect(table).to.not.have.property('id');
});
});
// From MSSQL documentation on ALTER COLUMN:
// The modified column cannot be any one of the following:
// - Used in a CHECK or UNIQUE constraint.
// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-table-transact-sql#arguments
if (dialect !== 'mssql') {
it('should be able to remove a column with unique contraint', function() {
return this.queryInterface.removeColumn({
tableName: 'users',
schema: 'archive'
}, 'email').bind(this).then(function() {
return this.queryInterface.describeTable({
tableName: 'users',
schema: 'archive'
});
}).then(table => {
expect(table).to.not.have.property('email');
});
});
}
});
});
});
\ No newline at end of file
...@@ -161,7 +161,7 @@ if (dialect === 'mysql') { ...@@ -161,7 +161,7 @@ if (dialect === 'mysql') {
expectation: 'CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255), `otherId` INTEGER, FOREIGN KEY (`otherId`) REFERENCES `otherTable` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION) ENGINE=InnoDB;' expectation: 'CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255), `otherId` INTEGER, FOREIGN KEY (`otherId`) REFERENCES `otherTable` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION) ENGINE=InnoDB;'
}, },
{ {
arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}, {uniqueKeys: [{fields: ['title', 'name']}]}], arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}, {uniqueKeys: [{fields: ['title', 'name'], customIndex: true}]}],
expectation: 'CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255), UNIQUE `uniq_myTable_title_name` (`title`, `name`)) ENGINE=InnoDB;' expectation: 'CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255), UNIQUE `uniq_myTable_title_name` (`title`, `name`)) ENGINE=InnoDB;'
}, },
{ {
......
...@@ -142,7 +142,7 @@ if (dialect === 'sqlite') { ...@@ -142,7 +142,7 @@ if (dialect === 'sqlite') {
expectation: 'CREATE TABLE IF NOT EXISTS `myTable` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` VARCHAR(255));' expectation: 'CREATE TABLE IF NOT EXISTS `myTable` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` VARCHAR(255));'
}, },
{ {
arguments: ['myTable', {id: 'INTEGER PRIMARY KEY AUTOINCREMENT', name: 'VARCHAR(255)', surname: 'VARCHAR(255)'}, {uniqueKeys: {uniqueConstraint: {fields: ['name', 'surname']}}}], arguments: ['myTable', {id: 'INTEGER PRIMARY KEY AUTOINCREMENT', name: 'VARCHAR(255)', surname: 'VARCHAR(255)'}, {uniqueKeys: {uniqueConstraint: {fields: ['name', 'surname'], customIndex: true }}}],
expectation: 'CREATE TABLE IF NOT EXISTS `myTable` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` VARCHAR(255), `surname` VARCHAR(255), UNIQUE (`name`, `surname`));' expectation: 'CREATE TABLE IF NOT EXISTS `myTable` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` VARCHAR(255), `surname` VARCHAR(255), UNIQUE (`name`, `surname`));'
} }
], ],
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!