| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| item.c_str(), | ||
| NewStringType::kNormal) | ||
| .ToLocalChecked()}; | ||
| MaybeLocal<Value> maybe_result = |
There was a problem hiding this comment.
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.
Sorry, something went wrong.
|
@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". |
Sorry, something went wrong.
Can't we stop attempting more work once the first exception is detected? |
Sorry, something went wrong.
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. |
Sorry, something went wrong.
Not from the filter callback. SQLite is calling us at that point.
@jasnell What should we do when multiple errors are thrown? |
Sorry, something went wrong.
|
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. |
Sorry, something went wrong.
|
@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. |
Sorry, something went wrong.
|
Once the first exception is detected, wouldn't it make sense to return SQLITE_CHANGESET_ABORT? I haven't used this API before, but based on the documentation, it seems like that may work? |
Sorry, something went wrong.
|
@cjihrig The filter callback cannot abort, that is the conflict handler callback (only called when a conflict is detected, not for every change). |
Sorry, something went wrong.
|
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. |
Sorry, something went wrong.
|
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. |
Sorry, something went wrong.
|
@cjihrig That won't work, because the conflict handler is not called if there are no conflicts. Sorry, edited my comment too late. |
Sorry, something went wrong.
|
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 false from all remaining filter functions to avoid calling into JS any more. |
Sorry, something went wrong.
Codecov ReportAttention: Patch coverage is 80.00000% with 2 lines in your changes missing coverage. Please review.
@@ 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
|
Sorry, something went wrong.
|
I looked into this a bit more, and it appears that the tables in the changeset are looped over when we call applyChangeset(). For each table, the filter() function is called. If filter() returns true, then the onConflict() function is called. Then, SQLite3 moves on to the next table. If onConflict() returns SQLITE_CHANGESET_ABORT at any point, then everything is rolled back and no more progress is attempted. 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. |
Sorry, something went wrong.
|
As I said in my previous comment, the conflict handler is not called when there are no conflicts... So that approach will not work. |
Sorry, something went wrong.
|
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 filter() immediately returns false when an exception is pending and onConflict() doesn't get to execute at all. |
Sorry, something went wrong.
OK this is doable. We could use SQLITE_CHANGESETAPPLY_NOSAVEPOINT to avoid the internal savepoint from being set once sqlite3changeset_apply_v2 is stable. |
Sorry, something went wrong.
There was a problem hiding this comment.
Looks good!
I think it would be an improvement to apply the same steps to the conflict callback, i.e. remove the TryCatch there and instead handle an empty MaybeLocal value, just like you're doing here.
If you want to improve on it even more, I think ideally we'd want to keep track of whether one of the callbacks has seen an exception (i.e. empty MaybeLocal result) on the context object, and not try to call any JS function if a previous call had already thrown.
Sorry, something went wrong.
| if (maybe_result.IsEmpty()) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Not technically wrong, but you don't need this because you already handle the empty case in the next conditional
| if (maybe_result.IsEmpty()) { | |
| return false; | |
| } |
Sorry, something went wrong.
There was a problem hiding this comment.
I will still add some logic here, I think this branch is taken when an exception is thrown, correct?
Sorry, something went wrong.
There was a problem hiding this comment.
Yes, it should be taken when filterFunc throws an exception. But the point of the comment here is that in the next condition maybe_result.ToLocal(&result) returns false in the same case.
Sorry, something went wrong.
|
@louwers are you still working on this (it's marked as a draft)? |
Sorry, something went wrong.
|
@cjihrig Yes, I still intend to finish this. I got stuck trying to write a RAII wrapper to create and roll back a savepoint. Do we have something like that already? Otherwise I think I will handle it manually. |
Sorry, something went wrong.
|
OK, thanks. No, nothing like that exists yet as far as I know. |
Sorry, something went wrong.
|
@louwers: I completely missed that you'd been working on this, and drafted up d017d7735de233a0dd9550d8061325a9a841c7c8. If it's worthless to you then no worries, but figured I'd share just in case! |
Sorry, something went wrong.
|
@Renegade334 OK thx I can use that. |
Sorry, something went wrong.
|
Part of this PR was merged with #59848. Will make another PR. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Resolves #56890
Also adds additional test coverage for filter callback and clarifies behavior in documentation.