use query::Query;
use dao::Dao;
use dao::Value;
use query::SqlType;
use database::{Database};
use writer::SqlFrag;
use database::SqlOption;
use config::DbConfig;
use rusqlite::SqliteConnection;
use rusqlite::types::ToSql;
use rusqlite::SqliteRow;
use std::path::Path;
use table::Table;
use database::DatabaseDDL;
pub struct Sqlite {
pub conn: Option<SqliteConnection>,
}
impl Sqlite{
pub fn new()->Self{
Sqlite{conn:None}
}
pub fn connect_with_url(url:&str)->Result<Self, String>{
let config = DbConfig::from_url(url);
let database:&str = &config.database;
let conn = match database{
":memory:" => {
println!("using in memory database");
SqliteConnection::open_in_memory()
},
_ => {
let path = Path::new(database);
println!("Using file path {:?}", path);
println!("config{:?}", config);
SqliteConnection::open(&path)
}
};
match conn{
Ok(conn) => {
let pg = Sqlite{conn: Some(conn)};
Ok(pg)
},
Err(e) => {
let error = format!("Unable to connect to database due to {}", e.message);
println!("{:?}",e);
Err(error)
}
}
}
fn from_rust_type_tosql<'a>(&self, types: &'a Vec<Value>)->Vec<&'a ToSql>{
let mut params:Vec<&ToSql> = vec![];
for t in types{
match t {
&Value::String(ref x) => {
params.push(x);
},
_ => panic!("not yet here {:?}", t),
};
}
params
}
fn from_sql_to_rust_type(&self, row: &SqliteRow, index:usize)->Value{
let value = row.get_opt(index as i32);
match value{
Ok(value) => Value::String(value),
Err(_) => Value::Null,
}
}
fn rust_type_to_dbtype(&self, rust_type: &str)->String{
let rust_type = match rust_type{
"bool" => {
"boolean".to_string()
},
"i8" => {
"integer".to_string()
},
"i16" => {
"integer".to_string()
},
"i32" => {
"integer".to_string()
},
"u32" => {
"integer".to_string()
},
"i64" => {
"integer".to_string()
},
"f32" => {
"real".to_string()
},
"f64" => {
"real".to_string()
},
"String" =>{
"text".to_string()
},
"Vec<u8>" =>{
"blob".to_string()
},
"Json" => {
"text".to_string()
},
"Uuid" => {
"text".to_string()
},
"NaiveDateTime" => {
"numeric".to_string()
},
"DateTime<UTC>" => {
"numeric".to_string()
},
"NaiveDate" => {
"numeric".to_string()
},
"NaiveTime" => {
"numeric".to_string()
},
"HashMap<String, Option<String>>" => {
"text".to_string()
},
_ => panic!("Unable to get the equivalent database data type for {}", rust_type),
};
rust_type
}
}
impl Database for Sqlite{
fn version(&mut self)->String{
panic!("not yet")
}
fn begin(&mut self){}
fn commit(&mut self){}
fn rollback(&mut self){}
fn is_transacted(&mut self)->bool{false}
fn is_closed(&mut self)->bool{false}
fn is_connected(&mut self)->bool{false}
fn close(&mut self){}
fn is_valid(&mut self)->bool{false}
fn reset(&mut self){}
fn sql_options(&mut self)->Vec<SqlOption>{
vec![
SqlOption::UsesNumberedParam, SqlOption::SupportsCTE,
]
}
fn insert(&mut self, query:&Query)->Dao{
let sql_frag = self.build_insert(query);
self.execute_sql_with_one_return(&sql_frag.sql, &sql_frag.params)
}
fn update(&mut self, query:&Query)->Dao{panic!("not yet")}
fn delete(&mut self, query:&Query)->Result<usize, String>{panic!("not yet");}
fn execute_sql_with_return(&mut self, sql:&str, params:&Vec<Value>)->Vec<Dao>{
panic!("unsupported!");
}
fn execute_sql_with_return_columns(&mut self, sql:&str, params:&Vec<Value>, return_columns:Vec<&str>)->Vec<Dao>{
println!("SQL: \n{}", sql);
println!("param: {:?}", params);
assert!(self.conn.is_some());
let mut stmt = self.conn.as_ref().unwrap().prepare(sql).unwrap();
let mut daos = vec![];
let param = self.from_rust_type_tosql(params);
let rows = stmt.query(¶m);
match rows{
Ok(rows) =>
for row in rows {
match row{
Ok(row) => {
let mut index = 0;
let mut dao = Dao::new();
for rc in &return_columns{
let rtype = self.from_sql_to_rust_type(&row, index);
dao.set_value(rc, rtype);
index += 1;
}
daos.push(dao);
}
Err(e) => {
println!("error! {}",e)
}
}
},
Err(e) => println!("Something is wrong")
}
daos
}
fn execute_sql_with_one_return(&mut self, sql:&str, params:&Vec<Value>)->Dao{
let dao = self.execute_sql_with_return(sql, params);
assert!(dao.len() == 1, "There should be 1 and only 1 record return here");
dao[0].clone()
}
fn execute_sql(&mut self, sql:&str, params:&Vec<Value>)->Result<usize, String>{
println!("SQL: \n{}", sql);
println!("param: {:?}", params);
let to_sql_types = self.from_rust_type_tosql(params);
assert!(self.conn.is_some());
let result = self.conn.as_ref().unwrap().execute(sql, &to_sql_types);
let result = match result{
Ok(x) => { Ok(x as usize)},
Err(e) => {
Err(format!("Something is wrong {:?}" ,e))
}
};
result
}
}
impl DatabaseDDL for Sqlite{
fn create_schema(&mut self, schema:&str){
panic!("sqlite does not support schema")
}
fn drop_schema(&mut self, schema:&str){
panic!("sqlite does not support schema")
}
fn build_create_table(&mut self, table:&Table)->SqlFrag{
let mut w = SqlFrag::new(self.sql_options());
w.append("CREATE TABLE ");
w.append(&table.name);
w.append("(");
w.ln_tab();
let mut do_comma = false;
for c in &table.columns{
if do_comma {w.commasp();}else {do_comma=true;}
w.append(&c.name);
w.append(" ");
let dt = self.rust_type_to_dbtype(&c.data_type);
w.append(&dt);
if c.is_primary {
w.append(" PRIMARY KEY ");
}
}
w.append(")");
w
}
fn create_table(&mut self, table:&Table){
let frag = self.build_create_table(table);
match self.execute_sql(&frag.sql, &vec![]){
Ok(x) => println!("created table.."),
Err(e) => panic!("table not created {}", e),
}
}
fn rename_table(&mut self, table:&Table, new_tablename:String){
}
fn drop_table(&mut self, table:&Table){
panic!("not yet");
}
fn set_foreign_constraint(&mut self, model:&Table){
panic!("not yet");
}
fn set_primary_constraint(&mut self, model:&Table){
panic!("not yet");
}
}