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.

Double include when reloading an instance with scopes

See original GitHub issue

What are you doing?

Trying to use a scope within an include. The example is contrived but in our codebase we actually reuse lots of named scopes. (This is a reopen of #10501 , as the bug appears to still exist on version 5.8.6.)

/**
 * Model definitions
 */
const User = sequelize.define('user', {
  id: { type: Sequelize.INTEGER, primaryKey: true },
  name: Sequelize.STRING
})

const Project = sequelize.define('project', {
  id: { type: Sequelize.INTEGER, primaryKey: true },
  title: Sequelize.STRING,
  userid: { type: Sequelize.INTEGER, references: { model: 'user', key: 'id' } }
})

const Task = sequelize.define('task', {
  id: { type: Sequelize.INTEGER, primaryKey: true },
  description: Sequelize.STRING,
  projectid: { type: Sequelize.INTEGER, references: { model: 'project', key: 'id' } }
})

/**
 * Associations
 */
Project.belongsTo(User, { foreignKey: 'userid' })
Task.belongsTo(Project, { foreignKey: 'projectid' })

/**
 * Go for bug
 */
Task.findByPk(1, {
  include: [
    Project.scope({ include: [User] }) // This is the offending include
  ]
}).then(task => {
  // The reload() will crash because it will try to include Project->User twice
  task.reload().then(task => {
    console.log(task)
  })
})

To Reproduce

  • Fetch Sequelize instance with a scope containing an include
  • Try to reload that instance

What do you expect to happen?

The task instance should just reload.

What is actually happening?

I get an SQL error and a crash :

sqlite: Unhandled rejection SequelizeDatabaseError: SQLITE_ERROR: ambiguous column name: project->user.id PostgreSQL : Unhandled rejection SequelizeDatabaseError: table name "project->user" specified more than once

The offending SQL looks like this (sqlite)

select
	`task`.`id`,
	`task`.`description`,
	`task`.`projectid`,
	`project`.`id` as `project.id`,
	`project`.`title` as `project.title`,
	`project`.`userid` as `project.userid`,
	`project->user`.`id` as `project.user.id`,
	`project->user`.`name` as `project.user.name`,
	`project->user`.`id` as `project.user.id`,
	`project->user`.`name` as `project.user.name`
from
	`task` as `task`
left outer join `project` as `project` on
	`task`.`projectid` = `project`.`id`
left outer join `user` as `project->user` on
	`project`.`userid` = `project->user`.`id`
left outer join `user` as `project->user` on
	`project`.`userid` = `project->user`.`id`
where
	`task`.`id` = 1;

Environment

  • Dialect:

    • postgres
    • sqlite
  • Dialect library version: "sqlite3": "^4.0.6", "pg": "^7.4.1"

  • Database version: XXX

  • Sequelize version: 5.8.6

  • Node Version: v10.15.3

  • OS: Ubuntu 19.04

  • Tested with latest release:

    • No
    • Yes, specify that version: 5.8.6

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:5
  • Comments:12 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
juliocanarescommented, Nov 9, 2020

example to reproduce in sequelize 6.3.5

const Feature = sequelize.define()

const Vendor = sequelize.define()

Vendor.hasMany(Feature)

Vendor.scope('withFeatures', () => ({include:[Feature]}))
const vendor = await models.Vendor.scope(['withFeatures']).findOne()

vendor.reload() // failed with "SequelizeDatabaseError: Not unique table/alias: 'Features'"

digging in the code I could see that reload uses

this._options.include https://github.com/sequelize/sequelize/blob/master/src/model.js#L4331 from the reloaded model and inject the scopes again in the findAll method which is called by findOne, in findAll this._conformIncludes https://github.com/sequelize/sequelize/blob/master/src/model.js#L1807 does not call to this._uniqIncludes https://github.com/sequelize/sequelize/blob/master/src/model.js#L850 that does the include deduplication, but it also fails in this case because of the omit function that uses ${include.model && include.model.name}-${include.as} https://github.com/sequelize/sequelize/blob/master/src/model.js#L854.

I fixed it by adding this._uniqIncludes right after this._conformIncludes and explicitly setting the alias in the scope

Vendor.scope('withFeatures', () => ({include:[{model: Feature, as: 'Features'}]}))
0reactions
Valetekcommented, Jun 2, 2022

Hello, as said upper, this is still present in Sequelize v6.5 too

After some investigation the issue is happening in sequelize/lib/model.js line 4070 (reload function)

    options = Utils.defaults({
      where: this.where()
    }, options, { 
      include: this._options.include || undefined // This include the previous include
    });

    const reloaded = await this.constructor.findOne(options); // This will trigger again the includes in the scopes

as a workaround I made this in my reload call

reload({ include: [] }) 
// This will override the include in _options and remove the duplicated call, should work even if it's not empty until it's not the same include than the one in the scope

Would be good if this case could be resolved still … scopes are useful

Read more comments on GitHub >

github_iconTop Results From Across the Web

Using Sequelize with associations and scopes with includes ...
I'm loading the model files ordered alphabetically. When I rename Task to, lets say Zask , I get an error since Zask is...
Read more >
Scopes - Sequelize
Observe how the four scopes were merged into one. The includes of scopes are merged based on the model being included. If one...
Read more >
How to preload Rails scopes - Justin Weiss
In your example, includes(:product) won't necessarily add the product table to the SQL clause. It could also do a second SQL call to...
Read more >
Authenticate workloads using service accounts - Google Cloud
You can enable multiple virtual machine instances to use the same service ... The combination of access scopes granted to the virtual machine...
Read more >
pytest fixtures: explicit, modular, scalable
Multiple test functions in a test module will thus each receive the same smtp_connection fixture instance, thus saving time. Possible values for scope...
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