Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix eager loading of HasManyThrough associations #533

Merged
merged 4 commits into from
May 25, 2019

Conversation

groue
Copy link
Owner

@groue groue commented May 24, 2019

This pull request fixes some bugs with eager loading of HasManyThrough associations.

The bugs may occur when two conditions are met:

  • One of the intermediate steps of the HasManyThrough association is a "to-one" association (belongsTo, hasOne, hasOneThrough).
  • The HasManyThrough association is eager-loaded in a request that also involves that to-one association.

For example, given this schema:

  1. A player belongs to a team
  2. A team has many competitions
  3. A player has many competitions through its team.
extension Player {
    static let team = belongsTo(Team.self)
    static let competitions = hasMany(
        Competition.self,
        through: team,
        using: Team.competitions)
}

Using both Player.team and Player.competitions is a single request may lead to unexpected or inconsistent behaviors.

A sample request which was not behaving correctly:

// WRONG
let buggyRequest = Player
    .including(required: Player.team)
    .including(all: Player.competitions)

After this PR is merged, buggyRequest will fatalError, complaining that the association key "team" is used in incompatible ways. Future GRDB versions may handle it, but GRDB 4.0 does not.

One possible fix is to include the competitions from the team:

// CORRECT
let fixedRequest = Player
    .including(required: Player.team
        .including(all: Team.competitions))

Another way to mix Player.team and Player.competitions in a single request is to make sure both teams (the one from Player.team, and the one hidden inside Player.competitions) use different association keys. For example:

// CORRECT
let request = Player
    .including(required: Player.team
        .filter(Team.Columns.isSelected == true)
        .forKey("selectedTeam"))         // "selectedTeam"
    .including(all: Player.competitions) // "team"

@groue groue added the bug label May 24, 2019
@groue groue merged commit 31e59f3 into development May 25, 2019
@groue groue deleted the dev/fix-has-many-through branch May 25, 2019 06:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant