question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

BulkCreate with composite unique constraint does not work

See original GitHub issue

Issue Description

What are you doing?

I am trying to use bulkCreate to updateOnDuplicate when a duplicate is detected. Duplicate should be detected by a “multi column unique index”.

"use strict";

const Sequelize = require("./index");
const sequelize = require("./test/support").createSequelizeInstance();
const chai = require("chai"),
  expect = chai.expect;

class Bar extends Sequelize.Model {}
Bar.init(
  {
    value: Sequelize.DataTypes.STRING,
    timestamp: {
      type: Sequelize.DataTypes.DATE,
      allowNull: false
    }
  },
  {
    indexes: [
      {
        name: "unique_timestamp_per_foo",
        unique: true,
        fields: ["timestamp", "FooId"]
      }
    ],
    sequelize
  }
);

class Foo extends Sequelize.Model {}
Foo.init({}, { sequelize });

Bar.belongsTo(Foo, {
  foreignKey: { allowNull: false },
  onDelete: "CASCADE"
});
Foo.hasMany(Bar, { foreignKey: { allowNull: false }, onDelete: "CASCADE" });

(async () => {
  await sequelize.sync({ force: true });

  // Create a _parent_ `Foo` model
  const foo = await Foo.create();

  // Create a `Bar` object
  const originalBar = await Bar.create({
    value: "original",
    timestamp: new Date(Date.UTC(2016, 0, 1)),
    FooId: foo.id
  });

  // We obviously expect a `Bar` object to exist with the unique constraints of `timestamp` and `FooId`
  expect(
    await Bar.count({
      where: { FooId: foo.id, timestamp: new Date(Date.UTC(2016, 0, 1)) }
    })
  ).to.equal(1);

  // We create a modified `Bar` object that has an overlapping `timestamp` and `FooId`
  // This should be detected as duplicate by the `uniqueTimestampPerFoo` unique constraint/index,
  // during the next `bulkCreate` operation with `updateOnDuplicate` set.
  const modifiedBar = Bar.build({
    value: "modified",
    timestamp: new Date(Date.UTC(2016, 0, 1)),
    FooId: foo.id
  });

  // "Bulk" create to "upsert" a modified `existingBar` when conflict detected.
  // We only want `value` and `updatedAt` to be updated on conflict.
  await expect(
    Bar.bulkCreate([modifiedBar.toJSON()], {
      updateOnDuplicate: ["value", "updatedAt"],
      logging: console.log
    })
  ).not.to.be.rejectedWith(Sequelize.SequelizeUniqueConstraintError);

  // *** The above function should not have been rejected with a `SequelizeUniqueConstraintError` ***

  // *** The below is what should have been expected ***

  // Reload the modified original `Bar` object by `id`
  const refreshedOriginalBar = await Bar.findByPk(originalBar.id);

  // The value should have been updated
  expect(refreshedOriginalBar.value).to.equal("modified");

  // The refreshed updatedAt should be greater than the original updatedAt
  expect(refreshedOriginalBar.updatedAt > originalBar.updatedAt).be.true;
})();

What do you expect to happen?

See the expected assertions from my SSCCE:

  // *** The below is what should have been expected ***

  // Reload the modified original `Bar` object by `id`
  const refreshedOriginalBar = await Bar.findByPk(originalBar.id);

  // The value should have been updated
  expect(refreshedOriginalBar.value).to.equal("modified");

  // The refreshed updatedAt should be greater than the original updatedAt
  expect(refreshedOriginalBar.updatedAt > originalBar.updatedAt).be.true;

I wanted a duplicate to be detected by the unique_timestamp_per_foo unique key when calling the bulkCreate method with updateOnDuplicate array set.

Furthermore, I wanted the insert SQL to be ON CONFLICT ("timestamp", "FooId") instead of ON CONFLICT ("id")

What is actually happening?

A SequelizeUniqueConstraintError is thrown because the expected multi-field index does not get added to upsertKeys here (https://github.com/sequelize/sequelize/blob/3517eb7e7bf5ce07cd33ad61fec291d0e8892d49/lib/model.js#L2705) because it is filtered out.

Environment

  • Sequelize version: sequelize@0.0.0-development
  • Node.js v10.16.3
  • Operating System: Windows 10

Issue Template Checklist

How does this problem relate to dialects?

  • I think this problem happens regardless of the dialect.
  • I think this problem happens only for the following dialect(s): Postgres

Would you be willing to resolve this issue by submitting a Pull Request?

  • Yes, I have the time and I know how to start.
  • Yes, I have the time but I don’t know how to start, I would need guidance.
  • No, I don’t have the time, although I believe I could do it if I had the time…
  • No, I don’t have the time and I wouldn’t even know how to start.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:4
  • Comments:14 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
fnimickcommented, Oct 25, 2019

@Rochet2 I’ve created a patch for my fix: https://gist.github.com/fnimick/5dd3c6855cea6e000c86d1bc38876958

PR coming shortly.

1reaction
vbhakta8commented, Mar 12, 2020

Do we have an ETA on when this PR might get looked at? Or how we can get it merged in? Is there any work required on contributors end to get this pushed through?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Enforce composite unique constraint that depends on parent ...
My first approach to the problem is to get rid of the table seat , other than being able to enumerate all the...
Read more >
Unique Constraints and Check Constraints - SQL Server
A CHECK constraint returns TRUE when the condition it is checking is not FALSE for any row in the table. A CHECK constraint...
Read more >
Defining Constraints and Indexes
This approach can't work when two or more foreign key constraints are involved in a ... Column("col3", Integer), # explicit/composite unique constraint.
Read more >
Documentation: 15: 11.6. Unique Indexes - PostgreSQL
When an index is declared unique, multiple table rows with equal indexed values are not allowed. Null values are not considered equal. A...
Read more >
SQL UNIQUE Constraint - W3Schools
The UNIQUE constraint ensures that all values in a column are different. ... SQL UNIQUE Constraint on CREATE TABLE ... ID int NOT...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found