`diesel-rs` ORM - typed SQL `sum(BigInt)` invocations
Using diesel-rs
2.2.x: sum()
with AS
and Type Handling (Version Control Example)
Introduction
diesel-rs
is a powerful ORM for Rust, enabling safe and efficient database interactions. In version 2.2.x, summing values with an AS
alias requires careful handling of SQL types and Diesel’s type system. This post explains how to use sum(_)
with AS
via raw SQL and the Diesel DSL, using a version control system as inspiration for the schema and tables.
Problem Statement
When using SUM(column) AS alias
in SQL, you must alias the result to map it to a Rust struct. Diesel’s type system, however, can complicate this for BigInt
columns due to the Foldable
trait’s dependency on Numeric
types.
Let's exercise using a fictive VCS database example:
Schema Setup
Tables Overview
Here's a simplified version control schema:
- commits: id, repository_id, author_id, message, timestamp
- files: id, repository_id, name, path
- file_versions: id, file_id, commit_id, content_hash, content_size
- repositories: id, name, description
- users: id, username, email
Setup Instructions
To generate the schema using diesel
with SQLite, omitting any migrations:
-- Create repositories table
-- Stores information about each repository
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT
);
-- Create users table
-- Stores information about users (authors of commits)
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
email TEXT NOT NULL UNIQUE
);
-- Create commits table
-- Records each commit made in a repository
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
repository_id INTEGER NOT NULL,
author_id INTEGER NOT NULL,
message TEXT NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (repository_id) REFERENCES repositories(id),
FOREIGN KEY (author_id) REFERENCES users(id)
);
-- Create files table
-- Stores information about files in a repository
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
repository_id INTEGER NOT NULL,
name TEXT NOT NULL,
path TEXT NOT NULL,
FOREIGN KEY (repository_id) REFERENCES repositories(id)
);
-- Create file_versions table
-- Tracks changes to files over time (e.g., each version of a file in a commit)
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
file_id INTEGER NOT NULL,
commit_id INTEGER NOT NULL,
content_hash TEXT NOT NULL,
content_size INTEGER NOT NULL,
FOREIGN KEY (file_id) REFERENCES files(id),
FOREIGN KEY (commit_id) REFERENCES commits(id)
);
Example Schema File
table!
table!
table!
Problem: Summing File Sizes
Let’s say we want to sum the total content size of all files in a specific repository.
Raw SQL Approach
Use sql_query()
for direct control over SQL and aliasing.
let repo_id = 1;
let result = sql_query
.
.
.optional?
.flatten;
dbg!;
Why this works:
- Uses raw SQL with
AS
to alias the result. QueryableByName
maps the alias"total_size"
to the struct field.- Binds parameters safely, avoiding type system conflicts.
Solution 2: Diesel DSL with BigDecimal
Using sum()
with AS
in Diesel requires type annotations and custom types.
use sum;
let maybe_total_size = select
.inner_join
.inner_join
.filter
.
.optional?
.flatten;
Note: If BigInt
is not supported, you may need to use BigDecimal
for more flexibility with large values.
Final Notes
Using diesel-rs
with a version control schema demonstrates how ORMs can be applied in a common, real-world scenario. The example shows how to:
- Set up a schema with multiple tables.
- Query using both raw SQL and the Diesel DSL.
- Sum values with type-safe handling.
ORMs like Diesel are excellent for productivity and safety in most applications, but they are not a one-size-fits-all solution. Use them when they align with your project’s needs, and be prepared to fall back to raw SQL when performance, flexibility, or database-specific features are required.