inspectdb
Introspect an existing database into models and a 0001_initial migration.
inspectdb is the porting on-ramp. Point it at an existing SQLite database
and it generates a models.rs with one #[derive(Model)] struct per table,
plus a 0001_initial.json migration that recreates the schema. The output
drops into the same M5 migration loop - no parallel porting code path.
Run it
cargo run -- inspectdb --output plugins/imported# -> Inspected 12 table(s), 47 column(s)# -> Wrote plugins/imported/models.rs# -> Wrote plugins/imported/migrations/app/0001_initial.jsonThe output:
plugins/imported/├── models.rs # one #[derive(Model)] per table└── migrations/ └── app/ └── 0001_initial.json # one CreateTable per tableWire each generated struct with App::builder().model::<T>() (or move them
into a real plugin once M7's plugin contract suits your project shape) and
the loop runs normally from there.
Marking the initial migration applied
When the target database already holds the tables you just introspected,
running the migration would fail on "table already exists". Pass
--mark-applied to record the row in umbral_migrations without running
the SQL:
cargo run -- inspectdb --output plugins/imported --mark-appliedThe next migrate is a no-op until you actually change a model.
inspectdb dispatches on the active backend: SQLite reads sqlite_master plus PRAGMA table_info, and Postgres reads information_schema (tables, columns, and the constraint joins that recover the primary key). Point the CLI at whichever backend DATABASE_URL resolves to; the same --output shape comes out either way. See the Postgres backend page for the Postgres specifics.
The SQLite type catalogue
The columns inspectdb's SQLite path knows about today (the Postgres path additionally recovers JSONB, native arrays, and INET / CIDR / MACADDR; see the Postgres backend page):
Integers
SMALLINT, INTEGER, BIGINT and their aliases map to i16 / i32 / i64.
Floats
REAL → f32; DOUBLE / FLOAT8 → f64.
Booleans
BOOLEAN / BOOL → bool.
Text
TEXT, VARCHAR, CHAR, CLOB all map to String. VARCHAR(n) width is recorded as a comment.
Dates and times
DATE → NaiveDate, TIME → NaiveTime, TIMESTAMP / DATETIME → DateTime<Utc>.
UUIDs
UUID → uuid::Uuid.
A JSON / JSONB-declared column maps to serde_json::Value and a BLOB / BYTEA column maps to a bytes field. A SQL type still outside the catalogue (NUMERIC, custom types) makes inspectdb stop with an UnsupportedColumnType error naming the offending column. Either add a matching SqlType variant or edit the generated models.rs by hand.
umbral_migrations, every sqlite_* internal table, and any table with an
SQL type the catalogue doesn't know are excluded automatically.
Still deferred
- Foreign-key and index detection. The generated
CreateTableops carry columns only; a Postgres FK column comes back as a plaini64and indexes are not read out. Once the field-level FK / index types land in the introspector, inspectdb picks them up. (Both backends.) - Generated plugin crates with a
Pluginimpl. Today the output is a flatmodels.rsyou wire by hand. M7's full plugin shape ships the crate too.
Postgres introspection has shipped (introspect_pool_pg, covered by
crates/umbral-core/tests/postgres_inspect.rs); it is no longer on this list.
The full target shape lives in docs/specs/07-inspectdb.md.