Field Types

All field types live in rango::prelude::*.

Primitive types

Rust typeSQL (Postgres)SQL (SQLite)Notes
FieldBoolBOOLEANINTEGER
FieldSmallIntSMALLINTINTEGER
FieldIntINTEGERINTEGER
FieldBigIntBIGINTINTEGER
FieldAutoIntINTEGER GENERATED BY DEFAULT AS IDENTITYINTEGER (rowid)Primary key only, None until inserted
FieldAutoBigIntBIGINT GENERATED BY DEFAULT AS IDENTITYINTEGER (rowid)Primary key only, None until inserted
FieldFloatREALREAL
FieldDoubleDOUBLE PRECISIONREAL

Text types

Rust typeSQL (Postgres)SQL (SQLite)Notes
FieldTextTEXTTEXTUnlimited length
FieldVarchar<MIN, MAX>VARCHAR(MAX)TEXTCompile-time length bounds
FieldEmailVARCHAR(254)TEXT
FieldUrlVARCHAR(2048)TEXT
FieldPassword<MIN, MAX>TEXTTEXTMIN / MAX bound the plain password, which is stored as an argon2id hash: the column is always TEXT (LONGTEXT on MySQL / MariaDB)

FieldPassword::new(plain) checks the bounds and hashes. verify(&self, candidate) checks a candidate against the stored hash. hash() returns the hash string and from_hash(hash) wraps a hash you already have.

FieldPassword hashes with argon2id on every write. This is deliberately slow (about 200 ms per value in a debug build, much less in release), so a bulk insert of users pays it per row.

Numeric types

Rust typeSQL (Postgres)SQL (SQLite)SQL (MySQL / MariaDB)Notes
FieldDecimal<P, S>NUMERICNUMERICDECIMAL(P, S)P = precision, S = scale. Wraps rango::Decimal (exact). Only MySQL / MariaDB enforce P and S in the column; on Postgres and SQLite the column is a plain NUMERIC, and FieldDecimal::from_decimal rescales the value to S in Rust. SQLite is exact up to 15 digits through the numeric column, beyond that the digits are kept as text
FieldRange<MIN, MAX>BIGINTINTEGERBIGINTChecked when built (FieldRange::new)

Working with decimals

rango::Decimal is rust_decimal::Decimal. Build one from an integer and a scale, or wrap it in the field with FieldDecimal::from_decimal, which rescales to S:

#![allow(unused)]
fn main() {
use rango::prelude::*;

let price = FieldDecimal::<10, 2>::from_decimal(Decimal::new(1250, 2)); // 12.50

// Filters take a plain `Decimal`, typed column or string name.
let pricey = Product::filter(&pool).gte(Product::PRICE, Decimal::new(2000, 2)).all().await?; // >= 20.00
let exact = Product::filter(&pool).eq("price", Decimal::new(1999, 2)).all().await?;         // = 19.99
}

sum() and avg() return Result<Option<f64>> on every backend (the database casts the result to a float), so they are not exact. For an exact decimal aggregate the rule depends on the backend:

Backendmin::<Decimal> / max::<Decimal>Exact sum
PostgreSQL, MySQL / MariaDBCompile and return an exact DecimalRead the rows and add the Decimals in Rust
SQLiteDo not compile: Decimal cannot be decoded from SQLiteSame

The portable way to get the exact extreme is to read the row that holds it:

#![allow(unused)]
fn main() {
let priciest = Product::filter(&pool).order_by("-fee").limit(1).one().await?; // Option<Product>
let max_fee: Option<Decimal> = priciest.map(|p| p.fee.0);
}

values() works too when you do not want the whole model. On SQLite a value with more than 15 significant digits is kept as text and sorts after the numbers. See Aggregations.

Date/time types

Rust typeSQL (Postgres)SQL (SQLite)
FieldDateTimeTIMESTAMPTZTEXT (ISO 8601)
FieldDateDATETEXT
FieldTimeTIMETEXT

Other types

Rust typeSQL (Postgres)SQL (SQLite)
FieldUuidUUIDTEXT
FieldJsonJSONBTEXT
FieldBytesBYTEABLOB

Relation types

Rust typeDescription
ForeignKey<M>Foreign key to model M; the column has the type of M's primary key (UUID, INTEGER or BIGINT). M needs a FieldUuid, FieldInt, FieldAutoInt, FieldBigInt or FieldAutoBigInt key
ManyToMany<M>Many-to-many relation: pivot table auto-generated, its key columns follow both primary key types. Same target key types as ForeignKey

Nullable

Wrap any field type in Option<T> to make it nullable:

#![allow(unused)]
fn main() {
pub score: Option<FieldInt>,     // INTEGER NULL
pub bio: Option<FieldText>,      // TEXT NULL
pub avatar: Option<FieldUuid>,   // UUID NULL
}