fix: fallback to entity property if referencedColumn returns nothing#9572
fix: fallback to entity property if referencedColumn returns nothing#9572akwodkiewicz wants to merge 9 commits intotypeorm:masterfrom
Conversation
8353daf to
e34b8f2
Compare
Both composite boolean expressions check if the value is an object. This means that value can never be of type "function", so these checks can be removed.
This predicate occurs a couple of times in getEntityValue method.
I tried to deduce its value (isEntityLike) from its usage.
This refactor is not 100% pure, because I incorporated
!(val instanceof Date)
expression into the method, even though it was mentioned in 2 out of 4
places that were replaced with the method call.
Again, studying the usage of the predicate, I decided that making sure
if the value is not a Date will only make the assumption more correct in
those 2 other places, because next operations assume that the value is
an entity.
e34b8f2 to
f1efc6c
Compare
82fd8a9 to
795c74d
Compare
|
@pleerock, apologies for reaching to you directly, but I'd like to make sure that the approach I chose here is a correct one. If you believe that the issue should be fixed in a different way, I can try doing it from scratch, just need your guidance here. |
|
Warning Rate limit exceeded@gioboa has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 14 minutes and 30 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (6)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
commit: |
PR Code Suggestions ✨Latest suggestions up to 9637703
Previous suggestionsSuggestions up to commit c02fe94
Suggestions up to commit bfce334
Suggestions up to commit d314518
|
|||||||||||||||||||||||||||||||||||||||||
|
I got notified about some commotion in this PR so I looked at it again, and I'm wondering if anybody with better understanding of the codebase can propose a better solution than my "isEntityLike". Or at least run it against Opus 4.5 or GPT 5.2 to point out the flaws of the approach I proposed. It was a long time ago when I wrote this and I did not continue development of TypeORM at all, so I understand this PR much less than I did when I first wrote it. |
Code Review by Qodo
1. Test added under github-issues
|
gioboa
left a comment
There was a problem hiding this comment.
@akwodkiewicz thanks for opening this.
Based on your message, I'm going on with the fix in this PR #12042 💪
Description of change
This PR adds a fallback mechanism to one of
ColumnMetadata.getEntityValueconditional branches. It fixes #9565 -- saving an entity with an object literal foreign key.The root cause of the linked issue is that there is no differentiation between object literal that represents an actual entity and an object literal representing a column value. This PR does not solve this, but it applies a band-aid around one of edge cases that are affected by this behaviour.
Let's assume we're performing an INSERT/save of an entity, where one of its columns is a foreign key column and this column is an object literal (for example, an opaque type boxing a string value). During the query execution, at some point there's a call to
ColumnMetadata.getEntityValue. Now see the following snippet (point in history before this PR):typeorm/src/metadata/ColumnMetadata.ts
Lines 806 to 832 in d305e5f
Eventually, execution falls into
ifin L806. Because our column is just the foreign key representation, but not the full entity, therelatedEntityin L807 is undefined. This means we'll skip the first nestedifand start checking theelse ifin L809-L815. The problem is that because the column value is an object literal it passes all the predicates and we enter the branch. And in L816 we're going to callgetEntityValueon the related column, passing our object literal as the entity. This is not correct, because the object literal only represents the "id", not the full entity. This call will most likely (but not always, more on that in a while) result invaluebeing undefined, because the property to retrieve will not exist in our object literal. And because of that, thevaluereturned eventually by thegetEntityValueis going to beundefined, which causes the INSERT/save to fail.The fix here is to assign raw
entity[this.propertyName]tovaluewhenever thethis.referencedColumn.getEntityValuereturnsundefined. This addresses directly the scenario that I described here.Very similar solution, but structured in a slightly different way, might be to assign
entity[this.propertyName]tovaluealmost at the end of the method (before transformation) if it's not assigned yet. I looked at the branching in this method and it looks it should work, but this might have side-effects to cases I did not look into, that's why I decided to put the fallback assignment specifically in the affected case.There's still risk that our object literal will contain the property of name
this.referencedColumn.propertyName, and then my fix will not work -- typeorm will try to assign extracted value from object literal instead of the whole object literal (so most likely the operation will fail with aTypeErrorduring the transformation step or not fail at all, saving wrong data to the database).To solve this problem completely, the execution should not fall into this 2nd nested branch at all -- it should go directly to the final
elseand use theentity[this.propertyName]value verbatim. I refactored the method a bit and named the compound predicate to better visualize what decisions are made in the branches:typeorm/src/metadata/ColumnMetadata.ts
Lines 969 to 984 in cc8d76b
If there is a way to make sure that the object literal passed to
getEntityValuedefinitely is or is not an entity and replacedisEntityLikewith such check, it should solve the issue entirely.Pull-Request Checklist
masterbranchnpm run formatto apply prettier formattingnpm run testpasses with this changeFixes #0000