#![allow(dead_code, unused, clippy::type_complexity)]
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use prax_query::filter::{Filter, FilterValue, ScalarFilter};
use prax_query::raw::Sql;
use prax_query::sql::{DatabaseType, FastSqlBuilder, QueryCapacity, templates};
use prax_query::types::{NullsOrder, OrderBy, OrderByField};
use std::hint::black_box;
fn bench_filter_construction(c: &mut Criterion) {
let mut group = c.benchmark_group("filter_construction");
group.bench_function("equals_filter", |b| {
b.iter(|| black_box(Filter::Equals("id".into(), FilterValue::Int(42))))
});
group.bench_function("in_filter_10", |b| {
let values: Vec<FilterValue> = (0..10).map(FilterValue::Int).collect();
b.iter(|| black_box(Filter::In("id".into(), values.clone())))
});
group.bench_function("in_filter_100", |b| {
let values: Vec<FilterValue> = (0..100).map(FilterValue::Int).collect();
b.iter(|| black_box(Filter::In("id".into(), values.clone())))
});
group.bench_function("and_filter_3", |b| {
b.iter(|| {
black_box(Filter::and([
Filter::Equals("id".into(), FilterValue::Int(1)),
Filter::Equals("name".into(), FilterValue::String("test".to_string())),
Filter::Equals("active".into(), FilterValue::Bool(true)),
]))
})
});
group.bench_function("complex_nested_filter", |b| {
b.iter(|| {
black_box(Filter::and([
Filter::or([
Filter::Equals("status".into(), FilterValue::String("active".into())),
Filter::Equals("status".into(), FilterValue::String("pending".to_string())),
]),
Filter::Not(Box::new(Filter::Equals(
"deleted".into(),
FilterValue::Bool(true),
))),
Filter::Gt("created_at".into(), FilterValue::Int(1000)),
]))
})
});
group.finish();
}
fn bench_scalar_filters(c: &mut Criterion) {
let mut group = c.benchmark_group("scalar_filters");
group.bench_function("equals", |b| b.iter(|| black_box(ScalarFilter::Equals(42))));
group.bench_function("not_equals", |b| {
b.iter(|| black_box(ScalarFilter::Not(Box::new(42))))
});
group.bench_function("contains_string", |b| {
b.iter(|| black_box(ScalarFilter::Contains("test".to_string())))
});
group.bench_function("starts_with", |b| {
b.iter(|| black_box(ScalarFilter::StartsWith("prefix".to_string())))
});
group.bench_function("in_list", |b| {
let values: Vec<i32> = (0..10).collect();
b.iter(|| black_box(ScalarFilter::In(values.clone())))
});
group.finish();
}
fn bench_sql_builder(c: &mut Criterion) {
let mut group = c.benchmark_group("sql_builder");
group.bench_function("simple_query", |b| {
b.iter(|| {
let sql = Sql::new("SELECT * FROM users WHERE id = ").bind(42);
black_box(sql.build())
})
});
group.bench_function("query_with_multiple_binds", |b| {
b.iter(|| {
let sql = Sql::new("SELECT * FROM users WHERE ")
.push("age > ")
.bind(18)
.push(" AND status = ")
.bind("active")
.push(" AND created_at > ")
.bind("2024-01-01");
black_box(sql.build())
})
});
group.bench_function("query_with_10_binds", |b| {
b.iter(|| {
let mut sql = Sql::new("INSERT INTO users (c1,c2,c3,c4,c5,c6,c7,c8,c9,c10) VALUES (");
for i in 0..10 {
if i > 0 {
sql = sql.push(",");
}
sql = sql.bind(i);
}
sql = sql.push(")");
black_box(sql.build())
})
});
group.bench_function("complex_query", |b| {
b.iter(|| {
let sql = Sql::new("SELECT u.id, u.name, u.email, p.title, p.content ")
.push("FROM users u ")
.push("LEFT JOIN posts p ON p.user_id = u.id ")
.push("WHERE u.active = ")
.bind(true)
.push(" AND u.created_at > ")
.bind("2024-01-01")
.push(" AND (u.role = ")
.bind("admin")
.push(" OR u.role = ")
.bind("moderator")
.push(") ")
.push("ORDER BY u.created_at DESC ")
.push("LIMIT ")
.bind(100)
.push(" OFFSET ")
.bind(0);
black_box(sql.build())
})
});
group.finish();
}
fn bench_fast_sql_builder(c: &mut Criterion) {
let mut group = c.benchmark_group("fast_sql_builder");
group.bench_function("simple_query_original", |b| {
b.iter(|| {
let sql = Sql::new("SELECT * FROM users WHERE id = ").bind(42);
black_box(sql.build())
})
});
group.bench_function("simple_query_fast", |b| {
b.iter(|| {
let mut builder = FastSqlBuilder::postgres(QueryCapacity::SimpleSelect);
builder.push_str("SELECT * FROM users WHERE id = ");
builder.bind(42);
black_box(builder.build())
})
});
group.bench_function("complex_query_original", |b| {
b.iter(|| {
let sql = Sql::new("SELECT * FROM users WHERE active = ")
.bind(true)
.push(" AND age > ")
.bind(18)
.push(" AND status = ")
.bind("approved")
.push(" ORDER BY created_at LIMIT ")
.bind(10);
black_box(sql.build())
})
});
group.bench_function("complex_query_fast", |b| {
b.iter(|| {
let mut builder = FastSqlBuilder::postgres(QueryCapacity::SelectWithFilters(4));
builder
.push_str("SELECT * FROM users WHERE active = ")
.bind(true)
.push_str(" AND age > ")
.bind(18)
.push_str(" AND status = ")
.bind("approved")
.push_str(" ORDER BY created_at LIMIT ")
.bind(10);
black_box(builder.build())
})
});
group.bench_function("in_clause_original_10", |b| {
let values: Vec<FilterValue> = (1..=10).map(FilterValue::Int).collect();
b.iter(|| {
let mut sql = Sql::new("SELECT * FROM users WHERE id IN (");
for (i, v) in values.iter().enumerate() {
if i > 0 {
sql = sql.push(", ");
}
sql = sql.bind(v.clone());
}
sql = sql.push(")");
black_box(sql.build())
})
});
group.bench_function("in_clause_fast_10", |b| {
let values: Vec<FilterValue> = (1..=10).map(FilterValue::Int).collect();
b.iter(|| {
let mut builder = FastSqlBuilder::postgres(QueryCapacity::Custom(64));
builder.push_str("SELECT * FROM users WHERE id IN (");
builder.bind_in_clause(values.clone());
builder.push_char(')');
black_box(builder.build())
})
});
group.bench_function("in_clause_original_100", |b| {
let values: Vec<FilterValue> = (1..=100).map(FilterValue::Int).collect();
b.iter(|| {
let mut sql = Sql::new("SELECT * FROM users WHERE id IN (");
for (i, v) in values.iter().enumerate() {
if i > 0 {
sql = sql.push(", ");
}
sql = sql.bind(v.clone());
}
sql = sql.push(")");
black_box(sql.build())
})
});
group.bench_function("in_clause_fast_100", |b| {
let values: Vec<FilterValue> = (1..=100).map(FilterValue::Int).collect();
b.iter(|| {
let mut builder = FastSqlBuilder::postgres(QueryCapacity::Custom(512));
builder.push_str("SELECT * FROM users WHERE id IN (");
builder.bind_in_clause(values.clone());
builder.push_char(')');
black_box(builder.build())
})
});
group.bench_function("template_select_by_id", |b| {
b.iter(|| black_box(templates::select_by_id("users", &["id", "name", "email"])))
});
group.bench_function("template_insert_returning", |b| {
b.iter(|| {
black_box(templates::insert_returning(
"users",
&["name", "email", "age"],
))
})
});
group.bench_function("template_batch_placeholders_10x3", |b| {
b.iter(|| {
black_box(templates::batch_placeholders(
DatabaseType::PostgreSQL,
3,
10,
))
})
});
group.finish();
}
fn bench_order_by(c: &mut Criterion) {
let mut group = c.benchmark_group("order_by");
group.bench_function("single_field", |b| {
b.iter(|| {
let order = OrderByField::desc("created_at");
black_box(order.to_sql())
})
});
group.bench_function("single_field_with_nulls", |b| {
b.iter(|| {
let order = OrderByField::asc("name").nulls(NullsOrder::Last);
black_box(order.to_sql())
})
});
group.bench_function("multiple_fields", |b| {
b.iter(|| {
let order = OrderBy::Field(OrderByField::desc("created_at"))
.then(OrderByField::asc("name"))
.then(OrderByField::asc("id"));
black_box(order.to_sql())
})
});
group.finish();
}
fn bench_filter_values(c: &mut Criterion) {
let mut group = c.benchmark_group("filter_values");
group.bench_function("int_value", |b| b.iter(|| black_box(FilterValue::Int(42))));
group.bench_function("string_value_short", |b| {
b.iter(|| black_box(FilterValue::String("test".to_string())))
});
group.bench_function("string_value_long", |b| {
let long_string = "a".repeat(1000);
b.iter(|| black_box(FilterValue::String(long_string.clone())))
});
group.bench_function("json_value", |b| {
let json = serde_json::json!({"key": "value", "nested": {"a": 1, "b": 2}});
b.iter(|| black_box(FilterValue::Json(json.clone())))
});
group.bench_function("list_value_10", |b| {
let values: Vec<FilterValue> = (0..10).map(FilterValue::Int).collect();
b.iter(|| black_box(FilterValue::List(values.clone())))
});
group.finish();
}
fn bench_sql_builder_by_db(c: &mut Criterion) {
let mut group = c.benchmark_group("sql_builder_by_db");
group.bench_function("postgres_builder", |b| {
b.iter(|| {
let sql = Sql::new("SELECT * FROM users WHERE id = ")
.with_db_type(DatabaseType::PostgreSQL)
.bind(42);
black_box(sql.build())
})
});
group.bench_function("mysql_builder", |b| {
b.iter(|| {
let sql = Sql::new("SELECT * FROM users WHERE id = ")
.with_db_type(DatabaseType::MySQL)
.bind(42);
black_box(sql.build())
})
});
group.bench_function("sqlite_builder", |b| {
b.iter(|| {
let sql = Sql::new("SELECT * FROM users WHERE id = ")
.with_db_type(DatabaseType::SQLite)
.bind(42);
black_box(sql.build())
})
});
group.finish();
}
fn bench_batch_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("batch_throughput");
for size in [10, 100, 1000].iter() {
group.throughput(Throughput::Elements(*size as u64));
group.bench_with_input(
BenchmarkId::new("filter_construction", size),
size,
|b, &size| {
b.iter(|| {
let filters: Vec<Filter> = (0..size)
.map(|i| Filter::Equals("id".into(), FilterValue::Int(i as i64)))
.collect();
black_box(filters)
})
},
);
group.bench_with_input(
BenchmarkId::new("sql_bind_params", size),
size,
|b, &size| {
b.iter(|| {
let mut sql = Sql::new("SELECT * FROM users WHERE id IN (");
for i in 0..size {
if i > 0 {
sql = sql.push(",");
}
sql = sql.bind(i as i64);
}
sql = sql.push(")");
black_box(sql.build())
})
},
);
}
group.finish();
}
fn bench_placeholder_generation(c: &mut Criterion) {
let mut group = c.benchmark_group("placeholder_generation");
group.bench_function("postgres_placeholder", |b| {
b.iter(|| {
let db = DatabaseType::PostgreSQL;
for i in 1..=100 {
black_box(db.placeholder(i));
}
})
});
group.bench_function("mysql_placeholder", |b| {
b.iter(|| {
let db = DatabaseType::MySQL;
for i in 1..=100 {
black_box(db.placeholder(i));
}
})
});
group.bench_function("sqlite_placeholder", |b| {
b.iter(|| {
let db = DatabaseType::SQLite;
for i in 1..=100 {
black_box(db.placeholder(i));
}
})
});
group.finish();
}
criterion_group!(
benches,
bench_filter_construction,
bench_scalar_filters,
bench_sql_builder,
bench_fast_sql_builder,
bench_order_by,
bench_filter_values,
bench_sql_builder_by_db,
bench_batch_throughput,
bench_placeholder_generation,
);
criterion_main!(benches);