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