sqlx-record/sqlx-record-derive
Michael Netshipise 3c0ae1983f Fix MySQL placeholder issue and add missing Value types
- Remove broken static-validation feature (hardcoded $1 placeholders)
- Add Value::Null variant for Option<T> support
- Add From<Option<T>> impl for all Value types
- Add f32, f64, NaiveTime, serde_json::Value support
- Add optional decimal feature for rust_decimal::Decimal
- All database backends now use runtime placeholder() function

Fixes issues:
- MySQL getting PostgreSQL $1 placeholders
- Missing From<Option<T>> implementations
- Missing base types (Decimal, JsonValue, NaiveTime, floats)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 21:01:31 +02:00
..
src Fix MySQL placeholder issue and add missing Value types 2026-01-28 21:01:31 +02:00
Cargo.toml Fix MySQL placeholder issue and add missing Value types 2026-01-28 21:01:31 +02:00
License.md Rename to sqlx-record with multi-database support 2026-01-28 15:31:24 +02:00
ReadMe.md Rename to sqlx-record with multi-database support 2026-01-28 15:31:24 +02:00

ReadMe.md

Update Macro for SQLx

Overview

This derive macro provides a convenient way to generate update forms and related methods for database entities using SQLx. It's designed to work with MySQL databases and provides flexibility in naming conventions for tables and fields. Additionally, it now supports the use of id, code, or any custom primary key field through the primary_key attribute.

Features

  • Generates an update form struct with optional fields
  • Creates methods for generating SQL update statements
  • Implements a builder pattern for setting update values
  • Provides methods for comparing the update form with both the original model and the database record, returning JSON objects
  • Allows custom table names and field names through optional attributes
  • Supports specifying custom primary keys with the primary_key attribute
  • Generates a method to get the table name for use in SQL queries
  • Includes an initial_diff method to get a JSON representation of all fields in the model
  • Supports database transactions for atomic operations
  • Optimized for better performance with large structs

Installation

Add the following to your Cargo.toml:

[dependencies]
entity_update_derive = { path = "path/to/entity_update_derive" }
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "mysql", "json"] }
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }

Usage

Basic Usage with Default Primary Key (id or code)

use entity_update_derive::Update;
use sqlx::FromRow;

#[derive(Update, FromRow, Debug)]
struct User {
    id: i32,
    name: String,
    email: Option<String>,
}

This will generate a UserUpdateForm struct and associated methods, with id as the primary key. If your struct uses code as the primary key, the macro will automatically detect and use it.

Specifying a Custom Primary Key

You can specify a custom primary key field using the primary_key attribute:

#[derive(Update, FromRow, Debug)]
struct User {
    #[primary_key]
    code: String,
    name: String,
    email: Option<String>,
}

Custom Table Name

You can specify a custom table name using the table_name attribute:

#[derive(Update, FromRow, Debug)]
#[table_name("customers")]
struct User {
    id: i32,
    name: String,
    email: Option<String>,
}

Custom Field Names

You can specify custom field names for the database using the rename attribute:

#[derive(Update, FromRow, Debug)]
struct User {
    id: i32,
    #[rename("user_name")]
    name: String,
    #[rename("user_email")]
    email: Option<String>,
}

Using the Generated Update Form with Transactions

use sqlx::mysql::MySqlPool;
use entity_update_derive::Update;
use sqlx::FromRow;

#[derive(Update, FromRow, Debug)]
struct User {
    id: i32,
    name: String,
    email: Option<String>,
}

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let pool = MySqlPool::connect("mysql://username:password@localhost/database_name").await?;

    let form = User::update_form()
        .with_name("new_username".to_string())
        .with_email(Some("new_email@example.com".to_string()));

    let user_id = 1;

    let mut tx = pool.begin().await?;

    form.execute_update(&user_id, &mut tx).await?;
    let diff = form.transactional_db_diff(&user_id, &mut tx).await?;
    println!("Diff: {:?}", diff);

    tx.commit().await?;

    println!("Update completed successfully");

    Ok(())
}

Generated Methods

update_form()

Creates a new instance of the update form.

let form = User::update_form();

with_field_name()

Builder method for each field in the struct.

let form = User::update_form()
    .with_name("New Name")
    .with_email(Some("new_email@example.com"));

update_stmt()

Generates the SQL SET clause for the update statement.

let stmt = form.update_stmt();

bind_values()

Binds the update values to a SQLx query.

let query = sqlx::query("UPDATE users SET ...");
let query = form.bind_values(query);

model_diff()

Compares the update form with an instance of the original struct and returns a JSON object.

let diff: serde_json::Value = form.model_diff(&original_user);

db_diff()

Compares the update form with the current database record and returns a JSON object.

let diff: serde_json::Value = form.db_diff(&user_id, &pool).await?;

table_name()

Returns the table name as a static string.

let table_name = UserUpdateForm::table_name();

initial_diff()

Returns a JSON object containing all fields of the original model and their current values.

let user = User {
    id: 1,
    name: "John Doe".to_string(),
    email: Some("john@example.com".to_string()),
};
let initial_diff: serde_json::Value = user.initial_diff();

Notes

  • This macro now supports custom primary key fields using the primary_key attribute. If not specified, it defaults to checking for an id or code field.
  • The macro assumes you're using MySQL. Modifications may be needed for other database types.
  • Error handling is minimal in these examples. In a production environment, you should implement proper error handling and logging.
  • The model_diff(), db_diff(), and initial_diff() methods return serde_json::Value objects, which are easy to work with when dealing with JSON data.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the Nandie Software Proprietary License. See the LICENSE file in the project root for the full license text.