-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
explicit (though not pretty) mysql support
- Loading branch information
Showing
17 changed files
with
418 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
[workspace] | ||
members = ["ormx-macros", "ormx", "example-postgres", "example-mariadb"] | ||
members = ["ormx-macros", "ormx", "example-postgres", "example-mariadb", "example-mysql"] | ||
resolver = "2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[package] | ||
name = "example-mysql" | ||
version = "0.1.0" | ||
authors = ["moritz"] | ||
edition = "2021" | ||
|
||
[dependencies] | ||
ormx = { path = "../ormx", features = ["mysql"] } | ||
tokio = { version = "1", features = ["full"] } | ||
anyhow = "1" | ||
dotenv = "0.15" | ||
chrono = "0.4" | ||
env_logger = "0.11.5" | ||
log = "0.4" | ||
|
||
[dependencies.sqlx] | ||
version = "0.8" | ||
default-features = false | ||
features = ["macros", "mysql", "runtime-tokio-rustls", "chrono"] |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
use chrono::{NaiveDateTime, Utc}; | ||
use log::LevelFilter; | ||
use ormx::{Delete, Insert, Table}; | ||
use sqlx::MySqlPool; | ||
|
||
// To run this example, first run `/scripts/mariadb.sh` to start mariadb in a docker container and | ||
// write the database URL to `.env`. Then, source `.env` (`. .env`) and run `cargo run` | ||
|
||
mod query2; | ||
|
||
#[tokio::main] | ||
async fn main() -> anyhow::Result<()> { | ||
dotenv::dotenv().ok(); | ||
env_logger::builder() | ||
.filter_level(LevelFilter::Debug) | ||
.init(); | ||
|
||
let db = MySqlPool::connect(&dotenv::var("DATABASE_URL")?).await?; | ||
|
||
log::info!("insert a new row into the database"); | ||
let mut new = InsertUser { | ||
first_name: "Moritz".to_owned(), | ||
last_name: "Bischof".to_owned(), | ||
email: "[email protected]".to_owned(), | ||
disabled: None, | ||
} | ||
.insert(&mut *db.acquire().await?) | ||
.await?; | ||
|
||
log::info!("update a single field"); | ||
new.set_last_login(&db, Some(Utc::now().naive_utc())) | ||
.await?; | ||
|
||
log::info!("update all fields at once"); | ||
new.email = "asdf".to_owned(); | ||
new.update(&db).await?; | ||
|
||
log::info!("apply a patch to the user, updating its first and last name"); | ||
new.patch( | ||
&db, | ||
UpdateName { | ||
first_name: "NewFirstName".to_owned(), | ||
last_name: "NewLastName".to_owned(), | ||
disabled: Some("Reason".to_owned()), | ||
}, | ||
) | ||
.await?; | ||
|
||
log::info!("reload the user, in case it has been modified"); | ||
new.reload(&db).await?; | ||
|
||
log::info!("use the improved query macro for searching users"); | ||
let search_result = query2::query_users(&db, Some("NewFirstName"), None).await?; | ||
println!("{:?}", search_result); | ||
|
||
log::info!("delete the user from the database"); | ||
new.delete(&db).await?; | ||
|
||
Ok(()) | ||
} | ||
|
||
#[derive(Debug, ormx::Table)] | ||
#[ormx(table = "users", id = user_id, insertable, deletable)] | ||
struct User { | ||
// map this field to the column "id" | ||
#[ormx(column = "id")] | ||
// generate `User::get_by_user_id(u32) -> Result<Self>` | ||
#[ormx(get_one = get_by_user_id(u32))] | ||
// this column is database-generated. | ||
#[ormx(default)] | ||
user_id: u32, | ||
first_name: String, | ||
last_name: String, | ||
// generate `User::by_email(&str) -> Result<Option<Self>>` | ||
#[ormx(get_optional(&str))] | ||
email: String, | ||
disabled: Option<String>, | ||
// don't include this field into `InsertUser` since it has a default value | ||
// generate `User::set_last_login(Option<NaiveDateTime>) -> Result<()>` | ||
#[ormx(default, set)] | ||
last_login: Option<NaiveDateTime>, | ||
} | ||
|
||
// Patches can be used to update multiple fields at once (in diesel, they're called "ChangeSets"). | ||
#[derive(ormx::Patch)] | ||
#[ormx(table_name = "users", table = crate::User, id = "id")] | ||
struct UpdateName { | ||
first_name: String, | ||
last_name: String, | ||
disabled: Option<String>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
use sqlx::MySqlPool; | ||
|
||
use crate::User; | ||
|
||
pub(crate) async fn query_users( | ||
db: &MySqlPool, | ||
filter: Option<&str>, | ||
limit: Option<usize>, | ||
) -> anyhow::Result<Vec<User>> { | ||
let result = ormx::conditional_query_as!( | ||
User, | ||
r#"SELECT id AS user_id, first_name, last_name, email, disabled, last_login"# | ||
"FROM users" | ||
Some(f) = filter => { | ||
"WHERE first_name LIKE" ?(f) | ||
"OR last_name LIKE" ?(f) | ||
} | ||
"ORDER BY first_name DESC" | ||
Some(l) = limit => { | ||
"LIMIT" ?(l as i64) | ||
} | ||
) | ||
.fetch_all(db) | ||
.await?; | ||
|
||
Ok(result) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ proc-macro = true | |
[features] | ||
sqlite = [] | ||
mysql = [] | ||
mariadb = [] | ||
postgres = [] | ||
|
||
[dependencies] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
use itertools::Itertools; | ||
use proc_macro2::{Span, TokenStream}; | ||
use quote::quote; | ||
use syn::Ident; | ||
|
||
use crate::{ | ||
backend::mariadb::{MariaBackend, MariaBindings}, | ||
table::{Table, TableField}, | ||
}; | ||
|
||
pub fn impl_insert(table: &Table<MariaBackend>) -> TokenStream { | ||
let insert_ident = match &table.insertable { | ||
Some(i) => &i.ident, | ||
None => return quote!(), | ||
}; | ||
|
||
let insert_fields: Vec<&TableField<MariaBackend>> = table.insertable_fields().collect(); | ||
let default_fields: Vec<&TableField<MariaBackend>> = table.default_fields().collect(); | ||
|
||
let table_ident = &table.ident; | ||
let insert_field_idents = insert_fields.iter().map(|field| &field.field); | ||
let default_field_idents = default_fields.iter().map(|field| &field.field); | ||
let default_field_ordinals = (0usize..).take(default_fields.len()); | ||
|
||
let insert_sql = insert_sql(table, &insert_fields); | ||
|
||
let insert_field_exprs = insert_fields.iter().map(|f| f.fmt_as_argument()); | ||
|
||
let fetch_fn = if default_fields.is_empty() { | ||
Ident::new("execute", Span::call_site()) | ||
} else { | ||
Ident::new("fetch_one", Span::call_site()) | ||
}; | ||
|
||
quote! { | ||
impl ormx::Insert for #insert_ident { | ||
type Table = #table_ident; | ||
|
||
async fn insert<'a, 'c: 'a>( | ||
self, | ||
db: impl sqlx::Executor<'c, Database = ormx::Db> + 'a, | ||
) -> sqlx::Result<Self::Table> { | ||
use sqlx::Row; | ||
|
||
let _generated = sqlx::query!(#insert_sql, #( #insert_field_exprs, )*) | ||
.#fetch_fn(db) | ||
.await?; | ||
|
||
Ok(Self::Table { | ||
#( #insert_field_idents: self.#insert_field_idents, )* | ||
#( #default_field_idents: _generated.get(#default_field_ordinals), )* | ||
}) | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn insert_sql(table: &Table<MariaBackend>, insert_fields: &[&TableField<MariaBackend>]) -> String { | ||
let columns = insert_fields.iter().map(|field| field.column()).join(", "); | ||
let fields = MariaBindings::default() | ||
.take(insert_fields.len()) | ||
.join(", "); | ||
let returning_fields = table | ||
.default_fields() | ||
.map(TableField::fmt_for_select) | ||
.join(", "); | ||
|
||
if returning_fields.is_empty() { | ||
format!( | ||
"INSERT INTO {} ({}) VALUES ({})", | ||
table.name(), | ||
columns, | ||
fields | ||
) | ||
} else { | ||
format!( | ||
"INSERT INTO {} ({}) VALUES ({}) RETURNING {}", | ||
table.name(), | ||
columns, | ||
fields, | ||
returning_fields | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
use std::borrow::Cow; | ||
|
||
use proc_macro2::TokenStream; | ||
use quote::quote; | ||
|
||
use crate::{backend::Backend, table::Table}; | ||
|
||
mod insert; | ||
|
||
#[derive(Clone)] | ||
pub struct MariaBackend; | ||
|
||
impl Backend for MariaBackend { | ||
const QUOTE: char = '`'; | ||
const IS_MYSQL: bool = true; | ||
|
||
type Bindings = MariaBindings; | ||
|
||
fn query_result() -> TokenStream { | ||
quote!(sqlx::mysql::MySqlQueryResult) | ||
} | ||
|
||
fn impl_insert(table: &Table<Self>) -> TokenStream { | ||
insert::impl_insert(table) | ||
} | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct MariaBindings; | ||
|
||
impl Iterator for MariaBindings { | ||
type Item = Cow<'static, str>; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
Some(Cow::Borrowed("?")) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.