-
Notifications
You must be signed in to change notification settings - Fork 1
add support for type hints in :bindings and output-columns #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
39bba73
264298b
0c27271
0d2834a
214f98a
ff00577
a99d4b2
3fe5498
067da4a
01973b9
ed2e506
4fc4e1e
7dc415d
f66d93c
f291594
0bc5445
ad24deb
bc6bb12
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,6 +48,182 @@ async fn main() -> Result<()> { | |
| cargo add --git https://github.com/swlkr/static_sqlite | ||
| ``` | ||
|
|
||
|
|
||
| # Example for Transactions | ||
|
|
||
| Use the methods begin_transaction, commit_transaction and rollback_transaction to manage Sqlite transactions. | ||
|
|
||
|
|
||
| ```rust | ||
|
|
||
| // migration and sql-fn definition goes here | ||
|
|
||
| let db = static_sqlite::open(":memory:").await?; | ||
|
|
||
| migrate(&db).await?; | ||
|
|
||
| db.begin_transaction()?; | ||
| insert_row(&db, "test1").await?.first_row()?; | ||
| insert_row(&db, "test2").await?.first_row()?; | ||
| db.commit_transaction()?; | ||
| ``` | ||
|
|
||
| # Example for First | ||
|
|
||
| If the name of your statement ends with "_first", the created fn return an Option<T> with the first value instead of a Vec<T>. | ||
|
|
||
| I the query returns more than one rows, it throws an error. | ||
|
|
||
| ```rust | ||
| sql! { | ||
| let migrate = r#" | ||
| create table Row ( | ||
| id integer primary key autoincrement, | ||
| txt text NOT NULL | ||
| ) | ||
| "#; | ||
|
|
||
| let insert_row = r#" | ||
| insert into Row (txt) values (:txt) returning * | ||
| "#; | ||
|
|
||
| let select_row = r#" | ||
| select * from Row where id = :id | ||
| "#; | ||
| } | ||
|
|
||
| let db = static_sqlite::open(":memory:").await?; | ||
| migrate(&db).await?; | ||
|
|
||
| insert_row(&db, "test1").await?.first_row()?; | ||
| insert_row(&db, "test2").await?.first_row()?; | ||
|
|
||
| match select_row_first(&db, 1).await? { | ||
| Some(row) => assert_eq!(row.txt, "test1"), | ||
| None => panic!("Row 1 not found"), | ||
| } | ||
| ``` | ||
|
|
||
| # Example for Streams | ||
|
|
||
| If the name of your statement ends with "_stream", the created fn return an async Stream<T> instead of a Vec<T>. | ||
|
|
||
| This way you can iterate over large result sets. | ||
|
|
||
| ```rust | ||
| sql! { | ||
| let migrate = r#" | ||
| create table Row ( | ||
| txt text | ||
| ) | ||
| "#; | ||
|
|
||
| let insert_row = r#" | ||
| insert into Row (txt) values (:txt) returning * | ||
| "#; | ||
|
|
||
| let select_rows_stream = r#" | ||
| select * from Row | ||
| "#; | ||
| } | ||
|
|
||
| let db = static_sqlite::open(":memory:").await?; | ||
| migrate(&db).await?; | ||
|
|
||
| insert_row(&db, Some("test1")).await?.first_row()?; | ||
| insert_row(&db, Some("test2")).await?.first_row()?; | ||
| insert_row(&db, Some("test3")).await?.first_row()?; | ||
| insert_row(&db, Some("test4")).await?.first_row()?; | ||
|
|
||
| let f = select_rows_stream(&db).await?; | ||
|
|
||
| pin_mut!(f); | ||
|
|
||
| assert_eq!(f.next().await.unwrap().unwrap().txt, Some("test1".into())); | ||
| assert_eq!(f.next().await.unwrap().unwrap().txt, Some("test2".into())); | ||
| assert_eq!(f.next().await.unwrap().unwrap().txt, Some("test3".into())); | ||
| assert_eq!(f.next().await.unwrap().unwrap().txt, Some("test4".into())); | ||
| } | ||
|
|
||
| ``` | ||
|
|
||
| # Example with aliased columns and type-hints | ||
|
|
||
| Sometimes the type of either a bound parameter or a returned column can not be inferred by | ||
| sqlite / static_sqlite (see [sqlite3 docs](https://www.sqlite.org/c3ref/column_decltype.html)) | ||
|
|
||
| In this case you can use type-hints to help the static_sqlite to use the correct type. | ||
|
|
||
| To use type-hints your parameter or column name needs to follow the following format: | ||
|
|
||
| ``` | ||
| <name>__<INTEGER|REAL|TEXT|BLOB> | ||
| ``` | ||
|
|
||
| or | ||
|
|
||
| ``` | ||
| <name>__<INTEGER|REAL|TEXT|BLOB>__<NULLABLE|NOT_NULL> | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I kind of want to keep it limited to valid sql only, even if it does sort of limit this lib
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 - I also like to still be able to copy/paste the SQL from/to other tools. the proposed attributes would still be legal though. I have no real idea about what a macro does/how exactly the tokenization etc works, but if the macro didn't just expect a string here but accepted additional "params" like this would keep the SQL clearer. |
||
| ``` | ||
|
|
||
| If not explicitly specified, the parameter or column is assumed to be NOT NULL. | ||
|
|
||
| ```rust | ||
| sql! { | ||
| let migrate = r#" | ||
| create table User ( | ||
| id integer primary key, | ||
| name text unique not null | ||
| ); | ||
| create table Friendship ( | ||
| id integer primary key, | ||
| user_id integer not null references User(id), | ||
| friend_id integer not null references User(id) | ||
| ); | ||
| "#; | ||
|
|
||
| let insert_user = r#" | ||
| insert into User (name) | ||
| values (:name) | ||
| returning * | ||
| "#; | ||
| let create_friendship = r#" | ||
| insert into Friendship (user_id, friend_id) | ||
| values (:user_id, :friend_id) | ||
| returning * | ||
| "#; | ||
| let get_friendship = r#" | ||
| select | ||
| u1.name as friend1_name__TEXT, | ||
| u2.name as friend2_name__TEXT | ||
| from Friendship, User as u1, User as u2 | ||
| where Friendship.user_id = u1.id | ||
| and Friendship.friend_id = u2.id | ||
| and Friendship.id = :friendship_id__INTEGER | ||
| "#; | ||
| } | ||
|
|
||
|
|
||
| #[tokio::main] | ||
| async fn main() -> Result<()> { | ||
| let db = static_sqlite::open(":memory:").await?; | ||
| let _ = migrate(&db).await?; | ||
| insert_user(&db, "swlkr").await?; | ||
| insert_user(&db, "toolbar23").await?; | ||
| create_friendship(&db, 1, 2).await?; | ||
|
|
||
| let friends = get_friendship(&db, 1).await?; | ||
|
|
||
| assert_eq!(friends.len(), 1); | ||
| assert_eq!(friends.first().unwrap().friend1_name, "swlkr"); | ||
| assert_eq!(friends.first().unwrap().friend2_name, "toolbar23"); | ||
|
|
||
| Ok(()) | ||
| } | ||
| ``` | ||
|
|
||
|
|
||
|
|
||
| # Treesitter | ||
|
|
||
| ``` | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| extern crate self as static_sqlite; | ||
| pub use static_sqlite_async::{ | ||
| execute, execute_all, open, query, rows, Error, FromRow, Result, Savepoint, Sqlite, Value, | ||
| execute, execute_all, open, query, query_first, rows, stream, Error, FromRow, Result, | ||
| Savepoint, Sqlite, Value, | ||
| }; | ||
| pub use static_sqlite_core::FirstRow; | ||
| pub use static_sqlite_macros::sql; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is interesting, I actually had a feature in a previous version where you could infer a single row from
limit 1want to bring that back? instead of a_firstsuffix?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I think that would be better than a suffix.