diff options
author | Melody Horn <melody@boringcactus.com> | 2021-06-30 15:58:48 -0600 |
---|---|---|
committer | Melody Horn <melody@boringcactus.com> | 2021-06-30 15:58:48 -0600 |
commit | b4c7c3cd480e626ac78b49ab91a95340ccfef26a (patch) | |
tree | c1966362001b67b80e8f54a7a9f94be8a47aaf7c | |
parent | bdfdcb98d39d1887b08748e1ea629f0a83f340a4 (diff) | |
download | tosin-b4c7c3cd480e626ac78b49ab91a95340ccfef26a.tar.gz tosin-b4c7c3cd480e626ac78b49ab91a95340ccfef26a.zip |
finish inserting save_mut for models
-rw-r--r-- | examples/tutorial02/main.rs | 4 | ||||
-rw-r--r-- | examples/tutorial02/polls/models.rs | 2 | ||||
-rw-r--r-- | src/db/backend/sqlite.rs | 45 | ||||
-rw-r--r-- | tosin-macros/src/lib.rs | 33 |
4 files changed, 73 insertions, 11 deletions
diff --git a/examples/tutorial02/main.rs b/examples/tutorial02/main.rs index 5d78e5b..785c8f0 100644 --- a/examples/tutorial02/main.rs +++ b/examples/tutorial02/main.rs @@ -45,14 +45,14 @@ mod test { chrono::Local::now().naive_local(), ); // save it - q.save_mut(); + q.save_mut(&connection); // it's got an id now! assert!(q.id().is_some()); // it's still got all the same fields! assert_eq!(q.question_text(), "What's new?"); // we can change them! q.set_question_text("What's up?"); - q.save_mut(); + q.save_mut(&connection); // it should be in the list now, too!! let all_questions = question::table.load::<Question>(&connection).unwrap(); diff --git a/examples/tutorial02/polls/models.rs b/examples/tutorial02/polls/models.rs index 48e58cc..cd480da 100644 --- a/examples/tutorial02/polls/models.rs +++ b/examples/tutorial02/polls/models.rs @@ -17,7 +17,7 @@ pub struct Choice { #[model(max_length = 200)] choice_text: String, #[model(default = 0)] - votes: usize, + votes: i64, } gather!(); diff --git a/src/db/backend/sqlite.rs b/src/db/backend/sqlite.rs index 002d0d3..eb4468c 100644 --- a/src/db/backend/sqlite.rs +++ b/src/db/backend/sqlite.rs @@ -1,6 +1,7 @@ use diesel::connection::Connection as _; pub use barrel::backend::Sqlite as SqlGenerator; +pub use diesel::sqlite::Sqlite as Backend; pub use diesel::sqlite::SqliteConnection as Connection; pub struct Database { @@ -20,3 +21,47 @@ impl Default for Database { } } } + +use diesel::{ + no_arg_sql_function, no_arg_sql_function_body, no_arg_sql_function_body_except_to_sql, QueryId, +}; +no_arg_sql_function!( + last_insert_rowid, + diesel::sql_types::BigInt, + "Represents the SQL last_insert_rowid() function" +); + +use diesel::{ + query_builder::{AsQuery, InsertStatement, QueryFragment, QueryId}, + query_dsl::methods::{FilterDsl, LoadQuery}, + sql_types::BigInt, + Column, Expression, Insertable, Queryable, +}; + +pub fn insert_and_retrieve<'a, Row, Table, Id>( + to_insert: &'a Row, + table: Table, + connection: &Connection, + id: Id, +) -> Row +where + &'a Row: Insertable<Table>, + Row: Queryable<Table::SqlType, Backend>, + Table: diesel::Table + AsQuery + Copy, + InsertStatement<Table, <&'a Row as Insertable<Table>>::Values>: QueryFragment<Backend>, + <Table as AsQuery>::Query: QueryFragment<Backend> + + QueryId + + FilterDsl<diesel::expression::operators::Eq<Id, last_insert_rowid>>, + Id: Column + Expression<SqlType = BigInt>, + <Table as FilterDsl<diesel::expression::operators::Eq<Id, last_insert_rowid>>>::Output: + LoadQuery<Connection, Row>, +{ + use diesel::prelude::*; + to_insert + .insert_into(table) + .execute(connection) + .expect("error saving to database"); // TODO propagate error + diesel::QueryDsl::filter(table, id.eq(last_insert_rowid)) + .get_result(connection) + .expect("error loading from database") // TODO propagate error +} diff --git a/tosin-macros/src/lib.rs b/tosin-macros/src/lib.rs index ff62f3d..1019540 100644 --- a/tosin-macros/src/lib.rs +++ b/tosin-macros/src/lib.rs @@ -84,15 +84,22 @@ fn to_field_spec(field: &syn::Field) -> FieldInfo<impl quote::ToTokens> { // TODO foreign key constraint FieldInfo { tosin_field: quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } }, + diesel_column: quote! { #field_name -> BigInt }, + diesel_type: quote! { ::tosin::db::sql_types::BigInt }, + } + } else if field_type == &parse_type("u64") { + // TODO allow at all since some dbs can't express that type + FieldInfo { + tosin_field: quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } }, diesel_column: quote! { #field_name -> Integer }, diesel_type: quote! { ::tosin::db::sql_types::Integer }, } - } else if field_type == &parse_type("usize") { + } else if field_type == &parse_type("i64") { // TODO default FieldInfo { tosin_field: quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } }, - diesel_column: quote! { #field_name -> Integer }, - diesel_type: quote! { ::tosin::db::sql_types::Integer }, + diesel_column: quote! { #field_name -> BigInt }, + diesel_type: quote! { ::tosin::db::sql_types::BigInt }, } } else if field_type == &parse_type("String") { let max_length = model_options @@ -211,13 +218,22 @@ fn impl_model(ast: &syn::DeriveInput) -> TokenStream { } } - pub fn save_mut(&mut self, connection: &mut tosin::db::backend::Connection) { + pub fn save_mut(&mut self, connection: &tosin::db::backend::Connection) { + use diesel::prelude::*; if self.id.is_none() { // no id yet, so not from db, so insert - let new_self = tosin::db::insert_into(#lowercase_name::table) - .values(&self) - .get_result(connection) - .expect("error saving to database"); // TODO propagate error + // unfortunately InsertStatement::get_result is only supported on pg for now, + // so we have to pull this shit + let new_self = tosin::db::backend::insert_and_retrieve::< + Self, // row + #lowercase_name::table, // table + #lowercase_name::id, // id + >( + self, + #lowercase_name::table, + connection, + #lowercase_name::id + ); *self = new_self; } else { todo!("update existing db item"); @@ -246,6 +262,7 @@ fn impl_model(ast: &syn::DeriveInput) -> TokenStream { type Values = <(#(#insertable_types,)*) as tosin::db::Insertable<#lowercase_name::table>>::Values; fn values(self) -> Self::Values { + use diesel::ExpressionMethods; (#(#insertable_values,)*).values() } } |