sqlite: initial work in connection

This commit is contained in:
Ryan Leckey
2020-03-11 11:01:17 -07:00
parent 5d042e35b1
commit 7ab07016da
16 changed files with 245 additions and 33 deletions

View File

@@ -17,14 +17,14 @@ default = [ "runtime-async-std" ]
unstable = []
postgres = [ "md-5", "sha2", "base64", "sha-1", "rand", "hmac" ]
mysql = [ "sha-1", "sha2", "generic-array", "num-bigint", "base64", "digest", "rand" ]
sqlite = [ ]
sqlite = [ "libc", "libsqlite3-sys" ]
tls = [ "async-native-tls" ]
runtime-async-std = [ "async-native-tls/runtime-async-std", "async-std" ]
runtime-tokio = [ "async-native-tls/runtime-tokio", "tokio" ]
[dependencies]
async-native-tls = { version = "0.3.2", default-features = false, optional = true }
async-std = { version = "1.5.0", optional = true }
async-std = { version = "1.5.0", features = [ "unstable" ], optional = true }
async-stream = { version = "0.2.1", default-features = false }
base64 = { version = "0.11.0", default-features = false, optional = true, features = [ "std" ] }
bitflags = { version = "1.2.1", default-features = false }
@@ -50,6 +50,14 @@ sha2 = { version = "0.8.1", default-features = false, optional = true }
tokio = { version = "0.2.13", default-features = false, features = [ "dns", "fs", "time", "tcp" ], optional = true }
url = { version = "2.1.1", default-features = false }
uuid = { version = "0.8.1", default-features = false, optional = true, features = [ "std" ] }
libc = { version = "0.2", optional = true }
# <https://github.com/jgallagher/rusqlite/tree/master/libsqlite3-sys>
[dependencies.libsqlite3-sys]
version = "0.17.1"
optional = true
default-features = false
features = [ "pkg-config", "vcpkg", "bundled" ]
[dev-dependencies]
matches = "0.1.8"

View File

