reinhardt-query
A type-safe SQL query builder for the Reinhardt framework.
Overview
reinhardt-query provides a fluent API for constructing SQL queries targeting PostgreSQL, MySQL, SQLite, and CockroachDB. It generates parameterized queries with proper identifier escaping and value placeholders for each backend.
Features
DML (Data Manipulation Language)
- Type-safe query construction - SELECT, INSERT, UPDATE, DELETE
- DCL (Data Control Language) support - GRANT and REVOKE statements
- Expression system - Arithmetic, comparison, logical, and pattern matching operators
- Advanced SQL - JOINs, GROUP BY, HAVING, DISTINCT, UNION, CTEs, Window functions
- Parameterized queries -
$1for PostgreSQL/CockroachDB,?for MySQL/SQLite - CASE WHEN expressions - Conditional expressions in queries
- Subqueries - EXISTS, IN, ALL, ANY, SOME operators
DDL (Data Definition Language)
- Table operations - CREATE TABLE, ALTER TABLE, DROP TABLE
- Index operations - CREATE INDEX, ALTER INDEX, DROP INDEX, REINDEX
- View operations - CREATE VIEW, DROP VIEW
- Schema management - CREATE/ALTER/DROP SCHEMA (PostgreSQL, CockroachDB)
- Sequence operations - CREATE/ALTER/DROP SEQUENCE (PostgreSQL, CockroachDB)
- Database operations - CREATE/ALTER/DROP DATABASE (all backends)
- Functions & Procedures - CREATE/ALTER/DROP FUNCTION/PROCEDURE (PostgreSQL, MySQL, CockroachDB)
- Custom types - CREATE/ALTER/DROP TYPE (PostgreSQL, CockroachDB)
- Materialized views - CREATE/ALTER/DROP/REFRESH MATERIALIZED VIEW (PostgreSQL, CockroachDB)
- Events - CREATE/ALTER/DROP EVENT (MySQL)
- Comments - COMMENT ON for all database objects (PostgreSQL, CockroachDB)
- Maintenance - VACUUM, ANALYZE, OPTIMIZE/REPAIR/CHECK TABLE
Multi-Backend Support
- PostgreSQL - Full DDL and DML support with advanced features
- MySQL - DML, Functions, Procedures, Events, and table maintenance
- SQLite - DML and basic DDL operations
- CockroachDB - Full PostgreSQL compatibility with distributed database features
Installation
Add to your Cargo.toml:
[]
= { = "0.1.0-alpha.1" }
Quick Start
use *;
// Build a SELECT query
let mut stmt = select;
stmt.column
.column
.from
.and_where
.order_by
.limit;
// Generate SQL for PostgreSQL
let builder = new;
let = builder.build_select;
// sql = r#"SELECT "name", "email" FROM "users" WHERE "active" = $1 ORDER BY "name" ASC LIMIT $2"#
Usage Examples
INSERT
use *;
let mut stmt = insert;
stmt.into_table
.columns
.values;
let builder = new;
let = builder.build_insert;
UPDATE
use *;
let mut stmt = update;
stmt.table
.set
.set
.and_where;
let builder = new;
let = builder.build_update;
DELETE
use *;
let mut stmt = delete;
stmt.from_table
.and_where;
let builder = new;
let = builder.build_delete;
CREATE TABLE
use *;
use ;
let mut stmt = create_table;
stmt.table
.if_not_exists
.column
.column;
let builder = new;
let = builder.build_create_table;
// sql = r#"CREATE TABLE IF NOT EXISTS "users" ("id" INTEGER NOT NULL PRIMARY KEY, "email" VARCHAR(255) NOT NULL UNIQUE)"#
ALTER TABLE
use *;
use AlterTableOperation;
use ;
let mut stmt = alter_table;
stmt.table
.add_column
.rename_column;
let builder = new;
let = builder.build_alter_table;
// sql = r#"ALTER TABLE "users" ADD COLUMN "created_at" TIMESTAMP NOT NULL, RENAME COLUMN "email" TO "email_address""#
DROP TABLE
use *;
let mut stmt = drop_table;
stmt.table
.if_exists
.cascade; // PostgreSQL only
let builder = new;
let = builder.build_drop_table;
// sql = "DROP TABLE IF EXISTS \"users\" CASCADE"
CREATE INDEX
use *;
let mut stmt = create_index;
stmt.name
.table
.col
.unique
.if_not_exists;
let builder = new;
let = builder.build_create_index;
// sql = r#"CREATE UNIQUE INDEX IF NOT EXISTS "idx_email" ON "users" ("email")"#
DROP INDEX
use *;
let mut stmt = drop_index;
stmt.name
.if_exists;
let builder = new;
let = builder.build_drop_index;
// sql = "DROP INDEX IF EXISTS \"idx_email\""
JOINs
use *;
use ;
let mut stmt = select;
stmt.column
.column
.from
.join;
Window Functions
use *;
use WindowStatement;
let mut stmt = select;
stmt.column
.expr
.from;
Common Table Expressions (CTEs)
use *;
use CommonTableExpr;
let mut cte_query = select;
cte_query.column.column.from
.and_where;
let mut stmt = select;
stmt.column
.from
.with_cte;
CREATE SCHEMA (PostgreSQL, CockroachDB)
use *;
let mut stmt = create_schema;
stmt.name
.if_not_exists
.authorization;
let builder = new;
let = builder.build_create_schema;
// sql = r#"CREATE SCHEMA IF NOT EXISTS "app_schema" AUTHORIZATION "owner_user""#
CREATE SEQUENCE (PostgreSQL, CockroachDB)
use *;
let mut stmt = create_sequence;
stmt.name
.if_not_exists
.start_with
.increment_by
.min_value
.max_value;
let builder = new;
let = builder.build_create_sequence;
CREATE FUNCTION (PostgreSQL, MySQL, CockroachDB)
use *;
let mut stmt = create_function;
stmt.name
.parameter
.parameter
.returns
.language_sql
.immutable
.body;
let builder = new;
let = builder.build_create_function;
CREATE MATERIALIZED VIEW (PostgreSQL, CockroachDB)
use *;
let select = select
.column
.column
.column
.from
.and_where;
let mut stmt = create_materialized_view;
stmt.name
.as_select
.if_not_exists;
let builder = new;
let = builder.build_create_materialized_view;
COMMENT ON (PostgreSQL, CockroachDB)
use *;
use CommentTarget;
let mut stmt = comment;
stmt.target
.comment;
let builder = new;
let = builder.build_comment;
// sql = r#"COMMENT ON TABLE "users" IS 'Stores user account information'"#
GRANT (DCL)
use *;
let stmt = grant
.privilege
.privilege
.on_table
.to
.with_grant_option;
let builder = new;
let = builder.build_grant;
// sql = r#"GRANT SELECT, INSERT ON TABLE "users" TO "app_user" WITH GRANT OPTION"#
REVOKE (DCL)
use *;
let stmt = revoke
.privilege
.from_table
.from
.cascade;
let builder = new;
let = builder.build_revoke;
// sql = r#"REVOKE INSERT ON TABLE "users" FROM "app_user" CASCADE"#
GRANT Role Membership (DCL)
use ;
let stmt = new
.role
.to
.with_admin_option;
let builder = new;
let = builder.build_grant_role;
// sql = r#"GRANT "developer" TO alice WITH ADMIN OPTION"#
REVOKE Role Membership (DCL)
use ;
let stmt = new
.role
.from
.cascade;
let builder = new;
let = builder.build_revoke_role;
// sql = r#"REVOKE "developer" FROM alice CASCADE"#
Extended Object Types (PostgreSQL)
PostgreSQL supports additional object types beyond tables and databases:
use *;
// Grant EXECUTE on function
let stmt = grant
.privilege
.on_function
.to;
let builder = new;
let = builder.build_grant;
// sql = r#"GRANT EXECUTE ON FUNCTION "calculate_total" TO "app_user""#
// Grant USAGE on type
let stmt = grant
.privilege
.on_type
.to;
let = builder.build_grant;
// sql = r#"GRANT USAGE ON TYPE "custom_type" TO "app_user""#
// Grant SET on parameter
let stmt = grant
.privilege
.on_parameter
.to;
let = builder.build_grant;
// sql = r#"GRANT SET ON PARAMETER "work_mem" TO "app_user""#
Supported object types: Function, Procedure, Routine, Type, Domain, ForeignDataWrapper, ForeignServer, Language, LargeObject, Tablespace, Parameter
Backend Differences
DML Features
| Feature | PostgreSQL | MySQL | SQLite | CockroachDB |
|---|---|---|---|---|
| Identifier quoting | "name" |
`name` |
"name" |
"name" |
| Placeholders | $1, $2, ... |
?, ?, ... |
?, ?, ... |
$1, $2, ... |
| NULLS FIRST/LAST | ✅ Native | ❌ | ✅ Native | ✅ Native |
| DISTINCT ON | ✅ | ❌ | ❌ | ✅ |
| Window functions | ✅ Full | ✅ Full | ✅ Full | ✅ Full |
| CTEs (WITH) | ✅ | ✅ | ✅ | ✅ |
DDL Features
| Feature | PostgreSQL | MySQL | SQLite | CockroachDB |
|---|---|---|---|---|
| CREATE/ALTER/DROP SCHEMA | ✅ | ❌ | ❌ | ✅ |
| CREATE/ALTER/DROP SEQUENCE | ✅ | ❌ | ❌ | ✅ |
| CREATE/ALTER/DROP DATABASE | ✅ | ✅ | ✅ | ✅ |
| CREATE/ALTER/DROP FUNCTION | ✅ | ✅ | ❌ | ✅ |
| CREATE/ALTER/DROP PROCEDURE | ✅ | ✅ | ❌ | ✅ |
| CREATE/ALTER/DROP TYPE | ✅ | ❌ | ❌ | ✅ |
| CREATE/ALTER/DROP EVENT | ❌ | ✅ | ❌ | ❌ |
| MATERIALIZED VIEW | ✅ | ❌ | ❌ | ✅ |
| COMMENT ON | ✅ | ❌ | ❌ | ✅ |
| VACUUM/ANALYZE | ✅ | ❌ | ✅ | ✅ |
| OPTIMIZE/REPAIR/CHECK | ❌ | ✅ | ❌ | ❌ |
DCL Features
| Feature | PostgreSQL | MySQL | SQLite | CockroachDB |
|---|---|---|---|---|
| GRANT/REVOKE | ✅ | ✅ | ❌ | ✅ |
SQL Generation Notes
Identifier Quoting
Each database backend uses different quoting styles for SQL identifiers (table names, column names, index names, etc.):
| Backend | Quote Style | Example |
|---|---|---|
| PostgreSQL | Double quotes | "table_name" |
| MySQL | Backticks | `table_name` |
| SQLite | Double quotes | "table_name" |
| CockroachDB | Double quotes | "table_name" |
Quoting Behavior
All backends automatically escape special characters within identifiers:
- PostgreSQL/SQLite/CockroachDB: Double quotes within identifiers are escaped by doubling (
"becomes"") - MySQL: Backticks within identifiers are escaped by doubling (
`becomes``)
Testing Generated SQL
When writing tests for generated SQL, ensure you account for identifier quoting:
use ;
use *;
let builder = new;
let stmt = select.column.from;
let = builder.build_select;
// PostgreSQL uses double quotes
assert_eq!;
// MySQL would generate: SELECT `name` FROM `users`
// SQLite would generate: SELECT "name" FROM "users"
Escape Methods
Each query builder provides an escape_identifier method for manual escaping:
use ;
let builder = new;
let escaped = builder.escape_identifier;
assert_eq!;
Parameter Placeholders
Each backend uses different placeholder styles for parameterized queries:
| Backend | Placeholder Style | Example |
|---|---|---|
| PostgreSQL | Numbered ($1, $2, ...) |
WHERE id = $1 |
| MySQL | Question mark (?) |
WHERE id = ? |
| SQLite | Question mark (?) |
WHERE id = ? |
| CockroachDB | Numbered ($1, $2, ...) |
WHERE id = $1 |
Case Sensitivity
When identifiers are quoted:
- Case is preserved exactly as specified
- PostgreSQL/MySQL/SQLite are case-sensitive for quoted identifiers
- Unquoted identifiers may be case-folded (uppercased or lowercased) depending on the backend
Reserved Keywords
All backends automatically quote identifiers, which allows using reserved keywords as identifiers:
use *;
// "user" is a reserved keyword in PostgreSQL, but quoting allows its use
let stmt = select.column.from;
// Generates: SELECT "name" FROM "user"
Feature Flags
| Flag | Description |
|---|---|
thread-safe |
Use Arc instead of Rc for DynIden |
with-chrono |
Enable chrono date/time types in Value |
with-uuid |
Enable UUID type in Value |
with-json |
Enable JSON type in Value |
with-rust_decimal |
Enable Decimal type in Value |
with-bigdecimal |
Enable BigDecimal type in Value |
full |
Enable all optional features |
Security Considerations
SQL Injection Prevention
This library uses parameterized queries by default, which protects against SQL injection attacks. However, some APIs allow raw SQL strings that can be vulnerable if misused.
Unsafe APIs
The following APIs accept raw SQL strings and should be used with caution:
| API | Severity | Alternative |
|---|---|---|
Expr::cust() / SimpleExpr::Custom |
Medium | Use Expr::cust_with_values() |
CreateFunctionStatement::body() |
High | Only use with trusted input |
CreateProcedureStatement::body() |
High | Only use with trusted input |
ColumnType::Custom |
Low | Only use with trusted input |
FunctionLanguage::Custom |
Low | Only use with trusted input |
Best Practices
- Never pass user input directly to custom SQL APIs
- Always use parameterized queries (
cust_with_values()) for dynamic values - Validate and sanitize any input before using it in custom SQL
- Use the type-safe query builder API when possible
License
See the repository root for license information.