# sqlx-record Skill Expert guidance for using the sqlx-record Rust library. ## Triggers - "create entity", "define entity", "entity struct" - "sqlx record", "sqlx-record" - "crud operations", "database entity" - "audit trail", "change tracking" - "lookup table", "lookup options" ## Overview sqlx-record provides derive macros for automatic CRUD operations and audit trails for SQL entities. It supports MySQL, PostgreSQL, and SQLite. ## Quick Reference ### Entity Definition ```rust use sqlx_record::prelude::*; use sqlx::FromRow; #[derive(Entity, FromRow)] #[table_name = "users"] struct User { #[primary_key] id: Uuid, #[rename("user_name")] // Maps to different DB column name: String, #[version] // Auto-increment on updates version: u32, #[field_type("TEXT")] // SQLx type hint bio: Option, } ``` ### CRUD Operations ```rust // Insert let user = User { id: new_uuid(), name: "Alice".into(), version: 0 }; user.insert(&pool).await?; // Get let user = User::get_by_id(&pool, &id).await?; let users = User::get_by_ids(&pool, &ids).await?; // Find with filters let users = User::find(&pool, filters![("is_active", true)], None).await?; let user = User::find_one(&pool, filters![("email", email)], None).await?; // Find with ordering and pagination let page = User::find_ordered_with_limit( &pool, filters![("role", "admin")], None, vec![("created_at", false)], // DESC Some((0, 10)) // offset, limit ).await?; // Count let count = User::count(&pool, filters![("is_active", true)], None).await?; // Update User::update_by_id(&pool, &id, User::update_form().with_name("Bob")).await?; ``` ### Filter System ```rust // Simple equality filters![("field", value)] // Multiple conditions (AND) filters![("active", true), ("role", "admin")] // OR conditions filter_or![("status", "active"), ("status", "pending")] // Operators "age".gt(18) // > "age".ge(18) // >= "age".lt(65) // < "age".le(65) // <= "name".eq("Bob") // = "name".ne("Bob") // != // Other filters Filter::Like("name", "%alice%".into()) Filter::In("status", vec!["active".into(), "pending".into()]) Filter::IsNull("deleted_at") Filter::IsNotNull("email") ``` ### Lookup Tables ```rust // With database entity lookup_table!(OrderStatus, "pending", "shipped", "delivered"); // Generates: struct OrderStatus, enum OrderStatusCode, constants // Without database entity lookup_options!(PaymentMethod, "credit-card", "paypal", "bank-transfer"); // Generates: enum PaymentMethodCode, struct PaymentMethod (constants only) // Usage let status = OrderStatus::PENDING; // "pending" let code = OrderStatusCode::try_from("pending")?; // OrderStatusCode::Pending ``` ### Time-Ordered UUIDs ```rust let id = new_uuid(); // Timestamp prefix for better indexing ``` ## Feature Flags ```toml [dependencies] sqlx-record = { version = "0.3", features = ["mysql", "derive"] } # Database: "mysql", "postgres", or "sqlite" (pick one) # Optional: "derive", "static-validation" ``` ## Soft Delete, Timestamps, Batch Operations ```rust #[derive(Entity, FromRow)] struct User { #[primary_key] id: Uuid, name: String, #[soft_delete] // Enables delete/restore/hard_delete is_deleted: bool, #[created_at] // Auto-set on insert created_at: i64, #[updated_at] // Auto-set on update updated_at: i64, } // Soft delete user.delete(&pool).await?; // is_deleted = true user.restore(&pool).await?; // is_deleted = false user.hard_delete(&pool).await?; // DELETE FROM // Batch insert User::insert_many(&pool, &users).await?; // Upsert (insert or update on conflict) user.upsert(&pool).await?; ``` ## Pagination ```rust use sqlx_record::prelude::{Page, PageRequest}; let page = User::paginate(&pool, filters![], None, vec![("name", true)], PageRequest::new(1, 20)).await?; page.items // Vec page.total_count // Total matching records page.total_pages() // Calculated pages page.has_next() // bool page.has_prev() // bool ``` ## Transaction Helper ```rust use sqlx_record::transaction; transaction!(&pool, |tx| { user.insert(&mut *tx).await?; order.insert(&mut *tx).await?; Ok::<_, sqlx::Error>(()) }).await?; ``` ## Advanced Updates (UpdateExpr) ```rust use sqlx_record::prelude::UpdateExpr; // Arithmetic: score = score + 10 User::update_by_id(&pool, &id, User::update_form().eval_score(UpdateExpr::Add(10.into())) ).await?; // CASE/WHEN User::update_by_id(&pool, &id, User::update_form().eval_tier(UpdateExpr::Case { branches: vec![("score".gt(100), "gold".into())], default: "bronze".into(), }) ).await?; // Raw SQL escape hatch User::update_by_id(&pool, &id, User::update_form().raw("computed", "COALESCE(a, 0) + b") ).await?; ``` ## ConnProvider (Flexible Connections) ```rust use sqlx_record::ConnProvider; // Borrowed or owned pool connections let conn = ConnProvider::Borrowed(&pool); let users = User::find(&*conn, filters![], None).await?; ``` ## Database Differences | Feature | MySQL | PostgreSQL | SQLite | |---------|-------|------------|--------| | Placeholder | `?` | `$1, $2` | `?` | | Table quote | `` ` `` | `"` | `"` | | Index hints | Supported | N/A | N/A |