aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2021-06-30 15:58:48 -0600
committerMelody Horn <melody@boringcactus.com>2021-06-30 15:58:48 -0600
commitb4c7c3cd480e626ac78b49ab91a95340ccfef26a (patch)
treec1966362001b67b80e8f54a7a9f94be8a47aaf7c
parentbdfdcb98d39d1887b08748e1ea629f0a83f340a4 (diff)
downloadtosin-b4c7c3cd480e626ac78b49ab91a95340ccfef26a.tar.gz
tosin-b4c7c3cd480e626ac78b49ab91a95340ccfef26a.zip
finish inserting save_mut for models
-rw-r--r--examples/tutorial02/main.rs4
-rw-r--r--examples/tutorial02/polls/models.rs2
-rw-r--r--src/db/backend/sqlite.rs45
-rw-r--r--tosin-macros/src/lib.rs33
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()
}
}