Skip to content

Commit

Permalink
Add PostgreSQL support (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
claymcleod authored Feb 24, 2025
1 parent d5447cb commit 05d3015
Show file tree
Hide file tree
Showing 23 changed files with 757 additions and 102 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,31 @@ jobs:
- name: cargo test
run: cargo test --workspace --all-features

test-postgresql:
needs: check
name: Run tests for PostgreSQL
runs-on: ubuntu-latest
services:
postgresql:
image: postgres:17
env:
POSTGRES_USER: toasty
POSTGRES_PASSWORD: toasty
POSTGRES_DB: toasty
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
env:
DATABASE_URL: postgres://toasty:toasty@localhost/toasty
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: cargo test
run: cargo run --bin example-hello-toasty --features postgresql

test-all-os:
needs: check
name: Run tests on all operating systems
Expand All @@ -80,3 +105,16 @@ jobs:
run: cargo test --workspace
- name: Run examples
run: scripts/gen-examples run

examples:
needs: check
name: Build the `hello-toasty` example with each feature
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- run: cargo install cargo-hack
- run: cd examples/hello-toasty && cargo hack build --each-feature
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
# Driver implementations
"src/db/ddb",
"src/db/sqlite",
"src/db/pgsql",

# General utilities.
"src/std-util",
Expand All @@ -31,10 +32,13 @@ async-recursion = "1.1.1"
async-stream = "0.3.6"
async-trait = "0.1.83"
by_address = "1.2.1"
cfg-if = "1.0.0"
clap = { version = "4.5.20", features = ["derive"] }
heck = "0.5.0"
indexmap = "2.6.0"
pluralizer = "0.4.0"
postgres = "0.19.10"
postgres-types = "0.2.9"
pretty_assertions = "1.4.1"
proc-macro2 = "1.0.37"
quote = "1.0.18"
Expand All @@ -43,6 +47,8 @@ serde = { version = "1.0.214", features = ["derive"] }
serde_json = "1.0.132"
std-util = { path = "src/std-util" }
syn = "2.0.86"
tokio = { version = "1.18", features = ["full"] }
tokio-postgres = "0.7.13"
tokio-stream = { version = "0.1.16", default-features = false }
toasty-codegen = { path = "src/codegen" }
toasty-core = { path = "src/core" }
Expand Down
2 changes: 1 addition & 1 deletion examples/composite-key/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ publish = false
toasty = { path = "../../src/toasty" }
toasty-sqlite = { path = "../../src/db/sqlite" }

tokio = { version = "1.18", features = ["full"] }
tokio.workspace = true
2 changes: 1 addition & 1 deletion examples/cratehub/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ publish = false
toasty = { path = "../../src/toasty" }
toasty-sqlite = { path = "../../src/db/sqlite" }

tokio = { version = "1.18", features = ["full"] }
tokio.workspace = true
64 changes: 32 additions & 32 deletions examples/cratehub/src/db/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ impl Package {
super::user::User::filter(super::user::User::ID.eq(&self.user_id)).into_select(),
)
}
pub async fn get_by_user_id(
db: &Db,
user_id: impl IntoExpr<Id<super::user::User>>,
) -> Result<Package> {
Self::filter_by_user_id(user_id).get(db).await
}
pub fn filter_by_user_id(user_id: impl IntoExpr<Id<super::user::User>>) -> Query {
Query::default().filter_by_user_id(user_id)
}
pub async fn get_by_user_id_and_id(
db: &Db,
user_id: impl IntoExpr<Id<super::user::User>>,
Expand All @@ -35,15 +44,6 @@ impl Package {
) -> Query {
Query::default().filter_by_user_id_and_id_batch(keys)
}
pub async fn get_by_user_id(
db: &Db,
user_id: impl IntoExpr<Id<super::user::User>>,
) -> Result<Package> {
Self::filter_by_user_id(user_id).get(db).await
}
pub fn filter_by_user_id(user_id: impl IntoExpr<Id<super::user::User>>) -> Query {
Query::default().filter_by_user_id(user_id)
}
pub fn create() -> builders::CreatePackage {
builders::CreatePackage::default()
}
Expand Down Expand Up @@ -133,6 +133,16 @@ impl Query {
pub const fn from_stmt(stmt: stmt::Select<Package>) -> Query {
Query { stmt }
}
pub async fn get_by_user_id(
self,
db: &Db,
user_id: impl IntoExpr<Id<super::user::User>>,
) -> Result<Package> {
self.filter_by_user_id(user_id).get(db).await
}
pub fn filter_by_user_id(self, user_id: impl IntoExpr<Id<super::user::User>>) -> Query {
self.filter(Package::USER_ID.eq(user_id))
}
pub async fn get_by_user_id_and_id(
self,
db: &Db,
Expand All @@ -157,16 +167,6 @@ impl Query {
) -> Query {
self.filter(stmt::Expr::in_list((Package::USER_ID, Package::ID), keys))
}
pub async fn get_by_user_id(
self,
db: &Db,
user_id: impl IntoExpr<Id<super::user::User>>,
) -> Result<Package> {
self.filter_by_user_id(user_id).get(db).await
}
pub fn filter_by_user_id(self, user_id: impl IntoExpr<Id<super::user::User>>) -> Query {
self.filter(Package::USER_ID.eq(user_id))
}
pub async fn all(self, db: &Db) -> Result<Cursor<Package>> {
db.all(self.stmt).await
}
Expand Down Expand Up @@ -407,6 +407,19 @@ pub mod relations {
pub fn filter_by_id(self, id: impl IntoExpr<Id<Package>>) -> Query {
Query::from_stmt(self.into_select()).filter(Package::ID.eq(id))
}
pub async fn get_by_user_id(
self,
db: &Db,
user_id: impl IntoExpr<Id<super::super::user::User>>,
) -> Result<Package> {
self.filter_by_user_id(user_id).get(db).await
}
pub fn filter_by_user_id(
self,
user_id: impl IntoExpr<Id<super::super::user::User>>,
) -> Query {
Query::from_stmt(self.into_select()).filter(Package::USER_ID.eq(user_id))
}
pub async fn get_by_user_id_and_id(
self,
db: &Db,
Expand All @@ -431,19 +444,6 @@ pub mod relations {
) -> Query {
Query::from_stmt(self.into_select()).filter_by_user_id_and_id_batch(keys)
}
pub async fn get_by_user_id(
self,
db: &Db,
user_id: impl IntoExpr<Id<super::super::user::User>>,
) -> Result<Package> {
self.filter_by_user_id(user_id).get(db).await
}
pub fn filter_by_user_id(
self,
user_id: impl IntoExpr<Id<super::super::user::User>>,
) -> Query {
Query::from_stmt(self.into_select()).filter(Package::USER_ID.eq(user_id))
}
#[doc = r" Iterate all entries in the relation"]
pub async fn all(self, db: &Db) -> Result<Cursor<Package>> {
db.all(self.stmt.into_select()).await
Expand Down
12 changes: 10 additions & 2 deletions examples/hello-toasty/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ publish = false

[dependencies]
toasty = { path = "../../src/toasty" }
toasty-sqlite = { path = "../../src/db/sqlite" }
toasty-sqlite = { path = "../../src/db/sqlite", optional = true }
toasty-pgsql = { path = "../../src/db/pgsql", optional = true }

tokio = { version = "1.18", features = ["full"] }
postgres = { workspace = true, optional = true }
cfg-if.workspace = true
tokio.workspace = true

[features]
default = []
sqlite = ["dep:toasty-sqlite"]
postgresql = ["dep:postgres", "dep:toasty-pgsql"]
54 changes: 27 additions & 27 deletions examples/hello-toasty/src/db/todo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,6 @@ impl Todo {
super::user::User::filter(super::user::User::ID.eq(&self.user_id)).into_select(),
)
}
pub async fn get_by_user_id(
db: &Db,
user_id: impl IntoExpr<Id<super::user::User>>,
) -> Result<Todo> {
Self::filter_by_user_id(user_id).get(db).await
}
pub fn filter_by_user_id(user_id: impl IntoExpr<Id<super::user::User>>) -> Query {
Query::default().filter_by_user_id(user_id)
}
pub async fn get_by_id(db: &Db, id: impl IntoExpr<Id<Todo>>) -> Result<Todo> {
Self::filter_by_id(id).get(db).await
}
Expand All @@ -35,6 +26,15 @@ impl Todo {
pub fn filter_by_id_batch(keys: impl IntoExpr<[Id<Todo>]>) -> Query {
Query::default().filter_by_id_batch(keys)
}
pub async fn get_by_user_id(
db: &Db,
user_id: impl IntoExpr<Id<super::user::User>>,
) -> Result<Todo> {
Self::filter_by_user_id(user_id).get(db).await
}
pub fn filter_by_user_id(user_id: impl IntoExpr<Id<super::user::User>>) -> Query {
Query::default().filter_by_user_id(user_id)
}
pub fn create() -> builders::CreateTodo {
builders::CreateTodo::default()
}
Expand Down Expand Up @@ -118,6 +118,15 @@ impl Query {
pub const fn from_stmt(stmt: stmt::Select<Todo>) -> Query {
Query { stmt }
}
pub async fn get_by_id(self, db: &Db, id: impl IntoExpr<Id<Todo>>) -> Result<Todo> {
self.filter_by_id(id).get(db).await
}
pub fn filter_by_id(self, id: impl IntoExpr<Id<Todo>>) -> Query {
self.filter(Todo::ID.eq(id))
}
pub fn filter_by_id_batch(self, keys: impl IntoExpr<[Id<Todo>]>) -> Query {
self.filter(stmt::Expr::in_list(Todo::ID, keys))
}
pub async fn get_by_user_id(
self,
db: &Db,
Expand All @@ -128,15 +137,6 @@ impl Query {
pub fn filter_by_user_id(self, user_id: impl IntoExpr<Id<super::user::User>>) -> Query {
self.filter(Todo::USER_ID.eq(user_id))
}
pub async fn get_by_id(self, db: &Db, id: impl IntoExpr<Id<Todo>>) -> Result<Todo> {
self.filter_by_id(id).get(db).await
}
pub fn filter_by_id(self, id: impl IntoExpr<Id<Todo>>) -> Query {
self.filter(Todo::ID.eq(id))
}
pub fn filter_by_id_batch(self, keys: impl IntoExpr<[Id<Todo>]>) -> Query {
self.filter(stmt::Expr::in_list(Todo::ID, keys))
}
pub async fn all(self, db: &Db) -> Result<Cursor<Todo>> {
db.all(self.stmt).await
}
Expand Down Expand Up @@ -371,6 +371,15 @@ pub mod relations {
pub fn from_stmt(stmt: stmt::Association<[Todo]>) -> Many {
Many { stmt }
}
pub async fn get_by_id(self, db: &Db, id: impl IntoExpr<Id<Todo>>) -> Result<Todo> {
self.filter_by_id(id).get(db).await
}
pub fn filter_by_id(self, id: impl IntoExpr<Id<Todo>>) -> Query {
Query::from_stmt(self.into_select()).filter(Todo::ID.eq(id))
}
pub fn filter_by_id_batch(self, keys: impl IntoExpr<[Id<Todo>]>) -> Query {
Query::from_stmt(self.into_select()).filter_by_id_batch(keys)
}
pub async fn get_by_user_id(
self,
db: &Db,
Expand All @@ -384,15 +393,6 @@ pub mod relations {
) -> Query {
Query::from_stmt(self.into_select()).filter(Todo::USER_ID.eq(user_id))
}
pub async fn get_by_id(self, db: &Db, id: impl IntoExpr<Id<Todo>>) -> Result<Todo> {
self.filter_by_id(id).get(db).await
}
pub fn filter_by_id(self, id: impl IntoExpr<Id<Todo>>) -> Query {
Query::from_stmt(self.into_select()).filter(Todo::ID.eq(id))
}
pub fn filter_by_id_batch(self, keys: impl IntoExpr<[Id<Todo>]>) -> Query {
Query::from_stmt(self.into_select()).filter_by_id_batch(keys)
}
#[doc = r" Iterate all entries in the relation"]
pub async fn all(self, db: &Db) -> Result<Cursor<Todo>> {
db.all(self.stmt.into_select()).await
Expand Down
36 changes: 18 additions & 18 deletions examples/hello-toasty/src/db/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ impl User {
Self::TODOS.into(),
))
}
pub async fn get_by_email(db: &Db, email: impl IntoExpr<String>) -> Result<User> {
Self::filter_by_email(email).get(db).await
}
pub fn filter_by_email(email: impl IntoExpr<String>) -> Query {
Query::default().filter_by_email(email)
}
pub async fn get_by_id(db: &Db, id: impl IntoExpr<Id<User>>) -> Result<User> {
Self::filter_by_id(id).get(db).await
}
Expand All @@ -35,6 +29,12 @@ impl User {
pub fn filter_by_id_batch(keys: impl IntoExpr<[Id<User>]>) -> Query {
Query::default().filter_by_id_batch(keys)
}
pub async fn get_by_email(db: &Db, email: impl IntoExpr<String>) -> Result<User> {
Self::filter_by_email(email).get(db).await
}
pub fn filter_by_email(email: impl IntoExpr<String>) -> Query {
Query::default().filter_by_email(email)
}
pub fn create() -> builders::CreateUser {
builders::CreateUser::default()
}
Expand Down Expand Up @@ -119,12 +119,6 @@ impl Query {
pub const fn from_stmt(stmt: stmt::Select<User>) -> Query {
Query { stmt }
}
pub async fn get_by_email(self, db: &Db, email: impl IntoExpr<String>) -> Result<User> {
self.filter_by_email(email).get(db).await
}
pub fn filter_by_email(self, email: impl IntoExpr<String>) -> Query {
self.filter(User::EMAIL.eq(email))
}
pub async fn get_by_id(self, db: &Db, id: impl IntoExpr<Id<User>>) -> Result<User> {
self.filter_by_id(id).get(db).await
}
Expand All @@ -134,6 +128,12 @@ impl Query {
pub fn filter_by_id_batch(self, keys: impl IntoExpr<[Id<User>]>) -> Query {
self.filter(stmt::Expr::in_list(User::ID, keys))
}
pub async fn get_by_email(self, db: &Db, email: impl IntoExpr<String>) -> Result<User> {
self.filter_by_email(email).get(db).await
}
pub fn filter_by_email(self, email: impl IntoExpr<String>) -> Query {
self.filter(User::EMAIL.eq(email))
}
pub async fn all(self, db: &Db) -> Result<Cursor<User>> {
db.all(self.stmt).await
}
Expand Down Expand Up @@ -390,12 +390,6 @@ pub mod relations {
pub fn from_stmt(stmt: stmt::Association<[User]>) -> Many {
Many { stmt }
}
pub async fn get_by_email(self, db: &Db, email: impl IntoExpr<String>) -> Result<User> {
self.filter_by_email(email).get(db).await
}
pub fn filter_by_email(self, email: impl IntoExpr<String>) -> Query {
Query::from_stmt(self.into_select()).filter(User::EMAIL.eq(email))
}
pub async fn get_by_id(self, db: &Db, id: impl IntoExpr<Id<User>>) -> Result<User> {
self.filter_by_id(id).get(db).await
}
Expand All @@ -405,6 +399,12 @@ pub mod relations {
pub fn filter_by_id_batch(self, keys: impl IntoExpr<[Id<User>]>) -> Query {
Query::from_stmt(self.into_select()).filter_by_id_batch(keys)
}
pub async fn get_by_email(self, db: &Db, email: impl IntoExpr<String>) -> Result<User> {
self.filter_by_email(email).get(db).await
}
pub fn filter_by_email(self, email: impl IntoExpr<String>) -> Query {
Query::from_stmt(self.into_select()).filter(User::EMAIL.eq(email))
}
#[doc = r" Iterate all entries in the relation"]
pub async fn all(self, db: &Db) -> Result<Cursor<User>> {
db.all(self.stmt.into_select()).await
Expand Down
Loading

0 comments on commit 05d3015

Please sign in to comment.