use expect_test::expect;
use toasty_core::{
schema::db::{Column, ColumnId, PrimaryKey, Schema, Table, TableId, Type as StorageType},
stmt::{
self, Cte, Expr, ExprColumn, Filter, Returning, Select, Source, SourceTable, SourceTableId,
TableFactor, TableRef, TableWithJoins, With,
},
};
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_values_pg(expr: Expr) -> String {
let values = stmt::Values::new(vec![Expr::record([expr])]);
let core_stmt: stmt::Statement = stmt::Query::values(values).into();
let stmt = SqlStatement::from(core_stmt);
let schema = Schema::default();
Serializer::postgresql(&schema).serialize(&stmt)
}
fn select_id_from_users() -> stmt::Statement {
let source = Source::Table(SourceTable {
tables: vec![TableRef::Table(TableId(0))],
from: vec![TableWithJoins {
relation: TableFactor::Table(SourceTableId(0)),
joins: vec![],
}],
});
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())
}
fn users_schema() -> Schema {
Schema {
tables: vec![make_table(0, "users", &[])],
}
}
#[test]
fn value_null() {
expect!["VALUES (NULL);"].assert_eq(&render_values_pg(Expr::Value(stmt::Value::Null)));
}
#[test]
fn value_bool_true_and_false() {
expect!["VALUES (TRUE);"].assert_eq(&render_values_pg(Expr::Value(stmt::Value::Bool(true))));
expect!["VALUES (FALSE);"].assert_eq(&render_values_pg(Expr::Value(stmt::Value::Bool(false))));
}
#[test]
fn value_i64() {
expect!["VALUES (42);"].assert_eq(&render_values_pg(Expr::Value(stmt::Value::I64(42))));
}
#[test]
fn value_string_simple() {
expect!["VALUES ('hello');"].assert_eq(&render_values_pg(Expr::Value(stmt::Value::String(
"hello".into(),
))));
}
#[test]
fn value_string_with_single_quote() {
expect!["VALUES ('O''Brien');"].assert_eq(&render_values_pg(Expr::Value(stmt::Value::String(
"O'Brien".into(),
))));
}
#[test]
fn value_list() {
let list = stmt::Value::List(vec![stmt::Value::I64(1), stmt::Value::I64(2)]);
expect!["VALUES ((1, 2));"].assert_eq(&render_values_pg(Expr::Value(list)));
}
#[test]
fn value_record() {
let record = stmt::Value::Record(stmt::ValueRecord::from_vec(vec![
stmt::Value::I64(1),
stmt::Value::I64(2),
]));
expect!["VALUES ((1, 2));"].assert_eq(&render_values_pg(Expr::Value(record)));
}
#[test]
fn ident_quoting_postgresql_double_quote() {
let schema = users_schema();
let stmt = select_id_from_users();
let sql = Serializer::postgresql(&schema).serialize(&SqlStatement::from(stmt));
expect![[r#"SELECT tbl_0_0."id" FROM "users" AS tbl_0_0;"#]].assert_eq(&sql);
}
#[test]
fn ident_quoting_sqlite_double_quote() {
let schema = users_schema();
let stmt = select_id_from_users();
let sql = Serializer::sqlite(&schema).serialize(&SqlStatement::from(stmt));
expect![[r#"SELECT tbl_0_0."id" FROM "users" AS tbl_0_0;"#]].assert_eq(&sql);
}
#[test]
fn ident_quoting_mysql_backtick() {
let schema = users_schema();
let stmt = select_id_from_users();
let sql = Serializer::mysql(&schema).serialize(&SqlStatement::from(stmt));
expect!["SELECT tbl_0_0.`id` FROM `users` AS tbl_0_0;"].assert_eq(&sql);
}
#[test]
fn name_renders_qualified_period_separated() {
let schema = Schema::default();
let drop = toasty_sql::stmt::DropTable {
name: toasty_sql::stmt::Name(vec!["public".into(), "users".into()]),
if_exists: false,
};
let stmt: SqlStatement = drop.into();
let sql = Serializer::postgresql(&schema).serialize(&stmt);
expect![[r#"DROP TABLE "public"."users";"#]].assert_eq(&sql);
}
fn select_from_cte() -> stmt::Statement {
let cte_query = {
let source = Source::Table(SourceTable {
tables: vec![TableRef::Table(TableId(0))],
from: vec![TableWithJoins {
relation: TableFactor::Table(SourceTableId(0)),
joins: vec![],
}],
});
let select = Select {
returning: Returning::Project(Expr::record([col(0, 0)])),
source,
filter: Filter::ALL,
distinct: false,
};
stmt::Query::builder(select).build()
};
let outer_source = Source::Table(SourceTable {
tables: vec![TableRef::Cte {
nesting: 0,
index: 0,
}],
from: vec![TableWithJoins {
relation: TableFactor::Table(SourceTableId(0)),
joins: vec![],
}],
});
let outer_select = Select {
returning: Returning::Project(Expr::record([col(0, 0)])),
source: outer_source,
filter: Filter::ALL,
distinct: false,
};
stmt::Statement::Query(
stmt::Query::builder(outer_select)
.with(With {
ctes: vec![Cte { query: cte_query }],
})
.build(),
)
}
#[test]
fn column_alias_format_per_flavor() {
let schema = users_schema();
let stmt = select_from_cte();
let sql_stmt = SqlStatement::from(stmt);
let pg = Serializer::postgresql(&schema).serialize(&sql_stmt);
expect![[r#"WITH cte_0_0 as (SELECT tbl_1_0."id" FROM "users" AS tbl_1_0) SELECT tbl_0_0.column1 FROM cte_0_0 AS tbl_0_0;"#]].assert_eq(&pg);
let sqlite = Serializer::sqlite(&schema).serialize(&sql_stmt);
expect![[r#"WITH cte_0_0 as (SELECT tbl_1_0."id" FROM "users" AS tbl_1_0) SELECT tbl_0_0.column1 FROM cte_0_0 AS tbl_0_0;"#]].assert_eq(&sqlite);
let mysql = Serializer::mysql(&schema).serialize(&sql_stmt);
expect!["WITH cte_0_0 as (SELECT tbl_1_0.`id` FROM `users` AS tbl_1_0) SELECT tbl_0_0.column_0 FROM cte_0_0 AS tbl_0_0;"].assert_eq(&mysql);
}
#[test]
fn placeholder_postgresql_dollar_n() {
let record = Expr::record([Expr::arg(0), Expr::arg(1)]);
let values = stmt::Values::new(vec![record]);
let core_stmt: stmt::Statement = stmt::Query::values(values).into();
let sql = Serializer::postgresql(&Schema::default()).serialize(&SqlStatement::from(core_stmt));
expect!["VALUES ($1, $2);"].assert_eq(&sql);
}
#[test]
fn placeholder_mysql_question_mark() {
let record = Expr::record([Expr::arg(0), Expr::arg(1)]);
let values = stmt::Values::new(vec![record]);
let core_stmt: stmt::Statement = stmt::Query::values(values).into();
let sql = Serializer::mysql(&Schema::default()).serialize(&SqlStatement::from(core_stmt));
expect!["VALUES ROW(?, ?);"].assert_eq(&sql);
}
#[test]
fn placeholder_sqlite_question_n() {
let record = Expr::record([Expr::arg(0), Expr::arg(1)]);
let values = stmt::Values::new(vec![record]);
let core_stmt: stmt::Statement = stmt::Query::values(values).into();
let sql = Serializer::sqlite(&Schema::default()).serialize(&SqlStatement::from(core_stmt));
expect!["VALUES (?1, ?2);"].assert_eq(&sql);
}
#[test]
fn placeholder_mysql_arg_order_reordering() {
let record = Expr::record([Expr::arg(1), Expr::arg(0)]);
let values = stmt::Values::new(vec![record]);
let core_stmt: stmt::Statement = stmt::Query::values(values).into();
let (sql, arg_order) = Serializer::mysql(&Schema::default())
.serialize_with_arg_order(&SqlStatement::from(core_stmt));
expect![[r#"
VALUES ROW(?, ?);
[1, 0]"#]]
.assert_eq(&format!("{}\n{:?}", sql, arg_order));
}