Implementing database connection with library


#1

I’m a total Rust noob that’s coming from the somewhat-magical world of Ruby web development. I’m wanting to make a library that can be used for core functionality of native apps. Basically I want the library to manage most of the logic and hand off requested data to the native app. Some of this functionality is interfacing with an SQLite database for now. I’m trying to set up a persistent connection pool that I don’t have to pass around. Follows is the code. Am I going about this the completely wrong way or am I missing something subtle? I kinda feel like a two year old with a shotgun.

In src/lib.rs:

extern crate r2d2;
extern crate r2d2_sqlite;
extern crate rusqlite;

use r2d2_sqlite::SqliteConnectionManager;

struct PoolParty<'c, 'e> {
    config: r2d2::Config<'c, 'e>,
    manager: r2d2_sqlite::SqliteConnectionManager,
    pool: String  // I'm assigning bogus values so that the compiler will tell me what I actually need. I haven't gotten this far yet.
}

impl<'c, 'e> PoolParty<'c, 'e> {
    fn setup_pool(&mut self) {
        self.pool = r2d2::Pool::new(self.config, self.manager).unwrap();
    }
}

static mut POOL: PoolParty = PoolParty {
    config: r2d2::Config::default(),
    manager: SqliteConnectionManager::new_in_memory(),
    pool: ""
};

#2

It’s probably painfully obvious I don’t understand lifetimes and types fully yet. At any rate, I think I’ve figured out the basic paradigm: I’m going to have to wait for the host application to call a method which I can then use to spawn additional thread(s) to keep track of things like the connection manager in the background.


#3

Finally found something that works. lazy_static! is what I was looking for but didn’t know it. There’s still probably a few things wrong with my code, but it works when used as an external crate. Criticism is welcome, as I’m just starting out with all of this.

#[macro_use]
extern crate lazy_static;

extern crate r2d2;
extern crate r2d2_sqlite;
extern crate rusqlite;

use r2d2_sqlite::SqliteConnectionManager;

lazy_static! {
    pub static ref DB_POOL: r2d2::Pool<r2d2_sqlite::SqliteConnectionManager> = setup_db();
}


pub struct Thing {
    pub id: i32,
    pub name: String
}

fn setup_db() -> r2d2::Pool<r2d2_sqlite::SqliteConnectionManager> {
    let config = r2d2::Config::default();
    let manager = SqliteConnectionManager::new("test.db");
    r2d2::Pool::new(config, manager).unwrap()
}

fn connection() -> r2d2::PooledConnection<r2d2_sqlite::SqliteConnectionManager> {
    let pool = DB_POOL.clone();
    pool.get().unwrap()
}


pub fn create_thing(name: &str) {
    connection().execute("INSERT INTO things (name) VALUES ($1)", &[&name.to_string()]).unwrap();
}

pub fn get_things() -> Vec<Thing> {
    let c = connection();
    let mut stmt = c.prepare("SELECT id, name FROM things").unwrap();
    let thing_itr = stmt.query_map(&[], |row| {
        Thing {
            id: row.get(0),
            name: row.get(1)
        }
    }).unwrap();
    let mut things = Vec::new();
    for thing in thing_itr { things.push( thing.unwrap() ); }
    things
}

pub fn migrate_db() {
    connection().execute("CREATE TABLE things (
        id INTEGER PRIMARY KEY,
        name TEXT NOT NULL
    );", &[]).unwrap();
}

#4

@bitgrinder, this wound up being exactly what I needed – I fought with this problem for eight hours, before I stumbled upon your post. Thank you :slight_smile: