|
@@ -3,100 +3,219 @@ extern crate r2d2; |
|
|
extern crate r2d2_sqlite; |
|
|
extern crate r2d2_sqlite; |
|
|
extern crate rusqlite; |
|
|
extern crate rusqlite; |
|
|
extern crate simple_error; |
|
|
extern crate simple_error; |
|
|
|
|
|
extern crate rpassword; |
|
|
|
|
|
|
|
|
use clap::{App, SubCommand}; |
|
|
|
|
|
|
|
|
//use std::fmt; |
|
|
|
|
|
use clap::{App, AppSettings, SubCommand, Arg}; |
|
|
|
|
|
use chrono::prelude::Utc; |
|
|
use r2d2::Pool; |
|
|
use r2d2::Pool; |
|
|
use r2d2_sqlite::SqliteConnectionManager; |
|
|
|
|
|
use rusqlite::params; |
|
|
use rusqlite::params; |
|
|
|
|
|
use postgres::{NoTls, Config}; |
|
|
|
|
|
use r2d2_sqlite::SqliteConnectionManager; |
|
|
|
|
|
use r2d2_postgres::PostgresConnectionManager; |
|
|
use serde_json::{Map, Value}; |
|
|
use serde_json::{Map, Value}; |
|
|
use serde_rusqlite::from_row; |
|
|
|
|
|
use serde_rusqlite::Result as SRResult; |
|
|
|
|
|
|
|
|
use serde_rusqlite::from_row as from_row_s; |
|
|
|
|
|
use serde_rusqlite::Result as SRSResult; |
|
|
|
|
|
use serde::Deserialize; |
|
|
|
|
|
use std::error::Error; |
|
|
|
|
|
// use serde_postgres::from_row as from_row_p; |
|
|
|
|
|
// use serde_postgres::DeResult as SRPResult; |
|
|
use simple_error::*; |
|
|
use simple_error::*; |
|
|
|
|
|
use std::str::FromStr; |
|
|
|
|
|
|
|
|
fn main() -> Result<(), SimpleError> { |
|
|
|
|
|
|
|
|
fn main() -> Result<(), Box<dyn Error>> { |
|
|
let matches = App::new("Rust DB Play") |
|
|
let matches = App::new("Rust DB Play") |
|
|
.version("1.0") |
|
|
.version("1.0") |
|
|
.author("Jonathan Altman <jonathan@async.io>") |
|
|
.author("Jonathan Altman <jonathan@async.io>") |
|
|
.about("Test out Rust database result serialization") |
|
|
.about("Test out Rust database result serialization") |
|
|
|
|
|
.setting(AppSettings::SubcommandRequiredElseHelp) |
|
|
.subcommand(SubCommand::with_name("sqlite").about("Test on sqlite")) |
|
|
.subcommand(SubCommand::with_name("sqlite").about("Test on sqlite")) |
|
|
.subcommand(SubCommand::with_name("postgres").about("Test on postgres")) |
|
|
|
|
|
|
|
|
.subcommand(SubCommand::with_name("postgres").about("Test on postgres").arg( |
|
|
|
|
|
Arg::with_name("dsn") // And their own arguments |
|
|
|
|
|
.help("postgres dsn to connect to (password will be prompted for, and entered into DSN where {password} is") |
|
|
|
|
|
.index(1) |
|
|
|
|
|
.required(true), |
|
|
|
|
|
),) |
|
|
.get_matches(); |
|
|
.get_matches(); |
|
|
|
|
|
|
|
|
return match matches.subcommand_name() { |
|
|
return match matches.subcommand_name() { |
|
|
Some("sqlite") => test_sqlite(), |
|
|
Some("sqlite") => test_sqlite(), |
|
|
Some("postgres") => test_postgres(), |
|
|
|
|
|
|
|
|
Some("postgres") => { |
|
|
|
|
|
let sub_matches = matches.subcommand_matches("postgres").unwrap(); |
|
|
|
|
|
let dsn_arg = sub_matches.value_of("dsn").unwrap(); |
|
|
|
|
|
test_postgres(dsn_arg) |
|
|
|
|
|
}, |
|
|
None => bail!("No database specified"), |
|
|
None => bail!("No database specified"), |
|
|
_ => bail!("Unknown database specified"), |
|
|
_ => bail!("Unknown database specified"), |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fn test_postgres() -> Result<(), SimpleError> { |
|
|
|
|
|
println!("Postgres: Not implemented yet"); |
|
|
|
|
|
return Ok(()); |
|
|
|
|
|
|
|
|
fn test_postgres(dsn_template: &str) -> Result<(), Box<dyn Error>> { |
|
|
|
|
|
let dsn = match dsn_template.contains("{password}") { |
|
|
|
|
|
true => { |
|
|
|
|
|
let pass = rpassword::read_password_from_tty(Some("DB Password: ")).unwrap(); |
|
|
|
|
|
dsn_template.replace("{password}", &pass) |
|
|
|
|
|
}, |
|
|
|
|
|
false => dsn_template.to_string() |
|
|
|
|
|
}; |
|
|
|
|
|
let pg_config = Config::from_str(&dsn).unwrap(); |
|
|
|
|
|
let (pool, table_name) = setup_postgres(pg_config)?; |
|
|
|
|
|
test_postgres_json(pool, &table_name) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fn test_sqlite() -> Result<(), SimpleError> { |
|
|
|
|
|
let (pool, table_name) = setup_sqlite().unwrap(); |
|
|
|
|
|
return match test_sqlite_json(pool, &table_name) { |
|
|
|
|
|
Ok(()) => Ok(()), |
|
|
|
|
|
// @TODO (2019-11-19, Jonathan) Extract sqlite error here |
|
|
|
|
|
Err(_) => bail!("Error in testing sqlite"), |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
fn setup_postgres(cfg: Config) -> Result<(Pool<PostgresConnectionManager<NoTls>>, String), SimpleError> { |
|
|
|
|
|
fn setup_pool(cfg: Config) -> Pool<PostgresConnectionManager<NoTls>> { |
|
|
|
|
|
let manager = PostgresConnectionManager::new( |
|
|
|
|
|
cfg, |
|
|
|
|
|
NoTls |
|
|
|
|
|
); |
|
|
|
|
|
let pool = Pool::new(manager).unwrap(); |
|
|
|
|
|
pool |
|
|
|
|
|
} |
|
|
|
|
|
fn setup_db_structure(pool: &Pool<PostgresConnectionManager<NoTls>>, table_name: &str) -> Result<u64, postgres::Error> { |
|
|
|
|
|
let mut conn = pool.get().unwrap(); |
|
|
|
|
|
|
|
|
fn setup_sqlite() -> SRResult<(Pool<SqliteConnectionManager>, String)> { |
|
|
|
|
|
fn setup_pool() -> Pool<SqliteConnectionManager> { |
|
|
|
|
|
let manager = SqliteConnectionManager::memory(); |
|
|
|
|
|
let pool = Pool::new(manager).unwrap(); |
|
|
|
|
|
pool |
|
|
|
|
|
} |
|
|
|
|
|
fn setup_db_structure(pool: &Pool<SqliteConnectionManager>, table_name: &str) -> SRResult<()> { |
|
|
|
|
|
let conn = pool.get().unwrap(); |
|
|
|
|
|
conn.execute( |
|
|
conn.execute( |
|
|
format!( |
|
|
|
|
|
"CREATE TABLE {} ( |
|
|
|
|
|
id INTEGER PRIMARY KEY, |
|
|
|
|
|
name TEXT NOT NULL, |
|
|
|
|
|
time_created TEXT NOT NULL, |
|
|
|
|
|
data BLOB, |
|
|
|
|
|
data_null BLOB |
|
|
|
|
|
)", |
|
|
|
|
|
table_name |
|
|
|
|
|
) |
|
|
|
|
|
.as_str(), |
|
|
|
|
|
params![], |
|
|
|
|
|
|
|
|
format!("DROP TABLE IF EXISTS {0}", table_name).as_str(), |
|
|
|
|
|
&[], |
|
|
)?; |
|
|
)?; |
|
|
|
|
|
|
|
|
conn.execute( |
|
|
|
|
|
"INSERT INTO person (name, time_created, data, data_null) |
|
|
|
|
|
VALUES (?1, ?2, ?3, ?4)", |
|
|
|
|
|
params![ |
|
|
|
|
|
"Steven".to_string(), |
|
|
|
|
|
time::get_time(), |
|
|
|
|
|
"This is some text".to_string(), |
|
|
|
|
|
None as Option<Vec<u8>> |
|
|
|
|
|
|
|
|
conn.execute( |
|
|
|
|
|
format!( |
|
|
|
|
|
"CREATE TABLE {0} ( |
|
|
|
|
|
id SERIAL PRIMARY KEY, |
|
|
|
|
|
name TEXT NOT NULL, |
|
|
|
|
|
time_created TIMESTAMP WITH TIME ZONE NOT NULL, |
|
|
|
|
|
data BYTEA, |
|
|
|
|
|
data_null BYTEA |
|
|
|
|
|
)", |
|
|
|
|
|
table_name |
|
|
|
|
|
) |
|
|
|
|
|
.as_str(), |
|
|
|
|
|
&[], |
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
|
|
return conn.execute( |
|
|
|
|
|
"INSERT INTO test_table (name, time_created, data, data_null) |
|
|
|
|
|
VALUES ($1, $2, $3, $4)" |
|
|
|
|
|
, |
|
|
|
|
|
&[ |
|
|
|
|
|
&"Steven".to_string(), |
|
|
|
|
|
&Utc::now(), |
|
|
|
|
|
&"this is a string".as_bytes(), |
|
|
|
|
|
&None::<&[u8]>, |
|
|
], |
|
|
], |
|
|
)?; |
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const TABLE_NAME: &str = "test_table"; |
|
|
|
|
|
let pool = setup_pool(cfg); |
|
|
|
|
|
if let Err(e) = setup_db_structure(&pool, &TABLE_NAME) { |
|
|
|
|
|
bail!("{}", e.to_string()) |
|
|
} |
|
|
} |
|
|
|
|
|
println!("Done setting up db and checking error"); |
|
|
|
|
|
|
|
|
const TABLE_NAME: &str = "person"; |
|
|
|
|
|
let pool = setup_pool(); |
|
|
|
|
|
setup_db_structure(&pool, TABLE_NAME)?; |
|
|
|
|
|
Ok((pool, TABLE_NAME.to_string())) |
|
|
|
|
|
|
|
|
return Ok((pool, TABLE_NAME.to_string())); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fn test_sqlite_json(pool: Pool<SqliteConnectionManager>, table_name: &String) -> SRResult<()> { |
|
|
|
|
|
let conn = pool.get().unwrap(); |
|
|
|
|
|
let mut stmt = conn.prepare(&format!("SELECT * FROM {}", table_name))?; |
|
|
|
|
|
let result_iter = stmt.query_map(params![], |row| { |
|
|
|
|
|
Ok(from_row::<Map<String, Value>>(row).unwrap()) |
|
|
|
|
|
})?; |
|
|
|
|
|
for cur_result in result_iter { |
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Deserialize)] |
|
|
|
|
|
struct Person { |
|
|
|
|
|
name: String, |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn test_postgres_json(pool: Pool<PostgresConnectionManager<NoTls>>, table_name: &String) -> Result<(), Box<dyn Error>> { |
|
|
|
|
|
let mut conn = pool.get().unwrap(); |
|
|
|
|
|
let stmt = conn.prepare(&format!("SELECT name FROM {}", table_name)).unwrap(); |
|
|
|
|
|
let rows: Vec<postgres::Row> = conn.query(&stmt, &[])?; |
|
|
|
|
|
for row in rows { |
|
|
|
|
|
println!("Dumping a row..."); |
|
|
|
|
|
let person: Person = serde_postgres::from_row(&row)?; |
|
|
|
|
|
dbg!(person); |
|
|
|
|
|
let cur_result: Map<String, Value> = serde_postgres::from_row(&row)?; |
|
|
println!( |
|
|
println!( |
|
|
"Found row {:?}", |
|
|
|
|
|
serde_json::to_string(&cur_result.unwrap()).unwrap() |
|
|
|
|
|
|
|
|
"Found row macro {:?}", |
|
|
|
|
|
serde_json::json!(&cur_result).to_string() |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
/* |
|
|
|
|
|
let cur_result: Vec<Map<String, Value>> = serde_postgres::from_rows(&rows)?; |
|
|
|
|
|
println!( |
|
|
|
|
|
"Found row macro {:?}", |
|
|
|
|
|
serde_json::json!(&cur_result).to_string() |
|
|
|
|
|
); |
|
|
|
|
|
*/ |
|
|
Ok(()) |
|
|
Ok(()) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn test_sqlite() -> Result<(), Box<dyn Error>> { |
|
|
|
|
|
let (pool, table_name) = setup_sqlite().unwrap(); |
|
|
|
|
|
return match test_sqlite_json(pool, &table_name) { |
|
|
|
|
|
Ok(()) => Ok(()), |
|
|
|
|
|
// @TODO (2019-11-19, Jonathan) Extract sqlite error here |
|
|
|
|
|
Err(_) => bail!("Error in testing sqlite"), |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn setup_sqlite() -> SRSResult<(Pool<SqliteConnectionManager>, String)> { |
|
|
|
|
|
fn setup_pool() -> Pool<SqliteConnectionManager> { |
|
|
|
|
|
let manager = SqliteConnectionManager::memory(); |
|
|
|
|
|
let pool = Pool::new(manager).unwrap(); |
|
|
|
|
|
pool |
|
|
|
|
|
} |
|
|
|
|
|
fn setup_db_structure(pool: &Pool<SqliteConnectionManager>, table_name: &str) -> SRSResult<()> { |
|
|
|
|
|
let conn = pool.get().unwrap(); |
|
|
|
|
|
conn.execute( |
|
|
|
|
|
format!( |
|
|
|
|
|
"CREATE TABLE {} ( |
|
|
|
|
|
id INTEGER PRIMARY KEY, |
|
|
|
|
|
name TEXT NOT NULL, |
|
|
|
|
|
time_created TEXT NOT NULL, |
|
|
|
|
|
data BLOB, |
|
|
|
|
|
data_null BLOB |
|
|
|
|
|
)", |
|
|
|
|
|
table_name |
|
|
|
|
|
) |
|
|
|
|
|
.as_str(), |
|
|
|
|
|
rusqlite::params![], |
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
|
|
conn.execute( |
|
|
|
|
|
"INSERT INTO person (name, time_created, data, data_null) |
|
|
|
|
|
VALUES (?1, ?2, ?3, ?4)", |
|
|
|
|
|
rusqlite::params![ |
|
|
|
|
|
"Steven".to_string(), |
|
|
|
|
|
&Utc::now(), |
|
|
|
|
|
"This is some text".to_string(), |
|
|
|
|
|
None as Option<Vec<u8>> |
|
|
|
|
|
], |
|
|
|
|
|
)?; |
|
|
|
|
|
Ok(()) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const TABLE_NAME: &str = "person"; |
|
|
|
|
|
let pool = setup_pool(); |
|
|
|
|
|
setup_db_structure(&pool, TABLE_NAME)?; |
|
|
|
|
|
Ok((pool, TABLE_NAME.to_string())) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn test_sqlite_json(pool: Pool<SqliteConnectionManager>, table_name: &String) -> SRSResult<()> { |
|
|
|
|
|
let conn = pool.get().unwrap(); |
|
|
|
|
|
let mut stmt = conn.prepare(&format!("SELECT * FROM {}", table_name))?; |
|
|
|
|
|
let result_iter = stmt.query_map(params![], |row| { |
|
|
|
|
|
Ok(from_row_s::<Map<String, Value>>(row).unwrap()) |
|
|
|
|
|
})?; |
|
|
|
|
|
for cur_result in result_iter { |
|
|
|
|
|
println!( |
|
|
|
|
|
"Found row macro {:?}", |
|
|
|
|
|
serde_json::json!(&cur_result.unwrap()).to_string() |
|
|
|
|
|
); |
|
|
|
|
|
/* |
|
|
|
|
|
println!( |
|
|
|
|
|
"Found row to_string {:?}", |
|
|
|
|
|
serde_json::to_string(&cur_result.unwrap()).unwrap() |
|
|
|
|
|
); |
|
|
|
|
|
*/ |
|
|
|
|
|
} |
|
|
|
|
|
Ok(()) |
|
|
|
|
|
} |