Skip to content

fix(belongsToMany): sync re-inserts rows on a column-mapped pivot#49

Merged
3m1n3nc3 merged 1 commit into
mainfrom
fix/belongstomany-sync-mapped-pivot
Jul 24, 2026
Merged

fix(belongsToMany): sync re-inserts rows on a column-mapped pivot#49
3m1n3nc3 merged 1 commit into
mainfrom
fix/belongstomany-sync-mapped-pivot

Conversation

@3m1n3nc3

Copy link
Copy Markdown
Member

selectPivotRows goes through the query adapter, which maps result rows back to attribute names via reverseColumnMap. So a pivot column like permission_id comes back under permissionId whenever the pivot table has a registered column map. sync read the related value with the raw column name (row[this.relatedPivotKey]), which was then undefined, so existingKeys stayed empty, every desired row looked new, and sync re-inserted rows that already existed, tripping the pivot's unique constraint.

Read pivot columns under whichever key the row carries: raw column first (lightweight table loader), then the attribute alias (query adapter). Applied at the three sites that indexed a selected pivot row by the raw key: sync, attachPivotToResults, and getQuery, so eager loads and attribute attachment are fixed alongside sync.

The existing pivot tests never used a column-mapped pivot table (role_users uses camelCase columns == attributes), which is why this escaped. A regression test needs a pivot whose db columns differ from its attribute names.

selectPivotRows goes through the query adapter, which maps result rows
back to attribute names via reverseColumnMap. So a pivot column like
`permission_id` comes back under `permissionId` whenever the pivot table
has a registered column map. sync read the related value with the raw
column name (`row[this.relatedPivotKey]`), which was then undefined, so
existingKeys stayed empty, every desired row looked new, and sync
re-inserted rows that already existed, tripping the pivot's unique
constraint.

Read pivot columns under whichever key the row carries: raw column first
(lightweight table loader), then the attribute alias (query adapter).
Applied at the three sites that indexed a selected pivot row by the raw
key: sync, attachPivotToResults, and getQuery, so eager loads and
attribute attachment are fixed alongside sync.

The existing pivot tests never used a column-mapped pivot table
(role_users uses camelCase columns == attributes), which is why this
escaped. A regression test needs a pivot whose db columns differ from
its attribute names.
@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 38.46154% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/relationship/BelongsToManyRelation.ts 38.46% 8 Missing ⚠️

📢 Thoughts on this report? Let us know!

@3m1n3nc3
3m1n3nc3 merged commit 92be3cc into main Jul 24, 2026
6 of 7 checks passed
@3m1n3nc3
3m1n3nc3 deleted the fix/belongstomany-sync-mapped-pivot branch July 24, 2026 08:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant