tusker
tusker copied to clipboard
Tusker rewrite in Rust
I'm currently in process of rewriting tuksker in Rust.
Why?
- I love writing Rust code.
- I would prefer if I could just call a statically linked binary instead of having to bring an entire Python runtime when using Tusker in my
devcontaineror deployment servers. migraandschemainspecthave some unsolved issues. The lack of types makes it really hard to reason about the code.- Tusker lacks a few features which I'd like to add but adding them to the current python code base doesn't feel right.
How is it going?
Head over to https://github.com/bikeshedder/tusker-rs and see for your own.
It doesn't work, yet. Don't bother trying it unless you want to start working on it. If that's your intention. Feel free to contact me and we can see what's the best way to collaborate.
@dlight wrote at https://github.com/bikeshedder/tusker/issues/35#issuecomment-1885169258
Not sure if this thread is the right place to ask, but, did you check out postgres_migrator? It says in the readme:
Credits
I've seen postgres_migrator but to me it is going in the wrong direction. It works just like tusker but runs migra and PostgreSQL inside a docker container. I want a lightweight tusker binary which can utilize my local running database. That also makes it compatible with a devcontainer without having to resort to awful solutions like DIND (Docker in Docker).
The most promising solution for me was renovate. I was not able to get it working and the whole idea of parsing the output of pgdump seams a bit weird to me. That project also seams to have stalled over the past few months.
Personally migra strikes a nice balance between sophistication and simplicity. PostgreSQL knows its schemas best. Accessing the pg_catalog.* tables for that purpose just makes sense.
The rewrite aims to keep the spirit of tusker while adding two new features:
- A simple migration manager which can run the files in the migrations directory against the database. Also supports embedding in existing rust applications so you don't have to ship two binaries when writing a Rust server.
- A simple type-safe query system. Just like the schemas you write SQL queries and tusker extracts the types from them via
EXPLAINqueries. It works similar tosqlxbut only takes care of the SQL queries and their types rather than providing a complete database driver.
Okay cool!
A simple type-safe query system. Just like the schemas you write SQL queries and tusker extracts the types from them via EXPLAIN queries. It works similar to sqlx but only takes care of the SQL queries and their types rather than providing a complete database driver.
Is this like cornucopia?
Cornucopia is really top notch; I think that you could use it in conjunction with tusker-rs.
When I first heard about cornucopia a few weeks ago - I did my research before starting this project - I was very hyped about it. After some experiments with it I had two main problems with it:
- The CLI expects docker (or podman) to prepare the queries. Again, that's against my liking of using a local database. I'm using devcontainers for all my newer projects now and I have a fresh DB available. No need for DIND madness.
- The code seams overly complex for what it's doing. Finding the type of a query should be just a matter of preparing the query against a database, reading the params and columns from it and dumping all that information into a JSON file. Cornucopia opted for a custom SQL parser, replacing the
$1, $2, $3...syntax by its own:foo, :bar, and adding all sorts of meta data in comments, etc. p.p.
The design I have in mind is a very small CLI tool and a single Query trait that can be derived:
-- get_post_by_id.sql, one file per query
SELECT id, author, text, created
FROM post
WHERE id = $1
#[derive(QueryOne)]
#[query(sql = "get_post_by_id.sql", row = Post)]
struct GetPostById {
// order of params must match, types are checked
pub id: i32,
}
struct Post {
// column name and type are checked, order is irrelevant
pub id: i32,
pub author: String,
pub text: String,
pub created: time::OffsetDateTime,
}
And when using the query I'm aiming for this API:
let post = db.query(GetPostById { id: 1 }).await?;
I got a prototype working but it's still very rough around the edges. I'm still figuring out how to resolve the paths relative to the #[derive(QueryOne)] and how to type check the Post without having to add a #[derive(FromRow)] to the row structure as well. I might actually end up doing just that if I can't find another way. :shrug:
tbh. working on this future is quite frustrating but I haven't given up, yet. I'm tempted to ditch this code and just use cornucopia or sqlx ... :see_no_evil:
Any updates on this ?
I finally came around implementing basic diffing of tables and constraints. It doesn't support functions, views, etc.
For simple schemas that just use tables and constraints it's almost usable today.
I plan to release a 0.1.0 shortly. I need to get this in the hand of other developers to add the missing features and fix the remaining bugs which I'm sure there are still plenty of: https://github.com/bikeshedder/tusker-rs
I ended up using https://github.com/stripe/pg-schema-diff as migra is not getting much support now a days. thanks for insights.