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

Commit f98bd7ed by Hugo Denizart Committed by GitHub

feat(add-constraint): add deferrable option (#13096)

Co-authored-by: Pedro Augusto de Paula Barbosa <papb1996@gmail.com>
1 parent 48225948
......@@ -684,6 +684,11 @@ class QueryGenerator {
break;
default: throw new Error(`${options.type} is invalid.`);
}
if (options.deferrable && ['UNIQUE', 'PRIMARY KEY', 'FOREIGN KEY'].includes(options.type.toUpperCase())) {
constraintSnippet += ` ${this.deferConstraintsQuery(options)}`;
}
return constraintSnippet;
}
......
......@@ -703,6 +703,7 @@ class QueryInterface {
* @param {string} [options.references.table] Target table name
* @param {string} [options.references.field] Target column name
* @param {string} [options.references.fields] Target column names for a composite primary key. Must match the order of fields in options.fields.
* @param {string} [options.deferrable] Sets the constraint to be deferred or immediately checked. See Sequelize.Deferrable. PostgreSQL Only
*
* @returns {Promise}
*/
......
......@@ -13,92 +13,136 @@ if (!Support.sequelize.dialect.supports.deferrableConstraints) {
describe(Support.getTestDialectTeaser('Sequelize'), () => {
describe('Deferrable', () => {
beforeEach(function() {
this.run = async function(deferrable, options) {
options = options || {};
const taskTableName = options.taskTableName || `tasks_${config.rand()}`;
const transactionOptions = { deferrable: Sequelize.Deferrable.SET_DEFERRED, ...options };
const userTableName = `users_${config.rand()}`;
const User = this.sequelize.define(
'User', { name: Sequelize.STRING }, { tableName: userTableName }
);
const Task = this.sequelize.define(
'Task', {
title: Sequelize.STRING,
user_id: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: userTableName,
key: 'id',
deferrable
}
}
}, {
tableName: taskTableName
}
);
await User.sync({ force: true });
await Task.sync({ force: true });
return this.sequelize.transaction(transactionOptions, async t => {
const task0 = await Task
.create({ title: 'a task', user_id: -1 }, { transaction: t });
const describeDeferrableTest = (title, defineModels) => {
describe(title, () => {
beforeEach(function() {
this.run = async function(deferrable, options) {
options = options || {};
const taskTableName = options.taskTableName || `tasks_${config.rand()}`;
const transactionOptions = { deferrable: Sequelize.Deferrable.SET_DEFERRED, ...options };
const userTableName = `users_${config.rand()}`;
const { Task, User } = await defineModels({ sequelize: this.sequelize, userTableName, deferrable, taskTableName });
return this.sequelize.transaction(transactionOptions, async t => {
const task0 = await Task
.create({ title: 'a task', user_id: -1 }, { transaction: t });
const [task, user] = await Promise.all([task0, User.create({}, { transaction: t })]);
task.user_id = user.id;
return task.save({ transaction: t });
});
};
});
const [task, user] = await Promise.all([task0, User.create({}, { transaction: t })]);
task.user_id = user.id;
return task.save({ transaction: t });
describe('NOT', () => {
it('does not allow the violation of the foreign key constraint', async function() {
await expect(this.run(Sequelize.Deferrable.NOT)).to.eventually.be.rejectedWith(Sequelize.ForeignKeyConstraintError);
});
});
};
});
describe('NOT', () => {
it('does not allow the violation of the foreign key constraint', async function() {
await expect(this.run(Sequelize.Deferrable.NOT)).to.eventually.be.rejectedWith(Sequelize.ForeignKeyConstraintError);
});
});
describe('INITIALLY_IMMEDIATE', () => {
it('allows the violation of the foreign key constraint if the transaction is deferred', async function() {
const task = await this
.run(Sequelize.Deferrable.INITIALLY_IMMEDIATE);
describe('INITIALLY_IMMEDIATE', () => {
it('allows the violation of the foreign key constraint if the transaction is deferred', async function() {
const task = await this
.run(Sequelize.Deferrable.INITIALLY_IMMEDIATE);
expect(task.title).to.equal('a task');
expect(task.user_id).to.equal(1);
});
expect(task.title).to.equal('a task');
expect(task.user_id).to.equal(1);
});
it('does not allow the violation of the foreign key constraint if the transaction is not deffered', async function() {
await expect(this.run(Sequelize.Deferrable.INITIALLY_IMMEDIATE, {
deferrable: undefined
})).to.eventually.be.rejectedWith(Sequelize.ForeignKeyConstraintError);
});
it('does not allow the violation of the foreign key constraint if the transaction is not deffered', async function() {
await expect(this.run(Sequelize.Deferrable.INITIALLY_IMMEDIATE, {
deferrable: undefined
})).to.eventually.be.rejectedWith(Sequelize.ForeignKeyConstraintError);
});
it('allows the violation of the foreign key constraint if the transaction deferres only the foreign key constraint', async function() {
const taskTableName = `tasks_${config.rand()}`;
it('allows the violation of the foreign key constraint if the transaction deferres only the foreign key constraint', async function() {
const taskTableName = `tasks_${config.rand()}`;
const task = await this
.run(Sequelize.Deferrable.INITIALLY_IMMEDIATE, {
deferrable: Sequelize.Deferrable.SET_DEFERRED([`${taskTableName}_user_id_fkey`]),
taskTableName
});
const task = await this
.run(Sequelize.Deferrable.INITIALLY_IMMEDIATE, {
deferrable: Sequelize.Deferrable.SET_DEFERRED([`${taskTableName}_user_id_fkey`]),
taskTableName
expect(task.title).to.equal('a task');
expect(task.user_id).to.equal(1);
});
});
describe('INITIALLY_DEFERRED', () => {
it('allows the violation of the foreign key constraint', async function() {
const task = await this
.run(Sequelize.Deferrable.INITIALLY_DEFERRED);
expect(task.title).to.equal('a task');
expect(task.user_id).to.equal(1);
expect(task.title).to.equal('a task');
expect(task.user_id).to.equal(1);
});
});
});
});
};
describeDeferrableTest('set in define', async ({ sequelize, userTableName, deferrable, taskTableName }) => {
const User = sequelize.define(
'User', { name: Sequelize.STRING }, { tableName: userTableName }
);
const Task = sequelize.define(
'Task', {
title: Sequelize.STRING,
user_id: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: userTableName,
key: 'id',
deferrable
}
}
}, {
tableName: taskTableName
}
);
await User.sync({ force: true });
await Task.sync({ force: true });
describe('INITIALLY_DEFERRED', () => {
it('allows the violation of the foreign key constraint', async function() {
const task = await this
.run(Sequelize.Deferrable.INITIALLY_DEFERRED);
return { Task, User };
});
expect(task.title).to.equal('a task');
expect(task.user_id).to.equal(1);
describeDeferrableTest('set in addConstraint', async ({ sequelize, userTableName, deferrable, taskTableName }) => {
const User = sequelize.define(
'User', { name: Sequelize.STRING }, { tableName: userTableName }
);
const Task = sequelize.define(
'Task', {
title: Sequelize.STRING,
user_id: {
allowNull: false,
type: Sequelize.INTEGER
}
}, {
tableName: taskTableName
}
);
await User.sync({ force: true });
await Task.sync({ force: true });
await sequelize.getQueryInterface().addConstraint(taskTableName, {
fields: ['user_id'],
type: 'foreign key',
name: `${taskTableName}_user_id_fkey`,
deferrable,
references: {
table: userTableName,
field: 'id'
}
});
return { Task, User };
});
});
});
......@@ -15,6 +15,7 @@ import { Sequelize, RetryOptions } from './sequelize';
import { Transaction } from './transaction';
import { SetRequired } from './../type-helpers/set-required';
import { Fn, Literal } from './utils';
import { Deferrable } from './deferrable';
type BindOrReplacements = { [key: string]: unknown } | unknown[];
type FieldMap = { [key: string]: string };
......@@ -216,6 +217,7 @@ export interface BaseConstraintOptions {
export interface AddUniqueConstraintOptions extends BaseConstraintOptions {
type: 'unique';
deferrable?: Deferrable;
}
export interface AddDefaultConstraintOptions extends BaseConstraintOptions {
......@@ -230,6 +232,7 @@ export interface AddCheckConstraintOptions extends BaseConstraintOptions {
export interface AddPrimaryKeyConstraintOptions extends BaseConstraintOptions {
type: 'primary key';
deferrable?: Deferrable;
}
export interface AddForeignKeyConstraintOptions extends BaseConstraintOptions {
......@@ -240,6 +243,7 @@ export interface AddForeignKeyConstraintOptions extends BaseConstraintOptions {
};
onDelete: string;
onUpdate: string;
deferrable?: Deferrable;
}
export type AddConstraintOptions =
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!