-
-
Notifications
You must be signed in to change notification settings - Fork 16
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 Custom Functions are now supported #25
Conversation
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.
Initial scan looks good, I'll do a proper review next week. Note the comment about CI
.github/workflows/test.yml
Outdated
@@ -10,7 +10,7 @@ defaults: | |||
jobs: | |||
dependents: | |||
runs-on: ubuntu-latest | |||
container: swift:5.2-bionic |
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.
This needs to stay as 5.2 to ensure we don't break 5.2 compatibility
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.
Fixed!
I updated the maxim-lobanov/setup-xcode
to v1.2.1
since macOS build won't start.
https://github.com/vapor/sqlite-nio/runs/2922618375?check_suite_focus=true
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.
@0xTim 5.2 compatibility is checked by the main Linux workflows in each repo (at least, that's how it's supposed to work) - I prefer to run the dependent checks in a "current"-version container to reflect the most common usage, since those checks don't have a version matrix.
Hello, and thank you for getting inspired from my work 👍 GRDB uses the the MIT license. It is a permissive license, which allows you to do anything you like with the licensed code. The only requirement is:
The SQLiteCustomFunction.swift file in this PR contains substantial portions of https://github.com/groue/GRDB.swift/blob/v5.8.0/GRDB/Core/DatabaseFunction.swift: please include the GRDB copyright and permission notices in your derived work. |
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.
LGTM other than the log levels. I'll let @gwynne have a look before merging though
Wonderful. I committed both of your suggested changes, @0xTim. |
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.
Sorry it's taken so long to get to this! The only thing I think is missing is a NOTICES.txt for attribution to GRDB. IANAL but this is what we've done in other projects, e.g. https://github.com/vapor/jwt-kit/blob/main/NOTICES.txt
Once added I'll merge
@danramteke Can I help get this across the finish line? I am interested in this functionality as well. I'd be happy to fill out the NOTICES.txt if you provided me access to your branch. Alternatively, I can create a merge to your original branch. |
@Austinpayne had no idea anyone else wanted this, thanks for the nudge! |
@0xTim I did my best to add a |
Thank you for implementing it! This unlocks some very interesting use cases and I actually had started on an implementation before seeing yours. Your PR is much more feature rich so really looking forward to seeing in mainlined. Thank you @groue as well for the original implementation! |
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.
Thanks!
@gwynne anything I've missed? |
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #25 +/- ##
===========================================
+ Coverage 48.03% 67.78% +19.75%
===========================================
Files 7 8 +1
Lines 535 832 +297
===========================================
+ Hits 257 564 +307
+ Misses 278 268 -10
|
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've commented inline about several minor issues. I'm also very uncomfortable with the amount of manual memory management being thrown around; I'll try to persuade @Lukasa to take a look at what's going on in that regard.
That concern aside, the overall code seems pretty solid and there's no question that having this functionality is both useful and desirable; if we can address the above, this will be good to go!
nil, nil, nil, nil, nil) | ||
|
||
guard code == SQLITE_OK else { | ||
// Assume a bug: there is no point throwing any error. |
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.
This is a weird comment above this code.
}) | ||
|
||
guard code == SQLITE_OK else { | ||
// Assume a bug: there is no point throwing any error. |
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.
This is a weird comment above this bug.
assert(!aggregateContext.hasErrored) // assert SQLite behavior | ||
do { | ||
let arguments = (0..<Int(argc)).map { index in | ||
SQLiteData(sqliteValue: argv.unsafelyUnwrapped[index]!) |
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.
There's no justification for unsafelyUnwrapped
here.
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.
done
aggregateContextBufferP.copyMemory(from: $0) | ||
} | ||
return aggregateContextU | ||
} |
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'm not wild about this function. I think there are a few places where it's being excessively clever.
First, I really don't love that it's a bit vague about what it's storing. In the initialization branch it stores the bytes of an OpaquePointer
into the buffer, but in the other branch it just immediately type-puns to Unmanaged<AggregateContext>
. That works, but it's not great: we should be consistent in the branches. I'd probably prefer we use OpaquePointer
and do the full Unmanaged
dance in both branches, rather than rely on Unmanaged
being bitwise-takeable (though I think it probably is).
The thing I like much less is the detection of non-zero bytes in the buffer as a mechanism for working out if the memory is initialized. Fortunately for us, we don't need to do that. If we define the sqlite_aggregate_context
as containing OpaquePointer?
(or, alternatively, Unmanaged<AggregateContext>?
, though I like that less) then we can use != nil
to detect the uninitialised case. This is way safer and less prone to error, as well as being in line with how the API expects to be used in C code.
Finally, this function returns an Unmanaged<AggregateContext>
, which sorta begs to be leaked. I'd rather that this function took a "consuming" parameter that decided whether it consumed the unbalanced retain or not.
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'm looking at this review with the greatest interest 😋 This code was written 5+ years ago and never had the privilege of multiple pairs of eyes. If you don't mind, I'd be happy to learn from your experience here.
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 understand what the reviewer is saying. I'm open to help for re-implementing this function 💜
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've taken a crack at it, hopefully Cory won't tell me I've made things even worse 😅
@danramteke I'm confused, you appear to have reverted the changes of #35 in this PR. Was that intentional? |
That's due to this branch not having been updated in a while. The conflicts need to be resolved; the seeming "revert" is just an artifact of that. @danramteke, can you get this up to date with the changes from |
And also @Lukasa could you take a peek and let us know if your issues were resolved? 🙂 |
@Austinpayne yep, @gwynne is right, it's the result of merge conflicts |
If anyone can help get this PR over the finish line, please lend a helping hand, as my bandwidth is limited at the moment. Remaining tasks:
|
f667516
to
02648dc
Compare
# Conflicts: # .github/workflows/test.yml # Package.swift # Sources/CSQLite/include/sqlite3.h # Sources/CSQLite/sqlite3.c # Sources/CSQLite/version.txt # Sources/SQLiteNIO/SQLiteConnection.swift # Sources/SQLiteNIO/SQLiteData.swift # Tests/SQLiteNIOTests/SQLiteNIOTests.swift
@danramteke @Lukasa Okay - I rebased onto Cory, did I get it even remotely right? 🙏 |
P.S.: Yes, Cory, I know I didn't make the method optionally consuming like you wanted, but the behavior of SQLite with regards to aggregate contexts during xFinal is so weird I wasn't entirely good with the idea of tinkering more than necessary 😕 |
Ugh, GitHub’s UI some days… 😬 |
What happened to the indentation in SQLiteCustomFunction.swift? It's all over the place, the code is extraordinarily hard to read. |
I thought I managed to fix that 😕 Heck with it, I’ll give it a swiftformat pass once I fix the 5.6 build |
@groue It seems as if I managed to stumble my way into what Cory was looking for in terms of improvements here - please feel free to use it in GRDB! |
That's lovely 😊 I certainly follow this issue and your improvements. The SQLite api regarding custom functions and aggregates is awful (an exception because SQLite is usually a very nice C library). Thank you very much again. |
Thank you @gwynne !!! |
Deliberately merging without a semver- label, will release manually. |
This PR adds custom function support to
sqlite-nio
. Both custom functions and custom aggregates are supported. To find out more about these features, please see the SQLite website about them: https://www.sqlite.org/appfunc.htmlThere are some queries only possible using custom functions. And further, custom function help soften the need for custom builds of SQLite with various parts of the SQLite amalgamation enabled or disabled: one can simply write a custom function instead.
I have adapted this code from
https://github.com/groue/GRDB.swift/blob/master/GRDB/Core/DatabaseFunction.swift
by @groue. Because GRDB doesn't support Linux or NIO, I was motivated to adapt his code.Example:
There are many more examples in the new unit test file.
Lastly, is there a swift-format configuration I can use to format my PR to match the Vapor's code style?