#[schema]
Expand description
Use this macro to define your schema.
§Supported data types:
i64
(sqliteinteger
)f64
(sqlitereal
)String
(sqlitetext
)Vec<u8>
(sqliteblob
)- Any table in the same schema (sqlite
integer
with foreign key constraint) Option<T>
whereT
is not anOption
(sqlite nullable)
Booleans are not supported in schemas yet.
§Unique constraints
To define a unique constraint on a column, you need to add an attribute to the table or field.
The attribute needs to start with unique
and can have any suffix.
Within a table, the different unique constraints must have different suffixes.
For example:
#[rust_query::migration::schema(Schema)]
#[version(0..=0)]
pub mod vN {
pub struct User {
#[unique_email]
pub email: String,
#[unique_username]
pub 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.
Note that optional types are not allowed in unique constraints.
§Multiple versions
The macro must be applied to a module named vN
. This is because the module
is a template that is used to generate multiple modules called v0
, v1
etc.
Each module corresponds to a schema version and contains the types to work with that schema.
Note in the previous example that the schema version range is 0..=0
so there is only a version 0.
The generated code will have a structure like this:
pub mod v0 {
pub struct Schema;
pub struct User{..};
// a bunch of other stuff
}
pub struct MacroRoot;
§Adding tables
At some point you might want to add a new table.
#[rust_query::migration::schema(Schema)]
#[version(0..=1)]
pub mod vN {
pub struct User {
#[unique_email]
pub email: String,
#[unique_username]
pub username: String,
}
#[version(1..)] // <-- note that `Game`` has a version range
pub struct Game {
pub name: String,
pub size: i64,
}
}
We now have two schema versions which generates two modules v0
and v1
.
They look something like this:
pub mod v0 {
pub struct Schema;
pub struct User{..};
pub mod migrate {..}
// a bunch of other stuff
}
pub mod v1 {
pub struct Schema;
pub struct User{..};
pub struct Game{..};
// a bunch of other stuff
}
pub struct MacroRoot;
§Changing columns
Changing columns is very similar to adding and removing structs.
use rust_query::migration::{schema, Config};
use rust_query::{LocalClient, Database};
#[schema(Schema)]
#[version(0..=1)]
pub mod vN {
pub struct User {
#[unique_email]
pub email: String,
#[unique_username]
pub username: String,
#[version(1..)] // <-- here
pub score: i64,
}
}
// In this case it is required to provide a value for each row that already exists.
// This is done with the `v0::migrate::User` struct:
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(|txn| v0::migrate::Schema {
user: txn.migrate_ok(|old: v0::User!(email)| v0::migrate::User {
score: old.email.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.
§#[from]
Attribute
You can use this attribute when renaming or splitting a table.
This will make it clear that data in the table should have the
same row ids as the from
table.
For example:
#[schema(Schema)]
#[version(0..=1)]
pub mod vN {
#[version(..1)]
pub struct User {
pub name: String,
}
#[version(1..)]
#[from(User)]
pub struct Author {
pub name: String,
}
pub struct Book {
pub author: Author,
}
}
In this example the Book
table exists in both v0
and v1
,
however User
only exists in v0
and Author
only exist in v1
.
Note that the pub author: Author
field only specifies the latest version
of the table, it will use the #[from]
attribute to find previous versions.
This will work correctly and will let you migrate data from User
to Author
with code like this:
let m = m.migrate(|txn| v0::migrate::Schema {
author: txn.migrate_ok(|old: v0::User!(name)| v0::migrate::Author {
name: old.name,
}),
});
§#[no_reference]
Attribute
You can put this attribute on your table definitions and it will make it impossible
to have foreign key references to such table.
This makes it possible to use TransactionWeak::delete_ok
.