Skip to content
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

feat(spin_sdk::pg): Extract type conversions into sdk #813

Merged
merged 4 commits into from
Nov 17, 2022

Conversation

etehtsea
Copy link
Contributor

This has to be the starting point on the way to providing the API that is able to automatically map a DB response into the user-defined struct.

Signed-off-by: Konstantin Shabanov mail@etehtsea.me

Copy link
Contributor

@itowlson itowlson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy for this to go into the SDK, but I suggest we have to make it more comprehensive first. A sample can get away with just doing the things needed for the sample (and to show the pattern), but a blessed library needs to be systematic. We'd also need to manage things like SQL NULL handling, which the sample dodged by making all of the columns NOT NULL. grin

We do have fuller conversion coverage in the .NET SDK (https://github.com/spin/spin-dotnet-sdk) -- if we have parity with that then that would be fine I reckon. Thoughts?

@etehtsea etehtsea marked this pull request as draft October 12, 2022 15:11
@etehtsea etehtsea force-pushed the pg-sdk branch 2 times, most recently from d259f5a to 711d019 Compare October 12, 2022 16:45
@etehtsea etehtsea marked this pull request as ready for review October 12, 2022 16:46
@etehtsea
Copy link
Contributor Author

etehtsea commented Oct 12, 2022

@itowlson I've added decoding support for the following DbValues so as their NULLable variants:

  • boolean(bool)
  • int16(s16)
  • int32(s32)
  • int64(s64)
  • floating32(float32)
  • floating64(float64)
  • str(string)
  • binary(list<u8>)

Initially, I wanted to review/test types mapping inside the outbound-pg crate first because I've got a few concerns:

Also, I want to share some other thoughts which are out of the scope of this PR but are related to:

  • Regarding other SQL DBs support: rather than adding support for different SQL DBs using various crates from different maintainers wouldn't it be beneficial to utilize sqlx for this matter?
    Originally, I was thinking that sqlx is just the layer above different DB drivers, but during my experiments last week I realized that this is not the case.
    I experimented with it last week and the code seems quite similar (but there are some drawbacks like Export PgType launchbadge/sqlx#1369).
    Besides checking the sqlx itself another goal was to check the ability to get information about the column's NOT NULL constraint from the column info. Unfortunately, there is no way to do this for dynamic SQL queries (at least in PG).
  • Regarding WIT format: at this moment I'm not sure that "generic" types (DbValue) are convenient because:
    • what to do with db-specific types?
    • there is no way to figure out the original (DB) type. For example, how to (de)serialize timestamps? They could be encoded as DbValue::Str(RFC 3339/ISO 8601) but this way it's not clear how to deserialize them. So it makes sense to add a separate enum variant for them. Another option might be to store original (DB) type in the column info.
    • current DbValue's format seems to be modeled after Rust's types but different languages might approach DB types in a different way (https://pkg.go.dev/github.com/lib/pq#hdr-Data_Types)
      All in all, it might be easier to introduce different mappings for different SQL DBs that are close to their types.
  • At this moment, it's not clear how to handle breaking WIT format changes (https://github.com/fermyon/spin-dotnet-sdk/tree/main/wit/ephemeral). Any change to the format will break C# SDK. When it gets more stable, it wouldn't be a big problem, but for now, it might become an issue.

@itowlson
Copy link
Contributor

Thanks for the great feedback. sqlx certainly looks like it might be a great four-for-the-price-of-one solution - I didn't know about it before so thanks for the pointer!

I agree we need a better story for DB-specific types and generally on how to represent DB-specific operations and options. There's a reason every database gets its own library and we can't afford to smooth that over too much. One option longer term is to not even have database WIT specs, and provide infrastructure whereby source languages can use their own database crates / packages / modules / etc. But that's hard to do at the moment because most of them sit over raw sockets, and that sockets code doesn't yet compile to Wasm.

The current situation is a sighting shot at what we can do in the meantime, and there's certainly a good case for moving the data types out of a common WIT and into database-specific ones. But at the WIT level we'd probably still end up with a big honkin' variant, which would require languages like Go and C# to do some manual unpicking to make it idiomatic. As noted in the MySQL POC, there are other things we should look at when we approach this, such as whether to go for a more resource-oriented view of connections and transactions and so on. In any case, we're very happy to iterate on this and we'd welcome your (and other contributors') thoughts and advice.

Regarding how we evolve WIT files - this (and contract versioning more generally) is a big concern of mine, and folks within Fermyon are actively debating when to stabilise various contracts and how to version them thereafter. We have muddled along so far by batching up breaking changes to limit the number of times we have to perform breaks, but we do need a more robust and systemic solution. Unfortunately, we don't yet know what that looks like, and it likely needs to align to broader Wasm Component Model standards which are still in flux. Sorry I can't give you a more satisfying answer for now, but it's very much on our radar.

Thanks again for the detailed thoughts. This is a very early-stage area and it's so helpful to have wider conversations about it!

@etehtsea etehtsea changed the title feat(outbound-pg): Extract type conversions into sdk feat(spin_sdk::pg): Extract type conversions into sdk Oct 13, 2022
@etehtsea
Copy link
Contributor Author

etehtsea commented Oct 13, 2022

such as whether to go for a more resource-oriented view of connections and transactions and so on

Yeah, I was looking into more resource-oriented approach but wit-bindgen is currently experiencing a big rewrite, so resource/handle support is dropped for now.
bytecodealliance/wit-bindgen@25596c7
So it looks like the current approach is the only way for the short-term.

P.S. async support is also temporary dropped
bytecodealliance/wit-bindgen@f1682df

@etehtsea etehtsea force-pushed the pg-sdk branch 2 times, most recently from 87fea2b to e9363c8 Compare October 15, 2022 18:24
@etehtsea
Copy link
Contributor Author

I've added basic integration test to test outbound-pg. It's currently excluded from the default integration test because it expects running postgresql server.
The test itself is a bit awkward but I couldn't find other easy way to test it atm.

@etehtsea
Copy link
Contributor Author

etehtsea commented Oct 31, 2022

UPD

Is there something else to cover?

int8(s8) support wasn't added into conversions because:

  • the only type that fits is "char" which is:

The type "char" (note the quotes) is different from char(1) in that it only uses one byte of storage, and therefore can store only a single ASCII character. It is used in the system catalogs as a simplistic enumeration type.
https://www.postgresql.org/docs/current/datatype-character.html

  • "char" isn't supported in outbound-pg;

Copy link
Contributor

@itowlson itowlson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this looks tremendous. Thank you so much for slogging through all the types, and for putting in the hard yards to produce an properly self-contained integration test.

I feel a bit guilty for raising a nit about doc strings now... everything else looks great!

}
}

