Skip to main content

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 {
398        use crate::{Database, Transaction, migrate::config::Config, migration};
399
400        #[migration::schema(Empty)]
401        pub mod vN {
402            pub struct User {
403                #[unique]
404                pub name: String,
405            }
406        }
407        pub use v0::*;
408
409        #[cfg_attr(test, mutants::skip)] // this function is only used in doc tests
410        pub fn get_txn(f: impl Send + FnOnce(&'static mut Transaction<Empty>)) {
411            let db = Database::new(Config::open_in_memory());
412            db.transaction_mut_ok(|txn| {
413                txn.insert(User {
414                    name: "Alice".to_owned(),
415                })
416                .unwrap();
417                f(txn)
418            })
419        }
420    }
421}
422
423/// This trait is implemented for all table types as generated by the [crate::migration::schema] macro.
424///
425/// **You can not implement this trait yourself!**
426pub trait Table: Sized + 'static {
427    #[doc(hidden)]
428    type Ext2<'t>;
429
430    #[doc(hidden)]
431    fn covariant_ext<'x, 't>(val: &'x Self::Ext2<'static>) -> &'x Self::Ext2<'t>;
432
433    #[doc(hidden)]
434    fn build_ext2<'t>(val: &Expr<'t, Self::Schema, TableRow<Self>>) -> Self::Ext2<'t>;
435
436    /// The schema that this table is a part of.
437    type Schema;
438
439    #[doc(hidden)]
440    /// The table that this table can be migrated from.
441    type MigrateFrom: Table;
442
443    /// The type of conflict that can result from inserting a row in this table.
444    /// This is the same type that is used for row updates too.
445    type Conflict: FromConflict;
446    /// The type of error when a delete fails due to a foreign key constraint.
447    type Referer;
448
449    #[doc(hidden)]
450    type Mutable: Deref;
451    #[doc(hidden)]
452    type Lazy<'t>;
453
454    #[doc(hidden)]
455    fn read(&self, f: &mut Reader);
456
457    #[doc(hidden)]
458    type Select;
459
460    #[doc(hidden)]
461    fn into_select(
462        val: Expr<'_, Self::Schema, TableRow<Self>>,
463    ) -> Select<'_, Self::Schema, Self::Select>;
464
465    #[doc(hidden)]
466    fn select_mutable(select: Self::Select) -> Self::Mutable;
467
468    #[doc(hidden)]
469    fn select_lazy<'t>(select: Self::Select) -> Self::Lazy<'t>;
470
471    #[doc(hidden)]
472    fn mutable_as_unique(val: &mut Self::Mutable) -> &mut <Self::Mutable as Deref>::Target;
473
474    #[doc(hidden)]
475    fn mutable_into_insert(val: Self::Mutable) -> Self
476    where
477        Self: Sized;
478
479    #[doc(hidden)]
480    fn get_referer_unchecked() -> Self::Referer;
481
482    #[doc(hidden)]
483    fn typs(f: &mut TypBuilder<Self::Schema>);
484
485    #[doc(hidden)]
486    const SPAN: (usize, usize);
487
488    #[doc(hidden)]
489    const ID: &'static str;
490    #[doc(hidden)]
491    const NAME: &'static str;
492}
493
494trait CustomJoin: Table {
495    fn name(&self) -> JoinableTable;
496}
497
498#[test]
499fn compile_tests() {
500    let t = trybuild::TestCases::new();
501    t.compile_fail("tests/compile/*.rs");
502}