#[macro_use]
extern crate log;
extern crate sql_builder;
use serde_json::value::Value as JValue;
use sql_builder::{esc as SqlBuilderEsc, quote as SqlBuilderQuote, SqlBuilder};
use sqlite3::Cursor;
use sqlite3::Value as SValue;
use std::error::Error;
type ConnPooled = r2d2::PooledConnection<r2d2_sqlite3::SqliteConnectionManager>;
pub struct Sqlite3Builder {
builder: SqlBuilder,
}
impl Sqlite3Builder {
pub fn select_from<S: ToString>(table: S) -> Self {
Self {
builder: SqlBuilder::select_from(table),
}
}
pub fn select_values<S: ToString>(values: &[S]) -> Self {
Self {
builder: SqlBuilder::select_values(values),
}
}
pub fn insert_into<S: ToString>(table: S) -> Self {
Self {
builder: SqlBuilder::insert_into(table),
}
}
pub fn update_table<S: ToString>(table: S) -> Self {
Self {
builder: SqlBuilder::update_table(table),
}
}
pub fn delete_from<S: ToString>(table: S) -> Self {
Self {
builder: SqlBuilder::delete_from(table),
}
}
pub fn natural(&mut self) -> &mut Self {
self.builder.natural();
self
}
pub fn left(&mut self) -> &mut Self {
self.builder.left();
self
}
pub fn left_outer(&mut self) -> &mut Self {
self.builder.left_outer();
self
}
pub fn right(&mut self) -> &mut Self {
self.builder.right();
self
}
pub fn inner(&mut self) -> &mut Self {
self.builder.inner();
self
}
pub fn cross(&mut self) -> &mut Self {
self.builder.cross();
self
}
pub fn join<S: ToString>(&mut self, table: S) -> &mut Self {
self.builder.join(table);
self
}
pub fn on<S: ToString>(&mut self, constraint: S) -> &mut Self {
self.builder.on(constraint);
self
}
pub fn distinct(&mut self) -> &mut Self {
self.builder.distinct();
self
}
pub fn fields<S: ToString>(&mut self, fields: &[S]) -> &mut Self {
self.builder.fields(fields);
self
}
pub fn set_fields<S: ToString>(&mut self, fields: &[S]) -> &mut Self {
self.builder.set_fields(fields);
self
}
pub fn field<S: ToString>(&mut self, field: S) -> &mut Self {
self.builder.field(field);
self
}
pub fn set_field<S: ToString>(&mut self, field: S) -> &mut Self {
self.builder.set_field(field);
self
}
pub fn set<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.set(field, value);
self
}
pub fn set_str<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.set_str(field, value);
self
}
pub fn values<S: ToString>(&mut self, values: &[S]) -> &mut Self {
self.builder.values(values);
self
}
pub fn select<S: ToString>(&mut self, query: S) -> &mut Self {
self.builder.select(query);
self
}
pub fn group_by<S: ToString>(&mut self, field: S) -> &mut Self {
self.builder.group_by(field);
self
}
pub fn having<S: ToString>(&mut self, cond: S) -> &mut Self {
self.builder.having(cond);
self
}
pub fn and_where<S: ToString>(&mut self, cond: S) -> &mut Self {
self.builder.and_where(cond);
self
}
pub fn and_where_eq<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_eq(field, value);
self
}
pub fn and_where_ne<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_ne(field, value);
self
}
pub fn and_where_gt<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_gt(field, value);
self
}
pub fn and_where_ge<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_ge(field, value);
self
}
pub fn and_where_lt<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_lt(field, value);
self
}
pub fn and_where_le<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_le(field, value);
self
}
pub fn and_where_like<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_like(field, mask);
self
}
pub fn and_where_like_right<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_like_right(field, mask);
self
}
pub fn and_where_like_left<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_like_left(field, mask);
self
}
pub fn and_where_like_any<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_like_any(field, mask);
self
}
pub fn and_where_not_like<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_not_like(field, mask);
self
}
pub fn and_where_not_like_right<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_not_like_right(field, mask);
self
}
pub fn and_where_not_like_left<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_not_like_left(field, mask);
self
}
pub fn and_where_not_like_any<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.and_where_not_like_any(field, mask);
self
}
pub fn and_where_is_null<S: ToString>(&mut self, field: S) -> &mut Self {
self.builder.and_where_is_null(field);
self
}
pub fn and_where_is_not_null<S: ToString>(&mut self, field: S) -> &mut Self {
self.builder.and_where_is_not_null(field);
self
}
pub fn or_where<S: ToString>(&mut self, cond: S) -> &mut Self {
self.builder.or_where(cond);
self
}
pub fn or_where_eq<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_eq(field, value);
self
}
pub fn or_where_ne<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_ne(field, value);
self
}
pub fn or_where_gt<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_gt(field, value);
self
}
pub fn or_where_ge<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_ge(field, value);
self
}
pub fn or_where_lt<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_lt(field, value);
self
}
pub fn or_where_le<S, T>(&mut self, field: S, value: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_le(field, value);
self
}
pub fn or_where_like<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_like(field, mask);
self
}
pub fn or_where_like_right<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_like_right(field, mask);
self
}
pub fn or_where_like_left<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_like_left(field, mask);
self
}
pub fn or_where_like_any<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_like_any(field, mask);
self
}
pub fn or_where_not_like<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_not_like(field, mask);
self
}
pub fn or_where_not_like_right<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_not_like_right(field, mask);
self
}
pub fn or_where_not_like_left<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_not_like_left(field, mask);
self
}
pub fn or_where_not_like_any<S, T>(&mut self, field: S, mask: T) -> &mut Self
where
S: ToString,
T: ToString,
{
self.builder.or_where_not_like_any(field, mask);
self
}
pub fn or_where_is_null<S: ToString>(&mut self, field: S) -> &mut Self {
self.builder.or_where_is_null(field);
self
}
pub fn or_where_is_not_null<S: ToString>(&mut self, field: S) -> &mut Self {
self.builder.or_where_is_not_null(field);
self
}
pub fn union<S: ToString>(&mut self, query: S) -> &mut Self {
self.builder.union(query);
self
}
pub fn union_all<S: ToString>(&mut self, query: S) -> &mut Self {
self.builder.union_all(query);
self
}
pub fn order_by<S: ToString>(&mut self, field: S, desc: bool) -> &mut Self {
self.builder.order_by(field, desc);
self
}
pub fn order_asc<S: ToString>(&mut self, field: S) -> &mut Self {
self.builder.order_asc(field);
self
}
pub fn order_desc<S: ToString>(&mut self, field: S) -> &mut Self {
self.builder.order_desc(field);
self
}
pub fn limit<S: ToString>(&mut self, limit: S) -> &mut Self {
self.builder.limit(limit);
self
}
pub fn offset<S: ToString>(&mut self, offset: S) -> &mut Self {
self.builder.offset(offset);
self
}
pub fn sql(&self) -> Result<String, Box<dyn Error>> {
self.builder.sql()
}
pub fn subquery(&self) -> Result<String, Box<dyn Error>> {
self.builder.subquery()
}
pub fn subquery_as<S: ToString>(&self, name: S) -> Result<String, Box<dyn Error>> {
self.builder.subquery_as(name)
}
pub fn query(&self) -> Result<String, Box<dyn Error>> {
self.builder.query()
}
pub fn query_values(&self) -> Result<String, Box<dyn Error>> {
self.builder.query_values()
}
fn s2j(src: &SValue) -> Result<JValue, Box<dyn Error>> {
match src {
SValue::Null => Ok(JValue::Null),
SValue::Integer(val) => Ok(JValue::Number((*val).into())),
SValue::String(val) => Ok(JValue::String(val.clone())),
_ => Err("Unsupported type".into()),
}
}
pub fn exec(&self, conn: &ConnPooled) -> Result<(), Box<dyn Error>> {
let sql = self.builder.sql()?;
debug!("Exec sql = {}", &sql);
conn.execute(sql).map_err(|err| err.into())
}
pub fn get(&self, conn: &ConnPooled) -> Result<Vec<Vec<JValue>>, Box<dyn Error>> {
let sql = self.builder.sql()?;
debug!("Get rows sql = {}", &sql);
let mut result = Vec::new();
let mut cursor = conn.prepare(sql)?.cursor();
while let Some(row) = cursor.next()? {
let jrow = row
.iter()
.map(|val| Self::s2j(&val).unwrap())
.collect::<Vec<JValue>>();
result.push(jrow);
}
Ok(result)
}
pub fn get_row(&self, conn: &ConnPooled) -> Result<Vec<JValue>, Box<dyn Error>> {
let sql = self.builder.sql()?;
debug!("Get row sql = {}", &sql);
let mut cursor = conn.prepare(sql)?.cursor();
let first_row = if let Some(row) = cursor.next()? {
row.iter()
.map(|val| Self::s2j(&val).unwrap())
.collect::<Vec<JValue>>()
} else {
Vec::new()
};
Ok(first_row)
}
pub fn get_value(&self, conn: &ConnPooled) -> Result<JValue, Box<dyn Error>> {
let sql = self.builder.sql()?;
debug!("Get value sql = {}", &sql);
let mut cursor = conn.prepare(sql)?.cursor();
let first_value = if let Some(row) = cursor.next()? {
Self::s2j(&row[0])?
} else {
return Err("No any value".into());
};
Ok(first_value)
}
pub fn get_int(&self, conn: &ConnPooled) -> Result<i64, Box<dyn Error>> {
Ok(self.get_value(&conn)?.as_i64().unwrap())
}
pub fn get_str(&self, conn: &ConnPooled) -> Result<String, Box<dyn Error>> {
Ok(self.get_value(&conn)?.as_str().unwrap().to_string())
}
pub fn get_cursor<'a>(&'a self, conn: &'a ConnPooled) -> Result<Cursor<'a>, Box<dyn Error>> {
let sql = self.builder.sql()?;
debug!("Get cursor sql = {}", &sql);
let cursor = conn.prepare(sql)?.cursor();
Ok(cursor)
}
}
pub fn esc(src: &str) -> String {
SqlBuilderEsc(src)
}
pub fn quote(src: &str) -> String {
SqlBuilderQuote(src)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_esc() -> Result<(), Box<dyn Error>> {
let sql = esc("Hello, 'World'");
assert_eq!(&sql, "Hello, ''World''");
Ok(())
}
#[test]
fn test_quote() -> Result<(), Box<dyn Error>> {
let sql = quote("Hello, 'World'");
assert_eq!(&sql, "'Hello, ''World'''");
Ok(())
}
#[test]
fn test_select_only_values() -> Result<(), Box<dyn Error>> {
let values = Sqlite3Builder::select_values(&["10", "e("100")]).sql()?;
assert_eq!("SELECT 10, '100';", &values);
Ok(())
}
#[test]
fn test_select_all_books() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books").sql()?;
assert_eq!(&sql, "SELECT * FROM books;");
Ok(())
}
#[test]
fn test_show_all_prices() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books")
.distinct()
.field("price")
.sql()?;
assert_eq!(&sql, "SELECT DISTINCT price FROM books;");
Ok(())
}
#[test]
fn test_select_title_and_price() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books")
.fields(&["title", "price"])
.sql()?;
assert_eq!(&sql, "SELECT title, price FROM books;");
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.sql()?;
assert_eq!(&sql, "SELECT title, price FROM books;");
Ok(())
}
#[test]
fn test_select_expensive_books() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where("price > 100")
.sql()?;
assert_eq!(&sql, "SELECT title, price FROM books WHERE price > 100;");
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where_gt("price", 200.to_string())
.sql()?;
assert_eq!(&sql, "SELECT title, price FROM books WHERE price > 200;");
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where_ge("price", 300.to_string())
.sql()?;
assert_eq!(&sql, "SELECT title, price FROM books WHERE price >= 300;");
Ok(())
}
#[test]
fn test_select_price_for_harry_potter_and_phil_stone() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books")
.field("price")
.and_where_eq("title", quote("Harry Potter and the Philosopher's Stone"))
.sql()?;
assert_eq!(
&sql,
"SELECT price FROM books WHERE title = 'Harry Potter and the Philosopher''s Stone';"
);
Ok(())
}
#[test]
fn test_select_price_not_for_harry_potter_and_phil_stone() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books")
.field("price")
.and_where_ne("title", quote("Harry Potter and the Philosopher's Stone"))
.sql()?;
assert_eq!(
&sql,
"SELECT price FROM books WHERE title <> 'Harry Potter and the Philosopher''s Stone';"
);
Ok(())
}
#[test]
fn test_select_expensive_harry_potter() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where("price > 100")
.and_where_like_left("title", "Harry Potter")
.sql()?;
assert_eq!(
&sql,
"SELECT title, price FROM books WHERE (price > 100) AND (title LIKE 'Harry Potter%');"
);
Ok(())
}
#[test]
fn test_select_strange_books() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where("price < 2")
.or_where("price > 1000")
.or_where_eq("title", quote("Harry Potter and the Philosopher's Stone"))
.or_where_ne("price", 100)
.or_where_like("title", "Alice's")
.or_where_not_like_any("LOWER(title)", " the ")
.or_where_is_null("title")
.or_where_is_not_null("price")
.sql()?;
assert_eq!(
&sql,
"SELECT title, price FROM books WHERE price < 2 OR price > 1000 OR title = 'Harry Potter and the Philosopher''s Stone' OR price <> 100 OR title LIKE 'Alice''s' OR LOWER(title) NOT LIKE '% the %' OR title IS NULL OR price IS NOT NULL;"
);
Ok(())
}
#[test]
fn test_order_harry_potter_by_price() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where_like_left("title", "Harry Potter")
.order_by("price", false)
.sql()?;
assert_eq!(
&sql,
"SELECT title, price FROM books WHERE title LIKE 'Harry Potter%' ORDER BY price;"
);
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where_like_left("title", "Harry Potter")
.order_desc("price")
.sql()?;
assert_eq!(
&sql,
"SELECT title, price FROM books WHERE title LIKE 'Harry Potter%' ORDER BY price DESC;"
);
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where_like_left("title", "Harry Potter")
.order_desc("price")
.order_asc("title")
.sql()?;
assert_eq!(&sql, "SELECT title, price FROM books WHERE title LIKE 'Harry Potter%' ORDER BY price DESC, title;");
Ok(())
}
#[test]
fn test_find_cheap_or_harry_potter() -> Result<(), Box<dyn Error>> {
let append = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where("price < 100")
.order_asc("title")
.query()?;
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where_like_left("title", "Harry Potter")
.order_desc("price")
.union(&append)
.sql()?;
assert_eq!(
"SELECT title, price FROM books WHERE title LIKE 'Harry Potter%' UNION SELECT title, price FROM books WHERE price < 100 ORDER BY title;",
&sql
);
let append =
Sqlite3Builder::select_values(&["'The Great Gatsby'", "124"]).query_values()?;
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where_like_left("title", "Harry Potter")
.order_desc("price")
.union_all(&append)
.sql()?;
assert_eq!(
"SELECT title, price FROM books WHERE title LIKE 'Harry Potter%' UNION ALL SELECT 'The Great Gatsby', 124;",
&sql
);
Ok(())
}
#[test]
fn test_select_first_3_harry_potter_books() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where_like_left("title", "Harry Potter")
.order_asc("title")
.limit(3)
.sql()?;
assert_eq!(&sql, "SELECT title, price FROM books WHERE title LIKE 'Harry Potter%' ORDER BY title LIMIT 3;");
Ok(())
}
#[test]
fn test_select_harry_potter_from_second_book() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where_like_left("title", "Harry Potter")
.order_asc("title")
.offset(2)
.sql()?;
assert_eq!(&sql, "SELECT title, price FROM books WHERE title LIKE 'Harry Potter%' ORDER BY title OFFSET 2;");
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.and_where_like_left("title", "Harry Potter")
.order_asc("title")
.limit(3)
.offset(2)
.sql()?;
assert_eq!(&sql, "SELECT title, price FROM books WHERE title LIKE 'Harry Potter%' ORDER BY title LIMIT 3 OFFSET 2;");
Ok(())
}
#[test]
fn test_find_books_not_about_alice() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books")
.field("title")
.and_where_not_like_any("title", "Alice's")
.sql()?;
assert_eq!(
"SELECT title FROM books WHERE title NOT LIKE '%Alice''s%';",
&sql
);
Ok(())
}
#[test]
fn test_books_without_price() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books")
.field("title")
.and_where_is_null("price")
.sql()?;
assert_eq!(&sql, "SELECT title FROM books WHERE price IS NULL;");
let sql = Sqlite3Builder::select_from("books")
.field("title")
.and_where_is_not_null("price")
.sql()?;
assert_eq!(&sql, "SELECT title FROM books WHERE price IS NOT NULL;");
Ok(())
}
#[test]
fn test_group_books_by_price() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books")
.field("price")
.field("COUNT(price) AS cnt")
.group_by("price")
.order_desc("cnt")
.sql()?;
assert_eq!(
&sql,
"SELECT price, COUNT(price) AS cnt FROM books GROUP BY price ORDER BY cnt DESC;"
);
let sql = Sqlite3Builder::select_from("books")
.field("price")
.field("COUNT(price) AS cnt")
.group_by("price")
.having("price > 100")
.order_desc("cnt")
.sql()?;
assert_eq!(&sql, "SELECT price, COUNT(price) AS cnt FROM books GROUP BY price HAVING price > 100 ORDER BY cnt DESC;");
Ok(())
}
#[test]
fn test_group_books_by_price_category() -> Result<(), Box<dyn Error>> {
let cat = Sqlite3Builder::select_from("books")
.field("CASE WHEN price < 100 THEN 'cheap' ELSE 'expensive' END AS category")
.subquery()?;
assert_eq!("(SELECT CASE WHEN price < 100 THEN 'cheap' ELSE 'expensive' END AS category FROM books)", &cat);
let sql = Sqlite3Builder::select_from(&cat)
.field("category")
.field("COUNT(category) AS cnt")
.group_by("category")
.order_desc("cnt")
.order_asc("category")
.sql()?;
assert_eq!("SELECT category, COUNT(category) AS cnt FROM (SELECT CASE WHEN price < 100 THEN 'cheap' ELSE 'expensive' END AS category FROM books) GROUP BY category ORDER BY cnt DESC, category;", &sql);
let cat = Sqlite3Builder::select_from("books")
.field("CASE WHEN price < 100 THEN 'cheap' ELSE 'expensive' END")
.subquery_as("category")?;
assert_eq!("(SELECT CASE WHEN price < 100 THEN 'cheap' ELSE 'expensive' END FROM books) AS category", &cat);
let sql = Sqlite3Builder::select_from("books")
.field("title")
.field("price")
.field(&cat)
.sql()?;
assert_eq!("SELECT title, price, (SELECT CASE WHEN price < 100 THEN 'cheap' ELSE 'expensive' END FROM books) AS category FROM books;", &sql);
Ok(())
}
#[test]
fn test_grow_price() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::update_table("books")
.set("price", "price + 10")
.sql()?;
assert_eq!(&sql, "UPDATE books SET price = price + 10;");
let sql = Sqlite3Builder::update_table("books")
.set("price", "price * 0.1")
.and_where_like_left("title", "Harry Potter")
.sql()?;
assert_eq!(
&sql,
"UPDATE books SET price = price * 0.1 WHERE title LIKE 'Harry Potter%';"
);
Ok(())
}
#[test]
fn test_add_new_books() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::insert_into("books")
.field("title")
.field("price")
.values(&[quote("In Search of Lost Time"), 150.to_string()])
.values(&["'Don Quixote', 200"])
.sql()?;
assert_eq!(&sql, "INSERT INTO books (title, price) VALUES ('In Search of Lost Time', 150), ('Don Quixote', 200);");
Ok(())
}
#[test]
fn test_add_books_from_warehouse() -> Result<(), Box<dyn Error>> {
let query = Sqlite3Builder::select_from("warehouse")
.field("title")
.field("preliminary_price * 2")
.query()?;
assert_eq!("SELECT title, preliminary_price * 2 FROM warehouse", &query);
let sql = Sqlite3Builder::insert_into("books")
.field("title")
.field("price")
.select(&query)
.sql()?;
assert_eq!(
"INSERT INTO books (title, price) SELECT title, preliminary_price * 2 FROM warehouse;",
&sql
);
Ok(())
}
#[test]
fn test_sold_all_harry_potter() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::update_table("books")
.set("price", 0)
.set("title", "'[SOLD!]' || title")
.and_where_like_left("title", "Harry Potter")
.sql()?;
assert_eq!(&sql, "UPDATE books SET price = 0, title = '[SOLD!]' || title WHERE title LIKE 'Harry Potter%';");
Ok(())
}
#[test]
fn test_mark_as_not_distr() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::update_table("books")
.set_str("comment", "Don't distribute!")
.and_where_le("price", "100")
.sql()?;
assert_eq!(
"UPDATE books SET comment = 'Don''t distribute!' WHERE price <= 100;",
&sql
);
Ok(())
}
#[test]
fn test_remove_all_expensive_books() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::delete_from("books")
.and_where("price > 100")
.sql()?;
assert_eq!(&sql, "DELETE FROM books WHERE price > 100;");
Ok(())
}
#[test]
fn test_count_books_in_shops() -> Result<(), Box<dyn Error>> {
let sql = Sqlite3Builder::select_from("books AS b")
.field("b.title")
.field("s.total")
.left_outer()
.join("shops AS s")
.on("b.id = s.book")
.sql()?;
assert_eq!(
&sql,
"SELECT b.title, s.total FROM books AS b LEFT OUTER JOIN shops AS s ON b.id = s.book;"
);
Ok(())
}
}