// A pg error
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I'm misreading this but I think pub stuff should have doc strings? The module deny(missing-docs) should be catching this though so perhaps I'm misunderstanding...

Copy link
Contributor Author

@etehtsea etehtsea Nov 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SDK allows missing_docs https://github.com/fermyon/spin/blob/main/sdk/rust/src/lib.rs#L41-L60 because, otherwise it fails with:

error: missing documentation for an enum
  --> sdk/rust/src/lib.rs:54:5
   |
54 |     wit_bindgen_rust::import!("../../wit/ephemeral/spin-config.wit");
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: this error originates in the macro `wit_bindgen_rust::import` (in Nightly builds, run with -Z macro-backtrace for more info)

error: could not document `spin-sdk`

But you're right, this isn't an excuse for not adding the docs :)
Fixed, thanks!

Signed-off-by: Konstantin Shabanov <mail@etehtsea.me>
This has to be the starting point on the way to providing the API that
is able to automatically map a DB response into the user-defined struct.

Signed-off-by: Konstantin Shabanov <mail@etehtsea.me>
Signed-off-by: Konstantin Shabanov <mail@etehtsea.me>
- Add conversion support for: int16, int32, int64, floating32,
floating64, binary, boolean;
- Add support for nullable variants;
- Introduce spin_sdk::pg::Error;

Signed-off-by: Konstantin Shabanov <mail@etehtsea.me>
Copy link
Contributor

@itowlson itowlson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - thanks!

@itowlson itowlson merged commit 013059c into fermyon:main Nov 17, 2022
@etehtsea etehtsea deleted the pg-sdk branch November 17, 2022 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants