-
-
Notifications
You must be signed in to change notification settings - Fork 66
jiff-sqlx: add integration crate for SQLx #240
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
Conversation
This PR adds a new `jiff-sqlx` crate. It defines wrapper types for `Timestamp`, `DateTime`, `Date`, `Time` and `Span`. For each wrapper type, the SQLx encoding traits are implemented. (Except, with `Span`, only the decoding trait is implemented.) This is similar to #141, but organizes things a bit differently. This also comes with SQLite support. MySQL support is missing since it seems, at present, to require exposing APIs in SQLx for a correct implementation. This initial implementation also omits `Zoned` entirely. I've left a comment in the source code explaining why. The quick summary is that, at least for PostgreSQL, I don't see a way to provide support for it without either silently losing data (the time zone) or just storing it as an RFC 9557 timestamp in a `TEXT` field. The downside of the latter is that it doesn't use PostgreSQL native datetime types. (Becuase we can't. Because PostgreSQL doesn't support storing anything other than civil time and timestamps with respect to its datetime types.) I do personally lean toward just using RFC 9557 as a `TEXT` type, but I'd like to collect real use cases first to make sure that's the right way to go. Ref #50, Closes #141 Ref launchbadge/sqlx#3487
I'm going to bring this in, but if anyone wants to offer feedback post-merge, I'm happy to follow-up! |
This is like #240, but adds a crate with wrapper types that implements Diesel traits. Unlike with SQLx, and unless I'm missing something, Diesel actually exposes enough of an API to implement datetime support for MySQL. So that's included here. Diesel does seem to use some internal privileged APIs for its own `chrono` and `time` integration that avoids unnecessary allocations when parsing datetimes from SQLite values. Again, unless I'm missing something, Jiff is forced to allocate into a `String` first. (But Jiff only really needs a `&[u8]`.) I found this experience, along with SQLx, to be absolutely mind-numbing. Just writing out the example code (which I also used for ad hoc testing) took an incredible amount of time. I spent _way_ more time playing fucking type tetris with both SQLx and Diesel than I did anything else _combined_. It's utterly ridiculous. This further solidifies my opinion that when you publish crates with an obscene amount of inter-connected traits, the resulting API becomes very difficult to use. I'm happy to iterate on the implementation and APIs of this crate (and `jiff-sqlx`) after an initial release. But I very much appreciate reviews from Diesel and SQLx experts. I'm going to say that this closes #50 since this I think this, along with `jiff-sqlx` and `jiff-icu`, gives us a solid foundation to build upon. We can track more specific integrations in new issues. Closes #50
This is like #240, but adds a crate with wrapper types that implements Diesel traits. Unlike with SQLx, and unless I'm missing something, Diesel actually exposes enough of an API to implement datetime support for MySQL. So that's included here. Diesel does seem to use some internal privileged APIs for its own `chrono` and `time` integration that avoids unnecessary allocations when parsing datetimes from SQLite values. Again, unless I'm missing something, Jiff is forced to allocate into a `String` first. (But Jiff only really needs a `&[u8]`.) I found this experience, along with SQLx, to be absolutely mind-numbing. Just writing out the example code (which I also used for ad hoc testing) took an incredible amount of time. I spent _way_ more time playing fucking type tetris with both SQLx and Diesel than I did anything else _combined_. It's utterly ridiculous. This further solidifies my opinion that when you publish crates with an obscene amount of inter-connected traits, the resulting API becomes very difficult to use. I'm happy to iterate on the implementation and APIs of this crate (and `jiff-sqlx`) after an initial release. But I very much appreciate reviews from Diesel and SQLx experts. I'm going to say that this closes #50 since this I think this, along with `jiff-sqlx` and `jiff-icu`, gives us a solid foundation to build upon. We can track more specific integrations in new issues. Closes #50
Just got this working, it did / does work well enough to get it rolling, I had to convert a Zoned to a Timestamp which was a bummer, one thing i've done in the past is use a separate "tz" column to hold the time zone names one area of friction was the so one writes the jiff type as a struct field type, however the not sure it's worth making jiff types into sqlx types directly, maybe worth a test to demonstrate the proper technique, though? Thank you for making jiff! I love it! /// an example struct for jiff_sqlx integration
#[derive(Debug)] // FromRow fails here
pub struct Tournament {
// ... other fields ...
/// timestamp at which the tournament was recorded
finished_at: Timestamp,
}
impl Tournament {
/// goal: borrow timestamp at which this tournament was recorded
pub fn finished_at(&self) -> &Timestamp {
&self.timestamp
}
}
impl<'r> sqlx::FromRow<'r, PgRow> for Tournament
{
fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
Ok(Tournament {
// ... other fields ...
finished_at: row
.try_get::<jiff_sqlx::Timestamp, &'static str>("finished_at")?
// ^^^^^^^^^^^^^^^^^^^^ get the column value as a jiff_sqlx type
.to_jiff(),
// ^^^^^^^ convert the jiff_sqlx type into a jiff type
})
}
} |
Thank you for the feedback! I have some follow-ups if you don't mind. :-)
Yeah I figured folks would run into this. So now that I have you here, what would you like
Can this be fixed by providing a method on
This defeats the purpose of having wrapper types. :-) Is isn't really appropriate for Jiff to depend on SQLx. The reverse is more plausible, but that's up to the SQLx maintainers. Why doesn't this work with the wrapper types? Can we add impls to Note that I am a SQLx newb. What I did in |
it makes sense to have jiff not depend on sqlx, that's a great point I'm not an expert on AsRef, but that sounds reasonable, maybe an "as_jiff" borrowing method would work, then we could put sqlx_jiff::Timestamp on structs directly and borrow the inner value without needing to make a full conversion, good idea maybe for zoned (since postgres apparently doesn't know what a time zone is 🤣) there would be two options
Seems like 0 would work for now, 1 is too annoying, and 2 could be nice to have but requires other people to make a big change to a big software system, I'm not gonna hold my breath for that! |
I think I still don't understand why you need to be able to get a
That's what I meant by using
I think this is definitely outside of the scope of
I imagine you could. But that's definitely not something I have the time or patience for. Someone else will need to lead that charge I think. |
I attempted to migrate one of my projects from I've created a minimal reproducer here: https://github.com/tonyfinn/jiff-sqlx-poc/tree/main but the gist is that if you don't enable any of the other time crates then a crate using (Let me know if you want me to file this as a seperate issue, just keeping it with the existing sqlx discussion as its newly implemented) I suspect this will require changes inside |
sqlx-core = { version = "0.8.0", default-features = false } | ||
sqlx-postgres = { version = "0.8.0", default-features = false, optional = true } | ||
sqlx-sqlite = { version = "0.8.0", default-features = false, optional = 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.
@BurntSushi I just noticed this while working on my own example for launchbadge/sqlx. These crates are semver-exempt, it's not recommended to depend on them directly without pinning the version: https://docs.rs/sqlx-core/latest/sqlx_core/#note-semver-exempt-api
Is there a particular reason you didn't just use the sqlx
crate? I don't see any relevant discussion in #50 or #141 (which actually did use sqlx
). I don't see any private types being used 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.
Hmmm I honestly don't remember. I think I was just trying to use the minimal number of dependencies as I could and this "worked" as far as I could tell.
Honestly the whole process of adding an integration crate was quite difficult from the perspective of someone who doesn't actually use SQLx. There was a lot of guesswork involved and "okay the chrono
integration does things this way so I'll do it that way too" style of reasoning.
I did not see the note about semver being an exempt. I created #322 to track fixing this. Thank you for flagging this!
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.
Honestly the whole process of adding an integration crate was quite difficult from the perspective of someone who doesn't actually use SQLx. There was a lot of guesswork involved and "okay the chrono integration does things this way so I'll do it that way too" style of reasoning.
Well honestly, no one's ever really bothered before. They usually just insist on upstreaming it into SQLx, even when, in retrospect, it doesn't make a whole lot of sense (supporting the whole git2
crate for just one type was a huge headache because of the native code dependency).
This wouldn't be a problem if jiff
wasn't so young still. time
and chrono
have been on their current major versions for years now. When jiff
is at that point, or you're ready to release a 1.0, I'd be happy to talk about upstreaming.
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.
Yeah I plan to release 1.0 this summer and commit to it indefinitely.
And yeah I added the ecosystem crates because I perceived it as one the main reasons why folks were bouncing of Jiff.
It's a nasty chicken and egg problem.
This PR adds a new
jiff-sqlx
crate. It defines wrapper types forTimestamp
,DateTime
,Date
,Time
andSpan
. For each wrappertype, the SQLx encoding traits are implemented. (Except, with
Span
,only the decoding trait is implemented.)
This is similar to #141, but organizes things a bit differently. This
also comes with SQLite support. MySQL support is missing since it seems,
at present, to require exposing APIs in SQLx for a correct
implementation.
This initial implementation also omits
Zoned
entirely. I've left acomment in the source code explaining why. The quick summary is that, at
least for PostgreSQL, I don't see a way to provide support for it
without either silently losing data (the time zone) or just storing it
as an RFC 9557 timestamp in a
TEXT
field. The downside of the latteris that it doesn't use PostgreSQL native datetime types. (Becuase we
can't. Because PostgreSQL doesn't support storing anything other than
civil time and timestamps with respect to its datetime types.) I do
personally lean toward just using RFC 9557 as a
TEXT
type, but I'dlike to collect real use cases first to make sure that's the right way
to go.
Ref #50, Closes #141
Ref launchbadge/sqlx#3487