#![allow(clippy::indexing_slicing)] #![allow(clippy::unwrap_used)]
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use mik_sql::{Cursor, Filter, FilterValidator, Operator, SortDir, Value, postgres, sqlite};
use mik_sql::{is_valid_sql_expression, is_valid_sql_identifier};
use std::hint::black_box;
fn bench_sql_validation(c: &mut Criterion) {
let mut group = c.benchmark_group("sql_validation");
let identifiers = [
("short", "id"),
("medium", "user_email_address"),
("long", "very_long_column_name_with_many_parts_here"),
("invalid", "DROP TABLE users--"),
];
for (name, ident) in identifiers {
group.bench_with_input(BenchmarkId::new("identifier", name), ident, |b, s| {
b.iter(|| is_valid_sql_identifier(black_box(s)));
});
}
let expressions = [
("simple", "price * quantity"),
("function", "COALESCE(name, 'unknown')"),
("complex", "(price * quantity) - discount + tax"),
("malicious", "1; DROP TABLE users--"),
];
for (name, expr) in expressions {
group.bench_with_input(BenchmarkId::new("expression", name), expr, |b, s| {
b.iter(|| is_valid_sql_expression(black_box(s)));
});
}
group.finish();
}
fn bench_query_builder(c: &mut Criterion) {
let mut group = c.benchmark_group("query_builder");
group.bench_function("select_simple", |b| {
b.iter(|| {
postgres(black_box("users"))
.fields(&["id", "name", "email"])
.build()
});
});
group.bench_function("select_with_where", |b| {
b.iter(|| {
postgres(black_box("users"))
.fields(&["id", "name", "email"])
.filter("active", Operator::Eq, Value::Bool(true))
.filter("role", Operator::Eq, Value::String("admin".to_string()))
.build()
});
});
group.bench_function("select_full", |b| {
b.iter(|| {
postgres(black_box("users"))
.fields(&["id", "name", "email", "created_at"])
.filter("active", Operator::Eq, Value::Bool(true))
.sort("created_at", SortDir::Desc)
.sort("id", SortDir::Asc)
.limit(50)
.build()
});
});
group.finish();
}
fn bench_dialects(c: &mut Criterion) {
let mut group = c.benchmark_group("dialects");
group.bench_function("postgres", |b| {
b.iter(|| {
postgres(black_box("users"))
.fields(&["id", "name"])
.filter("active", Operator::Eq, Value::Bool(true))
.limit(10)
.build()
});
});
group.bench_function("sqlite", |b| {
b.iter(|| {
sqlite(black_box("users"))
.fields(&["id", "name"])
.filter("active", Operator::Eq, Value::Bool(true))
.limit(10)
.build()
});
});
group.finish();
}
fn bench_cursor_pagination(c: &mut Criterion) {
let mut group = c.benchmark_group("cursor");
group.bench_function("create", |b| {
b.iter(|| {
Cursor::new()
.string("created_at", black_box("2024-01-15T10:30:00Z"))
.int("id", black_box(12345))
});
});
let cursor = Cursor::new()
.string("created_at", "2024-01-15T10:30:00Z")
.int("id", 12345);
group.bench_function("encode", |b| b.iter(|| cursor.encode()));
let encoded = cursor.encode();
group.bench_function("decode", |b| b.iter(|| Cursor::decode(black_box(&encoded))));
group.bench_function("query_with_cursor", |b| {
let cursor = Cursor::new()
.string("created_at", "2024-01-15T10:30:00Z")
.int("id", 12345);
b.iter(|| {
postgres(black_box("posts"))
.fields(&["id", "title", "created_at"])
.filter("published", Operator::Eq, Value::Bool(true))
.sort("created_at", SortDir::Desc)
.sort("id", SortDir::Desc)
.after_cursor(cursor.clone())
.limit(20)
.build()
});
});
group.finish();
}
fn bench_filter_validation(c: &mut Criterion) {
let mut group = c.benchmark_group("filter_validation");
group.bench_function("validator_new", |b| b.iter(FilterValidator::new));
group.bench_function("validator_with_fields", |b| {
b.iter(|| {
FilterValidator::new().allow_fields(black_box(&[
"name",
"email",
"status",
"created_at",
"updated_at",
]))
});
});
let validator = FilterValidator::new().allow_fields(&["name", "email", "status", "created_at"]);
let simple_filter = Filter::new("name", Operator::Eq, Value::String("Alice".to_string()));
group.bench_function("validate_simple", |b| {
b.iter(|| validator.validate(black_box(&simple_filter)));
});
let invalid_filter = Filter::new(
"password",
Operator::Eq,
Value::String("secret".to_string()),
);
group.bench_function("validate_invalid_field", |b| {
b.iter(|| validator.validate(black_box(&invalid_filter)));
});
let in_filter = Filter::new(
"status",
Operator::In,
Value::Array(vec![
Value::String("active".to_string()),
Value::String("pending".to_string()),
Value::String("review".to_string()),
]),
);
group.bench_function("validate_in_operator", |b| {
b.iter(|| validator.validate(black_box(&in_filter)));
});
let nested_array_filter = Filter::new(
"tags",
Operator::In,
Value::Array(vec![
Value::String("rust".to_string()),
Value::String("wasm".to_string()),
Value::String("web".to_string()),
Value::String("api".to_string()),
Value::String("http".to_string()),
]),
);
group.bench_function("validate_array_5_items", |b| {
b.iter(|| validator.validate(black_box(&nested_array_filter)));
});
let regex_filter = Filter::new(
"email",
Operator::Regex,
Value::String(".*@example\\.com".to_string()),
);
group.bench_function("validate_denied_operator", |b| {
b.iter(|| validator.validate(black_box(®ex_filter)));
});
group.finish();
}
fn bench_string_escaping(c: &mut Criterion) {
let mut group = c.benchmark_group("string_escaping");
let strings = [
("no_quotes", "hello world"),
("single_quote", "it's a test"),
("many_quotes", "he said 'hello' and she said 'goodbye'"),
("long_clean", &"abcdefghij".repeat(10)),
];
for (name, s) in strings {
group.bench_with_input(BenchmarkId::new("filter_string", name), s, |b, s| {
b.iter(|| {
postgres("users")
.fields(&["id"])
.filter(
"name",
Operator::Eq,
Value::String(black_box(s.to_string())),
)
.build()
});
});
}
group.finish();
}
fn bench_complex_queries(c: &mut Criterion) {
let mut group = c.benchmark_group("complex_queries");
group.bench_function("list_with_pagination", |b| {
b.iter(|| {
postgres(black_box("posts"))
.fields(&["id", "title", "slug", "created_at", "author_id", "status"])
.filter(
"status",
Operator::Eq,
Value::String("published".to_string()),
)
.filter("active", Operator::Eq, Value::Bool(true))
.sort("created_at", SortDir::Desc)
.sort("id", SortDir::Desc)
.limit_offset(20, 40)
.build()
});
});
group.bench_function("search_with_filters", |b| {
b.iter(|| {
postgres(black_box("products"))
.fields(&["id", "name", "price", "category", "in_stock"])
.filter(
"category",
Operator::In,
Value::Array(vec![
Value::String("electronics".to_string()),
Value::String("computers".to_string()),
]),
)
.filter("price", Operator::Gte, Value::Float(100.0))
.filter("price", Operator::Lte, Value::Float(1000.0))
.filter("in_stock", Operator::Eq, Value::Bool(true))
.sort("price", SortDir::Asc)
.limit(50)
.build()
});
});
group.bench_function("grouped_query", |b| {
b.iter(|| {
postgres(black_box("orders"))
.fields(&["customer_id", "status"])
.computed("total", "SUM(amount)")
.computed("count", "COUNT(*)")
.filter(
"created_at",
Operator::Gte,
Value::String("2024-01-01".to_string()),
)
.group_by(&["customer_id", "status"])
.sort("total", SortDir::Desc)
.limit(100)
.build()
});
});
group.finish();
}
criterion_group!(
benches,
bench_sql_validation,
bench_query_builder,
bench_dialects,
bench_cursor_pagination,
bench_filter_validation,
bench_string_escaping,
bench_complex_queries,
);
criterion_main!(benches);