use expect_test::expect;
use toasty_core::{
schema::db::{Column, ColumnId, PrimaryKey, Schema, Table, TableId, Type as StorageType},
stmt::{
self, Expr, ExprColumn, Filter, Join, JoinOp, Returning, Select, Source, SourceTable,
SourceTableId, TableFactor, TableRef, TableWithJoins,
},
};
use toasty_sql::{Serializer, Statement as SqlStatement};
fn make_table(id: usize, name: &str, cols: &[&str]) -> Table {
let mut columns = vec![Column {
id: ColumnId {
table: TableId(id),
index: 0,
},
name: "id".to_string(),
ty: stmt::Type::I64,
storage_ty: StorageType::Integer(8),
nullable: false,
primary_key: true,
auto_increment: false,
versionable: false,
}];
for (i, name) in cols.iter().enumerate() {
columns.push(Column {
id: ColumnId {
table: TableId(id),
index: i + 1,
},
name: (*name).to_string(),
ty: stmt::Type::I64,
storage_ty: StorageType::Integer(8),
nullable: false,
primary_key: false,
auto_increment: false,
versionable: false,
});
}
Table {
id: TableId(id),
name: name.to_string(),
columns,
primary_key: PrimaryKey {
columns: vec![ColumnId {
table: TableId(id),
index: 0,
}],
index: toasty_core::schema::db::IndexId {
table: TableId(id),
index: 0,
},
},
indices: vec![],
}
}
fn col(table: usize, column: usize) -> Expr {
Expr::column(ExprColumn {
nesting: 0,
table,
column,
})
}
fn render_sqlite(schema: &Schema, stmt: stmt::Statement) -> String {
let sql_stmt = SqlStatement::from(stmt);
Serializer::sqlite(schema).serialize(&sql_stmt)
}
fn select_with_join(constraint: JoinOp) -> stmt::Statement {
let source = Source::Table(SourceTable {
tables: vec![TableRef::Table(TableId(0)), TableRef::Table(TableId(1))],
from: vec![TableWithJoins {
relation: TableFactor::Table(SourceTableId(0)),
joins: vec![Join {
table: SourceTableId(1),
constraint,
}],
}],
});
let select = Select {
returning: Returning::Project(Expr::record([col(0, 0)])),
source,
filter: Filter::ALL,
distinct: false,
};
stmt::Statement::Query(stmt::Query::builder(select).build())
}
#[test]
fn left_join_renders_left_join_with_on_clause() {
let schema = Schema {
tables: vec![
make_table(0, "users", &[]),
make_table(1, "posts", &["user_id"]),
],
};
let on = Expr::eq(col(1, 1), col(0, 0));
expect![[r#"SELECT tbl_0_0."id" FROM "users" AS tbl_0_0 LEFT JOIN "posts" AS tbl_0_1 ON tbl_0_1."user_id" = tbl_0_0."id";"#]].assert_eq(&render_sqlite(&schema, select_with_join(JoinOp::Left(on))));
}
#[test]
fn inner_join_renders_inner_join_with_on_clause() {
let schema = Schema {
tables: vec![
make_table(0, "users", &[]),
make_table(1, "posts", &["user_id"]),
],
};
let on = Expr::eq(col(1, 1), col(0, 0));
expect![[r#"SELECT tbl_0_0."id" FROM "users" AS tbl_0_0 INNER JOIN "posts" AS tbl_0_1 ON tbl_0_1."user_id" = tbl_0_0."id";"#]].assert_eq(&render_sqlite(&schema, select_with_join(JoinOp::Inner(on))));
}
#[test]
fn multi_step_left_join_chain() {
let schema = Schema {
tables: vec![
make_table(0, "users", &[]),
make_table(1, "posts", &["user_id"]),
make_table(2, "comments", &["post_id"]),
],
};
let source = Source::Table(SourceTable {
tables: vec![
TableRef::Table(TableId(0)),
TableRef::Table(TableId(1)),
TableRef::Table(TableId(2)),
],
from: vec![TableWithJoins {
relation: TableFactor::Table(SourceTableId(0)),
joins: vec![
Join {
table: SourceTableId(1),
constraint: JoinOp::Left(Expr::eq(col(1, 1), col(0, 0))),
},
Join {
table: SourceTableId(2),
constraint: JoinOp::Left(Expr::eq(col(2, 1), col(1, 0))),
},
],
}],
});
let select = Select {
returning: Returning::Project(Expr::record([col(0, 0), col(1, 0), col(2, 0)])),
source,
filter: Filter::ALL,
distinct: false,
};
let stmt = stmt::Statement::Query(stmt::Query::builder(select).build());
expect![[r#"SELECT tbl_0_0."id", tbl_0_1."id", tbl_0_2."id" FROM "users" AS tbl_0_0 LEFT JOIN "posts" AS tbl_0_1 ON tbl_0_1."user_id" = tbl_0_0."id" LEFT JOIN "comments" AS tbl_0_2 ON tbl_0_2."post_id" = tbl_0_1."id";"#]].assert_eq(&render_sqlite(&schema, stmt));
}
#[test]
fn mixed_inner_then_left_join() {
let schema = Schema {
tables: vec![
make_table(0, "users", &[]),
make_table(1, "posts", &["user_id"]),
make_table(2, "comments", &["post_id"]),
],
};
let source = Source::Table(SourceTable {
tables: vec![
TableRef::Table(TableId(0)),
TableRef::Table(TableId(1)),
TableRef::Table(TableId(2)),
],
from: vec![TableWithJoins {
relation: TableFactor::Table(SourceTableId(0)),
joins: vec![
Join {
table: SourceTableId(1),
constraint: JoinOp::Inner(Expr::eq(col(1, 1), col(0, 0))),
},
Join {
table: SourceTableId(2),
constraint: JoinOp::Left(Expr::eq(col(2, 1), col(1, 0))),
},
],
}],
});
let select = Select {
returning: Returning::Project(Expr::record([col(0, 0)])),
source,
filter: Filter::ALL,
distinct: false,
};
let stmt = stmt::Statement::Query(stmt::Query::builder(select).build());
expect![[r#"SELECT tbl_0_0."id" FROM "users" AS tbl_0_0 INNER JOIN "posts" AS tbl_0_1 ON tbl_0_1."user_id" = tbl_0_0."id" LEFT JOIN "comments" AS tbl_0_2 ON tbl_0_2."post_id" = tbl_0_1."id";"#]].assert_eq(&render_sqlite(&schema, stmt));
}