rust_query/lib.rs
1#![allow(private_bounds, private_interfaces)]
2#![doc = include_str!("../README.md")]
3#![cfg(feature = "base0")]
4
5extern crate self as rust_query;
6
7#[macro_use]
8extern crate static_assertions;
9
10mod alias;
11mod ast;
12mod async_db;
13mod db;
14mod error;
15mod joinable;
16mod lazy;
17mod migrate;
18mod mutable;
19#[cfg(feature = "mutants")]
20mod mutants;
21mod mymap;
22mod pool;
23mod query;
24mod rows;
25mod schema;
26mod select;
27mod transaction;
28mod value;
29mod writable;
30
31use alias::JoinableTable;
32use private::Reader;
33use schema::from_macro::TypBuilder;
34use std::ops::Deref;
35
36pub use async_db::DatabaseAsync;
37pub use db::TableRow;
38pub use error::Conflict;
39pub use lazy::Lazy;
40pub use mutable::Mutable;
41pub use select::{IntoSelect, Select};
42pub use transaction::{Database, Transaction, TransactionWeak};
43pub use value::aggregate::aggregate;
44pub use value::from_expr::FromExpr;
45pub use value::{Expr, into_expr::IntoExpr, optional::optional};
46
47/// Derive [derive@Select] to create a new `*Select` struct.
48///
49/// This `*Select` struct will implement the [IntoSelect] trait and can be used
50/// with [args::Query::into_iter], [Transaction::query_one] etc.
51///
52/// Usage can also be nested.
53///
54/// ```
55/// #[rust_query::migration::schema(Schema)]
56/// pub mod vN {
57/// pub struct Thing {
58/// pub details: rust_query::TableRow<Details>,
59/// pub beta: f64,
60/// pub seconds: i64,
61/// }
62/// pub struct Details {
63/// pub name: String,
64/// }
65/// }
66/// use v0::*;
67/// use rust_query::{Table, Select, Transaction};
68///
69/// #[derive(Select)]
70/// struct MyData {
71/// seconds: i64,
72/// is_it_real: bool,
73/// name: String,
74/// other: OtherData
75/// }
76///
77/// #[derive(Select)]
78/// struct OtherData {
79/// alpha: f64,
80/// beta: f64,
81/// }
82///
83/// fn do_query(db: &Transaction<Schema>) -> Vec<MyData> {
84/// db.query(|rows| {
85/// let thing = rows.join(Thing);
86///
87/// rows.into_vec(MyDataSelect {
88/// seconds: &thing.seconds,
89/// is_it_real: thing.seconds.lt(100),
90/// name: &thing.details.name,
91/// other: OtherDataSelect {
92/// alpha: thing.beta.add(2.0),
93/// beta: &thing.beta,
94/// },
95/// })
96/// })
97/// }
98/// # fn main() {}
99/// ```
100pub use rust_query_macros::Select;
101
102/// Use in combination with `#[rust_query(From = Thing)]` to specify which tables
103/// this struct should implement [trait@FromExpr] for.
104///
105/// The implementation of [trait@FromExpr] will initialize every field from the column with
106/// the corresponding name. It is also possible to change the type of each field
107/// as long as the new field type implements [trait@FromExpr].
108///
109/// ```
110/// # use rust_query::migration::schema;
111/// # use rust_query::{TableRow, FromExpr};
112/// #[schema(Example)]
113/// pub mod vN {
114/// pub struct User {
115/// pub name: String,
116/// pub score: i64,
117/// pub best_game: Option<rust_query::TableRow<Game>>,
118/// }
119/// pub struct Game;
120/// }
121///
122/// #[derive(FromExpr)]
123/// #[rust_query(From = v0::User)]
124/// struct MyUserFields {
125/// name: String,
126/// best_game: Option<TableRow<v0::Game>>,
127/// }
128/// # fn main() {}
129/// ```
130pub use rust_query_macros::FromExpr;
131
132use crate::error::FromConflict;
133
134/// Types that are used as closure arguments.
135///
136/// You generally don't need to import these types.
137pub mod args {
138 pub use crate::query::{OrderBy, Query};
139 pub use crate::rows::Rows;
140 pub use crate::value::aggregate::Aggregate;
141 pub use crate::value::optional::Optional;
142}
143
144/// Types to declare schemas and migrations.
145///
146/// A good starting point is too look at [crate::migration::schema].
147pub mod migration {
148 pub use crate::migrate::{
149 Migrator,
150 config::{Config, ForeignKeys, Synchronous},
151 migration::{Migrated, TransactionMigrate},
152 };
153 #[cfg(feature = "dev")]
154 pub use crate::schema::dev::hash_schema;
155
156 /// Use this macro to define your schema.
157 ///
158 /// The macro must be applied to a module named `vN`. This is because the module
159 /// is a template that can used to generate multiple modules called `v0`, `v1` etc.
160 /// By default, only one module named `v0` is generated.
161 ///
162 /// ```
163 /// #[rust_query::migration::schema(SchemaName)]
164 /// pub mod vN {
165 /// pub struct TableName {
166 /// pub column_name: i64,
167 /// }
168 /// }
169 /// use v0::TableName; // the actual module name is `v0`
170 /// # fn main() {}
171 /// ```
172 ///
173 /// Note that the schema module, table structs and column fields all must be `pub`.
174 /// The `id` column field is currently reserved for internal use and can not be used.
175 ///
176 /// Supported data types are:
177 /// - `i64` (sqlite `integer`)
178 /// - `f64` (sqlite `real`)
179 /// - `String` (sqlite `text`)
180 /// - `Vec<u8>` (sqlite `blob`)
181 /// - `bool` (sqlite `integer` with `CHECK "col" IN (0, 1)`)
182 /// - Any table in the same schema (sqlite `integer` with foreign key constraint)
183 /// - `Option<T>` where `T` is not an `Option` (sqlite nullable)
184 ///
185 /// ## Unique constraints
186 ///
187 /// To define a unique constraint on a column, you need to add an attribute to the table or field.
188 ///
189 /// For example:
190 /// ```
191 /// #[rust_query::migration::schema(SchemaName)]
192 /// pub mod vN {
193 /// #[unique(username, movie)] // <-- here
194 /// pub struct Rating {
195 /// pub movie: String,
196 /// pub username: String,
197 /// #[unique] // <-- or here
198 /// pub uuid: String,
199 /// }
200 /// }
201 /// ```
202 /// This will create a single schema version with a single table called `rating` and three columns.
203 /// The table will also have two unique contraints (one on the `uuid` column and one on the combination of `username` and `movie`).
204 /// Note that optional types are not allowed in unique constraints.
205 ///
206 /// ## Indexes
207 /// Indexes are very similar to unique constraints, but they don't require the columns to be unique.
208 /// These are useful to prevent sqlite from having to scan a whole table.
209 /// To incentivise creating indices you also get some extra sugar to use the index!
210 ///
211 /// ```
212 /// #[rust_query::migration::schema(SchemaName)]
213 /// pub mod vN {
214 /// pub struct Topic {
215 /// #[unique]
216 /// pub title: String,
217 /// #[index]
218 /// pub category: String,
219 /// }
220 /// }
221 /// fn test(txn: &rust_query::Transaction<v0::SchemaName>) {
222 /// let _ = txn.lazy_iter(v0::Topic.category("sports"));
223 /// let _ = txn.lazy(v0::Topic.title("star wars"));
224 /// }
225 /// ```
226 ///
227 /// The `TableName.column_name(value)` syntax is only allowed if `TableName` has an index or
228 /// unique constraint that starts with `column_name`.
229 ///
230 /// Adding and removing indexes and changing the order of columns in indexes and unique constraints
231 /// is considered backwards compatible and thus does not require a new schema version.
232 ///
233 /// # Multiple schema versions
234 ///
235 /// At some point you might want to change something substantial in your schema.
236 /// It would be really sad if you had to throw away all the old data in your database.
237 /// That is why [rust_query] allows us to define multiple schema versions and how to transition between them.
238 ///
239 /// ## Adding tables
240 /// One of the simplest things to do is adding a new table.
241 ///
242 /// ```
243 /// #[rust_query::migration::schema(SchemaName)]
244 /// #[version(0..=1)]
245 /// pub mod vN {
246 /// pub struct User {
247 /// #[unique]
248 /// pub username: String,
249 /// }
250 /// #[version(1..)] // <-- note that `Game`` has a version range
251 /// pub struct Game {
252 /// #[unique]
253 /// pub name: String,
254 /// pub size: i64,
255 /// }
256 /// }
257 ///
258 /// // These are just examples of tables you can use
259 /// use v0::SchemaName as _;
260 /// use v1::SchemaName as _;
261 /// use v0::User as _;
262 /// use v1::User as _;
263 /// // `v0::Game` does not exist
264 /// use v1::Game as _;
265 /// # fn main() {}
266 ///
267 /// fn migrate() -> rust_query::Database<v1::SchemaName> {
268 /// rust_query::Database::migrator(rust_query::migration::Config::open("test.db"))
269 /// .expect("database version is before supported versions")
270 /// .migrate(|_txn| v0::migrate::SchemaName {})
271 /// .finish()
272 /// .expect("database version is after supported versions")
273 /// }
274 /// ```
275 /// The migration itself is not very interesting because new tables are automatically created
276 /// without any data. To have some initial data, take a look at the `#[from]` attribute down below or use
277 /// [crate::migration::Migrator::fixup].
278 ///
279 /// ## Changing columns
280 /// Changing columns is very similar to adding and removing structs.
281 /// ```
282 /// use rust_query::migration::{schema, Config};
283 /// use rust_query::{Database, Lazy};
284 /// #[schema(Schema)]
285 /// #[version(0..=1)]
286 /// pub mod vN {
287 /// pub struct User {
288 /// #[unique]
289 /// pub username: String,
290 /// #[version(1..)] // <-- here
291 /// pub score: i64,
292 /// }
293 /// }
294 /// pub fn migrate() -> Database<v1::Schema> {
295 /// Database::migrator(Config::open_in_memory()) // we use an in memory database for this test
296 /// .expect("database version is before supported versions")
297 /// .migrate(|txn| v0::migrate::Schema {
298 /// // In this case it is required to provide a value for each row that already exists.
299 /// // This is done with the `v0::migrate::User` struct:
300 /// user: txn.migrate_ok(|old: Lazy<v0::User>| v0::migrate::User {
301 /// score: old.username.len() as i64 // use the username length as the new score
302 /// }),
303 /// })
304 /// .finish()
305 /// .expect("database version is after supported versions")
306 /// }
307 /// # fn main() {}
308 /// ```
309 ///
310 /// ## `#[from(TableName)]` Attribute
311 /// You can use this attribute when renaming or splitting a table.
312 /// This will make it clear that data in the table should have the
313 /// same row ids as the `from` table.
314 ///
315 /// For example:
316 ///
317 /// ```
318 /// # use rust_query::migration::{schema, Config};
319 /// # use rust_query::{Database, Lazy};
320 /// # fn main() {}
321 /// #[schema(Schema)]
322 /// #[version(0..=1)]
323 /// pub mod vN {
324 /// #[version(..1)]
325 /// pub struct User {
326 /// pub name: String,
327 /// }
328 /// #[version(1..)]
329 /// #[from(User)]
330 /// pub struct Author {
331 /// pub name: String,
332 /// }
333 /// pub struct Book {
334 /// pub author: rust_query::TableRow<Author>,
335 /// }
336 /// }
337 /// pub fn migrate() -> Database<v1::Schema> {
338 /// Database::migrator(Config::open_in_memory()) // we use an in memory database for this test
339 /// .expect("database version is before supported versions")
340 /// .migrate(|txn| v0::migrate::Schema {
341 /// author: txn.migrate_ok(|old: Lazy<v0::User>| v0::migrate::Author {
342 /// name: old.name.clone(),
343 /// }),
344 /// })
345 /// .finish()
346 /// .expect("database version is after supported versions")
347 /// }
348 /// ```
349 /// In this example the `Book` table exists in both `v0` and `v1`,
350 /// however `User` only exists in `v0` and `Author` only exist in `v1`.
351 /// Note that the `pub author: Author` field only specifies the latest version
352 /// of the table, it will use the `#[from]` attribute to find previous versions.
353 ///
354 /// ## `#[no_reference]` Attribute
355 /// You can put this attribute on your table definitions and it will make it impossible
356 /// to have foreign key references to such table.
357 /// This makes it possible to use `TransactionWeak::delete_ok`.
358 pub use rust_query_macros::schema;
359}
360
361/// These items are only exposed for use by the proc macros.
362/// Direct use is unsupported.
363#[doc(hidden)]
364pub mod private {
365
366 pub use crate::joinable::{IntoJoinable, Joinable};
367 pub use crate::migrate::{
368 Schema, SchemaMigration, TableTypBuilder,
369 migration::{Migration, SchemaBuilder},
370 };
371 pub use crate::query::get_plan;
372 pub use crate::schema::from_macro::{SchemaType, TypBuilder};
373 pub use crate::value::{
374 DbTyp, DynTypedExpr, ValueBuilder, adhoc_expr, new_column, unique_from_joinable,
375 };
376 pub use crate::writable::Reader;
377
378 // pub trait Apply {
379 // type Out<T: MigrateTyp>;
380 // }
381
382 // pub struct AsNormal;
383 // impl Apply for AsNormal {
384 // type Out<T: MigrateTyp> = T;
385 // }
386
387 // struct AsExpr<'x, S>(PhantomData<(&'x (), S)>);
388 // impl<'x, S> Apply for AsExpr<'x, S> {
389 // type Out<T: MigrateTyp> = crate::Expr<'x, S, T::ExprTyp>;
390 // }
391
392 // struct AsLazy<'x>(PhantomData<&'x ()>);
393 // impl<'x> Apply for AsLazy<'x> {
394 // type Out<T: MigrateTyp> = T::Lazy<'x>;
395 // }
396
397 pub mod doctest_aggregate {
398 #[crate::migration::schema(M)]
399 pub mod vN {
400 pub struct Val {
401 pub x: i64,
402 }
403 }
404 pub use crate::aggregate;
405 pub use v0::*;
406
407 #[cfg_attr(test, mutants::skip)]
408 pub fn get_txn(f: impl Send + FnOnce(&'static mut crate::Transaction<M>)) {
409 crate::Database::new(rust_query::migration::Config::open_in_memory())
410 .transaction_mut_ok(f)
411 }
412 }
413
414 pub mod doctest {
415 use crate::{Database, Transaction, migrate::config::Config, migration};
416
417 #[migration::schema(Empty)]
418 pub mod vN {
419 pub struct User {
420 #[unique]
421 pub name: String,
422 }
423 }
424 pub use v0::*;
425
426 #[cfg_attr(test, mutants::skip)]
427 pub fn get_txn(f: impl Send + FnOnce(&'static mut Transaction<Empty>)) {
428 let db = Database::new(Config::open_in_memory());
429 db.transaction_mut_ok(|txn| {
430 txn.insert(User {
431 name: "Alice".to_owned(),
432 })
433 .unwrap();
434 f(txn)
435 })
436 }
437 }
438}
439
440/// This trait is implemented for all table types as generated by the [crate::migration::schema] macro.
441///
442/// **You can not implement this trait yourself!**
443pub trait Table: Sized + 'static {
444 #[doc(hidden)]
445 type Ext2<'t>;
446
447 #[doc(hidden)]
448 fn covariant_ext<'x, 't>(val: &'x Self::Ext2<'static>) -> &'x Self::Ext2<'t>;
449
450 #[doc(hidden)]
451 fn build_ext2<'t>(val: &Expr<'t, Self::Schema, TableRow<Self>>) -> Self::Ext2<'t>;
452
453 /// The schema that this table is a part of.
454 type Schema;
455
456 #[doc(hidden)]
457 /// The table that this table can be migrated from.
458 type MigrateFrom: Table;
459
460 /// The type of conflict that can result from inserting a row in this table.
461 /// This is the same type that is used for row updates too.
462 type Conflict: FromConflict;
463 /// The type of error when a delete fails due to a foreign key constraint.
464 type Referer;
465
466 #[doc(hidden)]
467 type Mutable: Deref;
468 #[doc(hidden)]
469 type Lazy<'t>;
470
471 #[doc(hidden)]
472 fn read(&self, f: &mut Reader);
473
474 #[doc(hidden)]
475 type Select;
476
477 #[doc(hidden)]
478 fn into_select(
479 val: Expr<'_, Self::Schema, TableRow<Self>>,
480 ) -> Select<'_, Self::Schema, Self::Select>;
481
482 #[doc(hidden)]
483 fn select_mutable(select: Self::Select) -> Self::Mutable;
484
485 #[doc(hidden)]
486 fn select_lazy<'t>(select: Self::Select) -> Self::Lazy<'t>;
487
488 #[doc(hidden)]
489 fn mutable_as_unique(val: &mut Self::Mutable) -> &mut <Self::Mutable as Deref>::Target;
490
491 #[doc(hidden)]
492 fn mutable_into_insert(val: Self::Mutable) -> Self
493 where
494 Self: Sized;
495
496 #[doc(hidden)]
497 fn get_referer_unchecked() -> Self::Referer;
498
499 #[doc(hidden)]
500 fn typs(f: &mut TypBuilder<Self::Schema>);
501
502 #[doc(hidden)]
503 const SPAN: (usize, usize);
504
505 #[doc(hidden)]
506 const ID: &'static str;
507 #[doc(hidden)]
508 const NAME: &'static str;
509}
510
511trait CustomJoin: Table {
512 fn name(&self) -> JoinableTable;
513}
514
515#[test]
516fn compile_tests() {
517 let t = trybuild::TestCases::new();
518 t.compile_fail("tests/compile/*.rs");
519}