sequelize / sequelize/sequelize

BelongsToMany Instance.add{$plural} sourceKey get() used, but set() on join table not

Open
#11,465 7 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

existing workaround type: bug
Dominant language
TypeScript
Stars
30.4k
Forks
4.3k
Avg merge
1d 6h
Merged PRs (30d)
68

Description

BelongsToMany association doesn't use custom set on sourceKey field.

I have custom set() and get() on my primary key on a message model. I'm using ulid on a BINARY(16) field. In my API I'm using the canonical form, e.g. '01DNBHWZZ9CMW7MW5Z9NHFNTNF'.

This works fine in and of itself, but as soon as I begin creating associations, the get is fired to get the association key, but the set is not when saving into the association table, or into a foreignKey field, etc.

getUlid and setUlid are functions for converting between binary and hex, and vice versa (UlidMonotonic is imported from npm package id128).

function getUlid( this: any ) {
  // outcoming Buffer -> canonical
  let ulidMonotonic = UlidMonotonic.construct( this.getDataValue('ulid') )
  return ulidMonotonic.toCanonical()
}
function setUlid( this: any, value: Buffer ) {
  //incoming canonical --> Buffer
  let ulidBuffer = UlidMonotonic.fromCanonical( value )
  this.setDataValue('ulid', Buffer.from(ulidBuffer.bytes))
}

Message table:

  Message.init({
      ulid: {
        type: 'BINARY(16)'
      , primaryKey: true
      , get: getUlid
      , set: setUlid
      }
    , message: {
        type: DataTypes.STRING(191).BINARY
      , allowNull: false
      }
    }
  , {
      sequelize
    , tableName
    }
  )
  Message.belongsToMany(User, { as: "receivers", through: "messageReceivers" })

Association table:

  MessageReceiver.init({
      ulid: {
        type: 'BINARY(16)'
      , primaryKey: true
      }
    , messageUlid: {
        type: 'BINARY(16)'
      , references: {
          model: Message
        , key: 'ulid'
        }
      , get: getUlid
      , set: setUlid
      }
    , userUlid: {
        type: 'BINARY(16)'
      , references: {
          model: User
        , key: 'ulid'
        }
      }
    }
  , {
      sequelize
    , tableName
    }
  )

Cut down version of User:

  User.init(
    {
      ulid: {
        type: 'BINARY(16)'
      , primaryKey: true
      }
    , firstName: { type: new DataTypes.STRING(32).BINARY, allowNull: false }
    , lastName: { type: new DataTypes.STRING(32).BINARY, allowNull: false }
    , role: { type: new DataTypes.STRING(32).BINARY, allowNull: false }
    }
  , {
      sequelize
    , tableName
    }
  )
I expect that .addReceivers([user1, user2]) should use the defined set() method on the messageUlid field to store the ulid in binary form on the association table.
Instead, this error occurs because it's trying to insert a 26 character string into a 16 byte binary field.
sqlMessage: "Data too long for column 'MessageUlid' at row 1",                                                                                                  
sql: "INSERT INTO 'messageReceivers' ('createdAt','updatedAt','MessageUlid','UserUlid') VALUES ('2019-09-22 03:58:28','2019-09-22 03:58:28','01DNBHWZZ9CMW7MW5Z9 NHFNTNF',X'016d571e7fe96548b85ff5c30820765d'),('2019-09-22 03:58:28','2019-09-22 03:58:28','01DNBHWZZ9CMW7MW5Z9NHFNTNF',X'016d571e7fe96545959960e350c63e05');"
Environment
  • Sequelize version: 5.15.0
  • Node.js version: v12.6.0
  • Operating System: Archlinux

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):
  • I don't know, I was using mariadb, with connector library version XXX and database version XXX
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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the BelongsToMany instance add{$plural} path exercised by .addReceivers([user1, user2]) and trace how sourceKey values are written to the association table. Reproduce the binary(16) ULID case with the shown custom getUlid and setUlid methods; done means the association insert invokes the setter and stores the binary value rather than the 26-character string.

Written by the indexing model from the issue text.

Assessment

Tech stack
mariadb, node.js, typescript
Domain
backend, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.