sqlx-record/sqlx-record-derive
Michael Netshipise aba05b2b52 Rename to sqlx-record with multi-database support
- Renamed project from entity-changes to sqlx-record
- Renamed derive crate from entity-update_derive to sqlx-record-derive
- Renamed CLI tool from entity-changes-ctl to sqlx-record-ctl
- Renamed Condition to Filter (condition.rs -> filter.rs)
- Renamed condition_or/condition_and/conditions macros to filter_or/filter_and/filters
- Added multi-database support via feature flags:
  - mysql: MySQL support
  - postgres: PostgreSQL support (with $1, $2 placeholders)
  - sqlite: SQLite support
- Updated query generation for database-specific syntax:
  - Placeholder styles (? vs $1)
  - Table quoting (` vs ")
  - COUNT expressions
  - ILIKE handling
  - Version increment (IF vs CASE WHEN)
- Updated CLI tool for all three databases
- Updated documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 15:31:24 +02:00
..
src Rename to sqlx-record with multi-database support 2026-01-28 15:31:24 +02:00
Cargo.toml Rename to sqlx-record with multi-database support 2026-01-28 15:31:24 +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.