|
2 | 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. |
4 | 4 |
|
5 | | -//! Structures stored to the database. |
| 5 | +//! Rust types that represent rows in the Omicron database (CockroachDB). |
| 6 | +//! |
| 7 | +//! Each struct in this crate maps to a database table. The table's columns and |
| 8 | +//! SQL types are declared in [`nexus_db_schema`] via Diesel's |
| 9 | +//! [`table!`](diesel::table) macro; the struct here provides the corresponding |
| 10 | +//! Rust types. [`nexus_db_queries`] then uses both to build and execute |
| 11 | +//! queries in its [`DataStore`](nexus_db_queries::db::DataStore) methods. |
| 12 | +//! |
| 13 | +//! # How the mapping works |
| 14 | +//! |
| 15 | +//! Consider the `vmm` table. The schema declares columns and SQL types: |
| 16 | +//! |
| 17 | +//! ```text |
| 18 | +//! // in nexus-db-schema |
| 19 | +//! table! { |
| 20 | +//! vmm (id) { |
| 21 | +//! id -> Uuid, |
| 22 | +//! sled_id -> Uuid, |
| 23 | +//! state_generation -> Int8, |
| 24 | +//! state -> VmmStateEnum, |
| 25 | +//! ... |
| 26 | +//! } |
| 27 | +//! } |
| 28 | +//! ``` |
| 29 | +//! |
| 30 | +//! The model struct provides the Rust representation of a row: |
| 31 | +//! |
| 32 | +//! ```text |
| 33 | +//! // in nexus-db-model |
| 34 | +//! #[derive(Queryable, Insertable, Selectable)] |
| 35 | +//! #[diesel(table_name = vmm)] |
| 36 | +//! pub struct Vmm { |
| 37 | +//! pub id: Uuid, |
| 38 | +//! pub sled_id: DbTypedUuid<SledKind>, |
| 39 | +//! #[diesel(column_name = state_generation)] |
| 40 | +//! pub generation: Generation, |
| 41 | +//! pub state: VmmState, |
| 42 | +//! ... |
| 43 | +//! } |
| 44 | +//! ``` |
| 45 | +//! |
| 46 | +//! A few things to note: |
| 47 | +//! |
| 48 | +//! - **Type translation.** Each field's Rust type must implement Diesel's |
| 49 | +//! [`FromSql`](diesel::deserialize::FromSql) and |
| 50 | +//! [`ToSql`](diesel::serialize::ToSql) traits for the corresponding column's |
| 51 | +//! SQL type. Diesel provides these impls for common types (`Uuid`, |
| 52 | +//! `DateTime<Utc>`, `String`, etc.); this crate defines wrapper types like |
| 53 | +//! [`DbTypedUuid`](typed_uuid::DbTypedUuid) for domain-specific conversions |
| 54 | +//! (e.g. distinguishing a sled UUID from any other UUID). |
| 55 | +//! - **Column renaming.** Field names must match column names by default, but |
| 56 | +//! `#[diesel(column_name = ...)]` allows the Rust name to differ (e.g. |
| 57 | +//! `generation` for the `state_generation` column). |
| 58 | +//! - **Positional mapping.** [`diesel::Queryable`] maps SQL result columns to |
| 59 | +//! struct fields by position, not by name. If the field order doesn't match |
| 60 | +//! the column order in the query, fields will silently receive wrong values. |
| 61 | +//! Deriving [`diesel::Selectable`] mitigates this by generating an explicit |
| 62 | +//! column list, so the result columns are always in the order `Queryable` |
| 63 | +//! expects. **Always derive both.** |
| 64 | +//! |
| 65 | +//! # Field ordering convention |
| 66 | +//! |
| 67 | +//! Fields in `Queryable`/`Insertable` structs should be ordered to match the |
| 68 | +//! column order in `dbinit.sql`. This keeps the Rust types easy to |
| 69 | +//! cross-reference with the schema definition and prevents subtle bugs if a |
| 70 | +//! query ever omits `Selectable`. |
| 71 | +//! |
| 72 | +//! # Representing enums |
| 73 | +//! |
| 74 | +//! For types that map to database enum columns, see the [`impl_enum_type!`] |
| 75 | +//! and [`impl_enum_wrapper!`] macros defined below. For string-backed enums, |
| 76 | +//! see the [`DatabaseString`] trait. |
6 | 77 |
|
7 | 78 | #[macro_use] |
8 | 79 | extern crate diesel; |
|
0 commit comments