5.6 KiB
5.6 KiB
sqlx-record Values Skill
Guide to Value types and database binding.
Triggers
- "value type", "sql value"
- "bind value", "query parameter"
- "type conversion"
Value Enum
pub enum Value {
// Integers
Int8(i8),
Uint8(u8),
Int16(i16),
Uint16(u16),
Int32(i32),
Uint32(u32),
Int64(i64),
Uint64(u64),
// Other primitives
String(String),
Bool(bool),
VecU8(Vec<u8>),
// Special types
Uuid(uuid::Uuid),
NaiveDate(NaiveDate),
NaiveDateTime(NaiveDateTime),
}
Auto-Conversions (From trait)
// Strings
Value::from("hello") // String
Value::from("hello".to_string()) // String
// Integers
Value::from(42i32) // Int32
Value::from(42i64) // Int64
Value::from(&42i32) // Int32 (from reference)
Value::from(&42i64) // Int64 (from reference)
// Boolean
Value::from(true) // Bool
Value::from(&true) // Bool (from reference)
// UUID
Value::from(uuid::Uuid::new_v4()) // Uuid
Value::from(&some_uuid) // Uuid (from reference)
// Dates
Value::from(NaiveDate::from_ymd(2024, 1, 15)) // NaiveDate
Value::from(NaiveDateTime::new(...)) // NaiveDateTime
values! Macro
// Empty
values![]
// Single value (auto-converts)
values!["hello"]
values![42]
values![true]
// Multiple values
values!["name", 30, true, uuid]
// With explicit types
values![
Value::String("test".into()),
Value::Int32(42),
Value::Bool(true)
]
Database-Specific Handling
Unsigned Integers
| Type | MySQL | PostgreSQL | SQLite |
|---|---|---|---|
| Uint8 | Native u8 | Cast to i16 | Cast to i16 |
| Uint16 | Native u16 | Cast to i32 | Cast to i32 |
| Uint32 | Native u32 | Cast to i64 | Cast to i64 |
| Uint64 | Native u64 | Cast to i64* | Cast to i64* |
*Note: Uint64 values > i64::MAX will overflow when cast.
UUID Storage
| Database | Type | Notes |
|---|---|---|
| MySQL | BINARY(16) | Stored as bytes |
| PostgreSQL | UUID | Native type |
| SQLite | BLOB | Stored as bytes |
JSON Storage
| Database | Type |
|---|---|
| MySQL | JSON |
| PostgreSQL | JSONB |
| SQLite | TEXT |
Bind Functions
bind_values
Bind values to a raw Query:
use sqlx_record::prelude::*;
let query = sqlx::query("SELECT * FROM users WHERE name = ? AND age > ?");
let query = bind_values(query, &values!["Alice", 18]);
let rows = query.fetch_all(&pool).await?;
bind_as_values
Bind values to a typed QueryAs:
let query = sqlx::query_as::<_, User>("SELECT * FROM users WHERE id = ?");
let query = bind_as_values(query, &values![user_id]);
let user = query.fetch_optional(&pool).await?;
bind_scalar_values
Bind values to a scalar QueryScalar:
let query = sqlx::query_scalar("SELECT COUNT(*) FROM users WHERE active = ?");
let query = bind_scalar_values(query, &values![true]);
let count: i64 = query.fetch_one(&pool).await?;
BindValues Trait
Extension trait for fluent binding:
use sqlx_record::prelude::BindValues;
let users = sqlx::query_as::<_, User>("SELECT * FROM users WHERE role = ?")
.bind_values(&values!["admin"])
.fetch_all(&pool)
.await?;
Placeholder Function
Database-specific placeholder generation:
use sqlx_record::prelude::placeholder;
let ph1 = placeholder(1);
let ph2 = placeholder(2);
// MySQL/SQLite: "?", "?"
// PostgreSQL: "$1", "$2"
let sql = format!("SELECT * FROM users WHERE id = {} AND role = {}", ph1, ph2);
With Filters
Filters automatically use Value internally:
// These are equivalent:
filters![("name", "Alice")]
filters![("name", Value::String("Alice".into()))]
// Values are extracted for binding:
let (where_clause, values) = Filter::build_where_clause(&filters);
// values: Vec<Value>
Custom Queries with Values
use sqlx_record::prelude::*;
async fn complex_query(pool: &Pool, status: &str, min_age: i32) -> Result<Vec<User>> {
let values = values![status, min_age];
let sql = format!(
"SELECT * FROM users WHERE status = {} AND age >= {} ORDER BY name",
placeholder(1),
placeholder(2)
);
let query = sqlx::query_as::<_, User>(&sql);
let query = bind_as_values(query, &values);
query.fetch_all(pool).await
}
Updater Enum
For more complex update patterns:
pub enum Updater<'a> {
Set(&'a str, Value), // SET field = value
Increment(&'a str, Value), // SET field = field + value
Decrement(&'a str, Value), // SET field = field - value
}
Type Helpers
query_fields
Extract field names from aliased field list:
let fields = vec!["id", "name as user_name", "email"];
let result = query_fields(fields);
// "id, name, email"
Common Patterns
Dynamic WHERE Clause
fn build_query(filters: &[(&str, Value)]) -> (String, Vec<Value>) {
let mut conditions = Vec::new();
let mut values = Vec::new();
for (i, (field, value)) in filters.iter().enumerate() {
conditions.push(format!("{} = {}", field, placeholder(i + 1)));
values.push(value.clone());
}
let where_clause = if conditions.is_empty() {
String::new()
} else {
format!("WHERE {}", conditions.join(" AND "))
};
(where_clause, values)
}
Batch Insert Values
fn batch_values(users: &[User]) -> Vec<Value> {
users.iter().flat_map(|u| {
vec![
Value::Uuid(u.id),
Value::String(u.name.clone()),
Value::String(u.email.clone()),
]
}).collect()
}