tokio-rs / rdbc

Rust DataBase Connectivity (RDBC) :: Common Rust API for database drivers
Apache License 2.0
568 stars 25 forks source link

Design async version of traits #53

Open andygrove opened 4 years ago

andygrove commented 4 years ago

The initial PoC of RDBC was pretty naive and copied from ODBC/JDBC and is not idiomatic Rust code. This issue has been created to discuss how to create an idiomatic Rust set of traits using async for the project.

Specific issues with the current traits:

andygrove commented 4 years ago

For reference, the current traits are:

/// Represents database driver that can be shared between threads, and can therefore implement
/// a connection pool
pub trait Driver: Sync + Send {
    /// Create a connection to the database. Note that connections are intended to be used
    /// in a single thread since most database connections are not thread-safe
    fn connect(&self, url: &str) -> Result<Box<dyn Connection>>;
}

/// Represents a connection to a database
pub trait Connection {
    /// Create a statement for execution
    fn create(&mut self, sql: &str) -> Result<Box<dyn Statement + '_>>;

    /// Create a prepared statement for execution
    fn prepare(&mut self, sql: &str) -> Result<Box<dyn Statement + '_>>;
}

/// Represents an executable statement
pub trait Statement {
    /// Execute a query that is expected to return a result set, such as a `SELECT` statement
    fn execute_query(&mut self, params: &[Value]) -> Result<Box<dyn ResultSet + '_>>;

    /// Execute a query that is expected to update some rows.
    fn execute_update(&mut self, params: &[Value]) -> Result<u64>;
}

/// Result set from executing a query against a statement
pub trait ResultSet {
    /// get meta data about this result set
    fn meta_data(&self) -> Result<Box<dyn ResultSetMetaData>>;

    /// Move the cursor to the next available row if one exists and return true if it does
    fn next(&mut self) -> bool;

    fn get_i8(&self, i: u64) -> Result<Option<i8>>;
    fn get_i16(&self, i: u64) -> Result<Option<i16>>;
    fn get_i32(&self, i: u64) -> Result<Option<i32>>;
    fn get_i64(&self, i: u64) -> Result<Option<i64>>;
    fn get_f32(&self, i: u64) -> Result<Option<f32>>;
    fn get_f64(&self, i: u64) -> Result<Option<f64>>;
    fn get_string(&self, i: u64) -> Result<Option<String>>;
    fn get_bytes(&self, i: u64) -> Result<Option<Vec<u8>>>;
}

/// Meta data for result set
pub trait ResultSetMetaData {
    fn num_columns(&self) -> u64;
    fn column_name(&self, i: u64) -> String;
    fn column_type(&self, i: u64) -> DataType;
}
95th commented 4 years ago

The Row trait has:

    fn get_string(&self, i: u64) -> Result<Option<String>>;
    fn get_bytes(&self, i: u64) -> Result<Option<Vec<u8>>>;

Shouldn't these be

    fn get_string(&self, i: u64) -> Result<Option<&str>>;
    fn get_bytes(&self, i: u64) -> Result<Option<&[u8]>>;

The user can always get its owned version if needed.

sd2k commented 3 years ago

I wrote an experimental version of an feature gated async API here: https://github.com/sd2k/rdbc-sync-async-apis. Perhaps it would serve as a starting point for something more polished!

sd2k commented 3 years ago

Also the sqlx::any module may provide useful insight.