@@ -6,7 +6,7 @@ pub use async_std::{
io::{Read as AsyncRead, Write as AsyncWrite},
net::TcpStream,
task::sleep,
task::spawn,
task::{spawn, spawn_blocking},
task::yield_now,
};
@@ -15,7 +15,7 @@ pub use tokio::{
fs,
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
net::TcpStream,
task::spawn,
task::{spawn, spawn_blocking},
task::yield_now,
time::delay_for as sleep,
time::timeout,

View File

@@ -2,23 +2,11 @@ use crate::arguments::Arguments;
use crate::encode::Encode;
use crate::sqlite::Sqlite;
use crate::types::Type;
#[derive(Debug, Clone)]
pub enum SqliteValue {
// TODO: Take by reference to remove the allocation
Text(String),
// TODO: Take by reference to remove the allocation
Blob(Vec<u8>),
Double(f64),
Int(i64),
}
use crate::sqlite::value::SqliteArgumentValue;
#[derive(Default)]
pub struct SqliteArguments {
values: Vec<SqliteValue>,
values: Vec<SqliteArgumentValue>,
}
impl Arguments for SqliteArguments {

View File

@@ -1,11 +1,70 @@
use core::ptr::{NonNull, null, null_mut};
use std::convert::TryInto;
use std::ffi::CString;
use std::fmt::{self, Debug};
use futures_core::future::BoxFuture;
use libsqlite3_sys::{
sqlite3, sqlite3_open_v2, SQLITE_OK, SQLITE_OPEN_CREATE, SQLITE_OPEN_NOMUTEX,
SQLITE_OPEN_READWRITE, SQLITE_OPEN_SHAREDCACHE,
};
use crate::runtime::spawn_blocking;
use crate::connection::{Connect, Connection};
use crate::url::Url;
use futures_util::future;
use crate::sqlite::SqliteError;
pub struct SqliteConnection {}
#[derive(Debug)]
pub struct SqliteConnection {
pub(super) handle: NonNull<sqlite3>,
}
// SAFE: A sqlite3 handle is safe to access from multiple threads provided
// that only one thread access it at a time. Or in other words,
// the same guarantees that [Sync] requires. This is upheld as long
// [SQLITE_CONFIG_MULTITHREAD] is enabled and [SQLITE_THREADSAFE] was
// enabled when sqlite was compiled. We refuse to work if these conditions are
// not upheld, see [SqliteConnection::establish].
//
// <https://www.sqlite.org/c3ref/threadsafe.html>
// <https://www.sqlite.org/c3ref/c_config_covering_index_scan.html#sqliteconfigmultithread>
#[allow(unsafe_code)]
unsafe impl Send for SqliteConnection {}
#[allow(unsafe_code)]
unsafe impl Sync for SqliteConnection {}
fn establish(url: crate::Result<Url>) -> crate::Result<SqliteConnection> {
let url = url?;
let url = url.as_str().trim_start_matches("sqlite://");
// By default, we connect to an in-memory database.
// TODO: Handle the error when there are internal NULs in the database URL
let filename = CString::new(url).unwrap();
let mut handle = null_mut();
// [SQLITE_OPEN_NOMUTEX] will instruct [sqlite3_open_v2] to return an error if it
// cannot satisfy our wish for a thread-safe, lock-free connection object
let flags = SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE
| SQLITE_OPEN_NOMUTEX
| SQLITE_OPEN_SHAREDCACHE;
// <https://www.sqlite.org/c3ref/open.html>
#[allow(unsafe_code)]
let status = unsafe {
sqlite3_open_v2(filename.as_ptr(), &mut handle, flags, null())
};
if status != SQLITE_OK {
return Err(SqliteError::new(status).into());
}
Ok(SqliteConnection {
handle: NonNull::new(handle).unwrap(),
})
}
impl Connect for SqliteConnection {
fn connect<T>(url: T) -> BoxFuture<'static, crate::Result<SqliteConnection>>
@@ -13,8 +72,8 @@ impl Connect for SqliteConnection {
T: TryInto<Url, Error = crate::Error>,
Self: Sized,
{
// Box::pin(SqliteConnection::new(url.try_into()))
todo!()
let url = url.try_into();
Box::pin(spawn_blocking(move || establish(url)))
}
}
@@ -25,7 +84,7 @@ impl Connection for SqliteConnection {
}
fn ping(&mut self) -> BoxFuture<crate::Result<()>> {
//Box::pin(Executor::execute(self, "SELECT 1").map_ok(|_| ()))
todo!()
// For SQLite connections, PING does effectively nothing
Box::pin(future::ok(()))
}
}

View File

@@ -1,5 +1,5 @@
use crate::database::{Database, HasCursor, HasRawValue, HasRow};
use crate::sqlite::arguments::SqliteValue;
use crate::sqlite::value::{SqliteResultValue, SqliteArgumentValue};
/// **Sqlite** database driver.
pub struct Sqlite;
@@ -14,13 +14,13 @@ impl Database for Sqlite {
// TODO?
type TableId = u32;
type RawBuffer = Vec<SqliteValue>;
type RawBuffer = Vec<SqliteArgumentValue>;
}
impl<'a> HasRow<'a> for Sqlite {
impl<'c> HasRow<'c> for Sqlite {
type Database = Sqlite;
type Row = super::SqliteRow<'a>;
type Row = super::SqliteRow<'c>;
}
impl<'s, 'q> HasCursor<'s, 'q> for Sqlite {
@@ -29,7 +29,6 @@ impl<'s, 'q> HasCursor<'s, 'q> for Sqlite {
type Cursor = super::SqliteCursor<'s, 'q>;
}
impl<'a> HasRawValue<'a> for Sqlite {
// TODO
type RawValue = Option<()>;
impl<'c> HasRawValue<'c> for Sqlite {
type RawValue = SqliteResultValue<'c>;
}

View File

@@ -1,10 +1,28 @@
use crate::error::DatabaseError;
use libc::c_int;
use std::ffi::CStr;
use libsqlite3_sys::{sqlite3, sqlite3_errstr};
pub struct SqliteError;
pub struct SqliteError {
#[allow(dead_code)]
code: c_int,
message: String,
}
impl SqliteError {
pub(crate) fn new(code: c_int) -> Self {
#[allow(unsafe_code)]
let message = unsafe {
CStr::from_ptr(sqlite3_errstr(code))
};
Self { code, message: message.to_string_lossy().into_owned() }
}
}
impl DatabaseError for SqliteError {
fn message(&self) -> &str {
todo!()
&self.message
}
}

View File

@@ -3,6 +3,7 @@ mod connection;
mod cursor;
mod database;
mod error;
mod value;
mod executor;
mod row;
mod types;
@@ -14,3 +15,11 @@ pub use database::Sqlite;
pub use error::SqliteError;
pub use row::SqliteRow;
pub use types::SqliteTypeInfo;
/// An alias for [`Pool`][crate::Pool], specialized for **Sqlite**.
pub type SqlitePool = crate::pool::Pool<SqliteConnection>;
make_query_as!(SqliteQueryAs, Sqlite, SqliteRow);
impl_map_row_for_row!(Sqlite, SqliteRow);
impl_column_index_for_row!(Sqlite);
impl_from_row_for_tuples!(Sqlite, SqliteRow);

View File

@@ -7,9 +7,11 @@ use std::sync::Arc;
use crate::error::UnexpectedNullError;
use crate::row::{ColumnIndex, Row};
use crate::sqlite::Sqlite;
use crate::sqlite::value::SqliteResultValue;
pub struct SqliteRow<'c> {
c: std::marker::PhantomData<&'c ()>,
pub(super) columns: Arc<HashMap<Box<str>, u16>>,
}
impl<'c> Row<'c> for SqliteRow<'c> {
@@ -19,7 +21,7 @@ impl<'c> Row<'c> for SqliteRow<'c> {
todo!()
}
fn try_get_raw<'r, I>(&'r self, index: I) -> crate::Result<Option<()>>
fn try_get_raw<'r, I>(&'r self, index: I) -> crate::Result<SqliteResultValue<'c>>
where
I: ColumnIndex<Self::Database>,
{

View File

@@ -0,0 +1,25 @@
use crate::types::Type;
use crate::sqlite::{Sqlite, SqliteTypeInfo};
use crate::encode::Encode;
use crate::sqlite::value::{SqliteArgumentValue, SqliteResultValue};
use crate::decode::Decode;
impl Type<Sqlite> for i32 {
fn type_info() -> SqliteTypeInfo {
// SqliteTypeInfo::new(ValueKind::Int)
todo!()
}
}
impl Encode<Sqlite> for i32 {
fn encode(&self, values: &mut Vec<SqliteArgumentValue>) {
values.push(SqliteArgumentValue::Int((*self).into()));
}
}
impl<'a> Decode<'a, Sqlite> for i32 {
fn decode(value: SqliteResultValue<'a>) -> crate::Result<i32> {
// Ok(value.int())
todo!()
}
}

View File

@@ -2,6 +2,12 @@ use std::fmt::{self, Display};
use crate::types::TypeInfo;
// mod bool;
// mod bytes;
// mod float;
mod int;
// mod str;
#[derive(Debug, Clone)]
pub struct SqliteTypeInfo {}

View File

@@ -0,0 +1,17 @@
#[derive(Debug, Clone)]
pub enum SqliteArgumentValue {
// TODO: Take by reference to remove the allocation
Text(String),
// TODO: Take by reference to remove the allocation
Blob(Vec<u8>),
Double(f64),
Int(i64),
}
pub struct SqliteResultValue<'c> {
// statement: SqliteStatement<'c>,
statement: std::marker::PhantomData<&'c ()>,
}

View File

@@ -1,6 +1,7 @@
use std::borrow::Cow;
use std::convert::{TryFrom, TryInto};
#[derive(Debug)]
pub struct Url(url::Url);
impl TryFrom<String> for Url {
@@ -28,6 +29,10 @@ impl<'s> TryFrom<&'s String> for Url {
}
impl Url {
pub(crate) fn as_str(&self) -> &str {
self.0.as_str()
}
pub fn host(&self) -> &str {
let host = self.0.host_str();