258 lines
5.6 KiB
Markdown
258 lines
5.6 KiB
Markdown
# sqlx-record Values Skill
|
|
|
|
Guide to Value types and database binding.
|
|
|
|
## Triggers
|
|
- "value type", "sql value"
|
|
- "bind value", "query parameter"
|
|
- "type conversion"
|
|
|
|
## Value Enum
|
|
|
|
```rust
|
|
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)
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
// 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:
|
|
```rust
|
|
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:
|
|
```rust
|
|
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:
|
|
```rust
|
|
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:
|
|
```rust
|
|
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:
|
|
```rust
|
|
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:
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
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:
|
|
```rust
|
|
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:
|
|
```rust
|
|
let fields = vec!["id", "name as user_name", "email"];
|
|
let result = query_fields(fields);
|
|
// "id, name, email"
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Dynamic WHERE Clause
|
|
```rust
|
|
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
|
|
```rust
|
|
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()
|
|
}
|
|
```
|