-
Notifications
You must be signed in to change notification settings - Fork 30.5k
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
sqlite: handle exceptions in filter callback database.applyChangeset() #56903
base: main
Are you sure you want to change the base?
Conversation
item.c_str(), | ||
NewStringType::kNormal) | ||
.ToLocalChecked()}; | ||
MaybeLocal<Value> maybe_result = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need the TryCatch
here. The problem is the use of ToLocalChecked()
. Take a look at this code. You can tell if V8 has an exception pending if the ToLocal()
call does not succeed.
@jasnell @cjihrig Actually I don't think we should propagate the exception. Because if different exceptions are thrown only the last is propagated. You can't really abort the whole application of the changeset from the filter callback. So I just interpret an exception as "do not include changes from this table". |
Can't we stop attempting more work once the first exception is detected? |
Unfortunately we need to. For instance, the exception thrown could be something like a fatal out of memory or some other condition where we really shoiuld not just ignore and proceed. Also, not throwing the error can lead to subtle bugs in user code where their filter may be throwing an error that gets swallowed. Any error thrown by the filter callback needs to be propagated. |
Not from the filter callback. SQLite is calling us at that point.
@jasnell What should we do when multiple errors are thrown? |
It looks like there is already a transaction in place, we just need to verify:
Once the first error is thrown we should avoid trying to do any more work that calls into JavaScript so that no more exceptions occur. |
@cjihrig In that case we should change the API. Instead of a function we can think about passing a Set of table names or a regular expression. |
Once the first exception is detected, wouldn't it make sense to return |
@cjihrig The filter callback cannot abort, that is the conflict handler callback (only called when a conflict is detected, not for every change). |
Maybe the filter callback is called prior to applying any changes. If that is the case it would be possible. I doubt it though, because that would probably require more memory. |
I think you'll need some state in the C++ code that can be shared between the filter and conflict functions. If the filter function creates a JS error, let it be thrown, but also roll back in the conflict function. |
@cjihrig That won't work, because the conflict handler is not called if there are no conflicts. Sorry, edited my comment too late. |
OK, I'll have to look into the code in more detail when I'm not at work 😄. In the worst case, once the first exception is encountered, we should probably return |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #56903 +/- ##
=======================================
Coverage 89.17% 89.18%
=======================================
Files 665 665
Lines 192602 192609 +7
Branches 37057 37057
=======================================
+ Hits 171755 171771 +16
Misses 13657 13657
+ Partials 7190 7181 -9
|
I looked into this a bit more, and it appears that the tables in the changeset are looped over when we call What I believe we need to do is:
Does that make sense? If my understanding of the control flow is correct, that should prevent us from ever getting in a situation where there are multiple exceptions. |
As I said in my previous comment, the conflict handler is not called when there are no conflicts... So that approach will not work. |
Then I think the only options are:
We will still need some logic like what I laid out in my previous comment though because we need to stop executing JS when there is an exception. The only difference is maybe |
OK this is doable. We could use |
Resolves #56890
Also adds additional test coverage for filter callback and clarifies behavior in documentation.