Expand description
Deterministic test data for drizzle-rs schemas.
SeedConfig turns a schema into INSERT statements, or into plain rows
with try_generate_rows for use with
any driver. The same seed and crate version always give the same rows.
How values are chosen, per column:
-
a
Generatorset with.generator(...): one fromgenerators(generators::int(18..=90),generators::one_of([...]),generators::from_fn(...), …), aGeneratorKind, or your own type; else -
a
GeneratorKindset with.kind(...); else -
DEFAULTwhen the column has a default (and is not the primary key), or is a non-keyPostgreSQLidentity column; else -
an inferred generator:
- integer primary keys count up from 1;
- enum columns pick one of their variants;
- MySQL columns follow their declared domain (integer ranges, inline
ENUM/SETlabels,DECIMALprecision, …); - otherwise the column name decides when a whole word is recognized
(
email,first_name,created_at,is_active, …) and the generated values fit the column type, then the SQL type alone.
Text is cut to a declared
VARCHAR(n)/CHAR(n)length.
UNIQUE columns and single-column primary keys get distinct values, and
a row that would repeat a composite primary key or multi-column UNIQUE
key (for example two equal (user_id, post_id) pairs in a join table)
is dropped.
Parent tables are seeded before their children, and foreign key columns
are overwritten to point at generated parent rows. A child table without
its own count gets parent rows × relation count rows (the relation count
defaults to 1; with several parents, the largest product wins).
reset_plan returns DELETE statements in child-before-parent order.
On PostgreSQL, text values for non-text columns (uuid, jsonb,
enums, arrays, …) are cast to the column type, GENERATED ALWAYS
identity keys are inserted with OVERRIDING SYSTEM VALUE, and each
table’s SERIAL/IDENTITY sequences are moved past the seeded ids with
a SELECT setval(...) statement after its rows.
The crate has no default dialect: enable sqlite, postgres, and/or
mysql.
§With the schema macros
This is the main path: pass the #[derive(...Schema)] struct. The
macros already record column types, keys, UNIQUE, defaults and enum
variants, and every table or column passed to the config is checked at
compile time. (Uses drizzle with the rusqlite feature; not compiled
here because drizzle is not a dependency of this crate.)
use drizzle::sqlite::prelude::*;
use drizzle_seed::{SeedConfig, generators::{self, GeneratorExt}};
#[SQLiteTable]
struct Users {
#[column(primary)]
id: i32,
#[column(unique)]
email: String, // inferred from the name: distinct emails
age: i32,
}
#[SQLiteTable]
struct Posts {
#[column(primary)]
id: i32,
#[column(references = Users::id)]
user_id: i32, // points at seeded users
title: String, // inferred from the name: a short title
}
#[derive(SQLiteSchema)]
struct AppSchema {
users: Users,
posts: Posts,
}
let schema = AppSchema::new();
let statements = SeedConfig::sqlite(&schema)
.seed(42)
.count(&schema.users, 5) // 5 users
.relation(&schema.users, &schema.posts, 3) // 3 posts per user: 15 posts
.generator(&schema.users.age, generators::int(18..=90).nullable(0.1))
.generate();
for statement in statements {
db.execute(statement)?; // parents first
}§Without the schema macros
Describe the existing tables with schema::Schema, then use the
*_by_name settings. Names are checked when the seed is generated, and
try_generate_rows gives plain rows
for any driver:
use drizzle_seed::schema::{Column, Schema, Table};
use drizzle_seed::{SeedConfig, SeedError};
let schema = Schema::postgres()
.table(
Table::new("users")
.column(Column::new("id", "BIGSERIAL").primary_key())
.column(Column::new("email", "TEXT").not_null().unique()),
)
.table(
Table::new("posts")
.column(Column::new("id", "BIGSERIAL").primary_key())
.column(Column::new("user_id", "BIGINT").not_null().references("users", "id"))
.column(Column::new("title", "TEXT").not_null()),
);
let config = SeedConfig::postgres(&schema)
.count_by_name("users", 5)
.relation_by_name("users", "posts", 3);
for table in config.try_generate_rows()? {
// INSERT INTO {table.table} ({table.columns}) VALUES ... for each row
assert_eq!(table.rows.len(), if table.table == "users" { 5 } else { 15 });
}
// Execute the reset plan, children first, to empty the tables again.
let reset = config.reset_plan()?;
assert_eq!(reset.len(), 2);
// A typo is an error, which lists the names that do exist.
let error = SeedConfig::postgres(&schema).count_by_name("user", 5).try_generate();
assert!(matches!(error, Err(SeedError::UnknownTable { .. })));§From a live database or a migration snapshot
Schema::from_snapshot (the
migrations feature) builds the schema from what a drizzle driver’s
introspect() reads, or from a migration folder’s snapshot.json, so a
database described nowhere in Rust can be seeded (on PostgreSQL,
introspect_schemas(&["app"]) reads only the schemas named). The
drizzle seed CLI command does this for the configured database.
use drizzle_seed::{SeedConfig, schema::Schema};
let schema = Schema::from_snapshot(&db.introspect()?)?;
for statement in SeedConfig::postgres(&schema)
.count_by_name("users", 100)
.relation_by_name("users", "posts", 3)
.generate()
{
db.execute(statement)?;
}§As a SQL script
Each statement’s inline_sql() writes its values as literals, and
SeedConfig::try_generate_script returns the whole seed as one script:
a fixture file, or input for any client.
Any other type that implements drizzle_core::SQLSchemaImpl works as
a schema too.
Modules§
- generators
- Ready-made generators to pass to
SeedConfig::generator. - schema
- Describe tables at runtime, for projects without the drizzle schema macros.
Structs§
- Seed
Config - Builder that generates seed INSERT statements for a schema.
- Seed
Rows - The generated rows for one table, before any SQL is rendered.
Enums§
- Generator
Kind - A built-in generator, chosen by type/name inference or set with
SeedConfig::kind. - Seed
Error - Why
SeedConfigcould not build the statements (returned bytry_generateandreset_plan). - Seed
Value - A generated column value, before it is rendered for a dialect.
Traits§
- Generator
- Produces one column value per row.
- Rng
- Re-export of
rand::Rng, for drawing values from the RNG aGeneratorreceives (rng.random_range(..),rng.random_bool(..)). User-level interface for RNGs - RngCore
- Re-export of
rand::RngCore, the RNG passed toGenerator::generate. Implementation-level interface for RNGs