use expect_test::expect;
use toasty_core::{
schema::db::Schema,
stmt::{
self, BinaryOp, Expr, ExprAnd, ExprIntersects, ExprIsSuperset, ExprLength, ExprLike,
ExprOr, FuncCount,
},
};
use toasty_sql::{Serializer, Statement as SqlStatement};
fn make_stmt(expr: Expr) -> SqlStatement {
let values = stmt::Values::new(vec![Expr::record([expr])]);
let core_stmt: stmt::Statement = stmt::Query::values(values).into();
SqlStatement::from(core_stmt)
}
fn render_pg(expr: Expr) -> String {
let stmt = make_stmt(expr);
let schema = Schema::default();
Serializer::postgresql(&schema).serialize(&stmt)
}
fn render_mysql(expr: Expr) -> String {
let stmt = make_stmt(expr);
let schema = Schema::default();
Serializer::mysql(&schema).serialize(&stmt)
}
fn render_sqlite(expr: Expr) -> String {
let stmt = make_stmt(expr);
let schema = Schema::default();
Serializer::sqlite(&schema).serialize(&stmt)
}
#[test]
fn binary_op_all_comparison_operators() {
let expr = Expr::binary_op(Expr::arg(0), BinaryOp::Eq, Expr::arg(1));
expect!["VALUES ($1 = $2);"].assert_eq(&render_pg(expr));
let expr = Expr::binary_op(Expr::arg(0), BinaryOp::Ne, Expr::arg(1));
expect!["VALUES ($1 <> $2);"].assert_eq(&render_pg(expr));
let expr = Expr::binary_op(Expr::arg(0), BinaryOp::Lt, Expr::arg(1));
expect!["VALUES ($1 < $2);"].assert_eq(&render_pg(expr));
let expr = Expr::binary_op(Expr::arg(0), BinaryOp::Le, Expr::arg(1));
expect!["VALUES ($1 <= $2);"].assert_eq(&render_pg(expr));
let expr = Expr::binary_op(Expr::arg(0), BinaryOp::Gt, Expr::arg(1));
expect!["VALUES ($1 > $2);"].assert_eq(&render_pg(expr));
let expr = Expr::binary_op(Expr::arg(0), BinaryOp::Ge, Expr::arg(1));
expect!["VALUES ($1 >= $2);"].assert_eq(&render_pg(expr));
}
#[test]
fn and_chain_joins_operands_with_and_keyword() {
let expr: Expr = ExprAnd {
operands: vec![
Expr::eq(Expr::arg(0), Expr::arg(1)),
Expr::eq(Expr::arg(2), Expr::arg(3)),
Expr::eq(Expr::arg(4), Expr::arg(5)),
],
}
.into();
expect!["VALUES ($1 = $2 AND $3 = $4 AND $5 = $6);"].assert_eq(&render_pg(expr));
}
#[test]
fn or_chain_joins_operands_with_or_keyword() {
let expr: Expr = ExprOr {
operands: vec![
Expr::eq(Expr::arg(0), Expr::arg(1)),
Expr::eq(Expr::arg(2), Expr::arg(3)),
Expr::eq(Expr::arg(4), Expr::arg(5)),
],
}
.into();
expect!["VALUES ($1 = $2 OR $3 = $4 OR $5 = $6);"].assert_eq(&render_pg(expr));
}
#[test]
fn like_renders_with_like_keyword() {
let expr = Expr::like(Expr::arg(0), Expr::arg(1));
expect!["VALUES ($1 LIKE $2);"].assert_eq(&render_pg(expr));
}
#[test]
fn like_with_escape_appends_escape_clause() {
let expr: Expr = ExprLike {
expr: Box::new(Expr::arg(0)),
pattern: Box::new(Expr::arg(1)),
escape: Some('\\'),
case_insensitive: false,
}
.into();
expect![[r#"VALUES ($1 LIKE $2 ESCAPE '\');"#]].assert_eq(&render_pg(expr));
}
#[test]
fn ilike_renders_on_postgresql() {
let expr = Expr::ilike(Expr::arg(0), Expr::arg(1));
expect!["VALUES ($1 ILIKE $2);"].assert_eq(&render_pg(expr));
}
#[test]
fn starts_with_uses_postgresql_prefix_operator() {
let expr = Expr::starts_with(Expr::arg(0), Expr::arg(1));
expect!["VALUES ($1 ^@ $2);"].assert_eq(&render_pg(expr));
}
#[test]
fn starts_with_uses_sqlite_glob_operator() {
let expr = Expr::starts_with(Expr::arg(0), Expr::arg(1));
expect!["VALUES (?1 GLOB ?2);"].assert_eq(&render_sqlite(expr));
}
#[test]
fn starts_with_uses_mysql_binary_like_operator() {
let expr = Expr::starts_with(Expr::arg(0), Expr::arg(1));
expect!["VALUES ROW(BINARY ? LIKE ? ESCAPE '!');"].assert_eq(&render_mysql(expr));
}
#[test]
fn count_star_renders_as_count_star() {
expect!["VALUES (COUNT(*));"].assert_eq(&render_pg(Expr::count_star()));
}
#[test]
fn count_star_with_filter_renders_filter_clause_on_postgresql() {
let expr: Expr = FuncCount {
arg: None,
filter: Some(Box::new(Expr::eq(Expr::arg(0), Expr::arg(1)))),
}
.into();
expect!["VALUES (COUNT(*) FILTER (WHERE $1 = $2));"].assert_eq(&render_pg(expr));
}
#[test]
fn count_star_with_filter_rewrites_to_case_on_mysql() {
let expr: Expr = FuncCount {
arg: None,
filter: Some(Box::new(Expr::eq(Expr::arg(0), Expr::arg(1)))),
}
.into();
expect!["VALUES ROW(COUNT(CASE WHEN ? = ? THEN 1 END));"].assert_eq(&render_mysql(expr));
}
#[test]
fn last_insert_id_renders_on_mysql() {
expect!["VALUES ROW(LAST_INSERT_ID());"].assert_eq(&render_mysql(Expr::last_insert_id()));
}
#[test]
fn is_superset_uses_at_gt_on_postgresql() {
let expr: Expr = ExprIsSuperset {
lhs: Box::new(Expr::arg(0)),
rhs: Box::new(Expr::arg(1)),
}
.into();
expect!["VALUES ($1 @> $2);"].assert_eq(&render_pg(expr));
}
#[test]
fn is_superset_uses_json_contains_on_mysql() {
let expr: Expr = ExprIsSuperset {
lhs: Box::new(Expr::arg(0)),
rhs: Box::new(Expr::arg(1)),
}
.into();
expect!["VALUES ROW(JSON_CONTAINS(?, ?));"].assert_eq(&render_mysql(expr));
}
#[test]
fn is_superset_uses_not_exists_on_sqlite() {
let expr: Expr = ExprIsSuperset {
lhs: Box::new(Expr::arg(0)),
rhs: Box::new(Expr::arg(1)),
}
.into();
expect!["VALUES (NOT EXISTS (SELECT 1 FROM json_each(?2) AS r WHERE r.value NOT IN (SELECT l.value FROM json_each(?1) AS l)));"].assert_eq(&render_sqlite(expr));
}
#[test]
fn intersects_uses_amp_amp_on_postgresql() {
let expr: Expr = ExprIntersects {
lhs: Box::new(Expr::arg(0)),
rhs: Box::new(Expr::arg(1)),
}
.into();
expect!["VALUES ($1 && $2);"].assert_eq(&render_pg(expr));
}
#[test]
fn intersects_uses_json_overlaps_on_mysql() {
let expr: Expr = ExprIntersects {
lhs: Box::new(Expr::arg(0)),
rhs: Box::new(Expr::arg(1)),
}
.into();
expect!["VALUES ROW(JSON_OVERLAPS(?, ?));"].assert_eq(&render_mysql(expr));
}
#[test]
fn intersects_uses_exists_on_sqlite() {
let expr: Expr = ExprIntersects {
lhs: Box::new(Expr::arg(0)),
rhs: Box::new(Expr::arg(1)),
}
.into();
expect!["VALUES (EXISTS (SELECT 1 FROM json_each(?2) AS r WHERE r.value IN (SELECT l.value FROM json_each(?1) AS l)));"].assert_eq(&render_sqlite(expr));
}
#[test]
fn length_uses_cardinality_on_postgresql() {
let expr: Expr = ExprLength {
expr: Box::new(Expr::arg(0)),
}
.into();
expect!["VALUES (cardinality($1));"].assert_eq(&render_pg(expr));
}
#[test]
fn length_uses_json_length_on_mysql() {
let expr: Expr = ExprLength {
expr: Box::new(Expr::arg(0)),
}
.into();
expect!["VALUES ROW(JSON_LENGTH(?));"].assert_eq(&render_mysql(expr));
}
#[test]
fn length_uses_json_array_length_on_sqlite() {
let expr: Expr = ExprLength {
expr: Box::new(Expr::arg(0)),
}
.into();
expect!["VALUES (json_array_length(?1));"].assert_eq(&render_sqlite(expr));
}
#[test]
fn default_renders_default_on_postgresql_and_mysql_and_null_on_sqlite() {
expect!["VALUES (DEFAULT);"].assert_eq(&render_pg(Expr::Default));
expect!["VALUES ROW(DEFAULT);"].assert_eq(&render_mysql(Expr::Default));
expect!["VALUES (NULL);"].assert_eq(&render_sqlite(Expr::Default));
}