#[schema]Expand description
Use this macro to define your schema.
§Supported data types:
i64(sqliteinteger)f64(sqlitereal)String(sqlitetext)- Any table in the same schema (sqlite
integerwith foreign key constraint) Option<T>whereTis not anOption(sqlite nullable)
Booleans are not supported in schemas yet.
§Unique constraints
For example:
#[rust_query::migration::schema]
#[version(0..=0)]
enum Schema {
User {
#[unique_email]
email: String,
#[unique_username]
username: String,
}
}This will create a single schema with a single table called user and two columns.
The table will also have two unique contraints.
To define a unique constraint on a column, you need to add an attribute to that field.
The attribute needs to start with unique and can have any suffix.
Within a table, the different unique constraints must have different suffixes.
Optional types are not allowed in unique constraints.
§Multiple versions
The macro uses enum syntax, but it generates multiple modules of types.
Note that the schema version range is 0..=0 so there is only a version 0.
The generated code will have a structure like this:
mod v0 {
struct User(..);
// a bunch of other stuff
}§Adding tables
At some point you might want to add a new table.
#[rust_query::migration::schema]
#[version(0..=1)]
enum Schema {
User {
#[unique_email]
email: String,
#[unique_username]
username: String,
},
#[version(1..)] // <-- note that `Game`` has a version range
Game {
name: String,
size: i64,
}
}We now have two schema versions which generates two modules v0 and v1.
They look something like this:
mod v0 {
struct User(..);
// a bunch of other stuff
}
mod v1 {
struct User(..);
struct Game(..);
// a bunch of other stuff
}§Changing columns
Changing columns is very similar to adding and removing structs.
use rust_query::migration::{schema, Config, Alter};
use rust_query::{Dummy, LocalClient, Database};
#[schema]
#[version(0..=1)]
enum Schema {
User {
#[unique_email]
email: String,
#[unique_username]
username: String,
#[version(1..)] // <-- here
score: i64,
},
}
// In this case it is required to provide a value for each row that already exists.
// This is done with the `v1::update::UserMigration`:
pub fn migrate(client: &mut LocalClient) -> Database<v1::Schema> {
let m = client.migrator(Config::open_in_memory()) // we use an in memory database for this test
.expect("database version is before supported versions");
let m = m.migrate(v1::update::Schema {
user: Box::new(|user|
Alter::new(v1::update::UserMigration {
score: user.email().map_dummy(|x| x.len() as i64) // use the email length as the new score
})
),
});
m.finish().expect("database version is after supported versions")
}The migrate function first creates an empty database if it does not exists.
Then it migrates the database if necessary, where it initializes every user score to the length of their email.
§Other features
You can delete columns and tables by specifying the version range end.
#[version(..3)]You can make a multi column unique constraint by specifying it before the table.
#[unique(user, game)]
UserGameStats {
user: User,
game: Game,
score: i64,
}