fn stmt_text(statement: &dyn crate::Statement) -> String {
crate::DefaultStatementTextRenderer::instance().render(statement)
}
use std::sync::Arc;
use crate::mappers::{DictionaryMapper, Mapper};
use crate::*;
fn mappings() -> StatementMappers {
let entities: Arc<dyn Mapper<String, String> + Send + Sync> = Arc::new(DictionaryMapper::new([
("User".to_string(), "people".to_string()),
("Role".to_string(), "roles".to_string()),
]));
let properties: Arc<dyn Mapper<String, String> + Send + Sync> =
Arc::new(DictionaryMapper::new([
("Name".to_string(), "full_name".to_string()),
("title".to_string(), "book.title".to_string()),
("id".to_string(), "user_id".to_string()),
]));
StatementMappers::new(entities, properties)
}
fn mapper(name: &str, value: &Value) -> Result<Value> {
Ok(match value {
Value::Text(text) => Value::Text(format!("{name}:{}", text.to_uppercase())),
other => other.clone(),
})
}
#[test]
fn maps_select() {
let where_clause = Where::new(Operator::equals("Name", "Bea"));
let having = Where::new(Operator::equals("Name", "Bea"));
let fields = [
"Name",
"title",
"scope.title",
"alias.other",
"\"schema.name\".id",
];
let joins = [
Join::inner("Role", "User.id", "Role.user_id"),
Join::cross("logs"),
Join::condition(JoinKind::Inner, "extra", "a = b OR c"),
];
let order = [OrderBy::desc("Name")];
let select = SelectStatement::new(SelectStatementOptions {
distinct: true,
where_clause: Some(&where_clause),
joins: &joins,
group_by: &["Name"],
having: Some(&having),
order_by: &order,
limit: Some(5),
offset: Some(1),
..SelectStatementOptions::new("User", &fields)
});
let mapped = mappings().apply_select(&select, &mapper).unwrap();
assert_eq!(mapped.table, "people");
assert_eq!(
mapped.fields,
[
"full_name",
"book.title",
"book.title",
"alias.other",
"\"schema.name\".user_id"
]
);
assert!(mapped.distinct);
assert_eq!(mapped.limit, Some(5));
assert_eq!(mapped.offset, Some(1));
assert_eq!(mapped.group_by, ["full_name"]);
assert_eq!(mapped.order_by, [OrderBy::desc("full_name")]);
assert_eq!(
mapped.joins[0].on.as_deref(),
Some("people.user_id = roles.user_id")
);
assert_eq!(mapped.joins[1].table, "logs");
assert!(mapped.joins[1].on.is_none());
assert_eq!(mapped.joins[2].on.as_deref(), Some("a = b OR c"));
assert!(stmt_text(&mapped).contains("full_name = $full_name"));
let parameters = mapped.predicate().unwrap().parameters();
assert_eq!(parameters[0].value, Value::Text("Name:BEA".into()));
let names = mappings();
assert_eq!(names.property("full_name"), "Name");
assert_eq!(names.property("people.full_name"), "Name");
assert_eq!(names.property("other"), "other");
assert_eq!(names.property("people.missing"), "people.missing");
}
#[test]
fn maps_writes() {
let names = mappings();
let insert = names.apply_insert(&InsertStatement::new(
"User",
&[Parameter::new("Name", "Ada")],
));
assert_eq!(
stmt_text(&insert),
"INSERT INTO people (full_name) VALUES ($full_name)"
);
assert_eq!(insert.fields()[0].value, Value::Text("Ada".into()));
let where_clause = Where::new(Operator::equals("Name", "Bea"));
let update = names
.apply_update(
&UpdateStatement::new(
"User",
&[Parameter::new("Name", "Ada")],
Some(&where_clause),
),
&mapper,
)
.unwrap();
assert_eq!(
stmt_text(&update),
"UPDATE people SET full_name = $full_name WHERE full_name = $full_name__where"
);
let parameters = update.parameters();
assert_eq!(parameters[0].value, Value::Text("Ada".into()));
assert_eq!(parameters[1].name, "full_name__where");
assert_eq!(parameters[1].value, Value::Text("Name:BEA".into()));
let delete = names
.apply_delete(
&DeleteStatement::new("User", Some(&where_clause)),
&mapper,
)
.unwrap();
assert_eq!(
stmt_text(&delete),
"DELETE FROM people WHERE full_name = $full_name"
);
let inserted = names.apply_inserted(&SelectInsertedIdStatement::new("User", "id"));
assert_eq!(inserted.table, "people");
assert_eq!(inserted.column, "user_id");
let bare = names
.apply_update(
&UpdateStatement::new("User", &[Parameter::new("Name", "Ada")], None),
&mapper,
)
.unwrap();
assert_eq!(stmt_text(&bare), "UPDATE people SET full_name = $full_name");
}
#[test]
fn leaves_raw_and_other_statements() {
let names = mappings();
let call = ExecuteStoredProcedureStatement::new("save_user", &[]);
assert_eq!(stmt_text(&call), "CALL save_user()");
assert!(ReadInput::as_where(&WhereText::new("id = 1")).is_some());
let text = WhereText::new("name = $name");
let kept = names
.apply_delete(&DeleteStatement::new("User", Some(&text)), &mapper)
.unwrap();
assert_eq!(stmt_text(&kept), "DELETE FROM people WHERE name = $name");
}
#[test]
fn maps_select_field_alias() {
let entities: Arc<dyn Mapper<String, String> + Send + Sync> = Arc::new(DictionaryMapper::new([
("Contact".to_string(), "contacts_base".to_string()),
("Address".to_string(), "address".to_string()),
]));
let properties: Arc<dyn Mapper<String, String> + Send + Sync> =
Arc::new(DictionaryMapper::new([
("Id".to_string(), "id".to_string()),
("FirstName".to_string(), "first_name".to_string()),
("ContactId".to_string(), "contact_id".to_string()),
]));
let mappings = StatementMappers::new(entities, properties);
let fields = [
"Contact.Id AS Id",
"Contact.FirstName AS FirstName",
"Address.Id AS AddressId",
];
let joins = [Join::left("Address", "Contact.Id", "Address.ContactId")];
let select = SelectStatement::new(SelectStatementOptions {
joins: &joins,
..SelectStatementOptions::new("Contact", &fields)
});
let mapped = mappings.apply_select(&select, &mapper).unwrap();
assert_eq!(
stmt_text(&mapped),
"SELECT contacts_base.id AS Id, contacts_base.first_name AS FirstName, address.id AS AddressId FROM contacts_base LEFT JOIN address ON contacts_base.id = address.contact_id"
);
}
#[test]
fn maps_contacts_select() {
let entities: Arc<dyn Mapper<String, String> + Send + Sync> = Arc::new(DictionaryMapper::new([
("Contact".to_string(), "contacts_base".to_string()),
("ContactSource".to_string(), "contact_source".to_string()),
("ContactType".to_string(), "contact_type".to_string()),
("Address".to_string(), "address".to_string()),
("AddressType".to_string(), "address_type".to_string()),
("Phone".to_string(), "phone".to_string()),
("PhoneType".to_string(), "phone_type".to_string()),
]));
let properties: Arc<dyn Mapper<String, String> + Send + Sync> =
Arc::new(DictionaryMapper::new([
("Id".to_string(), "id".to_string()),
("FirstName".to_string(), "first_name".to_string()),
("LastName".to_string(), "last_name".to_string()),
("Email".to_string(), "email".to_string()),
("Name".to_string(), "name".to_string()),
("Line1".to_string(), "line1".to_string()),
("City".to_string(), "city".to_string()),
("Number".to_string(), "number".to_string()),
("ContactId".to_string(), "contact_id".to_string()),
("ContactSourceId".to_string(), "contact_source_id".to_string()),
("ContactTypeId".to_string(), "contact_type_id".to_string()),
("AddressTypeId".to_string(), "address_type_id".to_string()),
("PhoneTypeId".to_string(), "phone_type_id".to_string()),
]));
let mappings = StatementMappers::new(entities, properties);
let fields = [
"Contact.FirstName",
"Contact.LastName",
"Contact.Email",
"ContactSource.Name",
"Address.Line1",
"Address.City",
"Phone.Number",
];
let joins = [
Join::inner(
"ContactSource",
"Contact.ContactSourceId",
"ContactSource.Id",
),
Join::inner("ContactType", "Contact.ContactTypeId", "ContactType.Id"),
Join::left("Address", "Contact.Id", "Address.ContactId"),
Join::left("AddressType", "Address.AddressTypeId", "AddressType.Id"),
Join::left("Phone", "Contact.Id", "Phone.ContactId"),
Join::left("PhoneType", "Phone.PhoneTypeId", "PhoneType.Id"),
];
let where_clause = Where::new(Operator::and(
Operator::equals("Contact.Email", "ada@ex.com"),
Operator::greater_than("Address.City", "A"),
));
let having = Where::new(Operator::equals("ContactType.Name", "person"));
let order = [
OrderBy::asc("Contact.LastName"),
OrderBy::desc("Contact.FirstName"),
];
let select = SelectStatement::new(SelectStatementOptions {
distinct: true,
where_clause: Some(&where_clause),
joins: &joins,
group_by: &["Contact.LastName"],
having: Some(&having),
order_by: &order,
limit: Some(20),
offset: Some(1),
..SelectStatementOptions::new("Contact", &fields)
});
let mapped = mappings.apply_select(&select, &mapper).unwrap();
assert_eq!(
stmt_text(&mapped),
"SELECT DISTINCT contacts_base.first_name, contacts_base.last_name, contacts_base.email, contact_source.name, address.line1, address.city, phone.number FROM contacts_base INNER JOIN contact_source ON contacts_base.contact_source_id = contact_source.id INNER JOIN contact_type ON contacts_base.contact_type_id = contact_type.id LEFT JOIN address ON contacts_base.id = address.contact_id LEFT JOIN address_type ON address.address_type_id = address_type.id LEFT JOIN phone ON contacts_base.id = phone.contact_id LEFT JOIN phone_type ON phone.phone_type_id = phone_type.id WHERE contacts_base.email = $contacts_base.email AND address.city > $address.city GROUP BY contacts_base.last_name HAVING contact_type.name = $contact_type.name ORDER BY contacts_base.last_name ASC, contacts_base.first_name DESC LIMIT 20 OFFSET 1"
);
let parameters = mapped.predicate().unwrap().parameters();
assert_eq!(
parameters[0].value,
Value::Text("Contact.Email:ADA@EX.COM".into())
);
assert_eq!(parameters[1].value, Value::Text("Address.City:A".into()));
}